@elytracms/studio 0.0.90 → 0.0.91

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.
Files changed (159) hide show
  1. package/convex/asset-revalidate.test.ts +14 -1
  2. package/convex/cms.ts +3 -4
  3. package/convex/document-revalidate.test.ts +14 -1
  4. package/convex/redirect-revalidate.test.ts +14 -1
  5. package/dist/components/assets/asset-selection.d.ts +8 -9
  6. package/dist/components/assets/asset-selection.js +26 -16
  7. package/dist/components/assets/asset-selection.js.map +1 -1
  8. package/dist/components/builder/blocks-model.d.ts +7 -5
  9. package/dist/components/builder/blocks-model.js +11 -9
  10. package/dist/components/builder/blocks-model.js.map +1 -1
  11. package/dist/components/builder/blocks-panel.js +1 -1
  12. package/dist/components/builder/blocks-panel.js.map +1 -1
  13. package/dist/components/builder/builder-store.d.ts +11 -3
  14. package/dist/components/builder/builder-store.js +12 -5
  15. package/dist/components/builder/builder-store.js.map +1 -1
  16. package/dist/components/builder/canvas-dnd.d.ts +7 -1
  17. package/dist/components/builder/canvas-dnd.js +6 -3
  18. package/dist/components/builder/canvas-dnd.js.map +1 -1
  19. package/dist/components/builder/canvas-render-context.d.ts +13 -1
  20. package/dist/components/builder/canvas-render-context.js +28 -18
  21. package/dist/components/builder/canvas-render-context.js.map +1 -1
  22. package/dist/components/builder/canvas.js +1 -1
  23. package/dist/components/builder/canvas.js.map +1 -1
  24. package/dist/components/builder/clipboard-actions.d.ts +0 -6
  25. package/dist/components/builder/clipboard-actions.js +9 -4
  26. package/dist/components/builder/clipboard-actions.js.map +1 -1
  27. package/dist/components/builder/composition-field-editor.d.ts +22 -0
  28. package/dist/components/builder/composition-field-editor.js +47 -8
  29. package/dist/components/builder/composition-field-editor.js.map +1 -1
  30. package/dist/components/builder/inline-edit.d.ts +7 -4
  31. package/dist/components/builder/inline-edit.js +11 -8
  32. package/dist/components/builder/inline-edit.js.map +1 -1
  33. package/dist/components/builder/insert-context.js +9 -5
  34. package/dist/components/builder/insert-context.js.map +1 -1
  35. package/dist/components/builder/layout-inspector.d.ts +2 -2
  36. package/dist/components/builder/layout-inspector.js +8 -6
  37. package/dist/components/builder/layout-inspector.js.map +1 -1
  38. package/dist/components/builder/node-factory.d.ts +31 -14
  39. package/dist/components/builder/node-factory.js +73 -93
  40. package/dist/components/builder/node-factory.js.map +1 -1
  41. package/dist/components/builder/page-tree.d.ts +3 -1
  42. package/dist/components/builder/page-tree.js +6 -4
  43. package/dist/components/builder/page-tree.js.map +1 -1
  44. package/dist/components/builder/property-inspector.js +2 -1
  45. package/dist/components/builder/property-inspector.js.map +1 -1
  46. package/dist/components/builder/reuse.d.ts +1 -1
  47. package/dist/components/builder/reuse.js +8 -5
  48. package/dist/components/builder/reuse.js.map +1 -1
  49. package/dist/components/builder/selection-overlay.d.ts +2 -2
  50. package/dist/components/builder/selection-overlay.js +3 -3
  51. package/dist/components/builder/selection-overlay.js.map +1 -1
  52. package/dist/components/builder/spacing-handles.d.ts +2 -2
  53. package/dist/components/builder/spacing-handles.js +3 -3
  54. package/dist/components/builder/spacing-handles.js.map +1 -1
  55. package/dist/components/cms/asset-field.d.ts +1 -1
  56. package/dist/components/cms/asset-field.js +15 -13
  57. package/dist/components/cms/asset-field.js.map +1 -1
  58. package/dist/components/cms/asset-picker-core.js +3 -4
  59. package/dist/components/cms/asset-picker-core.js.map +1 -1
  60. package/dist/components/cms/detail-editor.js +10 -5
  61. package/dist/components/cms/detail-editor.js.map +1 -1
  62. package/dist/components/cms/discard-draft.d.ts +1 -2
  63. package/dist/components/cms/discard-draft.js +1 -1
  64. package/dist/components/cms/discard-draft.js.map +1 -1
  65. package/dist/components/cms/editable-cell.js +11 -7
  66. package/dist/components/cms/editable-cell.js.map +1 -1
  67. package/dist/components/cms/field-control-lib.d.ts +10 -0
  68. package/dist/components/cms/field-control-lib.js +20 -0
  69. package/dist/components/cms/field-control-lib.js.map +1 -1
  70. package/dist/components/cms/field-control.js +3 -3
  71. package/dist/components/cms/field-control.js.map +1 -1
  72. package/dist/components/cms/object-array-field.js +3 -5
  73. package/dist/components/cms/object-array-field.js.map +1 -1
  74. package/dist/components/cms/prop-fields.d.ts +16 -3
  75. package/dist/components/cms/prop-fields.js +57 -12
  76. package/dist/components/cms/prop-fields.js.map +1 -1
  77. package/dist/components/cms/rich-text/embed-props.d.ts +6 -1
  78. package/dist/components/cms/rich-text/embed-props.js +7 -12
  79. package/dist/components/cms/rich-text/embed-props.js.map +1 -1
  80. package/dist/components/cms/rich-text/insert.d.ts +13 -6
  81. package/dist/components/cms/rich-text/insert.js +16 -17
  82. package/dist/components/cms/rich-text/insert.js.map +1 -1
  83. package/dist/components/cms/use-cell-edit.js +7 -12
  84. package/dist/components/cms/use-cell-edit.js.map +1 -1
  85. package/dist/components/cms/use-discard-controller.d.ts +1 -2
  86. package/dist/components/cms/use-discard-controller.js +1 -2
  87. package/dist/components/cms/use-discard-controller.js.map +1 -1
  88. package/dist/components/cms/versions-pane.js +2 -2
  89. package/dist/components/cms/versions-pane.js.map +1 -1
  90. package/dist/components/components/component-detail.d.ts +1 -1
  91. package/dist/components/components/component-detail.js +2 -4
  92. package/dist/components/components/component-detail.js.map +1 -1
  93. package/dist/components/components/component-preview.js +9 -4
  94. package/dist/components/components/component-preview.js.map +1 -1
  95. package/dist/components/components/components-index.d.ts +2 -2
  96. package/dist/components/components/components-index.js +3 -6
  97. package/dist/components/components/components-index.js.map +1 -1
  98. package/dist/components/components/field-inspector.js +5 -2
  99. package/dist/components/components/field-inspector.js.map +1 -1
  100. package/dist/components/components/registry-browser.d.ts +6 -5
  101. package/dist/components/components/registry-browser.js +11 -37
  102. package/dist/components/components/registry-browser.js.map +1 -1
  103. package/dist/components/components/taxonomy.d.ts +14 -21
  104. package/dist/components/components/taxonomy.js +5 -29
  105. package/dist/components/components/taxonomy.js.map +1 -1
  106. package/dist/components/components/transfer.d.ts +10 -8
  107. package/dist/components/components/transfer.js +17 -13
  108. package/dist/components/components/transfer.js.map +1 -1
  109. package/dist/lib/ai/constraints.d.ts +16 -14
  110. package/dist/lib/ai/constraints.js +64 -48
  111. package/dist/lib/ai/constraints.js.map +1 -1
  112. package/dist/lib/ai/update-fields.js +5 -3
  113. package/dist/lib/ai/update-fields.js.map +1 -1
  114. package/dist/lib/assets/asset-placements.d.ts +3 -10
  115. package/dist/lib/assets/asset-placements.js +17 -8
  116. package/dist/lib/assets/asset-placements.js.map +1 -1
  117. package/dist/lib/clipboard/paste-target.js +3 -3
  118. package/dist/lib/clipboard/paste-target.js.map +1 -1
  119. package/dist/lib/cms-views/view-model.js +11 -8
  120. package/dist/lib/cms-views/view-model.js.map +1 -1
  121. package/dist/lib/data/collection-content.d.ts +10 -7
  122. package/dist/lib/data/collection-content.js +34 -15
  123. package/dist/lib/data/collection-content.js.map +1 -1
  124. package/dist/lib/dnd/drop-rules.d.ts +22 -7
  125. package/dist/lib/dnd/drop-rules.js +21 -14
  126. package/dist/lib/dnd/drop-rules.js.map +1 -1
  127. package/dist/lib/engine-version.d.ts +1 -1
  128. package/dist/lib/engine-version.js +1 -1
  129. package/dist/lib/engine-version.js.map +1 -1
  130. package/dist/lib/i18n/messages.de.js +25 -9
  131. package/dist/lib/i18n/messages.de.js.map +1 -1
  132. package/dist/lib/i18n/messages.en.d.ts +21 -7
  133. package/dist/lib/i18n/messages.en.js +28 -9
  134. package/dist/lib/i18n/messages.en.js.map +1 -1
  135. package/dist/lib/operations/cms-persistence.js +7 -4
  136. package/dist/lib/operations/cms-persistence.js.map +1 -1
  137. package/dist/lib/persona/labels.d.ts +4 -9
  138. package/dist/lib/persona/labels.js +5 -12
  139. package/dist/lib/persona/labels.js.map +1 -1
  140. package/dist/lib/references/reference-model.d.ts +0 -2
  141. package/dist/lib/references/reference-model.js +3 -5
  142. package/dist/lib/references/reference-model.js.map +1 -1
  143. package/dist/lib/registry/__fixtures__/demo-components.d.ts +32 -0
  144. package/dist/lib/registry/{project-components.js → __fixtures__/demo-components.js} +18 -5
  145. package/dist/lib/registry/__fixtures__/demo-components.js.map +1 -0
  146. package/dist/lib/registry/index.d.ts +9 -13
  147. package/dist/lib/registry/index.js +34 -61
  148. package/dist/lib/registry/index.js.map +1 -1
  149. package/dist/lib/registry/preview.d.ts +22 -7
  150. package/dist/lib/registry/preview.js +26 -89
  151. package/dist/lib/registry/preview.js.map +1 -1
  152. package/dist/previews/enumerate.d.ts +23 -27
  153. package/dist/previews/enumerate.js +27 -48
  154. package/dist/previews/enumerate.js.map +1 -1
  155. package/dist/previews/harness.js +1 -1
  156. package/dist/previews/harness.js.map +1 -1
  157. package/package.json +4 -3
  158. package/dist/lib/registry/project-components.d.ts +0 -16
  159. package/dist/lib/registry/project-components.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"discard-draft.js","sourceRoot":"","sources":["../../../src/components/cms/discard-draft.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAuBhE,MAAM,UAAU,kBAAkB,CAAC,KAWlC;IACC,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,QAAQ,CAAA;IAC1D,IAAI,KAAK,CAAC,YAAY;QAAE,OAAO,gBAAgB,CAAA;IAC/C,IAAI,KAAK,CAAC,iBAAiB,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC3D,OAAO,sBAAsB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,KAAK,CAAC;QACnF,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,OAAO,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,KAA2C;IACzE,IAAI,KAAK,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,EAAE,CAAA;AAC/B,CAAC","sourcesContent":["import { documentsSemanticEqual } from '@elytracms/core/content'\nimport type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core'\n\n/**\n * EC-316 — the \"Discard changes\" header action's enablement, as one pure\n * decision. Discarding reverts the draft to exactly the published content (via\n * the EC-298 restore machinery), so it is offered iff there is a published\n * version to revert TO and the draft actually differs from it:\n *\n * - `hidden` — the rail can't discard at all (no publish/versions\n * capability) or the document isn't published: there is no\n * published content to revert to, so no menu item.\n * - `view-read-only`— viewing the Published snapshot (EC-226): the draft isn't\n * the active view, so the item is disabled with a hint.\n * - `loading` — the version history hasn't resolved yet; disabled.\n * - `up-to-date` — the draft SEMANTICALLY equals the published snapshot\n * (`documentsSemanticEqual`, the SAME comparator the\n * Versions pane's publish gate uses, EC-298 — so the two\n * surfaces never contradict); nothing to discard, disabled.\n * - `ready` — a published version exists and the draft differs: enabled.\n */\nexport type DiscardDraftStatus = 'hidden' | 'view-read-only' | 'loading' | 'up-to-date' | 'ready'\n\nexport function discardDraftStatus(input: {\n /** The rail can publish AND has version history (both required to discard). */\n canDiscard: boolean\n /** The document IS published (version pointer set). */\n published: boolean\n /** The editor is viewing the read-only Published snapshot (EC-226). */\n viewReadOnly: boolean\n /** The pinned published version's snapshot; `undefined` while loading / none pinned. */\n publishedSnapshot: CmsDocument | undefined\n collection: CollectionDef\n draft: CmsDocument\n}): DiscardDraftStatus {\n if (!input.canDiscard || !input.published) return 'hidden'\n if (input.viewReadOnly) return 'view-read-only'\n if (input.publishedSnapshot === undefined) return 'loading'\n return documentsSemanticEqual(input.collection, input.publishedSnapshot, input.draft)\n ? 'up-to-date'\n : 'ready'\n}\n\n/**\n * EC-325 — the flush-before-restore ordering \"Discard changes\" must follow,\n * mirroring the IDENTICAL `if (dirty) save()` step the Versions pane's\n * (`versions-pane.tsx`'s `handleRestore`) and the Compare view's\n * (`version-compare.tsx`'s `restore`) own restores already take before theirs.\n * `documents.restore` (`packages/core/src/operations/documents.ts`) snapshots\n * whatever is currently PERSISTED as its own \"pre-restore\" version — never the\n * client's in-memory draft — so an edit still sitting in the autosave debounce\n * window when Discard fires would otherwise never reach the server and be\n * silently unrecoverable once the draft is reverted. The underlying\n * save/restore commands are serialized by the shared operations queue\n * (EC-225), so the un-awaited `save()` here still lands before `restore` reads\n * the persisted document.\n */\nexport function flushDirtyDraft(input: { dirty: boolean; save: () => void }): void {\n if (input.dirty) input.save()\n}\n"]}
