uikit-react-public 0.45.5 → 0.47.1

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 (41) hide show
  1. package/dist/components/Dialog/BaseDialog.d.ts +1 -0
  2. package/dist/components/DropZone/DropZone.d.ts +21 -0
  3. package/dist/components/DropZone/DropZone.stories.d.ts +52 -0
  4. package/dist/components/DropZone/__tests__/DropZone.test.d.ts +1 -0
  5. package/dist/components/DropZone/index.d.ts +2 -0
  6. package/dist/components/Skeleton/Skeleton.d.ts +35 -0
  7. package/dist/components/Skeleton/Skeleton.stories.d.ts +27 -0
  8. package/dist/components/Skeleton/SkeletonAvatar.d.ts +7 -0
  9. package/dist/components/Skeleton/SkeletonInput.d.ts +4 -0
  10. package/dist/components/Skeleton/SkeletonTableRow.d.ts +9 -0
  11. package/dist/components/Skeleton/SkeletonText.d.ts +4 -0
  12. package/dist/components/Skeleton/__tests__/Skeleton.test.d.ts +1 -0
  13. package/dist/components/Skeleton/index.d.ts +6 -0
  14. package/dist/components/index.d.ts +4 -0
  15. package/dist/hooks/__tests__/useDraggable.test.d.ts +1 -0
  16. package/dist/hooks/index.d.ts +4 -0
  17. package/dist/hooks/useDraggable.d.ts +31 -0
  18. package/dist/hooks/useDropZone.d.ts +57 -0
  19. package/dist/index.js +7308 -6737
  20. package/lib/components/Dialog/BaseDialog.tsx +25 -15
  21. package/lib/components/DropZone/DropZone.mdx +72 -0
  22. package/lib/components/DropZone/DropZone.stories.tsx +139 -0
  23. package/lib/components/DropZone/DropZone.tsx +213 -0
  24. package/lib/components/DropZone/__tests__/DropZone.test.tsx +378 -0
  25. package/lib/components/DropZone/__tests__/__snapshots__/DropZone.test.tsx.snap +46 -0
  26. package/lib/components/DropZone/index.ts +2 -0
  27. package/lib/components/Skeleton/Skeleton.mdx +34 -0
  28. package/lib/components/Skeleton/Skeleton.stories.tsx +77 -0
  29. package/lib/components/Skeleton/Skeleton.tsx +158 -0
  30. package/lib/components/Skeleton/SkeletonAvatar.tsx +20 -0
  31. package/lib/components/Skeleton/SkeletonInput.tsx +13 -0
  32. package/lib/components/Skeleton/SkeletonTableRow.tsx +44 -0
  33. package/lib/components/Skeleton/SkeletonText.tsx +13 -0
  34. package/lib/components/Skeleton/__tests__/Skeleton.test.tsx +86 -0
  35. package/lib/components/Skeleton/index.ts +6 -0
  36. package/lib/components/index.ts +13 -0
  37. package/lib/hooks/__tests__/useDraggable.test.tsx +184 -0
  38. package/lib/hooks/index.ts +19 -0
  39. package/lib/hooks/useDraggable.ts +243 -0
  40. package/lib/hooks/useDropZone.ts +418 -0
  41. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import { Skeleton, SkeletonProps } from './Skeleton';
