@navikt/ds-react 7.2.1 → 7.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/cjs/accordion/AccordionHeader.js +1 -1
- package/cjs/accordion/AccordionHeader.js.map +1 -1
- package/cjs/alert/Alert.d.ts +0 -3
- package/cjs/alert/Alert.js +11 -17
- package/cjs/alert/Alert.js.map +1 -1
- package/cjs/chips/Removable.d.ts +5 -5
- package/cjs/chips/Removable.js +4 -2
- package/cjs/chips/Removable.js.map +1 -1
- package/cjs/collapsible/Collapsible.context.d.ts +1 -1
- package/cjs/date/datepicker/DatePicker.d.ts +2 -2
- package/cjs/date/monthpicker/MonthPicker.d.ts +2 -2
- package/cjs/form/file-upload/FileUpload.context.d.ts +1 -1
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +4 -2
- package/cjs/index.js.map +1 -1
- package/cjs/layout/base/BasePrimitive.d.ts +3 -0
- package/cjs/layout/base/BasePrimitive.js.map +1 -1
- package/cjs/layout/box/Box.d.ts +2 -2
- package/cjs/layout/box/Box.js.map +1 -1
- package/cjs/layout/grid/HGrid.d.ts +2 -2
- package/cjs/layout/grid/HGrid.js.map +1 -1
- package/cjs/layout/stack/Stack.d.ts +2 -2
- package/cjs/layout/stack/Stack.js.map +1 -1
- package/cjs/modal/ModalHeader.js +6 -1
- package/cjs/modal/ModalHeader.js.map +1 -1
- package/cjs/modal/dialog-polyfill.js +2 -2
- package/cjs/modal/dialog-polyfill.js.map +1 -1
- package/cjs/overlays/action-menu/ActionMenu.d.ts +310 -0
- package/cjs/overlays/action-menu/ActionMenu.js +227 -0
- package/cjs/overlays/action-menu/ActionMenu.js.map +1 -0
- package/cjs/overlays/action-menu/index.d.ts +1 -0
- package/cjs/overlays/action-menu/index.js +19 -0
- package/cjs/overlays/action-menu/index.js.map +1 -0
- package/cjs/overlays/floating/Floating.js +9 -10
- package/cjs/overlays/floating/Floating.js.map +1 -1
- package/cjs/overlays/floating/Floating.utils.d.ts +3 -5
- package/cjs/overlays/floating/Floating.utils.js +0 -2
- package/cjs/overlays/floating/Floating.utils.js.map +1 -1
- package/cjs/overlays/floating-menu/Menu.d.ts +15 -21
- package/cjs/overlays/floating-menu/Menu.js +119 -230
- package/cjs/overlays/floating-menu/Menu.js.map +1 -1
- package/cjs/overlays/floating-menu/parts/RovingFocus.d.ts +1 -1
- package/cjs/overlays/floating-menu/parts/RovingFocus.js.map +1 -1
- package/cjs/pagination/Pagination.d.ts +1 -6
- package/cjs/pagination/Pagination.js.map +1 -1
- package/cjs/provider/i18n/LanguageProvider.d.ts +3 -3
- package/cjs/stepper/context.d.ts +1 -1
- package/cjs/table/Body.d.ts +2 -4
- package/cjs/table/Body.js.map +1 -1
- package/cjs/table/ColumnHeader.d.ts +1 -2
- package/cjs/table/ColumnHeader.js.map +1 -1
- package/cjs/table/ExpandableRow.d.ts +1 -2
- package/cjs/table/ExpandableRow.js.map +1 -1
- package/cjs/table/Header.d.ts +2 -4
- package/cjs/table/Header.js.map +1 -1
- package/cjs/table/HeaderCell.d.ts +1 -2
- package/cjs/table/HeaderCell.js.map +1 -1
- package/cjs/table/Row.d.ts +1 -2
- package/cjs/table/Row.js.map +1 -1
- package/cjs/tabs/Tabs.context.d.ts +1 -1
- package/cjs/toggle-group/ToggleGroup.context.d.ts +1 -1
- package/cjs/util/i18n/get.d.ts +2 -2
- package/cjs/util/i18n/get.js.map +1 -1
- package/cjs/util/i18n/i18n.context.d.ts +2 -3
- package/cjs/util/i18n/i18n.context.js.map +1 -1
- package/cjs/util/i18n/i18n.types.d.ts +5 -9
- package/cjs/util/i18n/locales/en.d.ts +39 -0
- package/cjs/util/i18n/locales/en.js +41 -0
- package/cjs/util/i18n/locales/en.js.map +1 -0
- package/cjs/util/i18n/locales/nb.d.ts +14 -0
- package/cjs/util/i18n/locales/nb.js +14 -0
- package/cjs/util/i18n/locales/nb.js.map +1 -1
- package/cjs/util/i18n/locales/nn.d.ts +39 -0
- package/cjs/util/i18n/locales/nn.js +41 -0
- package/cjs/util/i18n/locales/nn.js.map +1 -0
- package/cjs/util/requireReactElement.d.ts +2 -0
- package/cjs/util/requireReactElement.js +22 -0
- package/cjs/util/requireReactElement.js.map +1 -0
- package/cjs/util/virtualfocus/Context.d.ts +1 -1
- package/cjs/util/virtualfocus/parts/VirtualFocusContent.d.ts +1 -2
- package/cjs/util/virtualfocus/parts/VirtualFocusContent.js.map +1 -1
- package/esm/accordion/AccordionHeader.js +1 -1
- package/esm/accordion/AccordionHeader.js.map +1 -1
- package/esm/alert/Alert.d.ts +0 -3
- package/esm/alert/Alert.js +11 -17
- package/esm/alert/Alert.js.map +1 -1
- package/esm/chips/Removable.d.ts +5 -5
- package/esm/chips/Removable.js +4 -2
- package/esm/chips/Removable.js.map +1 -1
- package/esm/collapsible/Collapsible.context.d.ts +1 -1
- package/esm/date/datepicker/DatePicker.d.ts +2 -2
- package/esm/date/monthpicker/MonthPicker.d.ts +2 -2
- package/esm/form/file-upload/FileUpload.context.d.ts +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/layout/base/BasePrimitive.d.ts +3 -0
- package/esm/layout/base/BasePrimitive.js.map +1 -1
- package/esm/layout/box/Box.d.ts +2 -2
- package/esm/layout/box/Box.js.map +1 -1
- package/esm/layout/grid/HGrid.d.ts +2 -2
- package/esm/layout/grid/HGrid.js.map +1 -1
- package/esm/layout/stack/Stack.d.ts +2 -2
- package/esm/layout/stack/Stack.js.map +1 -1
- package/esm/modal/ModalHeader.js +6 -1
- package/esm/modal/ModalHeader.js.map +1 -1
- package/esm/modal/dialog-polyfill.js +2 -2
- package/esm/modal/dialog-polyfill.js.map +1 -1
- package/esm/overlays/action-menu/ActionMenu.d.ts +310 -0
- package/esm/overlays/action-menu/ActionMenu.js +197 -0
- package/esm/overlays/action-menu/ActionMenu.js.map +1 -0
- package/esm/overlays/action-menu/index.d.ts +1 -0
- package/esm/overlays/action-menu/index.js +3 -0
- package/esm/overlays/action-menu/index.js.map +1 -0
- package/esm/overlays/floating/Floating.js +9 -10
- package/esm/overlays/floating/Floating.js.map +1 -1
- package/esm/overlays/floating/Floating.utils.d.ts +3 -5
- package/esm/overlays/floating/Floating.utils.js +0 -2
- package/esm/overlays/floating/Floating.utils.js.map +1 -1
- package/esm/overlays/floating-menu/Menu.d.ts +15 -21
- package/esm/overlays/floating-menu/Menu.js +119 -230
- package/esm/overlays/floating-menu/Menu.js.map +1 -1
- package/esm/overlays/floating-menu/parts/RovingFocus.d.ts +1 -1
- package/esm/overlays/floating-menu/parts/RovingFocus.js.map +1 -1
- package/esm/pagination/Pagination.d.ts +1 -6
- package/esm/pagination/Pagination.js.map +1 -1
- package/esm/provider/i18n/LanguageProvider.d.ts +3 -3
- package/esm/stepper/context.d.ts +1 -1
- package/esm/table/Body.d.ts +2 -4
- package/esm/table/Body.js.map +1 -1
- package/esm/table/ColumnHeader.d.ts +1 -2
- package/esm/table/ColumnHeader.js.map +1 -1
- package/esm/table/ExpandableRow.d.ts +1 -2
- package/esm/table/ExpandableRow.js.map +1 -1
- package/esm/table/Header.d.ts +2 -4
- package/esm/table/Header.js.map +1 -1
- package/esm/table/HeaderCell.d.ts +1 -2
- package/esm/table/HeaderCell.js.map +1 -1
- package/esm/table/Row.d.ts +1 -2
- package/esm/table/Row.js.map +1 -1
- package/esm/tabs/Tabs.context.d.ts +1 -1
- package/esm/toggle-group/ToggleGroup.context.d.ts +1 -1
- package/esm/util/i18n/get.d.ts +2 -2
- package/esm/util/i18n/get.js.map +1 -1
- package/esm/util/i18n/i18n.context.d.ts +2 -3
- package/esm/util/i18n/i18n.context.js.map +1 -1
- package/esm/util/i18n/i18n.types.d.ts +5 -9
- package/esm/util/i18n/locales/en.d.ts +39 -0
- package/esm/util/i18n/locales/en.js +39 -0
- package/esm/util/i18n/locales/en.js.map +1 -0
- package/esm/util/i18n/locales/nb.d.ts +14 -0
- package/esm/util/i18n/locales/nb.js +14 -0
- package/esm/util/i18n/locales/nb.js.map +1 -1
- package/esm/util/i18n/locales/nn.d.ts +39 -0
- package/esm/util/i18n/locales/nn.js +39 -0
- package/esm/util/i18n/locales/nn.js.map +1 -0
- package/esm/util/requireReactElement.d.ts +2 -0
- package/esm/util/requireReactElement.js +15 -0
- package/esm/util/requireReactElement.js.map +1 -0
- package/esm/util/virtualfocus/Context.d.ts +1 -1
- package/esm/util/virtualfocus/parts/VirtualFocusContent.d.ts +1 -2
- package/esm/util/virtualfocus/parts/VirtualFocusContent.js.map +1 -1
- package/package.json +15 -7
- package/src/accordion/AccordionHeader.tsx +0 -1
- package/src/alert/Alert.tsx +11 -20
- package/src/chips/Removable.tsx +13 -9
- package/src/date/datepicker/DatePicker.tsx +2 -2
- package/src/date/monthpicker/MonthPicker.tsx +2 -2
- package/src/form/checkbox/Checkbox.test.tsx +2 -3
- package/src/form/confirmation-panel/ConfirmationPanel.test.tsx +1 -2
- package/src/form/radio/Radio.test.tsx +4 -5
- package/src/index.ts +1 -0
- package/src/layout/base/BasePrimitive.tsx +3 -0
- package/src/layout/box/Box.tsx +35 -36
- package/src/layout/grid/HGrid.tsx +26 -27
- package/src/layout/stack/Stack.tsx +53 -54
- package/src/modal/ModalHeader.tsx +6 -0
- package/src/modal/dialog-polyfill.ts +2 -2
- package/src/overlays/action-menu/ActionMenu.tsx +971 -0
- package/src/overlays/action-menu/index.ts +29 -0
- package/src/overlays/floating/Floating.tsx +6 -12
- package/src/overlays/floating/Floating.utils.ts +2 -5
- package/src/overlays/floating-menu/Menu.tsx +183 -332
- package/src/overlays/floating-menu/parts/RovingFocus.tsx +3 -3
- package/src/pagination/Pagination.tsx +4 -1
- package/src/pagination/steps.test.ts +15 -16
- package/src/provider/i18n/LanguageProvider.tsx +3 -3
- package/src/table/Body.tsx +4 -6
- package/src/table/ColumnHeader.tsx +3 -4
- package/src/table/ExpandableRow.tsx +3 -4
- package/src/table/Header.tsx +4 -6
- package/src/table/HeaderCell.tsx +3 -4
- package/src/table/Row.tsx +3 -4
- package/src/util/i18n/get.ts +3 -3
- package/src/util/i18n/i18n.context.ts +2 -3
- package/src/util/i18n/i18n.types.ts +7 -11
- package/src/util/i18n/locales/en.ts +40 -0
- package/src/util/i18n/locales/nb.ts +23 -1
- package/src/util/i18n/locales/nn.ts +40 -0
- package/src/util/i18n/locales.test.tsx +23 -0
- package/src/util/requireReactElement.ts +25 -0
- package/src/util/virtualfocus/parts/VirtualFocusContent.tsx +4 -2
- package/cjs/util/i18n/merge.d.ts +0 -2
- package/cjs/util/i18n/merge.js +0 -28
- package/cjs/util/i18n/merge.js.map +0 -1
- package/esm/util/i18n/merge.d.ts +0 -2
- package/esm/util/i18n/merge.js +0 -25
- package/esm/util/i18n/merge.js.map +0 -1
- package/src/util/i18n/merge.ts +0 -35
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
import cl from "clsx";
|
|
2
|
+
import React, { forwardRef, useRef } from "react";
|
|
3
|
+
import { ChevronRightIcon } from "@navikt/aksel-icons";
|
|
4
|
+
import { useModalContext } from "../../modal/Modal.context";
|
|
5
|
+
import { Slot } from "../../slot/Slot";
|
|
6
|
+
import { OverridableComponent, useId } from "../../util";
|
|
7
|
+
import { composeEventHandlers } from "../../util/composeEventHandlers";
|
|
8
|
+
import { createContext } from "../../util/create-context";
|
|
9
|
+
import { useMergeRefs } from "../../util/hooks";
|
|
10
|
+
import { useControllableState } from "../../util/hooks/useControllableState";
|
|
11
|
+
import { requireReactElement } from "../../util/requireReactElement";
|
|
12
|
+
import { Menu, MenuPortalProps } from "../floating-menu/Menu";
|
|
13
|
+
|
|
14
|
+
/* -------------------------------------------------------------------------- */
|
|
15
|
+
/* ActionMenu */
|
|
16
|
+
/* -------------------------------------------------------------------------- */
|
|
17
|
+
type ActionMenuContextValue = {
|
|
18
|
+
triggerId: string;
|
|
19
|
+
triggerRef: React.RefObject<HTMLButtonElement>;
|
|
20
|
+
contentId: string;
|
|
21
|
+
open: boolean;
|
|
22
|
+
onOpenChange: (open: boolean) => void;
|
|
23
|
+
onOpenToggle: () => void;
|
|
24
|
+
rootElement: MenuPortalProps["rootElement"];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const [ActionMenuProvider, useActionMenuContext] =
|
|
28
|
+
createContext<ActionMenuContextValue>({
|
|
29
|
+
name: "ActionMenuContext",
|
|
30
|
+
errorMessage:
|
|
31
|
+
"ActionMenu sub-components cannot be rendered outside the ActionMenu component.",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
type ActionMenuProps = {
|
|
35
|
+
children?: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Whether the menu is open or not.
|
|
38
|
+
* Only needed if you want manually control state.
|
|
39
|
+
*/
|
|
40
|
+
open?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Callback for when the menu is opened or closed.
|
|
43
|
+
*/
|
|
44
|
+
onOpenChange?: (open: boolean) => void;
|
|
45
|
+
} & Pick<MenuPortalProps, "rootElement">;
|
|
46
|
+
|
|
47
|
+
interface ActionMenuComponent extends React.FC<ActionMenuProps> {
|
|
48
|
+
/**
|
|
49
|
+
* Acts as a trigger and anchor for the menu.
|
|
50
|
+
* Must be wrapped around a button. If you use your own component, make sure to forward ref and props.
|
|
51
|
+
* @example
|
|
52
|
+
* ```jsx
|
|
53
|
+
* <ActionMenu.Trigger>
|
|
54
|
+
* <button>Open Menu</button>
|
|
55
|
+
* </ActionMenu.Trigger>
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
Trigger: typeof ActionMenuTrigger;
|
|
59
|
+
/**
|
|
60
|
+
* The menu content, containing all the items.
|
|
61
|
+
* @example
|
|
62
|
+
* ```jsx
|
|
63
|
+
* <ActionMenu.Content>
|
|
64
|
+
* <ActionMenu.Item>
|
|
65
|
+
* Item 1
|
|
66
|
+
* </ActionMenu.Item>
|
|
67
|
+
* <ActionMenu.Item>
|
|
68
|
+
* Item 2
|
|
69
|
+
* </ActionMenu.Item>
|
|
70
|
+
* </ActionMenu.Content>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
Content: typeof ActionMenuContent;
|
|
74
|
+
/**
|
|
75
|
+
* Semantically and visually groups items together with a label.
|
|
76
|
+
* This is the prefered way to group items, as it provides better accessibility
|
|
77
|
+
* rather than using a standalone `ActionMenu.Label`.
|
|
78
|
+
*
|
|
79
|
+
* It is required to use either `label` or `aria-label` to provide an accessible name for the group.
|
|
80
|
+
* @example
|
|
81
|
+
* ```jsx
|
|
82
|
+
* <ActionMenu.Content>
|
|
83
|
+
* <ActionMenu.Group label="Group 1">
|
|
84
|
+
* <ActionMenu.Item>
|
|
85
|
+
* Item 1
|
|
86
|
+
* </ActionMenu.Item>
|
|
87
|
+
* <ActionMenu.Item>
|
|
88
|
+
* Item 2
|
|
89
|
+
* </ActionMenu.Item>
|
|
90
|
+
* </ActionMenu.Group>
|
|
91
|
+
* <ActionMenu.Group label="Group 2">
|
|
92
|
+
* <ActionMenu.Item>
|
|
93
|
+
* Item 3
|
|
94
|
+
* </ActionMenu.Item>
|
|
95
|
+
* <ActionMenu.Item>
|
|
96
|
+
* Item 4
|
|
97
|
+
* </ActionMenu.Item>
|
|
98
|
+
* </ActionMenu.Group>
|
|
99
|
+
* </ActionMenu.Content>
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
Group: typeof ActionMenuGroup;
|
|
103
|
+
/**
|
|
104
|
+
* Separate labeling option for the menu.
|
|
105
|
+
* This is not for grouping items, but rather for adding a label to the menu at the top. For grouping items, use `ActionMenu.Group`.
|
|
106
|
+
* @example
|
|
107
|
+
* ```jsx
|
|
108
|
+
* <ActionMenu.Content>
|
|
109
|
+
* <ActionMenu.Label>
|
|
110
|
+
* Label
|
|
111
|
+
* </ActionMenu.Label>
|
|
112
|
+
* <ActionMenu.Divider />
|
|
113
|
+
* </ActionMenu.Content
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
Label: typeof ActionMenuLabel;
|
|
117
|
+
/**
|
|
118
|
+
* A single item in the menu. Can be used standalone or grouped with other items.
|
|
119
|
+
* Use `onSelect` to handle the action when the item is selected, like navigating to a new page or performing an action.
|
|
120
|
+
* @example
|
|
121
|
+
* ```jsx
|
|
122
|
+
* <ActionMenu.Content>
|
|
123
|
+
* // Grouped
|
|
124
|
+
* <ActionMenu.Group label="Group 1">
|
|
125
|
+
* <ActionMenu.Item onSelect={navigate}>
|
|
126
|
+
* Item 1
|
|
127
|
+
* </ActionMenu.Item>
|
|
128
|
+
* <ActionMenu.Item onSelect={navigate}>
|
|
129
|
+
* Item 2
|
|
130
|
+
* </ActionMenu.Item>
|
|
131
|
+
* </ActionMenu.Group>
|
|
132
|
+
* <ActionMenu.Divider />
|
|
133
|
+
* // Standalone
|
|
134
|
+
* <ActionMenu.Item onSelect={updateState}>
|
|
135
|
+
* Item 3
|
|
136
|
+
* </ActionMenu.Item>
|
|
137
|
+
* </ActionMenu.Content>
|
|
138
|
+
* ```
|
|
139
|
+
* @example As link
|
|
140
|
+
* ```jsx
|
|
141
|
+
* <ActionMenu.Item as="a" href="#">
|
|
142
|
+
* Item
|
|
143
|
+
* </ActionMenu.Item>
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
Item: typeof ActionMenuItem;
|
|
147
|
+
/**
|
|
148
|
+
* A checkbox item in the menu. Can be used standalone or grouped with other items.
|
|
149
|
+
* @example
|
|
150
|
+
* ```jsx
|
|
151
|
+
* <ActionMenu.CheckboxItem
|
|
152
|
+
* checked={isChecked}
|
|
153
|
+
* onCheckedChange={handleChange}
|
|
154
|
+
* >
|
|
155
|
+
* Checkbox 1
|
|
156
|
+
* </ActionMenu.CheckboxItem>
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
CheckboxItem: typeof ActionMenuCheckboxItem;
|
|
160
|
+
/**
|
|
161
|
+
* A radio group in the menu.
|
|
162
|
+
*
|
|
163
|
+
* It is required to use either `label` or `aria-label` to provide an accessible name for the group.
|
|
164
|
+
* @example
|
|
165
|
+
* ```jsx
|
|
166
|
+
* <ActionMenu.RadioGroup
|
|
167
|
+
* onValueChange={handleValueChange}
|
|
168
|
+
* value={radioValue}
|
|
169
|
+
* label="Radio group"
|
|
170
|
+
* >
|
|
171
|
+
* <ActionMenu.RadioItem value="1">Radio 1</ActionMenu.RadioItem>
|
|
172
|
+
* <ActionMenu.RadioItem value="2">Radio 2</ActionMenu.RadioItem>
|
|
173
|
+
* </ActionMenu.RadioGroup>
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
RadioGroup: typeof ActionMenuRadioGroup;
|
|
177
|
+
/**
|
|
178
|
+
* A radio item in the menu. Should always be grouped with an `ActionMenu.RadioGroup`.
|
|
179
|
+
* @example
|
|
180
|
+
* ```jsx
|
|
181
|
+
* <ActionMenu.RadioGroup
|
|
182
|
+
* onValueChange={handleValueChange}
|
|
183
|
+
* value={radioValue}
|
|
184
|
+
* label="Radio group"
|
|
185
|
+
* >
|
|
186
|
+
* <ActionMenu.RadioItem value="1">Radio 1</ActionMenu.RadioItem>
|
|
187
|
+
* <ActionMenu.RadioItem value="2">Radio 2</ActionMenu.RadioItem>
|
|
188
|
+
* </ActionMenu.RadioGroup>
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
RadioItem: typeof ActionMenuRadioItem;
|
|
192
|
+
/**
|
|
193
|
+
* A simple divider to separate items in the menu.
|
|
194
|
+
*/
|
|
195
|
+
Divider: typeof ActionMenuDivider;
|
|
196
|
+
/**
|
|
197
|
+
* A sub-menu that can be nested inside the menu.
|
|
198
|
+
* The sub-menu can be nested inside other sub-menus allowing for multiple levels of nesting.
|
|
199
|
+
* @example
|
|
200
|
+
* ```jsx
|
|
201
|
+
* <ActionMenu.Sub>
|
|
202
|
+
* <ActionMenu.SubTrigger>Submenu 1</ActionMenu.SubTrigger>
|
|
203
|
+
* <ActionMenu.SubContent>
|
|
204
|
+
* <ActionMenu.Item>
|
|
205
|
+
* Subitem 1
|
|
206
|
+
* </ActionMenu.Item>
|
|
207
|
+
* <ActionMenu.Item>
|
|
208
|
+
* Subitem 2
|
|
209
|
+
* </ActionMenu.Item>
|
|
210
|
+
* </ActionMenu.SubContent>
|
|
211
|
+
* </ActionMenu.Sub>
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
Sub: typeof ActionMenuSub;
|
|
215
|
+
/**
|
|
216
|
+
* Acts as a trigger for a sub-menu.
|
|
217
|
+
* In contrast to `ActionMenu.Trigger`, this trigger is a standalone component and should not be wrapped around a React.ReactNode.
|
|
218
|
+
* @example
|
|
219
|
+
* ```jsx
|
|
220
|
+
* <ActionMenu.Sub>
|
|
221
|
+
* <ActionMenu.SubTrigger>Submenu 1</ActionMenu.SubTrigger>
|
|
222
|
+
* </ActionMenu.Sub>
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
SubTrigger: typeof ActionMenuSubTrigger;
|
|
226
|
+
/**
|
|
227
|
+
* The content of a sub-menu.
|
|
228
|
+
* @example
|
|
229
|
+
* ```jsx
|
|
230
|
+
* <ActionMenu.Sub>
|
|
231
|
+
* <ActionMenu.SubContent>
|
|
232
|
+
* <ActionMenu.Item>
|
|
233
|
+
* Subitem 1
|
|
234
|
+
* </ActionMenu.Item>
|
|
235
|
+
* <ActionMenu.Item>
|
|
236
|
+
* Subitem 2
|
|
237
|
+
* </ActionMenu.Item>
|
|
238
|
+
* </ActionMenu.SubContent>
|
|
239
|
+
* </ActionMenu.Sub>
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
SubContent: typeof ActionMenuSubContent;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const ActionMenuRoot = ({
|
|
246
|
+
children,
|
|
247
|
+
open: openProp,
|
|
248
|
+
onOpenChange,
|
|
249
|
+
rootElement: rootElementProp,
|
|
250
|
+
}: ActionMenuProps) => {
|
|
251
|
+
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
252
|
+
|
|
253
|
+
const modalContext = useModalContext(false);
|
|
254
|
+
const rootElement = modalContext ? modalContext.ref.current : rootElementProp;
|
|
255
|
+
|
|
256
|
+
const [open = false, setOpen] = useControllableState({
|
|
257
|
+
value: openProp,
|
|
258
|
+
defaultValue: false,
|
|
259
|
+
onChange: onOpenChange,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<ActionMenuProvider
|
|
264
|
+
triggerId={useId()}
|
|
265
|
+
triggerRef={triggerRef}
|
|
266
|
+
contentId={useId()}
|
|
267
|
+
open={open}
|
|
268
|
+
onOpenChange={setOpen}
|
|
269
|
+
onOpenToggle={() => setOpen((prevOpen) => !prevOpen)}
|
|
270
|
+
rootElement={rootElement}
|
|
271
|
+
>
|
|
272
|
+
<Menu open={open} onOpenChange={setOpen} modal>
|
|
273
|
+
{children}
|
|
274
|
+
</Menu>
|
|
275
|
+
</ActionMenuProvider>
|
|
276
|
+
);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export const ActionMenu = ActionMenuRoot as ActionMenuComponent;
|
|
280
|
+
|
|
281
|
+
/* -------------------------------------------------------------------------- */
|
|
282
|
+
/* ActionMenuTrigger */
|
|
283
|
+
/* -------------------------------------------------------------------------- */
|
|
284
|
+
interface ActionMenuTriggerProps
|
|
285
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
286
|
+
children: React.ReactElement;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export const ActionMenuTrigger = forwardRef<
|
|
290
|
+
HTMLButtonElement,
|
|
291
|
+
ActionMenuTriggerProps
|
|
292
|
+
>(
|
|
293
|
+
(
|
|
294
|
+
{ children, onKeyDown, style, onClick, ...rest }: ActionMenuTriggerProps,
|
|
295
|
+
ref,
|
|
296
|
+
) => {
|
|
297
|
+
const context = useActionMenuContext();
|
|
298
|
+
|
|
299
|
+
const mergedRefs = useMergeRefs(ref, context.triggerRef);
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<Menu.Anchor asChild>
|
|
303
|
+
<Slot
|
|
304
|
+
type="button"
|
|
305
|
+
id={context.triggerId}
|
|
306
|
+
aria-haspopup="menu"
|
|
307
|
+
aria-expanded={context.open}
|
|
308
|
+
aria-controls={context.open ? context.contentId : undefined}
|
|
309
|
+
data-state={context.open ? "open" : "closed"}
|
|
310
|
+
ref={mergedRefs}
|
|
311
|
+
{...rest}
|
|
312
|
+
style={{ ...style, pointerEvents: context.open ? "auto" : undefined }}
|
|
313
|
+
onClick={composeEventHandlers(onClick, context.onOpenToggle)}
|
|
314
|
+
onKeyDown={composeEventHandlers(onKeyDown, (event) => {
|
|
315
|
+
if (event.key === "ArrowDown") {
|
|
316
|
+
context.onOpenChange(true);
|
|
317
|
+
/* Stop keydown from scrolling window */
|
|
318
|
+
event.preventDefault();
|
|
319
|
+
}
|
|
320
|
+
})}
|
|
321
|
+
>
|
|
322
|
+
{requireReactElement(children)}
|
|
323
|
+
</Slot>
|
|
324
|
+
</Menu.Anchor>
|
|
325
|
+
);
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
/* -------------------------------------------------------------------------- */
|
|
330
|
+
/* ActionMenuContent */
|
|
331
|
+
/* -------------------------------------------------------------------------- */
|
|
332
|
+
interface ActionMenuContentProps
|
|
333
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "id"> {
|
|
334
|
+
children?: React.ReactNode;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export const ActionMenuContent = forwardRef<
|
|
338
|
+
HTMLDivElement,
|
|
339
|
+
ActionMenuContentProps
|
|
340
|
+
>(({ children, className, style, ...rest }: ActionMenuContentProps, ref) => {
|
|
341
|
+
const context = useActionMenuContext();
|
|
342
|
+
|
|
343
|
+
return (
|
|
344
|
+
<Menu.Portal rootElement={context.rootElement} asChild>
|
|
345
|
+
<Menu.Content
|
|
346
|
+
ref={ref}
|
|
347
|
+
id={context.contentId}
|
|
348
|
+
aria-labelledby={context.triggerId}
|
|
349
|
+
className={cl("navds-action-menu__content", className)}
|
|
350
|
+
{...rest}
|
|
351
|
+
align="start"
|
|
352
|
+
sideOffset={4}
|
|
353
|
+
collisionPadding={10}
|
|
354
|
+
onCloseAutoFocus={() => {
|
|
355
|
+
context.triggerRef.current?.focus();
|
|
356
|
+
}}
|
|
357
|
+
safeZone={{ anchor: context.triggerRef.current }}
|
|
358
|
+
style={{
|
|
359
|
+
...style,
|
|
360
|
+
...{
|
|
361
|
+
"--__ac-action-menu-content-transform-origin":
|
|
362
|
+
"var(--ac-floating-transform-origin)",
|
|
363
|
+
"--__ac-action-menu-content-available-height":
|
|
364
|
+
"var(--ac-floating-available-height)",
|
|
365
|
+
},
|
|
366
|
+
}}
|
|
367
|
+
>
|
|
368
|
+
<div className="navds-action-menu__content-inner">{children}</div>
|
|
369
|
+
</Menu.Content>
|
|
370
|
+
</Menu.Portal>
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
/* -------------------------------------------------------------------------- */
|
|
375
|
+
/* ActionMenuLabel */
|
|
376
|
+
/* -------------------------------------------------------------------------- */
|
|
377
|
+
interface ActionMenuLabelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
378
|
+
children: React.ReactNode;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export const ActionMenuLabel = forwardRef<HTMLDivElement, ActionMenuLabelProps>(
|
|
382
|
+
({ children, className, ...rest }: ActionMenuLabelProps, ref) => {
|
|
383
|
+
return (
|
|
384
|
+
<div
|
|
385
|
+
ref={ref}
|
|
386
|
+
{...rest}
|
|
387
|
+
className={cl("navds-action-menu__label", className)}
|
|
388
|
+
>
|
|
389
|
+
{children}
|
|
390
|
+
</div>
|
|
391
|
+
);
|
|
392
|
+
},
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
/* -------------------------------------------------------------------------- */
|
|
396
|
+
/* ActionMenuGroup */
|
|
397
|
+
/* -------------------------------------------------------------------------- */
|
|
398
|
+
type ActionMenuGroupElement = React.ElementRef<typeof Menu.Group>;
|
|
399
|
+
type MenuGroupProps = React.ComponentPropsWithoutRef<typeof Menu.Group>;
|
|
400
|
+
|
|
401
|
+
type ActionMenuGroupLabelingProps =
|
|
402
|
+
| {
|
|
403
|
+
/**
|
|
404
|
+
* Adds a visual and accessible label to the group.
|
|
405
|
+
*/
|
|
406
|
+
label: string;
|
|
407
|
+
/**
|
|
408
|
+
* Adds an aria-label to the group.
|
|
409
|
+
*/
|
|
410
|
+
"aria-label"?: never;
|
|
411
|
+
}
|
|
412
|
+
| {
|
|
413
|
+
/**
|
|
414
|
+
* Adds an aria-label to the group.
|
|
415
|
+
*/
|
|
416
|
+
"aria-label": string;
|
|
417
|
+
/**
|
|
418
|
+
* Adds a visual and accessible label to the group.
|
|
419
|
+
*/
|
|
420
|
+
label?: never;
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
type ActionMenuGroupProps = Omit<MenuGroupProps, "asChild"> &
|
|
424
|
+
ActionMenuGroupLabelingProps;
|
|
425
|
+
|
|
426
|
+
export const ActionMenuGroup = forwardRef<
|
|
427
|
+
ActionMenuGroupElement,
|
|
428
|
+
ActionMenuGroupProps
|
|
429
|
+
>(({ children, className, label, ...rest }: ActionMenuGroupProps, ref) => {
|
|
430
|
+
const labelId = useId();
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<Menu.Group
|
|
434
|
+
ref={ref}
|
|
435
|
+
{...rest}
|
|
436
|
+
className={cl("navds-action-menu__group", className)}
|
|
437
|
+
asChild={false}
|
|
438
|
+
aria-labelledby={label ? labelId : undefined}
|
|
439
|
+
>
|
|
440
|
+
{label && (
|
|
441
|
+
<ActionMenu.Label id={labelId} aria-hidden>
|
|
442
|
+
{label}
|
|
443
|
+
</ActionMenu.Label>
|
|
444
|
+
)}
|
|
445
|
+
{children}
|
|
446
|
+
</Menu.Group>
|
|
447
|
+
);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
/* -------------------------------------------------------------------------- */
|
|
451
|
+
/* Utility components */
|
|
452
|
+
/* -------------------------------------------------------------------------- */
|
|
453
|
+
type MarkerProps = {
|
|
454
|
+
children: React.ReactNode;
|
|
455
|
+
className?: string;
|
|
456
|
+
placement: "left" | "right";
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const Marker = ({ children, className, placement }: MarkerProps) => {
|
|
460
|
+
return (
|
|
461
|
+
<div
|
|
462
|
+
aria-hidden
|
|
463
|
+
className={cl(
|
|
464
|
+
className,
|
|
465
|
+
"navds-action-menu__marker",
|
|
466
|
+
`navds-action-menu__marker--${placement}`,
|
|
467
|
+
)}
|
|
468
|
+
>
|
|
469
|
+
{children}
|
|
470
|
+
</div>
|
|
471
|
+
);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
type ShortcutProps = {
|
|
475
|
+
children: string;
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const Shortcut = ({ children }: ShortcutProps) => {
|
|
479
|
+
/**
|
|
480
|
+
* Assumes the user will input either a single keyboard key
|
|
481
|
+
* or keys separated by "+"
|
|
482
|
+
*/
|
|
483
|
+
const parsed = children.split("+").filter((str) => str !== "");
|
|
484
|
+
|
|
485
|
+
return (
|
|
486
|
+
<Marker placement="right">
|
|
487
|
+
{parsed.map((char, index) => (
|
|
488
|
+
<span key={char + index} className="navds-action-menu__shortcut">
|
|
489
|
+
{char}
|
|
490
|
+
</span>
|
|
491
|
+
))}
|
|
492
|
+
</Marker>
|
|
493
|
+
);
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
/* -------------------------------------------------------------------------- */
|
|
497
|
+
/* ActionMenuItem */
|
|
498
|
+
/* -------------------------------------------------------------------------- */
|
|
499
|
+
type ActionMenuItemElement = React.ElementRef<typeof Menu.Item>;
|
|
500
|
+
type MenuItemProps = React.ComponentPropsWithoutRef<typeof Menu.Item>;
|
|
501
|
+
|
|
502
|
+
interface ActionMenuItemProps extends Omit<MenuItemProps, "asChild"> {
|
|
503
|
+
/**
|
|
504
|
+
* Shows connected shortcut-keys for the item.
|
|
505
|
+
* This is only a visual representation, you will have to implement the actual shortcut yourself.
|
|
506
|
+
*/
|
|
507
|
+
shortcut?: string;
|
|
508
|
+
/**
|
|
509
|
+
* Styles the item as a destructive action.
|
|
510
|
+
*/
|
|
511
|
+
variant?: "danger";
|
|
512
|
+
/**
|
|
513
|
+
* Adds an icon on the left side. The icon will always have aria-hidden.
|
|
514
|
+
*/
|
|
515
|
+
icon?: React.ReactNode;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
export const ActionMenuItem: OverridableComponent<
|
|
519
|
+
ActionMenuItemProps,
|
|
520
|
+
ActionMenuItemElement
|
|
521
|
+
> = forwardRef(
|
|
522
|
+
(
|
|
523
|
+
{
|
|
524
|
+
children,
|
|
525
|
+
as: Component = "div",
|
|
526
|
+
className,
|
|
527
|
+
icon,
|
|
528
|
+
shortcut,
|
|
529
|
+
variant,
|
|
530
|
+
...rest
|
|
531
|
+
},
|
|
532
|
+
ref,
|
|
533
|
+
) => {
|
|
534
|
+
return (
|
|
535
|
+
<Menu.Item
|
|
536
|
+
{...rest}
|
|
537
|
+
className={cl("navds-action-menu__item", className, {
|
|
538
|
+
"navds-action-menu__item--danger": variant === "danger",
|
|
539
|
+
"navds-action-menu__item--has-icon": icon,
|
|
540
|
+
})}
|
|
541
|
+
aria-keyshortcuts={shortcut ?? undefined}
|
|
542
|
+
asChild
|
|
543
|
+
>
|
|
544
|
+
<Component ref={ref}>
|
|
545
|
+
{children}
|
|
546
|
+
{icon && (
|
|
547
|
+
<Marker placement="left" className="navds-action-menu__marker-icon">
|
|
548
|
+
{icon}
|
|
549
|
+
</Marker>
|
|
550
|
+
)}
|
|
551
|
+
{shortcut && <Shortcut>{shortcut}</Shortcut>}
|
|
552
|
+
</Component>
|
|
553
|
+
</Menu.Item>
|
|
554
|
+
);
|
|
555
|
+
},
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
/* -------------------------------------------------------------------------- */
|
|
559
|
+
/* ActionMenuCheckboxItem */
|
|
560
|
+
/* -------------------------------------------------------------------------- */
|
|
561
|
+
type ActionMenuCheckboxItemElement = React.ElementRef<typeof Menu.CheckboxItem>;
|
|
562
|
+
type MenuCheckboxItemProps = React.ComponentPropsWithoutRef<
|
|
563
|
+
typeof Menu.CheckboxItem
|
|
564
|
+
>;
|
|
565
|
+
|
|
566
|
+
interface ActionMenuCheckboxItemProps
|
|
567
|
+
extends Omit<MenuCheckboxItemProps, "asChild"> {
|
|
568
|
+
children: React.ReactNode;
|
|
569
|
+
/**
|
|
570
|
+
* Shows connected shortcut-keys for the item.
|
|
571
|
+
* This is only a visual representation, you will have to implement the actual shortcut yourself.
|
|
572
|
+
*/
|
|
573
|
+
shortcut?: string;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export const ActionMenuCheckboxItem = forwardRef<
|
|
577
|
+
ActionMenuCheckboxItemElement,
|
|
578
|
+
ActionMenuCheckboxItemProps
|
|
579
|
+
>(
|
|
580
|
+
(
|
|
581
|
+
{
|
|
582
|
+
children,
|
|
583
|
+
className,
|
|
584
|
+
shortcut,
|
|
585
|
+
onSelect,
|
|
586
|
+
...rest
|
|
587
|
+
}: ActionMenuCheckboxItemProps,
|
|
588
|
+
ref,
|
|
589
|
+
) => {
|
|
590
|
+
return (
|
|
591
|
+
<Menu.CheckboxItem
|
|
592
|
+
ref={ref}
|
|
593
|
+
{...rest}
|
|
594
|
+
onSelect={composeEventHandlers(onSelect, (event) => {
|
|
595
|
+
/**
|
|
596
|
+
* Prevent default to avoid the menu from closing when clicking the checkbox
|
|
597
|
+
*/
|
|
598
|
+
event.preventDefault();
|
|
599
|
+
})}
|
|
600
|
+
asChild={false}
|
|
601
|
+
className={cl(
|
|
602
|
+
"navds-action-menu__item navds-action-menu__item--has-icon",
|
|
603
|
+
className,
|
|
604
|
+
)}
|
|
605
|
+
aria-keyshortcuts={shortcut}
|
|
606
|
+
>
|
|
607
|
+
{children}
|
|
608
|
+
<Marker placement="left">
|
|
609
|
+
<Menu.ItemIndicator className="navds-action-menu__indicator">
|
|
610
|
+
<svg
|
|
611
|
+
width="1em"
|
|
612
|
+
height="1em"
|
|
613
|
+
viewBox="0 0 24 24"
|
|
614
|
+
fill="none"
|
|
615
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
616
|
+
className="navds-action-menu__indicator-icon"
|
|
617
|
+
aria-hidden
|
|
618
|
+
>
|
|
619
|
+
<g className="navds-action-menu__indicator-icon--unchecked">
|
|
620
|
+
<rect
|
|
621
|
+
width="24"
|
|
622
|
+
height="24"
|
|
623
|
+
rx="4"
|
|
624
|
+
fill="var(--a-border-default)"
|
|
625
|
+
/>
|
|
626
|
+
<rect
|
|
627
|
+
x="1"
|
|
628
|
+
y="1"
|
|
629
|
+
width="22"
|
|
630
|
+
height="22"
|
|
631
|
+
rx="3"
|
|
632
|
+
fill="var(--a-surface-default)"
|
|
633
|
+
strokeWidth="2"
|
|
634
|
+
/>
|
|
635
|
+
</g>
|
|
636
|
+
<g className="navds-action-menu__indicator-icon--indeterminate">
|
|
637
|
+
<rect
|
|
638
|
+
width="24"
|
|
639
|
+
height="24"
|
|
640
|
+
rx="4"
|
|
641
|
+
fill="var(--a-surface-action-selected)"
|
|
642
|
+
/>
|
|
643
|
+
<rect
|
|
644
|
+
x="6"
|
|
645
|
+
y="10"
|
|
646
|
+
width="12"
|
|
647
|
+
height="4"
|
|
648
|
+
rx="1"
|
|
649
|
+
fill="var(--a-surface-default)"
|
|
650
|
+
/>
|
|
651
|
+
</g>
|
|
652
|
+
<g className="navds-action-menu__indicator-icon--checked">
|
|
653
|
+
<rect
|
|
654
|
+
width="24"
|
|
655
|
+
height="24"
|
|
656
|
+
rx="4"
|
|
657
|
+
fill="var(--a-surface-action-selected)"
|
|
658
|
+
/>
|
|
659
|
+
<path
|
|
660
|
+
d="M10.0352 13.4148L16.4752 7.40467C17.0792 6.83965 18.029 6.86933 18.5955 7.47478C19.162 8.08027 19.1296 9.03007 18.5245 9.59621L11.0211 16.5993C10.741 16.859 10.3756 17 10.0002 17C9.60651 17 9.22717 16.8462 8.93914 16.5611L6.43914 14.0611C5.85362 13.4756 5.85362 12.5254 6.43914 11.9399C7.02467 11.3544 7.97483 11.3544 8.56036 11.9399L10.0352 13.4148Z"
|
|
661
|
+
fill="var(--a-surface-default)"
|
|
662
|
+
/>
|
|
663
|
+
</g>
|
|
664
|
+
</svg>
|
|
665
|
+
</Menu.ItemIndicator>
|
|
666
|
+
</Marker>
|
|
667
|
+
|
|
668
|
+
{shortcut && <Shortcut>{shortcut}</Shortcut>}
|
|
669
|
+
</Menu.CheckboxItem>
|
|
670
|
+
);
|
|
671
|
+
},
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
/* -------------------------------------------------------------------------- */
|
|
675
|
+
/* ActionMenuRadioGroup */
|
|
676
|
+
/* -------------------------------------------------------------------------- */
|
|
677
|
+
type ActionMenuRadioGroupElement = React.ElementRef<typeof Menu.RadioGroup>;
|
|
678
|
+
type MenuRadioGroupProps = React.ComponentPropsWithoutRef<
|
|
679
|
+
typeof Menu.RadioGroup
|
|
680
|
+
>;
|
|
681
|
+
type ActionMenuRadioGroupProps = ActionMenuGroupLabelingProps &
|
|
682
|
+
Omit<MenuRadioGroupProps, "asChild"> & {
|
|
683
|
+
children: React.ReactNode;
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
export const ActionMenuRadioGroup = forwardRef<
|
|
687
|
+
ActionMenuRadioGroupElement,
|
|
688
|
+
ActionMenuRadioGroupProps
|
|
689
|
+
>(({ children, label, ...rest }: ActionMenuRadioGroupProps, ref) => {
|
|
690
|
+
const labelId = useId();
|
|
691
|
+
|
|
692
|
+
return (
|
|
693
|
+
<Menu.RadioGroup
|
|
694
|
+
ref={ref}
|
|
695
|
+
{...rest}
|
|
696
|
+
asChild={false}
|
|
697
|
+
aria-labelledby={label ? labelId : undefined}
|
|
698
|
+
>
|
|
699
|
+
{label && (
|
|
700
|
+
<ActionMenu.Label id={labelId} aria-hidden>
|
|
701
|
+
{label}
|
|
702
|
+
</ActionMenu.Label>
|
|
703
|
+
)}
|
|
704
|
+
{children}
|
|
705
|
+
</Menu.RadioGroup>
|
|
706
|
+
);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
/* -------------------------------------------------------------------------- */
|
|
710
|
+
/* ActionMenuRadioItem */
|
|
711
|
+
/* -------------------------------------------------------------------------- */
|
|
712
|
+
type ActionMenuRadioItemElement = React.ElementRef<typeof Menu.RadioItem>;
|
|
713
|
+
type MenuRadioItemProps = React.ComponentPropsWithoutRef<typeof Menu.RadioItem>;
|
|
714
|
+
interface ActionMenuRadioItemProps extends Omit<MenuRadioItemProps, "asChild"> {
|
|
715
|
+
children: React.ReactNode;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export const ActionMenuRadioItem = forwardRef<
|
|
719
|
+
ActionMenuRadioItemElement,
|
|
720
|
+
ActionMenuRadioItemProps
|
|
721
|
+
>(
|
|
722
|
+
(
|
|
723
|
+
{ children, className, onSelect, ...rest }: ActionMenuRadioItemProps,
|
|
724
|
+
ref,
|
|
725
|
+
) => {
|
|
726
|
+
return (
|
|
727
|
+
<Menu.RadioItem
|
|
728
|
+
ref={ref}
|
|
729
|
+
{...rest}
|
|
730
|
+
onSelect={composeEventHandlers(onSelect, (event) => {
|
|
731
|
+
/**
|
|
732
|
+
* Prevent default to avoid the menu from closing when clicking the radio
|
|
733
|
+
*/
|
|
734
|
+
event.preventDefault();
|
|
735
|
+
})}
|
|
736
|
+
asChild={false}
|
|
737
|
+
className={cl(
|
|
738
|
+
"navds-action-menu__item navds-action-menu__item--has-icon",
|
|
739
|
+
className,
|
|
740
|
+
)}
|
|
741
|
+
>
|
|
742
|
+
{children}
|
|
743
|
+
<Marker placement="left">
|
|
744
|
+
<Menu.ItemIndicator className="navds-action-menu__indicator">
|
|
745
|
+
<svg
|
|
746
|
+
width="1em"
|
|
747
|
+
height="1em"
|
|
748
|
+
viewBox="0 0 24 24"
|
|
749
|
+
fill="none"
|
|
750
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
751
|
+
className="navds-action-menu__indicator-icon"
|
|
752
|
+
aria-hidden
|
|
753
|
+
>
|
|
754
|
+
<g className="navds-action-menu__indicator-icon--unchecked">
|
|
755
|
+
<rect
|
|
756
|
+
width="24"
|
|
757
|
+
height="24"
|
|
758
|
+
rx="12"
|
|
759
|
+
fill="var(--a-border-default)"
|
|
760
|
+
/>
|
|
761
|
+
<rect
|
|
762
|
+
x="1"
|
|
763
|
+
y="1"
|
|
764
|
+
width="22"
|
|
765
|
+
height="22"
|
|
766
|
+
rx="11"
|
|
767
|
+
strokeWidth="2"
|
|
768
|
+
fill="var(--a-surface-default)"
|
|
769
|
+
/>
|
|
770
|
+
</g>
|
|
771
|
+
<g className="navds-action-menu__indicator-icon--checked">
|
|
772
|
+
<rect
|
|
773
|
+
x="1"
|
|
774
|
+
y="1"
|
|
775
|
+
width="22"
|
|
776
|
+
height="22"
|
|
777
|
+
rx="11"
|
|
778
|
+
fill="var(--a-surface-default)"
|
|
779
|
+
/>
|
|
780
|
+
<rect
|
|
781
|
+
x="1"
|
|
782
|
+
y="1"
|
|
783
|
+
width="22"
|
|
784
|
+
height="22"
|
|
785
|
+
rx="11"
|
|
786
|
+
stroke="var(--a-surface-action-selected)"
|
|
787
|
+
strokeWidth="2"
|
|
788
|
+
/>
|
|
789
|
+
<path
|
|
790
|
+
d="M20 12C20 16.4178 16.4178 20 12 20C7.58222 20 4 16.4178 4 12C4 7.58222 7.58222 4 12 4C16.4178 4 20 7.58222 20 12Z"
|
|
791
|
+
fill="var(--a-surface-action-selected)"
|
|
792
|
+
/>
|
|
793
|
+
</g>
|
|
794
|
+
</svg>
|
|
795
|
+
</Menu.ItemIndicator>
|
|
796
|
+
</Marker>
|
|
797
|
+
</Menu.RadioItem>
|
|
798
|
+
);
|
|
799
|
+
},
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
/* -------------------------------------------------------------------------- */
|
|
803
|
+
/* ActionMenuDivider */
|
|
804
|
+
/* -------------------------------------------------------------------------- */
|
|
805
|
+
type ActionMenuDividerElement = React.ElementRef<typeof Menu.Divider>;
|
|
806
|
+
type MenuDividerProps = React.ComponentPropsWithoutRef<typeof Menu.Divider>;
|
|
807
|
+
type ActionMenuDividerProps = Omit<MenuDividerProps, "asChild">;
|
|
808
|
+
|
|
809
|
+
export const ActionMenuDivider = forwardRef<
|
|
810
|
+
ActionMenuDividerElement,
|
|
811
|
+
ActionMenuDividerProps
|
|
812
|
+
>(({ className, ...rest }: ActionMenuDividerProps, ref) => {
|
|
813
|
+
return (
|
|
814
|
+
<Menu.Divider
|
|
815
|
+
ref={ref}
|
|
816
|
+
asChild={false}
|
|
817
|
+
{...rest}
|
|
818
|
+
className={cl("navds-action-menu__divider", className)}
|
|
819
|
+
/>
|
|
820
|
+
);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
/* -------------------------------------------------------------------------- */
|
|
824
|
+
/* ActionMenuSub */
|
|
825
|
+
/* -------------------------------------------------------------------------- */
|
|
826
|
+
interface ActionMenuSubProps {
|
|
827
|
+
children?: React.ReactNode;
|
|
828
|
+
/**
|
|
829
|
+
* Whether the sub-menu is open or not. Only needed if you want to manually control state.
|
|
830
|
+
*/
|
|
831
|
+
open?: boolean;
|
|
832
|
+
/**
|
|
833
|
+
* Callback for when the sub-menu is opened or closed.
|
|
834
|
+
*/
|
|
835
|
+
onOpenChange?: (open: boolean) => void;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
export const ActionMenuSub = (props: ActionMenuSubProps) => {
|
|
839
|
+
const { children, open: openProp, onOpenChange } = props;
|
|
840
|
+
|
|
841
|
+
const [open = false, setOpen] = useControllableState({
|
|
842
|
+
value: openProp,
|
|
843
|
+
defaultValue: false,
|
|
844
|
+
onChange: onOpenChange,
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
return (
|
|
848
|
+
<Menu.Sub open={open} onOpenChange={setOpen}>
|
|
849
|
+
{children}
|
|
850
|
+
</Menu.Sub>
|
|
851
|
+
);
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
/* -------------------------------------------------------------------------- */
|
|
855
|
+
/* ActionMenuSubTrigger */
|
|
856
|
+
/* -------------------------------------------------------------------------- */
|
|
857
|
+
type ActionMenuSubTriggerElement = React.ElementRef<typeof Menu.SubTrigger>;
|
|
858
|
+
type MenuSubTriggerProps = React.ComponentPropsWithoutRef<
|
|
859
|
+
typeof Menu.SubTrigger
|
|
860
|
+
>;
|
|
861
|
+
interface ActionMenuSubTriggerProps
|
|
862
|
+
extends Omit<MenuSubTriggerProps, "asChild"> {
|
|
863
|
+
icon?: React.ReactNode;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export const ActionMenuSubTrigger = forwardRef<
|
|
867
|
+
ActionMenuSubTriggerElement,
|
|
868
|
+
ActionMenuSubTriggerProps
|
|
869
|
+
>(({ children, className, icon, ...rest }: ActionMenuSubTriggerProps, ref) => {
|
|
870
|
+
return (
|
|
871
|
+
<Menu.SubTrigger
|
|
872
|
+
ref={ref}
|
|
873
|
+
{...rest}
|
|
874
|
+
asChild={false}
|
|
875
|
+
className={cl(
|
|
876
|
+
"navds-action-menu__item navds-action-menu__sub-trigger",
|
|
877
|
+
className,
|
|
878
|
+
{ "navds-action-menu__item--has-icon": icon },
|
|
879
|
+
)}
|
|
880
|
+
>
|
|
881
|
+
{children}
|
|
882
|
+
{icon && (
|
|
883
|
+
<Marker placement="left" className="navds-action-menu__marker-icon">
|
|
884
|
+
{icon}
|
|
885
|
+
</Marker>
|
|
886
|
+
)}
|
|
887
|
+
<Marker placement="right" className="navds-action-menu__marker-icon">
|
|
888
|
+
<ChevronRightIcon aria-hidden />
|
|
889
|
+
</Marker>
|
|
890
|
+
</Menu.SubTrigger>
|
|
891
|
+
);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
/* -------------------------------------------------------------------------- */
|
|
895
|
+
/* ActionMenuSubContent */
|
|
896
|
+
/* -------------------------------------------------------------------------- */
|
|
897
|
+
type ActionMenuSubContentElement = React.ElementRef<typeof Menu.Content>;
|
|
898
|
+
|
|
899
|
+
interface ActionMenuSubContentProps
|
|
900
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
901
|
+
children: React.ReactNode;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
export const ActionMenuSubContent = forwardRef<
|
|
905
|
+
ActionMenuSubContentElement,
|
|
906
|
+
ActionMenuSubContentProps
|
|
907
|
+
>(({ children, className, style, ...rest }: ActionMenuSubContentProps, ref) => {
|
|
908
|
+
const context = useActionMenuContext();
|
|
909
|
+
|
|
910
|
+
return (
|
|
911
|
+
<Menu.Portal rootElement={context.rootElement}>
|
|
912
|
+
<Menu.SubContent
|
|
913
|
+
ref={ref}
|
|
914
|
+
alignOffset={-4}
|
|
915
|
+
sideOffset={1}
|
|
916
|
+
collisionPadding={10}
|
|
917
|
+
{...rest}
|
|
918
|
+
className={cl(
|
|
919
|
+
"navds-action-menu__content navds-action-menu__sub-content",
|
|
920
|
+
className,
|
|
921
|
+
)}
|
|
922
|
+
style={{
|
|
923
|
+
...style,
|
|
924
|
+
...{
|
|
925
|
+
"--ac-action-menu-content-transform-origin":
|
|
926
|
+
"var(--ac-floating-transform-origin)",
|
|
927
|
+
"--ac-action-menu-content-available-width":
|
|
928
|
+
"var(--ac-floating-available-width)",
|
|
929
|
+
"--ac-action-menu-content-available-height":
|
|
930
|
+
"var(--ac-floating-available-height)",
|
|
931
|
+
"--ac-action-menu-trigger-width": "var(--ac-floating-anchor-width)",
|
|
932
|
+
"--ac-action-menu-trigger-height":
|
|
933
|
+
"var(--ac-floating-anchor-height)",
|
|
934
|
+
},
|
|
935
|
+
}}
|
|
936
|
+
>
|
|
937
|
+
<div className="navds-action-menu__content-inner">{children}</div>
|
|
938
|
+
</Menu.SubContent>
|
|
939
|
+
</Menu.Portal>
|
|
940
|
+
);
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
/* -------------------------------------------------------------------------- */
|
|
944
|
+
ActionMenu.Trigger = ActionMenuTrigger;
|
|
945
|
+
ActionMenu.Content = ActionMenuContent;
|
|
946
|
+
ActionMenu.Group = ActionMenuGroup;
|
|
947
|
+
ActionMenu.Label = ActionMenuLabel;
|
|
948
|
+
ActionMenu.Item = ActionMenuItem;
|
|
949
|
+
ActionMenu.CheckboxItem = ActionMenuCheckboxItem;
|
|
950
|
+
ActionMenu.RadioGroup = ActionMenuRadioGroup;
|
|
951
|
+
ActionMenu.RadioItem = ActionMenuRadioItem;
|
|
952
|
+
ActionMenu.Divider = ActionMenuDivider;
|
|
953
|
+
ActionMenu.Sub = ActionMenuSub;
|
|
954
|
+
ActionMenu.SubTrigger = ActionMenuSubTrigger;
|
|
955
|
+
ActionMenu.SubContent = ActionMenuSubContent;
|
|
956
|
+
|
|
957
|
+
export type {
|
|
958
|
+
ActionMenuItemProps,
|
|
959
|
+
ActionMenuCheckboxItemProps,
|
|
960
|
+
ActionMenuContentProps,
|
|
961
|
+
ActionMenuDividerProps,
|
|
962
|
+
ActionMenuGroupProps,
|
|
963
|
+
ActionMenuLabelProps,
|
|
964
|
+
ActionMenuProps,
|
|
965
|
+
ActionMenuRadioGroupProps,
|
|
966
|
+
ActionMenuRadioItemProps,
|
|
967
|
+
ActionMenuSubContentProps,
|
|
968
|
+
ActionMenuSubProps,
|
|
969
|
+
ActionMenuSubTriggerProps,
|
|
970
|
+
ActionMenuTriggerProps,
|
|
971
|
+
};
|