From aea87bd12d3fcea69837935a113fe5a48578cd4e Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Tue, 27 May 2025 14:22:26 -0300 Subject: [PATCH 1/4] UX: composer toolbar changes (icon, style, placement) --- .../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 | 54 ++++++++++--------- app/assets/stylesheets/common/d-editor.scss | 40 +++++++++----- .../initializers/discourse-local-dates.js | 35 ++++++------ .../spec/system/local_dates_spec.rb | 12 +++-- .../acceptance/local-dates-composer-test.js | 8 ++- 8 files changed, 104 insertions(+), 77 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.gjs b/app/assets/javascripts/discourse/app/components/composer-editor.gjs index c425fdd938ee1..6257919b8a2b3 100644 --- a/app/assets/javascripts/discourse/app/components/composer-editor.gjs +++ b/app/assets/javascripts/discourse/app/components/composer-editor.gjs @@ -917,7 +917,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: true, diff --git a/app/assets/javascripts/discourse/app/components/d-editor.gjs b/app/assets/javascripts/discourse/app/components/d-editor.gjs index 4dbc6a80f674a..d4e8acdef6d4a 100644 --- a/app/assets/javascripts/discourse/app/components/d-editor.gjs +++ b/app/assets/javascripts/discourse/app/components/d-editor.gjs @@ -139,18 +139,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 0d3425acc0b3e..b96f407d0f4e0 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 2f5c00e736b9d..dda8264f2171a 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,24 @@ 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; + width: calc(var(--toggle-switch-height) - 0.125rem); + height: calc(var(--toggle-switch-height) - 0.25rem); + top: 0.125rem; transition: left 0.25s, - right 0.25s; + right 0.25s, + transform 0.25s; 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 +64,38 @@ &__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; + transition: opacity 0.25s ease; 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; + opacity: 1; } + } - .--rte & { - right: 2px; - } + &__right-icon { + right: 0.125rem; - &.--active { + .--rte & { opacity: 1; } - - .d-icon { - font-size: var(--font-down-1); - color: var(--primary); - vertical-align: text-bottom; - } } } diff --git a/app/assets/stylesheets/common/d-editor.scss b/app/assets/stylesheets/common/d-editor.scss index fdc1e5a3ea034..cdd995b034520 100644 --- a/app/assets/stylesheets/common/d-editor.scss +++ b/app/assets/stylesheets/common/d-editor.scss @@ -35,7 +35,6 @@ flex-direction: column; background-color: var(--secondary); position: relative; - border: 1px solid var(--primary-400); border-radius: var(--d-input-border-radius); min-height: 0; @@ -319,21 +318,25 @@ // 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: 1px solid var(--primary-low); - 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)); + .composer-toggle-switch { + min-width: 3.35em; + height: 2.2rem; } - @include viewport.until(sm) { - font-size: var(--font-down-1); + @include viewport.until(md) { + .composer-toggle-switch { + min-width: 3.5rem; + } + + .btn { + min-width: 2.5rem; + } } .btn:focus-visible { @@ -341,10 +344,13 @@ } .btn { + min-width: 2.35em; + height: 2.2rem; margin: 0; background-color: transparent; color: var(--primary-medium); border-radius: var(--d-border-radius); + transition: background-color 0.2s ease; .d-icon { color: currentcolor; @@ -352,9 +358,17 @@ .discourse-no-touch & { &:hover { - color: var(--primary-low); + background-color: var(--primary-low); + + .d-icon { + color: var(--primary-medium); + } } } + + @media (prefers-reduced-motion: reduce) { + transition-duration: 0ms; + } } .select-kit-header-wrapper { 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 2bf8075e9aeef..189fbba0e1507 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"); - - event.addText(`[date=${date} time=${time} timezone="${timezone}"]`); - }, - }); + 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}"]`); + }, }); 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 935871fbf8f15..7be534754544d 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 @@ def formatted_date_for_year(month, day) 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 @@ def formatted_date_for_year(month, day) 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 @@ def formatted_date_for_year(month, day) 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 @@ def formatted_date_for_year(month, day) 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 8a2c4cc958b8c..e436bfc519b59 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'); From 98416faa8195cb566d85ff7d0d0001d243731aa8 Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Wed, 28 May 2025 11:18:55 -0300 Subject: [PATCH 2/4] UX: address PR comments + revert the editor border --- app/assets/stylesheets/common/d-editor.scss | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/app/assets/stylesheets/common/d-editor.scss b/app/assets/stylesheets/common/d-editor.scss index cdd995b034520..43fc249b8351f 100644 --- a/app/assets/stylesheets/common/d-editor.scss +++ b/app/assets/stylesheets/common/d-editor.scss @@ -35,6 +35,7 @@ flex-direction: column; background-color: var(--secondary); position: relative; + border: 1px solid var(--primary-400); border-radius: var(--d-input-border-radius); min-height: 0; @@ -325,18 +326,7 @@ margin-top: 0.25rem; .composer-toggle-switch { - min-width: 3.35em; - height: 2.2rem; - } - - @include viewport.until(md) { - .composer-toggle-switch { - min-width: 3.5rem; - } - - .btn { - min-width: 2.5rem; - } + padding: 0 0.5rem; } .btn:focus-visible { @@ -344,13 +334,10 @@ } .btn { - min-width: 2.35em; - height: 2.2rem; margin: 0; background-color: transparent; color: var(--primary-medium); border-radius: var(--d-border-radius); - transition: background-color 0.2s ease; .d-icon { color: currentcolor; @@ -365,10 +352,6 @@ } } } - - @media (prefers-reduced-motion: reduce) { - transition-duration: 0ms; - } } .select-kit-header-wrapper { From c716642e47968f534226d74496f982507121cb68 Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Thu, 29 May 2025 17:43:25 -0300 Subject: [PATCH 3/4] UX: toolbar popup shortcut font-family/weight --- .../common/select-kit/toolbar-popup-menu-options.scss | 2 ++ 1 file changed, 2 insertions(+) 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 afab5f8b571cb..8bd839188eb66 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 { From 4322ae7c28ddfc58af97f9be4a711b9b704606cd Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Tue, 3 Jun 2025 16:34:48 -0300 Subject: [PATCH 4/4] UX: different transition --- .../common/components/composer-toggle-switch.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/stylesheets/common/components/composer-toggle-switch.scss b/app/assets/stylesheets/common/components/composer-toggle-switch.scss index dda8264f2171a..87a161fb7ca0e 100644 --- a/app/assets/stylesheets/common/components/composer-toggle-switch.scss +++ b/app/assets/stylesheets/common/components/composer-toggle-switch.scss @@ -39,10 +39,7 @@ width: calc(var(--toggle-switch-height) - 0.125rem); height: calc(var(--toggle-switch-height) - 0.25rem); top: 0.125rem; - transition: - left 0.25s, - right 0.25s, - transform 0.25s; + transition: transform 0.2s ease-out; border-radius: 0.25em; box-shadow: 0 1px 2px 1px rgb(var(--tertiary-rgb), 0.2); @@ -69,7 +66,6 @@ justify-content: center; position: absolute; opacity: 0; - transition: opacity 0.25s ease; height: 100%; width: calc(var(--toggle-switch-height) - 0.125rem);