2
+
3
+ export interface SkeletonAvatarProps extends Omit<
4
+ SkeletonProps,
5
+ 'variant' | 'width' | 'height' | 'count'
6
+ > {
7
+ /** Matches the `Avatar` size options. @default 72 */
8
+ size?: 32 | 48 | 56 | 72 | 80;
9
+ }
10
+
11
+ const SkeletonAvatar = ({ size = 72, ...props }: SkeletonAvatarProps) => (
12
+ <Skeleton
13
+ variant='circle'
14
+ width={size}
15
+ height={size}
16
+ {...props}
17
+ />
18
+ );
19
+
20
+ export default SkeletonAvatar;
@@ -0,0 +1,13 @@
1
+ import { Skeleton, SkeletonProps } from './Skeleton';
2
+
3
+ export type SkeletonInputProps = Omit<SkeletonProps, 'variant' | 'count'>;
4
+
5
+ const SkeletonInput = ({ height = 48, ...props }: SkeletonInputProps) => (
6
+ <Skeleton
7
+ variant='rect'
8
+ height={height}
9
+ {...props}
10
+ />
11
+ );
12
+
13
+ export default SkeletonInput;
@@ -0,0 +1,44 @@
1
+ import { HTMLAttributes } from 'react';
2
+ import { css, cx } from '@emotion/css';
3
+ import { useTheme } from '../../theme';
4
+ import { Skeleton } from './Skeleton';
5
+
6
+ export const NAME = 'ucl-uikit-skeleton__table-row';
7
+
8
+ export interface SkeletonTableRowProps extends HTMLAttributes<HTMLTableRowElement> {
9
+ /** Number of cells to render. @default 4 */
10
+ columns?: number;
11
+ testId?: string;
12
+ }
13
+
14
+ const SkeletonTableRow = ({
15
+ columns = 4,
16
+ testId = NAME,
17
+ className,
18
+ ...props
19
+ }: SkeletonTableRowProps) => {
20
+ const [theme] = useTheme();
21
+
22
+ const cellStyle = css`
23
+ padding: ${theme.padding.p12} ${theme.padding.p16};
24
+ `;
25
+
26
+ return (
27
+ <tr
28
+ className={cx(NAME, className)}
29
+ data-testid={testId}
30
+ {...props}
31
+ >
32
+ {Array.from({ length: columns }, (_, index) => (
33
+ <td
34
+ key={index}
35
+ className={cellStyle}
36
+ >
37
+ <Skeleton variant='text' />
38
+ </td>
39
+ ))}
40
+ </tr>
41
+ );
42
+ };
43
+
44
+ export default SkeletonTableRow;
@@ -0,0 +1,13 @@
1
+ import { Skeleton, SkeletonProps } from './Skeleton';
2
+
3
+ export type SkeletonTextProps = Omit<SkeletonProps, 'variant'>;
4
+
5
+ const SkeletonText = ({ count = 4, ...props }: SkeletonTextProps) => (
6
+ <Skeleton
7
+ variant='text'
8
+ count={count}
9
+ {...props}
10
+ />
11
+ );
12
+
13
+ export default SkeletonText;
@@ -0,0 +1,86 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { render } from '@testing-library/react';
3
+ import { ThemeContextProvider } from '../../../theme/useTheme';
4
+ import darkTheme from '../../../theme/dark/darkTheme';
5
+ import lightTheme from '../../../theme/light/lightTheme';
6
+ import Skeleton, { getSkeletonShimmerBackground } from '../Skeleton';
7
+
8
+ const wrap = (ui: React.ReactElement) =>
9
+ render(<ThemeContextProvider>{ui}</ThemeContextProvider>);
10
+
11
+ describe('Skeleton', () => {
12
+ test('renders one bar per count', () => {
13
+ const { getByTestId } = wrap(
14
+ <Skeleton
15
+ count={4}
16
+ testId='skeleton'
17
+ />
18
+ );
19
+
20
+ expect(getByTestId('skeleton').childElementCount).toBe(4);
21
+ });
22
+
23
+ test('is hidden from assistive technology', () => {
24
+ const { getByTestId } = wrap(<Skeleton testId='skeleton' />);
25
+
26
+ expect(getByTestId('skeleton')).toHaveAttribute('aria-hidden', 'true');
27
+ });
28
+
29
+ test('applies a custom class name', () => {
30
+ const { getByTestId } = wrap(
31
+ <Skeleton
32
+ className='custom-class'
33
+ testId='skeleton'
34
+ />
35
+ );
36
+
37
+ expect(getByTestId('skeleton')).toHaveClass('custom-class');
38
+ });
39
+
40
+ test('renders a 61.8% final line for multiline text', () => {
41
+ const { getByTestId } = wrap(
42
+ <Skeleton
43
+ count={4}
44
+ testId='skeleton'
45
+ />
46
+ );
47
+
48
+ expect(getByTestId('skeleton').lastElementChild).toHaveStyle({
49
+ width: '61.8%',
50
+ });
51
+ });
52
+
53
+ test('does not shorten a single text line', () => {
54
+ const { getByTestId } = wrap(<Skeleton testId='skeleton' />);
55
+
56
+ expect(getByTestId('skeleton').firstElementChild).not.toHaveAttribute(
57
+ 'style'
58
+ );
59
+ });
60
+
61
+ test('Skeleton.TableRow renders one cell per column', () => {
62
+ const { getByTestId } = wrap(
63
+ <table>
64
+ <tbody>
65
+ <Skeleton.TableRow
66
+ columns={4}
67
+ testId='skeleton-row'
68
+ />
69
+ </tbody>
70
+ </table>
71
+ );
72
+
73
+ expect(getByTestId('skeleton-row').querySelectorAll('td')).toHaveLength(4);
74
+ });
75
+
76
+ test.each([
77
+ ['light', lightTheme],
78
+ ['dark', darkTheme],
79
+ ])('uses semantic theme colours for the %s shimmer', (_, theme) => {
80
+ const shimmer = getSkeletonShimmerBackground(theme);
81
+
82
+ expect(shimmer).toContain(theme.colour.bg.default);
83
+ expect(shimmer).toContain(theme.colour.surface.secondary);
84
+ expect(shimmer).toContain(theme.colour.fill.subtle);
85
+ });
86
+ });
@@ -0,0 +1,6 @@
1
+ export { default } from './Skeleton';
2
+ export type { SkeletonProps, SkeletonVariant } from './Skeleton';
3
+ export type { SkeletonAvatarProps } from './SkeletonAvatar';
4
+ export type { SkeletonInputProps } from './SkeletonInput';
5
+ export type { SkeletonTableRowProps } from './SkeletonTableRow';
6
+ export type { SkeletonTextProps } from './SkeletonText';
@@ -56,6 +56,16 @@ export type {
56
56
  ProgressBarVariant,
57
57
  } from './ProgressBar';
