camox 0.13.0 → 0.14.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/createApp.d.ts +4 -0
- package/dist/core/createBlock.d.ts +32 -5
- package/dist/core/createBlock.js +129 -74
- package/dist/core/lib/contentType.d.ts +46 -10
- package/dist/core/lib/contentType.js +56 -3
- package/dist/features/preview/components/AddBlockSheet.js +8 -5
- package/dist/features/preview/components/PageContentSheet.js +90 -2
- package/dist/features/preview/components/RepeatableItemsList.js +4 -0
- package/dist/lib/queries.js +1 -0
- package/dist/studio-overlays.css +38 -15
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox-block/SKILL.md +41 -11
|
@@ -57,6 +57,26 @@ const getSchemaForItem = (contentSchema, itemId, itemsMap) => {
|
|
|
57
57
|
return schema;
|
|
58
58
|
};
|
|
59
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
|
+
/**
|
|
60
80
|
* Builds the ancestor chain from root to this item (inclusive).
|
|
61
81
|
* Returns items in order from root-most ancestor to the item itself.
|
|
62
82
|
*/
|
|
@@ -74,6 +94,7 @@ const PageContentSheet = () => {
|
|
|
74
94
|
const updateContent = useMutation(blockMutations.updateContent());
|
|
75
95
|
const updateSettings = useMutation(blockMutations.updateSettings());
|
|
76
96
|
const updateRepeatableContent = useMutation(repeatableItemMutations.updateContent());
|
|
97
|
+
const updateRepeatableSettings = useMutation(repeatableItemMutations.updateSettings());
|
|
77
98
|
const isOpen = useSelector(previewStore, (state) => state.context.isPageContentSheetOpen);
|
|
78
99
|
const selection = useSelector(previewStore, (state_0) => state_0.context.selection);
|
|
79
100
|
const iframeElement = useSelector(previewStore, (state_1) => state_1.context.iframeElement);
|
|
@@ -109,6 +130,17 @@ const PageContentSheet = () => {
|
|
|
109
130
|
const settingsFields = React.useMemo(() => {
|
|
110
131
|
return blockDef ? getSettingsFields(blockDef._internal.settingsSchema) : [];
|
|
111
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]);
|
|
112
144
|
const currentSchema = React.useMemo(() => {
|
|
113
145
|
if (!blockDef) return null;
|
|
114
146
|
if (currentItemId == null) return blockDef._internal.contentSchema;
|
|
@@ -379,6 +411,62 @@ const PageContentSheet = () => {
|
|
|
379
411
|
return null;
|
|
380
412
|
})]
|
|
381
413
|
}),
|
|
414
|
+
currentItemId != null && !fieldHasOwnView && itemSettingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
415
|
+
className: "border-border space-y-4 border-b px-4 py-4",
|
|
416
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
417
|
+
className: "text-muted-foreground",
|
|
418
|
+
children: "Settings"
|
|
419
|
+
}), itemSettingsFields.map((field_0) => {
|
|
420
|
+
const label_0 = field_0.label ?? formatFieldName(field_0.name);
|
|
421
|
+
const itemSettingsValues = currentItem?.settings ?? {};
|
|
422
|
+
const itemSettingsSchemaProps = itemArraySchema?.itemSettingsSchema?.properties;
|
|
423
|
+
if (field_0.fieldType === "Enum") {
|
|
424
|
+
const value_4 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? "";
|
|
425
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
426
|
+
className: "space-y-2",
|
|
427
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
428
|
+
htmlFor: `item-setting-${field_0.name}`,
|
|
429
|
+
children: label_0
|
|
430
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
431
|
+
value: value_4,
|
|
432
|
+
onValueChange: (newValue_1) => {
|
|
433
|
+
updateRepeatableSettings.mutate({
|
|
434
|
+
id: currentItemId,
|
|
435
|
+
settings: { [field_0.name]: newValue_1 }
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
439
|
+
id: `item-setting-${field_0.name}`,
|
|
440
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
441
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: field_0.enumValues?.map((enumValue_0) => /* @__PURE__ */ jsx(SelectItem, {
|
|
442
|
+
value: enumValue_0,
|
|
443
|
+
children: field_0.enumLabels?.[enumValue_0] ?? enumValue_0
|
|
444
|
+
}, enumValue_0)) })]
|
|
445
|
+
})]
|
|
446
|
+
}, field_0.name);
|
|
447
|
+
}
|
|
448
|
+
if (field_0.fieldType === "Boolean") {
|
|
449
|
+
const checked_0 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? false;
|
|
450
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
451
|
+
className: "flex items-center justify-between",
|
|
452
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
453
|
+
htmlFor: `item-setting-${field_0.name}`,
|
|
454
|
+
children: label_0
|
|
455
|
+
}), /* @__PURE__ */ jsx(Switch, {
|
|
456
|
+
id: `item-setting-${field_0.name}`,
|
|
457
|
+
checked: checked_0,
|
|
458
|
+
onCheckedChange: (newValue_2) => {
|
|
459
|
+
updateRepeatableSettings.mutate({
|
|
460
|
+
id: currentItemId,
|
|
461
|
+
settings: { [field_0.name]: newValue_2 }
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
})]
|
|
465
|
+
}, field_0.name);
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
})]
|
|
469
|
+
}),
|
|
382
470
|
isViewingAsset && assetFieldName && isMultipleAsset && /* @__PURE__ */ jsx(MultipleAssetFieldEditor, {
|
|
383
471
|
fieldName: assetFieldName,
|
|
384
472
|
assetType,
|
|
@@ -401,8 +489,8 @@ const PageContentSheet = () => {
|
|
|
401
489
|
href: "",
|
|
402
490
|
newTab: false
|
|
403
491
|
},
|
|
404
|
-
onSave: (fieldName_1,
|
|
405
|
-
activeFieldChangeHandler(fieldName_1,
|
|
492
|
+
onSave: (fieldName_1, value_5) => {
|
|
493
|
+
activeFieldChangeHandler(fieldName_1, value_5);
|
|
406
494
|
}
|
|
407
495
|
})
|
|
408
496
|
}),
|
|
@@ -214,6 +214,7 @@ const RepeatableItemsList = ({ items, blockId, fieldName, minItems, maxItems, sc
|
|
|
214
214
|
if (prop.type === "array" && prop.items?.properties) continue;
|
|
215
215
|
if ("default" in prop) defaultContent[key] = prop.default;
|
|
216
216
|
}
|
|
217
|
+
const defaultSettings = schema?.defaultItemSettings;
|
|
217
218
|
const nestedItems = [];
|
|
218
219
|
if (itemsSchema?.properties) {
|
|
219
220
|
let seedCounter = 0;
|
|
@@ -230,6 +231,7 @@ const RepeatableItemsList = ({ items, blockId, fieldName, minItems, maxItems, sc
|
|
|
230
231
|
if (ps.type === "array" && ps.items?.properties) continue;
|
|
231
232
|
if ("default" in ps) nestedContent[propName] = ps.default;
|
|
232
233
|
}
|
|
234
|
+
const nestedSettingsDefaults = fs.defaultItemSettings;
|
|
233
235
|
let prevPos = null;
|
|
234
236
|
for (let i = 0; i < defaultCount; i++) {
|
|
235
237
|
const tempId = `nested_${++seedCounter}`;
|
|
@@ -240,6 +242,7 @@ const RepeatableItemsList = ({ items, blockId, fieldName, minItems, maxItems, sc
|
|
|
240
242
|
parentTempId,
|
|
241
243
|
fieldName: nestedFieldName,
|
|
242
244
|
content: { ...nestedContent },
|
|
245
|
+
settings: nestedSettingsDefaults ? { ...nestedSettingsDefaults } : void 0,
|
|
243
246
|
position
|
|
244
247
|
});
|
|
245
248
|
buildNestedSeeds(nestedItemProps, tempId);
|
|
@@ -252,6 +255,7 @@ const RepeatableItemsList = ({ items, blockId, fieldName, minItems, maxItems, sc
|
|
|
252
255
|
blockId,
|
|
253
256
|
fieldName,
|
|
254
257
|
content: defaultContent,
|
|
258
|
+
settings: defaultSettings ? { ...defaultSettings } : void 0,
|
|
255
259
|
nestedItems: nestedItems.length > 0 ? nestedItems : void 0
|
|
256
260
|
});
|
|
257
261
|
};
|
package/dist/lib/queries.js
CHANGED
|
@@ -105,6 +105,7 @@ const repeatableItemMutations = {
|
|
|
105
105
|
delete: () => getOrpc().repeatableItems.delete.mutationOptions(),
|
|
106
106
|
duplicate: () => getOrpc().repeatableItems.duplicate.mutationOptions(),
|
|
107
107
|
updateContent: () => getOrpc().repeatableItems.updateContent.mutationOptions(),
|
|
108
|
+
updateSettings: () => getOrpc().repeatableItems.updateSettings.mutationOptions(),
|
|
108
109
|
updatePosition: () => getOrpc().repeatableItems.updatePosition.mutationOptions()
|
|
109
110
|
};
|
|
110
111
|
const pageMutations = {
|
package/dist/studio-overlays.css
CHANGED
|
@@ -25,17 +25,19 @@
|
|
|
25
25
|
--camox-overlay-inset-block-selected: 0px;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/* Position context for field
|
|
28
|
+
/* Position context for typed-field, repeater, and detached overlays.
|
|
29
29
|
BlockComponent already has position: relative inline.
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
Inline string fields don't use ::after (see box-shadow rules below) so they
|
|
31
|
+
don't need position: relative — adding it to inline elements wouldn't help
|
|
32
|
+
since inline-relative containing blocks collapse to the start fragment. */
|
|
33
|
+
[data-camox-field-id][data-camox-field-type],
|
|
32
34
|
[data-camox-repeater-item-id] {
|
|
33
35
|
position: relative;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/* ---- Hovered state ---- */
|
|
37
39
|
|
|
38
|
-
[data-camox-field-id][data-camox-hovered]::after,
|
|
40
|
+
[data-camox-field-id][data-camox-field-type][data-camox-hovered]::after,
|
|
39
41
|
[data-camox-block-id][data-camox-hovered]:not([data-camox-detached])::after,
|
|
40
42
|
[data-camox-repeater-item-id][data-camox-hovered]::after {
|
|
41
43
|
content: "";
|
|
@@ -46,14 +48,9 @@
|
|
|
46
48
|
border: var(--camox-overlay-width-hover) solid var(--camox-overlay-color-hover);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
/* Field-level elements use field-specific inset */
|
|
50
|
-
[data-camox-field-id][data-camox-hovered]:not([data-camox-field-type])::after {
|
|
51
|
-
inset: var(--camox-overlay-inset-field-hover);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
51
|
/* ---- Focused state (overrides hovered via source order) ---- */
|
|
55
52
|
|
|
56
|
-
[data-camox-field-id][data-camox-focused]::after,
|
|
53
|
+
[data-camox-field-id][data-camox-field-type][data-camox-focused]::after,
|
|
57
54
|
[data-camox-block-id][data-camox-focused]:not([data-camox-detached])::after {
|
|
58
55
|
content: "";
|
|
59
56
|
position: absolute;
|
|
@@ -63,14 +60,28 @@
|
|
|
63
60
|
border: var(--camox-overlay-width-selected) solid var(--camox-overlay-color-selected);
|
|
64
61
|
}
|
|
65
62
|
|
|
66
|
-
/*
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
/* ---- Inline string fields ----
|
|
64
|
+
Use box-shadow + box-decoration-break: clone so the outline repeats per line
|
|
65
|
+
fragment when the inline wraps. Applied directly to the element via attribute
|
|
66
|
+
selectors with !important so user-supplied className/style box-shadows can't
|
|
67
|
+
clobber it. */
|
|
68
|
+
[data-camox-field-id]:not([data-camox-field-type])[data-camox-hovered],
|
|
69
|
+
[data-camox-field-id]:not([data-camox-field-type])[data-camox-focused] {
|
|
70
|
+
-webkit-box-decoration-break: clone;
|
|
71
|
+
box-decoration-break: clone;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-camox-field-id]:not([data-camox-field-type])[data-camox-hovered] {
|
|
75
|
+
box-shadow: 0 0 0 var(--camox-overlay-width-hover) var(--camox-overlay-color-hover) !important;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[data-camox-field-id]:not([data-camox-field-type])[data-camox-focused] {
|
|
79
|
+
box-shadow: 0 0 0 var(--camox-overlay-width-selected) var(--camox-overlay-color-selected) !important;
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
/* ---- Layout mode color overrides ---- */
|
|
72
83
|
|
|
73
|
-
[data-camox-field-id][data-camox-overlay-mode="layout"][data-camox-hovered]::after,
|
|
84
|
+
[data-camox-field-id][data-camox-field-type][data-camox-overlay-mode="layout"][data-camox-hovered]::after,
|
|
74
85
|
[data-camox-block-id][data-camox-overlay-mode="layout"][data-camox-hovered]:not(
|
|
75
86
|
[data-camox-detached]
|
|
76
87
|
)::after,
|
|
@@ -78,13 +89,25 @@
|
|
|
78
89
|
border-color: var(--camox-overlay-layout-color-hover);
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
[data-camox-field-id][data-camox-overlay-mode="layout"][data-camox-focused]::after,
|
|
92
|
+
[data-camox-field-id][data-camox-field-type][data-camox-overlay-mode="layout"][data-camox-focused]::after,
|
|
82
93
|
[data-camox-block-id][data-camox-overlay-mode="layout"][data-camox-focused]:not(
|
|
83
94
|
[data-camox-detached]
|
|
84
95
|
)::after {
|
|
85
96
|
border-color: var(--camox-overlay-layout-color-selected);
|
|
86
97
|
}
|
|
87
98
|
|
|
99
|
+
[data-camox-field-id]:not(
|
|
100
|
+
[data-camox-field-type]
|
|
101
|
+
)[data-camox-overlay-mode="layout"][data-camox-hovered] {
|
|
102
|
+
box-shadow: 0 0 0 var(--camox-overlay-width-hover) var(--camox-overlay-layout-color-hover) !important;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
[data-camox-field-id]:not(
|
|
106
|
+
[data-camox-field-type]
|
|
107
|
+
)[data-camox-overlay-mode="layout"][data-camox-focused] {
|
|
108
|
+
box-shadow: 0 0 0 var(--camox-overlay-width-selected) var(--camox-overlay-layout-color-selected) !important;
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
/* ---- Embed: z-index above click-interceptor ---- */
|
|
89
112
|
|
|
90
113
|
[data-camox-field-type="embed"][data-camox-hovered]::after,
|