@angular/aria 21.0.3 → 21.0.5
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/fesm2022/_accordion-chunk.mjs +108 -0
- package/fesm2022/_accordion-chunk.mjs.map +1 -0
- package/fesm2022/_combobox-chunk.mjs +2 -2
- package/fesm2022/_combobox-chunk.mjs.map +1 -1
- package/fesm2022/_combobox-listbox-chunk.mjs +4 -286
- package/fesm2022/_combobox-listbox-chunk.mjs.map +1 -1
- package/fesm2022/_combobox-tree-chunk.mjs +331 -0
- package/fesm2022/_combobox-tree-chunk.mjs.map +1 -0
- package/fesm2022/_deferred-content-chunk.mjs +124 -0
- package/fesm2022/_deferred-content-chunk.mjs.map +1 -0
- package/fesm2022/_expansion-chunk.mjs +41 -0
- package/fesm2022/_expansion-chunk.mjs.map +1 -0
- package/fesm2022/_list-chunk.mjs +287 -0
- package/fesm2022/_list-chunk.mjs.map +1 -0
- package/fesm2022/_list-navigation-chunk.mjs +1 -1
- package/fesm2022/_list-navigation-chunk.mjs.map +1 -1
- package/fesm2022/_menu-chunk.mjs +515 -0
- package/fesm2022/_menu-chunk.mjs.map +1 -0
- package/fesm2022/_pointer-event-manager-chunk.mjs +2 -82
- package/fesm2022/_pointer-event-manager-chunk.mjs.map +1 -1
- package/fesm2022/_signal-like-chunk.mjs +118 -0
- package/fesm2022/_signal-like-chunk.mjs.map +1 -0
- package/fesm2022/_tabs-chunk.mjs +159 -0
- package/fesm2022/_tabs-chunk.mjs.map +1 -0
- package/fesm2022/_toolbar-widget-group-chunk.mjs +148 -0
- package/fesm2022/_toolbar-widget-group-chunk.mjs.map +1 -0
- package/fesm2022/_widget-chunk.mjs +3 -2
- package/fesm2022/_widget-chunk.mjs.map +1 -1
- package/fesm2022/accordion.mjs +57 -52
- package/fesm2022/accordion.mjs.map +1 -1
- package/fesm2022/aria.mjs +1 -1
- package/fesm2022/aria.mjs.map +1 -1
- package/fesm2022/combobox.mjs +77 -40
- package/fesm2022/combobox.mjs.map +1 -1
- package/fesm2022/grid.mjs +70 -68
- package/fesm2022/grid.mjs.map +1 -1
- package/fesm2022/listbox.mjs +49 -47
- package/fesm2022/listbox.mjs.map +1 -1
- package/fesm2022/menu.mjs +69 -65
- package/fesm2022/menu.mjs.map +1 -1
- package/fesm2022/private.mjs +13 -1402
- package/fesm2022/private.mjs.map +1 -1
- package/fesm2022/tabs.mjs +49 -41
- package/fesm2022/tabs.mjs.map +1 -1
- package/fesm2022/toolbar.mjs +49 -45
- package/fesm2022/toolbar.mjs.map +1 -1
- package/fesm2022/tree.mjs +52 -44
- package/fesm2022/tree.mjs.map +1 -1
- package/package.json +2 -2
- package/types/_accordion-chunk.d.ts +100 -0
- package/types/_combobox-chunk.d.ts +187 -91
- package/types/_deferred-content-chunk.d.ts +42 -0
- package/types/_expansion-chunk.d.ts +40 -0
- package/types/_grid-chunk.d.ts +41 -41
- package/types/_keyboard-event-manager-chunk.d.ts +68 -0
- package/types/_list-chunk.d.ts +8 -9
- package/types/_list-navigation-chunk.d.ts +8 -101
- package/types/_listbox-chunk.d.ts +17 -16
- package/types/_menu-chunk.d.ts +267 -0
- package/types/_pointer-event-manager-chunk.d.ts +34 -0
- package/types/_tabs-chunk.d.ts +153 -0
- package/types/_toolbar-chunk.d.ts +124 -0
- package/types/_tree-chunk.d.ts +185 -0
- package/types/accordion.d.ts +13 -5
- package/types/aria.d.ts +1 -1
- package/types/combobox.d.ts +122 -6
- package/types/grid.d.ts +10 -0
- package/types/listbox.d.ts +17 -5
- package/types/menu.d.ts +23 -5
- package/types/private.d.ts +25 -885
- package/types/tabs.d.ts +24 -8
- package/types/toolbar.d.ts +12 -4
- package/types/tree.d.ts +16 -5
- package/fesm2022/_combobox-popup-chunk.mjs +0 -46
- package/fesm2022/_combobox-popup-chunk.mjs.map +0 -1
- package/types/_combobox-chunk.d2.ts +0 -193
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_list-navigation-chunk.mjs","sources":["../../../../../darwin_arm64-fastbuild-ST-fdfa778d11ba/bin/src/aria/private/behaviors/list-focus/list-focus.ts","../../../../../darwin_arm64-fastbuild-ST-fdfa778d11ba/bin/src/aria/private/behaviors/list-navigation/list-navigation.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {computed, signal} from '@angular/core';\nimport {SignalLike, WritableSignalLike} from '../signal-like/signal-like';\n\n/** Represents an item in a collection, such as a listbox option, than may receive focus. */\nexport interface ListFocusItem {\n /** A unique identifier for the item. */\n id: SignalLike<string>;\n\n /** The html element that should receive focus. */\n element: SignalLike<HTMLElement | undefined>;\n\n /** Whether an item is disabled. */\n disabled: SignalLike<boolean>;\n\n /** The index of the item in the list. */\n index: SignalLike<number>;\n}\n\n/** Represents the required inputs for a collection that contains focusable items. */\nexport interface ListFocusInputs<T extends ListFocusItem> {\n /** The focus strategy used by the list. */\n focusMode: SignalLike<'roving' | 'activedescendant'>;\n\n /** Whether the list is disabled. */\n disabled: SignalLike<boolean>;\n\n /** The items in the list. */\n items: SignalLike<T[]>;\n\n /** The active item. */\n activeItem: WritableSignalLike<T | undefined>;\n\n /** Whether disabled items in the list should be focusable. */\n softDisabled: SignalLike<boolean>;\n\n element: SignalLike<HTMLElement | undefined>;\n}\n\n/** Controls focus for a list of items. */\nexport class ListFocus<T extends ListFocusItem> {\n /** The last item that was active. */\n prevActiveItem = signal<T | undefined>(undefined);\n\n /** The index of the last item that was active. */\n prevActiveIndex = computed(() => {\n return this.prevActiveItem() ? this.inputs.items().indexOf(this.prevActiveItem()!) : -1;\n });\n\n /** The current active index in the list. */\n activeIndex = computed(() => {\n return this.inputs.activeItem() ? this.inputs.items().indexOf(this.inputs.activeItem()!) : -1;\n });\n\n constructor(readonly inputs: ListFocusInputs<T>) {}\n\n /** Whether the list is in a disabled state. */\n isListDisabled(): boolean {\n return this.inputs.disabled() || this.inputs.items().every(i => i.disabled());\n }\n\n /** The id of the current active item. */\n getActiveDescendant(): string | undefined {\n if (this.isListDisabled()) {\n return undefined;\n }\n if (this.inputs.focusMode() === 'roving') {\n return undefined;\n }\n return this.inputs.activeItem()?.id() ?? undefined;\n }\n\n /** The tab index for the list. */\n getListTabIndex(): -1 | 0 {\n if (this.isListDisabled()) {\n return 0;\n }\n return this.inputs.focusMode() === 'activedescendant' ? 0 : -1;\n }\n\n /** Returns the tab index for the given item. */\n getItemTabIndex(item: T): -1 | 0 {\n if (this.isListDisabled()) {\n return -1;\n }\n if (this.inputs.focusMode() === 'activedescendant') {\n return -1;\n }\n return this.inputs.activeItem() === item ? 0 : -1;\n }\n\n /** Moves focus to the given item if it is focusable. */\n focus(item: T, opts?: {focusElement?: boolean}): boolean {\n if (this.isListDisabled() || !this.isFocusable(item)) {\n return false;\n }\n\n this.prevActiveItem.set(this.inputs.activeItem());\n this.inputs.activeItem.set(item);\n\n if (opts?.focusElement || opts?.focusElement === undefined) {\n this.inputs.focusMode() === 'roving'\n ? item.element()?.focus()\n : this.inputs.element()?.focus();\n }\n\n return true;\n }\n\n /** Returns true if the given item can be navigated to. */\n isFocusable(item: T): boolean {\n return !item.disabled() || this.inputs.softDisabled();\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {SignalLike} from '../signal-like/signal-like';\nimport {ListFocus, ListFocusInputs, ListFocusItem} from '../list-focus/list-focus';\n\n/** Represents an item in a collection, such as a listbox option, than can be navigated to. */\nexport interface ListNavigationItem extends ListFocusItem {}\n\n/** Represents the required inputs for a collection that has navigable items. */\nexport interface ListNavigationInputs<T extends ListNavigationItem> extends ListFocusInputs<T> {\n /** Whether focus should wrap when navigating. */\n wrap: SignalLike<boolean>;\n\n /** Whether the list is vertically or horizontally oriented. */\n orientation: SignalLike<'vertical' | 'horizontal'>;\n\n /** The direction that text is read based on the users locale. */\n textDirection: SignalLike<'rtl' | 'ltr'>;\n}\n\n/** Controls navigation for a list of items. */\nexport class ListNavigation<T extends ListNavigationItem> {\n constructor(readonly inputs: ListNavigationInputs<T> & {focusManager: ListFocus<T>}) {}\n\n /** Navigates to the given item. */\n goto(item?: T, opts?: {focusElement?: boolean}): boolean {\n return item ? this.inputs.focusManager.focus(item, opts) : false;\n }\n\n /** Navigates to the next item in the list. */\n next(opts?: {focusElement?: boolean}): boolean {\n return this._advance(1, opts);\n }\n\n /** Peeks the next item in the list. */\n peekNext(): T | undefined {\n return this._peek(1);\n }\n\n /** Navigates to the previous item in the list. */\n prev(opts?: {focusElement?: boolean}): boolean {\n return this._advance(-1, opts);\n }\n\n /** Peeks the previous item in the list. */\n peekPrev(): T | undefined {\n return this._peek(-1);\n }\n\n /** Navigates to the first item in the list. */\n first(opts?: {focusElement?: boolean}): boolean {\n const item = this.peekFirst();\n return item ? this.goto(item, opts) : false;\n }\n\n /** Navigates to the last item in the list. */\n last(opts?: {focusElement?: boolean}): boolean {\n const item = this.peekLast();\n return item ? this.goto(item, opts) : false;\n }\n\n /** Gets the first focusable item from the given list of items. */\n peekFirst(items: T[] = this.inputs.items()): T | undefined {\n return items.find(i => this.inputs.focusManager.isFocusable(i));\n }\n\n /** Gets the last focusable item from the given list of items. */\n peekLast(items: T[] = this.inputs.items()): T | undefined {\n for (let i = items.length - 1; i >= 0; i--) {\n if (this.inputs.focusManager.isFocusable(items[i])) {\n return items[i];\n }\n }\n return;\n }\n\n /** Advances to the next or previous focusable item in the list based on the given delta. */\n private _advance(delta: 1 | -1, opts?: {focusElement?: boolean}): boolean {\n const item = this._peek(delta);\n return item ? this.goto(item, opts) : false;\n }\n\n /** Peeks the next or previous focusable item in the list based on the given delta. */\n private _peek(delta: 1 | -1): T | undefined {\n const items = this.inputs.items();\n const itemCount = items.length;\n const startIndex = this.inputs.focusManager.activeIndex();\n const step = (i: number) =>\n this.inputs.wrap() ? (i + delta + itemCount) % itemCount : i + delta;\n\n // If wrapping is enabled, this loop ultimately terminates when `i` gets back to `startIndex`\n // in the case that all options are disabled. If wrapping is disabled, the loop terminates\n // when the index goes out of bounds.\n for (let i = step(startIndex); i !== startIndex && i < itemCount && i >= 0; i = step(i)) {\n if (this.inputs.focusManager.isFocusable(items[i])) {\n return items[i];\n }\n }\n\n return;\n }\n}\n"],"names":["ListFocus","inputs","prevActiveItem","signal","undefined","prevActiveIndex","computed","items","indexOf","activeIndex","activeItem","constructor","isListDisabled","disabled","every","i","getActiveDescendant","focusMode","id","getListTabIndex","getItemTabIndex","item","focus","opts","isFocusable","set","focusElement","element","softDisabled","ListNavigation","goto","focusManager","next","_advance","peekNext","_peek","prev","peekPrev","first","peekFirst","last","peekLast","find","length","delta","itemCount","startIndex","step","wrap"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_list-navigation-chunk.mjs","sources":["../../../../../darwin_arm64-fastbuild-ST-fdfa778d11ba/bin/src/aria/private/behaviors/list-focus/list-focus.ts","../../../../../darwin_arm64-fastbuild-ST-fdfa778d11ba/bin/src/aria/private/behaviors/list-navigation/list-navigation.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {computed, signal, SignalLike, WritableSignalLike} from '../signal-like/signal-like';\n\n/** Represents an item in a collection, such as a listbox option, than may receive focus. */\nexport interface ListFocusItem {\n /** A unique identifier for the item. */\n id: SignalLike<string>;\n\n /** The html element that should receive focus. */\n element: SignalLike<HTMLElement | undefined>;\n\n /** Whether an item is disabled. */\n disabled: SignalLike<boolean>;\n\n /** The index of the item in the list. */\n index: SignalLike<number>;\n}\n\n/** Represents the required inputs for a collection that contains focusable items. */\nexport interface ListFocusInputs<T extends ListFocusItem> {\n /** The focus strategy used by the list. */\n focusMode: SignalLike<'roving' | 'activedescendant'>;\n\n /** Whether the list is disabled. */\n disabled: SignalLike<boolean>;\n\n /** The items in the list. */\n items: SignalLike<T[]>;\n\n /** The active item. */\n activeItem: WritableSignalLike<T | undefined>;\n\n /** Whether disabled items in the list should be focusable. */\n softDisabled: SignalLike<boolean>;\n\n element: SignalLike<HTMLElement | undefined>;\n}\n\n/** Controls focus for a list of items. */\nexport class ListFocus<T extends ListFocusItem> {\n /** The last item that was active. */\n prevActiveItem = signal<T | undefined>(undefined);\n\n /** The index of the last item that was active. */\n prevActiveIndex = computed(() => {\n return this.prevActiveItem() ? this.inputs.items().indexOf(this.prevActiveItem()!) : -1;\n });\n\n /** The current active index in the list. */\n activeIndex = computed(() => {\n return this.inputs.activeItem() ? this.inputs.items().indexOf(this.inputs.activeItem()!) : -1;\n });\n\n constructor(readonly inputs: ListFocusInputs<T>) {}\n\n /** Whether the list is in a disabled state. */\n isListDisabled(): boolean {\n return this.inputs.disabled() || this.inputs.items().every(i => i.disabled());\n }\n\n /** The id of the current active item. */\n getActiveDescendant(): string | undefined {\n if (this.isListDisabled()) {\n return undefined;\n }\n if (this.inputs.focusMode() === 'roving') {\n return undefined;\n }\n return this.inputs.activeItem()?.id() ?? undefined;\n }\n\n /** The tab index for the list. */\n getListTabIndex(): -1 | 0 {\n if (this.isListDisabled()) {\n return 0;\n }\n return this.inputs.focusMode() === 'activedescendant' ? 0 : -1;\n }\n\n /** Returns the tab index for the given item. */\n getItemTabIndex(item: T): -1 | 0 {\n if (this.isListDisabled()) {\n return -1;\n }\n if (this.inputs.focusMode() === 'activedescendant') {\n return -1;\n }\n return this.inputs.activeItem() === item ? 0 : -1;\n }\n\n /** Moves focus to the given item if it is focusable. */\n focus(item: T, opts?: {focusElement?: boolean}): boolean {\n if (this.isListDisabled() || !this.isFocusable(item)) {\n return false;\n }\n\n this.prevActiveItem.set(this.inputs.activeItem());\n this.inputs.activeItem.set(item);\n\n if (opts?.focusElement || opts?.focusElement === undefined) {\n this.inputs.focusMode() === 'roving'\n ? item.element()?.focus()\n : this.inputs.element()?.focus();\n }\n\n return true;\n }\n\n /** Returns true if the given item can be navigated to. */\n isFocusable(item: T): boolean {\n return !item.disabled() || this.inputs.softDisabled();\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {SignalLike} from '../signal-like/signal-like';\nimport {ListFocus, ListFocusInputs, ListFocusItem} from '../list-focus/list-focus';\n\n/** Represents an item in a collection, such as a listbox option, than can be navigated to. */\nexport interface ListNavigationItem extends ListFocusItem {}\n\n/** Represents the required inputs for a collection that has navigable items. */\nexport interface ListNavigationInputs<T extends ListNavigationItem> extends ListFocusInputs<T> {\n /** Whether focus should wrap when navigating. */\n wrap: SignalLike<boolean>;\n\n /** Whether the list is vertically or horizontally oriented. */\n orientation: SignalLike<'vertical' | 'horizontal'>;\n\n /** The direction that text is read based on the users locale. */\n textDirection: SignalLike<'rtl' | 'ltr'>;\n}\n\n/** Controls navigation for a list of items. */\nexport class ListNavigation<T extends ListNavigationItem> {\n constructor(readonly inputs: ListNavigationInputs<T> & {focusManager: ListFocus<T>}) {}\n\n /** Navigates to the given item. */\n goto(item?: T, opts?: {focusElement?: boolean}): boolean {\n return item ? this.inputs.focusManager.focus(item, opts) : false;\n }\n\n /** Navigates to the next item in the list. */\n next(opts?: {focusElement?: boolean}): boolean {\n return this._advance(1, opts);\n }\n\n /** Peeks the next item in the list. */\n peekNext(): T | undefined {\n return this._peek(1);\n }\n\n /** Navigates to the previous item in the list. */\n prev(opts?: {focusElement?: boolean}): boolean {\n return this._advance(-1, opts);\n }\n\n /** Peeks the previous item in the list. */\n peekPrev(): T | undefined {\n return this._peek(-1);\n }\n\n /** Navigates to the first item in the list. */\n first(opts?: {focusElement?: boolean}): boolean {\n const item = this.peekFirst();\n return item ? this.goto(item, opts) : false;\n }\n\n /** Navigates to the last item in the list. */\n last(opts?: {focusElement?: boolean}): boolean {\n const item = this.peekLast();\n return item ? this.goto(item, opts) : false;\n }\n\n /** Gets the first focusable item from the given list of items. */\n peekFirst(items: T[] = this.inputs.items()): T | undefined {\n return items.find(i => this.inputs.focusManager.isFocusable(i));\n }\n\n /** Gets the last focusable item from the given list of items. */\n peekLast(items: T[] = this.inputs.items()): T | undefined {\n for (let i = items.length - 1; i >= 0; i--) {\n if (this.inputs.focusManager.isFocusable(items[i])) {\n return items[i];\n }\n }\n return;\n }\n\n /** Advances to the next or previous focusable item in the list based on the given delta. */\n private _advance(delta: 1 | -1, opts?: {focusElement?: boolean}): boolean {\n const item = this._peek(delta);\n return item ? this.goto(item, opts) : false;\n }\n\n /** Peeks the next or previous focusable item in the list based on the given delta. */\n private _peek(delta: 1 | -1): T | undefined {\n const items = this.inputs.items();\n const itemCount = items.length;\n const startIndex = this.inputs.focusManager.activeIndex();\n const step = (i: number) =>\n this.inputs.wrap() ? (i + delta + itemCount) % itemCount : i + delta;\n\n // If wrapping is enabled, this loop ultimately terminates when `i` gets back to `startIndex`\n // in the case that all options are disabled. If wrapping is disabled, the loop terminates\n // when the index goes out of bounds.\n for (let i = step(startIndex); i !== startIndex && i < itemCount && i >= 0; i = step(i)) {\n if (this.inputs.focusManager.isFocusable(items[i])) {\n return items[i];\n }\n }\n\n return;\n }\n}\n"],"names":["ListFocus","inputs","prevActiveItem","signal","undefined","prevActiveIndex","computed","items","indexOf","activeIndex","activeItem","constructor","isListDisabled","disabled","every","i","getActiveDescendant","focusMode","id","getListTabIndex","getItemTabIndex","item","focus","opts","isFocusable","set","focusElement","element","softDisabled","ListNavigation","goto","focusManager","next","_advance","peekNext","_peek","prev","peekPrev","first","peekFirst","last","peekLast","find","length","delta","itemCount","startIndex","step","wrap"],"mappings":";;MA8CaA,SAAS,CAAA;EAcCC,MAAA;AAZrBC,EAAAA,cAAc,GAAGC,MAAM,CAAgBC,SAAS,CAAC;EAGjDC,eAAe,GAAGC,QAAQ,CAAC,MAAK;IAC9B,OAAO,IAAI,CAACJ,cAAc,EAAE,GAAG,IAAI,CAACD,MAAM,CAACM,KAAK,EAAE,CAACC,OAAO,CAAC,IAAI,CAACN,cAAc,EAAG,CAAC,GAAG,CAAC,CAAC;AACzF,GAAC,CAAC;EAGFO,WAAW,GAAGH,QAAQ,CAAC,MAAK;AAC1B,IAAA,OAAO,IAAI,CAACL,MAAM,CAACS,UAAU,EAAE,GAAG,IAAI,CAACT,MAAM,CAACM,KAAK,EAAE,CAACC,OAAO,CAAC,IAAI,CAACP,MAAM,CAACS,UAAU,EAAG,CAAC,GAAG,CAAC,CAAC;AAC/F,GAAC,CAAC;EAEFC,WAAAA,CAAqBV,MAA0B,EAAA;IAA1B,IAAM,CAAAA,MAAA,GAANA,MAAM;AAAuB;AAGlDW,EAAAA,cAAcA,GAAA;IACZ,OAAO,IAAI,CAACX,MAAM,CAACY,QAAQ,EAAE,IAAI,IAAI,CAACZ,MAAM,CAACM,KAAK,EAAE,CAACO,KAAK,CAACC,CAAC,IAAIA,CAAC,CAACF,QAAQ,EAAE,CAAC;AAC/E;AAGAG,EAAAA,mBAAmBA,GAAA;AACjB,IAAA,IAAI,IAAI,CAACJ,cAAc,EAAE,EAAE;AACzB,MAAA,OAAOR,SAAS;AAClB;IACA,IAAI,IAAI,CAACH,MAAM,CAACgB,SAAS,EAAE,KAAK,QAAQ,EAAE;AACxC,MAAA,OAAOb,SAAS;AAClB;AACA,IAAA,OAAO,IAAI,CAACH,MAAM,CAACS,UAAU,EAAE,EAAEQ,EAAE,EAAE,IAAId,SAAS;AACpD;AAGAe,EAAAA,eAAeA,GAAA;AACb,IAAA,IAAI,IAAI,CAACP,cAAc,EAAE,EAAE;AACzB,MAAA,OAAO,CAAC;AACV;AACA,IAAA,OAAO,IAAI,CAACX,MAAM,CAACgB,SAAS,EAAE,KAAK,kBAAkB,GAAG,CAAC,GAAG,CAAC,CAAC;AAChE;EAGAG,eAAeA,CAACC,IAAO,EAAA;AACrB,IAAA,IAAI,IAAI,CAACT,cAAc,EAAE,EAAE;AACzB,MAAA,OAAO,CAAC,CAAC;AACX;IACA,IAAI,IAAI,CAACX,MAAM,CAACgB,SAAS,EAAE,KAAK,kBAAkB,EAAE;AAClD,MAAA,OAAO,CAAC,CAAC;AACX;AACA,IAAA,OAAO,IAAI,CAAChB,MAAM,CAACS,UAAU,EAAE,KAAKW,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;AACnD;AAGAC,EAAAA,KAAKA,CAACD,IAAO,EAAEE,IAA+B,EAAA;AAC5C,IAAA,IAAI,IAAI,CAACX,cAAc,EAAE,IAAI,CAAC,IAAI,CAACY,WAAW,CAACH,IAAI,CAAC,EAAE;AACpD,MAAA,OAAO,KAAK;AACd;AAEA,IAAA,IAAI,CAACnB,cAAc,CAACuB,GAAG,CAAC,IAAI,CAACxB,MAAM,CAACS,UAAU,EAAE,CAAC;IACjD,IAAI,CAACT,MAAM,CAACS,UAAU,CAACe,GAAG,CAACJ,IAAI,CAAC;IAEhC,IAAIE,IAAI,EAAEG,YAAY,IAAIH,IAAI,EAAEG,YAAY,KAAKtB,SAAS,EAAE;AAC1D,MAAA,IAAI,CAACH,MAAM,CAACgB,SAAS,EAAE,KAAK,QAAQ,GAChCI,IAAI,CAACM,OAAO,EAAE,EAAEL,KAAK,EAAE,GACvB,IAAI,CAACrB,MAAM,CAAC0B,OAAO,EAAE,EAAEL,KAAK,EAAE;AACpC;AAEA,IAAA,OAAO,IAAI;AACb;EAGAE,WAAWA,CAACH,IAAO,EAAA;AACjB,IAAA,OAAO,CAACA,IAAI,CAACR,QAAQ,EAAE,IAAI,IAAI,CAACZ,MAAM,CAAC2B,YAAY,EAAE;AACvD;AACD;;MC5FYC,cAAc,CAAA;EACJ5B,MAAA;EAArBU,WAAAA,CAAqBV,MAA8D,EAAA;IAA9D,IAAM,CAAAA,MAAA,GAANA,MAAM;AAA2D;AAGtF6B,EAAAA,IAAIA,CAACT,IAAQ,EAAEE,IAA+B,EAAA;AAC5C,IAAA,OAAOF,IAAI,GAAG,IAAI,CAACpB,MAAM,CAAC8B,YAAY,CAACT,KAAK,CAACD,IAAI,EAAEE,IAAI,CAAC,GAAG,KAAK;AAClE;EAGAS,IAAIA,CAACT,IAA+B,EAAA;AAClC,IAAA,OAAO,IAAI,CAACU,QAAQ,CAAC,CAAC,EAAEV,IAAI,CAAC;AAC/B;AAGAW,EAAAA,QAAQA,GAAA;AACN,IAAA,OAAO,IAAI,CAACC,KAAK,CAAC,CAAC,CAAC;AACtB;EAGAC,IAAIA,CAACb,IAA+B,EAAA;IAClC,OAAO,IAAI,CAACU,QAAQ,CAAC,CAAC,CAAC,EAAEV,IAAI,CAAC;AAChC;AAGAc,EAAAA,QAAQA,GAAA;AACN,IAAA,OAAO,IAAI,CAACF,KAAK,CAAC,CAAC,CAAC,CAAC;AACvB;EAGAG,KAAKA,CAACf,IAA+B,EAAA;AACnC,IAAA,MAAMF,IAAI,GAAG,IAAI,CAACkB,SAAS,EAAE;IAC7B,OAAOlB,IAAI,GAAG,IAAI,CAACS,IAAI,CAACT,IAAI,EAAEE,IAAI,CAAC,GAAG,KAAK;AAC7C;EAGAiB,IAAIA,CAACjB,IAA+B,EAAA;AAClC,IAAA,MAAMF,IAAI,GAAG,IAAI,CAACoB,QAAQ,EAAE;IAC5B,OAAOpB,IAAI,GAAG,IAAI,CAACS,IAAI,CAACT,IAAI,EAAEE,IAAI,CAAC,GAAG,KAAK;AAC7C;EAGAgB,SAASA,CAAChC,KAAa,GAAA,IAAI,CAACN,MAAM,CAACM,KAAK,EAAE,EAAA;AACxC,IAAA,OAAOA,KAAK,CAACmC,IAAI,CAAC3B,CAAC,IAAI,IAAI,CAACd,MAAM,CAAC8B,YAAY,CAACP,WAAW,CAACT,CAAC,CAAC,CAAC;AACjE;EAGA0B,QAAQA,CAAClC,KAAa,GAAA,IAAI,CAACN,MAAM,CAACM,KAAK,EAAE,EAAA;AACvC,IAAA,KAAK,IAAIQ,CAAC,GAAGR,KAAK,CAACoC,MAAM,GAAG,CAAC,EAAE5B,CAAC,IAAI,CAAC,EAAEA,CAAC,EAAE,EAAE;AAC1C,MAAA,IAAI,IAAI,CAACd,MAAM,CAAC8B,YAAY,CAACP,WAAW,CAACjB,KAAK,CAACQ,CAAC,CAAC,CAAC,EAAE;QAClD,OAAOR,KAAK,CAACQ,CAAC,CAAC;AACjB;AACF;AACA,IAAA;AACF;AAGQkB,EAAAA,QAAQA,CAACW,KAAa,EAAErB,IAA+B,EAAA;AAC7D,IAAA,MAAMF,IAAI,GAAG,IAAI,CAACc,KAAK,CAACS,KAAK,CAAC;IAC9B,OAAOvB,IAAI,GAAG,IAAI,CAACS,IAAI,CAACT,IAAI,EAAEE,IAAI,CAAC,GAAG,KAAK;AAC7C;EAGQY,KAAKA,CAACS,KAAa,EAAA;IACzB,MAAMrC,KAAK,GAAG,IAAI,CAACN,MAAM,CAACM,KAAK,EAAE;AACjC,IAAA,MAAMsC,SAAS,GAAGtC,KAAK,CAACoC,MAAM;IAC9B,MAAMG,UAAU,GAAG,IAAI,CAAC7C,MAAM,CAAC8B,YAAY,CAACtB,WAAW,EAAE;IACzD,MAAMsC,IAAI,GAAIhC,CAAS,IACrB,IAAI,CAACd,MAAM,CAAC+C,IAAI,EAAE,GAAG,CAACjC,CAAC,GAAG6B,KAAK,GAAGC,SAAS,IAAIA,SAAS,GAAG9B,CAAC,GAAG6B,KAAK;IAKtE,KAAK,IAAI7B,CAAC,GAAGgC,IAAI,CAACD,UAAU,CAAC,EAAE/B,CAAC,KAAK+B,UAAU,IAAI/B,CAAC,GAAG8B,SAAS,IAAI9B,CAAC,IAAI,CAAC,EAAEA,CAAC,GAAGgC,IAAI,CAAChC,CAAC,CAAC,EAAE;AACvF,MAAA,IAAI,IAAI,CAACd,MAAM,CAAC8B,YAAY,CAACP,WAAW,CAACjB,KAAK,CAACQ,CAAC,CAAC,CAAC,EAAE;QAClD,OAAOR,KAAK,CAACQ,CAAC,CAAC;AACjB;AACF;AAEA,IAAA;AACF;AACD;;;;"}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import { computed, signal, KeyboardEventManager } from './_signal-like-chunk.mjs';
|
|
2
|
+
import { List } from './_list-chunk.mjs';
|
|
3
|
+
|
|
4
|
+
class MenuPattern {
|
|
5
|
+
inputs;
|
|
6
|
+
id;
|
|
7
|
+
role = () => 'menu';
|
|
8
|
+
disabled = () => this.inputs.disabled();
|
|
9
|
+
visible = computed(() => this.inputs.parent() ? !!this.inputs.parent()?.expanded() : true);
|
|
10
|
+
listBehavior;
|
|
11
|
+
isFocused = signal(false);
|
|
12
|
+
hasBeenFocused = signal(false);
|
|
13
|
+
hasBeenHovered = signal(false);
|
|
14
|
+
_openTimeout;
|
|
15
|
+
_closeTimeout;
|
|
16
|
+
tabIndex = () => this.listBehavior.tabIndex();
|
|
17
|
+
shouldFocus = computed(() => {
|
|
18
|
+
const root = this.root();
|
|
19
|
+
if (root instanceof MenuTriggerPattern) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (root instanceof MenuBarPattern || root instanceof MenuPattern) {
|
|
23
|
+
return root.isFocused();
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
});
|
|
27
|
+
_expandKey = computed(() => {
|
|
28
|
+
return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
|
|
29
|
+
});
|
|
30
|
+
_collapseKey = computed(() => {
|
|
31
|
+
return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
32
|
+
});
|
|
33
|
+
dynamicSpaceKey = computed(() => this.listBehavior.isTyping() ? '' : ' ');
|
|
34
|
+
typeaheadRegexp = /^.$/;
|
|
35
|
+
root = computed(() => {
|
|
36
|
+
const parent = this.inputs.parent();
|
|
37
|
+
if (!parent) {
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
if (parent instanceof MenuTriggerPattern) {
|
|
41
|
+
return parent;
|
|
42
|
+
}
|
|
43
|
+
const grandparent = parent.inputs.parent();
|
|
44
|
+
if (grandparent instanceof MenuBarPattern) {
|
|
45
|
+
return grandparent;
|
|
46
|
+
}
|
|
47
|
+
return grandparent?.root();
|
|
48
|
+
});
|
|
49
|
+
keydownManager = computed(() => {
|
|
50
|
+
return new KeyboardEventManager().on('ArrowDown', () => this.next()).on('ArrowUp', () => this.prev()).on('Home', () => this.first()).on('End', () => this.last()).on('Enter', () => this.trigger()).on('Escape', () => this.closeAll()).on(this._expandKey, () => this.expand()).on(this._collapseKey, () => this.collapse()).on(this.dynamicSpaceKey, () => this.trigger()).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key));
|
|
51
|
+
});
|
|
52
|
+
constructor(inputs) {
|
|
53
|
+
this.inputs = inputs;
|
|
54
|
+
this.id = inputs.id;
|
|
55
|
+
this.listBehavior = new List({
|
|
56
|
+
...inputs,
|
|
57
|
+
values: signal([])
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
setDefaultState() {
|
|
61
|
+
if (!this.inputs.parent()) {
|
|
62
|
+
this.listBehavior.goto(this.inputs.items()[0], {
|
|
63
|
+
focusElement: false
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
onKeydown(event) {
|
|
68
|
+
this.keydownManager().handle(event);
|
|
69
|
+
}
|
|
70
|
+
onMouseOver(event) {
|
|
71
|
+
if (!this.visible()) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.hasBeenHovered.set(true);
|
|
75
|
+
const item = this.inputs.items().find(i => i.element()?.contains(event.target));
|
|
76
|
+
if (!item) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const parent = this.inputs.parent();
|
|
80
|
+
const activeItem = this?.inputs.activeItem();
|
|
81
|
+
if (parent instanceof MenuItemPattern) {
|
|
82
|
+
const grandparent = parent.inputs.parent();
|
|
83
|
+
if (grandparent instanceof MenuPattern) {
|
|
84
|
+
grandparent._clearTimeouts();
|
|
85
|
+
grandparent.listBehavior.goto(parent, {
|
|
86
|
+
focusElement: false
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (activeItem && activeItem !== item) {
|
|
91
|
+
this._closeItem(activeItem);
|
|
92
|
+
}
|
|
93
|
+
if (item.expanded()) {
|
|
94
|
+
this._clearCloseTimeout();
|
|
95
|
+
}
|
|
96
|
+
this._openItem(item);
|
|
97
|
+
this.listBehavior.goto(item, {
|
|
98
|
+
focusElement: this.shouldFocus()
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
_closeItem(item) {
|
|
102
|
+
this._clearOpenTimeout();
|
|
103
|
+
if (!this._closeTimeout) {
|
|
104
|
+
this._closeTimeout = setTimeout(() => {
|
|
105
|
+
item.close();
|
|
106
|
+
this._closeTimeout = undefined;
|
|
107
|
+
}, this.inputs.expansionDelay());
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
_openItem(item) {
|
|
111
|
+
this._clearOpenTimeout();
|
|
112
|
+
this._openTimeout = setTimeout(() => {
|
|
113
|
+
item.open();
|
|
114
|
+
this._openTimeout = undefined;
|
|
115
|
+
}, this.inputs.expansionDelay());
|
|
116
|
+
}
|
|
117
|
+
onMouseOut(event) {
|
|
118
|
+
this._clearOpenTimeout();
|
|
119
|
+
if (this.isFocused()) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const root = this.root();
|
|
123
|
+
const parent = this.inputs.parent();
|
|
124
|
+
const relatedTarget = event.relatedTarget;
|
|
125
|
+
if (!root || !parent || parent instanceof MenuTriggerPattern) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const grandparent = parent.inputs.parent();
|
|
129
|
+
if (!grandparent || grandparent instanceof MenuBarPattern) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!grandparent.inputs.element()?.contains(relatedTarget)) {
|
|
133
|
+
parent.close();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
onClick(event) {
|
|
137
|
+
const relatedTarget = event.target;
|
|
138
|
+
const item = this.inputs.items().find(i => i.element()?.contains(relatedTarget));
|
|
139
|
+
if (item) {
|
|
140
|
+
item.open();
|
|
141
|
+
this.listBehavior.goto(item);
|
|
142
|
+
this.submit(item);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
onFocusIn() {
|
|
146
|
+
this.isFocused.set(true);
|
|
147
|
+
this.hasBeenFocused.set(true);
|
|
148
|
+
}
|
|
149
|
+
onFocusOut(event) {
|
|
150
|
+
const parent = this.inputs.parent();
|
|
151
|
+
const parentEl = parent?.inputs.element();
|
|
152
|
+
const relatedTarget = event.relatedTarget;
|
|
153
|
+
if (!relatedTarget) {
|
|
154
|
+
this.isFocused.set(false);
|
|
155
|
+
this.inputs.parent()?.close({
|
|
156
|
+
refocus: true
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (parent instanceof MenuItemPattern) {
|
|
160
|
+
const grandparent = parent.inputs.parent();
|
|
161
|
+
const siblings = grandparent?.inputs.items().filter(i => i !== parent);
|
|
162
|
+
const item = siblings?.find(i => i.element()?.contains(relatedTarget));
|
|
163
|
+
if (item) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (this.visible() && !parentEl?.contains(relatedTarget) && !this.inputs.element()?.contains(relatedTarget)) {
|
|
168
|
+
this.isFocused.set(false);
|
|
169
|
+
this.inputs.parent()?.close();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
prev() {
|
|
173
|
+
this.inputs.activeItem()?.close();
|
|
174
|
+
this.listBehavior.prev();
|
|
175
|
+
}
|
|
176
|
+
next() {
|
|
177
|
+
this.inputs.activeItem()?.close();
|
|
178
|
+
this.listBehavior.next();
|
|
179
|
+
}
|
|
180
|
+
first() {
|
|
181
|
+
this.inputs.activeItem()?.close();
|
|
182
|
+
this.listBehavior.first();
|
|
183
|
+
}
|
|
184
|
+
last() {
|
|
185
|
+
this.inputs.activeItem()?.close();
|
|
186
|
+
this.listBehavior.last();
|
|
187
|
+
}
|
|
188
|
+
trigger() {
|
|
189
|
+
this.inputs.activeItem()?.hasPopup() ? this.inputs.activeItem()?.open({
|
|
190
|
+
first: true
|
|
191
|
+
}) : this.submit();
|
|
192
|
+
}
|
|
193
|
+
submit(item = this.inputs.activeItem()) {
|
|
194
|
+
const root = this.root();
|
|
195
|
+
if (item && !item.disabled()) {
|
|
196
|
+
const isMenu = root instanceof MenuPattern;
|
|
197
|
+
const isMenuBar = root instanceof MenuBarPattern;
|
|
198
|
+
const isMenuTrigger = root instanceof MenuTriggerPattern;
|
|
199
|
+
if (!item.submenu() && isMenuTrigger) {
|
|
200
|
+
root.close({
|
|
201
|
+
refocus: true
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (!item.submenu() && isMenuBar) {
|
|
205
|
+
root.close();
|
|
206
|
+
root?.inputs.onSelect?.(item.value());
|
|
207
|
+
}
|
|
208
|
+
if (!item.submenu() && isMenu) {
|
|
209
|
+
root.inputs.activeItem()?.close({
|
|
210
|
+
refocus: true
|
|
211
|
+
});
|
|
212
|
+
root?.inputs.onSelect?.(item.value());
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
collapse() {
|
|
217
|
+
const root = this.root();
|
|
218
|
+
const parent = this.inputs.parent();
|
|
219
|
+
if (parent instanceof MenuItemPattern && !(parent.inputs.parent() instanceof MenuBarPattern)) {
|
|
220
|
+
parent.close({
|
|
221
|
+
refocus: true
|
|
222
|
+
});
|
|
223
|
+
} else if (root instanceof MenuBarPattern) {
|
|
224
|
+
root.prev();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
expand() {
|
|
228
|
+
const root = this.root();
|
|
229
|
+
const activeItem = this.inputs.activeItem();
|
|
230
|
+
if (activeItem?.submenu()) {
|
|
231
|
+
activeItem.open({
|
|
232
|
+
first: true
|
|
233
|
+
});
|
|
234
|
+
} else if (root instanceof MenuBarPattern) {
|
|
235
|
+
root.next();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
close() {
|
|
239
|
+
this.inputs.parent()?.close();
|
|
240
|
+
}
|
|
241
|
+
closeAll() {
|
|
242
|
+
const root = this.root();
|
|
243
|
+
if (root instanceof MenuTriggerPattern) {
|
|
244
|
+
root.close({
|
|
245
|
+
refocus: true
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
if (root instanceof MenuBarPattern) {
|
|
249
|
+
root.close();
|
|
250
|
+
}
|
|
251
|
+
if (root instanceof MenuPattern) {
|
|
252
|
+
root.inputs.activeItem()?.close({
|
|
253
|
+
refocus: true
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
_clearTimeouts() {
|
|
258
|
+
this._clearOpenTimeout();
|
|
259
|
+
this._clearCloseTimeout();
|
|
260
|
+
}
|
|
261
|
+
_clearOpenTimeout() {
|
|
262
|
+
if (this._openTimeout) {
|
|
263
|
+
clearTimeout(this._openTimeout);
|
|
264
|
+
this._openTimeout = undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
_clearCloseTimeout() {
|
|
268
|
+
if (this._closeTimeout) {
|
|
269
|
+
clearTimeout(this._closeTimeout);
|
|
270
|
+
this._closeTimeout = undefined;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
class MenuBarPattern {
|
|
275
|
+
inputs;
|
|
276
|
+
listBehavior;
|
|
277
|
+
tabIndex = () => this.listBehavior.tabIndex();
|
|
278
|
+
_nextKey = computed(() => {
|
|
279
|
+
return this.inputs.textDirection() === 'rtl' ? 'ArrowLeft' : 'ArrowRight';
|
|
280
|
+
});
|
|
281
|
+
_previousKey = computed(() => {
|
|
282
|
+
return this.inputs.textDirection() === 'rtl' ? 'ArrowRight' : 'ArrowLeft';
|
|
283
|
+
});
|
|
284
|
+
dynamicSpaceKey = computed(() => this.listBehavior.isTyping() ? '' : ' ');
|
|
285
|
+
typeaheadRegexp = /^.$/;
|
|
286
|
+
isFocused = signal(false);
|
|
287
|
+
hasBeenFocused = signal(false);
|
|
288
|
+
disabled = () => this.inputs.disabled();
|
|
289
|
+
keydownManager = computed(() => {
|
|
290
|
+
return new KeyboardEventManager().on(this._nextKey, () => this.next()).on(this._previousKey, () => this.prev()).on('End', () => this.listBehavior.last()).on('Home', () => this.listBehavior.first()).on('Enter', () => this.inputs.activeItem()?.open({
|
|
291
|
+
first: true
|
|
292
|
+
})).on('ArrowUp', () => this.inputs.activeItem()?.open({
|
|
293
|
+
last: true
|
|
294
|
+
})).on('ArrowDown', () => this.inputs.activeItem()?.open({
|
|
295
|
+
first: true
|
|
296
|
+
})).on(this.dynamicSpaceKey, () => this.inputs.activeItem()?.open({
|
|
297
|
+
first: true
|
|
298
|
+
})).on(this.typeaheadRegexp, e => this.listBehavior.search(e.key));
|
|
299
|
+
});
|
|
300
|
+
constructor(inputs) {
|
|
301
|
+
this.inputs = inputs;
|
|
302
|
+
this.listBehavior = new List(inputs);
|
|
303
|
+
}
|
|
304
|
+
setDefaultState() {
|
|
305
|
+
this.inputs.activeItem.set(this.inputs.items()[0]);
|
|
306
|
+
}
|
|
307
|
+
onKeydown(event) {
|
|
308
|
+
this.keydownManager().handle(event);
|
|
309
|
+
}
|
|
310
|
+
onClick(event) {
|
|
311
|
+
const item = this.inputs.items().find(i => i.element()?.contains(event.target));
|
|
312
|
+
if (!item) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
this.goto(item);
|
|
316
|
+
item.expanded() ? item.close() : item.open();
|
|
317
|
+
}
|
|
318
|
+
onMouseOver(event) {
|
|
319
|
+
const item = this.inputs.items().find(i => i.element()?.contains(event.target));
|
|
320
|
+
if (item) {
|
|
321
|
+
this.goto(item, {
|
|
322
|
+
focusElement: this.isFocused()
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
onFocusIn() {
|
|
327
|
+
this.isFocused.set(true);
|
|
328
|
+
this.hasBeenFocused.set(true);
|
|
329
|
+
}
|
|
330
|
+
onFocusOut(event) {
|
|
331
|
+
const relatedTarget = event.relatedTarget;
|
|
332
|
+
if (!this.inputs.element()?.contains(relatedTarget)) {
|
|
333
|
+
this.isFocused.set(false);
|
|
334
|
+
this.close();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
goto(item, opts) {
|
|
338
|
+
const prevItem = this.inputs.activeItem();
|
|
339
|
+
this.listBehavior.goto(item, opts);
|
|
340
|
+
if (prevItem?.expanded()) {
|
|
341
|
+
prevItem?.close();
|
|
342
|
+
this.inputs.activeItem()?.open();
|
|
343
|
+
}
|
|
344
|
+
if (item === prevItem) {
|
|
345
|
+
if (item.expanded() && item.submenu()?.inputs.activeItem()) {
|
|
346
|
+
item.submenu()?.inputs.activeItem()?.close();
|
|
347
|
+
item.submenu()?.listBehavior.unfocus();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
next() {
|
|
352
|
+
const prevItem = this.inputs.activeItem();
|
|
353
|
+
this.listBehavior.next();
|
|
354
|
+
if (prevItem?.expanded()) {
|
|
355
|
+
prevItem?.close();
|
|
356
|
+
this.inputs.activeItem()?.open({
|
|
357
|
+
first: true
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
prev() {
|
|
362
|
+
const prevItem = this.inputs.activeItem();
|
|
363
|
+
this.listBehavior.prev();
|
|
364
|
+
if (prevItem?.expanded()) {
|
|
365
|
+
prevItem?.close();
|
|
366
|
+
this.inputs.activeItem()?.open({
|
|
367
|
+
first: true
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
close() {
|
|
372
|
+
this.inputs.activeItem()?.close({
|
|
373
|
+
refocus: this.isFocused()
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
class MenuTriggerPattern {
|
|
378
|
+
inputs;
|
|
379
|
+
expanded = signal(false);
|
|
380
|
+
hasBeenFocused = signal(false);
|
|
381
|
+
role = () => 'button';
|
|
382
|
+
hasPopup = () => true;
|
|
383
|
+
menu;
|
|
384
|
+
tabIndex = computed(() => this.expanded() && this.menu()?.inputs.activeItem() ? -1 : 0);
|
|
385
|
+
disabled = () => this.inputs.disabled();
|
|
386
|
+
keydownManager = computed(() => {
|
|
387
|
+
return new KeyboardEventManager().on(' ', () => this.open({
|
|
388
|
+
first: true
|
|
389
|
+
})).on('Enter', () => this.open({
|
|
390
|
+
first: true
|
|
391
|
+
})).on('ArrowDown', () => this.open({
|
|
392
|
+
first: true
|
|
393
|
+
})).on('ArrowUp', () => this.open({
|
|
394
|
+
last: true
|
|
395
|
+
})).on('Escape', () => this.close({
|
|
396
|
+
refocus: true
|
|
397
|
+
}));
|
|
398
|
+
});
|
|
399
|
+
constructor(inputs) {
|
|
400
|
+
this.inputs = inputs;
|
|
401
|
+
this.menu = this.inputs.menu;
|
|
402
|
+
}
|
|
403
|
+
onKeydown(event) {
|
|
404
|
+
if (!this.inputs.disabled()) {
|
|
405
|
+
this.keydownManager().handle(event);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
onClick() {
|
|
409
|
+
if (!this.inputs.disabled()) {
|
|
410
|
+
this.expanded() ? this.close() : this.open({
|
|
411
|
+
first: true
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
onFocusIn() {
|
|
416
|
+
this.hasBeenFocused.set(true);
|
|
417
|
+
}
|
|
418
|
+
onFocusOut(event) {
|
|
419
|
+
const element = this.inputs.element();
|
|
420
|
+
const relatedTarget = event.relatedTarget;
|
|
421
|
+
if (this.expanded() && !element?.contains(relatedTarget) && !this.inputs.menu()?.inputs.element()?.contains(relatedTarget)) {
|
|
422
|
+
this.close();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
open(opts) {
|
|
426
|
+
this.expanded.set(true);
|
|
427
|
+
if (opts?.first) {
|
|
428
|
+
this.inputs.menu()?.first();
|
|
429
|
+
} else if (opts?.last) {
|
|
430
|
+
this.inputs.menu()?.last();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
close(opts = {}) {
|
|
434
|
+
this.expanded.set(false);
|
|
435
|
+
this.menu()?.listBehavior.unfocus();
|
|
436
|
+
if (opts.refocus) {
|
|
437
|
+
this.inputs.element()?.focus();
|
|
438
|
+
}
|
|
439
|
+
let menuitems = this.inputs.menu()?.inputs.items() ?? [];
|
|
440
|
+
while (menuitems.length) {
|
|
441
|
+
const menuitem = menuitems.pop();
|
|
442
|
+
menuitem?._expanded.set(false);
|
|
443
|
+
menuitem?.inputs.parent()?.listBehavior.unfocus();
|
|
444
|
+
menuitems = menuitems.concat(menuitem?.submenu()?.inputs.items() ?? []);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
class MenuItemPattern {
|
|
449
|
+
inputs;
|
|
450
|
+
value;
|
|
451
|
+
id;
|
|
452
|
+
disabled = () => this.inputs.parent()?.disabled() || this.inputs.disabled();
|
|
453
|
+
searchTerm;
|
|
454
|
+
element;
|
|
455
|
+
active = computed(() => this.inputs.parent()?.inputs.activeItem() === this);
|
|
456
|
+
hasBeenFocused = signal(false);
|
|
457
|
+
tabIndex = computed(() => {
|
|
458
|
+
if (this.submenu() && this.submenu()?.inputs.activeItem()) {
|
|
459
|
+
return -1;
|
|
460
|
+
}
|
|
461
|
+
return this.inputs.parent()?.listBehavior.getItemTabindex(this) ?? -1;
|
|
462
|
+
});
|
|
463
|
+
index = computed(() => this.inputs.parent()?.inputs.items().indexOf(this) ?? -1);
|
|
464
|
+
expanded = computed(() => this.submenu() ? this._expanded() : null);
|
|
465
|
+
_expanded = signal(false);
|
|
466
|
+
controls = signal(undefined);
|
|
467
|
+
role = () => 'menuitem';
|
|
468
|
+
hasPopup = computed(() => !!this.submenu());
|
|
469
|
+
submenu;
|
|
470
|
+
selectable;
|
|
471
|
+
constructor(inputs) {
|
|
472
|
+
this.inputs = inputs;
|
|
473
|
+
this.id = inputs.id;
|
|
474
|
+
this.value = inputs.value;
|
|
475
|
+
this.element = inputs.element;
|
|
476
|
+
this.submenu = this.inputs.submenu;
|
|
477
|
+
this.searchTerm = inputs.searchTerm;
|
|
478
|
+
this.selectable = computed(() => !this.submenu());
|
|
479
|
+
}
|
|
480
|
+
open(opts) {
|
|
481
|
+
if (this.disabled()) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
this._expanded.set(true);
|
|
485
|
+
if (opts?.first) {
|
|
486
|
+
this.submenu()?.first();
|
|
487
|
+
}
|
|
488
|
+
if (opts?.last) {
|
|
489
|
+
this.submenu()?.last();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
close(opts = {}) {
|
|
493
|
+
this._expanded.set(false);
|
|
494
|
+
if (opts.refocus) {
|
|
495
|
+
this.inputs.parent()?.listBehavior.goto(this);
|
|
496
|
+
}
|
|
497
|
+
let menuitems = this.inputs.submenu()?.inputs.items() ?? [];
|
|
498
|
+
while (menuitems.length) {
|
|
499
|
+
const menuitem = menuitems.pop();
|
|
500
|
+
menuitem?._expanded.set(false);
|
|
501
|
+
menuitem?.inputs.parent()?.listBehavior.unfocus();
|
|
502
|
+
menuitems = menuitems.concat(menuitem?.submenu()?.inputs.items() ?? []);
|
|
503
|
+
const parent = menuitem?.inputs.parent();
|
|
504
|
+
if (parent instanceof MenuPattern) {
|
|
505
|
+
parent._clearTimeouts();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
onFocusIn() {
|
|
510
|
+
this.hasBeenFocused.set(true);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export { MenuBarPattern, MenuItemPattern, MenuPattern, MenuTriggerPattern };
|
|
515
|
+
//# sourceMappingURL=_menu-chunk.mjs.map
|