astro-tractstack 2.0.19 → 2.0.21
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.
- package/dist/index.js +6 -32
- package/package.json +1 -1
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +1 -4
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -4
- package/templates/src/components/codehooks/ListContentSetup.tsx +1 -8
- package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -2
- package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -2
- package/templates/src/components/compositor/Compositor.tsx +3 -6
- package/templates/src/components/compositor/Node.tsx +13 -32
- package/templates/src/components/compositor/NodeWithGuid.tsx +49 -5
- package/templates/src/components/compositor/nodes/Pane.tsx +4 -21
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +27 -7
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +3 -1
- package/templates/src/components/compositor/preview/OgImagePreview.tsx +0 -5
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +5 -6
- package/templates/src/components/edit/PanelSwitch.tsx +3 -24
- package/templates/src/components/edit/SettingsPanel.tsx +0 -1
- package/templates/src/components/edit/ToolMode.tsx +6 -14
- package/templates/src/components/edit/pane/AddPanePanel.tsx +58 -25
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +140 -133
- package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +231 -282
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -13
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
- package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +29 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
- package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
- package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
- package/templates/src/stores/nodes.ts +14 -6
- package/templates/src/stores/storykeep.ts +3 -3
- package/templates/src/types/compositorTypes.ts +2 -0
- package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
- package/templates/src/utils/compositor/aiPaneParser.ts +3 -1
- package/templates/src/utils/compositor/designLibraryHelper.ts +523 -203
- package/templates/src/utils/helpers.ts +5 -4
- package/utils/inject-files.ts +6 -32
- package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
- package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
- package/templates/src/utils/compositor/processMarkdown.ts +0 -445
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useMemo } from 'react';
|
|
1
|
+
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { useStore } from '@nanostores/react';
|
|
3
3
|
import {
|
|
4
4
|
Dialog,
|
|
@@ -15,6 +15,7 @@ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
|
15
15
|
import { selectionStore } from '@/stores/selection';
|
|
16
16
|
import { getCtx, NodesContext } from '@/stores/nodes';
|
|
17
17
|
import { createEmptyStorykeep } from '@/utils/compositor/nodesHelper';
|
|
18
|
+
import { brandConfigStore } from '@/stores/storykeep';
|
|
18
19
|
import {
|
|
19
20
|
extractPaneCopy,
|
|
20
21
|
mergeCopyIntoTemplate,
|
|
@@ -24,10 +25,10 @@ import type {
|
|
|
24
25
|
PaneNode,
|
|
25
26
|
StoragePane,
|
|
26
27
|
TemplatePane,
|
|
27
|
-
TemplateMarkdown,
|
|
28
|
-
BaseNode,
|
|
28
|
+
TemplateMarkdown,
|
|
29
|
+
BaseNode,
|
|
29
30
|
} from '@/types/compositorTypes';
|
|
30
|
-
import type {
|
|
31
|
+
import type { DesignLibraryEntry } from '@/types/tractstack';
|
|
31
32
|
import {
|
|
32
33
|
PaneSnapshotGenerator,
|
|
33
34
|
type SnapshotData,
|
|
@@ -44,13 +45,11 @@ const VERBOSE = false;
|
|
|
44
45
|
|
|
45
46
|
interface TemplatePreviewItemProps {
|
|
46
47
|
template: TemplatePane;
|
|
47
|
-
config: BrandConfig;
|
|
48
48
|
onClick: () => void;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const TemplatePreviewItem = ({
|
|
52
52
|
template,
|
|
53
|
-
config,
|
|
54
53
|
onClick,
|
|
55
54
|
}: TemplatePreviewItemProps) => {
|
|
56
55
|
const [previewState, setPreviewState] = useState<{
|
|
@@ -60,7 +59,6 @@ const TemplatePreviewItem = ({
|
|
|
60
59
|
} | null>(null);
|
|
61
60
|
|
|
62
61
|
const fragmentRequest = useMemo((): PanePreviewRequest[] => {
|
|
63
|
-
// This preview logic is correct: it creates a *temporary* context.
|
|
64
62
|
const ctx = new NodesContext();
|
|
65
63
|
ctx.addNode(createEmptyStorykeep('tmp'));
|
|
66
64
|
ctx.addTemplatePane('tmp', template);
|
|
@@ -111,7 +109,6 @@ const TemplatePreviewItem = ({
|
|
|
111
109
|
id={template.id}
|
|
112
110
|
htmlString={previewState.htmlFragment}
|
|
113
111
|
outputWidth={800}
|
|
114
|
-
config={config}
|
|
115
112
|
onComplete={(_id, data) => handleSnapshotComplete(data)}
|
|
116
113
|
onError={(_id, err) =>
|
|
117
114
|
setPreviewState((prev) =>
|
|
@@ -141,16 +138,13 @@ const TemplatePreviewItem = ({
|
|
|
141
138
|
);
|
|
142
139
|
};
|
|
143
140
|
|
|
144
|
-
|
|
145
|
-
config: BrandConfig;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
141
|
+
export const RestylePaneModal = () => {
|
|
149
142
|
const ctx = getCtx();
|
|
150
143
|
const { isRestyleModalOpen, paneToRestyleId } = useStore(selectionStore, {
|
|
151
144
|
keys: ['isRestyleModalOpen', 'paneToRestyleId'],
|
|
152
145
|
});
|
|
153
|
-
const designLibrary =
|
|
146
|
+
const designLibrary = brandConfigStore.get()?.DESIGN_LIBRARY || [];
|
|
147
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
154
148
|
|
|
155
149
|
const [selectedCategory, setSelectedCategory] = useState<string>('all');
|
|
156
150
|
const [searchTerm, setSearchTerm] = useState('');
|
|
@@ -163,6 +157,33 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
163
157
|
return ['all', ...Array.from(allCategories)];
|
|
164
158
|
}, [designLibrary]);
|
|
165
159
|
|
|
160
|
+
const [targetMarkdownCount, setTargetMarkdownCount] = useState<number>(0);
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!paneToRestyleId || !isRestyleModalOpen) {
|
|
164
|
+
setTargetMarkdownCount(0);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const paneChildIds = ctx.getChildNodeIDs(paneToRestyleId);
|
|
168
|
+
const nodesMap = ctx.allNodes.get();
|
|
169
|
+
const gridNodeId = paneChildIds.find(
|
|
170
|
+
(id) => nodesMap.get(id)?.nodeType === 'GridLayoutNode'
|
|
171
|
+
);
|
|
172
|
+
if (gridNodeId) {
|
|
173
|
+
const columns = ctx.getChildNodeIDs(gridNodeId);
|
|
174
|
+
setTargetMarkdownCount(columns.length);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const markdownNodeId = paneChildIds.find(
|
|
178
|
+
(id) => nodesMap.get(id)?.nodeType === 'Markdown'
|
|
179
|
+
);
|
|
180
|
+
if (markdownNodeId) {
|
|
181
|
+
setTargetMarkdownCount(1);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
setTargetMarkdownCount(0);
|
|
185
|
+
}, [paneToRestyleId, isRestyleModalOpen]);
|
|
186
|
+
|
|
166
187
|
const originalPaneData = useMemo(() => {
|
|
167
188
|
if (!paneToRestyleId) return null;
|
|
168
189
|
const paneNode = ctx.allNodes.get().get(paneToRestyleId) as PaneNode;
|
|
@@ -189,9 +210,10 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
189
210
|
return designLibrary.filter(
|
|
190
211
|
(entry: DesignLibraryEntry) =>
|
|
191
212
|
(selectedCategory === 'all' || entry.category === selectedCategory) &&
|
|
192
|
-
entry.title.toLowerCase().includes(searchTerm.toLowerCase())
|
|
213
|
+
entry.title.toLowerCase().includes(searchTerm.toLowerCase()) &&
|
|
214
|
+
entry.markdownCount === targetMarkdownCount
|
|
193
215
|
);
|
|
194
|
-
}, [designLibrary, selectedCategory, searchTerm]);
|
|
216
|
+
}, [designLibrary, selectedCategory, searchTerm, targetMarkdownCount]);
|
|
195
217
|
|
|
196
218
|
const paginatedEntries = useMemo(() => {
|
|
197
219
|
const start = (currentPage - 1) * PAGE_SIZE;
|
|
@@ -230,123 +252,64 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
230
252
|
setSelectedCategory('all');
|
|
231
253
|
};
|
|
232
254
|
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
{
|
|
239
|
-
templateToApply: template,
|
|
240
|
-
originalPaneData: originalPaneData,
|
|
241
|
-
}
|
|
242
|
-
);
|
|
255
|
+
const handleDialogStateChange = (details: { open: boolean }) => {
|
|
256
|
+
if (!details.open) {
|
|
257
|
+
handleClose();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
243
260
|
|
|
261
|
+
const handleSelectTemplate = (template: TemplatePane) => {
|
|
244
262
|
if (!originalPaneData) {
|
|
245
|
-
console.error(
|
|
246
|
-
'%cDEBUG: handleSelectTemplate FAILED: originalPaneData is null.',
|
|
247
|
-
'color: red; font-weight: bold;'
|
|
248
|
-
);
|
|
249
263
|
return;
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
const originalPane = originalPaneData.paneNode;
|
|
253
267
|
const originalPaneId = originalPane.id;
|
|
254
268
|
|
|
255
|
-
|
|
256
|
-
console.log(
|
|
257
|
-
`%cDEBUG: STEP 1 - HOLLOWING OUT original pane ${originalPaneId}`,
|
|
258
|
-
'color: #A0A; font-weight: bold;'
|
|
259
|
-
);
|
|
260
|
-
const oldChildrenNodes = ctx
|
|
261
|
-
.getChildNodeIDs(originalPaneId)
|
|
262
|
-
.map((id) => ctx.allNodes.get().get(id));
|
|
263
|
-
if (VERBOSE)
|
|
264
|
-
console.log(
|
|
265
|
-
'%cDEBUG: Original pane children BEFORE delete:',
|
|
266
|
-
oldChildrenNodes
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const deletedChildren = ctx.deleteChildren(originalPaneId); // This deletes *children*, not the pane itself
|
|
270
|
-
|
|
271
|
-
const childrenAfterDelete = ctx.getChildNodeIDs(originalPaneId);
|
|
272
|
-
if (VERBOSE) {
|
|
273
|
-
console.log(
|
|
274
|
-
`%cDEBUG: Deleted ${deletedChildren.length} old child nodes.`,
|
|
275
|
-
'color: #A0A;'
|
|
276
|
-
);
|
|
277
|
-
console.log(
|
|
278
|
-
`%cDEBUG: Original pane children IDs AFTER delete: [${childrenAfterDelete.join(', ')}]`,
|
|
279
|
-
'color: #A0A;'
|
|
280
|
-
);
|
|
281
|
-
console.log(
|
|
282
|
-
`%cDEBUG: STEP 2 - REFILLING pane with new nodes...`,
|
|
283
|
-
'color: #0A0; font-weight: bold;'
|
|
284
|
-
);
|
|
285
|
-
}
|
|
269
|
+
ctx.deleteChildren(originalPaneId);
|
|
286
270
|
|
|
287
271
|
const newNodesToAdd: BaseNode[] = [];
|
|
288
272
|
const newMarkdown = template.markdown as TemplateMarkdown | undefined;
|
|
273
|
+
const newGridLayout = template.gridLayout;
|
|
289
274
|
const newBgPane = template.bgPane;
|
|
290
275
|
|
|
291
276
|
if (newMarkdown) {
|
|
292
|
-
// Re-parent the new Markdown node to the original pane
|
|
293
277
|
newMarkdown.parentId = originalPaneId;
|
|
294
278
|
newNodesToAdd.push(newMarkdown);
|
|
295
|
-
|
|
296
|
-
// The markdown.nodes are already parented to the newMarkdown.id, which is correct.
|
|
297
|
-
// We just need to add them to the context.
|
|
298
279
|
if (newMarkdown.nodes) {
|
|
299
280
|
newNodesToAdd.push(...newMarkdown.nodes);
|
|
300
281
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (newGridLayout) {
|
|
285
|
+
newGridLayout.parentId = originalPaneId;
|
|
286
|
+
newNodesToAdd.push(newGridLayout);
|
|
287
|
+
|
|
288
|
+
if (newGridLayout.nodes) {
|
|
289
|
+
newGridLayout.nodes.forEach((column) => {
|
|
290
|
+
column.parentId = newGridLayout.id;
|
|
291
|
+
newNodesToAdd.push(column);
|
|
292
|
+
|
|
293
|
+
if (column.nodes) {
|
|
294
|
+
newNodesToAdd.push(...column.nodes);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
307
297
|
}
|
|
308
298
|
}
|
|
309
299
|
|
|
310
300
|
if (newBgPane) {
|
|
311
|
-
// Re-parent the new BgPane node to the original pane
|
|
312
301
|
newBgPane.parentId = originalPaneId;
|
|
313
302
|
newNodesToAdd.push(newBgPane);
|
|
314
|
-
if (VERBOSE) console.log(`%cDEBUG: Prepared new BgPane:`, newBgPane);
|
|
315
303
|
}
|
|
316
304
|
|
|
317
|
-
ctx.addNodes(newNodesToAdd);
|
|
305
|
+
ctx.addNodes(newNodesToAdd);
|
|
318
306
|
|
|
319
|
-
const childrenAfterAdd = ctx.getChildNodeIDs(originalPaneId);
|
|
320
|
-
const childrenNodesAfterAdd = childrenAfterAdd.map((id) =>
|
|
321
|
-
ctx.allNodes.get().get(id)
|
|
322
|
-
);
|
|
323
|
-
if (VERBOSE) {
|
|
324
|
-
console.log(
|
|
325
|
-
`%cDEBUG: Original pane children IDs AFTER add: [${childrenAfterAdd.join(', ')}]`,
|
|
326
|
-
'color: #0A0;'
|
|
327
|
-
);
|
|
328
|
-
console.log(
|
|
329
|
-
`%cDEBUG: Original pane children nodes AFTER add:`,
|
|
330
|
-
childrenNodesAfterAdd
|
|
331
|
-
);
|
|
332
|
-
console.log(
|
|
333
|
-
`%cDEBUG: STEP 3 - UPDATING original pane properties...`,
|
|
334
|
-
'color: #00F; font-weight: bold;'
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// We must get a fresh reference from the store to modify
|
|
339
307
|
const paneToUpdate = ctx.allNodes.get().get(originalPaneId) as PaneNode;
|
|
340
308
|
|
|
341
309
|
if (!paneToUpdate) {
|
|
342
|
-
console.error(
|
|
343
|
-
`%cDEBUG: FAILED TO FIND PANE ${originalPaneId} IN STORE FOR FINAL UPDATE.`,
|
|
344
|
-
'color: red; font-weight: bold;'
|
|
345
|
-
);
|
|
346
310
|
return;
|
|
347
311
|
}
|
|
348
312
|
|
|
349
|
-
// Copy all style/config properties from the template, but keep the original ID, parentId, slug, title
|
|
350
313
|
paneToUpdate.bgColour = template.bgColour;
|
|
351
314
|
paneToUpdate.isDecorative = template.isDecorative;
|
|
352
315
|
paneToUpdate.heightOffsetDesktop = template.heightOffsetDesktop;
|
|
@@ -355,25 +318,9 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
355
318
|
paneToUpdate.heightRatioDesktop = template.heightRatioDesktop;
|
|
356
319
|
paneToUpdate.heightRatioMobile = template.heightRatioMobile;
|
|
357
320
|
paneToUpdate.heightRatioTablet = template.heightRatioTablet;
|
|
358
|
-
paneToUpdate.isChanged = true;
|
|
359
|
-
|
|
360
|
-
if (VERBOSE)
|
|
361
|
-
console.log(
|
|
362
|
-
`%cDEBUG: Calling modifyNodes with this pane object:`,
|
|
363
|
-
paneToUpdate
|
|
364
|
-
);
|
|
365
|
-
ctx.modifyNodes([paneToUpdate]); // This will save the changes and notify the UI
|
|
321
|
+
paneToUpdate.isChanged = true;
|
|
366
322
|
|
|
367
|
-
|
|
368
|
-
console.log(
|
|
369
|
-
'%cDEBUG: handleSelectTemplate FINISHED.',
|
|
370
|
-
'color: #00A; font-weight: bold;'
|
|
371
|
-
);
|
|
372
|
-
console.log(
|
|
373
|
-
'%cDEBUG: Notifying ROOT_NODE to force re-render.',
|
|
374
|
-
'color: green; font-weight: bold;'
|
|
375
|
-
);
|
|
376
|
-
}
|
|
323
|
+
ctx.modifyNodes([paneToUpdate]);
|
|
377
324
|
ctx.notifyNode('root');
|
|
378
325
|
|
|
379
326
|
handleClose();
|
|
@@ -400,177 +347,179 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
|
|
|
400
347
|
);
|
|
401
348
|
|
|
402
349
|
return (
|
|
403
|
-
<Dialog.Root
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
350
|
+
<Dialog.Root
|
|
351
|
+
open={isRestyleModalOpen}
|
|
352
|
+
onOpenChange={handleDialogStateChange}
|
|
353
|
+
modal={false}
|
|
354
|
+
>
|
|
355
|
+
<Dialog.Backdrop className="z-103 fixed inset-0 bg-black/70" />
|
|
356
|
+
<Dialog.Positioner className="z-104 fixed inset-0 flex items-center justify-center">
|
|
357
|
+
<Dialog.Content
|
|
358
|
+
ref={contentRef}
|
|
359
|
+
className="flex flex-col rounded-lg bg-white shadow-2xl"
|
|
360
|
+
style={{ maxHeight: '90vw', width: '90vw' }}
|
|
361
|
+
>
|
|
362
|
+
<header className="flex items-center justify-between border-b p-4">
|
|
363
|
+
<Dialog.Title className="text-xl font-bold">
|
|
364
|
+
Restyle Pane from Design Library
|
|
365
|
+
</Dialog.Title>
|
|
366
|
+
<Dialog.CloseTrigger
|
|
367
|
+
type="button"
|
|
368
|
+
className="rounded-full p-1 text-gray-600 hover:bg-gray-100"
|
|
369
|
+
>
|
|
370
|
+
<XMarkIcon className="h-6 w-6" />
|
|
371
|
+
</Dialog.CloseTrigger>
|
|
372
|
+
</header>
|
|
373
|
+
|
|
374
|
+
<nav className="flex items-center gap-x-4 border-b bg-gray-50 p-4">
|
|
375
|
+
<Select.Root
|
|
376
|
+
collection={selectCollection}
|
|
377
|
+
value={[selectedCategory]}
|
|
378
|
+
onValueChange={(details: SelectValueChangeDetails) =>
|
|
379
|
+
setSelectedCategory(details.value[0])
|
|
380
|
+
}
|
|
381
|
+
className="w-48"
|
|
382
|
+
positioning={{ gutter: 4 }}
|
|
383
|
+
>
|
|
384
|
+
<Select.Label className="mb-1 text-sm font-bold">
|
|
385
|
+
Category
|
|
386
|
+
</Select.Label>
|
|
387
|
+
<Select.Control>
|
|
388
|
+
<Select.Trigger className="flex w-full items-center justify-between rounded border bg-white p-2 text-left">
|
|
389
|
+
<Select.ValueText />
|
|
390
|
+
<Select.Indicator>▼</Select.Indicator>
|
|
391
|
+
</Select.Trigger>
|
|
392
|
+
</Select.Control>
|
|
393
|
+
<Portal>
|
|
394
|
+
<Select.Positioner>
|
|
395
|
+
<Select.Content className="z-105 rounded border bg-white shadow-lg">
|
|
396
|
+
{categories.map((c) => (
|
|
397
|
+
<Select.Item
|
|
398
|
+
key={c}
|
|
399
|
+
item={{ label: c, value: c }}
|
|
400
|
+
className="cursor-pointer p-2 hover:bg-gray-100"
|
|
401
|
+
>
|
|
402
|
+
<Select.ItemText>{c}</Select.ItemText>
|
|
403
|
+
</Select.Item>
|
|
404
|
+
))}
|
|
405
|
+
</Select.Content>
|
|
406
|
+
</Select.Positioner>
|
|
407
|
+
</Portal>
|
|
408
|
+
</Select.Root>
|
|
409
|
+
|
|
410
|
+
<Combobox.Root
|
|
411
|
+
collection={comboboxCollection}
|
|
412
|
+
onInputValueChange={(e: ComboboxInputValueChangeDetails) =>
|
|
413
|
+
setSearchTerm(e.inputValue)
|
|
414
|
+
}
|
|
415
|
+
className="flex-1"
|
|
416
|
+
positioning={{ gutter: 4 }}
|
|
417
|
+
>
|
|
418
|
+
<Combobox.Label className="mb-1 text-sm font-bold">
|
|
419
|
+
Filter by Title
|
|
420
|
+
</Combobox.Label>
|
|
421
|
+
<Combobox.Control>
|
|
422
|
+
<Combobox.Input
|
|
423
|
+
placeholder="Search by title..."
|
|
424
|
+
className="w-full rounded border p-2"
|
|
425
|
+
/>
|
|
426
|
+
</Combobox.Control>
|
|
427
|
+
<Portal>
|
|
428
|
+
<Combobox.Positioner>
|
|
429
|
+
<Combobox.Content className="z-105 rounded border bg-white shadow-lg">
|
|
430
|
+
{filteredEntries.map((entry: DesignLibraryEntry) => (
|
|
431
|
+
<Combobox.Item
|
|
432
|
+
key={entry.title}
|
|
433
|
+
item={entry}
|
|
434
|
+
className="cursor-pointer p-2 hover:bg-gray-100"
|
|
435
|
+
>
|
|
436
|
+
<Combobox.ItemText>{entry.title}</Combobox.ItemText>
|
|
437
|
+
</Combobox.Item>
|
|
438
|
+
))}
|
|
439
|
+
</Combobox.Content>
|
|
440
|
+
</Combobox.Positioner>
|
|
441
|
+
</Portal>
|
|
442
|
+
</Combobox.Root>
|
|
443
|
+
</nav>
|
|
444
|
+
|
|
445
|
+
<main className="flex-1 overflow-y-auto bg-gray-100 p-6">
|
|
446
|
+
{mergedTemplates.length === 0 ? (
|
|
447
|
+
<div className="flex h-full items-center justify-center">
|
|
448
|
+
<p className="text-gray-500">No designs found.</p>
|
|
449
|
+
</div>
|
|
450
|
+
) : (
|
|
451
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
452
|
+
{mergedTemplates.map(({ template }) => (
|
|
453
|
+
<TemplatePreviewItem
|
|
454
|
+
key={template.id}
|
|
455
|
+
template={template}
|
|
456
|
+
onClick={() => handleSelectTemplate(template)}
|
|
457
|
+
/>
|
|
458
|
+
))}
|
|
459
|
+
</div>
|
|
460
|
+
)}
|
|
461
|
+
</main>
|
|
462
|
+
|
|
463
|
+
{totalPages > 1 && (
|
|
464
|
+
<footer className="flex items-center justify-center border-t p-4">
|
|
465
|
+
<Pagination.Root
|
|
466
|
+
count={totalPages * PAGE_SIZE}
|
|
467
|
+
pageSize={PAGE_SIZE}
|
|
468
|
+
siblingCount={1}
|
|
469
|
+
page={currentPage}
|
|
470
|
+
onPageChange={(details: PaginationPageChangeDetails) =>
|
|
471
|
+
setCurrentPage(details.page)
|
|
429
472
|
}
|
|
430
|
-
className="
|
|
431
|
-
positioning={{ gutter: 4 }}
|
|
473
|
+
className="flex items-center gap-x-2"
|
|
432
474
|
>
|
|
433
|
-
<
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
475
|
+
<Pagination.PrevTrigger
|
|
476
|
+
type="button"
|
|
477
|
+
className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
|
|
478
|
+
disabled={currentPage === 1}
|
|
479
|
+
>
|
|
480
|
+
Previous
|
|
481
|
+
</Pagination.PrevTrigger>
|
|
482
|
+
<Pagination.Context>
|
|
483
|
+
{(pagination) =>
|
|
484
|
+
pagination.pages.map((page, index: number) =>
|
|
485
|
+
page.type === 'page' ? (
|
|
486
|
+
<Pagination.Item
|
|
487
|
+
key={index}
|
|
488
|
+
{...page}
|
|
489
|
+
type="page"
|
|
490
|
+
className={classNames(
|
|
491
|
+
'flex h-9 w-9 items-center justify-center rounded text-sm',
|
|
492
|
+
page.value === currentPage
|
|
493
|
+
? 'bg-blue-600 font-bold text-white'
|
|
494
|
+
: 'hover:bg-gray-100'
|
|
495
|
+
)}
|
|
450
496
|
>
|
|
451
|
-
|
|
452
|
-
</
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
<Combobox.Root
|
|
460
|
-
collection={comboboxCollection}
|
|
461
|
-
onInputValueChange={(e: ComboboxInputValueChangeDetails) =>
|
|
462
|
-
setSearchTerm(e.inputValue)
|
|
463
|
-
}
|
|
464
|
-
className="flex-1"
|
|
465
|
-
positioning={{ gutter: 4 }}
|
|
466
|
-
>
|
|
467
|
-
<Combobox.Label className="mb-1 text-sm font-bold">
|
|
468
|
-
Filter by Title
|
|
469
|
-
</Combobox.Label>
|
|
470
|
-
<Combobox.Control>
|
|
471
|
-
<Combobox.Input
|
|
472
|
-
placeholder="Search by title..."
|
|
473
|
-
className="w-full rounded border p-2"
|
|
474
|
-
/>
|
|
475
|
-
</Combobox.Control>
|
|
476
|
-
<Portal>
|
|
477
|
-
<Combobox.Positioner>
|
|
478
|
-
<Combobox.Content className="z-105 rounded border bg-white shadow-lg">
|
|
479
|
-
{filteredEntries.map((entry: DesignLibraryEntry) => (
|
|
480
|
-
<Combobox.Item
|
|
481
|
-
key={entry.title}
|
|
482
|
-
item={entry}
|
|
483
|
-
className="cursor-pointer p-2 hover:bg-gray-100"
|
|
497
|
+
{page.value}
|
|
498
|
+
</Pagination.Item>
|
|
499
|
+
) : (
|
|
500
|
+
<Pagination.Ellipsis
|
|
501
|
+
key={index}
|
|
502
|
+
index={index}
|
|
503
|
+
className="px-2 text-sm"
|
|
484
504
|
>
|
|
485
|
-
|
|
486
|
-
</
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
</Combobox.Positioner>
|
|
490
|
-
</Portal>
|
|
491
|
-
</Combobox.Root>
|
|
492
|
-
</nav>
|
|
493
|
-
|
|
494
|
-
<main className="flex-1 overflow-y-auto bg-gray-100 p-6">
|
|
495
|
-
{mergedTemplates.length === 0 ? (
|
|
496
|
-
<div className="flex h-full items-center justify-center">
|
|
497
|
-
<p className="text-gray-500">No designs found.</p>
|
|
498
|
-
</div>
|
|
499
|
-
) : (
|
|
500
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
501
|
-
{mergedTemplates.map(({ template }) => (
|
|
502
|
-
<TemplatePreviewItem
|
|
503
|
-
key={template.id}
|
|
504
|
-
template={template}
|
|
505
|
-
config={config}
|
|
506
|
-
onClick={() => handleSelectTemplate(template)}
|
|
507
|
-
/>
|
|
508
|
-
))}
|
|
509
|
-
</div>
|
|
510
|
-
)}
|
|
511
|
-
</main>
|
|
512
|
-
|
|
513
|
-
{totalPages > 1 && (
|
|
514
|
-
<footer className="flex items-center justify-center border-t p-4">
|
|
515
|
-
<Pagination.Root
|
|
516
|
-
count={totalPages * PAGE_SIZE}
|
|
517
|
-
pageSize={PAGE_SIZE}
|
|
518
|
-
siblingCount={1}
|
|
519
|
-
page={currentPage}
|
|
520
|
-
onPageChange={(details: PaginationPageChangeDetails) =>
|
|
521
|
-
setCurrentPage(details.page)
|
|
505
|
+
...
|
|
506
|
+
</Pagination.Ellipsis>
|
|
507
|
+
)
|
|
508
|
+
)
|
|
522
509
|
}
|
|
523
|
-
|
|
510
|
+
</Pagination.Context>
|
|
511
|
+
<Pagination.NextTrigger
|
|
512
|
+
type="button"
|
|
513
|
+
className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
|
|
514
|
+
disabled={currentPage === totalPages}
|
|
524
515
|
>
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
<Pagination.Context>
|
|
533
|
-
{(pagination) =>
|
|
534
|
-
pagination.pages.map((page, index: number) =>
|
|
535
|
-
page.type === 'page' ? (
|
|
536
|
-
<Pagination.Item
|
|
537
|
-
key={index}
|
|
538
|
-
{...page}
|
|
539
|
-
type="page"
|
|
540
|
-
className={classNames(
|
|
541
|
-
'flex h-9 w-9 items-center justify-center rounded text-sm',
|
|
542
|
-
page.value === currentPage
|
|
543
|
-
? 'bg-blue-600 font-bold text-white'
|
|
544
|
-
: 'hover:bg-gray-100'
|
|
545
|
-
)}
|
|
546
|
-
>
|
|
547
|
-
{page.value}
|
|
548
|
-
</Pagination.Item>
|
|
549
|
-
) : (
|
|
550
|
-
<Pagination.Ellipsis
|
|
551
|
-
key={index}
|
|
552
|
-
index={index}
|
|
553
|
-
className="px-2 text-sm"
|
|
554
|
-
>
|
|
555
|
-
...
|
|
556
|
-
</Pagination.Ellipsis>
|
|
557
|
-
)
|
|
558
|
-
)
|
|
559
|
-
}
|
|
560
|
-
</Pagination.Context>
|
|
561
|
-
<Pagination.NextTrigger
|
|
562
|
-
type="button"
|
|
563
|
-
className="rounded p-2 text-sm hover:bg-gray-100 disabled:text-gray-400"
|
|
564
|
-
disabled={currentPage === totalPages}
|
|
565
|
-
>
|
|
566
|
-
Next
|
|
567
|
-
</Pagination.NextTrigger>
|
|
568
|
-
</Pagination.Root>
|
|
569
|
-
</footer>
|
|
570
|
-
)}
|
|
571
|
-
</Dialog.Content>
|
|
572
|
-
</Dialog.Positioner>
|
|
573
|
-
</Portal>
|
|
516
|
+
Next
|
|
517
|
+
</Pagination.NextTrigger>
|
|
518
|
+
</Pagination.Root>
|
|
519
|
+
</footer>
|
|
520
|
+
)}
|
|
521
|
+
</Dialog.Content>
|
|
522
|
+
</Dialog.Positioner>
|
|
574
523
|
</Dialog.Root>
|
|
575
524
|
);
|
|
576
525
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
|
|
2
|
-
import type { BrandConfig } from '@/types/tractstack';
|
|
3
2
|
|
|
4
3
|
export interface AiDesignConfig {
|
|
5
4
|
harmony: string;
|
|
@@ -10,7 +9,6 @@ export interface AiDesignConfig {
|
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
interface AiDesignStepProps {
|
|
13
|
-
config: BrandConfig;
|
|
14
12
|
designConfig: AiDesignConfig;
|
|
15
13
|
onDesignConfigChange: (newConfig: AiDesignConfig) => void;
|
|
16
14
|
}
|
|
@@ -24,7 +22,6 @@ const harmonyOptions = [
|
|
|
24
22
|
const themeOptions = ['Light', 'Dark', 'Bright', 'Muted', 'Pastel', 'Earthy'];
|
|
25
23
|
|
|
26
24
|
export const AiDesignStep = ({
|
|
27
|
-
config,
|
|
28
25
|
designConfig,
|
|
29
26
|
onDesignConfigChange,
|
|
30
27
|
}: AiDesignStepProps) => {
|
|
@@ -71,7 +68,6 @@ export const AiDesignStep = ({
|
|
|
71
68
|
<div>
|
|
72
69
|
<ColorPickerCombo
|
|
73
70
|
title="Base Color (Optional)"
|
|
74
|
-
config={config}
|
|
75
71
|
defaultColor={designConfig.baseColor}
|
|
76
72
|
onColorChange={(color) => updateField('baseColor', color)}
|
|
77
73
|
allowNull={true}
|
|
@@ -80,7 +76,6 @@ export const AiDesignStep = ({
|
|
|
80
76
|
<div>
|
|
81
77
|
<ColorPickerCombo
|
|
82
78
|
title="Accent Color (Optional)"
|
|
83
|
-
config={config}
|
|
84
79
|
defaultColor={designConfig.accentColor}
|
|
85
80
|
onColorChange={(color) => updateField('accentColor', color)}
|
|
86
81
|
allowNull={true}
|