playbook_ui 15.6.0.pre.alpha.play266013023 → 15.6.0.pre.rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +163 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.jsx +190 -0
  4. data/app/pb_kits/playbook/pb_background/_background.tsx +6 -6
  5. data/app/pb_kits/playbook/pb_background/background.test.js +1 -5
  6. data/app/pb_kits/playbook/pb_background/docs/_background_light.html.erb +1 -1
  7. data/app/pb_kits/playbook/pb_background/docs/_background_light.jsx +1 -0
  8. data/app/pb_kits/playbook/pb_background/docs/example.yml +2 -2
  9. data/app/pb_kits/playbook/pb_card/docs/_card_header.md +1 -1
  10. data/app/pb_kits/playbook/pb_card/docs/_card_highlight.md +1 -1
  11. data/app/pb_kits/playbook/pb_collapsible/__snapshots__/collapsible.test.js.snap +2 -2
  12. data/app/pb_kits/playbook/pb_collapsible/child_kits/CollapsibleIcon.tsx +8 -10
  13. data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_icons.jsx +1 -0
  14. data/app/pb_kits/playbook/pb_collapsible/docs/_collapsible_state.jsx +3 -0
  15. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +0 -24
  16. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +3 -181
  17. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +1 -2
  18. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +1 -1
  19. data/app/pb_kits/playbook/pb_dialog/dialog.rb +0 -1
  20. data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +0 -14
  21. data/app/pb_kits/playbook/pb_dialog/dialog_header.html.erb +4 -5
  22. data/app/pb_kits/playbook/pb_dialog/dialog_header.rb +0 -2
  23. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +0 -31
  24. data/app/pb_kits/playbook/pb_dialog/docs/example.yml +0 -2
  25. data/app/pb_kits/playbook/pb_dialog/docs/index.js +1 -2
  26. data/app/pb_kits/playbook/pb_distribution_bar/docs/_distribution_bar_custom_colors.md +1 -1
  27. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +7 -458
  28. data/app/pb_kits/playbook/pb_draggable/context/types.ts +3 -8
  29. data/app/pb_kits/playbook/pb_draggable/docs/example.yml +2 -3
  30. data/app/pb_kits/playbook/pb_draggable/docs/index.js +1 -2
  31. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +1 -77
  32. data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +4 -4
  33. data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +22 -34
  34. data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.html.erb +12 -16
  35. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_default.html.erb +1 -1
  36. data/app/pb_kits/playbook/pb_home_address_street/none_emphasis.html.erb +12 -16
  37. data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.html.erb +12 -16
  38. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.scss +0 -10
  39. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +15 -66
  40. data/app/pb_kits/playbook/pb_multiple_users/docs/example.yml +0 -1
  41. data/app/pb_kits/playbook/pb_multiple_users/docs/index.js +0 -1
  42. data/app/pb_kits/playbook/pb_multiple_users/multiple_users.test.js +0 -25
  43. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +10 -44
  44. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +4 -34
  45. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +7 -16
  46. data/app/pb_kits/playbook/pb_radio/docs/_radio_error.md +1 -1
  47. data/app/pb_kits/playbook/pb_select/_select.tsx +3 -8
  48. data/app/pb_kits/playbook/pb_select/docs/_select_error.md +1 -1
  49. data/app/pb_kits/playbook/pb_select/docs/example.yml +0 -2
  50. data/app/pb_kits/playbook/pb_select/docs/index.js +0 -1
  51. data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
  52. data/app/pb_kits/playbook/pb_select/select.rb +1 -3
  53. data/app/pb_kits/playbook/pb_select/select.test.js +0 -23
  54. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_error.md +1 -1
  55. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_error.md +1 -1
  56. data/app/pb_kits/playbook/pb_timeline/_item.tsx +0 -3
  57. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_date.md +1 -1
  58. data/app/pb_kits/playbook/pb_timeline/docs/example.yml +0 -2
  59. data/app/pb_kits/playbook/pb_timeline/docs/index.js +0 -1
  60. data/app/pb_kits/playbook/pb_timeline/item.html.erb +1 -1
  61. data/app/pb_kits/playbook/pb_timeline/item.rb +0 -2
  62. data/app/pb_kits/playbook/pb_timeline/label.html.erb +1 -2
  63. data/app/pb_kits/playbook/pb_timeline/label.rb +0 -2
  64. data/app/pb_kits/playbook/pb_timeline/subcomponents/Label.tsx +0 -3
  65. data/app/pb_kits/playbook/pb_timeline/timeline.test.js +0 -51
  66. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +0 -15
  67. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +0 -3
  68. data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +2 -13
  69. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +1 -6
  70. data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +7 -34
  71. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +0 -2
  72. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -2
  73. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +1 -6
  74. data/app/pb_kits/playbook/tokens/_colors.scss +1 -2
  75. data/dist/chunks/_typeahead-kRdz5zPn.js +6 -0
  76. data/dist/chunks/lib-CgpqUb6l.js +29 -0
  77. data/dist/chunks/vendor.js +2 -2
  78. data/dist/playbook-rails-react-bindings.js +1 -1
  79. data/dist/playbook-rails.js +1 -1
  80. data/dist/playbook.css +1 -1
  81. data/lib/playbook/forms/builder/collection_select_field.rb +1 -9
  82. data/lib/playbook/forms/builder/select_field.rb +1 -9
  83. data/lib/playbook/forms/builder/time_zone_select_field.rb +1 -9
  84. data/lib/playbook/pb_kit_helper.rb +0 -35
  85. data/lib/playbook/version.rb +2 -2
  86. metadata +4 -22
  87. data/app/pb_kits/playbook/pb_background/docs/_background_light.md +0 -1
  88. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.html.erb +0 -24
  89. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.jsx +0 -60
  90. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_closeable.md +0 -3
  91. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.jsx +0 -180
  92. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.md +0 -22
  93. data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.jsx +0 -42
  94. data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.md +0 -1
  95. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.html.erb +0 -16
  96. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.jsx +0 -30
  97. data/app/pb_kits/playbook/pb_select/docs/_select_input_options.md +0 -1
  98. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.html.erb +0 -60
  99. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.jsx +0 -118
  100. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_show_current_year.md +0 -1
  101. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.html.erb +0 -30
  102. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.jsx +0 -37
  103. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.md +0 -3
  104. data/app/pb_kits/playbook/utilities/deprecated.ts +0 -73
  105. data/dist/chunks/_typeahead-CYNrKU10.js +0 -6
  106. data/dist/chunks/lib-DDDLiZuu.js +0 -29
