@proyecto-viviana/solidaria-components 0.2.9 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +39 -272
  2. package/dist/ActionBar.d.ts +21 -13
  3. package/dist/ActionBar.d.ts.map +1 -1
  4. package/dist/ActionGroup.d.ts +8 -8
  5. package/dist/ActionGroup.d.ts.map +1 -1
  6. package/dist/Alert.d.ts +5 -5
  7. package/dist/Alert.d.ts.map +1 -1
  8. package/dist/Autocomplete.d.ts +5 -5
  9. package/dist/Autocomplete.d.ts.map +1 -1
  10. package/dist/Breadcrumbs.d.ts +18 -7
  11. package/dist/Breadcrumbs.d.ts.map +1 -1
  12. package/dist/Button.d.ts +24 -5
  13. package/dist/Button.d.ts.map +1 -1
  14. package/dist/Calendar.d.ts +38 -7
  15. package/dist/Calendar.d.ts.map +1 -1
  16. package/dist/Checkbox.d.ts +32 -7
  17. package/dist/Checkbox.d.ts.map +1 -1
  18. package/dist/Collection.d.ts +19 -14
  19. package/dist/Collection.d.ts.map +1 -1
  20. package/dist/Color.d.ts +103 -14
  21. package/dist/Color.d.ts.map +1 -1
  22. package/dist/ColorEditor.d.ts +6 -6
  23. package/dist/ColorEditor.d.ts.map +1 -1
  24. package/dist/ComboBox.d.ts +85 -19
  25. package/dist/ComboBox.d.ts.map +1 -1
  26. package/dist/ContextualHelpTrigger.d.ts +2 -2
  27. package/dist/ContextualHelpTrigger.d.ts.map +1 -1
  28. package/dist/DateField.d.ts +8 -6
  29. package/dist/DateField.d.ts.map +1 -1
  30. package/dist/DatePicker.d.ts +53 -22
  31. package/dist/DatePicker.d.ts.map +1 -1
  32. package/dist/DateRangePickerContext.d.ts +30 -0
  33. package/dist/DateRangePickerContext.d.ts.map +1 -0
  34. package/dist/Dialog.d.ts +5 -5
  35. package/dist/Dialog.d.ts.map +1 -1
  36. package/dist/Disclosure.d.ts +23 -5
  37. package/dist/Disclosure.d.ts.map +1 -1
  38. package/dist/DragAndDrop.d.ts +6 -6
  39. package/dist/DragAndDrop.d.ts.map +1 -1
  40. package/dist/DragPreview.d.ts +2 -2
  41. package/dist/DragPreview.d.ts.map +1 -1
  42. package/dist/DropZone.d.ts +4 -4
  43. package/dist/DropZone.d.ts.map +1 -1
  44. package/dist/FieldError.d.ts +9 -5
  45. package/dist/FieldError.d.ts.map +1 -1
  46. package/dist/FileTrigger.d.ts +3 -3
  47. package/dist/FileTrigger.d.ts.map +1 -1
  48. package/dist/Focusable.d.ts +2 -2
  49. package/dist/Focusable.d.ts.map +1 -1
  50. package/dist/Form.d.ts +18 -4
  51. package/dist/Form.d.ts.map +1 -1
  52. package/dist/GridList.d.ts +32 -12
  53. package/dist/GridList.d.ts.map +1 -1
  54. package/dist/HiddenDateInput.d.ts +26 -0
  55. package/dist/HiddenDateInput.d.ts.map +1 -0
  56. package/dist/HiddenTimeInput.d.ts +25 -0
  57. package/dist/HiddenTimeInput.d.ts.map +1 -0
  58. package/dist/Icon.d.ts +5 -5
  59. package/dist/Icon.d.ts.map +1 -1
  60. package/dist/Keyboard.d.ts +1 -1
  61. package/dist/Landmark.d.ts +3 -3
  62. package/dist/Landmark.d.ts.map +1 -1
  63. package/dist/Link.d.ts +10 -4
  64. package/dist/Link.d.ts.map +1 -1
  65. package/dist/ListBox.d.ts +32 -12
  66. package/dist/ListBox.d.ts.map +1 -1
  67. package/dist/ListDropTargetDelegate.d.ts +6 -6
  68. package/dist/ListDropTargetDelegate.d.ts.map +1 -1
  69. package/dist/Menu.d.ts +65 -14
  70. package/dist/Menu.d.ts.map +1 -1
  71. package/dist/Meter.d.ts +3 -3
  72. package/dist/Meter.d.ts.map +1 -1
  73. package/dist/Modal.d.ts +5 -5
  74. package/dist/Modal.d.ts.map +1 -1
  75. package/dist/NumberField.d.ts +8 -12
  76. package/dist/NumberField.d.ts.map +1 -1
  77. package/dist/Popover.d.ts +28 -5
  78. package/dist/Popover.d.ts.map +1 -1
  79. package/dist/Pressable.d.ts +2 -2
  80. package/dist/Pressable.d.ts.map +1 -1
  81. package/dist/ProgressBar.d.ts +5 -3
  82. package/dist/ProgressBar.d.ts.map +1 -1
  83. package/dist/RadioGroup.d.ts +43 -9
  84. package/dist/RadioGroup.d.ts.map +1 -1
  85. package/dist/RangeCalendar.d.ts +34 -7
  86. package/dist/RangeCalendar.d.ts.map +1 -1
  87. package/dist/RouterProvider.d.ts +2 -2
  88. package/dist/RouterProvider.d.ts.map +1 -1
  89. package/dist/SearchField.d.ts +23 -20
  90. package/dist/SearchField.d.ts.map +1 -1
  91. package/dist/Select.d.ts +41 -11
  92. package/dist/Select.d.ts.map +1 -1
  93. package/dist/SelectionIndicator.d.ts +3 -3
  94. package/dist/SelectionIndicator.d.ts.map +1 -1
  95. package/dist/Separator.d.ts +9 -3
  96. package/dist/Separator.d.ts.map +1 -1
  97. package/dist/SharedElementTransition.d.ts +6 -4
  98. package/dist/SharedElementTransition.d.ts.map +1 -1
  99. package/dist/Slider.d.ts +12 -8
  100. package/dist/Slider.d.ts.map +1 -1
  101. package/dist/StepList.d.ts +90 -0
  102. package/dist/StepList.d.ts.map +1 -0
  103. package/dist/Switch.d.ts +11 -5
  104. package/dist/Switch.d.ts.map +1 -1
  105. package/dist/Table.d.ts +187 -23
  106. package/dist/Table.d.ts.map +1 -1
  107. package/dist/Tabs.d.ts +45 -9
  108. package/dist/Tabs.d.ts.map +1 -1
  109. package/dist/TagGroup.d.ts +12 -10
  110. package/dist/TagGroup.d.ts.map +1 -1
  111. package/dist/Text.d.ts +2 -2
  112. package/dist/TextField.d.ts +15 -11
  113. package/dist/TextField.d.ts.map +1 -1
  114. package/dist/TimeField.d.ts +6 -6
  115. package/dist/TimeField.d.ts.map +1 -1
  116. package/dist/Toast.d.ts +29 -14
  117. package/dist/Toast.d.ts.map +1 -1
  118. package/dist/ToggleButton.d.ts +11 -5
  119. package/dist/ToggleButton.d.ts.map +1 -1
  120. package/dist/ToggleButtonGroup.d.ts +7 -7
  121. package/dist/ToggleButtonGroup.d.ts.map +1 -1
  122. package/dist/Toolbar.d.ts +7 -3
  123. package/dist/Toolbar.d.ts.map +1 -1
  124. package/dist/Tooltip.d.ts +50 -8
  125. package/dist/Tooltip.d.ts.map +1 -1
  126. package/dist/Tree.d.ts +66 -17
  127. package/dist/Tree.d.ts.map +1 -1
  128. package/dist/Virtualizer.d.ts +12 -12
  129. package/dist/Virtualizer.d.ts.map +1 -1
  130. package/dist/VirtualizerLayouts.d.ts +2 -2
  131. package/dist/VirtualizerLayouts.d.ts.map +1 -1
  132. package/dist/VisuallyHidden.d.ts +1 -1
  133. package/dist/VisuallyHidden.d.ts.map +1 -1
  134. package/dist/contexts.d.ts +5 -1
  135. package/dist/contexts.d.ts.map +1 -1
  136. package/dist/index.d.ts +73 -71
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +23247 -18564
  139. package/dist/index.js.map +1 -1
  140. package/dist/index.jsx +18110 -0
  141. package/dist/index.jsx.map +1 -0
  142. package/dist/useDragAndDrop.d.ts +13 -13
  143. package/dist/useDragAndDrop.d.ts.map +1 -1
  144. package/dist/utils.d.ts +2 -2
  145. package/dist/utils.d.ts.map +1 -1
  146. package/dist/virtualizer/Layout.d.ts +1 -1
  147. package/dist/virtualizer/Layout.d.ts.map +1 -1
  148. package/package.json +31 -32
  149. package/src/ActionBar.tsx +75 -72
  150. package/src/ActionGroup.tsx +53 -61
  151. package/src/Alert.tsx +17 -42
  152. package/src/Autocomplete.tsx +39 -44
  153. package/src/Breadcrumbs.tsx +149 -80
  154. package/src/Button.tsx +267 -70
  155. package/src/Calendar.tsx +218 -138
  156. package/src/Checkbox.tsx +413 -121
  157. package/src/Collection.tsx +67 -58
  158. package/src/Color.tsx +803 -380
  159. package/src/ColorEditor.tsx +131 -149
  160. package/src/ComboBox.tsx +414 -249
  161. package/src/ContextualHelpTrigger.tsx +86 -74
  162. package/src/DateField.tsx +185 -91
  163. package/src/DatePicker.tsx +524 -213
  164. package/src/DateRangePickerContext.tsx +44 -0
  165. package/src/Dialog.tsx +156 -118
  166. package/src/Disclosure.tsx +127 -80
  167. package/src/DragAndDrop.tsx +60 -54
  168. package/src/DragPreview.tsx +13 -11
  169. package/src/DropZone.tsx +42 -22
  170. package/src/FieldError.tsx +45 -23
  171. package/src/FileTrigger.tsx +19 -19
  172. package/src/Focusable.tsx +21 -24
  173. package/src/Form.tsx +71 -16
  174. package/src/GridList.tsx +273 -197
  175. package/src/HiddenDateInput.tsx +153 -0
  176. package/src/HiddenTimeInput.tsx +133 -0
  177. package/src/Icon.tsx +22 -43
  178. package/src/Keyboard.tsx +3 -3
  179. package/src/Landmark.tsx +37 -63
  180. package/src/Link.tsx +125 -75
  181. package/src/ListBox.tsx +332 -233
  182. package/src/ListDropTargetDelegate.ts +81 -80
  183. package/src/Menu.tsx +1023 -274
  184. package/src/Meter.tsx +38 -56
  185. package/src/Modal.tsx +243 -175
  186. package/src/NumberField.tsx +139 -143
  187. package/src/Popover.tsx +386 -233
  188. package/src/Pressable.tsx +21 -21
  189. package/src/ProgressBar.tsx +48 -57
  190. package/src/RadioGroup.tsx +524 -122
  191. package/src/RangeCalendar.tsx +157 -90
  192. package/src/RouterProvider.tsx +30 -47
  193. package/src/SearchField.tsx +362 -143
  194. package/src/Select.tsx +656 -233
  195. package/src/SelectionIndicator.tsx +18 -15
  196. package/src/Separator.tsx +47 -49
  197. package/src/SharedElementTransition.tsx +103 -97
  198. package/src/Slider.tsx +138 -98
  199. package/src/StepList.tsx +272 -0
  200. package/src/Switch.tsx +93 -46
  201. package/src/Table.tsx +1308 -342
  202. package/src/Tabs.tsx +324 -103
  203. package/src/TagGroup.tsx +139 -126
  204. package/src/Text.tsx +3 -3
  205. package/src/TextField.tsx +389 -79
  206. package/src/TimeField.tsx +136 -76
  207. package/src/Toast.tsx +209 -157
  208. package/src/ToggleButton.tsx +47 -37
  209. package/src/ToggleButtonGroup.tsx +39 -34
  210. package/src/Toolbar.tsx +54 -69
  211. package/src/Tooltip.tsx +387 -119
  212. package/src/Tree.tsx +651 -368
  213. package/src/Virtualizer.tsx +208 -180
  214. package/src/VirtualizerLayouts.ts +45 -30
  215. package/src/VisuallyHidden.tsx +19 -19
  216. package/src/contexts.ts +29 -37
  217. package/src/index.ts +110 -195
  218. package/src/useDragAndDrop.ts +87 -71
  219. package/src/utils.tsx +40 -55
  220. package/src/virtualizer/Layout.ts +14 -22
  221. package/dist/index.ssr.js +0 -16996
  222. package/dist/index.ssr.js.map +0 -1
