@nldd/design-system 0.8.43 → 0.8.45
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/dist/components/actions/button/button.d.ts +19 -1
- package/dist/components/actions/button/button.d.ts.map +1 -1
- package/dist/components/actions/button/button.js +25 -1
- 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 +177 -299
- 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 +7 -0
- package/dist/components/actions/button/button.template.js.map +1 -1
- package/dist/components/actions/button-bar/button-bar.styles.d.ts.map +1 -1
- package/dist/components/actions/button-bar/button-bar.styles.js +33 -79
- package/dist/components/actions/button-bar/button-bar.styles.js.map +1 -1
- package/dist/components/actions/button-group/button-group.styles.d.ts.map +1 -1
- package/dist/components/actions/button-group/button-group.styles.js +24 -37
- package/dist/components/actions/button-group/button-group.styles.js.map +1 -1
- package/dist/components/actions/icon-button/icon-button.d.ts +28 -7
- package/dist/components/actions/icon-button/icon-button.d.ts.map +1 -1
- package/dist/components/actions/icon-button/icon-button.js +36 -9
- 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 +173 -299
- 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 +9 -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.d.ts.map +1 -1
- package/dist/components/actions/split-button/split-button.styles.js +32 -67
- package/dist/components/actions/split-button/split-button.styles.js.map +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 +0 -2
- package/dist/components/actions/toolbar/toolbar.d.ts.map +1 -1
- package/dist/components/actions/toolbar/toolbar.js +21 -17
- package/dist/components/actions/toolbar/toolbar.js.map +1 -1
- package/dist/components/actions/toolbar/toolbar.styles.d.ts.map +1 -1
- package/dist/components/actions/toolbar/toolbar.styles.js +52 -105
- package/dist/components/actions/toolbar/toolbar.styles.js.map +1 -1
- package/dist/components/actions/toolbar/toolbar.template.d.ts +1 -1
- package/dist/components/actions/toolbar/toolbar.template.d.ts.map +1 -1
- package/dist/components/actions/toolbar/toolbar.template.js +13 -5
- package/dist/components/actions/toolbar/toolbar.template.js.map +1 -1
- package/dist/components/components.js +1 -1
- package/dist/components/content/blockquote/blockquote.d.ts +1 -9
- package/dist/components/content/blockquote/blockquote.d.ts.map +1 -1
- package/dist/components/content/blockquote/blockquote.js +1 -6
- package/dist/components/content/blockquote/blockquote.js.map +1 -1
- package/dist/components/content/blockquote/blockquote.styles.d.ts.map +1 -1
- package/dist/components/content/blockquote/blockquote.styles.js +16 -15
- package/dist/components/content/blockquote/blockquote.styles.js.map +1 -1
- package/dist/components/content/blockquote/blockquote.template.d.ts.map +1 -1
- package/dist/components/content/blockquote/blockquote.template.js +4 -7
- package/dist/components/content/blockquote/blockquote.template.js.map +1 -1
- package/dist/components/content/code/code.d.ts +4 -0
- package/dist/components/content/code/code.d.ts.map +1 -1
- package/dist/components/content/code/code.js +20 -0
- package/dist/components/content/code/code.js.map +1 -1
- package/dist/components/content/code/code.styles.d.ts.map +1 -1
- package/dist/components/content/code/code.styles.js +10 -26
- package/dist/components/content/code/code.styles.js.map +1 -1
- package/dist/components/content/icon/icon-aliases.d.ts.map +1 -1
- package/dist/components/content/icon/icon-aliases.js +31 -7
- 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 +18 -6
- package/dist/components/content/icon/icon-registry.js.map +1 -1
- package/dist/components/content/icon/icon.d.ts +26 -1
- package/dist/components/content/icon/icon.d.ts.map +1 -1
- package/dist/components/content/icon/icon.js +30 -1
- package/dist/components/content/icon/icon.js.map +1 -1
- package/dist/components/content/icon/icon.styles.d.ts.map +1 -1
- package/dist/components/content/icon/icon.styles.js +65 -14
- package/dist/components/content/icon/icon.styles.js.map +1 -1
- package/dist/components/content/icon/icon.template.d.ts.map +1 -1
- package/dist/components/content/icon/icon.template.js +1 -5
- package/dist/components/content/icon/icon.template.js.map +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.d.ts.map +1 -1
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.styles.js +12 -18
- package/dist/components/content/keyboard-shortcut/keyboard-shortcut.styles.js.map +1 -1
- 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 +5 -0
- package/dist/components/content/tag/tag.d.ts.map +1 -1
- package/dist/components/content/tag/tag.js +22 -0
- 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 +16 -27
- package/dist/components/content/tag/tag.styles.js.map +1 -1
- package/dist/components/content/tag/tag.template.d.ts.map +1 -1
- package/dist/components/content/tag/tag.template.js +12 -8
- package/dist/components/content/tag/tag.template.js.map +1 -1
- package/dist/components/content/title/title.styles.d.ts.map +1 -1
- package/dist/components/content/title/title.styles.js +84 -97
- package/dist/components/content/title/title.styles.js.map +1 -1
- package/dist/components/content/tooltip/tooltip.styles.d.ts.map +1 -1
- package/dist/components/content/tooltip/tooltip.styles.js +19 -22
- package/dist/components/content/tooltip/tooltip.styles.js.map +1 -1
- package/dist/components/forms/form-actions/form-actions.styles.d.ts.map +1 -1
- package/dist/components/forms/form-actions/form-actions.styles.js +18 -18
- package/dist/components/forms/form-actions/form-actions.styles.js.map +1 -1
- package/dist/components/forms/form-field/form-field.styles.d.ts.map +1 -1
- package/dist/components/forms/form-field/form-field.styles.js +44 -53
- package/dist/components/forms/form-field/form-field.styles.js.map +1 -1
- package/dist/components/{index-By2jYHEw.js → index-iTa3Ua3y.js} +5021 -6751
- 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.d.ts.map +1 -1
- package/dist/components/inputs/checkbox/checkbox.styles.js +12 -33
- package/dist/components/inputs/checkbox/checkbox.styles.js.map +1 -1
- package/dist/components/inputs/checkbox-field/checkbox-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/checkbox-field/checkbox-field.styles.js +7 -10
- package/dist/components/inputs/checkbox-field/checkbox-field.styles.js.map +1 -1
- package/dist/components/inputs/code-editor/code-editor.styles.d.ts.map +1 -1
- package/dist/components/inputs/code-editor/code-editor.styles.js +26 -28
- package/dist/components/inputs/code-editor/code-editor.styles.js.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.d.ts +2 -0
- package/dist/components/inputs/combo-box/combo-box.d.ts.map +1 -1
- package/dist/components/inputs/combo-box/combo-box.js +5 -0
- 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 +43 -70
- 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 +3 -2
- package/dist/components/inputs/combo-box/combo-box.template.js.map +1 -1
- package/dist/components/inputs/dropdown/dropdown.d.ts +15 -5
- package/dist/components/inputs/dropdown/dropdown.d.ts.map +1 -1
- package/dist/components/inputs/dropdown/dropdown.js +28 -9
- 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 +98 -107
- 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 +2 -0
- 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 +5 -0
- 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 +40 -66
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.styles.js.map +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.template.d.ts.map +1 -1
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.template.js +1 -0
- package/dist/components/inputs/multi-line-text-field/multi-line-text-field.template.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 +37 -61
- 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 +8 -8
- package/dist/components/inputs/password-field/password-field.d.ts.map +1 -1
- package/dist/components/inputs/password-field/password-field.js +16 -16
- 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 +55 -101
- package/dist/components/inputs/password-field/password-field.styles.js.map +1 -1
- package/dist/components/inputs/password-field/password-field.template.d.ts.map +1 -1
- package/dist/components/inputs/password-field/password-field.template.js +4 -3
- package/dist/components/inputs/password-field/password-field.template.js.map +1 -1
- package/dist/components/inputs/radio-button/radio-button.styles.d.ts.map +1 -1
- package/dist/components/inputs/radio-button/radio-button.styles.js +14 -35
- package/dist/components/inputs/radio-button/radio-button.styles.js.map +1 -1
- package/dist/components/inputs/radio-button-field/radio-button-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/radio-button-field/radio-button-field.styles.js +7 -10
- package/dist/components/inputs/radio-button-field/radio-button-field.styles.js.map +1 -1
- package/dist/components/inputs/search-field/search-field.d.ts +2 -0
- package/dist/components/inputs/search-field/search-field.d.ts.map +1 -1
- package/dist/components/inputs/search-field/search-field.js +5 -0
- 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 +49 -83
- package/dist/components/inputs/search-field/search-field.styles.js.map +1 -1
- package/dist/components/inputs/search-field/search-field.template.d.ts.map +1 -1
- package/dist/components/inputs/search-field/search-field.template.js +1 -0
- package/dist/components/inputs/search-field/search-field.template.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 +64 -115
- 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/segmented-control/segmented-control.template.js.map +1 -1
- package/dist/components/inputs/stepper/stepper.styles.d.ts.map +1 -1
- package/dist/components/inputs/stepper/stepper.styles.js +18 -32
- package/dist/components/inputs/stepper/stepper.styles.js.map +1 -1
- package/dist/components/inputs/stepper/stepper.template.js +2 -2
- package/dist/components/inputs/switch/switch.styles.d.ts.map +1 -1
- package/dist/components/inputs/switch/switch.styles.js +37 -80
- package/dist/components/inputs/switch/switch.styles.js.map +1 -1
- package/dist/components/inputs/switch-field/switch-field.styles.d.ts.map +1 -1
- package/dist/components/inputs/switch-field/switch-field.styles.js +7 -10
- package/dist/components/inputs/switch-field/switch-field.styles.js.map +1 -1
- package/dist/components/inputs/text-field/text-field.d.ts +2 -0
- package/dist/components/inputs/text-field/text-field.d.ts.map +1 -1
- package/dist/components/inputs/text-field/text-field.js +5 -0
- 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 +42 -78
- package/dist/components/inputs/text-field/text-field.styles.js.map +1 -1
- package/dist/components/inputs/text-field/text-field.template.d.ts.map +1 -1
- package/dist/components/inputs/text-field/text-field.template.js +1 -0
- package/dist/components/inputs/text-field/text-field.template.js.map +1 -1
- package/dist/components/inputs/toggle-button/toggle-button.d.ts +5 -1
- package/dist/components/inputs/toggle-button/toggle-button.d.ts.map +1 -1
- package/dist/components/inputs/toggle-button/toggle-button.js +16 -2
- package/dist/components/inputs/toggle-button/toggle-button.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 +55 -102
- package/dist/components/inputs/toggle-button/toggle-button.styles.js.map +1 -1
- package/dist/components/inputs/toggle-button/toggle-button.template.d.ts.map +1 -1
- package/dist/components/inputs/toggle-button/toggle-button.template.js +10 -4
- package/dist/components/inputs/toggle-button/toggle-button.template.js.map +1 -1
- package/dist/components/inputs/token/token.styles.d.ts.map +1 -1
- package/dist/components/inputs/token/token.styles.js +23 -49
- package/dist/components/inputs/token/token.styles.js.map +1 -1
- package/dist/components/inputs/token/token.template.js +1 -1
- package/dist/components/layout/app-view/app-view.styles.d.ts.map +1 -1
- package/dist/components/layout/app-view/app-view.styles.js +15 -9
- package/dist/components/layout/app-view/app-view.styles.js.map +1 -1
- package/dist/components/layout/box/box.styles.js +2 -2
- package/dist/components/layout/card/card.styles.d.ts.map +1 -1
- package/dist/components/layout/card/card.styles.js +5 -11
- package/dist/components/layout/card/card.styles.js.map +1 -1
- package/dist/components/layout/collection/collection.styles.d.ts.map +1 -1
- package/dist/components/layout/collection/collection.styles.js +23 -45
- package/dist/components/layout/collection/collection.styles.js.map +1 -1
- package/dist/components/layout/collection/collection.template.d.ts.map +1 -1
- package/dist/components/layout/collection/collection.template.js +1 -0
- package/dist/components/layout/collection/collection.template.js.map +1 -1
- package/dist/components/layout/container/container.d.ts +41 -43
- package/dist/components/layout/container/container.d.ts.map +1 -1
- package/dist/components/layout/container/container.js +143 -132
- package/dist/components/layout/container/container.js.map +1 -1
- package/dist/components/layout/container/container.styles.d.ts.map +1 -1
- package/dist/components/layout/container/container.styles.js +87 -1133
- package/dist/components/layout/container/container.styles.js.map +1 -1
- package/dist/components/layout/container/container.template.d.ts.map +1 -1
- package/dist/components/layout/container/container.template.js +1 -5
- package/dist/components/layout/container/container.template.js.map +1 -1
- package/dist/components/layout/divider/divider.styles.d.ts.map +1 -1
- package/dist/components/layout/divider/divider.styles.js +5 -3
- package/dist/components/layout/divider/divider.styles.js.map +1 -1
- package/dist/components/layout/page/page.styles.d.ts.map +1 -1
- package/dist/components/layout/page/page.styles.js +21 -27
- package/dist/components/layout/page/page.styles.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 +16 -24
- 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.styles.d.ts.map +1 -1
- package/dist/components/layout/page-sections/one-half-one-half-section/one-half-one-half-section.styles.js +15 -14
- 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.styles.d.ts.map +1 -1
- package/dist/components/layout/page-sections/one-third-two-thirds-section/one-third-two-thirds-section.styles.js +15 -14
- 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.styles.d.ts.map +1 -1
- package/dist/components/layout/page-sections/simple-section/simple-section.styles.js +15 -24
- 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.styles.d.ts.map +1 -1
- package/dist/components/layout/page-sections/two-thirds-one-third-section/two-thirds-one-third-section.styles.js +15 -14
- 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.d.ts +2 -1
- package/dist/components/layout/popover/popover.d.ts.map +1 -1
- package/dist/components/layout/popover/popover.js +21 -4
- package/dist/components/layout/popover/popover.js.map +1 -1
- package/dist/components/layout/popover/popover.styles.d.ts.map +1 -1
- package/dist/components/layout/popover/popover.styles.js +10 -15
- package/dist/components/layout/popover/popover.styles.js.map +1 -1
- 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 +40 -6
- 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 +41 -59
- package/dist/components/layout/sheet/sheet.styles.js.map +1 -1
- package/dist/components/layout/spacer/spacer.styles.d.ts.map +1 -1
- package/dist/components/layout/spacer/spacer.styles.js +13 -5
- package/dist/components/layout/spacer/spacer.styles.js.map +1 -1
- package/dist/components/layout/split-views/bar-split-view/bar-split-view.styles.d.ts.map +1 -1
- package/dist/components/layout/split-views/bar-split-view/bar-split-view.styles.js +29 -26
- package/dist/components/layout/split-views/bar-split-view/bar-split-view.styles.js.map +1 -1
- package/dist/components/layout/split-views/navigation-split-view/navigation-split-view.styles.d.ts.map +1 -1
- package/dist/components/layout/split-views/navigation-split-view/navigation-split-view.styles.js +92 -105
- package/dist/components/layout/split-views/navigation-split-view/navigation-split-view.styles.js.map +1 -1
- package/dist/components/layout/split-views/side-by-side-split-view/side-by-side-split-view.styles.d.ts.map +1 -1
- package/dist/components/layout/split-views/side-by-side-split-view/side-by-side-split-view.styles.js +22 -15
- package/dist/components/layout/split-views/side-by-side-split-view/side-by-side-split-view.styles.js.map +1 -1
- package/dist/components/layout/split-views/split-view-divider/split-view-divider.styles.d.ts.map +1 -1
- package/dist/components/layout/split-views/split-view-divider/split-view-divider.styles.js +26 -46
- package/dist/components/layout/split-views/split-view-divider/split-view-divider.styles.js.map +1 -1
- package/dist/components/layout/split-views/split-view-pane/split-view-pane.styles.d.ts.map +1 -1
- package/dist/components/layout/split-views/split-view-pane/split-view-pane.styles.js +22 -8
- package/dist/components/layout/split-views/split-view-pane/split-view-pane.styles.js.map +1 -1
- package/dist/components/layout/split-views/stacked-split-view/stacked-split-view.styles.d.ts.map +1 -1
- package/dist/components/layout/split-views/stacked-split-view/stacked-split-view.styles.js +23 -16
- package/dist/components/layout/split-views/stacked-split-view/stacked-split-view.styles.js.map +1 -1
- package/dist/components/layout/window/window.styles.d.ts.map +1 -1
- package/dist/components/layout/window/window.styles.js +23 -31
- package/dist/components/layout/window/window.styles.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 +16 -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.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/description-cell/description-cell.styles.js +28 -31
- 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.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/drag-handle-cell/drag-handle-cell.styles.js +22 -28
- package/dist/components/lists-and-menus/cells/drag-handle-cell/drag-handle-cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/cells/icon-cell/icon-cell.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/icon-cell/icon-cell.styles.js +40 -92
- package/dist/components/lists-and-menus/cells/icon-cell/icon-cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.styles.js +26 -25
- package/dist/components/lists-and-menus/cells/spacer-cell/spacer-cell.styles.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 +68 -129
- 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.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.styles.js +75 -136
- package/dist/components/lists-and-menus/cells/title-cell/title-cell.styles.js.map +1 -1
- package/dist/components/lists-and-menus/list/list.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/list/list.styles.js +31 -58
- package/dist/components/lists-and-menus/list/list.styles.js.map +1 -1
- package/dist/components/lists-and-menus/list-item/list-item.styles.d.ts.map +1 -1
- package/dist/components/lists-and-menus/list-item/list-item.styles.js +46 -37
- package/dist/components/lists-and-menus/list-item/list-item.styles.js.map +1 -1
- package/dist/components/lists-and-menus/menu/menu.d.ts +184 -34
- 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 +633 -122
- 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 +133 -119
- 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 +15 -2
- 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.d.ts.map +1 -1
- package/dist/components/navigation/document-tab-bar/document-tab-bar.styles.js +57 -53
- package/dist/components/navigation/document-tab-bar/document-tab-bar.styles.js.map +1 -1
- 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 +11 -9
- 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 +26 -35
- 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 +101 -37
- package/dist/components/navigation/menu-bar/menu-bar.js.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.styles.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.styles.js +5 -4
- package/dist/components/navigation/menu-bar/menu-bar.styles.js.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.template.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar/menu-bar.template.js +13 -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 +18 -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 +35 -5
- 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.d.ts.map +1 -1
- package/dist/components/navigation/menu-bar-item/menu-bar-item.styles.js +25 -27
- package/dist/components/navigation/menu-bar-item/menu-bar-item.styles.js.map +1 -1
- 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 +2 -0
- package/dist/components/navigation/menu-bar-item/menu-bar-item.template.js.map +1 -1
- package/dist/components/navigation/pagination/pagination.d.ts +12 -0
- package/dist/components/navigation/pagination/pagination.d.ts.map +1 -1
- package/dist/components/navigation/pagination/pagination.js +19 -0
- 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 +65 -96
- 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.d.ts.map +1 -1
- package/dist/components/navigation/skip-link/skip-link.styles.js +13 -15
- package/dist/components/navigation/skip-link/skip-link.styles.js.map +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.d.ts +3 -10
- package/dist/components/navigation/tab-bar/tab-bar.d.ts.map +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.js +3 -28
- 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 +44 -110
- package/dist/components/navigation/tab-bar/tab-bar.styles.js.map +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.template.js +1 -1
- package/dist/components/navigation/tab-bar/tab-bar.template.js.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.d.ts +9 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.d.ts.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.js +56 -17
- 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 +68 -75
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.styles.js.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.template.d.ts.map +1 -1
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.template.js +6 -10
- package/dist/components/navigation/top-navigation-bar/top-navigation-bar.template.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 +27 -48
- package/dist/components/navigation/top-title-bar/top-title-bar.styles.js.map +1 -1
- package/dist/components/{prism-typescript-BlIir5CV.js → prism-typescript-DG_p4Wyh.js} +1 -1
- package/dist/components/status-and-feedback/badge/badge.styles.d.ts.map +1 -1
- package/dist/components/status-and-feedback/badge/badge.styles.js +27 -40
- package/dist/components/status-and-feedback/badge/badge.styles.js.map +1 -1
- package/dist/components/status-and-feedback/inline-dialog/inline-dialog.styles.d.ts.map +1 -1
- package/dist/components/status-and-feedback/inline-dialog/inline-dialog.styles.js +37 -38
- package/dist/components/status-and-feedback/inline-dialog/inline-dialog.styles.js.map +1 -1
- package/dist/components/status-and-feedback/modal-dialog/modal-dialog.styles.d.ts.map +1 -1
- package/dist/components/status-and-feedback/modal-dialog/modal-dialog.styles.js +22 -26
- package/dist/components/status-and-feedback/modal-dialog/modal-dialog.styles.js.map +1 -1
- package/dist/css/rich-text.css +85 -58
- package/dist/css/settings.css +69 -36
- package/dist/utilities/color-scheme-repaint.d.ts +44 -0
- package/dist/utilities/color-scheme-repaint.d.ts.map +1 -0
- package/dist/utilities/color-scheme-repaint.js +88 -0
- package/dist/utilities/color-scheme-repaint.js.map +1 -0
- 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/package.json +15 -12
- 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/content/blockquote/blockquote.i18n.d.ts +0 -5
- package/dist/components/content/blockquote/blockquote.i18n.d.ts.map +0 -1
- package/dist/components/content/blockquote/blockquote.i18n.js +0 -4
- package/dist/components/content/blockquote/blockquote.i18n.js.map +0 -1
- package/dist/utilities/popover-guard.d.ts +0 -10
- package/dist/utilities/popover-guard.d.ts.map +0 -1
- package/dist/utilities/popover-guard.js +0 -10
- package/dist/utilities/popover-guard.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,8 +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';
|
|
19
|
-
import { POPOVER_REOPEN_GUARD_MS } from '../../../utilities/popover-guard.js';
|
|
18
|
+
import { isKeyboardMode, isTouchMode } from '../../../utilities/input-modality.js';
|
|
20
19
|
import { breakpoints } from '../../../assets/styles/breakpoints.js';
|
|
21
20
|
// # nldd-menu-divider
|
|
22
21
|
export class NLDDMenuDivider extends LitElement {
|
|
@@ -83,6 +82,11 @@ if (!customElements.get('nldd-menu-group')) {
|
|
|
83
82
|
* @attr {string} icon - Icon name rendered before the text (nldd-icon name).
|
|
84
83
|
* @attr {string} type - Item type: 'button' | 'checkbox' | 'radio'. Default: 'button'.
|
|
85
84
|
* @attr {boolean} selected - Selected state for checkbox and radio types.
|
|
85
|
+
* @attr {boolean} destructive - Marks the item as destructive (red text; red highlight bg). Use for irreversible
|
|
86
|
+
* actions like "Delete". Colour is the only built-in signal, so per WCAG 1.4.1 the
|
|
87
|
+
* item's own label must convey the destructive nature (e.g. "Verwijder") — don't rely
|
|
88
|
+
* on the red alone. Confirming the action (e.g. a follow-up dialog) is the consumer's
|
|
89
|
+
* responsibility.
|
|
86
90
|
* @attr {boolean} disabled - Disabled state.
|
|
87
91
|
* @attr {string} query - Query substring to bold-highlight in text. Set by menu's filter(); also settable by consumers.
|
|
88
92
|
* @attr {string} query-mark-mode - 'match' | 'predictive' (default: 'predictive'). See text-cell for details.
|
|
@@ -101,6 +105,7 @@ export class NLDDMenuItem extends LitElement {
|
|
|
101
105
|
this.icon = '';
|
|
102
106
|
this.type = 'button';
|
|
103
107
|
this.selected = false;
|
|
108
|
+
this.destructive = false;
|
|
104
109
|
this.disabled = false;
|
|
105
110
|
this.query = '';
|
|
106
111
|
this.queryMarkMode = 'predictive';
|
|
@@ -183,12 +188,35 @@ export class NLDDMenuItem extends LitElement {
|
|
|
183
188
|
this._submenuMutationObserver?.disconnect();
|
|
184
189
|
this._submenuMutationObserver = null;
|
|
185
190
|
}
|
|
186
|
-
_handleClick() {
|
|
191
|
+
_handleClick(event) {
|
|
187
192
|
if (this.disabled)
|
|
188
193
|
return;
|
|
189
194
|
// Submenu items don't fire `select` — they open their submenu instead.
|
|
190
195
|
// Item is either an action OR a submenu opener, not both.
|
|
191
196
|
if (this._hasSubmenu) {
|
|
197
|
+
// The button is wired to the submenu via the popoverTargetElement
|
|
198
|
+
// IDL property (set in the template). That marks it as the
|
|
199
|
+
// submenu's invoker so the browser excludes the opener from
|
|
200
|
+
// popover light-dismiss — clicking the opener no longer closes
|
|
201
|
+
// the submenu underneath it. The flip side: the browser would
|
|
202
|
+
// also auto-show the popover with its default positioning,
|
|
203
|
+
// which races our anchor + placement set in `_handleSubmenuOpen`.
|
|
204
|
+
// preventDefault stops the browser action and lets our flow
|
|
205
|
+
// own positioning.
|
|
206
|
+
//
|
|
207
|
+
// We use the IDL property rather than the `popovertarget`
|
|
208
|
+
// attribute because the button lives in this menu-item's shadow
|
|
209
|
+
// root while the submenu is a light-DOM child — attribute-based
|
|
210
|
+
// ID resolution is tree-scoped and would silently fail to find
|
|
211
|
+
// the cross-tree target. The IDL property takes a direct element
|
|
212
|
+
// reference and works across shadow boundaries.
|
|
213
|
+
event?.preventDefault();
|
|
214
|
+
// Click on an already-open opener: keep the submenu visible and
|
|
215
|
+
// the opener visually "active" (aria-expanded + :hover). A second
|
|
216
|
+
// click should not toggle it closed — that conflicts with the
|
|
217
|
+
// hover-open semantics of cascade mode.
|
|
218
|
+
if (this._submenuOpen)
|
|
219
|
+
return;
|
|
192
220
|
this.dispatchEvent(new CustomEvent('submenu-open', {
|
|
193
221
|
detail: { submenu: this._submenuEl, item: this },
|
|
194
222
|
bubbles: true,
|
|
@@ -233,6 +261,9 @@ __decorate([
|
|
|
233
261
|
__decorate([
|
|
234
262
|
property({ type: Boolean, reflect: true })
|
|
235
263
|
], NLDDMenuItem.prototype, "selected", void 0);
|
|
264
|
+
__decorate([
|
|
265
|
+
property({ type: Boolean, reflect: true })
|
|
266
|
+
], NLDDMenuItem.prototype, "destructive", void 0);
|
|
236
267
|
__decorate([
|
|
237
268
|
property({ type: Boolean, reflect: true })
|
|
238
269
|
], NLDDMenuItem.prototype, "disabled", void 0);
|
|
@@ -274,9 +305,9 @@ const defaultFilterFn = (query, item) => {
|
|
|
274
305
|
* @attr {string} empty-text - Text of the default empty-state dialog. Falls back
|
|
275
306
|
* to Dutch i18n "Geen opties beschikbaar".
|
|
276
307
|
* @attr {string} empty-supporting-text - Supporting text of the default empty-state dialog.
|
|
277
|
-
* @attr {string} width - Explicit width. Sets --
|
|
308
|
+
* @attr {string} width - Explicit width. Sets --_width internally.
|
|
278
309
|
* @attr {number} max-items - Maximum number of visible items before scrolling.
|
|
279
|
-
* Sets --
|
|
310
|
+
* Sets --_max-items internally. Default: 0 (no limit).
|
|
280
311
|
* @attr {object} translations - Override one or more translation keys.
|
|
281
312
|
* @attr {Function} filterFn - Custom filter function (query, item) => boolean.
|
|
282
313
|
*
|
|
@@ -299,11 +330,11 @@ export class NLDDMenu extends LitElement {
|
|
|
299
330
|
this.variant = 'menu';
|
|
300
331
|
this.emptyText = '';
|
|
301
332
|
this.emptySupportingText = '';
|
|
302
|
-
/** Explicit width. Sets --
|
|
333
|
+
/** Explicit width. Sets --_width internally. */
|
|
303
334
|
this.width = '';
|
|
304
335
|
/**
|
|
305
336
|
* Maximum number of visible items before the menu scrolls.
|
|
306
|
-
* Sets --
|
|
337
|
+
* Sets --_max-items internally. Default: 0 (no limit).
|
|
307
338
|
*/
|
|
308
339
|
this.maxItems = 0;
|
|
309
340
|
/**
|
|
@@ -331,6 +362,30 @@ export class NLDDMenu extends LitElement {
|
|
|
331
362
|
* by one of this menu's items). null when no submenu is open. */
|
|
332
363
|
this._activeSubmenu = null;
|
|
333
364
|
this._activeSubmenuOpener = null;
|
|
365
|
+
/** Cleanup closure for the currently open child submenu. Invoked by the
|
|
366
|
+
* submenu's `toggle→closed` event, but also callable directly from a
|
|
367
|
+
* chain-close walk so the upper levels can be torn down without waiting
|
|
368
|
+
* for stale toggle events that will never fire (e.g. a hidden drill-in
|
|
369
|
+
* parent whose popover state is already 'closed'). */
|
|
370
|
+
this._activeSubmenuCleanup = null;
|
|
371
|
+
/** Signal that this menu is hiding itself to make room for a deeper drill-in
|
|
372
|
+
* level — its parent's onToggle should skip the normal close cleanup
|
|
373
|
+
* (re-parent + re-show) so the chain stays in deeper-level state. */
|
|
374
|
+
this._drillInHidingForDeeper = false;
|
|
375
|
+
/** Root-owned registry of every submenu currently open in this chain,
|
|
376
|
+
* in open order. Lives on the root menu only; submenus reach it via
|
|
377
|
+
* `_chainRoot`. Drives `_collapseChain()` so collapsing the whole
|
|
378
|
+
* stack never depends on `_activeSubmenu`/`_parentMenu` threading
|
|
379
|
+
* (which real interaction can leave stale). @internal */
|
|
380
|
+
this._openChain = [];
|
|
381
|
+
/** Back-reference to the chain's root, set on every submenu when it
|
|
382
|
+
* opens. Lets `_collapseChain()` find the registry without walking
|
|
383
|
+
* the (possibly stale) parent links. null on the root. @internal */
|
|
384
|
+
this._chainRoot = null;
|
|
385
|
+
/** Force-teardown for this submenu (detach toggle listener, hide,
|
|
386
|
+
* restore DOM, reset state — no parent re-show). Set when the submenu
|
|
387
|
+
* opens; called by `_collapseChain()`. @internal */
|
|
388
|
+
this._collapseSelf = null;
|
|
334
389
|
/** When this menu is itself a submenu, points to the parent menu that
|
|
335
390
|
* opened it. Set by the parent's _handleSubmenuOpen. null on the root.
|
|
336
391
|
* @internal */
|
|
@@ -340,22 +395,66 @@ export class NLDDMenu extends LitElement {
|
|
|
340
395
|
* @internal */
|
|
341
396
|
this._parentItem = null;
|
|
342
397
|
this._isOpen = false;
|
|
343
|
-
|
|
398
|
+
/** Set on the root when a pointer gesture (outside/anchor pointerdown
|
|
399
|
+
* or confirmed tap) collapsed the chain, or when a pointerdown landed
|
|
400
|
+
* on the anchor while the menu was open (covers native light-dismiss).
|
|
401
|
+
* The trailing `click` of that same gesture consumes it in
|
|
402
|
+
* `_handleDocumentClick` and must NOT reopen — gesture-correlated, so
|
|
403
|
+
* it's reliable on touch regardless of pointerup→click timing.
|
|
404
|
+
* @internal */
|
|
405
|
+
this._collapsedByPointerGesture = false;
|
|
344
406
|
this._cleanupAutoUpdate = null;
|
|
407
|
+
// — Status messages (WCAG 4.1.3) ———————————————————————————————————————————
|
|
408
|
+
//
|
|
409
|
+
// Drill-in mode swaps the whole view (parent hides, submenu shows, or
|
|
410
|
+
// vice versa). Focus moves to the new view's first item / opener, so AT
|
|
411
|
+
// announces *an* element — but not that the view itself changed. The
|
|
412
|
+
// polite live region in the shadow DOM (see menu.template) carries that
|
|
413
|
+
// explicit status. Cascade mode keeps both views visible: nothing is
|
|
414
|
+
// swapped, so it never announces.
|
|
415
|
+
/** Double-rAF handle for the announcement awaiting flush, so a rapid
|
|
416
|
+
* follow-up transition can cancel the stale one instead of stacking it. */
|
|
417
|
+
this._announceRaf = 0;
|
|
418
|
+
/** Message currently queued (not yet flushed to the region). Used to
|
|
419
|
+
* drop a doubled event in the same tick while still allowing the same
|
|
420
|
+
* text to be re-announced on a later, genuine transition. */
|
|
421
|
+
this._pendingAnnouncement = '';
|
|
345
422
|
// — Event handlers ————————————————————————————————————————————————————————
|
|
346
423
|
this._handleDocumentClick = (event) => {
|
|
347
424
|
if (this.anchorElement)
|
|
348
425
|
return;
|
|
426
|
+
// Consume the same-gesture collapse marker (set when an outside or
|
|
427
|
+
// anchor pointer gesture collapsed the chain). Read+clear for every
|
|
428
|
+
// click so it never leaks past a single gesture.
|
|
429
|
+
const collapsedByGesture = this._collapsedByPointerGesture;
|
|
430
|
+
this._collapsedByPointerGesture = false;
|
|
349
431
|
const anchorEl = this._getAnchorEl();
|
|
350
432
|
if (!anchorEl)
|
|
351
433
|
return;
|
|
352
434
|
const path = event.composedPath();
|
|
353
435
|
if (!path.includes(anchorEl))
|
|
354
436
|
return;
|
|
437
|
+
// This click is the tail of the very gesture that just collapsed the
|
|
438
|
+
// chain (e.g. tapping the anchor from deep in a drill-in submenu:
|
|
439
|
+
// the submenu's pointerdown/tap already collapsed). It must not
|
|
440
|
+
// reopen. Gesture-correlated, so reliable even when the
|
|
441
|
+
// pointerup→click gap exceeds the time-based reopen guard on touch.
|
|
442
|
+
if (collapsedByGesture)
|
|
443
|
+
return;
|
|
444
|
+
// Drill-in: the root is hidden while a submenu shows, so `_isOpen`
|
|
445
|
+
// is false on the root even though the menu is visibly open. A
|
|
446
|
+
// plain toggle would `showPopover()` the root — i.e. bounce back
|
|
447
|
+
// to the main level. The anchor is a toggle: if anything in the
|
|
448
|
+
// chain is open, clicking it collapses the whole stack (registry-
|
|
449
|
+
// driven, so it works even if chain links went stale).
|
|
450
|
+
if (this._drillInMode && (this._isOpen || this._openChain.length > 0)) {
|
|
451
|
+
this._collapseChain();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
355
454
|
if (this._isOpen) {
|
|
356
455
|
this.hidePopover();
|
|
357
456
|
}
|
|
358
|
-
else
|
|
457
|
+
else {
|
|
359
458
|
this.showPopover();
|
|
360
459
|
}
|
|
361
460
|
};
|
|
@@ -369,10 +468,13 @@ export class NLDDMenu extends LitElement {
|
|
|
369
468
|
// right edge if floating-ui flipped it to the other side). The wedge
|
|
370
469
|
// stays fixed until the cursor enters the submenu, so the user has a
|
|
371
470
|
// predictable, stable area to traverse without losing the submenu.
|
|
372
|
-
// While the cursor is inside the triangle, peer items don't activate
|
|
373
|
-
//
|
|
471
|
+
// While the cursor is inside the triangle, peer items don't activate.
|
|
472
|
+
//
|
|
473
|
+
// We deliberately do NOT auto-close on hover-out: moving the cursor to
|
|
474
|
+
// empty space outside every menu leaves the chain open. The native
|
|
475
|
+
// popover light-dismiss takes care of click-outside, which is the only
|
|
476
|
+
// gesture that should collapse the chain.
|
|
374
477
|
this._hoverOpenTimer = null;
|
|
375
|
-
this._hoverCloseTimer = null;
|
|
376
478
|
this._safeTriangleListener = null;
|
|
377
479
|
this._safeTriangleStallTimer = null;
|
|
378
480
|
this._lastCursorPos = null;
|
|
@@ -380,6 +482,10 @@ export class NLDDMenu extends LitElement {
|
|
|
380
482
|
this._safeTriangleSubmenu = null;
|
|
381
483
|
this._movingTowardSubmenu = false;
|
|
382
484
|
this._handleMenuItemMouseenter = (event) => {
|
|
485
|
+
// Touch suppress: synthesised mouseenter from touch (e.g. lifting
|
|
486
|
+
// after a scroll gesture on a long menu) shouldn't paint a highlight.
|
|
487
|
+
if (isTouchMode())
|
|
488
|
+
return;
|
|
383
489
|
const item = event.target.closest('nldd-menu-item');
|
|
384
490
|
if (!item)
|
|
385
491
|
return;
|
|
@@ -389,8 +495,9 @@ export class NLDDMenu extends LitElement {
|
|
|
389
495
|
if (this.variant !== 'listbox')
|
|
390
496
|
this._clearHighlight();
|
|
391
497
|
this._cancelHoverOpen();
|
|
392
|
-
//
|
|
393
|
-
// outside
|
|
498
|
+
// No auto-close on hover-out: leaving the menu rect leaves the chain
|
|
499
|
+
// open. Click-outside (popover light-dismiss) is the only gesture
|
|
500
|
+
// that collapses the chain.
|
|
394
501
|
};
|
|
395
502
|
// — Debug overlay ————————————————————————————————————————————————————
|
|
396
503
|
this._safeTriangleOverlay = null;
|
|
@@ -433,6 +540,13 @@ export class NLDDMenu extends LitElement {
|
|
|
433
540
|
}
|
|
434
541
|
submenu._parentMenu = this;
|
|
435
542
|
submenu._parentItem = item;
|
|
543
|
+
// Register this submenu in the root's open-chain so `_collapseChain()`
|
|
544
|
+
// can tear the whole stack down without relying on parent links being
|
|
545
|
+
// intact at collapse time. Captured here, while links are fresh.
|
|
546
|
+
const chainRoot = this._rootMenu;
|
|
547
|
+
submenu._chainRoot = chainRoot;
|
|
548
|
+
if (!chainRoot._openChain.includes(submenu))
|
|
549
|
+
chainRoot._openChain.push(submenu);
|
|
436
550
|
if (this._drillInMode) {
|
|
437
551
|
// Drill-in: anchor to the root's anchor so all submenus open at the
|
|
438
552
|
// same screen position — visually stacks. Inherit root placement
|
|
@@ -456,18 +570,58 @@ export class NLDDMenu extends LitElement {
|
|
|
456
570
|
item.removeAttribute('highlighted');
|
|
457
571
|
// Listen once for the submenu's close so we can clear state, ARIA and
|
|
458
572
|
// any leftover safe-triangle plumbing from the cascade open path.
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
573
|
+
const wasDrillIn = this._drillInMode;
|
|
574
|
+
// Shared teardown — runs from `toggle→closed` in the normal flow, and
|
|
575
|
+
// invoked directly from a select-chain-close walk for hidden ancestors
|
|
576
|
+
// whose toggle event will never fire (they were already closed when
|
|
577
|
+
// the deeper level took over the screen). `skipReshow` lets the chain
|
|
578
|
+
// walk suppress the parent-restore step so the whole stack collapses.
|
|
579
|
+
const cleanup = (skipReshow) => {
|
|
463
580
|
submenu.removeEventListener('toggle', onToggle);
|
|
464
581
|
if (this._activeSubmenu === submenu) {
|
|
465
582
|
this._activeSubmenu = null;
|
|
466
583
|
this._activeSubmenuOpener = null;
|
|
584
|
+
this._activeSubmenuCleanup = null;
|
|
467
585
|
}
|
|
468
586
|
submenu._parentMenu = null;
|
|
469
587
|
submenu._parentItem = null;
|
|
588
|
+
// Deregister from the root's open-chain registry.
|
|
589
|
+
const ci = chainRoot._openChain.indexOf(submenu);
|
|
590
|
+
if (ci !== -1)
|
|
591
|
+
chainRoot._openChain.splice(ci, 1);
|
|
592
|
+
submenu._collapseSelf = null;
|
|
593
|
+
submenu._chainRoot = null;
|
|
470
594
|
item._submenuOpen = false;
|
|
595
|
+
// Drill-in: restore the submenu to its original DOM position and
|
|
596
|
+
// re-show the parent so the user returns to the previous view.
|
|
597
|
+
// Captured at open-time so a viewport flip during navigation
|
|
598
|
+
// doesn't leave the chain in an inconsistent state.
|
|
599
|
+
if (wasDrillIn) {
|
|
600
|
+
if (drillInOriginalParent && drillInOriginalParent.isConnected) {
|
|
601
|
+
drillInOriginalParent.insertBefore(submenu, drillInNextSibling);
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
// Original parent was removed from the DOM while the
|
|
605
|
+
// submenu was reparented to <body> (SPA navigation,
|
|
606
|
+
// story teardown, etc.). There's nowhere to restore it
|
|
607
|
+
// to — drop the orphaned node instead of leaking a
|
|
608
|
+
// detached-but-connected element in <body> forever.
|
|
609
|
+
submenu.remove();
|
|
610
|
+
}
|
|
611
|
+
if (!skipReshow && this.isConnected && !this.matches(':popover-open')) {
|
|
612
|
+
this.showPopover?.();
|
|
613
|
+
}
|
|
614
|
+
// One level back (back button / ArrowLeft / Esc) — announce the
|
|
615
|
+
// destination view on the now-visible parent. `skipReshow` is a
|
|
616
|
+
// collapse-the-whole-chain teardown (select / outside-tap /
|
|
617
|
+
// resize): that's a dismiss, not a back-nav, so it stays silent.
|
|
618
|
+
if (!skipReshow && this.isConnected) {
|
|
619
|
+
const dest = this._parentItem?.text;
|
|
620
|
+
this._announce(dest
|
|
621
|
+
? this._t('components.menu.submenu-back-action', { title: dest })
|
|
622
|
+
: this._t('components.menu.back'));
|
|
623
|
+
}
|
|
624
|
+
}
|
|
471
625
|
// Drop any safe-triangle "in transit" highlight on the opener —
|
|
472
626
|
// without this, an opener whose submenu was closed via stall-
|
|
473
627
|
// dismissal or programmatic hidePopover keeps the bold accent
|
|
@@ -480,7 +634,6 @@ export class NLDDMenu extends LitElement {
|
|
|
480
634
|
if (!item.hasAttribute('data-focused')) {
|
|
481
635
|
item.removeAttribute('highlighted');
|
|
482
636
|
}
|
|
483
|
-
this._cancelHoverClose();
|
|
484
637
|
this._stopSafeTriangle();
|
|
485
638
|
// Counterpart to `submenu-open` — consumers tracking submenu state
|
|
486
639
|
// via declarative event listeners (rather than reaching into our
|
|
@@ -491,12 +644,59 @@ export class NLDDMenu extends LitElement {
|
|
|
491
644
|
composed: false,
|
|
492
645
|
}));
|
|
493
646
|
};
|
|
647
|
+
const onToggle = (e) => {
|
|
648
|
+
const tg = e;
|
|
649
|
+
if (tg.newState !== 'closed')
|
|
650
|
+
return;
|
|
651
|
+
// Submenu is hiding to make room for a deeper drill — leave the
|
|
652
|
+
// chain state alone, keep the listener around for the eventual
|
|
653
|
+
// real close.
|
|
654
|
+
if (submenu._drillInHidingForDeeper) {
|
|
655
|
+
submenu._drillInHidingForDeeper = false;
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
// A real toggle-close = one level back: tear down and re-show
|
|
659
|
+
// the parent. Collapsing the whole chain doesn't come through
|
|
660
|
+
// here — `_collapseChain()` drives `_collapseSelf` directly.
|
|
661
|
+
cleanup(false);
|
|
662
|
+
};
|
|
494
663
|
submenu.addEventListener('toggle', onToggle);
|
|
664
|
+
this._activeSubmenuCleanup = cleanup;
|
|
665
|
+
// Force-teardown used by `_collapseChain()`: detach the toggle
|
|
666
|
+
// listener (so this hide doesn't also run the back-nav onToggle),
|
|
667
|
+
// hide, then run the shared cleanup with no parent re-show.
|
|
668
|
+
submenu._collapseSelf = () => {
|
|
669
|
+
submenu.removeEventListener('toggle', onToggle);
|
|
670
|
+
submenu.hidePopover?.();
|
|
671
|
+
cleanup(true);
|
|
672
|
+
};
|
|
673
|
+
// Drill-in: re-parent the submenu to <body> before show so it isn't
|
|
674
|
+
// a popover-stack descendant of the parent — that lets us close the
|
|
675
|
+
// parent without cascading the submenu closed. Original placement is
|
|
676
|
+
// restored when the submenu closes.
|
|
677
|
+
let drillInOriginalParent = null;
|
|
678
|
+
let drillInNextSibling = null;
|
|
679
|
+
if (wasDrillIn) {
|
|
680
|
+
drillInOriginalParent = submenu.parentElement;
|
|
681
|
+
drillInNextSibling = submenu.nextSibling;
|
|
682
|
+
document.body.appendChild(submenu);
|
|
683
|
+
}
|
|
495
684
|
submenu.showPopover?.();
|
|
496
|
-
|
|
685
|
+
if (wasDrillIn) {
|
|
686
|
+
// View just swapped to the submenu — announce the level we
|
|
687
|
+
// entered (the opener's label is this view's title, same string
|
|
688
|
+
// the back button shows). Fires on the now-visible submenu.
|
|
689
|
+
submenu._announce(submenu._t('components.menu.submenu-title', { title: item.text }));
|
|
690
|
+
// Signal to our own parent's onToggle (if any) that this hide is
|
|
691
|
+
// "intentional, making room for a deeper drill" — skip the normal
|
|
692
|
+
// close cleanup that would re-parent us back and re-open them.
|
|
693
|
+
this._drillInHidingForDeeper = true;
|
|
694
|
+
this.hidePopover?.();
|
|
695
|
+
}
|
|
696
|
+
// Start the safe-triangle tracker for cascade-mode submenus only.
|
|
497
697
|
// Drill-in mode replaces the parent view, so there's no "moving toward"
|
|
498
698
|
// path between two visible menus to protect.
|
|
499
|
-
if (!
|
|
699
|
+
if (!wasDrillIn) {
|
|
500
700
|
// Wait one frame so the submenu's getBoundingClientRect reflects its
|
|
501
701
|
// final position (computePosition runs in the toggle handler).
|
|
502
702
|
requestAnimationFrame(() => {
|
|
@@ -520,34 +720,210 @@ export class NLDDMenu extends LitElement {
|
|
|
520
720
|
this._handleBackMouseenter = () => {
|
|
521
721
|
this._clearHighlight();
|
|
522
722
|
};
|
|
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;
|
|
723
|
+
this._touchStartX = 0;
|
|
724
|
+
this._touchStartY = 0;
|
|
725
|
+
/** @internal */
|
|
726
|
+
this._handleMenuTouchStart = (event) => {
|
|
727
|
+
const t = event.touches[0];
|
|
728
|
+
if (!t)
|
|
534
729
|
return;
|
|
535
|
-
|
|
536
|
-
|
|
730
|
+
this._touchStartX = t.clientX;
|
|
731
|
+
this._touchStartY = t.clientY;
|
|
732
|
+
this.removeAttribute('scroll-active');
|
|
733
|
+
};
|
|
734
|
+
/** @internal */
|
|
735
|
+
this._handleMenuTouchMove = (event) => {
|
|
736
|
+
if (this.hasAttribute('scroll-active'))
|
|
537
737
|
return;
|
|
538
|
-
|
|
539
|
-
if (
|
|
540
|
-
|
|
738
|
+
const t = event.touches[0];
|
|
739
|
+
if (!t)
|
|
740
|
+
return;
|
|
741
|
+
const dx = t.clientX - this._touchStartX;
|
|
742
|
+
const dy = t.clientY - this._touchStartY;
|
|
743
|
+
if (Math.hypot(dx, dy) > NLDDMenu._TOUCH_SCROLL_THRESHOLD_PX) {
|
|
744
|
+
this.setAttribute('scroll-active', '');
|
|
541
745
|
}
|
|
542
746
|
};
|
|
747
|
+
/** @internal */
|
|
748
|
+
this._handleMenuTouchEnd = () => {
|
|
749
|
+
this.removeAttribute('scroll-active');
|
|
750
|
+
};
|
|
751
|
+
/** Close the menu on a window resize. Floating-UI's autoUpdate keeps
|
|
752
|
+
* the position in sync with scrolls (where users genuinely expect the
|
|
753
|
+
* menu to follow the anchor), but a full window resize is a layout-
|
|
754
|
+
* shifting interaction the user isn't currently tracking — better to
|
|
755
|
+
* dismiss with a clean slate than to chase the anchor across a
|
|
756
|
+
* reflow, repaint a flipped placement, or get stuck mid-flight when
|
|
757
|
+
* crossing the cascade ↔ drill-in viewport threshold. The window
|
|
758
|
+
* listener is wired only while we're open and torn down on close, so
|
|
759
|
+
* idle menus pay nothing. */
|
|
760
|
+
this._handleWindowResize = () => {
|
|
761
|
+
this._collapseChain();
|
|
762
|
+
};
|
|
543
763
|
/**
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
764
|
+
* Collapse the entire menu chain at once — root + every open submenu —
|
|
765
|
+
* with no "back one level" re-show. Used when the action is final:
|
|
766
|
+
* selecting an item, clicking the anchor, clicking/tapping outside, or
|
|
767
|
+
* a viewport resize.
|
|
768
|
+
*
|
|
769
|
+
* Driven by the root's `_openChain` registry and each submenu's
|
|
770
|
+
* `_collapseSelf`, so it never depends on `_activeSubmenu` /
|
|
771
|
+
* `_parentMenu` threading being intact at collapse time — real
|
|
772
|
+
* interaction can leave those stale, which was the source of the
|
|
773
|
+
* recurring "anchor click doesn't close after the 2nd navigation"
|
|
774
|
+
* bug. Idempotent: every chain level's `select` listener may call it,
|
|
775
|
+
* but the first run empties the registry so the rest are no-ops.
|
|
548
776
|
*/
|
|
549
|
-
this.
|
|
550
|
-
this.
|
|
777
|
+
this._collapseChain = () => {
|
|
778
|
+
const root = this._chainRoot ?? this;
|
|
779
|
+
// Snapshot + clear first so the re-entrant cleanup splices are
|
|
780
|
+
// no-ops and a trailing same-gesture call finds nothing to do.
|
|
781
|
+
const chain = root._openChain;
|
|
782
|
+
root._openChain = [];
|
|
783
|
+
for (let i = chain.length - 1; i >= 0; i--)
|
|
784
|
+
chain[i]._collapseSelf?.();
|
|
785
|
+
if (root._isOpen)
|
|
786
|
+
root.hidePopover?.();
|
|
787
|
+
};
|
|
788
|
+
/**
|
|
789
|
+
* Drill-in click-outside: collapse the entire chain.
|
|
790
|
+
*
|
|
791
|
+
* In drill-in mode only the deepest level is `:popover-open` — popover
|
|
792
|
+
* light-dismiss would close just that level, and our onToggle cleanup
|
|
793
|
+
* would helpfully re-show the previous level. That's the wrong outcome
|
|
794
|
+
* for a click outside the menu: the user is dismissing the whole menu,
|
|
795
|
+
* not navigating one step back. Detect the outside click here and route
|
|
796
|
+
* to `_collapseChain`, which collapses every level at once.
|
|
797
|
+
*
|
|
798
|
+
* Cascade mode falls back to the native popover stack behaviour: a click
|
|
799
|
+
* outside light-dismisses every popover down to the click target's
|
|
800
|
+
* closest ancestor popover, so no extra handling is needed there.
|
|
801
|
+
*
|
|
802
|
+
* For mouse/pen, pointerdown (capture) is the right hook: it fires
|
|
803
|
+
* before light-dismiss does its work, so by the time the toggle event
|
|
804
|
+
* fires our chain walk has already detached listeners and prevented
|
|
805
|
+
* the parent re-show.
|
|
806
|
+
*
|
|
807
|
+
* For touch, an outside pointerdown is ambiguous: it's also how a page
|
|
808
|
+
* scroll begins. Closing the chain on the scroll-start would be
|
|
809
|
+
* inconsistent with a root menu (which native light-dismiss leaves
|
|
810
|
+
* open while you scroll outside it) and annoying. So on touch we defer
|
|
811
|
+
* the decision: track the pointer and only chain-close on a confirmed
|
|
812
|
+
* tap (pointerup without crossing the scroll threshold); a scroll
|
|
813
|
+
* leaves the chain untouched, matching root-menu behaviour.
|
|
814
|
+
*/
|
|
815
|
+
this._dragStartItem = null;
|
|
816
|
+
/**
|
|
817
|
+
* Native HTML `<select>` lets you press on one option, drag to another,
|
|
818
|
+
* and release to pick the one under the pointer. Standard click events
|
|
819
|
+
* don't fire across different elements, so we replicate it manually:
|
|
820
|
+
* remember the pointerdown'd item, listen for the matching pointerup,
|
|
821
|
+
* and if the release lands on a different non-disabled item, fire its
|
|
822
|
+
* `select` directly. The original button's click handler still owns the
|
|
823
|
+
* single-click no-drag path.
|
|
824
|
+
*/
|
|
825
|
+
this._handleItemPointerdown = (event) => {
|
|
826
|
+
if (event.button !== 0)
|
|
827
|
+
return;
|
|
828
|
+
const item = event.target?.closest('nldd-menu-item');
|
|
829
|
+
if (!item || item.disabled)
|
|
830
|
+
return;
|
|
831
|
+
this._dragStartItem = item;
|
|
832
|
+
document.addEventListener('pointerup', this._handleDragRelease, { capture: true, once: true });
|
|
833
|
+
};
|
|
834
|
+
this._handleDragRelease = (event) => {
|
|
835
|
+
const startItem = this._dragStartItem;
|
|
836
|
+
this._dragStartItem = null;
|
|
837
|
+
if (!startItem)
|
|
838
|
+
return;
|
|
839
|
+
// If the menu closed between pointerdown and pointerup (light-dismiss,
|
|
840
|
+
// keyboard shortcut), bail — elementFromPoint could otherwise resolve
|
|
841
|
+
// to an item in a different, newly-opened menu and fire its select.
|
|
842
|
+
if (!this._isOpen)
|
|
843
|
+
return;
|
|
844
|
+
const target = document.elementFromPoint(event.clientX, event.clientY);
|
|
845
|
+
const releaseItem = target?.closest('nldd-menu-item');
|
|
846
|
+
if (!releaseItem || releaseItem === startItem || releaseItem.disabled)
|
|
847
|
+
return;
|
|
848
|
+
// Different item — fire its select. Browser won't dispatch click here
|
|
849
|
+
// because pointerdown and pointerup landed on different targets, so
|
|
850
|
+
// there's no double-fire to suppress.
|
|
851
|
+
releaseItem._handleClick();
|
|
852
|
+
};
|
|
853
|
+
this._handleDocumentPointerdown = (event) => {
|
|
854
|
+
if (!this._isOpen)
|
|
855
|
+
return;
|
|
856
|
+
// Anchor pointerdown on a root menu while open: capture as a gesture
|
|
857
|
+
// so the trailing click — fired after native popover light-dismiss
|
|
858
|
+
// has already closed us — won't reopen us via `_handleDocumentClick`.
|
|
859
|
+
// Drill-in submenus take a different path further down.
|
|
860
|
+
if (!this._isSubmenu) {
|
|
861
|
+
const anchorEl = this._getAnchorEl();
|
|
862
|
+
if (anchorEl && event.composedPath().includes(anchorEl)) {
|
|
863
|
+
this._collapsedByPointerGesture = true;
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (!this._drillInMode)
|
|
868
|
+
return;
|
|
869
|
+
// Only nested drill-in submenus need this: they form a hidden chain
|
|
870
|
+
// of ancestors that won't auto-collapse on outside click. A root
|
|
871
|
+
// menu has nothing to chain-close — popover light-dismiss + the
|
|
872
|
+
// invoker's `popovertargetaction` handle outside / anchor clicks
|
|
873
|
+
// natively. Without this guard a click on the anchor of a root
|
|
874
|
+
// drill-in menu would fire chain-close → hide → auto-sync flips
|
|
875
|
+
// `popovertargetaction` to 'show' → click default action reopens
|
|
876
|
+
// (the "menu sluit en opent consistent weer" bug on small screens).
|
|
877
|
+
if (!this._isSubmenu)
|
|
878
|
+
return;
|
|
879
|
+
// Walk this menu + every parent in the drill-in chain. If the
|
|
880
|
+
// pointerdown landed inside any of them, it's an in-chain click
|
|
881
|
+
// (e.g. on the back button, on the menu background, on an item)
|
|
882
|
+
// and the existing handlers take care of it.
|
|
883
|
+
const path = event.composedPath();
|
|
884
|
+
let menu = this;
|
|
885
|
+
while (menu) {
|
|
886
|
+
if (path.includes(menu))
|
|
887
|
+
return;
|
|
888
|
+
menu = menu._parentMenu;
|
|
889
|
+
}
|
|
890
|
+
// Touch: defer to a confirmed tap so a page-scroll started outside
|
|
891
|
+
// the chain doesn't collapse it (consistent with the root menu,
|
|
892
|
+
// which stays open while scrolling outside).
|
|
893
|
+
if (event.pointerType === 'touch') {
|
|
894
|
+
this._outsideTapStartX = event.clientX;
|
|
895
|
+
this._outsideTapStartY = event.clientY;
|
|
896
|
+
this._teardownOutsideTap();
|
|
897
|
+
this._outsideTapTracking = true;
|
|
898
|
+
document.addEventListener('pointermove', this._outsideTapMove, true);
|
|
899
|
+
document.addEventListener('pointerup', this._outsideTapEnd, true);
|
|
900
|
+
document.addEventListener('pointercancel', this._outsideTapCancel, true);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
(this._chainRoot ?? this)._collapsedByPointerGesture = true;
|
|
904
|
+
this._collapseChain();
|
|
905
|
+
};
|
|
906
|
+
this._outsideTapStartX = 0;
|
|
907
|
+
this._outsideTapStartY = 0;
|
|
908
|
+
this._outsideTapTracking = false;
|
|
909
|
+
this._outsideTapMove = (e) => {
|
|
910
|
+
const dx = e.clientX - this._outsideTapStartX;
|
|
911
|
+
const dy = e.clientY - this._outsideTapStartY;
|
|
912
|
+
if (Math.hypot(dx, dy) > NLDDMenu._TOUCH_SCROLL_THRESHOLD_PX) {
|
|
913
|
+
// Became a scroll — leave the chain open.
|
|
914
|
+
this._teardownOutsideTap();
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
this._outsideTapEnd = () => {
|
|
918
|
+
const wasTap = this._outsideTapTracking;
|
|
919
|
+
this._teardownOutsideTap();
|
|
920
|
+
if (wasTap) {
|
|
921
|
+
(this._chainRoot ?? this)._collapsedByPointerGesture = true;
|
|
922
|
+
this._collapseChain();
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
this._outsideTapCancel = () => {
|
|
926
|
+
this._teardownOutsideTap();
|
|
551
927
|
};
|
|
552
928
|
// — Private handlers ——————————————————————————————————————————————————————
|
|
553
929
|
this._handleKeydown = (event) => {
|
|
@@ -645,10 +1021,21 @@ export class NLDDMenu extends LitElement {
|
|
|
645
1021
|
this._handleToggle = async (event) => {
|
|
646
1022
|
const toggleEvent = event;
|
|
647
1023
|
this._isOpen = toggleEvent.newState === 'open';
|
|
1024
|
+
// Sync the anchor's "is-expanded" state so an anchor button (or any
|
|
1025
|
+
// element exposing an `expanded` property) shows the active visual
|
|
1026
|
+
// while we're open. Skip menu-items as anchors — submenu openers
|
|
1027
|
+
// manage their own `_submenuOpen` lifecycle and don't have an
|
|
1028
|
+
// `expanded` prop. Also opt the anchor into menu/listbox semantics
|
|
1029
|
+
// via `popupType` when it supports it and the consumer hasn't
|
|
1030
|
+
// already chosen a value, so screen readers announce the role.
|
|
1031
|
+
this._syncAnchorPopupState(this._isOpen);
|
|
648
1032
|
if (toggleEvent.newState !== 'open') {
|
|
649
|
-
this._closedAt = Date.now();
|
|
650
1033
|
this._cleanupAutoUpdate?.();
|
|
651
1034
|
this._cleanupAutoUpdate = null;
|
|
1035
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
1036
|
+
document.removeEventListener('pointerdown', this._handleDocumentPointerdown, true);
|
|
1037
|
+
this._teardownOutsideTap();
|
|
1038
|
+
this._clearHighlight();
|
|
652
1039
|
return;
|
|
653
1040
|
}
|
|
654
1041
|
this._updateDividerVisibility();
|
|
@@ -662,6 +1049,14 @@ export class NLDDMenu extends LitElement {
|
|
|
662
1049
|
if (anchorEl) {
|
|
663
1050
|
this._cleanupAutoUpdate = autoUpdate(anchorEl, this, () => this.reposition());
|
|
664
1051
|
}
|
|
1052
|
+
// Wired only while open so idle menus pay nothing and we don't
|
|
1053
|
+
// accidentally close other menus on the page during a resize or
|
|
1054
|
+
// during an unrelated pointerdown elsewhere.
|
|
1055
|
+
window.addEventListener('resize', this._handleWindowResize, { passive: true });
|
|
1056
|
+
// Capture so we run before the popover light-dismiss algorithm
|
|
1057
|
+
// processes the pointerdown — gives us a chance to redirect the
|
|
1058
|
+
// dismissal into a chain-close in drill-in mode.
|
|
1059
|
+
document.addEventListener('pointerdown', this._handleDocumentPointerdown, true);
|
|
665
1060
|
await this.updateComplete;
|
|
666
1061
|
if (this.variant !== 'listbox') {
|
|
667
1062
|
const keyboard = isKeyboardMode();
|
|
@@ -679,8 +1074,14 @@ export class NLDDMenu extends LitElement {
|
|
|
679
1074
|
};
|
|
680
1075
|
}
|
|
681
1076
|
// — i18n ——————————————————————————————————————————————————————————————————
|
|
682
|
-
_t(key) {
|
|
683
|
-
|
|
1077
|
+
_t(key, vars) {
|
|
1078
|
+
let str = this.translations[key] ?? nlddMenuTranslations[key];
|
|
1079
|
+
if (vars) {
|
|
1080
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
1081
|
+
str = str.replace(`{${k}}`, String(v));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
return str;
|
|
684
1085
|
}
|
|
685
1086
|
/** Resolved empty text: emptyText attribute takes precedence, then i18n fallback. */
|
|
686
1087
|
get _resolvedEmptyText() {
|
|
@@ -695,22 +1096,54 @@ export class NLDDMenu extends LitElement {
|
|
|
695
1096
|
const parent = this._parentItem?.text ?? '';
|
|
696
1097
|
return parent ? `${back}: ${parent}` : back;
|
|
697
1098
|
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Write `message` to this menu's polite live region. Clears first, then
|
|
1101
|
+
* sets on a double rAF — the empty→text transition is what makes screen
|
|
1102
|
+
* readers reliably re-announce unchanged-looking content across engines.
|
|
1103
|
+
*
|
|
1104
|
+
* Spam control without muting real navigation: a doubled event in the
|
|
1105
|
+
* same tick (identical message still pending) is dropped; a *different*
|
|
1106
|
+
* message arriving first cancels the stale pending one (rapid navigation
|
|
1107
|
+
* announces only the level you land on); once flushed, the same text may
|
|
1108
|
+
* be announced again for a later genuine transition (e.g. re-entering a
|
|
1109
|
+
* submenu after going back).
|
|
1110
|
+
*/
|
|
1111
|
+
_announce(message) {
|
|
1112
|
+
if (!message)
|
|
1113
|
+
return;
|
|
1114
|
+
if (this._announceRaf && message === this._pendingAnnouncement)
|
|
1115
|
+
return;
|
|
1116
|
+
const region = this.shadowRoot?.querySelector('.menu__live-region');
|
|
1117
|
+
if (!region)
|
|
1118
|
+
return;
|
|
1119
|
+
if (this._announceRaf)
|
|
1120
|
+
cancelAnimationFrame(this._announceRaf);
|
|
1121
|
+
this._pendingAnnouncement = message;
|
|
1122
|
+
region.textContent = '';
|
|
1123
|
+
this._announceRaf = requestAnimationFrame(() => {
|
|
1124
|
+
this._announceRaf = requestAnimationFrame(() => {
|
|
1125
|
+
this._announceRaf = 0;
|
|
1126
|
+
this._pendingAnnouncement = '';
|
|
1127
|
+
region.textContent = message;
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
698
1131
|
// — Lifecycle ——————————————————————————————————————————————————————————————
|
|
699
1132
|
updated(changedProperties) {
|
|
700
1133
|
if (changedProperties.has('width')) {
|
|
701
1134
|
if (this.width) {
|
|
702
|
-
this.style.setProperty('--
|
|
1135
|
+
this.style.setProperty('--_width', this.width);
|
|
703
1136
|
}
|
|
704
1137
|
else {
|
|
705
|
-
this.style.removeProperty('--
|
|
1138
|
+
this.style.removeProperty('--_width');
|
|
706
1139
|
}
|
|
707
1140
|
}
|
|
708
1141
|
if (changedProperties.has('maxItems')) {
|
|
709
1142
|
if (this.maxItems > 0) {
|
|
710
|
-
this.style.setProperty('--
|
|
1143
|
+
this.style.setProperty('--_max-items', String(this.maxItems));
|
|
711
1144
|
}
|
|
712
1145
|
else {
|
|
713
|
-
this.style.removeProperty('--
|
|
1146
|
+
this.style.removeProperty('--_max-items');
|
|
714
1147
|
}
|
|
715
1148
|
}
|
|
716
1149
|
if (changedProperties.has('variant')) {
|
|
@@ -727,6 +1160,66 @@ export class NLDDMenu extends LitElement {
|
|
|
727
1160
|
return document.getElementById(this.anchor);
|
|
728
1161
|
return null;
|
|
729
1162
|
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Pick the drill-in side (above vs below the anchor) purely from the
|
|
1165
|
+
* available viewport space around the anchor — never from content
|
|
1166
|
+
* size. Content-independent so every level of the chain resolves to
|
|
1167
|
+
* the same side and a bigger submenu scrolls internally rather than
|
|
1168
|
+
* flipping the whole stack. Keeps the configured alignment suffix
|
|
1169
|
+
* (`-start` / `-end`) from `placement`.
|
|
1170
|
+
*/
|
|
1171
|
+
_resolveDrillInPlacement(anchorEl) {
|
|
1172
|
+
const base = this.placement || 'bottom-start';
|
|
1173
|
+
const align = base.includes('-') ? base.slice(base.indexOf('-')) : '';
|
|
1174
|
+
const rect = anchorEl.getBoundingClientRect();
|
|
1175
|
+
const margin = this._cssPx('--_viewport-margin');
|
|
1176
|
+
const spaceBelow = window.innerHeight - rect.bottom - margin;
|
|
1177
|
+
const spaceAbove = rect.top - margin;
|
|
1178
|
+
const side = spaceBelow >= spaceAbove ? 'bottom' : 'top';
|
|
1179
|
+
return `${side}${align}`;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Reflect this menu's open state on the anchor so an anchor button (or
|
|
1183
|
+
* any element exposing an `expanded` IDL property) shows the active
|
|
1184
|
+
* "is-expanded" visual while we're open. Skip menu-items as anchors —
|
|
1185
|
+
* they manage their own `_submenuOpen` lifecycle and don't have an
|
|
1186
|
+
* `expanded` property. Also default `popupType` to 'menu' / 'listbox'
|
|
1187
|
+
* (matching this menu's variant) when the anchor supports it and the
|
|
1188
|
+
* consumer hasn't already chosen a value, so screen readers announce
|
|
1189
|
+
* the popup role without each consumer having to set it manually.
|
|
1190
|
+
*
|
|
1191
|
+
* Finally, drive the anchor's `popoverTargetAction` so the browser's
|
|
1192
|
+
* native invoker action always matches the current state explicitly:
|
|
1193
|
+
* `'hide'` while open, `'show'` while closed. We avoid `'toggle'` on
|
|
1194
|
+
* purpose — toggle re-evaluates the popover's live state at click time
|
|
1195
|
+
* and would re-open the menu if it had just been light-dismissed
|
|
1196
|
+
* milliseconds earlier (the source of the "menu briefly closes and
|
|
1197
|
+
* reopens" bug). With explicit show/hide, the worst case is a no-op
|
|
1198
|
+
* (e.g. `'hide'` on an already-closed menu) — never a spurious open.
|
|
1199
|
+
*
|
|
1200
|
+
* The invoker association (`popoverTargetElement`) is set elsewhere
|
|
1201
|
+
* — persistently on anchor change rather than per-toggle — so the
|
|
1202
|
+
* browser's light-dismiss exclusion is in place from the very first
|
|
1203
|
+
* click instead of only after the menu has already opened once.
|
|
1204
|
+
*/
|
|
1205
|
+
_syncAnchorPopupState(isOpen) {
|
|
1206
|
+
const anchor = this._getAnchorEl();
|
|
1207
|
+
if (!anchor)
|
|
1208
|
+
return;
|
|
1209
|
+
if ('expanded' in anchor)
|
|
1210
|
+
anchor.expanded = isOpen;
|
|
1211
|
+
// Seed a valid aria-haspopup when the anchor has no usable popup
|
|
1212
|
+
// type. Falsy covers unset (undefined/null) AND `popup-type=""` —
|
|
1213
|
+
// an empty string is not a valid aria-haspopup token, so it must
|
|
1214
|
+
// still be seeded rather than left invalid. An intentional opt-out
|
|
1215
|
+
// is `popup-type="false"`, which is truthy and preserved here.
|
|
1216
|
+
if (isOpen && 'popupType' in anchor && !anchor.popupType) {
|
|
1217
|
+
anchor.popupType = this.variant === 'listbox' ? 'listbox' : 'menu';
|
|
1218
|
+
}
|
|
1219
|
+
if ('popoverTargetAction' in anchor) {
|
|
1220
|
+
anchor.popoverTargetAction = isOpen ? 'hide' : 'show';
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
730
1223
|
/**
|
|
731
1224
|
* Item-activation logic shared between the native `mouseenter` capture
|
|
732
1225
|
* handler and the safe-triangle recovery paths (sideways exit, direction
|
|
@@ -753,15 +1246,11 @@ export class NLDDMenu extends LitElement {
|
|
|
753
1246
|
if (this._movingTowardSubmenu && item !== this._activeSubmenuOpener)
|
|
754
1247
|
return;
|
|
755
1248
|
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
1249
|
if (this._drillInMode)
|
|
760
1250
|
return; // Touch / narrow viewport: no hover-open.
|
|
761
1251
|
// Settled on a peer item (not the active submenu's opener) — close the
|
|
762
1252
|
// 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.
|
|
1253
|
+
// hover-open below will then schedule its own.
|
|
765
1254
|
if (this._activeSubmenu && item !== this._activeSubmenuOpener) {
|
|
766
1255
|
this._activeSubmenu.hidePopover?.();
|
|
767
1256
|
}
|
|
@@ -824,9 +1313,9 @@ export class NLDDMenu extends LitElement {
|
|
|
824
1313
|
this._safeTrianglePinApex(p, openerRect);
|
|
825
1314
|
}
|
|
826
1315
|
else {
|
|
827
|
-
// 3c. Exited sideways — no protection
|
|
828
|
-
//
|
|
829
|
-
//
|
|
1316
|
+
// 3c. Exited sideways — no protection. Drop the apex and
|
|
1317
|
+
// activate whatever item is under the cursor so peers
|
|
1318
|
+
// take over immediately.
|
|
830
1319
|
this._safeTriangleSidewaysExit(p, wasMoving);
|
|
831
1320
|
}
|
|
832
1321
|
}
|
|
@@ -835,15 +1324,10 @@ export class NLDDMenu extends LitElement {
|
|
|
835
1324
|
this._safeTrianglePolygonTest(p, r);
|
|
836
1325
|
}
|
|
837
1326
|
this._lastCursorPos = p;
|
|
838
|
-
// 5. Common per-frame work: highlight, recovery,
|
|
1327
|
+
// 5. Common per-frame work: highlight, recovery, stall timer.
|
|
839
1328
|
this._safeTriangleSyncOpenerHighlight();
|
|
840
1329
|
this._safeTriangleDirectionReversalRecovery(p, wasMoving);
|
|
841
1330
|
this._safeTriangleScheduleStall();
|
|
842
|
-
if (this._movingTowardSubmenu) {
|
|
843
|
-
this._cancelHoverClose();
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
this._safeTriangleMaybeScheduleLingerClose(p);
|
|
847
1331
|
};
|
|
848
1332
|
window.addEventListener('mousemove', this._safeTriangleListener);
|
|
849
1333
|
}
|
|
@@ -854,7 +1338,6 @@ export class NLDDMenu extends LitElement {
|
|
|
854
1338
|
* return trip to the opener and re-pin a fresh apex for another exit. */
|
|
855
1339
|
_safeTriangleArrived(p) {
|
|
856
1340
|
this._movingTowardSubmenu = false;
|
|
857
|
-
this._cancelHoverClose();
|
|
858
1341
|
this._lastCursorPos = p;
|
|
859
1342
|
this._safeTriangleApex = null;
|
|
860
1343
|
this._activeSubmenuOpener?.removeAttribute('highlighted');
|
|
@@ -963,24 +1446,6 @@ export class NLDDMenu extends LitElement {
|
|
|
963
1446
|
this._activateItemAt(this._lastCursorPos);
|
|
964
1447
|
}, NLDDMenu._SAFE_TRIANGLE_STALL_DISMISS_MS);
|
|
965
1448
|
}
|
|
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
1449
|
/** True when the rect contains the point (inclusive). Returns false for
|
|
985
1450
|
* a missing rect or point — convenient for the optional-chained call
|
|
986
1451
|
* sites in the safe-triangle logic. */
|
|
@@ -1038,17 +1503,11 @@ export class NLDDMenu extends LitElement {
|
|
|
1038
1503
|
this._hoverOpenTimer = null;
|
|
1039
1504
|
}
|
|
1040
1505
|
}
|
|
1041
|
-
_cancelHoverClose() {
|
|
1042
|
-
if (this._hoverCloseTimer !== null) {
|
|
1043
|
-
clearTimeout(this._hoverCloseTimer);
|
|
1044
|
-
this._hoverCloseTimer = null;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
1506
|
/** Recursively checks whether a point sits inside the given menu OR any of
|
|
1048
|
-
* its descendant submenus. Used by the
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1507
|
+
* its descendant submenus. Used by the safe-triangle "arrived" check so a
|
|
1508
|
+
* cursor that has continued past the immediate submenu into a deeper
|
|
1509
|
+
* nested submenu still reads as "in the safe area" — without the recursive
|
|
1510
|
+
* walk we'd treat the deeper hover as an exit and re-pin a stale apex. */
|
|
1052
1511
|
static _isPointInMenuTree(p, menu) {
|
|
1053
1512
|
const r = menu.getBoundingClientRect();
|
|
1054
1513
|
if (p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom)
|
|
@@ -1100,6 +1559,12 @@ export class NLDDMenu extends LitElement {
|
|
|
1100
1559
|
get _isSubmenu() {
|
|
1101
1560
|
return this._parentMenu !== null;
|
|
1102
1561
|
}
|
|
1562
|
+
_teardownOutsideTap() {
|
|
1563
|
+
this._outsideTapTracking = false;
|
|
1564
|
+
document.removeEventListener('pointermove', this._outsideTapMove, true);
|
|
1565
|
+
document.removeEventListener('pointerup', this._outsideTapEnd, true);
|
|
1566
|
+
document.removeEventListener('pointercancel', this._outsideTapCancel, true);
|
|
1567
|
+
}
|
|
1103
1568
|
connectedCallback() {
|
|
1104
1569
|
super.connectedCallback();
|
|
1105
1570
|
if (!this.id) {
|
|
@@ -1115,15 +1580,9 @@ export class NLDDMenu extends LitElement {
|
|
|
1115
1580
|
this.addEventListener('mouseleave', this._handleMouseleave);
|
|
1116
1581
|
this.addEventListener('menu-item-focused', this._handleMenuItemFocused);
|
|
1117
1582
|
this.addEventListener('submenu-open', this._handleSubmenuOpen);
|
|
1118
|
-
this.addEventListener('select', this.
|
|
1583
|
+
this.addEventListener('select', this._collapseChain);
|
|
1584
|
+
this.addEventListener('pointerdown', this._handleItemPointerdown);
|
|
1119
1585
|
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
1586
|
}
|
|
1128
1587
|
firstUpdated() {
|
|
1129
1588
|
this._updateEmptyState();
|
|
@@ -1144,11 +1603,17 @@ export class NLDDMenu extends LitElement {
|
|
|
1144
1603
|
this.removeEventListener('mouseleave', this._handleMouseleave);
|
|
1145
1604
|
this.removeEventListener('menu-item-focused', this._handleMenuItemFocused);
|
|
1146
1605
|
this.removeEventListener('submenu-open', this._handleSubmenuOpen);
|
|
1147
|
-
this.removeEventListener('select', this.
|
|
1606
|
+
this.removeEventListener('select', this._collapseChain);
|
|
1607
|
+
this.removeEventListener('pointerdown', this._handleItemPointerdown);
|
|
1148
1608
|
document.removeEventListener('click', this._handleDocumentClick);
|
|
1149
|
-
|
|
1609
|
+
document.removeEventListener('pointerup', this._handleDragRelease, true);
|
|
1610
|
+
// Defensive: the pointerdown + resize listeners are added on open
|
|
1611
|
+
// and removed on close, but if the menu is torn down mid-open we'd
|
|
1612
|
+
// otherwise leak the global listeners.
|
|
1613
|
+
document.removeEventListener('pointerdown', this._handleDocumentPointerdown, true);
|
|
1614
|
+
this._teardownOutsideTap();
|
|
1615
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
1150
1616
|
this._cancelHoverOpen();
|
|
1151
|
-
this._cancelHoverClose();
|
|
1152
1617
|
this._stopSafeTriangle();
|
|
1153
1618
|
if (this._typeaheadTimer !== null) {
|
|
1154
1619
|
clearTimeout(this._typeaheadTimer);
|
|
@@ -1188,24 +1653,44 @@ export class NLDDMenu extends LitElement {
|
|
|
1188
1653
|
_updateDividerVisibility() {
|
|
1189
1654
|
const children = Array.from(this.children);
|
|
1190
1655
|
children.forEach(el => {
|
|
1191
|
-
|
|
1656
|
+
const tag = el.tagName.toLowerCase();
|
|
1657
|
+
if (tag === 'nldd-menu-divider')
|
|
1192
1658
|
el.removeAttribute('hidden');
|
|
1193
|
-
|
|
1659
|
+
if (tag === 'nldd-menu-group')
|
|
1660
|
+
el.removeAttribute('data-no-bottom-divider');
|
|
1194
1661
|
});
|
|
1195
1662
|
const visible = children.filter(el => !el.hasAttribute('hidden'));
|
|
1663
|
+
// Pass 1: hide redundant explicit dividers (first/last, adjacent to
|
|
1664
|
+
// another divider, adjacent to a group's auto-divider).
|
|
1196
1665
|
visible.forEach((el, index) => {
|
|
1197
1666
|
if (el.tagName.toLowerCase() !== 'nldd-menu-divider')
|
|
1198
1667
|
return;
|
|
1668
|
+
const prevTag = visible[index - 1]?.tagName.toLowerCase();
|
|
1669
|
+
const nextTag = visible[index + 1]?.tagName.toLowerCase();
|
|
1199
1670
|
const isFirst = index === 0;
|
|
1200
1671
|
const isLast = index === visible.length - 1;
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
if (isFirst || isLast || prevIsDivider || nextIsGroup) {
|
|
1672
|
+
if (isFirst || isLast
|
|
1673
|
+
|| prevTag === 'nldd-menu-divider'
|
|
1674
|
+
|| prevTag === 'nldd-menu-group'
|
|
1675
|
+
|| nextTag === 'nldd-menu-group') {
|
|
1206
1676
|
el.setAttribute('hidden', '');
|
|
1207
1677
|
}
|
|
1208
1678
|
});
|
|
1679
|
+
// Pass 2: determine group bottom-divider against the post-pass-1
|
|
1680
|
+
// visible set. Without this re-filter a group followed by an
|
|
1681
|
+
// explicit divider that pass 1 hid (e.g. because the divider is
|
|
1682
|
+
// last, or the items after it are hidden) would still see the
|
|
1683
|
+
// divider as its "next" sibling and keep the bottom border.
|
|
1684
|
+
const remaining = visible.filter(el => !el.hasAttribute('hidden'));
|
|
1685
|
+
remaining.forEach((el, index) => {
|
|
1686
|
+
if (el.tagName.toLowerCase() !== 'nldd-menu-group')
|
|
1687
|
+
return;
|
|
1688
|
+
const nextTag = remaining[index + 1]?.tagName.toLowerCase();
|
|
1689
|
+
const isLast = index === remaining.length - 1;
|
|
1690
|
+
if (isLast || nextTag === 'nldd-menu-group') {
|
|
1691
|
+
el.setAttribute('data-no-bottom-divider', '');
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1209
1694
|
}
|
|
1210
1695
|
// — Public API ————————————————————————————————————————————————————————————
|
|
1211
1696
|
/**
|
|
@@ -1294,29 +1779,48 @@ export class NLDDMenu extends LitElement {
|
|
|
1294
1779
|
getHighlightedId() {
|
|
1295
1780
|
return this.getHighlighted()?.id ?? '';
|
|
1296
1781
|
}
|
|
1782
|
+
/** Read a px-valued CSS custom property as a number. `parseFloat`
|
|
1783
|
+
* keeps fractional pixels; an unset/NaN value falls back to 0. */
|
|
1784
|
+
_cssPx(name) {
|
|
1785
|
+
const v = parseFloat(getComputedStyle(this).getPropertyValue(name));
|
|
1786
|
+
return Number.isNaN(v) ? 0 : v;
|
|
1787
|
+
}
|
|
1297
1788
|
/** Recalculate position and size relative to the anchor element. */
|
|
1298
1789
|
async reposition() {
|
|
1299
1790
|
const anchorEl = this._getAnchorEl();
|
|
1300
1791
|
if (!anchorEl || !this._isOpen)
|
|
1301
1792
|
return;
|
|
1302
|
-
const viewportMargin =
|
|
1793
|
+
const viewportMargin = this._cssPx('--_viewport-margin');
|
|
1303
1794
|
// Cascade-mode submenus: shift up by the menu's own padding so the
|
|
1304
1795
|
// first submenu item lines up vertically with the parent opener item.
|
|
1305
1796
|
// Without this, the submenu's top edge aligns with the opener and the
|
|
1306
1797
|
// inner padding pushes the first item down, leaving a visible step.
|
|
1307
1798
|
const submenuPadding = (this._isSubmenu && !this._drillInMode)
|
|
1308
|
-
?
|
|
1799
|
+
? this._cssPx('--_padding')
|
|
1309
1800
|
: 0;
|
|
1801
|
+
// Drill-in: pick the side from the available space around the
|
|
1802
|
+
// anchor — recomputed every reposition, but content-independent so
|
|
1803
|
+
// a larger submenu never changes it (no mid-chain jump). `flip` is
|
|
1804
|
+
// content-dependent and WOULD jump the stack when a bigger submenu
|
|
1805
|
+
// doesn't fit, so it's dropped here; `size` caps the height and
|
|
1806
|
+
// the menu scrolls internally instead. Re-resolving each time (vs
|
|
1807
|
+
// freezing) keeps it following the anchor on scroll and re-picking
|
|
1808
|
+
// the roomiest side after a close + reopen. Cascade keeps `flip`
|
|
1809
|
+
// (submenus open beside the opener, content-fit matters there).
|
|
1810
|
+
const drillIn = this._drillInMode;
|
|
1811
|
+
const effectivePlacement = drillIn
|
|
1812
|
+
? this._resolveDrillInPlacement(anchorEl)
|
|
1813
|
+
: this.placement;
|
|
1310
1814
|
const { x, y } = await computePosition(anchorEl, this, {
|
|
1311
|
-
placement:
|
|
1815
|
+
placement: effectivePlacement,
|
|
1312
1816
|
middleware: [
|
|
1313
1817
|
offset({ mainAxis: 0, alignmentAxis: -submenuPadding }),
|
|
1314
|
-
flip({ padding: viewportMargin }),
|
|
1818
|
+
...(drillIn ? [] : [flip({ padding: viewportMargin })]),
|
|
1315
1819
|
shift({ padding: viewportMargin }),
|
|
1316
1820
|
size({
|
|
1317
1821
|
padding: viewportMargin,
|
|
1318
1822
|
apply: ({ availableHeight }) => {
|
|
1319
|
-
this.style.setProperty('--
|
|
1823
|
+
this.style.setProperty('--_max-height', `${availableHeight}px`);
|
|
1320
1824
|
},
|
|
1321
1825
|
}),
|
|
1322
1826
|
],
|
|
@@ -1367,14 +1871,11 @@ export class NLDDMenu extends LitElement {
|
|
|
1367
1871
|
}
|
|
1368
1872
|
}
|
|
1369
1873
|
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
1874
|
/** When the cursor sits motionless inside the safe triangle for this
|
|
1374
1875
|
* long, dismiss the triangle and let whatever item is under the cursor
|
|
1375
1876
|
* become interactive. Without this, a paused cursor would stay
|
|
1376
1877
|
* "protected" indefinitely and the user couldn't peer-hover. */
|
|
1377
|
-
NLDDMenu._SAFE_TRIANGLE_STALL_DISMISS_MS =
|
|
1878
|
+
NLDDMenu._SAFE_TRIANGLE_STALL_DISMISS_MS = 500;
|
|
1378
1879
|
/** Pull the wedge apex this many pixels away from the submenu edge (i.e.
|
|
1379
1880
|
* to the left when the submenu is on the right). Widens the wedge near
|
|
1380
1881
|
* the cursor so brief diagonal wobble doesn't pop the cursor out at the
|
|
@@ -1395,10 +1896,20 @@ NLDDMenu._SAFE_TRIANGLE_APEX_X_OFFSET = 4;
|
|
|
1395
1896
|
*
|
|
1396
1897
|
* Cached MediaQueryList shared across instances — `matchMedia()` returns
|
|
1397
1898
|
* a live object and re-evaluating it for every mouseenter/submenu-open
|
|
1398
|
-
* is wasteful.
|
|
1399
|
-
* naturally route through `_handleViewportResize`.
|
|
1899
|
+
* is wasteful.
|
|
1400
1900
|
*/
|
|
1401
1901
|
NLDDMenu._drillInModeQuery = null;
|
|
1902
|
+
// — Touch-scroll press-flash suppression ————————————————————————————————
|
|
1903
|
+
//
|
|
1904
|
+
// On touch, pressing an item sets :active and the synthetic sticky
|
|
1905
|
+
// :hover lands on it, so `.menu__item:active:hover` flashes. If the
|
|
1906
|
+
// gesture becomes an internal scroll (cramped menu), :active lingers
|
|
1907
|
+
// until the browser recognises the pan and the highlight stays for
|
|
1908
|
+
// the whole scroll. We detect a scroll from the touch delta and set
|
|
1909
|
+
// `scroll-active` on the host; the styles neutralise the press flash
|
|
1910
|
+
// while it's set. A pure tap never moves past the threshold, so its
|
|
1911
|
+
// brief press feedback is preserved.
|
|
1912
|
+
NLDDMenu._TOUCH_SCROLL_THRESHOLD_PX = 8;
|
|
1402
1913
|
// — Lifecycle callbacks ————————————————————————————————————————————————————
|
|
1403
1914
|
NLDDMenu._menuIdCounter = 0;
|
|
1404
1915
|
/** Reset the typeahead buffer after this much idle time. 500ms is the
|