@kubohiroya/turbowarp-extension-data-editor 0.2.0 → 0.2.2

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.
Files changed (68) hide show
  1. package/README.md +9 -6
  2. package/dist/build-info.json +3 -3
  3. package/dist/fixed-input-branches.d.ts +12 -0
  4. package/dist/fixed-input-branches.js +180 -0
  5. package/dist/fixed-input-editor.d.ts +39 -0
  6. package/dist/fixed-input-editor.js +538 -0
  7. package/dist/fixed-input-schema.d.ts +15 -0
  8. package/dist/fixed-input-schema.js +21 -0
  9. package/dist/fixed-input-snapshot.d.ts +7 -0
  10. package/dist/fixed-input-snapshot.js +180 -0
  11. package/dist/fixed-input-types.d.ts +138 -0
  12. package/dist/fixed-input-types.js +23 -0
  13. package/dist/fixed-input-view.d.ts +14 -0
  14. package/dist/fixed-input-view.js +168 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.js +4 -0
  17. package/dist/react.css +168 -0
  18. package/dist/react.d.ts +8 -0
  19. package/dist/react.js +19 -2
  20. package/dist/registry.d.ts +4 -0
  21. package/dist/registry.js +45 -7
  22. package/dist/rjsf-templates.d.ts +11 -0
  23. package/dist/rjsf-templates.js +129 -0
  24. package/dist/rjsf.d.ts +24 -1
  25. package/dist/rjsf.js +35 -3
  26. package/dist/schema.d.ts +2 -0
  27. package/dist/schema.js +1 -0
  28. package/dist/session.d.ts +3 -1
  29. package/dist/session.js +205 -7
  30. package/dist/turbowarp/bridge.d.ts +2 -1
  31. package/dist/turbowarp/bridge.js +37 -5
  32. package/dist/turbowarp/fixed-fields.d.ts +82 -0
  33. package/dist/turbowarp/fixed-fields.js +311 -0
  34. package/dist/turbowarp/fixed-transaction.d.ts +18 -0
  35. package/dist/turbowarp/fixed-transaction.js +460 -0
  36. package/dist/turbowarp/host.d.ts +3 -8
  37. package/dist/turbowarp/host.js +68 -1
  38. package/dist/turbowarp.d.ts +4 -0
  39. package/dist/turbowarp.js +1 -1
  40. package/dist/types.d.ts +5 -0
  41. package/dist/view.js +4 -0
  42. package/dist/webpack4/index.js +22 -5
  43. package/dist/webpack4/metafile.json +592 -299
  44. package/dist/webpack4/react.js +20 -6
  45. package/dist/webpack4/react.js.map +2 -2
  46. package/dist/webpack4/rjsf.js +450 -60
  47. package/dist/webpack4/rjsf.js.map +4 -4
  48. package/dist/webpack4/schema.js +6 -3
  49. package/dist/webpack4/{shared-KQOUMWJ4.js → shared-75QIWLZY.js} +1129 -1244
  50. package/dist/webpack4/{shared-KQOUMWJ4.js.map → shared-75QIWLZY.js.map} +4 -4
  51. package/dist/webpack4/shared-FYUILHED.js +1 -0
  52. package/dist/webpack4/shared-GQLPPRT2.js +210 -0
  53. package/dist/webpack4/shared-GQLPPRT2.js.map +7 -0
  54. package/dist/webpack4/shared-P2JBRZFP.js +2206 -0
  55. package/dist/webpack4/shared-P2JBRZFP.js.map +7 -0
  56. package/dist/webpack4/turbowarp.js +913 -9
  57. package/dist/webpack4/turbowarp.js.map +4 -4
  58. package/docs/api.md +7 -0
  59. package/docs/fixed-input-contracts.md +77 -0
  60. package/docs/package-distribution.md +5 -0
  61. package/docs/stage-2-publication.ja.md +6 -0
  62. package/package.json +2 -1
  63. package/dist/webpack4/shared-CQB3NG2A.js +0 -1
  64. package/dist/webpack4/shared-MRPGBJIH.js +0 -75
  65. package/dist/webpack4/shared-MRPGBJIH.js.map +0 -7
  66. package/dist/webpack4/shared-N6YQE2X6.js +0 -1029
  67. package/dist/webpack4/shared-N6YQE2X6.js.map +0 -7
  68. /package/dist/webpack4/{shared-CQB3NG2A.js.map → shared-FYUILHED.js.map} +0 -0
package/dist/react.css CHANGED
@@ -8,6 +8,31 @@
8
8
  width: min(900px, calc(100vw - 32px));
9
9
  }
10
10
 
11
+ .modal-content .header {
12
+ height: auto;
13
+ min-height: 3.5rem;
14
+ align-items: center;
15
+ }
16
+
17
+ .modal-content .header > div:first-child {
18
+ flex: 1 1 0;
19
+ min-width: 0;
20
+ margin: 0;
21
+ overflow-wrap: anywhere;
22
+ text-align: center;
23
+ }
24
+
25
+ .modal-content .header > div:last-child {
26
+ flex: 0 0 auto;
27
+ }
28
+
29
+ .modal-content .header img {
30
+ flex-shrink: 0;
31
+ width: 1.75rem;
32
+ height: 1.75rem;
33
+ object-fit: contain;
34
+ }
35
+
11
36
  .tree {
12
37
  min-width: 0;
13
38
  }
@@ -152,6 +177,107 @@
152
177
  margin-bottom: 1rem;
153
178
  }
154
179
 
