@elytracms/studio 0.0.92 → 0.0.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/asset-alt-migration.test.ts +127 -0
- package/convex/asset-replace.test.ts +168 -0
- package/convex/asset-revalidate.test.ts +6 -6
- package/convex/assets.ts +41 -5
- package/convex/cms.ts +25 -11
- package/convex/contract-parity.test.ts +18 -3
- package/convex/document-revalidate.test.ts +10 -12
- package/convex/orphan-hydration.test.ts +116 -0
- package/convex/rail-write-parity.test.ts +11 -8
- package/convex/schema.ts +6 -0
- package/dist/components/ai/proposal-card.js +2 -1
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +4 -2
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -0
- package/dist/components/ai/translate-skipped.js +46 -0
- package/dist/components/ai/translate-skipped.js.map +1 -0
- package/dist/components/assets/asset-detail.js +18 -8
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +15 -4
- package/dist/components/assets/asset-metadata.js +15 -2
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +4 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +0 -8
- package/dist/components/assets/asset-selection.js +4 -23
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +3 -2
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +3 -3
- package/dist/components/assets/assets-collection-view-source.js +9 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +14 -0
- package/dist/components/assets/assets-store.js +23 -1
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/replace-file.d.ts +22 -0
- package/dist/components/assets/replace-file.js +59 -0
- package/dist/components/assets/replace-file.js.map +1 -0
- package/dist/components/builder/blocks-model.d.ts +14 -26
- package/dist/components/builder/blocks-model.js +35 -41
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +7 -3
- package/dist/components/builder/blocks-panel.js +6 -5
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +104 -15
- package/dist/components/builder/builder-store.js +156 -31
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-body.d.ts +30 -0
- package/dist/components/builder/canvas-body.js +38 -0
- package/dist/components/builder/canvas-body.js.map +1 -0
- package/dist/components/builder/canvas-chrome-css.js +7 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +7 -7
- package/dist/components/builder/canvas-dnd.js +5 -5
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +27 -19
- package/dist/components/builder/canvas-render-context.js +42 -43
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-runs.d.ts +45 -0
- package/dist/components/builder/canvas-runs.js +51 -0
- package/dist/components/builder/canvas-runs.js.map +1 -0
- package/dist/components/builder/canvas.js +32 -5
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +15 -0
- package/dist/components/builder/clipboard-actions.js +44 -7
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +3 -15
- package/dist/components/builder/composition-field-editor.js +29 -40
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/delete-key.d.ts +5 -0
- package/dist/components/builder/delete-key.js +4 -0
- package/dist/components/builder/delete-key.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +17 -25
- package/dist/components/builder/insert-context.js +45 -92
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +3 -3
- package/dist/components/builder/page-tree.js +3 -3
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.d.ts +18 -0
- package/dist/components/builder/placement-refusal.js +46 -0
- package/dist/components/builder/placement-refusal.js.map +1 -0
- package/dist/components/builder/property-inspector.js +4 -0
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +65 -27
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +79 -1
- package/dist/components/builder/reuse.js +69 -2
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/run-inspector.d.ts +11 -0
- package/dist/components/builder/run-inspector.js +33 -0
- package/dist/components/builder/run-inspector.js.map +1 -0
- package/dist/components/cms/asset-field.d.ts +38 -1
- package/dist/components/cms/asset-field.js +182 -58
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +16 -0
- package/dist/components/cms/asset-picker-core.js +20 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.js +12 -18
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +3 -19
- package/dist/components/cms/bulk-actions.js +3 -24
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +5 -4
- package/dist/components/cms/cms-store.js +16 -36
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/detail-editor.js +0 -1
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +3 -1
- package/dist/components/cms/document-diff-view.js +12 -7
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/field-anchors.d.ts +7 -4
- package/dist/components/cms/field-anchors.js +9 -7
- package/dist/components/cms/field-anchors.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +15 -1
- package/dist/components/cms/field-control-lib.js +22 -3
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +21 -30
- package/dist/components/cms/gate-validation.js +11 -32
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/object-array-field.js +4 -4
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +7 -2
- package/dist/components/cms/prop-fields.js +13 -6
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +20 -9
- package/dist/components/cms/rich-text/insert.js +25 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +1 -2
- package/dist/components/cms/use-publish-controller.js +7 -19
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/versions-pane.js +8 -1
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +3 -1
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +2 -1
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +10 -10
- package/dist/components/components/transfer.js +13 -12
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/lib/ai/apply-content.js +56 -116
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +8 -0
- package/dist/lib/ai/chat-context.js +37 -6
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -0
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +20 -5
- package/dist/lib/ai/constraints.js +125 -33
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +16 -3
- package/dist/lib/ai/fake-provider.js +22 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +2 -0
- package/dist/lib/ai/index.js +2 -0
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/leash.d.ts +34 -0
- package/dist/lib/ai/leash.js +28 -0
- package/dist/lib/ai/leash.js.map +1 -0
- package/dist/lib/ai/rich-text-input.d.ts +8 -8
- package/dist/lib/ai/rich-text-input.js +2 -1
- package/dist/lib/ai/rich-text-input.js.map +1 -1
- package/dist/lib/ai/rich-text-translate.d.ts +27 -4
- package/dist/lib/ai/rich-text-translate.js +28 -10
- package/dist/lib/ai/rich-text-translate.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +13 -13
- package/dist/lib/ai/shape.js +69 -93
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +36 -41
- package/dist/lib/ai/tools.js +83 -87
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +32 -4
- package/dist/lib/ai/translate-document.js +144 -46
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +34 -23
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +15 -12
- package/dist/lib/ai/update-fields.js +112 -188
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +116 -0
- package/dist/lib/ai/writer-form.js +429 -0
- package/dist/lib/ai/writer-form.js.map +1 -0
- package/dist/lib/assets/upload.d.ts +31 -2
- package/dist/lib/assets/upload.js +61 -6
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
- package/dist/lib/clipboard/os-clipboard.js +8 -4
- package/dist/lib/clipboard/os-clipboard.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +18 -9
- package/dist/lib/clipboard/paste-target.js +8 -7
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/field-patch.d.ts +0 -10
- package/dist/lib/cms/field-patch.js +4 -30
- package/dist/lib/cms/field-patch.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +3 -3
- package/dist/lib/collection-view/collection-view-source.d.ts +7 -3
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.js +3 -2
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +25 -31
- package/dist/lib/dnd/drop-rules.js +53 -59
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +40 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +34 -2
- package/dist/lib/i18n/messages.en.js +43 -3
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +25 -6
- package/dist/lib/operations/project-runtime-config.js +37 -10
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/publish-with-revision.d.ts +33 -1
- package/dist/lib/operations/publish-with-revision.js +32 -0
- package/dist/lib/operations/publish-with-revision.js.map +1 -1
- package/dist/lib/operations/runtime-validator.d.ts +19 -68
- package/dist/lib/operations/runtime-validator.js +12 -138
- package/dist/lib/operations/runtime-validator.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +9 -2
- package/dist/lib/operations/studio-operations.js +13 -11
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +6 -1
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.js +3 -1
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +8 -11
- package/dist/lib/workspace/index.js +12 -24
- package/dist/lib/workspace/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/components/cms/cms-composition-validators.d.ts +0 -16
- package/dist/components/cms/cms-composition-validators.js +0 -133
- package/dist/components/cms/cms-composition-validators.js.map +0 -1
- package/dist/components/cms/publish-clean.d.ts +0 -28
- package/dist/components/cms/publish-clean.js +0 -38
- package/dist/components/cms/publish-clean.js.map +0 -1
- package/dist/lib/assets/asset-placements.d.ts +0 -28
- package/dist/lib/assets/asset-placements.js +0 -83
- package/dist/lib/assets/asset-placements.js.map +0 -1
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { ImagePlus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react';
|
|
2
|
+
import { GripVertical, ImagePlus, Link2, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react';
|
|
3
3
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { assetAlt } from '@elytracms/core/persistence';
|
|
5
|
+
import { assetNode, assetSubfieldsOf, moveAssetPlacement, readAssetId, removeAssetPlacementAt, selfTypeFieldValue, } from '@elytracms/core/cms-core';
|
|
6
|
+
import { Button, Card, Code, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Field, Heading, Row, Separator, Spacer, Stack, Text, } from '@elytracms/studio/ui';
|
|
7
|
+
import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router';
|
|
6
8
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
7
9
|
import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
|
|
8
|
-
import { appendAssetSelections, applyAssetSelection, assetIdsOf,
|
|
10
|
+
import { appendAssetSelections, applyAssetSelection, assetIdsOf, singleAssetIdOf, } from '../assets/asset-selection.js';
|
|
9
11
|
import { useOptionalAssets } from '../assets/assets-store.js';
|
|
10
12
|
import { AssetThumb, FocalCropPreviews, FocalPointPicker, useAssetRecord } from '../assets/asset-thumb.js';
|
|
11
|
-
import { assetPickerBinding, splitByAccept, uploadBatchProgress } from './asset-picker-core.js';
|
|
12
|
-
import { assetMetaFieldsOf, pruneMeta } from '../assets/asset-metadata.js';
|
|
13
|
+
import { assetPickerBinding, batchAttachment, splitByAccept, uploadBatchProgress } from './asset-picker-core.js';
|
|
14
|
+
import { assetMetaFieldsOf, assetMetaSeed, pruneMeta } from '../assets/asset-metadata.js';
|
|
15
|
+
import { ReplaceFileButton } from '../assets/replace-file.js';
|
|
13
16
|
import { fieldLabel } from './cms-labels.js';
|
|
14
17
|
import { useCollectionView } from '../collection-view/collection-view-context.js';
|
|
15
18
|
import { useFieldDensity } from './field-density.js';
|
|
16
19
|
import { AssetPicker, AssetRejectedNotice } from './asset-picker.js';
|
|
17
20
|
import { FieldControl } from './field-control.js';
|
|
18
21
|
import { ObjectItemForm } from './object-array-field.js';
|
|
22
|
+
import { FieldPathContext, useFieldPath } from './field-anchors.js';
|
|
19
23
|
/**
|
|
20
24
|
* The asset-field family (EC-152 + EC-248 + EC-301) — split from
|
|
21
25
|
* `field-control.tsx` (EC-330): the {@link AssetField} control plus its
|
|
@@ -42,9 +46,19 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
42
46
|
const assets = useOptionalAssets();
|
|
43
47
|
const density = useFieldDensity();
|
|
44
48
|
const compact = density === 'compact';
|
|
49
|
+
// EC-582 / EC-572 #1: the anchor prefix of this field — a gallery placement's sub-field
|
|
50
|
+
// inputs are stamped `<field>/<entry index>/<sub-field>`, the path the validator gives
|
|
51
|
+
// their issues (a single placement's are `<field>/<sub-field>`, stamped by the form).
|
|
52
|
+
const fieldPath = useFieldPath();
|
|
45
53
|
const [open, setOpen] = useState(false);
|
|
46
|
-
|
|
54
|
+
// The placement whose image options dialog is open (EC-588 2i: every placement of a
|
|
55
|
+
// gallery has one, not only a single field's): its gallery index, or `null` for a
|
|
56
|
+
// single field's one placement.
|
|
57
|
+
const [editing, setEditing] = useState(null);
|
|
47
58
|
const [dragging, setDragging] = useState(false);
|
|
59
|
+
// Reorder by drag (EC-588 2i): the gallery index being dragged and the one under it.
|
|
60
|
+
const [moveFrom, setMoveFrom] = useState(null);
|
|
61
|
+
const [moveOver, setMoveOver] = useState(null);
|
|
48
62
|
// Names of files a drop/pick rejected against the field's `accept` (EC-301) — shown
|
|
49
63
|
// as a transient inline issue, cleared on the next valid upload. Never a silent drop.
|
|
50
64
|
const [rejected, setRejected] = useState([]);
|
|
@@ -73,22 +87,15 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
73
87
|
const batch = pendingRef.current;
|
|
74
88
|
if (!batch || !assets)
|
|
75
89
|
return;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
// EC-588 2i: the one batch-attach rule (the picker runs it too) — a `many` field
|
|
91
|
+
// appends the whole batch once it settles, IN FILE ORDER; a single field takes the
|
|
92
|
+
// first arrival.
|
|
93
|
+
const { attach, disarm } = batchAttachment(uploadBatchProgress(batch, assets, field.accept), many);
|
|
94
|
+
if (disarm)
|
|
95
|
+
pendingRef.current = null;
|
|
96
|
+
if (attach.length === 0)
|
|
80
97
|
return;
|
|
81
|
-
|
|
82
|
-
if (many) {
|
|
83
|
-
const next = appendAssetSelections(value, progress.added);
|
|
84
|
-
if (next !== value)
|
|
85
|
-
onChange(next);
|
|
86
|
-
if (progress.settled)
|
|
87
|
-
pendingRef.current = null;
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
pendingRef.current = null;
|
|
91
|
-
const next = applyAssetSelection(value, progress.added[0], false);
|
|
98
|
+
const next = many ? appendAssetSelections(value, attach) : applyAssetSelection(value, attach[0], false);
|
|
92
99
|
if (next !== value)
|
|
93
100
|
onChange(next);
|
|
94
101
|
// Reacts to the assets store changing (an upload landing in the library, the
|
|
@@ -114,6 +121,47 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
114
121
|
onChange(next);
|
|
115
122
|
});
|
|
116
123
|
const canUpload = !readOnly && assets !== null;
|
|
124
|
+
// The open dialog's placement: which asset it shows, and — when the field declares
|
|
125
|
+
// per-use sub-fields — the placement form it edits ("In this place").
|
|
126
|
+
const editTarget = (() => {
|
|
127
|
+
if (!editing)
|
|
128
|
+
return null;
|
|
129
|
+
if (editing.index === null) {
|
|
130
|
+
if (!single)
|
|
131
|
+
return null;
|
|
132
|
+
if (!hasSub)
|
|
133
|
+
return { assetId: single };
|
|
134
|
+
return {
|
|
135
|
+
assetId: single,
|
|
136
|
+
placement: {
|
|
137
|
+
fields: subFields,
|
|
138
|
+
value: assetSubfieldsOf(value),
|
|
139
|
+
onChange: (next) => onChange(assetNode(single, next)),
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (!hasSub) {
|
|
144
|
+
const aid = ids[editing.index];
|
|
145
|
+
return aid ? { assetId: aid } : null;
|
|
146
|
+
}
|
|
147
|
+
const entry = entries.filter((e) => readAssetId(e) !== undefined)[editing.index];
|
|
148
|
+
const aid = readAssetId(entry);
|
|
149
|
+
if (!aid)
|
|
150
|
+
return null;
|
|
151
|
+
const raw = entries.indexOf(entry);
|
|
152
|
+
return {
|
|
153
|
+
assetId: aid,
|
|
154
|
+
placement: {
|
|
155
|
+
fields: subFields,
|
|
156
|
+
value: assetSubfieldsOf(entry),
|
|
157
|
+
onChange: (next) => {
|
|
158
|
+
const nextEntries = entries.slice();
|
|
159
|
+
nextEntries[raw] = assetNode(aid, next);
|
|
160
|
+
onChange(nextEntries);
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
})();
|
|
117
165
|
// Drag-to-upload onto the field (EC-301): a depth counter keeps the overlay
|
|
118
166
|
// steady as the pointer crosses child element boundaries. `dragenter`/`dragleave`
|
|
119
167
|
// fire per descendant, so toggling on a bare `dragleave` blinks constantly; we
|
|
@@ -121,8 +169,10 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
121
169
|
// left the field). The overlay is pointer-events-none, so it never fires its own
|
|
122
170
|
// enter/leave to confuse the count.
|
|
123
171
|
const dragDepth = useRef(0);
|
|
172
|
+
// Only a FILE drag is an upload: a gallery reorder drag (EC-588) is not.
|
|
173
|
+
const isFileDrag = (e) => Array.from(e.dataTransfer.types).includes('Files');
|
|
124
174
|
function onDragEnter(e) {
|
|
125
|
-
if (!canUpload)
|
|
175
|
+
if (!canUpload || !isFileDrag(e))
|
|
126
176
|
return;
|
|
127
177
|
e.preventDefault();
|
|
128
178
|
dragDepth.current += 1;
|
|
@@ -130,12 +180,12 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
130
180
|
setDragging(true);
|
|
131
181
|
}
|
|
132
182
|
function onDragOver(e) {
|
|
133
|
-
if (!canUpload)
|
|
183
|
+
if (!canUpload || !isFileDrag(e))
|
|
134
184
|
return;
|
|
135
185
|
e.preventDefault(); // required so a drop is allowed
|
|
136
186
|
}
|
|
137
|
-
function onDragLeave() {
|
|
138
|
-
if (!canUpload)
|
|
187
|
+
function onDragLeave(e) {
|
|
188
|
+
if (!canUpload || !isFileDrag(e))
|
|
139
189
|
return;
|
|
140
190
|
dragDepth.current -= 1;
|
|
141
191
|
if (dragDepth.current <= 0) {
|
|
@@ -144,7 +194,7 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
144
194
|
}
|
|
145
195
|
}
|
|
146
196
|
function onDrop(e) {
|
|
147
|
-
if (!canUpload)
|
|
197
|
+
if (!canUpload || !isFileDrag(e))
|
|
148
198
|
return;
|
|
149
199
|
e.preventDefault();
|
|
150
200
|
dragDepth.current = 0;
|
|
@@ -156,17 +206,62 @@ export function AssetField({ field, value: stored, onChange, readOnly, }) {
|
|
|
156
206
|
// only be handled by the picker, never double-counted by the field too (EC-301). This
|
|
157
207
|
// is the belt to the picker's stopPropagation braces: the field cannot react at all.
|
|
158
208
|
const dragProps = canUpload && !open ? { onDragEnter, onDragOver, onDragLeave, onDrop } : {};
|
|
209
|
+
// Reorder by drag (EC-588 2i): one placement moves, every entry verbatim — core's
|
|
210
|
+
// `moveAssetPlacement`, the same index counting as the remove.
|
|
211
|
+
const reorderable = many && !readOnly && ids.length > 1;
|
|
212
|
+
function reorderProps(i) {
|
|
213
|
+
if (!reorderable)
|
|
214
|
+
return {};
|
|
215
|
+
return {
|
|
216
|
+
onDragOver: (e) => {
|
|
217
|
+
if (moveFrom === null)
|
|
218
|
+
return;
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
if (moveOver !== i)
|
|
221
|
+
setMoveOver(i);
|
|
222
|
+
},
|
|
223
|
+
onDrop: (e) => {
|
|
224
|
+
if (moveFrom === null)
|
|
225
|
+
return;
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
const next = moveAssetPlacement(value, moveFrom, i);
|
|
228
|
+
setMoveFrom(null);
|
|
229
|
+
setMoveOver(null);
|
|
230
|
+
if (next !== value)
|
|
231
|
+
onChange(next);
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function moveHandle(i) {
|
|
236
|
+
if (!reorderable)
|
|
237
|
+
return null;
|
|
238
|
+
return (_jsx("span", { draggable: true, onDragStart: (e) => {
|
|
239
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
240
|
+
e.dataTransfer.setData('text/plain', String(i));
|
|
241
|
+
setMoveFrom(i);
|
|
242
|
+
}, onDragEnd: () => {
|
|
243
|
+
setMoveFrom(null);
|
|
244
|
+
setMoveOver(null);
|
|
245
|
+
}, "aria-label": translate('cms.field.dragToReorder'), className: "cursor-grab text-muted-foreground/60 hover:text-muted-foreground active:cursor-grabbing", children: _jsx(GripVertical, { className: "size-3.5" }) }));
|
|
246
|
+
}
|
|
247
|
+
const moveTint = (i) => cn(moveFrom === i && 'opacity-50', moveOver === i && moveFrom !== null && moveFrom !== i && 'ring-2 ring-primary/50');
|
|
159
248
|
const selectButton = !readOnly ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setOpen(true), children: [_jsx(ImagePlus, {}), " ", translate('common.select')] })) : null;
|
|
160
249
|
return (_jsxs(Stack, { gap: "2", className: cn('relative rounded-lg transition-shadow', dragging && 'ring-2 ring-primary ring-offset-2 ring-offset-background'), ...dragProps, children: [many && hasSub ? (_jsxs(Stack, { gap: "2", children: [entries.length > 0 ? (_jsx(Stack, { gap: "2", children: entries.map((entry, i) => {
|
|
161
250
|
const aid = readAssetId(entry);
|
|
162
251
|
if (!aid)
|
|
163
252
|
return null;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
253
|
+
// The gallery position of this placement (readable entries only) — the
|
|
254
|
+
// index the one remove rule counts (core `removeAssetPlacementAt`).
|
|
255
|
+
const galleryIndex = entries.slice(0, i).filter((e) => readAssetId(e) !== undefined).length;
|
|
256
|
+
return (_jsx("div", { className: cn('rounded-lg', moveTint(galleryIndex)), ...reorderProps(galleryIndex), children: _jsx(FieldPathContext.Provider, { value: fieldPath === null ? null : `${fieldPath}/${i}`, children: _jsx(AssetGalleryItem, { assetId: aid, collectionId: assetCollectionId, fields: subFields, value: assetSubfieldsOf(entry), readOnly: readOnly, idPrefix: `asset-${i}`, handle: moveHandle(galleryIndex), onChangeSubs: (next) => {
|
|
257
|
+
const nextEntries = entries.slice();
|
|
258
|
+
nextEntries[i] = assetNode(aid, next);
|
|
259
|
+
onChange(nextEntries);
|
|
260
|
+
}, onEdit: assets ? () => setEditing({ index: galleryIndex }) : undefined, onRemove: readOnly ? undefined : () => onChange(removeAssetPlacementAt(value, galleryIndex)) }) }) }, `${aid}-${i}`));
|
|
261
|
+
}) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : many ? (_jsxs(Stack, { gap: "2", children: [ids.length > 0 ? (_jsx("div", { className: cn('grid gap-2', compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4'), children: ids.map((aid, i) => (_jsx("div", { className: cn('rounded-lg', moveTint(i)), ...reorderProps(i), children: _jsx(AssetChip, { assetId: aid, collectionId: assetCollectionId, handle: moveHandle(i), onEdit: assets ? () => setEditing({ index: i }) : undefined, onRemove: readOnly ? undefined : () => onChange(removeAssetPlacementAt(value, i)) }) }, `${aid}-${i}`))) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : single && hasSub ? (_jsx(Card, { className: "border-l-2 border-l-border p-3", children: _jsxs(Stack, { gap: "3", children: [_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: source.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing({ index: null }), onRemove: () => onChange(applyAssetSelection(value, null, many)) }), _jsx("div", { className: "border-t border-border/60 pt-3", children: _jsx(ObjectItemForm, { fields: subFields, value: assetSubfieldsOf(value), readOnly: readOnly, idPrefix: "asset-sub", onChange: (next) => onChange(assetNode(single, next)) }) })] }) })) : single ? (_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: source.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing({ index: null }), onRemove: () => onChange(applyAssetSelection(value, null, many)) })) : (_jsxs(Stack, { gap: "2", children: [_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) }), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })), editTarget && assets ? (_jsx(AssetOptionsDialog, { assetId: editTarget.assetId, readOnly: readOnly, open: true, onOpenChange: (next) => {
|
|
262
|
+
if (!next)
|
|
263
|
+
setEditing(null);
|
|
264
|
+
}, ...(editTarget.placement ? { placement: editTarget.placement } : {}) })) : null, _jsx(AssetPicker, { open: open, onOpenChange: setOpen, ...pickerBinding }), _jsx(AssetRejectedNotice, { rejected: rejected, accept: field.accept }), dragging ? (_jsx("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85", children: _jsxs(Row, { gap: "2", align: "center", children: [_jsx(Upload, { className: "size-4 text-primary" }), _jsx(Text, { size: "sm", className: "font-medium", children: translate('cms.asset.dropToUpload') })] }) })) : null] }));
|
|
170
265
|
}
|
|
171
266
|
/**
|
|
172
267
|
* Empty-state placeholder shown when an asset field has no selection (EC-301).
|
|
@@ -204,21 +299,23 @@ function AssetInfo({ assetId, fallbackTitle }) {
|
|
|
204
299
|
}
|
|
205
300
|
/**
|
|
206
301
|
* The selected image's alt text shown in the field (EC-301) — what an editor most
|
|
207
|
-
* wants at a glance; edited in the options dialog.
|
|
208
|
-
*
|
|
302
|
+
* wants at a glance; edited in the options dialog. Read through THE reader (EC-587),
|
|
303
|
+
* so the field shows exactly what delivery serves.
|
|
209
304
|
*/
|
|
210
305
|
function AssetAltLine({ assetId }) {
|
|
211
306
|
const record = useAssetRecord(assetId);
|
|
212
|
-
const
|
|
213
|
-
const alt = metaAlt ?? record?.alt;
|
|
307
|
+
const alt = record ? assetAlt(record) : null;
|
|
214
308
|
return (_jsxs(Row, { gap: "1", align: "baseline", className: "min-w-0", children: [_jsx(Text, { size: "xs", tone: "subtle", className: "shrink-0", children: translate('cms.asset.altLabel') }), _jsx(Text, { size: "xs", tone: alt ? 'muted' : 'subtle', className: cn('min-w-0 truncate', !alt && 'italic'), title: alt ?? undefined, children: alt || translate('cms.asset.notSet') })] }));
|
|
215
309
|
}
|
|
216
|
-
/**
|
|
217
|
-
|
|
310
|
+
/**
|
|
311
|
+
* A single asset reference: focal-aware thumbnail + title, with an optional drag
|
|
312
|
+
* handle (reorder, EC-588), image options and remove.
|
|
313
|
+
*/
|
|
314
|
+
function AssetChip({ assetId, collectionId, handle, onEdit, onRemove, }) {
|
|
218
315
|
const source = useCollectionView();
|
|
219
316
|
const record = useAssetRecord(assetId);
|
|
220
317
|
const title = source.titleOf({ collection: collectionId, id: assetId });
|
|
221
|
-
return (_jsxs("div", { className: "group relative overflow-hidden rounded-lg border border-border bg-muted/40", children: [_jsx(AssetThumb, { record: record, alt: title, className: "aspect-square w-full rounded-none border-0" }), _jsxs(Row, { gap: "1", align: "center", className: "px-2 py-1", children: [_jsx(Text, { size: "xs", tone: "subtle", className: "min-w-0 flex-1 truncate", title: title, children: title }), onRemove ? (_jsx("button", { type: "button", "aria-label": translate('common.remove'), onClick: onRemove, className: "text-muted-foreground hover:text-destructive", children: _jsx(X, { className: "size-3.5" }) })) : null] })] }));
|
|
318
|
+
return (_jsxs("div", { className: "group relative overflow-hidden rounded-lg border border-border bg-muted/40", children: [_jsx(AssetThumb, { record: record, alt: title, className: "aspect-square w-full rounded-none border-0" }), _jsxs(Row, { gap: "1", align: "center", className: "px-2 py-1", children: [handle, _jsx(Text, { size: "xs", tone: "subtle", className: "min-w-0 flex-1 truncate", title: title, children: title }), onEdit ? (_jsx("button", { type: "button", "aria-label": translate('cms.asset.imageOptions.title'), onClick: onEdit, className: "text-muted-foreground hover:text-foreground", children: _jsx(SlidersHorizontal, { className: "size-3.5" }) })) : null, onRemove ? (_jsx("button", { type: "button", "aria-label": translate('common.remove'), onClick: onRemove, className: "text-muted-foreground hover:text-destructive", children: _jsx(X, { className: "size-3.5" }) })) : null] })] }));
|
|
222
319
|
}
|
|
223
320
|
/**
|
|
224
321
|
* The selected single asset's preview row (EC-301): large focal-aware preview + info
|
|
@@ -234,22 +331,28 @@ function AssetSinglePreview({ assetId, compact, readOnly, hasLibrary, fallbackTi
|
|
|
234
331
|
* a thumbnail + filename + remove header over the placement's own sub-field form,
|
|
235
332
|
* modelled on a repeater item card so a gallery's placements read as grouped units.
|
|
236
333
|
*/
|
|
237
|
-
function AssetGalleryItem({ assetId, collectionId, fields, value, onChangeSubs, onRemove, readOnly, idPrefix, }) {
|
|
334
|
+
function AssetGalleryItem({ assetId, collectionId, fields, value, onChangeSubs, handle, onEdit, onRemove, readOnly, idPrefix, }) {
|
|
238
335
|
const source = useCollectionView();
|
|
239
336
|
const record = useAssetRecord(assetId);
|
|
240
337
|
const title = source.titleOf({ collection: collectionId, id: assetId });
|
|
241
|
-
return (_jsxs(Card, { className: "border-l-2 border-l-border p-0", children: [_jsxs(Row, { gap: "2", align: "center", className: "border-b border-border/60 bg-muted/30 px-2 py-1.5", children: [_jsx(AssetThumb, { record: record, alt: title, className: "size-10 shrink-0" }), _jsx(Text, { size: "sm", className: "min-w-0 flex-1 truncate font-medium", title: title, children: title }), onRemove ? (_jsx(Button, { variant: "ghost", size: "sm", "aria-label": translate('common.remove'), onClick: onRemove, children: _jsx(Trash2, { className: "size-3.5" }) })) : null] }), _jsx("div", { className: "px-3 pb-3", children: _jsx(ObjectItemForm, { fields: fields, value: value, onChange: onChangeSubs, readOnly: readOnly, idPrefix: idPrefix }) })] }));
|
|
338
|
+
return (_jsxs(Card, { className: "border-l-2 border-l-border p-0", children: [_jsxs(Row, { gap: "2", align: "center", className: "border-b border-border/60 bg-muted/30 px-2 py-1.5", children: [handle, _jsx(AssetThumb, { record: record, alt: title, className: "size-10 shrink-0" }), _jsx(Text, { size: "sm", className: "min-w-0 flex-1 truncate font-medium", title: title, children: title }), onEdit ? (_jsx(Button, { variant: "ghost", size: "sm", "aria-label": translate('cms.asset.imageOptions.title'), onClick: onEdit, children: _jsx(SlidersHorizontal, { className: "size-3.5" }) })) : null, onRemove ? (_jsx(Button, { variant: "ghost", size: "sm", "aria-label": translate('common.remove'), onClick: onRemove, children: _jsx(Trash2, { className: "size-3.5" }) })) : null] }), _jsx("div", { className: "px-3 pb-3", children: _jsx(ObjectItemForm, { fields: fields, value: value, onChange: onChangeSubs, readOnly: readOnly, idPrefix: idPrefix }) })] }));
|
|
242
339
|
}
|
|
243
340
|
/**
|
|
244
|
-
* The image options dialog (EC-301): the
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
341
|
+
* The image options dialog (EC-301 → EC-588, ratified 2i): the two schemas an image
|
|
342
|
+
* has, shown apart (`EC-565` § "2i widened").
|
|
343
|
+
* - **In this place** — the PLACEMENT schema: the asset field's (or prop's) per-use
|
|
344
|
+
* sub-fields (a caption, a badge layout), true of this one use only; written into the
|
|
345
|
+
* field value. Present only when the field declares sub-fields.
|
|
346
|
+
* - **About this image — applies everywhere it is used** — the ASSET schema: the focal
|
|
347
|
+
* point and the `kind:'asset'` collection's fields over the asset's `meta` bag (alt,
|
|
348
|
+
* copyright, credit, …), rendered through the generic field engine documents use;
|
|
349
|
+
* persisted on the asset, so it changes every use — the Where-used panel (the asset's
|
|
350
|
+
* library page) is one click away.
|
|
351
|
+
* Asset metadata is buffered locally and committed once on close (one write); the
|
|
352
|
+
* focal point commits immediately (it drives a live visual); the placement form writes
|
|
353
|
+
* through to the field as it is edited. No-ops without a library.
|
|
251
354
|
*/
|
|
252
|
-
function AssetOptionsDialog({ assetId, readOnly, open, onOpenChange, }) {
|
|
355
|
+
function AssetOptionsDialog({ assetId, readOnly, open, onOpenChange, placement, }) {
|
|
253
356
|
const source = useCollectionView();
|
|
254
357
|
const assets = useOptionalAssets();
|
|
255
358
|
const record = useAssetRecord(assetId);
|
|
@@ -260,7 +363,7 @@ function AssetOptionsDialog({ assetId, readOnly, open, onOpenChange, }) {
|
|
|
260
363
|
}, [source]);
|
|
261
364
|
// Local edit buffers, seeded once per open from the record (so an external edit
|
|
262
365
|
// still wins between opens). Metadata is non-localized in v1 (the asset
|
|
263
|
-
// resolution path is locale-agnostic) — localized asset metadata is
|
|
366
|
+
// resolution path is locale-agnostic) — localized asset metadata is EC-565 2a.
|
|
264
367
|
const [meta, setMeta] = useState({});
|
|
265
368
|
const [focal, setFocal] = useState(undefined);
|
|
266
369
|
const dirty = useRef(false);
|
|
@@ -272,12 +375,10 @@ function AssetOptionsDialog({ assetId, readOnly, open, onOpenChange, }) {
|
|
|
272
375
|
}
|
|
273
376
|
if (seeded.current || !record)
|
|
274
377
|
return;
|
|
275
|
-
// Seed from the meta bag
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
const seedMeta = { ...(record.alt !== undefined ? { alt: record.alt } : {}), ...(record.meta ?? {}) };
|
|
280
|
-
setMeta(seedMeta);
|
|
378
|
+
// Seed from the meta bag with a legacy `alt` column lifted in (EC-587,
|
|
379
|
+
// `assetMetaSeed`): a not-yet-migrated asset shows its alt, and the save writes it
|
|
380
|
+
// to `meta.alt` and clears the column (`assetMetadataWrite`).
|
|
381
|
+
setMeta(assetMetaSeed(record));
|
|
281
382
|
setFocal(record.focalPoint);
|
|
282
383
|
dirty.current = false;
|
|
283
384
|
seeded.current = true;
|
|
@@ -302,6 +403,29 @@ function AssetOptionsDialog({ assetId, readOnly, open, onOpenChange, }) {
|
|
|
302
403
|
setFocal(point ?? undefined);
|
|
303
404
|
void assets.updateMetadata(assetId, { focalPoint: point });
|
|
304
405
|
}
|
|
305
|
-
|
|
406
|
+
// EC-568: Replace file, from the dialog. Pending metadata edits are committed FIRST, so
|
|
407
|
+
// the buffer cannot overwrite what the new file brings; the focal point is kept and the
|
|
408
|
+
// dialog says to check it (the ticket's recommendation for open question 1).
|
|
409
|
+
const [replaced, setReplaced] = useState(false);
|
|
410
|
+
const replaceAction = record ? (_jsx(ReplaceFileButton, { record: record, disabled: readOnly, beforeReplace: async () => {
|
|
411
|
+
if (assets && dirty.current) {
|
|
412
|
+
dirty.current = false;
|
|
413
|
+
await assets.updateMetadata(assetId, { meta: pruneMeta(meta) });
|
|
414
|
+
}
|
|
415
|
+
}, onReplaced: (asset) => {
|
|
416
|
+
setMeta(assetMetaSeed(asset));
|
|
417
|
+
setFocal(asset.focalPoint);
|
|
418
|
+
dirty.current = false;
|
|
419
|
+
setReplaced(true);
|
|
420
|
+
} })) : null;
|
|
421
|
+
const whereUsed = source.projectId !== undefined ? (_jsxs(Link, { to: STUDIO_PROJECT_ROUTE, params: { projectId: source.projectId, _splat: studioPath.asset(assetId) }, className: "inline-flex items-center gap-1 text-xs text-primary hover:underline", children: [_jsx(Link2, { className: "size-3" }), " ", translate('cms.asset.imageOptions.whereUsed')] })) : null;
|
|
422
|
+
return (_jsx(Dialog, { open: open, onOpenChange: handleOpenChange, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('cms.asset.imageOptions.title') }), _jsx(DialogDescription, { children: translate('cms.asset.imageOptions.description') })] }), _jsx(AssetOptionsSections, { record: record, readOnly: readOnly, metaFields: metaFields, meta: meta, onMetaField: setField, focal: focal, onFocalCommit: commitFocal, onFocalPreview: setFocal, whereUsed: whereUsed, replaceAction: replaceAction, replaced: replaced, ...(placement ? { placement } : {}) })] }) }));
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* The two sections of the image options dialog (EC-588 2i), stateless — exported so a
|
|
426
|
+
* node smoke render can pin the split (the dialog itself portals, `null` in node).
|
|
427
|
+
*/
|
|
428
|
+
export function AssetOptionsSections({ record, readOnly, metaFields, meta, onMetaField, focal, onFocalCommit, onFocalPreview, placement, whereUsed, replaceAction, replaced, }) {
|
|
429
|
+
return (_jsxs(Stack, { gap: "4", children: [placement && placement.fields.length > 0 ? (_jsxs(Stack, { gap: "2", "data-ec-asset-options": "placement", children: [_jsxs(Stack, { gap: "1", children: [_jsx(Heading, { level: 3, size: "h4", children: translate('cms.asset.imageOptions.placementTitle') }), _jsx(Text, { size: "xs", tone: "subtle", children: translate('cms.asset.imageOptions.placementHint') })] }), _jsx(ObjectItemForm, { fields: placement.fields, value: placement.value, readOnly: readOnly, idPrefix: "asset-options-placement", onChange: placement.onChange }), _jsx(Separator, {})] })) : null, _jsxs(Stack, { gap: "3", "data-ec-asset-options": "asset", children: [_jsxs(Stack, { gap: "1", children: [_jsxs(Row, { align: "center", children: [_jsx(Heading, { level: 3, size: "h4", children: translate('cms.asset.imageOptions.assetTitle') }), _jsx(Spacer, {}), whereUsed] }), _jsx(Text, { size: "xs", tone: "subtle", children: translate('cms.asset.imageOptions.assetHint') }), replaceAction ? _jsx(Row, { gap: "2", align: "center", wrap: true, children: replaceAction }) : null] }), _jsxs(Stack, { gap: "2", children: [_jsxs(Row, { align: "center", children: [_jsx(Text, { size: "sm", className: "font-medium", children: translate('cms.asset.focalPoint') }), _jsx(Spacer, {}), focal && !readOnly ? (_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => onFocalCommit(null), children: [_jsx(RotateCcw, {}), " ", translate('cms.asset.resetToCentre')] })) : null] }), _jsx(FocalPointPicker, { record: record, point: focal, onChange: onFocalCommit, onPreview: onFocalPreview, disabled: readOnly }), replaced ? (_jsx(Text, { size: "xs", className: "font-medium", "data-ec-asset-options": "replaced", children: translate('cms.asset.replacedCheckFocal') })) : null, _jsx(Text, { size: "xs", tone: "subtle", children: translate('cms.asset.focalPointHint') }), _jsx(FocalCropPreviews, { record: record, point: focal })] }), metaFields.map((f) => (_jsx(Field, { label: fieldLabel(f), hint: localizedLabel(f.form?.description) || undefined, children: _jsx(FieldControl, { id: `asset-meta-${f.name}`, field: f, value: meta[f.name], readOnly: readOnly, onChange: (v) => onMetaField(f.name, v) }) }, f.name))), metaFields.length === 0 ? (_jsxs(Text, { size: "sm", tone: "subtle", children: ["No editable metadata fields are declared on the asset collection. Add fields (e.g.", _jsx(Code, { tone: "muted", children: " alt" }), ", ", _jsx(Code, { tone: "muted", children: "copyright" }), ") to it in code."] })) : null] })] }));
|
|
306
430
|
}
|
|
307
431
|
//# sourceMappingURL=asset-field.js.map
|