@nyaruka/temba-components 0.130.4 → 0.131.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 (104) hide show
  1. package/CHANGELOG.md +10 -13
  2. package/demo/sortable-rules-demo.html +155 -0
  3. package/dist/temba-components.js +150 -159
  4. package/dist/temba-components.js.map +1 -1
  5. package/out-tsc/src/events.js.map +1 -1
  6. package/out-tsc/src/flow/CanvasNode.js +13 -7
  7. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  8. package/out-tsc/src/flow/actions/send_msg.js +1 -0
  9. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  10. package/out-tsc/src/flow/nodes/split_by_groups.js +149 -1
  11. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  12. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +1 -0
  13. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  14. package/out-tsc/src/flow/nodes/split_by_random.js +1 -0
  15. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  16. package/out-tsc/src/flow/nodes/wait_for_response.js +332 -137
  17. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  18. package/out-tsc/src/form/ArrayEditor.js +301 -30
  19. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  20. package/out-tsc/src/form/select/Omnibox.js +4 -0
  21. package/out-tsc/src/form/select/Omnibox.js.map +1 -1
  22. package/out-tsc/src/form/select/Select.js +21 -25
  23. package/out-tsc/src/form/select/Select.js.map +1 -1
  24. package/out-tsc/src/list/SortableList.js +214 -140
  25. package/out-tsc/src/list/SortableList.js.map +1 -1
  26. package/out-tsc/src/live/ContactChat.js +9 -5
  27. package/out-tsc/src/live/ContactChat.js.map +1 -1
  28. package/out-tsc/test/nodes/split_by_groups.test.js +130 -0
  29. package/out-tsc/test/nodes/split_by_groups.test.js.map +1 -0
  30. package/out-tsc/test/nodes/wait_for_response.test.js +522 -8
  31. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  32. package/out-tsc/test/temba-field-config.test.js +56 -0
  33. package/out-tsc/test/temba-field-config.test.js.map +1 -1
  34. package/package.json +1 -1
  35. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  36. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  37. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  38. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  39. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  40. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  41. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  42. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  43. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  44. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  45. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  46. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  47. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  48. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  49. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  50. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  51. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  52. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  53. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  54. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  55. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  56. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  57. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  58. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  59. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  60. package/screenshots/truth/editor/wait.png +0 -0
  61. package/screenshots/truth/field-renderer/select-with-label.png +0 -0
  62. package/screenshots/truth/list/fields-dragging.png +0 -0
  63. package/screenshots/truth/list/sortable-dragging.png +0 -0
  64. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  65. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  66. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  67. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  68. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  69. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  70. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  71. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  72. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  73. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  74. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  75. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  76. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  77. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  78. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  79. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  80. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  81. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  82. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  83. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  84. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  85. package/screenshots/truth/select/search-enabled.png +0 -0
  86. package/screenshots/truth/select/search-selected-focus.png +0 -0
  87. package/screenshots/truth/select/search-selected.png +0 -0
  88. package/screenshots/truth/templates/default.png +0 -0
  89. package/screenshots/truth/templates/unapproved.png +0 -0
  90. package/src/events.ts +6 -6
  91. package/src/flow/CanvasNode.ts +15 -13
  92. package/src/flow/actions/send_msg.ts +1 -0
  93. package/src/flow/nodes/split_by_groups.ts +190 -1
  94. package/src/flow/nodes/split_by_llm_categorize.ts +1 -0
  95. package/src/flow/nodes/split_by_random.ts +1 -0
  96. package/src/flow/nodes/wait_for_response.ts +424 -145
  97. package/src/form/ArrayEditor.ts +372 -30
  98. package/src/form/select/Omnibox.ts +3 -0
  99. package/src/form/select/Select.ts +24 -25
  100. package/src/list/SortableList.ts +250 -149
  101. package/src/live/ContactChat.ts +11 -5
  102. package/test/nodes/split_by_groups.test.ts +165 -0
  103. package/test/nodes/wait_for_response.test.ts +608 -8
  104. package/test/temba-field-config.test.ts +69 -0