1
+ {"version":3,"file":"discard-draft.js","sourceRoot":"","sources":["../../../src/components/cms/discard-draft.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAuBhE,MAAM,UAAU,kBAAkB,CAAC,KAUlC;IACC,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,QAAQ,CAAA;IAC1D,IAAI,KAAK,CAAC,YAAY;QAAE,OAAO,gBAAgB,CAAA;IAC/C,IAAI,KAAK,CAAC,iBAAiB,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC3D,OAAO,sBAAsB,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,KAAK,CAAC;QACjE,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,OAAO,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,KAA2C;IACzE,IAAI,KAAK,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,EAAE,CAAA;AAC/B,CAAC","sourcesContent":["import { documentsSemanticEqual } from '@elytracms/core/content'\nimport type { CmsDocument } from '@elytracms/core/cms-core'\n\n/**\n * EC-316 — the \"Discard changes\" header action's enablement, as one pure\n * decision. Discarding reverts the draft to exactly the published content (via\n * the EC-298 restore machinery), so it is offered iff there is a published\n * version to revert TO and the draft actually differs from it:\n *\n * - `hidden` — the rail can't discard at all (no publish/versions\n * capability) or the document isn't published: there is no\n * published content to revert to, so no menu item.\n * - `view-read-only`— viewing the Published snapshot (EC-226): the draft isn't\n * the active view, so the item is disabled with a hint.\n * - `loading` — the version history hasn't resolved yet; disabled.\n * - `up-to-date` — the draft SEMANTICALLY equals the published snapshot\n * (`documentsSemanticEqual`, the SAME comparator the\n * Versions pane's publish gate uses, EC-298 — so the two\n * surfaces never contradict); nothing to discard, disabled.\n * - `ready` — a published version exists and the draft differs: enabled.\n */\nexport type DiscardDraftStatus = 'hidden' | 'view-read-only' | 'loading' | 'up-to-date' | 'ready'\n\nexport function discardDraftStatus(input: {\n /** The rail can publish AND has version history (both required to discard). */\n canDiscard: boolean\n /** The document IS published (version pointer set). */\n published: boolean\n /** The editor is viewing the read-only Published snapshot (EC-226). */\n viewReadOnly: boolean\n /** The pinned published version's snapshot; `undefined` while loading / none pinned. */\n publishedSnapshot: CmsDocument | undefined\n draft: CmsDocument\n}): DiscardDraftStatus {\n if (!input.canDiscard || !input.published) return 'hidden'\n if (input.viewReadOnly) return 'view-read-only'\n if (input.publishedSnapshot === undefined) return 'loading'\n return documentsSemanticEqual(input.publishedSnapshot, input.draft)\n ? 'up-to-date'\n : 'ready'\n}\n\n/**\n * EC-325 — the flush-before-restore ordering \"Discard changes\" must follow,\n * mirroring the IDENTICAL `if (dirty) save()` step the Versions pane's\n * (`versions-pane.tsx`'s `handleRestore`) and the Compare view's\n * (`version-compare.tsx`'s `restore`) own restores already take before theirs.\n * `documents.restore` (`packages/core/src/operations/documents.ts`) snapshots\n * whatever is currently PERSISTED as its own \"pre-restore\" version — never the\n * client's in-memory draft — so an edit still sitting in the autosave debounce\n * window when Discard fires would otherwise never reach the server and be\n * silently unrecoverable once the draft is reverted. The underlying\n * save/restore commands are serialized by the shared operations queue\n * (EC-225), so the un-awaited `save()` here still lands before `restore` reads\n * the persisted document.\n */\nexport function flushDirtyDraft(input: { dirty: boolean; save: () => void }): void {\n if (input.dirty) input.save()\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef, useState } from 'react';
3
- import { isFieldLocalized } from '@elytracms/core/cms-core';
3
+ import { canonicalDateValue, isFieldLocalized, selfTypeFieldValue } from '@elytracms/core/cms-core';
4
4
  import { Badge, Input, Row, Select, Text } from '@elytracms/studio/ui';
5
5
  import { cn } from '@elytracms/studio/lib/utils';
6
6
  import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
@@ -9,6 +9,7 @@ import { assetPickerBinding } from './asset-picker-core.js';
9
9
  import { CellValue, toRefArray } from './cell.js';
10
10
  import { DateInput } from './date-input.js';
11
11
  import { datePickerBounds } from './date-input-core.js';
12
+ import { selectedOptions } from './field-control-lib.js';
12
13
  import { cellEditKind, cellKey } from './cell-edit.js';
13
14
  import { fieldLabel } from './cms-labels.js';
14
15
  import { useCollectionView } from '../collection-view/collection-view-context.js';
@@ -176,7 +177,7 @@ function CellPicker({ field, value, onCommit, onCancel, }) {
176
177
  return (_jsx(AssetPicker, { open: true, onOpenChange: (open) => {
177
178
  if (!open)
178
179
  onCancel();
179
- }, ...assetPickerBinding(field, value, (next) => onCommit(next, null)) }));
180
+ }, ...assetPickerBinding(field, selfTypeFieldValue(field, value), (next) => onCommit(next, null)) }));
180
181
  }
181
182
  return null;
182
183
  }