180
+ .body :global([data-block-editor-item]) {
181
+ box-sizing: border-box;
182
+ min-width: 0;
183
+ margin: 0.75rem 0;
184
+ padding: 1rem;
185
+ border: 1px solid var(--block-editor-border, rgba(0, 0, 0, 0.15));
186
+ border-radius: var(--block-editor-radius, 0.5rem);
187
+ }
188
+
189
+ .body :global([data-block-editor-item-heading]),
190
+ .body :global([data-block-editor-item-actions]) {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 0.5rem;
194
+ }
195
+
196
+ .body :global([data-block-editor-item-heading]) {
197
+ justify-content: space-between;
198
+ margin-bottom: 0.75rem;
199
+ font-weight: bold;
200
+ }
201
+
202
+ .body :global([data-block-editor-item]) input,
203
+ .body :global([data-block-editor-item]) select,
204
+ .body :global([data-block-editor-item]) textarea {
205
+ box-sizing: border-box;
206
+ }
207
+
208
+ .body :global([data-block-editor-pair-row]) {
209
+ display: grid;
210
+ grid-template-columns: 3rem minmax(0, 1fr);
211
+ align-items: center;
212
+ gap: 0.5rem 0.75rem;
213
+ margin-top: 0.5rem;
214
+ }
215
+
216
+ .body :global([data-block-editor-pair-row]) > label {
217
+ grid-column: 1;
218
+ margin: 0;
219
+ }
220
+
221
+ .body :global([data-block-editor-pair-row]) > :not(label) {
222
+ grid-column: 2;
223
+ min-width: 0;
224
+ }
225
+
226
+ .body :global([data-block-editor-pair-row]) input:not([type="checkbox"]):not([type="radio"]),
227
+ .body :global([data-block-editor-pair-row]) select,
228
+ .body :global([data-block-editor-pair-row]) textarea {
229
+ margin-top: 0;
230
+ }
231
+
232
+ .body :global([data-block-editor-item-actions]) button,
233
+ .body :global([data-block-editor-item-menu]) button,
234
+ .body :global([data-block-editor-add-item]) {
235
+ min-height: 2.25rem;
236
+ padding: 0.5rem 0.75rem;
237
+ border: 1px solid var(--block-editor-border, rgba(0, 0, 0, 0.15));
238
+ border-radius: var(--block-editor-radius, 0.5rem);
239
+ color: var(--block-editor-text, #333);
240
+ background: var(--block-editor-button, white);
241
+ cursor: pointer;
242
+ font: inherit;
243
+ }
244
+
245
+ .body :global([data-block-editor-item-menu]) {
246
+ position: relative;
247
+ flex-shrink: 0;
248
+ }
249
+
250
+ .body :global([data-block-editor-item-menu]) :global([role="menu"]) {
251
+ position: absolute;
252
+ inset-inline-end: 0;
253
+ top: 100%;
254
+ z-index: 1;
255
+ min-width: max-content;
256
+ padding: 0.25rem;
257
+ background: var(--block-editor-background, white);
258
+ border: 1px solid var(--block-editor-border, rgba(0, 0, 0, 0.15));
259
+ border-radius: var(--block-editor-radius, 0.5rem);
260
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
261
+ }
262
+
263
+ .body :global([data-block-editor-item-menu]) :global([role="menuitem"]) {
264
+ width: 100%;
265
+ border: 0;
266
+ text-align: start;
267
+ }
268
+
269
+ .body :global([data-block-editor-item-actions]) button:disabled,
270
+ .body :global([data-block-editor-add-item]):disabled {
271
+ opacity: 0.5;
272
+ cursor: default;
273
+ }
274
+
275
+ .body :global([data-block-editor-item-menu]) button:focus-visible,
276
+ .body :global([data-block-editor-add-item]):focus-visible {
277
+ outline: 2px solid currentColor;
278
+ outline-offset: 2px;
279
+ }
280
+
155
281
  /* The adapter marks only controls belonging to the field's own RJSF errors. */
156
282
  .body :global(.rjsf) input:global([data-block-editor-invalid="true"]),
157
283
  .body :global(.rjsf) select:global([data-block-editor-invalid="true"]),
@@ -181,3 +307,45 @@
181
307
  outline: 2px solid currentColor;
182
308
  outline-offset: 2px;
183
309
  }
