@elytracms/studio 0.0.77 → 0.0.79
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/contract-parity.test.ts +238 -0
- package/convex/tsconfig.json +3 -1
- package/dist/components/app-views/default-modules.js +5 -1
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +12 -11
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +11 -5
- package/dist/components/builder/blocks-panel.js +18 -24
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +22 -0
- package/dist/components/builder/canvas-render-context.js +65 -9
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +25 -0
- package/dist/components/builder/reuse.js +11 -2
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/cms/asset-field.js +52 -19
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +40 -0
- package/dist/components/cms/asset-picker-core.js +21 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.d.ts +11 -2
- package/dist/components/cms/asset-picker.js +34 -13
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/cell.js +2 -1
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +8 -1
- package/dist/components/cms/cms-store.js +8 -7
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +8 -2
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/date-input-core.d.ts +64 -0
- package/dist/components/cms/date-input-core.js +78 -0
- package/dist/components/cms/date-input-core.js.map +1 -0
- package/dist/components/cms/date-input.d.ts +43 -0
- package/dist/components/cms/date-input.js +109 -0
- package/dist/components/cms/date-input.js.map +1 -0
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/document-diff-view.js +2 -1
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-staging.d.ts +15 -4
- package/dist/components/cms/document-staging.js +26 -15
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/editable-cell.js +39 -5
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-control.js +38 -7
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.js +4 -1
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/slug-auto-derive.d.ts +41 -0
- package/dist/components/cms/slug-auto-derive.js +14 -0
- package/dist/components/cms/slug-auto-derive.js.map +1 -0
- package/dist/components/components/transfer-dialog.js +3 -1
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/project-session.d.ts +44 -22
- package/dist/components/project-session.js +72 -36
- package/dist/components/project-session.js.map +1 -1
- package/dist/lib/assets/asset-url.d.ts +20 -0
- package/dist/lib/assets/asset-url.js +25 -0
- package/dist/lib/assets/asset-url.js.map +1 -0
- package/dist/lib/assets/browser-blob-storage.d.ts +80 -0
- package/dist/lib/assets/browser-blob-storage.js +86 -0
- package/dist/lib/assets/browser-blob-storage.js.map +1 -0
- package/dist/lib/assets/index.d.ts +2 -0
- package/dist/lib/assets/index.js +2 -0
- package/dist/lib/assets/index.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +20 -1
- package/dist/lib/data/collection-content.js +51 -1
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +74 -0
- package/dist/lib/data/content-session-refresh.js +138 -0
- package/dist/lib/data/content-session-refresh.js.map +1 -0
- package/dist/lib/data/index.d.ts +2 -0
- package/dist/lib/data/index.js +2 -0
- package/dist/lib/data/index.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +22 -0
- package/dist/lib/data/playground-unload-guard.js +73 -0
- package/dist/lib/data/playground-unload-guard.js.map +1 -0
- package/dist/lib/i18n/messages.de.js +3 -1
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +2 -0
- package/dist/lib/i18n/messages.en.js +5 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +3 -3
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/index.js +6 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/ui/input.d.ts +8 -3
- package/dist/ui/input.js +6 -1
- package/dist/ui/input.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Input } from '@elytracms/studio/ui';
|
|
4
|
+
import { dateCommitDecision, dateInputValue, parseDateEntry, storedDateValue, } from './date-input-core.js';
|
|
5
|
+
/**
|
|
6
|
+
* EC-463 — the studio's ONE `date` control: the document form, the inline table
|
|
7
|
+
* cell, and the filter/bulk scalar editor all render this, so the correctness
|
|
8
|
+
* rules below hold everywhere a date is entered.
|
|
9
|
+
*
|
|
10
|
+
* It is still a native `<input type="date">` (calendar picker, localized segment
|
|
11
|
+
* order, native keyboard handling — no bespoke date picker to maintain), wrapped
|
|
12
|
+
* with the three things the raw element does not give a CMS:
|
|
13
|
+
*
|
|
14
|
+
* 1. **A local draft.** The input is driven by its own state, not straight off
|
|
15
|
+
* the document. An autosave round-trip, a query invalidation or any other
|
|
16
|
+
* re-render arriving mid-entry can no longer rewrite `value` and blow away the
|
|
17
|
+
* half-typed segments (live-reproduced: `2026-01-18` snapping back to
|
|
18
|
+
* `2026-01-02` when a re-render landed between two keystrokes). External
|
|
19
|
+
* updates — locale switch, version restore, an AI draft apply — still land,
|
|
20
|
+
* they just wait until the control is not focused.
|
|
21
|
+
*
|
|
22
|
+
* 2. **Settle-gated commits.** Chrome fires `change` on every segment
|
|
23
|
+
* transition, so typing a year emits the complete dates `0002-…`, `0020-…`,
|
|
24
|
+
* `0202-…` before `2026-…`, and clearing a segment emits `""`. Those never
|
|
25
|
+
* reach the document now ({@link dateCommitDecision}); blur and Enter commit
|
|
26
|
+
* whatever is there, so nothing is unreachable.
|
|
27
|
+
*
|
|
28
|
+
* 3. **An insert path that works.** A native date input ignores `insertText`
|
|
29
|
+
* entirely, which is why pasting `2026-09-18` does nothing and why every
|
|
30
|
+
* documented automation verb (`fill`, `type`, `keyboard inserttext`,
|
|
31
|
+
* `find label … fill`) reported success while silently clearing or ignoring
|
|
32
|
+
* the field. Chrome still DISPATCHES `beforeinput`/`paste`, so this control
|
|
33
|
+
* listens and applies an unambiguous ISO date itself — humans get working
|
|
34
|
+
* paste, agent editors get a working `fill`.
|
|
35
|
+
*/
|
|
36
|
+
export function DateInput({ id, value, onChange, disabled = false, className, ariaLabel, testId, autoFocus = false, }) {
|
|
37
|
+
const incoming = dateInputValue(value);
|
|
38
|
+
const [draft, setDraft] = useState(incoming);
|
|
39
|
+
const ref = useRef(null);
|
|
40
|
+
const focusedRef = useRef(false);
|
|
41
|
+
// What we last handed to `onChange`, so a re-render carrying our own value
|
|
42
|
+
// back does not look like an external change (and we never re-emit it).
|
|
43
|
+
const committedRef = useRef(incoming);
|
|
44
|
+
const draftRef = useRef(draft);
|
|
45
|
+
draftRef.current = draft;
|
|
46
|
+
const onChangeRef = useRef(onChange);
|
|
47
|
+
onChangeRef.current = onChange;
|
|
48
|
+
// Accept external updates only while the editor is not mid-entry.
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (focusedRef.current)
|
|
51
|
+
return;
|
|
52
|
+
if (incoming === committedRef.current)
|
|
53
|
+
return;
|
|
54
|
+
committedRef.current = incoming;
|
|
55
|
+
setDraft(incoming);
|
|
56
|
+
}, [incoming]);
|
|
57
|
+
function commit(next, reason) {
|
|
58
|
+
if (!dateCommitDecision({ draft: next, reason }).commit)
|
|
59
|
+
return;
|
|
60
|
+
if (next === committedRef.current)
|
|
61
|
+
return;
|
|
62
|
+
committedRef.current = next;
|
|
63
|
+
onChangeRef.current(storedDateValue(next));
|
|
64
|
+
}
|
|
65
|
+
// `beforeinput` / `paste` are dispatched on a date input but never applied by
|
|
66
|
+
// it. Native listeners (not React's synthetic `onBeforeInput`) so the
|
|
67
|
+
// `preventDefault` and the value write are unambiguous.
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const el = ref.current;
|
|
70
|
+
if (!el)
|
|
71
|
+
return;
|
|
72
|
+
function apply(text, event) {
|
|
73
|
+
if (!text)
|
|
74
|
+
return;
|
|
75
|
+
const parsed = parseDateEntry(text);
|
|
76
|
+
if (parsed === null)
|
|
77
|
+
return;
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
const input = ref.current;
|
|
80
|
+
if (input)
|
|
81
|
+
input.value = parsed;
|
|
82
|
+
setDraft(parsed);
|
|
83
|
+
// A whole value arriving at once is settled by definition.
|
|
84
|
+
commit(parsed, 'settle');
|
|
85
|
+
}
|
|
86
|
+
const onBeforeInput = (event) => apply(event.data, event);
|
|
87
|
+
const onPaste = (event) => apply(event.clipboardData?.getData('text'), event);
|
|
88
|
+
el.addEventListener('beforeinput', onBeforeInput);
|
|
89
|
+
el.addEventListener('paste', onPaste);
|
|
90
|
+
return () => {
|
|
91
|
+
el.removeEventListener('beforeinput', onBeforeInput);
|
|
92
|
+
el.removeEventListener('paste', onPaste);
|
|
93
|
+
};
|
|
94
|
+
// `commit` reads only refs, so one subscription lasts the control's life.
|
|
95
|
+
}, []);
|
|
96
|
+
return (_jsx(Input, { ref: ref, id: id, type: "date", disabled: disabled, autoFocus: autoFocus, "aria-label": ariaLabel, "data-testid": testId, className: className, value: draft, onFocus: () => {
|
|
97
|
+
focusedRef.current = true;
|
|
98
|
+
}, onChange: (event) => {
|
|
99
|
+
setDraft(event.target.value);
|
|
100
|
+
commit(event.target.value, 'edit');
|
|
101
|
+
}, onBlur: () => {
|
|
102
|
+
focusedRef.current = false;
|
|
103
|
+
commit(draftRef.current, 'settle');
|
|
104
|
+
}, onKeyDown: (event) => {
|
|
105
|
+
if (event.key === 'Enter')
|
|
106
|
+
commit(draftRef.current, 'settle');
|
|
107
|
+
} }));
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=date-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-input.js","sourceRoot":"","sources":["../../../src/components/cms/date-input.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { Input } from '@elytracms/studio/ui'\nimport {\n dateCommitDecision,\n dateInputValue,\n parseDateEntry,\n storedDateValue,\n} from './date-input-core'\nimport type { DateCommitReason } from './date-input-core'\n\n/**\n * EC-463 — the studio's ONE `date` control: the document form, the inline table\n * cell, and the filter/bulk scalar editor all render this, so the correctness\n * rules below hold everywhere a date is entered.\n *\n * It is still a native `<input type=\"date\">` (calendar picker, localized segment\n * order, native keyboard handling — no bespoke date picker to maintain), wrapped\n * with the three things the raw element does not give a CMS:\n *\n * 1. **A local draft.** The input is driven by its own state, not straight off\n * the document. An autosave round-trip, a query invalidation or any other\n * re-render arriving mid-entry can no longer rewrite `value` and blow away the\n * half-typed segments (live-reproduced: `2026-01-18` snapping back to\n * `2026-01-02` when a re-render landed between two keystrokes). External\n * updates — locale switch, version restore, an AI draft apply — still land,\n * they just wait until the control is not focused.\n *\n * 2. **Settle-gated commits.** Chrome fires `change` on every segment\n * transition, so typing a year emits the complete dates `0002-…`, `0020-…`,\n * `0202-…` before `2026-…`, and clearing a segment emits `\"\"`. Those never\n * reach the document now ({@link dateCommitDecision}); blur and Enter commit\n * whatever is there, so nothing is unreachable.\n *\n * 3. **An insert path that works.** A native date input ignores `insertText`\n * entirely, which is why pasting `2026-09-18` does nothing and why every\n * documented automation verb (`fill`, `type`, `keyboard inserttext`,\n * `find label … fill`) reported success while silently clearing or ignoring\n * the field. Chrome still DISPATCHES `beforeinput`/`paste`, so this control\n * listens and applies an unambiguous ISO date itself — humans get working\n * paste, agent editors get a working `fill`.\n */\nexport function DateInput({\n id,\n value,\n onChange,\n disabled = false,\n className,\n ariaLabel,\n testId,\n autoFocus = false,\n}: {\n id?: string\n /** The stored value (an ISO instant, a date-only string, or absent). */\n value: unknown\n /** Called with the storage-shaped value, or `undefined` when cleared. */\n onChange: (stored: string | undefined) => void\n disabled?: boolean\n className?: string\n ariaLabel?: string\n testId?: string\n autoFocus?: boolean\n}) {\n const incoming = dateInputValue(value)\n const [draft, setDraft] = useState(incoming)\n const ref = useRef<HTMLInputElement>(null)\n const focusedRef = useRef(false)\n // What we last handed to `onChange`, so a re-render carrying our own value\n // back does not look like an external change (and we never re-emit it).\n const committedRef = useRef(incoming)\n const draftRef = useRef(draft)\n draftRef.current = draft\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n\n // Accept external updates only while the editor is not mid-entry.\n useEffect(() => {\n if (focusedRef.current) return\n if (incoming === committedRef.current) return\n committedRef.current = incoming\n setDraft(incoming)\n }, [incoming])\n\n function commit(next: string, reason: DateCommitReason) {\n if (!dateCommitDecision({ draft: next, reason }).commit) return\n if (next === committedRef.current) return\n committedRef.current = next\n onChangeRef.current(storedDateValue(next))\n }\n\n // `beforeinput` / `paste` are dispatched on a date input but never applied by\n // it. Native listeners (not React's synthetic `onBeforeInput`) so the\n // `preventDefault` and the value write are unambiguous.\n useEffect(() => {\n const el = ref.current\n if (!el) return\n function apply(text: string | null | undefined, event: Event): void {\n if (!text) return\n const parsed = parseDateEntry(text)\n if (parsed === null) return\n event.preventDefault()\n const input = ref.current\n if (input) input.value = parsed\n setDraft(parsed)\n // A whole value arriving at once is settled by definition.\n commit(parsed, 'settle')\n }\n const onBeforeInput = (event: Event) => apply((event as InputEvent).data, event)\n const onPaste = (event: Event) =>\n apply((event as ClipboardEvent).clipboardData?.getData('text'), event)\n el.addEventListener('beforeinput', onBeforeInput)\n el.addEventListener('paste', onPaste)\n return () => {\n el.removeEventListener('beforeinput', onBeforeInput)\n el.removeEventListener('paste', onPaste)\n }\n // `commit` reads only refs, so one subscription lasts the control's life.\n }, [])\n\n return (\n <Input\n ref={ref}\n id={id}\n type=\"date\"\n disabled={disabled}\n autoFocus={autoFocus}\n aria-label={ariaLabel}\n data-testid={testId}\n className={className}\n value={draft}\n onFocus={() => {\n focusedRef.current = true\n }}\n onChange={(event) => {\n setDraft(event.target.value)\n commit(event.target.value, 'edit')\n }}\n onBlur={() => {\n focusedRef.current = false\n commit(draftRef.current, 'settle')\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commit(draftRef.current, 'settle')\n }}\n />\n )\n}\n"]}
|