@api-client/ui 0.5.22 → 0.5.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,cAAc,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAI1D,OAAO,MAAM,MAAM,8BAA8B,CAAA;AACjD,OAAO,UAAU,MAAM,eAAe,CAAA;AACtC,OAAO,SAAS,MAAM,cAAc,CAAA;AAEpC,OAAO,UAAU,MAAM,kCAAkC,CAAA;AAGzD;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,MAAM;IACtC;;;OAGG;IACyC,QAAQ,CAAC,IAAI,UAAQ;IAEjE;;;OAGG;IACyC,QAAQ,CAAC,QAAQ,UAAQ;IAErE;;OAEG;IACM,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,IAAI,CAAO;IAExD;;OAEG;IACkD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAG,UAAU,EAAE,CAAA;;IAS/F,iBAAiB,IAAI,IAAI;cAYf,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI;IAQhE,aAAa,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO;IAYhD;;OAEG;IACH,IAAI,IAAI,IAAI;IAUZ;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ,YAAY,IAAI,IAAI;IAuCpB;;OAEG;IACH,SAAS,CAAC,kBAAkB,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;IAQ5C;;OAEG;IACM,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAuB9C,mBAAmB,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI;IAIzC;;OAEG;IACH,SAAS,CAAC,WAAW,IAAI,IAAI;IAO7B;;OAEG;IACH,YAAY,IAAI,IAAI;IAQpB;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI;IAKxC,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO;IAWhE;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAMhC;;OAEG;IACH,IAAI,YAAY,IAAI,UAAU,GAAG,IAAI,CAEpC;IAED;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI;IAO9C;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI;IAKjD;;OAEG;IACH,SAAS,CAAC,gBAAgB,IAAI,IAAI;IAKzB,MAAM,IAAI,cAAc;CAWlC"}
1
+ {"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,cAAc,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAI1D,OAAO,MAAM,MAAM,8BAA8B,CAAA;AACjD,OAAO,UAAU,MAAM,eAAe,CAAA;AACtC,OAAO,SAAS,MAAM,cAAc,CAAA;AAEpC,OAAO,UAAU,MAAM,kCAAkC,CAAA;AAGzD;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,MAAM;IACtC;;;OAGG;IACyC,QAAQ,CAAC,IAAI,UAAQ;IAEjE;;;OAGG;IACyC,QAAQ,CAAC,QAAQ,UAAQ;IAErE;;OAEG;IACM,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,IAAI,CAAO;IAExD;;OAEG;IACkD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAG,UAAU,EAAE,CAAA;;IAS/F,iBAAiB,IAAI,IAAI;cAYf,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI;IAQhE,aAAa,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO;IAYhD;;OAEG;IACH,IAAI,IAAI,IAAI;IAUZ;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ,YAAY,IAAI,IAAI;IA2BpB;;OAEG;IACH,SAAS,CAAC,kBAAkB,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI;IAQ5C;;OAEG;IACM,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAuB9C,mBAAmB,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI;IAIzC;;OAEG;IACH,SAAS,CAAC,WAAW,IAAI,IAAI;IAO7B;;OAEG;IACH,YAAY,IAAI,IAAI;IAQpB;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI;IAKxC,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO;IAWhE;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAMhC;;OAEG;IACH,IAAI,YAAY,IAAI,UAAU,GAAG,IAAI,CAEpC;IAED;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI;IAO9C;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI;IAKjD;;OAEG;IACH,SAAS,CAAC,gBAAgB,IAAI,IAAI;IAKzB,MAAM,IAAI,cAAc;CAWlC"}
@@ -136,40 +136,26 @@ let Menu = (() => {
136
136
  this.dispatchEvent(new CustomEvent('close', { bubbles: false, composed: true }));
137
137
  }
138
138
  positionMenu() {
139
- // when there's more space above the anchor, position the menu above it
139
+ // Let CSS anchor positioning handle the positioning automatically
140
+ // Only intervene if we need to set max-height for overflow cases
140
141
  const box = this.getBoundingClientRect();
141
- // Now, we determine, whether to position the menu above or below the anchor
142
- // in a way, that if we have enough space below the anchor, we position it below,
143
- // otherwise we position it above the anchor.
144
- // our starting point is the anchor being positioned below the anchor
145
142
  const menuBottom = box.top + box.height;
146
- if (menuBottom <= innerHeight) {
147
- // if the menu fits below the anchor, we leave it as is.
148
- return;
149
- }
150
- // we do not make association from the menu to the anchor, so we make an assumption
151
- // that the anchor is 40px high, which is the default height of a button.
152
- // it can be different, but this is a good starting point.
153
- const anchorHeight = 40;
154
- const anchorBottom = box.top;
155
- const anchorTop = anchorBottom - anchorHeight;
156
- const menuHeight = box.height;
157
- const spaceBelow = innerHeight - anchorBottom;
158
- const spaceAbove = anchorTop;
159
- const diffBelow = spaceBelow - menuHeight;
160
- const diffAbove = spaceAbove - menuHeight;
161
- // The initial check ensures the menu does not fit below. Now, check if it fits above.
162
- if (diffAbove >= 0) {
163
- this.style.setProperty('position-area', 'top span-right');
164
- }
165
- else if (diffAbove > diffBelow) {
166
- // It doesn't fit in either direction. Choose the one with less overflow (larger, i.e., less negative, diff).
167
- this.style.setProperty('position-area', 'top span-right');
168
- this.style.maxHeight = `${spaceAbove}px`;
143
+ const menuRight = box.left + box.width;
144
+ // Reset any previous manual positioning to let CSS anchor positioning work
145
+ this.style.removeProperty('position-area');
146
+ this.style.removeProperty('max-height');
147
+ // Only set max-height if the menu would overflow
148
+ if (menuBottom > innerHeight) {
149
+ const availableHeight = innerHeight - box.top;
150
+ this.style.maxHeight = `${Math.max(200, availableHeight - 20)}px`;
169
151
  }
170
- else {
171
- this.style.setProperty('position-area', 'bottom span-right');
172
- this.style.maxHeight = `${spaceBelow}px`;
152
+ if (menuRight > innerWidth) {
153
+ const availableWidth = innerWidth - box.left;
154
+ // Let CSS anchor positioning handle horizontal flipping
155
+ // We could set max-width if needed
156
+ if (availableWidth < 200) {
157
+ this.style.maxWidth = `${Math.max(180, availableWidth - 20)}px`;
158
+ }
173
159
  }
174
160
  }
175
161
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Menu.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, PropertyValues, TemplateResult } from 'lit'\nimport { property, state, queryAssignedElements } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { nanoid } from 'nanoid'\nimport UiList from '../../list/internals/List.js'\nimport UiMenuItem from './MenuItem.js'\nimport UiSubMenu from './SubMenu.js'\nimport { setDisabled } from '../../../lib/disabled.js'\nimport UiListItem from '../../list/internals/ListItem.js'\nimport { bound } from '../../../decorators/bound.js'\n\n/**\n * Material Design 3 Menu component with sub-menu support.\n * Uses Popover API and Anchor Positioning API for modern positioning.\n *\n * @fires select - Dispatched when a menu item is selected\n * @fires close - Dispatched when the menu is closed\n * @fires open - Dispatched when the menu is opened\n */\nexport default class Menu extends UiList {\n /**\n * Whether the menu is currently open\n * @attribute\n */\n @property({ type: Boolean, reflect: true }) accessor open = false\n\n /**\n * Whether the menu is disabled\n * @attribute\n */\n @property({ type: Boolean, reflect: true }) accessor disabled = false\n\n /**\n * Currently active sub-menu\n */\n @state() accessor activeSubMenu: UiSubMenu | null = null\n\n /**\n * Assigned menu items from light DOM\n */\n @queryAssignedElements({ selector: 'ui-menu-item' }) protected accessor assignedMenuItems!: UiMenuItem[]\n\n constructor() {\n super()\n this.selector = 'ui-menu-item'\n this.ariaExpanded = 'false'\n this.addEventListener('beforetoggle', this.handleBeforeToggle.bind(this))\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.setAttribute('role', 'menu')\n this.setAttribute('tabindex', '-1')\n if (!this.hasAttribute('popover')) {\n this.setAttribute('popover', 'auto')\n }\n if (!this.id) {\n this.id = nanoid()\n }\n }\n\n protected override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties)\n\n if (changedProperties.has('disabled')) {\n setDisabled(this, this.disabled)\n }\n }\n\n override togglePopover(force?: boolean): boolean {\n this.open = !this.open\n this.ariaExpanded = String(this.open)\n this.tabIndex = this.open ? 0 : -1\n const result = super.togglePopover(force)\n if (this.open) {\n this.positionMenu()\n this.focus()\n }\n return result\n }\n\n /**\n * Shows the menu\n */\n show(): void {\n this.tabIndex = 0 // Make menu focusable\n this.ariaExpanded = 'true'\n this.showPopover()\n this.open = true\n this.positionMenu()\n this.focus()\n this.dispatchEvent(new CustomEvent('open', { bubbles: false, composed: true }))\n }\n\n /**\n * Hides the menu\n */\n hide(): void {\n this.tabIndex = -1\n this.ariaExpanded = 'false'\n this.hidePopover()\n this.open = false\n this.closeSubMenu()\n this.dispatchEvent(new CustomEvent('close', { bubbles: false, composed: true }))\n }\n\n positionMenu(): void {\n // when there's more space above the anchor, position the menu above it\n const box = this.getBoundingClientRect()\n // Now, we determine, whether to position the menu above or below the anchor\n // in a way, that if we have enough space below the anchor, we position it below,\n // otherwise we position it above the anchor.\n\n // our starting point is the anchor being positioned below the anchor\n const menuBottom = box.top + box.height\n if (menuBottom <= innerHeight) {\n // if the menu fits below the anchor, we leave it as is.\n return\n }\n // we do not make association from the menu to the anchor, so we make an assumption\n // that the anchor is 40px high, which is the default height of a button.\n // it can be different, but this is a good starting point.\n const anchorHeight = 40\n const anchorBottom = box.top\n const anchorTop = anchorBottom - anchorHeight\n const menuHeight = box.height\n\n const spaceBelow = innerHeight - anchorBottom\n const spaceAbove = anchorTop\n\n const diffBelow = spaceBelow - menuHeight\n const diffAbove = spaceAbove - menuHeight\n // The initial check ensures the menu does not fit below. Now, check if it fits above.\n if (diffAbove >= 0) {\n this.style.setProperty('position-area', 'top span-right')\n } else if (diffAbove > diffBelow) {\n // It doesn't fit in either direction. Choose the one with less overflow (larger, i.e., less negative, diff).\n this.style.setProperty('position-area', 'top span-right')\n this.style.maxHeight = `${spaceAbove}px`\n } else {\n this.style.setProperty('position-area', 'bottom span-right')\n this.style.maxHeight = `${spaceBelow}px`\n }\n }\n\n /**\n * Handles beforetoggle event from popover\n */\n protected handleBeforeToggle(e: Event): void {\n const toggleEvent = e as ToggleEvent\n if (toggleEvent.newState === 'closed') {\n this.open = false\n this.closeSubMenu()\n }\n }\n\n /**\n * Handles keyboard navigation for the menu\n */\n override handleKeydown(e: KeyboardEvent): void {\n if (!this.open || e.defaultPrevented) return\n\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n this.hide()\n break\n case 'ArrowRight':\n e.preventDefault()\n this.openSubMenu()\n break\n case 'ArrowLeft':\n e.preventDefault()\n this.closeSubMenu()\n break\n default:\n // Let the parent UiList handle other keys\n super.handleKeydown(e)\n }\n }\n\n @bound\n handleSubMenuSelect(e: CustomEvent): void {\n super.notifySelect(e.detail.item, e.detail.index)\n }\n\n /**\n * Opens the sub-menu for the currently active item\n */\n protected openSubMenu(): void {\n const activeItem = this.activeListItem as UiMenuItem\n if (activeItem?.hasSubMenu) {\n activeItem.openSubMenu()\n }\n }\n\n /**\n * Closes the currently open sub-menu\n */\n closeSubMenu(): void {\n if (this.activeSubMenu) {\n this.activeSubMenu.removeEventListener('select', this.handleSubMenuSelect as EventListener)\n this.activeSubMenu.hide()\n this.activeSubMenu = null\n }\n }\n\n /**\n * Sets the active sub-menu\n */\n setActiveSubMenu(subMenu: UiSubMenu | null): void {\n this.activeSubMenu = subMenu\n subMenu?.addEventListener('select', this.handleSubMenuSelect as EventListener)\n }\n\n override notifySelect(item: UiListItem, index?: number): boolean {\n // Handle single selection\n if (item instanceof UiMenuItem) {\n this.clearSelection()\n item.selected = true\n }\n\n this.hide()\n return super.notifySelect(item, index)\n }\n\n /**\n * Clears selection from all menu items\n */\n protected clearSelection(): void {\n this.assignedMenuItems.forEach((menuItem) => {\n menuItem.selected = false\n })\n }\n\n /**\n * Gets the currently selected menu item\n */\n get selectedItem(): UiMenuItem | null {\n return this.assignedMenuItems.find((item) => item.selected) || null\n }\n\n /**\n * Sets the selected menu item\n */\n setSelectedItem(item: UiMenuItem | null): void {\n this.clearSelection()\n if (item) {\n item.selected = true\n }\n }\n\n /**\n * Handles sub-menu opening\n */\n protected handleSubMenuOpen(e: CustomEvent): void {\n const subMenu = e.detail.subMenu\n this.setActiveSubMenu(subMenu)\n }\n\n /**\n * Handles slot changes to update menu items\n */\n protected handleSlotChange(): void {\n // Update the items list when slot content changes\n this.updateItems()\n }\n\n override render(): TemplateResult {\n const classes = classMap({\n 'menu-container': true,\n })\n\n return html`\n <div class=${classes}>\n <slot @slotchange=${this.handleSlotChange}></slot>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"Menu.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, PropertyValues, TemplateResult } from 'lit'\nimport { property, state, queryAssignedElements } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { nanoid } from 'nanoid'\nimport UiList from '../../list/internals/List.js'\nimport UiMenuItem from './MenuItem.js'\nimport UiSubMenu from './SubMenu.js'\nimport { setDisabled } from '../../../lib/disabled.js'\nimport UiListItem from '../../list/internals/ListItem.js'\nimport { bound } from '../../../decorators/bound.js'\n\n/**\n * Material Design 3 Menu component with sub-menu support.\n * Uses Popover API and Anchor Positioning API for modern positioning.\n *\n * @fires select - Dispatched when a menu item is selected\n * @fires close - Dispatched when the menu is closed\n * @fires open - Dispatched when the menu is opened\n */\nexport default class Menu extends UiList {\n /**\n * Whether the menu is currently open\n * @attribute\n */\n @property({ type: Boolean, reflect: true }) accessor open = false\n\n /**\n * Whether the menu is disabled\n * @attribute\n */\n @property({ type: Boolean, reflect: true }) accessor disabled = false\n\n /**\n * Currently active sub-menu\n */\n @state() accessor activeSubMenu: UiSubMenu | null = null\n\n /**\n * Assigned menu items from light DOM\n */\n @queryAssignedElements({ selector: 'ui-menu-item' }) protected accessor assignedMenuItems!: UiMenuItem[]\n\n constructor() {\n super()\n this.selector = 'ui-menu-item'\n this.ariaExpanded = 'false'\n this.addEventListener('beforetoggle', this.handleBeforeToggle.bind(this))\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.setAttribute('role', 'menu')\n this.setAttribute('tabindex', '-1')\n if (!this.hasAttribute('popover')) {\n this.setAttribute('popover', 'auto')\n }\n if (!this.id) {\n this.id = nanoid()\n }\n }\n\n protected override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties)\n\n if (changedProperties.has('disabled')) {\n setDisabled(this, this.disabled)\n }\n }\n\n override togglePopover(force?: boolean): boolean {\n this.open = !this.open\n this.ariaExpanded = String(this.open)\n this.tabIndex = this.open ? 0 : -1\n const result = super.togglePopover(force)\n if (this.open) {\n this.positionMenu()\n this.focus()\n }\n return result\n }\n\n /**\n * Shows the menu\n */\n show(): void {\n this.tabIndex = 0 // Make menu focusable\n this.ariaExpanded = 'true'\n this.showPopover()\n this.open = true\n this.positionMenu()\n this.focus()\n this.dispatchEvent(new CustomEvent('open', { bubbles: false, composed: true }))\n }\n\n /**\n * Hides the menu\n */\n hide(): void {\n this.tabIndex = -1\n this.ariaExpanded = 'false'\n this.hidePopover()\n this.open = false\n this.closeSubMenu()\n this.dispatchEvent(new CustomEvent('close', { bubbles: false, composed: true }))\n }\n\n positionMenu(): void {\n // Let CSS anchor positioning handle the positioning automatically\n // Only intervene if we need to set max-height for overflow cases\n const box = this.getBoundingClientRect()\n const menuBottom = box.top + box.height\n const menuRight = box.left + box.width\n\n // Reset any previous manual positioning to let CSS anchor positioning work\n this.style.removeProperty('position-area')\n this.style.removeProperty('max-height')\n\n // Only set max-height if the menu would overflow\n if (menuBottom > innerHeight) {\n const availableHeight = innerHeight - box.top\n this.style.maxHeight = `${Math.max(200, availableHeight - 20)}px`\n }\n\n if (menuRight > innerWidth) {\n const availableWidth = innerWidth - box.left\n // Let CSS anchor positioning handle horizontal flipping\n // We could set max-width if needed\n if (availableWidth < 200) {\n this.style.maxWidth = `${Math.max(180, availableWidth - 20)}px`\n }\n }\n }\n\n /**\n * Handles beforetoggle event from popover\n */\n protected handleBeforeToggle(e: Event): void {\n const toggleEvent = e as ToggleEvent\n if (toggleEvent.newState === 'closed') {\n this.open = false\n this.closeSubMenu()\n }\n }\n\n /**\n * Handles keyboard navigation for the menu\n */\n override handleKeydown(e: KeyboardEvent): void {\n if (!this.open || e.defaultPrevented) return\n\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n this.hide()\n break\n case 'ArrowRight':\n e.preventDefault()\n this.openSubMenu()\n break\n case 'ArrowLeft':\n e.preventDefault()\n this.closeSubMenu()\n break\n default:\n // Let the parent UiList handle other keys\n super.handleKeydown(e)\n }\n }\n\n @bound\n handleSubMenuSelect(e: CustomEvent): void {\n super.notifySelect(e.detail.item, e.detail.index)\n }\n\n /**\n * Opens the sub-menu for the currently active item\n */\n protected openSubMenu(): void {\n const activeItem = this.activeListItem as UiMenuItem\n if (activeItem?.hasSubMenu) {\n activeItem.openSubMenu()\n }\n }\n\n /**\n * Closes the currently open sub-menu\n */\n closeSubMenu(): void {\n if (this.activeSubMenu) {\n this.activeSubMenu.removeEventListener('select', this.handleSubMenuSelect as EventListener)\n this.activeSubMenu.hide()\n this.activeSubMenu = null\n }\n }\n\n /**\n * Sets the active sub-menu\n */\n setActiveSubMenu(subMenu: UiSubMenu | null): void {\n this.activeSubMenu = subMenu\n subMenu?.addEventListener('select', this.handleSubMenuSelect as EventListener)\n }\n\n override notifySelect(item: UiListItem, index?: number): boolean {\n // Handle single selection\n if (item instanceof UiMenuItem) {\n this.clearSelection()\n item.selected = true\n }\n\n this.hide()\n return super.notifySelect(item, index)\n }\n\n /**\n * Clears selection from all menu items\n */\n protected clearSelection(): void {\n this.assignedMenuItems.forEach((menuItem) => {\n menuItem.selected = false\n })\n }\n\n /**\n * Gets the currently selected menu item\n */\n get selectedItem(): UiMenuItem | null {\n return this.assignedMenuItems.find((item) => item.selected) || null\n }\n\n /**\n * Sets the selected menu item\n */\n setSelectedItem(item: UiMenuItem | null): void {\n this.clearSelection()\n if (item) {\n item.selected = true\n }\n }\n\n /**\n * Handles sub-menu opening\n */\n protected handleSubMenuOpen(e: CustomEvent): void {\n const subMenu = e.detail.subMenu\n this.setActiveSubMenu(subMenu)\n }\n\n /**\n * Handles slot changes to update menu items\n */\n protected handleSlotChange(): void {\n // Update the items list when slot content changes\n this.updateItems()\n }\n\n override render(): TemplateResult {\n const classes = classMap({\n 'menu-container': true,\n })\n\n return html`\n <div class=${classes}>\n <slot @slotchange=${this.handleSlotChange}></slot>\n </div>\n `\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"Menu.styles.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.styles.ts"],"names":[],"mappings":";AAEA,wBA0LC"}
1
+ {"version":3,"file":"Menu.styles.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.styles.ts"],"names":[],"mappings":";AAEA,wBAsMC"}
@@ -3,7 +3,7 @@ export default css `
3
3
  :host {
4
4
  display: none;
5
5
  position-area: bottom span-right;
6
- position-try: normal flip-block;
6
+ position-try: --menu-fallback-bottom-left, --menu-fallback-top-right, --menu-fallback-top-left, flip-block;
7
7
  position: absolute;
8
8
  margin: 0;
9
9
  padding: 0;
@@ -14,9 +14,21 @@ export default css `
14
14
  overflow: auto;
15
15
  }
