@morphika/andami 0.5.3 → 0.5.4
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.
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useCallback } from "react";
|
|
3
|
+
import { useEffect, useCallback, useRef } from "react";
|
|
4
4
|
import { assetUrl } from "../../lib/assets";
|
|
5
5
|
|
|
6
|
+
const FOCUSABLE_SELECTOR =
|
|
7
|
+
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex="-1"])';
|
|
8
|
+
|
|
6
9
|
// ============================================
|
|
7
10
|
// Helpers
|
|
8
11
|
// ============================================
|
|
@@ -42,22 +45,51 @@ export default function NavContentLightbox({
|
|
|
42
45
|
contentUrl,
|
|
43
46
|
onClose,
|
|
44
47
|
}: NavContentLightboxProps) {
|
|
45
|
-
|
|
48
|
+
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
49
|
+
const closeButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
50
|
+
|
|
51
|
+
// Focus trap: cycle Tab between focusable children, close on Escape
|
|
46
52
|
const handleKey = useCallback(
|
|
47
53
|
(e: KeyboardEvent) => {
|
|
48
|
-
if (e.key === "Escape")
|
|
54
|
+
if (e.key === "Escape") {
|
|
55
|
+
onClose();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (e.key !== "Tab" || !containerRef.current) return;
|
|
59
|
+
|
|
60
|
+
const focusables = containerRef.current.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR);
|
|
61
|
+
if (focusables.length === 0) return;
|
|
62
|
+
|
|
63
|
+
const first = focusables[0];
|
|
64
|
+
const last = focusables[focusables.length - 1];
|
|
65
|
+
const active = document.activeElement as HTMLElement | null;
|
|
66
|
+
|
|
67
|
+
if (e.shiftKey && active === first) {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
last.focus();
|
|
70
|
+
} else if (!e.shiftKey && active === last) {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
first.focus();
|
|
73
|
+
}
|
|
49
74
|
},
|
|
50
75
|
[onClose]
|
|
51
76
|
);
|
|
52
77
|
|
|
53
78
|
useEffect(() => {
|
|
79
|
+
// Remember where focus came from so we can restore it on close
|
|
80
|
+
const previouslyFocused = document.activeElement as HTMLElement | null;
|
|
81
|
+
|
|
54
82
|
window.addEventListener("keydown", handleKey);
|
|
55
|
-
// Lock body scroll
|
|
56
83
|
const prev = document.body.style.overflow;
|
|
57
84
|
document.body.style.overflow = "hidden";
|
|
85
|
+
|
|
86
|
+
// Move focus into the lightbox (close button is always present)
|
|
87
|
+
closeButtonRef.current?.focus();
|
|
88
|
+
|
|
58
89
|
return () => {
|
|
59
90
|
window.removeEventListener("keydown", handleKey);
|
|
60
91
|
document.body.style.overflow = prev;
|
|
92
|
+
previouslyFocused?.focus?.();
|
|
61
93
|
};
|
|
62
94
|
}, [handleKey]);
|
|
63
95
|
|
|
@@ -78,11 +110,16 @@ export default function NavContentLightbox({
|
|
|
78
110
|
|
|
79
111
|
return (
|
|
80
112
|
<div
|
|
113
|
+
ref={containerRef}
|
|
114
|
+
role="dialog"
|
|
115
|
+
aria-modal="true"
|
|
116
|
+
aria-label="Media lightbox"
|
|
81
117
|
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/90 backdrop-blur-sm"
|
|
82
118
|
onClick={onClose}
|
|
83
119
|
>
|
|
84
120
|
{/* Close button */}
|
|
85
121
|
<button
|
|
122
|
+
ref={closeButtonRef}
|
|
86
123
|
onClick={onClose}
|
|
87
124
|
className="absolute top-5 right-5 w-10 h-10 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center transition-colors z-10"
|
|
88
125
|
aria-label="Close lightbox"
|
|
@@ -60,7 +60,6 @@ export function createBlockActions(set: StoreSet, get: StoreGet): BlockSliceActi
|
|
|
60
60
|
},
|
|
61
61
|
|
|
62
62
|
deleteBlock: (blockKey: string): void => {
|
|
63
|
-
get()._pushSnapshot();
|
|
64
63
|
const filterBlocks = (cols: import("../../lib/sanity/types").SectionColumn[]) =>
|
|
65
64
|
cols.map((c) => ({ ...c, blocks: c.blocks.filter((b) => b._key !== blockKey) }));
|
|
66
65
|
|
|
@@ -88,12 +87,13 @@ export function createBlockActions(set: StoreSet, get: StoreGet): BlockSliceActi
|
|
|
88
87
|
rows: finalRows,
|
|
89
88
|
selectedBlockKey: state.selectedBlockKey === blockKey ? null : state.selectedBlockKey,
|
|
90
89
|
isDirty: true,
|
|
90
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
91
|
+
_future: [],
|
|
91
92
|
};
|
|
92
93
|
});
|
|
93
94
|
},
|
|
94
95
|
|
|
95
96
|
duplicateBlock: (blockKey: string): void => {
|
|
96
|
-
get()._pushSnapshot();
|
|
97
97
|
set((state) => {
|
|
98
98
|
const newKey = generateKey();
|
|
99
99
|
const dupInColumns = (cols: import("../../lib/sanity/types").SectionColumn[]) =>
|
|
@@ -125,7 +125,13 @@ export function createBlockActions(set: StoreSet, get: StoreGet): BlockSliceActi
|
|
|
125
125
|
}
|
|
126
126
|
return item;
|
|
127
127
|
});
|
|
128
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
rows,
|
|
130
|
+
selectedBlockKey: newKey,
|
|
131
|
+
isDirty: true,
|
|
132
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
133
|
+
_future: [],
|
|
134
|
+
};
|
|
129
135
|
});
|
|
130
136
|
},
|
|
131
137
|
|
|
@@ -148,7 +154,6 @@ export function createBlockActions(set: StoreSet, get: StoreGet): BlockSliceActi
|
|
|
148
154
|
},
|
|
149
155
|
|
|
150
156
|
reorderBlocks: (sectionKey: string, columnKey: string, fromIndex: number, toIndex: number): void => {
|
|
151
|
-
get()._pushSnapshot();
|
|
152
157
|
set((state) => {
|
|
153
158
|
const path = findSectionPath(state.rows, sectionKey);
|
|
154
159
|
if (!path) return state;
|
|
@@ -162,7 +167,12 @@ export function createBlockActions(set: StoreSet, get: StoreGet): BlockSliceActi
|
|
|
162
167
|
return { ...c, blocks };
|
|
163
168
|
}),
|
|
164
169
|
}));
|
|
165
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
rows,
|
|
172
|
+
isDirty: true,
|
|
173
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
174
|
+
_future: [],
|
|
175
|
+
};
|
|
166
176
|
});
|
|
167
177
|
},
|
|
168
178
|
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
import { isCoverSection } from "../../lib/sanity/types";
|
|
20
20
|
import { generateKey } from "./utils";
|
|
21
21
|
import { createDefaultCoverSection, createDefaultCoverRow } from "./defaults";
|
|
22
|
+
import { pushSnapshot } from "./history";
|
|
22
23
|
|
|
23
24
|
type StoreSet = (
|
|
24
25
|
partial: Partial<BuilderState> | ((state: BuilderState) => Partial<BuilderState>)
|
|
@@ -82,7 +83,6 @@ function updateCoverInRows(
|
|
|
82
83
|
export function createCoverActions(set: StoreSet, get: StoreGet): CoverSliceActions {
|
|
83
84
|
return {
|
|
84
85
|
addCoverSection: (afterRowKey?: string | null): void => {
|
|
85
|
-
get()._pushSnapshot();
|
|
86
86
|
const section = createDefaultCoverSection();
|
|
87
87
|
set((state) => {
|
|
88
88
|
const rows = [...state.rows];
|
|
@@ -96,12 +96,17 @@ export function createCoverActions(set: StoreSet, get: StoreGet): CoverSliceActi
|
|
|
96
96
|
} else {
|
|
97
97
|
rows.push(section);
|
|
98
98
|
}
|
|
99
|
-
return {
|
|
99
|
+
return {
|
|
100
|
+
rows,
|
|
101
|
+
isDirty: true,
|
|
102
|
+
selectedRowKey: section._key,
|
|
103
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
104
|
+
_future: [],
|
|
105
|
+
};
|
|
100
106
|
});
|
|
101
107
|
},
|
|
102
108
|
|
|
103
109
|
addCoverRow: (sectionKey: string): void => {
|
|
104
|
-
get()._pushSnapshot();
|
|
105
110
|
set((state) => ({
|
|
106
111
|
rows: updateCoverInRows(state.rows, sectionKey, (section) => {
|
|
107
112
|
if (section.cover_rows.length >= 5) return section;
|
|
@@ -137,11 +142,12 @@ export function createCoverActions(set: StoreSet, get: StoreGet): CoverSliceActi
|
|
|
137
142
|
return { ...section, cover_rows: newRows };
|
|
138
143
|
}),
|
|
139
144
|
isDirty: true,
|
|
145
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
146
|
+
_future: [],
|
|
140
147
|
}));
|
|
141
148
|
},
|
|
142
149
|
|
|
143
150
|
removeCoverRow: (sectionKey: string, rowKey: string): void => {
|
|
144
|
-
get()._pushSnapshot();
|
|
145
151
|
set((state) => ({
|
|
146
152
|
rows: updateCoverInRows(state.rows, sectionKey, (section) => {
|
|
147
153
|
if (section.cover_rows.length <= 1) return section;
|
|
@@ -183,6 +189,8 @@ export function createCoverActions(set: StoreSet, get: StoreGet): CoverSliceActi
|
|
|
183
189
|
};
|
|
184
190
|
}),
|
|
185
191
|
isDirty: true,
|
|
192
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
193
|
+
_future: [],
|
|
186
194
|
}));
|
|
187
195
|
},
|
|
188
196
|
|
|
@@ -248,13 +256,14 @@ export function createCoverActions(set: StoreSet, get: StoreGet): CoverSliceActi
|
|
|
248
256
|
"nav_color"
|
|
249
257
|
>>
|
|
250
258
|
): void => {
|
|
251
|
-
get()._pushSnapshot();
|
|
252
259
|
set((state) => ({
|
|
253
260
|
rows: updateCoverInRows(state.rows, sectionKey, (section) => ({
|
|
254
261
|
...section,
|
|
255
262
|
...fields,
|
|
256
263
|
})),
|
|
257
264
|
isDirty: true,
|
|
265
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
266
|
+
_future: [],
|
|
258
267
|
}));
|
|
259
268
|
},
|
|
260
269
|
|
|
@@ -275,13 +284,14 @@ export function createCoverActions(set: StoreSet, get: StoreGet): CoverSliceActi
|
|
|
275
284
|
sectionKey: string,
|
|
276
285
|
height: CoverSection["height"]
|
|
277
286
|
): void => {
|
|
278
|
-
get()._pushSnapshot();
|
|
279
287
|
set((state) => ({
|
|
280
288
|
rows: updateCoverInRows(state.rows, sectionKey, (section) => ({
|
|
281
289
|
...section,
|
|
282
290
|
height,
|
|
283
291
|
})),
|
|
284
292
|
isDirty: true,
|
|
293
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
294
|
+
_future: [],
|
|
285
295
|
}));
|
|
286
296
|
},
|
|
287
297
|
};
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
moveColumnBetweenSectionsInState,
|
|
42
42
|
swapColumnsBetweenSectionsInState,
|
|
43
43
|
} from "./store-helpers";
|
|
44
|
+
import { pushSnapshot } from "./history";
|
|
44
45
|
|
|
45
46
|
type StoreSet = (
|
|
46
47
|
partial: Partial<BuilderState> | ((state: BuilderState) => Partial<BuilderState>)
|
|
@@ -60,7 +61,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
60
61
|
* the data migration in Session 165.
|
|
61
62
|
*/
|
|
62
63
|
addSection: (blockType: SectionBlockType, afterRowKey?: string | null): void => {
|
|
63
|
-
get()._pushSnapshot();
|
|
64
64
|
const block = createDefaultBlock(blockType);
|
|
65
65
|
const gridColumns = 12;
|
|
66
66
|
|
|
@@ -99,22 +99,31 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
99
99
|
} else {
|
|
100
100
|
rows.push(newSection);
|
|
101
101
|
}
|
|
102
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
rows,
|
|
104
|
+
isDirty: true,
|
|
105
|
+
selectedRowKey: newSection._key,
|
|
106
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
107
|
+
_future: [],
|
|
108
|
+
};
|
|
103
109
|
});
|
|
104
110
|
},
|
|
105
111
|
|
|
106
112
|
reorderRows: (fromIndex: number, toIndex: number): void => {
|
|
107
|
-
get()._pushSnapshot();
|
|
108
113
|
set((state) => {
|
|
109
114
|
const rows = [...state.rows];
|
|
110
115
|
const [moved] = rows.splice(fromIndex, 1);
|
|
111
116
|
rows.splice(toIndex, 0, moved);
|
|
112
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
rows,
|
|
119
|
+
isDirty: true,
|
|
120
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
121
|
+
_future: [],
|
|
122
|
+
};
|
|
113
123
|
});
|
|
114
124
|
},
|
|
115
125
|
|
|
116
126
|
deleteSection: (sectionKey: string): void => {
|
|
117
|
-
get()._pushSnapshot();
|
|
118
127
|
set((state) => ({
|
|
119
128
|
rows: state.rows.filter((item) => item._key !== sectionKey),
|
|
120
129
|
selectedRowKey:
|
|
@@ -124,11 +133,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
124
133
|
selectedBlockKey:
|
|
125
134
|
state.selectedRowKey === sectionKey ? null : state.selectedBlockKey,
|
|
126
135
|
isDirty: true,
|
|
136
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
137
|
+
_future: [],
|
|
127
138
|
}));
|
|
128
139
|
},
|
|
129
140
|
|
|
130
141
|
duplicateSection: (sectionKey: string): void => {
|
|
131
|
-
get()._pushSnapshot();
|
|
132
142
|
set((state) => {
|
|
133
143
|
const idx = state.rows.findIndex((item) => item._key === sectionKey);
|
|
134
144
|
if (idx === -1) return state;
|
|
@@ -173,6 +183,8 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
173
183
|
selectedColumnKey: null,
|
|
174
184
|
selectedBlockKey: null,
|
|
175
185
|
isDirty: true,
|
|
186
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
187
|
+
_future: [],
|
|
176
188
|
};
|
|
177
189
|
});
|
|
178
190
|
},
|
|
@@ -180,15 +192,19 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
180
192
|
// ---- V2 Section operations ----
|
|
181
193
|
|
|
182
194
|
addSectionV2: (preset: SectionV2Preset, afterRowKey?: string | null): void => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
195
|
+
set((state) => {
|
|
196
|
+
const result = addSectionV2InState(state.rows, preset, afterRowKey);
|
|
197
|
+
return {
|
|
198
|
+
rows: result.rows,
|
|
199
|
+
isDirty: true,
|
|
200
|
+
selectedRowKey: result.newSectionKey,
|
|
201
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
202
|
+
_future: [],
|
|
203
|
+
};
|
|
204
|
+
});
|
|
187
205
|
},
|
|
188
206
|
|
|
189
207
|
addColumnV2: (sectionKey: string, gridRow: number, gridColumn: number, span: number): void => {
|
|
190
|
-
get()._pushSnapshot();
|
|
191
|
-
|
|
192
208
|
set((state) => {
|
|
193
209
|
const path = findSectionPath(state.rows, sectionKey);
|
|
194
210
|
if (!path) return state;
|
|
@@ -216,13 +232,16 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
216
232
|
settings: { ...section.settings, preset: newPreset },
|
|
217
233
|
};
|
|
218
234
|
});
|
|
219
|
-
return {
|
|
235
|
+
return {
|
|
236
|
+
rows,
|
|
237
|
+
isDirty: true,
|
|
238
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
239
|
+
_future: [],
|
|
240
|
+
};
|
|
220
241
|
});
|
|
221
242
|
},
|
|
222
243
|
|
|
223
244
|
deleteColumnV2: (sectionKey: string, columnKey: string): void => {
|
|
224
|
-
get()._pushSnapshot();
|
|
225
|
-
|
|
226
245
|
set((state) => {
|
|
227
246
|
const path = findSectionPath(state.rows, sectionKey);
|
|
228
247
|
if (!path) return state;
|
|
@@ -251,13 +270,13 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
251
270
|
selectedColumnKey:
|
|
252
271
|
state.selectedColumnKey === columnKey ? null : state.selectedColumnKey,
|
|
253
272
|
isDirty: true,
|
|
273
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
274
|
+
_future: [],
|
|
254
275
|
};
|
|
255
276
|
});
|
|
256
277
|
},
|
|
257
278
|
|
|
258
279
|
resizeColumnV2: (sectionKey: string, columnKey: string, newSpan: number): void => {
|
|
259
|
-
get()._pushSnapshot();
|
|
260
|
-
|
|
261
280
|
set((state) => {
|
|
262
281
|
const path = findSectionPath(state.rows, sectionKey);
|
|
263
282
|
if (!path) return state;
|
|
@@ -282,7 +301,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
282
301
|
settings: { ...section.settings, preset: newPreset },
|
|
283
302
|
};
|
|
284
303
|
});
|
|
285
|
-
return {
|
|
304
|
+
return {
|
|
305
|
+
rows,
|
|
306
|
+
isDirty: true,
|
|
307
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
308
|
+
_future: [],
|
|
309
|
+
};
|
|
286
310
|
});
|
|
287
311
|
},
|
|
288
312
|
|
|
@@ -295,8 +319,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
295
319
|
);
|
|
296
320
|
if (!result) return; // Blocked by cascade or section not found
|
|
297
321
|
|
|
298
|
-
|
|
299
|
-
|
|
322
|
+
set((state) => ({
|
|
323
|
+
rows: result.rows,
|
|
324
|
+
isDirty: true,
|
|
325
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
326
|
+
_future: [],
|
|
327
|
+
}));
|
|
300
328
|
},
|
|
301
329
|
|
|
302
330
|
moveColumnV2: (sectionKey: string, columnKey: string, targetRow: number, targetColumn: number): void => {
|
|
@@ -309,8 +337,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
309
337
|
);
|
|
310
338
|
if (!result) return;
|
|
311
339
|
|
|
312
|
-
|
|
313
|
-
|
|
340
|
+
set((state) => ({
|
|
341
|
+
rows: result.rows,
|
|
342
|
+
isDirty: true,
|
|
343
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
344
|
+
_future: [],
|
|
345
|
+
}));
|
|
314
346
|
},
|
|
315
347
|
|
|
316
348
|
swapColumnV2: (sectionKey: string, draggedKey: string, targetKey: string): void => {
|
|
@@ -322,8 +354,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
322
354
|
);
|
|
323
355
|
if (!result) return;
|
|
324
356
|
|
|
325
|
-
|
|
326
|
-
|
|
357
|
+
set((state) => ({
|
|
358
|
+
rows: result.rows,
|
|
359
|
+
isDirty: true,
|
|
360
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
361
|
+
_future: [],
|
|
362
|
+
}));
|
|
327
363
|
},
|
|
328
364
|
|
|
329
365
|
moveColumnToGapV2: (sectionKey: string, columnKey: string, targetRow: number, targetColumn: number, targetSpan: number): void => {
|
|
@@ -337,8 +373,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
337
373
|
);
|
|
338
374
|
if (!result) return;
|
|
339
375
|
|
|
340
|
-
|
|
341
|
-
|
|
376
|
+
set((state) => ({
|
|
377
|
+
rows: result.rows,
|
|
378
|
+
isDirty: true,
|
|
379
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
380
|
+
_future: [],
|
|
381
|
+
}));
|
|
342
382
|
},
|
|
343
383
|
|
|
344
384
|
moveColumnBetweenSections: (
|
|
@@ -360,16 +400,17 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
360
400
|
);
|
|
361
401
|
if (!result) return;
|
|
362
402
|
|
|
363
|
-
get()._pushSnapshot();
|
|
364
403
|
// Clear any selection referencing the moved column in its old section —
|
|
365
404
|
// the column key remains but the "selected section" context has changed.
|
|
366
|
-
set({
|
|
405
|
+
set((state) => ({
|
|
367
406
|
rows: result.rows,
|
|
368
407
|
isDirty: true,
|
|
369
408
|
selectedColumnKey: columnKey,
|
|
370
409
|
selectedRowKey: targetSectionKey,
|
|
371
410
|
selectedBlockKey: null,
|
|
372
|
-
|
|
411
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
412
|
+
_future: [],
|
|
413
|
+
}));
|
|
373
414
|
},
|
|
374
415
|
|
|
375
416
|
swapColumnsBetweenSections: (
|
|
@@ -387,23 +428,22 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
387
428
|
);
|
|
388
429
|
if (!result) return;
|
|
389
430
|
|
|
390
|
-
get()._pushSnapshot();
|
|
391
431
|
// After swap: the dragged column now lives in the target section.
|
|
392
432
|
// Select it there (match mental model: "I just put this column here").
|
|
393
|
-
set({
|
|
433
|
+
set((state) => ({
|
|
394
434
|
rows: result.rows,
|
|
395
435
|
isDirty: true,
|
|
396
436
|
selectedColumnKey: sourceColumnKey,
|
|
397
437
|
selectedRowKey: targetSectionKey,
|
|
398
438
|
selectedBlockKey: null,
|
|
399
|
-
|
|
439
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
440
|
+
_future: [],
|
|
441
|
+
}));
|
|
400
442
|
},
|
|
401
443
|
|
|
402
444
|
applyPresetV2: (sectionKey: string, preset: SectionV2Preset): void => {
|
|
403
445
|
if (preset === "custom") return;
|
|
404
446
|
|
|
405
|
-
get()._pushSnapshot();
|
|
406
|
-
|
|
407
447
|
set((state) => {
|
|
408
448
|
const path = findSectionPath(state.rows, sectionKey);
|
|
409
449
|
if (!path) return state;
|
|
@@ -424,7 +464,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
424
464
|
settings: { ...section.settings, preset },
|
|
425
465
|
};
|
|
426
466
|
});
|
|
427
|
-
return {
|
|
467
|
+
return {
|
|
468
|
+
rows,
|
|
469
|
+
isDirty: true,
|
|
470
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
471
|
+
_future: [],
|
|
472
|
+
};
|
|
428
473
|
});
|
|
429
474
|
},
|
|
430
475
|
|
|
@@ -492,7 +537,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
492
537
|
},
|
|
493
538
|
|
|
494
539
|
addBlockV2: (sectionKey: string, columnKey: string, blockType: BlockType, insertIndex?: number): void => {
|
|
495
|
-
get()._pushSnapshot();
|
|
496
540
|
const newBlock = createDefaultBlock(blockType);
|
|
497
541
|
|
|
498
542
|
set((state) => {
|
|
@@ -514,7 +558,13 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
514
558
|
return { ...col, blocks: [...col.blocks, newBlock] };
|
|
515
559
|
}),
|
|
516
560
|
}));
|
|
517
|
-
return {
|
|
561
|
+
return {
|
|
562
|
+
rows,
|
|
563
|
+
selectedBlockKey: newBlock._key,
|
|
564
|
+
isDirty: true,
|
|
565
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
566
|
+
_future: [],
|
|
567
|
+
};
|
|
518
568
|
});
|
|
519
569
|
},
|
|
520
570
|
|
|
@@ -570,7 +620,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
570
620
|
title: string,
|
|
571
621
|
afterRowKey?: string | null
|
|
572
622
|
) => {
|
|
573
|
-
get()._pushSnapshot();
|
|
574
623
|
const instance: CustomSectionInstance = {
|
|
575
624
|
_type: "customSectionInstance",
|
|
576
625
|
_key: generateKey(),
|
|
@@ -591,7 +640,13 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
591
640
|
} else {
|
|
592
641
|
rows.push(instance);
|
|
593
642
|
}
|
|
594
|
-
return {
|
|
643
|
+
return {
|
|
644
|
+
rows,
|
|
645
|
+
isDirty: true,
|
|
646
|
+
selectedRowKey: instance._key,
|
|
647
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
648
|
+
_future: [],
|
|
649
|
+
};
|
|
595
650
|
});
|
|
596
651
|
},
|
|
597
652
|
|
|
@@ -599,7 +654,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
599
654
|
instanceKey: string,
|
|
600
655
|
sectionData: PageSectionV2
|
|
601
656
|
) => {
|
|
602
|
-
get()._pushSnapshot();
|
|
603
657
|
set((state) => ({
|
|
604
658
|
rows: state.rows.map((item) =>
|
|
605
659
|
item._key === instanceKey
|
|
@@ -607,6 +661,8 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
607
661
|
: item
|
|
608
662
|
),
|
|
609
663
|
isDirty: true,
|
|
664
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
665
|
+
_future: [],
|
|
610
666
|
}));
|
|
611
667
|
},
|
|
612
668
|
|
|
@@ -632,7 +688,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
632
688
|
instanceKey: string,
|
|
633
689
|
updates: Partial<SectionV2Settings>
|
|
634
690
|
) => {
|
|
635
|
-
get()._pushSnapshot();
|
|
636
691
|
set((state) => ({
|
|
637
692
|
rows: state.rows.map((item) =>
|
|
638
693
|
item._key === instanceKey && isCustomSectionInstance(item)
|
|
@@ -646,6 +701,8 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
646
701
|
: item
|
|
647
702
|
),
|
|
648
703
|
isDirty: true,
|
|
704
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
705
|
+
_future: [],
|
|
649
706
|
}));
|
|
650
707
|
},
|
|
651
708
|
|
|
@@ -661,13 +718,19 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
661
718
|
// ---- Parallax Group operations (Session 123) ----
|
|
662
719
|
|
|
663
720
|
addParallaxGroup: (afterRowKey?: string | null): void => {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
721
|
+
set((state) => {
|
|
722
|
+
const result = addParallaxGroupInState(state.rows, afterRowKey);
|
|
723
|
+
return {
|
|
724
|
+
rows: result.rows,
|
|
725
|
+
isDirty: true,
|
|
726
|
+
selectedRowKey: result.newGroupKey,
|
|
727
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
728
|
+
_future: [],
|
|
729
|
+
};
|
|
730
|
+
});
|
|
667
731
|
},
|
|
668
732
|
|
|
669
733
|
addParallaxSlide: (groupKey: string): void => {
|
|
670
|
-
get()._pushSnapshot();
|
|
671
734
|
set((state) => ({
|
|
672
735
|
rows: state.rows.map((item) => {
|
|
673
736
|
if (item._key !== groupKey || !isParallaxGroup(item)) return item;
|
|
@@ -676,11 +739,12 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
676
739
|
return { ...group, slides: [...group.slides, newSlide] } as ContentItem;
|
|
677
740
|
}),
|
|
678
741
|
isDirty: true,
|
|
742
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
743
|
+
_future: [],
|
|
679
744
|
}));
|
|
680
745
|
},
|
|
681
746
|
|
|
682
747
|
removeParallaxSlide: (groupKey: string, slideKey: string): void => {
|
|
683
|
-
get()._pushSnapshot();
|
|
684
748
|
set((state) => {
|
|
685
749
|
let changed = false;
|
|
686
750
|
const rows = state.rows.map((item) => {
|
|
@@ -694,7 +758,14 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
694
758
|
slides: group.slides.filter((s) => s._key !== slideKey),
|
|
695
759
|
} as ContentItem;
|
|
696
760
|
});
|
|
697
|
-
return changed
|
|
761
|
+
return changed
|
|
762
|
+
? {
|
|
763
|
+
rows,
|
|
764
|
+
isDirty: true,
|
|
765
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
766
|
+
_future: [],
|
|
767
|
+
}
|
|
768
|
+
: {};
|
|
698
769
|
});
|
|
699
770
|
},
|
|
700
771
|
|
|
@@ -703,7 +774,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
703
774
|
slideKey: string,
|
|
704
775
|
direction: "up" | "down"
|
|
705
776
|
) => {
|
|
706
|
-
get()._pushSnapshot();
|
|
707
777
|
set((state) => {
|
|
708
778
|
let changed = false;
|
|
709
779
|
const rows = state.rows.map((item) => {
|
|
@@ -718,7 +788,14 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
718
788
|
changed = true;
|
|
719
789
|
return { ...group, slides } as ContentItem;
|
|
720
790
|
});
|
|
721
|
-
return changed
|
|
791
|
+
return changed
|
|
792
|
+
? {
|
|
793
|
+
rows,
|
|
794
|
+
isDirty: true,
|
|
795
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
796
|
+
_future: [],
|
|
797
|
+
}
|
|
798
|
+
: {};
|
|
722
799
|
});
|
|
723
800
|
},
|
|
724
801
|
|
|
@@ -727,7 +804,6 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
727
804
|
slideKey: string,
|
|
728
805
|
fields: Partial<ParallaxSlideV2>
|
|
729
806
|
) => {
|
|
730
|
-
get()._pushSnapshot();
|
|
731
807
|
set((state) => ({
|
|
732
808
|
rows: state.rows.map((item) => {
|
|
733
809
|
if (item._key !== groupKey || !isParallaxGroup(item)) return item;
|
|
@@ -740,6 +816,8 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
740
816
|
} as ContentItem;
|
|
741
817
|
}),
|
|
742
818
|
isDirty: true,
|
|
819
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
820
|
+
_future: [],
|
|
743
821
|
}));
|
|
744
822
|
},
|
|
745
823
|
|
|
@@ -749,13 +827,14 @@ export function createSectionActions(set: StoreSet, get: StoreGet): SectionSlice
|
|
|
749
827
|
Pick<ParallaxGroup, "transition_effect" | "snap_enabled" | "parallax_intensity">
|
|
750
828
|
>
|
|
751
829
|
) => {
|
|
752
|
-
get()._pushSnapshot();
|
|
753
830
|
set((state) => ({
|
|
754
831
|
rows: state.rows.map((item) => {
|
|
755
832
|
if (item._key !== groupKey || !isParallaxGroup(item)) return item;
|
|
756
833
|
return { ...item, ...fields } as ContentItem;
|
|
757
834
|
}),
|
|
758
835
|
isDirty: true,
|
|
836
|
+
_history: pushSnapshot(state._history, { rows: state.rows, pageSettings: state.pageSettings }),
|
|
837
|
+
_future: [],
|
|
759
838
|
}));
|
|
760
839
|
},
|
|
761
840
|
};
|
package/lib/version.ts
CHANGED
package/package.json
CHANGED