package/src/GridList.tsx CHANGED
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import {
12
- type Accessor,
13
12
  type JSX,
14
13
  createContext,
15
14
  createEffect,
@@ -19,7 +18,8 @@ import {
19
18
  splitProps,
20
19
  useContext,
21
20
  For,
22
- } from 'solid-js';
21
+ Show,
22
+ } from "solid-js";
23
23
  import {
24
24
  createGridList,
25
25
  createGridListItem,
@@ -28,7 +28,7 @@ import {
28
28
  createHover,
29
29
  mergeProps,
30
30
  type AriaGridListProps,
31
- } from '@proyecto-viviana/solidaria';
31
+ } from "@proyecto-viviana/solidaria";
32
32
  import {
33
33
  createGridState,
34
34
  type GridState,
@@ -36,7 +36,7 @@ import {
36
36
  type GridNode,
37
37
  type Key,
38
38
  type DropTarget,
39
- } from '@proyecto-viviana/solid-stately';
39
+ } from "@proyecto-viviana/solid-stately";
40
40
  import {
41
41
  type RenderChildren,
42
42
  type ClassNameOrFunction,
@@ -44,27 +44,34 @@ import {
44
44
  type SlotProps,
45
45
  useRenderProps,
46
46
  filterDOMProps,
47
- } from './utils';
48
- import { SharedElementTransition } from './SharedElementTransition';
49
- import { type DragAndDropHooks } from './useDragAndDrop';
47
+ } from "./utils";
48
+ import { SharedElementTransition } from "./SharedElementTransition";
49
+ import { type DragAndDropHooks } from "./useDragAndDrop";
50
50
  import {
51
51
  CollectionRendererContext,
52
52
  type CollectionRendererContextValue,
53
53
  Section,
54
54
  type SectionProps,
55
55
  useCollectionRenderer,
56
- } from './Collection';
57
- import { useVirtualizerContext } from './Virtualizer';
56
+ } from "./Collection";
57
+ import { useVirtualizerContext } from "./Virtualizer";
58
58
  import {
59
59
  getNormalizedDropTargetKey,
60
60
  mergePersistedKeysIntoVirtualRange,
61
61
  useDndPersistedKeys,
62
62
  useRenderDropIndicator,
63
- } from './DragAndDrop';
63
+ } from "./DragAndDrop";
64
64
 
