@proyecto-viviana/solidaria-components 0.2.4 → 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/GridList.tsx
CHANGED
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
+
type Accessor,
|
|
12
13
|
type JSX,
|
|
13
14
|
createContext,
|
|
15
|
+
createEffect,
|
|
14
16
|
createMemo,
|
|
15
17
|
createSignal,
|
|
18
|
+
onCleanup,
|
|
16
19
|
splitProps,
|
|
17
20
|
useContext,
|
|
18
21
|
For,
|
|
@@ -23,6 +26,7 @@ import {
|
|
|
23
26
|
createGridListSelectionCheckbox,
|
|
24
27
|
createFocusRing,
|
|
25
28
|
createHover,
|
|
29
|
+
mergeProps,
|
|
26
30
|
type AriaGridListProps,
|
|
27
31
|
} from '@proyecto-viviana/solidaria';
|
|
28
32
|
import {
|
|
@@ -31,6 +35,7 @@ import {
|
|
|
31
35
|
type GridCollection,
|
|
32
36
|
type GridNode,
|
|
33
37
|
type Key,
|
|
38
|
+
type DropTarget,
|
|
34
39
|
} from '@proyecto-viviana/solid-stately';
|
|
35
40
|
import {
|
|
36
41
|
type RenderChildren,
|
|
@@ -40,6 +45,22 @@ import {
|
|
|
40
45
|
useRenderProps,
|
|
41
46
|
filterDOMProps,
|
|
42
47
|
} from './utils';
|
|
48
|
+
import { SharedElementTransition } from './SharedElementTransition';
|
|
49
|
+
import { type DragAndDropHooks } from './useDragAndDrop';
|
|
50
|
+
import {
|
|
51
|
+
CollectionRendererContext,
|
|
52
|
+
type CollectionRendererContextValue,
|
|
53
|
+
Section,
|
|
54
|
+
type SectionProps,
|
|
55
|
+
useCollectionRenderer,
|
|
56
|
+
} from './Collection';
|
|
57
|
+
import { useVirtualizerContext } from './Virtualizer';
|
|
58
|
+
import {
|
|
59
|
+
getNormalizedDropTargetKey,
|
|
60
|
+
mergePersistedKeysIntoVirtualRange,
|
|
61
|
+
useDndPersistedKeys,
|
|
62
|
+
useRenderDropIndicator,
|
|
63
|
+
} from './DragAndDrop';
|
|
43
64
|
|
|
44
65
|
// ============================================
|
|
45
66
|
// TYPES
|
|
@@ -83,6 +104,14 @@ export interface GridListProps<T extends object> extends Omit<AriaGridListProps,
|
|
|
83
104
|
style?: StyleOrFunction<GridListRenderProps>;
|
|
84
105
|
/** A function to render when the grid list is empty. */
|
|
85
106
|
renderEmptyState?: () => JSX.Element;
|
|
107
|
+
/** Whether there are more items to load. */
|
|
108
|
+
hasMore?: boolean;
|
|
109
|
+
/** Whether additional items are currently loading. */
|
|
110
|
+
isLoading?: boolean;
|
|
111
|
+
/** Called when the load more sentinel becomes visible. */
|
|
112
|
+
onLoadMore?: () => void | Promise<void>;
|
|
113
|
+
/** Drag and drop hooks from `useDragAndDrop`. */
|
|
114
|
+
dragAndDropHooks?: DragAndDropHooks<T>;
|
|
86
115
|
}
|
|
87
116
|
|
|
88
117
|
export interface GridListItemRenderProps {
|
|
@@ -100,7 +129,7 @@ export interface GridListItemRenderProps {
|
|
|
100
129
|
isDisabled: boolean;
|
|
101
130
|
}
|
|
102
131
|
|
|
103
|
-
export interface GridListItemProps<T extends object> extends SlotProps {
|
|
132
|
+
export interface GridListItemProps<T extends object> extends SlotProps, Omit<JSX.HTMLAttributes<HTMLDivElement>, 'class' | 'style' | 'children' | 'id'> {
|
|
104
133
|
/** The unique key for the item. */
|
|
105
134
|
id: Key;
|
|
106
135
|
/** The item value. */
|
|
@@ -117,6 +146,21 @@ export interface GridListItemProps<T extends object> extends SlotProps {
|
|
|
117
146
|
onAction?: () => void;
|
|
118
147
|
}
|
|
119
148
|
|
|
149
|
+
export interface GridListLoadMoreItemProps extends SlotProps {
|
|
150
|
+
onLoadMore: () => void | Promise<void>;
|
|
151
|
+
isLoading?: boolean;
|
|
152
|
+
children?: JSX.Element;
|
|
153
|
+
class?: ClassNameOrFunction<{ isLoading: boolean }>;
|
|
154
|
+
style?: StyleOrFunction<{ isLoading: boolean }>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface GridListSectionProps extends SectionProps {}
|
|
158
|
+
export interface GridListHeaderProps extends SlotProps {
|
|
159
|
+
children?: JSX.Element;
|
|
160
|
+
class?: string;
|
|
161
|
+
style?: JSX.CSSProperties;
|
|
162
|
+
}
|
|
163
|
+
|
|
120
164
|
// ============================================
|
|
121
165
|
// CONTEXT
|
|
122
166
|
// ============================================
|
|
@@ -125,10 +169,14 @@ interface GridListContextValue<T extends object> {
|
|
|
125
169
|
state: GridState<T, GridCollection<T>>;
|
|
126
170
|
collection: GridCollection<T>;
|
|
127
171
|
isDisabled: boolean;
|
|
172
|
+
dragAndDropHooks?: DragAndDropHooks<T>;
|
|
173
|
+
dragState?: unknown;
|
|
174
|
+
dropState?: unknown;
|
|
128
175
|
}
|
|
129
176
|
|
|
130
177
|
export const GridListContext = createContext<GridListContextValue<object> | null>(null);
|
|
131
178
|
export const GridListStateContext = createContext<GridState<object, GridCollection<object>> | null>(null);
|
|
179
|
+
export const GridListHeaderContext = createContext<null>(null);
|
|
132
180
|
|
|
133
181
|
// ============================================
|
|
134
182
|
// HELPER: Build GridCollection from items
|
|
@@ -222,7 +270,7 @@ function buildGridCollection<T extends object>(
|
|
|
222
270
|
export function GridList<T extends object>(props: GridListProps<T>): JSX.Element {
|
|
223
271
|
const [local, stateProps, ariaProps] = splitProps(
|
|
224
272
|
props,
|
|
225
|
-
['children', 'class', 'style', 'slot', 'renderEmptyState'],
|
|
273
|
+
['children', 'class', 'style', 'slot', 'renderEmptyState', 'hasMore', 'isLoading', 'onLoadMore', 'dragAndDropHooks'],
|
|
226
274
|
[
|
|
227
275
|
'items',
|
|
228
276
|
'getKey',
|
|
@@ -333,34 +381,224 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
|
|
|
333
381
|
};
|
|
334
382
|
|
|
335
383
|
const isEmpty = () => stateProps.items.length === 0;
|
|
384
|
+
const virtualizer = useVirtualizerContext();
|
|
385
|
+
const parentCollectionRenderer = useCollectionRenderer<T>();
|
|
386
|
+
const getItemNodes = createMemo(() => Array.from(state.collection).filter((node) => node.type === 'item'));
|
|
387
|
+
const getDropTargetByIndex = (index: number, position: 'before' | 'after' | 'on'): DropTarget | null => {
|
|
388
|
+
const node = getItemNodes()[index];
|
|
389
|
+
if (!node) return null;
|
|
390
|
+
return { type: 'item', key: node.key, dropPosition: position };
|
|
391
|
+
};
|
|
392
|
+
const hasDroppableDnd = createMemo(() => {
|
|
393
|
+
const hooks = local.dragAndDropHooks;
|
|
394
|
+
return Boolean(
|
|
395
|
+
hooks?.useDroppableCollectionState &&
|
|
396
|
+
hooks.useDroppableCollection &&
|
|
397
|
+
(hooks.dropTargetDelegate || parentCollectionRenderer?.dropTargetDelegate || hooks.ListDropTargetDelegate)
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
const hasDraggableDnd = createMemo(() => {
|
|
401
|
+
const hooks = local.dragAndDropHooks;
|
|
402
|
+
return Boolean(hooks?.useDraggableCollectionState && hooks.useDraggableCollection);
|
|
403
|
+
});
|
|
404
|
+
const dragState = createMemo(() => {
|
|
405
|
+
if (!hasDraggableDnd()) return undefined;
|
|
406
|
+
return local.dragAndDropHooks?.useDraggableCollectionState?.({
|
|
407
|
+
items: stateProps.items,
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
const dropState = createMemo(() => {
|
|
411
|
+
if (!hasDroppableDnd()) return undefined;
|
|
412
|
+
return local.dragAndDropHooks?.useDroppableCollectionState?.({});
|
|
413
|
+
});
|
|
414
|
+
createEffect(() => {
|
|
415
|
+
if (!hasDraggableDnd()) return;
|
|
416
|
+
const hooks = local.dragAndDropHooks;
|
|
417
|
+
const activeDragState = dragState();
|
|
418
|
+
if (!hooks?.useDraggableCollection || !activeDragState) return;
|
|
419
|
+
hooks.useDraggableCollection({}, activeDragState, () => ref());
|
|
420
|
+
});
|
|
421
|
+
const droppableCollection = createMemo(() => {
|
|
422
|
+
if (!hasDroppableDnd()) return undefined;
|
|
423
|
+
const hooks = local.dragAndDropHooks;
|
|
424
|
+
const activeDropState = dropState();
|
|
425
|
+
if (!hooks?.useDroppableCollection || !activeDropState) return undefined;
|
|
426
|
+
const resolveDirection = (): 'ltr' | 'rtl' => {
|
|
427
|
+
const el = ref();
|
|
428
|
+
if (el && typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {
|
|
429
|
+
const dir = window.getComputedStyle(el).direction;
|
|
430
|
+
if (dir === 'rtl') return 'rtl';
|
|
431
|
+
}
|
|
432
|
+
return typeof document !== 'undefined' && document.dir === 'rtl' ? 'rtl' : 'ltr';
|
|
433
|
+
};
|
|
434
|
+
const dropTargetDelegate = hooks.dropTargetDelegate
|
|
435
|
+
?? parentCollectionRenderer?.dropTargetDelegate
|
|
436
|
+
?? (hooks.ListDropTargetDelegate
|
|
437
|
+
? new hooks.ListDropTargetDelegate(
|
|
438
|
+
() => state.collection,
|
|
439
|
+
() => ref(),
|
|
440
|
+
{ layout: 'grid', orientation: 'vertical', direction: resolveDirection() }
|
|
441
|
+
)
|
|
442
|
+
: undefined);
|
|
443
|
+
if (!dropTargetDelegate) return undefined;
|
|
444
|
+
return hooks.useDroppableCollection(
|
|
445
|
+
{
|
|
446
|
+
dropTargetDelegate,
|
|
447
|
+
keyboardDelegate: {
|
|
448
|
+
getFirstKey: () => state.collection.getFirstKey?.() ?? null,
|
|
449
|
+
getLastKey: () => state.collection.getLastKey?.() ?? null,
|
|
450
|
+
getKeyBelow: (key) => state.collection.getKeyAfter?.(key) ?? null,
|
|
451
|
+
getKeyAbove: (key) => state.collection.getKeyBefore?.(key) ?? null,
|
|
452
|
+
getKeyLeftOf: (key) =>
|
|
453
|
+
resolveDirection() === 'rtl'
|
|
454
|
+
? state.collection.getKeyAfter?.(key) ?? null
|
|
455
|
+
: state.collection.getKeyBefore?.(key) ?? null,
|
|
456
|
+
getKeyRightOf: (key) =>
|
|
457
|
+
resolveDirection() === 'rtl'
|
|
458
|
+
? state.collection.getKeyBefore?.(key) ?? null
|
|
459
|
+
: state.collection.getKeyAfter?.(key) ?? null,
|
|
460
|
+
getKeyPageBelow: (key) => state.collection.getKeyAfter?.(key) ?? null,
|
|
461
|
+
getKeyPageAbove: (key) => state.collection.getKeyBefore?.(key) ?? null,
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
activeDropState,
|
|
465
|
+
() => ref()
|
|
466
|
+
);
|
|
467
|
+
});
|
|
468
|
+
const isRootDropTarget = createMemo(() => {
|
|
469
|
+
return Boolean(dropState()?.target?.type === 'root');
|
|
470
|
+
});
|
|
471
|
+
const dndRenderDropIndicator = createMemo(() => useRenderDropIndicator(local.dragAndDropHooks, dropState()));
|
|
472
|
+
const dndDropIndicator = (index: number, position: 'before' | 'after' | 'on') => {
|
|
473
|
+
const target = getDropTargetByIndex(index, position);
|
|
474
|
+
if (!target || target.type !== 'item') return undefined;
|
|
475
|
+
return dndRenderDropIndicator()?.(target);
|
|
476
|
+
};
|
|
477
|
+
const persistedKeys = useDndPersistedKeys(
|
|
478
|
+
{ focusedKey: () => state.focusedKey },
|
|
479
|
+
local.dragAndDropHooks,
|
|
480
|
+
dropState(),
|
|
481
|
+
state.collection
|
|
482
|
+
);
|
|
483
|
+
const virtualRange = createMemo(() => {
|
|
484
|
+
if (!virtualizer || !parentCollectionRenderer?.isVirtualized) return null;
|
|
485
|
+
const baseRange = virtualizer.getVisibleRange(stateProps.items.length);
|
|
486
|
+
const itemNodes = getItemNodes();
|
|
487
|
+
const persistedIndexes = Array.from(persistedKeys())
|
|
488
|
+
.map((key) => itemNodes.findIndex((node) => node.key === key))
|
|
489
|
+
.filter((index) => index >= 0);
|
|
490
|
+
const dropTarget = dropState()?.target;
|
|
491
|
+
const normalizedDropKey = getNormalizedDropTargetKey(dropTarget, state.collection);
|
|
492
|
+
const focusedKey = state.focusedKey;
|
|
493
|
+
const focusedIndex = focusedKey != null ? itemNodes.findIndex((node) => node.key === focusedKey) : -1;
|
|
494
|
+
const forceIncludeIndexes = [
|
|
495
|
+
dropTarget?.type === 'item' ? itemNodes.findIndex((node) => node.key === dropTarget.key) : -1,
|
|
496
|
+
normalizedDropKey != null ? itemNodes.findIndex((node) => node.key === normalizedDropKey) : -1,
|
|
497
|
+
dropTarget?.type === 'item' ? -1 : focusedIndex,
|
|
498
|
+
].filter((index) => index >= 0);
|
|
499
|
+
return mergePersistedKeysIntoVirtualRange(baseRange, persistedIndexes, stateProps.items.length, virtualizer, 80, {
|
|
500
|
+
forceIncludeIndexes,
|
|
501
|
+
forceIncludeMaxSpan: 320,
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
createEffect(() => {
|
|
505
|
+
if (!virtualizer || !parentCollectionRenderer?.isVirtualized) return;
|
|
506
|
+
virtualizer.setDropTargetItemCountResolver(() => state.collection.size);
|
|
507
|
+
virtualizer.setDropTargetIndexResolver((key) => {
|
|
508
|
+
const entries = Array.from(state.collection);
|
|
509
|
+
const index = entries.findIndex((node) => node.key === key);
|
|
510
|
+
return index >= 0 ? index : null;
|
|
511
|
+
});
|
|
512
|
+
virtualizer.setDropTargetResolver((target) => {
|
|
513
|
+
const node = Array.from(state.collection)[target.index];
|
|
514
|
+
if (!node) return target;
|
|
515
|
+
return {
|
|
516
|
+
...target,
|
|
517
|
+
key: typeof node.key === 'string' || typeof node.key === 'number' ? node.key : undefined,
|
|
518
|
+
};
|
|
519
|
+
});
|
|
520
|
+
onCleanup(() => {
|
|
521
|
+
virtualizer.setDropTargetIndexResolver(undefined);
|
|
522
|
+
virtualizer.setDropTargetItemCountResolver(undefined);
|
|
523
|
+
virtualizer.setDropTargetResolver(undefined);
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
const visibleItems = createMemo(() => {
|
|
527
|
+
const range = virtualRange();
|
|
528
|
+
if (!range) return stateProps.items;
|
|
529
|
+
return stateProps.items.slice(range.start, range.end);
|
|
530
|
+
});
|
|
336
531
|
|
|
337
532
|
const contextValue = createMemo<GridListContextValue<T>>(() => ({
|
|
338
533
|
state,
|
|
339
534
|
collection: collection(),
|
|
340
535
|
isDisabled: ariaProps.isDisabled ?? false,
|
|
536
|
+
dragAndDropHooks: local.dragAndDropHooks,
|
|
537
|
+
dragState: dragState(),
|
|
538
|
+
dropState: dropState(),
|
|
539
|
+
}));
|
|
540
|
+
const collectionRenderer = createMemo<CollectionRendererContextValue<unknown>>(() => ({
|
|
541
|
+
...parentCollectionRenderer,
|
|
542
|
+
renderItem: (item) => props.children(item as T),
|
|
543
|
+
renderDropIndicator: (index: number, position: 'before' | 'after' | 'on') =>
|
|
544
|
+
dndDropIndicator(index, position) ?? parentCollectionRenderer?.renderDropIndicator?.(index, position),
|
|
341
545
|
}));
|
|
342
546
|
|
|
343
547
|
return (
|
|
344
|
-
<GridListContext.Provider value={contextValue() as GridListContextValue<object>}>
|
|
548
|
+
<GridListContext.Provider value={contextValue() as unknown as GridListContextValue<object>}>
|
|
345
549
|
<GridListStateContext.Provider value={state as unknown as GridState<object, GridCollection<object>>}>
|
|
346
|
-
<
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
550
|
+
<CollectionRendererContext.Provider value={collectionRenderer()}>
|
|
551
|
+
<div
|
|
552
|
+
ref={setRef}
|
|
553
|
+
{...mergeProps(
|
|
554
|
+
domProps(),
|
|
555
|
+
cleanGridProps(),
|
|
556
|
+
cleanFocusProps(),
|
|
557
|
+
(droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ?? {}
|
|
558
|
+
)}
|
|
559
|
+
class={renderProps.class()}
|
|
560
|
+
style={renderProps.style()}
|
|
561
|
+
data-focused={state.isFocused || undefined}
|
|
562
|
+
data-focus-visible={isFocusVisible() || undefined}
|
|
563
|
+
data-disabled={ariaProps.isDisabled || undefined}
|
|
564
|
+
data-empty={isEmpty() || undefined}
|
|
565
|
+
data-drop-target={isRootDropTarget() || undefined}
|
|
566
|
+
>
|
|
567
|
+
<SharedElementTransition>
|
|
568
|
+
{isEmpty() && local.renderEmptyState ? (
|
|
569
|
+
local.renderEmptyState()
|
|
570
|
+
) : (
|
|
571
|
+
<>
|
|
572
|
+
{virtualRange()?.offsetTop
|
|
573
|
+
? <div role="presentation" aria-hidden="true" style={{ height: `${virtualRange()!.offsetTop}px` }} data-virtualizer-spacer="top" />
|
|
574
|
+
: null}
|
|
575
|
+
<For each={visibleItems()}>
|
|
576
|
+
{(item, index) => {
|
|
577
|
+
const itemIndex = () => (virtualRange()?.start ?? 0) + index();
|
|
578
|
+
const beforeIndicator = () => collectionRenderer().renderDropIndicator?.(itemIndex(), 'before');
|
|
579
|
+
const onIndicator = () => collectionRenderer().renderDropIndicator?.(itemIndex(), 'on');
|
|
580
|
+
const afterIndicator = () => collectionRenderer().renderDropIndicator?.(itemIndex(), 'after');
|
|
581
|
+
return (
|
|
582
|
+
<>
|
|
583
|
+
{beforeIndicator()}
|
|
584
|
+
{onIndicator()}
|
|
585
|
+
{props.children(item)}
|
|
586
|
+
{afterIndicator()}
|
|
587
|
+
</>
|
|
588
|
+
);
|
|
589
|
+
}}
|
|
590
|
+
</For>
|
|
591
|
+
{virtualRange()?.offsetBottom
|
|
592
|
+
? <div role="presentation" aria-hidden="true" style={{ height: `${virtualRange()!.offsetBottom}px` }} data-virtualizer-spacer="bottom" />
|
|
593
|
+
: null}
|
|
594
|
+
</>
|
|
595
|
+
)}
|
|
596
|
+
</SharedElementTransition>
|
|
597
|
+
{local.hasMore && local.onLoadMore && (
|
|
598
|
+
<GridListLoadMoreItem onLoadMore={local.onLoadMore} isLoading={local.isLoading} />
|
|
599
|
+
)}
|
|
600
|
+
</div>
|
|
601
|
+
</CollectionRendererContext.Provider>
|
|
364
602
|
</GridListStateContext.Provider>
|
|
365
603
|
</GridListContext.Provider>
|
|
366
604
|
);
|
|
@@ -370,7 +608,7 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
|
|
|
370
608
|
* An item in a grid list.
|
|
371
609
|
*/
|
|
372
610
|
export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX.Element {
|
|
373
|
-
const [local] = splitProps(props, [
|
|
611
|
+
const [local, domProps] = splitProps(props, [
|
|
374
612
|
'class',
|
|
375
613
|
'style',
|
|
376
614
|
'slot',
|
|
@@ -378,6 +616,7 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
|
|
|
378
616
|
'item',
|
|
379
617
|
'textValue',
|
|
380
618
|
'onAction',
|
|
619
|
+
'children',
|
|
381
620
|
]);
|
|
382
621
|
|
|
383
622
|
// Get state from context
|
|
@@ -386,9 +625,10 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
|
|
|
386
625
|
throw new Error('GridListItem must be used within a GridList');
|
|
387
626
|
}
|
|
388
627
|
const state = context as GridState<T, GridCollection<T>>;
|
|
628
|
+
const listContext = useContext(GridListContext) as GridListContextValue<T> | null;
|
|
389
629
|
|
|
390
630
|
// Create ref signal
|
|
391
|
-
const [ref, setRef] = createSignal<
|
|
631
|
+
const [ref, setRef] = createSignal<HTMLDivElement | null>(null);
|
|
392
632
|
|
|
393
633
|
// Find or create the item node
|
|
394
634
|
const itemNode = createMemo(() => {
|
|
@@ -410,19 +650,22 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
|
|
|
410
650
|
});
|
|
411
651
|
|
|
412
652
|
// Create item aria props
|
|
413
|
-
const
|
|
653
|
+
const itemAria = createGridListItem<T, GridCollection<T>>(
|
|
414
654
|
() => ({
|
|
415
655
|
node: itemNode(),
|
|
416
656
|
onAction: local.onAction,
|
|
417
657
|
}),
|
|
418
658
|
() => state,
|
|
419
|
-
ref
|
|
659
|
+
ref as Accessor<HTMLLIElement | null>
|
|
420
660
|
);
|
|
661
|
+
const isSelected = () => itemAria.isSelected;
|
|
662
|
+
const isDisabled = () => itemAria.isDisabled;
|
|
663
|
+
const isPressed = () => itemAria.isPressed;
|
|
421
664
|
|
|
422
665
|
// Create hover
|
|
423
666
|
const { isHovered, hoverProps } = createHover({
|
|
424
667
|
get isDisabled() {
|
|
425
|
-
return isDisabled;
|
|
668
|
+
return isDisabled();
|
|
426
669
|
},
|
|
427
670
|
});
|
|
428
671
|
|
|
@@ -431,15 +674,34 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
|
|
|
431
674
|
|
|
432
675
|
// Check if focused
|
|
433
676
|
const isFocused = createMemo(() => state.focusedKey === local.id);
|
|
677
|
+
const draggableItem = createMemo(() => {
|
|
678
|
+
if (!listContext?.dragAndDropHooks?.useDraggableItem || !listContext.dragState) return undefined;
|
|
679
|
+
return listContext.dragAndDropHooks.useDraggableItem(
|
|
680
|
+
{
|
|
681
|
+
key: local.id as string | number,
|
|
682
|
+
},
|
|
683
|
+
listContext.dragState as Parameters<NonNullable<DragAndDropHooks<T>['useDraggableItem']>>[1]
|
|
684
|
+
);
|
|
685
|
+
});
|
|
686
|
+
const droppableItem = createMemo(() => {
|
|
687
|
+
if (!listContext?.dragAndDropHooks?.useDroppableItem || !listContext.dropState) return undefined;
|
|
688
|
+
return listContext.dragAndDropHooks.useDroppableItem(
|
|
689
|
+
{
|
|
690
|
+
key: local.id as string | number,
|
|
691
|
+
},
|
|
692
|
+
listContext.dropState as Parameters<NonNullable<DragAndDropHooks<T>['useDroppableItem']>>[1],
|
|
693
|
+
() => ref()
|
|
694
|
+
);
|
|
695
|
+
});
|
|
434
696
|
|
|
435
697
|
// Render props values
|
|
436
698
|
const renderValues = createMemo<GridListItemRenderProps>(() => ({
|
|
437
|
-
isSelected,
|
|
699
|
+
isSelected: isSelected(),
|
|
438
700
|
isFocused: isFocused(),
|
|
439
701
|
isFocusVisible: isFocusVisible() && isFocused(),
|
|
440
|
-
isPressed,
|
|
702
|
+
isPressed: isPressed(),
|
|
441
703
|
isHovered: isHovered(),
|
|
442
|
-
isDisabled,
|
|
704
|
+
isDisabled: isDisabled(),
|
|
443
705
|
}));
|
|
444
706
|
|
|
445
707
|
// Resolve render props
|
|
@@ -455,7 +717,7 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
|
|
|
455
717
|
|
|
456
718
|
// Remove ref from spread props
|
|
457
719
|
const cleanRowProps = () => {
|
|
458
|
-
const { ref: _ref1, ...rest } = rowProps as Record<string, unknown>;
|
|
720
|
+
const { ref: _ref1, ...rest } = itemAria.rowProps as Record<string, unknown>;
|
|
459
721
|
return rest;
|
|
460
722
|
};
|
|
461
723
|
const cleanHoverProps = () => {
|
|
@@ -468,22 +730,29 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
|
|
|
468
730
|
};
|
|
469
731
|
|
|
470
732
|
return (
|
|
471
|
-
<
|
|
733
|
+
<div
|
|
472
734
|
ref={setRef}
|
|
473
|
-
{...
|
|
474
|
-
{...
|
|
475
|
-
|
|
735
|
+
{...domProps}
|
|
736
|
+
{...mergeProps(
|
|
737
|
+
cleanRowProps(),
|
|
738
|
+
cleanHoverProps(),
|
|
739
|
+
cleanFocusProps(),
|
|
740
|
+
(draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
|
|
741
|
+
(droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {}
|
|
742
|
+
)}
|
|
476
743
|
class={renderProps.class()}
|
|
477
744
|
style={renderProps.style()}
|
|
478
|
-
data-selected={isSelected || undefined}
|
|
745
|
+
data-selected={isSelected() || undefined}
|
|
479
746
|
data-focused={isFocused() || undefined}
|
|
480
747
|
data-focus-visible={(isFocusVisible() && isFocused()) || undefined}
|
|
481
|
-
data-pressed={isPressed || undefined}
|
|
748
|
+
data-pressed={isPressed() || undefined}
|
|
482
749
|
data-hovered={isHovered() || undefined}
|
|
483
|
-
data-disabled={isDisabled || undefined}
|
|
750
|
+
data-disabled={isDisabled() || undefined}
|
|
751
|
+
data-dragging={draggableItem()?.isDragging || undefined}
|
|
752
|
+
data-drop-target={droppableItem()?.isDropTarget || undefined}
|
|
484
753
|
>
|
|
485
|
-
<div {...gridCellProps}>{renderProps.renderChildren()}</div>
|
|
486
|
-
</
|
|
754
|
+
<div {...itemAria.gridCellProps}>{renderProps.renderChildren()}</div>
|
|
755
|
+
</div>
|
|
487
756
|
);
|
|
488
757
|
}
|
|
489
758
|
|
|
@@ -498,14 +767,83 @@ export function GridListSelectionCheckbox(props: { itemKey: Key }): JSX.Element
|
|
|
498
767
|
|
|
499
768
|
const state = context as GridState<object, GridCollection<object>>;
|
|
500
769
|
|
|
501
|
-
const
|
|
770
|
+
const checkboxAria = createGridListSelectionCheckbox<object, GridCollection<object>>(
|
|
502
771
|
() => ({ key: props.itemKey }),
|
|
503
772
|
() => state
|
|
504
773
|
);
|
|
505
774
|
|
|
506
|
-
return <input {...checkboxProps} />;
|
|
775
|
+
return <input {...checkboxAria.checkboxProps} />;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
export function GridListLoadMoreItem(props: GridListLoadMoreItemProps): JSX.Element {
|
|
779
|
+
let ref: HTMLDivElement | undefined;
|
|
780
|
+
const [isPending, setIsPending] = createSignal(false);
|
|
781
|
+
const isLoading = () => !!props.isLoading || isPending();
|
|
782
|
+
|
|
783
|
+
const triggerLoadMore = async () => {
|
|
784
|
+
if (isLoading()) return;
|
|
785
|
+
setIsPending(true);
|
|
786
|
+
try {
|
|
787
|
+
await props.onLoadMore();
|
|
788
|
+
} finally {
|
|
789
|
+
setIsPending(false);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
createEffect(() => {
|
|
794
|
+
if (!ref || typeof IntersectionObserver !== 'function') return;
|
|
795
|
+
const observer = new IntersectionObserver((entries) => {
|
|
796
|
+
if (entries[0]?.isIntersecting) {
|
|
797
|
+
void triggerLoadMore();
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
observer.observe(ref);
|
|
801
|
+
return () => observer.disconnect();
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
const renderProps = useRenderProps(
|
|
805
|
+
{
|
|
806
|
+
children: props.children ?? (() => (isLoading() ? 'Loading more...' : 'Load more')),
|
|
807
|
+
class: props.class,
|
|
808
|
+
style: props.style,
|
|
809
|
+
defaultClassName: 'solidaria-GridList-loadMore',
|
|
810
|
+
},
|
|
811
|
+
() => ({ isLoading: isLoading() })
|
|
812
|
+
);
|
|
813
|
+
|
|
814
|
+
return (
|
|
815
|
+
<div
|
|
816
|
+
ref={ref}
|
|
817
|
+
role="row"
|
|
818
|
+
tabIndex={0}
|
|
819
|
+
onFocus={() => {
|
|
820
|
+
void triggerLoadMore();
|
|
821
|
+
}}
|
|
822
|
+
class={renderProps.class()}
|
|
823
|
+
style={renderProps.style()}
|
|
824
|
+
data-loading={isLoading() || undefined}
|
|
825
|
+
>
|
|
826
|
+
{renderProps.renderChildren()}
|
|
827
|
+
</div>
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
export function GridListHeader(props: GridListHeaderProps): JSX.Element {
|
|
832
|
+
return (
|
|
833
|
+
<div class={props.class ?? 'solidaria-GridListHeader'} style={props.style}>
|
|
834
|
+
{props.children}
|
|
835
|
+
</div>
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Section primitive alias for GridList composition parity.
|
|
841
|
+
*/
|
|
842
|
+
export function GridListSection(props: GridListSectionProps): JSX.Element {
|
|
843
|
+
return <Section {...props} />;
|
|
507
844
|
}
|
|
508
845
|
|
|
509
846
|
// Attach Item and SelectionCheckbox as static properties
|
|
510
847
|
GridList.Item = GridListItem;
|
|
511
848
|
GridList.SelectionCheckbox = GridListSelectionCheckbox;
|
|
849
|
+
GridList.LoadMoreItem = GridListLoadMoreItem;
|