playbook_ui 15.5.0.pre.rc.4 → 15.6.0.pre.alpha.play265012865

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +96 -6
  3. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +1 -1
  4. data/app/pb_kits/playbook/pb_background/_background.tsx +6 -6
  5. data/app/pb_kits/playbook/pb_background/background.test.js +5 -1
  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 +0 -1
  8. data/app/pb_kits/playbook/pb_background/docs/_background_light.md +1 -0
  9. data/app/pb_kits/playbook/pb_background/docs/example.yml +2 -2
  10. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.tsx +6 -0
  11. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +31 -0
  12. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +156 -6
  13. data/app/pb_kits/playbook/pb_draggable/context/types.ts +8 -3
  14. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.jsx +180 -0
  15. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_multiple_containers_dropzone.md +22 -0
  16. data/app/pb_kits/playbook/pb_draggable/docs/example.yml +3 -2
  17. data/app/pb_kits/playbook/pb_draggable/docs/index.js +2 -1
  18. data/app/pb_kits/playbook/pb_draggable/draggable.test.jsx +77 -1
  19. data/app/pb_kits/playbook/pb_file_upload/_file_upload.scss +4 -4
  20. data/app/pb_kits/playbook/pb_home_address_street/_home_address_street.tsx +34 -22
  21. data/app/pb_kits/playbook/pb_home_address_street/city_emphasis.html.erb +16 -12
  22. data/app/pb_kits/playbook/pb_home_address_street/docs/_home_address_street_default.html.erb +1 -1
  23. data/app/pb_kits/playbook/pb_home_address_street/none_emphasis.html.erb +16 -12
  24. data/app/pb_kits/playbook/pb_home_address_street/street_emphasis.html.erb +16 -12
  25. data/app/pb_kits/playbook/pb_legend/_legend.tsx +7 -1
  26. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.scss +10 -0
  27. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +66 -15
  28. data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.jsx +42 -0
  29. data/app/pb_kits/playbook/pb_multiple_users/docs/_multiple_users_with_tooltip.md +1 -0
  30. data/app/pb_kits/playbook/pb_multiple_users/docs/example.yml +1 -0
  31. data/app/pb_kits/playbook/pb_multiple_users/docs/index.js +1 -0
  32. data/app/pb_kits/playbook/pb_multiple_users/multiple_users.test.js +25 -0
  33. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +44 -10
  34. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.html.erb +34 -4
  35. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_validation.jsx +16 -7
  36. data/app/pb_kits/playbook/pb_table/styles/_vertical_border.scss +49 -0
  37. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +15 -0
  38. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -0
  39. data/app/pb_kits/playbook/pb_typeahead/components/ClearIndicator.tsx +13 -2
  40. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +6 -1
  41. data/app/pb_kits/playbook/pb_typeahead/components/ValueContainer.tsx +34 -7
  42. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.html.erb +30 -0
  43. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.jsx +37 -0
  44. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_input_display.md +3 -0
  45. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  46. data/app/pb_kits/playbook/pb_typeahead/docs/index.js +2 -1
  47. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +6 -1
  48. data/app/pb_kits/playbook/utilities/DEPRECATION_WARNINGS.md +82 -0
  49. data/app/pb_kits/playbook/utilities/deprecated.ts +65 -0
  50. data/dist/chunks/_typeahead-Mjy3POtc.js +6 -0
  51. data/dist/chunks/vendor.js +2 -2
  52. data/dist/menu.yml +3 -2
  53. data/dist/playbook-rails-react-bindings.js +1 -1
  54. data/dist/playbook-rails.js +1 -1
  55. data/dist/playbook.css +1 -1
  56. data/lib/playbook/version.rb +2 -2
  57. metadata +13 -4
  58. data/app/pb_kits/playbook/pb_bar_graph/BarGraphStyles.scss +0 -58
  59. data/dist/chunks/_typeahead-Cd3O38ts.js +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 334e531aefb16fff6a6b9405de7a7d2882190b10e318a429fddd3db0efe5e393
4
- data.tar.gz: f8e7dede555ff290830f962dc6f42b1eccdbe69ce71cb26f5687bef18aaf882b
3
+ metadata.gz: 1f00eed8d72dd338dfb0665fdbc61354194cc91f5cdf0755368f84f909c8d251
4
+ data.tar.gz: 31c851467170e08092848d43f25a82f61ce30c41aba7814eada0d8a663319806
5
5
  SHA512:
