@vuu-ui/grid-layout 4.0.0-alpha.1

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 (142) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +141 -0
  3. package/package.json +40 -0
  4. package/src/GridCommand.js +395 -0
  5. package/src/GridComponentSettings.js +163 -0
  6. package/src/GridController.js +359 -0
  7. package/src/GridDragCoordinator.js +293 -0
  8. package/src/GridGeometry.js +1298 -0
  9. package/src/GridLayout.css.js +119 -0
  10. package/src/GridLayout.js +120 -0
  11. package/src/GridLayoutContext.js +58 -0
  12. package/src/GridLayoutDocument.js +307 -0
  13. package/src/GridLayoutDocumentIds.js +110 -0
  14. package/src/GridLayoutItem.css.js +46 -0
  15. package/src/GridLayoutItem.js +147 -0
  16. package/src/GridLayoutLegacyCompatibility.js +66 -0
  17. package/src/GridLayoutModel.js +138 -0
  18. package/src/GridLayoutProvider.js +263 -0
  19. package/src/GridLayoutStackedItem.css.js +26 -0
  20. package/src/GridLayoutStackedtem.js +140 -0
  21. package/src/GridModel.js +1289 -0
  22. package/src/GridPlaceholder.css.js +9 -0
  23. package/src/GridPlaceholder.js +24 -0
  24. package/src/GridSnapshot.js +11 -0
  25. package/src/GridSplitter.css.js +62 -0
  26. package/src/GridSplitter.js +26 -0
  27. package/src/GridStack.js +277 -0
  28. package/src/Icon.css.js +8 -0
  29. package/src/Icon.js +26 -0
  30. package/src/IconButton.css.js +9 -0
  31. package/src/IconButton.js +28 -0
  32. package/src/TabDialog.css.js +10 -0
  33. package/src/TabDialog.js +77 -0
  34. package/src/TabMenu.css.js +8 -0
  35. package/src/TabMenu.js +78 -0
  36. package/src/componentToJson.js +41 -0
  37. package/src/drag-drop-next/DragContextNext.js +173 -0
  38. package/src/drag-drop-next/DragDropProviderNext.css.js +72 -0
  39. package/src/drag-drop-next/DragDropProviderNext.js +91 -0
  40. package/src/drag-drop-next/SpaceMan.js +256 -0
  41. package/src/drag-drop-next/TemplateDragSession.js +36 -0
  42. package/src/drag-drop-next/tabstrip-drag-drop.js +251 -0
  43. package/src/grid-dom-utils.js +57 -0
  44. package/src/grid-layout-utils.js +39 -0
  45. package/src/grid-snapshot-adapters.js +435 -0
  46. package/src/index.js +24 -0
  47. package/src/json-value.js +56 -0
  48. package/src/layoutFromJson.js +15 -0
  49. package/src/layoutToJson.js +5 -0
  50. package/src/propUtils.js +15 -0
  51. package/src/react-element-utils.js +15 -0
  52. package/src/typeOf.js +15 -0
  53. package/src/useAsDropTarget.js +229 -0
  54. package/src/useDraggable.js +46 -0
  55. package/src/useEditTabName.js +70 -0
  56. package/src/useGridChildProps.js +47 -0
  57. package/src/useGridControllerSnapshot.js +21 -0
  58. package/src/useGridLayout.js +727 -0
  59. package/src/useGridSplitterResizing.js +262 -0
  60. package/src/useNotDropTarget.js +2 -0
  61. package/types/AddTabDialog.d.ts +5 -0
  62. package/types/GridCommand.d.ts +146 -0
  63. package/types/GridComponentSettings.d.ts +71 -0
  64. package/types/GridController.d.ts +50 -0
  65. package/types/GridDragCoordinator.d.ts +80 -0
  66. package/types/GridGeometry.d.ts +331 -0
  67. package/types/GridLayout.d.ts +20 -0
  68. package/types/GridLayoutContext.d.ts +141 -0
  69. package/types/GridLayoutDocument.d.ts +61 -0
  70. package/types/GridLayoutDocumentIds.d.ts +15 -0
  71. package/types/GridLayoutItem.d.ts +28 -0
  72. package/types/GridLayoutLegacyCompatibility.d.ts +57 -0
  73. package/types/GridLayoutModel.d.ts +72 -0
  74. package/types/GridLayoutProvider.d.ts +59 -0
  75. package/types/GridLayoutStackedtem.d.ts +10 -0
  76. package/types/GridModel.d.ts +504 -0
  77. package/types/GridPlaceholder.d.ts +5 -0
  78. package/types/GridSnapshot.d.ts +49 -0
  79. package/types/GridSplitter.d.ts +10 -0
  80. package/types/GridStack.d.ts +160 -0
  81. package/types/Icon.d.ts +6 -0
  82. package/types/IconButton.d.ts +6 -0
  83. package/types/TabDialog.d.ts +9 -0
  84. package/types/TabMenu.d.ts +7 -0
  85. package/types/componentToJson.d.ts +38 -0
  86. package/types/drag-drop-next/DragContextNext.d.ts +92 -0
  87. package/types/drag-drop-next/DragDropProviderNext.d.ts +23 -0
  88. package/types/drag-drop-next/SpaceMan.d.ts +35 -0
  89. package/types/drag-drop-next/TemplateDragSession.d.ts +21 -0
  90. package/types/drag-drop-next/tabstrip-drag-drop.d.ts +3 -0
  91. package/types/grid-dom-utils.d.ts +17 -0
  92. package/types/grid-layout-utils.d.ts +18 -0
  93. package/types/grid-matrix.d.ts +5 -0
  94. package/types/grid-snapshot-adapters.d.ts +22 -0
  95. package/types/index.d.ts +24 -0
  96. package/types/json-value.d.ts +23 -0
  97. package/types/layoutFromJson.d.ts +4 -0
  98. package/types/layoutToJson.d.ts +3 -0
  99. package/types/propUtils.d.ts +5 -0
  100. package/types/react-element-utils.d.ts +3 -0
  101. package/types/src/AddTabDialog.d.ts +5 -0
  102. package/types/src/GridLayout.d.ts +14 -0
  103. package/types/src/GridLayoutContext.d.ts +88 -0
  104. package/types/src/GridLayoutItem.d.ts +28 -0
  105. package/types/src/GridLayoutModel.d.ts +59 -0
  106. package/types/src/GridLayoutProvider.d.ts +37 -0
  107. package/types/src/GridLayoutStackedtem.d.ts +10 -0
  108. package/types/src/GridModel.d.ts +350 -0
  109. package/types/src/GridPlaceholder.d.ts +5 -0
  110. package/types/src/GridSplitter.d.ts +9 -0
  111. package/types/src/Icon.d.ts +6 -0
  112. package/types/src/IconButton.d.ts +6 -0
  113. package/types/src/TabMenu.d.ts +6 -0
  114. package/types/src/componentToJson.d.ts +32 -0
  115. package/types/src/drag-drop-next/DragContextNext.d.ts +73 -0
  116. package/types/src/drag-drop-next/DragDropProviderNext.d.ts +20 -0
  117. package/types/src/drag-drop-next/SpaceMan.d.ts +35 -0
  118. package/types/src/drag-drop-next/tabstrip-drag-drop.d.ts +3 -0
  119. package/types/src/grid-dom-utils.d.ts +17 -0
  120. package/types/src/grid-layout-utils.d.ts +80 -0
  121. package/types/src/grid-matrix.d.ts +5 -0
  122. package/types/src/index.d.ts +10 -0
  123. package/types/src/layoutFromJson.d.ts +3 -0
  124. package/types/src/layoutToJson.d.ts +2 -0
  125. package/types/src/propUtils.d.ts +5 -0
  126. package/types/src/react-element-utils.d.ts +3 -0
  127. package/types/src/typeOf.d.ts +6 -0
  128. package/types/src/useAsDropTarget.d.ts +7 -0
  129. package/types/src/useDraggable.d.ts +23 -0
  130. package/types/src/useGridChildProps.d.ts +13 -0
  131. package/types/src/useGridLayout.d.ts +43 -0
  132. package/types/src/useGridSplitterResizing.d.ts +12 -0
  133. package/types/src/useNotDropTarget.d.ts +1 -0
  134. package/types/typeOf.d.ts +6 -0
  135. package/types/useAsDropTarget.d.ts +7 -0
  136. package/types/useDraggable.d.ts +24 -0
  137. package/types/useEditTabName.d.ts +11 -0
  138. package/types/useGridChildProps.d.ts +13 -0
  139. package/types/useGridControllerSnapshot.d.ts +7 -0
  140. package/types/useGridLayout.d.ts +53 -0
  141. package/types/useGridSplitterResizing.d.ts +14 -0
  142. package/types/useNotDropTarget.d.ts +1 -0
