@proyecto-viviana/solidaria-components 0.2.5 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/ActionBar.d.ts +71 -0
- package/dist/ActionBar.d.ts.map +1 -0
- package/dist/ActionGroup.d.ts +74 -0
- package/dist/ActionGroup.d.ts.map +1 -0
- package/dist/Alert.d.ts +70 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Breadcrumbs.d.ts +10 -2
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +4 -0
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +13 -0
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +2 -2
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +125 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +114 -2
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +42 -0
- package/dist/ColorEditor.d.ts.map +1 -0
- package/dist/ComboBox.d.ts +64 -0
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +40 -0
- package/dist/ContextualHelpTrigger.d.ts.map +1 -0
- package/dist/DateField.d.ts +27 -2
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +67 -2
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +2 -0
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +80 -0
- package/dist/DragAndDrop.d.ts.map +1 -0
- package/dist/DragPreview.d.ts +14 -0
- package/dist/DragPreview.d.ts.map +1 -0
- package/dist/DropZone.d.ts +27 -0
- package/dist/DropZone.d.ts.map +1 -0
- package/dist/FieldError.d.ts +23 -0
- package/dist/FieldError.d.ts.map +1 -0
- package/dist/FileTrigger.d.ts +26 -0
- package/dist/FileTrigger.d.ts.map +1 -0
- package/dist/Focusable.d.ts +27 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Form.d.ts +27 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +40 -1
- package/dist/GridList.d.ts.map +1 -1
- package/dist/Icon.d.ts +57 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Keyboard.d.ts +13 -0
- package/dist/Keyboard.d.ts.map +1 -0
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +43 -1
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +38 -0
- package/dist/ListDropTargetDelegate.d.ts.map +1 -0
- package/dist/Menu.d.ts +20 -2
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +2 -2
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +2 -0
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +2 -0
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +4 -2
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +27 -0
- package/dist/Pressable.d.ts.map +1 -0
- package/dist/ProgressBar.d.ts +2 -2
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +5 -0
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +75 -0
- package/dist/RouterProvider.d.ts.map +1 -0
- package/dist/SearchField.d.ts +2 -3
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +11 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +30 -0
- package/dist/SelectionIndicator.d.ts.map +1 -0
- package/dist/SharedElementTransition.d.ts +39 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +6 -3
- package/dist/Slider.d.ts.map +1 -1
- package/dist/Table.d.ts +39 -0
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +4 -3
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +12 -2
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +10 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/TextField.d.ts +4 -0
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +26 -1
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +30 -0
- package/dist/ToggleButton.d.ts.map +1 -0
- package/dist/ToggleButtonGroup.d.ts +33 -0
- package/dist/ToggleButtonGroup.d.ts.map +1 -0
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +9 -0
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +44 -2
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +61 -0
- package/dist/Virtualizer.d.ts.map +1 -0
- package/dist/VirtualizerLayouts.d.ts +82 -0
- package/dist/VirtualizerLayouts.d.ts.map +1 -0
- package/dist/VisuallyHidden.d.ts +3 -1
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +1 -0
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +57 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13961 -5946
- package/dist/index.js.map +1 -7
- package/dist/index.ssr.js +9612 -2401
- package/dist/index.ssr.js.map +1 -7
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +79 -0
- package/dist/virtualizer/Layout.d.ts.map +1 -0
- package/package.json +8 -6
- package/src/ActionBar.tsx +248 -0
- package/src/ActionGroup.tsx +285 -0
- package/src/Alert.tsx +177 -0
- package/src/Autocomplete.tsx +1 -1
- package/src/Breadcrumbs.tsx +103 -17
- package/src/Button.tsx +65 -21
- package/src/Calendar.tsx +179 -53
- package/src/Checkbox.tsx +1 -2
- package/src/Collection.tsx +341 -0
- package/src/Color.tsx +652 -34
- package/src/ColorEditor.tsx +231 -0
- package/src/ComboBox.tsx +315 -81
- package/src/ContextualHelpTrigger.tsx +183 -0
- package/src/DateField.tsx +93 -19
- package/src/DatePicker.tsx +495 -25
- package/src/Dialog.tsx +40 -9
- package/src/Disclosure.tsx +33 -27
- package/src/DragAndDrop.tsx +334 -0
- package/src/DragPreview.tsx +45 -0
- package/src/DropZone.tsx +213 -0
- package/src/FieldError.tsx +67 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +106 -0
- package/src/Form.tsx +85 -0
- package/src/GridList.tsx +379 -41
- package/src/Icon.tsx +154 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Link.tsx +14 -1
- package/src/ListBox.tsx +484 -33
- package/src/ListDropTargetDelegate.ts +282 -0
- package/src/Menu.tsx +388 -35
- package/src/Meter.tsx +7 -3
- package/src/Modal.tsx +32 -4
- package/src/NumberField.tsx +163 -43
- package/src/Popover.tsx +136 -180
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +7 -3
- package/src/RadioGroup.tsx +35 -25
- package/src/RangeCalendar.tsx +100 -68
- package/src/RouterProvider.tsx +240 -0
- package/src/SearchField.tsx +142 -34
- package/src/Select.tsx +221 -73
- package/src/SelectionIndicator.tsx +105 -0
- package/src/SharedElementTransition.tsx +258 -0
- package/src/Slider.tsx +16 -6
- package/src/Table.tsx +417 -57
- package/src/Tabs.tsx +68 -35
- package/src/TagGroup.tsx +121 -36
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +25 -8
- package/src/TimeField.tsx +101 -151
- package/src/Toast.tsx +108 -14
- package/src/ToggleButton.tsx +159 -0
- package/src/ToggleButtonGroup.tsx +136 -0
- package/src/Toolbar.tsx +14 -8
- package/src/Tooltip.tsx +108 -19
- package/src/Tree.tsx +1143 -87
- package/src/Virtualizer.tsx +702 -0
- package/src/VirtualizerLayouts.ts +265 -0
- package/src/VisuallyHidden.tsx +15 -21
- package/src/contexts.ts +1 -0
- package/src/index.ts +1057 -620
- package/src/useDragAndDrop.ts +351 -0
- package/src/utils.tsx +37 -3
- package/src/virtualizer/Layout.ts +200 -0
package/src/Menu.tsx
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
10
|
createContext,
|
|
11
|
+
createEffect,
|
|
11
12
|
createMemo,
|
|
13
|
+
createSignal,
|
|
12
14
|
splitProps,
|
|
13
15
|
useContext,
|
|
14
16
|
For,
|
|
@@ -22,6 +24,7 @@ import {
|
|
|
22
24
|
createHover,
|
|
23
25
|
createButton,
|
|
24
26
|
createInteractOutside,
|
|
27
|
+
mergeProps,
|
|
25
28
|
FocusScope,
|
|
26
29
|
type AriaMenuProps,
|
|
27
30
|
type AriaMenuItemProps,
|
|
@@ -33,6 +36,7 @@ import {
|
|
|
33
36
|
type MenuState,
|
|
34
37
|
type OverlayTriggerState,
|
|
35
38
|
type Key,
|
|
39
|
+
type DropTarget,
|
|
36
40
|
} from '@proyecto-viviana/solid-stately';
|
|
37
41
|
import {
|
|
38
42
|
type RenderChildren,
|
|
@@ -41,6 +45,27 @@ import {
|
|
|
41
45
|
type SlotProps,
|
|
42
46
|
useRenderProps,
|
|
43
47
|
} from './utils';
|
|
48
|
+
import { SharedElementTransition } from './SharedElementTransition';
|
|
49
|
+
import { type DragAndDropHooks } from './useDragAndDrop';
|
|
50
|
+
import {
|
|
51
|
+
CollectionRendererContext,
|
|
52
|
+
Section,
|
|
53
|
+
Header,
|
|
54
|
+
Group,
|
|
55
|
+
type CollectionEntry,
|
|
56
|
+
type CollectionRendererContextValue,
|
|
57
|
+
type SectionProps,
|
|
58
|
+
useCollectionRenderer,
|
|
59
|
+
flattenCollectionEntries,
|
|
60
|
+
isCollectionSection,
|
|
61
|
+
} from './Collection';
|
|
62
|
+
import { useVirtualizerContext } from './Virtualizer';
|
|
63
|
+
import {
|
|
64
|
+
getNormalizedDropTargetKey,
|
|
65
|
+
mergePersistedKeysIntoVirtualRange,
|
|
66
|
+
useDndPersistedKeys,
|
|
67
|
+
useRenderDropIndicator,
|
|
68
|
+
} from './DragAndDrop';
|
|
44
69
|
|
|
45
70
|
// ============================================
|
|
46
71
|
// TYPES
|
|
@@ -57,7 +82,7 @@ export interface MenuProps<T>
|
|
|
57
82
|
extends Omit<AriaMenuProps, 'children'>,
|
|
58
83
|
SlotProps {
|
|
59
84
|
/** The items to render in the menu. */
|
|
60
|
-
items: T[];
|
|
85
|
+
items: CollectionEntry<T>[];
|
|
61
86
|
/** Function to get the key from an item. */
|
|
62
87
|
getKey?: (item: T) => Key;
|
|
63
88
|
/** Function to get the text value from an item. */
|
|
@@ -76,6 +101,8 @@ export interface MenuProps<T>
|
|
|
76
101
|
class?: ClassNameOrFunction<MenuRenderProps>;
|
|
77
102
|
/** The inline style for the element. */
|
|
78
103
|
style?: StyleOrFunction<MenuRenderProps>;
|
|
104
|
+
/** Drag and drop hooks from `useDragAndDrop`. */
|
|
105
|
+
dragAndDropHooks?: DragAndDropHooks<T>;
|
|
79
106
|
}
|
|
80
107
|
|
|
81
108
|
export interface MenuItemRenderProps {
|
|
@@ -140,12 +167,18 @@ export interface MenuTriggerProps extends Omit<AriaMenuTriggerProps, 'children'>
|
|
|
140
167
|
onOpenChange?: (isOpen: boolean) => void;
|
|
141
168
|
}
|
|
142
169
|
|
|
170
|
+
export interface SubmenuTriggerProps extends MenuTriggerProps {}
|
|
171
|
+
|
|
143
172
|
// ============================================
|
|
144
173
|
// CONTEXT
|
|
145
174
|
// ============================================
|
|
146
175
|
|
|
147
176
|
interface MenuContextValue<T> {
|
|
148
177
|
state: MenuState<T>;
|
|
178
|
+
isDisabled: () => boolean;
|
|
179
|
+
dragAndDropHooks?: DragAndDropHooks<T>;
|
|
180
|
+
dragState?: unknown;
|
|
181
|
+
dropState?: unknown;
|
|
149
182
|
}
|
|
150
183
|
|
|
151
184
|
interface MenuTriggerContextValue {
|
|
@@ -157,6 +190,7 @@ interface MenuTriggerContextValue {
|
|
|
157
190
|
export const MenuContext = createContext<MenuContextValue<unknown> | null>(null);
|
|
158
191
|
export const MenuStateContext = createContext<MenuState<unknown> | null>(null);
|
|
159
192
|
export const MenuTriggerContext = createContext<MenuTriggerContextValue | null>(null);
|
|
193
|
+
export const RootMenuTriggerStateContext = createContext<OverlayTriggerState | null>(null);
|
|
160
194
|
|
|
161
195
|
// ============================================
|
|
162
196
|
// COMPONENTS
|
|
@@ -192,22 +226,28 @@ export function MenuTrigger(props: MenuTriggerProps): JSX.Element {
|
|
|
192
226
|
);
|
|
193
227
|
|
|
194
228
|
return (
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
229
|
+
<RootMenuTriggerStateContext.Provider value={state}>
|
|
230
|
+
<MenuTriggerContext.Provider
|
|
231
|
+
value={{
|
|
232
|
+
state,
|
|
233
|
+
triggerProps: menuTriggerProps,
|
|
234
|
+
menuProps,
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
{props.children}
|
|
238
|
+
</MenuTriggerContext.Provider>
|
|
239
|
+
</RootMenuTriggerStateContext.Provider>
|
|
204
240
|
);
|
|
205
241
|
}
|
|
206
242
|
|
|
243
|
+
export function SubmenuTrigger(props: SubmenuTriggerProps): JSX.Element {
|
|
244
|
+
return <MenuTrigger {...props} />;
|
|
245
|
+
}
|
|
246
|
+
|
|
207
247
|
/**
|
|
208
248
|
* A button that opens a menu.
|
|
209
249
|
*/
|
|
210
|
-
export interface MenuButtonProps extends SlotProps {
|
|
250
|
+
export interface MenuButtonProps extends SlotProps, Omit<JSX.HTMLAttributes<HTMLButtonElement>, 'class' | 'style' | 'children'> {
|
|
211
251
|
/** The children of the button. A function may be provided to receive render props. */
|
|
212
252
|
children?: RenderChildren<MenuTriggerRenderProps>;
|
|
213
253
|
/** The CSS className for the element. */
|
|
@@ -218,8 +258,10 @@ export interface MenuButtonProps extends SlotProps {
|
|
|
218
258
|
isDisabled?: boolean;
|
|
219
259
|
}
|
|
220
260
|
|
|
261
|
+
export interface MenuSectionProps extends SectionProps {}
|
|
262
|
+
|
|
221
263
|
export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
222
|
-
const [local] = splitProps(props, ['class', 'style', 'slot', 'isDisabled']);
|
|
264
|
+
const [local, domProps] = splitProps(props, ['class', 'style', 'slot', 'isDisabled', 'children']);
|
|
223
265
|
|
|
224
266
|
// Get trigger context
|
|
225
267
|
const context = useContext(MenuTriggerContext);
|
|
@@ -289,6 +331,7 @@ export function MenuButton(props: MenuButtonProps): JSX.Element {
|
|
|
289
331
|
|
|
290
332
|
return (
|
|
291
333
|
<button
|
|
334
|
+
{...domProps}
|
|
292
335
|
{...cleanTriggerProps()}
|
|
293
336
|
{...cleanButtonProps()}
|
|
294
337
|
{...cleanFocusProps()}
|
|
@@ -315,19 +358,25 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
315
358
|
const [local, stateProps, ariaProps] = splitProps(
|
|
316
359
|
props,
|
|
317
360
|
['children', 'class', 'style', 'slot'],
|
|
318
|
-
['items', 'getKey', 'getTextValue', 'getDisabled', 'disabledKeys', 'onAction', 'onClose']
|
|
361
|
+
['items', 'getKey', 'getTextValue', 'getDisabled', 'disabledKeys', 'onAction', 'onClose', 'dragAndDropHooks']
|
|
319
362
|
);
|
|
320
363
|
|
|
321
364
|
// Get trigger context if available
|
|
322
365
|
const triggerContext = useContext(MenuTriggerContext);
|
|
323
366
|
|
|
324
367
|
// Ref for the menu element (for click outside detection)
|
|
325
|
-
|
|
368
|
+
const [menuRef, setMenuRef] = createSignal<HTMLUListElement | null>(null);
|
|
369
|
+
|
|
370
|
+
const flatItems = createMemo<T[]>(() => {
|
|
371
|
+
return flattenCollectionEntries(stateProps.items);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const hasSections = createMemo(() => stateProps.items.some((item) => isCollectionSection(item)));
|
|
326
375
|
|
|
327
376
|
// Create menu state
|
|
328
377
|
const state = createMenuState<T>({
|
|
329
378
|
get items() {
|
|
330
|
-
return
|
|
379
|
+
return flatItems();
|
|
331
380
|
},
|
|
332
381
|
get getKey() {
|
|
333
382
|
return stateProps.getKey;
|
|
@@ -349,9 +398,26 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
349
398
|
},
|
|
350
399
|
});
|
|
351
400
|
|
|
401
|
+
const resolveDisabled = (): boolean => {
|
|
402
|
+
const disabled = ariaProps.isDisabled;
|
|
403
|
+
if (typeof disabled === 'function') {
|
|
404
|
+
return (disabled as () => boolean)();
|
|
405
|
+
}
|
|
406
|
+
return !!disabled;
|
|
407
|
+
};
|
|
408
|
+
|
|
352
409
|
// Create menu aria props
|
|
353
|
-
const { menuProps } = createMenu(
|
|
410
|
+
const { menuProps, labelProps } = createMenu(
|
|
354
411
|
{
|
|
412
|
+
get isDisabled() {
|
|
413
|
+
return resolveDisabled();
|
|
414
|
+
},
|
|
415
|
+
get label() {
|
|
416
|
+
return ariaProps.label;
|
|
417
|
+
},
|
|
418
|
+
get onAction() {
|
|
419
|
+
return stateProps.onAction;
|
|
420
|
+
},
|
|
355
421
|
get onClose() {
|
|
356
422
|
return stateProps.onClose ?? (() => triggerContext?.state.close());
|
|
357
423
|
},
|
|
@@ -361,6 +427,9 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
361
427
|
get 'aria-labelledby'() {
|
|
362
428
|
return ariaProps['aria-labelledby'];
|
|
363
429
|
},
|
|
430
|
+
get 'aria-describedby'() {
|
|
431
|
+
return ariaProps['aria-describedby'];
|
|
432
|
+
},
|
|
364
433
|
},
|
|
365
434
|
state
|
|
366
435
|
);
|
|
@@ -370,7 +439,7 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
370
439
|
|
|
371
440
|
// Handle click outside to close menu
|
|
372
441
|
createInteractOutside({
|
|
373
|
-
ref: () => menuRef
|
|
442
|
+
ref: () => menuRef(),
|
|
374
443
|
onInteractOutside: () => {
|
|
375
444
|
if (triggerContext?.state.isOpen()) {
|
|
376
445
|
triggerContext.state.close();
|
|
@@ -411,27 +480,264 @@ export function Menu<T>(props: MenuProps<T>): JSX.Element {
|
|
|
411
480
|
const { ref: _ref3, ...rest } = focusProps as Record<string, unknown>;
|
|
412
481
|
return rest;
|
|
413
482
|
};
|
|
483
|
+
const cleanLabelProps = () => {
|
|
484
|
+
const { ref: _ref4, ...rest } = labelProps as Record<string, unknown>;
|
|
485
|
+
return rest;
|
|
486
|
+
};
|
|
414
487
|
|
|
415
488
|
// If inside a MenuTrigger, only render when open
|
|
416
489
|
// If standalone (no trigger context), always render
|
|
417
490
|
const shouldRender = () => triggerContext ? triggerContext.state.isOpen() : true;
|
|
491
|
+
const parentCollectionRenderer = useCollectionRenderer<unknown>();
|
|
492
|
+
const virtualizer = useVirtualizerContext();
|
|
493
|
+
const getItemNodes = createMemo(() => Array.from(state.collection()).filter((node) => node.type === 'item'));
|
|
494
|
+
const getDropTargetByIndex = (index: number, position: 'before' | 'after' | 'on'): DropTarget | null => {
|
|
495
|
+
const node = getItemNodes()[index];
|
|
496
|
+
if (!node) return null;
|
|
497
|
+
return { type: 'item', key: node.key, dropPosition: position };
|
|
498
|
+
};
|
|
499
|
+
const hasDroppableDnd = createMemo(() => {
|
|
500
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
501
|
+
return Boolean(
|
|
502
|
+
hooks?.useDroppableCollectionState &&
|
|
503
|
+
hooks.useDroppableCollection &&
|
|
504
|
+
(hooks.dropTargetDelegate || parentCollectionRenderer?.dropTargetDelegate || hooks.ListDropTargetDelegate)
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
const hasDraggableDnd = createMemo(() => {
|
|
508
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
509
|
+
return Boolean(hooks?.useDraggableCollectionState && hooks.useDraggableCollection);
|
|
510
|
+
});
|
|
511
|
+
const dragState = createMemo(() => {
|
|
512
|
+
if (!hasDraggableDnd()) return undefined;
|
|
513
|
+
return stateProps.dragAndDropHooks?.useDraggableCollectionState?.({
|
|
514
|
+
items: flatItems(),
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
const dropState = createMemo(() => {
|
|
518
|
+
if (!hasDroppableDnd()) return undefined;
|
|
519
|
+
return stateProps.dragAndDropHooks?.useDroppableCollectionState?.({});
|
|
520
|
+
});
|
|
521
|
+
const persistedKeys = useDndPersistedKeys(
|
|
522
|
+
{ focusedKey: state.focusedKey },
|
|
523
|
+
stateProps.dragAndDropHooks,
|
|
524
|
+
dropState(),
|
|
525
|
+
state.collection()
|
|
526
|
+
);
|
|
527
|
+
const virtualRange = createMemo(() => {
|
|
528
|
+
if (!virtualizer || !parentCollectionRenderer?.isVirtualized || hasSections()) return null;
|
|
529
|
+
const baseRange = virtualizer.getVisibleRange(stateProps.items.length);
|
|
530
|
+
const itemNodes = getItemNodes();
|
|
531
|
+
const persistedIndexes = Array.from(persistedKeys())
|
|
532
|
+
.map((key) => itemNodes.findIndex((node) => node.key === key))
|
|
533
|
+
.filter((index) => index >= 0);
|
|
534
|
+
const dropTarget = dropState()?.target;
|
|
535
|
+
const normalizedDropKey = getNormalizedDropTargetKey(dropTarget, state.collection());
|
|
536
|
+
const focusedKey = state.focusedKey();
|
|
537
|
+
const focusedIndex = focusedKey != null ? itemNodes.findIndex((node) => node.key === focusedKey) : -1;
|
|
538
|
+
const forceIncludeIndexes = [
|
|
539
|
+
dropTarget?.type === 'item' ? itemNodes.findIndex((node) => node.key === dropTarget.key) : -1,
|
|
540
|
+
normalizedDropKey != null ? itemNodes.findIndex((node) => node.key === normalizedDropKey) : -1,
|
|
541
|
+
dropTarget?.type === 'item' ? -1 : focusedIndex,
|
|
542
|
+
].filter((index) => index >= 0);
|
|
543
|
+
return mergePersistedKeysIntoVirtualRange(baseRange, persistedIndexes, stateProps.items.length, virtualizer, 80, {
|
|
544
|
+
forceIncludeIndexes,
|
|
545
|
+
forceIncludeMaxSpan: 320,
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
const visibleItems = createMemo(() => {
|
|
549
|
+
const range = virtualRange();
|
|
550
|
+
if (!range) return stateProps.items;
|
|
551
|
+
return stateProps.items.slice(range.start, range.end);
|
|
552
|
+
});
|
|
553
|
+
createEffect(() => {
|
|
554
|
+
if (!hasDraggableDnd()) return;
|
|
555
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
556
|
+
const activeDragState = dragState();
|
|
557
|
+
if (!hooks?.useDraggableCollection || !activeDragState) return;
|
|
558
|
+
hooks.useDraggableCollection({}, activeDragState, () => menuRef());
|
|
559
|
+
});
|
|
560
|
+
const droppableCollection = createMemo(() => {
|
|
561
|
+
if (!hasDroppableDnd()) return undefined;
|
|
562
|
+
const hooks = stateProps.dragAndDropHooks;
|
|
563
|
+
const activeDropState = dropState();
|
|
564
|
+
if (!hooks?.useDroppableCollection || !activeDropState) return undefined;
|
|
565
|
+
const resolveDirection = (): 'ltr' | 'rtl' => {
|
|
566
|
+
const menuEl = menuRef();
|
|
567
|
+
if (menuEl && typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {
|
|
568
|
+
const dir = window.getComputedStyle(menuEl).direction;
|
|
569
|
+
if (dir === 'rtl') return 'rtl';
|
|
570
|
+
}
|
|
571
|
+
return typeof document !== 'undefined' && document.dir === 'rtl' ? 'rtl' : 'ltr';
|
|
572
|
+
};
|
|
573
|
+
const dropTargetDelegate = hooks.dropTargetDelegate
|
|
574
|
+
?? parentCollectionRenderer?.dropTargetDelegate
|
|
575
|
+
?? (hooks.ListDropTargetDelegate
|
|
576
|
+
? new hooks.ListDropTargetDelegate(
|
|
577
|
+
() => state.collection(),
|
|
578
|
+
() => menuRef(),
|
|
579
|
+
{ layout: 'stack', orientation: 'vertical', direction: resolveDirection() }
|
|
580
|
+
)
|
|
581
|
+
: undefined);
|
|
582
|
+
if (!dropTargetDelegate) return undefined;
|
|
583
|
+
return hooks.useDroppableCollection(
|
|
584
|
+
{
|
|
585
|
+
dropTargetDelegate,
|
|
586
|
+
keyboardDelegate: {
|
|
587
|
+
getFirstKey: () => state.collection().getFirstKey(),
|
|
588
|
+
getLastKey: () => state.collection().getLastKey(),
|
|
589
|
+
getKeyBelow: (key) => state.collection().getKeyAfter(key),
|
|
590
|
+
getKeyAbove: (key) => state.collection().getKeyBefore(key),
|
|
591
|
+
getKeyPageBelow: (key) => state.collection().getKeyAfter(key),
|
|
592
|
+
getKeyPageAbove: (key) => state.collection().getKeyBefore(key),
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
activeDropState,
|
|
596
|
+
() => menuRef()
|
|
597
|
+
);
|
|
598
|
+
});
|
|
599
|
+
const isRootDropTarget = createMemo(() => {
|
|
600
|
+
return Boolean(dropState()?.target?.type === 'root');
|
|
601
|
+
});
|
|
602
|
+
const dndRenderDropIndicator = createMemo(() => useRenderDropIndicator(stateProps.dragAndDropHooks, dropState()));
|
|
603
|
+
const dndDropIndicator = (index: number, position: 'before' | 'after' | 'on') => {
|
|
604
|
+
const target = getDropTargetByIndex(index, position);
|
|
605
|
+
if (!target || target.type !== 'item') return undefined;
|
|
606
|
+
return dndRenderDropIndicator()?.(target);
|
|
607
|
+
};
|
|
608
|
+
const sectionedRenderEntries = createMemo(() => {
|
|
609
|
+
let globalIndex = 0;
|
|
610
|
+
return stateProps.items.map((entry) => {
|
|
611
|
+
if (isCollectionSection(entry)) {
|
|
612
|
+
const sectionItems = entry.items.map((item) => ({
|
|
613
|
+
item,
|
|
614
|
+
index: globalIndex++,
|
|
615
|
+
}));
|
|
616
|
+
return {
|
|
617
|
+
type: 'section' as const,
|
|
618
|
+
section: entry,
|
|
619
|
+
items: sectionItems,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
const indexedItem = {
|
|
623
|
+
item: entry as T,
|
|
624
|
+
index: globalIndex++,
|
|
625
|
+
};
|
|
626
|
+
return {
|
|
627
|
+
type: 'item' as const,
|
|
628
|
+
item: indexedItem,
|
|
629
|
+
};
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
const collectionRenderer = createMemo<CollectionRendererContextValue<unknown>>(() => ({
|
|
633
|
+
...parentCollectionRenderer,
|
|
634
|
+
renderItem: (item) => props.children(item as T),
|
|
635
|
+
renderDropIndicator: (index, position) =>
|
|
636
|
+
dndDropIndicator(index, position) ?? parentCollectionRenderer?.renderDropIndicator?.(index, position),
|
|
637
|
+
}));
|
|
418
638
|
|
|
419
639
|
// Only use FocusScope when inside a MenuTrigger (for popover behavior)
|
|
420
640
|
// Standalone menus don't need focus restoration
|
|
421
641
|
const menuContent = () => (
|
|
422
|
-
<MenuContext.Provider
|
|
642
|
+
<MenuContext.Provider
|
|
643
|
+
value={{
|
|
644
|
+
state,
|
|
645
|
+
isDisabled: resolveDisabled,
|
|
646
|
+
dragAndDropHooks: stateProps.dragAndDropHooks,
|
|
647
|
+
dragState: dragState(),
|
|
648
|
+
dropState: dropState(),
|
|
649
|
+
} as MenuContextValue<unknown>}
|
|
650
|
+
>
|
|
423
651
|
<MenuStateContext.Provider value={state}>
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
652
|
+
<CollectionRendererContext.Provider value={collectionRenderer()}>
|
|
653
|
+
<>
|
|
654
|
+
<Show when={ariaProps.label}>
|
|
655
|
+
<span {...cleanLabelProps()}>{ariaProps.label as JSX.Element}</span>
|
|
656
|
+
</Show>
|
|
657
|
+
<ul
|
|
658
|
+
ref={setMenuRef}
|
|
659
|
+
{...mergeProps(
|
|
660
|
+
cleanMenuProps(),
|
|
661
|
+
cleanTriggerMenuProps(),
|
|
662
|
+
cleanFocusProps(),
|
|
663
|
+
(droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ?? {}
|
|
664
|
+
)}
|
|
665
|
+
class={renderProps.class()}
|
|
666
|
+
style={renderProps.style()}
|
|
667
|
+
data-focused={state.isFocused() || undefined}
|
|
668
|
+
data-disabled={resolveDisabled() || undefined}
|
|
669
|
+
data-drop-target={isRootDropTarget() || undefined}
|
|
670
|
+
>
|
|
671
|
+
<SharedElementTransition>
|
|
672
|
+
{hasSections()
|
|
673
|
+
? (
|
|
674
|
+
<For each={sectionedRenderEntries()}>
|
|
675
|
+
{(entry) =>
|
|
676
|
+
entry.type === 'section'
|
|
677
|
+
? (
|
|
678
|
+
<li role="presentation" data-section-wrapper>
|
|
679
|
+
<Section class="solidaria-Menu-section">
|
|
680
|
+
{entry.section.title != null && (
|
|
681
|
+
<Header class="solidaria-Menu-sectionHeader">{entry.section.title}</Header>
|
|
682
|
+
)}
|
|
683
|
+
<Group class="solidaria-Menu-sectionGroup">
|
|
684
|
+
<ul role="group" aria-label={entry.section['aria-label']}>
|
|
685
|
+
<For each={entry.items}>
|
|
686
|
+
{(indexedItem) => (
|
|
687
|
+
<>
|
|
688
|
+
{collectionRenderer().renderDropIndicator?.(indexedItem.index, 'before')}
|
|
689
|
+
{collectionRenderer().renderDropIndicator?.(indexedItem.index, 'on')}
|
|
690
|
+
{props.children?.(indexedItem.item)}
|
|
691
|
+
{collectionRenderer().renderDropIndicator?.(indexedItem.index, 'after')}
|
|
692
|
+
</>
|
|
693
|
+
)}
|
|
694
|
+
</For>
|
|
695
|
+
</ul>
|
|
696
|
+
</Group>
|
|
697
|
+
</Section>
|
|
698
|
+
</li>
|
|
699
|
+
)
|
|
700
|
+
: (
|
|
701
|
+
<>
|
|
702
|
+
{collectionRenderer().renderDropIndicator?.(entry.item.index, 'before')}
|
|
703
|
+
{collectionRenderer().renderDropIndicator?.(entry.item.index, 'on')}
|
|
704
|
+
{props.children?.(entry.item.item)}
|
|
705
|
+
{collectionRenderer().renderDropIndicator?.(entry.item.index, 'after')}
|
|
706
|
+
</>
|
|
707
|
+
)
|
|
708
|
+
}
|
|
709
|
+
</For>
|
|
710
|
+
)
|
|
711
|
+
: (
|
|
712
|
+
<>
|
|
713
|
+
{virtualRange()?.offsetTop
|
|
714
|
+
? <li role="presentation" aria-hidden="true" style={{ height: `${virtualRange()!.offsetTop}px` }} data-virtualizer-spacer="top" />
|
|
715
|
+
: null}
|
|
716
|
+
<For each={visibleItems()}>
|
|
717
|
+
{(item, index) => {
|
|
718
|
+
const itemIndex = () => (virtualRange()?.start ?? 0) + index();
|
|
719
|
+
const beforeIndicator = () => collectionRenderer().renderDropIndicator?.(itemIndex(), 'before');
|
|
720
|
+
const onIndicator = () => collectionRenderer().renderDropIndicator?.(itemIndex(), 'on');
|
|
721
|
+
const afterIndicator = () => collectionRenderer().renderDropIndicator?.(itemIndex(), 'after');
|
|
722
|
+
return (
|
|
723
|
+
<>
|
|
724
|
+
{beforeIndicator()}
|
|
725
|
+
{onIndicator()}
|
|
726
|
+
{props.children?.(item as T)}
|
|
727
|
+
{afterIndicator()}
|
|
728
|
+
</>
|
|
729
|
+
);
|
|
730
|
+
}}
|
|
731
|
+
</For>
|
|
732
|
+
{virtualRange()?.offsetBottom
|
|
733
|
+
? <li role="presentation" aria-hidden="true" style={{ height: `${virtualRange()!.offsetBottom}px` }} data-virtualizer-spacer="bottom" />
|
|
734
|
+
: null}
|
|
735
|
+
</>
|
|
736
|
+
)}
|
|
737
|
+
</SharedElementTransition>
|
|
738
|
+
</ul>
|
|
739
|
+
</>
|
|
740
|
+
</CollectionRendererContext.Provider>
|
|
435
741
|
</MenuStateContext.Provider>
|
|
436
742
|
</MenuContext.Provider>
|
|
437
743
|
);
|
|
@@ -467,16 +773,18 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
|
|
|
467
773
|
throw new Error('MenuItem must be used within a Menu');
|
|
468
774
|
}
|
|
469
775
|
const state = context as MenuState<T>;
|
|
776
|
+
const menuContext = useContext(MenuContext) as MenuContextValue<T> | null;
|
|
777
|
+
const [ref, setRef] = createSignal<HTMLLIElement | null>(null);
|
|
470
778
|
|
|
471
779
|
// Create menu item aria props
|
|
472
780
|
const itemAria = createMenuItem<T>(
|
|
473
781
|
{
|
|
474
782
|
key: local.id,
|
|
475
783
|
get isDisabled() {
|
|
476
|
-
return ariaProps.isDisabled;
|
|
784
|
+
return Boolean(ariaProps.isDisabled || menuContext?.isDisabled());
|
|
477
785
|
},
|
|
478
786
|
get 'aria-label'() {
|
|
479
|
-
return ariaProps['aria-label'];
|
|
787
|
+
return ariaProps['aria-label'] ?? local.textValue;
|
|
480
788
|
},
|
|
481
789
|
get onAction() {
|
|
482
790
|
return local.onAction;
|
|
@@ -512,21 +820,55 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
|
|
|
512
820
|
},
|
|
513
821
|
renderValues
|
|
514
822
|
);
|
|
823
|
+
const hasPrimitiveLabel = () => {
|
|
824
|
+
return typeof props.children === 'string' || typeof props.children === 'number';
|
|
825
|
+
};
|
|
515
826
|
|
|
516
827
|
// Remove ref from spread props
|
|
517
828
|
const cleanItemProps = () => {
|
|
518
|
-
const {
|
|
829
|
+
const {
|
|
830
|
+
ref: _ref1,
|
|
831
|
+
'aria-describedby': _ariaDescribedby,
|
|
832
|
+
...rest
|
|
833
|
+
} = itemAria.menuItemProps as Record<string, unknown>;
|
|
834
|
+
if (!hasPrimitiveLabel() && rest['aria-label'] == null) {
|
|
835
|
+
delete rest['aria-labelledby'];
|
|
836
|
+
}
|
|
519
837
|
return rest;
|
|
520
838
|
};
|
|
521
839
|
const cleanHoverProps = () => {
|
|
522
840
|
const { ref: _ref2, ...rest } = hoverProps as Record<string, unknown>;
|
|
523
841
|
return rest;
|
|
524
842
|
};
|
|
843
|
+
const draggableItem = createMemo(() => {
|
|
844
|
+
if (!menuContext?.dragAndDropHooks?.useDraggableItem || !menuContext.dragState) return undefined;
|
|
845
|
+
return menuContext.dragAndDropHooks.useDraggableItem(
|
|
846
|
+
{
|
|
847
|
+
key: local.id as string | number,
|
|
848
|
+
},
|
|
849
|
+
menuContext.dragState as Parameters<NonNullable<DragAndDropHooks<T>['useDraggableItem']>>[1]
|
|
850
|
+
);
|
|
851
|
+
});
|
|
852
|
+
const droppableItem = createMemo(() => {
|
|
853
|
+
if (!menuContext?.dragAndDropHooks?.useDroppableItem || !menuContext.dropState) return undefined;
|
|
854
|
+
return menuContext.dragAndDropHooks.useDroppableItem(
|
|
855
|
+
{
|
|
856
|
+
key: local.id as string | number,
|
|
857
|
+
},
|
|
858
|
+
menuContext.dropState as Parameters<NonNullable<DragAndDropHooks<T>['useDroppableItem']>>[1],
|
|
859
|
+
() => ref()
|
|
860
|
+
);
|
|
861
|
+
});
|
|
525
862
|
|
|
526
863
|
return (
|
|
527
864
|
<li
|
|
528
|
-
{
|
|
529
|
-
{...
|
|
865
|
+
ref={setRef}
|
|
866
|
+
{...mergeProps(
|
|
867
|
+
cleanItemProps(),
|
|
868
|
+
cleanHoverProps(),
|
|
869
|
+
(draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
|
|
870
|
+
(droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {}
|
|
871
|
+
)}
|
|
530
872
|
class={renderProps.class()}
|
|
531
873
|
style={renderProps.style()}
|
|
532
874
|
data-focused={itemAria.isFocused() || undefined}
|
|
@@ -534,11 +876,22 @@ export function MenuItem<T>(props: MenuItemProps<T>): JSX.Element {
|
|
|
534
876
|
data-pressed={itemAria.isPressed() || undefined}
|
|
535
877
|
data-hovered={isHovered() || undefined}
|
|
536
878
|
data-disabled={itemAria.isDisabled() || undefined}
|
|
879
|
+
data-dragging={draggableItem()?.isDragging || undefined}
|
|
880
|
+
data-drop-target={droppableItem()?.isDropTarget || undefined}
|
|
537
881
|
>
|
|
538
|
-
{
|
|
882
|
+
{hasPrimitiveLabel()
|
|
883
|
+
? <span {...itemAria.labelProps}>{renderProps.renderChildren()}</span>
|
|
884
|
+
: renderProps.renderChildren()}
|
|
539
885
|
</li>
|
|
540
886
|
);
|
|
541
887
|
}
|
|
542
888
|
|
|
889
|
+
/**
|
|
890
|
+
* Section primitive alias for Menu composition parity.
|
|
891
|
+
*/
|
|
892
|
+
export function MenuSection(props: MenuSectionProps): JSX.Element {
|
|
893
|
+
return <Section {...props} />;
|
|
894
|
+
}
|
|
895
|
+
|
|
543
896
|
// Attach Item as a static property
|
|
544
897
|
Menu.Item = MenuItem;
|
package/src/Meter.tsx
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
type JSX,
|
|
13
|
-
type ParentProps,
|
|
14
13
|
createContext,
|
|
15
14
|
createMemo,
|
|
16
15
|
splitProps,
|
|
@@ -64,6 +63,11 @@ function clamp(value: number, min: number, max: number): number {
|
|
|
64
63
|
return Math.min(Math.max(value, min), max);
|
|
65
64
|
}
|
|
66
65
|
|
|
66
|
+
function getSafeRange(min: number, max: number): number {
|
|
67
|
+
const range = max - min;
|
|
68
|
+
return Number.isFinite(range) && range > 0 ? range : 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
67
71
|
// ============================================
|
|
68
72
|
// METER COMPONENT
|
|
69
73
|
// ============================================
|
|
@@ -85,7 +89,7 @@ function clamp(value: number, min: number, max: number): number {
|
|
|
85
89
|
* </Meter>
|
|
86
90
|
* ```
|
|
87
91
|
*/
|
|
88
|
-
export function Meter(props:
|
|
92
|
+
export function Meter(props: MeterProps): JSX.Element {
|
|
89
93
|
const [local, ariaProps] = splitProps(props, [
|
|
90
94
|
'children',
|
|
91
95
|
'class',
|
|
@@ -115,7 +119,7 @@ export function Meter(props: ParentProps<MeterProps>): JSX.Element {
|
|
|
115
119
|
// Calculate percentage
|
|
116
120
|
const percentage = createMemo(() => {
|
|
117
121
|
const clampedValue = clamp(value(), minValue(), maxValue());
|
|
118
|
-
return ((clampedValue - minValue()) / (
|
|
122
|
+
return ((clampedValue - minValue()) / getSafeRange(minValue(), maxValue())) * 100;
|
|
119
123
|
});
|
|
120
124
|
|
|
121
125
|
// Get value text from aria props
|