windmill-components 1.405.0 → 1.406.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 (109) hide show
  1. package/package/assets/app.css +20 -0
  2. package/package/components/AppConnectLightweightResourcePicker.svelte +66 -0
  3. package/package/components/AppConnectLightweightResourcePicker.svelte.d.ts +21 -0
  4. package/package/components/ArgInput.svelte +6 -2
  5. package/package/components/ArgInput.svelte.d.ts +1 -0
  6. package/package/components/CustomPopover.svelte +1 -1
  7. package/package/components/DisplayResult.svelte +1 -1
  8. package/package/components/FlowBuilder.svelte +2 -0
  9. package/package/components/FlowBuilder.svelte.d.ts +1 -0
  10. package/package/components/FlowInputViewer.svelte +25 -0
  11. package/package/components/FlowInputViewer.svelte.d.ts +19 -0
  12. package/package/components/FlowJobResult.svelte +8 -1
  13. package/package/components/FlowJobResult.svelte.d.ts +1 -0
  14. package/package/components/FlowMetadata.svelte +2 -2
  15. package/package/components/FlowPreviewContent.svelte +2 -1
  16. package/package/components/FlowStatusViewer.svelte +3 -1
  17. package/package/components/FlowStatusViewer.svelte.d.ts +1 -0
  18. package/package/components/FlowStatusViewerInner.svelte +4 -1
  19. package/package/components/FlowViewer.svelte +2 -21
  20. package/package/components/LightweightResourcePicker.svelte +34 -30
  21. package/package/components/LogViewer.svelte +4 -3
  22. package/package/components/LogViewer.svelte.d.ts +1 -0
  23. package/package/components/ManualPopover.svelte +1 -1
  24. package/package/components/ObjectResourceInput.svelte +5 -2
  25. package/package/components/ObjectResourceInput.svelte.d.ts +2 -0
  26. package/package/components/Popover.svelte +1 -1
  27. package/package/components/Portal.svelte +7 -8
  28. package/package/components/Portal.svelte.d.ts +3 -7
  29. package/package/components/QueueMetricsDrawer.svelte +368 -3
  30. package/package/components/QueueMetricsDrawer.svelte.d.ts +2 -0
  31. package/package/components/ResourceEditor.svelte +4 -5
  32. package/package/components/ResourcePicker.svelte +14 -9
  33. package/package/components/ResourcePicker.svelte.d.ts +1 -0
  34. package/package/components/RunForm.svelte +1 -1
  35. package/package/components/SavedInputs.svelte +2 -2
  36. package/package/components/ScriptVersionHistory.svelte +54 -59
  37. package/package/components/TestConnection.svelte +1 -0
  38. package/package/components/TimeAgo.svelte +5 -6
  39. package/package/components/TimeAgo.svelte.d.ts +1 -1
  40. package/package/components/apps/components/buttons/AppButton.svelte +1 -1
  41. package/package/components/apps/components/display/dbtable/AppDbExplorer.svelte +1 -1
  42. package/package/components/apps/components/inputs/AppMultiSelect.svelte +1 -1
  43. package/package/components/apps/components/inputs/AppMultiSelectV2.svelte +1 -1
  44. package/package/components/apps/components/layout/AppDrawer.svelte +1 -1
  45. package/package/components/apps/components/layout/AppModal.svelte +1 -1
  46. package/package/components/apps/editor/AppEditor.svelte +11 -3
  47. package/package/components/apps/editor/GridEditor.svelte +57 -4
  48. package/package/components/apps/editor/GridEditor.svelte.d.ts +8 -0
  49. package/package/components/apps/editor/GridEditorMenu.svelte +1 -1
  50. package/package/components/apps/editor/SubGridEditor.svelte +82 -3
  51. package/package/components/apps/editor/SubGridEditor.svelte.d.ts +16 -0
  52. package/package/components/apps/editor/appUtils.d.ts +19 -2
  53. package/package/components/apps/editor/appUtils.js +103 -7
  54. package/package/components/apps/editor/component/Component.svelte +36 -8
  55. package/package/components/apps/editor/component/Component.svelte.d.ts +3 -1
  56. package/package/components/apps/editor/contextPanel/components/OutputHeader.svelte +1 -1
  57. package/package/components/apps/svelte-grid/Grid.svelte +235 -12
  58. package/package/components/apps/svelte-grid/Grid.svelte.d.ts +7 -7
  59. package/package/components/apps/svelte-grid/MoveResize.svelte +104 -11
  60. package/package/components/apps/svelte-grid/MoveResize.svelte.d.ts +7 -0
  61. package/package/components/apps/svelte-grid/utils/item.d.ts +4 -1
  62. package/package/components/apps/svelte-grid/utils/item.js +2 -3
  63. package/package/components/apps/svelte-select/lib/ConditionalPortal.svelte +1 -1
  64. package/package/components/apps/svelte-select/lib/ConditionalPortalGlobal.svelte +1 -1
  65. package/package/components/common/button/ButtonDropdown.svelte +1 -1
  66. package/package/components/common/drawer/ConditionalPortal.svelte +1 -1
  67. package/package/components/common/menu/MenuV2.svelte +1 -1
  68. package/package/components/common/modal/AlwaysMountedModal.svelte +1 -1
  69. package/package/components/common/popup/PopupV2.svelte +1 -1
  70. package/package/components/copilot/StepGenQuick.svelte +3 -2
  71. package/package/components/copilot/StepGenQuick.svelte.d.ts +1 -0
  72. package/package/components/custom_ui.d.ts +1 -0
  73. package/package/components/details/Menu.svelte +1 -1
  74. package/package/components/flows/CreateActionsApp.svelte +1 -1
  75. package/package/components/flows/FlowEditor.svelte +2 -1
  76. package/package/components/flows/FlowEditor.svelte.d.ts +1 -0
  77. package/package/components/flows/FlowHistory.svelte +10 -195
  78. package/package/components/flows/FlowHistory.svelte.d.ts +0 -1
  79. package/package/components/flows/FlowHistoryInner.svelte +200 -0
  80. package/package/components/flows/FlowHistoryInner.svelte.d.ts +19 -0
  81. package/package/components/flows/content/FlowEditorPanel.svelte +2 -1
  82. package/package/components/flows/content/FlowEditorPanel.svelte.d.ts +1 -0
  83. package/package/components/flows/content/FlowInput.svelte +66 -58
  84. package/package/components/flows/content/FlowInput.svelte.d.ts +1 -0
  85. package/package/components/flows/content/FlowModuleScript.svelte +1 -1
  86. package/package/components/flows/header/FlowYamlEditor.svelte +6 -2
  87. package/package/components/flows/map/FlowModuleSchemaMap.svelte +3 -2
  88. package/package/components/flows/map/InsertModuleButton.svelte +1 -1
  89. package/package/components/flows/pickers/WorkspaceScriptPickerQuick.svelte +1 -1
  90. package/package/components/graph/model.d.ts +1 -0
  91. package/package/components/icons/WindmillIcon.svelte +119 -105
  92. package/package/components/icons/store.d.ts +4 -0
  93. package/package/components/icons/store.js +1 -0
  94. package/package/components/multiselect/MultiSelectWrapper.svelte +1 -1
  95. package/package/components/propertyPicker/ObjectViewer.svelte +1 -1
  96. package/package/components/runs/RunRow.svelte +3 -7
  97. package/package/components/schema/FlowPropertyEditor.svelte +1 -0
  98. package/package/components/search/GlobalSearchModal.svelte +1 -1
  99. package/package/script_helpers.d.ts +37 -38
  100. package/package/script_helpers.js +100 -38
  101. package/package.json +23 -1
  102. package/package/init_scripts/python_failure_module.d.ts +0 -2
  103. package/package/init_scripts/python_failure_module.js +0 -8
  104. package/package/init_scripts/python_init_code.d.ts +0 -2
  105. package/package/init_scripts/python_init_code.js +0 -40
  106. package/package/init_scripts/python_init_code_clear.d.ts +0 -2
  107. package/package/init_scripts/python_init_code_clear.js +0 -5
  108. package/package/init_scripts/python_init_code_trigger.d.ts +0 -2
  109. package/package/init_scripts/python_init_code_trigger.js +0 -14