58
58
 
59
+ export { default as Skeleton } from './Skeleton';
60
+ export type {
61
+ SkeletonProps,
62
+ SkeletonVariant,
63
+ SkeletonTextProps,
64
+ SkeletonAvatarProps,
65
+ SkeletonInputProps,
66
+ SkeletonTableRowProps,
67
+ } from './Skeleton';
68
+
59
69
  export { default as Textarea } from './Textarea';
60
70
  export type { TextareaProps } from './Textarea';
61
71
 
@@ -215,6 +225,9 @@ export type { CalendarProps, CalendarStatuses } from './Calendar';
215
225
 
216
226
  export { default as WeekPicker } from './WeekPicker';
217
227
 
228
+ export { default as DropZone } from './DropZone';
229
+ export type { DropZoneProps, DropZoneRenderProps } from './DropZone';
230
+
218
231
  export { default as FileInput } from './FileInput';
219
232
  export type { FileInputProps } from './FileInput';
220
233
 
@@ -0,0 +1,184 @@
1
+ import { afterEach, describe, expect, test, vitest } from 'vitest';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { UIKIT_DND_DATA_TYPE, useDraggable } from '..';
4
+ import { clearActiveKeyboardDrag, getActiveKeyboardDrag } from '../useDropZone';
5
+
6
+ const TABLE_CELL_TYPE = 'application/x-ucl-uikit-table-cell';
7
+
8
+ const createDataTransfer = () =>
9
+ ({
10
+ effectAllowed: 'all',
11
+ setData: vitest.fn(),
12
+ setDragImage: vitest.fn(),
13
+ }) as unknown as DataTransfer;
14
+
15
+ const DraggableDemo = () => {
16
+ const { getDragProps, isDragging } = useDraggable({
17
+ data: {
18
+ column: 'Status',
19
+ row: 'Research proposal',
20
+ value: 'Approved',
21
+ },
22
+ type: TABLE_CELL_TYPE,
23
+ });
24
+
25
+ return (
26
+ <div
27
+ data-testid='drag-source'
28
+ {...getDragProps()}
29
+ >
30
+ {isDragging ? 'Dragging' : 'Ready'}
31
+ </div>
32
+ );
33
+ };
34
+
35
+ const StringDraggableDemo = () => {
36
+ const { getDragProps } = useDraggable({
37
+ data: '123',
38
+ type: TABLE_CELL_TYPE,
39
+ });
40
+
41
+ return (
42
+ <div
43
+ data-testid='string-drag-source'
44
+ {...getDragProps()}
45
+ />
46
+ );
47
+ };
48
+
49
+ const DisabledDraggableDemo = ({ disabled }: { disabled: boolean }) => {
50
+ const { getDragProps } = useDraggable({
51
+ data: 'Approved',
52
+ disabled,
53
+ type: TABLE_CELL_TYPE,
54
+ });
55
+
56
+ return (
57
+ <div
58
+ data-testid='disabled-drag-source'
59
+ {...getDragProps()}
60
+ />
61
+ );
62
+ };
63
+
64
+ describe('useDraggable', () => {
65
+ afterEach(() => {
66
+ clearActiveKeyboardDrag();
67
+ });
68
+
69
+ test('sets draggable data on drag start', () => {
70
+ const dataTransfer = createDataTransfer();
71
+
72
+ render(<DraggableDemo />);
73
+
74
+ fireEvent.dragStart(screen.getByTestId('drag-source'), {
75
+ dataTransfer,
76
+ });
77
+
78
+ expect(dataTransfer.effectAllowed).toBe('move');
79
+ expect(dataTransfer.setData).toHaveBeenCalledWith(
80
+ TABLE_CELL_TYPE,
81
+ JSON.stringify({
82
+ column: 'Status',
83
+ row: 'Research proposal',
84
+ value: 'Approved',
85
+ })
86
+ );
87
+ expect(dataTransfer.setData).toHaveBeenCalledWith(
88
+ UIKIT_DND_DATA_TYPE,
89
+ JSON.stringify({
90
+ data: JSON.stringify({
91
+ column: 'Status',
92
+ row: 'Research proposal',
93
+ value: 'Approved',
94
+ }),
95
+ type: TABLE_CELL_TYPE,
96
+ })
97
+ );
98
+ expect(screen.getByText('Dragging')).toBeInTheDocument();
99
+ });
100
+
101
+ test('clears dragging state on drag end', () => {
102
+ render(<DraggableDemo />);
103
+
104
+ fireEvent.dragStart(screen.getByTestId('drag-source'), {
105
+ dataTransfer: createDataTransfer(),
106
+ });
107
+ fireEvent.dragEnd(screen.getByTestId('drag-source'));
108
+
109
+ expect(screen.getByText('Ready')).toBeInTheDocument();
110
+ });
111
+
112
+ test('serializes strings as JSON strings', () => {
113
+ const dataTransfer = createDataTransfer();
114
+
115
+ render(<StringDraggableDemo />);
116
+
117
+ fireEvent.dragStart(screen.getByTestId('string-drag-source'), {
118
+ dataTransfer,
119
+ });
120
+
121
+ expect(dataTransfer.setData).toHaveBeenCalledWith(
122
+ TABLE_CELL_TYPE,
123
+ JSON.stringify('123')
124
+ );
125
+ });
126
+
127
+ test('supports keyboard drag start and cancel', () => {
128
+ render(<DraggableDemo />);
129
+
130
+ fireEvent.keyDown(screen.getByTestId('drag-source'), { key: 'Enter' });
131
+
132
+ expect(screen.getByText('Dragging')).toBeInTheDocument();
133
+
134
+ fireEvent.keyDown(screen.getByTestId('drag-source'), { key: 'Escape' });
135
+
136
+ expect(screen.getByText('Ready')).toBeInTheDocument();
137
+ });
138
+
139
+ test('does not replace an active keyboard drag', () => {
140
+ render(
141
+ <>
142
+ <DraggableDemo />
143
+ <StringDraggableDemo />
144
+ </>
145
+ );
146
+
147
+ fireEvent.keyDown(screen.getByTestId('drag-source'), { key: 'Enter' });
148
+ fireEvent.keyDown(screen.getByTestId('string-drag-source'), {
149
+ key: 'Enter',
150
+ });
151
+
152
+ expect(getActiveKeyboardDrag()?.data).toEqual({
153
+ column: 'Status',
154
+ row: 'Research proposal',
155
+ value: 'Approved',
156
+ });
157
+ });
158
+
159
+ test('clears an owned keyboard drag on unmount', () => {
160
+ const { unmount } = render(<DraggableDemo />);
161
+
162
+ fireEvent.keyDown(screen.getByTestId('drag-source'), { key: 'Enter' });
163
+
164
+ expect(getActiveKeyboardDrag()).not.toBeNull();
165
+
166
+ unmount();
167
+
168
+ expect(getActiveKeyboardDrag()).toBeNull();
169
+ });
170
+
171
+ test('clears an owned keyboard drag when disabled', () => {
172
+ const { rerender } = render(<DisabledDraggableDemo disabled={false} />);
173
+
174
+ fireEvent.keyDown(screen.getByTestId('disabled-drag-source'), {
175
+ key: 'Enter',
176
+ });
177
+
178
+ expect(getActiveKeyboardDrag()).not.toBeNull();
179
+
180
+ rerender(<DisabledDraggableDemo disabled />);
181
+
182
+ expect(getActiveKeyboardDrag()).toBeNull();
183
+ });
184
+ });
@@ -1,2 +1,21 @@
1
1
  export { useFocusTrap } from './useFocusTrap';
