camox 0.32.0 → 0.33.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.
- package/dist/core/createBlock.d.ts +1 -1
- package/dist/core/createBlock.js +88 -108
- package/dist/core/hooks/useIsEditable.js +6 -10
- package/dist/core/lib/fieldTypes.js +10 -219
- package/dist/features/agent-chat/components/AgentChatSidebar.js +172 -0
- package/dist/features/agent-chat/components/AgentChatThread.js +9 -9
- package/dist/features/preview/CamoxPreview.js +137 -729
- package/dist/features/preview/components/{AddBlockSheet.js → AddBlockSidebar.js} +121 -131
- package/dist/features/preview/components/AssetFieldEditor.js +107 -82
- package/dist/features/preview/components/AssetLightbox.js +4 -4
- package/dist/features/preview/components/AssetPickerModal.js +277 -0
- package/dist/features/preview/components/BlockActionsPopover.js +18 -24
- package/dist/features/preview/components/CreatePageModal.js +6 -6
- package/dist/features/preview/components/FieldToolbar.js +64 -127
- package/dist/features/preview/components/ItemFieldsEditor.js +98 -125
- package/dist/features/preview/components/LeftSidebar.js +64 -0
- package/dist/features/preview/components/LinkFieldEditor.js +1 -1
- package/dist/features/preview/components/MultipleAssetFieldEditor.js +94 -56
- package/dist/features/preview/components/OverlayTracker.js +15 -15
- package/dist/features/preview/components/Overlays.js +12 -12
- package/dist/features/preview/components/PageEditorSidebar.js +563 -0
- package/dist/features/preview/components/PageInfoSidebar.js +1453 -0
- package/dist/features/preview/components/PageNavigatorSidebar.js +590 -0
- package/dist/features/preview/components/PagePicker.js +3 -3
- package/dist/features/preview/components/PageStatusBadge.js +1 -1
- package/dist/features/preview/components/PageTree.js +204 -564
- package/dist/features/preview/components/PreviewPanel.js +123 -158
- package/dist/features/preview/components/PreviewToolbar.js +260 -256
- package/dist/features/preview/components/PublishDialog.js +2 -2
- package/dist/features/preview/components/RepeatableItemsList.js +2 -2
- package/dist/features/preview/components/RightSidebar.js +109 -0
- package/dist/features/preview/components/UnlinkAssetButton.js +2 -2
- package/dist/features/preview/components/useRepeatableItemActions.js +3 -138
- package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
- package/dist/features/preview/previewQueryFns.js +23 -0
- package/dist/features/preview/previewStore.js +46 -63
- package/dist/features/provider/CamoxProvider.js +255 -70
- package/dist/features/provider/useAdminShortcuts.js +5 -48
- package/dist/features/routes/pageRoute.js +1 -1
- package/dist/features/studio/CamoxStudio.js +6 -5
- package/dist/features/studio/components/EnvironmentMenu.js +2 -2
- package/dist/features/studio/components/Navbar.js +53 -71
- package/dist/features/studio/components/ProjectMenu.js +1 -1
- package/dist/features/studio/components/UserButton.js +1 -1
- package/dist/features/studio/routes.js +7 -0
- package/dist/features/studio/useTheme.js +67 -33
- package/dist/features/vite/routeGeneration.js +68 -66
- package/dist/hooks/use-file-upload.js +1 -1
- package/dist/lib/auth.js +23 -22
- package/dist/lib/utils.js +1 -1
- package/dist/studio-overlays.css +1 -1
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/dist/features/agent-chat/components/AgentChatSheet.js +0 -207
- package/dist/features/preview/components/AssetPickerGrid.js +0 -236
- package/dist/features/preview/components/PageContentSheet.js +0 -608
- package/dist/features/preview/components/PageMetadataModal.js +0 -908
- package/dist/features/preview/components/PreviewSideSheet.js +0 -76
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import { blockMutations, blockQueries, fileQueries, repeatableItemMutations } from "../../../lib/queries.js";
|
|
2
|
+
import { trackClientEvent } from "../../../lib/telemetry-client.js";
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import { previewStore, selectionBlockId, selectionField, selectionItemId } from "../previewStore.js";
|
|
5
|
+
import { isFileMarker } from "../../../lib/normalized-data.js";
|
|
6
|
+
import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
|
|
7
|
+
import { useRequireDraftSource } from "../../../core/hooks/useRequireDraftSource.js";
|
|
8
|
+
import { fieldTypesDictionary } from "../../../core/lib/fieldTypes.js";
|
|
9
|
+
import { SingleAssetFieldEditor } from "./AssetFieldEditor.js";
|
|
10
|
+
import { useRepeatableItemActions } from "./useRepeatableItemActions.js";
|
|
11
|
+
import { ItemFieldsEditor, formatFieldName } from "./ItemFieldsEditor.js";
|
|
12
|
+
import { LinkFieldEditor } from "./LinkFieldEditor.js";
|
|
13
|
+
import { MultipleAssetFieldEditor } from "./MultipleAssetFieldEditor.js";
|
|
14
|
+
import { Label } from "@camox/ui/label";
|
|
15
|
+
import { useMutation, useQueries, useQuery } from "@tanstack/react-query";
|
|
16
|
+
import { useSelector } from "@xstate/store-react";
|
|
17
|
+
import * as React from "react";
|
|
18
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
import { Button } from "@camox/ui/button";
|
|
20
|
+
import { CircleMinus, CirclePlus, CornerLeftUp } from "lucide-react";
|
|
21
|
+
import { Spinner } from "@camox/ui/spinner";
|
|
22
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@camox/ui/select";
|
|
23
|
+
import { Switch } from "@camox/ui/switch";
|
|
24
|
+
|
|
25
|
+
//#region src/features/preview/components/PageEditorSidebar.tsx
|
|
26
|
+
const getSettingsFields = (schema) => {
|
|
27
|
+
const properties = schema?.properties;
|
|
28
|
+
if (!properties) return [];
|
|
29
|
+
return Object.keys(properties).map((fieldName) => {
|
|
30
|
+
const prop = properties[fieldName];
|
|
31
|
+
return {
|
|
32
|
+
name: fieldName,
|
|
33
|
+
fieldType: prop.fieldType,
|
|
34
|
+
label: prop.title,
|
|
35
|
+
enumLabels: prop.enumLabels,
|
|
36
|
+
enumValues: prop.enum
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Builds the path of fieldNames from the block root to the given item,
|
|
42
|
+
* then walks the schema down that path to return the sub-schema for the item's fields.
|
|
43
|
+
*/
|
|
44
|
+
const getSchemaForItem = (contentSchema, itemId, itemsMap) => {
|
|
45
|
+
const path = [];
|
|
46
|
+
let current = itemsMap.get(itemId);
|
|
47
|
+
while (current) {
|
|
48
|
+
path.unshift(current.fieldName);
|
|
49
|
+
current = current.parentItemId ? itemsMap.get(current.parentItemId) : void 0;
|
|
50
|
+
}
|
|
51
|
+
let schema = contentSchema;
|
|
52
|
+
for (const fieldName of path) {
|
|
53
|
+
const prop = schema?.properties?.[fieldName];
|
|
54
|
+
if (!prop?.items) return null;
|
|
55
|
+
schema = prop.items;
|
|
56
|
+
}
|
|
57
|
+
return schema;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Like `getSchemaForItem` but returns the **array** schema (one level above
|
|
61
|
+
* the items schema), where per-item settings metadata lives.
|
|
62
|
+
*/
|
|
63
|
+
const getArraySchemaForItem = (contentSchema, itemId, itemsMap) => {
|
|
64
|
+
const path = [];
|
|
65
|
+
let current = itemsMap.get(itemId);
|
|
66
|
+
while (current) {
|
|
67
|
+
path.unshift(current.fieldName);
|
|
68
|
+
current = current.parentItemId ? itemsMap.get(current.parentItemId) : void 0;
|
|
69
|
+
}
|
|
70
|
+
let schema = contentSchema;
|
|
71
|
+
for (let i = 0; i < path.length; i++) {
|
|
72
|
+
const prop = schema?.properties?.[path[i]];
|
|
73
|
+
if (!prop?.items) return null;
|
|
74
|
+
if (i === path.length - 1) return prop;
|
|
75
|
+
schema = prop.items;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Builds the ancestor chain from root to this item (inclusive).
|
|
81
|
+
* Returns items in order from root-most ancestor to the item itself.
|
|
82
|
+
*/
|
|
83
|
+
const buildAncestorChain = (itemId, itemsMap) => {
|
|
84
|
+
const chain = [];
|
|
85
|
+
let current = itemsMap.get(itemId);
|
|
86
|
+
while (current) {
|
|
87
|
+
chain.unshift(current);
|
|
88
|
+
current = current.parentItemId ? itemsMap.get(current.parentItemId) : void 0;
|
|
89
|
+
}
|
|
90
|
+
return chain;
|
|
91
|
+
};
|
|
92
|
+
const PageEditorSidebar = () => {
|
|
93
|
+
const camoxApp = useCamoxApp();
|
|
94
|
+
const updateContent = useMutation(blockMutations.updateContent());
|
|
95
|
+
const updateSettings = useMutation(blockMutations.updateSettings());
|
|
96
|
+
const updateRepeatableContent = useMutation(repeatableItemMutations.updateContent());
|
|
97
|
+
const updateRepeatableSettings = useMutation(repeatableItemMutations.updateSettings());
|
|
98
|
+
const requireDraft = useRequireDraftSource();
|
|
99
|
+
const selection = useSelector(previewStore, (state) => state.context.selection);
|
|
100
|
+
const iframeElement = useSelector(previewStore, (state_0) => state_0.context.iframeElement);
|
|
101
|
+
const isReadOnly = useSelector(previewStore, (state_1) => state_1.context.previewSource) !== "draft";
|
|
102
|
+
const postToIframe = React.useCallback((message) => {
|
|
103
|
+
if (!iframeElement?.contentWindow) return;
|
|
104
|
+
iframeElement.contentWindow.postMessage(message, "*");
|
|
105
|
+
}, [iframeElement]);
|
|
106
|
+
const blockId = selectionBlockId(selection);
|
|
107
|
+
const currentItemId = selectionItemId(selection);
|
|
108
|
+
const fieldInfo = selectionField(selection);
|
|
109
|
+
const { data: blockBundle } = useQuery({
|
|
110
|
+
...blockQueries.get(blockId),
|
|
111
|
+
enabled: blockId != null
|
|
112
|
+
});
|
|
113
|
+
const block = blockBundle?.block ?? null;
|
|
114
|
+
const itemsMap = React.useMemo(() => new Map((blockBundle?.repeatableItems ?? []).map((i) => [i.id, i])), [blockBundle?.repeatableItems]);
|
|
115
|
+
const fileIds = React.useMemo(() => (blockBundle?.files ?? []).map((f) => f.id), [blockBundle?.files]);
|
|
116
|
+
const fileResults = useQueries({ queries: fileIds.map((id) => fileQueries.get(id)) });
|
|
117
|
+
const filesMap = React.useMemo(() => {
|
|
118
|
+
const map = new Map((blockBundle?.files ?? []).map((f_0) => [f_0.id, f_0]));
|
|
119
|
+
for (let i_0 = 0; i_0 < fileIds.length; i_0++) {
|
|
120
|
+
const data = fileResults[i_0]?.data;
|
|
121
|
+
if (data) map.set(data.id, data);
|
|
122
|
+
}
|
|
123
|
+
return map;
|
|
124
|
+
}, [
|
|
125
|
+
blockBundle?.files,
|
|
126
|
+
fileIds,
|
|
127
|
+
fileResults
|
|
128
|
+
]);
|
|
129
|
+
const blockDef = block ? camoxApp.getBlockById(block.type) : null;
|
|
130
|
+
const settingsFields = React.useMemo(() => {
|
|
131
|
+
return blockDef ? getSettingsFields(blockDef._internal.settingsSchema) : [];
|
|
132
|
+
}, [blockDef]);
|
|
133
|
+
const itemArraySchema = React.useMemo(() => {
|
|
134
|
+
if (!blockDef || currentItemId == null) return null;
|
|
135
|
+
return getArraySchemaForItem(blockDef._internal.contentSchema, currentItemId, itemsMap);
|
|
136
|
+
}, [
|
|
137
|
+
blockDef,
|
|
138
|
+
currentItemId,
|
|
139
|
+
itemsMap
|
|
140
|
+
]);
|
|
141
|
+
const itemSettingsFields = React.useMemo(() => {
|
|
142
|
+
return getSettingsFields(itemArraySchema?.itemSettingsSchema);
|
|
143
|
+
}, [itemArraySchema]);
|
|
144
|
+
const currentSchema = React.useMemo(() => {
|
|
145
|
+
if (!blockDef) return null;
|
|
146
|
+
if (currentItemId == null) return blockDef._internal.contentSchema;
|
|
147
|
+
return getSchemaForItem(blockDef._internal.contentSchema, currentItemId, itemsMap);
|
|
148
|
+
}, [
|
|
149
|
+
blockDef,
|
|
150
|
+
currentItemId,
|
|
151
|
+
itemsMap
|
|
152
|
+
]);
|
|
153
|
+
const currentItem = currentItemId != null ? itemsMap.get(currentItemId) : null;
|
|
154
|
+
const isItemLoading = currentItemId != null && !currentItem;
|
|
155
|
+
const siblingCount = React.useMemo(() => {
|
|
156
|
+
if (!currentItem) return 0;
|
|
157
|
+
let count = 0;
|
|
158
|
+
for (const it of itemsMap.values()) if (it.fieldName === currentItem.fieldName && it.parentItemId === currentItem.parentItemId) count++;
|
|
159
|
+
return count;
|
|
160
|
+
}, [currentItem, itemsMap]);
|
|
161
|
+
const { canAdd: canAddSibling, addItem: addSibling, canRemove: canRemoveCurrent, removeItem: removeCurrent } = useRepeatableItemActions({
|
|
162
|
+
blockId: block?.id ?? -1,
|
|
163
|
+
fieldName: currentItem?.fieldName ?? "",
|
|
164
|
+
parentItemId: currentItem?.parentItemId ?? null,
|
|
165
|
+
arraySchema: itemArraySchema,
|
|
166
|
+
siblingCount
|
|
167
|
+
});
|
|
168
|
+
const rawCurrentData = currentItem ? currentItem.content : block?.content ?? {};
|
|
169
|
+
const currentData = React.useMemo(() => {
|
|
170
|
+
const resolveFile = (marker) => {
|
|
171
|
+
const file = filesMap.get(marker._fileId);
|
|
172
|
+
return file ? {
|
|
173
|
+
url: file.url,
|
|
174
|
+
alt: file.alt,
|
|
175
|
+
filename: file.filename,
|
|
176
|
+
mimeType: file.mimeType,
|
|
177
|
+
_fileId: marker._fileId
|
|
178
|
+
} : {
|
|
179
|
+
url: "",
|
|
180
|
+
alt: "",
|
|
181
|
+
filename: "",
|
|
182
|
+
mimeType: ""
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
const resolveValue = (value) => {
|
|
186
|
+
if (isFileMarker(value)) return resolveFile(value);
|
|
187
|
+
if (Array.isArray(value)) return value.map(resolveValue);
|
|
188
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
189
|
+
const obj = value;
|
|
190
|
+
const resolved = {};
|
|
191
|
+
for (const [k, v] of Object.entries(obj)) resolved[k] = resolveValue(v);
|
|
192
|
+
return resolved;
|
|
193
|
+
}
|
|
194
|
+
return value;
|
|
195
|
+
};
|
|
196
|
+
const resolved_0 = {};
|
|
197
|
+
for (const [key, value_0] of Object.entries(rawCurrentData)) resolved_0[key] = resolveValue(value_0);
|
|
198
|
+
return resolved_0;
|
|
199
|
+
}, [rawCurrentData, filesMap]);
|
|
200
|
+
const isViewingLink = fieldInfo?.fieldType === "Link";
|
|
201
|
+
const linkFieldName = isViewingLink ? fieldInfo.fieldName : null;
|
|
202
|
+
const isViewingImage = fieldInfo?.fieldType === "Image";
|
|
203
|
+
const imageFieldName = isViewingImage ? fieldInfo.fieldName : null;
|
|
204
|
+
const isViewingFile = fieldInfo?.fieldType === "File";
|
|
205
|
+
const fileFieldName = isViewingFile ? fieldInfo.fieldName : null;
|
|
206
|
+
const isViewingAsset = isViewingImage || isViewingFile;
|
|
207
|
+
const assetFieldName = imageFieldName ?? fileFieldName;
|
|
208
|
+
const assetType = isViewingImage ? "Image" : "File";
|
|
209
|
+
const isMultipleAsset = React.useMemo(() => {
|
|
210
|
+
if (!isViewingAsset || !assetFieldName) return false;
|
|
211
|
+
const prop = currentSchema?.properties?.[assetFieldName];
|
|
212
|
+
return prop?.fieldType === "ImageList" || prop?.fieldType === "FileList";
|
|
213
|
+
}, [
|
|
214
|
+
isViewingAsset,
|
|
215
|
+
assetFieldName,
|
|
216
|
+
currentSchema
|
|
217
|
+
]);
|
|
218
|
+
const sessionDirtyRef = React.useRef(false);
|
|
219
|
+
const trackedBlockIdRef = React.useRef(null);
|
|
220
|
+
React.useEffect(() => {
|
|
221
|
+
if (!block) {
|
|
222
|
+
trackedBlockIdRef.current = null;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (trackedBlockIdRef.current === block.id) return;
|
|
226
|
+
trackedBlockIdRef.current = block.id;
|
|
227
|
+
sessionDirtyRef.current = false;
|
|
228
|
+
trackClientEvent("page_editor_sidebar_opened", { blockType: block.type });
|
|
229
|
+
}, [block]);
|
|
230
|
+
React.useEffect(() => {
|
|
231
|
+
if (!block) return;
|
|
232
|
+
return () => {
|
|
233
|
+
if (!sessionDirtyRef.current) return;
|
|
234
|
+
trackClientEvent("block_edited", {
|
|
235
|
+
via: "page-editor-sidebar",
|
|
236
|
+
blockType: block.type
|
|
237
|
+
});
|
|
238
|
+
sessionDirtyRef.current = false;
|
|
239
|
+
};
|
|
240
|
+
}, [block]);
|
|
241
|
+
const fieldIdPrefix = React.useId();
|
|
242
|
+
const handleBlockFieldChange = React.useCallback((fieldName, value_1) => {
|
|
243
|
+
if (!block) return;
|
|
244
|
+
if (!requireDraft()) return;
|
|
245
|
+
sessionDirtyRef.current = true;
|
|
246
|
+
updateContent.mutate({
|
|
247
|
+
id: block.id,
|
|
248
|
+
content: { [fieldName]: value_1 }
|
|
249
|
+
});
|
|
250
|
+
}, [
|
|
251
|
+
block,
|
|
252
|
+
updateContent,
|
|
253
|
+
requireDraft
|
|
254
|
+
]);
|
|
255
|
+
const handleItemFieldChange = React.useCallback((fieldName_0, value_2) => {
|
|
256
|
+
if (currentItemId == null) return;
|
|
257
|
+
if (!requireDraft()) return;
|
|
258
|
+
sessionDirtyRef.current = true;
|
|
259
|
+
updateRepeatableContent.mutate({
|
|
260
|
+
id: currentItemId,
|
|
261
|
+
content: { [fieldName_0]: value_2 }
|
|
262
|
+
});
|
|
263
|
+
}, [
|
|
264
|
+
currentItemId,
|
|
265
|
+
updateRepeatableContent,
|
|
266
|
+
requireDraft
|
|
267
|
+
]);
|
|
268
|
+
const activeFieldChangeHandler = currentItemId != null ? handleItemFieldChange : handleBlockFieldChange;
|
|
269
|
+
const ancestorChain = React.useMemo(() => currentItemId != null ? buildAncestorChain(currentItemId, itemsMap) : [], [currentItemId, itemsMap]);
|
|
270
|
+
if (!block || !blockDef || !currentSchema) return /* @__PURE__ */ jsxs("div", {
|
|
271
|
+
className: "text-muted-foreground flex flex-1 items-center justify-center px-2 text-sm",
|
|
272
|
+
children: [/* @__PURE__ */ jsx(Spinner, { className: "mr-2 size-3.5" }), " Loading block..."]
|
|
273
|
+
});
|
|
274
|
+
const fieldHasOwnView = fieldInfo ? fieldTypesDictionary[fieldInfo.fieldType].hasOwnView : false;
|
|
275
|
+
const [pageNavigationItem, ...contentNavigationItems] = [
|
|
276
|
+
{
|
|
277
|
+
key: "page",
|
|
278
|
+
label: "Page",
|
|
279
|
+
isCurrent: false,
|
|
280
|
+
onClick: () => previewStore.send({ type: "clearSelection" })
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
key: "block",
|
|
284
|
+
label: block.summary || blockDef._internal.title,
|
|
285
|
+
isCurrent: ancestorChain.length === 0 && !fieldHasOwnView,
|
|
286
|
+
onClick: () => previewStore.send({
|
|
287
|
+
type: "setFocusedBlock",
|
|
288
|
+
blockId: block.id
|
|
289
|
+
})
|
|
290
|
+
},
|
|
291
|
+
...ancestorChain.map((ancestor) => ({
|
|
292
|
+
key: `item-${ancestor.id}`,
|
|
293
|
+
label: ancestor.summary || formatFieldName(ancestor.fieldName),
|
|
294
|
+
isCurrent: ancestor.id === currentItemId && !fieldHasOwnView && ancestor.id === ancestorChain[ancestorChain.length - 1]?.id,
|
|
295
|
+
onClick: () => previewStore.send({
|
|
296
|
+
type: "selectItem",
|
|
297
|
+
blockId: block.id,
|
|
298
|
+
itemId: ancestor.id
|
|
299
|
+
})
|
|
300
|
+
})),
|
|
301
|
+
...fieldHasOwnView && fieldInfo ? [{
|
|
302
|
+
key: `field-${fieldInfo.fieldName}`,
|
|
303
|
+
label: currentSchema?.properties?.[fieldInfo.fieldName]?.title ?? formatFieldName(fieldInfo.fieldName),
|
|
304
|
+
isCurrent: true,
|
|
305
|
+
onClick: void 0
|
|
306
|
+
}] : []
|
|
307
|
+
];
|
|
308
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
309
|
+
className: "border-border flex flex-col gap-1.5 border-b px-2 py-4",
|
|
310
|
+
children: /* @__PURE__ */ jsxs("nav", {
|
|
311
|
+
"aria-label": "Selection path",
|
|
312
|
+
className: "text-muted-foreground text-sm",
|
|
313
|
+
children: [pageNavigationItem?.onClick ? /* @__PURE__ */ jsx("button", {
|
|
314
|
+
type: "button",
|
|
315
|
+
className: "hover:text-foreground block min-w-0 cursor-pointer truncate py-0.5 text-left transition-colors",
|
|
316
|
+
onClick: pageNavigationItem.onClick,
|
|
317
|
+
children: pageNavigationItem.label
|
|
318
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
319
|
+
className: "text-foreground block min-w-0 truncate py-0.5 font-medium",
|
|
320
|
+
children: pageNavigationItem?.label
|
|
321
|
+
}), /* @__PURE__ */ jsx("ol", {
|
|
322
|
+
className: "flex flex-col",
|
|
323
|
+
children: contentNavigationItems.map((item, index) => {
|
|
324
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
325
|
+
className: "relative min-w-0 pl-6",
|
|
326
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
327
|
+
"aria-hidden": "true",
|
|
328
|
+
className: "text-muted-foreground pointer-events-none absolute top-0 left-0 h-full w-4",
|
|
329
|
+
fill: "none",
|
|
330
|
+
preserveAspectRatio: "none",
|
|
331
|
+
viewBox: "0 0 16 24",
|
|
332
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
333
|
+
d: index < contentNavigationItems.length - 1 ? "M1 0 V24 M1 5 Q1 12 8 12 H16" : "M1 0 V5 Q1 12 8 12 H16",
|
|
334
|
+
stroke: "currentColor",
|
|
335
|
+
strokeLinecap: "square",
|
|
336
|
+
strokeWidth: "1.25",
|
|
337
|
+
vectorEffect: "non-scaling-stroke"
|
|
338
|
+
})
|
|
339
|
+
}), item.onClick ? /* @__PURE__ */ jsx("button", {
|
|
340
|
+
type: "button",
|
|
341
|
+
className: cn("hover:text-foreground relative -top-px block min-w-0 cursor-pointer truncate py-0.5 text-left transition-colors", item.isCurrent && "text-foreground font-medium"),
|
|
342
|
+
onClick: item.onClick,
|
|
343
|
+
children: item.label
|
|
344
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
345
|
+
className: "text-foreground relative -top-px block min-w-0 truncate py-0.5 font-medium",
|
|
346
|
+
children: item.label
|
|
347
|
+
})]
|
|
348
|
+
}, item.key);
|
|
349
|
+
})
|
|
350
|
+
})]
|
|
351
|
+
})
|
|
352
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
353
|
+
className: "relative flex-1 overflow-auto",
|
|
354
|
+
children: [isReadOnly && /* @__PURE__ */ jsx("button", {
|
|
355
|
+
type: "button",
|
|
356
|
+
"aria-label": "Switch to draft to edit",
|
|
357
|
+
className: "absolute inset-0 z-10 h-full w-full cursor-not-allowed",
|
|
358
|
+
onClick: () => previewStore.send({ type: "requestDraftSwitch" })
|
|
359
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
360
|
+
className: cn(isReadOnly && "pointer-events-none opacity-50"),
|
|
361
|
+
children: isItemLoading ? /* @__PURE__ */ jsx("div", {
|
|
362
|
+
className: "flex h-full items-center justify-center py-12",
|
|
363
|
+
children: /* @__PURE__ */ jsx(Spinner, {})
|
|
364
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
365
|
+
currentItemId == null && !fieldHasOwnView && settingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
366
|
+
className: "border-border space-y-4 border-b px-2 py-4",
|
|
367
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
368
|
+
className: "text-muted-foreground",
|
|
369
|
+
children: "Settings"
|
|
370
|
+
}), settingsFields.map((field) => {
|
|
371
|
+
const label = field.label ?? formatFieldName(field.name);
|
|
372
|
+
const settingsValues = block.settings ?? {};
|
|
373
|
+
if (field.fieldType === "Enum") {
|
|
374
|
+
const value_3 = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? "";
|
|
375
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
376
|
+
className: "space-y-2",
|
|
377
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
378
|
+
htmlFor: `setting-${field.name}`,
|
|
379
|
+
children: label
|
|
380
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
381
|
+
value: value_3,
|
|
382
|
+
onValueChange: (newValue) => {
|
|
383
|
+
if (!requireDraft()) return;
|
|
384
|
+
sessionDirtyRef.current = true;
|
|
385
|
+
updateSettings.mutate({
|
|
386
|
+
id: block.id,
|
|
387
|
+
settings: { [field.name]: newValue }
|
|
388
|
+
});
|
|
389
|
+
},
|
|
390
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
391
|
+
id: `setting-${field.name}`,
|
|
392
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
393
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: field.enumValues?.map((enumValue) => /* @__PURE__ */ jsx(SelectItem, {
|
|
394
|
+
value: enumValue,
|
|
395
|
+
children: field.enumLabels?.[enumValue] ?? enumValue
|
|
396
|
+
}, enumValue)) })]
|
|
397
|
+
})]
|
|
398
|
+
}, field.name);
|
|
399
|
+
}
|
|
400
|
+
if (field.fieldType === "Boolean") {
|
|
401
|
+
const checked = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? false;
|
|
402
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
403
|
+
className: "flex items-center justify-between",
|
|
404
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
405
|
+
htmlFor: `setting-${field.name}`,
|
|
406
|
+
children: label
|
|
407
|
+
}), /* @__PURE__ */ jsx(Switch, {
|
|
408
|
+
id: `setting-${field.name}`,
|
|
409
|
+
checked,
|
|
410
|
+
onCheckedChange: (newValue_0) => {
|
|
411
|
+
if (!requireDraft()) return;
|
|
412
|
+
sessionDirtyRef.current = true;
|
|
413
|
+
updateSettings.mutate({
|
|
414
|
+
id: block.id,
|
|
415
|
+
settings: { [field.name]: newValue_0 }
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
})]
|
|
419
|
+
}, field.name);
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
})]
|
|
423
|
+
}),
|
|
424
|
+
currentItemId != null && !fieldHasOwnView && itemSettingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
425
|
+
className: "border-border space-y-4 border-b px-2 py-4",
|
|
426
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
427
|
+
className: "text-muted-foreground",
|
|
428
|
+
children: "Settings"
|
|
429
|
+
}), itemSettingsFields.map((field_0) => {
|
|
430
|
+
const label_0 = field_0.label ?? formatFieldName(field_0.name);
|
|
431
|
+
const itemSettingsValues = currentItem?.settings ?? {};
|
|
432
|
+
const itemSettingsSchemaProps = itemArraySchema?.itemSettingsSchema?.properties;
|
|
433
|
+
if (field_0.fieldType === "Enum") {
|
|
434
|
+
const value_4 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? "";
|
|
435
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
436
|
+
className: "space-y-2",
|
|
437
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
438
|
+
htmlFor: `item-setting-${field_0.name}`,
|
|
439
|
+
children: label_0
|
|
440
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
441
|
+
value: value_4,
|
|
442
|
+
onValueChange: (newValue_1) => {
|
|
443
|
+
if (!requireDraft()) return;
|
|
444
|
+
sessionDirtyRef.current = true;
|
|
445
|
+
updateRepeatableSettings.mutate({
|
|
446
|
+
id: currentItemId,
|
|
447
|
+
settings: { [field_0.name]: newValue_1 }
|
|
448
|
+
});
|
|
449
|
+
},
|
|
450
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
451
|
+
id: `item-setting-${field_0.name}`,
|
|
452
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
453
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: field_0.enumValues?.map((enumValue_0) => /* @__PURE__ */ jsx(SelectItem, {
|
|
454
|
+
value: enumValue_0,
|
|
455
|
+
children: field_0.enumLabels?.[enumValue_0] ?? enumValue_0
|
|
456
|
+
}, enumValue_0)) })]
|
|
457
|
+
})]
|
|
458
|
+
}, field_0.name);
|
|
459
|
+
}
|
|
460
|
+
if (field_0.fieldType === "Boolean") {
|
|
461
|
+
const checked_0 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? false;
|
|
462
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
463
|
+
className: "flex items-center justify-between",
|
|
464
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
465
|
+
htmlFor: `item-setting-${field_0.name}`,
|
|
466
|
+
children: label_0
|
|
467
|
+
}), /* @__PURE__ */ jsx(Switch, {
|
|
468
|
+
id: `item-setting-${field_0.name}`,
|
|
469
|
+
checked: checked_0,
|
|
470
|
+
onCheckedChange: (newValue_2) => {
|
|
471
|
+
if (!requireDraft()) return;
|
|
472
|
+
sessionDirtyRef.current = true;
|
|
473
|
+
updateRepeatableSettings.mutate({
|
|
474
|
+
id: currentItemId,
|
|
475
|
+
settings: { [field_0.name]: newValue_2 }
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
})]
|
|
479
|
+
}, field_0.name);
|
|
480
|
+
}
|
|
481
|
+
return null;
|
|
482
|
+
})]
|
|
483
|
+
}),
|
|
484
|
+
isViewingAsset && assetFieldName && isMultipleAsset && /* @__PURE__ */ jsx(MultipleAssetFieldEditor, {
|
|
485
|
+
fieldName: assetFieldName,
|
|
486
|
+
assetType,
|
|
487
|
+
currentData,
|
|
488
|
+
onFieldChange: activeFieldChangeHandler
|
|
489
|
+
}),
|
|
490
|
+
isViewingAsset && assetFieldName && !isMultipleAsset && /* @__PURE__ */ jsx(SingleAssetFieldEditor, {
|
|
491
|
+
fieldName: assetFieldName,
|
|
492
|
+
assetType,
|
|
493
|
+
currentData,
|
|
494
|
+
onFieldChange: activeFieldChangeHandler
|
|
495
|
+
}),
|
|
496
|
+
!isViewingAsset && isViewingLink && linkFieldName && /* @__PURE__ */ jsx("div", {
|
|
497
|
+
className: "px-2 py-4",
|
|
498
|
+
children: /* @__PURE__ */ jsx(LinkFieldEditor, {
|
|
499
|
+
fieldName: linkFieldName,
|
|
500
|
+
linkValue: currentData[linkFieldName] ?? {
|
|
501
|
+
type: "external",
|
|
502
|
+
text: "",
|
|
503
|
+
href: "",
|
|
504
|
+
newTab: false
|
|
505
|
+
},
|
|
506
|
+
onSave: (fieldName_1, value_5) => {
|
|
507
|
+
activeFieldChangeHandler(fieldName_1, value_5);
|
|
508
|
+
}
|
|
509
|
+
})
|
|
510
|
+
}),
|
|
511
|
+
!isViewingAsset && !isViewingLink && (currentItemId == null || currentItem) && /* @__PURE__ */ jsx(ItemFieldsEditor, {
|
|
512
|
+
schema: currentSchema,
|
|
513
|
+
data: currentData,
|
|
514
|
+
blockId: block.id,
|
|
515
|
+
itemId: currentItemId ?? void 0,
|
|
516
|
+
onFieldChange: activeFieldChangeHandler,
|
|
517
|
+
postToIframe,
|
|
518
|
+
filesMap,
|
|
519
|
+
itemsMap,
|
|
520
|
+
fieldIdPrefix
|
|
521
|
+
}, currentItemId ?? `block-${block.id}`),
|
|
522
|
+
!isViewingAsset && !isViewingLink && currentItemId != null && currentItem && /* @__PURE__ */ jsxs("div", {
|
|
523
|
+
className: "border-border flex items-center gap-1 border-t px-2 py-4",
|
|
524
|
+
children: [
|
|
525
|
+
canAddSibling && /* @__PURE__ */ jsxs(Button, {
|
|
526
|
+
type: "button",
|
|
527
|
+
variant: "ghost",
|
|
528
|
+
size: "sm",
|
|
529
|
+
className: "text-muted-foreground justify-start",
|
|
530
|
+
onClick: () => {
|
|
531
|
+
if (!requireDraft()) return;
|
|
532
|
+
addSibling({ afterPosition: currentItem.position });
|
|
533
|
+
},
|
|
534
|
+
children: [/* @__PURE__ */ jsx(CirclePlus, { className: "h-4 w-4" }), "Add item"]
|
|
535
|
+
}),
|
|
536
|
+
canRemoveCurrent && /* @__PURE__ */ jsxs(Button, {
|
|
537
|
+
type: "button",
|
|
538
|
+
variant: "ghost",
|
|
539
|
+
size: "sm",
|
|
540
|
+
className: "text-muted-foreground justify-start",
|
|
541
|
+
onClick: () => {
|
|
542
|
+
if (!requireDraft()) return;
|
|
543
|
+
removeCurrent(currentItemId, { onSuccess: () => previewStore.send({ type: "selectParent" }) });
|
|
544
|
+
},
|
|
545
|
+
children: [/* @__PURE__ */ jsx(CircleMinus, { className: "h-4 w-4" }), "Remove item"]
|
|
546
|
+
}),
|
|
547
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
548
|
+
type: "button",
|
|
549
|
+
variant: "ghost",
|
|
550
|
+
size: "sm",
|
|
551
|
+
className: "text-muted-foreground justify-start",
|
|
552
|
+
onClick: () => previewStore.send({ type: "selectParent" }),
|
|
553
|
+
children: [/* @__PURE__ */ jsx(CornerLeftUp, { className: "h-4 w-4" }), "Select parent"]
|
|
554
|
+
})
|
|
555
|
+
]
|
|
556
|
+
})
|
|
557
|
+
] })
|
|
558
|
+
})]
|
|
559
|
+
})] });
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
//#endregion
|
|
563
|
+
export { PageEditorSidebar };
|