diff --git a/packages/main/src/Toolbar.ts b/packages/main/src/Toolbar.ts index ae55e97411bd..f416ab69c9fa 100644 --- a/packages/main/src/Toolbar.ts +++ b/packages/main/src/Toolbar.ts @@ -27,7 +27,7 @@ import type ToolbarAlign from "./types/ToolbarAlign.js"; import type ToolbarDesign from "./types/ToolbarDesign.js"; import ToolbarItemOverflowBehavior from "./types/ToolbarItemOverflowBehavior.js"; -import type ToolbarItem from "./ToolbarItem.js"; +import type ToolbarItemBase from "./ToolbarItemBase.js"; import type ToolbarSeparator from "./ToolbarSeparator.js"; import type Button from "./Button.js"; @@ -155,11 +155,11 @@ class Toolbar extends UI5Element { @slot({ "default": true, type: HTMLElement, invalidateOnChildChange: true, individualSlots: true, }) - items!: DefaultSlot + items!: DefaultSlot _onResize!: ResizeObserverCallback; _onCloseOverflow!: EventListener; - itemsToOverflow: Array = []; + itemsToOverflow: Array = []; itemsWidth = 0; minContentWidth = 0; @@ -194,11 +194,11 @@ class Toolbar extends UI5Element { } get alwaysOverflowItems() { - return this.items.filter((item: ToolbarItem) => item.overflowPriority === ToolbarItemOverflowBehavior.AlwaysOverflow); + return this.items.filter((item: ToolbarItemBase) => item.overflowPriority === ToolbarItemOverflowBehavior.AlwaysOverflow); } get movableItems() { - return this.items.filter((item: ToolbarItem) => item.overflowPriority !== ToolbarItemOverflowBehavior.AlwaysOverflow && item.overflowPriority !== ToolbarItemOverflowBehavior.NeverOverflow); + return this.items.filter((item: ToolbarItemBase) => item.overflowPriority !== ToolbarItemOverflowBehavior.AlwaysOverflow && item.overflowPriority !== ToolbarItemOverflowBehavior.NeverOverflow); } get overflowItems() { @@ -216,7 +216,7 @@ class Toolbar extends UI5Element { } get interactiveItems() { - return this.items.filter((item: ToolbarItem) => item.isInteractive); + return this.items.filter((item: ToolbarItemBase) => item.isInteractive); } /** @@ -264,7 +264,7 @@ class Toolbar extends UI5Element { } get hasFlexibleSpacers() { - return this.items.some((item: ToolbarItem) => item.hasFlexibleWidth); + return this.items.some((item: ToolbarItemBase) => item.hasFlexibleWidth); } /** @@ -306,7 +306,7 @@ class Toolbar extends UI5Element { }); } - addItemsAdditionalProperties(item: ToolbarItem) { + addItemsAdditionalProperties(item: ToolbarItemBase) { item.isOverflowed = this.overflowItems.indexOf(item) !== -1; const itemWrapper = this.shadowRoot!.querySelector(`#${item._individualSlot}`) as HTMLElement; if (item.hasOverflow && !item.isOverflowed && itemWrapper) { @@ -386,7 +386,7 @@ class Toolbar extends UI5Element { let totalWidth = 0, minWidth = 0; - this.items.forEach((item: ToolbarItem) => { + this.items.forEach((item: ToolbarItemBase) => { const itemWidth = this.getItemWidth(item); totalWidth += itemWidth; if (item.overflowPriority === ToolbarItemOverflowBehavior.NeverOverflow) { @@ -437,7 +437,7 @@ class Toolbar extends UI5Element { } distributeItemsThatAlwaysOverflow() { - this.alwaysOverflowItems.forEach((item: ToolbarItem) => { + this.alwaysOverflowItems.forEach((item: ToolbarItemBase) => { this.itemsToOverflow.push(item); }); } @@ -450,7 +450,7 @@ class Toolbar extends UI5Element { }); } - shouldShowSeparatorInOverflow(separatorIdx: number, overflowItems: Array) { + shouldShowSeparatorInOverflow(separatorIdx: number, overflowItems: Array) { let foundPrevNonSeparatorItem = false; let foundNextNonSeperatorItem = false; @@ -514,7 +514,7 @@ class Toolbar extends UI5Element { this.contentWidth = 0; // re-render } - getItemWidth(item: ToolbarItem): number { + getItemWidth(item: ToolbarItemBase): number { // Spacer width - always 0 for flexible spacers, so that they shrink, otherwise - measure the width normally if (item.ignoreSpace || item.isSeparator) { return 0; diff --git a/packages/main/src/ToolbarButton.ts b/packages/main/src/ToolbarButton.ts index 4ee3fcc48eb0..2a0ba2e6a3ad 100644 --- a/packages/main/src/ToolbarButton.ts +++ b/packages/main/src/ToolbarButton.ts @@ -5,7 +5,8 @@ import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; import type { ButtonAccessibilityAttributes } from "./Button.js"; import type ButtonDesign from "./types/ButtonDesign.js"; -import ToolbarItem from "./ToolbarItem.js"; +import ToolbarItemBase from "./ToolbarItemBase.js"; +import type { ToolbarItemEventDetail } from "./ToolbarItemBase.js"; import ToolbarButtonTemplate from "./ToolbarButtonTemplate.js"; import ToolbarButtonCss from "./generated/themes/ToolbarButton.css.js"; @@ -22,7 +23,7 @@ type ToolbarButtonAccessibilityAttributes = ButtonAccessibilityAttributes; * `import "@ui5/webcomponents/dist/ToolbarButton.js";` * @constructor * @abstract - * @extends ToolbarItem + * @extends ToolbarItemBase * @public * @since 1.17.0 */ @@ -45,7 +46,10 @@ type ToolbarButtonAccessibilityAttributes = ButtonAccessibilityAttributes; bubbles: true, cancelable: true, }) -class ToolbarButton extends ToolbarItem { +class ToolbarButton extends ToolbarItemBase { + eventDetails!: ToolbarItemBase["eventDetails"] & { + click: ToolbarItemEventDetail; + } /** * Defines if the action is disabled. * diff --git a/packages/main/src/ToolbarItem.ts b/packages/main/src/ToolbarItem.ts index 3beb0a6660cd..dffe427f40b4 100644 --- a/packages/main/src/ToolbarItem.ts +++ b/packages/main/src/ToolbarItem.ts @@ -1,34 +1,11 @@ -import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; -import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; +import type UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import ToolbarItemTemplate from "./ToolbarItemTemplate.js"; import ToolbarItemCss from "./generated/themes/ToolbarItem.css.js"; -import type ToolbarItemOverflowBehavior from "./types/ToolbarItemOverflowBehavior.js"; - -type IEventOptions = { - preventClosing: boolean; -} - -type ToolbarItemEventDetail = { - targetRef: HTMLElement; -} - -interface IOverflowToolbarItem extends HTMLElement { - overflowCloseEvents?: string[] | undefined; - hasOverflow?: boolean | undefined; -} -/** - * Fired when the overflow popover is closed. - * @public - * @since 1.17.0 - */ -@event("close-overflow", { - bubbles: true, - cancelable: true, -}) +import ToolbarItemBase from "./ToolbarItemBase.js"; +import type { IEventOptions, IOverflowToolbarItem, ToolbarItemEventDetail } from "./ToolbarItemBase.js"; @customElement({ tag: "ui5-toolbar-item", @@ -48,42 +25,7 @@ interface IOverflowToolbarItem extends HTMLElement { * @experimental This module is experimental and its API might change significantly in future. * @since 1.17.0 */ -class ToolbarItem extends UI5Element { - // strictEvents: needed for parent class - eventDetails!: { - click: ToolbarItemEventDetail, - "close-overflow": void; - } - /** - * Property used to define the access of the item to the overflow Popover. If "NeverOverflow" option is set, - * the item never goes in the Popover, if "AlwaysOverflow" - it never comes out of it. - * @public - * @default "Default" - */ - @property() - overflowPriority: `${ToolbarItemOverflowBehavior}` = "Default"; - - /** - * Defines if the toolbar overflow popup should close upon intereaction with the item. - * It will close by default. - * @default false - * @public - */ - @property({ type: Boolean }) - preventOverflowClosing = false; - - /** - * Defines if the toolbar item is overflowed. - * @default false - * @protected - * @since 2.11.0 - */ - - @property({ type: Boolean }) - isOverflowed: boolean = false; - - _isRendering = true; - _maxWidth = 0; +class ToolbarItem extends ToolbarItemBase { _wrapperChecked = false; fireCloseOverflowRef = this.fireCloseOverflow.bind(this); @@ -106,10 +48,6 @@ class ToolbarItem extends UI5Element { this.attachCloseOverflowHandlers(); } - onAfterRendering(): void { - this._isRendering = false; - } - onExitDOM(): void { this.detachCloseOverflowHandlers(); } @@ -209,27 +147,6 @@ class ToolbarItem extends UI5Element { get hasOverflow(): boolean { return this.item[0]?.hasOverflow ?? false; } - - /** - * Returns if the item is separator. - * @protected - */ - get isSeparator() { - return false; - } - - get stableDomRef() { - return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; - } - - get classes() { - return { - root: { - "ui5-tb-popover-item": this.isOverflowed, - "ui5-tb-item": true, - }, - }; - } } export type { diff --git a/packages/main/src/ToolbarItemBase.ts b/packages/main/src/ToolbarItemBase.ts new file mode 100644 index 000000000000..7411678097ab --- /dev/null +++ b/packages/main/src/ToolbarItemBase.ts @@ -0,0 +1,139 @@ +import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; +import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js"; +import type ToolbarItemOverflowBehavior from "./types/ToolbarItemOverflowBehavior.js"; + +type IEventOptions = { + preventClosing: boolean; +} + +type ToolbarItemEventDetail = { + targetRef: HTMLElement; +} + +interface IOverflowToolbarItem extends HTMLElement { + overflowCloseEvents?: string[] | undefined; + hasOverflow?: boolean | undefined; +} + +/** + * Fired when the overflow popover is closed. + * @public + * @since 1.17.0 + */ +@event("close-overflow", { + bubbles: true, + cancelable: true, +}) + +/** + * @class + * + * Represents an abstract base class for items, used in the `ui5-toolbar`. + * @constructor + * @extends UI5Element + * @public + * @experimental This module is experimental and its API might change significantly in future. + * @since 2.20.0 + */ +abstract class ToolbarItemBase extends UI5Element { + // strictEvents: needed for parent class + eventDetails!: { + click: ToolbarItemEventDetail, + "close-overflow": void, + } + + /** + * Property used to define the access of the item to the overflow Popover. If "NeverOverflow" option is set, + * the item never goes in the Popover, if "AlwaysOverflow" - it never comes out of it. + * @public + * @default "Default" + */ + @property() + overflowPriority: `${ToolbarItemOverflowBehavior}` = "Default"; + + /** + * Defines if the toolbar overflow popup should close upon intereaction with the item. + * It will close by default. + * @default false + * @public + */ + @property({ type: Boolean }) + preventOverflowClosing = false; + + /** + * Defines if the toolbar item is overflowed. + * @default false + * @protected + * @since 2.11.0 + */ + @property({ type: Boolean }) + isOverflowed: boolean = false; + + _isRendering = true; + _maxWidth = 0; + + onAfterRendering(): void { + this._isRendering = false; + } + + /** + * Defines if the width of the item should be ignored in calculating the whole width of the toolbar + * @protected + */ + get ignoreSpace(): boolean { + return false; + } + + /** + * Returns if the item is flexible. An item that is returning true for this property will make + * the toolbar expand to fill the 100% width of its container. + * @protected + */ + get hasFlexibleWidth(): boolean { + return false; + } + + /** + * Returns if the item is interactive. + * This value is used to determinate if the toolbar should have its accessibility role and attributes set. + * At least two interactive items are needed for the toolbar to have the role="toolbar" attribute set. + * @protected + */ + get isInteractive(): boolean { + return true; + } + + get hasOverflow(): boolean { + return false; + } + + /** + * Returns if the item is separator. + * @protected + */ + get isSeparator() { + return false; + } + + get stableDomRef() { + return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; + } + + get classes() { + return { + root: { + "ui5-tb-popover-item": this.isOverflowed, + "ui5-tb-item": true, + }, + }; + } +} + +export type { + IEventOptions, + ToolbarItemEventDetail, + IOverflowToolbarItem, +}; + +export default ToolbarItemBase; diff --git a/packages/main/src/ToolbarSelect.ts b/packages/main/src/ToolbarSelect.ts index 554e35135f8d..306ff5e4be9d 100644 --- a/packages/main/src/ToolbarSelect.ts +++ b/packages/main/src/ToolbarSelect.ts @@ -9,8 +9,8 @@ import type Select from "./Select.js"; // Templates import ToolbarSelectTemplate from "./ToolbarSelectTemplate.js"; -import ToolbarItem from "./ToolbarItem.js"; -import type { ToolbarItemEventDetail } from "./ToolbarItem.js"; +import ToolbarItemBase from "./ToolbarItemBase.js"; +import type { ToolbarItemEventDetail } from "./ToolbarItemBase.js"; import type ToolbarSelectOption from "./ToolbarSelectOption.js"; import type { SelectChangeEventDetail } from "./Select.js"; import type { DefaultSlot, Slot } from "@ui5/webcomponents-base/dist/UI5Element.js"; @@ -30,7 +30,7 @@ type ToolbarSelectChangeEventDetail = ToolbarItemEventDetail & SelectChangeEvent * `import "@ui5/webcomponents/dist/ToolbarSelectOption.js";` (comes with `ui5-toolbar-select`) * @constructor * @abstract - * @extends ToolbarItem + * @extends ToolbarItemBase * @public * @since 1.17.0 */ @@ -64,8 +64,8 @@ type ToolbarSelectChangeEventDetail = ToolbarItemEventDetail & SelectChangeEvent * @public */ @event("close") -class ToolbarSelect extends ToolbarItem { - eventDetails!: ToolbarItem["eventDetails"] & { +class ToolbarSelect extends ToolbarItemBase { + eventDetails!: ToolbarItemBase["eventDetails"] & { change: ToolbarSelectChangeEventDetail; open: ToolbarItemEventDetail; close: ToolbarItemEventDetail; @@ -167,32 +167,23 @@ class ToolbarSelect extends ToolbarItem { onClick(e: Event): void { e.stopImmediatePropagation(); - const prevented = !this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); - if (prevented && !this.preventOverflowClosing) { - this.fireDecoratorEvent("close-overflow"); - } + this.fireDecoratorEvent("click", { targetRef: e.target as HTMLElement }); } onOpen(e: Event): void { e.stopImmediatePropagation(); - const prevented = !this.fireDecoratorEvent("open", { targetRef: e.target as HTMLElement }); - if (prevented) { - this.fireDecoratorEvent("close-overflow"); - } + this.fireDecoratorEvent("open", { targetRef: e.target as HTMLElement }); } onClose(e: Event): void { e.stopImmediatePropagation(); - const prevented = !this.fireDecoratorEvent("close", { targetRef: e.target as HTMLElement }); - if (prevented) { - this.fireDecoratorEvent("close-overflow"); - } + this.fireDecoratorEvent("close", { targetRef: e.target as HTMLElement }); } onChange(e: CustomEvent): void { e.stopImmediatePropagation(); const prevented = !this.fireDecoratorEvent("change", { ...e.detail, targetRef: e.target as HTMLElement }); - if (!prevented) { + if (!prevented && !this.preventOverflowClosing) { this.fireDecoratorEvent("close-overflow"); } diff --git a/packages/main/src/ToolbarSeparator.ts b/packages/main/src/ToolbarSeparator.ts index d7279d6fecec..57d68cf453cd 100644 --- a/packages/main/src/ToolbarSeparator.ts +++ b/packages/main/src/ToolbarSeparator.ts @@ -6,7 +6,7 @@ import ToolbarSeparatorTemplate from "./ToolbarSeparatorTemplate.js"; // Styles import ToolbarSeparatorCss from "./generated/themes/ToolbarSeparator.css.js"; -import ToolbarItem from "./ToolbarItem.js"; +import ToolbarItemBase from "./ToolbarItemBase.js"; /** * @class @@ -15,7 +15,7 @@ import ToolbarItem from "./ToolbarItem.js"; * The `ui5-toolbar-separator` is an element, used for visual separation between two elements. * It takes no space in calculating toolbar items width. * @constructor - * @extends ToolbarItem + * @extends ToolbarItemBase * @since 1.17.0 * @abstract * @public @@ -27,7 +27,7 @@ import ToolbarItem from "./ToolbarItem.js"; styles: [ToolbarSeparatorCss], }) -class ToolbarSeparator extends ToolbarItem { +class ToolbarSeparator extends ToolbarItemBase { @property({ type: Boolean }) visible = false; diff --git a/packages/main/src/ToolbarSpacer.ts b/packages/main/src/ToolbarSpacer.ts index 40b0cff7aa97..e1ad30f8ac31 100644 --- a/packages/main/src/ToolbarSpacer.ts +++ b/packages/main/src/ToolbarSpacer.ts @@ -3,7 +3,7 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement import ToolbarCss from "./generated/themes/Toolbar.css.js"; -import ToolbarItem from "./ToolbarItem.js"; +import ToolbarItemBase from "./ToolbarItemBase.js"; /** * @class @@ -12,7 +12,7 @@ import ToolbarItem from "./ToolbarItem.js"; * The `ui5-toolbar-spacer` is an element, used for taking needed space for toolbar items to take 100% width. * It takes no space in calculating toolbar items width. * @constructor - * @extends ToolbarItem + * @extends ToolbarItemBase * @abstract * @since 1.17.0 * @public @@ -22,7 +22,7 @@ import ToolbarItem from "./ToolbarItem.js"; styles: ToolbarCss, }) -class ToolbarSpacer extends ToolbarItem { +class ToolbarSpacer extends ToolbarItemBase { /** * Defines the width of the spacer. * diff --git a/packages/main/src/themes/ToolbarItem.css b/packages/main/src/themes/ToolbarItem.css index 20a03d10a16a..842b5078684d 100644 --- a/packages/main/src/themes/ToolbarItem.css +++ b/packages/main/src/themes/ToolbarItem.css @@ -1,5 +1,4 @@ :host { - display: inline-block; height: 100%; }