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.
@@ -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, value_4) => {
405
- activeFieldChangeHandler(fieldName_1, value_4);
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
  };
@@ -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 = {
@@ -25,17 +25,19 @@
25
25
  --camox-overlay-inset-block-selected: 0px;
26
26
  }
27
27
 
28
- /* Position context for field-level and repeater elements.
28
+ /* Position context for typed-field, repeater, and detached overlays.
29
29
  BlockComponent already has position: relative inline.
30
- Detached wraps user elements (e.g. fixed navbars) so must NOT get position: relative. */
31
- [data-camox-field-id],
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
- /* Field-level elements use field-specific inset */
67
- [data-camox-field-id][data-camox-focused]:not([data-camox-field-type])::after {
68
- inset: var(--camox-overlay-inset-field-selected);
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,