@script-it/json-views-react 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +301 -0
- package/THIRD_PARTY_NOTICES.txt +4917 -0
- package/dist/browser-device.d.ts +21 -0
- package/dist/browser-device.js +22 -0
- package/dist/file-content-edit-state.d.ts +10 -0
- package/dist/file-content-edit-state.js +1 -0
- package/dist/file-content-view.d.ts +8 -0
- package/dist/file-content-view.js +7 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -0
- package/dist/json/copy-json.d.ts +15 -0
- package/dist/json/copy-json.js +52 -0
- package/dist/json/html-preview.d.ts +5 -0
- package/dist/json/html-preview.js +12 -0
- package/dist/json/html-view/native-control.d.ts +44 -0
- package/dist/json/html-view/native-control.js +96 -0
- package/dist/json/html-view/settings.d.ts +2 -0
- package/dist/json/html-view/settings.js +58 -0
- package/dist/json/html-view/template.d.ts +12 -0
- package/dist/json/html-view/template.js +55 -0
- package/dist/json/html-view.d.ts +9 -0
- package/dist/json/html-view.js +163 -0
- package/dist/json/json-content.d.ts +61 -0
- package/dist/json/json-content.js +433 -0
- package/dist/json/json-source-editor.d.ts +36 -0
- package/dist/json/json-source-editor.js +203 -0
- package/dist/json/json-view.d.ts +3 -0
- package/dist/json/json-view.js +322 -0
- package/dist/json/kanban-view.d.ts +20 -0
- package/dist/json/kanban-view.js +328 -0
- package/dist/json/long-text.d.ts +3 -0
- package/dist/json/long-text.js +6 -0
- package/dist/json/record-view.d.ts +38 -0
- package/dist/json/record-view.js +85 -0
- package/dist/json/row-mutations.d.ts +7 -0
- package/dist/json/row-mutations.js +93 -0
- package/dist/json/schema-value.d.ts +57 -0
- package/dist/json/schema-value.js +294 -0
- package/dist/json/source-default.d.ts +1 -0
- package/dist/json/source-default.js +1 -0
- package/dist/json/source-edit-capabilities.d.ts +8 -0
- package/dist/json/source-edit-capabilities.js +22 -0
- package/dist/json/source-literals.d.ts +15 -0
- package/dist/json/source-literals.js +24 -0
- package/dist/json/source-recovery.d.ts +32 -0
- package/dist/json/source-recovery.js +38 -0
- package/dist/json/source-selection.d.ts +3 -0
- package/dist/json/source-selection.js +16 -0
- package/dist/json/table-view.d.ts +20 -0
- package/dist/json/table-view.js +300 -0
- package/dist/json/use-kanban-card-drag.d.ts +13 -0
- package/dist/json/use-kanban-card-drag.js +255 -0
- package/dist/json/use-kanban-column-drag.d.ts +14 -0
- package/dist/json/use-kanban-column-drag.js +179 -0
- package/dist/json/view-diagnostics.d.ts +4 -0
- package/dist/json/view-diagnostics.js +26 -0
- package/dist/json/view-model.d.ts +27 -0
- package/dist/json/view-model.js +180 -0
- package/dist/json/view-save-status.d.ts +12 -0
- package/dist/json/view-save-status.js +38 -0
- package/dist/json/view-settings.d.ts +36 -0
- package/dist/json/view-settings.js +661 -0
- package/dist/json/view-types.d.ts +47 -0
- package/dist/json/view-types.js +4 -0
- package/dist/json/viewers/general/general-json-view.d.ts +46 -0
- package/dist/json/viewers/general/general-json-view.js +163 -0
- package/dist/json/viewers/general/json-flatten.d.ts +33 -0
- package/dist/json/viewers/general/json-flatten.js +51 -0
- package/dist/json/viewers/general/json-node-view.d.ts +43 -0
- package/dist/json/viewers/general/json-node-view.js +215 -0
- package/dist/lib/clipboard.d.ts +1 -0
- package/dist/lib/clipboard.js +24 -0
- package/dist/lib/cn.d.ts +2 -0
- package/dist/lib/cn.js +5 -0
- package/dist/lib/file-data.d.ts +7 -0
- package/dist/lib/file-data.js +5 -0
- package/dist/lib/floating-portal-selectors.d.ts +4 -0
- package/dist/lib/floating-portal-selectors.js +4 -0
- package/dist/lib/time.d.ts +24 -0
- package/dist/lib/time.js +60 -0
- package/dist/lib/ui-size.d.ts +28 -0
- package/dist/lib/ui-size.js +39 -0
- package/dist/lib/use-anchored-position.d.ts +5 -0
- package/dist/lib/use-anchored-position.js +65 -0
- package/dist/lib/use-dismiss.d.ts +31 -0
- package/dist/lib/use-dismiss.js +65 -0
- package/dist/primitives/boolean-toggle.d.ts +10 -0
- package/dist/primitives/boolean-toggle.js +11 -0
- package/dist/primitives/button.d.ts +11 -0
- package/dist/primitives/button.js +34 -0
- package/dist/primitives/checkbox.d.ts +4 -0
- package/dist/primitives/checkbox.js +8 -0
- package/dist/primitives/choice-control.d.ts +29 -0
- package/dist/primitives/choice-control.js +131 -0
- package/dist/primitives/dialog.d.ts +29 -0
- package/dist/primitives/dialog.js +25 -0
- package/dist/primitives/dropdown-menu.d.ts +37 -0
- package/dist/primitives/dropdown-menu.js +39 -0
- package/dist/primitives/inline-feedback-action.d.ts +13 -0
- package/dist/primitives/inline-feedback-action.js +6 -0
- package/dist/primitives/menu-select.d.ts +12 -0
- package/dist/primitives/menu-select.js +7 -0
- package/dist/primitives/multi-select.d.ts +18 -0
- package/dist/primitives/multi-select.js +7 -0
- package/dist/primitives/number-input.d.ts +10 -0
- package/dist/primitives/number-input.js +8 -0
- package/dist/primitives/option-pill.d.ts +13 -0
- package/dist/primitives/option-pill.js +50 -0
- package/dist/primitives/tabs.d.ts +7 -0
- package/dist/primitives/tabs.js +12 -0
- package/dist/primitives/textarea.d.ts +4 -0
- package/dist/primitives/textarea.js +8 -0
- package/dist/primitives/tooltip.d.ts +31 -0
- package/dist/primitives/tooltip.js +37 -0
- package/dist/standalone.css +113 -0
- package/dist/structured-data/atomic-value-editor.d.ts +33 -0
- package/dist/structured-data/atomic-value-editor.js +195 -0
- package/dist/structured-data/atomic-value.d.ts +3 -0
- package/dist/structured-data/atomic-value.js +6 -0
- package/dist/structured-data/column-controls.d.ts +21 -0
- package/dist/structured-data/column-controls.js +180 -0
- package/dist/structured-data/copy-json-action.d.ts +10 -0
- package/dist/structured-data/copy-json-action.js +87 -0
- package/dist/structured-data/delete-row-action.d.ts +10 -0
- package/dist/structured-data/delete-row-action.js +37 -0
- package/dist/structured-data/edit-base.d.ts +3 -0
- package/dist/structured-data/edit-base.js +8 -0
- package/dist/structured-data/filter-value.d.ts +2 -0
- package/dist/structured-data/filter-value.js +28 -0
- package/dist/structured-data/row-selection-icon.d.ts +9 -0
- package/dist/structured-data/row-selection-icon.js +6 -0
- package/dist/structured-data/structured-cell-fill-context.d.ts +6 -0
- package/dist/structured-data/structured-cell-fill-context.js +7 -0
- package/dist/structured-data/structured-data-content.d.ts +1 -0
- package/dist/structured-data/structured-data-content.js +1 -0
- package/dist/structured-data/table-types.d.ts +108 -0
- package/dist/structured-data/table-types.js +1 -0
- package/dist/structured-data/use-metadata-persistence.d.ts +15 -0
- package/dist/structured-data/use-metadata-persistence.js +18 -0
- package/dist/structured-data/use-optimistic-text-document.d.ts +22 -0
- package/dist/structured-data/use-optimistic-text-document.js +30 -0
- package/dist/structured-data/value-cell.d.ts +15 -0
- package/dist/structured-data/value-cell.js +81 -0
- package/dist/styles.css +2 -0
- package/dist/surface.d.ts +14 -0
- package/dist/surface.js +73 -0
- package/dist/tabular-data-view.d.ts +5 -0
- package/dist/tabular-data-view.js +493 -0
- package/dist/viewer-state.d.ts +72 -0
- package/dist/viewer-state.js +79 -0
- package/dist/widget-registry.d.ts +60 -0
- package/dist/widget-registry.js +126 -0
- package/dist/widgets/boolean-widget.d.ts +2 -0
- package/dist/widgets/boolean-widget.js +15 -0
- package/dist/widgets/date-picker-widget.d.ts +18 -0
- package/dist/widgets/date-picker-widget.js +265 -0
- package/dist/widgets/dropdown-widget.d.ts +10 -0
- package/dist/widgets/dropdown-widget.js +19 -0
- package/dist/widgets/multi-select-widget.d.ts +11 -0
- package/dist/widgets/multi-select-widget.js +46 -0
- package/dist/widgets/number-widget.d.ts +2 -0
- package/dist/widgets/number-widget.js +19 -0
- package/dist/widgets/read-only-field.d.ts +36 -0
- package/dist/widgets/read-only-field.js +39 -0
- package/dist/widgets/text-widget.d.ts +2 -0
- package/dist/widgets/text-widget.js +19 -0
- package/dist/widgets/textarea-widget.d.ts +2 -0
- package/dist/widgets/textarea-widget.js +34 -0
- package/dist/widgets/types.d.ts +53 -0
- package/dist/widgets/types.js +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createElement, Fragment, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { htmlRepeatKeys, JSON_VIEW_PATH_MISSING, resolveHtmlViewBinding, schemaForJsonViewPath } from '@script-it/json-views-core';
|
|
5
|
+
import { useJsonViewsPortalContainer } from '../surface.js';
|
|
6
|
+
import { useAnchoredPosition } from '../lib/use-anchored-position.js';
|
|
7
|
+
import { EditBaseContext } from '../structured-data/edit-base.js';
|
|
8
|
+
import { isLongText } from './long-text.js';
|
|
9
|
+
import { SchemaEditor } from './schema-value.js';
|
|
10
|
+
import { useJsonSourceLiterals } from './source-literals.js';
|
|
11
|
+
import { parseHtmlTemplate } from './html-view/template.js';
|
|
12
|
+
import { NativeHtmlControl } from './html-view/native-control.js';
|
|
13
|
+
const skeleton = `<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'none'; style-src 'unsafe-inline'; img-src data:; font-src 'none'; connect-src 'none'; object-src 'none'; frame-src 'none'; base-uri 'none'; form-action 'none'"><style>body{margin:0;font:14px system-ui;background:var(--jv-background);color:var(--jv-foreground)}*{box-sizing:border-box}jv-field,jv-value{display:inline}button.jv-field{font:inherit;color:inherit;background:transparent;border:0;padding:0;text-align:inherit;cursor:pointer}button.jv-field:hover{outline:1px dashed currentColor}button.jv-field:focus-visible{outline:2px solid currentColor}</style></head><body><div id="mount"></div></body></html>`;
|
|
14
|
+
export function HtmlView({ compiled, view, editing, fillHeight, onCurrentPathChange }) {
|
|
15
|
+
const frame = useRef(null), editorRef = useRef(null);
|
|
16
|
+
const [doc, setDoc] = useState(), [error, setError] = useState(''), [height, setHeight] = useState(500);
|
|
17
|
+
const [selected, setSelected] = useState();
|
|
18
|
+
const base = useContext(EditBaseContext), literals = useJsonSourceLiterals(), portal = useJsonViewsPortalContainer();
|
|
19
|
+
const anchorRef = useRef(null);
|
|
20
|
+
anchorRef.current = selected?.anchor ?? null;
|
|
21
|
+
// A bound field is laid out by the template, not by a column, so the popup
|
|
22
|
+
// takes its width from the text it covers instead of a fixed card width.
|
|
23
|
+
const position = useAnchoredPosition(Boolean(selected), anchorRef, editorRef, 'bottom', 'start', true);
|
|
24
|
+
const parsed = useMemo(() => {
|
|
25
|
+
if (!doc)
|
|
26
|
+
return { nodes: [], warnings: [] };
|
|
27
|
+
try {
|
|
28
|
+
return parseHtmlTemplate(doc, view.html ?? '');
|
|
29
|
+
}
|
|
30
|
+
catch (cause) {
|
|
31
|
+
return { nodes: [], warnings: [cause instanceof Error ? cause.message : 'Invalid HTML'] };
|
|
32
|
+
}
|
|
33
|
+
}, [doc, view.html]);
|
|
34
|
+
useEffect(() => { onCurrentPathChange?.(view.sourcePath); }, [view.sourcePath, onCurrentPathChange]);
|
|
35
|
+
useEffect(() => { setSelected(undefined); setError(''); }, [view.html, view.id]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!doc)
|
|
38
|
+
return;
|
|
39
|
+
const style = doc.createElement('style');
|
|
40
|
+
style.textContent = view.css ?? '';
|
|
41
|
+
doc.head.appendChild(style);
|
|
42
|
+
return () => style.remove();
|
|
43
|
+
}, [doc, view.css]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!doc || !frame.current)
|
|
46
|
+
return;
|
|
47
|
+
const update = () => {
|
|
48
|
+
const computed = getComputedStyle(frame.current);
|
|
49
|
+
for (const name of ['background', 'foreground', 'muted', 'border', 'accent'])
|
|
50
|
+
doc.documentElement.style.setProperty('--jv-' + name, computed.getPropertyValue('--jv-' + name));
|
|
51
|
+
doc.documentElement.style.colorScheme = frame.current.closest('[data-json-views-theme]')?.getAttribute('data-json-views-theme') ?? 'light';
|
|
52
|
+
if (!fillHeight)
|
|
53
|
+
setHeight(Math.min(2000, Math.max(200, doc.body.scrollHeight)));
|
|
54
|
+
};
|
|
55
|
+
update();
|
|
56
|
+
const observer = typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(update);
|
|
57
|
+
observer?.observe(doc.body);
|
|
58
|
+
const themeObserver = new MutationObserver(update);
|
|
59
|
+
const host = frame.current.closest('[data-json-views-theme]');
|
|
60
|
+
if (host)
|
|
61
|
+
themeObserver.observe(host, { attributes: true });
|
|
62
|
+
return () => { observer?.disconnect(); themeObserver.disconnect(); };
|
|
63
|
+
}, [doc, fillHeight]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!selected || !doc)
|
|
66
|
+
return;
|
|
67
|
+
const close = (event) => {
|
|
68
|
+
if (event.type === 'keydown' && event.key !== 'Escape')
|
|
69
|
+
return;
|
|
70
|
+
const target = event.target;
|
|
71
|
+
if (event.type !== 'keydown' && (editorRef.current?.contains(target) || selected.anchor.contains(target) || target.closest?.('[data-radix-popper-content-wrapper], [data-anchored-popup]')))
|
|
72
|
+
return;
|
|
73
|
+
setSelected(undefined);
|
|
74
|
+
if (selected.anchor.isConnected)
|
|
75
|
+
selected.anchor.focus();
|
|
76
|
+
};
|
|
77
|
+
doc.addEventListener('mousedown', close);
|
|
78
|
+
doc.addEventListener('keydown', close);
|
|
79
|
+
document.addEventListener('mousedown', close);
|
|
80
|
+
return () => { doc.removeEventListener('mousedown', close); doc.removeEventListener('keydown', close); document.removeEventListener('mousedown', close); };
|
|
81
|
+
}, [selected, doc]);
|
|
82
|
+
let expanded = 0;
|
|
83
|
+
const warnings = [...parsed.warnings];
|
|
84
|
+
const resolve = (expression, aliases) => {
|
|
85
|
+
const result = resolveHtmlViewBinding(compiled.root, expression, aliases, view.sourcePath);
|
|
86
|
+
if (result.value === JSON_VIEW_PATH_MISSING)
|
|
87
|
+
throw new Error(`Unresolved binding: ${expression}`);
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
const render = (nodes, aliases = {}) => nodes.map(node => {
|
|
91
|
+
if (++expanded > 10000)
|
|
92
|
+
throw new Error('HTML view exceeds 10,000 expanded nodes');
|
|
93
|
+
if (!node.tag)
|
|
94
|
+
return node.text;
|
|
95
|
+
try {
|
|
96
|
+
if (node.tag === 'jv-repeat') {
|
|
97
|
+
const alias = node.attrs.as ?? 'item';
|
|
98
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(alias) || Object.hasOwn(aliases, alias))
|
|
99
|
+
throw new Error(`Invalid or duplicate repeat alias: ${alias}`);
|
|
100
|
+
const resolved = resolve(node.attrs.source ?? '', aliases);
|
|
101
|
+
if (!Array.isArray(resolved.value))
|
|
102
|
+
throw new Error('jv-repeat source must be an array');
|
|
103
|
+
const keys = htmlRepeatKeys(resolved.value, node.attrs.key);
|
|
104
|
+
return resolved.value.map((_, index) => _jsx(Fragment, { children: render(node.children, { ...aliases, [alias]: [...resolved.sourcePath, index] }) }, `${node.id}:${keys[index]}`));
|
|
105
|
+
}
|
|
106
|
+
const props = { key: node.id };
|
|
107
|
+
const mapping = { class: 'className', for: 'htmlFor', tabindex: 'tabIndex', readonly: 'readOnly', maxlength: 'maxLength', viewbox: 'viewBox', preserveaspectratio: 'preserveAspectRatio', 'stroke-width': 'strokeWidth', 'text-anchor': 'textAnchor', 'font-size': 'fontSize', 'font-family': 'fontFamily' };
|
|
108
|
+
for (const [key, value] of Object.entries(node.attrs)) {
|
|
109
|
+
if (key.startsWith('jv-attr-')) {
|
|
110
|
+
const scalar = resolve(value, aliases).value;
|
|
111
|
+
if (scalar !== null && typeof scalar === 'object' || typeof scalar === 'string' && /url\s*\(/i.test(scalar))
|
|
112
|
+
throw new Error('Attribute bindings require safe scalar values');
|
|
113
|
+
props[mapping[key.slice(8)] ?? key.slice(8)] = scalar;
|
|
114
|
+
}
|
|
115
|
+
else if (!key.startsWith('jv-') && !['bind', 'source', 'as', 'key', 'value', 'checked'].includes(key))
|
|
116
|
+
props[mapping[key] ?? key] = ['disabled', 'readonly'].includes(key) ? true : value;
|
|
117
|
+
}
|
|
118
|
+
if (node.tag === 'jv-field' || node.tag === 'jv-value') {
|
|
119
|
+
const resolved = resolve(node.attrs.bind ?? '', aliases), value = resolved.value;
|
|
120
|
+
if (value !== null && typeof value === 'object')
|
|
121
|
+
throw new Error('Use jv-repeat for arrays and objects');
|
|
122
|
+
const text = literals(resolved.sourcePath) ?? (value == null ? '—' : String(value));
|
|
123
|
+
const canEdit = node.tag === 'jv-field' && editing && editing.canReplace?.(resolved.sourcePath) !== false;
|
|
124
|
+
return _jsx("span", { children: canEdit ? _jsx("button", { className: "jv-field", "aria-label": `Edit ${node.attrs.bind}`, onClick: event => setSelected({ path: resolved.sourcePath, anchor: event.currentTarget, base }), children: text }) : text }, node.id);
|
|
125
|
+
}
|
|
126
|
+
if (node.attrs['jv-bind']) {
|
|
127
|
+
if (node.tag !== 'input' && node.tag !== 'textarea')
|
|
128
|
+
throw new Error('jv-bind requires input or textarea');
|
|
129
|
+
const resolved = resolve(node.attrs['jv-bind'], aliases);
|
|
130
|
+
return _jsx(NativeHtmlControl, { tag: node.tag, props: props, path: resolved.sourcePath, value: resolved.value, editing: editing, report: setError }, node.id);
|
|
131
|
+
}
|
|
132
|
+
if (node.attrs['jv-value']) {
|
|
133
|
+
if (!['progress', 'meter'].includes(node.tag))
|
|
134
|
+
throw new Error('jv-value attribute requires progress or meter');
|
|
135
|
+
props.value = resolve(node.attrs['jv-value'], aliases).value;
|
|
136
|
+
}
|
|
137
|
+
return createElement(node.tag, props, ...render(node.children, aliases));
|
|
138
|
+
}
|
|
139
|
+
catch (cause) {
|
|
140
|
+
if (cause instanceof Error && cause.message.includes('10,000 expanded nodes'))
|
|
141
|
+
throw cause;
|
|
142
|
+
const message = `${view.name}, node ${node.id}: ${cause instanceof Error ? cause.message : 'Invalid binding'}`;
|
|
143
|
+
warnings.push(message);
|
|
144
|
+
return _jsx("span", { children: "[Binding unavailable]" }, node.id);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
let content;
|
|
148
|
+
try {
|
|
149
|
+
content = render(parsed.nodes);
|
|
150
|
+
}
|
|
151
|
+
catch (cause) {
|
|
152
|
+
content = null;
|
|
153
|
+
warnings.push(cause instanceof Error ? cause.message : 'HTML expansion failed');
|
|
154
|
+
}
|
|
155
|
+
const selectedValue = selected ? resolveHtmlViewBinding(compiled.root, '$' + selected.path.map(p => typeof p === 'number' ? `[${p}]` : `['${p.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}']`).join(''), {}, view.sourcePath).value : undefined;
|
|
156
|
+
const descriptor = selected ? schemaForJsonViewPath(compiled.schema, selected.path)?.descriptor ?? { type: typeof selectedValue === 'number' ? 'number' : typeof selectedValue === 'boolean' ? 'checkbox' : 'text' } : undefined;
|
|
157
|
+
// Text too long for one line gets the resizable multi-line control, matching
|
|
158
|
+
// how tables and records present the same value.
|
|
159
|
+
const editorDescriptor = descriptor?.type === 'text' && isLongText(selectedValue, descriptor) ? { ...descriptor, multiline: true } : descriptor;
|
|
160
|
+
return _jsxs("div", { className: "flex min-h-0 flex-1 flex-col", "data-id": "jsonView-html-view", children: [(error || warnings.length > 0) && _jsxs("div", { role: "alert", className: "border-b p-2 text-xs", children: [error || warnings.slice(0, 10).join(' · '), error && _jsx("button", { onClick: () => setError(''), className: "ml-2 underline", children: "Dismiss" })] }), _jsx("iframe", { ref: frame, title: view.name, sandbox: "allow-same-origin", srcDoc: skeleton, className: "w-full flex-1 border-0", style: { minHeight: fillHeight ? 200 : height, height: fillHeight ? '100%' : height }, onLoad: event => { setDoc(event.currentTarget.contentDocument ?? undefined); setSelected(undefined); } }), doc?.getElementById('mount') && createPortal(content, doc.getElementById('mount')), selected && editorDescriptor && editing && portal && createPortal(_jsx("div", { ref: editorRef, "data-anchored-popup": true, className: "z-[120] w-80 rounded-md border bg-card p-3 text-card-foreground shadow-lg", style: position, children: _jsx(EditBaseContext.Provider, { value: base, children: _jsx(SchemaEditor, { descriptor: editorDescriptor, disabled: editing.saving, label: String(selected.path.at(-1) ?? 'value'), variant: "default", value: selectedValue, onCancel: () => { const anchor = selected.anchor; setSelected(undefined); if (anchor.isConnected)
|
|
161
|
+
anchor.focus(); }, onCommit: async (value) => { if (selected.base !== base)
|
|
162
|
+
throw new Error('This document changed outside this editor. Cancel and reopen it before saving.'); await editing.replace(selected.path, value); } }, `${view.id}:${JSON.stringify(selected.path)}`) }) }), portal)] });
|
|
163
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Format-neutral structured document orchestration with JSON-compatible wrappers. */
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import { type JsonDocumentSave, type StructuredDocumentAdapter, type ValuePath } from '@script-it/json-views-core';
|
|
4
|
+
import type { UiSize } from '../lib/ui-size.js';
|
|
5
|
+
import type { FileContentEditState } from '../file-content-edit-state.js';
|
|
6
|
+
import { type MetadataPersistence } from '../structured-data/use-metadata-persistence.js';
|
|
7
|
+
import type { JsonViewSchemaExternalWidgetRenderer } from './schema-value.js';
|
|
8
|
+
import type { JsonViewsPresentationState } from '../viewer-state.js';
|
|
9
|
+
export type JsonViewClearBehavior = 'null' | 'remove';
|
|
10
|
+
export type { MetadataPersistence } from '../structured-data/use-metadata-persistence.js';
|
|
11
|
+
export interface ObjectRootConversionRequest {
|
|
12
|
+
reason: 'save-view' | 'save-schema' | 'save-option-colors' | 'unsupported-csv-value';
|
|
13
|
+
sourceFormat: 'json-array' | 'csv' | 'json-scalar';
|
|
14
|
+
proposedFilename: string;
|
|
15
|
+
convertedSource: string;
|
|
16
|
+
}
|
|
17
|
+
export type MetadataPersistenceRequest = (request: ObjectRootConversionRequest) => void | boolean | Promise<void | boolean>;
|
|
18
|
+
export interface StructuredDataContentProps {
|
|
19
|
+
content: string;
|
|
20
|
+
adapter: StructuredDocumentAdapter<unknown>;
|
|
21
|
+
documentId?: string;
|
|
22
|
+
revision?: string;
|
|
23
|
+
theme?: 'light' | 'dark' | 'inherit';
|
|
24
|
+
portalContainer?: HTMLElement | null;
|
|
25
|
+
/** Initial visual state, restored on mount or documentId change. Cache separately from JSON metadata. */
|
|
26
|
+
presentationState?: JsonViewsPresentationState;
|
|
27
|
+
onPresentationStateChange?: (state: JsonViewsPresentationState) => void;
|
|
28
|
+
metadata?: unknown;
|
|
29
|
+
metadataPersistence?: MetadataPersistence;
|
|
30
|
+
onMetadataChange?: (metadata: Record<string, unknown>) => Promise<void>;
|
|
31
|
+
onRequestMetadataPersistence?: MetadataPersistenceRequest;
|
|
32
|
+
/** Use host confirmation when the persistence callback presents its own conversion dialog. */
|
|
33
|
+
metadataConversionConfirmation?: 'viewer' | 'host';
|
|
34
|
+
path?: string;
|
|
35
|
+
uiSize?: UiSize;
|
|
36
|
+
fillHeight?: boolean;
|
|
37
|
+
edit?: FileContentEditState;
|
|
38
|
+
/** Persist visual edits. Omit for a read-only source. Takes precedence over edit.onCommitContent. */
|
|
39
|
+
onSave?: JsonDocumentSave;
|
|
40
|
+
isSaving?: boolean;
|
|
41
|
+
/** Host dirty state displayed in the embedded source toolbar. */
|
|
42
|
+
isDirty?: boolean;
|
|
43
|
+
/** Host save error displayed in the embedded source toolbar. */
|
|
44
|
+
saveError?: string;
|
|
45
|
+
/** Controlled raw-source mode. Omit to let the embedded frame own its source toggle. */
|
|
46
|
+
sourceVisible?: boolean;
|
|
47
|
+
onSourceVisibleChange?: (visible: boolean) => void;
|
|
48
|
+
clearBehavior?: JsonViewClearBehavior;
|
|
49
|
+
onDraftRowsChange?: (paths: readonly ValuePath[]) => void;
|
|
50
|
+
renderMarkdown?: (content: string) => ReactNode;
|
|
51
|
+
renderExternalWidget?: JsonViewSchemaExternalWidgetRenderer;
|
|
52
|
+
/** @deprecated Use onRequestMetadataPersistence. */
|
|
53
|
+
allowRootArrayWrapping?: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface JSONContentProps extends Omit<StructuredDataContentProps, 'adapter'> {
|
|
56
|
+
}
|
|
57
|
+
export interface CSVContentProps extends Omit<StructuredDataContentProps, 'adapter'> {
|
|
58
|
+
}
|
|
59
|
+
export declare function StructuredDataContent(props: StructuredDataContentProps): import("react").JSX.Element;
|
|
60
|
+
export declare function JSONContent(props: JSONContentProps): import("react").JSX.Element;
|
|
61
|
+
export declare function CSVContent(props: CSVContentProps): import("react").JSX.Element;
|