@@ -188,11 +189,14 @@ function initialDraft(field, value) {
188
189
  case 'number':
189
190
  return typeof value === 'number' ? String(value) : '';
190
191
  case 'date':
191
- return typeof value === 'string' ? value.slice(0, 10) : '';
192
+ // EC-586: read through the one date canonicaliser — an epoch number or an
193
+ // offset instant shows its UTC day instead of digits or nothing.
194
+ return canonicalDateValue(value)?.slice(0, 10) ?? '';
192
195
  case 'select':
193
- if (field.multiple) {
194
- return Array.isArray(value) ? value.filter((v) => typeof v === 'string') : [];
195
- }
196
+ // EC-586: a legacy scalar in a multi-select reads as checked (the same reader
197
+ // as the form), so the next commit keeps it instead of dropping it.
198
+ if (field.multiple)
199
+ return selectedOptions(field, value);
196
200
  return typeof value === 'string' ? value : '';
197
201
  default:
198
202
  return typeof value === 'string' ? value : '';
@@ -205,7 +209,7 @@ function draftValue(field, draft) {
205
209
  case 'number':
206
210
  return draft === '' ? undefined : Number(draft);
207
211
  case 'date':
208
- return draft === '' || typeof draft !== 'string' ? undefined : `${draft}T00:00:00.000Z`;
212
+ return draft === '' || typeof draft !== 'string' ? undefined : canonicalDateValue(draft);
209
213
  case 'select':
210
214
  if (field.multiple)
211
215
  return Array.isArray(draft) ? draft : [];
@@ -1 +1 @@
1
- {"version":3,"file":"editable-cell.js","sourceRoot":"","sources":["../../../src/components/cms/editable-cell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useRef, useState } from 'react'\nimport type { FocusEvent, KeyboardEvent, ReactNode } from 'react'\nimport { isFieldLocalized } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, FieldDef, Locale, SelectFieldDef } from '@elytracms/core/cms-core'\nimport { Badge, Input, Row, Select, Text } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { editCreatesVariant, resolveDisplayedValue } from '@elytracms/studio/lib/locale'\nimport { assetPickerBinding } from './asset-picker-core'\nimport { CellValue, toRefArray } from './cell'\nimport { DateInput } from './date-input'\nimport { datePickerBounds } from './date-input-core'\nimport { cellEditKind, cellKey } from './cell-edit'\nimport { fieldLabel } from './cms-labels'\nimport type { AdvanceDirection } from './cell-edit'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { RelationPicker } from './relation-picker'\nimport { AssetPicker } from './asset-picker'\n\n/**\n * One editable table cell (EC-168, the Airtable-feel wedge of vision 3.2).\n *\n * Renders the field's `CellValue` until activated (click / Enter / F2), then\n * swaps to an inline editor (text, number, boolean, select, date) or opens the\n * existing relation/asset picker from the cell. Commits flow through the\n * table's cell-edit controller; rejections render as an error ring + message\n * on this cell — never a toast.\n */\nexport function EditableCell({\n collection,\n doc,\n field,\n editing,\n busy,\n errorMessage,\n onStartEdit,\n onCancel,\n onCommit,\n}: {\n collection: CollectionDef\n doc: CmsDocument\n field: FieldDef\n editing: boolean\n busy: boolean\n errorMessage: string | null\n onStartEdit: () => void\n onCancel: () => void\n onCommit: (value: unknown, advance: AdvanceDirection | null) => void\n}) {\n const source = useCollectionView()\n // Inline editing is a capability. EC-463: the gate used to sit only on the\n // Tab-advance list, so a rail WITHOUT it (redirects, users, the self-account\n // view) still rendered focusable, clickable, editor-opening cells whose commit\n // then hit \"no `inlineEditing.commitField` → close without dispatching\" — the\n // edit was accepted on screen and silently thrown away. A read-only rail now\n // really does yield no editable cells.\n const kind = source.capabilities.inlineEdit\n ? cellEditKind(field, source.isFieldReadOnly(field))\n : 'none'\n // The table's locale resolution (EC-176): the cell displays the AMBIENT\n // active locale (EC-018 fallback applied) and edits write that locale only.\n // Editing a fallback-shown value therefore creates the localized variant —\n // the fallback locale's value is never mutated.\n const locale = source.activeLocale\n const localized = isFieldLocalized(collection, field.name)\n const { value, fallback } = resolveDisplayedValue(\n collection,\n doc,\n field.name,\n locale,\n source.localeConfig,\n )\n const key = cellKey({ docId: doc.id, field: field.name })\n\n if (editing && kind === 'inline') {\n return (\n <CellEditorFrame\n locale={localized ? locale : null}\n createsLocalizedVariant={\n fallback !== null &&\n editCreatesVariant(collection, doc, field.name, locale, source.localeConfig)\n }\n cellKeyId={key}\n >\n <InlineCellEditor field={field} value={value} busy={busy} onCommit={onCommit} onCancel={onCancel} />\n </CellEditorFrame>\n )\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n // EC-463: Space too — it is half of the native activation contract the\n // `role=\"button\"` this cell now carries promises.\n if (event.key === 'Enter' || event.key === 'F2' || event.key === ' ') {\n event.preventDefault()\n onStartEdit()\n }\n }\n\n const editable = kind !== 'none'\n return (\n <div\n data-cell-key={key}\n data-testid={`cell-${key}`}\n tabIndex={editable ? 0 : undefined}\n // EC-463: an editable cell WAS an anonymous focusable `div` — the\n // accessibility tree listed it as a nameless \"generic, clickable\", so\n // neither a screen reader nor an agent editor could tell what activating it\n // would edit. It is an activation surface, so it says so and names itself.\n role={editable ? 'button' : undefined}\n aria-label={\n editable\n ? translate('cms.cell.editAria', {\n field: fieldLabel(field),\n title: source.titleOf({ collection: collection.id, id: doc.id }),\n })\n : undefined\n }\n // While a picker cell is editing, the dialog renders inside this div. React\n // bubbles portal events through the COMPONENT tree, so a click on the picker\n // (e.g. its ✕) would re-fire onStartEdit and reopen it — hence the `editing`\n // guard. ESC/click-outside dodge this (keydown / close-on-pointerdown).\n onClick={kind === 'none' || editing ? undefined : onStartEdit}\n onKeyDown={kind === 'none' || editing ? undefined : handleKeyDown}\n aria-invalid={errorMessage ? true : undefined}\n className={cn(\n '-mx-1.5 min-h-7 rounded-md px-1.5 py-0.5 outline-none',\n kind !== 'none' &&\n 'cursor-pointer transition-colors hover:bg-muted/70 focus-visible:ring-2 focus-visible:ring-ring/50',\n errorMessage && 'bg-destructive/5 ring-2 ring-inset ring-destructive/60',\n busy && 'opacity-60',\n )}\n >\n <CellValue collection={collection} doc={doc} field={field} />\n {errorMessage ? (\n <Text size=\"xs\" className=\"mt-0.5 text-destructive\" data-testid={`cell-error-${key}`}>\n {errorMessage}\n </Text>\n ) : null}\n {editing && kind === 'picker' ? (\n <CellPicker field={field} value={value} onCommit={onCommit} onCancel={onCancel} />\n ) : null}\n </div>\n )\n}\n\n/**\n * Editing chrome: marks the edited locale at the point of edit (EC-018), and\n * says so explicitly when committing will CREATE the active locale's variant\n * from a fallback-shown value (EC-176) — translation is a visible act.\n */\nfunction CellEditorFrame({\n locale,\n createsLocalizedVariant = false,\n cellKeyId,\n children,\n}: {\n locale: Locale | null\n /** True when the cell showed a fallback — committing creates the variant. */\n createsLocalizedVariant?: boolean\n cellKeyId: string\n children: ReactNode\n}) {\n return (\n <Row gap=\"1\" align=\"center\" data-cell-key={cellKeyId} className=\"-mx-1.5 min-w-[9rem]\">\n {locale ? (\n <Badge\n variant={createsLocalizedVariant ? 'info' : 'outline'}\n className=\"uppercase\"\n data-testid=\"cell-editing-locale\"\n title={\n createsLocalizedVariant\n ? translate('cms.cell.createsVariantTitle', { locale: locale.toUpperCase() })\n : undefined\n }\n >\n {locale}\n {createsLocalizedVariant ? (\n <span className=\"lowercase\"> · {translate('cms.cell.newVariant')}</span>\n ) : null}\n </Badge>\n ) : null}\n {children}\n </Row>\n )\n}\n\ntype CellDraft = string | boolean | string[]\n\n/**\n * The in-cell editor: draft → commit on Enter (advance down), Tab/Shift-Tab\n * (advance next/prev) or blur; Esc cancels without dispatching anything.\n */\nfunction InlineCellEditor({\n field,\n value,\n busy,\n onCommit,\n onCancel,\n}: {\n field: FieldDef\n value: unknown\n busy: boolean\n onCommit: (value: unknown, advance: AdvanceDirection | null) => void\n onCancel: () => void\n}) {\n const [draft, setDraft] = useState<CellDraft>(() => initialDraft(field, value))\n const draftRef = useRef(draft)\n draftRef.current = draft\n // EC-463: the ref moves SYNCHRONOUSLY with the control, not at the next\n // render. A control that only emits its final value on the settling keystroke\n // (the date control's Enter) would otherwise be committed one render stale —\n // the same event that produces the value also triggers `commit()` below.\n function updateDraft(next: CellDraft) {\n draftRef.current = next\n setDraft(next)\n }\n // Each editor instance settles exactly once (Tab keydown + the blur it\n // causes must not commit twice).\n const settledRef = useRef(false)\n\n function commit(advance: AdvanceDirection | null) {\n if (settledRef.current) return\n settledRef.current = true\n onCommit(draftValue(field, draftRef.current), advance)\n }\n\n function cancel() {\n if (settledRef.current) return\n settledRef.current = true\n onCancel()\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key === 'Enter') {\n event.preventDefault()\n commit('down')\n } else if (event.key === 'Tab') {\n event.preventDefault()\n commit(event.shiftKey ? 'prev' : 'next')\n } else if (event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n cancel()\n }\n }\n\n function handleBlur(event: FocusEvent<HTMLDivElement>) {\n // Focus left the editor entirely (e.g. a click elsewhere) → commit in place.\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) commit(null)\n }\n\n return (\n <div\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n onClick={(event) => event.stopPropagation()}\n className=\"min-w-0 flex-1\"\n data-testid=\"cell-editor\"\n aria-busy={busy || undefined}\n >\n <CellEditorControl field={field} draft={draft} onDraft={updateDraft} />\n </div>\n )\n}\n\n/** The actual input for the field type (compact, table-density variants). */\nfunction CellEditorControl({\n field,\n draft,\n onDraft,\n}: {\n field: FieldDef\n draft: CellDraft\n onDraft: (draft: CellDraft) => void\n}) {\n switch (field.type) {\n case 'boolean':\n return (\n <label className=\"flex h-7 items-center gap-2 text-sm\">\n <input\n autoFocus\n type=\"checkbox\"\n checked={draft === true}\n onChange={(event) => onDraft(event.target.checked)}\n className=\"size-4 accent-primary\"\n data-testid=\"cell-editor-boolean\"\n />\n <Text as=\"span\" size=\"xs\" tone=\"muted\">\n {draft === true ? translate('common.true') : translate('common.false')}\n </Text>\n </label>\n )\n case 'number':\n return (\n <Input\n autoFocus\n type=\"number\"\n value={typeof draft === 'string' ? draft : ''}\n onChange={(event) => onDraft(event.target.value)}\n className=\"h-7 text-sm\"\n data-testid=\"cell-editor-number\"\n />\n )\n case 'date':\n // EC-463: the shared date control. The cell's own draft already commits on\n // Enter/Tab/blur, so what this adds here is the working insert/paste path\n // and the guard against a re-render clobbering half-typed segments.\n return (\n <DateInput\n autoFocus\n value={typeof draft === 'string' ? draft : ''}\n onChange={(stored) => onDraft(stored ? stored.slice(0, 10) : '')}\n className=\"h-7 text-sm\"\n testId=\"cell-editor-date\"\n {...datePickerBounds(field.validation)}\n />\n )\n case 'select':\n if (field.multiple) {\n return <MultiSelectControl field={field} draft={draft} onDraft={onDraft} />\n }\n return (\n <Select\n autoFocus\n value={typeof draft === 'string' ? draft : ''}\n onChange={(event) => onDraft(event.target.value)}\n className=\"h-7 py-0 text-sm\"\n data-testid=\"cell-editor-select\"\n >\n <option value=\"\">—</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {localizedLabel(opt.label, opt.value)}\n </option>\n ))}\n </Select>\n )\n default:\n return (\n <Input\n autoFocus\n value={typeof draft === 'string' ? draft : ''}\n onChange={(event) => onDraft(event.target.value)}\n className=\"h-7 text-sm\"\n data-testid=\"cell-editor-text\"\n />\n )\n }\n}\n\nfunction MultiSelectControl({\n field,\n draft,\n onDraft,\n}: {\n field: SelectFieldDef\n draft: CellDraft\n onDraft: (draft: CellDraft) => void\n}) {\n const selected = Array.isArray(draft) ? draft : []\n return (\n <Row gap=\"2\" wrap data-testid=\"cell-editor-multiselect\">\n {field.options.map((opt, index) => {\n const on = selected.includes(opt.value)\n return (\n <label key={opt.value} className=\"flex items-center gap-1.5 text-xs\">\n <input\n autoFocus={index === 0}\n type=\"checkbox\"\n checked={on}\n onChange={() =>\n onDraft(on ? selected.filter((v) => v !== opt.value) : [...selected, opt.value])\n }\n className=\"size-3.5 accent-primary\"\n />\n {localizedLabel(opt.label, opt.value)}\n </label>\n )\n })}\n </Row>\n )\n}\n\n/** Relation/asset cells reuse the existing lightweight pickers, opened from the cell. */\nfunction CellPicker({\n field,\n value,\n onCommit,\n onCancel,\n}: {\n field: FieldDef\n value: unknown\n onCommit: (value: unknown, advance: AdvanceDirection | null) => void\n onCancel: () => void\n}) {\n if (field.type === 'relation') {\n return (\n <RelationPicker\n targetCollection={field.target}\n multiple={field.multiple}\n value={toRefArray(value)}\n open\n onOpenChange={(open) => {\n if (!open) onCancel()\n }}\n onChange={(refs) => onCommit(field.multiple ? refs : refs[0], null)}\n />\n )\n }\n if (field.type === 'asset') {\n return (\n <AssetPicker\n open\n onOpenChange={(open) => {\n if (!open) onCancel()\n }}\n // EC-588: the SAME picker configuration the detail editor uses, derived from\n // the field — its `accept` (the grid offered PDFs to an image field before),\n // `multiple` with the batch commit (an N-file upload attached one, EC-570 #1),\n // and the EC-558 pick operation that keeps each placement's sub-fields.\n {...assetPickerBinding(field, value, (next) => onCommit(next, null))}\n />\n )\n }\n return null\n}\n\n// --- Draft <-> stored value mapping (mirrors `FieldControl`'s conventions) -----\n\nfunction initialDraft(field: FieldDef, value: unknown): CellDraft {\n switch (field.type) {\n case 'boolean':\n return value === true\n case 'number':\n return typeof value === 'number' ? String(value) : ''\n case 'date':\n return typeof value === 'string' ? value.slice(0, 10) : ''\n case 'select':\n if (field.multiple) {\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === 'string') : []\n }\n return typeof value === 'string' ? value : ''\n default:\n return typeof value === 'string' ? value : ''\n }\n}\n\nfunction draftValue(field: FieldDef, draft: CellDraft): unknown {\n switch (field.type) {\n case 'boolean':\n return draft === true\n case 'number':\n return draft === '' ? undefined : Number(draft)\n case 'date':\n return draft === '' || typeof draft !== 'string' ? undefined : `${draft}T00:00:00.000Z`\n case 'select':\n if (field.multiple) return Array.isArray(draft) ? draft : []\n return draft === '' ? undefined : draft\n default:\n return draft === '' ? undefined : draft\n }\n}\n"]}
1
+ {"version":3,"file":"editable-cell.js","sourceRoot":"","sources":["../../../src/components/cms/editable-cell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useRef, useState } from 'react'\nimport type { FocusEvent, KeyboardEvent, ReactNode } from 'react'\nimport { canonicalDateValue, isFieldLocalized, selfTypeFieldValue } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, FieldDef, Locale, SelectFieldDef } from '@elytracms/core/cms-core'\nimport { Badge, Input, Row, Select, Text } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { editCreatesVariant, resolveDisplayedValue } from '@elytracms/studio/lib/locale'\nimport { assetPickerBinding } from './asset-picker-core'\nimport { CellValue, toRefArray } from './cell'\nimport { DateInput } from './date-input'\nimport { datePickerBounds } from './date-input-core'\nimport { selectedOptions } from './field-control-lib'\nimport { cellEditKind, cellKey } from './cell-edit'\nimport { fieldLabel } from './cms-labels'\nimport type { AdvanceDirection } from './cell-edit'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { RelationPicker } from './relation-picker'\nimport { AssetPicker } from './asset-picker'\n\n/**\n * One editable table cell (EC-168, the Airtable-feel wedge of vision 3.2).\n *\n * Renders the field's `CellValue` until activated (click / Enter / F2), then\n * swaps to an inline editor (text, number, boolean, select, date) or opens the\n * existing relation/asset picker from the cell. Commits flow through the\n * table's cell-edit controller; rejections render as an error ring + message\n * on this cell — never a toast.\n */\nexport function EditableCell({\n collection,\n doc,\n field,\n editing,\n busy,\n errorMessage,\n onStartEdit,\n onCancel,\n onCommit,\n}: {\n collection: CollectionDef\n doc: CmsDocument\n field: FieldDef\n editing: boolean\n busy: boolean\n errorMessage: string | null\n onStartEdit: () => void\n onCancel: () => void\n onCommit: (value: unknown, advance: AdvanceDirection | null) => void\n}) {\n const source = useCollectionView()\n // Inline editing is a capability. EC-463: the gate used to sit only on the\n // Tab-advance list, so a rail WITHOUT it (redirects, users, the self-account\n // view) still rendered focusable, clickable, editor-opening cells whose commit\n // then hit \"no `inlineEditing.commitField` → close without dispatching\" — the\n // edit was accepted on screen and silently thrown away. A read-only rail now\n // really does yield no editable cells.\n const kind = source.capabilities.inlineEdit\n ? cellEditKind(field, source.isFieldReadOnly(field))\n : 'none'\n // The table's locale resolution (EC-176): the cell displays the AMBIENT\n // active locale (EC-018 fallback applied) and edits write that locale only.\n // Editing a fallback-shown value therefore creates the localized variant —\n // the fallback locale's value is never mutated.\n const locale = source.activeLocale\n const localized = isFieldLocalized(collection, field.name)\n const { value, fallback } = resolveDisplayedValue(\n collection,\n doc,\n field.name,\n locale,\n source.localeConfig,\n )\n const key = cellKey({ docId: doc.id, field: field.name })\n\n if (editing && kind === 'inline') {\n return (\n <CellEditorFrame\n locale={localized ? locale : null}\n createsLocalizedVariant={\n fallback !== null &&\n editCreatesVariant(collection, doc, field.name, locale, source.localeConfig)\n }\n cellKeyId={key}\n >\n <InlineCellEditor field={field} value={value} busy={busy} onCommit={onCommit} onCancel={onCancel} />\n </CellEditorFrame>\n )\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n // EC-463: Space too — it is half of the native activation contract the\n // `role=\"button\"` this cell now carries promises.\n if (event.key === 'Enter' || event.key === 'F2' || event.key === ' ') {\n event.preventDefault()\n onStartEdit()\n }\n }\n\n const editable = kind !== 'none'\n return (\n <div\n data-cell-key={key}\n data-testid={`cell-${key}`}\n tabIndex={editable ? 0 : undefined}\n // EC-463: an editable cell WAS an anonymous focusable `div` — the\n // accessibility tree listed it as a nameless \"generic, clickable\", so\n // neither a screen reader nor an agent editor could tell what activating it\n // would edit. It is an activation surface, so it says so and names itself.\n role={editable ? 'button' : undefined}\n aria-label={\n editable\n ? translate('cms.cell.editAria', {\n field: fieldLabel(field),\n title: source.titleOf({ collection: collection.id, id: doc.id }),\n })\n : undefined\n }\n // While a picker cell is editing, the dialog renders inside this div. React\n // bubbles portal events through the COMPONENT tree, so a click on the picker\n // (e.g. its ✕) would re-fire onStartEdit and reopen it — hence the `editing`\n // guard. ESC/click-outside dodge this (keydown / close-on-pointerdown).\n onClick={kind === 'none' || editing ? undefined : onStartEdit}\n onKeyDown={kind === 'none' || editing ? undefined : handleKeyDown}\n aria-invalid={errorMessage ? true : undefined}\n className={cn(\n '-mx-1.5 min-h-7 rounded-md px-1.5 py-0.5 outline-none',\n kind !== 'none' &&\n 'cursor-pointer transition-colors hover:bg-muted/70 focus-visible:ring-2 focus-visible:ring-ring/50',\n errorMessage && 'bg-destructive/5 ring-2 ring-inset ring-destructive/60',\n busy && 'opacity-60',\n )}\n >\n <CellValue collection={collection} doc={doc} field={field} />\n {errorMessage ? (\n <Text size=\"xs\" className=\"mt-0.5 text-destructive\" data-testid={`cell-error-${key}`}>\n {errorMessage}\n </Text>\n ) : null}\n {editing && kind === 'picker' ? (\n <CellPicker field={field} value={value} onCommit={onCommit} onCancel={onCancel} />\n ) : null}\n </div>\n )\n}\n\n/**\n * Editing chrome: marks the edited locale at the point of edit (EC-018), and\n * says so explicitly when committing will CREATE the active locale's variant\n * from a fallback-shown value (EC-176) — translation is a visible act.\n */\nfunction CellEditorFrame({\n locale,\n createsLocalizedVariant = false,\n cellKeyId,\n children,\n}: {\n locale: Locale | null\n /** True when the cell showed a fallback — committing creates the variant. */\n createsLocalizedVariant?: boolean\n cellKeyId: string\n children: ReactNode\n}) {\n return (\n <Row gap=\"1\" align=\"center\" data-cell-key={cellKeyId} className=\"-mx-1.5 min-w-[9rem]\">\n {locale ? (\n <Badge\n variant={createsLocalizedVariant ? 'info' : 'outline'}\n className=\"uppercase\"\n data-testid=\"cell-editing-locale\"\n title={\n createsLocalizedVariant\n ? translate('cms.cell.createsVariantTitle', { locale: locale.toUpperCase() })\n : undefined\n }\n >\n {locale}\n {createsLocalizedVariant ? (\n <span className=\"lowercase\"> · {translate('cms.cell.newVariant')}</span>\n ) : null}\n </Badge>\n ) : null}\n {children}\n </Row>\n )\n}\n\ntype CellDraft = string | boolean | string[]\n\n/**\n * The in-cell editor: draft → commit on Enter (advance down), Tab/Shift-Tab\n * (advance next/prev) or blur; Esc cancels without dispatching anything.\n */\nfunction InlineCellEditor({\n field,\n value,\n busy,\n onCommit,\n onCancel,\n}: {\n field: FieldDef\n value: unknown\n busy: boolean\n onCommit: (value: unknown, advance: AdvanceDirection | null) => void\n onCancel: () => void\n}) {\n const [draft, setDraft] = useState<CellDraft>(() => initialDraft(field, value))\n const draftRef = useRef(draft)\n draftRef.current = draft\n // EC-463: the ref moves SYNCHRONOUSLY with the control, not at the next\n // render. A control that only emits its final value on the settling keystroke\n // (the date control's Enter) would otherwise be committed one render stale —\n // the same event that produces the value also triggers `commit()` below.\n function updateDraft(next: CellDraft) {\n draftRef.current = next\n setDraft(next)\n }\n // Each editor instance settles exactly once (Tab keydown + the blur it\n // causes must not commit twice).\n const settledRef = useRef(false)\n\n function commit(advance: AdvanceDirection | null) {\n if (settledRef.current) return\n settledRef.current = true\n onCommit(draftValue(field, draftRef.current), advance)\n }\n\n function cancel() {\n if (settledRef.current) return\n settledRef.current = true\n onCancel()\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key === 'Enter') {\n event.preventDefault()\n commit('down')\n } else if (event.key === 'Tab') {\n event.preventDefault()\n commit(event.shiftKey ? 'prev' : 'next')\n } else if (event.key === 'Escape') {\n event.preventDefault()\n event.stopPropagation()\n cancel()\n }\n }\n\n function handleBlur(event: FocusEvent<HTMLDivElement>) {\n // Focus left the editor entirely (e.g. a click elsewhere) → commit in place.\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) commit(null)\n }\n\n return (\n <div\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n onClick={(event) => event.stopPropagation()}\n className=\"min-w-0 flex-1\"\n data-testid=\"cell-editor\"\n aria-busy={busy || undefined}\n >\n <CellEditorControl field={field} draft={draft} onDraft={updateDraft} />\n </div>\n )\n}\n\n/** The actual input for the field type (compact, table-density variants). */\nfunction CellEditorControl({\n field,\n draft,\n onDraft,\n}: {\n field: FieldDef\n draft: CellDraft\n onDraft: (draft: CellDraft) => void\n}) {\n switch (field.type) {\n case 'boolean':\n return (\n <label className=\"flex h-7 items-center gap-2 text-sm\">\n <input\n autoFocus\n type=\"checkbox\"\n checked={draft === true}\n onChange={(event) => onDraft(event.target.checked)}\n className=\"size-4 accent-primary\"\n data-testid=\"cell-editor-boolean\"\n />\n <Text as=\"span\" size=\"xs\" tone=\"muted\">\n {draft === true ? translate('common.true') : translate('common.false')}\n </Text>\n </label>\n )\n case 'number':\n return (\n <Input\n autoFocus\n type=\"number\"\n value={typeof draft === 'string' ? draft : ''}\n onChange={(event) => onDraft(event.target.value)}\n className=\"h-7 text-sm\"\n data-testid=\"cell-editor-number\"\n />\n )\n case 'date':\n // EC-463: the shared date control. The cell's own draft already commits on\n // Enter/Tab/blur, so what this adds here is the working insert/paste path\n // and the guard against a re-render clobbering half-typed segments.\n return (\n <DateInput\n autoFocus\n value={typeof draft === 'string' ? draft : ''}\n onChange={(stored) => onDraft(stored ? stored.slice(0, 10) : '')}\n className=\"h-7 text-sm\"\n testId=\"cell-editor-date\"\n {...datePickerBounds(field.validation)}\n />\n )\n case 'select':\n if (field.multiple) {\n return <MultiSelectControl field={field} draft={draft} onDraft={onDraft} />\n }\n return (\n <Select\n autoFocus\n value={typeof draft === 'string' ? draft : ''}\n onChange={(event) => onDraft(event.target.value)}\n className=\"h-7 py-0 text-sm\"\n data-testid=\"cell-editor-select\"\n >\n <option value=\"\">—</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {localizedLabel(opt.label, opt.value)}\n </option>\n ))}\n </Select>\n )\n default:\n return (\n <Input\n autoFocus\n value={typeof draft === 'string' ? draft : ''}\n onChange={(event) => onDraft(event.target.value)}\n className=\"h-7 text-sm\"\n data-testid=\"cell-editor-text\"\n />\n )\n }\n}\n\nfunction MultiSelectControl({\n field,\n draft,\n onDraft,\n}: {\n field: SelectFieldDef\n draft: CellDraft\n onDraft: (draft: CellDraft) => void\n}) {\n const selected = Array.isArray(draft) ? draft : []\n return (\n <Row gap=\"2\" wrap data-testid=\"cell-editor-multiselect\">\n {field.options.map((opt, index) => {\n const on = selected.includes(opt.value)\n return (\n <label key={opt.value} className=\"flex items-center gap-1.5 text-xs\">\n <input\n autoFocus={index === 0}\n type=\"checkbox\"\n checked={on}\n onChange={() =>\n onDraft(on ? selected.filter((v) => v !== opt.value) : [...selected, opt.value])\n }\n className=\"size-3.5 accent-primary\"\n />\n {localizedLabel(opt.label, opt.value)}\n </label>\n )\n })}\n </Row>\n )\n}\n\n/** Relation/asset cells reuse the existing lightweight pickers, opened from the cell. */\nfunction CellPicker({\n field,\n value,\n onCommit,\n onCancel,\n}: {\n field: FieldDef\n value: unknown\n onCommit: (value: unknown, advance: AdvanceDirection | null) => void\n onCancel: () => void\n}) {\n if (field.type === 'relation') {\n return (\n <RelationPicker\n targetCollection={field.target}\n multiple={field.multiple}\n value={toRefArray(value)}\n open\n onOpenChange={(open) => {\n if (!open) onCancel()\n }}\n onChange={(refs) => onCommit(field.multiple ? refs : refs[0], null)}\n />\n )\n }\n if (field.type === 'asset') {\n return (\n <AssetPicker\n open\n onOpenChange={(open) => {\n if (!open) onCancel()\n }}\n // EC-588: the SAME picker configuration the detail editor uses, derived from\n // the field — its `accept` (the grid offered PDFs to an image field before),\n // `multiple` with the batch commit (an N-file upload attached one, EC-570 #1),\n // and the EC-558 pick operation that keeps each placement's sub-fields. EC-586:\n // it picks from the value read through the canonicaliser (the one cardinality\n // rule), so a lone placement in a `multiple` field is kept, not dropped.\n {...assetPickerBinding(field, selfTypeFieldValue(field, value), (next) => onCommit(next, null))}\n />\n )\n }\n return null\n}\n\n// --- Draft <-> stored value mapping (mirrors `FieldControl`'s conventions) -----\n\nfunction initialDraft(field: FieldDef, value: unknown): CellDraft {\n switch (field.type) {\n case 'boolean':\n return value === true\n case 'number':\n return typeof value === 'number' ? String(value) : ''\n case 'date':\n // EC-586: read through the one date canonicaliser — an epoch number or an\n // offset instant shows its UTC day instead of digits or nothing.\n return canonicalDateValue(value)?.slice(0, 10) ?? ''\n case 'select':\n // EC-586: a legacy scalar in a multi-select reads as checked (the same reader\n // as the form), so the next commit keeps it instead of dropping it.\n if (field.multiple) return selectedOptions(field, value)\n return typeof value === 'string' ? value : ''\n default:\n return typeof value === 'string' ? value : ''\n }\n}\n\nfunction draftValue(field: FieldDef, draft: CellDraft): unknown {\n switch (field.type) {\n case 'boolean':\n return draft === true\n case 'number':\n return draft === '' ? undefined : Number(draft)\n case 'date':\n return draft === '' || typeof draft !== 'string' ? undefined : canonicalDateValue(draft)\n case 'select':\n if (field.multiple) return Array.isArray(draft) ? draft : []\n return draft === '' ? undefined : draft\n default:\n return draft === '' ? undefined : draft\n }\n}\n"]}
@@ -13,6 +13,16 @@ import type { ObjectFieldDef, SelectFieldDef } from '@elytracms/core/cms-core';
13
13
  * so the two stay aligned through a reorder — a row's child/DOM state (an open
14
14
  * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).
15
15
  */
16
+ /**
17
+ * The options a multi-select currently holds, read through THE write canonicaliser
18
+ * (EC-586) — so a legacy scalar (`'news'`, written by the old bulk "set field") shows
19
+ * as checked instead of nothing, and the next toggle keeps it. The form used to read
20
+ * `Array.isArray(value) ? value : []`, which showed the scalar unchecked and DROPPED it
21
+ * on the next click (measured, round2-B §Q8 1(c)).
22
+ */
23
+ export declare function selectedOptions(field: SelectFieldDef, value: unknown): string[];
24
+ /** Toggle one option of a multi-select; the next value (an emptied list is absent). */
25
+ export declare function toggleSelectOption(field: SelectFieldDef, value: unknown, option: string): string[] | undefined;
16
26
  export declare function reorder<T>(list: readonly T[], from: number, to: number): T[];
17
27
  /**
18
28
  * A scannable one-line summary for a collapsed repeater item (EC-294): the first
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { selfTypeFieldValue } from '@elytracms/core/cms-core';
2
3
  import { Button, Row } from '@elytracms/studio/ui';
3
4
  import { localizedLabel } from '@elytracms/studio/lib/i18n';
4
5
  import { fieldLabel } from './cms-labels.js';
@@ -16,6 +17,25 @@ import { fieldLabel } from './cms-labels.js';
16
17
  * so the two stay aligned through a reorder — a row's child/DOM state (an open
17
18
  * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).
18
19
  */
20
+ /**
21
+ * The options a multi-select currently holds, read through THE write canonicaliser
22
+ * (EC-586) — so a legacy scalar (`'news'`, written by the old bulk "set field") shows
23
+ * as checked instead of nothing, and the next toggle keeps it. The form used to read
24
+ * `Array.isArray(value) ? value : []`, which showed the scalar unchecked and DROPPED it
25
+ * on the next click (measured, round2-B §Q8 1(c)).
26
+ */
27
+ export function selectedOptions(field, value) {
28
+ const canonical = selfTypeFieldValue(field, value);
29
+ return Array.isArray(canonical) ? canonical.filter((v) => typeof v === 'string') : [];
30
+ }
31
+ /** Toggle one option of a multi-select; the next value (an emptied list is absent). */
32
+ export function toggleSelectOption(field, value, option) {
33
+ const selected = selectedOptions(field, value);
34
+ const next = selected.includes(option)
35
+ ? selected.filter((v) => v !== option)
36
+ : [...selected, option];
37
+ return next.length === 0 ? undefined : next;
38
+ }
19
39
  export function reorder(list, from, to) {
20
40
  const next = list.slice();
21
41
  const [moved] = next.splice(from, 1);
@@ -1 +1 @@
1
- {"version":3,"file":"field-control-lib.js","sourceRoot":"","sources":["../../../src/components/cms/field-control-lib.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ObjectFieldDef, SelectFieldDef } from '@elytracms/core/cms-core'\nimport { Button, Row } from '@elytracms/studio/ui'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport { fieldLabel } from './cms-labels'\n\n/**\n * Pure companions of the document-editor field controls (EC-330, split from\n * `field-control.tsx`): the repeater reorder/summary/shape decisions and the\n * depth-encoding palette the object-array editors render from, plus the\n * `options` segmented control. Small and dependency-light so the behavioural\n * pins in `field-control.test.ts` reach them without pulling the whole\n * field-control dispatcher.\n */\n\n/**\n * Move `from` → `to`, returning a NEW array (all elements preserved). The repeater\n * applies the same `(from, to)` to its value array AND its parallel stable-key array,\n * so the two stay aligned through a reorder — a row's child/DOM state (an open\n * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).\n */\nexport function reorder<T>(list: readonly T[], from: number, to: number): T[] {\n const next = list.slice()\n const [moved] = next.splice(from, 1)\n if (moved !== undefined) next.splice(to, 0, moved)\n return next\n}\n\nconst SUMMARY_PREFERRED_FIELDS = ['title', 'label', 'name', 'heading', 'text', 'linkType']\n\n/**\n * A scannable one-line summary for a collapsed repeater item (EC-294): the first\n * meaningful scalar subfield value — preferring title-ish field names — else `#n`.\n * Pure (no store): summarises from the item's own scalar values, so it works for\n * any object shape without resolving relations/assets.\n */\nexport function objectItemSummary(\n fields: readonly { name: string }[],\n item: Record<string, unknown>,\n index: number,\n): string {\n const scalar = (name: string): string | null => {\n const v = item[name]\n if (typeof v === 'string' && v.trim()) return v.trim()\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n return null\n }\n const names = fields.map((f) => f.name)\n for (const name of SUMMARY_PREFERRED_FIELDS) {\n if (names.includes(name)) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n }\n for (const name of names) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n return `#${index + 1}`\n}\n\nfunction truncateSummary(s: string, max = 60): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s\n}\n\n// (B) Depth-keyed visuals: a coloured left rail + a matching faint tint, DISTINCT\n// per level so nesting reads at a glance. The outermost array (depth 0) starts at\n// the brand primary — no more neutral, un-encoded first level — then a fixed\n// high-contrast rotation whose adjacent hues never collide (primary→amber→green→red;\n// note `info` is a near-primary blue, so it is deliberately NOT adjacent to primary).\n// It cycles (mod) so pathological depth still renders. Literal classes so Tailwind emits them.\nconst DEPTH_RAIL = ['border-l-primary', 'border-l-warning', 'border-l-success', 'border-l-destructive']\nconst DEPTH_TINT = ['bg-primary/5', 'bg-warning/5', 'bg-success/5', 'bg-destructive/5']\n// `d` is always ≥ 0, so `d % length` is in-bounds — the assertion is sound and gives\n// callers a definite `string` (cn tolerates undefined, but tests read the value).\nexport const depthRail = (d: number) => DEPTH_RAIL[d % DEPTH_RAIL.length]!\nexport const depthTint = (d: number) => DEPTH_TINT[d % DEPTH_TINT.length]!\n\n// (C) An object array renders as a compact TABLE when every subfield is a scalar (a\n// flat row) and there are few enough columns to read; richer/nested object types stay\n// as cards with depth encoding (B). Conditional (function-hidden) subfields force the\n// card path — a table has no place to show/hide a column per row.\nconst TABULAR_FIELD_TYPES = new Set(['text', 'number', 'boolean', 'select', 'date'])\nconst MAX_TABLE_COLUMNS = 4\nexport function isTabularObjectType(field: ObjectFieldDef): boolean {\n return (\n field.fields.length > 0 &&\n field.fields.length <= MAX_TABLE_COLUMNS &&\n field.fields.every(\n (f) => TABULAR_FIELD_TYPES.has(f.type) && typeof f.form?.hidden !== 'function',\n )\n )\n}\n\n/**\n * EC-295: the `options` control — a `select` shown as inline segmented buttons\n * (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer\n * registration); single-select only. Each option shows `label ?? value` and a\n * `title` tooltip from the option's `description`; the field `default` + `readOnly`\n * flow through the shared form. Picks one — to allow an empty/unset value use the\n * default dropdown control.\n */\nexport function SegmentedOptions({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: SelectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const current = typeof value === 'string' ? value : ''\n return (\n <Row gap=\"1\" wrap role=\"radiogroup\" id={id} aria-label={fieldLabel(field)}>\n {field.options.map((opt) => {\n const active = current === opt.value\n return (\n <Button\n key={opt.value}\n type=\"button\"\n variant={active ? 'default' : 'outline'}\n size=\"sm\"\n role=\"radio\"\n aria-checked={active}\n title={localizedLabel(opt.description) || undefined}\n disabled={readOnly}\n onClick={() => onChange(opt.value)}\n >\n {localizedLabel(opt.label, opt.value)}\n </Button>\n )\n })}\n </Row>\n )\n}\n"]}
1
+ {"version":3,"file":"field-control-lib.js","sourceRoot":"","sources":["../../../src/components/cms/field-control-lib.tsx"],"names":[],"mappings":"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","sourcesContent":["import { selfTypeFieldValue } from '@elytracms/core/cms-core'\nimport type { ObjectFieldDef, SelectFieldDef } from '@elytracms/core/cms-core'\nimport { Button, Row } from '@elytracms/studio/ui'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport { fieldLabel } from './cms-labels'\n\n/**\n * Pure companions of the document-editor field controls (EC-330, split from\n * `field-control.tsx`): the repeater reorder/summary/shape decisions and the\n * depth-encoding palette the object-array editors render from, plus the\n * `options` segmented control. Small and dependency-light so the behavioural\n * pins in `field-control.test.ts` reach them without pulling the whole\n * field-control dispatcher.\n */\n\n/**\n * Move `from` → `to`, returning a NEW array (all elements preserved). The repeater\n * applies the same `(from, to)` to its value array AND its parallel stable-key array,\n * so the two stay aligned through a reorder — a row's child/DOM state (an open\n * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).\n */\n/**\n * The options a multi-select currently holds, read through THE write canonicaliser\n * (EC-586) — so a legacy scalar (`'news'`, written by the old bulk \"set field\") shows\n * as checked instead of nothing, and the next toggle keeps it. The form used to read\n * `Array.isArray(value) ? value : []`, which showed the scalar unchecked and DROPPED it\n * on the next click (measured, round2-B §Q8 1(c)).\n */\nexport function selectedOptions(field: SelectFieldDef, value: unknown): string[] {\n const canonical = selfTypeFieldValue(field, value)\n return Array.isArray(canonical) ? canonical.filter((v): v is string => typeof v === 'string') : []\n}\n\n/** Toggle one option of a multi-select; the next value (an emptied list is absent). */\nexport function toggleSelectOption(field: SelectFieldDef, value: unknown, option: string): string[] | undefined {\n const selected = selectedOptions(field, value)\n const next = selected.includes(option)\n ? selected.filter((v) => v !== option)\n : [...selected, option]\n return next.length === 0 ? undefined : next\n}\n\nexport function reorder<T>(list: readonly T[], from: number, to: number): T[] {\n const next = list.slice()\n const [moved] = next.splice(from, 1)\n if (moved !== undefined) next.splice(to, 0, moved)\n return next\n}\n\nconst SUMMARY_PREFERRED_FIELDS = ['title', 'label', 'name', 'heading', 'text', 'linkType']\n\n/**\n * A scannable one-line summary for a collapsed repeater item (EC-294): the first\n * meaningful scalar subfield value — preferring title-ish field names — else `#n`.\n * Pure (no store): summarises from the item's own scalar values, so it works for\n * any object shape without resolving relations/assets.\n */\nexport function objectItemSummary(\n fields: readonly { name: string }[],\n item: Record<string, unknown>,\n index: number,\n): string {\n const scalar = (name: string): string | null => {\n const v = item[name]\n if (typeof v === 'string' && v.trim()) return v.trim()\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n return null\n }\n const names = fields.map((f) => f.name)\n for (const name of SUMMARY_PREFERRED_FIELDS) {\n if (names.includes(name)) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n }\n for (const name of names) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n return `#${index + 1}`\n}\n\nfunction truncateSummary(s: string, max = 60): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s\n}\n\n// (B) Depth-keyed visuals: a coloured left rail + a matching faint tint, DISTINCT\n// per level so nesting reads at a glance. The outermost array (depth 0) starts at\n// the brand primary — no more neutral, un-encoded first level — then a fixed\n// high-contrast rotation whose adjacent hues never collide (primary→amber→green→red;\n// note `info` is a near-primary blue, so it is deliberately NOT adjacent to primary).\n// It cycles (mod) so pathological depth still renders. Literal classes so Tailwind emits them.\nconst DEPTH_RAIL = ['border-l-primary', 'border-l-warning', 'border-l-success', 'border-l-destructive']\nconst DEPTH_TINT = ['bg-primary/5', 'bg-warning/5', 'bg-success/5', 'bg-destructive/5']\n// `d` is always ≥ 0, so `d % length` is in-bounds — the assertion is sound and gives\n// callers a definite `string` (cn tolerates undefined, but tests read the value).\nexport const depthRail = (d: number) => DEPTH_RAIL[d % DEPTH_RAIL.length]!\nexport const depthTint = (d: number) => DEPTH_TINT[d % DEPTH_TINT.length]!\n\n// (C) An object array renders as a compact TABLE when every subfield is a scalar (a\n// flat row) and there are few enough columns to read; richer/nested object types stay\n// as cards with depth encoding (B). Conditional (function-hidden) subfields force the\n// card path — a table has no place to show/hide a column per row.\nconst TABULAR_FIELD_TYPES = new Set(['text', 'number', 'boolean', 'select', 'date'])\nconst MAX_TABLE_COLUMNS = 4\nexport function isTabularObjectType(field: ObjectFieldDef): boolean {\n return (\n field.fields.length > 0 &&\n field.fields.length <= MAX_TABLE_COLUMNS &&\n field.fields.every(\n (f) => TABULAR_FIELD_TYPES.has(f.type) && typeof f.form?.hidden !== 'function',\n )\n )\n}\n\n/**\n * EC-295: the `options` control — a `select` shown as inline segmented buttons\n * (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer\n * registration); single-select only. Each option shows `label ?? value` and a\n * `title` tooltip from the option's `description`; the field `default` + `readOnly`\n * flow through the shared form. Picks one — to allow an empty/unset value use the\n * default dropdown control.\n */\nexport function SegmentedOptions({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: SelectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const current = typeof value === 'string' ? value : ''\n return (\n <Row gap=\"1\" wrap role=\"radiogroup\" id={id} aria-label={fieldLabel(field)}>\n {field.options.map((opt) => {\n const active = current === opt.value\n return (\n <Button\n key={opt.value}\n type=\"button\"\n variant={active ? 'default' : 'outline'}\n size=\"sm\"\n role=\"radio\"\n aria-checked={active}\n title={localizedLabel(opt.description) || undefined}\n disabled={readOnly}\n onClick={() => onChange(opt.value)}\n >\n {localizedLabel(opt.label, opt.value)}\n </Button>\n )\n })}\n </Row>\n )\n}\n"]}
@@ -18,7 +18,7 @@ import { DocumentUrlSegment } from './document-url-path.js';
18
18
  import { DateInput } from './date-input.js';
19
19
  import { datePickerBounds } from './date-input-core.js';
20
20
  import { AssetField } from './asset-field.js';
21
- import { SegmentedOptions } from './field-control-lib.js';
21
+ import { SegmentedOptions, selectedOptions, toggleSelectOption } from './field-control-lib.js';
22
22
  import { slugAutoDeriveDecision } from './slug-auto-derive.js';
23
23
  import { engineOwnsUniqueness } from './slug-collision-report.js';
24
24
  import { ObjectField } from './object-array-field.js';
@@ -92,10 +92,10 @@ function BuiltInFieldControl({ field, value, onChange, readOnly, id }) {
92
92
  return (_jsx(DateInput, { id: id, value: value, onChange: onChange, disabled: readOnly, testId: `field-date-${field.name}`, ...datePickerBounds(field.validation) }));
93
93
  case 'select':
94
94
  if (field.multiple) {
95
- const selected = Array.isArray(value) ? value : [];
95
+ const selected = selectedOptions(field, value);
96
96
  return (_jsx(Row, { gap: "2", wrap: true, children: field.options.map((opt) => {
97
97
  const on = selected.includes(opt.value);
98
- return (_jsxs("label", { className: "flex items-center gap-1.5 text-sm", children: [_jsx("input", { type: "checkbox", checked: on, disabled: readOnly, onChange: () => onChange(on ? selected.filter((v) => v !== opt.value) : [...selected, opt.value]), className: "size-4 accent-primary" }), localizedLabel(opt.label, opt.value)] }, opt.value));
98
+ return (_jsxs("label", { className: "flex items-center gap-1.5 text-sm", children: [_jsx("input", { type: "checkbox", checked: on, disabled: readOnly, onChange: () => onChange(toggleSelectOption(field, value, opt.value)), className: "size-4 accent-primary" }), localizedLabel(opt.label, opt.value)] }, opt.value));
99
99
  }) }));
100
100
  }
101
101
  // EC-295: the `options` control renders a small fixed enumeration as inline
@@ -1 +1 @@
1
- {"version":3,"file":"field-control.js","sourceRoot":"","sources":["../../../src/components/cms/field-control.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, Link2, Pencil, Plus, RotateCw, X } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport type {\n DocumentRef,\n FieldDef,\n RelationFieldDef,\n UrlSegment,\n} from '@elytracms/core/cms-core'\nimport {\n BUILTIN_FIELD_CONTROLS,\n applySlugify,\n descendantIds,\n fieldViewLocale,\n findSlugCollision,\n parentRelationField,\n resolveSlugPath,\n resolveUrlSegments,\n slugSourceValue,\n valuesInLocale,\n} from '@elytracms/core/cms-core'\nimport type { SlugFieldConfig, SlugPathParts } from '@elytracms/core/cms-core'\nimport { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace'\nimport {\n Button,\n Input,\n Row,\n RowItem,\n Select,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { customizationIssueMessage } from '@elytracms/studio/lib/persona/labels'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { fieldLabel } from './cms-labels'\nimport { useOptionalDocumentEditor } from './document-editor-context'\nimport { toRefArray } from './cell'\nimport { RelationPicker } from './relation-picker'\nimport { RichTextEditor } from './rich-text'\nimport { DocumentUrlSegment } from './document-url-path'\nimport { DateInput } from './date-input'\nimport { datePickerBounds } from './date-input-core'\nimport { AssetField } from './asset-field'\nimport { SegmentedOptions } from './field-control-lib'\nimport { slugAutoDeriveDecision } from './slug-auto-derive'\nimport { engineOwnsUniqueness } from './slug-collision-report'\nimport { ObjectField } from './object-array-field'\nimport {\n FieldRendererBoundary,\n FieldRendererIssue,\n useFieldRenderers,\n} from './field-renderer'\n\ninterface FieldControlProps {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}\n\n/**\n * The document-editor field control. Resolves a project's custom renderer by the\n * field's `form.control` (EC-238, customization seam EC-236) and falls back to the\n * built-in control for the field's datatype: a registered renderer renders inside\n * an error boundary (a throw → the built-in + a visible issue); a rejected\n * registration (non-component) shows the built-in with the issue above it; a field\n * naming a control with NO renderer and NO built-in name (a typo) shows the\n * built-in with a loud issue — never a silent fall-through; otherwise the built-in\n * renders alone.\n *\n * EC-330 split: the asset-field family lives in `asset-field.tsx`, the object /\n * repeater editors in `object-array-field.tsx`, and their pure companions\n * (reorder/summary/depth/table decisions, the `options` segmented control) in\n * `field-control-lib.tsx` — this file keeps the dispatcher plus the slug and\n * relation controls.\n */\nexport function FieldControl(props: FieldControlProps) {\n const renderers = useFieldRenderers()\n const builtIn = <BuiltInFieldControl {...props} />\n const control = props.field.form?.control\n const Custom = control ? renderers.get(control) : undefined\n if (Custom) {\n return (\n <FieldRendererBoundary\n fallback={builtIn}\n issue={<FieldRendererIssue message={translate('cms.field.rendererCrashed')} />}\n >\n <Custom {...props} />\n </FieldRendererBoundary>\n )\n }\n if (control) {\n const rejected = renderers.issues.find((issue) => issue.id === control)\n if (rejected) {\n return (\n <Stack gap=\"1\">\n {/* EC-478: the issue's catalog ref, resolved for the active locale. */}\n <FieldRendererIssue message={customizationIssueMessage(rejected)} />\n {builtIn}\n </Stack>\n )\n }\n // The field asks for a control with no registered renderer and no built-in\n // name — surface it (a typo'd `control` would otherwise read as a plain input).\n if (!(BUILTIN_FIELD_CONTROLS as readonly string[]).includes(control)) {\n return (\n <Stack gap=\"1\">\n <FieldRendererIssue message={translate('cms.field.noRenderer', { control })} />\n {builtIn}\n </Stack>\n )\n }\n }\n return builtIn\n}\n\n/** The conventional slug field name (mirrors `url-for-document`'s `SLUG_FIELD`). */\nconst SLUG_FIELD_NAME = 'slug'\n/** The built-in control name that opts any `text` field into the slug control (EC-315). */\nconst SLUG_CONTROL = 'slug'\n\n/**\n * EC-315: a `text` field is rendered by the smart slug control when it opts in via\n * `form.control: 'slug'`, OR (the default auto-upgrade) when it is simply named\n * `slug` — the long-standing convention `url-for-document`/hierarchy already key off.\n */\nfunction isSlugControl(field: FieldDef): boolean {\n return field.type === 'text' && (field.form?.control === SLUG_CONTROL || field.name === SLUG_FIELD_NAME)\n}\n\nfunction BuiltInFieldControl({ field, value, onChange, readOnly, id }: FieldControlProps) {\n // EC-315: the smart slug control (composed path, auto-slug, per-locale segments)\n // subsumes the old inline `slug` input + PathPreview. It degrades to a plain input\n // when the collection isn't routable (name-based auto-upgrade only) or no document\n // editor context is present (e.g. a table-cell picker).\n if (isSlugControl(field)) {\n return <SlugField field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n }\n switch (field.type) {\n case 'boolean':\n return (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={value === true}\n disabled={readOnly}\n onChange={(e) => onChange(e.target.checked)}\n className=\"size-4 accent-primary disabled:opacity-50\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {translate(value === true ? 'common.true' : 'common.false')}\n </Text>\n </label>\n )\n case 'number':\n return (\n <Input\n id={id}\n type=\"number\"\n disabled={readOnly}\n value={typeof value === 'number' ? value : ''}\n onChange={(e) => onChange(e.target.value === '' ? undefined : Number(e.target.value))}\n />\n )\n case 'date':\n // EC-463: never write a mid-entry segment state into the document — see\n // `date-input.tsx` (this control silently stored `0002-09-18` for an\n // editor typing 2026-09-18).\n return (\n <DateInput\n id={id}\n value={value}\n onChange={onChange}\n disabled={readOnly}\n testId={`field-date-${field.name}`}\n {...datePickerBounds(field.validation)}\n />\n )\n case 'select':\n if (field.multiple) {\n const selected = Array.isArray(value) ? (value as string[]) : []\n return (\n <Row gap=\"2\" wrap>\n {field.options.map((opt) => {\n const on = selected.includes(opt.value)\n return (\n <label key={opt.value} className=\"flex items-center gap-1.5 text-sm\">\n <input\n type=\"checkbox\"\n checked={on}\n disabled={readOnly}\n onChange={() =>\n onChange(\n on ? selected.filter((v) => v !== opt.value) : [...selected, opt.value],\n )\n }\n className=\"size-4 accent-primary\"\n />\n {localizedLabel(opt.label, opt.value)}\n </label>\n )\n })}\n </Row>\n )\n }\n // EC-295: the `options` control renders a small fixed enumeration as inline\n // segmented buttons instead of the dropdown (single-select only).\n if (field.form?.control === 'options') {\n return (\n <SegmentedOptions field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n )\n }\n return (\n <Select\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n >\n <option value=\"\">—</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {localizedLabel(opt.label, opt.value)}\n </option>\n ))}\n </Select>\n )\n case 'richText':\n return (\n <RichTextEditor\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n id={id}\n allow={field.allow}\n // EC-436: the SAME resolved localized label the field row renders (the\n // EC-241 `fieldLabel` path) — titles the expand stage \"<label> — Rich text\".\n label={fieldLabel(field)}\n />\n )\n case 'relation':\n return <RelationField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'asset':\n return <AssetField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'object':\n return <ObjectField field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n default:\n return (\n <Input\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n />\n )\n }\n}\n\n/**\n * EC-315 — the smart slug / URL-segment control. A `text` field named `slug` (or any\n * `text` field with `form.control: 'slug'`) renders here instead of a bare input:\n *\n * - **Composed path**: the route-derived URL, decomposed by {@link resolveSlugPath}\n * into read-only parent/context segments + the editable slug segment inline + the\n * locale prefix (non-default locales). Reuses the same `urlForDocument` the live\n * site uses, so the preview matches what visitors get. Draft-derived (updates as\n * you type); the live \"View live\" button in the header stays the published open.\n * - **Auto-slug**: while the slug is empty or was last produced by auto-derivation,\n * it live-derives from the source field (default: the collection's `titleField`).\n * A manual edit stops the auto-fill; the ↻ button always re-derives.\n * - **Per-locale**: reads/writes through the shared `value`/`onChange` seam, so a\n * `localized` slug edits the active locale's segment with no bespoke locale code.\n * - **Uniqueness**: an inline warning when the candidate collides with a sibling in\n * the same scope (same parent when hierarchical, same locale). A custom\n * `slug.unique` hook replaces the default sibling scan.\n *\n * Degrades to a plain input when there is no document editor context (a table-cell\n * picker) or when a name-based auto-upgrade lands on a non-routable collection.\n */\nfunction SlugField({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const source = useCollectionView()\n const editor = useOptionalDocumentEditor()\n const slugConfig = field.slug as SlugFieldConfig | undefined\n const explicit = field.form?.control === SLUG_CONTROL\n const current = typeof value === 'string' ? value : ''\n const locale = source.activeLocale\n const localeConfig = source.localeConfig\n\n // 0.0.39 regression fix: resolve the deployment routes on EVERY render (a cheap\n // synchronous accessor), never freeze them at first paint. The earlier\n // `useMemo(() => resolveDeploymentRoutes(), [])` captured the EMPTY route set from\n // before the workspace/config finished loading — so the composed path never\n // appeared once routes arrived, and a stale set could paint a misleading prefix.\n const routes = resolveDeploymentRoutes()\n const routable = routes.some((r) => r.document?.collection === source.collectionId)\n\n const draft = editor?.draft\n // EC-463: a slug field that is NOT localized holds ONE value for every locale,\n // so its auto-slug must read ONE locale's source fields — the document's base\n // locale. Deriving from the ACTIVE locale made switching to the DE tab show a\n // German-derived slug preview for a field that is shared across locales (and,\n // with the EC-461 derive latch, a locale switch alone could then WRITE it).\n // A localized slug keeps per-locale derivation, which is the whole point of it.\n // EC-580: both halves are the one locale rule — `fieldViewLocale` names the view\n // (the default locale's for a shared field, the project's ONLY default), and the\n // source is read in that DISPLAYED view (`valuesInLocale`, with the fallback), so\n // the slug derives from the title the editor actually shows.\n const deriveLocale = fieldViewLocale(source.schema, field.name, locale, localeConfig)\n const values = useMemo<Record<string, unknown>>(\n () => (draft ? valuesInLocale(source.schema, draft, deriveLocale, localeConfig) : {}),\n [draft, deriveLocale, source.schema, localeConfig],\n )\n const derived = applySlugify(slugConfig, slugSourceValue(slugConfig, source.schema, values))\n\n // Track whether the slug was manually set — auto-derive stays active while the\n // slug is empty or was last produced by auto-derivation (never overwrites a manual\n // slug). Seeded true when the loaded document already carries a slug.\n const [manuallyEdited, setManuallyEdited] = useState(() => current.trim() !== '')\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n // EC-461: opening a document is not an edit. Auto-derive fires only once the\n // derived value has moved away from its mount state (the editor actually\n // changed a source field this session) — the old on-mount fill made every\n // slug-less document dirty on open, and the 1s autosave then WROTE it (in the\n // playground: the stale re-seeded baseline over the adapter's newer record —\n // the destructive half of the EC-461 loss family). Decision extracted pure in\n // `slug-auto-derive.ts`; the latch keeps derivation live after the first\n // divergence so title edits that return to the initial value still land.\n const initialDerivedRef = useRef(derived)\n const deriveLatchRef = useRef(false)\n useEffect(() => {\n const decision = slugAutoDeriveDecision({\n derived,\n current,\n initialDerived: initialDerivedRef.current,\n latchOpen: deriveLatchRef.current,\n manuallyEdited,\n readOnly,\n })\n if (decision.openLatch) deriveLatchRef.current = true\n if (decision.write) onChangeRef.current(derived)\n }, [derived, manuallyEdited, readOnly, current])\n\n // The composed path parts (routable collections with a document editor only).\n const siblings = draft ? source.documents() : []\n // Draft-derived (routes + locale only, no `frontendUrl`) — the composed path is a\n // read-only preview that updates as you type, mirroring the detail editor's draft\n // URL. The header's published-gated \"View live\" stays the sole open-live action,\n // so the draft/live distinction is preserved (an unpublished slug never links out).\n // EC-518: this preview therefore needs no `addressable` check — it renders the\n // segments as TEXT (the only link is an ancestor's EDITOR, via `DocumentUrlSegment`)\n // and never offers the live URL, so it cannot present an unreachable one as working;\n // the unaddressable finding itself surfaces as the field's inline issue (EC-492).\n const parts: SlugPathParts | undefined =\n draft && routable\n ? resolveSlugPath({ routes, localeConfig }, source.schema, draft, { siblings, locale })\n : undefined\n // The classified context segments (0.0.42, round 3) — the same `resolveUrlSegments`\n // the header URL row uses, so the slug prefix and the header path attribute each\n // segment identically. Everything before this document's own `current` slug is the\n // context prefix; its LAST segment is the immediate parent shown in the compact pill.\n const contextSegments: UrlSegment[] =\n draft && routable\n ? resolveUrlSegments({ routes, localeConfig }, source.schema, draft, { siblings, locale }).filter(\n (s) => s.kind !== 'current',\n )\n : []\n const lastContext = contextSegments[contextSegments.length - 1]\n\n // Default (sync) uniqueness — the first colliding sibling's title, or null.\n const defaultCollisionTitle = useMemo<string | null>(() => {\n if (slugConfig?.unique || !draft) return null\n const trimmed = current.trim()\n if (trimmed === '') return null\n const hit = findSlugCollision(trimmed, draft, source.documents(), {\n collection: source.schema,\n locale,\n localeConfig,\n })\n return hit ? source.titleOf({ collection: source.collectionId, id: hit.id }) : null\n }, [current, draft, slugConfig, locale, localeConfig, source])\n\n // Custom (possibly async) uniqueness hook — race-guarded.\n const [asyncTaken, setAsyncTaken] = useState(false)\n useEffect(() => {\n const unique = slugConfig?.unique\n if (!unique || !draft) {\n setAsyncTaken(false)\n return\n }\n const trimmed = current.trim()\n if (trimmed === '') {\n setAsyncTaken(false)\n return\n }\n let cancelled = false\n Promise.resolve(unique(trimmed, { document: draft, locale, siblings: source.documents() }))\n .then((ok) => {\n if (!cancelled) setAsyncTaken(ok === false)\n })\n .catch(() => {\n if (!cancelled) setAsyncTaken(false)\n })\n return () => {\n cancelled = true\n }\n }, [current, slugConfig, draft, locale, source])\n\n // EC-511 — one collision, one message. The engine's `validation.unique` finding\n // OWNS this field's uniqueness whenever it speaks: it is collection-wide,\n // localized, and it blocks publish, and it already renders inline in this very\n // column (the FieldRow `error` slot) plus the Issues pane. The control's hint then\n // fills only the gap the engine leaves — a collision that exists once the active\n // locale falls back, which `validation.unique` deliberately does not see, and any\n // field that declares no `validation.unique` at all. The two rules stay different\n // on purpose (AD-497b); only the double presentation goes. Full argument, and why\n // this matches on field+locale rather than on the colliding document's id, in\n // `slug-collision-report.ts`.\n const collisionMessage = engineOwnsUniqueness(editor?.issues ?? [], field.name, locale)\n ? undefined\n : slugConfig?.unique\n ? asyncTaken\n ? translate('cms.field.slug.takenGeneric')\n : undefined\n : defaultCollisionTitle\n ? translate('cms.field.slug.taken', { title: defaultCollisionTitle })\n : undefined\n\n function handleInput(next: string) {\n onChange(next || undefined)\n // Clearing re-enables auto-derive; any non-empty entry is a manual override.\n setManuallyEdited(next.trim() !== '')\n }\n\n function regenerate() {\n setManuallyEdited(false)\n const next = applySlugify(slugConfig, slugSourceValue(slugConfig, source.schema, values))\n onChange(next || undefined)\n }\n\n // No document editor (a picker), or a name-based auto-upgrade on a non-routable\n // collection: keep the historical plain input, no slug chrome (no regression).\n if (!editor || (!explicit && !routable)) {\n return (\n <Input\n id={id}\n disabled={readOnly}\n value={current}\n onChange={(e) => onChange(e.target.value || undefined)}\n />\n )\n }\n\n const input = (\n <Input\n id={id}\n disabled={readOnly}\n value={current}\n placeholder={derived || undefined}\n onChange={(e) => handleInput(e.target.value)}\n className={cn(parts ? 'h-7 min-w-[10ch] flex-1 bg-background font-mono text-xs' : 'flex-1')}\n />\n )\n const regenerateButton = !readOnly ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={regenerate}\n aria-label={translate('cms.field.slug.regenerate')}\n title={translate('cms.field.slug.regenerate')}\n >\n <RotateCw />\n </Button>\n ) : null\n\n // The compact, ellipsized read-only prefix (0.0.39 \"hybrid split\", round-3 clickable):\n // only the LAST context segment (`…/produkte/`) so a deep path can't overflow the\n // narrow Details sidebar — but that shown segment is now CLICKABLE with the same\n // navigation semantics as the header URL row (an ancestor DOCUMENT links to its\n // editor; a static route literal stays plain). The full composed path is the tooltip.\n const prefixTitle =\n parts?.path ??\n (contextSegments.length > 0 ? `/${contextSegments.map((s) => s.text).join('/')}/` : '/')\n\n return (\n <Stack gap=\"2\">\n {parts ? (\n <Row gap=\"1\" align=\"center\" data-testid=\"slug-composed-path\">\n <span\n className=\"inline-flex min-w-0 shrink-0 items-center rounded bg-muted/40 px-1.5 py-1 font-mono text-xs text-muted-foreground ring-1 ring-inset ring-border\"\n title={prefixTitle}\n data-testid=\"slug-prefix\"\n >\n {contextSegments.length === 0 ? (\n <span aria-hidden className=\"text-muted-foreground/50\">\n /\n </span>\n ) : (\n <>\n <span aria-hidden className=\"text-muted-foreground/50\">\n {contextSegments.length > 1 ? '…/' : '/'}\n </span>\n {lastContext ? (\n <DocumentUrlSegment\n segment={lastContext}\n projectId={editor?.projectId}\n collectionId={source.collectionId}\n />\n ) : null}\n <span aria-hidden className=\"text-muted-foreground/50\">\n /\n </span>\n </>\n )}\n </span>\n {input}\n {regenerateButton}\n </Row>\n ) : (\n <Row gap=\"1\" align=\"center\">\n {input}\n {regenerateButton}\n </Row>\n )}\n\n {collisionMessage ? (\n <Row gap=\"1\" align=\"center\" data-testid=\"slug-collision\">\n <AlertTriangle className=\"size-3.5 shrink-0 text-warning\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {collisionMessage}\n </Text>\n </Row>\n ) : null}\n </Stack>\n )\n}\n\nfunction RelationField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: RelationFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const source = useCollectionView()\n const editor = useOptionalDocumentEditor()\n const [open, setOpen] = useState(false)\n const refs = toRefArray(value)\n const projectId = source.projectId\n\n // EC-218: for the editing document's `parent` self-relation, exclude the\n // document itself and its descendants from the picker — choosing one would make\n // the page its own ancestor (a cycle). Only applies to the parent field of a\n // hierarchical collection; ordinary relations (and other self-relations) are\n // unrestricted. validateDocument is the backstop where this context is absent.\n const excludeIds = useMemo<Set<string> | undefined>(() => {\n if (!editor || field.target !== editor.collection.id) return undefined\n if (parentRelationField(editor.collection)?.name !== field.name) return undefined\n const ids = descendantIds(editor.docRef.id, source.referenceCandidates(editor.collection.id))\n ids.add(editor.docRef.id)\n return ids\n }, [editor, field.target, field.name, source])\n\n function apply(next: DocumentRef[]) {\n onChange(field.multiple ? next : (next[0] ?? undefined))\n }\n\n return (\n <Stack gap=\"2\">\n {refs.length > 0 ? (\n <Stack gap=\"1\">\n {refs.map((ref, i) => {\n // EC-296: the relation title navigates to the referenced document (autosave\n // keeps the current edit safe). Without a projectId (no persistence, e.g.\n // fixtures) it stays plain text. EC-246c: full-width row, not a pill —\n // long titles wrap awkwardly in a pill and a narrow field crushes it.\n const title = source.titleOf(ref)\n const label = (\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n <Link2 className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate\">{title}</span>\n </span>\n )\n return (\n <RowItem\n key={`${ref.id}-${i}`}\n data-testid=\"relation-value\"\n actions={\n !readOnly ? (\n <>\n {field.multiple === false ? (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('cms.relation.change')}\n title={translate('cms.relation.change')}\n onClick={() => setOpen(true)}\n >\n <Pencil />\n </Button>\n ) : null}\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('common.remove')}\n title={translate('common.remove')}\n className=\"hover:text-destructive\"\n onClick={() => apply(refs.filter((r) => r.id !== ref.id))}\n >\n <X />\n </Button>\n </>\n ) : undefined\n }\n >\n {projectId ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(ref.collection, ref.id) }}\n title={translate('cms.relation.openTitle', { title })}\n className=\"text-sm font-medium hover:text-primary hover:underline\"\n >\n {label}\n </Link>\n ) : (\n <Text as=\"span\" size=\"sm\" weight=\"medium\">\n {label}\n </Text>\n )}\n </RowItem>\n )\n })}\n </Stack>\n ) : null}\n {!readOnly && (refs.length === 0 || field.multiple) ? (\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <Plus /> {refs.length === 0 ? translate('cms.relation.pick') : translate('common.add')}\n </Button>\n </Row>\n ) : null}\n <RelationPicker\n targetCollection={field.target}\n multiple={field.multiple}\n value={refs}\n open={open}\n onOpenChange={setOpen}\n onChange={apply}\n excludeIds={excludeIds}\n />\n </Stack>\n )\n}\n"]}
1
+ {"version":3,"file":"field-control.js","sourceRoot":"","sources":["../../../src/components/cms/field-control.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, Link2, Pencil, Plus, RotateCw, X } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport type {\n DocumentRef,\n FieldDef,\n RelationFieldDef,\n UrlSegment,\n} from '@elytracms/core/cms-core'\nimport {\n BUILTIN_FIELD_CONTROLS,\n applySlugify,\n descendantIds,\n fieldViewLocale,\n findSlugCollision,\n parentRelationField,\n resolveSlugPath,\n resolveUrlSegments,\n slugSourceValue,\n valuesInLocale,\n} from '@elytracms/core/cms-core'\nimport type { SlugFieldConfig, SlugPathParts } from '@elytracms/core/cms-core'\nimport { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace'\nimport {\n Button,\n Input,\n Row,\n RowItem,\n Select,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { customizationIssueMessage } from '@elytracms/studio/lib/persona/labels'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { fieldLabel } from './cms-labels'\nimport { useOptionalDocumentEditor } from './document-editor-context'\nimport { toRefArray } from './cell'\nimport { RelationPicker } from './relation-picker'\nimport { RichTextEditor } from './rich-text'\nimport { DocumentUrlSegment } from './document-url-path'\nimport { DateInput } from './date-input'\nimport { datePickerBounds } from './date-input-core'\nimport { AssetField } from './asset-field'\nimport { SegmentedOptions, selectedOptions, toggleSelectOption } from './field-control-lib'\nimport { slugAutoDeriveDecision } from './slug-auto-derive'\nimport { engineOwnsUniqueness } from './slug-collision-report'\nimport { ObjectField } from './object-array-field'\nimport {\n FieldRendererBoundary,\n FieldRendererIssue,\n useFieldRenderers,\n} from './field-renderer'\n\ninterface FieldControlProps {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}\n\n/**\n * The document-editor field control. Resolves a project's custom renderer by the\n * field's `form.control` (EC-238, customization seam EC-236) and falls back to the\n * built-in control for the field's datatype: a registered renderer renders inside\n * an error boundary (a throw → the built-in + a visible issue); a rejected\n * registration (non-component) shows the built-in with the issue above it; a field\n * naming a control with NO renderer and NO built-in name (a typo) shows the\n * built-in with a loud issue — never a silent fall-through; otherwise the built-in\n * renders alone.\n *\n * EC-330 split: the asset-field family lives in `asset-field.tsx`, the object /\n * repeater editors in `object-array-field.tsx`, and their pure companions\n * (reorder/summary/depth/table decisions, the `options` segmented control) in\n * `field-control-lib.tsx` — this file keeps the dispatcher plus the slug and\n * relation controls.\n */\nexport function FieldControl(props: FieldControlProps) {\n const renderers = useFieldRenderers()\n const builtIn = <BuiltInFieldControl {...props} />\n const control = props.field.form?.control\n const Custom = control ? renderers.get(control) : undefined\n if (Custom) {\n return (\n <FieldRendererBoundary\n fallback={builtIn}\n issue={<FieldRendererIssue message={translate('cms.field.rendererCrashed')} />}\n >\n <Custom {...props} />\n </FieldRendererBoundary>\n )\n }\n if (control) {\n const rejected = renderers.issues.find((issue) => issue.id === control)\n if (rejected) {\n return (\n <Stack gap=\"1\">\n {/* EC-478: the issue's catalog ref, resolved for the active locale. */}\n <FieldRendererIssue message={customizationIssueMessage(rejected)} />\n {builtIn}\n </Stack>\n )\n }\n // The field asks for a control with no registered renderer and no built-in\n // name — surface it (a typo'd `control` would otherwise read as a plain input).\n if (!(BUILTIN_FIELD_CONTROLS as readonly string[]).includes(control)) {\n return (\n <Stack gap=\"1\">\n <FieldRendererIssue message={translate('cms.field.noRenderer', { control })} />\n {builtIn}\n </Stack>\n )\n }\n }\n return builtIn\n}\n\n/** The conventional slug field name (mirrors `url-for-document`'s `SLUG_FIELD`). */\nconst SLUG_FIELD_NAME = 'slug'\n/** The built-in control name that opts any `text` field into the slug control (EC-315). */\nconst SLUG_CONTROL = 'slug'\n\n/**\n * EC-315: a `text` field is rendered by the smart slug control when it opts in via\n * `form.control: 'slug'`, OR (the default auto-upgrade) when it is simply named\n * `slug` — the long-standing convention `url-for-document`/hierarchy already key off.\n */\nfunction isSlugControl(field: FieldDef): boolean {\n return field.type === 'text' && (field.form?.control === SLUG_CONTROL || field.name === SLUG_FIELD_NAME)\n}\n\nfunction BuiltInFieldControl({ field, value, onChange, readOnly, id }: FieldControlProps) {\n // EC-315: the smart slug control (composed path, auto-slug, per-locale segments)\n // subsumes the old inline `slug` input + PathPreview. It degrades to a plain input\n // when the collection isn't routable (name-based auto-upgrade only) or no document\n // editor context is present (e.g. a table-cell picker).\n if (isSlugControl(field)) {\n return <SlugField field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n }\n switch (field.type) {\n case 'boolean':\n return (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={value === true}\n disabled={readOnly}\n onChange={(e) => onChange(e.target.checked)}\n className=\"size-4 accent-primary disabled:opacity-50\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {translate(value === true ? 'common.true' : 'common.false')}\n </Text>\n </label>\n )\n case 'number':\n return (\n <Input\n id={id}\n type=\"number\"\n disabled={readOnly}\n value={typeof value === 'number' ? value : ''}\n onChange={(e) => onChange(e.target.value === '' ? undefined : Number(e.target.value))}\n />\n )\n case 'date':\n // EC-463: never write a mid-entry segment state into the document — see\n // `date-input.tsx` (this control silently stored `0002-09-18` for an\n // editor typing 2026-09-18).\n return (\n <DateInput\n id={id}\n value={value}\n onChange={onChange}\n disabled={readOnly}\n testId={`field-date-${field.name}`}\n {...datePickerBounds(field.validation)}\n />\n )\n case 'select':\n if (field.multiple) {\n const selected = selectedOptions(field, value)\n return (\n <Row gap=\"2\" wrap>\n {field.options.map((opt) => {\n const on = selected.includes(opt.value)\n return (\n <label key={opt.value} className=\"flex items-center gap-1.5 text-sm\">\n <input\n type=\"checkbox\"\n checked={on}\n disabled={readOnly}\n onChange={() => onChange(toggleSelectOption(field, value, opt.value))}\n className=\"size-4 accent-primary\"\n />\n {localizedLabel(opt.label, opt.value)}\n </label>\n )\n })}\n </Row>\n )\n }\n // EC-295: the `options` control renders a small fixed enumeration as inline\n // segmented buttons instead of the dropdown (single-select only).\n if (field.form?.control === 'options') {\n return (\n <SegmentedOptions field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n )\n }\n return (\n <Select\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n >\n <option value=\"\">—</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {localizedLabel(opt.label, opt.value)}\n </option>\n ))}\n </Select>\n )\n case 'richText':\n return (\n <RichTextEditor\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n id={id}\n allow={field.allow}\n // EC-436: the SAME resolved localized label the field row renders (the\n // EC-241 `fieldLabel` path) — titles the expand stage \"<label> — Rich text\".\n label={fieldLabel(field)}\n />\n )\n case 'relation':\n return <RelationField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'asset':\n return <AssetField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'object':\n return <ObjectField field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n default:\n return (\n <Input\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n />\n )\n }\n}\n\n/**\n * EC-315 — the smart slug / URL-segment control. A `text` field named `slug` (or any\n * `text` field with `form.control: 'slug'`) renders here instead of a bare input:\n *\n * - **Composed path**: the route-derived URL, decomposed by {@link resolveSlugPath}\n * into read-only parent/context segments + the editable slug segment inline + the\n * locale prefix (non-default locales). Reuses the same `urlForDocument` the live\n * site uses, so the preview matches what visitors get. Draft-derived (updates as\n * you type); the live \"View live\" button in the header stays the published open.\n * - **Auto-slug**: while the slug is empty or was last produced by auto-derivation,\n * it live-derives from the source field (default: the collection's `titleField`).\n * A manual edit stops the auto-fill; the ↻ button always re-derives.\n * - **Per-locale**: reads/writes through the shared `value`/`onChange` seam, so a\n * `localized` slug edits the active locale's segment with no bespoke locale code.\n * - **Uniqueness**: an inline warning when the candidate collides with a sibling in\n * the same scope (same parent when hierarchical, same locale). A custom\n * `slug.unique` hook replaces the default sibling scan.\n *\n * Degrades to a plain input when there is no document editor context (a table-cell\n * picker) or when a name-based auto-upgrade lands on a non-routable collection.\n */\nfunction SlugField({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const source = useCollectionView()\n const editor = useOptionalDocumentEditor()\n const slugConfig = field.slug as SlugFieldConfig | undefined\n const explicit = field.form?.control === SLUG_CONTROL\n const current = typeof value === 'string' ? value : ''\n const locale = source.activeLocale\n const localeConfig = source.localeConfig\n\n // 0.0.39 regression fix: resolve the deployment routes on EVERY render (a cheap\n // synchronous accessor), never freeze them at first paint. The earlier\n // `useMemo(() => resolveDeploymentRoutes(), [])` captured the EMPTY route set from\n // before the workspace/config finished loading — so the composed path never\n // appeared once routes arrived, and a stale set could paint a misleading prefix.\n const routes = resolveDeploymentRoutes()\n const routable = routes.some((r) => r.document?.collection === source.collectionId)\n\n const draft = editor?.draft\n // EC-463: a slug field that is NOT localized holds ONE value for every locale,\n // so its auto-slug must read ONE locale's source fields — the document's base\n // locale. Deriving from the ACTIVE locale made switching to the DE tab show a\n // German-derived slug preview for a field that is shared across locales (and,\n // with the EC-461 derive latch, a locale switch alone could then WRITE it).\n // A localized slug keeps per-locale derivation, which is the whole point of it.\n // EC-580: both halves are the one locale rule — `fieldViewLocale` names the view\n // (the default locale's for a shared field, the project's ONLY default), and the\n // source is read in that DISPLAYED view (`valuesInLocale`, with the fallback), so\n // the slug derives from the title the editor actually shows.\n const deriveLocale = fieldViewLocale(source.schema, field.name, locale, localeConfig)\n const values = useMemo<Record<string, unknown>>(\n () => (draft ? valuesInLocale(source.schema, draft, deriveLocale, localeConfig) : {}),\n [draft, deriveLocale, source.schema, localeConfig],\n )\n const derived = applySlugify(slugConfig, slugSourceValue(slugConfig, source.schema, values))\n\n // Track whether the slug was manually set — auto-derive stays active while the\n // slug is empty or was last produced by auto-derivation (never overwrites a manual\n // slug). Seeded true when the loaded document already carries a slug.\n const [manuallyEdited, setManuallyEdited] = useState(() => current.trim() !== '')\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n // EC-461: opening a document is not an edit. Auto-derive fires only once the\n // derived value has moved away from its mount state (the editor actually\n // changed a source field this session) — the old on-mount fill made every\n // slug-less document dirty on open, and the 1s autosave then WROTE it (in the\n // playground: the stale re-seeded baseline over the adapter's newer record —\n // the destructive half of the EC-461 loss family). Decision extracted pure in\n // `slug-auto-derive.ts`; the latch keeps derivation live after the first\n // divergence so title edits that return to the initial value still land.\n const initialDerivedRef = useRef(derived)\n const deriveLatchRef = useRef(false)\n useEffect(() => {\n const decision = slugAutoDeriveDecision({\n derived,\n current,\n initialDerived: initialDerivedRef.current,\n latchOpen: deriveLatchRef.current,\n manuallyEdited,\n readOnly,\n })\n if (decision.openLatch) deriveLatchRef.current = true\n if (decision.write) onChangeRef.current(derived)\n }, [derived, manuallyEdited, readOnly, current])\n\n // The composed path parts (routable collections with a document editor only).\n const siblings = draft ? source.documents() : []\n // Draft-derived (routes + locale only, no `frontendUrl`) — the composed path is a\n // read-only preview that updates as you type, mirroring the detail editor's draft\n // URL. The header's published-gated \"View live\" stays the sole open-live action,\n // so the draft/live distinction is preserved (an unpublished slug never links out).\n // EC-518: this preview therefore needs no `addressable` check — it renders the\n // segments as TEXT (the only link is an ancestor's EDITOR, via `DocumentUrlSegment`)\n // and never offers the live URL, so it cannot present an unreachable one as working;\n // the unaddressable finding itself surfaces as the field's inline issue (EC-492).\n const parts: SlugPathParts | undefined =\n draft && routable\n ? resolveSlugPath({ routes, localeConfig }, source.schema, draft, { siblings, locale })\n : undefined\n // The classified context segments (0.0.42, round 3) — the same `resolveUrlSegments`\n // the header URL row uses, so the slug prefix and the header path attribute each\n // segment identically. Everything before this document's own `current` slug is the\n // context prefix; its LAST segment is the immediate parent shown in the compact pill.\n const contextSegments: UrlSegment[] =\n draft && routable\n ? resolveUrlSegments({ routes, localeConfig }, source.schema, draft, { siblings, locale }).filter(\n (s) => s.kind !== 'current',\n )\n : []\n const lastContext = contextSegments[contextSegments.length - 1]\n\n // Default (sync) uniqueness — the first colliding sibling's title, or null.\n const defaultCollisionTitle = useMemo<string | null>(() => {\n if (slugConfig?.unique || !draft) return null\n const trimmed = current.trim()\n if (trimmed === '') return null\n const hit = findSlugCollision(trimmed, draft, source.documents(), {\n collection: source.schema,\n locale,\n localeConfig,\n })\n return hit ? source.titleOf({ collection: source.collectionId, id: hit.id }) : null\n }, [current, draft, slugConfig, locale, localeConfig, source])\n\n // Custom (possibly async) uniqueness hook — race-guarded.\n const [asyncTaken, setAsyncTaken] = useState(false)\n useEffect(() => {\n const unique = slugConfig?.unique\n if (!unique || !draft) {\n setAsyncTaken(false)\n return\n }\n const trimmed = current.trim()\n if (trimmed === '') {\n setAsyncTaken(false)\n return\n }\n let cancelled = false\n Promise.resolve(unique(trimmed, { document: draft, locale, siblings: source.documents() }))\n .then((ok) => {\n if (!cancelled) setAsyncTaken(ok === false)\n })\n .catch(() => {\n if (!cancelled) setAsyncTaken(false)\n })\n return () => {\n cancelled = true\n }\n }, [current, slugConfig, draft, locale, source])\n\n // EC-511 — one collision, one message. The engine's `validation.unique` finding\n // OWNS this field's uniqueness whenever it speaks: it is collection-wide,\n // localized, and it blocks publish, and it already renders inline in this very\n // column (the FieldRow `error` slot) plus the Issues pane. The control's hint then\n // fills only the gap the engine leaves — a collision that exists once the active\n // locale falls back, which `validation.unique` deliberately does not see, and any\n // field that declares no `validation.unique` at all. The two rules stay different\n // on purpose (AD-497b); only the double presentation goes. Full argument, and why\n // this matches on field+locale rather than on the colliding document's id, in\n // `slug-collision-report.ts`.\n const collisionMessage = engineOwnsUniqueness(editor?.issues ?? [], field.name, locale)\n ? undefined\n : slugConfig?.unique\n ? asyncTaken\n ? translate('cms.field.slug.takenGeneric')\n : undefined\n : defaultCollisionTitle\n ? translate('cms.field.slug.taken', { title: defaultCollisionTitle })\n : undefined\n\n function handleInput(next: string) {\n onChange(next || undefined)\n // Clearing re-enables auto-derive; any non-empty entry is a manual override.\n setManuallyEdited(next.trim() !== '')\n }\n\n function regenerate() {\n setManuallyEdited(false)\n const next = applySlugify(slugConfig, slugSourceValue(slugConfig, source.schema, values))\n onChange(next || undefined)\n }\n\n // No document editor (a picker), or a name-based auto-upgrade on a non-routable\n // collection: keep the historical plain input, no slug chrome (no regression).\n if (!editor || (!explicit && !routable)) {\n return (\n <Input\n id={id}\n disabled={readOnly}\n value={current}\n onChange={(e) => onChange(e.target.value || undefined)}\n />\n )\n }\n\n const input = (\n <Input\n id={id}\n disabled={readOnly}\n value={current}\n placeholder={derived || undefined}\n onChange={(e) => handleInput(e.target.value)}\n className={cn(parts ? 'h-7 min-w-[10ch] flex-1 bg-background font-mono text-xs' : 'flex-1')}\n />\n )\n const regenerateButton = !readOnly ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={regenerate}\n aria-label={translate('cms.field.slug.regenerate')}\n title={translate('cms.field.slug.regenerate')}\n >\n <RotateCw />\n </Button>\n ) : null\n\n // The compact, ellipsized read-only prefix (0.0.39 \"hybrid split\", round-3 clickable):\n // only the LAST context segment (`…/produkte/`) so a deep path can't overflow the\n // narrow Details sidebar — but that shown segment is now CLICKABLE with the same\n // navigation semantics as the header URL row (an ancestor DOCUMENT links to its\n // editor; a static route literal stays plain). The full composed path is the tooltip.\n const prefixTitle =\n parts?.path ??\n (contextSegments.length > 0 ? `/${contextSegments.map((s) => s.text).join('/')}/` : '/')\n\n return (\n <Stack gap=\"2\">\n {parts ? (\n <Row gap=\"1\" align=\"center\" data-testid=\"slug-composed-path\">\n <span\n className=\"inline-flex min-w-0 shrink-0 items-center rounded bg-muted/40 px-1.5 py-1 font-mono text-xs text-muted-foreground ring-1 ring-inset ring-border\"\n title={prefixTitle}\n data-testid=\"slug-prefix\"\n >\n {contextSegments.length === 0 ? (\n <span aria-hidden className=\"text-muted-foreground/50\">\n /\n </span>\n ) : (\n <>\n <span aria-hidden className=\"text-muted-foreground/50\">\n {contextSegments.length > 1 ? '…/' : '/'}\n </span>\n {lastContext ? (\n <DocumentUrlSegment\n segment={lastContext}\n projectId={editor?.projectId}\n collectionId={source.collectionId}\n />\n ) : null}\n <span aria-hidden className=\"text-muted-foreground/50\">\n /\n </span>\n </>\n )}\n </span>\n {input}\n {regenerateButton}\n </Row>\n ) : (\n <Row gap=\"1\" align=\"center\">\n {input}\n {regenerateButton}\n </Row>\n )}\n\n {collisionMessage ? (\n <Row gap=\"1\" align=\"center\" data-testid=\"slug-collision\">\n <AlertTriangle className=\"size-3.5 shrink-0 text-warning\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {collisionMessage}\n </Text>\n </Row>\n ) : null}\n </Stack>\n )\n}\n\nfunction RelationField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: RelationFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const source = useCollectionView()\n const editor = useOptionalDocumentEditor()\n const [open, setOpen] = useState(false)\n const refs = toRefArray(value)\n const projectId = source.projectId\n\n // EC-218: for the editing document's `parent` self-relation, exclude the\n // document itself and its descendants from the picker — choosing one would make\n // the page its own ancestor (a cycle). Only applies to the parent field of a\n // hierarchical collection; ordinary relations (and other self-relations) are\n // unrestricted. validateDocument is the backstop where this context is absent.\n const excludeIds = useMemo<Set<string> | undefined>(() => {\n if (!editor || field.target !== editor.collection.id) return undefined\n if (parentRelationField(editor.collection)?.name !== field.name) return undefined\n const ids = descendantIds(editor.docRef.id, source.referenceCandidates(editor.collection.id))\n ids.add(editor.docRef.id)\n return ids\n }, [editor, field.target, field.name, source])\n\n function apply(next: DocumentRef[]) {\n onChange(field.multiple ? next : (next[0] ?? undefined))\n }\n\n return (\n <Stack gap=\"2\">\n {refs.length > 0 ? (\n <Stack gap=\"1\">\n {refs.map((ref, i) => {\n // EC-296: the relation title navigates to the referenced document (autosave\n // keeps the current edit safe). Without a projectId (no persistence, e.g.\n // fixtures) it stays plain text. EC-246c: full-width row, not a pill —\n // long titles wrap awkwardly in a pill and a narrow field crushes it.\n const title = source.titleOf(ref)\n const label = (\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n <Link2 className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate\">{title}</span>\n </span>\n )\n return (\n <RowItem\n key={`${ref.id}-${i}`}\n data-testid=\"relation-value\"\n actions={\n !readOnly ? (\n <>\n {field.multiple === false ? (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('cms.relation.change')}\n title={translate('cms.relation.change')}\n onClick={() => setOpen(true)}\n >\n <Pencil />\n </Button>\n ) : null}\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={translate('common.remove')}\n title={translate('common.remove')}\n className=\"hover:text-destructive\"\n onClick={() => apply(refs.filter((r) => r.id !== ref.id))}\n >\n <X />\n </Button>\n </>\n ) : undefined\n }\n >\n {projectId ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(ref.collection, ref.id) }}\n title={translate('cms.relation.openTitle', { title })}\n className=\"text-sm font-medium hover:text-primary hover:underline\"\n >\n {label}\n </Link>\n ) : (\n <Text as=\"span\" size=\"sm\" weight=\"medium\">\n {label}\n </Text>\n )}\n </RowItem>\n )\n })}\n </Stack>\n ) : null}\n {!readOnly && (refs.length === 0 || field.multiple) ? (\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <Plus /> {refs.length === 0 ? translate('cms.relation.pick') : translate('common.add')}\n </Button>\n </Row>\n ) : null}\n <RelationPicker\n targetCollection={field.target}\n multiple={field.multiple}\n value={refs}\n open={open}\n onOpenChange={setOpen}\n onChange={apply}\n excludeIds={excludeIds}\n />\n </Stack>\n )\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronDown, ChevronUp, GripVertical, MoreHorizontal, Plus, Trash2 } from 'lucide-react';
3
3
  import { createContext, useContext, useEffect, useRef, useState } from 'react';
4
- import { isFieldHidden } from '@elytracms/core/cms-core';
4
+ import { isFieldHidden, seedObjectItem } from '@elytracms/core/cms-core';
5
5
  import { Button, Card, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, FIELD_PATH_ATTR, Field, Row, Stack, Text, } from '@elytracms/studio/ui';
6
6
  import { cn } from '@elytracms/studio/lib/utils';
7
7
  import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
@@ -59,10 +59,8 @@ function useObjectArray(field, value, onChange) {
59
59
  const move = (from, to) => commit(reorder(items, from, to), reorder(keys, from, to));
60
60
  const removeAt = (i) => commit(items.filter((_, j) => j !== i), keys.filter((_, j) => j !== i));
61
61
  const addItem = () => {
62
- const seed = {};
63
- for (const sub of field.fields)
64
- if (sub.default !== undefined)
65
- seed[sub.name] = sub.default;
62
+ // EC-581: the one seeding rule (core `seedObjectItem`) — nested defaults included.
63
+ const seed = seedObjectItem(field.fields);
66
64
  const key = seq.current++;
67
65
  commit([...items, seed], [...keys, key]);
68
66
  return key;