diff options
Diffstat (limited to 'toolkit/content/widgets/moz-button-group')
3 files changed, 177 insertions, 0 deletions
diff --git a/toolkit/content/widgets/moz-button-group/moz-button-group.css b/toolkit/content/widgets/moz-button-group/moz-button-group.css new file mode 100644 index 0000000000..f0cc0e5480 --- /dev/null +++ b/toolkit/content/widgets/moz-button-group/moz-button-group.css @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +:host { + display: flex; + gap: 8px; + justify-content: flex-end; +} + +::slotted(button) { + margin: 0 !important; +} diff --git a/toolkit/content/widgets/moz-button-group/moz-button-group.mjs b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs new file mode 100644 index 0000000000..04c75e5d10 --- /dev/null +++ b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs @@ -0,0 +1,107 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { html } from "chrome://global/content/vendor/lit.all.mjs"; +import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; + +export const PLATFORM_LINUX = "linux"; +export const PLATFORM_MACOS = "macosx"; +export const PLATFORM_WINDOWS = "win"; + +/** + * A grouping of buttons. Primary button order will be set automatically based + * on class="primary", type="submit" or autofocus attribute. Set slot="primary" + * on a primary button that does not have primary styling to set its position. + * + * @tagname moz-button-group + * @property {string} platform - The detected platform, set automatically. + */ +export default class MozButtonGroup extends MozLitElement { + static queries = { + defaultSlotEl: "slot:not([name])", + primarySlotEl: "slot[name=primary]", + }; + + static properties = { + platform: { state: true }, + }; + + // Use a relative URL in storybook to get faster reloads on style changes. + static stylesheetUrl = window.IS_STORYBOOK + ? "./moz-button-group/moz-button-group.css" + : "chrome://global/content/elements/moz-button-group.css"; + + constructor() { + super(); + this.#detectPlatform(); + } + + #detectPlatform() { + if (typeof AppConstants !== "undefined") { + this.platform = AppConstants.platform; + } else if (navigator.platform.includes("Linux")) { + this.platform = PLATFORM_LINUX; + } else if (navigator.platform.includes("Mac")) { + this.platform = PLATFORM_MACOS; + } else { + this.platform = PLATFORM_WINDOWS; + } + } + + onSlotchange(e) { + for (let child of this.defaultSlotEl.assignedNodes()) { + if (!(child instanceof Element)) { + // Text nodes won't support classList or getAttribute. + continue; + } + // Bug 1791816: These should check moz-button instead of button. + if ( + child.localName == "button" && + (child.classList.contains("primary") || + child.getAttribute("type") == "submit" || + child.hasAttribute("autofocus") || + child.hasAttribute("default")) + ) { + child.slot = "primary"; + } + } + this.#reorderLightDom(); + } + + #reorderLightDom() { + let primarySlottedChildren = [...this.primarySlotEl.assignedNodes()]; + if (this.platform == PLATFORM_WINDOWS) { + primarySlottedChildren.reverse(); + for (let child of primarySlottedChildren) { + child.parentElement.prepend(child); + } + } else { + for (let child of primarySlottedChildren) { + // Ensure the primary buttons are at the end of the light DOM. + child.parentElement.append(child); + } + } + } + + updated(changedProperties) { + if (changedProperties.has("platform")) { + this.#reorderLightDom(); + } + } + + render() { + let slots = [ + html` <slot @slotchange=${this.onSlotchange}></slot> `, + html` <slot name="primary"></slot> `, + ]; + if (this.platform == PLATFORM_WINDOWS) { + slots = [slots[1], slots[0]]; + } + return html` + <link rel="stylesheet" href=${this.constructor.stylesheetUrl} /> + ${slots} + `; + } +} +customElements.define("moz-button-group", MozButtonGroup); diff --git a/toolkit/content/widgets/moz-button-group/moz-button-group.stories.mjs b/toolkit/content/widgets/moz-button-group/moz-button-group.stories.mjs new file mode 100644 index 0000000000..444cc5373c --- /dev/null +++ b/toolkit/content/widgets/moz-button-group/moz-button-group.stories.mjs @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { html } from "../vendor/lit.all.mjs"; +import { + PLATFORM_LINUX, + PLATFORM_MACOS, + PLATFORM_WINDOWS, +} from "./moz-button-group.mjs"; + +export default { + title: "UI Widgets/Button Group", + component: "moz-button-group", + argTypes: { + platform: { + options: [PLATFORM_LINUX, PLATFORM_MACOS, PLATFORM_WINDOWS], + control: { type: "select" }, + }, + }, + parameters: { + status: "stable", + fluent: ` +moz-button-group-p = The button group is below. Card for emphasis. +moz-button-group-ok = OK +moz-button-group-cancel = Cancel + `, + }, +}; + +const Template = ({ platform }) => html` + <div class="card card-no-hover" style="max-width: 400px"> + <p data-l10n-id="moz-button-group-p"></p> + <moz-button-group .platform=${platform}> + <button class="primary" data-l10n-id="moz-button-group-ok"></button> + <button data-l10n-id="moz-button-group-cancel"></button> + </moz-button-group> + </div> +`; + +export const Default = Template.bind({}); +Default.args = { + // Platform will auto-detected. +}; + +export const Windows = Template.bind({}); +Windows.args = { + platform: PLATFORM_WINDOWS, +}; +export const Mac = Template.bind({}); +Mac.args = { + platform: PLATFORM_MACOS, +}; +export const Linux = Template.bind({}); +Linux.args = { + platform: PLATFORM_LINUX, +}; |