6
- metadata.gz: e8372ee5464a1993ec758aa5dc633be3c00b0b1b57185827e4325227053fa7bbf093814a5bff43956f7466ab6f31e03772e82e4723109a35369ab3c840be0372
7
- data.tar.gz: a62f5d3abde3f18cb8e4df32959d4d5cdeab6adb1294e4f8c482bebf65bf93173da832c1769ef803f9cc77b7145dac1d2e92dc33b6fb826a9e560d6008d760bb
6
+ metadata.gz: 9ed414cfd8bf2656d273e36c8ad280b64ced9172ae1914b42aaa652b4799738834f0d36206059a892f4cffa272af41ecfeaaeb0325e18f014bce04a78d697388
7
+ data.tar.gz: 2e155137b2e170bc7704c3a3419d61b9ae9da0f21d5505fdff5552e4d175433656e9cd9fe6d7fae829e903999b1917c02de35a382107d38aace4eee21e297581
@@ -791,6 +791,28 @@
791
791
  box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
792
792
  }
793
793
 
794
+ // Override last-header-cell border color in dark mode
795
+ .pb_advanced_table_header {
796
+ > tr:not(:first-child) {
797
+ .last-header-cell {
798
+ border-right: 1px solid $border_dark !important;
799
+ }
800
+ }
801
+
802
+ th[colspan]:not([colspan="1"]) {
803
+ border-right: 1px solid $border_dark !important;
804
+ }
805
+ }
806
+
807
+ // Override last-cell border color in dark mode for body cells
808
+ .pb_advanced_table_body {
809
+ tr {
810
+ .last-cell:not(:last-of-type) {
811
+ border-right: 1px solid $border_dark !important;
812
+ }
813
+ }
814
+ }
815
+
794
816
  // Apply border colors in dark mode
