@proyecto-viviana/solidaria-components 0.2.5 → 0.3.0
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/LICENSE +21 -0
- package/README.md +39 -272
- package/dist/ActionBar.d.ts +79 -0
- package/dist/ActionBar.d.ts.map +1 -0
- package/dist/ActionGroup.d.ts +74 -0
- package/dist/ActionGroup.d.ts.map +1 -0
- package/dist/Alert.d.ts +70 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Autocomplete.d.ts +5 -5
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Breadcrumbs.d.ts +27 -8
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +28 -5
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +51 -7
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +33 -8
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +130 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +210 -9
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +42 -0
- package/dist/ColorEditor.d.ts.map +1 -0
- package/dist/ComboBox.d.ts +146 -16
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +40 -0
- package/dist/ContextualHelpTrigger.d.ts.map +1 -0
- package/dist/DateField.d.ts +35 -8
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +101 -5
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DateRangePickerContext.d.ts +30 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -0
- package/dist/Dialog.d.ts +5 -5
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +25 -5
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +80 -0
- package/dist/DragAndDrop.d.ts.map +1 -0
- package/dist/DragPreview.d.ts +14 -0
- package/dist/DragPreview.d.ts.map +1 -0
- package/dist/DropZone.d.ts +27 -0
- package/dist/DropZone.d.ts.map +1 -0
- package/dist/FieldError.d.ts +27 -0
- package/dist/FieldError.d.ts.map +1 -0
- package/dist/FileTrigger.d.ts +26 -0
- package/dist/FileTrigger.d.ts.map +1 -0
- package/dist/Focusable.d.ts +27 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Form.d.ts +41 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +69 -10
- package/dist/GridList.d.ts.map +1 -1
- package/dist/HiddenDateInput.d.ts +26 -0
- package/dist/HiddenDateInput.d.ts.map +1 -0
- package/dist/HiddenTimeInput.d.ts +25 -0
- package/dist/HiddenTimeInput.d.ts.map +1 -0
- package/dist/Icon.d.ts +57 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Keyboard.d.ts +13 -0
- package/dist/Keyboard.d.ts.map +1 -0
- package/dist/Landmark.d.ts +3 -3
- package/dist/Landmark.d.ts.map +1 -1
- package/dist/Link.d.ts +10 -4
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +73 -11
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +38 -0
- package/dist/ListDropTargetDelegate.d.ts.map +1 -0
- package/dist/Menu.d.ts +79 -10
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +4 -4
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +6 -4
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +10 -12
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +32 -7
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +27 -0
- package/dist/Pressable.d.ts.map +1 -0
- package/dist/ProgressBar.d.ts +6 -4
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts +43 -9
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +39 -7
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +75 -0
- package/dist/RouterProvider.d.ts.map +1 -0
- package/dist/SearchField.d.ts +23 -21
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +48 -7
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +30 -0
- package/dist/SelectionIndicator.d.ts.map +1 -0
- package/dist/Separator.d.ts +9 -3
- package/dist/Separator.d.ts.map +1 -1
- package/dist/SharedElementTransition.d.ts +41 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +15 -8
- package/dist/Slider.d.ts.map +1 -1
- package/dist/StepList.d.ts +90 -0
- package/dist/StepList.d.ts.map +1 -0
- package/dist/Switch.d.ts +11 -5
- package/dist/Switch.d.ts.map +1 -1
- package/dist/Table.d.ts +222 -19
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +47 -10
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +22 -10
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +10 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/TextField.d.ts +19 -11
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +32 -7
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts +29 -14
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +36 -0
- package/dist/ToggleButton.d.ts.map +1 -0
- package/dist/ToggleButtonGroup.d.ts +33 -0
- package/dist/ToggleButtonGroup.d.ts.map +1 -0
- package/dist/Toolbar.d.ts +7 -3
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +58 -7
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +102 -11
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +61 -0
- package/dist/Virtualizer.d.ts.map +1 -0
- package/dist/VirtualizerLayouts.d.ts +82 -0
- package/dist/VirtualizerLayouts.d.ts.map +1 -0
- package/dist/VisuallyHidden.d.ts +4 -2
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +6 -1
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +73 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23342 -10644
- package/dist/index.js.map +1 -7
- package/dist/index.jsx +18110 -0
- package/dist/index.jsx.map +1 -0
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +79 -0
- package/dist/virtualizer/Layout.d.ts.map +1 -0
- package/package.json +33 -32
- package/src/ActionBar.tsx +251 -0
- package/src/ActionGroup.tsx +277 -0
- package/src/Alert.tsx +152 -0
- package/src/Autocomplete.tsx +39 -44
- package/src/Breadcrumbs.tsx +227 -72
- package/src/Button.tsx +315 -74
- package/src/Calendar.tsx +347 -141
- package/src/Checkbox.tsx +414 -123
- package/src/Collection.tsx +350 -0
- package/src/Color.tsx +1325 -284
- package/src/ColorEditor.tsx +213 -0
- package/src/ComboBox.tsx +644 -245
- package/src/ContextualHelpTrigger.tsx +195 -0
- package/src/DateField.tsx +274 -106
- package/src/DatePicker.tsx +892 -111
- package/src/DateRangePickerContext.tsx +44 -0
- package/src/Dialog.tsx +173 -104
- package/src/Disclosure.tsx +158 -105
- package/src/DragAndDrop.tsx +340 -0
- package/src/DragPreview.tsx +47 -0
- package/src/DropZone.tsx +233 -0
- package/src/FieldError.tsx +89 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +103 -0
- package/src/Form.tsx +140 -0
- package/src/GridList.tsx +542 -128
- package/src/HiddenDateInput.tsx +153 -0
- package/src/HiddenTimeInput.tsx +133 -0
- package/src/Icon.tsx +133 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Landmark.tsx +37 -63
- package/src/Link.tsx +132 -69
- package/src/ListBox.tsx +656 -106
- package/src/ListDropTargetDelegate.ts +283 -0
- package/src/Menu.tsx +1234 -132
- package/src/Meter.tsx +44 -58
- package/src/Modal.tsx +262 -166
- package/src/NumberField.tsx +267 -151
- package/src/Popover.tsx +452 -343
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +54 -59
- package/src/RadioGroup.tsx +533 -121
- package/src/RangeCalendar.tsx +249 -150
- package/src/RouterProvider.tsx +223 -0
- package/src/SearchField.tsx +460 -133
- package/src/Select.tsx +804 -233
- package/src/SelectionIndicator.tsx +108 -0
- package/src/Separator.tsx +47 -49
- package/src/SharedElementTransition.tsx +264 -0
- package/src/Slider.tsx +148 -98
- package/src/StepList.tsx +272 -0
- package/src/Switch.tsx +93 -46
- package/src/Table.tsx +1551 -225
- package/src/Tabs.tsx +377 -123
- package/src/TagGroup.tsx +233 -135
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +413 -86
- package/src/TimeField.tsx +232 -222
- package/src/Toast.tsx +306 -160
- package/src/ToggleButton.tsx +169 -0
- package/src/ToggleButtonGroup.tsx +141 -0
- package/src/Toolbar.tsx +61 -70
- package/src/Tooltip.tsx +473 -116
- package/src/Tree.tsx +1514 -175
- package/src/Virtualizer.tsx +730 -0
- package/src/VirtualizerLayouts.ts +280 -0
- package/src/VisuallyHidden.tsx +32 -38
- package/src/contexts.ts +29 -36
- package/src/index.ts +972 -620
- package/src/useDragAndDrop.ts +367 -0
- package/src/utils.tsx +69 -50
- package/src/virtualizer/Layout.ts +192 -0
- package/dist/index.ssr.js +0 -9785
- package/dist/index.ssr.js.map +0 -7
package/src/Menu.tsx
CHANGED
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
10
|
createContext,
|
|
11
|
+
createEffect,
|
|
11
12
|
createMemo,
|
|
13
|
+
createSignal,
|
|
14
|
+
createUniqueId,
|
|
15
|
+
onCleanup,
|
|
12
16
|
splitProps,
|
|
13
17
|
useContext,
|
|
14
18
|
For,
|
|
15
19
|
Show,
|
|
16
|
-
} from
|
|
20
|
+
} from "solid-js";
|
|
17
21
|
import {
|
|
18
22
|
createMenu,
|
|
19
23
|
createMenuItem,
|
|
@@ -22,42 +26,80 @@ import {
|
|
|
22
26
|
createHover,
|
|
23
27
|
createButton,
|
|
24
28
|
createInteractOutside,
|
|
29
|
+
mergeProps,
|
|
25
30
|
FocusScope,
|
|
26
31
|
type AriaMenuProps,
|
|
27
32
|
type AriaMenuItemProps,
|
|
28
33
|
type AriaMenuTriggerProps,
|
|
29
|
-
} from
|
|
34
|
+
} from "@proyecto-viviana/solidaria";
|
|
30
35
|
import {
|
|
36
|
+
createSelectionState,
|
|
31
37
|
createMenuState,
|
|
32
38
|
createMenuTriggerState,
|
|
33
39
|
type MenuState,
|
|
40
|
+
type MenuStateProps,
|
|
34
41
|
type OverlayTriggerState,
|
|
35
42
|
type Key,
|
|
36
|
-
|
|
43
|
+
type DropTarget,
|
|
44
|
+
type SelectionMode,
|
|
45
|
+
type SelectionStateProps,
|
|
46
|
+
} from "@proyecto-viviana/solid-stately";
|
|
37
47
|
import {
|
|
38
48
|
type RenderChildren,
|
|
39
49
|
type ClassNameOrFunction,
|
|
40
50
|
type StyleOrFunction,
|
|
41
51
|
type SlotProps,
|
|
42
52
|
useRenderProps,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
filterDOMProps,
|
|
54
|
+
} from "./utils";
|
|
55
|
+
import { SharedElementTransition } from "./SharedElementTransition";
|
|
56
|
+
import { type DragAndDropHooks } from "./useDragAndDrop";
|
|
57
|
+
import {
|
|
58
|
+
CollectionRendererContext,
|
|
59
|
+
Section,
|
|
60
|
+
Header,
|
|
61
|
+
Group,
|
|
62
|
+
type CollectionEntry,
|
|
63
|
+
type CollectionRendererContextValue,
|
|
64
|
+
type SectionProps,
|
|
65
|
+
useCollectionRenderer,
|
|
66
|
+
flattenCollectionEntries,
|
|
67
|
+
isCollectionSection,
|
|
68
|
+
} from "./Collection";
|
|
69
|
+
import { useVirtualizerContext } from "./Virtualizer";
|
|
70
|
+
import {
|
|
71
|
+
getNormalizedDropTargetKey,
|
|
72
|
+
mergePersistedKeysIntoVirtualRange,
|
|
73
|
+
useDndPersistedKeys,
|
|
74
|
+
useRenderDropIndicator,
|
|
75
|
+
} from "./DragAndDrop";
|
|
76
|
+
import { PopoverTriggerContext } from "./contexts";
|
|
48
77
|
|
|
49
78
|
export interface MenuRenderProps {
|
|
50
79
|
/** Whether the menu is focused. */
|
|
51
80
|
isFocused: boolean;
|
|
52
81
|
/** Whether the menu is open. */
|
|
53
82
|
isOpen: boolean;
|
|
83
|
+
/** Whether the menu has no items. */
|
|
84
|
+
isEmpty: boolean;
|
|
54
85
|
}
|
|
55
86
|
|
|
56
87
|
export interface MenuProps<T>
|
|
57
|
-
extends
|
|
58
|
-
|
|
88
|
+
extends
|
|
89
|
+
Omit<AriaMenuProps, "children">,
|
|
90
|
+
SlotProps,
|
|
91
|
+
Pick<
|
|
92
|
+
MenuStateProps<T>,
|
|
93
|
+
| "selectionMode"
|
|
94
|
+
| "selectionBehavior"
|
|
95
|
+
| "disallowEmptySelection"
|
|
96
|
+
| "selectedKeys"
|
|
97
|
+
| "defaultSelectedKeys"
|
|
98
|
+
| "onSelectionChange"
|
|
99
|
+
| "allowDuplicateSelectionEvents"
|
|
100
|
+
> {
|
|
59
101
|
/** The items to render in the menu. */
|
|
60
|
-
items
|
|
102
|
+
items?: CollectionEntry<T>[];
|
|
61
103
|
/** Function to get the key from an item. */
|
|
62
104
|
getKey?: (item: T) => Key;
|
|
63
105
|
/** Function to get the text value from an item. */
|
|
@@ -71,16 +113,33 @@ export interface MenuProps<T>
|
|
|
71
113
|
/** Handler called when the menu should close. */
|
|
72
114
|
onClose?: () => void;
|
|
73
115
|
/** The children of the component. A function may be provided to render each item. */
|
|
74
|
-
children
|
|
116
|
+
children?: JSX.Element | ((item: T) => JSX.Element);
|
|
117
|
+
/** Internal lazy static children accessor used when collection children need menu context. */
|
|
118
|
+
staticChildren?: () => JSX.Element | undefined;
|
|
75
119
|
/** The CSS className for the element. */
|
|
76
120
|
class?: ClassNameOrFunction<MenuRenderProps>;
|
|
77
121
|
/** The inline style for the element. */
|
|
78
122
|
style?: StyleOrFunction<MenuRenderProps>;
|
|
123
|
+
/** Content to display when the menu has no items. */
|
|
124
|
+
renderEmptyState?: () => JSX.Element;
|
|
125
|
+
/** Whether the menu should close when an item is selected. */
|
|
126
|
+
shouldCloseOnSelect?: boolean;
|
|
127
|
+
/** Ref for the menu element. */
|
|
128
|
+
ref?: RefLike<HTMLUListElement>;
|
|
129
|
+
/** Custom renderer for the menu element. */
|
|
130
|
+
render?: (
|
|
131
|
+
props: JSX.HTMLAttributes<HTMLUListElement>,
|
|
132
|
+
renderProps: MenuRenderProps,
|
|
133
|
+
) => JSX.Element;
|
|
134
|
+
/** Drag and drop hooks from `useDragAndDrop`. */
|
|
135
|
+
dragAndDropHooks?: DragAndDropHooks<T>;
|
|
79
136
|
}
|
|
80
137
|
|
|
81
138
|
export interface MenuItemRenderProps {
|
|
82
139
|
/** Whether the item is selected. */
|
|
83
140
|
isSelected: boolean;
|
|
141
|
+
/** The parent menu selection mode. */
|
|
142
|
+
selectionMode: SelectionMode;
|
|
84
143
|
/** Whether the item is focused. */
|
|
85
144
|
isFocused: boolean;
|
|
86
145
|
/** Whether the item has keyboard focus. */
|
|
@@ -91,11 +150,13 @@ export interface MenuItemRenderProps {
|
|
|
91
150
|
isHovered: boolean;
|
|
92
151
|
/** Whether the item is disabled. */
|
|
93
152
|
isDisabled: boolean;
|
|
153
|
+
/** Whether the item opens a submenu. */
|
|
154
|
+
hasSubmenu: boolean;
|
|
155
|
+
/** Whether the submenu is currently open. */
|
|
156
|
+
isOpen: boolean;
|
|
94
157
|
}
|
|
95
158
|
|
|
96
|
-
export interface MenuItemProps<T>
|
|
97
|
-
extends Omit<AriaMenuItemProps, 'children' | 'key'>,
|
|
98
|
-
SlotProps {
|
|
159
|
+
export interface MenuItemProps<T> extends Omit<AriaMenuItemProps, "children" | "key">, SlotProps {
|
|
99
160
|
/** The unique key for the item. */
|
|
100
161
|
id: Key;
|
|
101
162
|
/** The item value. */
|
|
@@ -110,6 +171,27 @@ export interface MenuItemProps<T>
|
|
|
110
171
|
textValue?: string;
|
|
111
172
|
/** Handler called when the item is activated. */
|
|
112
173
|
onAction?: () => void;
|
|
174
|
+
/** A URL to link to. Turns the menu item into a link. */
|
|
175
|
+
href?: string;
|
|
176
|
+
/** The target window for the link. */
|
|
177
|
+
target?: string;
|
|
178
|
+
/** The relationship between the linked resource and the current page. */
|
|
179
|
+
rel?: string;
|
|
180
|
+
/** Causes the browser to download the linked URL. */
|
|
181
|
+
download?: boolean | string;
|
|
182
|
+
/** Handler called when hover starts. */
|
|
183
|
+
onHoverStart?: () => void;
|
|
184
|
+
/** Handler called when hover ends. */
|
|
185
|
+
onHoverEnd?: () => void;
|
|
186
|
+
/** Handler called when hover state changes. */
|
|
187
|
+
onHoverChange?: (isHovered: boolean) => void;
|
|
188
|
+
/** Ref for the menu item element. */
|
|
189
|
+
ref?: RefLike<HTMLLIElement>;
|
|
190
|
+
/** Custom renderer for the menu item element. */
|
|
191
|
+
render?: (
|
|
192
|
+
props: JSX.HTMLAttributes<HTMLLIElement>,
|
|
193
|
+
renderProps: MenuItemRenderProps,
|
|
194
|
+
) => JSX.Element;
|
|
113
195
|
}
|
|
114
196
|
|
|
115
197
|
export interface MenuTriggerRenderProps {
|
|
@@ -127,7 +209,7 @@ export interface MenuTriggerRenderProps {
|
|
|
127
209
|
isDisabled: boolean;
|
|
128
210
|
}
|
|
129
211
|
|
|
130
|
-
export interface MenuTriggerProps extends Omit<AriaMenuTriggerProps,
|
|
212
|
+
export interface MenuTriggerProps extends Omit<AriaMenuTriggerProps, "children">, SlotProps {
|
|
131
213
|
/** The children of the trigger (typically a Button and Menu). */
|
|
132
214
|
children: JSX.Element;
|
|
133
215
|
/** Whether the menu trigger is disabled. */
|
|
@@ -140,35 +222,95 @@ export interface MenuTriggerProps extends Omit<AriaMenuTriggerProps, 'children'>
|
|
|
140
222
|
onOpenChange?: (isOpen: boolean) => void;
|
|
141
223
|
}
|
|
142
224
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
225
|
+
export interface SubmenuTriggerProps extends SlotProps {
|
|
226
|
+
/** The trigger item followed by submenu content. */
|
|
227
|
+
children: JSX.Element;
|
|
228
|
+
/** Delay before opening the submenu on hover. */
|
|
229
|
+
delay?: number;
|
|
230
|
+
/** Whether the submenu is open (controlled). */
|
|
231
|
+
isOpen?: boolean;
|
|
232
|
+
/** Whether the submenu is open by default. */
|
|
233
|
+
defaultOpen?: boolean;
|
|
234
|
+
/** Handler called when the submenu open state changes. */
|
|
235
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
236
|
+
}
|
|
146
237
|
|
|
147
238
|
interface MenuContextValue<T> {
|
|
148
239
|
state: MenuState<T>;
|
|
240
|
+
isDisabled: () => boolean;
|
|
241
|
+
dragAndDropHooks?: DragAndDropHooks<T>;
|
|
242
|
+
dragState?: unknown;
|
|
243
|
+
dropState?: unknown;
|
|
149
244
|
}
|
|
150
245
|
|
|
246
|
+
type MenuSelectionEvent = { shiftKey?: boolean; ctrlKey?: boolean; metaKey?: boolean };
|
|
247
|
+
|
|
151
248
|
interface MenuTriggerContextValue {
|
|
152
249
|
state: OverlayTriggerState;
|
|
153
250
|
triggerProps: JSX.HTMLAttributes<HTMLElement>;
|
|
154
251
|
menuProps: JSX.HTMLAttributes<HTMLElement>;
|
|
155
252
|
}
|
|
156
253
|
|
|
254
|
+
interface MenuItemContextValue {
|
|
255
|
+
props?: () => JSX.HTMLAttributes<HTMLElement>;
|
|
256
|
+
closeOnSelect?: boolean;
|
|
257
|
+
onAction?: () => void;
|
|
258
|
+
setItemRef?: (el: HTMLLIElement | null) => void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
interface StaticMenuCollectionItem {
|
|
262
|
+
id: Key;
|
|
263
|
+
textValue?: string;
|
|
264
|
+
isDisabled?: boolean;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
interface StaticMenuCollectionContextValue {
|
|
268
|
+
registerItem(item: StaticMenuCollectionItem): void;
|
|
269
|
+
unregisterItem(id: Key): void;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
interface MenuSectionSelectionContextValue {
|
|
273
|
+
selectionMode: () => SelectionMode;
|
|
274
|
+
isSelected(key: Key): boolean;
|
|
275
|
+
isDisabled(key: Key): boolean;
|
|
276
|
+
select(key: Key, event?: MenuSelectionEvent): void;
|
|
277
|
+
shouldCloseOnSelect(): boolean | undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
interface MenuSectionSelectionRegistryContextValue {
|
|
281
|
+
registerItem(key: Key, selection: MenuSectionSelectionContextValue): void;
|
|
282
|
+
unregisterItem(key: Key, selection: MenuSectionSelectionContextValue): void;
|
|
283
|
+
selectItem(key: Key, event?: MenuSelectionEvent): boolean;
|
|
284
|
+
}
|
|
285
|
+
|
|
157
286
|
export const MenuContext = createContext<MenuContextValue<unknown> | null>(null);
|
|
158
287
|
export const MenuStateContext = createContext<MenuState<unknown> | null>(null);
|
|
159
288
|
export const MenuTriggerContext = createContext<MenuTriggerContextValue | null>(null);
|
|
289
|
+
export const RootMenuTriggerStateContext = createContext<OverlayTriggerState | null>(null);
|
|
290
|
+
const MenuItemContext = createContext<MenuItemContextValue | null>(null);
|
|
291
|
+
const StaticMenuCollectionContext = createContext<StaticMenuCollectionContextValue | null>(null);
|
|
292
|
+
const MenuSectionSelectionContext = createContext<MenuSectionSelectionContextValue | null>(null);
|
|
293
|
+
const MenuSectionSelectionRegistryContext =
|
|
294
|
+
createContext<MenuSectionSelectionRegistryContextValue | null>(null);
|
|
295
|
+
|
|
296
|
+
type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
|
|
297
|
+
|
|
298
|
+
function assignRef<T>(ref: RefLike<T>, el: T): void {
|
|
299
|
+
if (!ref) return;
|
|
300
|
+
if (typeof ref === "function") ref(el);
|
|
301
|
+
else ref.current = el;
|
|
302
|
+
}
|
|
160
303
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
304
|
+
function resolveBoolean(value: unknown): boolean {
|
|
305
|
+
return typeof value === "function" ? Boolean((value as () => unknown)()) : Boolean(value);
|
|
306
|
+
}
|
|
164
307
|
|
|
165
308
|
/**
|
|
166
309
|
* A menu trigger wraps a button and menu, handling the open/close state.
|
|
167
310
|
*/
|
|
168
311
|
export function MenuTrigger(props: MenuTriggerProps): JSX.Element {
|
|
169
|
-
const [local, stateProps] = splitProps(props, [
|
|
312
|
+
const [local, stateProps] = splitProps(props, ["slot"]);
|
|
170
313
|
|
|
171
|
-
// Create trigger state
|
|
172
314
|
const state = createMenuTriggerState({
|
|
173
315
|
get isOpen() {
|
|
174
316
|
return stateProps.isOpen;
|
|
@@ -181,33 +323,153 @@ export function MenuTrigger(props: MenuTriggerProps): JSX.Element {
|
|
|
181
323
|
},
|
|
182
324
|
});
|
|
183
325
|
|
|
184
|
-
|
|
185
|
-
const { menuTriggerProps, menuProps } = createMenuTrigger(
|
|
326
|
+
const menuTrigger = createMenuTrigger(
|
|
186
327
|
{
|
|
187
328
|
get isDisabled() {
|
|
188
329
|
return stateProps.isDisabled;
|
|
189
330
|
},
|
|
190
331
|
},
|
|
191
|
-
state
|
|
332
|
+
state,
|
|
192
333
|
);
|
|
193
334
|
|
|
194
335
|
return (
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
336
|
+
<RootMenuTriggerStateContext.Provider value={state}>
|
|
337
|
+
<MenuTriggerContext.Provider
|
|
338
|
+
value={{
|
|
339
|
+
state,
|
|
340
|
+
get triggerProps() {
|
|
341
|
+
return menuTrigger.menuTriggerProps;
|
|
342
|
+
},
|
|
343
|
+
get menuProps() {
|
|
344
|
+
return menuTrigger.menuProps;
|
|
345
|
+
},
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
{props.children}
|
|
349
|
+
</MenuTriggerContext.Provider>
|
|
350
|
+
</RootMenuTriggerStateContext.Provider>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
|
|
355
|
+
const children = () =>
|
|
356
|
+
(Array.isArray(props.children) ? props.children : [props.children]) as JSX.Element[];
|
|
357
|
+
const trigger = () => children()[0];
|
|
358
|
+
const content = () => children()[1];
|
|
359
|
+
const parentMenuItemContext = useContext(MenuItemContext);
|
|
360
|
+
const state = createMenuTriggerState({
|
|
361
|
+
get isOpen() {
|
|
362
|
+
return props.isOpen;
|
|
363
|
+
},
|
|
364
|
+
get defaultOpen() {
|
|
365
|
+
return props.defaultOpen;
|
|
366
|
+
},
|
|
367
|
+
get onOpenChange() {
|
|
368
|
+
return props.onOpenChange;
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
let triggerRef: HTMLLIElement | null = null;
|
|
373
|
+
const triggerId = createUniqueId();
|
|
374
|
+
const menuId = createUniqueId();
|
|
375
|
+
let hoverTimeout: number | undefined;
|
|
376
|
+
const delay = () => props.delay ?? 200;
|
|
377
|
+
|
|
378
|
+
const clearHoverTimeout = () => {
|
|
379
|
+
if (hoverTimeout != null) {
|
|
380
|
+
window.clearTimeout(hoverTimeout);
|
|
381
|
+
hoverTimeout = undefined;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const openSubmenu = () => {
|
|
386
|
+
clearHoverTimeout();
|
|
387
|
+
state.open();
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const scheduleOpen = () => {
|
|
391
|
+
clearHoverTimeout();
|
|
392
|
+
hoverTimeout = window.setTimeout(() => {
|
|
393
|
+
hoverTimeout = undefined;
|
|
394
|
+
state.open();
|
|
395
|
+
}, delay());
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
onCleanup(clearHoverTimeout);
|
|
399
|
+
|
|
400
|
+
const menuTriggerContext = createMemo<MenuTriggerContextValue>(() => ({
|
|
401
|
+
state,
|
|
402
|
+
triggerProps: {},
|
|
403
|
+
menuProps: {
|
|
404
|
+
id: menuId,
|
|
405
|
+
"aria-labelledby": triggerId,
|
|
406
|
+
},
|
|
407
|
+
}));
|
|
408
|
+
|
|
409
|
+
const popoverTriggerContext = createMemo(() => ({
|
|
410
|
+
state: {
|
|
411
|
+
isOpen: () => state.isOpen(),
|
|
412
|
+
open: () => state.open(),
|
|
413
|
+
close: () => state.close(),
|
|
414
|
+
toggle: () => state.toggle(),
|
|
415
|
+
},
|
|
416
|
+
triggerRef: () => triggerRef,
|
|
417
|
+
setTriggerRef: (el: HTMLElement | null) => {
|
|
418
|
+
triggerRef = el as HTMLLIElement | null;
|
|
419
|
+
},
|
|
420
|
+
triggerId,
|
|
421
|
+
trigger: "SubmenuTrigger",
|
|
422
|
+
}));
|
|
423
|
+
|
|
424
|
+
const itemContext = createMemo<MenuItemContextValue>(() => ({
|
|
425
|
+
closeOnSelect: false,
|
|
426
|
+
onAction: () => openSubmenu(),
|
|
427
|
+
setItemRef: (el) => {
|
|
428
|
+
triggerRef = el;
|
|
429
|
+
},
|
|
430
|
+
props: () => ({
|
|
431
|
+
id: triggerId,
|
|
432
|
+
"aria-haspopup": "menu",
|
|
433
|
+
"aria-expanded": state.isOpen() || undefined,
|
|
434
|
+
"aria-controls": state.isOpen() ? menuId : undefined,
|
|
435
|
+
onPointerEnter: (event: PointerEvent) => {
|
|
436
|
+
if (event.pointerType === "touch") return;
|
|
437
|
+
scheduleOpen();
|
|
438
|
+
},
|
|
439
|
+
onPointerOver: (event: PointerEvent) => {
|
|
440
|
+
if (event.pointerType === "touch") return;
|
|
441
|
+
scheduleOpen();
|
|
442
|
+
},
|
|
443
|
+
onMouseEnter: () => scheduleOpen(),
|
|
444
|
+
onKeyDown: (event: KeyboardEvent) => {
|
|
445
|
+
if (event.key === "ArrowRight" || event.key === "Enter" || event.key === " ") {
|
|
446
|
+
event.preventDefault();
|
|
447
|
+
openSubmenu();
|
|
448
|
+
} else if (event.key === "ArrowLeft" && state.isOpen()) {
|
|
449
|
+
event.preventDefault();
|
|
450
|
+
state.close();
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
}),
|
|
454
|
+
}));
|
|
455
|
+
|
|
456
|
+
return (
|
|
457
|
+
<PopoverTriggerContext.Provider value={popoverTriggerContext()}>
|
|
458
|
+
<MenuTriggerContext.Provider value={menuTriggerContext()}>
|
|
459
|
+
<MenuItemContext.Provider value={{ ...parentMenuItemContext, ...itemContext() }}>
|
|
460
|
+
{trigger()}
|
|
461
|
+
</MenuItemContext.Provider>
|
|
462
|
+
{content()}
|
|
463
|
+
</MenuTriggerContext.Provider>
|
|
464
|
+
</PopoverTriggerContext.Provider>
|
|
204
465
|
);
|
|
205
466
|
}
|
|
206
467
|
|
|
207
468
|
/**
|
|
208
469
|
* A button that opens a menu.
|
|
209
470
|
*/
|
|
210
|
-
export interface MenuButtonProps
|
|
471
|
+
export interface MenuButtonProps
|
|
472
|
+
extends SlotProps, Omit<JSX.HTMLAttributes<HTMLButtonElement>, "class" | "style" | "children"> {
|
|
211
473
|
/** The children of the button. A function may be provided to receive render props. */
|
|
212
474
|
children?: RenderChildren<MenuTriggerRenderProps>;
|
|
213
475
|
/** The CSS className for the element. */
|
|
@@ -218,17 +480,34 @@ export interface MenuButtonProps extends SlotProps {
|
|
|
218
480
|
isDisabled?: boolean;
|
|
219
481
|
}
|
|
220
482
|
|
|
483
|
+
export interface MenuSectionProps
|
|
484
|
+
extends
|
|
485
|
+
SectionProps,
|
|
486
|
+
Pick<
|
|
487
|
+
SelectionStateProps,
|
|
488
|
+
| "selectionMode"
|
|
489
|
+
| "selectionBehavior"
|
|
490
|
+
| "disallowEmptySelection"
|
|
491
|
+
| "selectedKeys"
|
|
492
|
+
| "defaultSelectedKeys"
|
|
493
|
+
| "onSelectionChange"
|
|
494
|
+
| "disabledKeys"
|
|
495
|
+
| "disabledBehavior"
|
|
496
|
+
| "allowDuplicateSelectionEvents"
|
|
497
|
+
> {
|
|
498
|
+
/** Whether menu items in this section should close the menu when selected. */
|
|
499
|
+
shouldCloseOnSelect?: boolean;
|
|
500
|
+
}
|
|
501
|
+
|
|
221
502
|
export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
222
|
-
const [local] = splitProps(props, [
|
|
503
|
+
const [local, domProps] = splitProps(props, ["class", "style", "slot", "isDisabled", "children"]);
|
|
223
504
|
|
|
224
|
-
// Get trigger context
|
|
225
505
|
const context = useContext(MenuTriggerContext);
|
|
226
506
|
if (!context) {
|
|
227
|
-
throw new Error(
|
|
507
|
+
throw new Error("MenuButton must be used within a MenuTrigger");
|
|
228
508
|
}
|
|
229
|
-
const { state
|
|
509
|
+
const { state } = context;
|
|
230
510
|
|
|
231
|
-
// Create button aria props for proper press handling
|
|
232
511
|
const buttonAria = createButton({
|
|
233
512
|
get isDisabled() {
|
|
234
513
|
return local.isDisabled;
|
|
@@ -238,17 +517,14 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
|
238
517
|
},
|
|
239
518
|
});
|
|
240
519
|
|
|
241
|
-
// Create focus ring
|
|
242
520
|
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
243
521
|
|
|
244
|
-
// Create hover
|
|
245
522
|
const { isHovered, hoverProps } = createHover({
|
|
246
523
|
get isDisabled() {
|
|
247
524
|
return local.isDisabled;
|
|
248
525
|
},
|
|
249
526
|
});
|
|
250
527
|
|
|
251
|
-
// Render props values
|
|
252
528
|
const renderValues = createMemo<MenuTriggerRenderProps>(() => ({
|
|
253
529
|
isOpen: state.isOpen(),
|
|
254
530
|
isFocused: isFocused(),
|
|
@@ -258,20 +534,26 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
|
258
534
|
isDisabled: !!local.isDisabled,
|
|
259
535
|
}));
|
|
260
536
|
|
|
261
|
-
// Resolve render props
|
|
262
537
|
const renderProps = useRenderProps(
|
|
263
538
|
{
|
|
264
539
|
children: props.children,
|
|
265
540
|
class: local.class,
|
|
266
541
|
style: local.style,
|
|
267
|
-
defaultClassName:
|
|
542
|
+
defaultClassName: "solidaria-MenuButton",
|
|
268
543
|
},
|
|
269
|
-
renderValues
|
|
544
|
+
renderValues,
|
|
270
545
|
);
|
|
271
546
|
|
|
272
|
-
|
|
547
|
+
const resolvedTriggerProps = () => context.triggerProps as Record<string, unknown>;
|
|
273
548
|
const cleanTriggerProps = () => {
|
|
274
|
-
const {
|
|
549
|
+
const {
|
|
550
|
+
ref: _ref1,
|
|
551
|
+
"aria-haspopup": _ariaHasPopup,
|
|
552
|
+
"aria-expanded": _ariaExpanded,
|
|
553
|
+
"aria-controls": _ariaControls,
|
|
554
|
+
"aria-disabled": _ariaDisabled,
|
|
555
|
+
...rest
|
|
556
|
+
} = resolvedTriggerProps();
|
|
275
557
|
return rest;
|
|
276
558
|
};
|
|
277
559
|
const cleanButtonProps = () => {
|
|
@@ -289,6 +571,7 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
|
289
571
|
|
|
290
572
|
return (
|
|
291
573
|
<button
|
|
574
|
+
{...domProps}
|
|
292
575
|
{...cleanTriggerProps()}
|
|
293
576
|
{...cleanButtonProps()}
|
|
294
577
|
{...cleanFocusProps()}
|
|
@@ -296,6 +579,10 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
|
296
579
|
type="button"
|
|
297
580
|
class={renderProps.class()}
|
|
298
581
|
style={renderProps.style()}
|
|
582
|
+
aria-haspopup={resolvedTriggerProps()["aria-haspopup"] as "menu" | "listbox" | undefined}
|
|
583
|
+
aria-expanded={resolvedTriggerProps()["aria-expanded"] as boolean | undefined}
|
|
584
|
+
aria-controls={resolvedTriggerProps()["aria-controls"] as string | undefined}
|
|
585
|
+
aria-disabled={resolvedTriggerProps()["aria-disabled"] as boolean | undefined}
|
|
299
586
|
data-open={state.isOpen() || undefined}
|
|
300
587
|
data-focused={isFocused() || undefined}
|
|
301
588
|
data-focus-visible={isFocusVisible() || undefined}
|
|
@@ -314,63 +601,192 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
|
314
601
|
export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
315
602
|
const [local, stateProps, ariaProps] = splitProps(
|
|
316
603
|
props,
|
|
317
|
-
[
|
|
318
|
-
|
|
604
|
+
[
|
|
605
|
+
"children",
|
|
606
|
+
"class",
|
|
607
|
+
"style",
|
|
608
|
+
"render",
|
|
609
|
+
"slot",
|
|
610
|
+
"renderEmptyState",
|
|
611
|
+
"shouldCloseOnSelect",
|
|
612
|
+
"ref",
|
|
613
|
+
"staticChildren",
|
|
614
|
+
],
|
|
615
|
+
[
|
|
616
|
+
"items",
|
|
617
|
+
"getKey",
|
|
618
|
+
"getTextValue",
|
|
619
|
+
"getDisabled",
|
|
620
|
+
"disabledKeys",
|
|
621
|
+
"selectionMode",
|
|
622
|
+
"selectionBehavior",
|
|
623
|
+
"disallowEmptySelection",
|
|
624
|
+
"selectedKeys",
|
|
625
|
+
"defaultSelectedKeys",
|
|
626
|
+
"onSelectionChange",
|
|
627
|
+
"allowDuplicateSelectionEvents",
|
|
628
|
+
"onAction",
|
|
629
|
+
"onClose",
|
|
630
|
+
"dragAndDropHooks",
|
|
631
|
+
],
|
|
319
632
|
);
|
|
320
633
|
|
|
321
|
-
// Get trigger context if available
|
|
322
634
|
const triggerContext = useContext(MenuTriggerContext);
|
|
323
635
|
|
|
324
|
-
|
|
325
|
-
|
|
636
|
+
const [menuRef, setMenuRef] = createSignal<HTMLUListElement | null>(null);
|
|
637
|
+
const [staticItems, setStaticItems] = createSignal<StaticMenuCollectionItem[]>([]);
|
|
638
|
+
const staticItemMap = new Map<Key, StaticMenuCollectionItem>();
|
|
639
|
+
const sectionSelectionMap = new Map<Key, MenuSectionSelectionContextValue>();
|
|
640
|
+
const usesStaticChildren = () => local.staticChildren != null || stateProps.items == null;
|
|
641
|
+
|
|
642
|
+
const syncStaticItems = () => {
|
|
643
|
+
setStaticItems(Array.from(staticItemMap.values()));
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
const staticCollectionContext: StaticMenuCollectionContextValue = {
|
|
647
|
+
registerItem(item) {
|
|
648
|
+
const previous = staticItemMap.get(item.id);
|
|
649
|
+
if (
|
|
650
|
+
previous &&
|
|
651
|
+
previous.textValue === item.textValue &&
|
|
652
|
+
previous.isDisabled === item.isDisabled
|
|
653
|
+
) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
staticItemMap.set(item.id, item);
|
|
658
|
+
syncStaticItems();
|
|
659
|
+
},
|
|
660
|
+
unregisterItem(id) {
|
|
661
|
+
if (staticItemMap.delete(id)) {
|
|
662
|
+
syncStaticItems();
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
const sectionSelectionRegistry: MenuSectionSelectionRegistryContextValue = {
|
|
667
|
+
registerItem(key, selection) {
|
|
668
|
+
sectionSelectionMap.set(key, selection);
|
|
669
|
+
},
|
|
670
|
+
unregisterItem(key, selection) {
|
|
671
|
+
if (sectionSelectionMap.get(key) === selection) {
|
|
672
|
+
sectionSelectionMap.delete(key);
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
selectItem(key, event) {
|
|
676
|
+
const selection = sectionSelectionMap.get(key);
|
|
677
|
+
if (!selection || selection.selectionMode() === "none") {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
selection.select(key, event);
|
|
682
|
+
return true;
|
|
683
|
+
},
|
|
684
|
+
};
|
|
685
|
+
const handleAction = (key: Key) => {
|
|
686
|
+
sectionSelectionRegistry.selectItem(key);
|
|
687
|
+
stateProps.onAction?.(key);
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
const flatItems = createMemo<T[]>(() => {
|
|
691
|
+
return flattenCollectionEntries(stateProps.items ?? []);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const hasSections = createMemo(() =>
|
|
695
|
+
(stateProps.items ?? []).some((item) => isCollectionSection(item)),
|
|
696
|
+
);
|
|
326
697
|
|
|
327
|
-
// Create menu state
|
|
328
698
|
const state = createMenuState<T>({
|
|
329
699
|
get items() {
|
|
330
|
-
return
|
|
700
|
+
return usesStaticChildren() ? (staticItems() as T[]) : flatItems();
|
|
331
701
|
},
|
|
332
702
|
get getKey() {
|
|
333
|
-
return
|
|
703
|
+
return usesStaticChildren()
|
|
704
|
+
? (item: T) => (item as StaticMenuCollectionItem).id
|
|
705
|
+
: stateProps.getKey;
|
|
334
706
|
},
|
|
335
707
|
get getTextValue() {
|
|
336
|
-
return
|
|
708
|
+
return usesStaticChildren()
|
|
709
|
+
? (item: T) =>
|
|
710
|
+
(item as StaticMenuCollectionItem).textValue ??
|
|
711
|
+
String((item as StaticMenuCollectionItem).id)
|
|
712
|
+
: stateProps.getTextValue;
|
|
337
713
|
},
|
|
338
714
|
get getDisabled() {
|
|
339
|
-
return
|
|
715
|
+
return usesStaticChildren()
|
|
716
|
+
? (item: T) => Boolean((item as StaticMenuCollectionItem).isDisabled)
|
|
717
|
+
: stateProps.getDisabled;
|
|
340
718
|
},
|
|
341
719
|
get disabledKeys() {
|
|
342
720
|
return stateProps.disabledKeys;
|
|
343
721
|
},
|
|
722
|
+
get selectionMode() {
|
|
723
|
+
return stateProps.selectionMode;
|
|
724
|
+
},
|
|
725
|
+
get selectionBehavior() {
|
|
726
|
+
return stateProps.selectionBehavior;
|
|
727
|
+
},
|
|
728
|
+
get disallowEmptySelection() {
|
|
729
|
+
return stateProps.disallowEmptySelection;
|
|
730
|
+
},
|
|
731
|
+
get selectedKeys() {
|
|
732
|
+
return stateProps.selectedKeys;
|
|
733
|
+
},
|
|
734
|
+
get defaultSelectedKeys() {
|
|
735
|
+
return stateProps.defaultSelectedKeys;
|
|
736
|
+
},
|
|
737
|
+
get onSelectionChange() {
|
|
738
|
+
return stateProps.onSelectionChange;
|
|
739
|
+
},
|
|
740
|
+
get allowDuplicateSelectionEvents() {
|
|
741
|
+
return stateProps.allowDuplicateSelectionEvents;
|
|
742
|
+
},
|
|
344
743
|
get onAction() {
|
|
345
|
-
return
|
|
744
|
+
return handleAction;
|
|
346
745
|
},
|
|
347
746
|
get onClose() {
|
|
348
747
|
return stateProps.onClose ?? (() => triggerContext?.state.close());
|
|
349
748
|
},
|
|
350
749
|
});
|
|
351
750
|
|
|
352
|
-
|
|
353
|
-
|
|
751
|
+
const resolveDisabled = (): boolean => {
|
|
752
|
+
const disabled = ariaProps.isDisabled;
|
|
753
|
+
if (typeof disabled === "function") {
|
|
754
|
+
return (disabled as () => boolean)();
|
|
755
|
+
}
|
|
756
|
+
return !!disabled;
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
const { menuProps, labelProps } = createMenu(
|
|
354
760
|
{
|
|
761
|
+
get isDisabled() {
|
|
762
|
+
return resolveDisabled();
|
|
763
|
+
},
|
|
764
|
+
get label() {
|
|
765
|
+
return ariaProps.label;
|
|
766
|
+
},
|
|
767
|
+
get onAction() {
|
|
768
|
+
return handleAction;
|
|
769
|
+
},
|
|
355
770
|
get onClose() {
|
|
356
771
|
return stateProps.onClose ?? (() => triggerContext?.state.close());
|
|
357
772
|
},
|
|
358
|
-
get
|
|
359
|
-
return ariaProps[
|
|
773
|
+
get "aria-label"() {
|
|
774
|
+
return ariaProps["aria-label"];
|
|
775
|
+
},
|
|
776
|
+
get "aria-labelledby"() {
|
|
777
|
+
return ariaProps["aria-labelledby"];
|
|
360
778
|
},
|
|
361
|
-
get
|
|
362
|
-
return ariaProps[
|
|
779
|
+
get "aria-describedby"() {
|
|
780
|
+
return ariaProps["aria-describedby"];
|
|
363
781
|
},
|
|
364
782
|
},
|
|
365
|
-
state
|
|
783
|
+
state,
|
|
366
784
|
);
|
|
367
785
|
|
|
368
|
-
// Create focus ring
|
|
369
786
|
const { isFocused, focusProps } = createFocusRing();
|
|
370
787
|
|
|
371
|
-
// Handle click outside to close menu
|
|
372
788
|
createInteractOutside({
|
|
373
|
-
ref: () => menuRef
|
|
789
|
+
ref: () => menuRef(),
|
|
374
790
|
onInteractOutside: () => {
|
|
375
791
|
if (triggerContext?.state.isOpen()) {
|
|
376
792
|
triggerContext.state.close();
|
|
@@ -381,23 +797,21 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
381
797
|
},
|
|
382
798
|
});
|
|
383
799
|
|
|
384
|
-
// Render props values
|
|
385
800
|
const renderValues = createMemo<MenuRenderProps>(() => ({
|
|
386
801
|
isFocused: state.isFocused() || isFocused(),
|
|
387
802
|
isOpen: triggerContext?.state.isOpen() ?? true,
|
|
803
|
+
isEmpty: state.collection().size === 0,
|
|
388
804
|
}));
|
|
389
805
|
|
|
390
|
-
// Resolve render props
|
|
391
806
|
const renderProps = useRenderProps(
|
|
392
807
|
{
|
|
393
808
|
class: local.class,
|
|
394
809
|
style: local.style,
|
|
395
|
-
defaultClassName:
|
|
810
|
+
defaultClassName: "solidaria-Menu",
|
|
396
811
|
},
|
|
397
|
-
renderValues
|
|
812
|
+
renderValues,
|
|
398
813
|
);
|
|
399
814
|
|
|
400
|
-
// Remove ref from spread props
|
|
401
815
|
const cleanMenuProps = () => {
|
|
402
816
|
const { ref: _ref1, ...rest } = menuProps as Record<string, unknown>;
|
|
403
817
|
return rest;
|
|
@@ -411,27 +825,373 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
411
825
|
const { ref: _ref3, ...rest } = focusProps as Record<string, unknown>;
|
|
412
826
|
return rest;
|
|
413
827
|
};
|
|
828
|
+
const domProps = createMemo(() =>
|
|
829
|
+
filterDOMProps(ariaProps as Record<string, unknown>, { global: true }),
|
|
830
|
+
);
|
|
831
|
+
const cleanLabelProps = () => {
|
|
832
|
+
const { ref: _ref4, ...rest } = labelProps as Record<string, unknown>;
|
|
833
|
+
return rest;
|
|
834
|
+
};
|
|
835
|
+
const setResolvedMenuRef = (el: HTMLUListElement): void => {
|
|
836
|
+
setMenuRef(el);
|
|
837
|
+
assignRef(local.ref, el);
|
|
838
|
+
};
|
|
414
839
|
|
|
415
840
|
// If inside a MenuTrigger, only render when open
|
|
416
841
|
// If standalone (no trigger context), always render
|
|
417
|
-
const shouldRender = () => triggerContext ? triggerContext.state.isOpen() : true;
|
|
842
|
+
const shouldRender = () => (triggerContext ? triggerContext.state.isOpen() : true);
|
|
843
|
+
const parentCollectionRenderer = useCollectionRenderer<unknown>();
|
|
844
|
+
const virtualizer = useVirtualizerContext();
|
|
845
|
+
const getItemNodes = createMemo(() =>
|
|
846
|
+
Array.from(state.collection()).filter((node) => node.type === "item"),
|
|
847
|
+
);
|
|
848
|
+
const getDropTargetByIndex = (
|
|
849
|
+
index: number,
|
|
850
|
+
position: "before" | "after" | "on",
|
|
851
|
+
): DropTarget | null => {
|
|
852
|
+
const node = getItemNodes()[index];
|
|
853
|
+
if (!node) return null;
|
|
854
|
+
return { type: "item", key: node.key, dropPosition: position };
|
|
855
|
+
};
|
|
856
|
+
const hasDroppableDnd = createMemo(() => {
|
|
857
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
858
|
+
return Boolean(
|
|
859
|
+
hooks?.useDroppableCollectionState &&
|
|
860
|
+
hooks.useDroppableCollection &&
|
|
861
|
+
(hooks.dropTargetDelegate ||
|
|
862
|
+
parentCollectionRenderer?.dropTargetDelegate ||
|
|
863
|
+
hooks.ListDropTargetDelegate),
|
|
864
|
+
);
|
|
865
|
+
});
|
|
866
|
+
const hasDraggableDnd = createMemo(() => {
|
|
867
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
868
|
+
return Boolean(hooks?.useDraggableCollectionState && hooks.useDraggableCollection);
|
|
869
|
+
});
|
|
870
|
+
const dragState = createMemo(() => {
|
|
871
|
+
if (!hasDraggableDnd()) return undefined;
|
|
872
|
+
return stateProps.dragAndDropHooks?.useDraggableCollectionState?.({
|
|
873
|
+
items: flatItems(),
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
const dropState = createMemo(() => {
|
|
877
|
+
if (!hasDroppableDnd()) return undefined;
|
|
878
|
+
return stateProps.dragAndDropHooks?.useDroppableCollectionState?.({});
|
|
879
|
+
});
|
|
880
|
+
const persistedKeys = useDndPersistedKeys(
|
|
881
|
+
{ focusedKey: state.focusedKey },
|
|
882
|
+
stateProps.dragAndDropHooks,
|
|
883
|
+
dropState(),
|
|
884
|
+
state.collection(),
|
|
885
|
+
);
|
|
886
|
+
const virtualRange = createMemo(() => {
|
|
887
|
+
if (!virtualizer || !parentCollectionRenderer?.isVirtualized || hasSections()) return null;
|
|
888
|
+
const dynamicItems = stateProps.items ?? [];
|
|
889
|
+
const baseRange = virtualizer.getVisibleRange(dynamicItems.length);
|
|
890
|
+
const itemNodes = getItemNodes();
|
|
891
|
+
const persistedIndexes = Array.from(persistedKeys())
|
|
892
|
+
.map((key) => itemNodes.findIndex((node) => node.key === key))
|
|
893
|
+
.filter((index) => index >= 0);
|
|
894
|
+
const dropTarget = dropState()?.target;
|
|
895
|
+
const normalizedDropKey = getNormalizedDropTargetKey(dropTarget, state.collection());
|
|
896
|
+
const focusedKey = state.focusedKey();
|
|
897
|
+
const focusedIndex =
|
|
898
|
+
focusedKey != null ? itemNodes.findIndex((node) => node.key === focusedKey) : -1;
|
|
899
|
+
const forceIncludeIndexes = [
|
|
900
|
+
dropTarget?.type === "item" ? itemNodes.findIndex((node) => node.key === dropTarget.key) : -1,
|
|
901
|
+
normalizedDropKey != null
|
|
902
|
+
? itemNodes.findIndex((node) => node.key === normalizedDropKey)
|
|
903
|
+
: -1,
|
|
904
|
+
dropTarget?.type === "item" ? -1 : focusedIndex,
|
|
905
|
+
].filter((index) => index >= 0);
|
|
906
|
+
return mergePersistedKeysIntoVirtualRange(
|
|
907
|
+
baseRange,
|
|
908
|
+
persistedIndexes,
|
|
909
|
+
dynamicItems.length,
|
|
910
|
+
virtualizer,
|
|
911
|
+
80,
|
|
912
|
+
{
|
|
913
|
+
forceIncludeIndexes,
|
|
914
|
+
forceIncludeMaxSpan: 320,
|
|
915
|
+
},
|
|
916
|
+
);
|
|
917
|
+
});
|
|
918
|
+
const visibleItems = createMemo(() => {
|
|
919
|
+
const range = virtualRange();
|
|
920
|
+
const items = stateProps.items ?? [];
|
|
921
|
+
if (!range) return items;
|
|
922
|
+
return items.slice(range.start, range.end);
|
|
923
|
+
});
|
|
924
|
+
createEffect(() => {
|
|
925
|
+
if (!hasDraggableDnd()) return;
|
|
926
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
927
|
+
const activeDragState = dragState();
|
|
928
|
+
if (!hooks?.useDraggableCollection || !activeDragState) return;
|
|
929
|
+
hooks.useDraggableCollection({}, activeDragState, () => menuRef());
|
|
930
|
+
});
|
|
931
|
+
const droppableCollection = createMemo(() => {
|
|
932
|
+
if (!hasDroppableDnd()) return undefined;
|
|
933
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
934
|
+
const activeDropState = dropState();
|
|
935
|
+
if (!hooks?.useDroppableCollection || !activeDropState) return undefined;
|
|
936
|
+
const resolveDirection = (): "ltr" | "rtl" => {
|
|
937
|
+
const menuEl = menuRef();
|
|
938
|
+
if (
|
|
939
|
+
menuEl &&
|
|
940
|
+
typeof window !== "undefined" &&
|
|
941
|
+
typeof window.getComputedStyle === "function"
|
|
942
|
+
) {
|
|
943
|
+
const dir = window.getComputedStyle(menuEl).direction;
|
|
944
|
+
if (dir === "rtl") return "rtl";
|
|
945
|
+
}
|
|
946
|
+
return typeof document !== "undefined" && document.dir === "rtl" ? "rtl" : "ltr";
|
|
947
|
+
};
|
|
948
|
+
const dropTargetDelegate =
|
|
949
|
+
hooks.dropTargetDelegate ??
|
|
950
|
+
parentCollectionRenderer?.dropTargetDelegate ??
|
|
951
|
+
(hooks.ListDropTargetDelegate
|
|
952
|
+
? new hooks.ListDropTargetDelegate(
|
|
953
|
+
() => state.collection(),
|
|
954
|
+
() => menuRef(),
|
|
955
|
+
{ layout: "stack", orientation: "vertical", direction: resolveDirection() },
|
|
956
|
+
)
|
|
957
|
+
: undefined);
|
|
958
|
+
if (!dropTargetDelegate) return undefined;
|
|
959
|
+
return hooks.useDroppableCollection(
|
|
960
|
+
{
|
|
961
|
+
dropTargetDelegate,
|
|
962
|
+
keyboardDelegate: {
|
|
963
|
+
getFirstKey: () => state.collection().getFirstKey(),
|
|
964
|
+
getLastKey: () => state.collection().getLastKey(),
|
|
965
|
+
getKeyBelow: (key) => state.collection().getKeyAfter(key),
|
|
966
|
+
getKeyAbove: (key) => state.collection().getKeyBefore(key),
|
|
967
|
+
getKeyPageBelow: (key) => state.collection().getKeyAfter(key),
|
|
968
|
+
getKeyPageAbove: (key) => state.collection().getKeyBefore(key),
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
activeDropState,
|
|
972
|
+
() => menuRef(),
|
|
973
|
+
);
|
|
974
|
+
});
|
|
975
|
+
const isRootDropTarget = createMemo(() => {
|
|
976
|
+
return Boolean(dropState()?.target?.type === "root");
|
|
977
|
+
});
|
|
978
|
+
const dndRenderDropIndicator = createMemo(() =>
|
|
979
|
+
useRenderDropIndicator(stateProps.dragAndDropHooks, dropState()),
|
|
980
|
+
);
|
|
981
|
+
const dndDropIndicator = (index: number, position: "before" | "after" | "on") => {
|
|
982
|
+
const target = getDropTargetByIndex(index, position);
|
|
983
|
+
if (!target || target.type !== "item") return undefined;
|
|
984
|
+
return dndRenderDropIndicator()?.(target);
|
|
985
|
+
};
|
|
986
|
+
const sectionedRenderEntries = createMemo(() => {
|
|
987
|
+
let globalIndex = 0;
|
|
988
|
+
return (stateProps.items ?? []).map((entry) => {
|
|
989
|
+
if (isCollectionSection(entry)) {
|
|
990
|
+
const sectionItems = entry.items.map((item) => ({
|
|
991
|
+
item,
|
|
992
|
+
index: globalIndex++,
|
|
993
|
+
}));
|
|
994
|
+
return {
|
|
995
|
+
type: "section" as const,
|
|
996
|
+
section: entry,
|
|
997
|
+
items: sectionItems,
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
const indexedItem = {
|
|
1001
|
+
item: entry as T,
|
|
1002
|
+
index: globalIndex++,
|
|
1003
|
+
};
|
|
1004
|
+
return {
|
|
1005
|
+
type: "item" as const,
|
|
1006
|
+
item: indexedItem,
|
|
1007
|
+
};
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
const renderDynamicItem = (item: T) =>
|
|
1011
|
+
typeof local.children === "function" ? local.children(item) : undefined;
|
|
1012
|
+
const resolveStaticChild = (child: unknown): JSX.Element | undefined => {
|
|
1013
|
+
return typeof child === "function"
|
|
1014
|
+
? (child as () => JSX.Element | undefined)()
|
|
1015
|
+
: (child as JSX.Element | undefined);
|
|
1016
|
+
};
|
|
1017
|
+
const renderStaticChildren = () => {
|
|
1018
|
+
const staticChildren = (local.staticChildren?.() ?? local.children) as unknown;
|
|
1019
|
+
if (Array.isArray(staticChildren)) {
|
|
1020
|
+
return staticChildren.map(resolveStaticChild);
|
|
1021
|
+
}
|
|
1022
|
+
return resolveStaticChild(staticChildren);
|
|
1023
|
+
};
|
|
1024
|
+
const collectionRenderer = createMemo<CollectionRendererContextValue<unknown>>(() => ({
|
|
1025
|
+
...parentCollectionRenderer,
|
|
1026
|
+
renderItem: (item) => renderDynamicItem(item as T),
|
|
1027
|
+
renderDropIndicator: (index, position) =>
|
|
1028
|
+
dndDropIndicator(index, position) ??
|
|
1029
|
+
parentCollectionRenderer?.renderDropIndicator?.(index, position),
|
|
1030
|
+
}));
|
|
1031
|
+
const menuListChildren = () => (
|
|
1032
|
+
<SharedElementTransition>
|
|
1033
|
+
{state.collection().size === 0 && !usesStaticChildren() && local.renderEmptyState ? (
|
|
1034
|
+
<li role="presentation" data-empty-state>
|
|
1035
|
+
<div role="menuitem" style={{ display: "contents" }}>
|
|
1036
|
+
{local.renderEmptyState()}
|
|
1037
|
+
</div>
|
|
1038
|
+
</li>
|
|
1039
|
+
) : usesStaticChildren() ? (
|
|
1040
|
+
renderStaticChildren()
|
|
1041
|
+
) : hasSections() ? (
|
|
1042
|
+
<For each={sectionedRenderEntries()}>
|
|
1043
|
+
{(entry) =>
|
|
1044
|
+
entry.type === "section" ? (
|
|
1045
|
+
<li role="presentation" data-section-wrapper>
|
|
1046
|
+
<Section class="solidaria-Menu-section">
|
|
1047
|
+
{entry.section.title != null && (
|
|
1048
|
+
<Header class="solidaria-Menu-sectionHeader">{entry.section.title}</Header>
|
|
1049
|
+
)}
|
|
1050
|
+
<Group class="solidaria-Menu-sectionGroup">
|
|
1051
|
+
<ul role="group" aria-label={entry.section["aria-label"]}>
|
|
1052
|
+
<For each={entry.items}>
|
|
1053
|
+
{(indexedItem) => (
|
|
1054
|
+
<>
|
|
1055
|
+
{collectionRenderer().renderDropIndicator?.(
|
|
1056
|
+
indexedItem.index,
|
|
1057
|
+
"before",
|
|
1058
|
+
)}
|
|
1059
|
+
{collectionRenderer().renderDropIndicator?.(indexedItem.index, "on")}
|
|
1060
|
+
{renderDynamicItem(indexedItem.item)}
|
|
1061
|
+
{collectionRenderer().renderDropIndicator?.(indexedItem.index, "after")}
|
|
1062
|
+
</>
|
|
1063
|
+
)}
|
|
1064
|
+
</For>
|
|
1065
|
+
</ul>
|
|
1066
|
+
</Group>
|
|
1067
|
+
</Section>
|
|
1068
|
+
</li>
|
|
1069
|
+
) : (
|
|
1070
|
+
<>
|
|
1071
|
+
{collectionRenderer().renderDropIndicator?.(entry.item.index, "before")}
|
|
1072
|
+
{collectionRenderer().renderDropIndicator?.(entry.item.index, "on")}
|
|
1073
|
+
{renderDynamicItem(entry.item.item)}
|
|
1074
|
+
{collectionRenderer().renderDropIndicator?.(entry.item.index, "after")}
|
|
1075
|
+
</>
|
|
1076
|
+
)
|
|
1077
|
+
}
|
|
1078
|
+
</For>
|
|
1079
|
+
) : (
|
|
1080
|
+
<>
|
|
1081
|
+
{virtualRange()?.offsetTop ? (
|
|
1082
|
+
<li
|
|
1083
|
+
role="presentation"
|
|
1084
|
+
aria-hidden="true"
|
|
1085
|
+
style={{ height: `${virtualRange()!.offsetTop}px` }}
|
|
1086
|
+
data-virtualizer-spacer="top"
|
|
1087
|
+
/>
|
|
1088
|
+
) : null}
|
|
1089
|
+
<For each={visibleItems()}>
|
|
1090
|
+
{(item, index) => {
|
|
1091
|
+
const itemIndex = () => (virtualRange()?.start ?? 0) + index();
|
|
1092
|
+
const beforeIndicator = () =>
|
|
1093
|
+
collectionRenderer().renderDropIndicator?.(itemIndex(), "before");
|
|
1094
|
+
const onIndicator = () =>
|
|
1095
|
+
collectionRenderer().renderDropIndicator?.(itemIndex(), "on");
|
|
1096
|
+
const afterIndicator = () =>
|
|
1097
|
+
collectionRenderer().renderDropIndicator?.(itemIndex(), "after");
|
|
1098
|
+
return (
|
|
1099
|
+
<>
|
|
1100
|
+
{beforeIndicator()}
|
|
1101
|
+
{onIndicator()}
|
|
1102
|
+
{renderDynamicItem(item as T)}
|
|
1103
|
+
{afterIndicator()}
|
|
1104
|
+
</>
|
|
1105
|
+
);
|
|
1106
|
+
}}
|
|
1107
|
+
</For>
|
|
1108
|
+
{virtualRange()?.offsetBottom ? (
|
|
1109
|
+
<li
|
|
1110
|
+
role="presentation"
|
|
1111
|
+
aria-hidden="true"
|
|
1112
|
+
style={{ height: `${virtualRange()!.offsetBottom}px` }}
|
|
1113
|
+
data-virtualizer-spacer="bottom"
|
|
1114
|
+
/>
|
|
1115
|
+
) : null}
|
|
1116
|
+
</>
|
|
1117
|
+
)}
|
|
1118
|
+
</SharedElementTransition>
|
|
1119
|
+
);
|
|
1120
|
+
const menuListProps = () =>
|
|
1121
|
+
({
|
|
1122
|
+
ref: setResolvedMenuRef,
|
|
1123
|
+
...mergeProps(
|
|
1124
|
+
domProps(),
|
|
1125
|
+
cleanMenuProps(),
|
|
1126
|
+
cleanTriggerMenuProps(),
|
|
1127
|
+
cleanFocusProps(),
|
|
1128
|
+
(droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ?? {},
|
|
1129
|
+
),
|
|
1130
|
+
class: renderProps.class(),
|
|
1131
|
+
style: renderProps.style(),
|
|
1132
|
+
slot: local.slot,
|
|
1133
|
+
"data-focused": state.isFocused() || undefined,
|
|
1134
|
+
"data-disabled": resolveDisabled() || undefined,
|
|
1135
|
+
"data-empty": state.collection().size === 0 || undefined,
|
|
1136
|
+
"data-drop-target": isRootDropTarget() || undefined,
|
|
1137
|
+
children: menuListChildren(),
|
|
1138
|
+
}) as JSX.HTMLAttributes<HTMLUListElement>;
|
|
418
1139
|
|
|
419
1140
|
// Only use FocusScope when inside a MenuTrigger (for popover behavior)
|
|
420
1141
|
// Standalone menus don't need focus restoration
|
|
421
1142
|
const menuContent = () => (
|
|
422
|
-
<MenuContext.Provider
|
|
1143
|
+
<MenuContext.Provider
|
|
1144
|
+
value={
|
|
1145
|
+
{
|
|
1146
|
+
state,
|
|
1147
|
+
isDisabled: resolveDisabled,
|
|
1148
|
+
dragAndDropHooks: stateProps.dragAndDropHooks,
|
|
1149
|
+
dragState: dragState(),
|
|
1150
|
+
dropState: dropState(),
|
|
1151
|
+
} as MenuContextValue<unknown>
|
|
1152
|
+
}
|
|
1153
|
+
>
|
|
423
1154
|
<MenuStateContext.Provider value={state}>
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
1155
|
+
<MenuSectionSelectionRegistryContext.Provider value={sectionSelectionRegistry}>
|
|
1156
|
+
<StaticMenuCollectionContext.Provider
|
|
1157
|
+
value={usesStaticChildren() ? staticCollectionContext : null}
|
|
1158
|
+
>
|
|
1159
|
+
<MenuItemContext.Provider value={{ closeOnSelect: local.shouldCloseOnSelect }}>
|
|
1160
|
+
<CollectionRendererContext.Provider value={collectionRenderer()}>
|
|
1161
|
+
<>
|
|
1162
|
+
<Show when={ariaProps.label}>
|
|
1163
|
+
<span {...cleanLabelProps()}>{ariaProps.label as JSX.Element}</span>
|
|
1164
|
+
</Show>
|
|
1165
|
+
{local.render ? (
|
|
1166
|
+
local.render(menuListProps(), renderValues())
|
|
1167
|
+
) : (
|
|
1168
|
+
<ul
|
|
1169
|
+
ref={setResolvedMenuRef}
|
|
1170
|
+
{...mergeProps(
|
|
1171
|
+
domProps(),
|
|
1172
|
+
cleanMenuProps(),
|
|
1173
|
+
cleanTriggerMenuProps(),
|
|
1174
|
+
cleanFocusProps(),
|
|
1175
|
+
(droppableCollection()?.collectionProps as
|
|
1176
|
+
| Record<string, unknown>
|
|
1177
|
+
| undefined) ?? {},
|
|
1178
|
+
)}
|
|
1179
|
+
class={renderProps.class()}
|
|
1180
|
+
style={renderProps.style()}
|
|
1181
|
+
slot={local.slot}
|
|
1182
|
+
data-focused={state.isFocused() || undefined}
|
|
1183
|
+
data-disabled={resolveDisabled() || undefined}
|
|
1184
|
+
data-empty={state.collection().size === 0 || undefined}
|
|
1185
|
+
data-drop-target={isRootDropTarget() || undefined}
|
|
1186
|
+
>
|
|
1187
|
+
{menuListChildren()}
|
|
1188
|
+
</ul>
|
|
1189
|
+
)}
|
|
1190
|
+
</>
|
|
1191
|
+
</CollectionRendererContext.Provider>
|
|
1192
|
+
</MenuItemContext.Provider>
|
|
1193
|
+
</StaticMenuCollectionContext.Provider>
|
|
1194
|
+
</MenuSectionSelectionRegistryContext.Provider>
|
|
435
1195
|
</MenuStateContext.Provider>
|
|
436
1196
|
</MenuContext.Provider>
|
|
437
1197
|
);
|
|
@@ -452,93 +1212,435 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
452
1212
|
*/
|
|
453
1213
|
export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
|
|
454
1214
|
const [local, ariaProps] = splitProps(props, [
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
1215
|
+
"class",
|
|
1216
|
+
"style",
|
|
1217
|
+
"render",
|
|
1218
|
+
"slot",
|
|
1219
|
+
"id",
|
|
1220
|
+
"item",
|
|
1221
|
+
"textValue",
|
|
1222
|
+
"onAction",
|
|
1223
|
+
"href",
|
|
1224
|
+
"target",
|
|
1225
|
+
"rel",
|
|
1226
|
+
"download",
|
|
1227
|
+
"onHoverStart",
|
|
1228
|
+
"onHoverEnd",
|
|
1229
|
+
"onHoverChange",
|
|
1230
|
+
"ref",
|
|
462
1231
|
]);
|
|
463
1232
|
|
|
464
|
-
// Get state from context
|
|
465
1233
|
const context = useContext(MenuStateContext);
|
|
466
1234
|
if (!context) {
|
|
467
|
-
throw new Error(
|
|
1235
|
+
throw new Error("MenuItem must be used within a Menu");
|
|
468
1236
|
}
|
|
469
1237
|
const state = context as MenuState<T>;
|
|
1238
|
+
const menuContext = useContext(MenuContext) as MenuContextValue<T> | null;
|
|
1239
|
+
const itemContext = useContext(MenuItemContext);
|
|
1240
|
+
const staticCollection = useContext(StaticMenuCollectionContext);
|
|
1241
|
+
const sectionSelection = useContext(MenuSectionSelectionContext);
|
|
1242
|
+
const sectionSelectionRegistry = useContext(MenuSectionSelectionRegistryContext);
|
|
1243
|
+
const [ref, setRef] = createSignal<HTMLLIElement | null>(null);
|
|
1244
|
+
const contextProps = () => itemContext?.props?.() ?? {};
|
|
1245
|
+
const combinedOnAction = () => {
|
|
1246
|
+
local.onAction?.();
|
|
1247
|
+
itemContext?.onAction?.();
|
|
1248
|
+
};
|
|
1249
|
+
const activeSectionSelection = () =>
|
|
1250
|
+
sectionSelection && sectionSelection.selectionMode() !== "none" ? sectionSelection : null;
|
|
1251
|
+
let registeredStaticKey: Key | null = null;
|
|
1252
|
+
let registeredSectionSelectionKey: Key | null = null;
|
|
1253
|
+
let registeredSectionSelection: MenuSectionSelectionContextValue | null = null;
|
|
1254
|
+
|
|
1255
|
+
const unregisterSectionSelection = () => {
|
|
1256
|
+
if (registeredSectionSelectionKey != null && registeredSectionSelection) {
|
|
1257
|
+
sectionSelectionRegistry?.unregisterItem(
|
|
1258
|
+
registeredSectionSelectionKey,
|
|
1259
|
+
registeredSectionSelection,
|
|
1260
|
+
);
|
|
1261
|
+
registeredSectionSelectionKey = null;
|
|
1262
|
+
registeredSectionSelection = null;
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
createEffect(() => {
|
|
1267
|
+
if (!staticCollection) return;
|
|
1268
|
+
|
|
1269
|
+
if (registeredStaticKey != null && registeredStaticKey !== local.id) {
|
|
1270
|
+
staticCollection.unregisterItem(registeredStaticKey);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
registeredStaticKey = local.id;
|
|
1274
|
+
staticCollection.registerItem({
|
|
1275
|
+
id: local.id,
|
|
1276
|
+
textValue: local.textValue ?? ariaProps["aria-label"],
|
|
1277
|
+
isDisabled:
|
|
1278
|
+
resolveBoolean(ariaProps.isDisabled) || (sectionSelection?.isDisabled(local.id) ?? false),
|
|
1279
|
+
});
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
onCleanup(() => {
|
|
1283
|
+
if (registeredStaticKey != null) {
|
|
1284
|
+
staticCollection?.unregisterItem(registeredStaticKey);
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
createEffect(() => {
|
|
1289
|
+
const selection = activeSectionSelection();
|
|
1290
|
+
if (!sectionSelectionRegistry || !selection) {
|
|
1291
|
+
unregisterSectionSelection();
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
if (registeredSectionSelectionKey === local.id && registeredSectionSelection === selection) {
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
unregisterSectionSelection();
|
|
1300
|
+
registeredSectionSelectionKey = local.id;
|
|
1301
|
+
registeredSectionSelection = selection;
|
|
1302
|
+
sectionSelectionRegistry.registerItem(local.id, selection);
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
onCleanup(unregisterSectionSelection);
|
|
470
1306
|
|
|
471
|
-
// Create menu item aria props
|
|
472
1307
|
const itemAria = createMenuItem<T>(
|
|
473
1308
|
{
|
|
474
1309
|
key: local.id,
|
|
475
1310
|
get isDisabled() {
|
|
476
|
-
return
|
|
1311
|
+
return Boolean(
|
|
1312
|
+
ariaProps.isDisabled ||
|
|
1313
|
+
sectionSelection?.isDisabled(local.id) ||
|
|
1314
|
+
menuContext?.isDisabled(),
|
|
1315
|
+
);
|
|
477
1316
|
},
|
|
478
|
-
get
|
|
479
|
-
return ariaProps[
|
|
1317
|
+
get "aria-label"() {
|
|
1318
|
+
return ariaProps["aria-label"] ?? local.textValue;
|
|
480
1319
|
},
|
|
481
1320
|
get onAction() {
|
|
482
|
-
return
|
|
1321
|
+
return combinedOnAction;
|
|
1322
|
+
},
|
|
1323
|
+
get closeOnSelect() {
|
|
1324
|
+
return (
|
|
1325
|
+
ariaProps.closeOnSelect ??
|
|
1326
|
+
sectionSelection?.shouldCloseOnSelect() ??
|
|
1327
|
+
itemContext?.closeOnSelect
|
|
1328
|
+
);
|
|
1329
|
+
},
|
|
1330
|
+
get href() {
|
|
1331
|
+
return local.href;
|
|
1332
|
+
},
|
|
1333
|
+
get target() {
|
|
1334
|
+
return local.target;
|
|
1335
|
+
},
|
|
1336
|
+
get rel() {
|
|
1337
|
+
return local.rel;
|
|
1338
|
+
},
|
|
1339
|
+
get download() {
|
|
1340
|
+
return local.download;
|
|
483
1341
|
},
|
|
484
1342
|
},
|
|
485
|
-
state
|
|
1343
|
+
state,
|
|
486
1344
|
);
|
|
487
1345
|
|
|
488
|
-
// Create hover
|
|
489
1346
|
const { isHovered, hoverProps } = createHover({
|
|
490
1347
|
get isDisabled() {
|
|
491
1348
|
return itemAria.isDisabled();
|
|
492
1349
|
},
|
|
1350
|
+
onHoverStart: local.onHoverStart,
|
|
1351
|
+
onHoverEnd: local.onHoverEnd,
|
|
1352
|
+
onHoverChange: local.onHoverChange,
|
|
493
1353
|
});
|
|
494
1354
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
1355
|
+
const renderValues = createMemo<MenuItemRenderProps>(() => {
|
|
1356
|
+
const selection = activeSectionSelection();
|
|
1357
|
+
return {
|
|
1358
|
+
isSelected: selection?.isSelected(local.id) ?? itemAria.isSelected(),
|
|
1359
|
+
selectionMode: selection?.selectionMode() ?? itemAria.selectionMode(),
|
|
1360
|
+
isFocused: itemAria.isFocused(),
|
|
1361
|
+
isFocusVisible: itemAria.isFocusVisible(),
|
|
1362
|
+
isPressed: itemAria.isPressed(),
|
|
1363
|
+
isHovered: isHovered(),
|
|
1364
|
+
isDisabled: itemAria.isDisabled(),
|
|
1365
|
+
hasSubmenu: Boolean(contextProps()["aria-haspopup"]),
|
|
1366
|
+
isOpen: contextProps()["aria-expanded"] === true,
|
|
1367
|
+
};
|
|
1368
|
+
});
|
|
504
1369
|
|
|
505
|
-
// Resolve render props
|
|
506
1370
|
const renderProps = useRenderProps(
|
|
507
1371
|
{
|
|
508
1372
|
children: props.children,
|
|
509
1373
|
class: local.class,
|
|
510
1374
|
style: local.style,
|
|
511
|
-
defaultClassName:
|
|
1375
|
+
defaultClassName: "solidaria-Menu-item",
|
|
512
1376
|
},
|
|
513
|
-
renderValues
|
|
1377
|
+
renderValues,
|
|
514
1378
|
);
|
|
1379
|
+
const hasPrimitiveLabel = () => {
|
|
1380
|
+
return typeof props.children === "string" || typeof props.children === "number";
|
|
1381
|
+
};
|
|
515
1382
|
|
|
516
|
-
// Remove ref from spread props
|
|
517
1383
|
const cleanItemProps = () => {
|
|
518
|
-
const {
|
|
1384
|
+
const {
|
|
1385
|
+
ref: _ref1,
|
|
1386
|
+
"aria-describedby": _ariaDescribedby,
|
|
1387
|
+
...rest
|
|
1388
|
+
} = itemAria.menuItemProps as Record<string, unknown>;
|
|
1389
|
+
if (!hasPrimitiveLabel() && rest["aria-label"] == null) {
|
|
1390
|
+
delete rest["aria-labelledby"];
|
|
1391
|
+
}
|
|
1392
|
+
const selection = activeSectionSelection();
|
|
1393
|
+
const selectionMode = selection?.selectionMode();
|
|
1394
|
+
if (selectionMode) {
|
|
1395
|
+
rest.role =
|
|
1396
|
+
selectionMode === "single"
|
|
1397
|
+
? "menuitemradio"
|
|
1398
|
+
: selectionMode === "multiple"
|
|
1399
|
+
? "menuitemcheckbox"
|
|
1400
|
+
: "menuitem";
|
|
1401
|
+
if (selectionMode !== "none") {
|
|
1402
|
+
rest["aria-checked"] = selection?.isSelected(local.id) ?? false;
|
|
1403
|
+
} else {
|
|
1404
|
+
delete rest["aria-checked"];
|
|
1405
|
+
}
|
|
1406
|
+
rest["data-selected"] = selection?.isSelected(local.id) || undefined;
|
|
1407
|
+
}
|
|
519
1408
|
return rest;
|
|
520
1409
|
};
|
|
521
1410
|
const cleanHoverProps = () => {
|
|
522
1411
|
const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
|
|
523
1412
|
return rest;
|
|
524
1413
|
};
|
|
1414
|
+
const domProps = createMemo(() =>
|
|
1415
|
+
filterDOMProps(ariaProps as Record<string, unknown>, { global: true }),
|
|
1416
|
+
);
|
|
1417
|
+
const draggableItem = createMemo(() => {
|
|
1418
|
+
if (!menuContext?.dragAndDropHooks?.useDraggableItem || !menuContext.dragState)
|
|
1419
|
+
return undefined;
|
|
1420
|
+
return menuContext.dragAndDropHooks.useDraggableItem(
|
|
1421
|
+
{
|
|
1422
|
+
key: local.id as string | number,
|
|
1423
|
+
},
|
|
1424
|
+
menuContext.dragState as Parameters<NonNullable<DragAndDropHooks<T>["useDraggableItem"]>>[1],
|
|
1425
|
+
);
|
|
1426
|
+
});
|
|
1427
|
+
const droppableItem = createMemo(() => {
|
|
1428
|
+
if (!menuContext?.dragAndDropHooks?.useDroppableItem || !menuContext.dropState)
|
|
1429
|
+
return undefined;
|
|
1430
|
+
return menuContext.dragAndDropHooks.useDroppableItem(
|
|
1431
|
+
{
|
|
1432
|
+
key: local.id as string | number,
|
|
1433
|
+
},
|
|
1434
|
+
menuContext.dropState as Parameters<NonNullable<DragAndDropHooks<T>["useDroppableItem"]>>[1],
|
|
1435
|
+
() => ref(),
|
|
1436
|
+
);
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
const isLink = () => !!local.href;
|
|
1440
|
+
|
|
1441
|
+
const cleanItemPropsForLink = () => {
|
|
1442
|
+
const all = cleanItemProps();
|
|
1443
|
+
const { href: _href, target: _target, rel: _rel, download: _download, ...rest } = all;
|
|
1444
|
+
return rest;
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
const linkDomProps = () => {
|
|
1448
|
+
const all = cleanItemProps();
|
|
1449
|
+
const result: Record<string, unknown> = {};
|
|
1450
|
+
if (all.href !== undefined) result.href = all.href;
|
|
1451
|
+
if (all.target !== undefined) result.target = all.target;
|
|
1452
|
+
if (all.rel !== undefined) result.rel = all.rel;
|
|
1453
|
+
if (all.download !== undefined) result.download = all.download;
|
|
1454
|
+
return result;
|
|
1455
|
+
};
|
|
1456
|
+
|
|
1457
|
+
const dataAttrs = () => {
|
|
1458
|
+
const selection = activeSectionSelection();
|
|
1459
|
+
return {
|
|
1460
|
+
"data-focused": itemAria.isFocused() || undefined,
|
|
1461
|
+
"data-focus-visible": itemAria.isFocusVisible() || undefined,
|
|
1462
|
+
"data-pressed": itemAria.isPressed() || undefined,
|
|
1463
|
+
"data-hovered": isHovered() || undefined,
|
|
1464
|
+
"data-disabled": itemAria.isDisabled() || undefined,
|
|
1465
|
+
"data-selected": (selection?.isSelected(local.id) ?? itemAria.isSelected()) || undefined,
|
|
1466
|
+
"data-has-submenu": Boolean(contextProps()["aria-haspopup"]) || undefined,
|
|
1467
|
+
"data-open": contextProps()["aria-expanded"] === true || undefined,
|
|
1468
|
+
"data-dragging": draggableItem()?.isDragging || undefined,
|
|
1469
|
+
"data-drop-target": droppableItem()?.isDropTarget || undefined,
|
|
1470
|
+
};
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
const childContent = () =>
|
|
1474
|
+
hasPrimitiveLabel() ? (
|
|
1475
|
+
<span {...itemAria.labelProps}>{renderProps.renderChildren()}</span>
|
|
1476
|
+
) : (
|
|
1477
|
+
renderProps.renderChildren()
|
|
1478
|
+
);
|
|
1479
|
+
const setResolvedItemRef = (el: HTMLLIElement | null) => {
|
|
1480
|
+
setRef(el);
|
|
1481
|
+
itemContext?.setItemRef?.(el);
|
|
1482
|
+
if (el) assignRef(local.ref, el);
|
|
1483
|
+
};
|
|
1484
|
+
const menuItemProps = () =>
|
|
1485
|
+
({
|
|
1486
|
+
ref: setResolvedItemRef,
|
|
1487
|
+
...mergeProps(
|
|
1488
|
+
cleanItemProps(),
|
|
1489
|
+
contextProps() as Record<string, unknown>,
|
|
1490
|
+
domProps(),
|
|
1491
|
+
cleanHoverProps(),
|
|
1492
|
+
(draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
|
|
1493
|
+
(droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
|
|
1494
|
+
),
|
|
1495
|
+
class: renderProps.class(),
|
|
1496
|
+
style: renderProps.style(),
|
|
1497
|
+
...dataAttrs(),
|
|
1498
|
+
children: childContent(),
|
|
1499
|
+
}) as JSX.HTMLAttributes<HTMLLIElement>;
|
|
1500
|
+
const linkMenuItemProps = () =>
|
|
1501
|
+
({
|
|
1502
|
+
...mergeProps(
|
|
1503
|
+
cleanItemPropsForLink(),
|
|
1504
|
+
contextProps() as Record<string, unknown>,
|
|
1505
|
+
domProps(),
|
|
1506
|
+
cleanHoverProps(),
|
|
1507
|
+
linkDomProps(),
|
|
1508
|
+
(draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
|
|
1509
|
+
(droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
|
|
1510
|
+
),
|
|
1511
|
+
class: renderProps.class(),
|
|
1512
|
+
style: renderProps.style(),
|
|
1513
|
+
...dataAttrs(),
|
|
1514
|
+
children: childContent(),
|
|
1515
|
+
}) as JSX.HTMLAttributes<HTMLLIElement>;
|
|
1516
|
+
|
|
1517
|
+
if (local.render && !isLink()) {
|
|
1518
|
+
return local.render(menuItemProps(), renderValues());
|
|
1519
|
+
}
|
|
525
1520
|
|
|
526
1521
|
return (
|
|
527
|
-
<
|
|
528
|
-
{
|
|
529
|
-
{
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
1522
|
+
<Show
|
|
1523
|
+
when={isLink()}
|
|
1524
|
+
fallback={
|
|
1525
|
+
<li
|
|
1526
|
+
ref={(el) => {
|
|
1527
|
+
setRef(el);
|
|
1528
|
+
itemContext?.setItemRef?.(el);
|
|
1529
|
+
assignRef(local.ref, el);
|
|
1530
|
+
}}
|
|
1531
|
+
{...mergeProps(
|
|
1532
|
+
cleanItemProps(),
|
|
1533
|
+
contextProps() as Record<string, unknown>,
|
|
1534
|
+
domProps(),
|
|
1535
|
+
cleanHoverProps(),
|
|
1536
|
+
(draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
|
|
1537
|
+
(droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
|
|
1538
|
+
)}
|
|
1539
|
+
class={renderProps.class()}
|
|
1540
|
+
style={renderProps.style()}
|
|
1541
|
+
{...dataAttrs()}
|
|
1542
|
+
>
|
|
1543
|
+
{childContent()}
|
|
1544
|
+
</li>
|
|
1545
|
+
}
|
|
537
1546
|
>
|
|
538
|
-
|
|
539
|
-
|
|
1547
|
+
<li
|
|
1548
|
+
ref={(el) => {
|
|
1549
|
+
setRef(el);
|
|
1550
|
+
itemContext?.setItemRef?.(el);
|
|
1551
|
+
assignRef(local.ref, el);
|
|
1552
|
+
}}
|
|
1553
|
+
role="presentation"
|
|
1554
|
+
>
|
|
1555
|
+
{local.render ? (
|
|
1556
|
+
local.render(linkMenuItemProps(), renderValues())
|
|
1557
|
+
) : (
|
|
1558
|
+
<a
|
|
1559
|
+
{...mergeProps(
|
|
1560
|
+
cleanItemPropsForLink(),
|
|
1561
|
+
contextProps() as Record<string, unknown>,
|
|
1562
|
+
domProps(),
|
|
1563
|
+
cleanHoverProps(),
|
|
1564
|
+
linkDomProps(),
|
|
1565
|
+
(draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
|
|
1566
|
+
(droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
|
|
1567
|
+
)}
|
|
1568
|
+
class={renderProps.class()}
|
|
1569
|
+
style={renderProps.style()}
|
|
1570
|
+
{...dataAttrs()}
|
|
1571
|
+
>
|
|
1572
|
+
{childContent()}
|
|
1573
|
+
</a>
|
|
1574
|
+
)}
|
|
1575
|
+
</li>
|
|
1576
|
+
</Show>
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/**
|
|
1581
|
+
* Section primitive alias for Menu composition parity.
|
|
1582
|
+
*/
|
|
1583
|
+
export function MenuSection(props: MenuSectionProps): JSX.Element {
|
|
1584
|
+
const [selectionProps, sectionProps] = splitProps(props, [
|
|
1585
|
+
"selectionMode",
|
|
1586
|
+
"selectionBehavior",
|
|
1587
|
+
"disallowEmptySelection",
|
|
1588
|
+
"selectedKeys",
|
|
1589
|
+
"defaultSelectedKeys",
|
|
1590
|
+
"onSelectionChange",
|
|
1591
|
+
"disabledKeys",
|
|
1592
|
+
"disabledBehavior",
|
|
1593
|
+
"allowDuplicateSelectionEvents",
|
|
1594
|
+
"shouldCloseOnSelect",
|
|
1595
|
+
]);
|
|
1596
|
+
|
|
1597
|
+
const selectionState = createSelectionState({
|
|
1598
|
+
get selectionMode() {
|
|
1599
|
+
return selectionProps.selectionMode ?? "none";
|
|
1600
|
+
},
|
|
1601
|
+
get selectionBehavior() {
|
|
1602
|
+
return selectionProps.selectionBehavior;
|
|
1603
|
+
},
|
|
1604
|
+
get disallowEmptySelection() {
|
|
1605
|
+
return selectionProps.disallowEmptySelection;
|
|
1606
|
+
},
|
|
1607
|
+
get selectedKeys() {
|
|
1608
|
+
return selectionProps.selectionMode ? selectionProps.selectedKeys : undefined;
|
|
1609
|
+
},
|
|
1610
|
+
get defaultSelectedKeys() {
|
|
1611
|
+
return selectionProps.selectionMode ? selectionProps.defaultSelectedKeys : undefined;
|
|
1612
|
+
},
|
|
1613
|
+
get onSelectionChange() {
|
|
1614
|
+
return selectionProps.selectionMode ? selectionProps.onSelectionChange : undefined;
|
|
1615
|
+
},
|
|
1616
|
+
get disabledKeys() {
|
|
1617
|
+
return selectionProps.disabledKeys;
|
|
1618
|
+
},
|
|
1619
|
+
get disabledBehavior() {
|
|
1620
|
+
return selectionProps.disabledBehavior;
|
|
1621
|
+
},
|
|
1622
|
+
get allowDuplicateSelectionEvents() {
|
|
1623
|
+
return selectionProps.allowDuplicateSelectionEvents;
|
|
1624
|
+
},
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
const sectionSelection: MenuSectionSelectionContextValue = {
|
|
1628
|
+
selectionMode: selectionState.selectionMode,
|
|
1629
|
+
isSelected: selectionState.isSelected,
|
|
1630
|
+
isDisabled: selectionState.isDisabled,
|
|
1631
|
+
select(key, event) {
|
|
1632
|
+
selectionState.select(key, event);
|
|
1633
|
+
},
|
|
1634
|
+
shouldCloseOnSelect() {
|
|
1635
|
+
return selectionProps.shouldCloseOnSelect;
|
|
1636
|
+
},
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1639
|
+
return (
|
|
1640
|
+
<MenuSectionSelectionContext.Provider value={sectionSelection}>
|
|
1641
|
+
<Section {...sectionProps} />
|
|
1642
|
+
</MenuSectionSelectionContext.Provider>
|
|
540
1643
|
);
|
|
541
1644
|
}
|
|
542
1645
|
|
|
543
|
-
// Attach Item as a static property
|
|
544
1646
|
Menu.Item = MenuItem;
|