playbook_ui 15.5.0.pre.alpha.draggableask12815 → 15.5.0.pre.alpha.draggablefix12557
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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +6 -96
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +1 -1
- data/app/pb_kits/playbook/pb_background/_background.tsx +6 -6
- data/app/pb_kits/playbook/pb_background/background.test.js +1 -5
- data/app/pb_kits/playbook/pb_background/docs/_background_light.html.erb +1 -1
- data/app/pb_kits/playbook/pb_background/docs/_background_light.jsx +1 -0
- data/app/pb_kits/playbook/pb_background/docs/example.yml +2 -2
- data/app/pb_kits/playbook/pb_bar_graph/BarGraphStyles.scss +58 -0
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +0 -31
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +56 -233
- data/app/pb_kits/playbook/pb_draggable/context/types.ts +2 -4
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.jsx +0 -1
- data/app/pb_kits/playbook/pb_draggable/docs/example.yml +3 -3
- data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +1 -77
- data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +4 -4
- data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +22 -34
- data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.html.erb +12 -16
- data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_default.html.erb +1 -1
- data/app/pb_kits/playbook/pb_home_address_street/none_emphasis.html.erb +12 -16
- data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.html.erb +12 -16
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.scss +0 -10
- data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +15 -66
- data/app/pb_kits/playbook/pb_multiple_users/docs/example.yml +0 -1
- data/app/pb_kits/playbook/pb_multiple_users/docs/index.js +0 -1
- data/app/pb_kits/playbook/pb_multiple_users/multiple_users.test.js +0 -25
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +10 -44
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +4 -34
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +7 -16
- data/app/pb_kits/playbook/pb_table/styles/_vertical_border.scss +0 -49
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +0 -15
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +0 -3
- data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +2 -13
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +1 -6
- data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +7 -34
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +0 -2
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -2
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +1 -6
- data/dist/chunks/_typeahead-LoB4DX1N.js +6 -0
- data/dist/chunks/vendor.js +2 -2
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +4 -10
- data/app/pb_kits/playbook/pb_background/docs/_background_light.md +0 -1
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.md +0 -22
- data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.jsx +0 -42
- data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.md +0 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.html.erb +0 -30
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.jsx +0 -37
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.md +0 -3
- data/dist/chunks/_typeahead-Dt3l7Dj8.js +0 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useReducer, useContext, useEffect, useMemo
|
|
1
|
+
import React, { createContext, useReducer, useContext, useEffect, useMemo } from "react";
|
|
2
2
|
import { InitialStateType, ActionType, DraggableProviderType } from "./types";
|
|
3
3
|
|
|
4
4
|
const initialState: InitialStateType = {
|
|
@@ -39,61 +39,46 @@ const reducer = (state: InitialStateType, action: ActionType) => {
|
|
|
39
39
|
|
|
40
40
|
return { ...state, items: newItems };
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
// Used only when enableCrossContainerPreview is true
|
|
44
|
-
case "REORDER_ITEMS_CROSS_CONTAINER": {
|
|
42
|
+
case 'REORDER_ITEMS_CROSS_CONTAINER': {
|
|
45
43
|
const { dragId, targetId, newContainer } = action.payload;
|
|
46
44
|
const newItems = [...state.items];
|
|
47
|
-
const draggedItem = newItems.find(
|
|
48
|
-
|
|
49
|
-
if (!draggedItem) return state;
|
|
50
|
-
|
|
45
|
+
const draggedItem = newItems.find(item => item.id === dragId);
|
|
51
46
|
const draggedIndex = newItems.indexOf(draggedItem);
|
|
52
|
-
const targetIndex = newItems.findIndex(
|
|
53
|
-
(item) => item && item.id === targetId
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
if (draggedIndex === -1 || targetIndex === -1) return state;
|
|
47
|
+
const targetIndex = newItems.findIndex(item => item.id === targetId);
|
|
57
48
|
|
|
49
|
+
// Update container temporarily so dropzone preview works correctly
|
|
58
50
|
const updatedItem = { ...draggedItem, container: newContainer };
|
|
51
|
+
|
|
59
52
|
newItems.splice(draggedIndex, 1);
|
|
60
53
|
newItems.splice(targetIndex, 0, updatedItem);
|
|
61
54
|
|
|
62
55
|
return { ...state, items: newItems };
|
|
63
56
|
}
|
|
64
|
-
|
|
65
|
-
// Used only when enableCrossContainerPreview is true
|
|
66
|
-
case "MOVE_TO_CONTAINER_END": {
|
|
57
|
+
case 'MOVE_TO_CONTAINER_END': {
|
|
67
58
|
const { dragId, newContainer } = action.payload;
|
|
68
59
|
const newItems = [...state.items];
|
|
69
|
-
const draggedItem = newItems.find(
|
|
70
|
-
|
|
71
|
-
if (!draggedItem) return state;
|
|
72
|
-
|
|
60
|
+
const draggedItem = newItems.find(item => item.id === dragId);
|
|
73
61
|
const draggedIndex = newItems.indexOf(draggedItem);
|
|
74
|
-
if (draggedIndex === -1) return state;
|
|
75
62
|
|
|
63
|
+
// Update container temporarily so dropzone preview works correctly
|
|
76
64
|
const updatedItem = { ...draggedItem, container: newContainer };
|
|
77
65
|
|
|
78
66
|
// Remove from current position
|
|
79
67
|
newItems.splice(draggedIndex, 1);
|
|
80
68
|
|
|
81
|
-
//
|
|
82
|
-
const lastIndexInContainer = newItems
|
|
83
|
-
.map((item) => item && item.container)
|
|
84
|
-
.lastIndexOf(newContainer);
|
|
85
|
-
|
|
69
|
+
// Find the last item in the target container and insert after it
|
|
70
|
+
const lastIndexInContainer = newItems.map(item => item.container).lastIndexOf(newContainer);
|
|
86
71
|
if (lastIndexInContainer === -1) {
|
|
72
|
+
// Container is empty, add to end
|
|
87
73
|
newItems.push(updatedItem);
|
|
88
74
|
} else {
|
|
75
|
+
// Insert after last item in container
|
|
89
76
|
newItems.splice(lastIndexInContainer + 1, 0, updatedItem);
|
|
90
77
|
}
|
|
91
78
|
|
|
92
79
|
return { ...state, items: newItems };
|
|
93
80
|
}
|
|
94
|
-
|
|
95
|
-
// Reset item back to its original container (e.g., when drag ends without valid drop)
|
|
96
|
-
case "RESET_DRAG_CONTAINER": {
|
|
81
|
+
case 'RESET_DRAG_CONTAINER': {
|
|
97
82
|
const { itemId, originalContainer } = action.payload;
|
|
98
83
|
return {
|
|
99
84
|
...state,
|
|
@@ -104,7 +89,6 @@ const reducer = (state: InitialStateType, action: ActionType) => {
|
|
|
104
89
|
)
|
|
105
90
|
};
|
|
106
91
|
}
|
|
107
|
-
|
|
108
92
|
default:
|
|
109
93
|
return state;
|
|
110
94
|
}
|
|
@@ -127,24 +111,9 @@ export const DraggableProvider = ({
|
|
|
127
111
|
onDrop,
|
|
128
112
|
onDragOver,
|
|
129
113
|
dropZone = { type: 'ghost', color: 'neutral', direction: 'vertical' },
|
|
130
|
-
providerId = 'default', // fallback provided for backward compatibility
|
|
131
|
-
// Opt-in flag for cross-container preview
|
|
132
|
-
enableCrossContainerPreview = false,
|
|
114
|
+
providerId = 'default', // fallback provided for backward compatibility, so this does not become a required prop
|
|
133
115
|
}: DraggableProviderType) => {
|
|
134
116
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
135
|
-
|
|
136
|
-
// Track drag state for global listener
|
|
137
|
-
const dragStateRef = useRef<{
|
|
138
|
-
isDragging: boolean;
|
|
139
|
-
draggedItemId: string;
|
|
140
|
-
originalContainer: string;
|
|
141
|
-
dropOccurred: boolean;
|
|
142
|
-
}>({
|
|
143
|
-
isDragging: false,
|
|
144
|
-
draggedItemId: '',
|
|
145
|
-
originalContainer: '',
|
|
146
|
-
dropOccurred: false,
|
|
147
|
-
});
|
|
148
117
|
|
|
149
118
|
// Parse dropZone prop - handle both string format (backward compatibility) and object format
|
|
150
119
|
let dropZoneType = 'ghost';
|
|
@@ -174,94 +143,7 @@ export const DraggableProvider = ({
|
|
|
174
143
|
onReorder(state.items);
|
|
175
144
|
}, [state.items]);
|
|
176
145
|
|
|
177
|
-
// Monitor for failed drops by detecting mouse/pointer release during drag (this is needed for cross container preview)
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
if (!enableCrossContainerPreview) return;
|
|
180
|
-
|
|
181
|
-
const handleGlobalMouseUp = () => {
|
|
182
|
-
// If we're dragging and mouse is released, wait a bit to see if drop occurs
|
|
183
|
-
if (dragStateRef.current.isDragging) {
|
|
184
|
-
setTimeout(() => {
|
|
185
|
-
// If drop still hasn't occurred, reset
|
|
186
|
-
if (dragStateRef.current.isDragging && !dragStateRef.current.dropOccurred) {
|
|
187
|
-
dispatch({
|
|
188
|
-
type: 'RESET_DRAG_CONTAINER',
|
|
189
|
-
payload: {
|
|
190
|
-
itemId: dragStateRef.current.draggedItemId,
|
|
191
|
-
originalContainer: dragStateRef.current.originalContainer,
|
|
192
|
-
},
|
|
193
|
-
});
|
|
194
|
-
dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
|
|
195
|
-
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
|
|
196
|
-
dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
|
|
197
|
-
|
|
198
|
-
// Clear drag state
|
|
199
|
-
dragStateRef.current = {
|
|
200
|
-
isDragging: false,
|
|
201
|
-
draggedItemId: '',
|
|
202
|
-
originalContainer: '',
|
|
203
|
-
dropOccurred: false,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}, 50); // Small delay to let drop event fire if it's going to
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
document.addEventListener('mouseup', handleGlobalMouseUp);
|
|
211
|
-
document.addEventListener('pointerup', handleGlobalMouseUp);
|
|
212
|
-
|
|
213
|
-
return () => {
|
|
214
|
-
document.removeEventListener('mouseup', handleGlobalMouseUp);
|
|
215
|
-
document.removeEventListener('pointerup', handleGlobalMouseUp);
|
|
216
|
-
};
|
|
217
|
-
}, [enableCrossContainerPreview]);
|
|
218
|
-
|
|
219
|
-
// Detect when dragging stops (isDragging goes from truthy to empty)
|
|
220
|
-
const prevIsDraggingRef = useRef(state.isDragging);
|
|
221
|
-
|
|
222
|
-
useEffect(() => {
|
|
223
|
-
if (!enableCrossContainerPreview) return;
|
|
224
|
-
|
|
225
|
-
const wasDragging = prevIsDraggingRef.current;
|
|
226
|
-
const isNowDragging = state.isDragging;
|
|
227
|
-
|
|
228
|
-
// Drag just ended (was dragging, now not)
|
|
229
|
-
if (wasDragging && !isNowDragging) {
|
|
230
|
-
|
|
231
|
-
// If drop didn't occur, reset to original container
|
|
232
|
-
if (!dragStateRef.current.dropOccurred && dragStateRef.current.draggedItemId) {
|
|
233
|
-
dispatch({
|
|
234
|
-
type: 'RESET_DRAG_CONTAINER',
|
|
235
|
-
payload: {
|
|
236
|
-
itemId: dragStateRef.current.draggedItemId,
|
|
237
|
-
originalContainer: dragStateRef.current.originalContainer,
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Clear drag state
|
|
243
|
-
dragStateRef.current = {
|
|
244
|
-
isDragging: false,
|
|
245
|
-
draggedItemId: '',
|
|
246
|
-
originalContainer: '',
|
|
247
|
-
dropOccurred: false,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
prevIsDraggingRef.current = isNowDragging;
|
|
252
|
-
}, [state.isDragging, enableCrossContainerPreview]);
|
|
253
|
-
|
|
254
146
|
const handleDragStart = (id: string, container: string) => {
|
|
255
|
-
// Track drag in ref for global listener
|
|
256
|
-
if (enableCrossContainerPreview) {
|
|
257
|
-
dragStateRef.current = {
|
|
258
|
-
isDragging: true,
|
|
259
|
-
draggedItemId: id,
|
|
260
|
-
originalContainer: container,
|
|
261
|
-
dropOccurred: false,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
147
|
dispatch({ type: 'SET_DRAG_DATA', payload: { id: id, initialGroup: container, originId: providerId } });
|
|
266
148
|
dispatch({ type: 'SET_IS_DRAGGING', payload: id });
|
|
267
149
|
if (onDragStart) onDragStart(id, container);
|
|
@@ -271,42 +153,17 @@ export const DraggableProvider = ({
|
|
|
271
153
|
if (state.dragData.originId !== providerId) return; // Ignore drag events from other providers
|
|
272
154
|
|
|
273
155
|
if (state.dragData.id !== id) {
|
|
274
|
-
if
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
draggedItem && draggedItem.container
|
|
281
|
-
? draggedItem.container
|
|
282
|
-
: state.dragData.initialGroup;
|
|
283
|
-
|
|
284
|
-
const isCrossContainer =
|
|
285
|
-
currentContainer !== container &&
|
|
286
|
-
(currentContainer !== undefined || container !== undefined);
|
|
287
|
-
|
|
288
|
-
if (isCrossContainer) {
|
|
289
|
-
dispatch({
|
|
290
|
-
type: "REORDER_ITEMS_CROSS_CONTAINER",
|
|
291
|
-
payload: {
|
|
292
|
-
dragId: state.dragData.id,
|
|
293
|
-
targetId: id,
|
|
294
|
-
newContainer: container,
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
} else {
|
|
298
|
-
// Same container: keep original behavior
|
|
299
|
-
dispatch({
|
|
300
|
-
type: "REORDER_ITEMS",
|
|
301
|
-
payload: { dragId: state.dragData.id, targetId: id },
|
|
302
|
-
});
|
|
303
|
-
}
|
|
156
|
+
// Check if this is a cross-container drag
|
|
157
|
+
const isCrossContainer = state.dragData.initialGroup !== container;
|
|
158
|
+
|
|
159
|
+
if (isCrossContainer) {
|
|
160
|
+
// Use cross-container reorder to update container temporarily for dropzone preview
|
|
161
|
+
dispatch({ type: 'REORDER_ITEMS_CROSS_CONTAINER', payload: { dragId: state.dragData.id, targetId: id, newContainer: container } });
|
|
304
162
|
} else {
|
|
305
|
-
//
|
|
306
|
-
dispatch({type:
|
|
163
|
+
// Same container: use normal reorder no need to be fancy nancy
|
|
164
|
+
dispatch({ type: 'REORDER_ITEMS', payload: { dragId: state.dragData.id, targetId: id } });
|
|
307
165
|
}
|
|
308
|
-
|
|
309
|
-
dispatch({type: "SET_DRAG_DATA",payload: {id: state.dragData.id, initialGroup: container, originId: providerId}});
|
|
166
|
+
dispatch({ type: 'SET_DRAG_DATA', payload: { id: state.dragData.id, initialGroup: container, originId: providerId } });
|
|
310
167
|
}
|
|
311
168
|
if (onDragEnter) onDragEnter(id, container);
|
|
312
169
|
};
|
|
@@ -314,37 +171,21 @@ export const DraggableProvider = ({
|
|
|
314
171
|
const handleDragEnd = () => {
|
|
315
172
|
const draggedItemId = state.dragData.id;
|
|
316
173
|
const originalContainer = state.dragData.initialGroup;
|
|
174
|
+
const draggedItem = state.items.find(item => item.id === draggedItemId);
|
|
175
|
+
const finalContainer = draggedItem ? draggedItem.container : originalContainer;
|
|
317
176
|
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
177
|
+
// Find items above and below in the same container
|
|
178
|
+
const itemsInContainer = state.items.filter(item => item.container === finalContainer);
|
|
179
|
+
const indexInContainer = itemsInContainer.findIndex(item => item.id === draggedItemId);
|
|
180
|
+
const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
|
|
181
|
+
const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
|
|
322
182
|
|
|
323
183
|
dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
|
|
324
184
|
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
|
|
325
185
|
dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
|
|
326
186
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
onDragEnd();
|
|
330
|
-
} else {
|
|
331
|
-
const draggedItem = state.items.find(item => item && item.id === draggedItemId);
|
|
332
|
-
const finalContainer = draggedItem ? draggedItem.container : originalContainer;
|
|
333
|
-
|
|
334
|
-
const itemsInContainer = state.items.filter(item => item && item.container === finalContainer);
|
|
335
|
-
const indexInContainer = itemsInContainer.findIndex(item => item && item.id === draggedItemId);
|
|
336
|
-
const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
|
|
337
|
-
const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
|
|
338
|
-
|
|
339
|
-
onDragEnd(
|
|
340
|
-
draggedItemId,
|
|
341
|
-
finalContainer,
|
|
342
|
-
originalContainer,
|
|
343
|
-
itemAbove,
|
|
344
|
-
itemBelow
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
187
|
+
// Pass enhanced info to onDragEnd callback to give dev more context
|
|
188
|
+
if (onDragEnd) onDragEnd(draggedItemId, finalContainer, originalContainer, itemAbove, itemBelow);
|
|
348
189
|
};
|
|
349
190
|
|
|
350
191
|
const changeCategory = (itemId: string, container: string) => {
|
|
@@ -356,36 +197,23 @@ export const DraggableProvider = ({
|
|
|
356
197
|
|
|
357
198
|
const draggedItemId = state.dragData.id;
|
|
358
199
|
const originalContainer = state.dragData.initialGroup;
|
|
200
|
+
const draggedItem = state.items.find(item => item.id === draggedItemId);
|
|
201
|
+
|
|
202
|
+
// Find items above and below in the same container (before changeCategory updates it)
|
|
203
|
+
const itemsInContainer = state.items.filter(item => item.container === container);
|
|
204
|
+
const indexInContainer = itemsInContainer.findIndex(item => item.id === draggedItemId);
|
|
205
|
+
const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
|
|
206
|
+
const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
|
|
359
207
|
|
|
360
|
-
// Mark drop as successful in ref for global listener
|
|
361
|
-
if (enableCrossContainerPreview) {
|
|
362
|
-
dragStateRef.current.dropOccurred = true;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
208
|
dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
|
|
366
209
|
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
|
|
367
|
-
changeCategory
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const itemsInContainer = state.items.filter(item => item && item.container === container);
|
|
376
|
-
const indexInContainer = itemsInContainer.findIndex(item => item && item.id === draggedItemId);
|
|
377
|
-
const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
|
|
378
|
-
const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
|
|
379
|
-
|
|
380
|
-
onDrop(
|
|
381
|
-
draggedItemId,
|
|
382
|
-
container,
|
|
383
|
-
originalContainer,
|
|
384
|
-
updatedItem,
|
|
385
|
-
itemAbove,
|
|
386
|
-
itemBelow
|
|
387
|
-
);
|
|
388
|
-
}
|
|
210
|
+
// changeCategory will ensure the container is set correctly on drop for cross container and same container drops
|
|
211
|
+
changeCategory(draggedItemId, container);
|
|
212
|
+
|
|
213
|
+
// Pass enhanced info to onDrop callback so devs have more context
|
|
214
|
+
if (onDrop && draggedItem) {
|
|
215
|
+
const updatedItem = { ...draggedItem, container };
|
|
216
|
+
onDrop(draggedItemId, container, originalContainer, updatedItem, itemAbove, itemBelow);
|
|
389
217
|
}
|
|
390
218
|
};
|
|
391
219
|
|
|
@@ -394,20 +222,15 @@ export const DraggableProvider = ({
|
|
|
394
222
|
|
|
395
223
|
e.preventDefault();
|
|
396
224
|
dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: container });
|
|
397
|
-
|
|
398
|
-
if
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
dispatch({
|
|
405
|
-
type: "MOVE_TO_CONTAINER_END",
|
|
406
|
-
payload: { dragId: state.dragData.id, newContainer: container },
|
|
407
|
-
});
|
|
408
|
-
}
|
|
225
|
+
|
|
226
|
+
// Check if we're dragging over a different container than where the item currently is
|
|
227
|
+
const draggedItem = state.items.find(item => item.id === state.dragData.id);
|
|
228
|
+
if (draggedItem && draggedItem.container !== container) {
|
|
229
|
+
// This handles the case when dragging to empty space at bottom of container OR in empty container
|
|
230
|
+
dispatch({ type: 'MOVE_TO_CONTAINER_END', payload: { dragId: state.dragData.id, newContainer: container } });
|
|
231
|
+
dispatch({ type: 'SET_DRAG_DATA', payload: { id: state.dragData.id, initialGroup: container, originId: providerId } });
|
|
409
232
|
}
|
|
410
|
-
|
|
233
|
+
|
|
411
234
|
if (onDragOver) onDragOver(e, container);
|
|
412
235
|
};
|
|
413
236
|
|
|
@@ -431,4 +254,4 @@ export const DraggableProvider = ({
|
|
|
431
254
|
return (
|
|
432
255
|
<DragContext.Provider value={contextValue}>{children}</DragContext.Provider>
|
|
433
256
|
);
|
|
434
|
-
};
|
|
257
|
+
};
|
|
@@ -18,7 +18,6 @@ export type ActionType =
|
|
|
18
18
|
} }
|
|
19
19
|
| { type: 'SET_IS_DRAGGING'; payload: string }
|
|
20
20
|
| { type: 'SET_ACTIVE_CONTAINER'; payload: string }
|
|
21
|
-
| { type: 'SET_CROSS_CONTAINER_PREVIEW'; payload: boolean }
|
|
22
21
|
| { type: 'CHANGE_CATEGORY'; payload: { itemId: string; container: string } }
|
|
23
22
|
| { type: 'REORDER_ITEMS'; payload: { dragId: string; targetId: string } }
|
|
24
23
|
| { type: 'REORDER_ITEMS_CROSS_CONTAINER'; payload: { dragId: string; targetId: string; newContainer: string } }
|
|
@@ -37,10 +36,9 @@ export type ActionType =
|
|
|
37
36
|
onReorder: (items: ItemType[]) => void;
|
|
38
37
|
onDragStart?: (id: string, container: string) => void;
|
|
39
38
|
onDragEnter?: (id: string, container: string) => void;
|
|
40
|
-
onDragEnd?: (
|
|
41
|
-
onDrop?: (
|
|
39
|
+
onDragEnd?: (draggedItemId: string, finalContainer: string, originalContainer: string, itemAbove: ItemType | null, itemBelow: ItemType | null) => void;
|
|
40
|
+
onDrop?: (draggedItemId: string, droppedContainer: string, originalContainer: string, item: ItemType, itemAbove: ItemType | null, itemBelow: ItemType | null) => void;
|
|
42
41
|
onDragOver?: (e: Event, container: string) => void;
|
|
43
42
|
dropZone?: DropZoneConfig | string; // Can accept string for backward compatibility
|
|
44
43
|
providerId?: string;
|
|
45
|
-
enableCrossContainerPreview?: boolean;
|
|
46
44
|
}
|
|
@@ -83,7 +83,6 @@ const DraggableMultipleContainersDropzone = (props) => {
|
|
|
83
83
|
return (
|
|
84
84
|
<DraggableProvider
|
|
85
85
|
dropZone={{type: "outline"}}
|
|
86
|
-
enableCrossContainerPreview
|
|
87
86
|
initialItems={data}
|
|
88
87
|
onDragEnd={(draggedItemId, finalContainer, originalContainer, itemAbove, itemBelow) => {
|
|
89
88
|
console.log(`Dragged Item ID: ${draggedItemId}`);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
examples:
|
|
2
2
|
react:
|
|
3
3
|
- draggable_default: Default
|
|
4
|
+
- draggable_multiple_containers_dropzone: Dragging Across Multiple Containers with Dropzones
|
|
4
5
|
- draggable_with_list: Draggable with List Kit
|
|
5
6
|
- draggable_with_selectable_list: Draggable with SelectableList Kit
|
|
6
7
|
- draggable_with_cards: Draggable with Cards
|
|
7
8
|
- draggable_with_table: Draggable with Table
|
|
9
|
+
- draggable_multiple_containers: Dragging Across Multiple Containers
|
|
8
10
|
- draggable_drop_zones: Draggable Drop Zones
|
|
9
11
|
- draggable_drop_zones_colors: Draggable Drop Zones Colors
|
|
10
12
|
- draggable_drop_zones_line: Draggable Drop Zones Line
|
|
11
13
|
- draggable_event_listeners: Draggable Event Listeners
|
|
12
|
-
- draggable_multiple_containers: Dragging Across Multiple Containers
|
|
13
|
-
- draggable_multiple_containers_dropzone: Dragging Across Multiple Containers with Dropzones
|
|
14
14
|
|
|
15
15
|
rails:
|
|
16
16
|
- draggable_default: Default
|
|
@@ -18,8 +18,8 @@ examples:
|
|
|
18
18
|
- draggable_with_selectable_list: Draggable with SelectableList Kit
|
|
19
19
|
- draggable_with_cards: Draggable with Cards
|
|
20
20
|
- draggable_with_table: Draggable with Table
|
|
21
|
+
- draggable_multiple_containers: Dragging Across Multiple Containers
|
|
21
22
|
- draggable_drop_zones: Draggable Drop Zones
|
|
22
23
|
- draggable_drop_zones_colors: Draggable Drop Zones Colors
|
|
23
24
|
- draggable_drop_zones_line: Draggable Drop Zones Line
|
|
24
25
|
- draggable_event_listeners: Draggable Event Listeners
|
|
25
|
-
- draggable_multiple_containers: Dragging Across Multiple Containers
|
|
@@ -255,80 +255,4 @@ test("line dropZone with horizontal direction applies 'line_horizontal' class to
|
|
|
255
255
|
const container = kit.querySelector(".pb_draggable_container");
|
|
256
256
|
|
|
257
257
|
expect(container).toHaveClass("line_horizontal");
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Cross-container drag tests
|
|
261
|
-
const multiContainerData = [
|
|
262
|
-
{ id: "1", container: "To Do", text: "Task 1" },
|
|
263
|
-
{ id: "2", container: "To Do", text: "Task 2" },
|
|
264
|
-
{ id: "3", container: "In Progress", text: "Task 3" },
|
|
265
|
-
{ id: "4", container: "Done", text: "Task 4" },
|
|
266
|
-
];
|
|
267
|
-
|
|
268
|
-
const containers = ["To Do", "In Progress", "Done"];
|
|
269
|
-
|
|
270
|
-
const DraggableMultipleContainers = () => {
|
|
271
|
-
const [initialState, setInitialState] = useState(multiContainerData);
|
|
272
|
-
|
|
273
|
-
return (
|
|
274
|
-
<div data-testid={testId}>
|
|
275
|
-
<DraggableProvider
|
|
276
|
-
dropZone={{ type: "outline" }}
|
|
277
|
-
initialItems={multiContainerData}
|
|
278
|
-
onReorder={(items) => setInitialState(items)}
|
|
279
|
-
>
|
|
280
|
-
{containers.map((container) => (
|
|
281
|
-
<Draggable.Container
|
|
282
|
-
container={container}
|
|
283
|
-
data={{testid:`container-${container}`}}
|
|
284
|
-
key={container}
|
|
285
|
-
>
|
|
286
|
-
{initialState
|
|
287
|
-
.filter((item) => item.container === container)
|
|
288
|
-
.map(({ id, text }) => (
|
|
289
|
-
<Draggable.Item
|
|
290
|
-
container={container}
|
|
291
|
-
data-testid={`item-${id}`}
|
|
292
|
-
dragId={id}
|
|
293
|
-
key={id}
|
|
294
|
-
>
|
|
295
|
-
{text}
|
|
296
|
-
</Draggable.Item>
|
|
297
|
-
))}
|
|
298
|
-
</Draggable.Container>
|
|
299
|
-
))}
|
|
300
|
-
</DraggableProvider>
|
|
301
|
-
</div>
|
|
302
|
-
);
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
test("renders multiple containers with correct items", () => {
|
|
306
|
-
render(<DraggableMultipleContainers />);
|
|
307
|
-
|
|
308
|
-
const kit = screen.getByTestId(testId);
|
|
309
|
-
expect(kit).toBeInTheDocument();
|
|
310
|
-
|
|
311
|
-
containers.forEach((container) => {
|
|
312
|
-
const containerEl = kit.querySelector(`[data-testid="container-${container}"]`);
|
|
313
|
-
expect(containerEl).toBeInTheDocument();
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// Check items are in correct containers
|
|
317
|
-
expect(screen.getByText("Task 1")).toBeInTheDocument();
|
|
318
|
-
expect(screen.getByText("Task 2")).toBeInTheDocument();
|
|
319
|
-
expect(screen.getByText("Task 3")).toBeInTheDocument();
|
|
320
|
-
expect(screen.getByText("Task 4")).toBeInTheDocument();
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
test("items have correct container association", () => {
|
|
324
|
-
const { container } = render(<DraggableMultipleContainers />);
|
|
325
|
-
|
|
326
|
-
// items rendered within their respective containers
|
|
327
|
-
const todoContainer = container.querySelector('[data-testid="container-To Do"]');
|
|
328
|
-
const inProgressContainer = container.querySelector('[data-testid="container-In Progress"]');
|
|
329
|
-
const doneContainer = container.querySelector('[data-testid="container-Done"]');
|
|
330
|
-
|
|
331
|
-
expect(todoContainer.querySelectorAll('.pb_draggable_item')).toHaveLength(2);
|
|
332
|
-
expect(inProgressContainer.querySelectorAll('.pb_draggable_item')).toHaveLength(1);
|
|
333
|
-
expect(doneContainer.querySelectorAll('.pb_draggable_item')).toHaveLength(1);
|
|
334
|
-
})
|
|
258
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.pb_file_upload_kit {
|
|
2
|
-
.
|
|
2
|
+
.pb_card_kit_deselected_border_radius_md {
|
|
3
3
|
border: 1px #ccc dashed;
|
|
4
4
|
text-align: center;
|
|
5
5
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
&.error,
|
|
13
13
|
&.pb_file_upload_kit_error {
|
|
14
|
-
.
|
|
14
|
+
.pb_card_kit_deselected_border_radius_md {
|
|
15
15
|
border-color: $error;
|
|
16
16
|
}
|
|
17
17
|
.pb_body_kit_negative {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.dark .pb_file_upload_kit {
|
|
33
|
-
.
|
|
33
|
+
.pb_card_kit_deselected_border_radius_md {
|
|
34
34
|
border: 1px $text_dk_lighter dashed;
|
|
35
35
|
}
|
|
36
36
|
&.error,
|
|
37
37
|
&.pb_file_upload_kit_error {
|
|
38
|
-
.
|
|
38
|
+
.pb_card_kit_deselected_border_radius_md {
|
|
39
39
|
border-color: $error_dark;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -96,24 +96,20 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
|
96
96
|
{hasAllEmptyProps && '—'}
|
|
97
97
|
{emphasis == 'street' && !hasAllEmptyProps &&
|
|
98
98
|
<div>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
>
|
|
114
|
-
{titleize(addressCont)}
|
|
115
|
-
</Title>
|
|
116
|
-
)}
|
|
99
|
+
<Title
|
|
100
|
+
className="pb_home_address_street_address"
|
|
101
|
+
dark={dark}
|
|
102
|
+
size={4}
|
|
103
|
+
>
|
|
104
|
+
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
|
105
|
+
</Title>
|
|
106
|
+
<Title
|
|
107
|
+
className="pb_home_address_street_address"
|
|
108
|
+
dark={dark}
|
|
109
|
+
size={4}
|
|
110
|
+
>
|
|
111
|
+
{titleize(addressCont)}
|
|
112
|
+
</Title>
|
|
117
113
|
<Body color="light">
|
|
118
114
|
{`${city ? `${titleize(city)}, ` : ''}${uppercaseState}${zipcode ? ` ${zipcode}` : ''}`}
|
|
119
115
|
</Body>
|
|
@@ -121,14 +117,10 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
|
121
117
|
}
|
|
122
118
|
{emphasis == 'city' && !hasAllEmptyProps &&
|
|
123
119
|
<div>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)}
|
|
129
|
-
{addressCont && (
|
|
130
|
-
<Body color="light">{titleize(addressCont)}</Body>
|
|
131
|
-
)}
|
|
120
|
+
<Body color="light">
|
|
121
|
+
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
|
122
|
+
</Body>
|
|
123
|
+
<Body color="light">{titleize(addressCont)}</Body>
|
|
132
124
|
<div>
|
|
133
125
|
<Title
|
|
134
126
|
className="pb_home_address_street_address"
|
|
@@ -149,14 +141,10 @@ const HomeAddressStreet = (props: HomeAddressStreetProps): React.ReactElement =>
|
|
|
149
141
|
}
|
|
150
142
|
{emphasis == 'none' && !hasAllEmptyProps &&
|
|
151
143
|
<div>
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)}
|
|
157
|
-
{addressCont && (
|
|
158
|
-
<Body dark={dark}>{formatStreetAdr(addressCont)}</Body>
|
|
159
|
-
)}
|
|
144
|
+
<Body dark={dark}>
|
|
145
|
+
{joinPresent([formatStreetAdr(address), houseStyle], ' · ')}
|
|
146
|
+
</Body>
|
|
147
|
+
<Body dark={dark}>{formatStreetAdr(addressCont)}</Body>
|
|
160
148
|
<div>
|
|
161
149
|
<Body
|
|
162
150
|
color="light"
|