795
817
  &[class*="column-group-border-"] {
796
818
  // For top-level column groups (ENROLLMENT DATA, PERFORMANCE DATA)
@@ -850,7 +872,7 @@
850
872
  // Restore vertical border styling in dark mode when verticalBorder is true
851
873
  .pb_table[data-vertical-border="true"] {
852
874
  .pb_advanced_table_header {
853
- > tr:not(:first-child) {
875
+ > tr {
854
876
  th:not(:last-child) {
855
877
  border-right: 1px solid $border_dark !important;
856
878
  }
@@ -864,11 +886,17 @@
864
886
  }
865
887
  }
866
888
 
889
+ tr.virtualized-table-row {
890
+ td:not(:last-child) {
891
+ border-right: 1px solid $border_dark !important;
892
+ }
893
+ }
894
+
867
895
  // When both verticalBorder AND columnGroupBorderColor are set in dark mode,
868
896
  // override the default border-dark with the custom color
869
897
  &.pb_advanced_table[class*="column-group-border-"] {
870
898
  .pb_advanced_table_header {
871
- > tr:not(:first-child) {
899
+ > tr {
872
900
  th:not(:last-child) {
873
901
  border-right: 1px solid var(--column-border-color) !important;
874
902
  }
@@ -881,6 +909,12 @@
881
909
  border-right: 1px solid var(--column-border-color) !important;
882
910
  }
883
911
  }
912
+
913
+ tr.virtualized-table-row {
914
+ td:not(:last-child) {
915
+ border-right: 1px solid var(--column-border-color) !important;
916
+ }
917
+ }
884
918
  }
885
919
  }
886
920
 
@@ -985,34 +1019,90 @@
985
1019
  // Firefox-specific fix for last-header-cell and last-cell vertical borders
986
1020
  @-moz-document url-prefix() {
987
1021
  .pb_advanced_table_header {
988
- .last-header-cell:not(:last-child) {
1022
+ th[colspan]:not([colspan="1"]):not(:last-child) {
1023
+ border-right: none !important;
1024
+ box-shadow: 1px 0 0 0 $border_light !important;
1025
+ }
1026
+
1027
+ .last-header-cell:not(:last-child),
1028
+ > tr:last-child .last-header-cell:not(:last-child) {
989
1029
  border-right: none !important;
990
1030
  box-shadow: 1px 0 0 0 $border_light !important;
991
1031
  }
992
1032
  }
993
1033
 
994
1034
  .pb_advanced_table_body {
995
- .last-cell:not(:last-child) {
1035
+ tr .last-cell:not(:last-of-type),
1036
+ td.last-cell:not(:last-child),
1037
+ .pb_table_td.last-cell:not(:last-child) {
996
1038
  border-right: none !important;
997
1039
  box-shadow: 1px 0 0 0 $border_light !important;
998
1040
  }
999
1041
  }
1000
1042
 
1043
+ &[class*="column-group-border-"] {
1044
+ .pb_advanced_table_header {
1045
+ th[colspan]:not([colspan="1"]):not(:last-child),
1046
+ .last-header-cell:not(:last-child),
1047
+ > tr:last-child .last-header-cell:not(:last-child) {
1048
+ box-shadow: 1px 0 0 0 var(--column-border-color) !important;
1049
+ }
1050
+ }
1051
+
1052
+ .pb_advanced_table_body {
1053
+ tr .last-cell:not(:last-of-type),
1054
+ td.last-cell:not(:last-child),
1055
+ .pb_table_td.last-cell:not(:last-child) {
1056
+ box-shadow: 1px 0 0 0 var(--column-border-color) !important;
1057
+ }
1058
+ }
1059
+ }
1060
+
1001
1061
  // Dark mode Firefox fixes
1002
1062
  &.dark {
1003
1063
  .pb_advanced_table_header {
1004
- .last-header-cell:not(:last-child) {
1064
+ // Convert all colspan headers to box-shadow with dark color
1065
+ th[colspan]:not([colspan="1"]) {
1066
+ border-right: none !important;
1067
+ box-shadow: 1px 0 0 0 $border_dark !important;
1068
+ }
1069
+
1070
+ // Convert all last-header-cell borders to box-shadow with dark color
1071
+ .last-header-cell:not(:last-child),
1072
+ > tr:last-child .last-header-cell:not(:last-child),
1073
+ > tr:not(:first-child) .last-header-cell:not(:last-child),
1074
+ > tr:not(:first-child) .last-header-cell:last-child {
1005
1075
  border-right: none !important;
1006
1076
  box-shadow: 1px 0 0 0 $border_dark !important;
1007
1077
  }
1008
1078
  }
1009
1079
 
1010
1080
  .pb_advanced_table_body {
1011
- .last-cell:not(:last-child) {
1081
+ tr .last-cell:not(:last-of-type),
1082
+ td.last-cell:not(:last-child),
1083
+ .pb_table_td.last-cell:not(:last-child) {
1012
1084
  border-right: none !important;
1013
1085
  box-shadow: 1px 0 0 0 $border_dark !important;
1014
1086
  }
1015
1087
  }
1088
+
1089
+ &[class*="column-group-border-"] {
1090
+ .pb_advanced_table_header {
1091
+ th[colspan]:not([colspan="1"]):not(:last-child),
1092
+ .last-header-cell:not(:last-child),
1093
+ > tr:last-child .last-header-cell:not(:last-child) {
1094
+ box-shadow: 1px 0 0 0 var(--column-border-color) !important;
1095
+ }
1096
+ }
1097
+
1098
+ .pb_advanced_table_body {
1099
+ tr .last-cell:not(:last-of-type),
1100
+ td.last-cell:not(:last-child),
1101
+ .pb_table_td.last-cell:not(:last-child) {
1102
+ box-shadow: 1px 0 0 0 var(--column-border-color) !important;
1103
+ }
1104
+ }
1105
+ }
1016
1106
  }
1017
1107
  }
1018
1108
  }
@@ -30,4 +30,4 @@
30
30
  }
31
31
  ] %>
32
32
 
33
- <%= pb_rails("advanced_table", props: { id: "table_props_table", table_data: @table_data, column_definitions: column_definitions, table_props: { vertical_border: true, container: false }}) %>
33
+ <%= pb_rails("advanced_table", props: { id: "table_props_table", table_data: @table_data, column_definitions: column_definitions, table_props: { vertical_border: true, container: false }}) %>
@@ -102,16 +102,16 @@ const Background = (props: BackgroundProps): React.ReactElement => {
102
102
  useEffect(() => {
103
103
  const updateResponsiveProps = () => {
104
104
  setResponsiveProps({
105
- backgroundSize: getResponsiveValue(props.backgroundSize),
106
- backgroundPosition: getResponsiveValue(props.backgroundPosition),
107
- backgroundRepeat: getResponsiveValue(props.backgroundRepeat),
108
- backgroundColor: getResponsiveValue(props.backgroundColor),
109
- imageUrl: getResponsiveValue(props.imageUrl),
105
+ backgroundSize: getResponsiveValue(backgroundSize),
106
+ backgroundPosition: getResponsiveValue(backgroundPosition),
107
+ backgroundRepeat: getResponsiveValue(backgroundRepeat),
108
+ backgroundColor: getResponsiveValue(backgroundColor),
109
+ imageUrl: getResponsiveValue(imageUrl),
110
110
  });
111
111
  };
112
112
  window.addEventListener('resize', updateResponsiveProps);
113
113
  return () => window.removeEventListener('resize', updateResponsiveProps);
114
- }, [props]);
114
+ }, [backgroundSize, backgroundPosition, backgroundRepeat, backgroundColor, imageUrl]);
115
115
 
116
116
 
117
117
  // Extract currently applicable responsive values.
@@ -4,7 +4,6 @@ import Background from './_background'
4
4
 
5
5
  const props = {
6
6
  data: { testid: 'background' },
7
- backgroundColor: null,
8
7
  }
9
8
 
10
9
  it('Should be accessible', async () => {
@@ -42,3 +41,8 @@ test('applies correct overlay class when imageOverlay prop is provided', () => {
42
41
  const kit = renderKit(Background, props, { imageOverlay: 'opacity_6' });
43
42
  expect(kit).toHaveClass('imageoverlay_opacity_6');
44
43
  });
44
+
45
+ test('Sets backgroundColor to light as default when no backgroundColor prop is provided', () => {
46
+ const kit = renderKit(Background, props);
47
+ expect(kit).toHaveClass('pb_background_color_light');
48
+ });
@@ -1,3 +1,3 @@
1
- <%= pb_rails("background", props: { background_color: "light", padding: "xl" }) do %>
1
+ <%= pb_rails("background", props: { padding: "xl" }) do %>
2
2
 
3
3
  <% end %>
@@ -3,7 +3,6 @@ import Background from '../../pb_background/_background'
3
3
 
4
4
  const BackgroundLight = (props) => (
5
5
  <Background
6
- backgroundColor="light"
7
6
  padding="xl"
8
7
  {...props}
9
8
  />
@@ -0,0 +1 @@
1
+ By default, the Background kit sets background color to 'light' as seen here.
@@ -1,7 +1,7 @@
1
1
  examples:
2
2
 
3
3
  rails:
4
- - background_light: Light
4
+ - background_light: Default
5
5
  - background_white: White
6
6
  - background_gradient: Gradient
7
7
  - background_image: Image
@@ -11,7 +11,7 @@ examples:
11
11
  - background_size: Size
12
12
 
13
13
  react:
14
- - background_light: Light
14
+ - background_light: Default
15
15
  - background_white: White
16
16
  - background_gradient: Gradient
17
17
  - background_image: Image
@@ -1,6 +1,7 @@
1
1
  import React, { useState, useEffect } from "react";
2
2
  import { globalProps } from "../utilities/globalProps";
3
3
  import { buildAriaProps, buildDataProps, buildHtmlProps } from "../utilities/props";
4
+ import { deprecatedKitWarning } from "../utilities/deprecated";
4
5
 
5
6
  import HighchartsReact from "highcharts-react-official";
6
7
  import Highcharts from "highcharts";
@@ -168,6 +169,11 @@ if (Array.isArray(axisTitle) && axisTitle.length > 1 && axisTitle[1].name) {
168
169
 
169
170
  const [options, setOptions] = useState({});
170
171
 
172
+ useEffect(() => {
173
+ // Warn about deprecated kit (only once per page load, dev mode only)
174
+ deprecatedKitWarning('BarGraph', '[Playbook] The "BarGraph" kit is deprecated. Please use "PbBarGraph" instead.');
175
+ }, []);
176
+
171
177
  useEffect(() => {
172
178
  setOptions(merge(staticOptions, customOptions));
173
179
  }, [chartData]);
@@ -1,3 +1,24 @@
1
+ <%
2
+ options = [
3
+ {
4
+ label: "United States",
5
+ value: "unitedStates",
6
+ id: "us"
7
+ },
8
+ {
9
+ label: "United Kingdom",
10
+ value: "unitedKingdom",
11
+ id: "gb"
12
+ },
13
+ {
14
+ label: "Pakistan",
15
+ value: "pakistan",
16
+ id: "pk"
17
+ }
18
+ ]
19
+ %>
20
+
21
+
1
22
  <%= pb_rails("button", props: { text: "Open Complex Dialog", data:{"open-dialog": "dialog-complex"} }) %>
2
23
 
3
24
  <%= pb_rails("dialog", props: { id:"dialog-complex", size: "lg", full_height: true }) do %>
@@ -10,6 +31,16 @@
10
31
  <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
11
32
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
12
33
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
34
+ <%= pb_rails("dropdown", props: {options: options, autocomplete: true}) %>
35
+ <%= pb_rails("typeahead", props: {
36
+ id: "typeahead-default",
37
+ placeholder: "Select one...",
38
+ options: options,
39
+ name: :foo,
40
+ margin_top: "sm",
41
+ is_multi: false
42
+ })
43
+ %>
13
44
 
14
45
  <% end %>
15
46
  <%= pb_rails("dialog/dialog_footer", props: {cancel_button: "Back", confirm_button: "Send my Issue", confirm_button_id:"confirm-complex", id: "dialog-complex"}) %>
@@ -39,6 +39,59 @@ 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
+
42
95
  default:
43
96
  return state;
44
97
  }
@@ -61,7 +114,9 @@ export const DraggableProvider = ({
61
114
  onDrop,
62
115
  onDragOver,
63
116
  dropZone = { type: 'ghost', color: 'neutral', direction: 'vertical' },
64
- providerId = 'default', // fallback provided for backward compatibility, so this does not become a required prop
117
+ providerId = 'default', // fallback provided for backward compatibility
118
+ // Opt-in flag for cross-container preview
119
+ enableCrossContainerPreview = false,
65
120
  }: DraggableProviderType) => {
66
121
  const [state, dispatch] = useReducer(reducer, initialState);
67
122
 
@@ -103,17 +158,74 @@ export const DraggableProvider = ({
103
158
  if (state.dragData.originId !== providerId) return; // Ignore drag events from other providers
104
159
 
105
160
  if (state.dragData.id !== id) {
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 } });
161
+ if (enableCrossContainerPreview) {
162
+ // Used only when enableCrossContainerPreview is true
163
+ const draggedItem = state.items.find(
164
+ (item) => item && item.id === state.dragData.id
165
+ );
166
+ const currentContainer =
167
+ draggedItem && draggedItem.container
168
+ ? draggedItem.container
169
+ : state.dragData.initialGroup;
170
+
171
+ const isCrossContainer =
172
+ currentContainer !== container &&
173
+ (currentContainer !== undefined || container !== undefined);
174
+
175
+ if (isCrossContainer) {
176
+ dispatch({
177
+ type: "REORDER_ITEMS_CROSS_CONTAINER",
178
+ payload: {
179
+ dragId: state.dragData.id,
180
+ targetId: id,
181
+ newContainer: container,
182
+ },
183
+ });
184
+ } else {
185
+ // Same container: keep original behavior
186
+ dispatch({
187
+ type: "REORDER_ITEMS",
188
+ payload: { dragId: state.dragData.id, targetId: id },
189
+ });
190
+ }
191
+ } else {
192
+ // Original behavior (no preview across containers)
193
+ dispatch({type: "REORDER_ITEMS", payload: { dragId: state.dragData.id, targetId: id }});
194
+ }
195
+
196
+ dispatch({type: "SET_DRAG_DATA",payload: {id: state.dragData.id, initialGroup: container, originId: providerId}});
108
197
  }
109
198
  if (onDragEnter) onDragEnter(id, container);
110
199
  };
111
200
 
112
201
  const handleDragEnd = () => {
202
+ const draggedItemId = state.dragData.id;
203
+ const originalContainer = state.dragData.initialGroup;
204
+
113
205
  dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
114
206
  dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
115
207
  dispatch({ type: 'SET_DRAG_DATA', payload: { id: "", initialGroup: "", originId: "" } });
116
- if (onDragEnd) onDragEnd();
208
+ if (onDragEnd) {
209
+ if (!enableCrossContainerPreview) {
210
+ onDragEnd();
211
+ } else {
212
+ const draggedItem = state.items.find(item => item && item.id === draggedItemId);
213
+ const finalContainer = draggedItem ? draggedItem.container : originalContainer;
214
+
215
+ const itemsInContainer = state.items.filter(item => item && item.container === finalContainer);
216
+ const indexInContainer = itemsInContainer.findIndex(item => item && item.id === draggedItemId);
217
+ const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
218
+ const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
219
+
220
+ onDragEnd(
221
+ draggedItemId,
222
+ finalContainer,
223
+ originalContainer,
224
+ itemAbove,
225
+ itemBelow
226
+ );
227
+ }
228
+ }
117
229
  };
118
230
 
119
231
  const changeCategory = (itemId: string, container: string) => {
@@ -123,10 +235,34 @@ export const DraggableProvider = ({
123
235
  const handleDrop = (container: string) => {
124
236
  if (state.dragData.originId !== providerId) return; // Ignore drop events from other providers
125
237
 
238
+ const draggedItemId = state.dragData.id;
239
+ const originalContainer = state.dragData.initialGroup;
240
+
126
241
  dispatch({ type: 'SET_IS_DRAGGING', payload: "" });
127
242
  dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: "" });
128
243
  changeCategory(state.dragData.id, container);
129
- if (onDrop) onDrop(container);
244
+ if (onDrop) {
245
+ if (!enableCrossContainerPreview) {
246
+ onDrop(container);
247
+ } else {
248
+ const draggedItem = state.items.find(item => item && item.id === draggedItemId);
249
+ const updatedItem = draggedItem ? { ...draggedItem, container } : null;
250
+
251
+ const itemsInContainer = state.items.filter(item => item && item.container === container);
252
+ const indexInContainer = itemsInContainer.findIndex(item => item && item.id === draggedItemId);
253
+ const itemAbove = indexInContainer > 0 ? itemsInContainer[indexInContainer - 1] : null;
254
+ const itemBelow = indexInContainer < itemsInContainer.length - 1 ? itemsInContainer[indexInContainer + 1] : null;
255
+
256
+ onDrop(
257
+ draggedItemId,
258
+ container,
259
+ originalContainer,
260
+ updatedItem,
261
+ itemAbove,
262
+ itemBelow
263
+ );
264
+ }
265
+ }
130
266
  };
131
267
 
132
268
  const handleDragOver = (e: Event, container: string) => {
@@ -134,6 +270,20 @@ export const DraggableProvider = ({
134
270
 
135
271
  e.preventDefault();
136
272
  dispatch({ type: 'SET_ACTIVE_CONTAINER', payload: container });
273
+
274
+ if (enableCrossContainerPreview && state.dragData.id) {
275
+ // Only when enableCrossContainerPreview is true: when hovering over a different container, move item to end
276
+ const draggedItem = state.items.find(
277
+ (item) => item && item.id === state.dragData.id
278
+ );
279
+ if (draggedItem && draggedItem.container !== container) {
280
+ dispatch({
281
+ type: "MOVE_TO_CONTAINER_END",
282
+ payload: { dragId: state.dragData.id, newContainer: container },
283
+ });
284
+ }
285
+ }
286
+
137
287
  if (onDragOver) onDragOver(e, container);
138
288
  };
139
289
 
@@ -157,4 +307,4 @@ export const DraggableProvider = ({
157
307
  return (
158
308
  <DragContext.Provider value={contextValue}>{children}</DragContext.Provider>
159
309
  );
160
- };
310
+ };
@@ -18,8 +18,12 @@ 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 }
21
22
  | { type: 'CHANGE_CATEGORY'; payload: { itemId: string; container: string } }
22
- | { type: 'REORDER_ITEMS'; payload: { dragId: string; targetId: 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 } };
23
27
 
24
28
  export interface DropZoneConfig {
25
29
  type?: 'ghost' | 'outline' | 'shadow' | 'line';
@@ -33,9 +37,10 @@ export type ActionType =
33
37
  onReorder: (items: ItemType[]) => void;
34
38
  onDragStart?: (id: string, container: string) => void;
35
39
  onDragEnter?: (id: string, container: string) => void;
36
- onDragEnd?: () => void;
37
- onDrop?: (container: string) => void;
40
+ onDragEnd?: (...args: any[]) => void;
41
+ onDrop?: (...args: any[]) => void;
38
42
  onDragOver?: (e: Event, container: string) => void;
39
43
  dropZone?: DropZoneConfig | string; // Can accept string for backward compatibility
40
44
  providerId?: string;
45
+ enableCrossContainerPreview?: boolean;
41
46
  }