@@ -1,4 +1,4 @@
1
- import React, { createContext, useReducer, useContext, useEffect, useMemo, useRef } from "react";
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,88 +39,6 @@ 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": {
45
- const { dragId, targetId, newContainer } = action.payload;
46
- const newItems = [...state.items];
47
- const draggedItem = newItems.find((item) => item && item.id === dragId);
48
-
49
- if (!draggedItem) return state;
50
-
51
- 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;
57
-
58
- const updatedItem = { ...draggedItem, container: newContainer };
59
- newItems.splice(draggedIndex, 1);
60
- newItems.splice(targetIndex, 0, updatedItem);
61
-
62
- return { ...state, items: newItems };
63
- }
64
-
65
- // Used only when enableCrossContainerPreview is true
66
- case "MOVE_TO_CONTAINER_END": {
67
- const { dragId, newContainer } = action.payload;
68
- const newItems = [...state.items];
69
- const draggedItem = newItems.find((item) => item && item.id === dragId);
70
-
71
- if (!draggedItem) return state;
72
-
73
- const draggedIndex = newItems.indexOf(draggedItem);
74
- if (draggedIndex === -1) return state;
75
-
76
- const updatedItem = { ...draggedItem, container: newContainer };
77
-
78
- // Remove from current position
79
- newItems.splice(draggedIndex, 1);
80
-
81
- // Insert at end of target container
82
- const lastIndexInContainer = newItems
83
- .map((item) => item && item.container)
84
- .lastIndexOf(newContainer);
85
-
86
- if (lastIndexInContainer === -1) {
87
- newItems.push(updatedItem);
88
- } else {
89
- newItems.splice(lastIndexInContainer + 1, 0, updatedItem);
90
- }
91
-
92
- return { ...state, items: newItems };
93
- }
94
-
95
- // Reset item back to its original container and position (e.g., when drag ends without valid drop)
96
- case "RESET_DRAG_CONTAINER": {
97
- const { itemId, originalContainer, originalIndex } = action.payload;
98
- const newItems = [...state.items];
99
- const draggedItem = newItems.find(item => item && item.id === itemId);
100
-
101
- if (!draggedItem) return state;
102
-
103
- const currentIndex = newItems.indexOf(draggedItem);
104
-
105
- // Remove from current position
106
- newItems.splice(currentIndex, 1);
107
-
108
- // Restore container property and insert at original index
109
- const restoredItem = { ...draggedItem, container: originalContainer };
110
-
111
- // Insert at original index, or at end if index is invalid
112
- if (originalIndex !== undefined && originalIndex >= 0) {
113
- newItems.splice(originalIndex, 0, restoredItem);
114
- } else {
115
- newItems.push(restoredItem);
116
- }
117
-
118
- return {
119
- ...state,
120
- items: newItems
121
- };
122
- }
123
-
124
42
  default:
