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:
Родитель
faaf48f959
Коммит
dcb6b8521d
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче