@recursica/mantine-adapter 0.10.0 → 0.12.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/CHANGELOG.md +16 -0
- package/README.md +6 -28
- package/dist/mantine-adapter.cjs +1 -1
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +1650 -1344
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Checkbox/Checkbox.d.ts +3 -1
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +21 -0
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +2 -0
- package/dist/src/components/Label/Label.d.ts +1 -3
- package/dist/src/components/Menu/Menu.d.ts +85 -3
- package/dist/src/components/Radio/Radio.d.ts +3 -1
- package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +6 -6
- package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +7 -1
- package/dist/src/components/ReadOnlyField/index.d.ts +2 -0
- package/dist/src/components/Switch/Switch.d.ts +3 -1
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/utils/RequireAccessibleLabel.d.ts +18 -0
- package/package.json +1 -1
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +6 -4
- package/src/components/Avatar/Avatar.stories.tsx +10 -5
- package/src/components/Badge/Badge.stories.tsx +2 -1
- package/src/components/Button/Button.stories.tsx +2 -4
- package/src/components/Checkbox/Checkbox.module.css +1 -1
- package/src/components/Checkbox/Checkbox.stories.tsx +24 -3
- package/src/components/Checkbox/Checkbox.tsx +72 -15
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +26 -25
- package/src/components/Checkbox/CheckboxGroup.tsx +38 -58
- package/src/components/Container/Container.stories.tsx +6 -3
- package/src/components/DatePicker/DatePicker.stories.tsx +1 -5
- package/src/components/Dropdown/Dropdown.stories.tsx +0 -3
- package/src/components/Dropdown/Dropdown.tsx +6 -5
- package/src/components/FileInput/FileInput.stories.tsx +1 -5
- package/src/components/FileUpload/FileUpload.stories.tsx +1 -5
- package/src/components/Flex/Flex.stories.tsx +6 -3
- package/src/components/FormControlLayout/FormControlLayout.module.css +80 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +82 -0
- package/src/components/FormControlLayout/FormControlLayout.tsx +78 -0
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +3 -41
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +18 -5
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +28 -41
- package/src/components/Group/Group.stories.tsx +6 -3
- package/src/components/Label/Label.module.css +0 -37
- package/src/components/Label/Label.stories.tsx +8 -9
- package/src/components/Label/Label.tsx +2 -7
- package/src/components/Loader/Loader.stories.tsx +10 -5
- package/src/components/Menu/MENU_IMPLEMENTATION_NOTES.md +78 -0
- package/src/components/Menu/Menu.module.css +244 -0
- package/src/components/Menu/Menu.stories.tsx +309 -5
- package/src/components/Menu/Menu.tsx +317 -4
- package/src/components/NumberInput/NumberInput.stories.tsx +1 -5
- package/src/components/Radio/Radio.stories.tsx +22 -8
- package/src/components/Radio/Radio.tsx +134 -75
- package/src/components/Radio/RadioGroup.stories.tsx +26 -7
- package/src/components/Radio/RadioGroup.tsx +37 -57
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +43 -0
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +30 -3
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +3 -1
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +10 -2
- package/src/components/ReadOnlyField/index.ts +2 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +1 -5
- package/src/components/Slider/Slider.stories.tsx +1 -5
- package/src/components/Stack/Stack.stories.tsx +6 -3
- package/src/components/Switch/Switch.module.css +2 -10
- package/src/components/Switch/Switch.stories.tsx +42 -27
- package/src/components/Switch/Switch.tsx +138 -89
- package/src/components/Switch/SwitchGroup.stories.tsx +28 -8
- package/src/components/Switch/SwitchGroup.tsx +37 -57
- package/src/components/TextArea/TextArea.stories.tsx +0 -3
- package/src/components/TextArea/TextArea.tsx +6 -5
- package/src/components/TextField/TextField.tsx +6 -5
- package/src/components/TimePicker/TimePicker.stories.tsx +1 -5
- package/src/components/index.ts +1 -0
- package/src/utils/RequireAccessibleLabel.ts +12 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/* HARDCODED VALUES:
|
|
2
|
+
- border-style: solid. Structural rendering rule for the dropdown border (Mantine uses Paper which may not set border natively).
|
|
3
|
+
- opacity: 0.5 on disabled items. Recursica has no dedicated disabled-opacity token for menu items.
|
|
4
|
+
- NOTE: overflow is intentionally NOT set on .dropdown. Mantine renders sub-menu dropdowns
|
|
5
|
+
inside the parent dropdown DOM tree using Floating UI absolute positioning. Setting
|
|
6
|
+
overflow: hidden clips sub-menus; overflow-y: auto causes unwanted scrollbars.
|
|
7
|
+
- All structural layout (flex, cursor, pointer-events on disabled) is deferred to Mantine's
|
|
8
|
+
native behavior. We only override visual design tokens (colors, typography, spacing, borders).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* ======================================
|
|
12
|
+
DROPDOWN CONTAINER
|
|
13
|
+
====================================== */
|
|
14
|
+
|
|
15
|
+
.dropdown {
|
|
16
|
+
background-color: var(
|
|
17
|
+
--recursica_ui-kit_components_menu_properties_colors_background
|
|
18
|
+
);
|
|
19
|
+
border-style: solid; /* HARDCODE: Mantine Paper does not set border-style natively */
|
|
20
|
+
border-width: var(--recursica_ui-kit_components_menu_properties_border-size);
|
|
21
|
+
border-color: var(
|
|
22
|
+
--recursica_ui-kit_components_menu_properties_colors_border-color
|
|
23
|
+
);
|
|
24
|
+
border-radius: var(
|
|
25
|
+
--recursica_ui-kit_components_menu_properties_border-radius
|
|
26
|
+
);
|
|
27
|
+
box-shadow: var(--recursica_ui-kit_components_menu_properties_elevation);
|
|
28
|
+
|
|
29
|
+
min-width: var(--recursica_ui-kit_components_menu_properties_min-width);
|
|
30
|
+
max-width: var(--recursica_ui-kit_components_menu_properties_max-width);
|
|
31
|
+
|
|
32
|
+
padding: var(
|
|
33
|
+
--recursica_ui-kit_components_menu-item_properties_divider-item-gap
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ======================================
|
|
38
|
+
MENU ITEM
|
|
39
|
+
====================================== */
|
|
40
|
+
|
|
41
|
+
.item {
|
|
42
|
+
position: relative;
|
|
43
|
+
|
|
44
|
+
/* Geometry */
|
|
45
|
+
padding: var(
|
|
46
|
+
--recursica_ui-kit_components_menu-item_properties_vertical-padding
|
|
47
|
+
)
|
|
48
|
+
var(--recursica_ui-kit_components_menu-item_properties_horizontal-padding);
|
|
49
|
+
border-radius: var(
|
|
50
|
+
--recursica_ui-kit_components_menu-item_properties_border-radius
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/* Typography */
|
|
54
|
+
font-family: var(
|
|
55
|
+
--recursica_ui-kit_components_menu-item_properties_text_font-family
|
|
56
|
+
);
|
|
57
|
+
font-size: var(
|
|
58
|
+
--recursica_ui-kit_components_menu-item_properties_text_font-size
|
|
59
|
+
);
|
|
60
|
+
font-style: var(
|
|
61
|
+
--recursica_ui-kit_components_menu-item_properties_text_font-style
|
|
62
|
+
);
|
|
63
|
+
font-weight: var(
|
|
64
|
+
--recursica_ui-kit_components_menu-item_properties_text_font-weight
|
|
65
|
+
);
|
|
66
|
+
letter-spacing: var(
|
|
67
|
+
--recursica_ui-kit_components_menu-item_properties_text_letter-spacing
|
|
68
|
+
);
|
|
69
|
+
line-height: var(
|
|
70
|
+
--recursica_ui-kit_components_menu-item_properties_text_line-height
|
|
71
|
+
);
|
|
72
|
+
text-decoration: var(
|
|
73
|
+
--recursica_ui-kit_components_menu-item_properties_text_text-decoration
|
|
74
|
+
);
|
|
75
|
+
text-transform: var(
|
|
76
|
+
--recursica_ui-kit_components_menu-item_properties_text_text-transform
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/* Default State: Unselected */
|
|
80
|
+
background-color: var(
|
|
81
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_background
|
|
82
|
+
);
|
|
83
|
+
color: var(
|
|
84
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_text
|
|
85
|
+
);
|
|
86
|
+
opacity: var(
|
|
87
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_opacity
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Override Mantine's native hover background to use our overlay technique */
|
|
92
|
+
.item:hover {
|
|
93
|
+
background-color: var(
|
|
94
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_background
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Hover overlay using ::after pseudo-element (same technique as Accordion) */
|
|
99
|
+
.item::after {
|
|
100
|
+
content: "";
|
|
101
|
+
position: absolute;
|
|
102
|
+
inset: 0;
|
|
103
|
+
border-radius: inherit;
|
|
104
|
+
z-index: 0;
|
|
105
|
+
pointer-events: none;
|
|
106
|
+
background-color: var(
|
|
107
|
+
--recursica_ui-kit_components_menu-item_properties_hover-color
|
|
108
|
+
);
|
|
109
|
+
opacity: 0;
|
|
110
|
+
transition: opacity 150ms ease;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.item:hover:not([data-disabled])::after {
|
|
114
|
+
opacity: var(
|
|
115
|
+
--recursica_ui-kit_components_menu-item_properties_hover-opacity,
|
|
116
|
+
0.1
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Selected item state */
|
|
121
|
+
.item[data-selected] {
|
|
122
|
+
background-color: var(
|
|
123
|
+
--recursica_ui-kit_components_menu-item_properties_colors_selected-item_background
|
|
124
|
+
);
|
|
125
|
+
color: var(
|
|
126
|
+
--recursica_ui-kit_components_menu-item_properties_colors_selected-item_text
|
|
127
|
+
);
|
|
128
|
+
opacity: var(
|
|
129
|
+
--recursica_ui-kit_components_menu-item_properties_colors_selected-item_opacity
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Hovered item state (keyboard navigation) */
|
|
134
|
+
.item[data-hovered] {
|
|
135
|
+
background-color: var(
|
|
136
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_background
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
.item[data-hovered]::after {
|
|
140
|
+
opacity: var(
|
|
141
|
+
--recursica_ui-kit_components_menu-item_properties_hover-opacity,
|
|
142
|
+
0.1
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Disabled item */
|
|
147
|
+
.item[data-disabled] {
|
|
148
|
+
/* HARDCODE: 0.5 opacity fallback; no dedicated disabled-opacity token for menu items */
|
|
149
|
+
opacity: 0.5;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ======================================
|
|
153
|
+
ITEM LABEL
|
|
154
|
+
====================================== */
|
|
155
|
+
|
|
156
|
+
.itemLabel {
|
|
157
|
+
position: relative;
|
|
158
|
+
z-index: 1;
|
|
159
|
+
color: inherit;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* ======================================
|
|
163
|
+
ITEM SECTION (LEFT / RIGHT ICONS)
|
|
164
|
+
====================================== */
|
|
165
|
+
|
|
166
|
+
.itemSection {
|
|
167
|
+
position: relative;
|
|
168
|
+
z-index: 1;
|
|
169
|
+
color: inherit;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* ======================================
|
|
173
|
+
DIVIDER
|
|
174
|
+
====================================== */
|
|
175
|
+
|
|
176
|
+
.divider {
|
|
177
|
+
height: var(
|
|
178
|
+
--recursica_ui-kit_components_menu-item_properties_divider-height
|
|
179
|
+
);
|
|
180
|
+
background-color: var(
|
|
181
|
+
--recursica_ui-kit_components_menu-item_properties_colors_divider-color
|
|
182
|
+
);
|
|
183
|
+
opacity: var(
|
|
184
|
+
--recursica_ui-kit_components_menu-item_properties_divider-opacity
|
|
185
|
+
);
|
|
186
|
+
margin: var(
|
|
187
|
+
--recursica_ui-kit_components_menu-item_properties_divider-item-gap
|
|
188
|
+
)
|
|
189
|
+
0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* ======================================
|
|
193
|
+
SECTION LABEL
|
|
194
|
+
====================================== */
|
|
195
|
+
|
|
196
|
+
.label {
|
|
197
|
+
padding: var(
|
|
198
|
+
--recursica_ui-kit_components_menu-item_properties_vertical-padding
|
|
199
|
+
)
|
|
200
|
+
var(--recursica_ui-kit_components_menu-item_properties_horizontal-padding);
|
|
201
|
+
|
|
202
|
+
/* Supporting Text Typography */
|
|
203
|
+
font-family: var(
|
|
204
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-family
|
|
205
|
+
);
|
|
206
|
+
font-size: var(
|
|
207
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-size
|
|
208
|
+
);
|
|
209
|
+
font-style: var(
|
|
210
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-style
|
|
211
|
+
);
|
|
212
|
+
font-weight: var(
|
|
213
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-weight
|
|
214
|
+
);
|
|
215
|
+
letter-spacing: var(
|
|
216
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_letter-spacing
|
|
217
|
+
);
|
|
218
|
+
line-height: var(
|
|
219
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_line-height
|
|
220
|
+
);
|
|
221
|
+
text-decoration: var(
|
|
222
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_text-decoration
|
|
223
|
+
);
|
|
224
|
+
text-transform: var(
|
|
225
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_text-transform
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
color: var(
|
|
229
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_supporting-text-color
|
|
230
|
+
);
|
|
231
|
+
opacity: var(
|
|
232
|
+
--recursica_ui-kit_components_menu-item_properties_colors_unselected-item_supporting-text-opacity
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* ======================================
|
|
237
|
+
SUB-MENU CHEVRON
|
|
238
|
+
====================================== */
|
|
239
|
+
|
|
240
|
+
.chevron {
|
|
241
|
+
position: relative;
|
|
242
|
+
z-index: 1;
|
|
243
|
+
color: inherit;
|
|
244
|
+
}
|
|
@@ -1,17 +1,321 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { Menu } from "./Menu";
|
|
3
|
-
import {
|
|
3
|
+
import { Button } from "../Button";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Menu story axes:
|
|
7
|
+
* - trigger: click (default) vs hover behavior
|
|
8
|
+
* - position: dropdown alignment relative to target
|
|
9
|
+
* - withArrow: arrow indicator on the dropdown
|
|
10
|
+
* - content variations: icons, dividers, labels, disabled items, submenus
|
|
11
|
+
* - layer: tested via the global Layer decorator (withLayer/layer args)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const SettingsIcon = () => (
|
|
15
|
+
<svg
|
|
16
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
17
|
+
width="14"
|
|
18
|
+
height="14"
|
|
19
|
+
viewBox="0 0 24 24"
|
|
20
|
+
fill="none"
|
|
21
|
+
stroke="currentColor"
|
|
22
|
+
strokeWidth="2"
|
|
23
|
+
strokeLinecap="round"
|
|
24
|
+
strokeLinejoin="round"
|
|
25
|
+
>
|
|
26
|
+
<circle cx="12" cy="12" r="3" />
|
|
27
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const SearchIcon = () => (
|
|
32
|
+
<svg
|
|
33
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
34
|
+
width="14"
|
|
35
|
+
height="14"
|
|
36
|
+
viewBox="0 0 24 24"
|
|
37
|
+
fill="none"
|
|
38
|
+
stroke="currentColor"
|
|
39
|
+
strokeWidth="2"
|
|
40
|
+
strokeLinecap="round"
|
|
41
|
+
strokeLinejoin="round"
|
|
42
|
+
>
|
|
43
|
+
<circle cx="11" cy="11" r="8" />
|
|
44
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const MessageIcon = () => (
|
|
49
|
+
<svg
|
|
50
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
51
|
+
width="14"
|
|
52
|
+
height="14"
|
|
53
|
+
viewBox="0 0 24 24"
|
|
54
|
+
fill="none"
|
|
55
|
+
stroke="currentColor"
|
|
56
|
+
strokeWidth="2"
|
|
57
|
+
strokeLinecap="round"
|
|
58
|
+
strokeLinejoin="round"
|
|
59
|
+
>
|
|
60
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
61
|
+
</svg>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const ImageIcon = () => (
|
|
65
|
+
<svg
|
|
66
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
67
|
+
width="14"
|
|
68
|
+
height="14"
|
|
69
|
+
viewBox="0 0 24 24"
|
|
70
|
+
fill="none"
|
|
71
|
+
stroke="currentColor"
|
|
72
|
+
strokeWidth="2"
|
|
73
|
+
strokeLinecap="round"
|
|
74
|
+
strokeLinejoin="round"
|
|
75
|
+
>
|
|
76
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
|
77
|
+
<circle cx="8.5" cy="8.5" r="1.5" />
|
|
78
|
+
<polyline points="21 15 16 10 5 21" />
|
|
79
|
+
</svg>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const TrashIcon = () => (
|
|
83
|
+
<svg
|
|
84
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
85
|
+
width="14"
|
|
86
|
+
height="14"
|
|
87
|
+
viewBox="0 0 24 24"
|
|
88
|
+
fill="none"
|
|
89
|
+
stroke="currentColor"
|
|
90
|
+
strokeWidth="2"
|
|
91
|
+
strokeLinecap="round"
|
|
92
|
+
strokeLinejoin="round"
|
|
93
|
+
>
|
|
94
|
+
<polyline points="3 6 5 6 21 6" />
|
|
95
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
96
|
+
</svg>
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const ArrowsIcon = () => (
|
|
100
|
+
<svg
|
|
101
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
102
|
+
width="14"
|
|
103
|
+
height="14"
|
|
104
|
+
viewBox="0 0 24 24"
|
|
105
|
+
fill="none"
|
|
106
|
+
stroke="currentColor"
|
|
107
|
+
strokeWidth="2"
|
|
108
|
+
strokeLinecap="round"
|
|
109
|
+
strokeLinejoin="round"
|
|
110
|
+
>
|
|
111
|
+
<polyline points="17 1 21 5 17 9" />
|
|
112
|
+
<path d="M3 11V9a4 4 0 0 1 4-4h14" />
|
|
113
|
+
<polyline points="7 23 3 19 7 15" />
|
|
114
|
+
<path d="M21 13v2a4 4 0 0 1-4 4H3" />
|
|
115
|
+
</svg>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
|
+
type MenuStoryArgs = Record<string, any>;
|
|
120
|
+
|
|
121
|
+
const meta: Meta = {
|
|
122
|
+
title: "UI-Kit/Menu",
|
|
7
123
|
component: Menu,
|
|
8
124
|
tags: ["autodocs"],
|
|
125
|
+
argTypes: {
|
|
126
|
+
trigger: {
|
|
127
|
+
control: "select",
|
|
128
|
+
options: ["click", "hover", "click-hover"],
|
|
129
|
+
description:
|
|
130
|
+
"Determines how the menu is triggered: click (default), hover, or both.",
|
|
131
|
+
},
|
|
132
|
+
position: {
|
|
133
|
+
control: "select",
|
|
134
|
+
options: [
|
|
135
|
+
"bottom",
|
|
136
|
+
"bottom-start",
|
|
137
|
+
"bottom-end",
|
|
138
|
+
"top",
|
|
139
|
+
"top-start",
|
|
140
|
+
"top-end",
|
|
141
|
+
"left",
|
|
142
|
+
"left-start",
|
|
143
|
+
"left-end",
|
|
144
|
+
"right",
|
|
145
|
+
"right-start",
|
|
146
|
+
"right-end",
|
|
147
|
+
],
|
|
148
|
+
description: "The position of the dropdown relative to the target.",
|
|
149
|
+
},
|
|
150
|
+
withArrow: {
|
|
151
|
+
control: "boolean",
|
|
152
|
+
description: "Whether to display an arrow pointing to the target.",
|
|
153
|
+
},
|
|
154
|
+
offset: {
|
|
155
|
+
control: "number",
|
|
156
|
+
description:
|
|
157
|
+
"Distance in px between the dropdown and the target element.",
|
|
158
|
+
},
|
|
159
|
+
opened: {
|
|
160
|
+
control: "boolean",
|
|
161
|
+
description:
|
|
162
|
+
"Controlled open state. Leave undefined for uncontrolled behavior.",
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
parameters: {
|
|
166
|
+
docs: {
|
|
167
|
+
description: {
|
|
168
|
+
component: `
|
|
169
|
+
The \`Menu\` component combines a list of secondary actions into a single interactive overlay area, wrapping Mantine's composable \`Menu\` while enforcing strict Recursica design token styling.
|
|
170
|
+
|
|
171
|
+
### Composable API
|
|
172
|
+
|
|
173
|
+
Menu uses a dot-notation composition pattern:
|
|
174
|
+
- \`<Menu>\` — Root container managing open/close state
|
|
175
|
+
- \`<Menu.Target>\` — Wrapper for the trigger element (must be a single element supporting ref)
|
|
176
|
+
- \`<Menu.Dropdown>\` — The popup panel
|
|
177
|
+
- \`<Menu.Item>\` — An actionable item with optional \`leftSection\` / \`rightSection\`
|
|
178
|
+
- \`<Menu.Divider>\` — A visual separator
|
|
179
|
+
- \`<Menu.Label>\` — A non-interactive section header
|
|
180
|
+
|
|
181
|
+
### Sub-menus
|
|
182
|
+
Nested menus are supported via \`<Menu.Sub>\`, \`<Menu.Sub.Target>\`, \`<Menu.Sub.Item>\`, and \`<Menu.Sub.Dropdown>\`.
|
|
183
|
+
|
|
184
|
+
### Design Token Limitations
|
|
185
|
+
Mantine's \`color\` prop on \`Menu.Item\` is stripped in strict mode to enforce design token adherence. Use \`overStyled={true}\` to bypass this restriction.
|
|
186
|
+
`,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
},
|
|
9
190
|
};
|
|
10
191
|
|
|
11
192
|
export default meta;
|
|
12
193
|
|
|
13
|
-
type Story = StoryObj<
|
|
194
|
+
type Story = StoryObj<MenuStoryArgs>;
|
|
14
195
|
|
|
15
196
|
export const Default: Story = {
|
|
16
|
-
|
|
197
|
+
args: {
|
|
198
|
+
trigger: "click",
|
|
199
|
+
position: "bottom-start",
|
|
200
|
+
withArrow: false,
|
|
201
|
+
offset: 5,
|
|
202
|
+
opened: undefined,
|
|
203
|
+
},
|
|
204
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
205
|
+
render: ({ withLayer, layer, ...args }: MenuStoryArgs) => {
|
|
206
|
+
return (
|
|
207
|
+
<Menu {...args}>
|
|
208
|
+
<Menu.Target>
|
|
209
|
+
<Button variant="solid">Toggle Menu</Button>
|
|
210
|
+
</Menu.Target>
|
|
211
|
+
|
|
212
|
+
<Menu.Dropdown>
|
|
213
|
+
<Menu.Label>Application</Menu.Label>
|
|
214
|
+
<Menu.Item leftSection={<SettingsIcon />}>Settings</Menu.Item>
|
|
215
|
+
<Menu.Item leftSection={<MessageIcon />}>Messages</Menu.Item>
|
|
216
|
+
<Menu.Item leftSection={<ImageIcon />}>Gallery</Menu.Item>
|
|
217
|
+
<Menu.Item leftSection={<SearchIcon />}>Search</Menu.Item>
|
|
218
|
+
|
|
219
|
+
<Menu.Divider />
|
|
220
|
+
|
|
221
|
+
<Menu.Label>Danger zone</Menu.Label>
|
|
222
|
+
<Menu.Item leftSection={<ArrowsIcon />}>Transfer my data</Menu.Item>
|
|
223
|
+
<Menu.Item leftSection={<TrashIcon />}>Delete my account</Menu.Item>
|
|
224
|
+
</Menu.Dropdown>
|
|
225
|
+
</Menu>
|
|
226
|
+
);
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const WithDisabledItems: Story = {
|
|
231
|
+
args: {
|
|
232
|
+
position: "bottom-start",
|
|
233
|
+
},
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
235
|
+
render: ({ withLayer, layer, ...args }: MenuStoryArgs) => {
|
|
236
|
+
return (
|
|
237
|
+
<Menu {...args}>
|
|
238
|
+
<Menu.Target>
|
|
239
|
+
<Button variant="solid">Menu with Disabled</Button>
|
|
240
|
+
</Menu.Target>
|
|
241
|
+
|
|
242
|
+
<Menu.Dropdown>
|
|
243
|
+
<Menu.Item leftSection={<SettingsIcon />}>Settings</Menu.Item>
|
|
244
|
+
<Menu.Item leftSection={<SearchIcon />} disabled>
|
|
245
|
+
Search (disabled)
|
|
246
|
+
</Menu.Item>
|
|
247
|
+
<Menu.Item leftSection={<MessageIcon />}>Messages</Menu.Item>
|
|
248
|
+
<Menu.Item leftSection={<TrashIcon />} disabled>
|
|
249
|
+
Delete (disabled)
|
|
250
|
+
</Menu.Item>
|
|
251
|
+
</Menu.Dropdown>
|
|
252
|
+
</Menu>
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const WithSubmenus: Story = {
|
|
258
|
+
args: {
|
|
259
|
+
position: "bottom-start",
|
|
260
|
+
width: 200,
|
|
261
|
+
},
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
263
|
+
render: ({ withLayer, layer, ...args }: MenuStoryArgs) => {
|
|
264
|
+
return (
|
|
265
|
+
<Menu {...args}>
|
|
266
|
+
<Menu.Target>
|
|
267
|
+
<Button variant="solid">Menu with Submenus</Button>
|
|
268
|
+
</Menu.Target>
|
|
269
|
+
|
|
270
|
+
<Menu.Dropdown>
|
|
271
|
+
<Menu.Item>Dashboard</Menu.Item>
|
|
272
|
+
<Menu.Sub>
|
|
273
|
+
<Menu.Sub.Target>
|
|
274
|
+
<Menu.Sub.Item>Products</Menu.Sub.Item>
|
|
275
|
+
</Menu.Sub.Target>
|
|
276
|
+
<Menu.Sub.Dropdown>
|
|
277
|
+
<Menu.Item>All products</Menu.Item>
|
|
278
|
+
<Menu.Item>Categories</Menu.Item>
|
|
279
|
+
<Menu.Item>Tags</Menu.Item>
|
|
280
|
+
</Menu.Sub.Dropdown>
|
|
281
|
+
</Menu.Sub>
|
|
282
|
+
<Menu.Sub>
|
|
283
|
+
<Menu.Sub.Target>
|
|
284
|
+
<Menu.Sub.Item>Orders</Menu.Sub.Item>
|
|
285
|
+
</Menu.Sub.Target>
|
|
286
|
+
<Menu.Sub.Dropdown>
|
|
287
|
+
<Menu.Item>Open</Menu.Item>
|
|
288
|
+
<Menu.Item>Completed</Menu.Item>
|
|
289
|
+
<Menu.Item>Cancelled</Menu.Item>
|
|
290
|
+
</Menu.Sub.Dropdown>
|
|
291
|
+
</Menu.Sub>
|
|
292
|
+
</Menu.Dropdown>
|
|
293
|
+
</Menu>
|
|
294
|
+
);
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export const HoverTrigger: Story = {
|
|
299
|
+
args: {
|
|
300
|
+
trigger: "click-hover",
|
|
301
|
+
position: "bottom-start",
|
|
302
|
+
openDelay: 100,
|
|
303
|
+
closeDelay: 400,
|
|
304
|
+
},
|
|
305
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
306
|
+
render: ({ withLayer, layer, ...args }: MenuStoryArgs) => {
|
|
307
|
+
return (
|
|
308
|
+
<Menu {...args}>
|
|
309
|
+
<Menu.Target>
|
|
310
|
+
<Button variant="solid">Hover or Click</Button>
|
|
311
|
+
</Menu.Target>
|
|
312
|
+
|
|
313
|
+
<Menu.Dropdown>
|
|
314
|
+
<Menu.Item leftSection={<SettingsIcon />}>Settings</Menu.Item>
|
|
315
|
+
<Menu.Item leftSection={<MessageIcon />}>Messages</Menu.Item>
|
|
316
|
+
<Menu.Item leftSection={<ImageIcon />}>Gallery</Menu.Item>
|
|
317
|
+
</Menu.Dropdown>
|
|
318
|
+
</Menu>
|
|
319
|
+
);
|
|
320
|
+
},
|
|
17
321
|
};
|