65
- // ============================================
66
- // TYPES
67
- // ============================================
65
+ type RefLike<T> = ((el: T) => void) | { current?: T | null } | undefined;
66
+
67
+ function assignRef<T>(ref: RefLike<T>, el: T): void {
68
+ if (!ref) return;
69
+ if (typeof ref === "function") {
70
+ ref(el);
71
+ } else {
72
+ ref.current = el;
73
+ }
74
+ }
68
75
 
69
76
  export interface GridListRenderProps {
70
77
  /** Whether the grid list has focus. */
@@ -77,7 +84,8 @@ export interface GridListRenderProps {
77
84
  isEmpty: boolean;
78
85
  }
79
86
 
80
- export interface GridListProps<T extends object> extends Omit<AriaGridListProps, 'children'>, SlotProps {
87
+ export interface GridListProps<T extends object>
88
+ extends Omit<AriaGridListProps, "children">, SlotProps {
81
89
  /** The items to render in the grid list. */
82
90
  items: T[];
83
91
  /** Function to get the key from an item. */
@@ -87,21 +95,25 @@ export interface GridListProps<T extends object> extends Omit<AriaGridListProps,
87
95
  /** Function to check if an item is disabled. */
88
96
  getDisabled?: (item: T) => boolean;
89
97
  /** The selection mode. */
90
- selectionMode?: 'none' | 'single' | 'multiple';
98
+ selectionMode?: "none" | "single" | "multiple";
99
+ /** How selection should behave when pressing an item. */
100
+ selectionBehavior?: "replace" | "toggle";
91
101
  /** Keys of disabled items. */
92
102
  disabledKeys?: Iterable<Key>;
93
103
  /** Currently selected keys (controlled). */
94
- selectedKeys?: 'all' | Iterable<Key>;
104
+ selectedKeys?: "all" | Iterable<Key>;
95
105
  /** Default selected keys (uncontrolled). */
96
- defaultSelectedKeys?: 'all' | Iterable<Key>;
106
+ defaultSelectedKeys?: "all" | Iterable<Key>;
97
107
  /** Handler called when selection changes. */
98
- onSelectionChange?: (keys: 'all' | Set<Key>) => void;
108
+ onSelectionChange?: (keys: "all" | Set<Key>) => void;
99
109
  /** The children of the component. A function may be provided to render each item. */
100
110
  children: (item: T) => JSX.Element;
101
111
  /** The CSS className for the element. */
102
112
  class?: ClassNameOrFunction<GridListRenderProps>;
103
113
  /** The inline style for the element. */
104
114
  style?: StyleOrFunction<GridListRenderProps>;
115
+ /** Ref for the grid list root element. */
116
+ ref?: RefLike<HTMLDivElement>;
105
117
  /** A function to render when the grid list is empty. */
106
118
  renderEmptyState?: () => JSX.Element;
107
119
  /** Whether there are more items to load. */
@@ -127,9 +139,16 @@ export interface GridListItemRenderProps {
127
139
  isHovered: boolean;
128
140
  /** Whether the item is disabled. */
129
141
  isDisabled: boolean;
142
+ /** The grid list selection mode. */
143
+ selectionMode: "none" | "single" | "multiple";
144
+ /** How selection behaves when pressing an item. */
145
+ selectionBehavior: "replace" | "toggle";
130
146
  }
131
147
 
132
- export interface GridListItemProps<T extends object> extends SlotProps, Omit<JSX.HTMLAttributes<HTMLDivElement>, 'class' | 'style' | 'children' | 'id'> {
148
+ export interface GridListItemProps<T extends object>
149
+ extends
150
+ SlotProps,
151
+ Omit<JSX.HTMLAttributes<HTMLDivElement>, "class" | "style" | "children" | "id" | "ref"> {
133
152
  /** The unique key for the item. */
134
153
  id: Key;
135
154
  /** The item value. */
@@ -144,11 +163,15 @@ export interface GridListItemProps<T extends object> extends SlotProps, Omit<JSX
144
163
  textValue?: string;
145
164
  /** Handler called when the item is activated. */
146
165
  onAction?: () => void;
166
+ /** Ref for the rendered row element. */
167
+ ref?: RefLike<HTMLDivElement>;
147
168
  }
148
169
 
149
170
  export interface GridListLoadMoreItemProps extends SlotProps {
150
171
  onLoadMore: () => void | Promise<void>;
151
172
  isLoading?: boolean;
173
+ /** Scroll offset multiplier for early loading trigger (default: 1 = 100% of viewport height). */
174
+ scrollOffset?: number;
152
175
  children?: JSX.Element;
153
176
  class?: ClassNameOrFunction<{ isLoading: boolean }>;
154
177
  style?: StyleOrFunction<{ isLoading: boolean }>;
@@ -161,37 +184,32 @@ export interface GridListHeaderProps extends SlotProps {
161
184
  style?: JSX.CSSProperties;
162
185
  }
163
186
 
164
- // ============================================
165
- // CONTEXT
166
- // ============================================
167
-
168
187
  interface GridListContextValue<T extends object> {
169
188
  state: GridState<T, GridCollection<T>>;
170
189
  collection: GridCollection<T>;
171
190
  isDisabled: boolean;
191
+ selectionBehavior: "replace" | "toggle";
172
192
  dragAndDropHooks?: DragAndDropHooks<T>;
173
193
  dragState?: unknown;
174
194
  dropState?: unknown;
175
195
  }
176
196
 
177
197
  export const GridListContext = createContext<GridListContextValue<object> | null>(null);
178
- export const GridListStateContext = createContext<GridState<object, GridCollection<object>> | null>(null);
198
+ export const GridListStateContext = createContext<GridState<object, GridCollection<object>> | null>(
199
+ null,
200
+ );
179
201
  export const GridListHeaderContext = createContext<null>(null);
180
202
 
181
- // ============================================
182
- // HELPER: Build GridCollection from items
183
- // ============================================
184
-
185
203
  function buildGridCollection<T extends object>(
186
204
  items: T[],
187
205
  getKey?: (item: T) => Key,
188
206
  getTextValue?: (item: T) => string,
189
- getDisabled?: (item: T) => boolean
207
+ getDisabled?: (item: T) => boolean,
190
208
  ): GridCollection<T> {
191
209
  const nodes: GridNode<T>[] = items.map((item, index) => {
192
210
  const key = getKey?.(item) ?? index;
193
211
  return {
194
- type: 'item' as const,
212
+ type: "item" as const,
195
213
  key,
196
214
  value: item,
197
215
  textValue: getTextValue?.(item) ?? String(key),
@@ -248,7 +266,7 @@ function buildGridCollection<T extends object>(
248
266
  return [];
249
267
  },
250
268
  getTextValue(key: Key) {
251
- return keyMap.get(key)?.textValue ?? '';
269
+ return keyMap.get(key)?.textValue ?? "";
252
270
  },
253
271
  getCell(_rowKey: Key, _columnKey: Key) {
254
272
  return null;
@@ -259,10 +277,6 @@ function buildGridCollection<T extends object>(
259
277
  };
260
278
  }
261
279
 
262
- // ============================================
263
- // COMPONENTS
264
- // ============================================
265
-
266
280
  /**
267
281
  * A grid list displays a list of interactive items, with support for
268
282
  * keyboard navigation, single or multiple selection, and row actions.
@@ -270,45 +284,52 @@ function buildGridCollection<T extends object>(
270
284
  export function GridList<T extends object>(props: GridListProps<T>): JSX.Element {
271
285
  const [local, stateProps, ariaProps] = splitProps(
272
286
  props,
273
- ['children', 'class', 'style', 'slot', 'renderEmptyState', 'hasMore', 'isLoading', 'onLoadMore', 'dragAndDropHooks'],
274
287
  [
275
- 'items',
276
- 'getKey',
277
- 'getTextValue',
278
- 'getDisabled',
279
- 'disabledKeys',
280
- 'selectionMode',
281
- 'selectedKeys',
282
- 'defaultSelectedKeys',
283
- 'onSelectionChange',
284
- ]
288
+ "children",
289
+ "class",
290
+ "style",
291
+ "ref",
292
+ "slot",
293
+ "renderEmptyState",
294
+ "hasMore",
295
+ "isLoading",
296
+ "onLoadMore",
297
+ "dragAndDropHooks",
298
+ ],
299
+ [
300
+ "items",
301
+ "getKey",
302
+ "getTextValue",
303
+ "getDisabled",
304
+ "disabledKeys",
305
+ "selectionMode",
306
+ "selectedKeys",
307
+ "defaultSelectedKeys",
308
+ "onSelectionChange",
309
+ "selectionBehavior",
310
+ ],
285
311
  );
286
312
 
287
- // Create ref signal
288
- const [ref, setRef] = createSignal<HTMLUListElement | null>(null);
313
+ const [ref, setRef] = createSignal<HTMLDivElement | null>(null);
289
314
 
290
- // Build collection
291
315
  const collection = createMemo(() =>
292
316
  buildGridCollection(
293
317
  stateProps.items,
294
318
  stateProps.getKey,
295
319
  stateProps.getTextValue,
296
- stateProps.getDisabled
297
- )
320
+ stateProps.getDisabled,
321
+ ),
298
322
  );
299
323
 
300
- // Get disabled keys from items + explicit disabledKeys
301
324
  const allDisabledKeys = createMemo(() => {
302
325
  const keys = new Set<Key>();
303
326
 
304
- // Add explicitly disabled keys
305
327
  if (stateProps.disabledKeys) {
306
328
  for (const key of stateProps.disabledKeys) {
307
329
  keys.add(key);
308
330
  }
309
331
  }
310
332
 
311
- // Add keys from items marked as disabled
312
333
  for (const node of collection().rows) {
313
334
  if (node.isDisabled) {
314
335
  keys.add(node.key);
@@ -318,35 +339,33 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
318
339
  return keys;
319
340
  });
320
341
 
321
- // Create grid state
322
342
  const state = createGridState<T, GridCollection<T>>(() => ({
323
343
  collection: collection(),
324
344
  disabledKeys: allDisabledKeys(),
325
345
  selectionMode: stateProps.selectionMode,
346
+ selectionBehavior: stateProps.selectionBehavior,
326
347
  selectedKeys: stateProps.selectedKeys,
327
348
  defaultSelectedKeys: stateProps.defaultSelectedKeys,
328
349
  onSelectionChange: stateProps.onSelectionChange,
329
350
  }));
330
351
 
331
- // Create grid list aria props
332
352
  const { gridProps } = createGridList<T, GridCollection<T>>(
333
353
  () => ({
334
354
  id: ariaProps.id,
335
- 'aria-label': ariaProps['aria-label'],
336
- 'aria-labelledby': ariaProps['aria-labelledby'],
337
- 'aria-describedby': ariaProps['aria-describedby'],
355
+ "aria-label": ariaProps["aria-label"],
356
+ "aria-labelledby": ariaProps["aria-labelledby"],
357
+ "aria-describedby": ariaProps["aria-describedby"],
338
358
  isVirtualized: ariaProps.isVirtualized,
339
359
  onAction: ariaProps.onAction,
340
360
  isDisabled: ariaProps.isDisabled,
361
+ selectionBehavior: stateProps.selectionBehavior,
341
362
  }),
342
363
  () => state,
343
- ref
364
+ ref,
344
365
  );
345
366
 
346
- // Create focus ring
347
367
  const { isFocused, isFocusVisible, focusProps } = createFocusRing();
348
368
 
349
- // Render props values
350
369
  const renderValues = createMemo<GridListRenderProps>(() => ({
351
370
  isFocused: state.isFocused || isFocused(),
352
371
  isFocusVisible: isFocusVisible(),
@@ -354,23 +373,20 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
354
373
  isEmpty: stateProps.items.length === 0,
355
374
  }));
356
375
 
357
- // Resolve render props
358
376
  const renderProps = useRenderProps(
359
377
  {
360
378
  class: local.class,
361
379
  style: local.style,
362
- defaultClassName: 'solidaria-GridList',
380
+ defaultClassName: "solidaria-GridList",
363
381
  },
364
- renderValues
382
+ renderValues,
365
383
  );
366
384
 
367
- // Filter DOM props
368
385
  const domProps = createMemo(() => {
369
386
  const filtered = filterDOMProps(ariaProps as Record<string, unknown>, { global: true });
370
387
  return filtered;
371
388
  });
372
389
 
373
- // Remove ref from spread props
374
390
  const cleanGridProps = () => {
375
391
  const { ref: _ref1, ...rest } = gridProps as Record<string, unknown>;
376
392
  return rest;
@@ -383,18 +399,25 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
383
399
  const isEmpty = () => stateProps.items.length === 0;
384
400
  const virtualizer = useVirtualizerContext();
385
401
  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 => {
402
+ const getItemNodes = createMemo(() =>
403
+ Array.from(state.collection).filter((node) => node.type === "item"),
404
+ );
405
+ const getDropTargetByIndex = (
406
+ index: number,
407
+ position: "before" | "after" | "on",
408
+ ): DropTarget | null => {
388
409
  const node = getItemNodes()[index];
389
410
  if (!node) return null;
390
- return { type: 'item', key: node.key, dropPosition: position };
411
+ return { type: "item", key: node.key, dropPosition: position };
391
412
  };
392
413
  const hasDroppableDnd = createMemo(() => {
393
414
  const hooks = local.dragAndDropHooks;
394
415
  return Boolean(
395
416
  hooks?.useDroppableCollectionState &&
396
417
  hooks.useDroppableCollection &&
397
- (hooks.dropTargetDelegate || parentCollectionRenderer?.dropTargetDelegate || hooks.ListDropTargetDelegate)
418
+ (hooks.dropTargetDelegate ||
419
+ parentCollectionRenderer?.dropTargetDelegate ||
420
+ hooks.ListDropTargetDelegate),
398
421
  );
399
422
  });
400
423
  const hasDraggableDnd = createMemo(() => {
@@ -423,22 +446,23 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
423
446
  const hooks = local.dragAndDropHooks;
424
447
  const activeDropState = dropState();
425
448
  if (!hooks?.useDroppableCollection || !activeDropState) return undefined;
426
- const resolveDirection = (): 'ltr' | 'rtl' => {
449
+ const resolveDirection = (): "ltr" | "rtl" => {
427
450
  const el = ref();
428
- if (el && typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {
451
+ if (el && typeof window !== "undefined" && typeof window.getComputedStyle === "function") {
429
452
  const dir = window.getComputedStyle(el).direction;
430
- if (dir === 'rtl') return 'rtl';
453
+ if (dir === "rtl") return "rtl";
431
454
  }
432
- return typeof document !== 'undefined' && document.dir === 'rtl' ? 'rtl' : 'ltr';
455
+ return typeof document !== "undefined" && document.dir === "rtl" ? "rtl" : "ltr";
433
456
  };
434
- const dropTargetDelegate = hooks.dropTargetDelegate
435
- ?? parentCollectionRenderer?.dropTargetDelegate
436
- ?? (hooks.ListDropTargetDelegate
457
+ const dropTargetDelegate =
458
+ hooks.dropTargetDelegate ??
459
+ parentCollectionRenderer?.dropTargetDelegate ??
460
+ (hooks.ListDropTargetDelegate
437
461
  ? new hooks.ListDropTargetDelegate(
438
- () => state.collection,
439
- () => ref(),
440
- { layout: 'grid', orientation: 'vertical', direction: resolveDirection() }
441
- )
462
+ () => state.collection,
463
+ () => ref(),
464
+ { layout: "grid", orientation: "vertical", direction: resolveDirection() },
465
+ )
442
466
  : undefined);
443
467
  if (!dropTargetDelegate) return undefined;
444
468
  return hooks.useDroppableCollection(
@@ -450,35 +474,37 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
450
474
  getKeyBelow: (key) => state.collection.getKeyAfter?.(key) ?? null,
451
475
  getKeyAbove: (key) => state.collection.getKeyBefore?.(key) ?? null,
452
476
  getKeyLeftOf: (key) =>
453
- resolveDirection() === 'rtl'
454
- ? state.collection.getKeyAfter?.(key) ?? null
455
- : state.collection.getKeyBefore?.(key) ?? null,
477
+ resolveDirection() === "rtl"
478
+ ? (state.collection.getKeyAfter?.(key) ?? null)
479
+ : (state.collection.getKeyBefore?.(key) ?? null),
456
480
  getKeyRightOf: (key) =>
457
- resolveDirection() === 'rtl'
458
- ? state.collection.getKeyBefore?.(key) ?? null
459
- : state.collection.getKeyAfter?.(key) ?? null,
481
+ resolveDirection() === "rtl"
482
+ ? (state.collection.getKeyBefore?.(key) ?? null)
483
+ : (state.collection.getKeyAfter?.(key) ?? null),
460
484
  getKeyPageBelow: (key) => state.collection.getKeyAfter?.(key) ?? null,
461
485
  getKeyPageAbove: (key) => state.collection.getKeyBefore?.(key) ?? null,
462
486
  },
463
487
  },
464
488
  activeDropState,
465
- () => ref()
489
+ () => ref(),
466
490
  );
467
491
  });
468
492
  const isRootDropTarget = createMemo(() => {
469
- return Boolean(dropState()?.target?.type === 'root');
493
+ return Boolean(dropState()?.target?.type === "root");
470
494
  });
471
- const dndRenderDropIndicator = createMemo(() => useRenderDropIndicator(local.dragAndDropHooks, dropState()));
472
- const dndDropIndicator = (index: number, position: 'before' | 'after' | 'on') => {
495
+ const dndRenderDropIndicator = createMemo(() =>
496
+ useRenderDropIndicator(local.dragAndDropHooks, dropState()),
497
+ );
498
+ const dndDropIndicator = (index: number, position: "before" | "after" | "on") => {
473
499
  const target = getDropTargetByIndex(index, position);
474
- if (!target || target.type !== 'item') return undefined;
500
+ if (!target || target.type !== "item") return undefined;
475
501
  return dndRenderDropIndicator()?.(target);
476
502
  };
477
503
  const persistedKeys = useDndPersistedKeys(
478
504
  { focusedKey: () => state.focusedKey },
479
505
  local.dragAndDropHooks,
480
506
  dropState(),
481
- state.collection
507
+ state.collection,
482
508
  );
483
509
  const virtualRange = createMemo(() => {
484
510
  if (!virtualizer || !parentCollectionRenderer?.isVirtualized) return null;
@@ -490,16 +516,26 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
490
516
  const dropTarget = dropState()?.target;
491
517
  const normalizedDropKey = getNormalizedDropTargetKey(dropTarget, state.collection);
492
518
  const focusedKey = state.focusedKey;
493
- const focusedIndex = focusedKey != null ? itemNodes.findIndex((node) => node.key === focusedKey) : -1;
519
+ const focusedIndex =
520
+ focusedKey != null ? itemNodes.findIndex((node) => node.key === focusedKey) : -1;
494
521
  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,
522
+ dropTarget?.type === "item" ? itemNodes.findIndex((node) => node.key === dropTarget.key) : -1,
523
+ normalizedDropKey != null
524
+ ? itemNodes.findIndex((node) => node.key === normalizedDropKey)
525
+ : -1,
526
+ dropTarget?.type === "item" ? -1 : focusedIndex,
498
527
  ].filter((index) => index >= 0);
499
- return mergePersistedKeysIntoVirtualRange(baseRange, persistedIndexes, stateProps.items.length, virtualizer, 80, {
500
- forceIncludeIndexes,
501
- forceIncludeMaxSpan: 320,
502
- });
528
+ return mergePersistedKeysIntoVirtualRange(
529
+ baseRange,
530
+ persistedIndexes,
531
+ stateProps.items.length,
532
+ virtualizer,
533
+ 80,
534
+ {
535
+ forceIncludeIndexes,
536
+ forceIncludeMaxSpan: 320,
537
+ },
538
+ );
503
539
  });
504
540
  createEffect(() => {
505
541
  if (!virtualizer || !parentCollectionRenderer?.isVirtualized) return;
@@ -514,7 +550,7 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
514
550
  if (!node) return target;
515
551
  return {
516
552
  ...target,
517
- key: typeof node.key === 'string' || typeof node.key === 'number' ? node.key : undefined,
553
+ key: typeof node.key === "string" || typeof node.key === "number" ? node.key : undefined,
518
554
  };
519
555
  });
520
556
  onCleanup(() => {
@@ -533,6 +569,7 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
533
569
  state,
534
570
  collection: collection(),
535
571
  isDisabled: ariaProps.isDisabled ?? false,
572
+ selectionBehavior: stateProps.selectionBehavior ?? "replace",
536
573
  dragAndDropHooks: local.dragAndDropHooks,
537
574
  dragState: dragState(),
538
575
  dropState: dropState(),
@@ -540,21 +577,27 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
540
577
  const collectionRenderer = createMemo<CollectionRendererContextValue<unknown>>(() => ({
541
578
  ...parentCollectionRenderer,
542
579
  renderItem: (item) => props.children(item as T),
543
- renderDropIndicator: (index: number, position: 'before' | 'after' | 'on') =>
544
- dndDropIndicator(index, position) ?? parentCollectionRenderer?.renderDropIndicator?.(index, position),
580
+ renderDropIndicator: (index: number, position: "before" | "after" | "on") =>
581
+ dndDropIndicator(index, position) ??
582
+ parentCollectionRenderer?.renderDropIndicator?.(index, position),
545
583
  }));
546
584
 
547
585
  return (
548
586
  <GridListContext.Provider value={contextValue() as unknown as GridListContextValue<object>}>
549
- <GridListStateContext.Provider value={state as unknown as GridState<object, GridCollection<object>>}>
587
+ <GridListStateContext.Provider
588
+ value={state as unknown as GridState<object, GridCollection<object>>}
589
+ >
550
590
  <CollectionRendererContext.Provider value={collectionRenderer()}>
551
591
  <div
552
- ref={setRef}
592
+ ref={(element) => {
593
+ setRef(element);
594
+ assignRef(local.ref, element);
595
+ }}
553
596
  {...mergeProps(
554
597
  domProps(),
555
598
  cleanGridProps(),
556
599
  cleanFocusProps(),
557
- (droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ?? {}
600
+ (droppableCollection()?.collectionProps as Record<string, unknown> | undefined) ?? {},
558
601
  )}
559
602
  class={renderProps.class()}
560
603
  style={renderProps.style()}
@@ -565,34 +608,47 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
565
608
  data-drop-target={isRootDropTarget() || undefined}
566
609
  >
567
610
  <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
- )}
611
+ {isEmpty() && local.renderEmptyState ? (
612
+ local.renderEmptyState()
613
+ ) : (
614
+ <>
615
+ {virtualRange()?.offsetTop ? (
616
+ <div
617
+ role="presentation"
618
+ aria-hidden="true"
619
+ style={{ height: `${virtualRange()!.offsetTop}px` }}
620
+ data-virtualizer-spacer="top"
621
+ />
622
+ ) : null}
623
+ <For each={visibleItems()}>
624
+ {(item, index) => {
625
+ const itemIndex = () => (virtualRange()?.start ?? 0) + index();
626
+ const beforeIndicator = () =>
627
+ collectionRenderer().renderDropIndicator?.(itemIndex(), "before");
628
+ const onIndicator = () =>
629
+ collectionRenderer().renderDropIndicator?.(itemIndex(), "on");
630
+ const afterIndicator = () =>
631
+ collectionRenderer().renderDropIndicator?.(itemIndex(), "after");
632
+ return (
633
+ <>
634
+ {beforeIndicator()}
635
+ {onIndicator()}
636
+ {props.children(item)}
637
+ {afterIndicator()}
638
+ </>
639
+ );
640
+ }}
641
+ </For>
642
+ {virtualRange()?.offsetBottom ? (
643
+ <div
644
+ role="presentation"
645
+ aria-hidden="true"
646
+ style={{ height: `${virtualRange()!.offsetBottom}px` }}
647
+ data-virtualizer-spacer="bottom"
648
+ />
649
+ ) : null}
650
+ </>
651
+ )}
596
652
  </SharedElementTransition>
597
653
  {local.hasMore && local.onLoadMore && (
598
654
  <GridListLoadMoreItem onLoadMore={local.onLoadMore} isLoading={local.isLoading} />
@@ -609,34 +665,31 @@ export function GridList<T extends object>(props: GridListProps<T>): JSX.Element
609
665
  */
610
666
  export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX.Element {
611
667
  const [local, domProps] = splitProps(props, [
612
- 'class',
613
- 'style',
614
- 'slot',
615
- 'id',
616
- 'item',
617
- 'textValue',
618
- 'onAction',
619
- 'children',
668
+ "class",
669
+ "style",
670
+ "slot",
671
+ "id",
672
+ "item",
673
+ "textValue",
674
+ "onAction",
675
+ "children",
676
+ "ref",
620
677
  ]);
621
678
 
622
- // Get state from context
623
679
  const context = useContext(GridListStateContext);
624
680
  if (!context) {
625
- throw new Error('GridListItem must be used within a GridList');
681
+ throw new Error("GridListItem must be used within a GridList");
626
682
  }
627
683
  const state = context as GridState<T, GridCollection<T>>;
628
684
  const listContext = useContext(GridListContext) as GridListContextValue<T> | null;
629
685
 
630
- // Create ref signal
631
686
  const [ref, setRef] = createSignal<HTMLDivElement | null>(null);
632
687
 
633
- // Find or create the item node
634
688
  const itemNode = createMemo(() => {
635
689
  const node = state.collection.getItem(local.id);
636
690
  if (!node) {
637
- // Create a simple node for the item
638
691
  return {
639
- type: 'item' as const,
692
+ type: "item" as const,
640
693
  key: local.id,
641
694
  value: local.item ?? null,
642
695
  textValue: local.textValue ?? String(local.id),
@@ -649,52 +702,50 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
649
702
  return node as GridNode<T>;
650
703
  });
651
704
 
652
- // Create item aria props
653
705
  const itemAria = createGridListItem<T, GridCollection<T>>(
654
706
  () => ({
655
707
  node: itemNode(),
656
708
  onAction: local.onAction,
709
+ selectionBehavior: listContext?.selectionBehavior ?? "replace",
657
710
  }),
658
711
  () => state,
659
- ref as Accessor<HTMLLIElement | null>
712
+ ref,
660
713
  );
661
714
  const isSelected = () => itemAria.isSelected;
662
715
  const isDisabled = () => itemAria.isDisabled;
663
716
  const isPressed = () => itemAria.isPressed;
664
717
 
665
- // Create hover
666
718
  const { isHovered, hoverProps } = createHover({
667
719
  get isDisabled() {
668
720
  return isDisabled();
669
721
  },
670
722
  });
671
723
 
672
- // Create focus ring
673
724
  const { isFocusVisible, focusProps } = createFocusRing();
674
725
 
675
- // Check if focused
676
726
  const isFocused = createMemo(() => state.focusedKey === local.id);
677
727
  const draggableItem = createMemo(() => {
678
- if (!listContext?.dragAndDropHooks?.useDraggableItem || !listContext.dragState) return undefined;
728
+ if (!listContext?.dragAndDropHooks?.useDraggableItem || !listContext.dragState)
729
+ return undefined;
679
730
  return listContext.dragAndDropHooks.useDraggableItem(
680
731
  {
681
732
  key: local.id as string | number,
682
733
  },
683
- listContext.dragState as Parameters<NonNullable<DragAndDropHooks<T>['useDraggableItem']>>[1]
734
+ listContext.dragState as Parameters<NonNullable<DragAndDropHooks<T>["useDraggableItem"]>>[1],
684
735
  );
685
736
  });
686
737
  const droppableItem = createMemo(() => {
687
- if (!listContext?.dragAndDropHooks?.useDroppableItem || !listContext.dropState) return undefined;
738
+ if (!listContext?.dragAndDropHooks?.useDroppableItem || !listContext.dropState)
739
+ return undefined;
688
740
  return listContext.dragAndDropHooks.useDroppableItem(
689
741
  {
690
742
  key: local.id as string | number,
691
743
  },
692
- listContext.dropState as Parameters<NonNullable<DragAndDropHooks<T>['useDroppableItem']>>[1],
693
- () => ref()
744
+ listContext.dropState as Parameters<NonNullable<DragAndDropHooks<T>["useDroppableItem"]>>[1],
745
+ () => ref(),
694
746
  );
695
747
  });
696
748
 
697
- // Render props values
698
749
  const renderValues = createMemo<GridListItemRenderProps>(() => ({
699
750
  isSelected: isSelected(),
700
751
  isFocused: isFocused(),
@@ -702,20 +753,20 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
702
753
  isPressed: isPressed(),
703
754
  isHovered: isHovered(),
704
755
  isDisabled: isDisabled(),
756
+ selectionMode: state.selectionMode,
757
+ selectionBehavior: listContext?.selectionBehavior ?? "replace",
705
758
  }));
706
759
 
707
- // Resolve render props
708
760
  const renderProps = useRenderProps(
709
761
  {
710
762
  children: props.children,
711
763
  class: local.class,
712
764
  style: local.style,
713
- defaultClassName: 'solidaria-GridList-item',
765
+ defaultClassName: "solidaria-GridList-item",
714
766
  },
715
- renderValues
767
+ renderValues,
716
768
  );
717
769
 
718
- // Remove ref from spread props
719
770
  const cleanRowProps = () => {
720
771
  const { ref: _ref1, ...rest } = itemAria.rowProps as Record<string, unknown>;
721
772
  return rest;
@@ -731,14 +782,17 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
731
782
 
732
783
  return (
733
784
  <div
734
- ref={setRef}
785
+ ref={(element) => {
786
+ setRef(element);
787
+ assignRef(local.ref, element);
788
+ }}
735
789
  {...domProps}
736
790
  {...mergeProps(
737
791
  cleanRowProps(),
738
792
  cleanHoverProps(),
739
793
  cleanFocusProps(),
740
794
  (draggableItem()?.dragProps as Record<string, unknown> | undefined) ?? {},
741
- (droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {}
795
+ (droppableItem()?.dropProps as Record<string, unknown> | undefined) ?? {},
742
796
  )}
743
797
  class={renderProps.class()}
744
798
  style={renderProps.style()}
@@ -759,24 +813,38 @@ export function GridListItem<T extends object>(props: GridListItemProps<T>): JSX
759
813
  /**
760
814
  * A checkbox for item selection in a grid list.
761
815
  */
762
- export function GridListSelectionCheckbox(props: { itemKey: Key }): JSX.Element {
816
+ export function GridListSelectionCheckbox(props: {
817
+ itemKey: Key;
818
+ class?: string;
819
+ style?: JSX.CSSProperties;
820
+ excludeFromTabOrder?: boolean;
821
+ "aria-label"?: string;
822
+ }): JSX.Element {
763
823
  const context = useContext(GridListStateContext);
764
824
  if (!context) {
765
- throw new Error('GridListSelectionCheckbox must be used within a GridList');
825
+ throw new Error("GridListSelectionCheckbox must be used within a GridList");
766
826
  }
767
827
 
768
828
  const state = context as GridState<object, GridCollection<object>>;
769
829
 
770
830
  const checkboxAria = createGridListSelectionCheckbox<object, GridCollection<object>>(
771
831
  () => ({ key: props.itemKey }),
772
- () => state
832
+ () => state,
773
833
  );
774
834
 
775
- return <input {...checkboxAria.checkboxProps} />;
835
+ return (
836
+ <input
837
+ {...checkboxAria.checkboxProps}
838
+ class={props.class}
839
+ style={props.style}
840
+ tabIndex={props.excludeFromTabOrder ? -1 : undefined}
841
+ aria-label={props["aria-label"] ?? "Select"}
842
+ />
843
+ );
776
844
  }
777
845
 
778
846
  export function GridListLoadMoreItem(props: GridListLoadMoreItemProps): JSX.Element {
779
- let ref: HTMLDivElement | undefined;
847
+ let sentinelRef: HTMLDivElement | undefined;
780
848
  const [isPending, setIsPending] = createSignal(false);
781
849
  const isLoading = () => !!props.isLoading || isPending();
782
850
 
@@ -791,46 +859,55 @@ export function GridListLoadMoreItem(props: GridListLoadMoreItemProps): JSX.Elem
791
859
  };
792
860
 
793
861
  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);
862
+ if (!sentinelRef || typeof IntersectionObserver !== "function") return;
863
+ const offset = props.scrollOffset ?? 1;
864
+ const margin = `0px 0px ${100 * offset}% 0px`;
865
+ const observer = new IntersectionObserver(
866
+ (entries) => {
867
+ if (entries[0]?.isIntersecting) {
868
+ void triggerLoadMore();
869
+ }
870
+ },
871
+ { rootMargin: margin },
872
+ );
873
+ observer.observe(sentinelRef);
801
874
  return () => observer.disconnect();
802
875
  });
803
876
 
804
877
  const renderProps = useRenderProps(
805
878
  {
806
- children: props.children ?? (() => (isLoading() ? 'Loading more...' : 'Load more')),
879
+ children: props.children ?? (() => (isLoading() ? "Loading more..." : "Load more")),
807
880
  class: props.class,
808
881
  style: props.style,
809
- defaultClassName: 'solidaria-GridList-loadMore',
882
+ defaultClassName: "solidaria-GridList-loadMore",
810
883
  },
811
- () => ({ isLoading: isLoading() })
884
+ () => ({ isLoading: isLoading() }),
812
885
  );
813
886
 
814
887
  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>
888
+ <>
889
+ <div style={{ position: "relative", width: 0, height: 0, overflow: "hidden" }} inert>
890
+ <div ref={sentinelRef} style={{ position: "absolute", height: "1px", width: "1px" }} />
891
+ </div>
892
+ <div
893
+ role="row"
894
+ tabIndex={0}
895
+ onFocus={() => {
896
+ void triggerLoadMore();
897
+ }}
898
+ class={renderProps.class()}
899
+ style={renderProps.style()}
900
+ data-loading={isLoading() || undefined}
901
+ >
902
+ {renderProps.renderChildren()}
903
+ </div>
904
+ </>
828
905
  );
829
906
  }
830
907
 
831
908
  export function GridListHeader(props: GridListHeaderProps): JSX.Element {
832
909
  return (
833
- <div class={props.class ?? 'solidaria-GridListHeader'} style={props.style}>
910
+ <div class={props.class ?? "solidaria-GridListHeader"} style={props.style}>
834
911
  {props.children}
835
912
  </div>
836
913
  );
@@ -843,7 +920,6 @@ export function GridListSection(props: GridListSectionProps): JSX.Element {
843
920
  return <Section {...props} />;
844
921
  }
845
922
 
846
- // Attach Item and SelectionCheckbox as static properties
847
923
  GridList.Item = GridListItem;
848
924
  GridList.SelectionCheckbox = GridListSelectionCheckbox;
849
925
  GridList.LoadMoreItem = GridListLoadMoreItem;