@proyecto-viviana/solidaria-components 0.1.3 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Color.d.ts +6 -2
- package/dist/Color.d.ts.map +1 -1
- package/dist/ComboBox.d.ts +3 -3
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/GridList.d.ts +2 -2
- package/dist/GridList.d.ts.map +1 -1
- package/dist/ListBox.d.ts +5 -5
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/Menu.d.ts +3 -3
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Select.d.ts +3 -3
- package/dist/Select.d.ts.map +1 -1
- package/dist/Table.d.ts +2 -2
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/index.js +15 -15
- package/dist/index.js.map +2 -2
- package/dist/index.jsx +9056 -0
- package/dist/index.jsx.map +7 -0
- package/dist/index.ssr.js +15 -15
- package/dist/index.ssr.js.map +2 -2
- package/package.json +8 -10
- package/src/Autocomplete.tsx +0 -174
- package/src/Breadcrumbs.tsx +0 -264
- package/src/Button.tsx +0 -238
- package/src/Calendar.tsx +0 -471
- package/src/Checkbox.tsx +0 -387
- package/src/Color.tsx +0 -1370
- package/src/ComboBox.tsx +0 -824
- package/src/DateField.tsx +0 -337
- package/src/DatePicker.tsx +0 -367
- package/src/Dialog.tsx +0 -262
- package/src/Disclosure.tsx +0 -439
- package/src/GridList.tsx +0 -511
- package/src/Landmark.tsx +0 -203
- package/src/Link.tsx +0 -201
- package/src/ListBox.tsx +0 -346
- package/src/Menu.tsx +0 -544
- package/src/Meter.tsx +0 -157
- package/src/Modal.tsx +0 -433
- package/src/NumberField.tsx +0 -542
- package/src/Popover.tsx +0 -540
- package/src/ProgressBar.tsx +0 -162
- package/src/RadioGroup.tsx +0 -356
- package/src/RangeCalendar.tsx +0 -462
- package/src/SearchField.tsx +0 -479
- package/src/Select.tsx +0 -734
- package/src/Separator.tsx +0 -130
- package/src/Slider.tsx +0 -500
- package/src/Switch.tsx +0 -213
- package/src/Table.tsx +0 -857
- package/src/Tabs.tsx +0 -552
- package/src/TagGroup.tsx +0 -421
- package/src/TextField.tsx +0 -271
- package/src/TimeField.tsx +0 -455
- package/src/Toast.tsx +0 -503
- package/src/Toolbar.tsx +0 -160
- package/src/Tooltip.tsx +0 -423
- package/src/Tree.tsx +0 -551
- package/src/VisuallyHidden.tsx +0 -60
- package/src/contexts.ts +0 -74
- package/src/index.ts +0 -620
- package/src/utils.tsx +0 -329
package/src/Menu.tsx
DELETED
|
@@ -1,544 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Menu component for solidaria-components
|
|
3
|
-
*
|
|
4
|
-
* A pre-wired headless menu that combines state + aria hooks.
|
|
5
|
-
* Port of react-aria-components/src/Menu.tsx
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
type JSX,
|
|
10
|
-
createContext,
|
|
11
|
-
createMemo,
|
|
12
|
-
splitProps,
|
|
13
|
-
useContext,
|
|
14
|
-
For,
|
|
15
|
-
Show,
|
|
16
|
-
} from 'solid-js';
|
|
17
|
-
import {
|
|
18
|
-
createMenu,
|
|
19
|
-
createMenuItem,
|
|
20
|
-
createMenuTrigger,
|
|
21
|
-
createFocusRing,
|
|
22
|
-
createHover,
|
|
23
|
-
createButton,
|
|
24
|
-
createInteractOutside,
|
|
25
|
-
FocusScope,
|
|
26
|
-
type AriaMenuProps,
|
|
27
|
-
type AriaMenuItemProps,
|
|
28
|
-
type AriaMenuTriggerProps,
|
|
29
|
-
} from '@proyecto-viviana/solidaria';
|
|
30
|
-
import {
|
|
31
|
-
createMenuState,
|
|
32
|
-
createMenuTriggerState,
|
|
33
|
-
type MenuState,
|
|
34
|
-
type OverlayTriggerState,
|
|
35
|
-
type Key,
|
|
36
|
-
} from '@proyecto-viviana/solid-stately';
|
|
37
|
-
import {
|
|
38
|
-
type RenderChildren,
|
|
39
|
-
type ClassNameOrFunction,
|
|
40
|
-
type StyleOrFunction,
|
|
41
|
-
type SlotProps,
|
|
42
|
-
useRenderProps,
|
|
43
|
-
} from './utils';
|
|
44
|
-
|
|
45
|
-
// ============================================
|
|
46
|
-
// TYPES
|
|
47
|
-
// ============================================
|
|
48
|
-
|
|
49
|
-
export interface MenuRenderProps {
|
|
50
|
-
/** Whether the menu is focused. */
|
|
51
|
-
isFocused: boolean;
|
|
52
|
-
/** Whether the menu is open. */
|
|
53
|
-
isOpen: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface MenuProps<T>
|
|
57
|
-
extends Omit<AriaMenuProps, 'children'>,
|
|
58
|
-
SlotProps {
|
|
59
|
-
/** The items to render in the menu. */
|
|
60
|
-
items: T[];
|
|
61
|
-
/** Function to get the key from an item. */
|
|
62
|
-
getKey?: (item: T) => Key;
|
|
63
|
-
/** Function to get the text value from an item. */
|
|
64
|
-
getTextValue?: (item: T) => string;
|
|
65
|
-
/** Function to check if an item is disabled. */
|
|
66
|
-
getDisabled?: (item: T) => boolean;
|
|
67
|
-
/** Keys of disabled items. */
|
|
68
|
-
disabledKeys?: Iterable<Key>;
|
|
69
|
-
/** Handler called when an item is activated. */
|
|
70
|
-
onAction?: (key: Key) => void;
|
|
71
|
-
/** Handler called when the menu should close. */
|
|
72
|
-
onClose?: () => void;
|
|
73
|
-
/** The children of the component. A function may be provided to render each item. */
|
|
74
|
-
children: (item: T) => JSX.Element;
|
|
75
|
-
/** The CSS className for the element. */
|
|
76
|
-
class?: ClassNameOrFunction<MenuRenderProps>;
|
|
77
|
-
/** The inline style for the element. */
|
|
78
|
-
style?: StyleOrFunction<MenuRenderProps>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface MenuItemRenderProps {
|
|
82
|
-
/** Whether the item is selected. */
|
|
83
|
-
isSelected: boolean;
|
|
84
|
-
/** Whether the item is focused. */
|
|
85
|
-
isFocused: boolean;
|
|
86
|
-
/** Whether the item has keyboard focus. */
|
|
87
|
-
isFocusVisible: boolean;
|
|
88
|
-
/** Whether the item is pressed. */
|
|
89
|
-
isPressed: boolean;
|
|
90
|
-
/** Whether the item is hovered. */
|
|
91
|
-
isHovered: boolean;
|
|
92
|
-
/** Whether the item is disabled. */
|
|
93
|
-
isDisabled: boolean;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface MenuItemProps<T>
|
|
97
|
-
extends Omit<AriaMenuItemProps, 'children' | 'key'>,
|
|
98
|
-
SlotProps {
|
|
99
|
-
/** The unique key for the item. */
|
|
100
|
-
id: Key;
|
|
101
|
-
/** The item value. */
|
|
102
|
-
item?: T;
|
|
103
|
-
/** The children of the item. A function may be provided to receive render props. */
|
|
104
|
-
children?: RenderChildren<MenuItemRenderProps>;
|
|
105
|
-
/** The CSS className for the element. */
|
|
106
|
-
class?: ClassNameOrFunction<MenuItemRenderProps>;
|
|
107
|
-
/** The inline style for the element. */
|
|
108
|
-
style?: StyleOrFunction<MenuItemRenderProps>;
|
|
109
|
-
/** The text value of the item (for typeahead). */
|
|
110
|
-
textValue?: string;
|
|
111
|
-
/** Handler called when the item is activated. */
|
|
112
|
-
onAction?: () => void;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface MenuTriggerRenderProps {
|
|
116
|
-
/** Whether the menu is open. */
|
|
117
|
-
isOpen: boolean;
|
|
118
|
-
/** Whether the trigger is focused. */
|
|
119
|
-
isFocused: boolean;
|
|
120
|
-
/** Whether the trigger has keyboard focus. */
|
|
121
|
-
isFocusVisible: boolean;
|
|
122
|
-
/** Whether the trigger is pressed. */
|
|
123
|
-
isPressed: boolean;
|
|
124
|
-
/** Whether the trigger is hovered. */
|
|
125
|
-
isHovered: boolean;
|
|
126
|
-
/** Whether the trigger is disabled. */
|
|
127
|
-
isDisabled: boolean;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface MenuTriggerProps extends Omit<AriaMenuTriggerProps, 'children'>, SlotProps {
|
|
131
|
-
/** The children of the trigger (typically a Button and Menu). */
|
|
132
|
-
children: JSX.Element;
|
|
133
|
-
/** Whether the menu trigger is disabled. */
|
|
134
|
-
isDisabled?: boolean;
|
|
135
|
-
/** Whether the menu is open by default. */
|
|
136
|
-
defaultOpen?: boolean;
|
|
137
|
-
/** Whether the menu is open (controlled). */
|
|
138
|
-
isOpen?: boolean;
|
|
139
|
-
/** Handler called when the open state changes. */
|
|
140
|
-
onOpenChange?: (isOpen: boolean) => void;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ============================================
|
|
144
|
-
// CONTEXT
|
|
145
|
-
// ============================================
|
|
146
|
-
|
|
147
|
-
interface MenuContextValue<T> {
|
|
148
|
-
state: MenuState<T>;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
interface MenuTriggerContextValue {
|
|
152
|
-
state: OverlayTriggerState;
|
|
153
|
-
triggerProps: JSX.HTMLAttributes<HTMLElement>;
|
|
154
|
-
menuProps: JSX.HTMLAttributes<HTMLElement>;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export const MenuContext = createContext<MenuContextValue<unknown> | null>(null);
|
|
158
|
-
export const MenuStateContext = createContext<MenuState<unknown> | null>(null);
|
|
159
|
-
export const MenuTriggerContext = createContext<MenuTriggerContextValue | null>(null);
|
|
160
|
-
|
|
161
|
-
// ============================================
|
|
162
|
-
// COMPONENTS
|
|
163
|
-
// ============================================
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* A menu trigger wraps a button and menu, handling the open/close state.
|
|
167
|
-
*/
|
|
168
|
-
export function MenuTrigger(props: MenuTriggerProps): JSX.Element {
|
|
169
|
-
const [local, stateProps] = splitProps(props, ['slot']);
|
|
170
|
-
|
|
171
|
-
// Create trigger state
|
|
172
|
-
const state = createMenuTriggerState({
|
|
173
|
-
get isOpen() {
|
|
174
|
-
return stateProps.isOpen;
|
|
175
|
-
},
|
|
176
|
-
get defaultOpen() {
|
|
177
|
-
return stateProps.defaultOpen;
|
|
178
|
-
},
|
|
179
|
-
get onOpenChange() {
|
|
180
|
-
return stateProps.onOpenChange;
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Create trigger aria props
|
|
185
|
-
const { menuTriggerProps, menuProps } = createMenuTrigger(
|
|
186
|
-
{
|
|
187
|
-
get isDisabled() {
|
|
188
|
-
return stateProps.isDisabled;
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
state
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
<MenuTriggerContext.Provider
|
|
196
|
-
value={{
|
|
197
|
-
state,
|
|
198
|
-
triggerProps: menuTriggerProps,
|
|
199
|
-
menuProps,
|
|
200
|
-
}}
|
|
201
|
-
>
|
|
202
|
-
{props.children}
|
|
203
|
-
</MenuTriggerContext.Provider>
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* A button that opens a menu.
|
|
209
|
-
*/
|
|
210
|
-
export interface MenuButtonProps extends SlotProps {
|
|
211
|
-
/** The children of the button. A function may be provided to receive render props. */
|
|
212
|
-
children?: RenderChildren<MenuTriggerRenderProps>;
|
|
213
|
-
/** The CSS className for the element. */
|
|
214
|
-
class?: ClassNameOrFunction<MenuTriggerRenderProps>;
|
|
215
|
-
/** The inline style for the element. */
|
|
216
|
-
style?: StyleOrFunction<MenuTriggerRenderProps>;
|
|
217
|
-
/** Whether the button is disabled. */
|
|
218
|
-
isDisabled?: boolean;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
222
|
-
const [local] = splitProps(props, ['class', 'style', 'slot', 'isDisabled']);
|
|
223
|
-
|
|
224
|
-
// Get trigger context
|
|
225
|
-
const context = useContext(MenuTriggerContext);
|
|
226
|
-
if (!context) {
|
|
227
|
-
throw new Error('MenuButton must be used within a MenuTrigger');
|
|
228
|
-
}
|
|
229
|
-
const { state, triggerProps } = context;
|
|
230
|
-
|
|
231
|
-
// Create button aria props for proper press handling
|
|
232
|
-
const buttonAria = createButton({
|
|
233
|
-
get isDisabled() {
|
|
234
|
-
return local.isDisabled;
|
|
235
|
-
},
|
|
236
|
-
onPress() {
|
|
237
|
-
state.toggle();
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Create focus ring
|
|
242
|
-
const { isFocused, isFocusVisible, focusProps } = createFocusRing();
|
|
243
|
-
|
|
244
|
-
// Create hover
|
|
245
|
-
const { isHovered, hoverProps } = createHover({
|
|
246
|
-
get isDisabled() {
|
|
247
|
-
return local.isDisabled;
|
|
248
|
-
},
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// Render props values
|
|
252
|
-
const renderValues = createMemo<MenuTriggerRenderProps>(() => ({
|
|
253
|
-
isOpen: state.isOpen(),
|
|
254
|
-
isFocused: isFocused(),
|
|
255
|
-
isFocusVisible: isFocusVisible(),
|
|
256
|
-
isPressed: buttonAria.isPressed(),
|
|
257
|
-
isHovered: isHovered(),
|
|
258
|
-
isDisabled: !!local.isDisabled,
|
|
259
|
-
}));
|
|
260
|
-
|
|
261
|
-
// Resolve render props
|
|
262
|
-
const renderProps = useRenderProps(
|
|
263
|
-
{
|
|
264
|
-
children: props.children,
|
|
265
|
-
class: local.class,
|
|
266
|
-
style: local.style,
|
|
267
|
-
defaultClassName: 'solidaria-MenuButton',
|
|
268
|
-
},
|
|
269
|
-
renderValues
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
// Remove ref from spread props
|
|
273
|
-
const cleanTriggerProps = () => {
|
|
274
|
-
const { ref: _ref1, ...rest } = triggerProps as Record<string, unknown>;
|
|
275
|
-
return rest;
|
|
276
|
-
};
|
|
277
|
-
const cleanButtonProps = () => {
|
|
278
|
-
const { ref: _ref2, ...rest } = buttonAria.buttonProps as Record<string, unknown>;
|
|
279
|
-
return rest;
|
|
280
|
-
};
|
|
281
|
-
const cleanFocusProps = () => {
|
|
282
|
-
const { ref: _ref3, ...rest } = focusProps as Record<string, unknown>;
|
|
283
|
-
return rest;
|
|
284
|
-
};
|
|
285
|
-
const cleanHoverProps = () => {
|
|
286
|
-
const { ref: _ref4, ...rest } = hoverProps as Record<string, unknown>;
|
|
287
|
-
return rest;
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
return (
|
|
291
|
-
<button
|
|
292
|
-
{...cleanTriggerProps()}
|
|
293
|
-
{...cleanButtonProps()}
|
|
294
|
-
{...cleanFocusProps()}
|
|
295
|
-
{...cleanHoverProps()}
|
|
296
|
-
type="button"
|
|
297
|
-
class={renderProps.class()}
|
|
298
|
-
style={renderProps.style()}
|
|
299
|
-
data-open={state.isOpen() || undefined}
|
|
300
|
-
data-focused={isFocused() || undefined}
|
|
301
|
-
data-focus-visible={isFocusVisible() || undefined}
|
|
302
|
-
data-pressed={buttonAria.isPressed() || undefined}
|
|
303
|
-
data-hovered={isHovered() || undefined}
|
|
304
|
-
data-disabled={local.isDisabled || undefined}
|
|
305
|
-
>
|
|
306
|
-
{renderProps.renderChildren()}
|
|
307
|
-
</button>
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* A menu displays a list of actions or options for the user to choose from.
|
|
313
|
-
*/
|
|
314
|
-
export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
315
|
-
const [local, stateProps, ariaProps] = splitProps(
|
|
316
|
-
props,
|
|
317
|
-
['children', 'class', 'style', 'slot'],
|
|
318
|
-
['items', 'getKey', 'getTextValue', 'getDisabled', 'disabledKeys', 'onAction', 'onClose']
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
// Get trigger context if available
|
|
322
|
-
const triggerContext = useContext(MenuTriggerContext);
|
|
323
|
-
|
|
324
|
-
// Ref for the menu element (for click outside detection)
|
|
325
|
-
let menuRef: HTMLUListElement | undefined;
|
|
326
|
-
|
|
327
|
-
// Create menu state
|
|
328
|
-
const state = createMenuState<T>({
|
|
329
|
-
get items() {
|
|
330
|
-
return stateProps.items;
|
|
331
|
-
},
|
|
332
|
-
get getKey() {
|
|
333
|
-
return stateProps.getKey;
|
|
334
|
-
},
|
|
335
|
-
get getTextValue() {
|
|
336
|
-
return stateProps.getTextValue;
|
|
337
|
-
},
|
|
338
|
-
get getDisabled() {
|
|
339
|
-
return stateProps.getDisabled;
|
|
340
|
-
},
|
|
341
|
-
get disabledKeys() {
|
|
342
|
-
return stateProps.disabledKeys;
|
|
343
|
-
},
|
|
344
|
-
get onAction() {
|
|
345
|
-
return stateProps.onAction;
|
|
346
|
-
},
|
|
347
|
-
get onClose() {
|
|
348
|
-
return stateProps.onClose ?? (() => triggerContext?.state.close());
|
|
349
|
-
},
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// Create menu aria props
|
|
353
|
-
const { menuProps } = createMenu(
|
|
354
|
-
{
|
|
355
|
-
get onClose() {
|
|
356
|
-
return stateProps.onClose ?? (() => triggerContext?.state.close());
|
|
357
|
-
},
|
|
358
|
-
get 'aria-label'() {
|
|
359
|
-
return ariaProps['aria-label'];
|
|
360
|
-
},
|
|
361
|
-
get 'aria-labelledby'() {
|
|
362
|
-
return ariaProps['aria-labelledby'];
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
state
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
// Create focus ring
|
|
369
|
-
const { isFocused, focusProps } = createFocusRing();
|
|
370
|
-
|
|
371
|
-
// Handle click outside to close menu
|
|
372
|
-
createInteractOutside({
|
|
373
|
-
ref: () => menuRef ?? null,
|
|
374
|
-
onInteractOutside: () => {
|
|
375
|
-
if (triggerContext?.state.isOpen()) {
|
|
376
|
-
triggerContext.state.close();
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
get isDisabled() {
|
|
380
|
-
return !triggerContext?.state.isOpen();
|
|
381
|
-
},
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Render props values
|
|
385
|
-
const renderValues = createMemo<MenuRenderProps>(() => ({
|
|
386
|
-
isFocused: state.isFocused() || isFocused(),
|
|
387
|
-
isOpen: triggerContext?.state.isOpen() ?? true,
|
|
388
|
-
}));
|
|
389
|
-
|
|
390
|
-
// Resolve render props
|
|
391
|
-
const renderProps = useRenderProps(
|
|
392
|
-
{
|
|
393
|
-
class: local.class,
|
|
394
|
-
style: local.style,
|
|
395
|
-
defaultClassName: 'solidaria-Menu',
|
|
396
|
-
},
|
|
397
|
-
renderValues
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
// Remove ref from spread props
|
|
401
|
-
const cleanMenuProps = () => {
|
|
402
|
-
const { ref: _ref1, ...rest } = menuProps as Record<string, unknown>;
|
|
403
|
-
return rest;
|
|
404
|
-
};
|
|
405
|
-
const cleanTriggerMenuProps = () => {
|
|
406
|
-
if (!triggerContext) return {};
|
|
407
|
-
const { ref: _ref2, ...rest } = triggerContext.menuProps as Record<string, unknown>;
|
|
408
|
-
return rest;
|
|
409
|
-
};
|
|
410
|
-
const cleanFocusProps = () => {
|
|
411
|
-
const { ref: _ref3, ...rest } = focusProps as Record<string, unknown>;
|
|
412
|
-
return rest;
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
// If inside a MenuTrigger, only render when open
|
|
416
|
-
// If standalone (no trigger context), always render
|
|
417
|
-
const shouldRender = () => triggerContext ? triggerContext.state.isOpen() : true;
|
|
418
|
-
|
|
419
|
-
// Only use FocusScope when inside a MenuTrigger (for popover behavior)
|
|
420
|
-
// Standalone menus don't need focus restoration
|
|
421
|
-
const menuContent = () => (
|
|
422
|
-
<MenuContext.Provider value={{ state }}>
|
|
423
|
-
<MenuStateContext.Provider value={state}>
|
|
424
|
-
<ul
|
|
425
|
-
ref={(el) => (menuRef = el)}
|
|
426
|
-
{...cleanMenuProps()}
|
|
427
|
-
{...cleanTriggerMenuProps()}
|
|
428
|
-
{...cleanFocusProps()}
|
|
429
|
-
class={renderProps.class()}
|
|
430
|
-
style={renderProps.style()}
|
|
431
|
-
data-focused={state.isFocused() || undefined}
|
|
432
|
-
>
|
|
433
|
-
<For each={stateProps.items}>{(item) => props.children?.(item)}</For>
|
|
434
|
-
</ul>
|
|
435
|
-
</MenuStateContext.Provider>
|
|
436
|
-
</MenuContext.Provider>
|
|
437
|
-
);
|
|
438
|
-
|
|
439
|
-
return (
|
|
440
|
-
<Show when={shouldRender()}>
|
|
441
|
-
<Show when={triggerContext} fallback={menuContent()}>
|
|
442
|
-
<FocusScope restoreFocus autoFocus>
|
|
443
|
-
{menuContent()}
|
|
444
|
-
</FocusScope>
|
|
445
|
-
</Show>
|
|
446
|
-
</Show>
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* An item in a menu.
|
|
452
|
-
*/
|
|
453
|
-
export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
|
|
454
|
-
const [local, ariaProps] = splitProps(props, [
|
|
455
|
-
'class',
|
|
456
|
-
'style',
|
|
457
|
-
'slot',
|
|
458
|
-
'id',
|
|
459
|
-
'item',
|
|
460
|
-
'textValue',
|
|
461
|
-
'onAction',
|
|
462
|
-
]);
|
|
463
|
-
|
|
464
|
-
// Get state from context
|
|
465
|
-
const context = useContext(MenuStateContext);
|
|
466
|
-
if (!context) {
|
|
467
|
-
throw new Error('MenuItem must be used within a Menu');
|
|
468
|
-
}
|
|
469
|
-
const state = context as MenuState<T>;
|
|
470
|
-
|
|
471
|
-
// Create menu item aria props
|
|
472
|
-
const itemAria = createMenuItem<T>(
|
|
473
|
-
{
|
|
474
|
-
key: local.id,
|
|
475
|
-
get isDisabled() {
|
|
476
|
-
return ariaProps.isDisabled;
|
|
477
|
-
},
|
|
478
|
-
get 'aria-label'() {
|
|
479
|
-
return ariaProps['aria-label'];
|
|
480
|
-
},
|
|
481
|
-
get onAction() {
|
|
482
|
-
return local.onAction;
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
state
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
// Create hover
|
|
489
|
-
const { isHovered, hoverProps } = createHover({
|
|
490
|
-
get isDisabled() {
|
|
491
|
-
return itemAria.isDisabled();
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// Render props values
|
|
496
|
-
const renderValues = createMemo<MenuItemRenderProps>(() => ({
|
|
497
|
-
isSelected: false, // Menu items don't have selection state
|
|
498
|
-
isFocused: itemAria.isFocused(),
|
|
499
|
-
isFocusVisible: itemAria.isFocusVisible(),
|
|
500
|
-
isPressed: itemAria.isPressed(),
|
|
501
|
-
isHovered: isHovered(),
|
|
502
|
-
isDisabled: itemAria.isDisabled(),
|
|
503
|
-
}));
|
|
504
|
-
|
|
505
|
-
// Resolve render props
|
|
506
|
-
const renderProps = useRenderProps(
|
|
507
|
-
{
|
|
508
|
-
children: props.children,
|
|
509
|
-
class: local.class,
|
|
510
|
-
style: local.style,
|
|
511
|
-
defaultClassName: 'solidaria-Menu-item',
|
|
512
|
-
},
|
|
513
|
-
renderValues
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
// Remove ref from spread props
|
|
517
|
-
const cleanItemProps = () => {
|
|
518
|
-
const { ref: _ref1, ...rest } = itemAria.menuItemProps as Record<string, unknown>;
|
|
519
|
-
return rest;
|
|
520
|
-
};
|
|
521
|
-
const cleanHoverProps = () => {
|
|
522
|
-
const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
|
|
523
|
-
return rest;
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
return (
|
|
527
|
-
<li
|
|
528
|
-
{...cleanItemProps()}
|
|
529
|
-
{...cleanHoverProps()}
|
|
530
|
-
class={renderProps.class()}
|
|
531
|
-
style={renderProps.style()}
|
|
532
|
-
data-focused={itemAria.isFocused() || undefined}
|
|
533
|
-
data-focus-visible={itemAria.isFocusVisible() || undefined}
|
|
534
|
-
data-pressed={itemAria.isPressed() || undefined}
|
|
535
|
-
data-hovered={isHovered() || undefined}
|
|
536
|
-
data-disabled={itemAria.isDisabled() || undefined}
|
|
537
|
-
>
|
|
538
|
-
{renderProps.renderChildren()}
|
|
539
|
-
</li>
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Attach Item as a static property
|
|
544
|
-
Menu.Item = MenuItem;
|
package/src/Meter.tsx
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Meter component for solidaria-components
|
|
3
|
-
*
|
|
4
|
-
* Pre-wired headless meter component that combines aria hooks.
|
|
5
|
-
* Port of react-aria-components/src/Meter.tsx
|
|
6
|
-
*
|
|
7
|
-
* Meters represent a quantity within a known range, or a fractional value.
|
|
8
|
-
* Unlike progress bars, meters represent a current value rather than progress toward a goal.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
type JSX,
|
|
13
|
-
type ParentProps,
|
|
14
|
-
createContext,
|
|
15
|
-
createMemo,
|
|
16
|
-
splitProps,
|
|
17
|
-
} from 'solid-js';
|
|
18
|
-
import {
|
|
19
|
-
createMeter,
|
|
20
|
-
type AriaMeterProps,
|
|
21
|
-
} from '@proyecto-viviana/solidaria';
|
|
22
|
-
import {
|
|
23
|
-
type RenderChildren,
|
|
24
|
-
type ClassNameOrFunction,
|
|
25
|
-
type StyleOrFunction,
|
|
26
|
-
type SlotProps,
|
|
27
|
-
useRenderProps,
|
|
28
|
-
filterDOMProps,
|
|
29
|
-
} from './utils';
|
|
30
|
-
|
|
31
|
-
// ============================================
|
|
32
|
-
// TYPES
|
|
33
|
-
// ============================================
|
|
34
|
-
|
|
35
|
-
export interface MeterRenderProps {
|
|
36
|
-
/** The value as a percentage between the minimum and maximum (0-100). */
|
|
37
|
-
percentage: number;
|
|
38
|
-
/** A formatted version of the value. */
|
|
39
|
-
valueText: string | undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface MeterProps
|
|
43
|
-
extends AriaMeterProps,
|
|
44
|
-
SlotProps {
|
|
45
|
-
/** The children of the component. A function may be provided to receive render props. */
|
|
46
|
-
children?: RenderChildren<MeterRenderProps>;
|
|
47
|
-
/** The CSS className for the element. */
|
|
48
|
-
class?: ClassNameOrFunction<MeterRenderProps>;
|
|
49
|
-
/** The inline style for the element. */
|
|
50
|
-
style?: StyleOrFunction<MeterRenderProps>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ============================================
|
|
54
|
-
// CONTEXT
|
|
55
|
-
// ============================================
|
|
56
|
-
|
|
57
|
-
export const MeterContext = createContext<MeterProps | null>(null);
|
|
58
|
-
|
|
59
|
-
// ============================================
|
|
60
|
-
// UTILITIES
|
|
61
|
-
// ============================================
|
|
62
|
-
|
|
63
|
-
function clamp(value: number, min: number, max: number): number {
|
|
64
|
-
return Math.min(Math.max(value, min), max);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ============================================
|
|
68
|
-
// METER COMPONENT
|
|
69
|
-
// ============================================
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* A meter represents a quantity within a known range, or a fractional value.
|
|
73
|
-
* Unlike progress bars, meters represent a current value rather than progress toward a goal.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```tsx
|
|
77
|
-
* <Meter value={75}>
|
|
78
|
-
* {({ percentage, valueText }) => (
|
|
79
|
-
* <>
|
|
80
|
-
* <Label>Storage space</Label>
|
|
81
|
-
* <span>{valueText}</span>
|
|
82
|
-
* <div class="bar" style={{ width: `${percentage}%` }} />
|
|
83
|
-
* </>
|
|
84
|
-
* )}
|
|
85
|
-
* </Meter>
|
|
86
|
-
* ```
|
|
87
|
-
*/
|
|
88
|
-
export function Meter(props: ParentProps<MeterProps>): JSX.Element {
|
|
89
|
-
const [local, ariaProps] = splitProps(props, [
|
|
90
|
-
'children',
|
|
91
|
-
'class',
|
|
92
|
-
'style',
|
|
93
|
-
'slot',
|
|
94
|
-
]);
|
|
95
|
-
|
|
96
|
-
// Get values for calculations
|
|
97
|
-
const value = () => ariaProps.value ?? 0;
|
|
98
|
-
const minValue = () => ariaProps.minValue ?? 0;
|
|
99
|
-
const maxValue = () => ariaProps.maxValue ?? 100;
|
|
100
|
-
|
|
101
|
-
// Create meter aria props
|
|
102
|
-
const meterAria = createMeter({
|
|
103
|
-
get value() { return ariaProps.value; },
|
|
104
|
-
get minValue() { return ariaProps.minValue; },
|
|
105
|
-
get maxValue() { return ariaProps.maxValue; },
|
|
106
|
-
get valueLabel() { return ariaProps.valueLabel; },
|
|
107
|
-
get formatOptions() { return ariaProps.formatOptions; },
|
|
108
|
-
get label() { return ariaProps.label; },
|
|
109
|
-
get 'aria-label'() { return ariaProps['aria-label']; },
|
|
110
|
-
get 'aria-labelledby'() { return ariaProps['aria-labelledby']; },
|
|
111
|
-
get 'aria-describedby'() { return ariaProps['aria-describedby']; },
|
|
112
|
-
get 'aria-details'() { return ariaProps['aria-details']; },
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Calculate percentage
|
|
116
|
-
const percentage = createMemo(() => {
|
|
117
|
-
const clampedValue = clamp(value(), minValue(), maxValue());
|
|
118
|
-
return ((clampedValue - minValue()) / (maxValue() - minValue())) * 100;
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// Get value text from aria props
|
|
122
|
-
const valueText = createMemo(() => {
|
|
123
|
-
return meterAria.meterProps['aria-valuetext'] as string | undefined;
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Render props values
|
|
127
|
-
const renderValues = createMemo<MeterRenderProps>(() => ({
|
|
128
|
-
percentage: percentage(),
|
|
129
|
-
valueText: valueText(),
|
|
130
|
-
}));
|
|
131
|
-
|
|
132
|
-
// Resolve render props
|
|
133
|
-
const renderProps = useRenderProps(
|
|
134
|
-
{
|
|
135
|
-
children: props.children,
|
|
136
|
-
class: local.class,
|
|
137
|
-
style: local.style,
|
|
138
|
-
defaultClassName: 'solidaria-Meter',
|
|
139
|
-
},
|
|
140
|
-
renderValues
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// Filter DOM props
|
|
144
|
-
const domProps = createMemo(() => filterDOMProps(ariaProps, { global: true }));
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<div
|
|
148
|
-
{...domProps()}
|
|
149
|
-
{...meterAria.meterProps}
|
|
150
|
-
class={renderProps.class()}
|
|
151
|
-
style={renderProps.style()}
|
|
152
|
-
slot={local.slot}
|
|
153
|
-
>
|
|
154
|
-
{renderProps.renderChildren()}
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|