@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.
Files changed (194) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ActionBar.d.ts +71 -0
  3. package/dist/ActionBar.d.ts.map +1 -0
  4. package/dist/ActionGroup.d.ts +74 -0
  5. package/dist/ActionGroup.d.ts.map +1 -0
  6. package/dist/Alert.d.ts +70 -0
  7. package/dist/Alert.d.ts.map +1 -0
  8. package/dist/Breadcrumbs.d.ts +10 -2
  9. package/dist/Breadcrumbs.d.ts.map +1 -1
  10. package/dist/Button.d.ts +4 -0
  11. package/dist/Button.d.ts.map +1 -1
  12. package/dist/Calendar.d.ts +13 -0
  13. package/dist/Calendar.d.ts.map +1 -1
  14. package/dist/Checkbox.d.ts +2 -2
  15. package/dist/Checkbox.d.ts.map +1 -1
  16. package/dist/Collection.d.ts +125 -0
  17. package/dist/Collection.d.ts.map +1 -0
  18. package/dist/Color.d.ts +114 -2
  19. package/dist/Color.d.ts.map +1 -1
  20. package/dist/ColorEditor.d.ts +42 -0
  21. package/dist/ColorEditor.d.ts.map +1 -0
  22. package/dist/ComboBox.d.ts +64 -0
  23. package/dist/ComboBox.d.ts.map +1 -1
  24. package/dist/ContextualHelpTrigger.d.ts +40 -0
  25. package/dist/ContextualHelpTrigger.d.ts.map +1 -0
  26. package/dist/DateField.d.ts +27 -2
  27. package/dist/DateField.d.ts.map +1 -1
  28. package/dist/DatePicker.d.ts +67 -2
  29. package/dist/DatePicker.d.ts.map +1 -1
  30. package/dist/Dialog.d.ts.map +1 -1
  31. package/dist/Disclosure.d.ts +2 -0
  32. package/dist/Disclosure.d.ts.map +1 -1
  33. package/dist/DragAndDrop.d.ts +80 -0
  34. package/dist/DragAndDrop.d.ts.map +1 -0
  35. package/dist/DragPreview.d.ts +14 -0
  36. package/dist/DragPreview.d.ts.map +1 -0
  37. package/dist/DropZone.d.ts +27 -0
  38. package/dist/DropZone.d.ts.map +1 -0
  39. package/dist/FieldError.d.ts +23 -0
  40. package/dist/FieldError.d.ts.map +1 -0
  41. package/dist/FileTrigger.d.ts +26 -0
  42. package/dist/FileTrigger.d.ts.map +1 -0
  43. package/dist/Focusable.d.ts +27 -0
  44. package/dist/Focusable.d.ts.map +1 -0
  45. package/dist/Form.d.ts +27 -0
  46. package/dist/Form.d.ts.map +1 -0
  47. package/dist/GridList.d.ts +40 -1
  48. package/dist/GridList.d.ts.map +1 -1
  49. package/dist/Icon.d.ts +57 -0
  50. package/dist/Icon.d.ts.map +1 -0
  51. package/dist/Keyboard.d.ts +13 -0
  52. package/dist/Keyboard.d.ts.map +1 -0
  53. package/dist/Link.d.ts.map +1 -1
  54. package/dist/ListBox.d.ts +43 -1
  55. package/dist/ListBox.d.ts.map +1 -1
  56. package/dist/ListDropTargetDelegate.d.ts +38 -0
  57. package/dist/ListDropTargetDelegate.d.ts.map +1 -0
  58. package/dist/Menu.d.ts +20 -2
  59. package/dist/Menu.d.ts.map +1 -1
  60. package/dist/Meter.d.ts +2 -2
  61. package/dist/Meter.d.ts.map +1 -1
  62. package/dist/Modal.d.ts +2 -0
  63. package/dist/Modal.d.ts.map +1 -1
  64. package/dist/NumberField.d.ts +2 -0
  65. package/dist/NumberField.d.ts.map +1 -1
  66. package/dist/Popover.d.ts +4 -2
  67. package/dist/Popover.d.ts.map +1 -1
  68. package/dist/Pressable.d.ts +27 -0
  69. package/dist/Pressable.d.ts.map +1 -0
  70. package/dist/ProgressBar.d.ts +2 -2
  71. package/dist/ProgressBar.d.ts.map +1 -1
  72. package/dist/RadioGroup.d.ts.map +1 -1
  73. package/dist/RangeCalendar.d.ts +5 -0
  74. package/dist/RangeCalendar.d.ts.map +1 -1
  75. package/dist/RouterProvider.d.ts +75 -0
  76. package/dist/RouterProvider.d.ts.map +1 -0
  77. package/dist/SearchField.d.ts +2 -3
  78. package/dist/SearchField.d.ts.map +1 -1
  79. package/dist/Select.d.ts +11 -0
  80. package/dist/Select.d.ts.map +1 -1
  81. package/dist/SelectionIndicator.d.ts +30 -0
  82. package/dist/SelectionIndicator.d.ts.map +1 -0
  83. package/dist/SharedElementTransition.d.ts +39 -0
  84. package/dist/SharedElementTransition.d.ts.map +1 -0
  85. package/dist/Slider.d.ts +6 -3
  86. package/dist/Slider.d.ts.map +1 -1
  87. package/dist/Table.d.ts +39 -0
  88. package/dist/Table.d.ts.map +1 -1
  89. package/dist/Tabs.d.ts +4 -3
  90. package/dist/Tabs.d.ts.map +1 -1
  91. package/dist/TagGroup.d.ts +12 -2
  92. package/dist/TagGroup.d.ts.map +1 -1
  93. package/dist/Text.d.ts +10 -0
  94. package/dist/Text.d.ts.map +1 -0
  95. package/dist/TextField.d.ts +4 -0
  96. package/dist/TextField.d.ts.map +1 -1
  97. package/dist/TimeField.d.ts +26 -1
  98. package/dist/TimeField.d.ts.map +1 -1
  99. package/dist/Toast.d.ts.map +1 -1
  100. package/dist/ToggleButton.d.ts +30 -0
  101. package/dist/ToggleButton.d.ts.map +1 -0
  102. package/dist/ToggleButtonGroup.d.ts +33 -0
  103. package/dist/ToggleButtonGroup.d.ts.map +1 -0
  104. package/dist/Toolbar.d.ts.map +1 -1
  105. package/dist/Tooltip.d.ts +9 -0
  106. package/dist/Tooltip.d.ts.map +1 -1
  107. package/dist/Tree.d.ts +44 -2
  108. package/dist/Tree.d.ts.map +1 -1
  109. package/dist/Virtualizer.d.ts +61 -0
  110. package/dist/Virtualizer.d.ts.map +1 -0
  111. package/dist/VirtualizerLayouts.d.ts +82 -0
  112. package/dist/VirtualizerLayouts.d.ts.map +1 -0
  113. package/dist/VisuallyHidden.d.ts +3 -1
  114. package/dist/VisuallyHidden.d.ts.map +1 -1
  115. package/dist/contexts.d.ts +1 -0
  116. package/dist/contexts.d.ts.map +1 -1
  117. package/dist/index.d.ts +57 -25
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +13961 -5946
  120. package/dist/index.js.map +1 -7
  121. package/dist/index.ssr.js +9612 -2401
  122. package/dist/index.ssr.js.map +1 -7
  123. package/dist/useDragAndDrop.d.ts +93 -0
  124. package/dist/useDragAndDrop.d.ts.map +1 -0
  125. package/dist/utils.d.ts +7 -1
  126. package/dist/utils.d.ts.map +1 -1
  127. package/dist/virtualizer/Layout.d.ts +79 -0
  128. package/dist/virtualizer/Layout.d.ts.map +1 -0
  129. package/package.json +8 -6
  130. package/src/ActionBar.tsx +248 -0
  131. package/src/ActionGroup.tsx +285 -0
  132. package/src/Alert.tsx +177 -0
  133. package/src/Autocomplete.tsx +1 -1
  134. package/src/Breadcrumbs.tsx +103 -17
  135. package/src/Button.tsx +65 -21
  136. package/src/Calendar.tsx +179 -53
  137. package/src/Checkbox.tsx +1 -2
  138. package/src/Collection.tsx +341 -0
  139. package/src/Color.tsx +652 -34
  140. package/src/ColorEditor.tsx +231 -0
  141. package/src/ComboBox.tsx +315 -81
  142. package/src/ContextualHelpTrigger.tsx +183 -0
  143. package/src/DateField.tsx +93 -19
  144. package/src/DatePicker.tsx +495 -25
  145. package/src/Dialog.tsx +40 -9
  146. package/src/Disclosure.tsx +33 -27
  147. package/src/DragAndDrop.tsx +334 -0
  148. package/src/DragPreview.tsx +45 -0
  149. package/src/DropZone.tsx +213 -0
  150. package/src/FieldError.tsx +67 -0
  151. package/src/FileTrigger.tsx +83 -0
  152. package/src/Focusable.tsx +106 -0
  153. package/src/Form.tsx +85 -0
  154. package/src/GridList.tsx +379 -41
  155. package/src/Icon.tsx +154 -0
  156. package/src/Keyboard.tsx +26 -0
  157. package/src/Link.tsx +14 -1
  158. package/src/ListBox.tsx +484 -33
  159. package/src/ListDropTargetDelegate.ts +282 -0
  160. package/src/Menu.tsx +388 -35
  161. package/src/Meter.tsx +7 -3
  162. package/src/Modal.tsx +32 -4
  163. package/src/NumberField.tsx +163 -43
  164. package/src/Popover.tsx +136 -180
  165. package/src/Pressable.tsx +108 -0
  166. package/src/ProgressBar.tsx +7 -3
  167. package/src/RadioGroup.tsx +35 -25
  168. package/src/RangeCalendar.tsx +100 -68
  169. package/src/RouterProvider.tsx +240 -0
  170. package/src/SearchField.tsx +142 -34
  171. package/src/Select.tsx +221 -73
  172. package/src/SelectionIndicator.tsx +105 -0
  173. package/src/SharedElementTransition.tsx +258 -0
  174. package/src/Slider.tsx +16 -6
  175. package/src/Table.tsx +417 -57
  176. package/src/Tabs.tsx +68 -35
  177. package/src/TagGroup.tsx +121 -36
  178. package/src/Text.tsx +18 -0
  179. package/src/TextField.tsx +25 -8
  180. package/src/TimeField.tsx +101 -151
  181. package/src/Toast.tsx +108 -14
  182. package/src/ToggleButton.tsx +159 -0
  183. package/src/ToggleButtonGroup.tsx +136 -0
  184. package/src/Toolbar.tsx +14 -8
  185. package/src/Tooltip.tsx +108 -19
  186. package/src/Tree.tsx +1143 -87
  187. package/src/Virtualizer.tsx +702 -0
  188. package/src/VirtualizerLayouts.ts +265 -0
  189. package/src/VisuallyHidden.tsx +15 -21
  190. package/src/contexts.ts +1 -0
  191. package/src/index.ts +1057 -620
  192. package/src/useDragAndDrop.ts +351 -0
  193. package/src/utils.tsx +37 -3
  194. package/src/virtualizer/Layout.ts +200 -0
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Drag and drop compatibility hook for collection components.
3
+ *
4
+ * Mirrors RAC's `useDragAndDrop` shape while delegating to Solid
5
+ * state and aria primitives.
6
+ */
7
+
8
+ import type { Accessor, JSX } from 'solid-js';
9
+ import { DragPreview } from './DragPreview';
10
+ import { ListDropTargetDelegate } from './ListDropTargetDelegate';
11
+ import {
12
+ createDraggableCollection,
13
+ createDraggableItem,
14
+ createDroppableCollection,
15
+ createDroppableItem,
16
+ getGlobalDraggingCollectionRef,
17
+ getGlobalDraggingKeys,
18
+ type DraggableCollectionOptions,
19
+ type DraggableCollectionAria,
20
+ type DraggableItemOptions,
21
+ type DraggableItemAria,
22
+ type DroppableCollectionOptions,
23
+ type DroppableCollectionAria,
24
+ type DroppableItemOptions,
25
+ type DroppableItemAria,
26
+ type DropTargetDelegate as AriaDropTargetDelegate,
27
+ } from '@proyecto-viviana/solidaria';
28
+ import {
29
+ createDraggableCollectionState,
30
+ createDroppableCollectionState,
31
+ type DragItem,
32
+ type DragPreviewRenderer,
33
+ type DraggableCollectionProps,
34
+ type DraggableCollectionState,
35
+ type DraggableCollectionStateOptions,
36
+ type DropTarget,
37
+ type DroppableCollectionProps,
38
+ type DroppableCollectionState,
39
+ type DroppableCollectionStateOptions,
40
+ } from '@proyecto-viviana/solid-stately';
41
+
42
+ interface DraggableCollectionStateOpts<T = object>
43
+ extends Omit<DraggableCollectionStateOptions<T>, 'getItems'> {
44
+ items?: T[];
45
+ }
46
+
47
+ interface DragHooks<T = object> {
48
+ useDraggableCollectionState?: (props: DraggableCollectionStateOpts<T>) => DraggableCollectionState;
49
+ useDraggableCollection?: (
50
+ props: Omit<DraggableCollectionOptions, 'ref'>,
51
+ state: DraggableCollectionState,
52
+ ref: Accessor<HTMLElement | null>
53
+ ) => DraggableCollectionAria;
54
+ useDraggableItem?: (props: DraggableItemOptions, state: DraggableCollectionState) => DraggableItemAria;
55
+ DragPreview?: typeof DragPreview;
56
+ renderDragPreview?: DragAndDropOptions<T>['renderDragPreview'];
57
+ isVirtualDragging?: () => boolean;
58
+ }
59
+
60
+ interface DropHooks {
61
+ useDroppableCollectionState?: (props: DroppableCollectionStateOptions) => DroppableCollectionState;
62
+ useDroppableCollection?: (
63
+ props: Omit<DroppableCollectionOptions, 'ref'>,
64
+ state: DroppableCollectionState,
65
+ ref: Accessor<HTMLElement | null>
66
+ ) => DroppableCollectionAria;
67
+ useDroppableItem?: (
68
+ options: Omit<DroppableItemOptions, 'ref'>,
69
+ state: DroppableCollectionState,
70
+ ref: Accessor<HTMLElement | null>
71
+ ) => DroppableItemAria;
72
+ useDropIndicator?: (
73
+ props: { target: DropTarget },
74
+ state: DroppableCollectionState,
75
+ ref: Accessor<HTMLElement | null>
76
+ ) => {
77
+ dropIndicatorProps: JSX.HTMLAttributes<HTMLElement>;
78
+ isDropTarget: boolean;
79
+ isHidden: boolean;
80
+ };
81
+ renderDropIndicator?: (target: DropTarget) => JSX.Element;
82
+ dropTargetDelegate?: AriaDropTargetDelegate;
83
+ ListDropTargetDelegate?: typeof ListDropTargetDelegate;
84
+ }
85
+
86
+ export type DragAndDropHooks<T = object> = DragHooks<T> & DropHooks;
87
+
88
+ export interface DragAndDrop<T = object> {
89
+ /** Drag and drop hooks for the collection element. */
90
+ dragAndDropHooks: DragAndDropHooks<T>;
91
+ }
92
+
93
+ export interface DragAndDropOptions<T = object>
94
+ extends Partial<Omit<DraggableCollectionProps<T>, 'preview'>>,
95
+ Partial<DroppableCollectionProps> {
96
+ /**
97
+ * Optional keyboard delegate forwarded to the collection droppable hook.
98
+ */
99
+ keyboardDelegate?: DroppableCollectionOptions['keyboardDelegate'];
100
+ /**
101
+ * Optional keydown handler composed with collection droppable keyboard behavior.
102
+ */
103
+ onKeyDown?: DroppableCollectionOptions['onKeyDown'];
104
+ /**
105
+ * A function that returns the items being dragged.
106
+ * If omitted, draggable hooks are not added.
107
+ */
108
+ getItems?: (keys: Set<string | number>, items: T[]) => DragItem[];
109
+ /**
110
+ * Optional custom drag preview renderer.
111
+ */
112
+ renderDragPreview?: (items: DragItem[]) => JSX.Element | { element: JSX.Element; x: number; y: number };
113
+ /**
114
+ * Optional drop indicator renderer for collection components.
115
+ */
116
+ renderDropIndicator?: (target: DropTarget) => JSX.Element;
117
+ /**
118
+ * Optional custom drop target delegate.
119
+ */
120
+ dropTargetDelegate?: AriaDropTargetDelegate;
121
+ /**
122
+ * Optional drag preview ref.
123
+ */
124
+ preview?: { current: DragPreviewRenderer | null };
125
+ /**
126
+ * Optional item snapshot used by draggable state `getItems`.
127
+ */
128
+ items?: T[];
129
+ /**
130
+ * Disable both drag and drop behavior.
131
+ */
132
+ isDisabled?: boolean;
133
+ }
134
+
135
+ /**
136
+ * Provides hooks to enable drag-and-drop behavior for collection components.
137
+ */
138
+ export function useDragAndDrop<T = object>(options: DragAndDropOptions<T> = {}): DragAndDrop<T> {
139
+ const {
140
+ getItems,
141
+ onDrop,
142
+ onInsert,
143
+ onItemDrop,
144
+ onReorder,
145
+ onMove,
146
+ onRootDrop,
147
+ renderDragPreview,
148
+ renderDropIndicator,
149
+ dropTargetDelegate,
150
+ } = options;
151
+
152
+ const isDraggable = typeof getItems === 'function';
153
+ const isDroppable = Boolean(onDrop || onInsert || onItemDrop || onReorder || onMove || onRootDrop);
154
+
155
+ const hooks: DragAndDropHooks<T> = {};
156
+ const hasDom = typeof HTMLElement !== 'undefined';
157
+ const isElementNode = (value: unknown): value is HTMLElement => {
158
+ if (!value) return false;
159
+ if (hasDom && value instanceof HTMLElement) return true;
160
+ return typeof value === 'object' && (value as { nodeType?: number }).nodeType === 1;
161
+ };
162
+ const resolvedPreview = options.preview ?? (
163
+ renderDragPreview
164
+ ? {
165
+ current: (items: DragItem[], callback: (node: HTMLElement | null, x?: number, y?: number) => void) => {
166
+ const rendered = renderDragPreview(items);
167
+ if (!rendered) {
168
+ callback(null);
169
+ return;
170
+ }
171
+ if (
172
+ typeof rendered === 'object' &&
173
+ rendered !== null &&
174
+ 'element' in rendered
175
+ ) {
176
+ const previewValue = rendered as { element: unknown; x?: number; y?: number };
177
+ callback(isElementNode(previewValue.element) ? previewValue.element : null, previewValue.x, previewValue.y);
178
+ return;
179
+ }
180
+ callback(isElementNode(rendered) ? rendered : null);
181
+ },
182
+ }
183
+ : undefined
184
+ );
185
+
186
+ if (isDraggable && getItems) {
187
+ hooks.useDraggableCollectionState = (props: DraggableCollectionStateOpts<T>) => {
188
+ return createDraggableCollectionState<T>(() => ({
189
+ onDragStart: options.onDragStart ?? props.onDragStart,
190
+ onDragMove: options.onDragMove ?? props.onDragMove,
191
+ onDragEnd: options.onDragEnd ?? props.onDragEnd,
192
+ getAllowedDropOperations: options.getAllowedDropOperations ?? props.getAllowedDropOperations,
193
+ isDisabled: options.isDisabled ?? props.isDisabled,
194
+ preview: resolvedPreview ?? props.preview,
195
+ getItems: (keys) => {
196
+ const sourceItems = props.items ?? options.items ?? [];
197
+ return getItems(keys, sourceItems);
198
+ },
199
+ }));
200
+ };
201
+ hooks.useDraggableCollection = (
202
+ props: Omit<DraggableCollectionOptions, 'ref'>,
203
+ state: DraggableCollectionState,
204
+ ref: Accessor<HTMLElement | null>
205
+ ) => createDraggableCollection({ ...props, ref }, state);
206
+ hooks.useDraggableItem = (props, state) => createDraggableItem(() => props, state);
207
+ hooks.DragPreview = DragPreview;
208
+ hooks.renderDragPreview = renderDragPreview;
209
+ hooks.isVirtualDragging = () =>
210
+ getGlobalDraggingCollectionRef() != null || getGlobalDraggingKeys().size > 0;
211
+ }
212
+
213
+ if (isDroppable) {
214
+ hooks.useDroppableCollectionState = (props: DroppableCollectionStateOptions) => {
215
+ return createDroppableCollectionState(() => ({
216
+ acceptedDragTypes: options.acceptedDragTypes ?? props.acceptedDragTypes,
217
+ getDropOperation: options.getDropOperation ?? props.getDropOperation,
218
+ onDropEnter: options.onDropEnter ?? props.onDropEnter,
219
+ onDropActivate: options.onDropActivate ?? props.onDropActivate,
220
+ onDropExit: options.onDropExit ?? props.onDropExit,
221
+ onDrop: options.onDrop ?? props.onDrop,
222
+ onInsert: options.onInsert ?? props.onInsert,
223
+ onRootDrop: options.onRootDrop ?? props.onRootDrop,
224
+ onItemDrop: options.onItemDrop ?? props.onItemDrop,
225
+ onReorder: options.onReorder ?? props.onReorder,
226
+ onMove: options.onMove ?? props.onMove,
227
+ shouldAcceptItemDrop: options.shouldAcceptItemDrop ?? props.shouldAcceptItemDrop,
228
+ isDisabled: options.isDisabled ?? props.isDisabled,
229
+ }));
230
+ };
231
+ hooks.useDroppableCollection = (
232
+ props: Omit<DroppableCollectionOptions, 'ref'>,
233
+ state: DroppableCollectionState,
234
+ ref: Accessor<HTMLElement | null>
235
+ ) => {
236
+ const acceptedDragTypes = (options.acceptedDragTypes ?? props.acceptedDragTypes);
237
+ const normalizedAcceptedDragTypes = acceptedDragTypes === 'all'
238
+ ? 'all'
239
+ : acceptedDragTypes?.filter((type): type is string | symbol =>
240
+ typeof type === 'string' || typeof type === 'symbol');
241
+ return (
242
+ createDroppableCollection(
243
+ () => ({
244
+ ref,
245
+ dropTargetDelegate: options.dropTargetDelegate ?? props.dropTargetDelegate,
246
+ keyboardDelegate: options.keyboardDelegate ?? props.keyboardDelegate,
247
+ onKeyDown: options.onKeyDown ?? props.onKeyDown,
248
+ acceptedDragTypes: normalizedAcceptedDragTypes,
249
+ isDisabled: options.isDisabled ?? props.isDisabled,
250
+ onDropActivate: (e) => {
251
+ (options.onDropActivate ?? props.onDropActivate)?.({
252
+ type: 'dropactivate',
253
+ target: e.target,
254
+ x: e.x,
255
+ y: e.y,
256
+ });
257
+ },
258
+ onDrop: (e) => {
259
+ (options.onDrop ?? props.onDrop)?.({
260
+ type: 'drop',
261
+ target: e.target,
262
+ x: e.x,
263
+ y: e.y,
264
+ items: e.items,
265
+ dropOperation: e.dropOperation,
266
+ });
267
+ },
268
+ onRootDrop: options.onRootDrop ?? props.onRootDrop,
269
+ onItemDrop: (e) => {
270
+ if (e.target.type === 'item') {
271
+ (options.onItemDrop ?? props.onItemDrop)?.({
272
+ items: e.items,
273
+ target: e.target,
274
+ dropOperation: e.dropOperation,
275
+ isInternal: e.isInternal,
276
+ });
277
+ }
278
+ },
279
+ onInsert: (e) => {
280
+ if (e.target.type === 'item') {
281
+ (options.onInsert ?? props.onInsert)?.({
282
+ items: e.items,
283
+ target: e.target,
284
+ dropOperation: e.dropOperation,
285
+ });
286
+ }
287
+ },
288
+ onReorder: (e) => {
289
+ if (e.target.type === 'item') {
290
+ (options.onReorder ?? props.onReorder)?.({
291
+ keys: e.keys,
292
+ target: e.target,
293
+ dropOperation: e.dropOperation,
294
+ });
295
+ }
296
+ },
297
+ onMove: (e) => {
298
+ if (e.target.type === 'item') {
299
+ (options.onMove ?? props.onMove)?.({
300
+ keys: e.keys,
301
+ target: e.target,
302
+ dropOperation: e.dropOperation,
303
+ });
304
+ }
305
+ },
306
+ }),
307
+ state
308
+ ));
309
+ };
310
+ hooks.useDroppableItem = (
311
+ props: Omit<DroppableItemOptions, 'ref'>,
312
+ state: DroppableCollectionState,
313
+ ref: Accessor<HTMLElement | null>
314
+ ) => createDroppableItem(() => ({ ...props, ref }), state);
315
+ hooks.useDropIndicator = (
316
+ props: { target: DropTarget },
317
+ state: DroppableCollectionState,
318
+ _ref: Accessor<HTMLElement | null>
319
+ ) => {
320
+ const target = props.target;
321
+ const activeTarget = state.target;
322
+ const isDropTarget =
323
+ activeTarget?.type === target.type &&
324
+ (target.type === 'root'
325
+ || (
326
+ activeTarget.type === 'item' &&
327
+ target.type === 'item' &&
328
+ activeTarget.key === target.key &&
329
+ activeTarget.dropPosition === target.dropPosition
330
+ ));
331
+ return {
332
+ dropIndicatorProps: {
333
+ role: 'option',
334
+ 'aria-disabled': true,
335
+ 'aria-hidden': isDropTarget ? undefined : 'true',
336
+ tabIndex: -1,
337
+ 'data-drop-target': isDropTarget ? '' : undefined,
338
+ },
339
+ isDropTarget,
340
+ isHidden: !isDropTarget,
341
+ };
342
+ };
343
+ hooks.renderDropIndicator = renderDropIndicator;
344
+ hooks.dropTargetDelegate = dropTargetDelegate;
345
+ hooks.ListDropTargetDelegate = ListDropTargetDelegate;
346
+ }
347
+
348
+ return {
349
+ dragAndDropHooks: hooks,
350
+ };
351
+ }
package/src/utils.tsx CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  type JSX,
8
8
  type Accessor,