@@ -77,14 +77,17 @@ import AppDisplayComponentByJobId from '../../components/display/AppRecomputeAll
77
77
  import AppRecomputeAll from '../../components/display/AppRecomputeAll.svelte';
78
78
  import AppUserResource from '../../components/inputs/AppUserResource.svelte';
79
79
  import { Anchor } from 'lucide-svelte';
80
+ import { findGridItemParentGrid, isContainer } from '../appUtils';
80
81
  export let component;
81
82
  export let selected;
82
83
  export let locked = false;
83
84
  export let render;
84
85
  export let hidden;
85
86
  export let fullHeight;
86
- export let overlapped = false;
87
- const { mode, app, hoverStore, connectingInput, selectedComponent } = getContext('AppViewerContext');
87
+ export let overlapped = undefined;
88
+ export let moveMode = undefined;
89
+ export let componentDraggedId = undefined;
90
+ const { mode, app, hoverStore, connectingInput } = getContext('AppViewerContext');
88
91
  const editorContext = getContext('AppEditorContext');
89
92
  const componentActive = editorContext?.componentActive;
90
93
  const movingcomponents = editorContext?.movingcomponents;
@@ -108,6 +111,26 @@ function mouseOut() {
108
111
  }
109
112
  }, 50);
110
113
  }