310
+
311
+ .body :global(.block-editor-fixed-inputs) > fieldset {
312
+ margin: 0 0 1rem;
313
+ padding: 0.75rem;
314
+ border: 1px solid var(--block-editor-border, #ccc);
315
+ border-radius: var(--block-editor-radius, 0.5rem);
316
+ }
317
+
318
+ .body :global(.block-editor-fixed-inputs) output {
319
+ white-space: pre-wrap;
320
+ overflow-wrap: anywhere;
321
+ }
322
+
323
+ .body :global(.block-editor-fixed-color) {
324
+ display: flex;
325
+ align-items: center;
326
+ gap: 0.5rem;
327
+ }
328
+
329
+ .body :global(.block-editor-fixed-color) input[type="text"] {
330
+ flex: 1;
331
+ min-width: 0;
332
+ }
333
+
334
+ .body :global(.block-editor-fixed-color) input[type="color"] {
335
+ width: 3rem;
336
+ min-width: 3rem;
337
+ height: 2.75rem;
338
+ padding: 0.25rem;
339
+ box-sizing: border-box;
340
+ }
341
+
342
+ .body :global(.block-editor-fixed-readonly),
343
+ .body :global(.block-editor-fixed-unverified) {
344
+ margin-bottom: 0;
345
+ font-size: 0.875em;
346
+ }
347
+
348
+ .body :global(.block-editor-fixed-error) {
349
+ color: var(--block-editor-error-text, #a61b1b);
350
+ margin-bottom: 0;
351
+ }
package/dist/react.d.ts CHANGED
@@ -10,7 +10,11 @@ export interface DialogSessionState {
10
10
  readonly error: unknown;
11
11
  }
12
12
  export interface DialogSession {
13
+ readonly reference?: {
14
+ readonly blockId: string;
15
+ };
13
16
  getState(): DialogSessionState;
17
+ getFixedInputView?(): unknown;
14
18
  subscribe(listener: () => void): () => void;
15
19
  mount?(container: HTMLElement): () => void;
16
20
  getNodes?(): readonly DescendantNodeDescriptor[];
@@ -29,6 +33,8 @@ export interface EditorDialogMessages {
29
33
  readonly conflict: string;
30
34
  readonly failed: string;
31
35
  readonly structuralNode: string;
36
+ /** Fixed forms retain technical diagnostics in the API while showing a localized summary. */
37
+ readonly invalidInput?: string;
32
38
  }
33
39
  export interface EditorNodeAppearance {
34
40
  readonly name?: string;
@@ -40,6 +46,8 @@ export interface EditorModalProps {
40
46
  readonly id: string;
41
47
  readonly className: string;
42
48
  readonly contentLabel: string;
49
+ readonly headerImage?: string;
50
+ readonly headerClassName?: string | undefined;
43
51
  readonly onRequestClose: () => void;
44
52
  readonly children?: React.ReactNode;
45
53
  }
package/dist/react.js CHANGED
@@ -203,10 +203,27 @@ export class BlockEditorDialog extends React.Component {
203
203
  }
204
204
  }
205
205
  render() {
206
+ var _a, _b, _c, _d;
206
207
  const { styles, messages, ModalComponent, session } = this.props;
207
208
  const saving = this.state.status === 'saving';
208
209
  const hierarchy = typeof session.getNodes === 'function';
209
- return React.createElement(ModalComponent, { className: `${styles.modalContent || ''} ${hierarchy ? styles.hierarchyModal : ''} ${this.props.className || ''}`, contentLabel: messages.title, id: "blockEditorModal", onRequestClose: this.handleClose },
210
+ let fixed = false;
211
+ if (session.getFixedInputView && (this.state.status === 'editing' || saving)) {
212
+ try {
213
+ fixed = Boolean(session.getFixedInputView());
214
+ }
215
+ catch (_e) {
216
+ fixed = true;
217
+ }
218
+ }
219
+ const root = (_a = session.getNodes) === null || _a === void 0 ? void 0 : _a.call(session).find(node => node.inputPath.length === 0);
220
+ const rootId = ((_b = session.reference) === null || _b === void 0 ? void 0 : _b.blockId) || (root === null || root === void 0 ? void 0 : root.reference.blockId);
221
+ const appearance = rootId ? (_c = this.props.nodeAppearances) === null || _c === void 0 ? void 0 : _c[rootId] : undefined;
222
+ const name = (appearance === null || appearance === void 0 ? void 0 : appearance.name) || (rootId ? (_d = this.props.nodeLabels) === null || _d === void 0 ? void 0 : _d[rootId] : undefined) || (root === null || root === void 0 ? void 0 : root.opcode);
223
+ const title = name ? messages.title.includes('{name}') ? messages.title.replace('{name}', () => name) :
224
+ /ブロック|block/i.test(messages.title) ? messages.title.replace(/ブロック|block/i, () => name) :
225
+ `${name}: ${messages.title}` : messages.title;
226
+ return React.createElement(ModalComponent, { className: `${styles.modalContent || ''} ${hierarchy ? styles.hierarchyModal : ''} ${this.props.className || ''}`, contentLabel: title, ...((appearance === null || appearance === void 0 ? void 0 : appearance.iconURI) ? { headerImage: appearance.iconURI } : {}), headerClassName: styles.header, id: "blockEditorModal", onRequestClose: this.handleClose },
210
227
  React.createElement("div", { "aria-busy": saving, className: styles.body, onCompositionStart: () => { this.composing = true; }, onCompositionEnd: () => { this.composing = false; }, onKeyDownCapture: event => {
211
228
  if (this.composing || event.nativeEvent.isComposing || event.keyCode === 229)
212
229
  event.stopPropagation();
@@ -215,7 +232,7 @@ export class BlockEditorDialog extends React.Component {
215
232
  React.createElement("div", { ref: this.handleBody }, hierarchy ? React.createElement(DescendantTree, { session: session, styles: styles, messages: messages, disabled: saving, nodeAppearances: this.props.nodeAppearances, nodeLabels: this.props.nodeLabels, onError: this.handleMountError, ref: tree => { this.tree = tree; } }) : null),
216
233
  React.createElement("div", { "aria-live": "polite", className: styles.errors, role: "status" },
217
234
  saving ? messages.saving : null,
218
- this.state.issues.map((issue, index) => {
235
+ fixed && this.state.issues.length ? React.createElement("p", null, messages.invalidInput || messages.failed) : this.state.issues.map((issue, index) => {
219
236
  var _a;
220
237
  return hierarchy && 'reference' in issue ?
221
238
  React.createElement("button", { className: styles.nodeError, "data-block-editor-issue": index, disabled: saving, key: index, type: "button", onClick: () => { var _a; return (_a = this.tree) === null || _a === void 0 ? void 0 : _a.reveal(issue); } },
@@ -3,11 +3,15 @@ import { DescendantSession } from './descendants.js';
3
3
  import type { DescendantLimits } from './descendant-types.js';
4
4
  import type { BlockEditorHost, BlockReference, EditorDefinition, EditorDescriptor } from './types.js';
5
5
  import type { PreparedEditorDefinition } from './prepared-types.js';
6
+ import type { FixedInputDeclaration, FixedInputEditorDefinition } from './fixed-input-types.js';
6
7
  export declare class EditorRegistry {
7
8
  #private;
8
9
  register(definition: EditorDefinition): () => void;
9
10
  registerPrepared(definition: PreparedEditorDefinition): () => void;
11
+ registerFixed<C = null>(definition: FixedInputEditorDefinition<C>): () => void;
10
12
  resolve(opcode: string): EditorDescriptor | undefined;
13
+ /** Stable, callback-free authority for this exact registration, never caller metadata. */
14
+ resolveFixed(opcode: string): FixedInputDeclaration | undefined;
11
15
  open(reference: BlockReference, host: BlockEditorHost): EditorSession | undefined;
12
16
  openDescendants(reference: BlockReference, host: BlockEditorHost, limits?: DescendantLimits): DescendantSession | undefined;
13
17
  }
package/dist/registry.js CHANGED
@@ -3,7 +3,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
3
3
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
4
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
5
  };
6
- var _EditorRegistry_instances, _EditorRegistry_editors, _EditorRegistry_opcodes, _EditorRegistry_changes, _EditorRegistry_register, _EditorRegistry_notifyChanges;
6
+ var _EditorRegistry_instances, _EditorRegistry_editors, _EditorRegistry_opcodes, _EditorRegistry_fixedDeclarations, _EditorRegistry_changes, _EditorRegistry_register, _EditorRegistry_notifyChanges;
7
7
  import { captureReference, captureSnapshot } from './data.js';
8
8
  import { EditorSession } from './session.js';
9
9
  import { DescendantSession } from './descendants.js';
@@ -11,14 +11,20 @@ import { supportsDescendantEditing } from './descendant-types.js';
11
11
  import { captureSubtree, descendantLimits } from './subtree.js';
12
12
  import { EDITOR_API_VERSION } from './types.js';
13
13
  import { isPreparedEditor } from './prepared-save.js';
14
+ import { isFixedInputEditor, captureFixedInputEditorDefinition } from './fixed-input-editor.js';
15
+ import { captureFixedInputDeclaration, captureFixedSnapshot } from './fixed-input-snapshot.js';
16
+ import { supportsFixedInputEditing } from './fixed-input-types.js';
14
17
  export class EditorRegistry {
15
18
  constructor() {
16
19
  _EditorRegistry_instances.add(this);
17
20
  _EditorRegistry_editors.set(this, new Map());
18
21
  _EditorRegistry_opcodes.set(this, new Map());
22
+ _EditorRegistry_fixedDeclarations.set(this, new Map());
19
23
  _EditorRegistry_changes.set(this, new Set());
20
24
  }
21
25
  register(definition) {
26
+ if (isFixedInputEditor(definition))
27
+ throw new TypeError('Fixed input editors require registerFixed().');
22
28
  if (isPreparedEditor(definition))
23
29
  throw new TypeError('Prepared editors require registerPrepared().');
24
30
  return __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_register).call(this, definition);
@@ -29,6 +35,9 @@ export class EditorRegistry {
29
35
  }
30
36
  return __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_register).call(this, definition);
31
37
  }
38
+ registerFixed(definition) {
39
+ return __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_register).call(this, captureFixedInputEditorDefinition(definition));
40
+ }
32
41
  resolve(opcode) {
33
42
  const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode);
34
43
  if (!entry)
@@ -38,15 +47,37 @@ export class EditorRegistry {
38
47
  opcodes: entry.opcodes, editableArguments: entry.editableArguments
39
48
  });
40
49
  }
50
+ /** Stable, callback-free authority for this exact registration, never caller metadata. */
51
+ resolveFixed(opcode) {
52
+ const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode);
53
+ return entry ? __classPrivateFieldGet(this, _EditorRegistry_fixedDeclarations, "f").get(entry) : undefined;
54
+ }
41
55
  open(reference, host) {
42
56
  const capturedReference = captureReference(reference);
43
57
  const live = host.readBlock(capturedReference);
44
58
  if (!live)
45
59
  return undefined;
60
+ const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(live.opcode);
61
+ if (!entry) {
62
+ // Preserve legacy snapshot validation even when no editor is registered.
63
+ captureSnapshot(live, capturedReference);
64
+ return undefined;
65
+ }
66
+ if (isFixedInputEditor(entry)) {
67
+ if (!supportsFixedInputEditing(host))
68
+ return undefined;
69
+ const fixed = host.fixedInputs.readBlock(capturedReference, entry);
70
+ if (!fixed || fixed.opcode !== live.opcode)
71
+ return undefined;
72
+ const snapshot = captureFixedSnapshot(fixed, capturedReference, entry);
73
+ return new EditorSession(entry, snapshot, host, () => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode) === entry, listener => {
74
+ __classPrivateFieldGet(this, _EditorRegistry_changes, "f").add(listener);
75
+ return () => { __classPrivateFieldGet(this, _EditorRegistry_changes, "f").delete(listener); };
76
+ });
77
+ }
46
78
  const snapshot = captureSnapshot(live, capturedReference);
47
- const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode);
48
- return entry ? new EditorSession(entry, snapshot, host, isPreparedEditor(entry)
49
- ? () => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode) === entry : undefined) : undefined;
79
+ return new EditorSession(entry, snapshot, host, isPreparedEditor(entry)
80
+ ? () => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(snapshot.opcode) === entry : undefined);
50
81
  }
51
82
  openDescendants(reference, host, limits) {
52
83
  if (!supportsDescendantEditing(host))
@@ -58,15 +89,19 @@ export class EditorRegistry {
58
89
  if (!live)
59
90
  return undefined;
60
91
  const captured = captureSubtree(live, capturedReference, resolvedLimits);
61
- if (!__classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").has(captured.snapshot.nodes[0].block.opcode))
92
+ const root = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(captured.snapshot.nodes[0].block.opcode);
93
+ if (!root || isFixedInputEditor(root))
62
94
  return undefined;
63
- return new DescendantSession(captured, port, opcode => __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode), listener => {
95
+ return new DescendantSession(captured, port, opcode => {
96
+ const entry = __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").get(opcode);
97
+ return entry && !isFixedInputEditor(entry) ? entry : undefined;
98
+ }, listener => {
64
99
  __classPrivateFieldGet(this, _EditorRegistry_changes, "f").add(listener);
65
100
  return () => { __classPrivateFieldGet(this, _EditorRegistry_changes, "f").delete(listener); };
66
101
  }, resolvedLimits);
67
102
  }
68
103
  }
69
- _EditorRegistry_editors = new WeakMap(), _EditorRegistry_opcodes = new WeakMap(), _EditorRegistry_changes = new WeakMap(), _EditorRegistry_instances = new WeakSet(), _EditorRegistry_register = function _EditorRegistry_register(definition) {
104
+ _EditorRegistry_editors = new WeakMap(), _EditorRegistry_opcodes = new WeakMap(), _EditorRegistry_fixedDeclarations = new WeakMap(), _EditorRegistry_changes = new WeakMap(), _EditorRegistry_instances = new WeakSet(), _EditorRegistry_register = function _EditorRegistry_register(definition) {
70
105
  if (definition.apiVersion !== EDITOR_API_VERSION)
71
106
  throw new TypeError('Unsupported editor API version.');
72
107
  if (!/^[a-z0-9]+$/i.test(definition.extensionId) ||
@@ -98,6 +133,8 @@ _EditorRegistry_editors = new WeakMap(), _EditorRegistry_opcodes = new WeakMap()
98
133
  opcodes: Object.freeze([...definition.opcodes]),
99
134
  editableArguments: Object.freeze([...definition.editableArguments])
100
135
  });
136
+ if (isFixedInputEditor(entry))
137
+ __classPrivateFieldGet(this, _EditorRegistry_fixedDeclarations, "f").set(entry, captureFixedInputDeclaration(entry));
101
138
  __classPrivateFieldGet(this, _EditorRegistry_editors, "f").set(key, entry);
102
139
  for (const opcode of entry.opcodes)
103
140
  __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").set(opcode, entry);
@@ -108,6 +145,7 @@ _EditorRegistry_editors = new WeakMap(), _EditorRegistry_opcodes = new WeakMap()
108
145
  __classPrivateFieldGet(this, _EditorRegistry_editors, "f").delete(key);
109
146
  for (const opcode of entry.opcodes)
110
147
  __classPrivateFieldGet(this, _EditorRegistry_opcodes, "f").delete(opcode);
148
+ __classPrivateFieldGet(this, _EditorRegistry_fixedDeclarations, "f").delete(entry);
111
149
  __classPrivateFieldGet(this, _EditorRegistry_instances, "m", _EditorRegistry_notifyChanges).call(this);
112
150
  };
113
151
  }, _EditorRegistry_notifyChanges = function _EditorRegistry_notifyChanges() {
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import type { ArrayFieldTemplateItemType, FieldTemplateProps, IconButtonProps, ObjectFieldTemplateProps, WrapIfAdditionalTemplateProps } from '@rjsf/utils';
3
+ /** Keep the property key out of the value's visible label. */
4
+ export declare function valueFieldTemplate(DefaultTemplate: React.ComponentType<FieldTemplateProps>, flag: string): (props: FieldTemplateProps) => React.ReactElement;
5
+ /** Keep RJSF's object layout while numbering each editable additional property. */
6
+ export declare function numberedObjectTemplate(DefaultTemplate: React.ComponentType<ObjectFieldTemplateProps>, flag: string): (props: ObjectFieldTemplateProps) => React.ReactElement;
7
+ export declare function additionalItemTemplate(flag: string): (props: WrapIfAdditionalTemplateProps) => React.ReactElement;
8
+ /** Deletion is available only after explicitly opening the item's menu. */
9
+ export declare function ItemMenu({ disabled, onClick }: Pick<IconButtonProps, 'disabled' | 'onClick'>): React.ReactElement;
10
+ export declare function AddItemButton({ disabled, onClick, className }: IconButtonProps): React.ReactElement;
11
+ export declare function ArrayItem(props: ArrayFieldTemplateItemType): React.ReactElement;
@@ -0,0 +1,129 @@
1
+ import * as React from 'react';
2
+ const ItemNumber = React.createContext(1);
3
+ const PropertyKey = React.createContext(undefined);
4
+ /** Keep the property key out of the value's visible label. */
5
+ export function valueFieldTemplate(DefaultTemplate, flag) {
6
+ return function ValueField(props) {
7
+ if (!(flag in props.schema))
8
+ return React.createElement(DefaultTemplate, { ...props });
9
+ return React.createElement(PropertyKey.Provider, { value: props.label },
10
+ React.createElement(DefaultTemplate, { ...props, label: "\u5024" }));
11
+ };
12
+ }
13
+ /** Keep RJSF's object layout while numbering each editable additional property. */
14
+ export function numberedObjectTemplate(DefaultTemplate, flag) {
15
+ return function ObjectItems(props) {
16
+ let number = 0;
17
+ const properties = props.properties.map(property => {
18
+ var _a;
19
+ const schema = (_a = props.schema.properties) === null || _a === void 0 ? void 0 : _a[property.name];
20
+ if (!schema || typeof schema !== 'object' || !(flag in schema) || property.hidden)
21
+ return property;
22
+ return { ...property, content: React.createElement(ItemNumber.Provider, { key: property.name, value: ++number }, property.content) };
23
+ });
24
+ return React.createElement(DefaultTemplate, { ...props, properties: properties });
25
+ };
26
+ }
27
+ export function additionalItemTemplate(flag) {
28
+ return function AdditionalItem(props) {
29
+ const number = React.useContext(ItemNumber);
30
+ const propertyKey = React.useContext(PropertyKey);
31
+ const { id, classNames, style, children, schema, label, disabled, readonly, onKeyChange, onDropPropertyClick } = props;
32
+ if (!(flag in schema))
33
+ return React.createElement("div", { className: classNames, style: style }, children);
34
+ const key = propertyKey !== null && propertyKey !== void 0 ? propertyKey : label;
35
+ return React.createElement("div", { className: classNames, style: style, "data-block-editor-item": "" },
36
+ React.createElement("div", { "data-block-editor-item-heading": "" },
37
+ React.createElement("span", null,
38
+ "\u30FB\u9805\u76EE",
39
+ number),
40
+ React.createElement(ItemMenu, { disabled: disabled || readonly, onClick: onDropPropertyClick(key) })),
41
+ React.createElement("div", { "data-block-editor-pair-row": "" },
42
+ React.createElement("label", { htmlFor: `${id}-key` }, "\u30AD\u30FC"),
43
+ React.createElement("input", { id: `${id}-key`, type: "text", defaultValue: key, disabled: disabled, readOnly: readonly, onBlur: event => onKeyChange(event.target.value) })),
44
+ React.createElement("div", { "data-block-editor-pair-row": "" }, children));
45
+ };
46
+ }
47
+ /** Deletion is available only after explicitly opening the item's menu. */
48
+ export function ItemMenu({ disabled, onClick }) {
49
+ const [open, setOpen] = React.useState(false);
50
+ const container = React.useRef(null);
51
+ const trigger = React.useRef(null);
52
+ const item = React.useRef(null);
53
+ React.useEffect(() => {
54
+ var _a;
55
+ if (!open)
56
+ return;
57
+ (_a = item.current) === null || _a === void 0 ? void 0 : _a.focus();
58
+ const closeOutside = (event) => {
59
+ var _a;
60
+ if (!((_a = container.current) === null || _a === void 0 ? void 0 : _a.contains(event.target)))
61
+ setOpen(false);
62
+ };
63
+ // RJSF and its host modal have separate React roots. Stop Escape before either root delegates it.
64
+ const keepEscapeInMenu = (event) => {
65
+ var _a;
66
+ if (event.key !== 'Escape' || event.isComposing || event.keyCode === 229)
67
+ return;
68
+ event.preventDefault();
69
+ event.stopPropagation();
70
+ setOpen(false);
71
+ (_a = trigger.current) === null || _a === void 0 ? void 0 : _a.focus();
72
+ };
73
+ const menu = container.current;
74
+ menu === null || menu === void 0 ? void 0 : menu.addEventListener('keydown', keepEscapeInMenu);
75
+ document.addEventListener('mousedown', closeOutside);
76
+ return () => {
77
+ menu === null || menu === void 0 ? void 0 : menu.removeEventListener('keydown', keepEscapeInMenu);
78
+ document.removeEventListener('mousedown', closeOutside);
79
+ };
80
+ }, [open]);
81
+ React.useEffect(() => { if (disabled)
82
+ setOpen(false); }, [disabled]);
83
+ return React.createElement("div", { "data-block-editor-item-menu": "", ref: container, onBlur: event => {
84
+ if (!event.currentTarget.contains(event.relatedTarget))
85
+ setOpen(false);
86
+ }, onKeyDown: event => {
87
+ var _a, _b;
88
+ if (event.key === 'Escape' && open && !event.nativeEvent.isComposing && event.keyCode !== 229) {
89
+ event.preventDefault();
90
+ event.stopPropagation();
91
+ setOpen(false);
92
+ (_a = trigger.current) === null || _a === void 0 ? void 0 : _a.focus();
93
+ }
94
+ else if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
95
+ event.preventDefault();
96
+ if (open)
97
+ (_b = item.current) === null || _b === void 0 ? void 0 : _b.focus();
98
+ else if (!disabled)
99
+ setOpen(true);
100
+ }
101
+ } },
102
+ React.createElement("button", { "aria-label": "\u9805\u76EE\u306E\u30E1\u30CB\u30E5\u30FC", "aria-haspopup": "menu", "aria-expanded": open && !disabled, disabled: disabled, ref: trigger, type: "button", onClick: () => setOpen(!open) }, "\u2026"),
103
+ open && !disabled ? React.createElement("div", { role: "menu", "aria-label": "\u9805\u76EE\u306E\u64CD\u4F5C" },
104
+ React.createElement("button", { ref: item, role: "menuitem", type: "button", onClick: event => {
105
+ var _a;
106
+ setOpen(false);
107
+ (_a = trigger.current) === null || _a === void 0 ? void 0 : _a.focus();
108
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
109
+ } }, "\uD83D\uDDD1\uFE0F\u3053\u306E\u9805\u76EE\u3092\u524A\u9664")) : null);
110
+ }
111
+ export function AddItemButton({ disabled, onClick, className }) {
112
+ return React.createElement("button", { className: className, "data-block-editor-add-item": "", disabled: disabled, type: "button", onClick: onClick }, "\u2795\u9805\u76EE\u3092\u8FFD\u52A0");
113
+ }
114
+ export function ArrayItem(props) {
115
+ const { children, className, disabled, readonly, index, hasMoveUp, hasMoveDown, hasCopy, hasRemove, onReorderClick, onCopyIndexClick, onDropIndexClick } = props;
116
+ const locked = disabled || readonly;
117
+ return React.createElement("div", { className: className, "data-block-editor-item": "" },
118
+ React.createElement("div", { "data-block-editor-item-heading": "" },
119
+ React.createElement("span", null,
120
+ "\u30FB\u9805\u76EE",
121
+ index + 1),
122
+ React.createElement("div", { "data-block-editor-item-actions": "" },
123
+ hasMoveUp || hasMoveDown ? React.createElement(React.Fragment, null,
124
+ React.createElement("button", { "aria-label": "\u9805\u76EE\u3092\u4E0A\u3078\u79FB\u52D5", disabled: locked || !hasMoveUp, type: "button", onClick: onReorderClick(index, index - 1) }, "\u2191"),
125
+ React.createElement("button", { "aria-label": "\u9805\u76EE\u3092\u4E0B\u3078\u79FB\u52D5", disabled: locked || !hasMoveDown, type: "button", onClick: onReorderClick(index, index + 1) }, "\u2193")) : null,
126
+ hasCopy ? React.createElement("button", { disabled: locked, type: "button", onClick: onCopyIndexClick(index) }, "\u9805\u76EE\u3092\u8907\u88FD") : null,
127
+ hasRemove ? React.createElement(ItemMenu, { disabled: locked, onClick: onDropIndexClick(index) }) : null)),
128
+ children);
129
+ }
package/dist/rjsf.d.ts CHANGED
@@ -1,3 +1,9 @@
1
+ import type { FixedInputMessageSource } from './fixed-input-view.js';
2
+ export type { FixedInputViewMessages, FixedInputMessageSource } from './fixed-input-view.js';
3
+ import type { FixedInputEditorOptions } from './fixed-input-editor.js';
4
+ export { createFixedInputEditor } from './fixed-input-editor.js';
5
+ export type { FixedInputEditorOptions, FixedInputBinding, FixedInputBranchPolicy } from './fixed-input-editor.js';
6
+ export type * from './fixed-input-types.js';
1
7
  import type { SourceEditorOptions } from './source-types.js';
2
8
  export { createSourceEditor } from './source-editor.js';
3
9
  export type * from './source-types.js';
@@ -35,6 +41,7 @@ export interface BlockEditorRegistrationAPI {
35
41
  registerSchema(definition: SchemaEditorOptions): () => void;
36
42
  registerSchemaBundle?<C>(definition: SchemaBundleEditorOptions<C>): () => void;
37
43
  registerSourceEditor?<C = null>(definition: SourceEditorOptions<C>): () => void;
44
+ registerFixedInputEditor?<C = null>(definition: FixedInputEditorOptions<C>): () => void;
38
45
  }
39
46
  /** Capability guaranteed by the service factory, optional on a legacy host-supplied API. */
40
47
  export interface SchemaBundleBlockEditorRegistrationAPI extends BlockEditorRegistrationAPI {
@@ -44,14 +51,30 @@ export interface SchemaBundleBlockEditorRegistrationAPI extends BlockEditorRegis
44
51
  export interface SourceBlockEditorRegistrationAPI extends SchemaBundleBlockEditorRegistrationAPI {
45
52
  registerSourceEditor<C = null>(definition: SourceEditorOptions<C>): () => void;
46
53
  }
54
+ export interface FixedBlockEditorRegistrationAPI extends SourceBlockEditorRegistrationAPI {
55
+ registerFixedInputEditor<C = null>(definition: FixedInputEditorOptions<C>): () => void;
56
+ }
47
57
  export interface BlockEditorService {
48
58
  readonly registry: EditorRegistry;
49
59
  readonly api: BlockEditorRegistrationAPI;
50
60
  subscribe(listener: () => void): () => void;
51
61
  }
52
62
  /** A host owns the service's lifetime; this module never stores VM or workspace instances. */
53
- export declare function createBlockEditorService(): BlockEditorService & {
63
+ export declare function createBlockEditorService(options: {
64
+ readonly fixedInputs: true;
65
+ readonly fixedInputMessages?: FixedInputMessageSource;
66
+ }): BlockEditorService & {
67
+ readonly api: FixedBlockEditorRegistrationAPI;
68
+ };
69
+ export declare function createBlockEditorService(options?: {
70
+ readonly fixedInputs?: false;
71
+ readonly fixedInputMessages?: FixedInputMessageSource;
72
+ }): BlockEditorService & {
54
73
  readonly api: SourceBlockEditorRegistrationAPI;
55
74
  };
75
+ export declare function createBlockEditorService(options: {
76
+ readonly fixedInputs?: boolean;
77
+ readonly fixedInputMessages?: FixedInputMessageSource;
78
+ }): BlockEditorService;
56
79
  /** Build an editor with JSON/YAML serialization, schema validation and the standard RJSF body. */
57
80
  export declare function createSchemaEditor(options: SchemaEditorOptions): EditorDefinition;
package/dist/rjsf.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { createSourceEditor } from './source-editor.js';
3
+ import { createFixedInputEditor } from './fixed-input-editor.js';
4
+ import { captureFixedInputMessages, resolveFixedInputMessages, mountFixedInputView } from './fixed-input-view.js';
5
+ export { createFixedInputEditor } from './fixed-input-editor.js';
3
6
  export { createSourceEditor } from './source-editor.js';
4
7
  import * as ReactDOM from 'react-dom';
5
8
  import * as RjsfCore from '@rjsf/core';
@@ -13,6 +16,7 @@ import { cloneJson } from './data.js';
13
16
  import { decodeSource, encodeSource, sameData } from './codec.js';
14
17
  import { EditorRegistry } from './registry.js';
15
18
  import { EDITOR_API_VERSION } from './types.js';
19
+ import { AddItemButton, ArrayItem, ItemMenu, additionalItemTemplate, numberedObjectTemplate, valueFieldTemplate } from './rjsf-templates.js';
16
20
  // RJSF v5 exposes CommonJS to Node and ESM to browser bundlers.
17
21
  function moduleExports(namespace) {
18
22
  const fallback = namespace.default;
@@ -20,8 +24,12 @@ function moduleExports(namespace) {
20
24
  ? fallback : namespace;
21
25
  }
22
26
  const Form = moduleExports(RjsfCore).default;
23
- const { getDefaultFormState } = moduleExports(RjsfUtils);
27
+ const { getDefaultFormState, ADDITIONAL_PROPERTY_FLAG } = moduleExports(RjsfUtils);
24
28
  const { customizeValidator } = moduleExports(RjsfAjv);
29
+ const defaultTemplates = moduleExports(RjsfCore).getDefaultRegistry().templates;
30
+ const ObjectItems = numberedObjectTemplate(defaultTemplates.ObjectFieldTemplate, ADDITIONAL_PROPERTY_FLAG);
31
+ const AdditionalItem = additionalItemTemplate(ADDITIONAL_PROPERTY_FLAG);
32
+ const ValueField = valueFieldTemplate(defaultTemplates.FieldTemplate, ADDITIONAL_PROPERTY_FLAG);
25
33
  let nextViewId = 0;
26
34
  export function createSchemaBundleEditor(options) {
27
35
  const { schemaBundle, validationContext: provider, ownerValidate, apiVersion, extensionId, editorId, opcodes, argument, format, uiSchema, validateData } = options;
@@ -65,13 +73,30 @@ export function createSchemaBundleEditor(options) {
65
73
  ...(format === undefined ? {} : { format }), ...(uiSchema === undefined ? {} : { uiSchema }),
66
74
  ...(validateData === undefined ? {} : { validateData }) }, { issuesFor, dialect: result.validator.dialect });
67
75
  }
68
- /** A host owns the service's lifetime; this module never stores VM or workspace instances. */
69
- export function createBlockEditorService() {
76
+ export function createBlockEditorService(options) {
77
+ if ((options === null || options === void 0 ? void 0 : options.fixedInputs) !== undefined && typeof options.fixedInputs !== 'boolean')
78
+ throw new TypeError('Invalid fixed input capability option.');
79
+ const messageSource = options === null || options === void 0 ? void 0 : options.fixedInputMessages;
80
+ const fixedMessages = typeof messageSource === 'function' ? messageSource : captureFixedInputMessages(messageSource);
70
81
  const registry = new EditorRegistry();
71
82
  const listeners = new Set();
72
83
  const notify = () => { for (const listener of [...listeners])
73
84
  listener(); };
85
+ const fixed = (options === null || options === void 0 ? void 0 : options.fixedInputs) === true ? {
86
+ registerFixedInputEditor: (definition) => {
87
+ const fixedDefinition = createFixedInputEditor(definition);
88
+ const remove = registry.registerFixed({ ...fixedDefinition, mount: (container, context) => mountFixedInputView(container, context, resolveFixedInputMessages(fixedMessages)) });
89
+ notify();
90
+ let active = true;
91
+ return () => { if (active) {
92
+ active = false;
93
+ remove();
94
+ notify();
95
+ } };
96
+ }
97
+ } : {};
74
98
  const api = Object.freeze({
99
+ ...fixed,
75
100
  apiVersion: EDITOR_API_VERSION,
76
101
  register: (definition) => {
77
102
  const custom = definition;
@@ -189,6 +214,13 @@ function createSchemaEditorImpl(options, bundle) {
189
214
  const state = context.getState();
190
215
  const props = {
191
216
  schema, uiSchema, validator, idPrefix,
217
+ templates: {
218
+ ArrayFieldItemTemplate: ArrayItem,
219
+ ObjectFieldTemplate: ObjectItems,
220
+ WrapIfAdditionalTemplate: AdditionalItem,
221
+ FieldTemplate: ValueField,
222
+ ButtonTemplates: { AddButton: AddItemButton, RemoveButton: ItemMenu }
223
+ },
192
224
  formData: dataFor(context.getDraft()),
193
225
  // Defaults belong to read(), not subsequent renders or remounts.
194
226
  experimental_defaultFormStateBehavior: {