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

<img width="758" alt="image"
src="https://github.com/user-attachments/assets/84d8f5aa-519e-40a2-ba44-d58d7294f6b0"
/>

### 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>
This commit is contained in:
Renato Atilio
2025-06-10 12:16:19 -03:00
committed by GitHub
parent 0c6dec79e5
commit 512a31339a
9 changed files with 87 additions and 80 deletions

View File

@ -924,7 +924,7 @@ export default class ComposerEditor extends Component {
toolbar.addButton({ toolbar.addButton({
id: "options", id: "options",
group: "extras", group: "extras",
icon: "gear", icon: "circle-plus",
title: "composer.options", title: "composer.options",
sendAction: this.onExpandPopupMenuOptions.bind(this), sendAction: this.onExpandPopupMenuOptions.bind(this),
popupMenu: { popupMenu: {

View File

@ -140,18 +140,22 @@ export default class DEditor extends Component {
}; };
}); });
if (this.popupMenuOptions && this.onPopupMenuAction) { this.popupMenuOptions?.forEach((popupButton) => {
this.popupMenuOptions.forEach((popupButton) => { if (popupButton.shortcut && popupButton.condition) {
if (popupButton.shortcut && popupButton.condition) { const shortcut =
const shortcut = `${PLATFORM_KEY_MODIFIER}+${popupButton.shortcut}`.toLowerCase();
`${PLATFORM_KEY_MODIFIER}+${popupButton.shortcut}`.toLowerCase(); keymap[shortcut] = () => {
keymap[shortcut] = () => { this.onPopupMenuAction(
this.onPopupMenuAction(popupButton, this.newToolbarEvent()); {
return false; ...popupButton,
}; action: popupButton.shortcutAction ?? popupButton.action,
} },
}); this.newToolbarEvent()
} );
return false;
};
}
});
keymap["tab"] = () => this.textManipulation.indentSelection("right"); keymap["tab"] = () => this.textManipulation.indentSelection("right");
keymap["shift+tab"] = () => this.textManipulation.indentSelection("left"); keymap["shift+tab"] = () => this.textManipulation.indentSelection("left");

View File

@ -16,7 +16,7 @@ export default {
toolbar.addButton({ toolbar.addButton({
id: "emoji", id: "emoji",
group: "extras", group: "extras",
icon: "face-smile", icon: "far-face-smile",
sendAction: () => { sendAction: () => {
const menu = api.container.lookup("service:menu"); const menu = api.container.lookup("service:menu");
menu.show(document.querySelector(".insert-composer-emoji"), { menu.show(document.querySelector(".insert-composer-emoji"), {

View File

@ -1,8 +1,6 @@
.composer-toggle-switch { .composer-toggle-switch {
--toggle-switch-width: 40px; --toggle-switch-width: 40px;
--toggle-switch-height: 24px; --toggle-switch-height: 24px;
height: 100%;
grid-column: span 2;
justify-content: center; justify-content: center;
display: flex; display: flex;
align-items: center; align-items: center;
@ -38,21 +36,20 @@
display: block; display: block;
position: absolute; position: absolute;
background-color: var(--tertiary-low); background-color: var(--tertiary-low);
width: calc(var(--toggle-switch-height) - 2px); width: calc(var(--toggle-switch-height) - 0.125rem);
height: calc(var(--toggle-switch-height) - 4px); height: calc(var(--toggle-switch-height) - 0.25rem);
top: 2px; top: 0.125rem;
transition:
left 0.25s,
right 0.25s;
border-radius: 0.25em; 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 & { .--markdown & {
left: 2px; transform: translateX(0.125rem);
} }
.--rte & { .--rte & {
right: 2px; transform: translateX(
calc(var(--toggle-switch-width) - var(--toggle-switch-height))
);
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
@ -63,35 +60,37 @@
&__left-icon, &__left-icon,
&__right-icon { &__right-icon {
display: inline-block; display: inline-flex;
align-items: center;
justify-content: center;
position: absolute; position: absolute;
opacity: 0; opacity: 0;
transition:
opacity 0.25s left 0.25s,
right 0.25s;
height: 100%; 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) { @media (prefers-reduced-motion: reduce) {
transition-duration: 0ms; transition-duration: 0ms;
} }
}
&__left-icon {
left: 0.125rem;
.--markdown & { .--markdown & {
left: 2px;
}
.--rte & {
right: 2px;
}
&.--active {
opacity: 1; opacity: 1;
} }
}
.d-icon { &__right-icon {
font-size: var(--font-down-1); right: 0.125rem;
color: var(--primary);
vertical-align: text-bottom; .--rte & {
opacity: 1;
} }
} }
} }

View File

@ -313,21 +313,14 @@
// d-editor bar button sizing // d-editor bar button sizing
.d-editor-button-bar { .d-editor-button-bar {
display: grid; display: flex;
grid-template-columns: repeat(auto-fill, minmax(2.35em, 1fr)); flex-direction: row;
flex-wrap: wrap;
align-items: center; align-items: center;
border-bottom: var(--d-input-border); margin-top: 0.25rem;
width: 100%;
box-sizing: border-box;
flex-shrink: 0;
@include viewport.until(md) { .composer-toggle-switch {
// occupy available space on narrower screens padding: 0 0.5rem;
grid-template-columns: repeat(auto-fit, minmax(2em, 1fr));
}
@include viewport.until(sm) {
font-size: var(--font-down-1);
} }
.btn:focus-visible { .btn:focus-visible {
@ -346,7 +339,11 @@
.discourse-no-touch & { .discourse-no-touch & {
&:hover { &:hover {
color: var(--primary-low); background-color: var(--primary-low);
.d-icon {
color: var(--primary-medium);
}
} }
} }
} }

View File

@ -28,6 +28,8 @@
font-size: var(--font-down-2); font-size: var(--font-down-2);
color: var(--primary-high); color: var(--primary-high);
margin-left: 1.8rem; margin-left: 1.8rem;
font-family: var(--font-family);
font-weight: bold;
} }
&:last-child { &:last-child {

View File

@ -162,25 +162,22 @@ function initializeDiscourseLocalDates(api) {
}); });
}); });
api.onToolbarCreate((toolbar) => { api.addComposerToolbarPopupMenuOption({
toolbar.addButton({ name: "local-dates",
title: "discourse_local_dates.title", label: "discourse_local_dates.title",
id: "local-dates", icon: "far-clock",
group: "extras", action: (event) =>
icon: "calendar-days", modal.show(LocalDatesCreateModal, {
perform: (event) => model: { insertDate: (markup) => event.addText(markup) },
modal.show(LocalDatesCreateModal, { }),
model: { insertDate: (markup) => event.addText(markup) }, shortcut: "Shift+.",
}), shortcutAction: (event) => {
shortcut: "Shift+.", const timezone = api.getCurrentUser().user_option.timezone;
shortcutAction: (event) => { const time = moment().format("HH:mm:ss");
const timezone = api.getCurrentUser().user_option.timezone; const date = moment().format("YYYY-MM-DD");
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( addTextDecorateCallback(

View File

@ -86,7 +86,8 @@ describe "Local dates", type: :system do
it "allows selecting a date without a time and inserts into the post" do it "allows selecting a date without a time and inserts into the post" do
topic_page.visit_topic_and_open_composer(topic) topic_page.visit_topic_and_open_composer(topic)
expect(topic_page).to have_expanded_composer 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 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_year(year)
insert_datetime_modal.calendar_date_time_picker.select_day(16) 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 it "allows selecting a date with a time and inserts into the post" do
topic_page.visit_topic_and_open_composer(topic) topic_page.visit_topic_and_open_composer(topic)
expect(topic_page).to have_expanded_composer 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 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_year(year)
insert_datetime_modal.calendar_date_time_picker.select_day(16) 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 it "allows selecting a start date and time and an end date and time" do
topic_page.visit_topic_and_open_composer(topic) topic_page.visit_topic_and_open_composer(topic)
expect(topic_page).to have_expanded_composer 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 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_year(year)
insert_datetime_modal.calendar_date_time_picker.select_day(16) 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 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 expect(insert_datetime_modal).to be_open

View File

@ -78,7 +78,9 @@ acceptance("Local Dates - composer", function (needs) {
const categoryChooser = selectKit(".category-chooser"); const categoryChooser = selectKit(".category-chooser");
await categoryChooser.expand(); await categoryChooser.expand();
await categoryChooser.selectRowByValue(2); 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"); const timezoneChooser = selectKit(".timezone-input");
await timezoneChooser.expand(); await timezoneChooser.expand();
@ -95,7 +97,9 @@ acceptance("Local Dates - composer", function (needs) {
const categoryChooser = selectKit(".category-chooser"); const categoryChooser = selectKit(".category-chooser");
await categoryChooser.expand(); await categoryChooser.expand();
await categoryChooser.selectRowByValue(2); 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'); await click('.pika-table td[data-day="5"] > .pika-button');