@kubohiroya/turbowarp-extension-data-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/dist/build-info.json +7 -2
- package/dist/descendants.js +11 -2
- package/dist/index.d.ts +1 -0
- package/dist/json-value.d.ts +4 -0
- package/dist/json-value.js +12 -0
- package/dist/prepared-save.d.ts +5 -0
- package/dist/prepared-save.js +47 -0
- package/dist/prepared-types.d.ts +13 -0
- package/dist/prepared-types.js +1 -0
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +57 -45
- package/dist/rjsf.d.ts +25 -1
- package/dist/rjsf.js +70 -6
- package/dist/schema-compose.d.ts +6 -0
- package/dist/schema-compose.js +103 -0
- package/dist/schema-engine.d.ts +18 -0
- package/dist/schema-engine.js +298 -0
- package/dist/schema-json.d.ts +10 -0
- package/dist/schema-json.js +68 -0
- package/dist/schema-types.d.ts +161 -0
- package/dist/schema-types.js +2 -0
- package/dist/schema-view.d.ts +7 -0
- package/dist/schema-view.js +102 -0
- package/dist/schema.d.ts +8 -0
- package/dist/schema.js +86 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +18 -5
- package/dist/source-document.d.ts +3 -0
- package/dist/source-document.js +415 -0
- package/dist/source-editor.d.ts +13 -0
- package/dist/source-editor.js +235 -0
- package/dist/source-schema-hints.d.ts +3 -0
- package/dist/source-schema-hints.js +13 -0
- package/dist/source-types.d.ts +47 -0
- package/dist/source-types.js +1 -0
- package/dist/source-view.d.ts +3 -0
- package/dist/source-view.js +318 -0
- package/dist/types.d.ts +2 -4
- package/dist/webpack4/index.js +4 -2
- package/dist/webpack4/metafile.json +9551 -8644
- package/dist/webpack4/react.js +2 -1
- package/dist/webpack4/react.js.map +1 -1
- package/dist/webpack4/rjsf.js +15014 -20975
- package/dist/webpack4/rjsf.js.map +4 -4
- package/dist/webpack4/schema.js +17 -0
- package/dist/webpack4/schema.js.map +7 -0
- package/dist/webpack4/shared-BKTRZUZH.js +42 -0
- package/dist/webpack4/{shared-KIFRVTSM.js.map → shared-BKTRZUZH.js.map} +1 -1
- package/dist/webpack4/shared-KQOUMWJ4.js +8490 -0
- package/dist/webpack4/shared-KQOUMWJ4.js.map +7 -0
- package/dist/webpack4/shared-MRPGBJIH.js +75 -0
- package/dist/webpack4/shared-MRPGBJIH.js.map +7 -0
- package/dist/webpack4/{shared-WHOAW2VF.js → shared-N6YQE2X6.js} +116 -44
- package/dist/webpack4/shared-N6YQE2X6.js.map +7 -0
- package/dist/webpack4/{shared-KIFRVTSM.js → shared-RN7GOT65.js} +2 -41
- package/dist/webpack4/shared-RN7GOT65.js.map +7 -0
- package/dist/webpack4/turbowarp.js +4 -2
- package/dist/webpack4/turbowarp.js.map +1 -1
- package/docs/README.md +1 -0
- package/docs/block-editor-e2e.md +98 -16
- package/docs/ci-success-gate.md +84 -0
- package/docs/editor/issue-12-public-validation/README.md +61 -0
- package/docs/editor/issue-12-public-validation/dark-invalid.png +0 -0
- package/docs/editor/issue-12-public-validation/dark-styles.json +173 -0
- package/docs/editor/issue-12-public-validation/dark-valid.png +0 -0
- package/docs/editor/issue-12-public-validation/deployment-target.json +5 -0
- package/docs/editor/issue-12-public-validation/deployment-wait.json +7 -0
- package/docs/editor/issue-12-public-validation/light-invalid.png +0 -0
- package/docs/editor/issue-12-public-validation/light-styles.json +173 -0
- package/docs/editor/issue-12-public-validation/light-valid.png +0 -0
- package/docs/editor/issue-12-public-validation/provenance.json +88 -0
- package/docs/editor/issue-12-public-validation/results.json +677 -0
- package/docs/editor/issue-12-public-validation/summary.json +206 -0
- package/docs/github-pages.md +48 -41
- package/docs/package-distribution.md +2 -2
- package/docs/schema-stage1-browser.json +10 -0
- package/docs/schema-stage1-contracts.json +242 -0
- package/docs/schema-stage1-dependencies.json +202 -0
- package/docs/schema-stage1-git-artifact-20261018.ja.md +17 -0
- package/docs/schema-stage1-profile-compat.json +30 -0
- package/docs/schema-stage1-review-fixes-20261018.ja.md +36 -0
- package/docs/schema-stage1-verification.json +541 -0
- package/docs/schema-stage1.ja.md +80 -0
- package/docs/schema.md +43 -0
- package/docs/source-forms.ja.md +19 -0
- package/docs/stage-2-publication.ja.md +29 -0
- package/docs/template-fixture-separation.ja.md +43 -0
- package/package.json +11 -3
- package/webpack4.cjs +1 -1
- package/dist/webpack4/shared-WHOAW2VF.js.map +0 -7
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Ajv } from 'ajv';
|
|
2
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
3
|
+
import formats from 'ajv-formats';
|
|
4
|
+
import { prepare } from './schema-engine.js';
|
|
5
|
+
import { snapshotJson } from './schema-json.js';
|
|
6
|
+
/** Resolve refs on a private view using the same URI/anchor registry as canonical validation. */
|
|
7
|
+
export function prepareSourceVariant(variant) {
|
|
8
|
+
const view = snapshotJson(variant);
|
|
9
|
+
const ajv = view.schemaBundle.dialect === 'draft-07' ? new Ajv({ strict: false }) : new Ajv2020({ strict: false });
|
|
10
|
+
formats.default(ajv);
|
|
11
|
+
prepare(view.schemaBundle, ajv);
|
|
12
|
+
return view;
|
|
13
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SchemaBundle, SchemaJsonObject, SchemaJsonValue, SchemaValidationContext } from './schema-types.js';
|
|
2
|
+
/** Source strings remain authoritative; decoded values are only an editing view. */
|
|
3
|
+
export interface SourceDocument {
|
|
4
|
+
readonly source: string;
|
|
5
|
+
readonly format: 'json' | 'yaml';
|
|
6
|
+
readonly variantId: string;
|
|
7
|
+
readonly data: SchemaJsonValue;
|
|
8
|
+
}
|
|
9
|
+
export interface SourceVariant {
|
|
10
|
+
readonly variantId: string;
|
|
11
|
+
readonly format: 'json' | 'yaml';
|
|
12
|
+
readonly schemaBundle: SchemaBundle;
|
|
13
|
+
readonly uiSchema?: SchemaJsonObject;
|
|
14
|
+
}
|
|
15
|
+
export interface SourceValidation {
|
|
16
|
+
readonly source: string;
|
|
17
|
+
readonly result: {
|
|
18
|
+
readonly kind: 'valid';
|
|
19
|
+
readonly diagnostics: readonly [];
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'invalid' | 'execution-error' | 'schema-error';
|
|
22
|
+
readonly diagnostics: readonly {
|
|
23
|
+
readonly code: string;
|
|
24
|
+
readonly dataPath?: string;
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface SourceBinding<C = null> {
|
|
29
|
+
readonly argument: string;
|
|
30
|
+
readonly label?: string;
|
|
31
|
+
readonly variants: readonly SourceVariant[];
|
|
32
|
+
/** Explicit owner-defined source-only cases, for example a valid empty scene. */
|
|
33
|
+
readonly sourceOnlyVariants?: readonly {
|
|
34
|
+
readonly variantId: string;
|
|
35
|
+
readonly format: 'json' | 'yaml';
|
|
36
|
+
}[];
|
|
37
|
+
decode(source: string): SourceDocument;
|
|
38
|
+
validateSource(source: string, context: SchemaValidationContext<C>): SourceValidation;
|
|
39
|
+
}
|
|
40
|
+
export interface SourceEditorOptions<C = null> {
|
|
41
|
+
readonly apiVersion: 1;
|
|
42
|
+
readonly extensionId: string;
|
|
43
|
+
readonly editorId: string;
|
|
44
|
+
readonly opcodes: readonly string[];
|
|
45
|
+
readonly bindings: readonly SourceBinding<C>[];
|
|
46
|
+
readonly validationContext: () => SchemaValidationContext<C>;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ReactDOM from 'react-dom';
|
|
3
|
+
import { createSourceCandidate, patchDecodedSource, readSourceDocument } from './source-editor.js';
|
|
4
|
+
import { cloneJson } from './data.js';
|
|
5
|
+
const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
6
|
+
const pointer = (path, key) => `${path}/${key.replace(/~/g, '~0').replace(/\//g, '~1')}`;
|
|
7
|
+
const typeOf = (v) => v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v;
|
|
8
|
+
const initial = (type) => {
|
|
9
|
+
switch (type) {
|
|
10
|
+
case 'object': return {};
|
|
11
|
+
case 'array': return [];
|
|
12
|
+
case 'string': return '';
|
|
13
|
+
case 'number': return 0;
|
|
14
|
+
case 'boolean': return false;
|
|
15
|
+
default: return null;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
function mergeHints(left, right) {
|
|
19
|
+
const result = { ...left, ...right };
|
|
20
|
+
for (const field of ['properties', 'patternProperties']) {
|
|
21
|
+
if (!record(left[field]) && !record(right[field]))
|
|
22
|
+
continue;
|
|
23
|
+
const a = record(left[field]) ? left[field] : {};
|
|
24
|
+
const b = record(right[field]) ? right[field] : {};
|
|
25
|
+
const combined = { ...a };
|
|
26
|
+
for (const [key, child] of Object.entries(b))
|
|
27
|
+
Object.defineProperty(combined, key, {
|
|
28
|
+
value: Object.hasOwn(a, key) ? { allOf: [a[key], child] } : child, enumerable: true
|
|
29
|
+
});
|
|
30
|
+
result[field] = combined;
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(left.enum) && Array.isArray(right.enum)) {
|
|
33
|
+
result.enum = left.enum.filter(value => right.enum.some(other => JSON.stringify(value) === JSON.stringify(other)));
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
function propertyHint(hints, key) {
|
|
38
|
+
const matches = [];
|
|
39
|
+
if (record(hints.properties) && Object.hasOwn(hints.properties, key))
|
|
40
|
+
matches.push(hints.properties[key]);
|
|
41
|
+
if (record(hints.patternProperties))
|
|
42
|
+
for (const [pattern, schema] of Object.entries(hints.patternProperties)) {
|
|
43
|
+
try {
|
|
44
|
+
if (new RegExp(pattern, 'u').test(key))
|
|
45
|
+
matches.push(schema);
|
|
46
|
+
}
|
|
47
|
+
catch ( /* The canonical compiler rejects invalid patterns. */_a) { /* The canonical compiler rejects invalid patterns. */ }
|
|
48
|
+
}
|
|
49
|
+
return matches.length > 1 ? { allOf: matches } : matches.length ? matches[0] : hints.additionalProperties;
|
|
50
|
+
}
|
|
51
|
+
/** Presentation hints never replace canonical validation or remove schema constraints. */
|
|
52
|
+
function resolveHints(variant, schema, data, seen = new Set()) {
|
|
53
|
+
var _a;
|
|
54
|
+
if (!record(schema) || seen.has(schema))
|
|
55
|
+
return {};
|
|
56
|
+
seen.add(schema);
|
|
57
|
+
let result = { ...schema };
|
|
58
|
+
if (typeof schema.$ref === 'string') {
|
|
59
|
+
const ref = schema.$ref;
|
|
60
|
+
const hash = ref.indexOf('#');
|
|
61
|
+
const uri = hash < 0 ? ref : ref.slice(0, hash);
|
|
62
|
+
const resources = [variant.schemaBundle.root, ...variant.schemaBundle.resources];
|
|
63
|
+
const resource = uri ? resources.find(r => r.retrievalUri === uri || (record(r.document) && r.document.$id === uri)) : variant.schemaBundle.root;
|
|
64
|
+
let target = resource === null || resource === void 0 ? void 0 : resource.document;
|
|
65
|
+
const fragment = hash < 0 ? '' : decodeURIComponent(ref.slice(hash + 1));
|
|
66
|
+
if (fragment.startsWith('/'))
|
|
67
|
+
for (const part of fragment.slice(1).split('/')) {
|
|
68
|
+
target = record(target) || Array.isArray(target) ? target[part.replace(/~1/g, '/').replace(/~0/g, '~')] : undefined;
|
|
69
|
+
}
|
|
70
|
+
if (!fragment || fragment.startsWith('/'))
|
|
71
|
+
result = mergeHints(resolveHints(variant, target, data, seen), result);
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(schema.allOf))
|
|
74
|
+
for (const part of schema.allOf) {
|
|
75
|
+
const next = resolveHints(variant, part, data, seen);
|
|
76
|
+
result = mergeHints(result, next);
|
|
77
|
+
}
|
|
78
|
+
const alternatives = (_a = schema.oneOf) !== null && _a !== void 0 ? _a : schema.anyOf;
|
|
79
|
+
if (Array.isArray(alternatives)) {
|
|
80
|
+
const choices = alternatives.filter(part => {
|
|
81
|
+
if (!record(part))
|
|
82
|
+
return false;
|
|
83
|
+
if (typeof part.type === 'string' && part.type !== typeOf(data) && !(part.type === 'integer' && typeof data === 'number'))
|
|
84
|
+
return false;
|
|
85
|
+
if (record(part.properties) && record(data))
|
|
86
|
+
for (const [key, child] of Object.entries(part.properties)) {
|
|
87
|
+
if (record(child) && Object.hasOwn(child, 'const') && data[key] !== child.const)
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
});
|
|
92
|
+
if (choices.length === 1)
|
|
93
|
+
result = mergeHints(result, resolveHints(variant, choices[0], data, seen));
|
|
94
|
+
}
|
|
95
|
+
if (!Array.isArray(result.enum)) {
|
|
96
|
+
if (Object.hasOwn(result, 'const'))
|
|
97
|
+
result.enum = [result.const];
|
|
98
|
+
else if (Array.isArray(alternatives)) {
|
|
99
|
+
const enums = alternatives.map(part => resolveHints(variant, part, data, new Set(seen)));
|
|
100
|
+
if (enums.length && enums.every(part => Array.isArray(part.enum))) {
|
|
101
|
+
const values = enums.flatMap(part => part.enum);
|
|
102
|
+
result.enum = [...new Map(values.map(value => [JSON.stringify(value), value])).values()];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
function Tree({ value, path, schema, ui, variant, invalidNumbers, change: onChange }) {
|
|
109
|
+
var _a;
|
|
110
|
+
const hints = resolveHints(variant, schema, value);
|
|
111
|
+
const uiHints = record(ui) ? ui : {};
|
|
112
|
+
const [newKey, setNewKey] = React.useState('');
|
|
113
|
+
const change = (next, edit = { path }) => onChange(next, edit);
|
|
114
|
+
const choices = Array.isArray(hints.enum) ? hints.enum : Array.isArray(uiHints['ui:enum']) ? uiHints['ui:enum'] : undefined;
|
|
115
|
+
const label = typeof hints.title === 'string' ? hints.title : path || '/';
|
|
116
|
+
const props = { 'aria-label': label };
|
|
117
|
+
const type = typeOf(value);
|
|
118
|
+
let field;
|
|
119
|
+
if (choices) {
|
|
120
|
+
const serialized = JSON.stringify(value);
|
|
121
|
+
const options = choices.map(item => JSON.stringify(item));
|
|
122
|
+
if (!options.includes(serialized))
|
|
123
|
+
options.unshift(serialized);
|
|
124
|
+
field = React.createElement("select", { ...props, value: serialized, onChange: e => change(JSON.parse(e.target.value)) }, options.map(item => React.createElement("option", { key: item, value: item }, item)));
|
|
125
|
+
}
|
|
126
|
+
else if (typeof value === 'string') {
|
|
127
|
+
field = React.createElement("textarea", { ...props, value: value, onChange: e => change(e.target.value) });
|
|
128
|
+
}
|
|
129
|
+
else if (typeof value === 'number') {
|
|
130
|
+
field = React.createElement("input", { ...props, type: "text", inputMode: "decimal", value: (_a = invalidNumbers[path]) !== null && _a !== void 0 ? _a : String(value), onChange: e => {
|
|
131
|
+
const text = e.target.value;
|
|
132
|
+
const number = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/.test(text) ? Number(text) : NaN;
|
|
133
|
+
change(Number.isFinite(number) ? number : value, { path, ...(Number.isFinite(number) ? {} : { invalidText: text }) });
|
|
134
|
+
} });
|
|
135
|
+
}
|
|
136
|
+
else if (typeof value === 'boolean') {
|
|
137
|
+
field = React.createElement("input", { ...props, type: "checkbox", checked: value, onChange: e => change(e.target.checked) });
|
|
138
|
+
}
|
|
139
|
+
else if (Array.isArray(value)) {
|
|
140
|
+
field = React.createElement("div", null,
|
|
141
|
+
value.map((child, index) => {
|
|
142
|
+
var _a;
|
|
143
|
+
return React.createElement("div", { key: index },
|
|
144
|
+
React.createElement(Tree, { invalidNumbers: invalidNumbers, value: child, path: pointer(path, String(index)), variant: variant, schema: Array.isArray(hints.prefixItems) ? (_a = hints.prefixItems[index]) !== null && _a !== void 0 ? _a : hints.items : Array.isArray(hints.items) ? hints.items[index] : hints.items, ui: uiHints.items, change: (next, edit) => { const copy = [...value]; copy[index] = next; change(copy, edit); } }),
|
|
145
|
+
React.createElement("button", { type: "button", "aria-label": `Remove ${pointer(path, String(index))}`, onClick: () => change(value.filter((_, i) => i !== index), { path, removeIndex: index }) }, "Remove"),
|
|
146
|
+
React.createElement("button", { type: "button", disabled: index === 0, "aria-label": `Move up ${pointer(path, String(index))}`, onClick: () => {
|
|
147
|
+
const copy = [...value];
|
|
148
|
+
[copy[index - 1], copy[index]] = [copy[index], copy[index - 1]];
|
|
149
|
+
change(copy, { path, swap: [index - 1, index] });
|
|
150
|
+
} }, "\u2191"));
|
|
151
|
+
}),
|
|
152
|
+
React.createElement("button", { type: "button", "aria-label": `Add item ${path || '/'}`, onClick: () => change([...value, null], { path: pointer(path, String(value.length)) }) }, "Add item"));
|
|
153
|
+
}
|
|
154
|
+
else if (value !== null && typeof value === 'object') {
|
|
155
|
+
const properties = record(hints.properties) ? hints.properties : {};
|
|
156
|
+
field = React.createElement("div", null,
|
|
157
|
+
Object.entries(value).map(([key, child]) => React.createElement("div", { key: key },
|
|
158
|
+
React.createElement(Tree, { invalidNumbers: invalidNumbers, value: child, path: pointer(path, key), variant: variant, schema: propertyHint(hints, key), ui: uiHints[key], change: (next, edit) => {
|
|
159
|
+
const copy = cloneJson(value);
|
|
160
|
+
Object.defineProperty(copy, key, { value: next, enumerable: true, writable: true, configurable: true });
|
|
161
|
+
change(copy, edit);
|
|
162
|
+
} }),
|
|
163
|
+
React.createElement("button", { type: "button", "aria-label": `Remove ${pointer(path, key)}`, onClick: () => {
|
|
164
|
+
const copy = cloneJson(value);
|
|
165
|
+
delete copy[key];
|
|
166
|
+
change(copy, { path: pointer(path, key) });
|
|
167
|
+
} }, "Remove"))),
|
|
168
|
+
React.createElement("label", null,
|
|
169
|
+
"Property ",
|
|
170
|
+
React.createElement("input", { "aria-label": `New property ${path || '/'}`, value: newKey, onChange: e => setNewKey(e.target.value) })),
|
|
171
|
+
React.createElement("select", { "aria-label": `Known property ${path || '/'}`, value: "", onChange: e => setNewKey(e.target.value) },
|
|
172
|
+
React.createElement("option", { value: "" }, "Choose property"),
|
|
173
|
+
Object.keys(properties).filter(key => !Object.hasOwn(value, key)).map(key => React.createElement("option", { key: key }, key))),
|
|
174
|
+
React.createElement("button", { type: "button", disabled: !newKey || Object.hasOwn(value, newKey), onClick: () => {
|
|
175
|
+
const copy = cloneJson(value);
|
|
176
|
+
Object.defineProperty(copy, newKey, { value: null, enumerable: true, writable: true, configurable: true });
|
|
177
|
+
change(copy, { path: pointer(path, newKey) });
|
|
178
|
+
setNewKey('');
|
|
179
|
+
} }, "Add property"));
|
|
180
|
+
}
|
|
181
|
+
else
|
|
182
|
+
field = React.createElement("span", null, "null");
|
|
183
|
+
return React.createElement("fieldset", { "data-source-path": path },
|
|
184
|
+
React.createElement("legend", null, label),
|
|
185
|
+
React.createElement("select", { "aria-label": `Type ${path || '/'}`, value: type, onChange: e => change(initial(e.target.value)) }, ['object', 'array', 'string', 'number', 'boolean', 'null'].map(t => React.createElement("option", { key: t }, t))),
|
|
186
|
+
field);
|
|
187
|
+
}
|
|
188
|
+
function liveIssues(draft, bindings) {
|
|
189
|
+
const issues = [];
|
|
190
|
+
const sources = draft.sources;
|
|
191
|
+
for (const binding of bindings) {
|
|
192
|
+
const entry = sources[binding.argument];
|
|
193
|
+
try {
|
|
194
|
+
if (entry.invalidForm === true)
|
|
195
|
+
throw new TypeError();
|
|
196
|
+
const doc = record(entry.creating)
|
|
197
|
+
? { ...entry.creating, data: entry.data }
|
|
198
|
+
: readSourceDocument(binding, entry.source);
|
|
199
|
+
const validator = binding.validators.get(JSON.stringify([doc.format, doc.variantId]));
|
|
200
|
+
if (validator && validator.validate((entry.mode === 'form' ? entry.data : doc.data)).kind !== 'valid') {
|
|
201
|
+
issues.push({ message: 'Value does not satisfy the schema.', path: `/${binding.argument}` });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (_a) {
|
|
205
|
+
issues.push({ message: 'Repair the invalid source or form value.', path: `/${binding.argument}` });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return issues;
|
|
209
|
+
}
|
|
210
|
+
function SourceView({ context, bindings }) {
|
|
211
|
+
const [, redraw] = React.useState(0);
|
|
212
|
+
const [creating, selectCreateVariant] = React.useState({});
|
|
213
|
+
React.useEffect(() => {
|
|
214
|
+
const stop = context.subscribe(() => redraw(n => n + 1));
|
|
215
|
+
context.setValidationIssues(liveIssues(context.getDraft(), bindings));
|
|
216
|
+
return stop;
|
|
217
|
+
}, [context, bindings]);
|
|
218
|
+
const state = context.getState();
|
|
219
|
+
if (state.status !== 'editing' && state.status !== 'saving')
|
|
220
|
+
return React.createElement(React.Fragment, null);
|
|
221
|
+
const draft = context.getDraft();
|
|
222
|
+
const sources = draft.sources;
|
|
223
|
+
const disabled = context.getState().status !== 'editing';
|
|
224
|
+
const update = (argument, entry) => {
|
|
225
|
+
const next = context.getDraft();
|
|
226
|
+
const nextSources = next.sources;
|
|
227
|
+
Object.defineProperty(nextSources, argument, { value: entry, enumerable: true, configurable: true, writable: true });
|
|
228
|
+
context.setDraft(next);
|
|
229
|
+
context.setValidationIssues(liveIssues(next, bindings));
|
|
230
|
+
};
|
|
231
|
+
return React.createElement("div", { className: "block-editor-source-forms" }, bindings.map(binding => {
|
|
232
|
+
var _a, _b;
|
|
233
|
+
const entry = sources[binding.argument];
|
|
234
|
+
const selectedCreation = Object.hasOwn(creating, binding.argument) ? creating[binding.argument] : '';
|
|
235
|
+
let document;
|
|
236
|
+
let variant;
|
|
237
|
+
try {
|
|
238
|
+
document = readSourceDocument(binding, entry.source);
|
|
239
|
+
variant = binding.presentationVariants.find(v => v.format === document.format && v.variantId === document.variantId);
|
|
240
|
+
}
|
|
241
|
+
catch ( /* The Source textarea remains available for repair. */_c) { /* The Source textarea remains available for repair. */ }
|
|
242
|
+
if (record(entry.creating)) {
|
|
243
|
+
const creation = entry.creating;
|
|
244
|
+
variant = binding.presentationVariants.find(v => v.format === creation.format && v.variantId === creation.variantId);
|
|
245
|
+
}
|
|
246
|
+
return React.createElement("fieldset", { key: binding.argument, disabled: disabled },
|
|
247
|
+
React.createElement("legend", null, (_a = binding.label) !== null && _a !== void 0 ? _a : binding.argument),
|
|
248
|
+
React.createElement("button", { type: "button", "aria-pressed": entry.mode === 'source', onClick: () => {
|
|
249
|
+
try {
|
|
250
|
+
if (entry.invalidForm === true)
|
|
251
|
+
throw new TypeError();
|
|
252
|
+
const source = record(entry.creating)
|
|
253
|
+
? createSourceCandidate(entry.creating.format, entry.data)
|
|
254
|
+
: entry.mode === 'form' && document && Object.hasOwn(entry, 'data')
|
|
255
|
+
? patchDecodedSource(document, entry.data, source => readSourceDocument(binding, source)) : entry.source;
|
|
256
|
+
const next = { ...entry, mode: 'source', source, invalidForm: false, invalidNumbers: {} };
|
|
257
|
+
delete next.creating;
|
|
258
|
+
update(binding.argument, next);
|
|
259
|
+
}
|
|
260
|
+
catch (_a) {
|
|
261
|
+
context.setValidationIssues([{ message: 'This edit cannot preserve the source. Repair the Source explicitly.' }]);
|
|
262
|
+
}
|
|
263
|
+
} }, "Source"),
|
|
264
|
+
React.createElement("select", { "aria-label": `${binding.argument} New format and variant`, value: selectedCreation, onChange: e => selectCreateVariant({ ...creating, [binding.argument]: e.target.value }) },
|
|
265
|
+
React.createElement("option", { value: "" }, "Choose new format and variant"),
|
|
266
|
+
binding.presentationVariants.map(v => React.createElement("option", { key: JSON.stringify([v.format, v.variantId]), value: JSON.stringify([v.format, v.variantId]) },
|
|
267
|
+
v.format,
|
|
268
|
+
" / ",
|
|
269
|
+
v.variantId))),
|
|
270
|
+
React.createElement("button", { type: "button", disabled: !selectedCreation, onClick: () => {
|
|
271
|
+
const [format, variantId] = JSON.parse(selectedCreation);
|
|
272
|
+
const chosen = binding.presentationVariants.find(v => v.format === format && v.variantId === variantId);
|
|
273
|
+
const root = chosen.schemaBundle.root.document;
|
|
274
|
+
const type = record(root) && typeof root.type === 'string' ? root.type : 'null';
|
|
275
|
+
update(binding.argument, { mode: 'form', source: entry.source, sourceEdited: true, creating: { format, variantId }, data: initial(type) });
|
|
276
|
+
} }, "Create document (replace source)"),
|
|
277
|
+
React.createElement("button", { type: "button", disabled: !variant, "aria-pressed": entry.mode === 'form', onClick: () => {
|
|
278
|
+
if (document || entry.creating)
|
|
279
|
+
update(binding.argument, { ...entry, mode: 'form', data: Object.hasOwn(entry, 'data') ? entry.data : document.data });
|
|
280
|
+
} }, "Form"),
|
|
281
|
+
entry.mode === 'form' && React.createElement("button", { type: "button", onClick: () => { var _a; return update(binding.argument, { mode: 'source', source: entry.source, sourceEdited: (_a = entry.sourceEdited) !== null && _a !== void 0 ? _a : false }); } }, "Discard form edits"),
|
|
282
|
+
entry.mode === 'form' && variant && Object.hasOwn(entry, 'data')
|
|
283
|
+
? React.createElement(Tree, { invalidNumbers: ((_b = entry.invalidNumbers) !== null && _b !== void 0 ? _b : {}), value: entry.data, path: "", schema: variant.schemaBundle.root.document, ui: variant.uiSchema, variant: variant, change: (data, edit = { path: '' }) => {
|
|
284
|
+
var _a;
|
|
285
|
+
const pending = ((_a = entry.invalidNumbers) !== null && _a !== void 0 ? _a : {});
|
|
286
|
+
const invalidNumbers = {};
|
|
287
|
+
const prefix = `${edit.path}/`;
|
|
288
|
+
for (const [path, text] of Object.entries(pending)) {
|
|
289
|
+
let target = path;
|
|
290
|
+
if (edit.removeIndex !== undefined || edit.swap) {
|
|
291
|
+
if (path.startsWith(prefix)) {
|
|
292
|
+
const [head, ...tail] = path.slice(prefix.length).split('/');
|
|
293
|
+
let index = Number(head);
|
|
294
|
+
if (index === edit.removeIndex)
|
|
295
|
+
continue;
|
|
296
|
+
if (edit.removeIndex !== undefined && index > edit.removeIndex)
|
|
297
|
+
index--;
|
|
298
|
+
if (edit.swap)
|
|
299
|
+
index = index === edit.swap[0] ? edit.swap[1] : index === edit.swap[1] ? edit.swap[0] : index;
|
|
300
|
+
target = `${prefix}${index}${tail.length ? '/' + tail.join('/') : ''}`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else if (path === edit.path || path.startsWith(prefix))
|
|
304
|
+
continue;
|
|
305
|
+
Object.defineProperty(invalidNumbers, target, { value: text, enumerable: true });
|
|
306
|
+
}
|
|
307
|
+
if (edit.invalidText !== undefined)
|
|
308
|
+
Object.defineProperty(invalidNumbers, edit.path, { value: edit.invalidText, enumerable: true });
|
|
309
|
+
update(binding.argument, { ...entry, data, invalidNumbers, invalidForm: Object.keys(invalidNumbers).length > 0 });
|
|
310
|
+
} })
|
|
311
|
+
: React.createElement("textarea", { "aria-label": `${binding.argument} Source`, value: entry.source, onChange: e => update(binding.argument, { mode: 'source', source: e.target.value, sourceEdited: true }) }),
|
|
312
|
+
entry.invalidForm === true && React.createElement("p", { role: "alert" }, "Enter a valid JSON number."));
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
export function mountSourceView(container, context, bindings) {
|
|
316
|
+
ReactDOM.render(React.createElement(SourceView, { context: context, bindings: bindings }), container);
|
|
317
|
+
return () => { ReactDOM.unmountComponentAtNode(container); };
|
|
318
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ValidationIssue } from './schema-types.js';
|
|
2
|
+
export type { ValidationIssue } from './schema-types.js';
|
|
1
3
|
import type { DescendantEditorHost } from './descendant-types.js';
|
|
2
4
|
export declare const EDITOR_API_VERSION: 1;
|
|
3
5
|
export type LiteralValue = string | number | boolean;
|
|
@@ -41,10 +43,6 @@ export interface BlockEditorHost {
|
|
|
41
43
|
*/
|
|
42
44
|
commit(request: CommitRequest): void | Promise<void>;
|
|
43
45
|
}
|
|
44
|
-
export interface ValidationIssue {
|
|
45
|
-
readonly message: string;
|
|
46
|
-
readonly path?: string;
|
|
47
|
-
}
|
|
48
46
|
export type SessionStatus = 'editing' | 'saving' | 'saved' | 'cancelled' | 'disposed';
|
|
49
47
|
export interface SessionState {
|
|
50
48
|
readonly status: SessionStatus;
|
package/dist/webpack4/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DescendantSession,
|
|
4
4
|
EditorRegistry,
|
|
5
5
|
EditorSession
|
|
6
|
-
} from "./shared-
|
|
6
|
+
} from "./shared-N6YQE2X6.js";
|
|
7
7
|
import {
|
|
8
8
|
BlockEditorConflictError,
|
|
9
9
|
BlockEditorStructureError,
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
DescendantNodeError,
|
|
13
13
|
EDITOR_API_VERSION,
|
|
14
14
|
supportsDescendantEditing
|
|
15
|
-
} from "./shared-
|
|
15
|
+
} from "./shared-BKTRZUZH.js";
|
|
16
|
+
import "./shared-MRPGBJIH.js";
|
|
17
|
+
import "./shared-RN7GOT65.js";
|
|
16
18
|
export {
|
|
17
19
|
BlockEditorConflictError,
|
|
18
20
|
BlockEditorStructureError,
|