@@ -18,6 +18,8 @@ export class SortableList extends RapidElement {
18
18
  .container {
19
19
  user-select: none;
20
20
  position: relative;
21
+ display: grid;
22
+ grid-template-columns: 1fr;
21
23
  }
22
24
 
23
25
  .container.horizontal {
@@ -30,58 +32,6 @@ export class SortableList extends RapidElement {
30
32
  background: var(--color-selection);
31
33
  }
32
34
 
33
- .dragged-item {
34
- opacity: 0;
35
- pointer-events: none;
36
- }
37
-
38
- .sortable {
39
- transition: all 300ms ease-in-out;
40
- display: flex;
41
- padding: 0.4em 0;
42
- }
43
-
44
- .container.horizontal .sortable {
45
- padding: 0;
46
- margin-right: 0.25em;
47
- margin-bottom: 0.25em;
48
- }
49
-
50
- .drop-indicator {
51
- position: absolute;
52
- background: var(--color-primary-dark, #1c7cd6);
53
- z-index: 1000;
54
- pointer-events: none;
55
- }
56
-
57
- .container.horizontal .drop-indicator {
58
- width: 2px;
59
- margin-top: -5px;
60
- padding-bottom: 10px;
61
- }
62
-
63
- .container:not(.horizontal) .drop-indicator {
64
- height: 2px;
65
- left: 0;
66
- }
67
-
68
- .sortable:hover temba-icon {
69
- opacity: 1;
70
- cursor: move;
71
- }
72
-
73
- .ghost {
74
- position: absolute;
75
- opacity: 0.7;
76
- transition: none;
77
- background: var(--color-background, white);
78
- border: 1px solid var(--color-primary, #1c7cd6);
79
- border-radius: 4px;
80
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
81
- pointer-events: none;
82
- z-index: 999;
83
- }
84
-
85
35
  .slot {
86
36
  flex-grow: 1;
87
37
  }
@@ -100,15 +50,18 @@ export class SortableList extends RapidElement {
100
50
  constructor() {
101
51
  super();
102
52
  this.horizontal = false;
53
+ this.gap = '0em';
103
54
  this.ghostElement = null;
104
55
  this.downEle = null;
56
+ this.originalElementRect = null; // Store original dimensions
57
+ this.originalDragIndex = -1; // Store original index before moving element
105
58
  this.xOffset = 0;
106
59
  this.yOffset = 0;
107
60
  this.yDown = 0;
108
61
  this.xDown = 0;
109
62
  this.draggingIdx = -1;
110
63
  this.draggingEle = null;
111
- this.dropIndicator = null;
64
+ this.dropPlaceholder = null;
112
65
  this.pendingDropIndex = -1;
113
66
  this.pendingTargetElement = null;
114
67
  this.clickBlocker = null;
@@ -116,14 +69,115 @@ export class SortableList extends RapidElement {
116
69
  this.handleMouseUp = this.handleMouseUp.bind(this);
117
70
  this.handleMouseDown = this.handleMouseDown.bind(this);
118
71
  }
119
- firstUpdated(_changedProperties) {
120
- super.firstUpdated(_changedProperties);
121
- }
122
72
  getSortableElements() {
123
- return this.shadowRoot
73
+ const eles = this.shadowRoot
124
74
  .querySelector('slot')
125
75
  .assignedElements()
126
- .filter((ele) => ele.classList.contains('sortable'));
76
+ .filter((ele) => ele.classList.contains('sortable') &&
77
+ !ele.classList.contains('drop-placeholder'));
78
+ return eles;
79
+ }
80
+ cloneElementWithState(element) {
81
+ // First create a basic clone
82
+ const clone = element.cloneNode(true);
83
+ // Helper function to copy form element values recursively
84
+ const copyFormValues = (original, cloned) => {
85
+ try {
86
+ // Copy input values
87
+ const originalInputs = original.querySelectorAll('input, textarea, select');
88
+ const clonedInputs = cloned.querySelectorAll('input, textarea, select');
89
+ originalInputs.forEach((originalInput, index) => {
90
+ const clonedInput = clonedInputs[index];
91
+ if (clonedInput) {
92
+ if (originalInput instanceof HTMLInputElement) {
93
+ const originalHtmlInput = originalInput;
94
+ const clonedHtmlInput = clonedInput;
95
+ if (originalHtmlInput.type === 'checkbox' ||
96
+ originalHtmlInput.type === 'radio') {
97
+ clonedHtmlInput.checked = originalHtmlInput.checked;
98
+ }
99
+ else {
100
+ clonedHtmlInput.value = originalHtmlInput.value;
101
+ }
102
+ }
103
+ else if (originalInput instanceof HTMLTextAreaElement) {
104
+ clonedInput.value = originalInput.value;
105
+ }
106
+ else if (originalInput instanceof HTMLSelectElement) {
107
+ clonedInput.selectedIndex =
108
+ originalInput.selectedIndex;
109
+ }
110
+ }
111
+ });
112
+ // Copy properties from all custom elements that might have a value property
113
+ const allOriginalElements = Array.from(original.querySelectorAll('*'));
114
+ const allClonedElements = Array.from(cloned.querySelectorAll('*'));
115
+ allOriginalElements.forEach((originalEl, index) => {
116
+ const clonedEl = allClonedElements[index];
117
+ if (clonedEl && originalEl) {
118
+ // Special handling for temba components
119
+ if (originalEl.tagName &&
120
+ originalEl.tagName.toLowerCase().startsWith('temba-')) {
121
+ try {
122
+ // Copy common temba component properties
123
+ const tembaProps = [
124
+ 'value',
125
+ 'values',
126
+ 'selectedValue',
127
+ 'checked',
128
+ 'selected',
129
+ 'textContent'
130
+ ];
131
+ tembaProps.forEach((prop) => {
132
+ if (prop in originalEl &&
133
+ originalEl[prop] !== undefined) {
134
+ clonedEl[prop] = originalEl[prop];
135
+ }
136
+ });
137
+ // Copy all attributes for temba components to preserve state
138
+ Array.from(originalEl.attributes).forEach((attr) => {
139
+ clonedEl.setAttribute(attr.name, attr.value);
140
+ });
141
+ }
142
+ catch (e) {
143
+ // Ignore errors when copying temba properties
144
+ }
145
+ }
146
+ else {
147
+ // Try to copy value property for other elements
148
+ try {
149
+ if ('value' in originalEl &&
150
+ originalEl.value !== undefined) {
151
+ clonedEl.value = originalEl.value;
152
+ }
153
+ }
154
+ catch (e) {
155
+ // Ignore errors when copying properties
156
+ }
157
+ // Copy data attributes that might contain state
158
+ try {
159
+ Array.from(originalEl.attributes).forEach((attr) => {
160
+ if (attr.name.startsWith('data-') ||
161
+ attr.name.startsWith('aria-')) {
162
+ clonedEl.setAttribute(attr.name, attr.value);
163
+ }
164
+ });
165
+ }
166
+ catch (e) {
167
+ // Ignore errors when copying attributes
168
+ }
169
+ }
170
+ }
171
+ });
172
+ }
173
+ catch (e) {
174
+ // If anything fails, just return the basic clone
175
+ console.warn('Failed to copy form values in cloneElementWithState:', e);
176
+ }
177
+ };
178
+ // Copy form values for the root element and all descendants
179
+ copyFormValues(element, clone);
180
+ return clone;
127
181
  }
128
182
  getIds() {
129
183
  return this.getSortableElements().map((ele) => ele.id);
@@ -170,54 +224,57 @@ export class SortableList extends RapidElement {
170
224
  };
171
225
  }
172
226
  }
173
- showDropIndicator(targetElement, insertAfter) {
174
- this.hideDropIndicator();
175
- if (!targetElement)
227
+ showDropPlaceholder(targetElement, insertAfter) {
228
+ this.hideDropPlaceholder();
229
+ if (!targetElement || !this.draggingEle)
176
230
  return;
177
- const container = this.shadowRoot.querySelector('.container');
178
- this.dropIndicator = document.createElement('div');
179
- this.dropIndicator.className = 'drop-indicator';
180
- const targetRect = targetElement.getBoundingClientRect();
181
- const containerRect = container.getBoundingClientRect();
182
- if (this.horizontal) {
183
- // For horizontal layout, show vertical line
184
- this.dropIndicator.style.height = targetRect.height + 'px';
185
- this.dropIndicator.style.top = targetRect.top - containerRect.top + 'px';
186
- if (insertAfter) {
187
- // Show line after target
188
- this.dropIndicator.style.left =
189
- targetRect.right - containerRect.left + 'px';
190
- }
191
- else {
192
- // Show line before target
193
- this.dropIndicator.style.left =
194
- targetRect.left - containerRect.left + 'px';
195
- }
231
+ // Don't show placeholder if we're targeting the dragging element itself
232
+ if (targetElement === this.draggingEle)
233
+ return;
234
+ this.dropPlaceholder = document.createElement('div');
235
+ this.dropPlaceholder.className = 'drop-placeholder sortable';
236
+ // Copy dimensions from the original element (before it was hidden)
237
+ if (this.originalElementRect) {
238
+ const rect = this.originalElementRect;
239
+ this.dropPlaceholder.style.width = rect.width + 'px';
240
+ this.dropPlaceholder.style.height = rect.height + 'px';
241
+ this.dropPlaceholder.style.minHeight = rect.height + 'px';
242
+ this.dropPlaceholder.style.borderRadius = 'var(--curvature)';
243
+ this.dropPlaceholder.style.flexShrink = '0';
244
+ }
245
+ // Insert the placeholder in the correct position in the DOM
246
+ if (insertAfter) {
247
+ targetElement.insertAdjacentElement('afterend', this.dropPlaceholder);
196
248
  }
197
249
  else {
198
- // For vertical layout, show horizontal line
199
- this.dropIndicator.style.width = targetRect.width + 'px';
200
- this.dropIndicator.style.left =
201
- targetRect.left - containerRect.left + 'px';
202
- if (insertAfter) {
203
- // Show line after target
204
- this.dropIndicator.style.top =
205
- targetRect.bottom - containerRect.top + 'px';
206
- }
207
- else {
208
- // Show line before target
209
- this.dropIndicator.style.top =
210
- targetRect.top - containerRect.top + 'px';
211
- }
250
+ targetElement.insertAdjacentElement('beforebegin', this.dropPlaceholder);
212
251
  }
213
- container.appendChild(this.dropIndicator);
214
252
  }
215
- hideDropIndicator() {
216
- if (this.dropIndicator) {
217
- this.dropIndicator.remove();
218
- this.dropIndicator = null;
253
+ hideDropPlaceholder() {
254
+ if (this.dropPlaceholder) {
255
+ this.dropPlaceholder.remove();
256
+ this.dropPlaceholder = null;
219
257
  }
220
258
  }
259
+ showInitialPlaceholder() {
260
+ if (!this.downEle || !this.originalElementRect)
261
+ return;
262
+ this.dropPlaceholder = document.createElement('div');
263
+ this.dropPlaceholder.className = 'drop-placeholder sortable';
264
+ // Copy dimensions from the original element
265
+ const rect = this.originalElementRect;
266
+ this.dropPlaceholder.style.width = rect.width + 'px';
267
+ this.dropPlaceholder.style.height = rect.height + 'px';
268
+ this.dropPlaceholder.style.minHeight = rect.height + 'px';
269
+ this.dropPlaceholder.style.borderRadius = 'var(--curvature)';
270
+ this.dropPlaceholder.style.flexShrink = '0';
271
+ this.dropPlaceholder.style.background =
272
+ 'rgba(var(--color-primary-rgb), 0.1)';
273
+ this.dropPlaceholder.style.border =
274
+ '2px dashed rgba(var(--color-primary-rgb), 0.3)';
275
+ // Insert the placeholder right after the hidden original element
276
+ this.downEle.insertAdjacentElement('afterend', this.dropPlaceholder);
277
+ }
221
278
  handleMouseDown(event) {
222
279
  let ele = event.target;
223
280
  // if we have a drag handle, only allow dragging from that element
@@ -234,8 +291,9 @@ export class SortableList extends RapidElement {
234
291
  this.draggingId = ele.id;
235
292
  this.draggingIdx = this.getRowIndex(ele.id);
236
293
  this.draggingEle = ele;
237
- // Use getBoundingClientRect for accurate offsets
294
+ // Use getBoundingClientRect for accurate offsets and store original dimensions
238
295
  const rect = ele.getBoundingClientRect();
296
+ this.originalElementRect = rect; // Store the original rect before hiding
239
297
  this.xOffset = event.clientX - rect.left;
240
298
  this.yOffset = event.clientY - rect.top;
241
299
  this.yDown = event.clientY;
@@ -252,32 +310,33 @@ export class SortableList extends RapidElement {
252
310
  this.fireCustomEvent(CustomEventType.DragStart, {
253
311
  id: this.downEle.id
254
312
  });
255
- this.ghostElement = this.downEle.cloneNode(true);
313
+ // Capture the original index BEFORE hiding the element
314
+ this.originalDragIndex = this.getRowIndex(this.downEle.id);
315
+ // Create a clone of the element to use as the ghost
316
+ this.ghostElement = this.cloneElementWithState(this.downEle);
317
+ // Hide the original element during dragging using inline styles
318
+ this.originalDownDisplay = this.downEle.style.display;
319
+ this.downEle.style.display = 'none';
320
+ // Style the clone as a ghost
256
321
  this.ghostElement.classList.add('ghost');
257
- // dim the original element while dragging
258
- this.downEle.style.pointerEvents = 'none';
259
- this.downEle.style.opacity = '0.5';
260
- const rect = this.downEle.getBoundingClientRect();
261
- this.ghostElement.style.transition = 'transform 300ms linear';
262
- this.ghostElement.style.width = rect.width + 'px';
263
- this.ghostElement.style.height = rect.height + 'px';
322
+ // Use the stored original dimensions for positioning
323
+ const rect = this.originalElementRect;
264
324
  this.ghostElement.style.position = 'fixed';
265
325
  this.ghostElement.style.left = event.clientX - this.xOffset + 'px';
266
326
  this.ghostElement.style.top = event.clientY - this.yOffset + 'px';
267
- this.ghostElement.style.pointerEvents = 'none';
268
- this.ghostElement.style.border =
269
- '1px solid var(--color-primary, #1c7cd6)';
327
+ this.ghostElement.style.width = rect.width + 'px';
328
+ this.ghostElement.style.height = rect.height + 'px';
270
329
  this.ghostElement.style.zIndex = '99999';
271
- this.ghostElement.style.background = '#fff';
272
- this.ghostElement.style.opacity = '0.7';
273
- this.ghostElement.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2)';
274
- this.ghostElement.style.borderRadius = 'var(--curvature)';
330
+ this.ghostElement.style.opacity = '0.8';
331
+ this.ghostElement.style.transform = 'scale(1.03)';
275
332
  // allow component to customize the ghost node
276
333
  if (this.prepareGhost) {
277
334
  this.prepareGhost(this.ghostElement);
278
335
  }
336
+ // Add the clone to document.body for dragging
279
337
  document.body.appendChild(this.ghostElement);
280
- // this.downEle = null;
338
+ // Show initial placeholder in the original position to maintain layout
339
+ this.showInitialPlaceholder();
281
340
  // Add global click blocker when drag starts
282
341
  if (!this.clickBlocker) {
283
342
  this.clickBlocker = (e) => {
@@ -295,25 +354,31 @@ export class SortableList extends RapidElement {
295
354
  if (targetInfo) {
296
355
  const { element: targetElement, insertAfter } = targetInfo;
297
356
  const targetIdx = this.getRowIndex(targetElement.id);
298
- const originalDragIdx = this.getRowIndex(this.draggingEle.id);
299
- // Calculate the intended drop index
300
- let dropIdx = targetIdx;
301
- if (insertAfter) {
302
- dropIdx += 1;
357
+ // Use the original drag index we captured before moving the element
358
+ const originalDragIdx = this.originalDragIndex;
359
+ // Calculate where the dragged element will end up in the final array
360
+ // targetIdx is the position of target element in current DOM (missing dragged element)
361
+ let dropIdx;
362
+ if (targetIdx < originalDragIdx) {
363
+ // Target is before the original drag position - moving backward
364
+ dropIdx = insertAfter ? targetIdx + 1 : targetIdx;
303
365
  }
304
- // Adjust dropIdx if dragging forward in the list
305
- if (originalDragIdx < dropIdx) {
306
- dropIdx -= 1;
366
+ else {
367
+ // Target was originally after the drag position - moving forward
368
+ // When moving the dragged element forward (i.e., to a higher index), the targetIdx is based on the current DOM,
369
+ // which no longer includes the dragged element. This means all elements after the original position have shifted left by one,
370
+ // so we need to subtract 1 from targetIdx to get the correct insertion index. If inserting after the target, we use targetIdx as is.
371
+ dropIdx = insertAfter ? targetIdx : targetIdx - 1;
307
372
  }
308
373
  // Store pending drop info but don't fire event yet
309
374
  this.dropTargetId = targetElement.id;
310
375
  this.pendingDropIndex = dropIdx;
311
376
  this.pendingTargetElement = targetElement;
312
- // Show drop indicator
313
- this.showDropIndicator(targetElement, insertAfter);
377
+ // Show drop placeholder
378
+ this.showDropPlaceholder(targetElement, insertAfter);
314
379
  }
315
380
  else {
316
- this.hideDropIndicator();
381
+ this.hideDropPlaceholder();
317
382
  this.dropTargetId = null;
318
383
  this.pendingDropIndex = -1;
319
384
  this.pendingTargetElement = null;
@@ -324,14 +389,20 @@ export class SortableList extends RapidElement {
324
389
  if (this.draggingId && this.ghostElement) {
325
390
  evt.preventDefault();
326
391
  evt.stopPropagation();
327
- // restore visibility of the dragged item
392
+ // Remove the ghost clone from document.body
393
+ if (this.ghostElement) {
394
+ this.ghostElement.remove();
395
+ }
396
+ // Restore visibility of the original element by clearing inline styles
328
397
  if (this.downEle) {
329
- this.downEle.style.pointerEvents = '';
330
- this.downEle.style.opacity = '1';
398
+ this.downEle.style.display = this.originalDownDisplay;
331
399
  }
400
+ // Clear visual effects before firing events
401
+ this.hideDropPlaceholder();
332
402
  // fire the order changed event only when dropped if we have a valid drop position
333
403
  if (this.pendingDropIndex >= 0 && this.pendingTargetElement) {
334
- const originalDragIdx = this.getRowIndex(this.draggingEle.id);
404
+ // Use the original drag index we captured before hiding the element
405
+ const originalDragIdx = this.originalDragIndex;
335
406
  // use swap-based logic - report which indexes need to be swapped
336
407
  const fromIdx = originalDragIdx;
337
408
  const toIdx = this.pendingDropIndex;
@@ -348,16 +419,13 @@ export class SortableList extends RapidElement {
348
419
  this.draggingId = null;
349
420
  this.dropTargetId = null;
350
421
  this.downEle = null;
422
+ this.originalElementRect = null;
423
+ this.originalDragIndex = -1;
351
424
  this.pendingDropIndex = -1;
352
425
  this.pendingTargetElement = null;
353
- if (this.ghostElement) {
354
- // Remove from body if present
355
- if (this.ghostElement.parentNode) {
356
- this.ghostElement.parentNode.removeChild(this.ghostElement);
357
- }
358
- this.ghostElement = null;
359
- }
360
- this.hideDropIndicator();
426
+ // Clear the ghost reference since we removed it
427
+ this.ghostElement = null;
428
+ this.hideDropPlaceholder();
361
429
  // Keep the click blocker active for a short time after drop
362
430
  if (this.clickBlocker) {
363
431
  // We'll clean it up after a timeout
@@ -375,7 +443,10 @@ export class SortableList extends RapidElement {
375
443
  }
376
444
  render() {
377
445
  return html `
378
- <div class="container ${this.horizontal ? 'horizontal' : ''}">
446
+ <div
447
+ class="container ${this.horizontal ? 'horizontal' : ''}"
448
+ style="gap: ${this.gap}"
449
+ >
379
450
  <slot @mousedown=${this.handleMouseDown}></slot>
380
451
  </div>
381
452
  `;
@@ -393,6 +464,9 @@ __decorate([
393
464
  __decorate([
394
465
  property({ type: String })
395
466
  ], SortableList.prototype, "dragHandle", void 0);
467
+ __decorate([
468
+ property({ type: String })
469
+ ], SortableList.prototype, "gap", void 0);
396
470
  __decorate([
397
471
  property({ attribute: false })
398
472
  ], SortableList.prototype, "prepareGhost", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"SortableList.js","sourceRoot":"","sources":["../../../src/list/SortableList.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { CustomEventType } from '../interfaces';\nimport { RapidElement } from '../RapidElement';\n\n/**\n * A simple list that can be sorted by dragging\n */\n\n// how far we have to drag before it starts\nconst DRAG_THRESHOLD = 2;\nexport class SortableList extends RapidElement {\n static get styles() {\n return css`\n :host {\n margin: auto;\n }\n\n .container {\n user-select: none;\n position: relative;\n }\n\n .container.horizontal {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n }\n\n .dragging {\n background: var(--color-selection);\n }\n\n .dragged-item {\n opacity: 0;\n pointer-events: none;\n }\n\n .sortable {\n transition: all 300ms ease-in-out;\n display: flex;\n padding: 0.4em 0;\n }\n\n .container.horizontal .sortable {\n padding: 0;\n margin-right: 0.25em;\n margin-bottom: 0.25em;\n }\n\n .drop-indicator {\n position: absolute;\n background: var(--color-primary-dark, #1c7cd6);\n z-index: 1000;\n pointer-events: none;\n }\n\n .container.horizontal .drop-indicator {\n width: 2px;\n margin-top: -5px;\n padding-bottom: 10px;\n }\n\n .container:not(.horizontal) .drop-indicator {\n height: 2px;\n left: 0;\n }\n\n .sortable:hover temba-icon {\n opacity: 1;\n cursor: move;\n }\n\n .ghost {\n position: absolute;\n opacity: 0.7;\n transition: none;\n background: var(--color-background, white);\n border: 1px solid var(--color-primary, #1c7cd6);\n border-radius: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n pointer-events: none;\n z-index: 999;\n }\n\n .slot {\n flex-grow: 1;\n }\n\n slot > * {\n user-select: none;\n }\n\n temba-icon {\n opacity: 0.1;\n padding: 0.2em 0.5em;\n transition: all 300ms ease-in-out;\n }\n `;\n }\n\n @property({ type: String })\n draggingId: string;\n\n @property({ type: Boolean })\n horizontal: boolean = false;\n\n @property({ type: String })\n dropTargetId: string;\n\n @property({ type: String })\n dragHandle: string;\n\n /**\n * Optional callback to allow parent components to customize the ghost node.\n * Called after the ghost node is cloned but before it is appended to the DOM.\n */\n @property({ attribute: false })\n prepareGhost?: (ghost: HTMLElement) => void;\n\n ghostElement: HTMLDivElement = null;\n downEle: HTMLDivElement = null;\n xOffset = 0;\n yOffset = 0;\n yDown = 0;\n xDown = 0;\n\n draggingIdx = -1;\n draggingEle = null;\n dropIndicator: HTMLDivElement = null;\n pendingDropIndex = -1;\n pendingTargetElement: HTMLElement = null;\n\n private clickBlocker: ((e: MouseEvent) => void) | null = null;\n\n public constructor() {\n super();\n this.handleMouseMove = this.handleMouseMove.bind(this);\n this.handleMouseUp = this.handleMouseUp.bind(this);\n this.handleMouseDown = this.handleMouseDown.bind(this);\n }\n\n protected firstUpdated(\n _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.firstUpdated(_changedProperties);\n }\n\n private getSortableElements(): Element[] {\n return this.shadowRoot\n .querySelector('slot')\n .assignedElements()\n .filter((ele) => ele.classList.contains('sortable'));\n }\n\n public getIds() {\n return this.getSortableElements().map((ele) => ele.id);\n }\n\n private getRowIndex(id: string): number {\n return this.getSortableElements().findIndex((ele) => ele.id === id);\n }\n\n private getDropTargetInfo(\n mouseX: number,\n mouseY: number\n ): { element: HTMLDivElement; insertAfter: boolean } | null {\n const elements = this.getSortableElements().filter(\n (ele) => ele.id !== this.draggingEle?.id\n );\n\n if (elements.length === 0) return null;\n\n if (this.horizontal) {\n // For horizontal layout, find the insertion point based on mouse X position\n for (let i = 0; i < elements.length; i++) {\n const ele = elements[i];\n const rect = ele.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n\n if (mouseX < centerX) {\n // Insert before this element\n return { element: ele as HTMLDivElement, insertAfter: false };\n }\n }\n // If we're past all elements, insert after the last one\n return {\n element: elements[elements.length - 1] as HTMLDivElement,\n insertAfter: true\n };\n } else {\n // For vertical layout, find the insertion point based on mouse Y position\n for (let i = 0; i < elements.length; i++) {\n const ele = elements[i];\n const rect = ele.getBoundingClientRect();\n const centerY = rect.top + rect.height / 2;\n\n if (mouseY < centerY) {\n // Insert before this element\n return { element: ele as HTMLDivElement, insertAfter: false };\n }\n }\n // If we're past all elements, insert after the last one\n return {\n element: elements[elements.length - 1] as HTMLDivElement,\n insertAfter: true\n };\n }\n }\n\n private showDropIndicator(targetElement: HTMLElement, insertAfter: boolean) {\n this.hideDropIndicator();\n\n if (!targetElement) return;\n\n const container = this.shadowRoot.querySelector('.container');\n this.dropIndicator = document.createElement('div');\n this.dropIndicator.className = 'drop-indicator';\n\n const targetRect = targetElement.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n\n if (this.horizontal) {\n // For horizontal layout, show vertical line\n this.dropIndicator.style.height = targetRect.height + 'px';\n this.dropIndicator.style.top = targetRect.top - containerRect.top + 'px';\n\n if (insertAfter) {\n // Show line after target\n this.dropIndicator.style.left =\n targetRect.right - containerRect.left + 'px';\n } else {\n // Show line before target\n this.dropIndicator.style.left =\n targetRect.left - containerRect.left + 'px';\n }\n } else {\n // For vertical layout, show horizontal line\n this.dropIndicator.style.width = targetRect.width + 'px';\n this.dropIndicator.style.left =\n targetRect.left - containerRect.left + 'px';\n\n if (insertAfter) {\n // Show line after target\n this.dropIndicator.style.top =\n targetRect.bottom - containerRect.top + 'px';\n } else {\n // Show line before target\n this.dropIndicator.style.top =\n targetRect.top - containerRect.top + 'px';\n }\n }\n\n container.appendChild(this.dropIndicator);\n }\n\n private hideDropIndicator() {\n if (this.dropIndicator) {\n this.dropIndicator.remove();\n this.dropIndicator = null;\n }\n }\n\n private handleMouseDown(event: MouseEvent) {\n let ele = event.target as HTMLDivElement;\n\n // if we have a drag handle, only allow dragging from that element\n if (this.dragHandle) {\n if (!ele.classList.contains(this.dragHandle)) {\n return;\n }\n }\n\n ele = ele.closest('.sortable');\n if (ele) {\n event.preventDefault();\n event.stopPropagation();\n\n this.downEle = ele;\n this.draggingId = ele.id;\n this.draggingIdx = this.getRowIndex(ele.id);\n this.draggingEle = ele;\n\n // Use getBoundingClientRect for accurate offsets\n const rect = ele.getBoundingClientRect();\n this.xOffset = event.clientX - rect.left;\n this.yOffset = event.clientY - rect.top;\n this.yDown = event.clientY;\n this.xDown = event.clientX;\n\n document.addEventListener('mousemove', this.handleMouseMove);\n document.addEventListener('mouseup', this.handleMouseUp);\n }\n }\n\n private handleMouseMove(event: MouseEvent) {\n if (\n !this.ghostElement &&\n this.downEle &&\n (Math.abs(event.clientY - this.yDown) > DRAG_THRESHOLD ||\n Math.abs(event.clientX - this.xDown) > DRAG_THRESHOLD)\n ) {\n this.fireCustomEvent(CustomEventType.DragStart, {\n id: this.downEle.id\n });\n\n this.ghostElement = this.downEle.cloneNode(true) as HTMLDivElement;\n this.ghostElement.classList.add('ghost');\n\n // dim the original element while dragging\n this.downEle.style.pointerEvents = 'none';\n this.downEle.style.opacity = '0.5';\n\n const rect = this.downEle.getBoundingClientRect();\n this.ghostElement.style.transition = 'transform 300ms linear';\n\n this.ghostElement.style.width = rect.width + 'px';\n this.ghostElement.style.height = rect.height + 'px';\n this.ghostElement.style.position = 'fixed';\n this.ghostElement.style.left = event.clientX - this.xOffset + 'px';\n this.ghostElement.style.top = event.clientY - this.yOffset + 'px';\n this.ghostElement.style.pointerEvents = 'none';\n this.ghostElement.style.border =\n '1px solid var(--color-primary, #1c7cd6)';\n this.ghostElement.style.zIndex = '99999';\n this.ghostElement.style.background = '#fff';\n this.ghostElement.style.opacity = '0.7';\n this.ghostElement.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.2)';\n this.ghostElement.style.borderRadius = 'var(--curvature)';\n\n // allow component to customize the ghost node\n if (this.prepareGhost) {\n this.prepareGhost(this.ghostElement);\n }\n\n document.body.appendChild(this.ghostElement);\n\n // this.downEle = null;\n\n // Add global click blocker when drag starts\n if (!this.clickBlocker) {\n this.clickBlocker = (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n };\n // Use capture phase to intercept clicks before they reach any elements\n document.addEventListener('click', this.clickBlocker, true);\n }\n }\n\n if (this.ghostElement) {\n this.ghostElement.style.left = event.clientX - this.xOffset + 'px';\n this.ghostElement.style.top = event.clientY - this.yOffset + 'px';\n\n const targetInfo = this.getDropTargetInfo(event.clientX, event.clientY);\n if (targetInfo) {\n const { element: targetElement, insertAfter } = targetInfo;\n const targetIdx = this.getRowIndex(targetElement.id);\n const originalDragIdx = this.getRowIndex(this.draggingEle.id);\n\n // Calculate the intended drop index\n let dropIdx = targetIdx;\n if (insertAfter) {\n dropIdx += 1;\n }\n\n // Adjust dropIdx if dragging forward in the list\n if (originalDragIdx < dropIdx) {\n dropIdx -= 1;\n }\n\n // Store pending drop info but don't fire event yet\n this.dropTargetId = targetElement.id;\n this.pendingDropIndex = dropIdx;\n this.pendingTargetElement = targetElement;\n\n // Show drop indicator\n this.showDropIndicator(targetElement, insertAfter);\n } else {\n this.hideDropIndicator();\n this.dropTargetId = null;\n this.pendingDropIndex = -1;\n this.pendingTargetElement = null;\n }\n }\n }\n\n private handleMouseUp(evt: MouseEvent) {\n if (this.draggingId && this.ghostElement) {\n evt.preventDefault();\n evt.stopPropagation();\n\n // restore visibility of the dragged item\n\n if (this.downEle) {\n this.downEle.style.pointerEvents = '';\n this.downEle.style.opacity = '1';\n }\n\n // fire the order changed event only when dropped if we have a valid drop position\n if (this.pendingDropIndex >= 0 && this.pendingTargetElement) {\n const originalDragIdx = this.getRowIndex(this.draggingEle.id);\n\n // use swap-based logic - report which indexes need to be swapped\n const fromIdx = originalDragIdx;\n const toIdx = this.pendingDropIndex;\n\n // only fire if the position actually changed\n if (fromIdx !== toIdx) {\n this.fireCustomEvent(CustomEventType.OrderChanged, {\n swap: [fromIdx, toIdx]\n });\n }\n }\n\n this.fireCustomEvent(CustomEventType.DragStop, {\n id: this.draggingId\n });\n\n this.draggingId = null;\n this.dropTargetId = null;\n this.downEle = null;\n this.pendingDropIndex = -1;\n this.pendingTargetElement = null;\n\n if (this.ghostElement) {\n // Remove from body if present\n if (this.ghostElement.parentNode) {\n this.ghostElement.parentNode.removeChild(this.ghostElement);\n }\n this.ghostElement = null;\n }\n\n this.hideDropIndicator();\n\n // Keep the click blocker active for a short time after drop\n if (this.clickBlocker) {\n // We'll clean it up after a timeout\n setTimeout(() => {\n if (this.clickBlocker) {\n document.removeEventListener('click', this.clickBlocker, true);\n this.clickBlocker = null;\n }\n }, 100);\n }\n }\n document.removeEventListener('mousemove', this.handleMouseMove);\n document.removeEventListener('mouseup', this.handleMouseUp);\n this.dispatchEvent(new Event('change'));\n }\n\n public render(): TemplateResult {\n return html`\n <div class=\"container ${this.horizontal ? 'horizontal' : ''}\">\n <slot @mousedown=${this.handleMouseDown}></slot>\n </div>\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"SortableList.js","sourceRoot":"","sources":["../../../src/list/SortableList.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;GAEG;AAEH,2CAA2C;AAC3C,MAAM,cAAc,GAAG,CAAC,CAAC;AACzB,MAAM,OAAO,YAAa,SAAQ,YAAY;IAE5C,MAAM,KAAK,MAAM;QACf,OAAO,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmCT,CAAC;IACJ,CAAC;IAyCD;QACE,KAAK,EAAE,CAAC;QApCV,eAAU,GAAY,KAAK,CAAC;QAS5B,QAAG,GAAW,KAAK,CAAC;QASpB,iBAAY,GAAmB,IAAI,CAAC;QACpC,YAAO,GAAmB,IAAI,CAAC;QAC/B,wBAAmB,GAAY,IAAI,CAAC,CAAC,4BAA4B;QACjE,sBAAiB,GAAW,CAAC,CAAC,CAAC,CAAC,6CAA6C;QAC7E,YAAO,GAAG,CAAC,CAAC;QACZ,YAAO,GAAG,CAAC,CAAC;QACZ,UAAK,GAAG,CAAC,CAAC;QACV,UAAK,GAAG,CAAC,CAAC;QAEV,gBAAW,GAAG,CAAC,CAAC,CAAC;QACjB,gBAAW,GAAG,IAAI,CAAC;QACnB,oBAAe,GAAmB,IAAI,CAAC;QACvC,qBAAgB,GAAG,CAAC,CAAC,CAAC;QACtB,yBAAoB,GAAgB,IAAI,CAAC;QAEjC,iBAAY,GAAqC,IAAI,CAAC;QAI5D,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;IAEO,mBAAmB;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU;aACzB,aAAa,CAAC,MAAM,CAAC;aACrB,gBAAgB,EAAE;aAClB,MAAM,CACL,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC;YAClC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAC9C,CAAC;QACJ,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,qBAAqB,CAAC,OAAoB;QAChD,6BAA6B;QAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAgB,CAAC;QAErD,0DAA0D;QAC1D,MAAM,cAAc,GAAG,CAAC,QAAqB,EAAE,MAAmB,EAAE,EAAE;YACpE,IAAI,CAAC;gBACH,oBAAoB;gBACpB,MAAM,cAAc,GAAG,QAAQ,CAAC,gBAAgB,CAC9C,yBAAyB,CAC1B,CAAC;gBACF,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,yBAAyB,CAAC,CAAC;gBAExE,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE;oBAC9C,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAGjB,CAAC;oBACtB,IAAI,WAAW,EAAE,CAAC;wBAChB,IAAI,aAAa,YAAY,gBAAgB,EAAE,CAAC;4BAC9C,MAAM,iBAAiB,GAAG,aAAiC,CAAC;4BAC5D,MAAM,eAAe,GAAG,WAA+B,CAAC;4BAExD,IACE,iBAAiB,CAAC,IAAI,KAAK,UAAU;gCACrC,iBAAiB,CAAC,IAAI,KAAK,OAAO,EAClC,CAAC;gCACD,eAAe,CAAC,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC;4BACtD,CAAC;iCAAM,CAAC;gCACN,eAAe,CAAC,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC;4BAClD,CAAC;wBACH,CAAC;6BAAM,IAAI,aAAa,YAAY,mBAAmB,EAAE,CAAC;4BACvD,WAAmC,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC;wBACnE,CAAC;6BAAM,IAAI,aAAa,YAAY,iBAAiB,EAAE,CAAC;4BACrD,WAAiC,CAAC,aAAa;gCAC9C,aAAa,CAAC,aAAa,CAAC;wBAChC,CAAC;oBACH,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,4EAA4E;gBAC5E,MAAM,mBAAmB,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvE,MAAM,iBAAiB,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;gBAEnE,mBAAmB,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;oBAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;oBAC1C,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;wBAC3B,wCAAwC;wBACxC,IACE,UAAU,CAAC,OAAO;4BAClB,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EACrD,CAAC;4BACD,IAAI,CAAC;gCACH,yCAAyC;gCACzC,MAAM,UAAU,GAAG;oCACjB,OAAO;oCACP,QAAQ;oCACR,eAAe;oCACf,SAAS;oCACT,UAAU;oCACV,aAAa;iCACd,CAAC;gCACF,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;oCAC1B,IACE,IAAI,IAAI,UAAU;wCACjB,UAAkB,CAAC,IAAI,CAAC,KAAK,SAAS,EACvC,CAAC;wCACA,QAAgB,CAAC,IAAI,CAAC,GAAI,UAAkB,CAAC,IAAI,CAAC,CAAC;oCACtD,CAAC;gCACH,CAAC,CAAC,CAAC;gCAEH,6DAA6D;gCAC7D,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;oCACjD,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gCAC/C,CAAC,CAAC,CAAC;4BACL,CAAC;4BAAC,OAAO,CAAC,EAAE,CAAC;gCACX,8CAA8C;4BAChD,CAAC;wBACH,CAAC;6BAAM,CAAC;4BACN,gDAAgD;4BAChD,IAAI,CAAC;gCACH,IACE,OAAO,IAAI,UAAU;oCACpB,UAAkB,CAAC,KAAK,KAAK,SAAS,EACvC,CAAC;oCACA,QAAgB,CAAC,KAAK,GAAI,UAAkB,CAAC,KAAK,CAAC;gCACtD,CAAC;4BACH,CAAC;4BAAC,OAAO,CAAC,EAAE,CAAC;gCACX,wCAAwC;4BAC1C,CAAC;4BAED,gDAAgD;4BAChD,IAAI,CAAC;gCACH,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;oCACjD,IACE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;wCAC7B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAC7B,CAAC;wCACD,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;oCAC/C,CAAC;gCACH,CAAC,CAAC,CAAC;4BACL,CAAC;4BAAC,OAAO,CAAC,EAAE,CAAC;gCACX,wCAAwC;4BAC1C,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,iDAAiD;gBACjD,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,CAAC,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC,CAAC;QAEF,4DAA4D;QAC5D,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAE/B,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,MAAM;QACX,OAAO,IAAI,CAAC,mBAAmB,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC;IAEO,WAAW,CAAC,EAAU;QAC5B,OAAO,IAAI,CAAC,mBAAmB,EAAE,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;IAEO,iBAAiB,CACvB,MAAc,EACd,MAAc;QAEd,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC,MAAM,CAChD,CAAC,GAAG,EAAE,EAAE,WAAC,OAAA,GAAG,CAAC,EAAE,MAAK,MAAA,IAAI,CAAC,WAAW,0CAAE,EAAE,CAAA,CAAA,EAAA,CACzC,CAAC;QAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEvC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,4EAA4E;YAC5E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,GAAG,CAAC,qBAAqB,EAAE,CAAC;gBACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;gBAE3C,IAAI,MAAM,GAAG,OAAO,EAAE,CAAC;oBACrB,6BAA6B;oBAC7B,OAAO,EAAE,OAAO,EAAE,GAAqB,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;gBAChE,CAAC;YACH,CAAC;YACD,wDAAwD;YACxD,OAAO;gBACL,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAmB;gBACxD,WAAW,EAAE,IAAI;aAClB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,0EAA0E;YAC1E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,GAAG,CAAC,qBAAqB,EAAE,CAAC;gBACzC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;gBAE3C,IAAI,MAAM,GAAG,OAAO,EAAE,CAAC;oBACrB,6BAA6B;oBAC7B,OAAO,EAAE,OAAO,EAAE,GAAqB,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;gBAChE,CAAC;YACH,CAAC;YACD,wDAAwD;YACxD,OAAO;gBACL,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAmB;gBACxD,WAAW,EAAE,IAAI;aAClB,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,mBAAmB,CACzB,aAA0B,EAC1B,WAAoB;QAEpB,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAE3B,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAEhD,wEAAwE;QACxE,IAAI,aAAa,KAAK,IAAI,CAAC,WAAW;YAAE,OAAO;QAE/C,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAErD,IAAI,CAAC,eAAe,CAAC,SAAS,GAAG,2BAA2B,CAAC;QAE7D,mEAAmE;QACnE,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC;YACtC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YACrD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACvD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YAC1D,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,YAAY,GAAG,kBAAkB,CAAC;YAC7D,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC;QAC9C,CAAC;QAED,4DAA4D;QAC5D,IAAI,WAAW,EAAE,CAAC;YAChB,aAAa,CAAC,qBAAqB,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,aAAa,CAAC,qBAAqB,CAAC,aAAa,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAEO,mBAAmB;QACzB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,mBAAmB;YAAE,OAAO;QAEvD,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,eAAe,CAAC,SAAS,GAAG,2BAA2B,CAAC;QAE7D,4CAA4C;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACtC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACrD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACvD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAC1D,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,YAAY,GAAG,kBAAkB,CAAC;QAC7D,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC;QAC5C,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,UAAU;YACnC,qCAAqC,CAAC;QACxC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM;YAC/B,gDAAgD,CAAC;QAEnD,iEAAiE;QACjE,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACvE,CAAC;IAEO,eAAe,CAAC,KAAiB;QACvC,IAAI,GAAG,GAAG,KAAK,CAAC,MAAwB,CAAC;QAEzC,kEAAkE;QAClE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7C,OAAO;YACT,CAAC;QACH,CAAC;QAED,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC/B,IAAI,GAAG,EAAE,CAAC;YACR,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;YACnB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5C,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;YAEvB,+EAA+E;YAC/E,MAAM,IAAI,GAAG,GAAG,CAAC,qBAAqB,EAAE,CAAC;YACzC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC,wCAAwC;YACzE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;YACzC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC;YACxC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC;YAC3B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC;YAE3B,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YAC7D,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,KAAiB;QACvC,IACE,CAAC,IAAI,CAAC,YAAY;YAClB,IAAI,CAAC,OAAO;YACZ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,cAAc;gBACpD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC,EACxD,CAAC;YACD,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,SAAS,EAAE;gBAC9C,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;aACpB,CAAC,CAAC;YAEH,uDAAuD;YACvD,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAE3D,oDAAoD;YACpD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,qBAAqB,CAC5C,IAAI,CAAC,OAAO,CACK,CAAC;YAEpB,gEAAgE;YAChE,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;YAEpC,6BAA6B;YAC7B,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAEzC,qDAAqD;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC;YAEtC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC;YAC3C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACnE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YAClE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACpD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;YACzC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;YACxC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,GAAG,aAAa,CAAC;YAElD,8CAA8C;YAC9C,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACvC,CAAC;YAED,8CAA8C;YAC9C,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAE7C,uEAAuE;YACvE,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAE9B,4CAA4C;YAC5C,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACvB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAa,EAAE,EAAE;oBACpC,CAAC,CAAC,eAAe,EAAE,CAAC;oBACpB,CAAC,CAAC,cAAc,EAAE,CAAC;gBACrB,CAAC,CAAC;gBACF,uEAAuE;gBACvE,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACnE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YAElE,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACxE,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC;gBAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;gBAErD,oEAAoE;gBACpE,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC;gBAE/C,qEAAqE;gBACrE,uFAAuF;gBAEvF,IAAI,OAAO,CAAC;gBACZ,IAAI,SAAS,GAAG,eAAe,EAAE,CAAC;oBAChC,gEAAgE;oBAChE,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACpD,CAAC;qBAAM,CAAC;oBACN,iEAAiE;oBACjE,gHAAgH;oBAChH,8HAA8H;oBAC9H,qIAAqI;oBACrI,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;gBACpD,CAAC;gBAED,mDAAmD;gBACnD,IAAI,CAAC,YAAY,GAAG,aAAa,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;gBAChC,IAAI,CAAC,oBAAoB,GAAG,aAAa,CAAC;gBAE1C,wBAAwB;gBACxB,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;YACvD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC;gBAC3B,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,GAAe;QACnC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACzC,GAAG,CAAC,cAAc,EAAE,CAAC;YACrB,GAAG,CAAC,eAAe,EAAE,CAAC;YAEtB,4CAA4C;YAC5C,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YAC7B,CAAC;YAED,uEAAuE;YACvE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC;YACxD,CAAC;YAED,4CAA4C;YAC5C,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAE3B,kFAAkF;YAClF,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC5D,oEAAoE;gBACpE,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC;gBAE/C,iEAAiE;gBACjE,MAAM,OAAO,GAAG,eAAe,CAAC;gBAChC,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC;gBAEpC,6CAA6C;gBAC7C,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;oBACtB,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,YAAY,EAAE;wBACjD,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;qBACvB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,QAAQ,EAAE;gBAC7C,EAAE,EAAE,IAAI,CAAC,UAAU;aACpB,CAAC,CAAC;YAEH,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;YAChC,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC;YAC5B,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAEjC,gDAAgD;YAChD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAEzB,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAE3B,4DAA4D;YAC5D,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,oCAAoC;gBACpC,UAAU,CAAC,GAAG,EAAE;oBACd,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;wBACtB,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;wBAC/D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;oBAC3B,CAAC;gBACH,CAAC,EAAE,GAAG,CAAC,CAAC;YACV,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAChE,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5D,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC1C,CAAC;IAEM,MAAM;QACX,OAAO,IAAI,CAAA;;2BAEY,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;sBACxC,IAAI,CAAC,GAAG;;2BAEH,IAAI,CAAC,eAAe;;KAE1C,CAAC;IACJ,CAAC;CACF;AA1fC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gDACR;AAGnB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gDACA;AAG5B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;kDACN;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gDACR;AAGnB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;yCACP;AAOpB;IADC,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;kDACa","sourcesContent":["import { css, html, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { CustomEventType } from '../interfaces';\nimport { RapidElement } from '../RapidElement';\n\n/**\n * A simple list that can be sorted by dragging\n */\n\n// how far we have to drag before it starts\nconst DRAG_THRESHOLD = 2;\nexport class SortableList extends RapidElement {\n originalDownDisplay: string;\n static get styles() {\n return css`\n :host {\n margin: auto;\n }\n\n .container {\n user-select: none;\n position: relative;\n display: grid;\n grid-template-columns: 1fr;\n }\n\n .container.horizontal {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n }\n\n .dragging {\n background: var(--color-selection);\n }\n\n .slot {\n flex-grow: 1;\n }\n\n slot > * {\n user-select: none;\n }\n\n temba-icon {\n opacity: 0.1;\n padding: 0.2em 0.5em;\n transition: all 300ms ease-in-out;\n }\n `;\n }\n\n @property({ type: String })\n draggingId: string;\n\n @property({ type: Boolean })\n horizontal: boolean = false;\n\n @property({ type: String })\n dropTargetId: string;\n\n @property({ type: String })\n dragHandle: string;\n\n @property({ type: String })\n gap: string = '0em';\n\n /**\n * Optional callback to allow parent components to customize the ghost node.\n * Called after the ghost node is cloned but before it is appended to the DOM.\n */\n @property({ attribute: false })\n prepareGhost?: (ghost: HTMLElement) => void;\n\n ghostElement: HTMLDivElement = null;\n downEle: HTMLDivElement = null;\n originalElementRect: DOMRect = null; // Store original dimensions\n originalDragIndex: number = -1; // Store original index before moving element\n xOffset = 0;\n yOffset = 0;\n yDown = 0;\n xDown = 0;\n\n draggingIdx = -1;\n draggingEle = null;\n dropPlaceholder: HTMLDivElement = null;\n pendingDropIndex = -1;\n pendingTargetElement: HTMLElement = null;\n\n private clickBlocker: ((e: MouseEvent) => void) | null = null;\n\n public constructor() {\n super();\n this.handleMouseMove = this.handleMouseMove.bind(this);\n this.handleMouseUp = this.handleMouseUp.bind(this);\n this.handleMouseDown = this.handleMouseDown.bind(this);\n }\n\n private getSortableElements(): Element[] {\n const eles = this.shadowRoot\n .querySelector('slot')\n .assignedElements()\n .filter(\n (ele) =>\n ele.classList.contains('sortable') &&\n !ele.classList.contains('drop-placeholder')\n );\n return eles;\n }\n\n private cloneElementWithState(element: HTMLElement): HTMLElement {\n // First create a basic clone\n const clone = element.cloneNode(true) as HTMLElement;\n\n // Helper function to copy form element values recursively\n const copyFormValues = (original: HTMLElement, cloned: HTMLElement) => {\n try {\n // Copy input values\n const originalInputs = original.querySelectorAll(\n 'input, textarea, select'\n );\n const clonedInputs = cloned.querySelectorAll('input, textarea, select');\n\n originalInputs.forEach((originalInput, index) => {\n const clonedInput = clonedInputs[index] as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n if (clonedInput) {\n if (originalInput instanceof HTMLInputElement) {\n const originalHtmlInput = originalInput as HTMLInputElement;\n const clonedHtmlInput = clonedInput as HTMLInputElement;\n\n if (\n originalHtmlInput.type === 'checkbox' ||\n originalHtmlInput.type === 'radio'\n ) {\n clonedHtmlInput.checked = originalHtmlInput.checked;\n } else {\n clonedHtmlInput.value = originalHtmlInput.value;\n }\n } else if (originalInput instanceof HTMLTextAreaElement) {\n (clonedInput as HTMLTextAreaElement).value = originalInput.value;\n } else if (originalInput instanceof HTMLSelectElement) {\n (clonedInput as HTMLSelectElement).selectedIndex =\n originalInput.selectedIndex;\n }\n }\n });\n\n // Copy properties from all custom elements that might have a value property\n const allOriginalElements = Array.from(original.querySelectorAll('*'));\n const allClonedElements = Array.from(cloned.querySelectorAll('*'));\n\n allOriginalElements.forEach((originalEl, index) => {\n const clonedEl = allClonedElements[index];\n if (clonedEl && originalEl) {\n // Special handling for temba components\n if (\n originalEl.tagName &&\n originalEl.tagName.toLowerCase().startsWith('temba-')\n ) {\n try {\n // Copy common temba component properties\n const tembaProps = [\n 'value',\n 'values',\n 'selectedValue',\n 'checked',\n 'selected',\n 'textContent'\n ];\n tembaProps.forEach((prop) => {\n if (\n prop in originalEl &&\n (originalEl as any)[prop] !== undefined\n ) {\n (clonedEl as any)[prop] = (originalEl as any)[prop];\n }\n });\n\n // Copy all attributes for temba components to preserve state\n Array.from(originalEl.attributes).forEach((attr) => {\n clonedEl.setAttribute(attr.name, attr.value);\n });\n } catch (e) {\n // Ignore errors when copying temba properties\n }\n } else {\n // Try to copy value property for other elements\n try {\n if (\n 'value' in originalEl &&\n (originalEl as any).value !== undefined\n ) {\n (clonedEl as any).value = (originalEl as any).value;\n }\n } catch (e) {\n // Ignore errors when copying properties\n }\n\n // Copy data attributes that might contain state\n try {\n Array.from(originalEl.attributes).forEach((attr) => {\n if (\n attr.name.startsWith('data-') ||\n attr.name.startsWith('aria-')\n ) {\n clonedEl.setAttribute(attr.name, attr.value);\n }\n });\n } catch (e) {\n // Ignore errors when copying attributes\n }\n }\n }\n });\n } catch (e) {\n // If anything fails, just return the basic clone\n console.warn('Failed to copy form values in cloneElementWithState:', e);\n }\n };\n\n // Copy form values for the root element and all descendants\n copyFormValues(element, clone);\n\n return clone;\n }\n\n public getIds() {\n return this.getSortableElements().map((ele) => ele.id);\n }\n\n private getRowIndex(id: string): number {\n return this.getSortableElements().findIndex((ele) => ele.id === id);\n }\n\n private getDropTargetInfo(\n mouseX: number,\n mouseY: number\n ): { element: HTMLDivElement; insertAfter: boolean } | null {\n const elements = this.getSortableElements().filter(\n (ele) => ele.id !== this.draggingEle?.id\n );\n\n if (elements.length === 0) return null;\n\n if (this.horizontal) {\n // For horizontal layout, find the insertion point based on mouse X position\n for (let i = 0; i < elements.length; i++) {\n const ele = elements[i];\n const rect = ele.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n\n if (mouseX < centerX) {\n // Insert before this element\n return { element: ele as HTMLDivElement, insertAfter: false };\n }\n }\n // If we're past all elements, insert after the last one\n return {\n element: elements[elements.length - 1] as HTMLDivElement,\n insertAfter: true\n };\n } else {\n // For vertical layout, find the insertion point based on mouse Y position\n for (let i = 0; i < elements.length; i++) {\n const ele = elements[i];\n const rect = ele.getBoundingClientRect();\n const centerY = rect.top + rect.height / 2;\n\n if (mouseY < centerY) {\n // Insert before this element\n return { element: ele as HTMLDivElement, insertAfter: false };\n }\n }\n // If we're past all elements, insert after the last one\n return {\n element: elements[elements.length - 1] as HTMLDivElement,\n insertAfter: true\n };\n }\n }\n\n private showDropPlaceholder(\n targetElement: HTMLElement,\n insertAfter: boolean\n ) {\n this.hideDropPlaceholder();\n\n if (!targetElement || !this.draggingEle) return;\n\n // Don't show placeholder if we're targeting the dragging element itself\n if (targetElement === this.draggingEle) return;\n\n this.dropPlaceholder = document.createElement('div');\n\n this.dropPlaceholder.className = 'drop-placeholder sortable';\n\n // Copy dimensions from the original element (before it was hidden)\n if (this.originalElementRect) {\n const rect = this.originalElementRect;\n this.dropPlaceholder.style.width = rect.width + 'px';\n this.dropPlaceholder.style.height = rect.height + 'px';\n this.dropPlaceholder.style.minHeight = rect.height + 'px';\n this.dropPlaceholder.style.borderRadius = 'var(--curvature)';\n this.dropPlaceholder.style.flexShrink = '0';\n }\n\n // Insert the placeholder in the correct position in the DOM\n if (insertAfter) {\n targetElement.insertAdjacentElement('afterend', this.dropPlaceholder);\n } else {\n targetElement.insertAdjacentElement('beforebegin', this.dropPlaceholder);\n }\n }\n\n private hideDropPlaceholder() {\n if (this.dropPlaceholder) {\n this.dropPlaceholder.remove();\n this.dropPlaceholder = null;\n }\n }\n\n private showInitialPlaceholder() {\n if (!this.downEle || !this.originalElementRect) return;\n\n this.dropPlaceholder = document.createElement('div');\n this.dropPlaceholder.className = 'drop-placeholder sortable';\n\n // Copy dimensions from the original element\n const rect = this.originalElementRect;\n this.dropPlaceholder.style.width = rect.width + 'px';\n this.dropPlaceholder.style.height = rect.height + 'px';\n this.dropPlaceholder.style.minHeight = rect.height + 'px';\n this.dropPlaceholder.style.borderRadius = 'var(--curvature)';\n this.dropPlaceholder.style.flexShrink = '0';\n this.dropPlaceholder.style.background =\n 'rgba(var(--color-primary-rgb), 0.1)';\n this.dropPlaceholder.style.border =\n '2px dashed rgba(var(--color-primary-rgb), 0.3)';\n\n // Insert the placeholder right after the hidden original element\n this.downEle.insertAdjacentElement('afterend', this.dropPlaceholder);\n }\n\n private handleMouseDown(event: MouseEvent) {\n let ele = event.target as HTMLDivElement;\n\n // if we have a drag handle, only allow dragging from that element\n if (this.dragHandle) {\n if (!ele.classList.contains(this.dragHandle)) {\n return;\n }\n }\n\n ele = ele.closest('.sortable');\n if (ele) {\n event.preventDefault();\n event.stopPropagation();\n this.downEle = ele;\n this.draggingId = ele.id;\n this.draggingIdx = this.getRowIndex(ele.id);\n this.draggingEle = ele;\n\n // Use getBoundingClientRect for accurate offsets and store original dimensions\n const rect = ele.getBoundingClientRect();\n this.originalElementRect = rect; // Store the original rect before hiding\n this.xOffset = event.clientX - rect.left;\n this.yOffset = event.clientY - rect.top;\n this.yDown = event.clientY;\n this.xDown = event.clientX;\n\n document.addEventListener('mousemove', this.handleMouseMove);\n document.addEventListener('mouseup', this.handleMouseUp);\n }\n }\n\n private handleMouseMove(event: MouseEvent) {\n if (\n !this.ghostElement &&\n this.downEle &&\n (Math.abs(event.clientY - this.yDown) > DRAG_THRESHOLD ||\n Math.abs(event.clientX - this.xDown) > DRAG_THRESHOLD)\n ) {\n this.fireCustomEvent(CustomEventType.DragStart, {\n id: this.downEle.id\n });\n\n // Capture the original index BEFORE hiding the element\n this.originalDragIndex = this.getRowIndex(this.downEle.id);\n\n // Create a clone of the element to use as the ghost\n this.ghostElement = this.cloneElementWithState(\n this.downEle\n ) as HTMLDivElement;\n\n // Hide the original element during dragging using inline styles\n this.originalDownDisplay = this.downEle.style.display;\n this.downEle.style.display = 'none';\n\n // Style the clone as a ghost\n this.ghostElement.classList.add('ghost');\n\n // Use the stored original dimensions for positioning\n const rect = this.originalElementRect;\n\n this.ghostElement.style.position = 'fixed';\n this.ghostElement.style.left = event.clientX - this.xOffset + 'px';\n this.ghostElement.style.top = event.clientY - this.yOffset + 'px';\n this.ghostElement.style.width = rect.width + 'px';\n this.ghostElement.style.height = rect.height + 'px';\n this.ghostElement.style.zIndex = '99999';\n this.ghostElement.style.opacity = '0.8';\n this.ghostElement.style.transform = 'scale(1.03)';\n\n // allow component to customize the ghost node\n if (this.prepareGhost) {\n this.prepareGhost(this.ghostElement);\n }\n\n // Add the clone to document.body for dragging\n document.body.appendChild(this.ghostElement);\n\n // Show initial placeholder in the original position to maintain layout\n this.showInitialPlaceholder();\n\n // Add global click blocker when drag starts\n if (!this.clickBlocker) {\n this.clickBlocker = (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n };\n // Use capture phase to intercept clicks before they reach any elements\n document.addEventListener('click', this.clickBlocker, true);\n }\n }\n\n if (this.ghostElement) {\n this.ghostElement.style.left = event.clientX - this.xOffset + 'px';\n this.ghostElement.style.top = event.clientY - this.yOffset + 'px';\n\n const targetInfo = this.getDropTargetInfo(event.clientX, event.clientY);\n if (targetInfo) {\n const { element: targetElement, insertAfter } = targetInfo;\n const targetIdx = this.getRowIndex(targetElement.id);\n\n // Use the original drag index we captured before moving the element\n const originalDragIdx = this.originalDragIndex;\n\n // Calculate where the dragged element will end up in the final array\n // targetIdx is the position of target element in current DOM (missing dragged element)\n\n let dropIdx;\n if (targetIdx < originalDragIdx) {\n // Target is before the original drag position - moving backward\n dropIdx = insertAfter ? targetIdx + 1 : targetIdx;\n } else {\n // Target was originally after the drag position - moving forward\n // When moving the dragged element forward (i.e., to a higher index), the targetIdx is based on the current DOM,\n // which no longer includes the dragged element. This means all elements after the original position have shifted left by one,\n // so we need to subtract 1 from targetIdx to get the correct insertion index. If inserting after the target, we use targetIdx as is.\n dropIdx = insertAfter ? targetIdx : targetIdx - 1;\n }\n\n // Store pending drop info but don't fire event yet\n this.dropTargetId = targetElement.id;\n this.pendingDropIndex = dropIdx;\n this.pendingTargetElement = targetElement;\n\n // Show drop placeholder\n this.showDropPlaceholder(targetElement, insertAfter);\n } else {\n this.hideDropPlaceholder();\n this.dropTargetId = null;\n this.pendingDropIndex = -1;\n this.pendingTargetElement = null;\n }\n }\n }\n\n private handleMouseUp(evt: MouseEvent) {\n if (this.draggingId && this.ghostElement) {\n evt.preventDefault();\n evt.stopPropagation();\n\n // Remove the ghost clone from document.body\n if (this.ghostElement) {\n this.ghostElement.remove();\n }\n\n // Restore visibility of the original element by clearing inline styles\n if (this.downEle) {\n this.downEle.style.display = this.originalDownDisplay;\n }\n\n // Clear visual effects before firing events\n this.hideDropPlaceholder();\n\n // fire the order changed event only when dropped if we have a valid drop position\n if (this.pendingDropIndex >= 0 && this.pendingTargetElement) {\n // Use the original drag index we captured before hiding the element\n const originalDragIdx = this.originalDragIndex;\n\n // use swap-based logic - report which indexes need to be swapped\n const fromIdx = originalDragIdx;\n const toIdx = this.pendingDropIndex;\n\n // only fire if the position actually changed\n if (fromIdx !== toIdx) {\n this.fireCustomEvent(CustomEventType.OrderChanged, {\n swap: [fromIdx, toIdx]\n });\n }\n }\n\n this.fireCustomEvent(CustomEventType.DragStop, {\n id: this.draggingId\n });\n\n this.draggingId = null;\n this.dropTargetId = null;\n this.downEle = null;\n this.originalElementRect = null;\n this.originalDragIndex = -1;\n this.pendingDropIndex = -1;\n this.pendingTargetElement = null;\n\n // Clear the ghost reference since we removed it\n this.ghostElement = null;\n\n this.hideDropPlaceholder();\n\n // Keep the click blocker active for a short time after drop\n if (this.clickBlocker) {\n // We'll clean it up after a timeout\n setTimeout(() => {\n if (this.clickBlocker) {\n document.removeEventListener('click', this.clickBlocker, true);\n this.clickBlocker = null;\n }\n }, 100);\n }\n }\n document.removeEventListener('mousemove', this.handleMouseMove);\n document.removeEventListener('mouseup', this.handleMouseUp);\n this.dispatchEvent(new Event('change'));\n }\n\n public render(): TemplateResult {\n return html`\n <div\n class=\"container ${this.horizontal ? 'horizontal' : ''}\"\n style=\"gap: ${this.gap}\"\n >\n <slot @mousedown=${this.handleMouseDown}></slot>\n </div>\n `;\n }\n}\n"]}