From 9c0f58f7df9e3c706e6b6623596dab7d8b10e43d Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Tue, 7 May 2024 12:24:48 +0300 Subject: [PATCH 01/24] refactor(ui5-option): make options physical elements --- packages/main/src/Option.ts | 62 +- packages/main/src/OptionBase.hbs | 1 + packages/main/src/OptionBase.ts | 80 ++ packages/main/src/OptionCustom.ts | 36 + packages/main/src/Select.hbs | 2 +- packages/main/src/Select.ts | 378 ++---- packages/main/src/SelectMenu.hbs | 57 - packages/main/src/SelectMenu.ts | 291 ----- packages/main/src/SelectMenuOption.ts | 120 -- packages/main/src/SelectPopover.hbs | 53 +- packages/main/src/bundle.esm.ts | 4 +- packages/main/src/themes/OptionCustom.css | 7 + packages/main/src/themes/SelectMenu.css | 16 - packages/main/src/themes/SelectPopover.css | 4 + .../themes/sap_horizon/parameters-bundle.css | 1 - .../sap_horizon_dark/parameters-bundle.css | 1 - .../parameters-bundle.css | 1 - .../sap_horizon_exp/parameters-bundle.css | 1 - packages/main/test/pages/Select.html | 20 +- packages/main/test/pages/SelectMenu.html | 1159 ++++++++--------- .../main/test/pages/styles/SelectMenu.css | 5 +- packages/main/test/specs/SelectMenu.spec.js | 153 --- .../test/specs/SelectMenuConnector.spec.js | 43 - packages/main/test/ssr/component-imports.js | 4 +- .../Select/SelectMenu/SelectMenu.stories.ts | 65 - .../SelectMenuOption.stories.ts | 61 - .../_components_pages/main/SelectMenu.mdx | 12 - .../main/SelectMenuOption.mdx | 7 - .../main/Select/CustomOptions/main.js | 3 +- .../main/Select/CustomOptions/sample.html | 18 +- .../_samples/main/SelectMenu/Basic/Basic.md | 4 - .../_samples/main/SelectMenu/Basic/main.js | 6 - .../main/SelectMenu/Basic/sample.html | 56 - 33 files changed, 862 insertions(+), 1869 deletions(-) create mode 100644 packages/main/src/OptionBase.hbs create mode 100644 packages/main/src/OptionBase.ts create mode 100644 packages/main/src/OptionCustom.ts delete mode 100644 packages/main/src/SelectMenu.hbs delete mode 100644 packages/main/src/SelectMenu.ts delete mode 100644 packages/main/src/SelectMenuOption.ts create mode 100644 packages/main/src/themes/OptionCustom.css delete mode 100644 packages/main/src/themes/SelectMenu.css delete mode 100644 packages/main/test/specs/SelectMenu.spec.js delete mode 100644 packages/main/test/specs/SelectMenuConnector.spec.js delete mode 100644 packages/playground/_stories/main/Select/SelectMenu/SelectMenu.stories.ts delete mode 100644 packages/playground/_stories/main/Select/SelectMenuOption/SelectMenuOption.stories.ts delete mode 100644 packages/website/docs/_components_pages/main/SelectMenu.mdx delete mode 100644 packages/website/docs/_components_pages/main/SelectMenuOption.mdx delete mode 100644 packages/website/docs/_samples/main/SelectMenu/Basic/Basic.md delete mode 100644 packages/website/docs/_samples/main/SelectMenu/Basic/main.js delete mode 100644 packages/website/docs/_samples/main/SelectMenu/Basic/sample.html diff --git a/packages/main/src/Option.ts b/packages/main/src/Option.ts index 241ee9c01397..0a3d187c8e36 100644 --- a/packages/main/src/Option.ts +++ b/packages/main/src/Option.ts @@ -1,8 +1,8 @@ -import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; -import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import type { IOption } from "./Select.js"; + +import OptionBase from "./OptionBase.js"; + /** * @class * @@ -15,29 +15,12 @@ import type { IOption } from "./Select.js"; * `import "@ui5/webcomponents/dist/Option.js";` * @constructor * @extends UI5Element - * @implements {IOption} * @public - * @abstract */ -@customElement("ui5-option") -class Option extends UI5Element implements IOption { - /** - * Defines the selected state of the component. - * @default false - * @public - */ - @property({ type: Boolean }) - selected!: boolean; - - /** - * Defines the text of the tooltip that would be displayed for the option component. - * @default "" - * @public - * @since 2.0.0 - */ - @property() - tooltip!: string; - +@customElement({ + tag: "ui5-option", +}) +class Option extends OptionBase { /** * Defines the `icon` source URI. * @@ -50,15 +33,6 @@ class Option extends UI5Element implements IOption { @property({ defaultValue: null }) icon?: string | null; - /** - * Defines the value of the `ui5-select` inside an HTML Form element when this component is selected. - * For more information on HTML Form support, see the `name` property of `ui5-select`. - * @default "" - * @public - */ - @property() - value!: string; - /** * Defines the additional text displayed at the end of the option element. * @default "" @@ -67,28 +41,6 @@ class Option extends UI5Element implements IOption { */ @property() additionalText!: string; - - /** - * Defines the focused state of the component. - * @default false - * @since 1.0.0-rc.13 - * @private - */ - @property({ type: Boolean }) - focused!: boolean; - - /** - * Defines the text of the component. - * - * **Note:** Although this slot accepts HTML Elements, it is strongly recommended that you only use text in order to preserve the intended design. - * @public - */ - @slot({ type: Node, "default": true, invalidateOnChildChange: true }) - text!: Array; - - get stableDomRef() { - return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; - } } Option.define(); diff --git a/packages/main/src/OptionBase.hbs b/packages/main/src/OptionBase.hbs new file mode 100644 index 000000000000..49aeb95a1db9 --- /dev/null +++ b/packages/main/src/OptionBase.hbs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/main/src/OptionBase.ts b/packages/main/src/OptionBase.ts new file mode 100644 index 000000000000..4afbf087e908 --- /dev/null +++ b/packages/main/src/OptionBase.ts @@ -0,0 +1,80 @@ +import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; +import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; + +import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; + +import OptionBaseTemplate from "./generated/templates/OptionBaseTemplate.lit.js"; + +/** + * @class + * + * ### Overview + * + * The base of the `ui5-option` and `ui5-option-custom` components defines the content of an option in the `ui5-select`. + * + * ### ES6 Module Import + * + * @constructor + * @abstract + * @extends UI5Element + * @public + */ +@customElement({ + template: OptionBaseTemplate, + renderer: litRender, +}) +class OptionBase extends UI5Element { + /** + * Defines the selected state of the component. + * @default false + * @public + */ + @property({ type: Boolean }) + selected!: boolean; + + /** + * Defines the text of the tooltip that would be displayed for the option component. + * @default "" + * @public + * @since 2.0.0 + */ + @property() + tooltip!: string; + + /** + * Defines the value of the `ui5-select` inside an HTML Form element when this component is selected. + * For more information on HTML Form support, see the `name` property of `ui5-select`. + * @default "" + * @public + */ + @property() + value!: string; + + /** + * Defines the focused state of the component. + * @default false + * @since 1.0.0-rc.13 + * @private + */ + @property({ type: Boolean }) + focused!: boolean; + + /** + * Defines the content of the component. + * @public + */ + @slot({ type: HTMLElement, "default": true }) + content!: Array; + + get stableDomRef() { + return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; + } + + get isCustom() { + return false; + } +} + +export default OptionBase; diff --git a/packages/main/src/OptionCustom.ts b/packages/main/src/OptionCustom.ts new file mode 100644 index 000000000000..c7f8fba758b6 --- /dev/null +++ b/packages/main/src/OptionCustom.ts @@ -0,0 +1,36 @@ +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; + +import OptionBase from "./OptionBase.js"; +import optionCustomCss from "./generated/themes/OptionCustom.css.js"; + +/** + * @class + * + * ### Overview + * + * The `ui5-option-custom` component defines a custom content of an option in the `ui5-select`. + * A component to be the same way as the standard `ui5-option`. + * The component accepts arbitrary HTML content to allow full customization. + * + * ### ES6 Module Import + * + * `import "@ui5/webcomponents/dist/OptionCustom.js";` + * @constructor + * @extends UI5Element + * @public + */ +@customElement({ + tag: "ui5-option-custom", + styles: [ + optionCustomCss, + ], +}) +class OptionCustom extends OptionBase { + get isCustom() { + return true; + } +} + +OptionCustom.define(); + +export default OptionCustom; diff --git a/packages/main/src/Select.hbs b/packages/main/src/Select.hbs index b9d80992a1b1..b48285d44eca 100644 --- a/packages/main/src/Select.hbs +++ b/packages/main/src/Select.hbs @@ -31,7 +31,7 @@ {{#if hasCustomLabel}} {{else}} - {{_text}} + {{text}} {{/if}} diff --git a/packages/main/src/Select.ts b/packages/main/src/Select.ts index 3c4b14b3b009..adeb0cb7d999 100644 --- a/packages/main/src/Select.ts +++ b/packages/main/src/Select.ts @@ -4,7 +4,6 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import event from "@ui5/webcomponents-base/dist/decorators/event.js"; import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; -import connectToComponent from "@ui5/webcomponents-base/dist/connectToComponent.js"; import { isSpace, isUp, @@ -17,7 +16,6 @@ import { isTabNext, isTabPrevious, } from "@ui5/webcomponents-base/dist/Keys.js"; -import DOMReference from "@ui5/webcomponents-base/dist/types/DOMReference.js"; import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; import { getFeature } from "@ui5/webcomponents-base/dist/FeaturesRegistry.js"; import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AriaLabelHelper.js"; @@ -54,45 +52,26 @@ import Option from "./Option.js"; import Label from "./Label.js"; import ResponsivePopover from "./ResponsivePopover.js"; import Popover from "./Popover.js"; -import StandardListItem from "./StandardListItem.js"; +import CustomListItem from "./CustomListItem.js"; import Icon from "./Icon.js"; import Button from "./Button.js"; // Templates import SelectTemplate from "./generated/templates/SelectTemplate.lit.js"; - -// Styles -import selectCss from "./generated/themes/Select.css.js"; import ResponsivePopoverCommonCss from "./generated/themes/ResponsivePopoverCommon.css.js"; import ValueStateMessageCss from "./generated/themes/ValueStateMessage.css.js"; import SelectPopoverCss from "./generated/themes/SelectPopover.css.js"; + +// Styles +import selectCss from "./generated/themes/Select.css.js"; import type FormSupport from "./features/InputElementsFormSupport.js"; import type { IFormElement, NativeFormElement } from "./features/InputElementsFormSupport.js"; -import type ListItemBase from "./ListItemBase.js"; -import type SelectMenu from "./SelectMenu.js"; -import type { SelectMenuOptionClick, SelectMenuChange } from "./SelectMenu.js"; - -/** - * Interface for components that may be slotted inside `ui5-select` as options - * @public - */ -interface IOption extends UI5Element { - selected: boolean, - tooltip: string, - icon?: string | null, - value: string, - additionalText?: string, - focused?: boolean, - text?: Array, - stableDomRef: string, - displayText?: string, -} type SelectChangeEventDetail = { - selectedOption: IOption, + selectedOption: Option, } type SelectLiveChangeEventDetail = { - selectedOption: IOption, + selectedOption: Option, } /** @@ -111,16 +90,6 @@ type SelectLiveChangeEventDetail = { * The available options of the Select are defined by using the Option component. * The Option comes with predefined design and layout, including `icon`, `text` and `additional-text`. * - * 2. With SelectMenu (`ui5-select-menu`) and SelectMenuOption (`ui5-select-menu-option`) web components: - * - * The SelectMenu can be used as alternative to define the Select's dropdown - * and can be used via the `menu` property of the Select to reference SelectMenu by its ID. - * The component gives the possibility to customize the Select's dropdown - * by slotting entirely custom options (via the SelectMenuOption component) and adding custom styles. - * - * **Note:** SelectMenu is a popover and placing it top-level in the HTML page is recommended, - * because some page styles (for example transitions) can misplace the SelectMenu. - * * ### Keyboard Handling * The `ui5-select` provides advanced keyboard handling. * @@ -157,7 +126,7 @@ type SelectLiveChangeEventDetail = { ResponsivePopover, Popover, List, - StandardListItem, + CustomListItem, Icon, Button, ], @@ -165,7 +134,7 @@ type SelectLiveChangeEventDetail = { /** * Fired when the selected option changes. * @allowPreventDefault - * @param {IOption} selectedOption the selected option. + * @param {Option} selectedOption the selected option. * @public */ @event("change", { @@ -179,7 +148,7 @@ type SelectLiveChangeEventDetail = { /** * Fired when the user navigates through the options, but the selection is not finalized, * or when pressing the ESC key to revert the current selection. - * @param {IOption} selectedOption the selected option. + * @param {Option} selectedOption the selected option. * @public * @since 1.17.0 */ @@ -204,18 +173,6 @@ type SelectLiveChangeEventDetail = { class Select extends UI5Element implements IFormElement { static i18nBundle: I18nBundle; - /** - * Defines a reference (ID or DOM element) of component's menu of options - * as alternative to define the select's dropdown. - * - * **Note:** Usage of `ui5-select-menu` is recommended. - * @default undefined - * @public - * @since 1.17.0 - */ - @property({ validator: DOMReference }) - menu?: HTMLElement | string; - /** * Defines whether the component is in disabled state. * @@ -319,24 +276,14 @@ class Select extends UI5Element implements IFormElement { @property({ type: Boolean }) focused!: boolean; - /** - * @private - */ - @property({ validator: Integer, defaultValue: -1, noAttribute: true }) - _selectedIndex!: number; - - _syncedOptions: Array; _selectedIndexBeforeOpen: number; _escapePressed: boolean; - _lastSelectedOption: IOption | null; + _lastSelectedOption: Option | null; _typedChars: string; _typingTimeoutID?: Timeout | number; responsivePopover!: ResponsivePopover; - selectedItem?: string | null; valueStatePopover?: Popover; - selectMenu?: SelectMenu; - /** * Defines the component options. * @@ -346,8 +293,13 @@ class Select extends UI5Element implements IFormElement { * **Note:** Use the `ui5-option` component to define the desired options. * @public */ - @slot({ "default": true, type: HTMLElement, invalidateOnChildChange: true }) - options!: Array; + @slot({ + "default": true, + type: HTMLElement, + individualSlots: true, + invalidateOnChildChange: true, + }) + options!: Array