@nldd/design-system 0.8.42 → 0.8.44
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.
- package/README.md +1 -1
- package/dist/components/actions/button/button.d.ts +36 -5
- package/dist/components/actions/button/button.d.ts.map +1 -1
- package/dist/components/actions/button/button.js +62 -6
- package/dist/components/actions/button/button.js.map +1 -1
- package/dist/components/actions/button/button.styles.d.ts.map +1 -1
- package/dist/components/actions/button/button.styles.js +149 -63
- package/dist/components/actions/button/button.styles.js.map +1 -1
- package/dist/components/actions/button/button.template.d.ts.map +1 -1
- package/dist/components/actions/button/button.template.js +19 -1
- package/dist/components/actions/button/button.template.js.map +1 -1
- package/dist/components/actions/button-bar/button-bar.styles.js +4 -4
- package/dist/components/actions/button-group/button-group.js +2 -2
- package/dist/components/actions/button-group/button-group.js.map +1 -1
- package/dist/components/actions/button-group/button-group.styles.js +1 -1
- package/dist/components/actions/icon-button/icon-button.d.ts +43 -11
- package/dist/components/actions/icon-button/icon-button.d.ts.map +1 -1
- package/dist/components/actions/icon-button/icon-button.js +68 -14
- package/dist/components/actions/icon-button/icon-button.js.map +1 -1
- package/dist/components/actions/icon-button/icon-button.styles.d.ts.map +1 -1
- package/dist/components/actions/icon-button/icon-button.styles.js +163 -106
- package/dist/components/actions/icon-button/icon-button.styles.js.map +1 -1
- package/dist/components/actions/icon-button/icon-button.template.d.ts.map +1 -1
- package/dist/components/actions/icon-button/icon-button.template.js +20 -2
- package/dist/components/actions/icon-button/icon-button.template.js.map +1 -1
- package/dist/components/actions/split-button/split-button.d.ts +5 -5
- package/dist/components/actions/split-button/split-button.d.ts.map +1 -1
- package/dist/components/actions/split-button/split-button.js +19 -15
- package/dist/components/actions/split-button/split-button.js.map +1 -1
- package/dist/components/actions/split-button/split-button.styles.js +1 -1
- package/dist/components/actions/split-button/split-button.template.d.ts.map +1 -1
- package/dist/components/actions/split-button/split-button.template.js +14 -12
- package/dist/components/actions/split-button/split-button.template.js.map +1 -1
- package/dist/components/actions/toolbar/toolbar.d.ts +4 -6
- package/dist/components/actions/toolbar/toolbar.d.ts.map +1 -1
- package/dist/components/actions/toolbar/toolbar.js +31 -28
- package/dist/components/actions/toolbar/toolbar.js.map +1 -1
- package/dist/components/actions/toolbar/toolbar.styles.js +17 -17
- package/dist/components/actions/toolbar/toolbar.template.d.ts +3 -3
- package/dist/components/actions/toolbar/toolbar.template.d.ts.map +1 -1
- package/dist/components/actions/toolbar/toolbar.template.js +17 -9
- package/dist/components/actions/toolbar/toolbar.template.js.map +1 -1
- package/dist/components/components.js +1 -1
- package/dist/components/content/blockquote/blockquote.styles.js +6 -6
- package/dist/components/content/blockquote/blockquote.styles.js.map +1 -1
- package/dist/components/content/code/code.styles.js +3 -3
- package/dist/components/content/icon/icon-aliases.d.ts.map +1 -1
- package/dist/components/content/icon/icon-aliases.js +15 -0
- package/dist/components/content/icon/icon-aliases.js.map +1 -1
- package/dist/components/content/icon/icon-registry.d.ts.map +1 -1
- package/dist/components/content/icon/icon-registry.js +11 -5
- package/dist/components/content/icon/icon-registry.js.map +1 -1
- package/dist/components/content/icon/icon.styles.js +1 -1
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.d.ts +22 -0
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.d.ts.map +1 -1
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.js +46 -2
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.js.map +1 -1
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.styles.js +5 -5
- package/dist/components/content/rich-text/rich-text.d.ts +3 -1
- package/dist/components/content/rich-text/rich-text.d.ts.map +1 -1
- package/dist/components/content/rich-text/rich-text.js +6 -1
- package/dist/components/content/rich-text/rich-text.js.map +1 -1
- package/dist/components/content/tag/tag.d.ts +3 -3
- package/dist/components/content/tag/tag.d.ts.map +1 -1
- package/dist/components/content/tag/tag.js +3 -3
- package/dist/components/content/tag/tag.js.map +1 -1
- package/dist/components/content/tag/tag.styles.d.ts.map +1 -1
- package/dist/components/content/tag/tag.styles.js +113 -15
- package/dist/components/content/tag/tag.styles.js.map +1 -1
- package/dist/components/content/title/title.styles.js +20 -20
- package/dist/components/content/title/title.styles.js.map +1 -1
- package/dist/components/content/tooltip/tooltip.d.ts +13 -7
- package/dist/components/content/tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/content/tooltip/tooltip.js +34 -22
- package/dist/components/content/tooltip/tooltip.js.map +1 -1
- package/dist/components/content/tooltip/tooltip.styles.js +8 -8
- package/dist/components/forms/form-actions/form-actions.styles.js +2 -2
- package/dist/components/forms/form-field/form-field.styles.js +6 -6
- package/dist/components/{index-CJ3mdHHo.js → index-1v33GjGX.js} +4134 -3286
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/inputs/checkbox/checkbox.styles.js +9 -9
- package/dist/components/inputs/checkbox-field/checkbox-field.styles.js +4 -4
- package/dist/components/inputs/code-editor/code-editor.styles.js +14 -14
- package/dist/components/inputs/combo-box/combo-box.d.ts +1 -1
- package/dist/components/inputs/combo-box/combo-box.d.ts.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.js +8 -2
- package/dist/components/inputs/combo-box/combo-box.js.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.styles.d.ts.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.styles.js +17 -14
- package/dist/components/inputs/combo-box/combo-box.styles.js.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.template.d.ts.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.template.js +4 -2
- package/dist/components/inputs/combo-box/combo-box.template.js.map +1 -1
- package/dist/components/inputs/dropdown/dropdown.d.ts +13 -1
- package/dist/components/inputs/dropdown/dropdown.d.ts.map +1 -1
- package/dist/components/inputs/dropdown/dropdown.js +33 -2
- package/dist/components/inputs/dropdown/dropdown.js.map +1 -1
- package/dist/components/inputs/dropdown/dropdown.styles.d.ts.map +1 -1
- package/dist/components/inputs/dropdown/dropdown.styles.js +24 -16
- package/dist/components/inputs/dropdown/dropdown.styles.js.map +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.d.ts +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.d.ts.map +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.js +8 -2
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.js.map +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.js +14 -11
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.js.map +1 -1
- package/dist/components/inputs/number-field/number-field.d.ts +2 -4
- package/dist/components/inputs/number-field/number-field.d.ts.map +1 -1
- package/dist/components/inputs/number-field/number-field.js +8 -12
- package/dist/components/inputs/number-field/number-field.js.map +1 -1
- package/dist/components/inputs/number-field/number-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/number-field/number-field.styles.js +19 -18
- package/dist/components/inputs/number-field/number-field.styles.js.map +1 -1
- package/dist/components/inputs/number-field/number-field.template.js +2 -2
- package/dist/components/inputs/password-field/password-field.d.ts +9 -9
- package/dist/components/inputs/password-field/password-field.d.ts.map +1 -1
- package/dist/components/inputs/password-field/password-field.js +24 -18
- package/dist/components/inputs/password-field/password-field.js.map +1 -1
- package/dist/components/inputs/password-field/password-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/password-field/password-field.styles.js +20 -17
- package/dist/components/inputs/password-field/password-field.styles.js.map +1 -1
- package/dist/components/inputs/password-field/password-field.template.js +3 -3
- package/dist/components/inputs/password-field/password-field.template.js.map +1 -1
- package/dist/components/inputs/radio-button/radio-button.styles.js +11 -11
- package/dist/components/inputs/radio-button-field/radio-button-field.styles.js +4 -4
- package/dist/components/inputs/search-field/search-field.d.ts +1 -1
- package/dist/components/inputs/search-field/search-field.d.ts.map +1 -1
- package/dist/components/inputs/search-field/search-field.js +8 -2
- package/dist/components/inputs/search-field/search-field.js.map +1 -1
- package/dist/components/inputs/search-field/search-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/search-field/search-field.styles.js +19 -16
- package/dist/components/inputs/search-field/search-field.styles.js.map +1 -1
- package/dist/components/inputs/segmented-control/segmented-control.d.ts +8 -2
- package/dist/components/inputs/segmented-control/segmented-control.d.ts.map +1 -1
- package/dist/components/inputs/segmented-control/segmented-control.js +25 -4
- package/dist/components/inputs/segmented-control/segmented-control.js.map +1 -1
- package/dist/components/inputs/segmented-control/segmented-control.styles.d.ts.map +1 -1
- package/dist/components/inputs/segmented-control/segmented-control.styles.js +78 -107
- package/dist/components/inputs/segmented-control/segmented-control.styles.js.map +1 -1
- package/dist/components/inputs/segmented-control/segmented-control.template.js +1 -1
- package/dist/components/inputs/stepper/stepper.styles.js +2 -2
- package/dist/components/inputs/stepper/stepper.template.js +2 -2
- package/dist/components/inputs/switch/switch.styles.js +13 -13
- package/dist/components/inputs/switch-field/switch-field.styles.js +4 -4
- package/dist/components/inputs/text-field/text-field.d.ts +1 -1
- package/dist/components/inputs/text-field/text-field.d.ts.map +1 -1
- package/dist/components/inputs/text-field/text-field.js +8 -2
- package/dist/components/inputs/text-field/text-field.js.map +1 -1
- package/dist/components/inputs/text-field/text-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/text-field/text-field.styles.js +19 -16
- package/dist/components/inputs/text-field/text-field.styles.js.map +1 -1
- package/dist/components/inputs/toggle-button/toggle-button.styles.d.ts.map +1 -1
- package/dist/components/inputs/toggle-button/toggle-button.styles.js +38 -18
- package/dist/components/inputs/toggle-button/toggle-button.styles.js.map +1 -1
- package/dist/components/inputs/token/token.d.ts +3 -3
- package/dist/components/inputs/token/token.d.ts.map +1 -1
- package/dist/components/inputs/token/token.js +6 -6
- package/dist/components/inputs/token/token.js.map +1 -1
- package/dist/components/inputs/token/token.styles.js +7 -7
- package/dist/components/inputs/token/token.template.js +2 -2
- package/dist/components/inputs/token/token.template.js.map +1 -1
- package/dist/components/layout/app-view/app-view.styles.js +4 -4
- package/dist/components/layout/box/box.styles.js +2 -2
- package/dist/components/layout/card/card.js +1 -1
- package/dist/components/layout/card/card.js.map +1 -1
- package/dist/components/layout/card/card.styles.js +4 -4
- package/dist/components/layout/collection/collection.styles.js +6 -6
- package/dist/components/layout/container/container.d.ts +4 -4
- package/dist/components/layout/container/container.js +4 -4
- package/dist/components/layout/container/container.styles.js +399 -399
- package/dist/components/layout/container/container.styles.js.map +1 -1
- package/dist/components/layout/divider/divider.styles.js +2 -2
- package/dist/components/layout/page/page.js +1 -1
- package/dist/components/layout/page/page.js.map +1 -1
- package/dist/components/layout/page/page.styles.js +9 -9
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.d.ts +6 -3
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.d.ts.map +1 -1
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.js +21 -5
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.js.map +1 -1
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.styles.d.ts.map +1 -1
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.styles.js +24 -23
- package/dist/components/layout/page-sections/full-bleed-section/full-bleed-section.styles.js.map +1 -1
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.d.ts +6 -3
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.d.ts.map +1 -1
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.js +21 -5
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.js.map +1 -1
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.styles.js +18 -18
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.styles.js.map +1 -1
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.d.ts +6 -3
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.d.ts.map +1 -1
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.js +21 -5
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.js.map +1 -1
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.styles.js +18 -18
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.styles.js.map +1 -1
- package/dist/components/layout/page-sections/simple-section/simple-section.d.ts +6 -3
- package/dist/components/layout/page-sections/simple-section/simple-section.d.ts.map +1 -1
- package/dist/components/layout/page-sections/simple-section/simple-section.js +21 -5
- package/dist/components/layout/page-sections/simple-section/simple-section.js.map +1 -1
- package/dist/components/layout/page-sections/simple-section/simple-section.styles.js +23 -23
- package/dist/components/layout/page-sections/simple-section/simple-section.styles.js.map +1 -1
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.d.ts +6 -3
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.d.ts.map +1 -1
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.js +21 -5
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.js.map +1 -1
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.styles.js +18 -18
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.styles.js.map +1 -1
- package/dist/components/layout/popover/popover.js +1 -1
- package/dist/components/layout/popover/popover.js.map +1 -1
- package/dist/components/layout/popover/popover.styles.js +4 -4
- package/dist/components/layout/sheet/sheet.d.ts +17 -4
- package/dist/components/layout/sheet/sheet.d.ts.map +1 -1
- package/dist/components/layout/sheet/sheet.js +43 -9
- package/dist/components/layout/sheet/sheet.js.map +1 -1
- package/dist/components/layout/sheet/sheet.styles.d.ts.map +1 -1
- package/dist/components/layout/sheet/sheet.styles.js +33 -40
- package/dist/components/layout/sheet/sheet.styles.js.map +1 -1
- package/dist/components/layout/spacer/spacer.styles.js +5 -5
- package/dist/components/layout/split-views/bar-split-view/bar-split-view.styles.js +9 -9
- package/dist/components/layout/split-views/navigation-split-view/navigation-split-view.styles.js +34 -34
- package/dist/components/layout/split-views/side-by-side-split-view/side-by-side-split-view.styles.js +6 -6
- package/dist/components/layout/split-views/split-view-divider/split-view-divider.styles.js +4 -4
- package/dist/components/layout/split-views/split-view-pane/split-view-pane.js +1 -1
- package/dist/components/layout/split-views/split-view-pane/split-view-pane.js.map +1 -1
- package/dist/components/layout/split-views/split-view-pane/split-view-pane.styles.js +3 -3
- package/dist/components/layout/split-views/stacked-split-view/stacked-split-view.styles.js +7 -7
- package/dist/components/layout/window/window.js +1 -1
- package/dist/components/layout/window/window.js.map +1 -1
- package/dist/components/layout/window/window.styles.js +8 -8
- package/dist/components/lists-and-menus/cells/cell/cell.d.ts +4 -2
- package/dist/components/lists-and-menus/cells/cell/cell.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/cell/cell.js +17 -6
- package/dist/components/lists-and-menus/cells/cell/cell.js.map +1 -1
- package/dist/components/lists-and-menus/cells/cell/cell.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/cell/cell.styles.js +26 -15
- package/dist/components/lists-and-menus/cells/cell/cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.d.ts +2 -2
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.js +12 -7
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.js.map +1 -1
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.styles.js +9 -8
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/cells/drag-handle-cell/drag-handle-cell.styles.js +9 -9
- package/dist/components/lists-and-menus/cells/icon-cell/icon-cell.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/icon-cell/icon-cell.js +1 -1
- package/dist/components/lists-and-menus/cells/icon-cell/icon-cell.js.map +1 -1
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.js +1 -1
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.js.map +1 -1
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.styles.js +1 -1
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.d.ts +3 -4
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.js +13 -9
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.js.map +1 -1
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.styles.js +26 -9
- package/dist/components/lists-and-menus/cells/text-cell/text-cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.d.ts +3 -4
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.js +12 -8
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.js.map +1 -1
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.styles.js +26 -9
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/list/list.d.ts.map +1 -1
- package/dist/components/lists-and-menus/list/list.i18n.js +1 -1
- package/dist/components/lists-and-menus/list/list.i18n.js.map +1 -1
- package/dist/components/lists-and-menus/list/list.js +7 -0
- package/dist/components/lists-and-menus/list/list.js.map +1 -1
- package/dist/components/lists-and-menus/list/list.styles.js +13 -13
- package/dist/components/lists-and-menus/list-item/list-item.styles.js +21 -21
- package/dist/components/lists-and-menus/menu/menu.d.ts +160 -29
- package/dist/components/lists-and-menus/menu/menu.d.ts.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.i18n.d.ts +2 -0
- package/dist/components/lists-and-menus/menu/menu.i18n.d.ts.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.i18n.js +2 -0
- package/dist/components/lists-and-menus/menu/menu.i18n.js.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.js +534 -102
- package/dist/components/lists-and-menus/menu/menu.js.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.styles.js +93 -32
- package/dist/components/lists-and-menus/menu/menu.styles.js.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.template.d.ts.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.template.js +13 -0
- package/dist/components/lists-and-menus/menu/menu.template.js.map +1 -1
- package/dist/components/navigation/document-tab-bar/document-tab-bar.d.ts +0 -2
- package/dist/components/navigation/document-tab-bar/document-tab-bar.d.ts.map +1 -1
- package/dist/components/navigation/document-tab-bar/document-tab-bar.js +20 -22
- package/dist/components/navigation/document-tab-bar/document-tab-bar.js.map +1 -1
- package/dist/components/navigation/document-tab-bar/document-tab-bar.styles.js +48 -48
- package/dist/components/navigation/document-tab-bar/document-tab-bar.template.d.ts.map +1 -1
- package/dist/components/navigation/document-tab-bar/document-tab-bar.template.js +12 -10
- package/dist/components/navigation/document-tab-bar/document-tab-bar.template.js.map +1 -1
- package/dist/components/navigation/link/link.d.ts.map +1 -0
- package/dist/components/navigation/link/link.js.map +1 -0
- package/dist/components/navigation/link/link.styles.d.ts.map +1 -0
- package/dist/components/{actions → navigation}/link/link.styles.js +4 -4
- package/dist/components/navigation/link/link.styles.js.map +1 -0
- package/dist/components/navigation/link/link.template.d.ts.map +1 -0
- package/dist/components/navigation/link/link.template.js.map +1 -0
- package/dist/components/navigation/menu-bar/menu-bar.d.ts +17 -5
- package/dist/components/navigation/menu-bar/menu-bar.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.js +80 -35
- package/dist/components/navigation/menu-bar/menu-bar.js.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.styles.js +2 -2
- package/dist/components/navigation/menu-bar/menu-bar.template.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.template.js +12 -2
- package/dist/components/navigation/menu-bar/menu-bar.template.js.map +1 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.d.ts +17 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.js +28 -6
- package/dist/components/navigation/menu-bar-item/menu-bar-item.js.map +1 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.styles.js +21 -21
- package/dist/components/navigation/menu-bar-item/menu-bar-item.template.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.template.js +3 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.template.js.map +1 -1
- package/dist/components/navigation/pagination/pagination.d.ts +15 -2
- package/dist/components/navigation/pagination/pagination.d.ts.map +1 -1
- package/dist/components/navigation/pagination/pagination.js +24 -4
- package/dist/components/navigation/pagination/pagination.js.map +1 -1
- package/dist/components/navigation/pagination/pagination.styles.d.ts.map +1 -1
- package/dist/components/navigation/pagination/pagination.styles.js +48 -76
- package/dist/components/navigation/pagination/pagination.styles.js.map +1 -1
- package/dist/components/navigation/pagination/pagination.template.d.ts.map +1 -1
- package/dist/components/navigation/pagination/pagination.template.js +23 -16
- package/dist/components/navigation/pagination/pagination.template.js.map +1 -1
- package/dist/components/navigation/skip-link/skip-link.styles.js +9 -9
- package/dist/components/navigation/tab-bar/tab-bar.d.ts +3 -1
- package/dist/components/navigation/tab-bar/tab-bar.d.ts.map +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.js +5 -3
- package/dist/components/navigation/tab-bar/tab-bar.js.map +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.styles.d.ts.map +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.styles.js +55 -58
- package/dist/components/navigation/tab-bar/tab-bar.styles.js.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.js +2 -2
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.js.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.d.ts.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js +57 -70
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js.map +1 -1
- package/dist/components/navigation/top-title-bar/top-title-bar.styles.d.ts.map +1 -1
- package/dist/components/navigation/top-title-bar/top-title-bar.styles.js +16 -14
- package/dist/components/navigation/top-title-bar/top-title-bar.styles.js.map +1 -1
- package/dist/components/navigation/top-title-bar/top-title-bar.template.d.ts.map +1 -1
- package/dist/components/navigation/top-title-bar/top-title-bar.template.js +1 -0
- package/dist/components/navigation/top-title-bar/top-title-bar.template.js.map +1 -1
- package/dist/components/{prism-typescript-Cdpexfvg.js → prism-typescript-D2EqsCqV.js} +1 -1
- package/dist/components/status-and-feedback/badge/badge.d.ts +3 -3
- package/dist/components/status-and-feedback/badge/badge.d.ts.map +1 -1
- package/dist/components/status-and-feedback/badge/badge.js +3 -3
- package/dist/components/status-and-feedback/badge/badge.js.map +1 -1
- package/dist/components/status-and-feedback/badge/badge.styles.js +13 -13
- package/dist/components/status-and-feedback/inline-dialog/inline-dialog.styles.js +7 -7
- package/dist/components/status-and-feedback/modal-dialog/modal-dialog.styles.js +5 -5
- package/dist/css/global.css +1 -0
- package/dist/css/reset.css +7 -0
- package/dist/css/rich-text.css +106 -79
- package/dist/css/settings.css +121 -89
- package/dist/utilities/os.d.ts +27 -0
- package/dist/utilities/os.d.ts.map +1 -0
- package/dist/utilities/os.js +60 -0
- package/dist/utilities/os.js.map +1 -0
- package/dist/utilities/visibility-mixin.d.ts +18 -12
- package/dist/utilities/visibility-mixin.d.ts.map +1 -1
- package/dist/utilities/visibility-mixin.js +21 -14
- package/dist/utilities/visibility-mixin.js.map +1 -1
- package/package.json +6 -6
- package/dist/components/actions/link/link.d.ts.map +0 -1
- package/dist/components/actions/link/link.js.map +0 -1
- package/dist/components/actions/link/link.styles.d.ts.map +0 -1
- package/dist/components/actions/link/link.styles.js.map +0 -1
- package/dist/components/actions/link/link.template.d.ts.map +0 -1
- package/dist/components/actions/link/link.template.js.map +0 -1
- /package/dist/components/{actions → navigation}/link/link.d.ts +0 -0
- /package/dist/components/{actions → navigation}/link/link.js +0 -0
- /package/dist/components/{actions → navigation}/link/link.styles.d.ts +0 -0
- /package/dist/components/{actions → navigation}/link/link.template.d.ts +0 -0
- /package/dist/components/{actions → navigation}/link/link.template.js +0 -0
|
@@ -15,7 +15,7 @@ import '../../lists-and-menus/cells/spacer-cell/spacer-cell.js';
|
|
|
15
15
|
import '../../lists-and-menus/cells/text-cell/text-cell.js';
|
|
16
16
|
import '../../content/icon/icon.js';
|
|
17
17
|
import '../../status-and-feedback/inline-dialog/inline-dialog.js';
|
|
18
|
-
import { isKeyboardMode } from '../../../utilities/input-modality.js';
|
|
18
|
+
import { isKeyboardMode, isTouchMode } from '../../../utilities/input-modality.js';
|
|
19
19
|
import { POPOVER_REOPEN_GUARD_MS } from '../../../utilities/popover-guard.js';
|
|
20
20
|
import { breakpoints } from '../../../assets/styles/breakpoints.js';
|
|
21
21
|
// # nldd-menu-divider
|
|
@@ -183,12 +183,35 @@ export class NLDDMenuItem extends LitElement {
|
|
|
183
183
|
this._submenuMutationObserver?.disconnect();
|
|
184
184
|
this._submenuMutationObserver = null;
|
|
185
185
|
}
|
|
186
|
-
_handleClick() {
|
|
186
|
+
_handleClick(event) {
|
|
187
187
|
if (this.disabled)
|
|
188
188
|
return;
|
|
189
189
|
// Submenu items don't fire `select` — they open their submenu instead.
|
|
190
190
|
// Item is either an action OR a submenu opener, not both.
|
|
191
191
|
if (this._hasSubmenu) {
|
|
192
|
+
// The button is wired to the submenu via the popoverTargetElement
|
|
193
|
+
// IDL property (set in the template). That marks it as the
|
|
194
|
+
// submenu's invoker so the browser excludes the opener from
|
|
195
|
+
// popover light-dismiss — clicking the opener no longer closes
|
|
196
|
+
// the submenu underneath it. The flip side: the browser would
|
|
197
|
+
// also auto-show the popover with its default positioning,
|
|
198
|
+
// which races our anchor + placement set in `_handleSubmenuOpen`.
|
|
199
|
+
// preventDefault stops the browser action and lets our flow
|
|
200
|
+
// own positioning.
|
|
201
|
+
//
|
|
202
|
+
// We use the IDL property rather than the `popovertarget`
|
|
203
|
+
// attribute because the button lives in this menu-item's shadow
|
|
204
|
+
// root while the submenu is a light-DOM child — attribute-based
|
|
205
|
+
// ID resolution is tree-scoped and would silently fail to find
|
|
206
|
+
// the cross-tree target. The IDL property takes a direct element
|
|
207
|
+
// reference and works across shadow boundaries.
|
|
208
|
+
event?.preventDefault();
|
|
209
|
+
// Click on an already-open opener: keep the submenu visible and
|
|
210
|
+
// the opener visually "active" (aria-expanded + :hover). A second
|
|
211
|
+
// click should not toggle it closed — that conflicts with the
|
|
212
|
+
// hover-open semantics of cascade mode.
|
|
213
|
+
if (this._submenuOpen)
|
|
214
|
+
return;
|
|
192
215
|
this.dispatchEvent(new CustomEvent('submenu-open', {
|
|
193
216
|
detail: { submenu: this._submenuEl, item: this },
|
|
194
217
|
bubbles: true,
|
|
@@ -331,6 +354,30 @@ export class NLDDMenu extends LitElement {
|
|
|
331
354
|
* by one of this menu's items). null when no submenu is open. */
|
|
332
355
|
this._activeSubmenu = null;
|
|
333
356
|
this._activeSubmenuOpener = null;
|
|
357
|
+
/** Cleanup closure for the currently open child submenu. Invoked by the
|
|
358
|
+
* submenu's `toggle→closed` event, but also callable directly from a
|
|
359
|
+
* chain-close walk so the upper levels can be torn down without waiting
|
|
360
|
+
* for stale toggle events that will never fire (e.g. a hidden drill-in
|
|
361
|
+
* parent whose popover state is already 'closed'). */
|
|
362
|
+
this._activeSubmenuCleanup = null;
|
|
363
|
+
/** Signal that this menu is hiding itself to make room for a deeper drill-in
|
|
364
|
+
* level — its parent's onToggle should skip the normal close cleanup
|
|
365
|
+
* (re-parent + re-show) so the chain stays in deeper-level state. */
|
|
366
|
+
this._drillInHidingForDeeper = false;
|
|
367
|
+
/** Root-owned registry of every submenu currently open in this chain,
|
|
368
|
+
* in open order. Lives on the root menu only; submenus reach it via
|
|
369
|
+
* `_chainRoot`. Drives `_collapseChain()` so collapsing the whole
|
|
370
|
+
* stack never depends on `_activeSubmenu`/`_parentMenu` threading
|
|
371
|
+
* (which real interaction can leave stale). @internal */
|
|
372
|
+
this._openChain = [];
|
|
373
|
+
/** Back-reference to the chain's root, set on every submenu when it
|
|
374
|
+
* opens. Lets `_collapseChain()` find the registry without walking
|
|
375
|
+
* the (possibly stale) parent links. null on the root. @internal */
|
|
376
|
+
this._chainRoot = null;
|
|
377
|
+
/** Force-teardown for this submenu (detach toggle listener, hide,
|
|
378
|
+
* restore DOM, reset state — no parent re-show). Set when the submenu
|
|
379
|
+
* opens; called by `_collapseChain()`. @internal */
|
|
380
|
+
this._collapseSelf = null;
|
|
334
381
|
/** When this menu is itself a submenu, points to the parent menu that
|
|
335
382
|
* opened it. Set by the parent's _handleSubmenuOpen. null on the root.
|
|
336
383
|
* @internal */
|
|
@@ -341,17 +388,60 @@ export class NLDDMenu extends LitElement {
|
|
|
341
388
|
this._parentItem = null;
|
|
342
389
|
this._isOpen = false;
|
|
343
390
|
this._closedAt = 0;
|
|
391
|
+
/** Set on the root when a pointer gesture (outside/anchor pointerdown
|
|
392
|
+
* or confirmed tap) collapsed the chain. The trailing `click` of that
|
|
393
|
+
* same gesture consumes it in `_handleDocumentClick` and must NOT
|
|
394
|
+
* reopen — gesture-correlated, so it's reliable on touch where the
|
|
395
|
+
* pointerup→click gap exceeds the time-based reopen guard. @internal */
|
|
396
|
+
this._collapsedByPointerGesture = false;
|
|
344
397
|
this._cleanupAutoUpdate = null;
|
|
398
|
+
// — Status messages (WCAG 4.1.3) ———————————————————————————————————————————
|
|
399
|
+
//
|
|
400
|
+
// Drill-in mode swaps the whole view (parent hides, submenu shows, or
|
|
401
|
+
// vice versa). Focus moves to the new view's first item / opener, so AT
|
|
402
|
+
// announces *an* element — but not that the view itself changed. The
|
|
403
|
+
// polite live region in the shadow DOM (see menu.template) carries that
|
|
404
|
+
// explicit status. Cascade mode keeps both views visible: nothing is
|
|
405
|
+
// swapped, so it never announces.
|
|
406
|
+
/** Double-rAF handle for the announcement awaiting flush, so a rapid
|
|
407
|
+
* follow-up transition can cancel the stale one instead of stacking it. */
|
|
408
|
+
this._announceRaf = 0;
|
|
409
|
+
/** Message currently queued (not yet flushed to the region). Used to
|
|
410
|
+
* drop a doubled event in the same tick while still allowing the same
|
|
411
|
+
* text to be re-announced on a later, genuine transition. */
|
|
412
|
+
this._pendingAnnouncement = '';
|
|
345
413
|
// — Event handlers ————————————————————————————————————————————————————————
|
|
346
414
|
this._handleDocumentClick = (event) => {
|
|
347
415
|
if (this.anchorElement)
|
|
348
416
|
return;
|
|
417
|
+
// Consume the same-gesture collapse marker (set when an outside or
|
|
418
|
+
// anchor pointer gesture collapsed the chain). Read+clear for every
|
|
419
|
+
// click so it never leaks past a single gesture.
|
|
420
|
+
const collapsedByGesture = this._collapsedByPointerGesture;
|
|
421
|
+
this._collapsedByPointerGesture = false;
|
|
349
422
|
const anchorEl = this._getAnchorEl();
|
|
350
423
|
if (!anchorEl)
|
|
351
424
|
return;
|
|
352
425
|
const path = event.composedPath();
|
|
353
426
|
if (!path.includes(anchorEl))
|
|
354
427
|
return;
|
|
428
|
+
// This click is the tail of the very gesture that just collapsed the
|
|
429
|
+
// chain (e.g. tapping the anchor from deep in a drill-in submenu:
|
|
430
|
+
// the submenu's pointerdown/tap already collapsed). It must not
|
|
431
|
+
// reopen. Gesture-correlated, so reliable even when the
|
|
432
|
+
// pointerup→click gap exceeds the time-based reopen guard on touch.
|
|
433
|
+
if (collapsedByGesture)
|
|
434
|
+
return;
|
|
435
|
+
// Drill-in: the root is hidden while a submenu shows, so `_isOpen`
|
|
436
|
+
// is false on the root even though the menu is visibly open. A
|
|
437
|
+
// plain toggle would `showPopover()` the root — i.e. bounce back
|
|
438
|
+
// to the main level. The anchor is a toggle: if anything in the
|
|
439
|
+
// chain is open, clicking it collapses the whole stack (registry-
|
|
440
|
+
// driven, so it works even if chain links went stale).
|
|
441
|
+
if (this._drillInMode && (this._isOpen || this._openChain.length > 0)) {
|
|
442
|
+
this._collapseChain();
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
355
445
|
if (this._isOpen) {
|
|
356
446
|
this.hidePopover();
|
|
357
447
|
}
|
|
@@ -369,10 +459,13 @@ export class NLDDMenu extends LitElement {
|
|
|
369
459
|
// right edge if floating-ui flipped it to the other side). The wedge
|
|
370
460
|
// stays fixed until the cursor enters the submenu, so the user has a
|
|
371
461
|
// predictable, stable area to traverse without losing the submenu.
|
|
372
|
-
// While the cursor is inside the triangle, peer items don't activate
|
|
373
|
-
//
|
|
462
|
+
// While the cursor is inside the triangle, peer items don't activate.
|
|
463
|
+
//
|
|
464
|
+
// We deliberately do NOT auto-close on hover-out: moving the cursor to
|
|
465
|
+
// empty space outside every menu leaves the chain open. The native
|
|
466
|
+
// popover light-dismiss takes care of click-outside, which is the only
|
|
467
|
+
// gesture that should collapse the chain.
|
|
374
468
|
this._hoverOpenTimer = null;
|
|
375
|
-
this._hoverCloseTimer = null;
|
|
376
469
|
this._safeTriangleListener = null;
|
|
377
470
|
this._safeTriangleStallTimer = null;
|
|
378
471
|
this._lastCursorPos = null;
|
|
@@ -380,6 +473,10 @@ export class NLDDMenu extends LitElement {
|
|
|
380
473
|
this._safeTriangleSubmenu = null;
|
|
381
474
|
this._movingTowardSubmenu = false;
|
|
382
475
|
this._handleMenuItemMouseenter = (event) => {
|
|
476
|
+
// Touch suppress: synthesised mouseenter from touch (e.g. lifting
|
|
477
|
+
// after a scroll gesture on a long menu) shouldn't paint a highlight.
|
|
478
|
+
if (isTouchMode())
|
|
479
|
+
return;
|
|
383
480
|
const item = event.target.closest('nldd-menu-item');
|
|
384
481
|
if (!item)
|
|
385
482
|
return;
|
|
@@ -389,8 +486,9 @@ export class NLDDMenu extends LitElement {
|
|
|
389
486
|
if (this.variant !== 'listbox')
|
|
390
487
|
this._clearHighlight();
|
|
391
488
|
this._cancelHoverOpen();
|
|
392
|
-
//
|
|
393
|
-
// outside
|
|
489
|
+
// No auto-close on hover-out: leaving the menu rect leaves the chain
|
|
490
|
+
// open. Click-outside (popover light-dismiss) is the only gesture
|
|
491
|
+
// that collapses the chain.
|
|
394
492
|
};
|
|
395
493
|
// — Debug overlay ————————————————————————————————————————————————————
|
|
396
494
|
this._safeTriangleOverlay = null;
|
|
@@ -433,6 +531,13 @@ export class NLDDMenu extends LitElement {
|
|
|
433
531
|
}
|
|
434
532
|
submenu._parentMenu = this;
|
|
435
533
|
submenu._parentItem = item;
|
|
534
|
+
// Register this submenu in the root's open-chain so `_collapseChain()`
|
|
535
|
+
// can tear the whole stack down without relying on parent links being
|
|
536
|
+
// intact at collapse time. Captured here, while links are fresh.
|
|
537
|
+
const chainRoot = this._rootMenu;
|
|
538
|
+
submenu._chainRoot = chainRoot;
|
|
539
|
+
if (!chainRoot._openChain.includes(submenu))
|
|
540
|
+
chainRoot._openChain.push(submenu);
|
|
436
541
|
if (this._drillInMode) {
|
|
437
542
|
// Drill-in: anchor to the root's anchor so all submenus open at the
|
|
438
543
|
// same screen position — visually stacks. Inherit root placement
|
|
@@ -456,18 +561,58 @@ export class NLDDMenu extends LitElement {
|
|
|
456
561
|
item.removeAttribute('highlighted');
|
|
457
562
|
// Listen once for the submenu's close so we can clear state, ARIA and
|
|
458
563
|
// any leftover safe-triangle plumbing from the cascade open path.
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
564
|
+
const wasDrillIn = this._drillInMode;
|
|
565
|
+
// Shared teardown — runs from `toggle→closed` in the normal flow, and
|
|
566
|
+
// invoked directly from a select-chain-close walk for hidden ancestors
|
|
567
|
+
// whose toggle event will never fire (they were already closed when
|
|
568
|
+
// the deeper level took over the screen). `skipReshow` lets the chain
|
|
569
|
+
// walk suppress the parent-restore step so the whole stack collapses.
|
|
570
|
+
const cleanup = (skipReshow) => {
|
|
463
571
|
submenu.removeEventListener('toggle', onToggle);
|
|
464
572
|
if (this._activeSubmenu === submenu) {
|
|
465
573
|
this._activeSubmenu = null;
|
|
466
574
|
this._activeSubmenuOpener = null;
|
|
575
|
+
this._activeSubmenuCleanup = null;
|
|
467
576
|
}
|
|
468
577
|
submenu._parentMenu = null;
|
|
469
578
|
submenu._parentItem = null;
|
|
579
|
+
// Deregister from the root's open-chain registry.
|
|
580
|
+
const ci = chainRoot._openChain.indexOf(submenu);
|
|
581
|
+
if (ci !== -1)
|
|
582
|
+
chainRoot._openChain.splice(ci, 1);
|
|
583
|
+
submenu._collapseSelf = null;
|
|
584
|
+
submenu._chainRoot = null;
|
|
470
585
|
item._submenuOpen = false;
|
|
586
|
+
// Drill-in: restore the submenu to its original DOM position and
|
|
587
|
+
// re-show the parent so the user returns to the previous view.
|
|
588
|
+
// Captured at open-time so a viewport flip during navigation
|
|
589
|
+
// doesn't leave the chain in an inconsistent state.
|
|
590
|
+
if (wasDrillIn) {
|
|
591
|
+
if (drillInOriginalParent && drillInOriginalParent.isConnected) {
|
|
592
|
+
drillInOriginalParent.insertBefore(submenu, drillInNextSibling);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
// Original parent was removed from the DOM while the
|
|
596
|
+
// submenu was reparented to <body> (SPA navigation,
|
|
597
|
+
// story teardown, etc.). There's nowhere to restore it
|
|
598
|
+
// to — drop the orphaned node instead of leaking a
|
|
599
|
+
// detached-but-connected element in <body> forever.
|
|
600
|
+
submenu.remove();
|
|
601
|
+
}
|
|
602
|
+
if (!skipReshow && this.isConnected && !this.matches(':popover-open')) {
|
|
603
|
+
this.showPopover?.();
|
|
604
|
+
}
|
|
605
|
+
// One level back (back button / ArrowLeft / Esc) — announce the
|
|
606
|
+
// destination view on the now-visible parent. `skipReshow` is a
|
|
607
|
+
// collapse-the-whole-chain teardown (select / outside-tap /
|
|
608
|
+
// resize): that's a dismiss, not a back-nav, so it stays silent.
|
|
609
|
+
if (!skipReshow && this.isConnected) {
|
|
610
|
+
const dest = this._parentItem?.text;
|
|
611
|
+
this._announce(dest
|
|
612
|
+
? this._t('components.menu.submenu-back-action', { title: dest })
|
|
613
|
+
: this._t('components.menu.back'));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
471
616
|
// Drop any safe-triangle "in transit" highlight on the opener —
|
|
472
617
|
// without this, an opener whose submenu was closed via stall-
|
|
473
618
|
// dismissal or programmatic hidePopover keeps the bold accent
|
|
@@ -480,7 +625,6 @@ export class NLDDMenu extends LitElement {
|
|
|
480
625
|
if (!item.hasAttribute('data-focused')) {
|
|
481
626
|
item.removeAttribute('highlighted');
|
|
482
627
|
}
|
|
483
|
-
this._cancelHoverClose();
|
|
484
628
|
this._stopSafeTriangle();
|
|
485
629
|
// Counterpart to `submenu-open` — consumers tracking submenu state
|
|
486
630
|
// via declarative event listeners (rather than reaching into our
|
|
@@ -491,12 +635,59 @@ export class NLDDMenu extends LitElement {
|
|
|
491
635
|
composed: false,
|
|
492
636
|
}));
|
|
493
637
|
};
|
|
638
|
+
const onToggle = (e) => {
|
|
639
|
+
const tg = e;
|
|
640
|
+
if (tg.newState !== 'closed')
|
|
641
|
+
return;
|
|
642
|
+
// Submenu is hiding to make room for a deeper drill — leave the
|
|
643
|
+
// chain state alone, keep the listener around for the eventual
|
|
644
|
+
// real close.
|
|
645
|
+
if (submenu._drillInHidingForDeeper) {
|
|
646
|
+
submenu._drillInHidingForDeeper = false;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
// A real toggle-close = one level back: tear down and re-show
|
|
650
|
+
// the parent. Collapsing the whole chain doesn't come through
|
|
651
|
+
// here — `_collapseChain()` drives `_collapseSelf` directly.
|
|
652
|
+
cleanup(false);
|
|
653
|
+
};
|
|
494
654
|
submenu.addEventListener('toggle', onToggle);
|
|
655
|
+
this._activeSubmenuCleanup = cleanup;
|
|
656
|
+
// Force-teardown used by `_collapseChain()`: detach the toggle
|
|
657
|
+
// listener (so this hide doesn't also run the back-nav onToggle),
|
|
658
|
+
// hide, then run the shared cleanup with no parent re-show.
|
|
659
|
+
submenu._collapseSelf = () => {
|
|
660
|
+
submenu.removeEventListener('toggle', onToggle);
|
|
661
|
+
submenu.hidePopover?.();
|
|
662
|
+
cleanup(true);
|
|
663
|
+
};
|
|
664
|
+
// Drill-in: re-parent the submenu to <body> before show so it isn't
|
|
665
|
+
// a popover-stack descendant of the parent — that lets us close the
|
|
666
|
+
// parent without cascading the submenu closed. Original placement is
|
|
667
|
+
// restored when the submenu closes.
|
|
668
|
+
let drillInOriginalParent = null;
|
|
669
|
+
let drillInNextSibling = null;
|
|
670
|
+
if (wasDrillIn) {
|
|
671
|
+
drillInOriginalParent = submenu.parentElement;
|
|
672
|
+
drillInNextSibling = submenu.nextSibling;
|
|
673
|
+
document.body.appendChild(submenu);
|
|
674
|
+
}
|
|
495
675
|
submenu.showPopover?.();
|
|
496
|
-
|
|
676
|
+
if (wasDrillIn) {
|
|
677
|
+
// View just swapped to the submenu — announce the level we
|
|
678
|
+
// entered (the opener's label is this view's title, same string
|
|
679
|
+
// the back button shows). Fires on the now-visible submenu.
|
|
680
|
+
submenu._announce(submenu._t('components.menu.submenu-title', { title: item.text }));
|
|
681
|
+
// Signal to our own parent's onToggle (if any) that this hide is
|
|
682
|
+
// "intentional, making room for a deeper drill" — skip the normal
|
|
683
|
+
// close cleanup that would re-parent us back and re-open them.
|
|
684
|
+
this._drillInHidingForDeeper = true;
|
|
685
|
+
this.hidePopover?.();
|
|
686
|
+
}
|
|
687
|
+
// Start the safe-triangle tracker for cascade-mode submenus only.
|
|
497
688
|
// Drill-in mode replaces the parent view, so there's no "moving toward"
|
|
498
689
|
// path between two visible menus to protect.
|
|
499
|
-
if (!
|
|
690
|
+
if (!wasDrillIn) {
|
|
500
691
|
// Wait one frame so the submenu's getBoundingClientRect reflects its
|
|
501
692
|
// final position (computePosition runs in the toggle handler).
|
|
502
693
|
requestAnimationFrame(() => {
|
|
@@ -520,34 +711,162 @@ export class NLDDMenu extends LitElement {
|
|
|
520
711
|
this._handleBackMouseenter = () => {
|
|
521
712
|
this._clearHighlight();
|
|
522
713
|
};
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
* disorienting than a clean reset to the root view. */
|
|
530
|
-
this._handleViewportResize = () => {
|
|
531
|
-
const current = this._drillInMode;
|
|
532
|
-
if (this._lastDrillInMode === null) {
|
|
533
|
-
this._lastDrillInMode = current;
|
|
714
|
+
this._touchStartX = 0;
|
|
715
|
+
this._touchStartY = 0;
|
|
716
|
+
/** @internal */
|
|
717
|
+
this._handleMenuTouchStart = (event) => {
|
|
718
|
+
const t = event.touches[0];
|
|
719
|
+
if (!t)
|
|
534
720
|
return;
|
|
535
|
-
|
|
536
|
-
|
|
721
|
+
this._touchStartX = t.clientX;
|
|
722
|
+
this._touchStartY = t.clientY;
|
|
723
|
+
this.removeAttribute('scroll-active');
|
|
724
|
+
};
|
|
725
|
+
/** @internal */
|
|
726
|
+
this._handleMenuTouchMove = (event) => {
|
|
727
|
+
if (this.hasAttribute('scroll-active'))
|
|
537
728
|
return;
|
|
538
|
-
|
|
539
|
-
if (
|
|
540
|
-
|
|
729
|
+
const t = event.touches[0];
|
|
730
|
+
if (!t)
|
|
731
|
+
return;
|
|
732
|
+
const dx = t.clientX - this._touchStartX;
|
|
733
|
+
const dy = t.clientY - this._touchStartY;
|
|
734
|
+
if (Math.hypot(dx, dy) > NLDDMenu._TOUCH_SCROLL_THRESHOLD_PX) {
|
|
735
|
+
this.setAttribute('scroll-active', '');
|
|
541
736
|
}
|
|
542
737
|
};
|
|
738
|
+
/** @internal */
|
|
739
|
+
this._handleMenuTouchEnd = () => {
|
|
740
|
+
this.removeAttribute('scroll-active');
|
|
741
|
+
};
|
|
742
|
+
/** Close the menu on a window resize. Floating-UI's autoUpdate keeps
|
|
743
|
+
* the position in sync with scrolls (where users genuinely expect the
|
|
744
|
+
* menu to follow the anchor), but a full window resize is a layout-
|
|
745
|
+
* shifting interaction the user isn't currently tracking — better to
|
|
746
|
+
* dismiss with a clean slate than to chase the anchor across a
|
|
747
|
+
* reflow, repaint a flipped placement, or get stuck mid-flight when
|
|
748
|
+
* crossing the cascade ↔ drill-in viewport threshold. The window
|
|
749
|
+
* listener is wired only while we're open and torn down on close, so
|
|
750
|
+
* idle menus pay nothing. */
|
|
751
|
+
this._handleWindowResize = () => {
|
|
752
|
+
this._collapseChain();
|
|
753
|
+
};
|
|
543
754
|
/**
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
755
|
+
* Collapse the entire menu chain at once — root + every open submenu —
|
|
756
|
+
* with no "back one level" re-show. Used when the action is final:
|
|
757
|
+
* selecting an item, clicking the anchor, clicking/tapping outside, or
|
|
758
|
+
* a viewport resize.
|
|
759
|
+
*
|
|
760
|
+
* Driven by the root's `_openChain` registry and each submenu's
|
|
761
|
+
* `_collapseSelf`, so it never depends on `_activeSubmenu` /
|
|
762
|
+
* `_parentMenu` threading being intact at collapse time — real
|
|
763
|
+
* interaction can leave those stale, which was the source of the
|
|
764
|
+
* recurring "anchor click doesn't close after the 2nd navigation"
|
|
765
|
+
* bug. Idempotent: every chain level's `select` listener may call it,
|
|
766
|
+
* but the first run empties the registry so the rest are no-ops.
|
|
548
767
|
*/
|
|
549
|
-
this.
|
|
550
|
-
this.
|
|
768
|
+
this._collapseChain = () => {
|
|
769
|
+
const root = this._chainRoot ?? this;
|
|
770
|
+
root._closedAt = Date.now();
|
|
771
|
+
// Snapshot + clear first so the re-entrant cleanup splices are
|
|
772
|
+
// no-ops and a trailing same-gesture call finds nothing to do.
|
|
773
|
+
const chain = root._openChain;
|
|
774
|
+
root._openChain = [];
|
|
775
|
+
for (let i = chain.length - 1; i >= 0; i--)
|
|
776
|
+
chain[i]._collapseSelf?.();
|
|
777
|
+
if (root._isOpen)
|
|
778
|
+
root.hidePopover?.();
|
|
779
|
+
};
|
|
780
|
+
/**
|
|
781
|
+
* Drill-in click-outside: collapse the entire chain.
|
|
782
|
+
*
|
|
783
|
+
* In drill-in mode only the deepest level is `:popover-open` — popover
|
|
784
|
+
* light-dismiss would close just that level, and our onToggle cleanup
|
|
785
|
+
* would helpfully re-show the previous level. That's the wrong outcome
|
|
786
|
+
* for a click outside the menu: the user is dismissing the whole menu,
|
|
787
|
+
* not navigating one step back. Detect the outside click here and route
|
|
788
|
+
* to `_collapseChain`, which collapses every level at once.
|
|
789
|
+
*
|
|
790
|
+
* Cascade mode falls back to the native popover stack behaviour: a click
|
|
791
|
+
* outside light-dismisses every popover down to the click target's
|
|
792
|
+
* closest ancestor popover, so no extra handling is needed there.
|
|
793
|
+
*
|
|
794
|
+
* For mouse/pen, pointerdown (capture) is the right hook: it fires
|
|
795
|
+
* before light-dismiss does its work, so by the time the toggle event
|
|
796
|
+
* fires our chain walk has already detached listeners and prevented
|
|
797
|
+
* the parent re-show.
|
|
798
|
+
*
|
|
799
|
+
* For touch, an outside pointerdown is ambiguous: it's also how a page
|
|
800
|
+
* scroll begins. Closing the chain on the scroll-start would be
|
|
801
|
+
* inconsistent with a root menu (which native light-dismiss leaves
|
|
802
|
+
* open while you scroll outside it) and annoying. So on touch we defer
|
|
803
|
+
* the decision: track the pointer and only chain-close on a confirmed
|
|
804
|
+
* tap (pointerup without crossing the scroll threshold); a scroll
|
|
805
|
+
* leaves the chain untouched, matching root-menu behaviour.
|
|
806
|
+
*/
|
|
807
|
+
this._handleDocumentPointerdown = (event) => {
|
|
808
|
+
if (!this._isOpen)
|
|
809
|
+
return;
|
|
810
|
+
if (!this._drillInMode)
|
|
811
|
+
return;
|
|
812
|
+
// Only nested drill-in submenus need this: they form a hidden chain
|
|
813
|
+
// of ancestors that won't auto-collapse on outside click. A root
|
|
814
|
+
// menu has nothing to chain-close — popover light-dismiss + the
|
|
815
|
+
// invoker's `popovertargetaction` handle outside / anchor clicks
|
|
816
|
+
// natively. Without this guard a click on the anchor of a root
|
|
817
|
+
// drill-in menu would fire chain-close → hide → auto-sync flips
|
|
818
|
+
// `popovertargetaction` to 'show' → click default action reopens
|
|
819
|
+
// (the "menu sluit en opent consistent weer" bug on small screens).
|
|
820
|
+
if (!this._isSubmenu)
|
|
821
|
+
return;
|
|
822
|
+
// Walk this menu + every parent in the drill-in chain. If the
|
|
823
|
+
// pointerdown landed inside any of them, it's an in-chain click
|
|
824
|
+
// (e.g. on the back button, on the menu background, on an item)
|
|
825
|
+
// and the existing handlers take care of it.
|
|
826
|
+
const path = event.composedPath();
|
|
827
|
+
let menu = this;
|
|
828
|
+
while (menu) {
|
|
829
|
+
if (path.includes(menu))
|
|
830
|
+
return;
|
|
831
|
+
menu = menu._parentMenu;
|
|
832
|
+
}
|
|
833
|
+
// Touch: defer to a confirmed tap so a page-scroll started outside
|
|
834
|
+
// the chain doesn't collapse it (consistent with the root menu,
|
|
835
|
+
// which stays open while scrolling outside).
|
|
836
|
+
if (event.pointerType === 'touch') {
|
|
837
|
+
this._outsideTapStartX = event.clientX;
|
|
838
|
+
this._outsideTapStartY = event.clientY;
|
|
839
|
+
this._teardownOutsideTap();
|
|
840
|
+
this._outsideTapTracking = true;
|
|
841
|
+
document.addEventListener('pointermove', this._outsideTapMove, true);
|
|
842
|
+
document.addEventListener('pointerup', this._outsideTapEnd, true);
|
|
843
|
+
document.addEventListener('pointercancel', this._outsideTapCancel, true);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
(this._chainRoot ?? this)._collapsedByPointerGesture = true;
|
|
847
|
+
this._collapseChain();
|
|
848
|
+
};
|
|
849
|
+
this._outsideTapStartX = 0;
|
|
850
|
+
this._outsideTapStartY = 0;
|
|
851
|
+
this._outsideTapTracking = false;
|
|
852
|
+
this._outsideTapMove = (e) => {
|
|
853
|
+
const dx = e.clientX - this._outsideTapStartX;
|
|
854
|
+
const dy = e.clientY - this._outsideTapStartY;
|
|
855
|
+
if (Math.hypot(dx, dy) > NLDDMenu._TOUCH_SCROLL_THRESHOLD_PX) {
|
|
856
|
+
// Became a scroll — leave the chain open.
|
|
857
|
+
this._teardownOutsideTap();
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
this._outsideTapEnd = () => {
|
|
861
|
+
const wasTap = this._outsideTapTracking;
|
|
862
|
+
this._teardownOutsideTap();
|
|
863
|
+
if (wasTap) {
|
|
864
|
+
(this._chainRoot ?? this)._collapsedByPointerGesture = true;
|
|
865
|
+
this._collapseChain();
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
this._outsideTapCancel = () => {
|
|
869
|
+
this._teardownOutsideTap();
|
|
551
870
|
};
|
|
552
871
|
// — Private handlers ——————————————————————————————————————————————————————
|
|
553
872
|
this._handleKeydown = (event) => {
|
|
@@ -645,10 +964,22 @@ export class NLDDMenu extends LitElement {
|
|
|
645
964
|
this._handleToggle = async (event) => {
|
|
646
965
|
const toggleEvent = event;
|
|
647
966
|
this._isOpen = toggleEvent.newState === 'open';
|
|
967
|
+
// Sync the anchor's "is-expanded" state so an anchor button (or any
|
|
968
|
+
// element exposing an `expanded` property) shows the active visual
|
|
969
|
+
// while we're open. Skip menu-items as anchors — submenu openers
|
|
970
|
+
// manage their own `_submenuOpen` lifecycle and don't have an
|
|
971
|
+
// `expanded` prop. Also opt the anchor into menu/listbox semantics
|
|
972
|
+
// via `popupType` when it supports it and the consumer hasn't
|
|
973
|
+
// already chosen a value, so screen readers announce the role.
|
|
974
|
+
this._syncAnchorPopupState(this._isOpen);
|
|
648
975
|
if (toggleEvent.newState !== 'open') {
|
|
649
976
|
this._closedAt = Date.now();
|
|
650
977
|
this._cleanupAutoUpdate?.();
|
|
651
978
|
this._cleanupAutoUpdate = null;
|
|
979
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
980
|
+
document.removeEventListener('pointerdown', this._handleDocumentPointerdown, true);
|
|
981
|
+
this._teardownOutsideTap();
|
|
982
|
+
this._clearHighlight();
|
|
652
983
|
return;
|
|
653
984
|
}
|
|
654
985
|
this._updateDividerVisibility();
|
|
@@ -662,6 +993,14 @@ export class NLDDMenu extends LitElement {
|
|
|
662
993
|
if (anchorEl) {
|
|
663
994
|
this._cleanupAutoUpdate = autoUpdate(anchorEl, this, () => this.reposition());
|
|
664
995
|
}
|
|
996
|
+
// Wired only while open so idle menus pay nothing and we don't
|
|
997
|
+
// accidentally close other menus on the page during a resize or
|
|
998
|
+
// during an unrelated pointerdown elsewhere.
|
|
999
|
+
window.addEventListener('resize', this._handleWindowResize, { passive: true });
|
|
1000
|
+
// Capture so we run before the popover light-dismiss algorithm
|
|
1001
|
+
// processes the pointerdown — gives us a chance to redirect the
|
|
1002
|
+
// dismissal into a chain-close in drill-in mode.
|
|
1003
|
+
document.addEventListener('pointerdown', this._handleDocumentPointerdown, true);
|
|
665
1004
|
await this.updateComplete;
|
|
666
1005
|
if (this.variant !== 'listbox') {
|
|
667
1006
|
const keyboard = isKeyboardMode();
|
|
@@ -679,8 +1018,14 @@ export class NLDDMenu extends LitElement {
|
|
|
679
1018
|
};
|
|
680
1019
|
}
|
|
681
1020
|
// — i18n ——————————————————————————————————————————————————————————————————
|
|
682
|
-
_t(key) {
|
|
683
|
-
|
|
1021
|
+
_t(key, vars) {
|
|
1022
|
+
let str = this.translations[key] ?? nlddMenuTranslations[key];
|
|
1023
|
+
if (vars) {
|
|
1024
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
1025
|
+
str = str.replace(`{${k}}`, String(v));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return str;
|
|
684
1029
|
}
|
|
685
1030
|
/** Resolved empty text: emptyText attribute takes precedence, then i18n fallback. */
|
|
686
1031
|
get _resolvedEmptyText() {
|
|
@@ -695,6 +1040,38 @@ export class NLDDMenu extends LitElement {
|
|
|
695
1040
|
const parent = this._parentItem?.text ?? '';
|
|
696
1041
|
return parent ? `${back}: ${parent}` : back;
|
|
697
1042
|
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Write `message` to this menu's polite live region. Clears first, then
|
|
1045
|
+
* sets on a double rAF — the empty→text transition is what makes screen
|
|
1046
|
+
* readers reliably re-announce unchanged-looking content across engines.
|
|
1047
|
+
*
|
|
1048
|
+
* Spam control without muting real navigation: a doubled event in the
|
|
1049
|
+
* same tick (identical message still pending) is dropped; a *different*
|
|
1050
|
+
* message arriving first cancels the stale pending one (rapid navigation
|
|
1051
|
+
* announces only the level you land on); once flushed, the same text may
|
|
1052
|
+
* be announced again for a later genuine transition (e.g. re-entering a
|
|
1053
|
+
* submenu after going back).
|
|
1054
|
+
*/
|
|
1055
|
+
_announce(message) {
|
|
1056
|
+
if (!message)
|
|
1057
|
+
return;
|
|
1058
|
+
if (this._announceRaf && message === this._pendingAnnouncement)
|
|
1059
|
+
return;
|
|
1060
|
+
const region = this.shadowRoot?.querySelector('.menu__live-region');
|
|
1061
|
+
if (!region)
|
|
1062
|
+
return;
|
|
1063
|
+
if (this._announceRaf)
|
|
1064
|
+
cancelAnimationFrame(this._announceRaf);
|
|
1065
|
+
this._pendingAnnouncement = message;
|
|
1066
|
+
region.textContent = '';
|
|
1067
|
+
this._announceRaf = requestAnimationFrame(() => {
|
|
1068
|
+
this._announceRaf = requestAnimationFrame(() => {
|
|
1069
|
+
this._announceRaf = 0;
|
|
1070
|
+
this._pendingAnnouncement = '';
|
|
1071
|
+
region.textContent = message;
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
698
1075
|
// — Lifecycle ——————————————————————————————————————————————————————————————
|
|
699
1076
|
updated(changedProperties) {
|
|
700
1077
|
if (changedProperties.has('width')) {
|
|
@@ -727,6 +1104,66 @@ export class NLDDMenu extends LitElement {
|
|
|
727
1104
|
return document.getElementById(this.anchor);
|
|
728
1105
|
return null;
|
|
729
1106
|
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Pick the drill-in side (above vs below the anchor) purely from the
|
|
1109
|
+
* available viewport space around the anchor — never from content
|
|
1110
|
+
* size. Content-independent so every level of the chain resolves to
|
|
1111
|
+
* the same side and a bigger submenu scrolls internally rather than
|
|
1112
|
+
* flipping the whole stack. Keeps the configured alignment suffix
|
|
1113
|
+
* (`-start` / `-end`) from `placement`.
|
|
1114
|
+
*/
|
|
1115
|
+
_resolveDrillInPlacement(anchorEl) {
|
|
1116
|
+
const base = this.placement || 'bottom-start';
|
|
1117
|
+
const align = base.includes('-') ? base.slice(base.indexOf('-')) : '';
|
|
1118
|
+
const rect = anchorEl.getBoundingClientRect();
|
|
1119
|
+
const margin = this._cssPx('--_viewport-margin');
|
|
1120
|
+
const spaceBelow = window.innerHeight - rect.bottom - margin;
|
|
1121
|
+
const spaceAbove = rect.top - margin;
|
|
1122
|
+
const side = spaceBelow >= spaceAbove ? 'bottom' : 'top';
|
|
1123
|
+
return `${side}${align}`;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Reflect this menu's open state on the anchor so an anchor button (or
|
|
1127
|
+
* any element exposing an `expanded` IDL property) shows the active
|
|
1128
|
+
* "is-expanded" visual while we're open. Skip menu-items as anchors —
|
|
1129
|
+
* they manage their own `_submenuOpen` lifecycle and don't have an
|
|
1130
|
+
* `expanded` property. Also default `popupType` to 'menu' / 'listbox'
|
|
1131
|
+
* (matching this menu's variant) when the anchor supports it and the
|
|
1132
|
+
* consumer hasn't already chosen a value, so screen readers announce
|
|
1133
|
+
* the popup role without each consumer having to set it manually.
|
|
1134
|
+
*
|
|
1135
|
+
* Finally, drive the anchor's `popoverTargetAction` so the browser's
|
|
1136
|
+
* native invoker action always matches the current state explicitly:
|
|
1137
|
+
* `'hide'` while open, `'show'` while closed. We avoid `'toggle'` on
|
|
1138
|
+
* purpose — toggle re-evaluates the popover's live state at click time
|
|
1139
|
+
* and would re-open the menu if it had just been light-dismissed
|
|
1140
|
+
* milliseconds earlier (the source of the "menu briefly closes and
|
|
1141
|
+
* reopens" bug). With explicit show/hide, the worst case is a no-op
|
|
1142
|
+
* (e.g. `'hide'` on an already-closed menu) — never a spurious open.
|
|
1143
|
+
*
|
|
1144
|
+
* The invoker association (`popoverTargetElement`) is set elsewhere
|
|
1145
|
+
* — persistently on anchor change rather than per-toggle — so the
|
|
1146
|
+
* browser's light-dismiss exclusion is in place from the very first
|
|
1147
|
+
* click instead of only after the menu has already opened once.
|
|
1148
|
+
*/
|
|
1149
|
+
_syncAnchorPopupState(isOpen) {
|
|
1150
|
+
const anchor = this._getAnchorEl();
|
|
1151
|
+
if (!anchor)
|
|
1152
|
+
return;
|
|
1153
|
+
if ('expanded' in anchor)
|
|
1154
|
+
anchor.expanded = isOpen;
|
|
1155
|
+
// Seed a valid aria-haspopup when the anchor has no usable popup
|
|
1156
|
+
// type. Falsy covers unset (undefined/null) AND `popup-type=""` —
|
|
1157
|
+
// an empty string is not a valid aria-haspopup token, so it must
|
|
1158
|
+
// still be seeded rather than left invalid. An intentional opt-out
|
|
1159
|
+
// is `popup-type="false"`, which is truthy and preserved here.
|
|
1160
|
+
if (isOpen && 'popupType' in anchor && !anchor.popupType) {
|
|
1161
|
+
anchor.popupType = this.variant === 'listbox' ? 'listbox' : 'menu';
|
|
1162
|
+
}
|
|
1163
|
+
if ('popoverTargetAction' in anchor) {
|
|
1164
|
+
anchor.popoverTargetAction = isOpen ? 'hide' : 'show';
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
730
1167
|
/**
|
|
731
1168
|
* Item-activation logic shared between the native `mouseenter` capture
|
|
732
1169
|
* handler and the safe-triangle recovery paths (sideways exit, direction
|
|
@@ -753,15 +1190,11 @@ export class NLDDMenu extends LitElement {
|
|
|
753
1190
|
if (this._movingTowardSubmenu && item !== this._activeSubmenuOpener)
|
|
754
1191
|
return;
|
|
755
1192
|
this._setHighlight(item);
|
|
756
|
-
// Cursor is on a real item now (not in transit) — cancel any pending
|
|
757
|
-
// linger-close from a previous sweep through.
|
|
758
|
-
this._cancelHoverClose();
|
|
759
1193
|
if (this._drillInMode)
|
|
760
1194
|
return; // Touch / narrow viewport: no hover-open.
|
|
761
1195
|
// Settled on a peer item (not the active submenu's opener) — close the
|
|
762
1196
|
// active submenu immediately. If this peer itself has a submenu, the
|
|
763
|
-
// hover-open below will then schedule its own.
|
|
764
|
-
// linger-close: the user has already committed to a different item.
|
|
1197
|
+
// hover-open below will then schedule its own.
|
|
765
1198
|
if (this._activeSubmenu && item !== this._activeSubmenuOpener) {
|
|
766
1199
|
this._activeSubmenu.hidePopover?.();
|
|
767
1200
|
}
|
|
@@ -824,9 +1257,9 @@ export class NLDDMenu extends LitElement {
|
|
|
824
1257
|
this._safeTrianglePinApex(p, openerRect);
|
|
825
1258
|
}
|
|
826
1259
|
else {
|
|
827
|
-
// 3c. Exited sideways — no protection
|
|
828
|
-
//
|
|
829
|
-
//
|
|
1260
|
+
// 3c. Exited sideways — no protection. Drop the apex and
|
|
1261
|
+
// activate whatever item is under the cursor so peers
|
|
1262
|
+
// take over immediately.
|
|
830
1263
|
this._safeTriangleSidewaysExit(p, wasMoving);
|
|
831
1264
|
}
|
|
832
1265
|
}
|
|
@@ -835,15 +1268,10 @@ export class NLDDMenu extends LitElement {
|
|
|
835
1268
|
this._safeTrianglePolygonTest(p, r);
|
|
836
1269
|
}
|
|
837
1270
|
this._lastCursorPos = p;
|
|
838
|
-
// 5. Common per-frame work: highlight, recovery,
|
|
1271
|
+
// 5. Common per-frame work: highlight, recovery, stall timer.
|
|
839
1272
|
this._safeTriangleSyncOpenerHighlight();
|
|
840
1273
|
this._safeTriangleDirectionReversalRecovery(p, wasMoving);
|
|
841
1274
|
this._safeTriangleScheduleStall();
|
|
842
|
-
if (this._movingTowardSubmenu) {
|
|
843
|
-
this._cancelHoverClose();
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
this._safeTriangleMaybeScheduleLingerClose(p);
|
|
847
1275
|
};
|
|
848
1276
|
window.addEventListener('mousemove', this._safeTriangleListener);
|
|
849
1277
|
}
|
|
@@ -854,7 +1282,6 @@ export class NLDDMenu extends LitElement {
|
|
|
854
1282
|
* return trip to the opener and re-pin a fresh apex for another exit. */
|
|
855
1283
|
_safeTriangleArrived(p) {
|
|
856
1284
|
this._movingTowardSubmenu = false;
|
|
857
|
-
this._cancelHoverClose();
|
|
858
1285
|
this._lastCursorPos = p;
|
|
859
1286
|
this._safeTriangleApex = null;
|
|
860
1287
|
this._activeSubmenuOpener?.removeAttribute('highlighted');
|
|
@@ -963,24 +1390,6 @@ export class NLDDMenu extends LitElement {
|
|
|
963
1390
|
this._activateItemAt(this._lastCursorPos);
|
|
964
1391
|
}, NLDDMenu._SAFE_TRIANGLE_STALL_DISMISS_MS);
|
|
965
1392
|
}
|
|
966
|
-
/** Schedule submenu close when cursor sits outside the parent menu rect.
|
|
967
|
-
* No-op if cursor is inside the menu (peer-hover logic handles it) or
|
|
968
|
-
* if a close is already scheduled. */
|
|
969
|
-
_safeTriangleMaybeScheduleLingerClose(p) {
|
|
970
|
-
const submenu = this._safeTriangleSubmenu;
|
|
971
|
-
if (!submenu)
|
|
972
|
-
return;
|
|
973
|
-
const rootRect = this.getBoundingClientRect();
|
|
974
|
-
const inRoot = NLDDMenu._rectContainsPoint(rootRect, p);
|
|
975
|
-
if (!inRoot && this._hoverCloseTimer === null) {
|
|
976
|
-
this._hoverCloseTimer = window.setTimeout(() => {
|
|
977
|
-
this._hoverCloseTimer = null;
|
|
978
|
-
if (this._activeSubmenu === submenu) {
|
|
979
|
-
submenu.hidePopover?.();
|
|
980
|
-
}
|
|
981
|
-
}, NLDDMenu._SUBMENU_LINGER_CLOSE_MS);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
1393
|
/** True when the rect contains the point (inclusive). Returns false for
|
|
985
1394
|
* a missing rect or point — convenient for the optional-chained call
|
|
986
1395
|
* sites in the safe-triangle logic. */
|
|
@@ -1038,17 +1447,11 @@ export class NLDDMenu extends LitElement {
|
|
|
1038
1447
|
this._hoverOpenTimer = null;
|
|
1039
1448
|
}
|
|
1040
1449
|
}
|
|
1041
|
-
_cancelHoverClose() {
|
|
1042
|
-
if (this._hoverCloseTimer !== null) {
|
|
1043
|
-
clearTimeout(this._hoverCloseTimer);
|
|
1044
|
-
this._hoverCloseTimer = null;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
1450
|
/** Recursively checks whether a point sits inside the given menu OR any of
|
|
1048
|
-
* its descendant submenus. Used by the
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1451
|
+
* its descendant submenus. Used by the safe-triangle "arrived" check so a
|
|
1452
|
+
* cursor that has continued past the immediate submenu into a deeper
|
|
1453
|
+
* nested submenu still reads as "in the safe area" — without the recursive
|
|
1454
|
+
* walk we'd treat the deeper hover as an exit and re-pin a stale apex. */
|
|
1052
1455
|
static _isPointInMenuTree(p, menu) {
|
|
1053
1456
|
const r = menu.getBoundingClientRect();
|
|
1054
1457
|
if (p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom)
|
|
@@ -1100,6 +1503,12 @@ export class NLDDMenu extends LitElement {
|
|
|
1100
1503
|
get _isSubmenu() {
|
|
1101
1504
|
return this._parentMenu !== null;
|
|
1102
1505
|
}
|
|
1506
|
+
_teardownOutsideTap() {
|
|
1507
|
+
this._outsideTapTracking = false;
|
|
1508
|
+
document.removeEventListener('pointermove', this._outsideTapMove, true);
|
|
1509
|
+
document.removeEventListener('pointerup', this._outsideTapEnd, true);
|
|
1510
|
+
document.removeEventListener('pointercancel', this._outsideTapCancel, true);
|
|
1511
|
+
}
|
|
1103
1512
|
connectedCallback() {
|
|
1104
1513
|
super.connectedCallback();
|
|
1105
1514
|
if (!this.id) {
|
|
@@ -1115,15 +1524,8 @@ export class NLDDMenu extends LitElement {
|
|
|
1115
1524
|
this.addEventListener('mouseleave', this._handleMouseleave);
|
|
1116
1525
|
this.addEventListener('menu-item-focused', this._handleMenuItemFocused);
|
|
1117
1526
|
this.addEventListener('submenu-open', this._handleSubmenuOpen);
|
|
1118
|
-
this.addEventListener('select', this.
|
|
1527
|
+
this.addEventListener('select', this._collapseChain);
|
|
1119
1528
|
document.addEventListener('click', this._handleDocumentClick);
|
|
1120
|
-
// Close any open submenu when the viewport crosses the cascade ↔ drill-in
|
|
1121
|
-
// threshold mid-session — re-rendering between modes mid-flight would
|
|
1122
|
-
// require recomputing anchors and is more disorienting than a clean reset.
|
|
1123
|
-
// Subscribe to the MediaQueryList's `change` event rather than every
|
|
1124
|
-
// `window.resize` frame: it fires only when the threshold is actually
|
|
1125
|
-
// crossed and matches the comment above the cached query.
|
|
1126
|
-
NLDDMenu._getDrillInModeQuery().addEventListener('change', this._handleViewportResize);
|
|
1127
1529
|
}
|
|
1128
1530
|
firstUpdated() {
|
|
1129
1531
|
this._updateEmptyState();
|
|
@@ -1144,11 +1546,15 @@ export class NLDDMenu extends LitElement {
|
|
|
1144
1546
|
this.removeEventListener('mouseleave', this._handleMouseleave);
|
|
1145
1547
|
this.removeEventListener('menu-item-focused', this._handleMenuItemFocused);
|
|
1146
1548
|
this.removeEventListener('submenu-open', this._handleSubmenuOpen);
|
|
1147
|
-
this.removeEventListener('select', this.
|
|
1549
|
+
this.removeEventListener('select', this._collapseChain);
|
|
1148
1550
|
document.removeEventListener('click', this._handleDocumentClick);
|
|
1149
|
-
|
|
1551
|
+
// Defensive: the pointerdown + resize listeners are added on open
|
|
1552
|
+
// and removed on close, but if the menu is torn down mid-open we'd
|
|
1553
|
+
// otherwise leak the global listeners.
|
|
1554
|
+
document.removeEventListener('pointerdown', this._handleDocumentPointerdown, true);
|
|
1555
|
+
this._teardownOutsideTap();
|
|
1556
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
1150
1557
|
this._cancelHoverOpen();
|
|
1151
|
-
this._cancelHoverClose();
|
|
1152
1558
|
this._stopSafeTriangle();
|
|
1153
1559
|
if (this._typeaheadTimer !== null) {
|
|
1154
1560
|
clearTimeout(this._typeaheadTimer);
|
|
@@ -1294,24 +1700,43 @@ export class NLDDMenu extends LitElement {
|
|
|
1294
1700
|
getHighlightedId() {
|
|
1295
1701
|
return this.getHighlighted()?.id ?? '';
|
|
1296
1702
|
}
|
|
1703
|
+
/** Read a px-valued CSS custom property as a number. `parseFloat`
|
|
1704
|
+
* keeps fractional pixels; an unset/NaN value falls back to 0. */
|
|
1705
|
+
_cssPx(name) {
|
|
1706
|
+
const v = parseFloat(getComputedStyle(this).getPropertyValue(name));
|
|
1707
|
+
return Number.isNaN(v) ? 0 : v;
|
|
1708
|
+
}
|
|
1297
1709
|
/** Recalculate position and size relative to the anchor element. */
|
|
1298
1710
|
async reposition() {
|
|
1299
1711
|
const anchorEl = this._getAnchorEl();
|
|
1300
1712
|
if (!anchorEl || !this._isOpen)
|
|
1301
1713
|
return;
|
|
1302
|
-
const viewportMargin =
|
|
1714
|
+
const viewportMargin = this._cssPx('--_viewport-margin');
|
|
1303
1715
|
// Cascade-mode submenus: shift up by the menu's own padding so the
|
|
1304
1716
|
// first submenu item lines up vertically with the parent opener item.
|
|
1305
1717
|
// Without this, the submenu's top edge aligns with the opener and the
|
|
1306
1718
|
// inner padding pushes the first item down, leaving a visible step.
|
|
1307
1719
|
const submenuPadding = (this._isSubmenu && !this._drillInMode)
|
|
1308
|
-
?
|
|
1720
|
+
? this._cssPx('--_menu-padding')
|
|
1309
1721
|
: 0;
|
|
1722
|
+
// Drill-in: pick the side from the available space around the
|
|
1723
|
+
// anchor — recomputed every reposition, but content-independent so
|
|
1724
|
+
// a larger submenu never changes it (no mid-chain jump). `flip` is
|
|
1725
|
+
// content-dependent and WOULD jump the stack when a bigger submenu
|
|
1726
|
+
// doesn't fit, so it's dropped here; `size` caps the height and
|
|
1727
|
+
// the menu scrolls internally instead. Re-resolving each time (vs
|
|
1728
|
+
// freezing) keeps it following the anchor on scroll and re-picking
|
|
1729
|
+
// the roomiest side after a close + reopen. Cascade keeps `flip`
|
|
1730
|
+
// (submenus open beside the opener, content-fit matters there).
|
|
1731
|
+
const drillIn = this._drillInMode;
|
|
1732
|
+
const effectivePlacement = drillIn
|
|
1733
|
+
? this._resolveDrillInPlacement(anchorEl)
|
|
1734
|
+
: this.placement;
|
|
1310
1735
|
const { x, y } = await computePosition(anchorEl, this, {
|
|
1311
|
-
placement:
|
|
1736
|
+
placement: effectivePlacement,
|
|
1312
1737
|
middleware: [
|
|
1313
1738
|
offset({ mainAxis: 0, alignmentAxis: -submenuPadding }),
|
|
1314
|
-
flip({ padding: viewportMargin }),
|
|
1739
|
+
...(drillIn ? [] : [flip({ padding: viewportMargin })]),
|
|
1315
1740
|
shift({ padding: viewportMargin }),
|
|
1316
1741
|
size({
|
|
1317
1742
|
padding: viewportMargin,
|
|
@@ -1367,14 +1792,11 @@ export class NLDDMenu extends LitElement {
|
|
|
1367
1792
|
}
|
|
1368
1793
|
}
|
|
1369
1794
|
NLDDMenu.styles = menuStyles;
|
|
1370
|
-
/** Wait this long after cursor stops being on a path toward the submenu
|
|
1371
|
-
* before closing. Smooths over brief cursor stalls and accidental nudges. */
|
|
1372
|
-
NLDDMenu._SUBMENU_LINGER_CLOSE_MS = 300;
|
|
1373
1795
|
/** When the cursor sits motionless inside the safe triangle for this
|
|
1374
1796
|
* long, dismiss the triangle and let whatever item is under the cursor
|
|
1375
1797
|
* become interactive. Without this, a paused cursor would stay
|
|
1376
1798
|
* "protected" indefinitely and the user couldn't peer-hover. */
|
|
1377
|
-
NLDDMenu._SAFE_TRIANGLE_STALL_DISMISS_MS =
|
|
1799
|
+
NLDDMenu._SAFE_TRIANGLE_STALL_DISMISS_MS = 500;
|
|
1378
1800
|
/** Pull the wedge apex this many pixels away from the submenu edge (i.e.
|
|
1379
1801
|
* to the left when the submenu is on the right). Widens the wedge near
|
|
1380
1802
|
* the cursor so brief diagonal wobble doesn't pop the cursor out at the
|
|
@@ -1395,10 +1817,20 @@ NLDDMenu._SAFE_TRIANGLE_APEX_X_OFFSET = 4;
|
|
|
1395
1817
|
*
|
|
1396
1818
|
* Cached MediaQueryList shared across instances — `matchMedia()` returns
|
|
1397
1819
|
* a live object and re-evaluating it for every mouseenter/submenu-open
|
|
1398
|
-
* is wasteful.
|
|
1399
|
-
* naturally route through `_handleViewportResize`.
|
|
1820
|
+
* is wasteful.
|
|
1400
1821
|
*/
|
|
1401
1822
|
NLDDMenu._drillInModeQuery = null;
|
|
1823
|
+
// — Touch-scroll press-flash suppression ————————————————————————————————
|
|
1824
|
+
//
|
|
1825
|
+
// On touch, pressing an item sets :active and the synthetic sticky
|
|
1826
|
+
// :hover lands on it, so `.menu__item:active:hover` flashes. If the
|
|
1827
|
+
// gesture becomes an internal scroll (cramped menu), :active lingers
|
|
1828
|
+
// until the browser recognises the pan and the highlight stays for
|
|
1829
|
+
// the whole scroll. We detect a scroll from the touch delta and set
|
|
1830
|
+
// `scroll-active` on the host; the styles neutralise the press flash
|
|
1831
|
+
// while it's set. A pure tap never moves past the threshold, so its
|
|
1832
|
+
// brief press feedback is preserved.
|
|
1833
|
+
NLDDMenu._TOUCH_SCROLL_THRESHOLD_PX = 8;
|
|
1402
1834
|
// — Lifecycle callbacks ————————————————————————————————————————————————————
|
|
1403
1835
|
NLDDMenu._menuIdCounter = 0;
|
|
1404
1836
|
/** Reset the typeahead buffer after this much idle time. 500ms is the
|