Bug 1803569 - Provide central facility to define items for unified toolbar. r=aleca

Differential Revision: https://phabricator.services.mozilla.com/D163755

--HG--
extra : rebase_source : c5a93185f95525ac666bd5ef6d55fe8a629f687b
extra : histedit_source : 5e863a34f2283d7fe2597add984d61cd394a10a1
This commit is contained in:
Martin Giger 2022-12-02 12:32:05 +01:00
Родитель faaf48f959
Коммит dcb6b8521d
15 изменённых файлов: 298 добавлений и 48 удалений

Просмотреть файл

@ -2,11 +2,16 @@
* 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 CUSTOMIZABLE_ITEMS from "resource:///modules/CustomizableItemsDetails.mjs";
/**
* Wrapper element for elements whose position can be customized.
*
* Template ID: #unifiedToolbarCustomizableElementTemplate
* Attributes:
* - palette: ID of the palette the item belongs to.
* - item-id: ID of the customizable item this represents. Not observed.
*/
class CustomizableElement extends HTMLLIElement {
export default class CustomizableElement extends HTMLLIElement {
connectedCallback() {
if (this.hasConnected) {
return;
@ -14,6 +19,52 @@ class CustomizableElement extends HTMLLIElement {
this.hasConnected = true;
this.setAttribute("is", "customizable-element");
const template = document
.getElementById("unifiedToolbarCustomizableElementTemplate")
.content.cloneNode(true);
const details = CUSTOMIZABLE_ITEMS.find(
item => item.id === this.getAttribute("item-id")
);
if (!details) {
throw new Error(
`Could not find definition for ${this.getAttribute("item-id")}`
);
}
this.append(template);
this.#initializeFromDetails(details).catch(console.error);
}
/**
* Initialize the template contents from item details. Can't operate on the
* template directly due to being async.
*
* @param {CustomizableItemDetails} itemDetails
*/
async #initializeFromDetails(itemDetails) {
if (this.details) {
return;
}
this.details = itemDetails;
this.classList.add(itemDetails.id);
if (Array.isArray(itemDetails.requiredModules)) {
await Promise.all(
itemDetails.requiredModules.map(module => {
return import(module); // eslint-disable-line no-unsanitized/method
})
);
}
if (itemDetails.templateId) {
const contentTemplate = document.getElementById(itemDetails.templateId);
this.querySelector(".live-content").append(
contentTemplate.content.cloneNode(true)
);
}
document.l10n.setAttributes(
this.querySelector(".preview-label"),
`${itemDetails.labelId}-label`
);
}
/**
@ -22,7 +73,28 @@ class CustomizableElement extends HTMLLIElement {
* @type {CustomizationPalette}
*/
get palette() {
return this.getRootNode().querySelector(`#${this.getAttribute("palette")}`);
const paletteClass = this.details.spaces?.length
? "space-specific-palette"
: "generic-palette";
return this.getRootNode().querySelector(`.${paletteClass}`);
}
/**
* If multiple instances of this element are allowed in the same space.
*
* @type {boolean}
*/
get allowMultiple() {
return Boolean(this.details?.allowMultiple);
}
/**
* Human readable label for the widget.
*
* @type {string}
*/
get label() {
return this.querySelector(".preview-label").textContent;
}
}
customElements.define("customizable-element", CustomizableElement, {

Просмотреть файл

@ -3,17 +3,40 @@
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
import ListBoxSelection from "./list-box-selection.mjs";
import { getItemIdsForSpace } from "resource:///modules/CustomizableItems.mjs";
import "./customizable-element.mjs"; // eslint-disable-line import/no-unassigned-import
/**
* Customization palette containing items that can be added to a customization
* target.
* Attributes:
* - space: ID of the space the widgets are for. "all" for space agnostic
* widgets.
* widgets. Not observed.
*/
class CustomizationPalette extends ListBoxSelection {
contextMenuId = "customizationPaletteMenu";
connectedCallback() {
if (super.connectedCallback()) {
return;
}
let space = this.getAttribute("space");
if (space === "all") {
space = undefined;
}
const items = getItemIdsForSpace(space);
this.replaceChildren(
...items.map(itemId => {
const element = document.createElement("li", {
is: "customizable-element",
});
element.setAttribute("item-id", itemId);
return element;
})
);
}
/**
* Overwritten context menu handler. Before showing the menu, initializes the
* menu with items for all the target areas available.
@ -65,12 +88,16 @@ class CustomizationPalette extends ListBoxSelection {
* Defaults to the first target in the root.
*/
primaryAction(item, target) {
if (super.primaryAction(item)) {
return;
}
if (!target) {
target = this.getRootNode().querySelector('[is="customization-target"]');
}
if (item?.allowMultiple) {
target.addItem(item.cloneNode(true));
return;
}
if (super.primaryAction(item)) {
return;
}
target.addItem(item);
}
@ -80,7 +107,10 @@ class CustomizationPalette extends ListBoxSelection {
* @param {CustomizableElement} item - Item to return to this palette.
*/
returnItem(item) {
//TODO items should probably be sorted by something like a label.
if (item.allowMultiple) {
item.remove();
return;
}
this.append(item);
}
}

Просмотреть файл

@ -5,7 +5,6 @@
import "./search-bar.mjs"; // eslint-disable-line import/no-unassigned-import
import "./customization-palette.mjs"; // eslint-disable-line import/no-unassigned-import
import "./customization-target.mjs"; // eslint-disable-line import/no-unassigned-import
import "./customizable-element.mjs"; // eslint-disable-line import/no-unassigned-import
/**
* Template ID: unifiedToolbarCustomizationPaneTemplate
@ -43,6 +42,9 @@ class UnifiedToolbarCustomizationPane extends HTMLElement {
);
spaceSpecificPalette.id = `${space}Palette`;
spaceSpecificPalette.setAttribute("aria-labelledby", spaceSpecificTitle.id);
spaceSpecificPalette.setAttribute("space", space);
// TODO hide space specific palette if there are no items in it (probably
// fairly like for extension spaces, hard to tell for the rest of the app)
const genericTitle = template.querySelector(".generic-palette-title");
genericTitle.id = `${space}GenericPaletteTitle`;
const genericPalette = template.querySelector(".generic-palette");
@ -50,28 +52,6 @@ class UnifiedToolbarCustomizationPane extends HTMLElement {
genericPalette.setAttribute("aria-labelledby", genericTitle.id);
shadowRoot.append(styles, template);
// Temporary example items added to palettes.
customElements.whenDefined("customizable-element").then(() => {
const item1 = document.createElement("li", {
is: "customizable-element",
});
item1.textContent = "lorem ipsum";
item1.setAttribute("palette", spaceSpecificPalette.id);
spaceSpecificPalette.append(item1);
const item2 = document.createElement("li", {
is: "customizable-element",
});
item2.textContent = "foo bar";
item2.setAttribute("palette", genericPalette.id);
const item3 = document.createElement("li", {
is: "customizable-element",
});
item3.textContent = "example item";
item3.setAttribute("palette", genericPalette.id);
genericPalette.append(item2, item3);
});
}
disconnectedCallback() {

Просмотреть файл

@ -19,6 +19,8 @@ class UnifiedToolbarCustomization extends HTMLElement {
return;
}
this.hasConnected = true;
document.l10n.addResourceIds(["messenger/unifiedToolbarItems.ftl"]);
const template = document
.getElementById("unifiedToolbarCustomizationTemplate")
.content.cloneNode(true);

Просмотреть файл

@ -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/.
<html:template id="searchBarItemTemplate"
xmlns="http://www.w3.org/1999/xhtml">
<search-bar class="search-bar" data-l10n-id="search-bar-item" data-l10n-attrs="label" aria-keyshortcuts="Control+K">
<span slot="placeholder" data-l10n-id="search-bar-placeholder"></span>
<img data-l10n-id="search-bar-button"
slot="button"
class="search-button-icon"
src="" />
</search-bar>
</html:template>

Просмотреть файл

@ -2,8 +2,7 @@
# 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/.
# Required for placing the window controls in the proper place without having
# them inside the toolbar.
#include ./unifiedToolbarCustomizableItems.inc.xhtml
<html:template id="searchBarTemplate"
xmlns="http://www.w3.org/1999/xhtml">
@ -15,19 +14,12 @@
</html:template>
<html:template id="unifiedToolbarTemplate">
# Required for placing the window controls in the proper place without having
# them inside the toolbar.
<html:div id="unifiedToolbarContainer">
<html:div id="unifiedToolbar" role="toolbar">
#include ../../../base/content/spacesToolbarPin.inc.xhtml
<html:div id="unifiedToolbarContent">
<html:div class="spacer"></html:div>
<html:search-bar class="search-bar" label="Search:" aria-keyshortcuts="Control+K">
<html:span slot="placeholder">Search... <html:kbd>Ctrl</html:kbd> + <html:kbd>K</html:kbd></html:span>
<html:img data-l10n-id="search-bar-button"
slot="button"
class="search-button-icon"
src="" />
</html:search-bar>
<html:div class="spacer"></html:div>
</html:div>
<html:div id="notification-popup-box" hidden="true">
<html:img id="addons-notification-icon"
@ -102,9 +94,10 @@
<ul is="customization-target"
data-l10n-id="customize-main-toolbar-target"
class="toolbar-target"></ul>
<search-bar data-l10n-id="customize-search-bar" data-l10n-attrs="label">
<search-bar data-l10n-id="customize-search-bar" data-l10n-attrs="label" class="palette-search">
<img data-l10n-id="search-bar-button"
slot="button"
src=""
class="search-button-icon" />
</search-bar>
<h2 class="space-specific-title"></h2>
@ -115,3 +108,12 @@
<ul is="customization-palette" space="all" class="generic-palette">
</ul>
</html:template>
<html:template id="unifiedToolbarCustomizableElementTemplate"
xmlns="http://www.w3.org/1999/xhtml">
<div class="live-content"></div>
<div class="preview">
<img src="" alt="" class="preview-icon" />
<span class="preview-label"></span>
</div>
</html:template>

Просмотреть файл

@ -0,0 +1,22 @@
/* 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 CUSTOMIZABLE_ITEMS from "resource:///modules/CustomizableItemsDetails.mjs";
//TODO dynamic registry for extensions that have dynamic labels etc.
/**
* Get the items available for the unified toolbar in a given space.
*
* @param {string} [space] - ID of the space to get the available exclusive
* items of. When omitted only items allowed in all spaces are returned.
* @returns {string[]} Array of item IDs available in the space.
*/
export function getItemIdsForSpace(space) {
return CUSTOMIZABLE_ITEMS.filter(item =>
space
? item.spaces?.includes(space)
: !item.spaces || item.spaces.length === 0
).map(item => item.id);
}

Просмотреть файл

@ -0,0 +1,42 @@
/* 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/. */
/* This has the following companion definition files:
* - unifiedToolbarCustomizableItems.css for the preview icons based on the id.
* - unifiedToolbarItems.ftl for the labels associated with the labelId.
* - unifiedToolbarCustomizableItems.inc.xhtml for the templates referenced with
* templateId.
*/
/**
* @typedef {object} CustomizableItemDetails
* @property {string} id - The ID of the item. Will be set as a class on the
* outer wrapper.
* @property {string} labelId
* @property {boolean} [allowMultiple] - If this item can be added more than
* once to a space.
* @property {string[]} [spaces] - If empty or omitted, item is allowed in all
* spaces.
* @property {string} [templateId] - ID of template defining the "live" markup.
* @property {string[]} [requiredModules]
*/
/**
* @type {CustomizableItemDetails[]}
*/
export default [
{
id: "spacer",
labelId: "spacer",
allowMultiple: true,
},
{
id: "search-bar",
labelId: "search-bar",
templateId: "searchBarItemTemplate",
requiredModules: [
"chrome://messenger/content/unifiedtoolbar/search-bar.mjs",
],
},
];

Просмотреть файл

@ -5,6 +5,11 @@
JAR_MANIFESTS += ["jar.mn"]
EXTRA_JS_MODULES += [
"modules/CustomizableItems.mjs",
"modules/CustomizableItemsDetails.mjs",
]
BROWSER_CHROME_MANIFESTS += [
"test/browser/browser.ini",
]

Просмотреть файл

@ -4,9 +4,16 @@
### Unified Toolbar strings
## Search bar
search-bar-button =
.alt = Search
search-bar-item =
.label = Search:
search-bar-placeholder = Search…
## Unified toolbar context menu
customize-menu-customize =

Просмотреть файл

@ -0,0 +1,9 @@
# 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/.
### Unified Toolbar Item Label strings
spacer-label = Flexible Space
search-bar-label = Search

Просмотреть файл

@ -399,6 +399,7 @@
skin/classic/messenger/shared/grid-layout.css (../shared/mail/grid-layout.css)
skin/classic/messenger/shared/input-fields.css (../shared/mail/input-fields.css)
skin/classic/messenger/shared/unifiedToolbar.css (../shared/mail/unifiedToolbar.css)
skin/classic/messenger/shared/unifiedToolbarCustomizableItems.css (../shared/mail/unifiedToolbarCustomizableItems.css)
skin/classic/messenger/shared/unifiedToolbarCustomizationPane.css (../shared/mail/unifiedToolbarCustomizationPane.css)
skin/classic/messenger/shared/unifiedToolbarTab.css (../shared/mail/unifiedToolbarTab.css)
skin/classic/messenger/shared/variables.css (../shared/mail/variables.css)

Просмотреть файл

@ -28,6 +28,7 @@
#unifiedToolbarContent {
flex: 1 1 auto;
display: flex;
justify-content: start;
}
.spacer {

Просмотреть файл

@ -0,0 +1,15 @@
/* 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/. */
/* Icon definitions for customizable item previews.
* CustomizableItemsDetails.mjs contains the definitions for the root class
* names. */
.spacer .preview-icon {
content: var(--icon-spaces-menu)
}
.search-bar .preview-icon {
content: var(--icon-search);
}

Просмотреть файл

@ -2,11 +2,13 @@
* 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 url("chrome://messenger/skin/shared/unifiedToolbarCustomizableItems.css");
.search-button-icon {
content: var(--icon-search);
}
search-bar {
.palette-search {
display: block;
margin: 12px auto;
max-width: 50ch;
@ -29,6 +31,26 @@ search-bar {
margin-block: 1rem;
height: 2em;
align-items: center;
justify-content: start;
}
/* TODO factor out stuff shared with real toolbar? */
.toolbar-target .spacer {
flex: 1 1 auto;
height: 2em;
background: green;
}
.toolbar-target .search-bar {
flex: 1 0 33%;
}
[is="customization-target"] [is="customizable-element"] .live-content {
pointer-events: none;
}
[is="customization-target"] [is="customizable-element"] .preview {
display: none;
}
[is="customization-palette"] {
@ -44,17 +66,44 @@ h2 {
[is="customizable-element"] {
margin: 0;
padding: 0;
/* TODO: children should have no mouse interactions/pointer events */
}
[is="customizable-element"][aria-selected=true] {
[is="customizable-element"][aria-selected="true"] {
outline: 2px dotted var(--primary);
}
[is="customization-palette"] [is="customizable-element"] {
width: 4em;
height: 3em;
width: 8em;
height: 4em;
background: lightgrey;
padding: 0.5rem;
border-radius: 6px;
overflow: hidden;
}
[is="customization-palette"] [is="customizable-element"] .live-content {
display: none;
}
.preview {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
overflow: hidden;
gap: 0.5rem;
-moz-context-properties: fill, stroke;
fill: color-mix(in srgb, currentColor 20%, transparent);
stroke: currentColor;
}
.preview-icon {
height: 2em;
width: 2em;
object-fit: contain;
}
.preview-label {
overflow: hidden;
text-overflow: ellipsis;
}