125
43
  return state;
126
44
  }
@@ -143,34 +61,9 @@ export const DraggableProvider = ({
143
61
  onDrop,
144
62
  onDragOver,
145
63
  dropZone = { type: 'ghost', color: 'neutral', direction: 'vertical' },
146
- providerId = 'default', // fallback provided for backward compatibility
147
- // Opt-in flag for cross-container preview
148
- enableCrossContainerPreview = false,
64
+ providerId = 'default', // fallback provided for backward compatibility, so this does not become a required prop
149
65
  }: DraggableProviderType) => {
150
66
  const [state, dispatch] = useReducer(reducer, initialState);
151
-
152
- // Track drag state for global listener
153
- const dragStateRef = useRef<{
154
- isDragging: boolean;
155
- draggedItemId: string;
156
- originalContainer: string;
157
- originalIndex: number;
158
- currentContainer: string;
159
- dropOccurred: boolean;
160
- }>({
161
- isDragging: false,
162
- draggedItemId: '',
163
- originalContainer: '',
164
- originalIndex: -1,
165
- currentContainer: '',
166
- dropOccurred: false,
167
- });
168
-
169
- // Track current state for use in gated event listeners (avoid stale closures)
170
- const stateRef = useRef(state);
171
- useEffect(() => {
172
- stateRef.current = state;
173
- }, [state]);
174
67
 
175
68
  // Parse dropZone prop - handle both string format (backward compatibility) and object format
176
69
  let dropZoneType = 'ghost';
@@ -200,209 +93,7 @@ export const DraggableProvider = ({
200
93
  onReorder(state.items);
201
94
  }, [state.items]);
202
95
 
203
- // Monitor for failed drops by detecting mouse/pointer release during drag (this is needed for cross container preview)
204
- useEffect(() => {
205
- if (!enableCrossContainerPreview) return;
206
-
207
- // Allow drops anywhere on the document by preventing default dragover
208
- const handleGlobalDragOver = (e: DragEvent) => {
209
- if (dragStateRef.current.isDragging) {
210
- e.preventDefault();
211
- }
212
- };
213
-
214
- // Handle drops anywhere on the document (including non-container areas)
215
- const handleGlobalDrop = (e: DragEvent) => {
216
- if (!dragStateRef.current.isDragging) return;
217
-
218
- // If a container already handled the drop, don't process again
219
- if (dragStateRef.current.dropOccurred) return;
220
-
221
- e.preventDefault();
222
-
223
- // If we reach here, it means the drop was NOT on a valid container
224
- // (otherwise the container's handleDrop would have set dropOccurred = true)
225
- // So we should ALWAYS reset to original container for invalid drops
226
- const originalContainer = dragStateRef.current.originalContainer;
227
-
228
- dispatch({
229
- type: 'RESET_DRAG_CONTAINER',
230
- payload: {
231
- itemId: dragStateRef.current.draggedItemId,
232
- originalContainer: originalContainer,
233
- originalIndex: dragStateRef.current.originalIndex,
234
- },
235
- });
236
-
237
- dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
238
- dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
239
- dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
240
-
241
- // Clear drag state
242
- dragStateRef.current = {
243
- isDragging: false,
244
- draggedItemId: '',
245
- originalContainer: '',
246
- originalIndex: -1,
247
- currentContainer: '',
248
- dropOccurred: false,
249
- };
250
- };
251
-
252
- const handleGlobalMouseUp = () => {
253
- // If we're dragging and mouse is released, wait a bit to see if drop occurs
254
- if (dragStateRef.current.isDragging) {
255
- setTimeout(() => {
256
- const currentContainer = dragStateRef.current.currentContainer;
257
-
258
- // If drop still hasn't occurred, check if item is in a different container
259
- if (dragStateRef.current.isDragging && !dragStateRef.current.dropOccurred) {
260
- // If item is in a different container than original, treat it as a successful drop
261
- if (currentContainer && currentContainer !== dragStateRef.current.originalContainer) {
262
- // Trigger onDrop callback with the current container
263
- if (onDrop) {
264
- const draggedItem = stateRef.current.items.find(item => item && item.id === dragStateRef.current.draggedItemId);
265
- const updatedItem = draggedItem ? { ...draggedItem, container: currentContainer } : null;
266
- const itemsInContainer = stateRef.current.items.filter(item => item && item.container === currentContainer);
267
- const indexInContainer = itemsInContainer.findIndex(item => item && item.id === dragStateRef.current.draggedItemId);
268
- const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
269
- const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
270
-
271
- onDrop(
272
- dragStateRef.current.draggedItemId,
273
- currentContainer,
274
- dragStateRef.current.originalContainer,
275
- updatedItem,
276
- itemAbove,
277
- itemBelow
278
- );
279
- }
280
- } else {
281
- dispatch({
282
- type: 'RESET_DRAG_CONTAINER',
283
- payload: {
284
- itemId: dragStateRef.current.draggedItemId,
285
- originalContainer: dragStateRef.current.originalContainer,
286
- originalIndex: dragStateRef.current.originalIndex,
287
- },
288
- });
289
- }
290
- dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
291
- dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
292
- dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
293
-
294
- // Clear drag state
295
- dragStateRef.current = {
296
- isDragging: false,
297
- draggedItemId: '',
298
- originalContainer: '',
299
- originalIndex: -1,
300
- currentContainer: '',
301
- dropOccurred: false,
302
- };
303
- }
304
- }, 50); // Small delay to let drop event fire if it's going to
305
- }
306
- };
307
-
308
- // Detect when drag leaves document boundaries
309
- const handleDragLeave = (e: DragEvent) => {
310
- // Check if we're leaving the document (relatedTarget will be null)
311
- if (!e.relatedTarget && dragStateRef.current.isDragging && !dragStateRef.current.dropOccurred) {
312
- // Drag left the document: reset to original container immediately
313
- dispatch({
314
- type: 'RESET_DRAG_CONTAINER',
315
- payload: {
316
- itemId: dragStateRef.current.draggedItemId,
317
- originalContainer: dragStateRef.current.originalContainer,
318
- originalIndex: dragStateRef.current.originalIndex,
319
- },
320
- });
321
- dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
322
- dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
323
- dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
324
-
325
- // Clear drag state
326
- dragStateRef.current = {
327
- isDragging: false,
328
- draggedItemId: '',
329
- originalContainer: '',
330
- originalIndex: -1,
331
- currentContainer: '',
332
- dropOccurred: false,
333
- };
334
- }
335
- };
336
-
337
- document.addEventListener('dragover', handleGlobalDragOver);
338
- document.addEventListener('drop', handleGlobalDrop);
339
- document.addEventListener('dragleave', handleDragLeave);
340
- document.addEventListener('mouseup', handleGlobalMouseUp);
341
- document.addEventListener('pointerup', handleGlobalMouseUp);
342
-
343
- return () => {
344
- document.removeEventListener('dragover', handleGlobalDragOver);
345
- document.removeEventListener('drop', handleGlobalDrop);
346
- document.removeEventListener('dragleave', handleDragLeave);
347
- document.removeEventListener('mouseup', handleGlobalMouseUp);
348
- document.removeEventListener('pointerup', handleGlobalMouseUp);
349
- };
350
- }, [enableCrossContainerPreview]);
351
-
352
- // Detect when dragging stops (isDragging goes from truthy to empty)
353
- const prevIsDraggingRef = useRef(state.isDragging);
354
-
355
- useEffect(() => {
356
- if (!enableCrossContainerPreview) return;
357
-
358
- const wasDragging = prevIsDraggingRef.current;
359
- const isNowDragging = state.isDragging;
360
-
361
- // Drag just ended (was dragging, now not)
362
- if (wasDragging && !isNowDragging) {
363
-
364
- // If drop didn't occur, reset to original container
365
- if (!dragStateRef.current.dropOccurred && dragStateRef.current.draggedItemId) {
366
- dispatch({
367
- type: 'RESET_DRAG_CONTAINER',
368
- payload: {
369
- itemId: dragStateRef.current.draggedItemId,
370
- originalContainer: dragStateRef.current.originalContainer,
371
- originalIndex: dragStateRef.current.originalIndex,
372
- },
373
- });
374
- }
375
-
376
- // Clear drag state
377
- dragStateRef.current = {
378
- isDragging: false,
379
- draggedItemId: '',
380
- originalContainer: '',
381
- originalIndex: -1,
382
- currentContainer: '',
383
- dropOccurred: false,
384
- };
385
- }
386
-
387
- prevIsDraggingRef.current = isNowDragging;
388
- }, [state.isDragging, enableCrossContainerPreview]);
389
-
390
96
  const handleDragStart = (id: string, container: string) => {
391
- // Track drag in ref for global listener
392
- if (enableCrossContainerPreview) {
393
- // Find the original index of the item
394
- const originalIndex = state.items.findIndex(item => item && item.id === id);
395
-
396
- dragStateRef.current = {
397
- isDragging: true,
398
- draggedItemId: id,
399
- originalContainer: container,
400
- originalIndex: originalIndex,
401
- currentContainer: container,
402
- dropOccurred: false,
403
- };
404
- }
405
-
406
97
  dispatch({ type: 'SET_DRAG_DATA', payload: { id: id, initialGroup: container, originId: providerId } });
407
98
  dispatch({ type: 'SET_IS_DRAGGING', payload: id });
408
99
  if (onDragStart) onDragStart(id, container);
@@ -412,93 +103,17 @@ export const DraggableProvider = ({
412
103
  if (state.dragData.originId !== providerId) return; // Ignore drag events from other providers
413
104
 
414
105
  if (state.dragData.id !== id) {
415
- if (enableCrossContainerPreview) {
416
- // Used only when enableCrossContainerPreview is true
417
- const draggedItem = state.items.find(
418
- (item) => item && item.id === state.dragData.id
419
- );
420
- const currentContainer =
421
- draggedItem && draggedItem.container
422
- ? draggedItem.container
423
- : state.dragData.initialGroup;
424
-
425
- const isCrossContainer =
426
- currentContainer !== container &&
427
- (currentContainer !== undefined || container !== undefined);
428
-
429
- if (isCrossContainer) {
430
- dispatch({
431
- type: "REORDER_ITEMS_CROSS_CONTAINER",
432
- payload: {
433
- dragId: state.dragData.id,
434
- targetId: id,
435
- newContainer: container,
436
- },
437
- });
438
- // Update current container in ref
439
- if (enableCrossContainerPreview) {
440
- dragStateRef.current.currentContainer = container;
441
- }
442
- } else {
443
- // Same container: keep original behavior
444
- dispatch({
445
- type: "REORDER_ITEMS",
446
- payload: { dragId: state.dragData.id, targetId: id },
447
- });
448
- }
449
- } else {
450
- // Original behavior (no preview across containers)
451
- dispatch({type: "REORDER_ITEMS", payload: { dragId: state.dragData.id, targetId: id }});
452
- }
453
-
454
- // When enableCrossContainerPreview is true, preserve the original initialGroup
455
- // Otherwise, update it to track the current container
456
- const newInitialGroup = enableCrossContainerPreview ? state.dragData.initialGroup : container;
457
- dispatch({type: "SET_DRAG_DATA",payload: {id: state.dragData.id, initialGroup: newInitialGroup, originId: providerId}});
106
+ dispatch({ type: 'REORDER_ITEMS', payload: { dragId: state.dragData.id, targetId: id } });
107
+ dispatch({ type: 'SET_DRAG_DATA', payload: { id: state.dragData.id, initialGroup: container, originId: providerId } });
458
108
  }
459
109
  if (onDragEnter) onDragEnter(id, container);
460
110
  };
461
111
 
462
112
  const handleDragEnd = () => {
463
- const draggedItemId = state.dragData.id;
464
- const originalContainer = state.dragData.initialGroup;
465
-
466
- // If enableCrossContainerPreview is true and no drop occurred, reset item to original container
467
- if (enableCrossContainerPreview && !dragStateRef.current.dropOccurred && draggedItemId && originalContainer) {
468
- dispatch({ type: 'RESET_DRAG_CONTAINER', payload: { itemId: draggedItemId, originalContainer, originalIndex: dragStateRef.current.originalIndex } });
469
- }
470
-
471
113
  dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
472
114
  dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
473
115
  dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
474
-
475
- // Only call onDragEnd if drop didn't already occur (for enableCrossContainerPreview)
476
- // If drop occurred, handleDrop or global drop handler already called onDragEnd
477
- if (enableCrossContainerPreview && dragStateRef.current.dropOccurred) {
478
- return;
479
- }
480
-
481
- if (onDragEnd) {
482
- if (!enableCrossContainerPreview) {
483
- onDragEnd();
484
- } else {
485
- const draggedItem = stateRef.current.items.find(item => item && item.id === draggedItemId);
486
- const finalContainer = draggedItem ? draggedItem.container : originalContainer;
487
-
488
- const itemsInContainer = stateRef.current.items.filter(item => item && item.container === finalContainer);
489
- const indexInContainer = itemsInContainer.findIndex(item => item && item.id === draggedItemId);
490
- const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
491
- const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
492
-
493
- onDragEnd(
494
- draggedItemId,
495
- finalContainer,
496
- originalContainer,
497
- itemAbove,
498
- itemBelow
499
- );
500
- }
501
- }
116
+ if (onDragEnd) onDragEnd();
502
117
  };
503
118
 
504
119
  const changeCategory = (itemId: string, container: string) => {
@@ -508,60 +123,10 @@ export const DraggableProvider = ({
508
123
  const handleDrop = (container: string) => {
509
124
  if (state.dragData.originId !== providerId) return; // Ignore drop events from other providers
510
125
 
511
- const draggedItemId = state.dragData.id;
512
- const originalContainer = state.dragData.initialGroup;
513
-
514
- // Mark drop as successful in ref for global listener
515
- if (enableCrossContainerPreview) {
516
- dragStateRef.current.dropOccurred = true;
517
- }
518
-
519
- // Gather data for callbacks BEFORE clearing state
520
- const isCrossContainer = container !== originalContainer;
521
- let callbackData = null;
522
-
523
- if (enableCrossContainerPreview) {
524
- const draggedItem = stateRef.current.items.find(item => item && item.id === draggedItemId);
525
- const updatedItem = draggedItem ? { ...draggedItem, container } : null;
526
- const itemsInContainer = stateRef.current.items.filter(item => item && item.container === container);
527
- const indexInContainer = itemsInContainer.findIndex(item => item && item.id === draggedItemId);
528
- const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
529
- const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
530
-
531
- callbackData = { updatedItem, itemAbove, itemBelow };
532
- }
533
-
534
126
  dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
535
127
  dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
536
- dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
537
128
  changeCategory(state.dragData.id, container);
538
-
539
- if (onDrop) {
540
- if (!enableCrossContainerPreview) {
541
- onDrop(container);
542
- } else {
543
- onDrop(
544
- draggedItemId,
545
- container,
546
- originalContainer,
547
- callbackData.updatedItem,
548
- callbackData.itemAbove,
549
- callbackData.itemBelow
550
- );
551
- }
552
- }
553
-
554
- // Trigger onDragEnd ONLY for cross-container drops (dragend doesn't fire reliably in that case)
555
- // For same-container drops, handleDragEnd will be called normally
556
- if (enableCrossContainerPreview && isCrossContainer && onDragEnd && callbackData) {
557
- onDragEnd(
558
- draggedItemId,
559
- container,
560
- originalContainer,
561
- callbackData.itemAbove,
562
- callbackData.itemBelow
563
- );
564
- }
129
+ if (onDrop) onDrop(container);
565
130
  };
566
131
 
567
132
  const handleDragOver = (e: Event, container: string) => {
@@ -569,22 +134,6 @@ export const DraggableProvider = ({
569
134
 
570
135
  e.preventDefault();
571
136
  dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: container });
572
-
573
- if (enableCrossContainerPreview && state.dragData.id) {
574
- // Only when enableCrossContainerPreview is true: when hovering over a different container, move item to end
575
- const draggedItem = state.items.find(
576
- (item) => item && item.id === state.dragData.id
577
- );
578
- if (draggedItem && draggedItem.container !== container) {
579
- dispatch({
580
- type: "MOVE_TO_CONTAINER_END",
581
- payload: { dragId: state.dragData.id, newContainer: container },
582
- });
583
- // Update current container in ref
584
- dragStateRef.current.currentContainer = container;
585
- }
586
- }
587
-
588
137
  if (onDragOver) onDragOver(e, container);
589
138
  };
590
139
 
@@ -608,4 +157,4 @@ export const DraggableProvider = ({
608
157
  return (
609
158
  <DragContext.Provider value={contextValue}>{children}</DragContext.Provider>
610
159
  );
611
- };
160
+ };
@@ -18,12 +18,8 @@ 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
- | { type: 'REORDER_ITEMS'; payload: { dragId: string; targetId: string } }
24
- | { type: 'REORDER_ITEMS_CROSS_CONTAINER'; payload: { dragId: string; targetId: string; newContainer: string } }
25
- | { type: 'MOVE_TO_CONTAINER_END'; payload: { dragId: string; newContainer: string } }
26
- | { type: 'RESET_DRAG_CONTAINER'; payload: { itemId: string; originalContainer: string, originalIndex: number } };
22
+ | { type: 'REORDER_ITEMS'; payload: { dragId: string; targetId: string } };
27
23
 
28
24
  export interface DropZoneConfig {
29
25
  type?: 'ghost' | 'outline' | 'shadow' | 'line';
@@ -37,10 +33,9 @@ export type ActionType =
37
33
  onReorder: (items: ItemType[]) => void;
38
34
  onDragStart?: (id: string, container: string) => void;
39
35
  onDragEnter?: (id: string, container: string) => void;
40
- onDragEnd?: (...args: any[]) => void;
41
- onDrop?: (...args: any[]) => void;
36
+ onDragEnd?: () => void;
37
+ onDrop?: (container: string) => void;
42
38
  onDragOver?: (e: Event, container: string) => void;
43
39
  dropZone?: DropZoneConfig | string; // Can accept string for backward compatibility
44
40
  providerId?: string;
45
- enableCrossContainerPreview?: boolean;
46
41
  }
@@ -5,12 +5,11 @@ examples:
5
5
  - draggable_with_selectable_list: Draggable with SelectableList Kit
6
6
  - draggable_with_cards: Draggable with Cards
7
7
  - draggable_with_table: Draggable with Table
8
+ - draggable_multiple_containers: Dragging Across Multiple Containers
8
9
  - draggable_drop_zones: Draggable Drop Zones
9
10
  - draggable_drop_zones_colors: Draggable Drop Zones Colors
10
11
  - draggable_drop_zones_line: Draggable Drop Zones Line
11
12
  - 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
13
 
15
14
  rails:
16
15
  - draggable_default: Default
@@ -18,8 +17,8 @@ examples:
18
17
  - draggable_with_selectable_list: Draggable with SelectableList Kit
19
18
  - draggable_with_cards: Draggable with Cards
20
19
  - draggable_with_table: Draggable with Table
20
+ - draggable_multiple_containers: Dragging Across Multiple Containers
21
21
  - draggable_drop_zones: Draggable Drop Zones
22
22
  - draggable_drop_zones_colors: Draggable Drop Zones Colors
23
23
  - draggable_drop_zones_line: Draggable Drop Zones Line
24
24
  - draggable_event_listeners: Draggable Event Listeners
25
- - draggable_multiple_containers: Dragging Across Multiple Containers
@@ -7,5 +7,4 @@ export { default as DraggableWithTable } from './_draggable_with_table.jsx'
7
7
  export { default as DraggableDropZones } from './_draggable_drop_zones.jsx'
8
8
  export { default as DraggableDropZonesColors } from './_draggable_drop_zones_colors.jsx'
9
9
  export { default as DraggableDropZonesLine } from './_draggable_drop_zones_line.jsx'
10
- export { default as DraggableEventListeners } from './_draggable_event_listeners.jsx'
11
- export { default as DraggableMultipleContainersDropzone } from './_draggable_multiple_containers_dropzone.jsx'
10
+ export { default as DraggableEventListeners } from './_draggable_event_listeners.jsx'
@@ -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
+ });