@kubohiroya/turbowarp-extension-data-editor 0.2.2 → 0.3.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/README.md +2 -0
- package/dist/build-info.json +3 -3
- package/dist/config/feature-flags.d.ts +5 -0
- package/dist/config/feature-flags.js +6 -0
- package/dist/form-presentation.d.ts +8 -0
- package/dist/form-presentation.js +62 -0
- package/dist/layout-view.d.ts +21 -0
- package/dist/layout-view.js +127 -0
- package/dist/layout.d.ts +31 -0
- package/dist/layout.js +63 -0
- package/dist/rjsf-templates.d.ts +8 -1
- package/dist/rjsf-templates.js +71 -1
- package/dist/rjsf.d.ts +11 -8
- package/dist/rjsf.js +24 -13
- package/dist/schema-presentation.d.ts +10 -0
- package/dist/schema-presentation.js +109 -0
- package/dist/source-editor.d.ts +2 -1
- package/dist/source-editor.js +23 -5
- package/dist/source-view.d.ts +1 -1
- package/dist/source-view.js +222 -122
- package/dist/turbowarp/appearance.d.ts +1 -1
- package/dist/turbowarp/appearance.js +23 -5
- package/dist/turbowarp/bridge.js +3 -2
- package/dist/turbowarp.d.ts +2 -1
- package/dist/turbowarp.js +3 -2
- package/dist/webpack4/metafile.json +136 -19
- package/dist/webpack4/rjsf.js +961 -279
- package/dist/webpack4/rjsf.js.map +4 -4
- package/dist/webpack4/turbowarp.js +20 -6
- package/dist/webpack4/turbowarp.js.map +2 -2
- package/docs/README.md +1 -0
- package/docs/common-layout.ja.md +125 -0
- package/docs/github-pages.md +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ TurboWarp拡張が扱う構造化データを、フォームや独自UIで編集
|
|
|
6
6
|
|
|
7
7
|
日本語の利用手順・API・GUI接続・公開・検証記録は[ドキュメント一覧](docs/README.md)へ集約しています。
|
|
8
8
|
|
|
9
|
+
タブ・グループ・グリッドは[共通レイアウトガイド](docs/common-layout.ja.md)を参照してください。標準RJSFとSource/Formで同じJSON宣言を使用し、ホストの起動時フラグ `commonLayout` で有効にします(既定OFF)。
|
|
10
|
+
|
|
9
11
|
[公開エディター・日英案内](https://kubohiroya.github.io/turbowarp-extension-data-editor/) と
|
|
10
12
|
[公開・検証の運用Issue #26](https://github.com/kubohiroya/turbowarp-extension-data-editor/issues/26) も本リポジトリが所有します。
|
|
11
13
|
|
package/dist/build-info.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubohiroya/turbowarp-extension-data-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"repository": "https://github.com/kubohiroya/turbowarp-extension-data-editor",
|
|
5
|
-
"revision": "
|
|
5
|
+
"revision": "b5da552d5e5a9a4b3cde49f6410d463df48eb62f",
|
|
6
6
|
"sourceState": "commit",
|
|
7
|
-
"sourceSha256": "
|
|
7
|
+
"sourceSha256": "0e297f919ee81101e825bea99a6c20203fbd918a555e1c19e4b14f0132a2b1b7",
|
|
8
8
|
"capabilities": {
|
|
9
9
|
"preparedSource": 1
|
|
10
10
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Captured once by the host at service/editor creation; never controlled by extension metadata. */
|
|
2
|
+
export interface EditorFeatureFlags {
|
|
3
|
+
readonly commonLayout?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function captureFeatureFlags(options?: EditorFeatureFlags): Readonly<Required<EditorFeatureFlags>>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export function captureFeatureFlags(options = {}) {
|
|
2
|
+
if (options.commonLayout !== undefined && typeof options.commonLayout !== 'boolean') {
|
|
3
|
+
throw new TypeError('Invalid common layout feature flag.');
|
|
4
|
+
}
|
|
5
|
+
return Object.freeze({ commonLayout: options.commonLayout === true });
|
|
6
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/** Explicit schema/ui titles remain untouched; fallback property keys become readable labels. */
|
|
3
|
+
export declare function propertyLabel(key: string): string;
|
|
4
|
+
export declare function FormHelp({ description, id }: {
|
|
5
|
+
description: unknown;
|
|
6
|
+
id?: string;
|
|
7
|
+
}): React.ReactElement | null;
|
|
8
|
+
export declare const formStyles = "\n[data-editor-form]{--editor-accent:var(--block-editor-accent,#426ad6);--editor-line:var(--block-editor-border,#dde3ed);--editor-muted:var(--block-editor-muted,var(--block-editor-text,#637086));color:var(--block-editor-text,#25334a);font:14px/1.5 system-ui,sans-serif;min-width:0}\n[data-editor-form] *,[data-editor-form] *:before,[data-editor-form] *:after{box-sizing:border-box}\n[data-editor-form] fieldset{min-width:0}\n[data-editor-form] fieldset.editor-form-binding,[data-editor-form] .editor-form-section,[data-editor-form] .rjsf fieldset{padding:0;border:0;margin:0 0 18px}\n[data-editor-form] .editor-form-section>legend,[data-editor-form] .rjsf fieldset>legend{font-size:15px;font-weight:650;padding:0;margin:0 0 10px;color:inherit}\n[data-editor-form] .editor-form-field,[data-editor-form] .rjsf .form-group{margin:0 0 14px;min-width:0}\n[data-editor-form] .editor-form-field>label,[data-editor-form] .rjsf .control-label{display:block;font-size:13px;font-weight:600;margin:0 0 6px;color:inherit}\n[data-editor-form] .required{color:var(--editor-accent);margin-left:3px}\n[data-editor-form] input:not([type=checkbox]):not([type=radio]),[data-editor-form] select,[data-editor-form] textarea{display:block;width:100%;min-height:38px;margin:0;padding:8px 11px;border:1px solid var(--editor-line);border-radius:7px;background:var(--block-editor-input,white);color:inherit;font:inherit;box-shadow:0 1px 2px #172b4d05}\n[data-editor-form] textarea{min-height:76px;resize:vertical}\n[data-editor-form] input:focus-visible,[data-editor-form] select:focus-visible,[data-editor-form] textarea:focus-visible,[data-editor-form] button:focus-visible,[data-editor-form] summary:focus-visible{outline:2px solid var(--editor-accent);outline-offset:2px}\n[data-editor-form] input[readonly]{background:var(--block-editor-subtle,var(--block-editor-background,#f5f7fb));color:var(--editor-muted)}\n[data-editor-form] input[type=checkbox]{width:17px;height:17px;margin:0;accent-color:var(--editor-accent)}\n[data-editor-form] .editor-form-field>label.editor-form-toggle,[data-editor-form] .rjsf .checkbox>label{display:flex;align-items:center;gap:8px;font-weight:500;margin:8px 0}\n[data-editor-form] button{font:inherit;cursor:pointer;border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);color:inherit;padding:6px 10px;margin:0}\n[data-editor-form] button:disabled{cursor:default;opacity:.5}\n[data-editor-form] .editor-form-hint,[data-editor-form] .editor-form-help{color:var(--editor-muted);font-size:12px;line-height:1.5;margin:5px 0 10px}\n[data-editor-form] .editor-form-help>summary{display:list-item;list-style:disclosure-closed inside;cursor:pointer;width:fit-content;font-weight:500}\n[data-editor-form] .editor-form-help[open]>summary{list-style-type:disclosure-open}\n[data-editor-form] .editor-form-help p{margin:8px 0}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]{display:flex;gap:4px;flex-wrap:wrap;padding:5px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9));border:1px solid var(--editor-line);border-radius:9px;margin:4px 0 20px}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{border:0;border-radius:6px;background:transparent;color:var(--editor-muted);padding:8px 18px;margin:0;font-weight:550;text-decoration:none;min-width:76px}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]>[aria-selected=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 4px #1d30431a}\n[data-editor-form] .editor-form-error-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;margin-left:7px;padding:0 5px;border-radius:10px;background:#fff0f0;color:#b53d45;font-size:11px}\n[data-editor-form] fieldset.block-editor-layout-group{border:1px solid var(--editor-line);border-radius:10px;padding:16px 18px 4px;margin:0 0 18px;background:var(--block-editor-background,white)}\n[data-editor-form] fieldset.block-editor-layout-group>legend{padding:0 6px;font-size:13px;font-weight:650}\n[data-editor-form] .block-editor-layout-grid{display:grid;gap:0 18px;grid-template-columns:repeat(var(--editor-columns),minmax(0,1fr));align-items:start}\n[data-editor-form] .block-editor-layout-grid>*{min-width:0}\n[data-editor-form] .block-editor-layout-tabs>[hidden]{display:none!important}\n[data-editor-form] .editor-form-toolbar{display:flex;align-items:start;justify-content:space-between;gap:12px;margin:0 0 18px;padding-bottom:14px;border-bottom:1px solid var(--editor-line)}\n[data-editor-form] .editor-form-modes{display:flex;gap:3px;border:1px solid var(--editor-line);border-radius:8px;padding:3px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9))}\n[data-editor-form] .editor-form-modes>button{border:0;border-radius:5px;padding:5px 14px;background:transparent;color:var(--editor-muted);margin:0}\n[data-editor-form] .editor-form-modes>[aria-pressed=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 3px #1d30431a}\n[data-editor-form] .editor-form-tools{font-size:12px;color:var(--editor-muted);margin:0 0 12px}\n[data-editor-form] .editor-form-tools>summary{cursor:pointer;padding:5px 0;list-style-position:inside}\n[data-editor-form] .editor-form-tools[open]{padding:10px 12px;border:1px solid var(--editor-line);border-radius:8px;background:var(--block-editor-subtle,var(--block-editor-background,#f7f9fc))}\n[data-editor-form] .editor-form-tools button{border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);padding:5px 9px;margin:5px 5px 5px 0;color:inherit}\n[data-editor-form] .editor-form-tools label{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:5px 0}\n[data-editor-form] .editor-form-invalid{font-size:12px;color:#b53d45;margin:5px 0}\n[data-editor-form] [data-source-error=true]>input,[data-editor-form] [data-source-error=true]>textarea{border-color:#b53d45}\n[data-editor-form] .editor-form-array-item{display:flex;align-items:start;gap:10px}\n[data-editor-form] .editor-form-array-item>.editor-form-field{flex:1;min-width:0}\n@media(max-width:600px){[data-editor-form] .block-editor-layout-grid{grid-template-columns:minmax(0,1fr)}[data-editor-form] fieldset.block-editor-layout-group{padding:12px 12px 0}[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{padding:7px 12px;min-width:60px}[data-editor-form] .editor-form-toolbar{flex-wrap:wrap}}\n";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/** Explicit schema/ui titles remain untouched; fallback property keys become readable labels. */
|
|
3
|
+
export function propertyLabel(key) {
|
|
4
|
+
if (/^[a-z]{1,3}$/.test(key))
|
|
5
|
+
return key;
|
|
6
|
+
const text = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').replace(/\b(id|uri|url|json)\b/gi, word => word.toUpperCase());
|
|
7
|
+
return text ? text[0].toUpperCase() + text.slice(1) : '設定';
|
|
8
|
+
}
|
|
9
|
+
export function FormHelp({ description, id }) {
|
|
10
|
+
if (typeof description !== 'string' || !description.trim())
|
|
11
|
+
return null;
|
|
12
|
+
return description.length > 130 ? React.createElement("details", { className: "editor-form-help", id: id },
|
|
13
|
+
React.createElement("summary", null, "\u8AAC\u660E\u3092\u8868\u793A"),
|
|
14
|
+
React.createElement("p", null, description))
|
|
15
|
+
: React.createElement("p", { className: "editor-form-hint", id: id }, description);
|
|
16
|
+
}
|
|
17
|
+
// Scoped to the opt-in form root; the same rules ship with both rendering paths.
|
|
18
|
+
export const formStyles = `
|
|
19
|
+
[data-editor-form]{--editor-accent:var(--block-editor-accent,#426ad6);--editor-line:var(--block-editor-border,#dde3ed);--editor-muted:var(--block-editor-muted,var(--block-editor-text,#637086));color:var(--block-editor-text,#25334a);font:14px/1.5 system-ui,sans-serif;min-width:0}
|
|
20
|
+
[data-editor-form] *,[data-editor-form] *:before,[data-editor-form] *:after{box-sizing:border-box}
|
|
21
|
+
[data-editor-form] fieldset{min-width:0}
|
|
22
|
+
[data-editor-form] fieldset.editor-form-binding,[data-editor-form] .editor-form-section,[data-editor-form] .rjsf fieldset{padding:0;border:0;margin:0 0 18px}
|
|
23
|
+
[data-editor-form] .editor-form-section>legend,[data-editor-form] .rjsf fieldset>legend{font-size:15px;font-weight:650;padding:0;margin:0 0 10px;color:inherit}
|
|
24
|
+
[data-editor-form] .editor-form-field,[data-editor-form] .rjsf .form-group{margin:0 0 14px;min-width:0}
|
|
25
|
+
[data-editor-form] .editor-form-field>label,[data-editor-form] .rjsf .control-label{display:block;font-size:13px;font-weight:600;margin:0 0 6px;color:inherit}
|
|
26
|
+
[data-editor-form] .required{color:var(--editor-accent);margin-left:3px}
|
|
27
|
+
[data-editor-form] input:not([type=checkbox]):not([type=radio]),[data-editor-form] select,[data-editor-form] textarea{display:block;width:100%;min-height:38px;margin:0;padding:8px 11px;border:1px solid var(--editor-line);border-radius:7px;background:var(--block-editor-input,white);color:inherit;font:inherit;box-shadow:0 1px 2px #172b4d05}
|
|
28
|
+
[data-editor-form] textarea{min-height:76px;resize:vertical}
|
|
29
|
+
[data-editor-form] input:focus-visible,[data-editor-form] select:focus-visible,[data-editor-form] textarea:focus-visible,[data-editor-form] button:focus-visible,[data-editor-form] summary:focus-visible{outline:2px solid var(--editor-accent);outline-offset:2px}
|
|
30
|
+
[data-editor-form] input[readonly]{background:var(--block-editor-subtle,var(--block-editor-background,#f5f7fb));color:var(--editor-muted)}
|
|
31
|
+
[data-editor-form] input[type=checkbox]{width:17px;height:17px;margin:0;accent-color:var(--editor-accent)}
|
|
32
|
+
[data-editor-form] .editor-form-field>label.editor-form-toggle,[data-editor-form] .rjsf .checkbox>label{display:flex;align-items:center;gap:8px;font-weight:500;margin:8px 0}
|
|
33
|
+
[data-editor-form] button{font:inherit;cursor:pointer;border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);color:inherit;padding:6px 10px;margin:0}
|
|
34
|
+
[data-editor-form] button:disabled{cursor:default;opacity:.5}
|
|
35
|
+
[data-editor-form] .editor-form-hint,[data-editor-form] .editor-form-help{color:var(--editor-muted);font-size:12px;line-height:1.5;margin:5px 0 10px}
|
|
36
|
+
[data-editor-form] .editor-form-help>summary{display:list-item;list-style:disclosure-closed inside;cursor:pointer;width:fit-content;font-weight:500}
|
|
37
|
+
[data-editor-form] .editor-form-help[open]>summary{list-style-type:disclosure-open}
|
|
38
|
+
[data-editor-form] .editor-form-help p{margin:8px 0}
|
|
39
|
+
[data-editor-form] .block-editor-layout-tabs>[role=tablist]{display:flex;gap:4px;flex-wrap:wrap;padding:5px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9));border:1px solid var(--editor-line);border-radius:9px;margin:4px 0 20px}
|
|
40
|
+
[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{border:0;border-radius:6px;background:transparent;color:var(--editor-muted);padding:8px 18px;margin:0;font-weight:550;text-decoration:none;min-width:76px}
|
|
41
|
+
[data-editor-form] .block-editor-layout-tabs>[role=tablist]>[aria-selected=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 4px #1d30431a}
|
|
42
|
+
[data-editor-form] .editor-form-error-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;margin-left:7px;padding:0 5px;border-radius:10px;background:#fff0f0;color:#b53d45;font-size:11px}
|
|
43
|
+
[data-editor-form] fieldset.block-editor-layout-group{border:1px solid var(--editor-line);border-radius:10px;padding:16px 18px 4px;margin:0 0 18px;background:var(--block-editor-background,white)}
|
|
44
|
+
[data-editor-form] fieldset.block-editor-layout-group>legend{padding:0 6px;font-size:13px;font-weight:650}
|
|
45
|
+
[data-editor-form] .block-editor-layout-grid{display:grid;gap:0 18px;grid-template-columns:repeat(var(--editor-columns),minmax(0,1fr));align-items:start}
|
|
46
|
+
[data-editor-form] .block-editor-layout-grid>*{min-width:0}
|
|
47
|
+
[data-editor-form] .block-editor-layout-tabs>[hidden]{display:none!important}
|
|
48
|
+
[data-editor-form] .editor-form-toolbar{display:flex;align-items:start;justify-content:space-between;gap:12px;margin:0 0 18px;padding-bottom:14px;border-bottom:1px solid var(--editor-line)}
|
|
49
|
+
[data-editor-form] .editor-form-modes{display:flex;gap:3px;border:1px solid var(--editor-line);border-radius:8px;padding:3px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9))}
|
|
50
|
+
[data-editor-form] .editor-form-modes>button{border:0;border-radius:5px;padding:5px 14px;background:transparent;color:var(--editor-muted);margin:0}
|
|
51
|
+
[data-editor-form] .editor-form-modes>[aria-pressed=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 3px #1d30431a}
|
|
52
|
+
[data-editor-form] .editor-form-tools{font-size:12px;color:var(--editor-muted);margin:0 0 12px}
|
|
53
|
+
[data-editor-form] .editor-form-tools>summary{cursor:pointer;padding:5px 0;list-style-position:inside}
|
|
54
|
+
[data-editor-form] .editor-form-tools[open]{padding:10px 12px;border:1px solid var(--editor-line);border-radius:8px;background:var(--block-editor-subtle,var(--block-editor-background,#f7f9fc))}
|
|
55
|
+
[data-editor-form] .editor-form-tools button{border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);padding:5px 9px;margin:5px 5px 5px 0;color:inherit}
|
|
56
|
+
[data-editor-form] .editor-form-tools label{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:5px 0}
|
|
57
|
+
[data-editor-form] .editor-form-invalid{font-size:12px;color:#b53d45;margin:5px 0}
|
|
58
|
+
[data-editor-form] [data-source-error=true]>input,[data-editor-form] [data-source-error=true]>textarea{border-color:#b53d45}
|
|
59
|
+
[data-editor-form] .editor-form-array-item{display:flex;align-items:start;gap:10px}
|
|
60
|
+
[data-editor-form] .editor-form-array-item>.editor-form-field{flex:1;min-width:0}
|
|
61
|
+
@media(max-width:600px){[data-editor-form] .block-editor-layout-grid{grid-template-columns:minmax(0,1fr)}[data-editor-form] fieldset.block-editor-layout-group{padding:12px 12px 0}[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{padding:7px 12px;min-width:60px}[data-editor-form] .editor-form-toolbar{flex-wrap:wrap}}
|
|
62
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { EditorViewContext } from './types.js';
|
|
3
|
+
export declare const useCommonLayout: () => boolean;
|
|
4
|
+
/** One environment for the entire editor; saving reveals errors without changing the draft. */
|
|
5
|
+
export declare function LayoutHost({ enabled, context, children }: {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
context: EditorViewContext;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}): React.ReactElement;
|
|
10
|
+
export interface LayoutField {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly content: React.ReactNode;
|
|
13
|
+
readonly errors?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Share layout behavior while leaving each renderer in charge of its controls. */
|
|
16
|
+
export declare function ObjectLayout({ ui, fields, knownFields, fallback }: {
|
|
17
|
+
ui: unknown;
|
|
18
|
+
fields: readonly LayoutField[];
|
|
19
|
+
knownFields: readonly string[];
|
|
20
|
+
fallback: React.ReactNode;
|
|
21
|
+
}): React.ReactElement;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { formStyles } from './form-presentation.js';
|
|
3
|
+
import { resolveEditorLayout } from './layout.js';
|
|
4
|
+
const Environment = React.createContext({ enabled: false, reveal: 0, disabled: false });
|
|
5
|
+
export const useCommonLayout = () => React.useContext(Environment).enabled;
|
|
6
|
+
let nextId = 0;
|
|
7
|
+
/** One environment for the entire editor; saving reveals errors without changing the draft. */
|
|
8
|
+
export function LayoutHost({ enabled, context, children }) {
|
|
9
|
+
const [reveal, setReveal] = React.useState(0);
|
|
10
|
+
const [status, setStatus] = React.useState(context.getState().status);
|
|
11
|
+
const root = React.useRef(null);
|
|
12
|
+
React.useLayoutEffect(() => {
|
|
13
|
+
let saving = context.getState().status === 'saving';
|
|
14
|
+
let active = true;
|
|
15
|
+
const stop = context.subscribe(() => {
|
|
16
|
+
const state = context.getState();
|
|
17
|
+
const failed = saving && state.status === 'editing' && state.issues.length > 0;
|
|
18
|
+
saving = state.status === 'saving';
|
|
19
|
+
setStatus(state.status);
|
|
20
|
+
if (failed)
|
|
21
|
+
void Promise.resolve().then(() => { if (active)
|
|
22
|
+
setReveal(n => n + 1); });
|
|
23
|
+
});
|
|
24
|
+
return () => { active = false; stop(); };
|
|
25
|
+
}, [context]);
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
if (!enabled || !reveal)
|
|
28
|
+
return;
|
|
29
|
+
const timer = setTimeout(() => {
|
|
30
|
+
var _a, _b, _c;
|
|
31
|
+
const controls = (_a = root.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('[data-layout-error="true"] input, [data-layout-error="true"] textarea, [data-layout-error="true"] select');
|
|
32
|
+
const candidates = Array.from(controls !== null && controls !== void 0 ? controls : []).filter(control => !control.closest('[hidden]') && !control.matches(':disabled') && !control.hasAttribute('data-source-type-control'));
|
|
33
|
+
const target = (_c = (_b = candidates.find(control => { var _a; return ((_a = control.closest('[data-source-path]')) === null || _a === void 0 ? void 0 : _a.getAttribute('data-source-error')) === 'true'; })) !== null && _b !== void 0 ? _b : candidates.find(control => { var _a; return (_a = control.closest('.form-group')) === null || _a === void 0 ? void 0 : _a.classList.contains('has-error'); })) !== null && _c !== void 0 ? _c : candidates.find(control => { var _a; return ((_a = control.closest('[data-layout-field]')) === null || _a === void 0 ? void 0 : _a.getAttribute('data-layout-error')) === 'true'; });
|
|
34
|
+
target === null || target === void 0 ? void 0 : target.focus();
|
|
35
|
+
}, 0);
|
|
36
|
+
return () => clearTimeout(timer);
|
|
37
|
+
}, [enabled, reveal]);
|
|
38
|
+
return React.createElement(Environment.Provider, { value: { enabled, reveal, disabled: status !== 'editing' } }, enabled ? React.createElement("div", { ref: root, "data-editor-form": "" },
|
|
39
|
+
React.createElement("style", null, formStyles),
|
|
40
|
+
children) : children);
|
|
41
|
+
}
|
|
42
|
+
function names(items) {
|
|
43
|
+
return items.flatMap(item => 'field' in item ? [item.field] : 'tabs' in item ? item.tabs.flatMap(tab => names(tab.items)) : names(item.items));
|
|
44
|
+
}
|
|
45
|
+
function Items({ items, fields }) {
|
|
46
|
+
return React.createElement(React.Fragment, null, items.map((item, index) => {
|
|
47
|
+
if ('field' in item) {
|
|
48
|
+
const field = fields.get(item.field);
|
|
49
|
+
return field ? React.createElement("div", { key: index, "data-layout-field": item.field, "data-layout-error": !!field.errors }, field.content) : null;
|
|
50
|
+
}
|
|
51
|
+
if ('tabs' in item)
|
|
52
|
+
return React.createElement(Tabs, { key: index, tabs: item.tabs, fields: fields });
|
|
53
|
+
if ('group' in item)
|
|
54
|
+
return React.createElement("fieldset", { className: "block-editor-layout-group", key: index },
|
|
55
|
+
React.createElement("legend", null, item.group),
|
|
56
|
+
React.createElement(Items, { items: item.items, fields: fields }));
|
|
57
|
+
return React.createElement("div", { key: index, className: "block-editor-layout-grid", style: { '--editor-columns': item.grid } },
|
|
58
|
+
React.createElement(Items, { items: item.items, fields: fields }));
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
function Tabs({ tabs, fields }) {
|
|
62
|
+
const { reveal, disabled } = React.useContext(Environment);
|
|
63
|
+
const [selected, select] = React.useState(0);
|
|
64
|
+
const [id] = React.useState(() => `block-editor-layout-${++nextId}`);
|
|
65
|
+
const buttons = React.useRef([]);
|
|
66
|
+
const counts = tabs.map(tab => names(tab.items).reduce((total, name) => { var _a, _b; return total + ((_b = (_a = fields.get(name)) === null || _a === void 0 ? void 0 : _a.errors) !== null && _b !== void 0 ? _b : 0); }, 0));
|
|
67
|
+
const errors = counts.join(',');
|
|
68
|
+
const handled = React.useRef(0);
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (reveal <= handled.current)
|
|
71
|
+
return;
|
|
72
|
+
const first = counts.findIndex(count => count > 0);
|
|
73
|
+
if (first >= 0) {
|
|
74
|
+
handled.current = reveal;
|
|
75
|
+
select(first);
|
|
76
|
+
}
|
|
77
|
+
// Wait for delayed RJSF error propagation, then consume this save attempt once.
|
|
78
|
+
}, [reveal, errors]);
|
|
79
|
+
const current = Math.min(selected, tabs.length - 1);
|
|
80
|
+
return React.createElement("div", { className: "block-editor-layout-tabs", "data-layout-errors": errors },
|
|
81
|
+
React.createElement("div", { role: "tablist", "aria-label": "\u30D5\u30A9\u30FC\u30E0\u306E\u9805\u76EE" }, tabs.map((tab, index) => React.createElement("button", { key: index, ref: element => { buttons.current[index] = element; }, type: "button", role: "tab", id: `${id}-tab-${index}`, "aria-controls": `${id}-panel-${index}`, "aria-selected": current === index, disabled: disabled, tabIndex: current === index ? 0 : -1, onClick: () => select(index), onKeyDown: event => {
|
|
82
|
+
var _a;
|
|
83
|
+
if (event.nativeEvent.isComposing || event.keyCode === 229)
|
|
84
|
+
return;
|
|
85
|
+
let target;
|
|
86
|
+
switch (event.key) {
|
|
87
|
+
case 'ArrowRight':
|
|
88
|
+
target = (index + 1) % tabs.length;
|
|
89
|
+
break;
|
|
90
|
+
case 'ArrowLeft':
|
|
91
|
+
target = (index + tabs.length - 1) % tabs.length;
|
|
92
|
+
break;
|
|
93
|
+
case 'Home':
|
|
94
|
+
target = 0;
|
|
95
|
+
break;
|
|
96
|
+
case 'End':
|
|
97
|
+
target = tabs.length - 1;
|
|
98
|
+
break;
|
|
99
|
+
default: return;
|
|
100
|
+
}
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
event.stopPropagation();
|
|
103
|
+
select(target);
|
|
104
|
+
(_a = buttons.current[target]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
105
|
+
} },
|
|
106
|
+
tab.label,
|
|
107
|
+
counts[index] ? React.createElement("span", { className: "editor-form-error-badge", "aria-label": `エラー ${counts[index]} 件` }, counts[index]) : null))),
|
|
108
|
+
tabs.map((tab, index) => React.createElement("div", { key: index, role: "tabpanel", id: `${id}-panel-${index}`, "aria-labelledby": `${id}-tab-${index}`, hidden: current !== index },
|
|
109
|
+
React.createElement(Items, { items: tab.items, fields: fields }))));
|
|
110
|
+
}
|
|
111
|
+
/** Share layout behavior while leaving each renderer in charge of its controls. */
|
|
112
|
+
export function ObjectLayout({ ui, fields, knownFields, fallback }) {
|
|
113
|
+
const { enabled } = React.useContext(Environment);
|
|
114
|
+
const resolution = enabled ? resolveEditorLayout(ui, knownFields) : { kind: 'absent' };
|
|
115
|
+
if (resolution.kind === 'absent')
|
|
116
|
+
return React.createElement(React.Fragment, null, fallback);
|
|
117
|
+
if (resolution.kind === 'invalid')
|
|
118
|
+
return React.createElement(React.Fragment, null,
|
|
119
|
+
React.createElement("p", { role: "status", "data-layout-diagnostic": "" },
|
|
120
|
+
"\u30EC\u30A4\u30A2\u30A6\u30C8\u3092\u9069\u7528\u3067\u304D\u307E\u305B\u3093: ",
|
|
121
|
+
resolution.diagnostic),
|
|
122
|
+
fallback);
|
|
123
|
+
const map = new Map(fields.map(field => [field.name, field]));
|
|
124
|
+
return React.createElement(React.Fragment, null,
|
|
125
|
+
React.createElement(Items, { items: resolution.items, fields: map }),
|
|
126
|
+
fields.filter(field => !resolution.fields.has(field.name)).map(field => React.createElement("div", { key: field.name, "data-layout-field": field.name, "data-layout-error": !!field.errors }, field.content)));
|
|
127
|
+
}
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** JSON-only presentation metadata. Field names are literal immediate object keys, never paths. */
|
|
2
|
+
export type EditorLayoutItem = {
|
|
3
|
+
readonly field: string;
|
|
4
|
+
} | {
|
|
5
|
+
readonly group: string;
|
|
6
|
+
readonly items: readonly EditorLayoutItem[];
|
|
7
|
+
} | {
|
|
8
|
+
readonly grid: number;
|
|
9
|
+
readonly items: readonly EditorLayoutItem[];
|
|
10
|
+
} | {
|
|
11
|
+
readonly tabs: readonly {
|
|
12
|
+
readonly label: string;
|
|
13
|
+
readonly items: readonly EditorLayoutItem[];
|
|
14
|
+
}[];
|
|
15
|
+
};
|
|
16
|
+
export type EditorLayout = {
|
|
17
|
+
readonly version: 1;
|
|
18
|
+
readonly items: readonly EditorLayoutItem[];
|
|
19
|
+
};
|
|
20
|
+
export type LayoutResolution = {
|
|
21
|
+
readonly kind: 'absent';
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: 'invalid';
|
|
24
|
+
readonly diagnostic: string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: 'layout';
|
|
27
|
+
readonly items: readonly EditorLayoutItem[];
|
|
28
|
+
readonly fields: ReadonlySet<string>;
|
|
29
|
+
};
|
|
30
|
+
/** Validate the whole declaration before moving any controls. Invalid metadata never hides fields. */
|
|
31
|
+
export declare function resolveEditorLayout(ui: unknown, knownFields: readonly string[]): LayoutResolution;
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
2
|
+
/** Validate the whole declaration before moving any controls. Invalid metadata never hides fields. */
|
|
3
|
+
export function resolveEditorLayout(ui, knownFields) {
|
|
4
|
+
if (!record(ui) || !record(ui['ui:options']) || !Object.hasOwn(ui['ui:options'], 'extensionDataLayout'))
|
|
5
|
+
return { kind: 'absent' };
|
|
6
|
+
const input = ui['ui:options'].extensionDataLayout;
|
|
7
|
+
const known = new Set(knownFields), fields = new Set();
|
|
8
|
+
let count = 0;
|
|
9
|
+
const fail = (message) => { throw new TypeError(message); };
|
|
10
|
+
const keys = (node, expected) => {
|
|
11
|
+
if (Object.keys(node).some(key => !expected.includes(key)))
|
|
12
|
+
fail('Unknown layout option.');
|
|
13
|
+
};
|
|
14
|
+
const label = (text) => typeof text === 'string' && text.trim().length > 0 && text.length <= 128 ? text : fail('Invalid layout label.');
|
|
15
|
+
const items = (value, depth) => {
|
|
16
|
+
if (depth > 8 || !Array.isArray(value) || value.length > 256)
|
|
17
|
+
return fail('Layout limit exceeded.');
|
|
18
|
+
return value.map((node) => {
|
|
19
|
+
if (++count > 256 || !record(node))
|
|
20
|
+
return fail('Invalid layout item or limit exceeded.');
|
|
21
|
+
if (Object.hasOwn(node, 'field')) {
|
|
22
|
+
keys(node, ['field']);
|
|
23
|
+
if (typeof node.field !== 'string' || !known.has(node.field))
|
|
24
|
+
return fail('Unknown layout field.');
|
|
25
|
+
if (fields.has(node.field))
|
|
26
|
+
return fail('Duplicate layout field.');
|
|
27
|
+
fields.add(node.field);
|
|
28
|
+
return { field: node.field };
|
|
29
|
+
}
|
|
30
|
+
if (Object.hasOwn(node, 'group')) {
|
|
31
|
+
keys(node, ['group', 'items']);
|
|
32
|
+
return { group: label(node.group), items: items(node.items, depth + 1) };
|
|
33
|
+
}
|
|
34
|
+
if (Object.hasOwn(node, 'grid')) {
|
|
35
|
+
keys(node, ['grid', 'items']);
|
|
36
|
+
if (typeof node.grid !== 'number' || !Number.isInteger(node.grid) || node.grid < 1 || node.grid > 4)
|
|
37
|
+
return fail('Grid columns must be 1–4.');
|
|
38
|
+
return { grid: node.grid, items: items(node.items, depth + 1) };
|
|
39
|
+
}
|
|
40
|
+
if (Object.hasOwn(node, 'tabs')) {
|
|
41
|
+
keys(node, ['tabs']);
|
|
42
|
+
if (!Array.isArray(node.tabs) || !node.tabs.length || node.tabs.length > 16)
|
|
43
|
+
return fail('Invalid tab count.');
|
|
44
|
+
return { tabs: node.tabs.map((tab) => {
|
|
45
|
+
if (++count > 256 || !record(tab))
|
|
46
|
+
return fail('Invalid tab or limit exceeded.');
|
|
47
|
+
keys(tab, ['label', 'items']);
|
|
48
|
+
return { label: label(tab.label), items: items(tab.items, depth + 1) };
|
|
49
|
+
}) };
|
|
50
|
+
}
|
|
51
|
+
return fail('Unknown layout item.');
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
try {
|
|
55
|
+
if (!record(input) || input.version !== 1)
|
|
56
|
+
return fail('Unsupported layout version.');
|
|
57
|
+
keys(input, ['version', 'items']);
|
|
58
|
+
return { kind: 'layout', items: items(input.items, 0), fields };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return { kind: 'invalid', diagnostic: error instanceof Error ? error.message : 'Invalid layout.' };
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/rjsf-templates.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { ArrayFieldTemplateItemType, FieldTemplateProps, IconButtonProps, ObjectFieldTemplateProps, WrapIfAdditionalTemplateProps } from '@rjsf/utils';
|
|
2
|
+
import type { ArrayFieldTemplateItemType, ArrayFieldTemplateProps, ArrayFieldTitleProps, DescriptionFieldProps, FieldTemplateProps, IconButtonProps, ObjectFieldTemplateProps, WidgetProps, WrapIfAdditionalTemplateProps } from '@rjsf/utils';
|
|
3
|
+
/** Reuse the v5 object template's title, description and add-property controls. */
|
|
4
|
+
export declare function layoutObjectTemplate(DefaultTemplate: React.ComponentType<ObjectFieldTemplateProps>): (props: ObjectFieldTemplateProps) => React.ReactElement;
|
|
5
|
+
export declare function compactDescriptionTemplate(DefaultTemplate: React.ComponentType<DescriptionFieldProps>): (props: DescriptionFieldProps) => React.ReactElement;
|
|
6
|
+
export declare function compactArrayTitleTemplate(DefaultTemplate: React.ComponentType<ArrayFieldTitleProps>): (props: ArrayFieldTitleProps) => React.ReactElement;
|
|
7
|
+
export declare function compactArrayTemplate(DefaultTemplate: React.ComponentType<ArrayFieldTemplateProps>): (props: ArrayFieldTemplateProps) => React.ReactElement;
|
|
8
|
+
/** Boolean widgets own their visible label instead of using the surrounding FieldTemplate. */
|
|
9
|
+
export declare function compactCheckboxWidget(DefaultWidget: React.ComponentType<WidgetProps>): (props: WidgetProps) => React.ReactElement;
|
|
3
10
|
/** Keep the property key out of the value's visible label. */
|
|
4
11
|
export declare function valueFieldTemplate(DefaultTemplate: React.ComponentType<FieldTemplateProps>, flag: string): (props: FieldTemplateProps) => React.ReactElement;
|
|
5
12
|
/** Keep RJSF's object layout while numbering each editable additional property. */
|
package/dist/rjsf-templates.js
CHANGED
|
@@ -1,11 +1,81 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { ObjectLayout, useCommonLayout } from './layout-view.js';
|
|
3
|
+
import { resolveEditorLayout } from './layout.js';
|
|
4
|
+
import { FormHelp, propertyLabel } from './form-presentation.js';
|
|
2
5
|
const ItemNumber = React.createContext(1);
|
|
3
6
|
const PropertyKey = React.createContext(undefined);
|
|
7
|
+
function errorCount(value) {
|
|
8
|
+
if (!value || typeof value !== 'object')
|
|
9
|
+
return 0;
|
|
10
|
+
if (Array.isArray(value))
|
|
11
|
+
return value.length;
|
|
12
|
+
return Object.values(value).reduce((count, child) => count + errorCount(child), 0);
|
|
13
|
+
}
|
|
14
|
+
/** Reuse the v5 object template's title, description and add-property controls. */
|
|
15
|
+
export function layoutObjectTemplate(DefaultTemplate) {
|
|
16
|
+
return function LayoutObject(props) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
const enabled = useCommonLayout();
|
|
19
|
+
const title = enabled && !props.schema.title && !((_a = props.uiSchema) === null || _a === void 0 ? void 0 : _a['ui:title']) ? propertyLabel(props.title) : props.title;
|
|
20
|
+
const view = { ...props, title };
|
|
21
|
+
const knownFields = [...new Set([...Object.keys((_b = props.schema.properties) !== null && _b !== void 0 ? _b : {}), ...props.properties.map(property => property.name)])];
|
|
22
|
+
const resolution = enabled ? resolveEditorLayout(props.uiSchema, knownFields) : { kind: 'absent' };
|
|
23
|
+
if (resolution.kind === 'absent')
|
|
24
|
+
return React.createElement(DefaultTemplate, { ...view });
|
|
25
|
+
if (resolution.kind === 'invalid')
|
|
26
|
+
return React.createElement(React.Fragment, null,
|
|
27
|
+
React.createElement("p", { role: "status", "data-layout-diagnostic": "" },
|
|
28
|
+
"\u30EC\u30A4\u30A2\u30A6\u30C8\u3092\u9069\u7528\u3067\u304D\u307E\u305B\u3093: ",
|
|
29
|
+
resolution.diagnostic),
|
|
30
|
+
React.createElement(DefaultTemplate, { ...props }));
|
|
31
|
+
const content = React.createElement(ObjectLayout, { key: "__editor_layout__", ui: props.uiSchema, knownFields: knownFields, fallback: null, fields: props.properties.map(property => {
|
|
32
|
+
var _a;
|
|
33
|
+
return ({
|
|
34
|
+
name: property.name, content: property.content, errors: errorCount((_a = props.errorSchema) === null || _a === void 0 ? void 0 : _a[property.name])
|
|
35
|
+
});
|
|
36
|
+
}) });
|
|
37
|
+
return React.createElement(DefaultTemplate, { ...view, properties: [{ name: '__editor_layout__', content, hidden: false, disabled: !!props.disabled, readonly: !!props.readonly }] });
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function compactDescriptionTemplate(DefaultTemplate) {
|
|
41
|
+
return function CompactDescription(props) {
|
|
42
|
+
const enabled = useCommonLayout();
|
|
43
|
+
return enabled && typeof props.description === 'string' ? React.createElement(FormHelp, { id: props.id, description: props.description }) : React.createElement(DefaultTemplate, { ...props });
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function compactArrayTitleTemplate(DefaultTemplate) {
|
|
47
|
+
return function CompactArrayTitle(props) {
|
|
48
|
+
var _a;
|
|
49
|
+
const enabled = useCommonLayout();
|
|
50
|
+
if (!enabled || !props.title || props.schema.title || ((_a = props.uiSchema) === null || _a === void 0 ? void 0 : _a['ui:title']))
|
|
51
|
+
return React.createElement(DefaultTemplate, { ...props });
|
|
52
|
+
return React.createElement(DefaultTemplate, { ...props, title: propertyLabel(props.title) });
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function compactArrayTemplate(DefaultTemplate) {
|
|
56
|
+
return function CompactArray(props) {
|
|
57
|
+
const enabled = useCommonLayout();
|
|
58
|
+
return React.createElement(React.Fragment, null,
|
|
59
|
+
React.createElement(DefaultTemplate, { ...props }),
|
|
60
|
+
enabled && props.items.length === 0 && React.createElement("p", { className: "editor-form-hint" }, "\u9805\u76EE\u306A\u3057"));
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Boolean widgets own their visible label instead of using the surrounding FieldTemplate. */
|
|
64
|
+
export function compactCheckboxWidget(DefaultWidget) {
|
|
65
|
+
return function CompactCheckbox(props) {
|
|
66
|
+
var _a;
|
|
67
|
+
const enabled = useCommonLayout();
|
|
68
|
+
const label = enabled && !props.schema.title && !((_a = props.uiSchema) === null || _a === void 0 ? void 0 : _a['ui:title']) && !props.options.title ? propertyLabel(props.label) : props.label;
|
|
69
|
+
return React.createElement(DefaultWidget, { ...props, label: label });
|
|
70
|
+
};
|
|
71
|
+
}
|
|
4
72
|
/** Keep the property key out of the value's visible label. */
|
|
5
73
|
export function valueFieldTemplate(DefaultTemplate, flag) {
|
|
6
74
|
return function ValueField(props) {
|
|
75
|
+
var _a;
|
|
76
|
+
const enabled = useCommonLayout();
|
|
7
77
|
if (!(flag in props.schema))
|
|
8
|
-
return React.createElement(DefaultTemplate, { ...props });
|
|
78
|
+
return React.createElement(DefaultTemplate, { ...props, label: enabled && !props.schema.title && !((_a = props.uiSchema) === null || _a === void 0 ? void 0 : _a['ui:title']) ? propertyLabel(props.label) : props.label });
|
|
9
79
|
return React.createElement(PropertyKey.Provider, { value: props.label },
|
|
10
80
|
React.createElement(DefaultTemplate, { ...props, label: "\u5024" }));
|
|
11
81
|
};
|
package/dist/rjsf.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { EditorFeatureFlags } from './config/feature-flags.js';
|
|
2
|
+
export type { EditorFeatureFlags } from './config/feature-flags.js';
|
|
3
|
+
export type { EditorLayout, EditorLayoutItem } from './layout.js';
|
|
1
4
|
import type { FixedInputMessageSource } from './fixed-input-view.js';
|
|
2
5
|
export type { FixedInputViewMessages, FixedInputMessageSource } from './fixed-input-view.js';
|
|
3
6
|
import type { FixedInputEditorOptions } from './fixed-input-editor.js';
|
|
@@ -9,11 +12,11 @@ export { createSourceEditor } from './source-editor.js';
|
|
|
9
12
|
export type * from './source-types.js';
|
|
10
13
|
export type * from './prepared-types.js';
|
|
11
14
|
import type { RJSFSchema } from '@rjsf/utils';
|
|
12
|
-
import type { SchemaBundle, SchemaValidationContext, SyncOwnerValidator } from './schema-types.js';
|
|
15
|
+
import type { SchemaBundle, SchemaJsonObject, SchemaValidationContext, SyncOwnerValidator } from './schema-types.js';
|
|
13
16
|
import type { SourceFormat } from './codec.js';
|
|
14
17
|
import { EditorRegistry } from './registry.js';
|
|
15
18
|
import { EDITOR_API_VERSION } from './types.js';
|
|
16
|
-
import type { BlockSnapshot, EditorDefinition,
|
|
19
|
+
import type { BlockSnapshot, EditorDefinition, JsonValue, ValidationIssue } from './types.js';
|
|
17
20
|
export interface SchemaEditorOptions {
|
|
18
21
|
readonly apiVersion: typeof EDITOR_API_VERSION;
|
|
19
22
|
readonly extensionId: string;
|
|
@@ -24,7 +27,7 @@ export interface SchemaEditorOptions {
|
|
|
24
27
|
/** Defaults to JSON for compatibility with existing registrations. */
|
|
25
28
|
readonly format?: SourceFormat;
|
|
26
29
|
readonly schema: RJSFSchema;
|
|
27
|
-
readonly uiSchema?:
|
|
30
|
+
readonly uiSchema?: SchemaJsonObject;
|
|
28
31
|
/** Optional business validation, always run after schema validation when saving. */
|
|
29
32
|
validateData?(data: JsonValue, snapshot: BlockSnapshot): readonly ValidationIssue[] | Promise<readonly ValidationIssue[]>;
|
|
30
33
|
}
|
|
@@ -34,7 +37,7 @@ export interface SchemaBundleEditorOptions<C = null> extends Omit<SchemaEditorOp
|
|
|
34
37
|
readonly validationContext: () => SchemaValidationContext<C>;
|
|
35
38
|
readonly ownerValidate?: SyncOwnerValidator<C>;
|
|
36
39
|
}
|
|
37
|
-
export declare function createSchemaBundleEditor<C>(options: SchemaBundleEditorOptions<C
|
|
40
|
+
export declare function createSchemaBundleEditor<C>(options: SchemaBundleEditorOptions<C>, features?: EditorFeatureFlags): EditorDefinition;
|
|
38
41
|
export interface BlockEditorRegistrationAPI {
|
|
39
42
|
readonly apiVersion: typeof EDITOR_API_VERSION;
|
|
40
43
|
register(definition: EditorDefinition | SchemaEditorOptions): () => void;
|
|
@@ -60,21 +63,21 @@ export interface BlockEditorService {
|
|
|
60
63
|
subscribe(listener: () => void): () => void;
|
|
61
64
|
}
|
|
62
65
|
/** A host owns the service's lifetime; this module never stores VM or workspace instances. */
|
|
63
|
-
export declare function createBlockEditorService(options: {
|
|
66
|
+
export declare function createBlockEditorService(options: EditorFeatureFlags & {
|
|
64
67
|
readonly fixedInputs: true;
|
|
65
68
|
readonly fixedInputMessages?: FixedInputMessageSource;
|
|
66
69
|
}): BlockEditorService & {
|
|
67
70
|
readonly api: FixedBlockEditorRegistrationAPI;
|
|
68
71
|
};
|
|
69
|
-
export declare function createBlockEditorService(options?: {
|
|
72
|
+
export declare function createBlockEditorService(options?: EditorFeatureFlags & {
|
|
70
73
|
readonly fixedInputs?: false;
|
|
71
74
|
readonly fixedInputMessages?: FixedInputMessageSource;
|
|
72
75
|
}): BlockEditorService & {
|
|
73
76
|
readonly api: SourceBlockEditorRegistrationAPI;
|
|
74
77
|
};
|
|
75
|
-
export declare function createBlockEditorService(options: {
|
|
78
|
+
export declare function createBlockEditorService(options: EditorFeatureFlags & {
|
|
76
79
|
readonly fixedInputs?: boolean;
|
|
77
80
|
readonly fixedInputMessages?: FixedInputMessageSource;
|
|
78
81
|
}): BlockEditorService;
|
|
79
82
|
/** Build an editor with JSON/YAML serialization, schema validation and the standard RJSF body. */
|
|
80
|
-
export declare function createSchemaEditor(options: SchemaEditorOptions): EditorDefinition;
|
|
83
|
+
export declare function createSchemaEditor(options: SchemaEditorOptions, features?: EditorFeatureFlags): EditorDefinition;
|