16
16
 
17
+ @position-try --menu-fallback-bottom-left {
18
+ position-area: bottom span-left;
19
+ }
20
+
21
+ @position-try --menu-fallback-top-right {
22
+ position-area: top span-right;
23
+ }
24
+
25
+ @position-try --menu-fallback-top-left {
26
+ position-area: top span-left;
27
+ }
28
+
17
29
  :host(:popover-open) {
18
30
  display: block;
19
- background-color: var(--md-sys-color-surface);
31
+ background-color: var(--md-sys-color-surface-container-low);
20
32
  border-radius: var(--md-sys-shape-corner-extra-small);
21
33
  box-shadow: var(--md-sys-elevation-3);
22
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Menu.styles.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0LjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: none;\n position-area: bottom span-right;\n position-try: normal flip-block;\n position: absolute;\n margin: 0;\n padding: 0;\n border: none;\n overflow: hidden;\n /* in most cases the max-height won't matter as this assumes the whole screen to be available, which is rarely the truth. */\n max-height: 90vh;\n overflow: auto;\n }\n\n :host(:popover-open) {\n display: block;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-3);\n }\n\n .menu-container {\n min-width: 200px;\n padding: 8px 0;\n outline: none;\n }\n\n .menu-divider {\n height: 1px;\n background-color: var(--md-sys-color-outline-variant);\n margin: 8px 0;\n }\n\n /* Menu Item Styles */\n .menu-item {\n position: relative;\n display: flex;\n align-items: center;\n min-height: 48px;\n padding: 0 16px;\n cursor: pointer;\n outline: none;\n transition: background-color 0.2s ease;\n }\n\n .menu-item:hover {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item:focus {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item[disabled] {\n opacity: 0.38;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .menu-item-content {\n display: flex;\n align-items: center;\n width: 100%;\n gap: 12px;\n }\n\n .menu-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 20px;\n }\n\n .menu-item-label {\n flex: 1;\n color: var(--md-sys-color-on-surface);\n font-family: var(--md-sys-typescale-label-large-font-family-name);\n font-size: var(--md-sys-typescale-label-large-font-size);\n font-weight: var(--md-sys-typescale-label-large-font-weight);\n line-height: var(--md-sys-typescale-label-large-line-height);\n letter-spacing: var(--md-sys-typescale-label-large-letter-spacing);\n }\n\n .menu-item-arrow {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 18px;\n font-weight: 500;\n }\n\n .menu-item-with-submenu {\n position: relative;\n }\n\n .menu-item-with-submenu:hover .menu-item-arrow {\n color: var(--md-sys-color-primary);\n }\n\n /* Sub-menu Styles */\n .submenu-container {\n min-width: 200px;\n max-width: 320px;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-level3);\n padding: 8px 0;\n }\n\n /* Submenu positioning with Anchor API */\n ui-sub-menu {\n display: none;\n }\n\n ui-sub-menu:popover-open {\n display: block;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-level3);\n min-width: 200px;\n max-width: 320px;\n padding: 8px 0;\n z-index: 1000;\n }\n\n /* Fallback positioning for browsers without anchor positioning */\n @supports not (anchor-name: --test) {\n ui-sub-menu:popover-open {\n position: fixed;\n transform: translateX(200px);\n }\n }\n\n /* Focus Ring */\n md-focus-ring {\n --md-focus-ring-color: var(--md-sys-color-primary);\n --md-focus-ring-width: 2px;\n }\n\n /* Ripple Effect */\n ui-ripple {\n --md-ripple-color: var(--md-sys-color-primary);\n --md-ripple-opacity: 0.12;\n }\n\n /* Responsive Design */\n @media (max-width: 600px) {\n .menu-container {\n min-width: 180px;\n max-width: 280px;\n }\n\n .submenu-container {\n min-width: 180px;\n max-width: 280px;\n }\n }\n\n /* High Contrast Mode */\n @media (prefers-contrast: high) {\n .menu-container {\n border: 1px solid var(--md-sys-color-outline);\n }\n\n .submenu-container {\n border: 1px solid var(--md-sys-color-outline);\n }\n\n .menu-divider {\n background-color: var(--md-sys-color-outline);\n }\n }\n\n /* Reduced Motion */\n @media (prefers-reduced-motion: reduce) {\n .menu-item {\n transition: none;\n }\n }\n`\n"]}
1
+ {"version":3,"file":"Menu.styles.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsMjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: none;\n position-area: bottom span-right;\n position-try: --menu-fallback-bottom-left, --menu-fallback-top-right, --menu-fallback-top-left, flip-block;\n position: absolute;\n margin: 0;\n padding: 0;\n border: none;\n overflow: hidden;\n /* in most cases the max-height won't matter as this assumes the whole screen to be available, which is rarely the truth. */\n max-height: 90vh;\n overflow: auto;\n }\n\n @position-try --menu-fallback-bottom-left {\n position-area: bottom span-left;\n }\n\n @position-try --menu-fallback-top-right {\n position-area: top span-right;\n }\n\n @position-try --menu-fallback-top-left {\n position-area: top span-left;\n }\n\n :host(:popover-open) {\n display: block;\n background-color: var(--md-sys-color-surface-container-low);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-3);\n }\n\n .menu-container {\n min-width: 200px;\n padding: 8px 0;\n outline: none;\n }\n\n .menu-divider {\n height: 1px;\n background-color: var(--md-sys-color-outline-variant);\n margin: 8px 0;\n }\n\n /* Menu Item Styles */\n .menu-item {\n position: relative;\n display: flex;\n align-items: center;\n min-height: 48px;\n padding: 0 16px;\n cursor: pointer;\n outline: none;\n transition: background-color 0.2s ease;\n }\n\n .menu-item:hover {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item:focus {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item[disabled] {\n opacity: 0.38;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .menu-item-content {\n display: flex;\n align-items: center;\n width: 100%;\n gap: 12px;\n }\n\n .menu-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 20px;\n }\n\n .menu-item-label {\n flex: 1;\n color: var(--md-sys-color-on-surface);\n font-family: var(--md-sys-typescale-label-large-font-family-name);\n font-size: var(--md-sys-typescale-label-large-font-size);\n font-weight: var(--md-sys-typescale-label-large-font-weight);\n line-height: var(--md-sys-typescale-label-large-line-height);\n letter-spacing: var(--md-sys-typescale-label-large-letter-spacing);\n }\n\n .menu-item-arrow {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 18px;\n font-weight: 500;\n }\n\n .menu-item-with-submenu {\n position: relative;\n }\n\n .menu-item-with-submenu:hover .menu-item-arrow {\n color: var(--md-sys-color-primary);\n }\n\n /* Sub-menu Styles */\n .submenu-container {\n min-width: 200px;\n max-width: 320px;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-level3);\n padding: 8px 0;\n }\n\n /* Submenu positioning with Anchor API */\n ui-sub-menu {\n display: none;\n }\n\n ui-sub-menu:popover-open {\n display: block;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-level3);\n min-width: 200px;\n max-width: 320px;\n padding: 8px 0;\n z-index: 1000;\n }\n\n /* Fallback positioning for browsers without anchor positioning */\n @supports not (anchor-name: --test) {\n ui-sub-menu:popover-open {\n position: fixed;\n transform: translateX(200px);\n }\n }\n\n /* Focus Ring */\n md-focus-ring {\n --md-focus-ring-color: var(--md-sys-color-primary);\n --md-focus-ring-width: 2px;\n }\n\n /* Ripple Effect */\n ui-ripple {\n --md-ripple-color: var(--md-sys-color-primary);\n --md-ripple-opacity: 0.12;\n }\n\n /* Responsive Design */\n @media (max-width: 600px) {\n .menu-container {\n min-width: 180px;\n max-width: 280px;\n }\n\n .submenu-container {\n min-width: 180px;\n max-width: 280px;\n }\n }\n\n /* High Contrast Mode */\n @media (prefers-contrast: high) {\n .menu-container {\n border: 1px solid var(--md-sys-color-outline);\n }\n\n .submenu-container {\n border: 1px solid var(--md-sys-color-outline);\n }\n\n .menu-divider {\n background-color: var(--md-sys-color-outline);\n }\n }\n\n /* Reduced Motion */\n @media (prefers-reduced-motion: reduce) {\n .menu-item {\n transition: none;\n }\n }\n`\n"]}
@@ -467,6 +467,45 @@ class ComponentDemoPage extends DemoPage {
467
467
  </ui-menu>
468
468
  ${autoCheckMenuOutput ? html`<p>${autoCheckMenuOutput}</p>` : ''}
469
469
  </section>
470
+
471
+ <section class="demo-section">
472
+ <h2 class="title-large">Edge Positioning Test</h2>
473
+ <p>Test menu positioning at screen edges:</p>
474
+ <div style="display: flex; justify-content: space-between; margin: 20px 0;">
475
+ <ui-button id="left-edge-menu-trigger" color="filled" popovertarget="left-edge-menu">Left Edge</ui-button>
476
+ <ui-button id="right-edge-menu-trigger" color="filled" popovertarget="right-edge-menu">Right Edge</ui-button>
477
+ </div>
478
+
479
+ <ui-menu id="left-edge-menu" popover="auto" @select="${this.handleBasicMenuSelect}">
480
+ <ui-menu-item>
481
+ <span slot="start"><ui-icon>check</ui-icon></span>
482
+ <span>Left Edge Menu Item 1</span>
483
+ </ui-menu-item>
484
+ <ui-menu-item>
485
+ <span slot="start"><ui-icon>check</ui-icon></span>
486
+ <span>Left Edge Menu Item 2</span>
487
+ </ui-menu-item>
488
+ <ui-menu-item>
489
+ <span slot="start"><ui-icon>check</ui-icon></span>
490
+ <span>Left Edge Menu Item 3</span>
491
+ </ui-menu-item>
492
+ </ui-menu>
493
+
494
+ <ui-menu id="right-edge-menu" popover="auto" @select="${this.handleBasicMenuSelect}">
495
+ <ui-menu-item>
496
+ <span slot="start"><ui-icon>check</ui-icon></span>
497
+ <span>Right Edge Menu Item 1</span>
498
+ </ui-menu-item>
499
+ <ui-menu-item>
500
+ <span slot="start"><ui-icon>check</ui-icon></span>
501
+ <span>Right Edge Menu Item 2</span>
502
+ </ui-menu-item>
503
+ <ui-menu-item>
504
+ <span slot="start"><ui-icon>check</ui-icon></span>
505
+ <span>Right Edge Menu Item 3</span>
506
+ </ui-menu-item>
507
+ </ui-menu>
508
+ </section>
470
509
  `
471
510
  }
472
511
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -4,7 +4,7 @@ export default css`
4
4
  :host {
5
5
  display: none;
6
6
  position-area: bottom span-right;
7
- position-try: normal flip-block;
7
+ position-try: --menu-fallback-bottom-left, --menu-fallback-top-right, --menu-fallback-top-left, flip-block;
8
8
  position: absolute;
9
9
  margin: 0;
10
10
  padding: 0;
@@ -15,9 +15,21 @@ export default css`
15
15
  overflow: auto;
16
16
  }
17
17
 
18
+ @position-try --menu-fallback-bottom-left {
19
+ position-area: bottom span-left;
20
+ }
21
+
22
+ @position-try --menu-fallback-top-right {
23
+ position-area: top span-right;
24
+ }
25
+
26
+ @position-try --menu-fallback-top-left {
27
+ position-area: top span-left;
28
+ }
29
+
18
30
  :host(:popover-open) {
19
31
  display: block;
20
- background-color: var(--md-sys-color-surface);
32
+ background-color: var(--md-sys-color-surface-container-low);
21
33
  border-radius: var(--md-sys-shape-corner-extra-small);
22
34
  box-shadow: var(--md-sys-elevation-3);
23
35
  }
@@ -105,41 +105,29 @@ export default class Menu extends UiList {
105
105
  }
106
106
 
107
107
  positionMenu(): void {
108
- // when there's more space above the anchor, position the menu above it
108
+ // Let CSS anchor positioning handle the positioning automatically
109
+ // Only intervene if we need to set max-height for overflow cases
109
110
  const box = this.getBoundingClientRect()
110
- // Now, we determine, whether to position the menu above or below the anchor
111
- // in a way, that if we have enough space below the anchor, we position it below,
112
- // otherwise we position it above the anchor.
113
-
114
- // our starting point is the anchor being positioned below the anchor
115
111
  const menuBottom = box.top + box.height
116
- if (menuBottom <= innerHeight) {
117
- // if the menu fits below the anchor, we leave it as is.
118
- return
119
- }
120
- // we do not make association from the menu to the anchor, so we make an assumption
121
- // that the anchor is 40px high, which is the default height of a button.
122
- // it can be different, but this is a good starting point.
123
- const anchorHeight = 40
124
- const anchorBottom = box.top
125
- const anchorTop = anchorBottom - anchorHeight
126
- const menuHeight = box.height
112
+ const menuRight = box.left + box.width
127
113
 
128
- const spaceBelow = innerHeight - anchorBottom
129
- const spaceAbove = anchorTop
114
+ // Reset any previous manual positioning to let CSS anchor positioning work
115
+ this.style.removeProperty('position-area')
116
+ this.style.removeProperty('max-height')
117
+
118
+ // Only set max-height if the menu would overflow
119
+ if (menuBottom > innerHeight) {
120
+ const availableHeight = innerHeight - box.top
121
+ this.style.maxHeight = `${Math.max(200, availableHeight - 20)}px`
122
+ }
130
123
 
131
- const diffBelow = spaceBelow - menuHeight
132
- const diffAbove = spaceAbove - menuHeight
133
- // The initial check ensures the menu does not fit below. Now, check if it fits above.
134
- if (diffAbove >= 0) {
135
- this.style.setProperty('position-area', 'top span-right')
136
- } else if (diffAbove > diffBelow) {
137
- // It doesn't fit in either direction. Choose the one with less overflow (larger, i.e., less negative, diff).
138
- this.style.setProperty('position-area', 'top span-right')
139
- this.style.maxHeight = `${spaceAbove}px`
140
- } else {
141
- this.style.setProperty('position-area', 'bottom span-right')
142
- this.style.maxHeight = `${spaceBelow}px`
124
+ if (menuRight > innerWidth) {
125
+ const availableWidth = innerWidth - box.left
126
+ // Let CSS anchor positioning handle horizontal flipping
127
+ // We could set max-width if needed
128
+ if (availableWidth < 200) {
129
+ this.style.maxWidth = `${Math.max(180, availableWidth - 20)}px`
130
+ }
143
131
  }
144
132
  }
145
133