From 512a31339ab7b2efc86f2877f1917799b30b7051 Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Tue, 10 Jun 2025 12:16:19 -0300 Subject: [PATCH] UX: composer toolbar changes (icon, style, placement) (#32918) Changes the gear icon for the more menu to a circle-plus icon. Changes the emoji icon to its outline version, to make it less similar to the circle-plus icon. Changes the styles (eg. icon sizes) of the toolbar, using a flexbox instead of a grid, with some tweaks and animations to the toggle switch, which occupies a smaller width now. Removes the gray button-bar bottom border. Moves the Insert Date/Time item to the more menu, and changes its icon to a clock. ### Before, hovering more menu image ### After, hovering more menu ![image](https://github.com/user-attachments/assets/b54eac09-9dd0-4b7f-b93c-82d452cc5ded) --------- Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com> --- .../app/components/composer-editor.gjs | 2 +- .../discourse/app/components/d-editor.gjs | 28 ++++++---- .../app/instance-initializers/enable-emoji.js | 2 +- .../components/composer-toggle-switch.scss | 55 +++++++++---------- app/assets/stylesheets/common/d-editor.scss | 25 ++++----- .../toolbar-popup-menu-options.scss | 2 + .../initializers/discourse-local-dates.js | 33 +++++------ .../spec/system/local_dates_spec.rb | 12 ++-- .../acceptance/local-dates-composer-test.js | 8 ++- 9 files changed, 87 insertions(+), 80 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.gjs b/app/assets/javascripts/discourse/app/components/composer-editor.gjs index 84a163976e9..7977679fdb0 100644 --- a/app/assets/javascripts/discourse/app/components/composer-editor.gjs +++ b/app/assets/javascripts/discourse/app/components/composer-editor.gjs @@ -924,7 +924,7 @@ export default class ComposerEditor extends Component { toolbar.addButton({ id: "options", group: "extras", - icon: "gear", + icon: "circle-plus", title: "composer.options", sendAction: this.onExpandPopupMenuOptions.bind(this), popupMenu: { diff --git a/app/assets/javascripts/discourse/app/components/d-editor.gjs b/app/assets/javascripts/discourse/app/components/d-editor.gjs index 8ba4e0c2d03..25bbd9431f5 100644 --- a/app/assets/javascripts/discourse/app/components/d-editor.gjs +++ b/app/assets/javascripts/discourse/app/components/d-editor.gjs @@ -140,18 +140,22 @@ export default class DEditor extends Component { }; }); - if (this.popupMenuOptions && this.onPopupMenuAction) { - this.popupMenuOptions.forEach((popupButton) => { - if (popupButton.shortcut && popupButton.condition) { - const shortcut = - `${PLATFORM_KEY_MODIFIER}+${popupButton.shortcut}`.toLowerCase(); - keymap[shortcut] = () => { - this.onPopupMenuAction(popupButton, this.newToolbarEvent()); - return false; - }; - } - }); - } + this.popupMenuOptions?.forEach((popupButton) => { + if (popupButton.shortcut && popupButton.condition) { + const shortcut = + `${PLATFORM_KEY_MODIFIER}+${popupButton.shortcut}`.toLowerCase(); + keymap[shortcut] = () => { + this.onPopupMenuAction( + { + ...popupButton, + action: popupButton.shortcutAction ?? popupButton.action, + }, + this.newToolbarEvent() + ); + return false; + }; + } + }); keymap["tab"] = () => this.textManipulation.indentSelection("right"); keymap["shift+tab"] = () => this.textManipulation.indentSelection("left"); diff --git a/app/assets/javascripts/discourse/app/instance-initializers/enable-emoji.js b/app/assets/javascripts/discourse/app/instance-initializers/enable-emoji.js index 0d3425acc0b..b96f407d0f4 100644 --- a/app/assets/javascripts/discourse/app/instance-initializers/enable-emoji.js +++ b/app/assets/javascripts/discourse/app/instance-initializers/enable-emoji.js @@ -16,7 +16,7 @@ export default { toolbar.addButton({ id: "emoji", group: "extras", - icon: "face-smile", + icon: "far-face-smile", sendAction: () => { const menu = api.container.lookup("service:menu"); menu.show(document.querySelector(".insert-composer-emoji"), { diff --git a/app/assets/stylesheets/common/components/composer-toggle-switch.scss b/app/assets/stylesheets/common/components/composer-toggle-switch.scss index 2f5c00e736b..b1d2783c77c 100644 --- a/app/assets/stylesheets/common/components/composer-toggle-switch.scss +++ b/app/assets/stylesheets/common/components/composer-toggle-switch.scss @@ -1,8 +1,6 @@ .composer-toggle-switch { --toggle-switch-width: 40px; --toggle-switch-height: 24px; - height: 100%; - grid-column: span 2; justify-content: center; display: flex; align-items: center; @@ -38,21 +36,20 @@ display: block; position: absolute; background-color: var(--tertiary-low); - width: calc(var(--toggle-switch-height) - 2px); - height: calc(var(--toggle-switch-height) - 4px); - top: 2px; - transition: - left 0.25s, - right 0.25s; + width: calc(var(--toggle-switch-height) - 0.125rem); + height: calc(var(--toggle-switch-height) - 0.25rem); + top: 0.125rem; border-radius: 0.25em; - box-shadow: 0 1px 3px 1px rgb(0, 0, 0, 0.1); + box-shadow: 0 1px 2px 1px rgb(var(--tertiary-rgb), 0.2); .--markdown & { - left: 2px; + transform: translateX(0.125rem); } .--rte & { - right: 2px; + transform: translateX( + calc(var(--toggle-switch-width) - var(--toggle-switch-height)) + ); } @media (prefers-reduced-motion: reduce) { @@ -63,35 +60,37 @@ &__left-icon, &__right-icon { - display: inline-block; + display: inline-flex; + align-items: center; + justify-content: center; position: absolute; opacity: 0; - transition: - opacity 0.25s left 0.25s, - right 0.25s; height: 100%; - width: calc(var(--toggle-switch-height) - 2px); + width: calc(var(--toggle-switch-height) - 0.125rem); + + .d-icon { + color: var(--primary); + vertical-align: text-bottom; + } @media (prefers-reduced-motion: reduce) { transition-duration: 0ms; } + } + + &__left-icon { + left: 0.125rem; .--markdown & { - left: 2px; - } - - .--rte & { - right: 2px; - } - - &.--active { opacity: 1; } + } - .d-icon { - font-size: var(--font-down-1); - color: var(--primary); - vertical-align: text-bottom; + &__right-icon { + right: 0.125rem; + + .--rte & { + opacity: 1; } } } diff --git a/app/assets/stylesheets/common/d-editor.scss b/app/assets/stylesheets/common/d-editor.scss index 147769b7f27..276d4f3720b 100644 --- a/app/assets/stylesheets/common/d-editor.scss +++ b/app/assets/stylesheets/common/d-editor.scss @@ -313,21 +313,14 @@ // d-editor bar button sizing .d-editor-button-bar { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(2.35em, 1fr)); + display: flex; + flex-direction: row; + flex-wrap: wrap; align-items: center; - border-bottom: var(--d-input-border); - width: 100%; - box-sizing: border-box; - flex-shrink: 0; + margin-top: 0.25rem; - @include viewport.until(md) { - // occupy available space on narrower screens - grid-template-columns: repeat(auto-fit, minmax(2em, 1fr)); - } - - @include viewport.until(sm) { - font-size: var(--font-down-1); + .composer-toggle-switch { + padding: 0 0.5rem; } .btn:focus-visible { @@ -346,7 +339,11 @@ .discourse-no-touch & { &:hover { - color: var(--primary-low); + background-color: var(--primary-low); + + .d-icon { + color: var(--primary-medium); + } } } } diff --git a/app/assets/stylesheets/common/select-kit/toolbar-popup-menu-options.scss b/app/assets/stylesheets/common/select-kit/toolbar-popup-menu-options.scss index afab5f8b571..8bd839188eb 100644 --- a/app/assets/stylesheets/common/select-kit/toolbar-popup-menu-options.scss +++ b/app/assets/stylesheets/common/select-kit/toolbar-popup-menu-options.scss @@ -28,6 +28,8 @@ font-size: var(--font-down-2); color: var(--primary-high); margin-left: 1.8rem; + font-family: var(--font-family); + font-weight: bold; } &:last-child { diff --git a/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js b/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js index 2bf8075e9ae..189fbba0e15 100644 --- a/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js +++ b/plugins/discourse-local-dates/assets/javascripts/initializers/discourse-local-dates.js @@ -162,25 +162,22 @@ function initializeDiscourseLocalDates(api) { }); }); - api.onToolbarCreate((toolbar) => { - toolbar.addButton({ - title: "discourse_local_dates.title", - id: "local-dates", - group: "extras", - icon: "calendar-days", - perform: (event) => - modal.show(LocalDatesCreateModal, { - model: { insertDate: (markup) => event.addText(markup) }, - }), - shortcut: "Shift+.", - shortcutAction: (event) => { - const timezone = api.getCurrentUser().user_option.timezone; - const time = moment().format("HH:mm:ss"); - const date = moment().format("YYYY-MM-DD"); + api.addComposerToolbarPopupMenuOption({ + name: "local-dates", + label: "discourse_local_dates.title", + icon: "far-clock", + action: (event) => + modal.show(LocalDatesCreateModal, { + model: { insertDate: (markup) => event.addText(markup) }, + }), + shortcut: "Shift+.", + shortcutAction: (event) => { + const timezone = api.getCurrentUser().user_option.timezone; + const time = moment().format("HH:mm:ss"); + const date = moment().format("YYYY-MM-DD"); - event.addText(`[date=${date} time=${time} timezone="${timezone}"]`); - }, - }); + event.addText(`[date=${date} time=${time} timezone="${timezone}"]`); + }, }); addTextDecorateCallback( diff --git a/plugins/discourse-local-dates/spec/system/local_dates_spec.rb b/plugins/discourse-local-dates/spec/system/local_dates_spec.rb index 935871fbf8f..7be53475454 100644 --- a/plugins/discourse-local-dates/spec/system/local_dates_spec.rb +++ b/plugins/discourse-local-dates/spec/system/local_dates_spec.rb @@ -86,7 +86,8 @@ describe "Local dates", type: :system do it "allows selecting a date without a time and inserts into the post" do topic_page.visit_topic_and_open_composer(topic) expect(topic_page).to have_expanded_composer - composer.click_toolbar_button("local-dates") + find(".d-editor-button-bar .toolbar-popup-menu-options").click + page.find(".toolbar-popup-menu-options [data-name=local-dates]").click expect(insert_datetime_modal).to be_open insert_datetime_modal.calendar_date_time_picker.select_year(year) insert_datetime_modal.calendar_date_time_picker.select_day(16) @@ -99,7 +100,8 @@ describe "Local dates", type: :system do it "allows selecting a date with a time and inserts into the post" do topic_page.visit_topic_and_open_composer(topic) expect(topic_page).to have_expanded_composer - composer.click_toolbar_button("local-dates") + find(".d-editor-button-bar .toolbar-popup-menu-options").click + page.find(".toolbar-popup-menu-options [data-name=local-dates]").click expect(insert_datetime_modal).to be_open insert_datetime_modal.calendar_date_time_picker.select_year(year) insert_datetime_modal.calendar_date_time_picker.select_day(16) @@ -114,7 +116,8 @@ describe "Local dates", type: :system do it "allows selecting a start date and time and an end date and time" do topic_page.visit_topic_and_open_composer(topic) expect(topic_page).to have_expanded_composer - composer.click_toolbar_button("local-dates") + find(".d-editor-button-bar .toolbar-popup-menu-options").click + page.find(".toolbar-popup-menu-options [data-name=local-dates]").click expect(insert_datetime_modal).to be_open insert_datetime_modal.calendar_date_time_picker.select_year(year) insert_datetime_modal.calendar_date_time_picker.select_day(16) @@ -136,7 +139,8 @@ describe "Local dates", type: :system do expect(topic_page).to have_expanded_composer - composer.click_toolbar_button("local-dates") + find(".d-editor-button-bar .toolbar-popup-menu-options").click + page.find(".toolbar-popup-menu-options [data-name=local-dates]").click expect(insert_datetime_modal).to be_open diff --git a/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js b/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js index 8a2c4cc958b..e436bfc519b 100644 --- a/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js +++ b/plugins/discourse-local-dates/test/javascripts/acceptance/local-dates-composer-test.js @@ -78,7 +78,9 @@ acceptance("Local Dates - composer", function (needs) { const categoryChooser = selectKit(".category-chooser"); await categoryChooser.expand(); await categoryChooser.selectRowByValue(2); - await click(".d-editor-button-bar .local-dates"); + const optionsMenu = selectKit(".toolbar-popup-menu-options"); + await optionsMenu.expand(); + await optionsMenu.selectRowByName("local-dates"); const timezoneChooser = selectKit(".timezone-input"); await timezoneChooser.expand(); @@ -95,7 +97,9 @@ acceptance("Local Dates - composer", function (needs) { const categoryChooser = selectKit(".category-chooser"); await categoryChooser.expand(); await categoryChooser.selectRowByValue(2); - await click(".d-editor-button-bar .local-dates"); + const optionsMenu = selectKit(".toolbar-popup-menu-options"); + await optionsMenu.expand(); + await optionsMenu.selectRowByName("local-dates"); await click('.pika-table td[data-day="5"] > .pika-button');