@@ -0,0 +1,359 @@
1
+ import { LegacyGridCommandExecutor } from "./GridCommand.js";
2
+ import { gridLayoutDescriptorToSnapshot, normalizeGridSnapshot } from "./grid-snapshot-adapters.js";
3
+ import { toGridStackSnapshot } from "./GridStack.js";
4
+ const semanticSnapshot = ({ revision: _revision, ...snapshot })=>JSON.stringify(snapshot);
5
+ const transactionError = (code, message)=>({
6
+ code,
7
+ message
8
+ });
9
+ class GridController {
10
+ gridModel;
11
+ #executor;
12
+ #listeners = new Set();
13
+ #commitListeners = new Set();
14
+ #activeTransaction;
15
+ #snapshot;
16
+ constructor(gridModel, revision = 0, executor = new LegacyGridCommandExecutor(gridModel)){
17
+ this.gridModel = gridModel;
18
+ this.#executor = executor;
19
+ this.#snapshot = this.#readSnapshot(revision);
20
+ }
21
+ getSnapshot = ()=>this.#snapshot;
22
+ subscribe = (listener)=>{
23
+ this.#listeners.add(listener);
24
+ return ()=>{
25
+ this.#listeners.delete(listener);
26
+ };
27
+ };
28
+ subscribeCommitted = (listener)=>{
29
+ this.#commitListeners.add(listener);
30
+ return ()=>{
31
+ this.#commitListeners.delete(listener);
32
+ };
33
+ };
34
+ dispatch(command) {
35
+ if (this.#activeTransaction) return {
36
+ command: command.type,
37
+ error: transactionError("TRANSACTION_ACTIVE", `Cannot dispatch ${command.type} while a grid transaction is active`),
38
+ ok: false
39
+ };
40
+ const checkpoint = this.gridModel.createCheckpoint();
41
+ const previous = this.#snapshot;
42
+ const result = this.#executeAtomically(command, checkpoint);
43
+ if (!result.ok) return result;
44
+ const candidate = this.#readSnapshot(previous.revision);
45
+ if (semanticSnapshot(candidate) === semanticSnapshot(previous)) return result;
46
+ this.#snapshot = this.#readSnapshot(previous.revision + 1);
47
+ this.#notifyState();
48
+ this.#notifyCommitted({
49
+ commands: [
50
+ command
51
+ ],
52
+ kind: "dispatch",
53
+ previous,
54
+ snapshot: this.#snapshot
55
+ });
56
+ return result;
57
+ }
58
+ beginTransaction(kind) {
59
+ if (this.#activeTransaction) return {
60
+ error: transactionError("TRANSACTION_ACTIVE", `Cannot begin ${kind}; a grid transaction is already active`),
61
+ ok: false
62
+ };
63
+ let transaction;
64
+ transaction = new GridControllerTransaction(kind, this.gridModel.createCheckpoint(), this.#snapshot, (command)=>this.#executePreview(transaction, command), (commands)=>this.#establishWorkingBaseline(transaction, commands), (commands)=>this.#replacePreview(transaction, commands), (commands)=>this.#validateReplacement(transaction, commands), (commands)=>this.#commitTransaction(transaction, kind, transaction.baseline, commands), ()=>this.#rollbackTransaction(transaction, transaction.checkpoint, transaction.baseline));
65
+ this.#activeTransaction = transaction;
66
+ return {
67
+ ok: true,
68
+ transaction
69
+ };
70
+ }
71
+ #executePreview(transaction, command) {
72
+ if (this.#activeTransaction !== transaction) return {
73
+ command: command.type,
74
+ error: transactionError("TRANSACTION_CLOSED", `Cannot dispatch ${command.type}; the grid transaction is closed`),
75
+ ok: false
76
+ };
77
+ const checkpoint = this.gridModel.createCheckpoint();
78
+ const result = this.#executeAtomically(command, checkpoint);
79
+ if (!result.ok) return result;
80
+ const candidate = this.#readSnapshot(this.#snapshot.revision);
81
+ if (semanticSnapshot(candidate) !== semanticSnapshot(this.#snapshot)) {
82
+ this.#snapshot = semanticSnapshot(candidate) === semanticSnapshot(transaction.baseline) ? transaction.baseline : candidate;
83
+ this.#notifyState();
84
+ }
85
+ return result;
86
+ }
87
+ #replacePreview(transaction, commands) {
88
+ const representative = commands[0] ?? {
89
+ type: "regenerate-placeholders"
90
+ };
91
+ if (this.#activeTransaction !== transaction) return {
92
+ command: representative.type,
93
+ error: transactionError("TRANSACTION_CLOSED", "Cannot replace preview; the grid transaction is closed"),
94
+ ok: false
95
+ };
96
+ this.gridModel.restoreCheckpoint(transaction.workingCheckpoint);
97
+ this.#snapshot = transaction.workingBaseline;
98
+ try {
99
+ for (const command of commands){
100
+ const checkpoint = this.gridModel.createCheckpoint();
101
+ const result = this.#executeAtomically(command, checkpoint);
102
+ if (!result.ok) {
103
+ this.gridModel.restoreCheckpoint(transaction.workingCheckpoint);
104
+ this.#snapshot = transaction.workingBaseline;
105
+ transaction.setReplacementCommands([]);
106
+ this.#notifyState();
107
+ return result;
108
+ }
109
+ }
110
+ } catch (error) {
111
+ this.gridModel.restoreCheckpoint(transaction.workingCheckpoint);
112
+ this.#snapshot = transaction.workingBaseline;
113
+ transaction.setReplacementCommands([]);
114
+ this.#notifyState();
115
+ throw error;
116
+ }
117
+ transaction.setReplacementCommands(commands);
118
+ const candidate = this.#readSnapshot(transaction.baseline.revision);
119
+ this.#snapshot = semanticSnapshot(candidate) === semanticSnapshot(transaction.baseline) ? transaction.baseline : candidate;
120
+ this.#notifyState();
121
+ return {
122
+ command: representative.type,
123
+ ok: true
124
+ };
125
+ }
126
+ #establishWorkingBaseline(transaction, commands) {
127
+ const representative = commands[0] ?? {
128
+ type: "regenerate-placeholders"
129
+ };
130
+ if (this.#activeTransaction !== transaction) return {
131
+ command: representative.type,
132
+ error: transactionError("TRANSACTION_CLOSED", "Cannot establish a working baseline; the grid transaction is closed"),
133
+ ok: false
134
+ };
135
+ this.gridModel.restoreCheckpoint(transaction.checkpoint);
136
+ this.#snapshot = transaction.baseline;
137
+ for (const command of commands){
138
+ const checkpoint = this.gridModel.createCheckpoint();
139
+ const result = this.#executeAtomically(command, checkpoint);
140
+ if (!result.ok) {
141
+ this.gridModel.restoreCheckpoint(transaction.checkpoint);
142
+ this.#snapshot = transaction.baseline;
143
+ transaction.resetWorkingBaseline();
144
+ return result;
145
+ }
146
+ }
147
+ const candidate = this.#readSnapshot(transaction.baseline.revision);
148
+ this.#snapshot = semanticSnapshot(candidate) === semanticSnapshot(transaction.baseline) ? transaction.baseline : candidate;
149
+ transaction.setWorkingBaseline(this.gridModel.createCheckpoint(), this.#snapshot, commands);
150
+ this.#notifyState();
151
+ return {
152
+ command: representative.type,
153
+ ok: true
154
+ };
155
+ }
156
+ #validateReplacement(transaction, commands) {
157
+ const representative = commands[0] ?? {
158
+ type: "regenerate-placeholders"
159
+ };
160
+ if (this.#activeTransaction !== transaction) return {
161
+ command: representative.type,
162
+ error: transactionError("TRANSACTION_CLOSED", "Cannot validate replacement; the grid transaction is closed"),
163
+ ok: false
164
+ };
165
+ this.gridModel.restoreCheckpoint(transaction.workingCheckpoint);
166
+ this.#snapshot = transaction.workingBaseline;
167
+ try {
168
+ for (const command of commands){
169
+ const checkpoint = this.gridModel.createCheckpoint();
170
+ const result = this.#executeAtomically(command, checkpoint);
171
+ if (!result.ok) return result;
172
+ }
173
+ return {
174
+ command: representative.type,
175
+ ok: true
176
+ };
177
+ } finally{
178
+ this.gridModel.restoreCheckpoint(transaction.workingCheckpoint);
179
+ this.#snapshot = transaction.workingBaseline;
180
+ }
181
+ }
182
+ #commitTransaction(transaction, kind, baseline, commands) {
183
+ if (this.#activeTransaction !== transaction) return this.#closedResult("commit");
184
+ const changed = semanticSnapshot(this.#snapshot) !== semanticSnapshot(baseline);
185
+ this.#activeTransaction = void 0;
186
+ if (!changed) {
187
+ this.#snapshot = baseline;
188
+ return {
189
+ ok: true,
190
+ snapshot: this.#snapshot
191
+ };
192
+ }
193
+ this.#snapshot = this.#readSnapshot(baseline.revision + 1);
194
+ this.#notifyState();
195
+ this.#notifyCommitted({
196
+ commands: [
197
+ ...commands
198
+ ],
199
+ kind,
200
+ previous: baseline,
201
+ snapshot: this.#snapshot
202
+ });
203
+ return {
204
+ ok: true,
205
+ snapshot: this.#snapshot
206
+ };
207
+ }
208
+ #rollbackTransaction(transaction, checkpoint, baseline) {
209
+ if (this.#activeTransaction !== transaction) return this.#closedResult("rollback");
210
+ const changed = semanticSnapshot(this.#snapshot) !== semanticSnapshot(baseline);
211
+ this.gridModel.restoreCheckpoint(checkpoint);
212
+ this.#snapshot = baseline;
213
+ this.#activeTransaction = void 0;
214
+ if (changed) this.#notifyState();
215
+ return {
216
+ ok: true,
217
+ snapshot: this.#snapshot
218
+ };
219
+ }
220
+ #closedResult(action) {
221
+ return {
222
+ error: transactionError("TRANSACTION_CLOSED", `Cannot ${action}; the grid transaction is closed`),
223
+ ok: false
224
+ };
225
+ }
226
+ #executeAtomically(command, checkpoint) {
227
+ try {
228
+ const result = this.#executor.execute(command);
229
+ if (!result.ok) this.gridModel.restoreCheckpoint(checkpoint);
230
+ return result;
231
+ } catch (error) {
232
+ this.gridModel.restoreCheckpoint(checkpoint);
233
+ throw error;
234
+ }
235
+ }
236
+ #readSnapshot(revision) {
237
+ const snapshot = gridLayoutDescriptorToSnapshot(this.gridModel.toGridLayoutDescriptor(), {
238
+ gridId: this.gridModel.id,
239
+ revision
240
+ });
241
+ return normalizeGridSnapshot({
242
+ ...snapshot,
243
+ stacks: snapshot.stacks.map(({ id })=>toGridStackSnapshot(this.gridModel.getStackState(id)))
244
+ });
245
+ }
246
+ #notifyState() {
247
+ for (const listener of [
248
+ ...this.#listeners
249
+ ])if (this.#listeners.has(listener)) listener();
250
+ }
251
+ #notifyCommitted(transition) {
252
+ for (const listener of [
253
+ ...this.#commitListeners
254
+ ])if (this.#commitListeners.has(listener)) listener(transition);
255
+ }
256
+ }
257
+ class GridControllerTransaction {
258
+ kind;
259
+ checkpoint;
260
+ baseline;
261
+ executePreview;
262
+ establishBaseline;
263
+ replacePreview;
264
+ validatePreviewReplacement;
265
+ commitTransaction;
266
+ rollbackTransaction;
267
+ #commands = [];
268
+ #workingCommands = [];
269
+ #closed = false;
270
+ #workingCheckpoint;
271
+ #workingBaseline;
272
+ constructor(kind, checkpoint, baseline, executePreview, establishBaseline, replacePreview, validatePreviewReplacement, commitTransaction, rollbackTransaction){
273
+ this.kind = kind;
274
+ this.checkpoint = checkpoint;
275
+ this.baseline = baseline;
276
+ this.executePreview = executePreview;
277
+ this.establishBaseline = establishBaseline;
278
+ this.replacePreview = replacePreview;
279
+ this.validatePreviewReplacement = validatePreviewReplacement;
280
+ this.commitTransaction = commitTransaction;
281
+ this.rollbackTransaction = rollbackTransaction;
282
+ this.#workingCheckpoint = checkpoint;
283
+ this.#workingBaseline = baseline;
284
+ }
285
+ get workingCheckpoint() {
286
+ return this.#workingCheckpoint;
287
+ }
288
+ get workingBaseline() {
289
+ return this.#workingBaseline;
290
+ }
291
+ dispatch(command) {
292
+ if (this.#closed) return {
293
+ command: command.type,
294
+ error: transactionError("TRANSACTION_CLOSED", `Cannot dispatch ${command.type}; the grid transaction is closed`),
295
+ ok: false
296
+ };
297
+ const result = this.executePreview(command);
298
+ if (result.ok) this.#commands.push(command);
299
+ return result;
300
+ }
301
+ replace(commands) {
302
+ if (this.#closed) return {
303
+ command: commands[0]?.type ?? "regenerate-placeholders",
304
+ error: transactionError("TRANSACTION_CLOSED", "Cannot replace preview; the grid transaction is closed"),
305
+ ok: false
306
+ };
307
+ return this.replacePreview(commands);
308
+ }
309
+ establishWorkingBaseline(commands) {
310
+ if (this.#closed) return {
311
+ command: commands[0]?.type ?? "regenerate-placeholders",
312
+ error: transactionError("TRANSACTION_CLOSED", "Cannot establish a working baseline; the grid transaction is closed"),
313
+ ok: false
314
+ };
315
+ return this.establishBaseline(commands);
316
+ }
317
+ validateReplacement(commands) {
318
+ if (this.#closed) return {
319
+ command: commands[0]?.type ?? "regenerate-placeholders",
320
+ error: transactionError("TRANSACTION_CLOSED", "Cannot validate replacement; the grid transaction is closed"),
321
+ ok: false
322
+ };
323
+ return this.validatePreviewReplacement(commands);
324
+ }
325
+ setWorkingBaseline(checkpoint, snapshot, commands) {
326
+ this.#workingCheckpoint = checkpoint;
327
+ this.#workingBaseline = snapshot;
328
+ this.#workingCommands.splice(0, this.#workingCommands.length, ...commands);
329
+ this.setReplacementCommands([]);
330
+ }
331
+ resetWorkingBaseline() {
332
+ this.#workingCheckpoint = this.checkpoint;
333
+ this.#workingBaseline = this.baseline;
334
+ this.#workingCommands.length = 0;
335
+ this.#commands.length = 0;
336
+ }
337
+ setReplacementCommands(commands) {
338
+ this.#commands.splice(0, this.#commands.length, ...this.#workingCommands, ...commands);
339
+ }
340
+ commit() {
341
+ if (this.#closed) return {
342
+ error: transactionError("TRANSACTION_CLOSED", "Cannot commit; the grid transaction is closed"),
343
+ ok: false
344
+ };
345
+ const result = this.commitTransaction(this.#commands);
346
+ if (result.ok) this.#closed = true;
347
+ return result;
348
+ }
349
+ rollback() {
350
+ if (this.#closed) return {
351
+ error: transactionError("TRANSACTION_CLOSED", "Cannot rollback; the grid transaction is closed"),
352
+ ok: false
353
+ };
354
+ const result = this.rollbackTransaction();
355
+ if (result.ok) this.#closed = true;
356
+ return result;
357
+ }
358
+ }
359
+ export { GridController };
@@ -0,0 +1,293 @@
1
+ const failure = (code, message)=>({
2
+ error: {
3
+ code,
4
+ message
5
+ },
6
+ ok: false
7
+ });
8
+ const stackPlacement = (intent)=>intent.position && intent.targetItemId ? {
9
+ position: intent.position,
10
+ targetItemId: intent.targetItemId
11
+ } : {};
12
+ const createGridDropPlan = (source, target)=>{
13
+ if ("palette-template" !== source.kind && source.sourceGridId !== target.gridId) return {
14
+ code: "CROSS_GRID_DRAG",
15
+ message: `Grid item #${source.itemId} cannot move from #${source.sourceGridId} to #${target.gridId}`
16
+ };
17
+ const commands = [];
18
+ if ("palette-template" === source.kind) if ("stack" === target.intent.kind) {
19
+ commands.push({
20
+ item: source.item,
21
+ ...stackPlacement(target.intent),
22
+ stackId: target.targetId,
23
+ type: "add-stack-item"
24
+ });
25
+ commands.push({
26
+ itemId: source.item.id,
27
+ stackId: target.targetId,
28
+ type: "select-stack-item"
29
+ });
30
+ } else commands.push({
31
+ item: source.item,
32
+ type: "add-item"
33
+ });
34
+ const itemId = "palette-template" === source.kind ? source.item.id : source.itemId;
35
+ switch(target.intent.kind){
36
+ case "split":
37
+ if ("stack-member" === source.kind) commands.push({
38
+ itemId,
39
+ stackId: source.stackId,
40
+ targetId: target.targetId,
41
+ position: target.intent.position,
42
+ type: "move-stack-item-to-grid"
43
+ });
44
+ else commands.push({
45
+ itemId,
46
+ position: target.intent.position,
47
+ targetId: target.targetId,
48
+ type: "move-item"
49
+ });
50
+ break;
51
+ case "replace":
52
+ if ("stack-member" === source.kind) commands.push({
53
+ itemId,
54
+ stackId: source.stackId,
55
+ targetId: target.targetId,
56
+ type: "replace-stack-item"
57
+ });
58
+ else commands.push({
59
+ itemId,
60
+ targetId: target.targetId,
61
+ type: "replace-item"
62
+ });
63
+ break;
64
+ case "create-stack":
65
+ if ("stack-member" === source.kind) return {
66
+ code: "UNSUPPORTED_DROP",
67
+ message: "A stack member cannot create a stack without first becoming standalone"
68
+ };
69
+ commands.push({
70
+ itemId,
71
+ selectedItemId: itemId,
72
+ targetId: target.targetId,
73
+ type: "create-stack"
74
+ });
75
+ break;
76
+ case "stack":
77
+ if ("palette-template" === source.kind) break;
78
+ if ("stack-member" === source.kind && source.stackId === target.targetId) {
79
+ if (!target.intent.position || !target.intent.targetItemId) return {
80
+ code: "UNSUPPORTED_DROP",
81
+ message: "Reordering a stack member requires a target item and position"
82
+ };
83
+ commands.push({
84
+ activate: source.selected,
85
+ itemId,
86
+ position: target.intent.position,
87
+ stackId: source.stackId,
88
+ targetItemId: target.intent.targetItemId,
89
+ type: "reorder-stack-item"
90
+ });
91
+ } else commands.push({
92
+ ...stackPlacement(target.intent),
93
+ fromStackId: "stack-member" === source.kind ? source.stackId : void 0,
94
+ itemId,
95
+ stackId: target.targetId,
96
+ type: "move-item-to-stack"
97
+ });
98
+ break;
99
+ }
100
+ if (!commands.some(({ type })=>"reorder-stack-item" === type)) commands.push({
101
+ type: "regenerate-placeholders"
102
+ });
103
+ return {
104
+ commands,
105
+ source,
106
+ target
107
+ };
108
+ };
109
+ const isPlanError = (value)=>"code" in value;
110
+ class GridDragCoordinator {
111
+ gridId;
112
+ controller;
113
+ #sourceRemovedBaseline = false;
114
+ #state = {
115
+ phase: "idle"
116
+ };
117
+ #transaction;
118
+ constructor(gridId, controller){
119
+ this.gridId = gridId;
120
+ this.controller = controller;
121
+ }
122
+ get state() {
123
+ return this.#state;
124
+ }
125
+ begin(source) {
126
+ if ("dragging" === this.#state.phase || "previewing" === this.#state.phase) return failure("INVALID_TRANSITION", `Cannot begin a drag while ${this.#state.phase}`);
127
+ if ("palette-template" !== source.kind && source.sourceGridId !== this.gridId) return failure("CROSS_GRID_DRAG", `Grid item #${source.itemId} cannot move from #${source.sourceGridId} to #${this.gridId}`);
128
+ this.#state = {
129
+ phase: "dragging",
130
+ source
131
+ };
132
+ if ("existing-item" === source.kind) {
133
+ const started = this.controller.beginTransaction("drag");
134
+ if (!started.ok) {
135
+ this.#state = {
136
+ phase: "idle"
137
+ };
138
+ return {
139
+ error: started.error,
140
+ ok: false
141
+ };
142
+ }
143
+ this.#transaction = started.transaction;
144
+ const removed = this.#transaction.establishWorkingBaseline([
145
+ {
146
+ itemId: source.itemId,
147
+ reason: "drag",
148
+ type: "remove-item"
149
+ }
150
+ ]);
151
+ if (!removed.ok) {
152
+ this.#transaction.rollback();
153
+ this.#transaction = void 0;
154
+ this.#state = {
155
+ phase: "idle"
156
+ };
157
+ return this.#commandFailure(removed);
158
+ }
159
+ this.#sourceRemovedBaseline = true;
160
+ }
161
+ return {
162
+ ok: true,
163
+ state: this.#state
164
+ };
165
+ }
166
+ preview(target) {
167
+ if ("dragging" !== this.#state.phase && "previewing" !== this.#state.phase) return failure("INVALID_TRANSITION", `Cannot preview a drop while ${this.#state.phase}`);
168
+ if (target.gridId !== this.gridId) {
169
+ this.#clearTargetPreview();
170
+ return failure("CROSS_GRID_DRAG", `Coordinator #${this.gridId} cannot preview target grid #${target.gridId}`);
171
+ }
172
+ const source = this.#state.source;
173
+ const plan = createGridDropPlan(source, target);
174
+ if (isPlanError(plan)) {
175
+ this.#clearTargetPreview();
176
+ return {
177
+ error: plan,
178
+ ok: false
179
+ };
180
+ }
181
+ if (!this.#transaction) {
182
+ const result = this.controller.beginTransaction("drag");
183
+ if (!result.ok) return {
184
+ error: result.error,
185
+ ok: false
186
+ };
187
+ this.#transaction = result.transaction;
188
+ }
189
+ const result = "existing-item" === source.kind ? this.#transaction.validateReplacement(plan.commands) : this.#transaction.replace(plan.commands);
190
+ if (!result.ok) {
191
+ this.#clearTargetPreview();
192
+ return this.#commandFailure(result);
193
+ }
194
+ this.#state = {
195
+ phase: "previewing",
196
+ plan,
197
+ source,
198
+ target
199
+ };
200
+ return {
201
+ ok: true,
202
+ state: this.#state
203
+ };
204
+ }
205
+ commit() {
206
+ if ("previewing" !== this.#state.phase || !this.#transaction) return failure("INVALID_TRANSITION", `Cannot commit a drag while ${this.#state.phase}`);
207
+ const { plan, source } = this.#state;
208
+ if ("existing-item" === source.kind) {
209
+ const applied = this.#transaction.replace(plan.commands);
210
+ if (!applied.ok) return this.#commandFailure(applied);
211
+ }
212
+ const result = this.#transaction.commit();
213
+ if (!result.ok) return {
214
+ error: result.error,
215
+ ok: false
216
+ };
217
+ this.#transaction = void 0;
218
+ this.#sourceRemovedBaseline = false;
219
+ this.#state = {
220
+ phase: "committed",
221
+ source
222
+ };
223
+ return {
224
+ ok: true,
225
+ state: this.#state
226
+ };
227
+ }
228
+ clearPreview() {
229
+ if ("previewing" !== this.#state.phase) return failure("INVALID_TRANSITION", `Cannot clear a preview while ${this.#state.phase}`);
230
+ const result = this.#clearTargetPreview();
231
+ if (result && !result.ok) return "command" in result ? this.#commandFailure(result) : {
232
+ error: result.error,
233
+ ok: false
234
+ };
235
+ return {
236
+ ok: true,
237
+ state: this.#state
238
+ };
239
+ }
240
+ cancel() {
241
+ if ("dragging" !== this.#state.phase && "previewing" !== this.#state.phase) return failure("INVALID_TRANSITION", `Cannot cancel a drag while ${this.#state.phase}`);
242
+ const source = this.#state.source;
243
+ const result = this.#rollbackPreview();
244
+ if (result && !result.ok) return {
245
+ error: result.error,
246
+ ok: false
247
+ };
248
+ this.#state = {
249
+ phase: "cancelled",
250
+ source
251
+ };
252
+ return {
253
+ ok: true,
254
+ state: this.#state
255
+ };
256
+ }
257
+ dispose() {
258
+ if (this.#transaction) {
259
+ const result = this.#transaction.rollback();
260
+ this.#transaction = void 0;
261
+ if (!result.ok) throw Error(result.error.message);
262
+ }
263
+ this.#sourceRemovedBaseline = false;
264
+ this.#state = {
265
+ phase: "idle"
266
+ };
267
+ }
268
+ #clearTargetPreview() {
269
+ if (this.#sourceRemovedBaseline && this.#transaction) {
270
+ const result = this.#transaction.replace([]);
271
+ if ("previewing" === this.#state.phase) this.#state = {
272
+ phase: "dragging",
273
+ source: this.#state.source
274
+ };
275
+ return result;
276
+ }
277
+ return this.#rollbackPreview();
278
+ }
279
+ #rollbackPreview() {
280
+ const result = this.#transaction?.rollback();
281
+ this.#transaction = void 0;
282
+ this.#sourceRemovedBaseline = false;
283
+ if ("previewing" === this.#state.phase) this.#state = {
284
+ phase: "dragging",
285
+ source: this.#state.source
286
+ };
287
+ return result;
288
+ }
289
+ #commandFailure(result) {
290
+ return failure("TRANSACTION_FAILURE", `${result.error.code}: ${result.error.message}`);
291
+ }
292
+ }
293
+ export { GridDragCoordinator, createGridDropPlan };