114
+ function componentDraggedIsNotChild(componentDraggedId, componentId) {
115
+ let parentGrid = findGridItemParentGrid($app, componentDraggedId);
116
+ return !parentGrid?.startsWith(`${componentId}-`);
117
+ }
118
+ function areOnTheSameSubgrid(componentDraggedId, componentId) {
119
+ return (findGridItemParentGrid($app, componentDraggedId) === findGridItemParentGrid($app, componentId));
120
+ }
121
+ let cachedComponentDraggedIsNotChild;
122
+ let cachedAreOnTheSameSubgrid;
123
+ function updateCache(componentDraggedId) {
124
+ if (componentDraggedId) {
125
+ cachedComponentDraggedIsNotChild = componentDraggedIsNotChild(componentDraggedId, component.id);
126
+ cachedAreOnTheSameSubgrid = areOnTheSameSubgrid(componentDraggedId, component.id);
127
+ }
128
+ else {
129
+ cachedComponentDraggedIsNotChild = undefined;
130
+ cachedAreOnTheSameSubgrid = undefined;
131
+ }
132
+ }
133
+ $: updateCache(componentDraggedId);
111
134
  </script>
112
135
 
113
136
  <!-- svelte-ignore a11y-mouse-events-have-key-events -->
@@ -126,18 +149,23 @@ function mouseOut() {
126
149
  hidden && $mode === 'preview' ? 'hidden' : ''
127
150
  )}
128
151
  >
