diff --git a/packages/main/cypress/specs/LitKeyFunction.cy.ts b/packages/main/cypress/specs/LitKeyFunction.cy.ts index 367b47cf0609..7a0fa7716051 100644 --- a/packages/main/cypress/specs/LitKeyFunction.cy.ts +++ b/packages/main/cypress/specs/LitKeyFunction.cy.ts @@ -20,16 +20,11 @@ describe("Lit HTML key function for #each", () => { cy.realPress("a"); cy.get("@mcb") - .shadow() - .find(".ui5-multi-combobox-all-items-responsive-popover") - .as("popover"); - - cy.get("@popover") - .find(".ui5-multi-combobox-all-items-list > ui5-li") + .find("[ui5-mcb-item]") .as("items"); cy.get("@items") - .eq(0) + .eq(3) .realClick(); cy.get("@mcb") @@ -39,12 +34,12 @@ describe("Lit HTML key function for #each", () => { cy.get("@items") .eq(0) - .should("contain.text", "") + .invoke("attr", "text", "") .should("not.have.attr", "selected"); cy.get("@items") .eq(3) - .should("contain.text", "USA") + .invoke("attr", "text", "USA") .should("have.attr", "selected"); }); }); diff --git a/packages/main/cypress/specs/base/Events.cy.ts b/packages/main/cypress/specs/base/Events.cy.ts index b3d6e710ecf4..1dd73ab1094a 100644 --- a/packages/main/cypress/specs/base/Events.cy.ts +++ b/packages/main/cypress/specs/base/Events.cy.ts @@ -185,8 +185,7 @@ describe("Event bubbling", () => { .realClick(); cy.get("@multiCombobox") - .shadow() - .find("[ui5-responsive-popover]") + .find("[ui5-mcb-item]") .should("be.visible"); // act - close the MultiComboBox diff --git a/packages/main/src/ComboBox.ts b/packages/main/src/ComboBox.ts index 71a46c5a9710..3501380da787 100644 --- a/packages/main/src/ComboBox.ts +++ b/packages/main/src/ComboBox.ts @@ -98,7 +98,6 @@ interface IComboBoxItem extends UI5Element { isGroupItem?: boolean, selected?: boolean, additionalText?: string, - stableDomRef: string, _isVisible?: boolean, items?: Array } @@ -106,10 +105,6 @@ interface IComboBoxItem extends UI5Element { type ValueStateAnnouncement = Record, string>; type ValueStateTypeAnnouncement = Record, string>; -type ComboBoxListItem = ListItemStandard & { - mappedItem: ComboBoxItem -}; - enum ValueStateIconMapping { Negative = "error", Critical = "alert", @@ -398,7 +393,12 @@ class ComboBox extends UI5Element implements IFormInputElement { * Defines the component items. * @public */ - @slot({ type: HTMLElement, "default": true, invalidateOnChildChange: true }) + @slot({ + type: HTMLElement, + "default": true, + individualSlots: true, + invalidateOnChildChange: true, + }) items!: Array; /** @@ -504,10 +504,6 @@ class ComboBox extends UI5Element implements IFormInputElement { } this.storeResponsivePopoverWidth(); - - this.items.forEach(item => { - item._getRealDomRef = () => this._getPicker().querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!; - }); } _focusin(e: FocusEvent) { @@ -1129,9 +1125,9 @@ class ComboBox extends UI5Element implements IFormInputElement { } _selectItem(e: CustomEvent) { - const listItem = e.detail.item as ComboBoxListItem; + const item = e.detail.item as ComboBoxItem; - this._selectedItemText = listItem.mappedItem.text || ""; + this._selectedItemText = item.text || ""; this._selectionPerformed = true; const sameItemSelected = this.value === this._selectedItemText; @@ -1144,17 +1140,12 @@ class ComboBox extends UI5Element implements IFormInputElement { this.value = this._selectedItemText; - if (!listItem.mappedItem.selected) { + if (!item.selected) { this.fireDecoratorEvent("selection-change", { - item: listItem.mappedItem, + item, }); } - this._filteredItems.map(item => { - item.selected = (item === listItem.mappedItem && !item.isGroupItem); - return item; - }); - this._fireChangeEvent(); this._closeRespPopover(); diff --git a/packages/main/src/ComboBoxItem.hbs b/packages/main/src/ComboBoxItem.hbs new file mode 100644 index 000000000000..cf3506e83bae --- /dev/null +++ b/packages/main/src/ComboBoxItem.hbs @@ -0,0 +1,18 @@ +{{>include "./ListItemBase.hbs"}} + +{{#*inline "listItemContent"}} +
+
+ + {{{text}}} + + {{#if additionalText}} + {{additionalText}} + {{/if}} +
+
+{{/inline}} + +{{#*inline "listItemAttributes"}} + role="option" +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/ComboBoxItem.ts b/packages/main/src/ComboBoxItem.ts index 426e141e6362..3406c8970040 100644 --- a/packages/main/src/ComboBoxItem.ts +++ b/packages/main/src/ComboBoxItem.ts @@ -1,19 +1,24 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import type { IComboBoxItem } from "./ComboBox.js"; +import ListItemBase from "./ListItemBase.js"; +import ComboBoxItemTemplate from "./generated/templates/ComboBoxItemTemplate.lit.js"; +import ComboboxItemCss from "./generated/themes/ComboBoxItem.css.js"; /** * @class * The `ui5-cb-item` represents the item for a `ui5-combobox`. * @constructor - * @extends UI5Element - * @abstract + * @extends ListItemBase * @implements {IComboBoxItem} * @public */ -@customElement("ui5-cb-item") -class ComboBoxItem extends UI5Element implements IComboBoxItem { +@customElement({ + tag: "ui5-cb-item", + template: ComboBoxItemTemplate, + styles: [ListItemBase.styles, ComboboxItemCss], +}) +class ComboBoxItem extends ListItemBase implements IComboBoxItem { /** * Defines the text of the component. * @default undefined @@ -52,9 +57,15 @@ class ComboBoxItem extends UI5Element implements IComboBoxItem { @property({ type: Boolean }) selected = false; - get stableDomRef() { - return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; - } + /** + * Defines the markup text that will be displayed as suggestion. + * Used for highlighting the matching parts of the text. + * + * @since 2.4.0 + * @private + */ + @property() + markupText = ""; } ComboBoxItem.define(); diff --git a/packages/main/src/ComboBoxItemGroup.hbs b/packages/main/src/ComboBoxItemGroup.hbs new file mode 100644 index 000000000000..4cc129d71a5d --- /dev/null +++ b/packages/main/src/ComboBoxItemGroup.hbs @@ -0,0 +1,9 @@ +{{>include "./ListItemGroup.hbs"}} + +{{#*inline "items"}} + {{#each items}} + {{#if _isVisible}} + + {{/if}} + {{/each}} +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/ComboBoxItemGroup.ts b/packages/main/src/ComboBoxItemGroup.ts index 07f5859ce834..632b5d3e6079 100644 --- a/packages/main/src/ComboBoxItemGroup.ts +++ b/packages/main/src/ComboBoxItemGroup.ts @@ -1,63 +1,45 @@ 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 type { IComboBoxItem } from "./ComboBox.js"; +import ListItemGroup from "./ListItemGroup.js"; +import type ComboBoxItem from "./ComboBoxItem.js"; +import ComboBoxItemGroupTemplate from "./generated/templates/ComboBoxItemGroupTemplate.lit.js"; /** * @class * The `ui5-cb-group-item` is type of suggestion item, * that can be used to split the `ui5-combobox` suggestions into groups. * @constructor - * @extends UI5Element + * @extends ListItemGroup * @abstract * @public * @implements {IComboBoxItem} * @since 1.0.0-rc.15 */ -@customElement("ui5-cb-item-group") -class ComboBoxItemGroup extends UI5Element implements IComboBoxItem { - /** - * Defines the text of the component. - * @default undefined - * @public - */ - @property() - headerText?: string; - - /** - * Indicates whether the item is focused - * @protected - */ - @property({ type: Boolean }) - focused = false - - /** - * Defines the items of the ui5-cb-item-group. - * @public - */ - @slot({ - "default": true, - invalidateOnChildChange: true, - type: HTMLElement, - }) - items!: Array; - - /** - * Used to avoid tag name checks - * @protected - */ - get isGroupItem(): boolean { - return true; - } - - get stableDomRef() { - return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; - } - - get _isVisible() { - return this.items.some(item => item._isVisible); - } +@customElement({ + tag: "ui5-cb-item-group", + template: ComboBoxItemGroupTemplate, +}) +class ComboBoxItemGroup extends ListItemGroup implements IComboBoxItem { + /** + * Defines the items of the ui5-cb-item-group. + * @public + */ + @slot({ + "default": true, + invalidateOnChildChange: true, + individualSlots: true, + type: HTMLElement, + }) + items!: Array; + + get isGroupItem(): boolean { + return true; + } + + get _isVisible() { + return this.items.some(item => item._isVisible); + } } ComboBoxItemGroup.define(); diff --git a/packages/main/src/ComboBoxPopover.hbs b/packages/main/src/ComboBoxPopover.hbs index 56dc7739c369..fb906e90e5fe 100644 --- a/packages/main/src/ComboBoxPopover.hbs +++ b/packages/main/src/ComboBoxPopover.hbs @@ -85,20 +85,7 @@ selection-mode="Single" > {{#each _filteredItems}} - {{#if isGroupItem}} - {{#if _isVisible}} - - {{#each this.items}} - {{#if _isVisible}} - {{> listItem}} - {{/if}} - {{/each}} - - {{/if}} - {{else}} - {{> listItem}} - {{/if}} - + {{/each}} @@ -140,20 +127,4 @@ {{else}} {{/if}} -{{/inline}} - -{{#*inline "listItem"}} - - {{this.text}} - -{{/inline}} +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/List.ts b/packages/main/src/List.ts index 7cdfa74a5a50..61f3ebea1f43 100644 --- a/packages/main/src/List.ts +++ b/packages/main/src/List.ts @@ -873,10 +873,10 @@ class List extends UI5Element { slottedItems.forEach(item => { if (isInstanceOfListItemGroup(item)) { - const groupItems = [item.groupHeaderItem, ...item.items].filter(Boolean); + const groupItems = [item.groupHeaderItem, ...item.items.filter(listItem => listItem.assignedSlot)].filter(Boolean); items.push(...groupItems); } else { - items.push(item); + item.assignedSlot && items.push(item); } }); diff --git a/packages/main/src/ListItemGroup.hbs b/packages/main/src/ListItemGroup.hbs index c32938fee9da..d61eecb794d4 100644 --- a/packages/main/src/ListItemGroup.hbs +++ b/packages/main/src/ListItemGroup.hbs @@ -4,18 +4,25 @@ @dragover="{{_ondragover}}" @drop="{{_ondrop}}" @dragleave="{{_ondragleave}}"> + {{#if hasHeader}} {{#if hasFormattedHeader}} - + {{else}} - {{headerText}} + {{headerText}} {{/if}} {{/if}} - + + {{> items}} - \ No newline at end of file + + + +{{#*inline "items"}} + +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/MultiComboBox.ts b/packages/main/src/MultiComboBox.ts index de316f5927a3..62819aad1d00 100644 --- a/packages/main/src/MultiComboBox.ts +++ b/packages/main/src/MultiComboBox.ts @@ -117,7 +117,6 @@ interface IMultiComboBoxItem extends UI5Element { headerText?: string, selected: boolean, isGroupItem?: boolean, - stableDomRef: string, _isVisible?: boolean, items?: Array, } @@ -461,7 +460,12 @@ class MultiComboBox extends UI5Element implements IFormInputElement { * Defines the component items. * @public */ - @slot({ type: HTMLElement, "default": true, invalidateOnChildChange: true }) + @slot({ + type: HTMLElement, + "default": true, + invalidateOnChildChange: true, + individualSlots: true, + }) items!: Array; /** @@ -992,7 +996,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } _handleSelectAll() { - const filteredItems = this._filteredItems.filter(item => !item.isGroupItem); + const filteredItems = this._getItems().filter(item => item._isVisible && !item.isGroupItem); const allItemsSelected = filteredItems.every(item => item.selected); this._previouslySelectedItems = filteredItems.filter(item => item.selected).map(item => item); @@ -1007,7 +1011,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } } - async _onListHeaderKeydown(e: KeyboardEvent) { + _onListHeaderKeydown(e: KeyboardEvent) { const isArrowDown = isDown(e); const isArrowUp = isUp(e); const isSelectAllFocused = (e.target as HTMLElement).classList.contains("ui5-mcb-select-all-checkbox"); @@ -1062,8 +1066,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } _onItemKeydown(e: KeyboardEvent) { - const isFirstItemGroup = this.list?.items[1] === e.target && this.list?.items[0].hasAttribute("ui5-li-group"); - const isFirstItem = this.list?.items[0] === e.target || isFirstItemGroup; + const isFirstItemGroup = this.list?.getSlottedNodes("items")[1] === e.target && this.list?.getSlottedNodes("items")[0].hasAttribute("ui5-li-group"); + const isFirstItem = this.list?.getSlottedNodes("items")[0] === e.target || isFirstItemGroup; const isArrowUp = isUp(e) || isUpCtrl(e); if (this.hasValueStateMessage && !this.valueStateHeader) { @@ -1077,12 +1081,12 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (isHomeCtrl(e)) { this.list?._itemNavigation._handleHome(); - this.list?.items[this.list?._itemNavigation._currentIndex].focus(); + this.list?.getSlottedNodes("items")[this.list?._itemNavigation._currentIndex].focus(); } if (isEndCtrl(e)) { this.list?._itemNavigation._handleEnd(); - this.list?.items[this.list?._itemNavigation._currentIndex].focus(); + this.list?.getSlottedNodes("items")[this.list?._itemNavigation._currentIndex].focus(); } e.preventDefault(); @@ -1094,12 +1098,12 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if ((isUpCtrl(e)) && !isFirstItem) { this.list?._itemNavigation._handleUp(); - this.list?.items[this.list?._itemNavigation._currentIndex].focus(); + this.list?.getSlottedNodes("items")[this.list?._itemNavigation._currentIndex].focus(); } if (isDownCtrl(e)) { this.list?._itemNavigation._handleDown(); - this.list?.items[this.list?._itemNavigation._currentIndex].focus(); + this.list?.getSlottedNodes("items")[this.list?._itemNavigation._currentIndex].focus(); } if (isShow(e)) { @@ -1114,7 +1118,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (isFirstItem && isArrowUp) { if (this.showSelectAll) { if (isFirstItemGroup) { - this.list?.items[0].focus(); + this.list?.getSlottedNodes("items")[0].focus(); return; } @@ -1181,10 +1185,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } async _handleArrowDown() { - const firstListItem = this.list?.listItems[0]; + const firstListItem = this.list?.getSlottedNodes("items")[0]; + const focusRef = firstListItem?.hasAttribute("ui5-mcb-item-group") ? (firstListItem as MultiComboBoxItemGroup).getFocusDomRef() : firstListItem; if (this.open) { - firstListItem && this.list?._itemNavigation.setCurrentItem(firstListItem); + firstListItem && focusRef && this.list?._itemNavigation.setCurrentItem(focusRef); this.value = this.valueBeforeAutoComplete || this.value; // wait item navigation to apply correct tabindex @@ -1197,8 +1202,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement { _handleItemRangeSelection(e: KeyboardEvent) { const items = this._getItems(); - const listItems = this.list?.items; - const currentItemIdx = Number(listItems?.indexOf(e.target as ListItemBase)); + const listItems = this.list?.getSlottedNodes("items"); + const currentItemIdx = Number(listItems?.indexOf(e.target as IMultiComboBoxItem)); const nextItemIdx = currentItemIdx + 1; const prevItemIdx = currentItemIdx - 1; this._previouslySelectedItems = this._getSelectedItems(); @@ -1365,8 +1370,19 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const itemsToFilter = this._getItems().filter(item => !item.isGroupItem); const filteredItems = (Filters[this.filter] || Filters.StartsWithPerTerm)(str, itemsToFilter, "text"); - // Return the filtered items and their group items - return this._getItems().filter((item, idx, allItems) => MultiComboBox._groupItemFilter(item, ++idx, allItems, filteredItems) || filteredItems.indexOf(item) !== -1); + this._getItems().forEach(item => { + if (isInstanceOfMultiComboBoxItem(item)) { + item._isVisible = filteredItems.includes(item); + } + }); + + return this.items.filter(item => { + if (item.isGroupItem) { + return (item as MultiComboBoxItemGroup).items.some(listItem => listItem._isVisible) ? item : false; + } + + return item._isVisible; + }); } /** @@ -1609,11 +1625,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (this.open) { const list = this._getList(); - const selectedListItemsCount = list?.querySelectorAll("[ui5-li][selected]")?.length; - const items = this._getItems().filter(item => !item.isGroupItem); - const listItems = list?.querySelectorAll("[ui5-li]")?.length; - const selectedItemsCount = items.filter(item => item.selected).length; - this._allSelected = selectedItemsCount === items.length || selectedListItemsCount === listItems; + const selectedListItemsCount = this.items.filter(item => item.selected).length; + this._allSelected = selectedListItemsCount > 0 && ((selectedListItemsCount === this.items.length) || (list?.getSlottedNodes("items").length === selectedListItemsCount)); } this._effectiveShowClearIcon = (this.showClearIcon && !!this.value && !this.readonly && !this.disabled); @@ -1629,6 +1642,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this.style.setProperty(getScopedVarName("--_ui5-input-icons-count"), `${this.iconsCount}`); if (!input || !value) { + this._getItems().forEach(item => { + if (isInstanceOfMultiComboBoxItem(item)) { + item._isVisible = true; + } + }); return; } // Typehead causes issues on Android devices, so we disable it for now @@ -1665,10 +1683,6 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (this._tokenizer.expanded && this.hasAttribute("focused")) { this._tokenizer.scrollToEnd(); } - - this._getItems().forEach(item => { - item._getRealDomRef = () => this._getResponsivePopover()!.querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!; - }); } get _isPhone() { @@ -1769,7 +1783,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._clearingValue = false; - if (!isPhone() && (((e.relatedTarget as HTMLElement)?.tagName !== "UI5-STATIC-AREA-ITEM") || !e.relatedTarget)) { + if (!isPhone() && e.target === this._innerInput) { this._innerInput.setSelectionRange(0, this.value.length); } this._tokenizer.tokens.forEach(token => { @@ -1786,6 +1800,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const focusIsGoingInValueStatePopup = this?.contains(e.relatedTarget as Node); if (focusIsGoingInValueStatePopup) { + this.focused = false; e.stopImmediatePropagation(); return; } diff --git a/packages/main/src/MultiComboBoxItem.hbs b/packages/main/src/MultiComboBoxItem.hbs new file mode 100644 index 000000000000..d9124fc1a1a8 --- /dev/null +++ b/packages/main/src/MultiComboBoxItem.hbs @@ -0,0 +1,19 @@ +{{>include "./ComboBoxItem.hbs"}} + +{{#*inline "listItemContent"}} + +
+
+ + {{{text}}} + + {{#if additionalText}} + {{additionalText}} + {{/if}} +
+
+{{/inline}} + +{{#*inline "listItemAttributes"}} + role="option" +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/MultiComboBoxItem.ts b/packages/main/src/MultiComboBoxItem.ts index 726742708340..78067336819b 100644 --- a/packages/main/src/MultiComboBoxItem.ts +++ b/packages/main/src/MultiComboBoxItem.ts @@ -1,18 +1,32 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; import ComboBoxItem from "./ComboBoxItem.js"; +import CheckBox from "./CheckBox.js"; import type { IMultiComboBoxItem } from "./MultiComboBox.js"; +import { + ARIA_LABEL_LIST_ITEM_CHECKBOX, +} from "./generated/i18n/i18n-defaults.js"; + +import styles from "./generated/themes/MultiComboBoxItem.css.js"; +import MultiComboBoxItemTemplate from "./generated/templates/MultiComboBoxItemTemplate.lit.js"; +import type { SelectionRequestEventDetail } from "./ListItem.js"; /** * @class * The `ui5-mcb-item` represents the item for a `ui5-multi-combobox`. * @constructor * @extends ComboBoxItem - * @abstract * @implements {IMultiComboBoxItem} * @public */ -@customElement("ui5-mcb-item") +@customElement({ + tag: "ui5-mcb-item", + template: MultiComboBoxItemTemplate, + styles: [ComboBoxItem.styles, styles], + dependencies: [...ComboBoxItem.dependencies, CheckBox], +}) class MultiComboBoxItem extends ComboBoxItem implements IMultiComboBoxItem { /** * Defines the selected state of the component. @@ -29,9 +43,24 @@ class MultiComboBoxItem extends ComboBoxItem implements IMultiComboBoxItem { @property({ type: Boolean, noAttribute: true }) _isVisible = false; + @i18n("@ui5/webcomponents") + static i18nBundle: I18nBundle; + get isMultiComboBoxItem() { return true; } + + _onclick(e: MouseEvent) { + if ((e.target as HTMLElement)?.hasAttribute("ui5-checkbox")) { + return this.fireEvent("_selection-requested", { item: this, selected: (e.target as CheckBox).checked, selectionComponentPressed: true }); + } + + super._onclick(e); + } + + get _accessibleName() { + return MultiComboBoxItem.i18nBundle.getText(ARIA_LABEL_LIST_ITEM_CHECKBOX); + } } const isInstanceOfMultiComboBoxItem = (object: any): object is MultiComboBoxItem => { diff --git a/packages/main/src/MultiComboBoxItemGroup.hbs b/packages/main/src/MultiComboBoxItemGroup.hbs new file mode 100644 index 000000000000..4cc129d71a5d --- /dev/null +++ b/packages/main/src/MultiComboBoxItemGroup.hbs @@ -0,0 +1,9 @@ +{{>include "./ListItemGroup.hbs"}} + +{{#*inline "items"}} + {{#each items}} + {{#if _isVisible}} + + {{/if}} + {{/each}} +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/MultiComboBoxItemGroup.ts b/packages/main/src/MultiComboBoxItemGroup.ts index 2997679a0a9a..6fbc2a1bbb73 100644 --- a/packages/main/src/MultiComboBoxItemGroup.ts +++ b/packages/main/src/MultiComboBoxItemGroup.ts @@ -1,31 +1,26 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; -import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; import type { IMultiComboBoxItem } from "./MultiComboBox.js"; import type MultiComboBoxItem from "./MultiComboBoxItem.js"; +import MultiComboBoxItemGroupTemplate from "./generated/templates/MultiComboBoxItemGroupTemplate.lit.js"; +import type ListItemGroupHeader from "./ListItemGroupHeader.js"; +import ComboBoxItemGroup from "./ComboBoxItemGroup.js"; /** * @class * The `ui5-mcb-item-group` is type of suggestion item, * that can be used to split the `ui5-multi-combobox` suggestions into groups. * @constructor - * @extends UI5Element - * @abstract + * @extends ComboBoxItemGroup * @public * @implements {IMultiComboBoxItem} * @since 2.0.0 */ -@customElement("ui5-mcb-item-group") -class MultiComboBoxItemGroup extends UI5Element implements IMultiComboBoxItem { - /** - * Defines the text of the component. - * @default undefined - * @public - */ - @property() - headerText?: string; - +@customElement({ + tag: "ui5-mcb-item-group", + template: MultiComboBoxItemGroupTemplate, +}) +class MultiComboBoxItemGroup extends ComboBoxItemGroup implements IMultiComboBoxItem { /** * Defines the items of the ui5-mcb-item-group. * @public @@ -33,6 +28,7 @@ class MultiComboBoxItemGroup extends UI5Element implements IMultiComboBoxItem { @slot({ "default": true, invalidateOnChildChange: true, + individualSlots: true, type: HTMLElement, }) items!: Array; @@ -52,6 +48,10 @@ class MultiComboBoxItemGroup extends UI5Element implements IMultiComboBoxItem { get stableDomRef() { return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`; } + + getFocusDomRef() { + return this.shadowRoot!.querySelector("[ui5-li-group-header]") as ListItemGroupHeader; + } } MultiComboBoxItemGroup.define(); diff --git a/packages/main/src/MultiComboBoxPopover.hbs b/packages/main/src/MultiComboBoxPopover.hbs index ee5fc1308e79..0144d1911a20 100644 --- a/packages/main/src/MultiComboBoxPopover.hbs +++ b/packages/main/src/MultiComboBoxPopover.hbs @@ -80,33 +80,13 @@ {{#if filterSelected}} {{#each selectedItems}} - {{#if isGroupItem}} - - {{#each this.items}} - {{#if _isVisible}} - {{> listItem}} - {{/if}} - {{/each}} - - {{else}} - {{> listItem}} - {{/if}} + {{> listItem}} {{/each}} {{else}} - + {{#each _filteredItems}} - {{#if isGroupItem}} - - {{#each this.items}} - {{#if _isVisible}} - {{> listItem}} - {{/if}} - {{/each}} - - {{else}} - {{> listItem}} - {{/if}} + {{> listItem}} {{/each}} {{/if}} @@ -152,18 +132,7 @@ {{/inline}} {{#*inline "listItem"}} - - {{this.text}} - + {{/inline}} {{#*inline "selectAllWrapper"}} diff --git a/packages/main/src/themes/ComboBoxItem.css b/packages/main/src/themes/ComboBoxItem.css new file mode 100644 index 000000000000..c500bb0dd498 --- /dev/null +++ b/packages/main/src/themes/ComboBoxItem.css @@ -0,0 +1,14 @@ +:host([ui5-cb-item]) { + height: auto; + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-cb-item]) .ui5-li-root { + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-cb-item]) .ui5-li-content { + padding-bottom: .875rem; + padding-top: .875rem; + box-sizing: border-box; +} \ No newline at end of file diff --git a/packages/main/src/themes/MultiComboBoxItem.css b/packages/main/src/themes/MultiComboBoxItem.css new file mode 100644 index 000000000000..c9256a465239 --- /dev/null +++ b/packages/main/src/themes/MultiComboBoxItem.css @@ -0,0 +1,19 @@ +:host([ui5-mcb-item]) { + height: auto; + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-mcb-item]) .ui5-li-root { + padding-inline-start: 0; + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-mcb-item]) .ui5-li-content { + padding-bottom: .875rem; + padding-top: .875rem; + box-sizing: border-box; +} + +:host([ui5-mcb-item]) [ui5-checkbox] { + overflow: visible; +} \ No newline at end of file diff --git a/packages/main/test/pages/ComboBox.html b/packages/main/test/pages/ComboBox.html index 61e6c8906bbe..d6725c22c9b9 100644 --- a/packages/main/test/pages/ComboBox.html +++ b/packages/main/test/pages/ComboBox.html @@ -389,7 +389,9 @@

ComboBox in Compact

// remove busy state event.target.loading = false; - }); + + // load the items once or turn off filtering + }, { once: true });