9
9
  type FlowComponent,
10
+ type ParentComponent,
10
11
  createContext,
11
12
  useContext,
12
13
  createMemo,
@@ -55,6 +56,8 @@ export interface SlotProps {
55
56
  slot?: string;
56
57
  }
57
58
 
59
+ export const DEFAULT_SLOT = 'default';
60
+
58
61
  // ============================================
59
62
  // RENDER PROPS
60
63
  // ============================================
@@ -97,7 +100,9 @@ export function useRenderProps<T extends object>(
97
100
  props: RenderPropsBase<T> & { defaultClassName?: string },
98
101
  values: Accessor<T>
99
102
  ): RenderPropsResult<T> {
100
- const { children, class: className, style, defaultClassName = '' } = props;
103
+ // Don't destructure children access lazily to avoid eager evaluation
104
+ // that would trigger child component creation before context providers mount.
105
+ const { class: className, style, defaultClassName = '' } = props;
101
106
 
102
107
  // Compute class and style eagerly (they don't depend on context)
103
108
  const computedClass = createMemo(() => {
@@ -115,21 +120,35 @@ export function useRenderProps<T extends object>(
115
120
  });
116
121
 
117
122
  // Return object with explicit function for rendering children
118
- // This avoids the getter pattern that causes SSR issues
123
+ // Children are accessed lazily during render (inside context providers)
119
124
  return {
120
125
  class: computedClass,
121
126
  style: computedStyle,
122
127
  renderChildren: () => {
123
128
  const currentValues = values();
129
+ const children = props.children;
124
130
  return typeof children === 'function'
125
131
  ? children(currentValues)
126
132
  : children;
127
133
  },
128
- children,
134
+ get children() { return props.children; },
129
135
  values,
130
136
  };
131
137
  }
132
138
 
139
+ export function composeRenderProps<T extends object>(
140
+ base: RenderPropsBase<T> | undefined,
141
+ override: RenderPropsBase<T> | undefined
142
+ ): RenderPropsBase<T> {
143
+ if (!base) return override ?? {};
144
+ if (!override) return base;
145
+ return {
146
+ children: override.children ?? base.children,
147
+ class: override.class ?? base.class,
148
+ style: override.style ?? base.style,
149
+ };
150
+ }
151
+
133
152
  // ============================================
134
153
  // CONTEXT UTILITIES
135
154
  // ============================================
@@ -153,6 +172,21 @@ export function useSlottedContext<T>(context: ReturnType<typeof createContext<T
153
172
  return useContext(context);
154
173
  }
155
174
 
175
+ export function useContextProps<TProps extends object, TRef>(
176
+ props: TProps,
177
+ ref: TRef,
178
+ context?: ContextValue<Partial<TProps>>
179
+ ): [TProps, TRef] {
180
+ if (!context) return [props, ref];
181
+ return [{ ...(context as TProps), ...props }, ref];
182
+ }
183
+
184
+ export const Provider: ParentComponent<{
185
+ values: Array<[ReturnType<typeof createContext<unknown>>, unknown]>;
186
+ }> = (props) => {
187
+ return props.children;
188
+ };
189
+
156
190
  // ============================================
157
191
  // DATA ATTRIBUTES
158
192
  // ============================================
@@ -0,0 +1,200 @@
1
+ import type { Key } from '@proyecto-viviana/solid-stately';
2
+
3
+ export interface InvalidationContext<O = unknown> {
4
+ contentChanged?: boolean;
5
+ offsetChanged?: boolean;
6
+ sizeChanged?: boolean;
7
+ itemSizeChanged?: boolean;
8
+ layoutOptionsChanged?: boolean;
9
+ layoutOptions?: O;
10
+ }
11
+
12
+ export class Point {
13
+ constructor(public x = 0, public y = 0) {}
14
+ copy(): Point {
15
+ return new Point(this.x, this.y);
16
+ }
17
+ equals(point: Point): boolean {
18
+ return this.x === point.x && this.y === point.y;
19
+ }
20
+ isOrigin(): boolean {
21
+ return this.x === 0 && this.y === 0;
22
+ }
23
+ }
24
+
25
+ export class Size {
26
+ width: number;
27
+ height: number;
28
+
29
+ constructor(width = 0, height = 0) {
30
+ this.width = Math.max(0, width);
31
+ this.height = Math.max(0, height);
32
+ }
33
+
34
+ copy(): Size {
35
+ return new Size(this.width, this.height);
36
+ }
37
+
38
+ equals(other: Size): boolean {
39
+ return this.width === other.width && this.height === other.height;
40
+ }
41
+
42
+ get area(): number {
43
+ return this.width * this.height;
44
+ }
45
+ }
46
+
47
+ export class Rect {
48
+ constructor(
49
+ public x = 0,
50
+ public y = 0,
51
+ public width = 0,
52
+ public height = 0
53
+ ) {}
54
+
55
+ get maxX(): number {
56
+ return this.x + this.width;
57
+ }
58
+
59
+ get maxY(): number {
60
+ return this.y + this.height;
61
+ }
62
+
63
+ get area(): number {
64
+ return this.width * this.height;
65
+ }
66
+
67
+ get topLeft(): Point {
68
+ return new Point(this.x, this.y);
69
+ }
70
+
71
+ get topRight(): Point {
72
+ return new Point(this.maxX, this.y);
73
+ }
74
+
75
+ get bottomLeft(): Point {
76
+ return new Point(this.x, this.maxY);
77
+ }
78
+
79
+ get bottomRight(): Point {
80
+ return new Point(this.maxX, this.maxY);
81
+ }
82
+
83
+ intersects(rect: Rect): boolean {
84
+ return (
85
+ this.x <= rect.maxX &&
86
+ rect.x <= this.maxX &&
87
+ this.y <= rect.maxY &&
88
+ rect.y <= this.maxY
89
+ );
90
+ }
91
+
92
+ containsRect(rect: Rect): boolean {
93
+ return (
94
+ this.x <= rect.x &&
95
+ this.y <= rect.y &&
96
+ this.maxX >= rect.maxX &&
97
+ this.maxY >= rect.maxY
98
+ );
99
+ }
100
+
101
+ containsPoint(point: Point): boolean {
102
+ return (
103
+ this.x <= point.x &&
104
+ this.y <= point.y &&
105
+ this.maxX >= point.x &&
106
+ this.maxY >= point.y
107
+ );
108
+ }
109
+
110
+ union(other: Rect): Rect {
111
+ const x = Math.min(this.x, other.x);
112
+ const y = Math.min(this.y, other.y);
113
+ const width = Math.max(this.maxX, other.maxX) - x;
114
+ const height = Math.max(this.maxY, other.maxY) - y;
115
+ return new Rect(x, y, width, height);
116
+ }
117
+
118
+ intersection(other: Rect): Rect {
119
+ if (!this.intersects(other)) {
120
+ return new Rect(0, 0, 0, 0);
121
+ }
122
+ const x = Math.max(this.x, other.x);
123
+ const y = Math.max(this.y, other.y);
124
+ const width = Math.min(this.maxX, other.maxX) - x;
125
+ const height = Math.min(this.maxY, other.maxY) - y;
126
+ return new Rect(x, y, width, height);
127
+ }
128
+
129
+ copy(): Rect {
130
+ return new Rect(this.x, this.y, this.width, this.height);
131
+ }
132
+
133
+ equals(rect: Rect): boolean {
134
+ return (
135
+ this.x === rect.x &&
136
+ this.y === rect.y &&
137
+ this.width === rect.width &&
138
+ this.height === rect.height
139
+ );
140
+ }
141
+ }
142
+
143
+ export class LayoutInfo {
144
+ parentKey: Key | null = null;
145
+ content: unknown = null;
146
+ estimatedSize = false;
147
+ isSticky = false;
148
+ opacity = 1;
149
+ transform: string | null = null;
150
+ zIndex = 0;
151
+ allowOverflow = false;
152
+
153
+ constructor(public type: string, public key: Key, public rect: Rect) {}
154
+
155
+ copy(): LayoutInfo {
156
+ const copy = new LayoutInfo(this.type, this.key, this.rect.copy());
157
+ copy.parentKey = this.parentKey;
158
+ copy.content = this.content;
159
+ copy.estimatedSize = this.estimatedSize;
160
+ copy.isSticky = this.isSticky;
161
+ copy.opacity = this.opacity;
162
+ copy.transform = this.transform;
163
+ copy.zIndex = this.zIndex;
164
+ copy.allowOverflow = this.allowOverflow;
165
+ return copy;
166
+ }
167
+ }
168
+
169
+ interface VirtualizerLike {
170
+ visibleRect: Rect;
171
+ }
172
+
173
+ export abstract class Layout<T extends object = object, O = unknown> {
174
+ virtualizer: VirtualizerLike | null = null;
175
+
176
+ abstract getVisibleLayoutInfos(rect: Rect): LayoutInfo[];
177
+ abstract getLayoutInfo(key: Key): LayoutInfo | null;
178
+ abstract getContentSize(): Size;
179
+
180
+ shouldInvalidate(newRect: Rect, oldRect: Rect): boolean {
181
+ return newRect.width !== oldRect.width || newRect.height !== oldRect.height;
182
+ }
183
+
184
+ shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean {
185
+ return newOptions !== oldOptions;
186
+ }
187
+
188
+ update(_context: InvalidationContext<O>): void {}
189
+
190
+ updateItemSize?(_key: Key, _size: Size): boolean;
191
+ getDropTargetLayoutInfo?(_target: unknown): LayoutInfo | null;
192
+
193
+ protected getItemRect(key: Key): Rect | null {
194
+ return this.getLayoutInfo(key)?.rect ?? null;
195
+ }
196
+
197
+ getVisibleRect(): Rect {
198
+ return this.virtualizer?.visibleRect ?? new Rect();
199
+ }
200
+ }