129
- {#if locked && componentActive && $componentActive && $selectedComponent?.[0] !== component.id && overlapped}
152
+ {#if locked && componentActive && $componentActive && moveMode === 'move' && componentDraggedId && componentDraggedId !== component.id && cachedAreOnTheSameSubgrid}
130
153
  <div
131
- class={twMerge(
132
- 'absolute inset-0 bg-locked center-center flex-col z-50',
133
- overlapped ? 'bg-locked-hover' : ''
134
- )}
154
+ class={twMerge('absolute inset-0 bg-locked center-center flex-col z-50', 'bg-locked-hover')}
135
155
  >
136
156
  <div class="bg-surface p-2 shadow-sm rounded-md flex center-center flex-col gap-2">
137
157
  <Anchor size={24} class="text-primary " />
138
- <div class="text-xs">Anchored: The component cannot be moved</div>
158
+ <div class="text-xs"> Anchored: The component cannot be moved. </div>
139
159
  </div>
140
160
  </div>
161
+ {:else if moveMode === 'insert' && isContainer(component.type) && componentDraggedId && componentDraggedId !== component.id && cachedComponentDraggedIsNotChild}
162
+ <div
163
+ class={twMerge(
164
+ 'absolute inset-0 flex-col rounded-md bg-blue-100 dark:bg-gray-800 bg-opacity-50',
165
+ 'outline-dashed outline-offset-2 outline-2 outline-blue-300 dark:outline-blue-700',
166
+ overlapped === component?.id ? 'bg-draggedover dark:bg-draggedover-dark' : ''
167
+ )}
168
+ />
141
169
  {/if}
142
170
  {#if $mode !== 'preview'}
143
171
  <ComponentHeader
@@ -8,7 +8,9 @@ declare const __propDef: {
8
8
  render: boolean;
9
9
  hidden: boolean;
10
10
  fullHeight: boolean;
11
- overlapped?: boolean | undefined;
11
+ overlapped?: string | undefined;
12
+ moveMode?: string | undefined;
13
+ componentDraggedId?: string | undefined;
12
14
  };
13
15
  events: {
14
16
  lock: CustomEvent<any>;
@@ -222,7 +222,7 @@ function processRunnable(from, to, runnable) {
222
222
  <IdEditor
223
223
  {id}
224
224
  on:selected={() => ($selectedComponent = [id])}
225
- on:change={({ detail }) => renameId(detail)}
225
+ on:save={({ detail }) => renameId(detail)}
226
226
  /></div
227
227
  >
228
228
  {/if}
@@ -1,10 +1,21 @@
1
- <script>import { getContainerHeight } from './utils/container';
1
+ <script context="module">import { writable } from 'svelte/store';
2
+ const componentDraggedIdStore = writable(undefined);
3
+ const componentDraggedParentIdStore = writable(undefined);
4
+ const overlappedStore = writable(undefined);
5
+ const fakeShadowStore = writable(undefined);
6
+ const isCtrlOrMetaPressedStore = writable(false);
7
+ </script>
8
+
9
+ <script>import gridHelp from './utils/helper';
10
+ import { twMerge } from 'tailwind-merge';
11
+ import { getContainerHeight } from './utils/container';
2
12
  import { moveItem, getItemById, specifyUndefinedColumns } from './utils/item';
3
- import { onMount, createEventDispatcher } from 'svelte';
13
+ import { onMount, createEventDispatcher, getContext } from 'svelte';
4
14
  import { getColumn, throttle } from './utils/other';
5
15
  import MoveResize from './MoveResize.svelte';
6
- import { ROW_GAP_X, ROW_GAP_Y, ROW_HEIGHT, sortGridItemsPosition } from '../editor/appUtils';
16
+ import { areShadowsTheSame, findGridItemParentGrid, getDeltaXByComponent, getDeltaYByComponent, isContainer, ROW_GAP_X, ROW_GAP_Y, ROW_HEIGHT, sortGridItemsPosition, subGridIndexKey } from '../editor/appUtils';
7
17
  const dispatch = createEventDispatcher();
18
+ const { app, worldStore } = getContext('AppViewerContext');
8
19
  export let items;
9
20
  export let rowHeight = ROW_HEIGHT;
10
21
  export let cols;
@@ -16,6 +27,7 @@ export let allIdsInPath;
16
27
  export let containerWidth = undefined;
17
28
  export let scroller = undefined;
18
29
  export let sensor = 20;
30
+ export let root = false;
19
31
  export let parentWidth = undefined;
20
32
  let getComputedCols;
21
33
  let container;
@@ -59,6 +71,16 @@ onMount(() => {
59
71
  });
60
72
  let sortedItems = [];
61
73
  $: sortedItems = JSON.parse(JSON.stringify(items)).sort((a, b) => a.id.localeCompare(b.id));
74
+ let resizing = false;
75
+ function handleKeyUp(event) {
76
+ if ((event.key === 'Control' || event.key === 'Meta') && $isCtrlOrMetaPressedStore) {
77
+ setTimeout(() => {
78
+ $isCtrlOrMetaPressedStore = false;
79
+ $fakeShadowStore = undefined;
80
+ }, 50);
81
+ }
82
+ }
83
+ const initialFixedStates = new Map();
62
84
  let initItems = undefined;
63
85
  const updateMatrix = ({ detail }) => {
64
86
  let isPointerUp = detail.isPointerUp;
@@ -93,9 +115,37 @@ const updateMatrix = ({ detail }) => {
93
115
  ...shadows[id]
94
116
  }
95
117
  };
96
- let { items, overlap } = moveItem(activeItem, sortedItems, getComputedCols);
97
- sortedItems = items;
98
- overlapped = overlap ? id : undefined;
118
+ if ($isCtrlOrMetaPressedStore) {
119
+ if ($componentDraggedParentIdStore === $overlappedStore) {
120
+ const fixedContainer = sortedItems.map((item) => {
121
+ if (isContainer(item.data['type'])) {
122
+ initialFixedStates.set(item.id, {
123
+ item3Fixed: item[3].fixed,
124
+ item12Fixed: item[12].fixed
125
+ });
126
+ item[3].fixed = true;
127
+ item[12].fixed = true;
128
+ }
129
+ return item;
130
+ });
131
+ let { items } = moveItem(activeItem, fixedContainer, getComputedCols);
132
+ items = items.map((item) => {
133
+ if (initialFixedStates.has(item.id)) {
134
+ const initialState = initialFixedStates.get(item.id);
135
+ if (initialState) {
136
+ item[3].fixed = initialState.item3Fixed;
137
+ item[12].fixed = initialState.item12Fixed;
138
+ }
139
+ }
140
+ return item;
141
+ });
142
+ sortedItems = items;
143
+ }
144
+ }
145
+ else {
146
+ let { items } = moveItem(activeItem, sortedItems, getComputedCols);
147
+ sortedItems = items;
148
+ }
99
149
  }
100
150
  }
101
151
  for (let id of nselectedIds ?? []) {
@@ -109,6 +159,7 @@ const updateMatrix = ({ detail }) => {
109
159
  };
110
160
  const throttleMatrix = throttle(updateMatrix, throttleResize);
111
161
  //let hiddenComponents = writable({})
162
+ let lastDetail = undefined;
112
163
  const handleRepaint = ({ detail }) => {
113
164
  if (!detail.isPointerUp) {
114
165
  throttleMatrix({ detail });
@@ -125,6 +176,18 @@ const handleRepaint = ({ detail }) => {
125
176
  }, 0)
126
177
  */
127
178
  };
179
+ function handleKeyDown(event) {
180
+ if ((event.key === 'Control' || event.key === 'Meta') && !$isCtrlOrMetaPressedStore) {
181
+ if (resizing) {
182
+ return;
183
+ }
184
+ $isCtrlOrMetaPressedStore = true;
185
+ if (lastDetail) {
186
+ throttleMatrix({ detail: lastDetail });
187
+ lastDetail = undefined;
188
+ }
189
+ }
190
+ }
128
191
  let moveResizes = {};
129
192
  let shadows = {};
130
193
  export function handleMove({ detail }) {
@@ -133,31 +196,183 @@ export function handleMove({ detail }) {
133
196
  moveResize?.updateMove(JSON.parse(JSON.stringify(detail.cordDiff)), detail.eventY);
134
197
  }
135
198
  });
199
+ lastDetail = detail;
136
200
  throttleMatrix({ detail: { isPointerUp: false, activate: false } });
201
+ if (!$isCtrlOrMetaPressedStore) {
202
+ $overlappedStore = undefined;
203
+ return;
204
+ }
205
+ if (
206
+ // We don't display the fake shadow if the dragged component is a child of the overlapped component
207
+ $componentDraggedParentIdStore !== $overlappedStore &&
208
+ detail.shadow &&
209
+ // only update the fake shadow if the are different
210
+ !areShadowsTheSame($fakeShadowStore, detail.shadow)) {
211
+ const draggedItem = sortedItems.find((item) => item.id === $componentDraggedIdStore);
212
+ if (draggedItem) {
213
+ draggedItem[getComputedCols].x = detail.shadow.x;
214
+ draggedItem[getComputedCols].y = detail.shadow.y;
215
+ }
216
+ let items = [];
217
+ if ($overlappedStore) {
218
+ const div = document.getElementById(`component-${$overlappedStore}`);
219
+ const type = div?.getAttribute('data-componenttype');
220
+ if (!$app.subgrids) {
221
+ return;
222
+ }
223
+ const index = type ? subGridIndexKey(type, $overlappedStore, $worldStore) : 0;
224
+ items = $app.subgrids[`${$overlappedStore}-${index}`] ?? [];
225
+ }
226
+ else {
227
+ items = $app.grid ?? [];
228
+ }
229
+ if (!draggedItem) {
230
+ return;
231
+ }
232
+ const freeSpace = gridHelp.findSpace(draggedItem, items, getComputedCols);
233
+ $fakeShadowStore = {
234
+ x: freeSpace.x,
235
+ y: freeSpace.y,
236
+ xPerPx: detail.shadow.xPerPx,
237
+ yPerPx: detail.shadow.yPerPx,
238
+ w: detail.shadow.w,
239
+ h: detail.shadow.h
240
+ };
241
+ }
242
+ // When leaving the overlapped component, we clear the fake shadow
243
+ // to avoid rendering it with the wrong position at the next intersection
244
+ if (detail.intersectingElement !== $overlappedStore) {
245
+ $fakeShadowStore = undefined;
246
+ }
247
+ // Update the overlapped component
248
+ $overlappedStore = detail.intersectingElement;
137
249
  }
138
- export function handleInitMove({ detail }) {
250
+ export function handleInitMove(id) {
251
+ $componentDraggedIdStore = id;
252
+ $componentDraggedParentIdStore = findGridItemParentGrid($app, id)?.split('-')[0] ?? undefined;
139
253
  Object.entries(moveResizes).forEach(([id, moveResize]) => {
140
254
  if (selectedIds?.includes(id)) {
141
255
  moveResize?.initmove();
142
256
  }
143
257
  });
144
258
  }
145
- let overlapped = undefined;
146
259
  </script>
147
260
 
148
- <div class="svlt-grid-container" style="height: {containerHeight}px" bind:this={container}>
261
+ <svelte:window on:keydown={handleKeyDown} on:keyup={handleKeyUp} />
262
+
263
+ <div
264
+ class="svlt-grid-container"
265
+ style="height: {containerHeight}px"
266
+ bind:this={container}
267
+ id={root ? 'root-grid' : undefined}
268
+ data-xperpx={xPerPx}
269
+ >
270
+ <!-- ROOT SHADOW-->
271
+ {#if $isCtrlOrMetaPressedStore && root && $overlappedStore !== $componentDraggedParentIdStore}
272
+ <div
273
+ class={twMerge(
274
+ 'absolute inset-0 flex-col rounded-md bg-blue-100 dark:bg-gray-800 bg-opacity-50',
275
+ 'outline-dashed outline-offset-2 outline-2 outline-blue-300 dark:outline-blue-700',
276
+ $componentDraggedIdStore && $overlappedStore === undefined
277
+ ? 'bg-draggedover dark:bg-draggedover-dark'
278
+ : ''
279
+ )}
280
+ />
281
+ {#if $overlappedStore === undefined && $componentDraggedIdStore && $fakeShadowStore}
282
+ {@const columnGap = gapX}
283
+ <!-- gap between the columns in px -->
284
+ {@const containerBorder = 0.5 * 16}
285
+ <!-- 0.5rem converted to px (1rem = 16px) -->
286
+ {@const gridTotalWidth = containerWidth ? containerWidth - 2 * containerBorder : 0}
287
+ <!-- subtract borders -->
288
+ {@const availableWidth = gridTotalWidth - 11 * columnGap}
289
+ <!-- subtract gaps between the 12 columns (11 gaps) -->
290
+ {@const columnWidthPx = availableWidth / 12}
291
+ <!-- divide by the number of columns -->
292
+ {@const maxX = Math.floor(availableWidth / columnWidthPx) - $fakeShadowStore.w}
293
+
294
+ <div class="absolute inset-0">
295
+ <div class="relative h-full w-full">
296
+ <div
297
+ class="absolute bg-blue-300 transition-all"
298
+ style={`
299
+ left:${Math.min(maxX, $fakeShadowStore.x) * xPerPx + gapX}px ;
300
+ top: ${$fakeShadowStore.y * yPerPx + gapY}px;
301
+ width: ${$fakeShadowStore.w * xPerPx - gapX}px;
302
+ height: ${$fakeShadowStore.h * yPerPx - gapY}px;
303
+ `}
304
+ />
305
+ </div>
306
+ </div>
307
+ {/if}
308
+ {/if}
309
+
149
310
  {#each sortedItems as item (item.id)}
150
311
  {#if item[getComputedCols] != undefined}
312
+ {#if $isCtrlOrMetaPressedStore && item.id === $overlappedStore && $componentDraggedIdStore && $componentDraggedParentIdStore !== item.id && $fakeShadowStore}
313
+ {@const columnGap = gapX}
314
+ <!-- gap between the columns in px -->
315
+ {@const containerBorder = 0.5 * 16}
316
+ <!-- 0.5rem converted to px (1rem = 16px) -->
317
+ {@const gridTotalWidth = containerWidth ? containerWidth - 2 * containerBorder : 0}
318
+ <!-- subtract borders -->
319
+ {@const availableWidth = gridTotalWidth - 11 * columnGap}
320
+ <!-- subtract gaps between the 12 columns (11 gaps) -->
321
+ {@const columnWidthPx = availableWidth / 12}
322
+ <!-- divide by the number of columns -->
323
+ {@const maxX = Math.floor(availableWidth / columnWidthPx) - $fakeShadowStore.w}
324
+
325
+ <div
326
+ class="absolute"
327
+ style={`
328
+ left: ${item[getComputedCols].x * xPerPx + gapX}px;
329
+ top: ${item[getComputedCols].y * yPerPx + gapY}px;
330
+ `}
331
+ >
332
+ <div class="relative h-full w-full">
333
+ <div
334
+ class={twMerge('absolute transition-all duration-[50ms] bg-blue-300')}
335
+ style={`
336
+ left: calc(${
337
+ Math.min($fakeShadowStore.x, maxX) * $fakeShadowStore.xPerPx + gapX
338
+ }px + 0.5rem + ${getDeltaXByComponent(item.data['type'])});
339
+ top: calc(${
340
+ $fakeShadowStore.y * $fakeShadowStore.yPerPx + gapY
341
+ }px + 0.5rem + ${getDeltaYByComponent(item.data['type'])});
342
+ width: ${$fakeShadowStore.w * $fakeShadowStore.xPerPx - gapX * 2}px;
343
+ height: ${$fakeShadowStore.h * $fakeShadowStore.yPerPx - gapY * 2}px;
344
+ `}
345
+ />
346
+ </div>
347
+ </div>
348
+ {/if}
349
+
151
350
  <MoveResize
152
- on:initmove={handleInitMove}
351
+ on:initmove={() => handleInitMove(item.id)}
153
352
  on:move={handleMove}
154
353
  bind:shadow={shadows[item.id]}
155
354
  bind:this={moveResizes[item.id]}
156
355
  on:repaint={handleRepaint}
356
+ on:resizeStart={() => (resizing = true)}
357
+ on:resizeEnd={() => (resizing = false)}
157
358
  onTop={Boolean(allIdsInPath?.includes(item.id))}
158
359
  id={item.id}
159
360
  {xPerPx}
160
361
  {yPerPx}
362
+ fakeShadow={$fakeShadowStore}
363
+ on:dropped={(e) => {
364
+ $componentDraggedIdStore = undefined
365
+ $componentDraggedParentIdStore = undefined
366
+ $overlappedStore = undefined
367
+ $fakeShadowStore = undefined
368
+ lastDetail = undefined
369
+
370
+ if (!$isCtrlOrMetaPressedStore) {
371
+ return
372
+ }
373
+
374
+ dispatch('dropped', e.detail)
375
+ }}
161
376
  width={xPerPx == 0
162
377
  ? 0
163
378
  : Math.min(getComputedCols, item[getComputedCols] && item[getComputedCols].w) * xPerPx -
@@ -172,10 +387,18 @@ let overlapped = undefined;
172
387
  {sensor}
173
388
  container={scroller}
174
389
  nativeContainer={container}
175
- {overlapped}
390
+ overlapped={$overlappedStore}
391
+ moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
392
+ type={item.data['type']}
176
393
  >
177
394
  {#if item[getComputedCols]}
178
- <slot dataItem={item} hidden={false} {overlapped} />
395
+ <slot
396
+ dataItem={item}
397
+ hidden={false}
398
+ overlapped={$overlappedStore}
399
+ moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
400
+ componentDraggedId={$componentDraggedIdStore}
401
+ />
179
402
  {/if}
180
403
  </MoveResize>
181
404
  {/if}
@@ -13,15 +13,15 @@ declare class __sveltets_Render<T> {
13
13
  containerWidth?: number | undefined;
14
14
  scroller?: HTMLElement | undefined;
15
15
  sensor?: number | undefined;
16
+ root?: boolean | undefined;
16
17
  parentWidth?: number | undefined;
17
18
  handleMove?: (({ detail }: {
18
19
  detail: any;
19
20
  }) => void) | undefined;
20
- handleInitMove?: (({ detail }: {
21
- detail: any;
22
- }) => void) | undefined;
21
+ handleInitMove?: ((id: string) => void) | undefined;
23
22
  };
24
23
  events(): {
24
+ dropped: CustomEvent<any>;
25
25
  resize: CustomEvent<any>;
26
26
  mount: CustomEvent<any>;
27
27
  redraw: CustomEvent<any>;
@@ -32,7 +32,9 @@ declare class __sveltets_Render<T> {
32
32
  default: {
33
33
  dataItem: FilledItem<T>;
34
34
  hidden: boolean;
35
- overlapped: undefined;
35
+ overlapped: string | undefined;
36
+ moveMode: string;
37
+ componentDraggedId: string | undefined;
36
38
  };
37
39
  };
38
40
  }
@@ -43,8 +45,6 @@ export default class Grid<T> extends SvelteComponent<GridProps<T>, GridEvents<T>
43
45
  get handleMove(): ({ detail }: {
44
46
  detail: any;
45
47
  }) => void;
46
- get handleInitMove(): ({ detail }: {
47
- detail: any;
48
- }) => void;
48
+ get handleInitMove(): (id: string) => void;
49
49
  }
50
50
  export {};
@@ -1,6 +1,8 @@
1
1
  <script>import { createEventDispatcher, getContext, onMount } from 'svelte';
2
2
  import { writable } from 'svelte/store';
3
3
  import { twMerge } from 'tailwind-merge';
4
+ import { computePosition, findGridItemParentGrid, isContainer } from '../editor/appUtils';
5
+ import { throttle } from './utils/other';
4
6
  const dispatch = createEventDispatcher();
5
7
  export let sensor;
6
8
  export let width;
@@ -19,8 +21,11 @@ export let nativeContainer;
19
21
  export let onTop;
20
22
  export let shadow = undefined;
21
23
  export let overlapped = undefined;
24
+ export let moveMode = 'move';
25
+ export let type = undefined;
26
+ export let fakeShadow = undefined;
22
27
  const ctx = getContext('AppEditorContext');
23
- const { mode } = getContext('AppViewerContext');
28
+ const { mode, app } = getContext('AppViewerContext');
24
29
  const scale = ctx ? ctx.scale : writable(100);
25
30
  const divId = `component-${id}`;
26
31
  let shadowElement;
@@ -128,7 +133,7 @@ function computeRect() {
128
133
  }
129
134
  }
130
135
  let dragClosure = undefined;
131
- const pointerdown = ({ clientX, clientY, pageX, pageY }) => {
136
+ const pointerdown = ({ clientX, clientY }) => {
132
137
  dragClosure = () => {
133
138
  dragClosure = undefined;
134
139
  ctx.componentActive.set(true);
@@ -173,14 +178,35 @@ const update = () => {
173
178
  }
174
179
  }
175
180
  };
181
+ // Shared state for the shadow position
182
+ let currentShadowPosition = undefined;
183
+ let currentIntersectingElementId = undefined;
184
+ let moving = false;
176
185
  const pointermove = (event) => {
177
186
  dragClosure && dragClosure();
178
187
  event.preventDefault();
179
188
  event.stopPropagation();
180
189
  event.stopImmediatePropagation();
190
+ moving = true;
181
191
  const { clientX, clientY } = event;
182
192
  const cordDiff = { x: (clientX / $scale) * 100 - initX, y: (clientY / $scale) * 100 - initY };
183
- dispatch('move', { cordDiff, clientY });
193
+ if (moveMode === 'move') {
194
+ dispatch('move', {
195
+ cordDiff,
196
+ clientY,
197
+ intersectingElement: undefined,
198
+ shadow: undefined
199
+ });
200
+ return;
201
+ }
202
+ throttledComputeShadow(clientX, clientY);
203
+ dispatch('move', {
204
+ cordDiff,
205
+ clientY,
206
+ intersectingElement: currentIntersectingElementId,
207
+ shadow: currentShadowPosition,
208
+ overlapped
209
+ });
184
210
  };
185
211
  export function updateMove(newCoordDiff, clientY) {
186
212
  if (!active) {
@@ -220,6 +246,37 @@ export function updateMove(newCoordDiff, clientY) {
220
246
  update();
221
247
  }
222
248
  }
249
+ let element = undefined;
250
+ function computeShadow(clientX, clientY) {
251
+ const elementsAtPoint = document.elementsFromPoint(clientX, clientY);
252
+ const intersectingElement = elementsAtPoint.find((el) => el.id !== divId &&
253
+ el.classList.contains('svlt-grid-item') &&
254
+ el.getAttribute('data-iscontainer') === 'true');
255
+ const newOverlapped = intersectingElement ? intersectingElement?.id.split('-')[1] : undefined;
256
+ const container = newOverlapped
257
+ ? intersectingElement?.querySelector('.svlt-grid-container')
258
+ : document.getElementById('root-grid');
259
+ const xPerPxComputed = Number(container?.getAttribute('data-xperpx')) ?? xPerPx;
260
+ const position = computePosition(clientX, clientY, xPerPxComputed, yPerPx, newOverlapped, element);
261
+ if (overlapped !== newOverlapped) {
262
+ currentShadowPosition = undefined;
263
+ }
264
+ else {
265
+ // Update shared state
266
+ currentShadowPosition = {
267
+ x: position.x,
268
+ y: position.y,
269
+ xPerPx: xPerPxComputed,
270
+ yPerPx,
271
+ h: item.h,
272
+ w: item.w
273
+ };
274
+ }
275
+ currentIntersectingElementId = newOverlapped;
276
+ }
277
+ const throttledComputeShadow = throttle((clientX, clientY) => {
278
+ computeShadow(clientX, clientY);
279
+ }, 50);
223
280
  const pointerup = (e) => {
224
281
  ctx.componentActive.set(false);
225
282
  stopAutoscroll();
@@ -232,6 +289,19 @@ const pointerup = (e) => {
232
289
  else {
233
290
  dragClosure = undefined;
234
291
  }
292
+ if (!moving) {
293
+ return;
294
+ }
295
+ const parent = findGridItemParentGrid($app, id);
296
+ if (overlapped && (overlapped === parent || parent?.startsWith(overlapped))) {
297
+ return;
298
+ }
299
+ dispatch('dropped', {
300
+ id,
301
+ overlapped,
302
+ x: fakeShadow?.x,
303
+ y: fakeShadow?.y
304
+ });
235
305
  };
236
306
  let resizeInitPos = { x: 0, y: 0 };
237
307
  let initSize = { width: 0, height: 0 };
@@ -252,6 +322,7 @@ const resizePointerDown = (e, o) => {
252
322
  scrollElement = getScroller(container);
253
323
  window.addEventListener('pointermove', resizePointerMove);
254
324
  window.addEventListener('pointerup', resizePointerUp);
325
+ dispatch('resizeStart');
255
326
  };
256
327
  const resizePointerMove = ({ pageX, pageY }) => {
257
328
  if (shadow) {
@@ -281,15 +352,33 @@ const resizePointerUp = (e) => {
281
352
  repaint(true, true);
282
353
  window.removeEventListener('pointermove', resizePointerMove);
283
354
  window.removeEventListener('pointerup', resizePointerUp);
355
+ dispatch('resizeEnd');
284
356
  };
357
+ function shouldDisplayShadow(moveMode, overlapped) {
358
+ if (moveMode === 'move') {
359
+ return true;
360
+ }
361
+ const parent = findGridItemParentGrid($app, id);
362
+ if (parent === undefined) {
363
+ return overlapped === undefined;
364
+ }
365
+ else if (overlapped) {
366
+ return parent.startsWith(overlapped);
367
+ }
368
+ return false;
369
+ }
285
370
  </script>
286
371
 
287
372
  <!-- svelte-ignore a11y-click-events-have-key-events -->
373
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
288
374
  <div
375
+ bind:this={element}
289
376
  draggable="false"
290
377
  on:pointerdown|stopPropagation|preventDefault={pointerdown}
291
378
  id={divId}
292
379
  class="svlt-grid-item"
380
+ data-iscontainer={type ? isContainer(type) : false}
381
+ data-componenttype={type}
293
382
  class:svlt-grid-active={active || (trans && rect)}
294
383
  style="width: {xPerPx == 0 ? 0 : active ? newSize.width : width}px; height:{xPerPx == 0
295
384
  ? 0
@@ -308,16 +397,24 @@ const resizePointerUp = (e) => {
308
397
  } transform: translate(${left}px, ${top}px); `} "
309
398
  >
310
399
  <slot />
311
- <div class="svlt-grid-resizer-bottom" on:pointerdown={(e) => resizePointerDown(e, 'vertical')} />
312
- <div class="svlt-grid-resizer-side" on:pointerdown={(e) => resizePointerDown(e, 'horizontal')} />
313
- <div class="svlt-grid-resizer" on:pointerdown={(e) => resizePointerDown(e, 'both')} />
400
+ {#if moveMode === 'move'}
401
+ <div
402
+ class="svlt-grid-resizer-bottom"
403
+ on:pointerdown={(e) => resizePointerDown(e, 'vertical')}
404
+ />
405
+ <div
406
+ class="svlt-grid-resizer-side"
407
+ on:pointerdown={(e) => resizePointerDown(e, 'horizontal')}
408
+ />
409
+ <div class="svlt-grid-resizer" on:pointerdown={(e) => resizePointerDown(e, 'both')} />
410
+ {/if}
314
411
  </div>
315
412
 
316
413
  {#if xPerPx > 0 && (active || trans) && shadow}
317
414
  <div
318
415
  class={twMerge(
319
416
  'svlt-grid-shadow shadow-active',
320
- overlapped ? 'svlte-grid-shadow-forbidden' : ''
417
+ shouldDisplayShadow(moveMode, overlapped) ? '' : 'hidden'
321
418
  )}
322
419
  style="width: {shadow.w * xPerPx - gapX * 2}px; height: {shadow.h * yPerPx -
323
420
  gapY * 2}px; transform: translate({shadow.x * xPerPx + gapX}px, {shadow.y * yPerPx +
@@ -408,8 +505,4 @@ const resizePointerUp = (e) => {
408
505
  backface-visibility: hidden;
409
506
  -webkit-backface-visibility: hidden;
410
507
  background: #93c4fdd0;
411
- }
412
-
413
- .svlte-grid-shadow-forbidden {
414
- background: rgba(255, 99, 71, 0.2) !important;
415
508
  }</style>