2
+ export { default as useDraggable } from './useDraggable';
3
+ export type {
4
+ DraggableProps,
5
+ UseDraggableOptions,
6
+ UseDraggableReturn,
7
+ } from './useDraggable';
8
+ export { default as useDropZone, UIKIT_DND_DATA_TYPE } from './useDropZone';
9
+ export type {
10
+ DragData,
11
+ DropZoneAcceptedTypes,
12
+ DropZoneDrop,
13
+ DropZoneEvent,
14
+ DropZoneRejection,
15
+ DropZoneRejectionReason,
16
+ DropZoneRootProps,
17
+ KeyboardDragData,
18
+ UseDropZoneOptions,
19
+ UseDropZoneReturn,
20
+ } from './useDropZone';
2
21
  export { default as useMediaQuery } from './useMediaQuery';
@@ -0,0 +1,243 @@
1
+ import {
2
+ DragEvent,
3
+ HTMLAttributes,
4
+ KeyboardEvent,
5
+ Ref,
6
+ RefCallback,
7
+ useCallback,
8
+ useEffect,
9
+ useId,
10
+ useRef,
11
+ useState,
12
+ } from 'react';
13
+ import {
14
+ clearActiveKeyboardDrag,
15
+ DragData,
16
+ getActiveKeyboardDrag,
17
+ setActiveKeyboardDrag,
18
+ subscribeToKeyboardDrag,
19
+ UIKIT_DND_DATA_TYPE,
20
+ } from './useDropZone';
21
+
22
+ export interface UseDraggableOptions<TData = unknown> {
23
+ data: TData;
24
+ disabled?: boolean;
25
+ dragImage?: Element;
26
+ dragImageOffset?: {
27
+ x: number;
28
+ y: number;
29
+ };
30
+ effectAllowed?: DataTransfer['effectAllowed'];
31
+ keyboardDrag?: boolean;
32
+ onDragEnd?: (event: DragEvent<HTMLElement>) => void;
33
+ onKeyboardDragCancel?: (dragData: DragData<TData>) => void;
34
+ onDragStart?: (
35
+ dragData: DragData<TData>,
36
+ event: DragEvent<HTMLElement>
37
+ ) => void;
38
+ onKeyboardDragStart?: (dragData: DragData<TData>) => void;
39
+ serializeData?: (data: TData) => string;
40
+ type: string;
41
+ }
42
+
43
+ export interface DraggableProps<
44
+ TElement extends HTMLElement,
45
+ > extends HTMLAttributes<TElement> {
46
+ draggable: boolean;
47
+ ref: RefCallback<TElement>;
48
+ }
49
+
50
+ export interface UseDraggableReturn {
51
+ getDragProps: <TElement extends HTMLElement = HTMLElement>(
52
+ props?: HTMLAttributes<TElement> & { ref?: Ref<TElement> }
53
+ ) => DraggableProps<TElement>;
54
+ isDragging: boolean;
55
+ }
56
+
57
+ const serializeDefaultData = <TData>(data: TData): string => {
58
+ const serializedData = JSON.stringify(data);
59
+
60
+ if (serializedData === undefined) {
61
+ throw new TypeError('Drag data must be JSON-serializable');
62
+ }
63
+
64
+ return serializedData;
65
+ };
66
+
67
+ const setRef = <TValue>(ref: Ref<TValue> | undefined, value: TValue) => {
68
+ if (!ref) return;
69
+
70
+ if (typeof ref === 'function') {
71
+ ref(value);
72
+ return;
73
+ }
74
+
75
+ ref.current = value;
76
+ };
77
+
78
+ const useDraggable = <TData = unknown>({
79
+ data,
80
+ disabled = false,
81
+ dragImage,
82
+ dragImageOffset = { x: 0, y: 0 },
83
+ effectAllowed = 'move',
84
+ keyboardDrag = true,
85
+ onDragEnd,
86
+ onKeyboardDragCancel,
87
+ onDragStart,
88
+ onKeyboardDragStart,
89
+ serializeData = serializeDefaultData,
90
+ type,
91
+ }: UseDraggableOptions<TData>): UseDraggableReturn => {
92
+ const dragId = useId();
93
+ const dragRef = useRef<HTMLElement | null>(null);
94
+ const [isDragging, setIsDragging] = useState(false);
95
+
96
+ const getDragData = useCallback((): DragData<TData> => {
97
+ const rawData = serializeData(data);
98
+
99
+ return {
100
+ data,
101
+ rawData,
102
+ type,
103
+ };
104
+ }, [data, serializeData, type]);
105
+
106
+ useEffect(() => {
107
+ const unsubscribe = subscribeToKeyboardDrag(() => {
108
+ if (getActiveKeyboardDrag()?.id !== dragId) {
109
+ setIsDragging(false);
110
+ }
111
+ });
112
+
113
+ return () => {
114
+ unsubscribe();
115
+ if (getActiveKeyboardDrag()?.id === dragId) {
116
+ clearActiveKeyboardDrag();
117
+ }
118
+ };
119
+ }, [dragId]);
120
+
121
+ useEffect(() => {
122
+ if (disabled && getActiveKeyboardDrag()?.id === dragId) {
123
+ clearActiveKeyboardDrag();
124
+ }
125
+ }, [disabled, dragId]);
126
+
127
+ const getDragProps = useCallback(
128
+ <TElement extends HTMLElement = HTMLElement>({
129
+ onDragEnd: userOnDragEnd,
130
+ onDragStart: userOnDragStart,
131
+ onKeyDown: userOnKeyDown,
132
+ ref: userRef,
133
+ tabIndex,
134
+ ...props
135
+ }: HTMLAttributes<TElement> & { ref?: Ref<TElement> } = {}) => {
136
+ const ref: RefCallback<TElement> = (element) => {
137
+ dragRef.current = element;
138
+ setRef(userRef, element);
139
+ };
140
+
141
+ const onDragStartHandler = (event: DragEvent<TElement>) => {
142
+ userOnDragStart?.(event);
143
+ if (disabled || event.defaultPrevented) return;
144
+
145
+ const dragData = getDragData();
146
+
147
+ event.dataTransfer.effectAllowed = effectAllowed;
148
+ event.dataTransfer.setData(type, dragData.rawData ?? '');
149
+ event.dataTransfer.setData(
150
+ UIKIT_DND_DATA_TYPE,
151
+ JSON.stringify({
152
+ data: dragData.rawData,
153
+ type,
154
+ })
155
+ );
156
+
157
+ if (dragImage) {
158
+ event.dataTransfer.setDragImage(
159
+ dragImage,
160
+ dragImageOffset.x,
161
+ dragImageOffset.y
162
+ );
163
+ }
164
+
165
+ setIsDragging(true);
166
+ onDragStart?.(dragData, event);
167
+ };
168
+
169
+ const onDragEndHandler = (event: DragEvent<TElement>) => {
170
+ userOnDragEnd?.(event);
171
+ setIsDragging(false);
172
+ onDragEnd?.(event);
173
+ };
174
+
175
+ const onKeyDownHandler = (event: KeyboardEvent<TElement>) => {
176
+ userOnKeyDown?.(event);
177
+ if (disabled || !keyboardDrag || event.defaultPrevented) return;
178
+
179
+ if (event.target !== event.currentTarget) return;
180
+
181
+ if (event.key === 'Escape' && isDragging) {
182
+ event.preventDefault();
183
+ const dragData = getDragData();
184
+ clearActiveKeyboardDrag();
185
+ setIsDragging(false);
186
+ onKeyboardDragCancel?.(dragData);
187
+ return;
188
+ }
189
+
190
+ if (
191
+ (event.key !== 'Enter' && event.key !== ' ') ||
192
+ getActiveKeyboardDrag()
193
+ ) {
194
+ return;
195
+ }
196
+
197
+ event.preventDefault();
198
+
199
+ const dragData = getDragData();
200
+ setActiveKeyboardDrag({
201
+ ...dragData,
202
+ id: dragId,
203
+ rawData: dragData.rawData ?? '',
204
+ });
205
+ setIsDragging(true);
206
+ onKeyboardDragStart?.(dragData);
207
+ };
208
+
209
+ return {
210
+ ...props,
211
+ ref,
212
+ draggable: !disabled,
213
+ tabIndex: disabled ? -1 : keyboardDrag ? (tabIndex ?? 0) : tabIndex,
214
+ onDragEnd: onDragEndHandler,
215
+ onDragStart: onDragStartHandler,
216
+ onKeyDown: onKeyDownHandler,
217
+ };
218
+ },
219
+ [
220
+ disabled,
221
+ dragImage,
222
+ dragImageOffset.x,
223
+ dragImageOffset.y,
224
+ effectAllowed,
225
+ getDragData,
226
+ isDragging,
227
+ keyboardDrag,
228
+ onKeyboardDragCancel,
229
+ onDragEnd,
230
+ onKeyboardDragStart,
231
+ onDragStart,
232
+ type,
233
+ dragId,
234
+ ]
235
+ );
236
+
237
+ return {
238
+ getDragProps,
239
+ isDragging,
240
+ };
241
+ };
242
+
243
+ export default useDraggable;