@proyecto-viviana/solidaria-components 0.2.5 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/ActionBar.d.ts +71 -0
- package/dist/ActionBar.d.ts.map +1 -0
- package/dist/ActionGroup.d.ts +74 -0
- package/dist/ActionGroup.d.ts.map +1 -0
- package/dist/Alert.d.ts +70 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Breadcrumbs.d.ts +10 -2
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Button.d.ts +4 -0
- package/dist/Button.d.ts.map +1 -1
- package/dist/Calendar.d.ts +13 -0
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +2 -2
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Collection.d.ts +125 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Color.d.ts +114 -2
- package/dist/Color.d.ts.map +1 -1
- package/dist/ColorEditor.d.ts +42 -0
- package/dist/ColorEditor.d.ts.map +1 -0
- package/dist/ComboBox.d.ts +64 -0
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ContextualHelpTrigger.d.ts +40 -0
- package/dist/ContextualHelpTrigger.d.ts.map +1 -0
- package/dist/DateField.d.ts +27 -2
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DatePicker.d.ts +67 -2
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Disclosure.d.ts +2 -0
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/DragAndDrop.d.ts +80 -0
- package/dist/DragAndDrop.d.ts.map +1 -0
- package/dist/DragPreview.d.ts +14 -0
- package/dist/DragPreview.d.ts.map +1 -0
- package/dist/DropZone.d.ts +27 -0
- package/dist/DropZone.d.ts.map +1 -0
- package/dist/FieldError.d.ts +23 -0
- package/dist/FieldError.d.ts.map +1 -0
- package/dist/FileTrigger.d.ts +26 -0
- package/dist/FileTrigger.d.ts.map +1 -0
- package/dist/Focusable.d.ts +27 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Form.d.ts +27 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/GridList.d.ts +40 -1
- package/dist/GridList.d.ts.map +1 -1
- package/dist/Icon.d.ts +57 -0
- package/dist/Icon.d.ts.map +1 -0
- package/dist/Keyboard.d.ts +13 -0
- package/dist/Keyboard.d.ts.map +1 -0
- package/dist/Link.d.ts.map +1 -1
- package/dist/ListBox.d.ts +43 -1
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListDropTargetDelegate.d.ts +38 -0
- package/dist/ListDropTargetDelegate.d.ts.map +1 -0
- package/dist/Menu.d.ts +20 -2
- package/dist/Menu.d.ts.map +1 -1
- package/dist/Meter.d.ts +2 -2
- package/dist/Meter.d.ts.map +1 -1
- package/dist/Modal.d.ts +2 -0
- package/dist/Modal.d.ts.map +1 -1
- package/dist/NumberField.d.ts +2 -0
- package/dist/NumberField.d.ts.map +1 -1
- package/dist/Popover.d.ts +4 -2
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Pressable.d.ts +27 -0
- package/dist/Pressable.d.ts.map +1 -0
- package/dist/ProgressBar.d.ts +2 -2
- package/dist/ProgressBar.d.ts.map +1 -1
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RangeCalendar.d.ts +5 -0
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RouterProvider.d.ts +75 -0
- package/dist/RouterProvider.d.ts.map +1 -0
- package/dist/SearchField.d.ts +2 -3
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/Select.d.ts +11 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/SelectionIndicator.d.ts +30 -0
- package/dist/SelectionIndicator.d.ts.map +1 -0
- package/dist/SharedElementTransition.d.ts +39 -0
- package/dist/SharedElementTransition.d.ts.map +1 -0
- package/dist/Slider.d.ts +6 -3
- package/dist/Slider.d.ts.map +1 -1
- package/dist/Table.d.ts +39 -0
- package/dist/Table.d.ts.map +1 -1
- package/dist/Tabs.d.ts +4 -3
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TagGroup.d.ts +12 -2
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/Text.d.ts +10 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/TextField.d.ts +4 -0
- package/dist/TextField.d.ts.map +1 -1
- package/dist/TimeField.d.ts +26 -1
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/Toast.d.ts.map +1 -1
- package/dist/ToggleButton.d.ts +30 -0
- package/dist/ToggleButton.d.ts.map +1 -0
- package/dist/ToggleButtonGroup.d.ts +33 -0
- package/dist/ToggleButtonGroup.d.ts.map +1 -0
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Tooltip.d.ts +9 -0
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tree.d.ts +44 -2
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Virtualizer.d.ts +61 -0
- package/dist/Virtualizer.d.ts.map +1 -0
- package/dist/VirtualizerLayouts.d.ts +82 -0
- package/dist/VirtualizerLayouts.d.ts.map +1 -0
- package/dist/VisuallyHidden.d.ts +3 -1
- package/dist/VisuallyHidden.d.ts.map +1 -1
- package/dist/contexts.d.ts +1 -0
- package/dist/contexts.d.ts.map +1 -1
- package/dist/index.d.ts +57 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13961 -5946
- package/dist/index.js.map +1 -7
- package/dist/index.ssr.js +9612 -2401
- package/dist/index.ssr.js.map +1 -7
- package/dist/useDragAndDrop.d.ts +93 -0
- package/dist/useDragAndDrop.d.ts.map +1 -0
- package/dist/utils.d.ts +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/virtualizer/Layout.d.ts +79 -0
- package/dist/virtualizer/Layout.d.ts.map +1 -0
- package/package.json +8 -6
- package/src/ActionBar.tsx +248 -0
- package/src/ActionGroup.tsx +285 -0
- package/src/Alert.tsx +177 -0
- package/src/Autocomplete.tsx +1 -1
- package/src/Breadcrumbs.tsx +103 -17
- package/src/Button.tsx +65 -21
- package/src/Calendar.tsx +179 -53
- package/src/Checkbox.tsx +1 -2
- package/src/Collection.tsx +341 -0
- package/src/Color.tsx +652 -34
- package/src/ColorEditor.tsx +231 -0
- package/src/ComboBox.tsx +315 -81
- package/src/ContextualHelpTrigger.tsx +183 -0
- package/src/DateField.tsx +93 -19
- package/src/DatePicker.tsx +495 -25
- package/src/Dialog.tsx +40 -9
- package/src/Disclosure.tsx +33 -27
- package/src/DragAndDrop.tsx +334 -0
- package/src/DragPreview.tsx +45 -0
- package/src/DropZone.tsx +213 -0
- package/src/FieldError.tsx +67 -0
- package/src/FileTrigger.tsx +83 -0
- package/src/Focusable.tsx +106 -0
- package/src/Form.tsx +85 -0
- package/src/GridList.tsx +379 -41
- package/src/Icon.tsx +154 -0
- package/src/Keyboard.tsx +26 -0
- package/src/Link.tsx +14 -1
- package/src/ListBox.tsx +484 -33
- package/src/ListDropTargetDelegate.ts +282 -0
- package/src/Menu.tsx +388 -35
- package/src/Meter.tsx +7 -3
- package/src/Modal.tsx +32 -4
- package/src/NumberField.tsx +163 -43
- package/src/Popover.tsx +136 -180
- package/src/Pressable.tsx +108 -0
- package/src/ProgressBar.tsx +7 -3
- package/src/RadioGroup.tsx +35 -25
- package/src/RangeCalendar.tsx +100 -68
- package/src/RouterProvider.tsx +240 -0
- package/src/SearchField.tsx +142 -34
- package/src/Select.tsx +221 -73
- package/src/SelectionIndicator.tsx +105 -0
- package/src/SharedElementTransition.tsx +258 -0
- package/src/Slider.tsx +16 -6
- package/src/Table.tsx +417 -57
- package/src/Tabs.tsx +68 -35
- package/src/TagGroup.tsx +121 -36
- package/src/Text.tsx +18 -0
- package/src/TextField.tsx +25 -8
- package/src/TimeField.tsx +101 -151
- package/src/Toast.tsx +108 -14
- package/src/ToggleButton.tsx +159 -0
- package/src/ToggleButtonGroup.tsx +136 -0
- package/src/Toolbar.tsx +14 -8
- package/src/Tooltip.tsx +108 -19
- package/src/Tree.tsx +1143 -87
- package/src/Virtualizer.tsx +702 -0
- package/src/VirtualizerLayouts.ts +265 -0
- package/src/VisuallyHidden.tsx +15 -21
- package/src/contexts.ts +1 -0
- package/src/index.ts +1057 -620
- package/src/useDragAndDrop.ts +351 -0
- package/src/utils.tsx +37 -3
- package/src/virtualizer/Layout.ts +200 -0
package/src/Dialog.tsx
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {
|
|
9
9
|
type JSX,
|
|
10
10
|
createContext,
|
|
11
|
+
createEffect,
|
|
11
12
|
createMemo,
|
|
12
13
|
createUniqueId,
|
|
13
14
|
splitProps,
|
|
@@ -109,10 +110,18 @@ export function DialogTrigger(props: DialogTriggerProps): JSX.Element {
|
|
|
109
110
|
() => triggerRef
|
|
110
111
|
)
|
|
111
112
|
|
|
113
|
+
const setTriggerRef = (el: HTMLElement | null) => {
|
|
114
|
+
if (!el) return
|
|
115
|
+
if (!triggerRef || !triggerRef.isConnected) {
|
|
116
|
+
triggerRef = el
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
112
120
|
// Context value - memoized to avoid unnecessary re-renders
|
|
113
121
|
const contextValue = createMemo(() => ({
|
|
114
122
|
state,
|
|
115
123
|
triggerRef: () => triggerRef,
|
|
124
|
+
setTriggerRef,
|
|
116
125
|
triggerId,
|
|
117
126
|
}))
|
|
118
127
|
|
|
@@ -154,8 +163,7 @@ export function Dialog(props: DialogProps): JSX.Element {
|
|
|
154
163
|
return ariaProps['aria-label']
|
|
155
164
|
},
|
|
156
165
|
get 'aria-labelledby'() {
|
|
157
|
-
|
|
158
|
-
return ariaProps['aria-labelledby'] ?? triggerContext?.triggerId
|
|
166
|
+
return ariaProps['aria-labelledby']
|
|
159
167
|
},
|
|
160
168
|
get 'aria-describedby'() {
|
|
161
169
|
return ariaProps['aria-describedby']
|
|
@@ -172,7 +180,10 @@ export function Dialog(props: DialogProps): JSX.Element {
|
|
|
172
180
|
|
|
173
181
|
const close = () => {
|
|
174
182
|
local.onClose?.()
|
|
175
|
-
overlayState
|
|
183
|
+
if (overlayState) {
|
|
184
|
+
overlayState.close()
|
|
185
|
+
return
|
|
186
|
+
}
|
|
176
187
|
triggerContext?.state.close()
|
|
177
188
|
}
|
|
178
189
|
|
|
@@ -233,26 +244,46 @@ export function Heading(props: HeadingProps): JSX.Element {
|
|
|
233
244
|
const dialogContext = useContext(DialogContext)
|
|
234
245
|
const level = () => props.level ?? 2
|
|
235
246
|
const id = () => dialogContext?.titleId
|
|
247
|
+
let headingRef: HTMLHeadingElement | undefined
|
|
248
|
+
|
|
249
|
+
createEffect(() => {
|
|
250
|
+
const el = headingRef
|
|
251
|
+
if (!el) return
|
|
252
|
+
|
|
253
|
+
const contextId = id()
|
|
254
|
+
if (contextId) {
|
|
255
|
+
el.id = contextId
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!el.id) {
|
|
260
|
+
const dialog = el.closest('[role="dialog"],[role="alertdialog"]')
|
|
261
|
+
const labelledBy = dialog?.getAttribute('aria-labelledby')
|
|
262
|
+
if (labelledBy && !el.ownerDocument.getElementById(labelledBy)) {
|
|
263
|
+
el.id = labelledBy
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
})
|
|
236
267
|
|
|
237
268
|
return (
|
|
238
269
|
<Switch>
|
|
239
270
|
<Match when={level() === 1}>
|
|
240
|
-
<h1 id={id()} class={props.class}>{props.children}</h1>
|
|
271
|
+
<h1 ref={headingRef} id={id()} class={props.class}>{props.children}</h1>
|
|
241
272
|
</Match>
|
|
242
273
|
<Match when={level() === 2}>
|
|
243
|
-
<h2 id={id()} class={props.class}>{props.children}</h2>
|
|
274
|
+
<h2 ref={headingRef} id={id()} class={props.class}>{props.children}</h2>
|
|
244
275
|
</Match>
|
|
245
276
|
<Match when={level() === 3}>
|
|
246
|
-
<h3 id={id()} class={props.class}>{props.children}</h3>
|
|
277
|
+
<h3 ref={headingRef} id={id()} class={props.class}>{props.children}</h3>
|
|
247
278
|
</Match>
|
|
248
279
|
<Match when={level() === 4}>
|
|
249
|
-
<h4 id={id()} class={props.class}>{props.children}</h4>
|
|
280
|
+
<h4 ref={headingRef} id={id()} class={props.class}>{props.children}</h4>
|
|
250
281
|
</Match>
|
|
251
282
|
<Match when={level() === 5}>
|
|
252
|
-
<h5 id={id()} class={props.class}>{props.children}</h5>
|
|
283
|
+
<h5 ref={headingRef} id={id()} class={props.class}>{props.children}</h5>
|
|
253
284
|
</Match>
|
|
254
285
|
<Match when={level() === 6}>
|
|
255
|
-
<h6 id={id()} class={props.class}>{props.children}</h6>
|
|
286
|
+
<h6 ref={headingRef} id={id()} class={props.class}>{props.children}</h6>
|
|
256
287
|
</Match>
|
|
257
288
|
</Switch>
|
|
258
289
|
)
|
package/src/Disclosure.tsx
CHANGED
|
@@ -107,6 +107,7 @@ interface DisclosureContextValue {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export const DisclosureContext = createContext<DisclosureContextValue | null>(null);
|
|
110
|
+
export const DisclosureStateContext = createContext<DisclosureState | null>(null);
|
|
110
111
|
|
|
111
112
|
export function useDisclosureContext(): DisclosureContextValue | null {
|
|
112
113
|
return useContext(DisclosureContext);
|
|
@@ -117,6 +118,7 @@ interface DisclosureGroupContextValue {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
export const DisclosureGroupContext = createContext<DisclosureGroupContextValue | null>(null);
|
|
121
|
+
export const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(null);
|
|
120
122
|
|
|
121
123
|
export function useDisclosureGroupContext(): DisclosureGroupContextValue | null {
|
|
122
124
|
return useContext(DisclosureGroupContext);
|
|
@@ -160,17 +162,17 @@ export function DisclosureGroup(props: DisclosureGroupProps): JSX.Element {
|
|
|
160
162
|
]);
|
|
161
163
|
|
|
162
164
|
// Create group state
|
|
163
|
-
const state = createDisclosureGroupState({
|
|
165
|
+
const state = createDisclosureGroupState(() => ({
|
|
164
166
|
allowsMultipleExpanded: local.allowsMultipleExpanded,
|
|
165
167
|
isDisabled: local.isDisabled,
|
|
166
168
|
expandedKeys: local.expandedKeys,
|
|
167
169
|
defaultExpandedKeys: local.defaultExpandedKeys,
|
|
168
170
|
onExpandedChange: local.onExpandedChange,
|
|
169
|
-
});
|
|
171
|
+
}));
|
|
170
172
|
|
|
171
173
|
// Create group accessibility props
|
|
172
174
|
const { groupProps } = createDisclosureGroup(
|
|
173
|
-
{ isDisabled: local.isDisabled },
|
|
175
|
+
() => ({ isDisabled: local.isDisabled }),
|
|
174
176
|
state
|
|
175
177
|
);
|
|
176
178
|
|
|
@@ -199,17 +201,19 @@ export function DisclosureGroup(props: DisclosureGroupProps): JSX.Element {
|
|
|
199
201
|
const { ref: _ref, ...cleanGroupProps } = groupProps as Record<string, unknown>;
|
|
200
202
|
|
|
201
203
|
return (
|
|
202
|
-
<
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
204
|
+
<DisclosureGroupStateContext.Provider value={state}>
|
|
205
|
+
<DisclosureGroupContext.Provider value={contextValue}>
|
|
206
|
+
<div
|
|
207
|
+
{...domProps()}
|
|
208
|
+
{...cleanGroupProps}
|
|
209
|
+
class={renderProps.class()}
|
|
210
|
+
style={renderProps.style()}
|
|
211
|
+
data-disabled={dataAttr(state.isDisabled)}
|
|
212
|
+
>
|
|
213
|
+
{props.children}
|
|
214
|
+
</div>
|
|
215
|
+
</DisclosureGroupContext.Provider>
|
|
216
|
+
</DisclosureGroupStateContext.Provider>
|
|
213
217
|
);
|
|
214
218
|
}
|
|
215
219
|
|
|
@@ -315,19 +319,21 @@ export function Disclosure(props: DisclosureProps): JSX.Element {
|
|
|
315
319
|
};
|
|
316
320
|
|
|
317
321
|
return (
|
|
318
|
-
<
|
|
319
|
-
<
|
|
320
|
-
<
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
322
|
+
<DisclosureStateContext.Provider value={state}>
|
|
323
|
+
<DisclosureContext.Provider value={contextValue}>
|
|
324
|
+
<DisclosurePanelRefContext.Provider value={setPanelRef}>
|
|
325
|
+
<div
|
|
326
|
+
{...domProps()}
|
|
327
|
+
class={renderProps.class()}
|
|
328
|
+
style={renderProps.style()}
|
|
329
|
+
data-expanded={dataAttr(state.isExpanded())}
|
|
330
|
+
data-disabled={dataAttr(isDisabled())}
|
|
331
|
+
>
|
|
332
|
+
{props.children}
|
|
333
|
+
</div>
|
|
334
|
+
</DisclosurePanelRefContext.Provider>
|
|
335
|
+
</DisclosureContext.Provider>
|
|
336
|
+
</DisclosureStateContext.Provider>
|
|
331
337
|
);
|
|
332
338
|
}
|
|
333
339
|
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drag and drop composition helpers for solidaria-components.
|
|
3
|
+
*
|
|
4
|
+
* Compatibility target: react-aria-components DragAndDrop exports.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type JSX,
|
|
9
|
+
type Accessor,
|
|
10
|
+
createContext,
|
|
11
|
+
createMemo,
|
|
12
|
+
useContext,
|
|
13
|
+
} from 'solid-js';
|
|
14
|
+
import type {
|
|
15
|
+
DragTypes,
|
|
16
|
+
DropOperation,
|
|
17
|
+
DropTarget,
|
|
18
|
+
ItemDropTarget,
|
|
19
|
+
Key,
|
|
20
|
+
} from '@proyecto-viviana/solid-stately';
|
|
21
|
+
import type { DragAndDropHooks } from './useDragAndDrop';
|
|
22
|
+
import {
|
|
23
|
+
type ClassNameOrFunction,
|
|
24
|
+
type StyleOrFunction,
|
|
25
|
+
type SlotProps,
|
|
26
|
+
useRenderProps,
|
|
27
|
+
dataAttr,
|
|
28
|
+
} from './utils';
|
|
29
|
+
|
|
30
|
+
export interface DragAndDropContextValue {
|
|
31
|
+
dragAndDropHooks?: DragAndDropHooks<unknown>;
|
|
32
|
+
dragState?: unknown;
|
|
33
|
+
dropState?: {
|
|
34
|
+
target?: DropTarget | null;
|
|
35
|
+
isDropTarget?: (target: DropTarget) => boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const DragAndDropContext = createContext<DragAndDropContextValue>({});
|
|
40
|
+
|
|
41
|
+
export interface DropIndicatorRenderProps {
|
|
42
|
+
isDropTarget: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface DropIndicatorProps extends SlotProps {
|
|
46
|
+
target: ItemDropTarget;
|
|
47
|
+
children?: JSX.Element | ((props: DropIndicatorRenderProps) => JSX.Element);
|
|
48
|
+
class?: ClassNameOrFunction<DropIndicatorRenderProps>;
|
|
49
|
+
style?: StyleOrFunction<DropIndicatorRenderProps>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface DropIndicatorContextValue {
|
|
53
|
+
render: (props: DropIndicatorProps) => JSX.Element;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const DropIndicatorContext = createContext<DropIndicatorContextValue | null>(null);
|
|
57
|
+
|
|
58
|
+
function DefaultDropIndicator(props: DropIndicatorProps): JSX.Element {
|
|
59
|
+
const dnd = useContext(DragAndDropContext);
|
|
60
|
+
const isDropTarget = createMemo(() => {
|
|
61
|
+
const target = props.target;
|
|
62
|
+
return dnd.dropState?.isDropTarget?.(target) ?? false;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const renderProps = useRenderProps(
|
|
66
|
+
{
|
|
67
|
+
children: props.children,
|
|
68
|
+
class: props.class,
|
|
69
|
+
style: props.style,
|
|
70
|
+
defaultClassName: 'solidaria-DropIndicator',
|
|
71
|
+
},
|
|
72
|
+
() => ({
|
|
73
|
+
isDropTarget: isDropTarget(),
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
role="option"
|
|
80
|
+
aria-disabled={true}
|
|
81
|
+
class={renderProps.class()}
|
|
82
|
+
style={renderProps.style()}
|
|
83
|
+
data-drop-target={dataAttr(isDropTarget())}
|
|
84
|
+
>
|
|
85
|
+
{renderProps.renderChildren()}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function DropIndicator(props: DropIndicatorProps): JSX.Element {
|
|
91
|
+
const context = useContext(DropIndicatorContext);
|
|
92
|
+
if (context) return context.render(props);
|
|
93
|
+
return <DefaultDropIndicator {...props} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function useRenderDropIndicator(
|
|
97
|
+
hooksOrDropState?:
|
|
98
|
+
| Pick<DragAndDropHooks<unknown>, 'renderDropIndicator' | 'isVirtualDragging' | 'useDropIndicator'>
|
|
99
|
+
| {
|
|
100
|
+
target?: DropTarget | null;
|
|
101
|
+
isDropTarget?: ((target: DropTarget) => boolean) | boolean;
|
|
102
|
+
},
|
|
103
|
+
maybeDropState?: {
|
|
104
|
+
target?: DropTarget | null;
|
|
105
|
+
isDropTarget?: ((target: DropTarget) => boolean) | boolean;
|
|
106
|
+
}
|
|
107
|
+
): ((target: ItemDropTarget) => JSX.Element | undefined) | undefined {
|
|
108
|
+
const looksLikeDropState = (
|
|
109
|
+
value: unknown
|
|
110
|
+
): value is {
|
|
111
|
+
target?: DropTarget | null;
|
|
112
|
+
isDropTarget?: ((target: DropTarget) => boolean) | boolean;
|
|
113
|
+
} => {
|
|
114
|
+
return Boolean(
|
|
115
|
+
value &&
|
|
116
|
+
typeof value === 'object' &&
|
|
117
|
+
('isDropTarget' in (value as Record<string, unknown>) || 'target' in (value as Record<string, unknown>))
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const dragAndDropHooks = looksLikeDropState(hooksOrDropState)
|
|
122
|
+
? undefined
|
|
123
|
+
: hooksOrDropState;
|
|
124
|
+
const dropState = looksLikeDropState(hooksOrDropState) ? hooksOrDropState : maybeDropState;
|
|
125
|
+
|
|
126
|
+
// RAC only renders collection indicators when drop hooks are present.
|
|
127
|
+
if (dragAndDropHooks && !dragAndDropHooks.useDropIndicator) return undefined;
|
|
128
|
+
if (!dropState && !dragAndDropHooks?.renderDropIndicator) return undefined;
|
|
129
|
+
|
|
130
|
+
const targetsEqual = (a: DropTarget | null | undefined, b: DropTarget): boolean => {
|
|
131
|
+
if (!a) return false;
|
|
132
|
+
if (a.type !== b.type) return false;
|
|
133
|
+
if (a.type === 'root' && b.type === 'root') return true;
|
|
134
|
+
if (a.type !== 'item' || b.type !== 'item') return false;
|
|
135
|
+
return a.key === b.key && a.dropPosition === b.dropPosition;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (target: ItemDropTarget) => {
|
|
139
|
+
const stateIsDropTarget = dropState?.isDropTarget;
|
|
140
|
+
const isTarget = typeof stateIsDropTarget === 'function'
|
|
141
|
+
? stateIsDropTarget(target)
|
|
142
|
+
: stateIsDropTarget === true
|
|
143
|
+
? targetsEqual(dropState?.target, target)
|
|
144
|
+
: false;
|
|
145
|
+
const isVirtualDragging = dragAndDropHooks?.isVirtualDragging?.() ?? false;
|
|
146
|
+
if (!isTarget && !isVirtualDragging) return undefined;
|
|
147
|
+
return dragAndDropHooks?.renderDropIndicator
|
|
148
|
+
? dragAndDropHooks.renderDropIndicator(target)
|
|
149
|
+
: <DropIndicator target={target} />;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
type KeyAccessor = Key | null | undefined | Accessor<Key | null | undefined>;
|
|
154
|
+
|
|
155
|
+
interface SelectionManagerLike {
|
|
156
|
+
focusedKey?: KeyAccessor;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface CollectionLike {
|
|
160
|
+
getKeyAfter: (key: Key) => Key | null;
|
|
161
|
+
getItem: (key: Key) => { level?: number; type?: string } | null | undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
interface DroppableCollectionStateLike {
|
|
165
|
+
target?: DropTarget | null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface VirtualRangeLike {
|
|
169
|
+
start: number;
|
|
170
|
+
end: number;
|
|
171
|
+
offsetTop: number;
|
|
172
|
+
offsetBottom: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface LayoutInfoProviderLike {
|
|
176
|
+
getLayoutInfo: (index: number) => { rect: { y: number; height: number } };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function resolveKey(value: KeyAccessor): Key | null | undefined {
|
|
180
|
+
if (typeof value === 'function') {
|
|
181
|
+
return (value as Accessor<Key | null | undefined>)();
|
|
182
|
+
}
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getAfterDropNormalizedKey(
|
|
187
|
+
target: ItemDropTarget,
|
|
188
|
+
collection?: CollectionLike
|
|
189
|
+
): Key {
|
|
190
|
+
if (target.dropPosition !== 'after' || !collection) return target.key;
|
|
191
|
+
|
|
192
|
+
let nextKey = collection.getKeyAfter(target.key);
|
|
193
|
+
let lastDescendantKey: Key | null = null;
|
|
194
|
+
|
|
195
|
+
if (nextKey != null) {
|
|
196
|
+
const targetLevel = collection.getItem(target.key)?.level ?? 0;
|
|
197
|
+
while (nextKey != null) {
|
|
198
|
+
const node = collection.getItem(nextKey);
|
|
199
|
+
if (!node) break;
|
|
200
|
+
if (node.type && node.type !== 'item') {
|
|
201
|
+
nextKey = collection.getKeyAfter(nextKey);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if ((node.level ?? 0) <= targetLevel) break;
|
|
205
|
+
lastDescendantKey = nextKey;
|
|
206
|
+
nextKey = collection.getKeyAfter(nextKey);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return nextKey ?? lastDescendantKey ?? target.key;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function getNormalizedDropTargetKey(
|
|
214
|
+
target: DropTarget | null | undefined,
|
|
215
|
+
collection?: CollectionLike
|
|
216
|
+
): Key | null {
|
|
217
|
+
if (!target || target.type !== 'item') return null;
|
|
218
|
+
return getAfterDropNormalizedKey(target, collection);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function useDndPersistedKeys(
|
|
222
|
+
selectionManager: SelectionManagerLike | null | undefined,
|
|
223
|
+
dragAndDropHooks?: Pick<DragAndDropHooks<unknown>, 'isVirtualDragging'>,
|
|
224
|
+
dropState?: DroppableCollectionStateLike,
|
|
225
|
+
collection?: CollectionLike
|
|
226
|
+
): Accessor<Set<Key>> {
|
|
227
|
+
return createMemo(() => {
|
|
228
|
+
const focusedKey = resolveKey(selectionManager?.focusedKey);
|
|
229
|
+
let dropTargetKey: Key | null | undefined;
|
|
230
|
+
|
|
231
|
+
if (dragAndDropHooks?.isVirtualDragging?.() && dropState?.target?.type === 'item') {
|
|
232
|
+
dropTargetKey = getNormalizedDropTargetKey(dropState.target, collection) ?? undefined;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const keys = new Set<Key>();
|
|
236
|
+
if (focusedKey != null) keys.add(focusedKey);
|
|
237
|
+
if (dropTargetKey != null) keys.add(dropTargetKey);
|
|
238
|
+
return keys;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function mergePersistedKeysIntoVirtualRange(
|
|
243
|
+
baseRange: VirtualRangeLike,
|
|
244
|
+
persistedIndexes: number[],
|
|
245
|
+
itemCount: number,
|
|
246
|
+
layoutInfoProvider: LayoutInfoProviderLike,
|
|
247
|
+
maxExtraItems = 60,
|
|
248
|
+
options?: {
|
|
249
|
+
forceIncludeIndexes?: number[];
|
|
250
|
+
forceIncludeMaxSpan?: number;
|
|
251
|
+
fallbackToForcedWindow?: boolean;
|
|
252
|
+
}
|
|
253
|
+
): VirtualRangeLike {
|
|
254
|
+
const validPersistedIndexes = Array.from(
|
|
255
|
+
new Set(persistedIndexes.filter((index) => index >= 0 && index < itemCount))
|
|
256
|
+
).sort((a, b) => a - b);
|
|
257
|
+
const forceIndexes = Array.from(
|
|
258
|
+
new Set((options?.forceIncludeIndexes ?? []).filter((index) => index >= 0 && index < itemCount))
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
if (itemCount <= 0) return baseRange;
|
|
262
|
+
if (validPersistedIndexes.length === 0 && forceIndexes.length === 0) return baseRange;
|
|
263
|
+
|
|
264
|
+
const baseSpan = Math.max(1, baseRange.end - baseRange.start);
|
|
265
|
+
const maxSpan = Math.max(baseSpan, baseSpan + maxExtraItems);
|
|
266
|
+
|
|
267
|
+
let start = baseRange.start;
|
|
268
|
+
let end = baseRange.end;
|
|
269
|
+
|
|
270
|
+
const distanceToBaseRange = (index: number): number => {
|
|
271
|
+
if (index < baseRange.start) return baseRange.start - index;
|
|
272
|
+
if (index >= baseRange.end) return index - (baseRange.end - 1);
|
|
273
|
+
return 0;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
for (const index of validPersistedIndexes.sort((a, b) => distanceToBaseRange(a) - distanceToBaseRange(b))) {
|
|
277
|
+
const nextStart = Math.min(start, index);
|
|
278
|
+
const nextEnd = Math.max(end, index + 1);
|
|
279
|
+
if (nextEnd - nextStart <= maxSpan) {
|
|
280
|
+
start = nextStart;
|
|
281
|
+
end = nextEnd;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (forceIndexes.length > 0) {
|
|
286
|
+
const forceMaxSpan = Math.max(maxSpan, options?.forceIncludeMaxSpan ?? Math.max(maxSpan, 300));
|
|
287
|
+
for (const index of forceIndexes) {
|
|
288
|
+
const nextStart = Math.min(start, index);
|
|
289
|
+
const nextEnd = Math.max(end, index + 1);
|
|
290
|
+
if (nextEnd - nextStart <= forceMaxSpan) {
|
|
291
|
+
start = nextStart;
|
|
292
|
+
end = nextEnd;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (options?.fallbackToForcedWindow !== false) {
|
|
297
|
+
const missingForced = forceIndexes.filter((index) => index < start || index >= end);
|
|
298
|
+
if (missingForced.length > 0) {
|
|
299
|
+
const nearestForced = missingForced[0];
|
|
300
|
+
const forceMaxSpan = Math.max(maxSpan, options?.forceIncludeMaxSpan ?? Math.max(maxSpan, 300));
|
|
301
|
+
const windowSpan = Math.min(itemCount, Math.max(baseSpan, forceMaxSpan));
|
|
302
|
+
const centeredStart = Math.max(0, Math.min(itemCount - windowSpan, nearestForced - Math.floor(windowSpan / 2)));
|
|
303
|
+
start = centeredStart;
|
|
304
|
+
end = Math.min(itemCount, centeredStart + windowSpan);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (start === baseRange.start && end === baseRange.end) return baseRange;
|
|
310
|
+
|
|
311
|
+
const startRect = start > 0 ? layoutInfoProvider.getLayoutInfo(start).rect : { y: 0, height: 0 };
|
|
312
|
+
const lastRect = layoutInfoProvider.getLayoutInfo(itemCount - 1).rect;
|
|
313
|
+
const endRect = end > 0 ? layoutInfoProvider.getLayoutInfo(end - 1).rect : { y: 0, height: 0 };
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
start,
|
|
317
|
+
end,
|
|
318
|
+
offsetTop: Math.max(0, startRect.y),
|
|
319
|
+
offsetBottom: Math.max(0, (lastRect.y + lastRect.height) - (endRect.y + endRect.height)),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export type DropTargetDelegate = {
|
|
324
|
+
getDropTargetFromPoint: (
|
|
325
|
+
x: number,
|
|
326
|
+
y: number,
|
|
327
|
+
isValidDropTarget: (target: DropTarget) => boolean
|
|
328
|
+
) => DropTarget | null;
|
|
329
|
+
getDropOperation: (
|
|
330
|
+
target: DropTarget,
|
|
331
|
+
types: DragTypes,
|
|
332
|
+
allowedOperations: DropOperation[]
|
|
333
|
+
) => DropOperation;
|
|
334
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { onCleanup, type JSX } from 'solid-js';
|
|
2
|
+
import type { DragItem, DragPreviewRenderer } from '@proyecto-viviana/solid-stately';
|
|
3
|
+
|
|
4
|
+
export interface DragPreviewProps {
|
|
5
|
+
ref?: { current: DragPreviewRenderer | null };
|
|
6
|
+
children: (items: DragItem[]) => JSX.Element | { element: JSX.Element; x?: number; y?: number } | null | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function DragPreview(props: DragPreviewProps): JSX.Element {
|
|
10
|
+
const hasDom = typeof HTMLElement !== 'undefined';
|
|
11
|
+
const isElementNode = (value: unknown): value is HTMLElement => {
|
|
12
|
+
if (!value) return false;
|
|
13
|
+
if (hasDom && value instanceof HTMLElement) return true;
|
|
14
|
+
return typeof value === 'object' && (value as { nodeType?: number }).nodeType === 1;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
if (props.ref) {
|
|
18
|
+
const renderer: DragPreviewRenderer = (items, callback) => {
|
|
19
|
+
const rendered = props.children(items);
|
|
20
|
+
if (!rendered) {
|
|
21
|
+
callback(null);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (
|
|
25
|
+
typeof rendered === 'object' &&
|
|
26
|
+
rendered !== null &&
|
|
27
|
+
'element' in rendered
|
|
28
|
+
) {
|
|
29
|
+
const previewValue = rendered as { element: unknown; x?: number; y?: number };
|
|
30
|
+
callback(isElementNode(previewValue.element) ? previewValue.element : null, previewValue.x, previewValue.y);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
callback(isElementNode(rendered) ? rendered : null);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
props.ref.current = renderer;
|
|
37
|
+
onCleanup(() => {
|
|
38
|
+
if (props.ref?.current === renderer) {
|
|
39
|
+
props.ref.current = null;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null as unknown as JSX.Element;
|
|
45
|
+
}
|