@flanksource/clicky-ui 0.3.40 → 0.3.41
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 -0
- package/dist/ai.cjs +10 -0
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.js +10 -0
- package/dist/ai.js.map +1 -1
- package/dist/components/AccordionList.cjs +5 -1
- package/dist/components/AccordionList.cjs.map +1 -1
- package/dist/components/AccordionList.d.ts +3 -1
- package/dist/components/AccordionList.d.ts.map +1 -1
- package/dist/components/AccordionList.js +5 -1
- package/dist/components/AccordionList.js.map +1 -1
- package/dist/components/JsonSchemaForm.cjs +9 -4
- package/dist/components/JsonSchemaForm.cjs.map +1 -1
- package/dist/components/JsonSchemaForm.d.ts +1 -1
- package/dist/components/JsonSchemaForm.d.ts.map +1 -1
- package/dist/components/JsonSchemaForm.js +8 -3
- package/dist/components/JsonSchemaForm.js.map +1 -1
- package/dist/components/json-schema-form-accordion-array.cjs +3 -1
- package/dist/components/json-schema-form-accordion-array.cjs.map +1 -1
- package/dist/components/json-schema-form-accordion-array.d.ts +4 -1
- package/dist/components/json-schema-form-accordion-array.d.ts.map +1 -1
- package/dist/components/json-schema-form-accordion-array.js +3 -1
- package/dist/components/json-schema-form-accordion-array.js.map +1 -1
- package/dist/components/json-schema-form-array-view.cjs +63 -0
- package/dist/components/json-schema-form-array-view.cjs.map +1 -0
- package/dist/components/json-schema-form-array-view.d.ts +21 -0
- package/dist/components/json-schema-form-array-view.d.ts.map +1 -0
- package/dist/components/json-schema-form-array-view.js +63 -0
- package/dist/components/json-schema-form-array-view.js.map +1 -0
- package/dist/components/json-schema-form-array.cjs +3 -7
- package/dist/components/json-schema-form-array.cjs.map +1 -1
- package/dist/components/json-schema-form-array.d.ts.map +1 -1
- package/dist/components/json-schema-form-array.js +3 -7
- package/dist/components/json-schema-form-array.js.map +1 -1
- package/dist/components/json-schema-form-conditionals.cjs +67 -0
- package/dist/components/json-schema-form-conditionals.cjs.map +1 -0
- package/dist/components/json-schema-form-conditionals.d.ts +9 -0
- package/dist/components/json-schema-form-conditionals.d.ts.map +1 -0
- package/dist/components/json-schema-form-conditionals.js +67 -0
- package/dist/components/json-schema-form-conditionals.js.map +1 -0
- package/dist/components/json-schema-form-defaults.cjs +68 -0
- package/dist/components/json-schema-form-defaults.cjs.map +1 -0
- package/dist/components/json-schema-form-defaults.d.ts +4 -0
- package/dist/components/json-schema-form-defaults.d.ts.map +1 -0
- package/dist/components/json-schema-form-defaults.js +68 -0
- package/dist/components/json-schema-form-defaults.js.map +1 -0
- package/dist/components/json-schema-form-errors.cjs +49 -31
- package/dist/components/json-schema-form-errors.cjs.map +1 -1
- package/dist/components/json-schema-form-errors.d.ts +4 -2
- package/dist/components/json-schema-form-errors.d.ts.map +1 -1
- package/dist/components/json-schema-form-errors.js +51 -33
- package/dist/components/json-schema-form-errors.js.map +1 -1
- package/dist/components/json-schema-form-filter.cjs +2 -1
- package/dist/components/json-schema-form-filter.cjs.map +1 -1
- package/dist/components/json-schema-form-filter.js +2 -1
- package/dist/components/json-schema-form-filter.js.map +1 -1
- package/dist/components/json-schema-form-item-summary.cjs +6 -2
- package/dist/components/json-schema-form-item-summary.cjs.map +1 -1
- package/dist/components/json-schema-form-item-summary.d.ts.map +1 -1
- package/dist/components/json-schema-form-item-summary.js +6 -2
- package/dist/components/json-schema-form-item-summary.js.map +1 -1
- package/dist/components/json-schema-form-layout.cjs +28 -19
- package/dist/components/json-schema-form-layout.cjs.map +1 -1
- package/dist/components/json-schema-form-layout.d.ts +8 -6
- package/dist/components/json-schema-form-layout.d.ts.map +1 -1
- package/dist/components/json-schema-form-layout.js +29 -20
- package/dist/components/json-schema-form-layout.js.map +1 -1
- package/dist/components/json-schema-form-listener-paths.cjs +52 -0
- package/dist/components/json-schema-form-listener-paths.cjs.map +1 -0
- package/dist/components/json-schema-form-listener-paths.d.ts +10 -0
- package/dist/components/json-schema-form-listener-paths.d.ts.map +1 -0
- package/dist/components/json-schema-form-listener-paths.js +52 -0
- package/dist/components/json-schema-form-listener-paths.js.map +1 -0
- package/dist/components/json-schema-form-listener-types.d.ts +22 -0
- package/dist/components/json-schema-form-listener-types.d.ts.map +1 -0
- package/dist/components/json-schema-form-listeners.cjs +250 -0
- package/dist/components/json-schema-form-listeners.cjs.map +1 -0
- package/dist/components/json-schema-form-listeners.d.ts +13 -0
- package/dist/components/json-schema-form-listeners.d.ts.map +1 -0
- package/dist/components/json-schema-form-listeners.js +250 -0
- package/dist/components/json-schema-form-listeners.js.map +1 -0
- package/dist/components/json-schema-form-object-array-view.cjs +62 -0
- package/dist/components/json-schema-form-object-array-view.cjs.map +1 -0
- package/dist/components/json-schema-form-object-array-view.d.ts +14 -0
- package/dist/components/json-schema-form-object-array-view.d.ts.map +1 -0
- package/dist/components/json-schema-form-object-array-view.js +62 -0
- package/dist/components/json-schema-form-object-array-view.js.map +1 -0
- package/dist/components/json-schema-form-object.cjs +3 -0
- package/dist/components/json-schema-form-object.cjs.map +1 -1
- package/dist/components/json-schema-form-object.d.ts.map +1 -1
- package/dist/components/json-schema-form-object.js +3 -0
- package/dist/components/json-schema-form-object.js.map +1 -1
- package/dist/components/json-schema-form-render.cjs +17 -9
- package/dist/components/json-schema-form-render.cjs.map +1 -1
- package/dist/components/json-schema-form-render.d.ts.map +1 -1
- package/dist/components/json-schema-form-render.js +19 -11
- package/dist/components/json-schema-form-render.js.map +1 -1
- package/dist/components/json-schema-form-resolve.cjs +6 -99
- package/dist/components/json-schema-form-resolve.cjs.map +1 -1
- package/dist/components/json-schema-form-resolve.d.ts +1 -8
- package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
- package/dist/components/json-schema-form-resolve.js +5 -98
- package/dist/components/json-schema-form-resolve.js.map +1 -1
- package/dist/components/json-schema-form-runtime-types.d.ts +15 -0
- package/dist/components/json-schema-form-runtime-types.d.ts.map +1 -1
- package/dist/components/json-schema-form-table-array.cjs +67 -16
- package/dist/components/json-schema-form-table-array.cjs.map +1 -1
- package/dist/components/json-schema-form-table-array.d.ts +4 -1
- package/dist/components/json-schema-form-table-array.d.ts.map +1 -1
- package/dist/components/json-schema-form-table-array.js +70 -19
- package/dist/components/json-schema-form-table-array.js.map +1 -1
- package/dist/components/json-schema-form-types.d.ts +16 -0
- package/dist/components/json-schema-form-types.d.ts.map +1 -1
- package/dist/components/json-schema-form-utils.cjs +7 -1
- package/dist/components/json-schema-form-utils.cjs.map +1 -1
- package/dist/components/json-schema-form-utils.d.ts +2 -1
- package/dist/components/json-schema-form-utils.d.ts.map +1 -1
- package/dist/components/json-schema-form-utils.js +7 -1
- package/dist/components/json-schema-form-utils.js.map +1 -1
- package/dist/components.cjs +3 -2
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -2
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +2 -1
- package/dist/components.js.map +1 -1
- package/dist/data/DataTable.cjs +9 -3
- package/dist/data/DataTable.cjs.map +1 -1
- package/dist/data/DataTable.d.ts +6 -0
- package/dist/data/DataTable.d.ts.map +1 -1
- package/dist/data/DataTable.js +9 -3
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/FixtureEditor/FixtureEditor.cjs +43 -24
- package/dist/data/FixtureEditor/FixtureEditor.cjs.map +1 -1
- package/dist/data/FixtureEditor/FixtureEditor.d.ts.map +1 -1
- package/dist/data/FixtureEditor/FixtureEditor.js +43 -24
- package/dist/data/FixtureEditor/FixtureEditor.js.map +1 -1
- package/dist/data/Tree.cjs +10 -2
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts +6 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js +10 -2
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/ai/SessionInspector.cjs +143 -167
- package/dist/data/ai/SessionInspector.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.collection-types.d.ts +5 -0
- package/dist/data/ai/SessionInspector.collection-types.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.collection.cjs +18 -5
- package/dist/data/ai/SessionInspector.collection.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.collection.d.ts +2 -0
- package/dist/data/ai/SessionInspector.collection.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.collection.fixtures.d.ts +6 -0
- package/dist/data/ai/SessionInspector.collection.fixtures.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.collection.js +18 -5
- package/dist/data/ai/SessionInspector.collection.js.map +1 -1
- package/dist/data/ai/SessionInspector.d.ts +24 -2
- package/dist/data/ai/SessionInspector.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.hierarchy-state.cjs +105 -7
- package/dist/data/ai/SessionInspector.hierarchy-state.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.hierarchy-state.d.ts +12 -1
- package/dist/data/ai/SessionInspector.hierarchy-state.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.hierarchy-state.js +107 -9
- package/dist/data/ai/SessionInspector.hierarchy-state.js.map +1 -1
- package/dist/data/ai/SessionInspector.hierarchy.cjs +1 -1
- package/dist/data/ai/SessionInspector.hierarchy.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.hierarchy.js +1 -1
- package/dist/data/ai/SessionInspector.hierarchy.js.map +1 -1
- package/dist/data/ai/SessionInspector.js +144 -168
- package/dist/data/ai/SessionInspector.js.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.cjs +116 -0
- package/dist/data/ai/SessionInspector.legacy.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.legacy.d.ts +19 -0
- package/dist/data/ai/SessionInspector.legacy.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.legacy.js +116 -0
- package/dist/data/ai/SessionInspector.legacy.js.map +1 -0
- package/dist/data/ai/SessionInspector.panels.cjs +3 -0
- package/dist/data/ai/SessionInspector.panels.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.panels.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.panels.js +3 -0
- package/dist/data/ai/SessionInspector.panels.js.map +1 -1
- package/dist/data/ai/SessionInspector.plan.cjs +29 -12
- package/dist/data/ai/SessionInspector.plan.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.plan.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.plan.js +30 -13
- package/dist/data/ai/SessionInspector.plan.js.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.cjs +44 -0
- package/dist/data/ai/SessionInspector.remote-view.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.remote-view.d.ts +11 -0
- package/dist/data/ai/SessionInspector.remote-view.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.remote-view.js +44 -0
- package/dist/data/ai/SessionInspector.remote-view.js.map +1 -0
- package/dist/data/ai/SessionInspector.remote.cjs +275 -0
- package/dist/data/ai/SessionInspector.remote.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.remote.d.ts +57 -0
- package/dist/data/ai/SessionInspector.remote.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.remote.js +275 -0
- package/dist/data/ai/SessionInspector.remote.js.map +1 -0
- package/dist/data/ai/SessionInspector.summary.cjs +0 -156
- package/dist/data/ai/SessionInspector.summary.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.summary.d.ts +0 -3
- package/dist/data/ai/SessionInspector.summary.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.summary.js +2 -158
- package/dist/data/ai/SessionInspector.summary.js.map +1 -1
- package/dist/data/ai/SessionInspector.tab-state.cjs +15 -0
- package/dist/data/ai/SessionInspector.tab-state.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.tab-state.d.ts +3 -0
- package/dist/data/ai/SessionInspector.tab-state.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.tab-state.js +15 -0
- package/dist/data/ai/SessionInspector.tab-state.js.map +1 -0
- package/dist/data/ai/SessionInspector.tabs.model.cjs +31 -18
- package/dist/data/ai/SessionInspector.tabs.model.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.tabs.model.d.ts +3 -1
- package/dist/data/ai/SessionInspector.tabs.model.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.tabs.model.js +32 -19
- package/dist/data/ai/SessionInspector.tabs.model.js.map +1 -1
- package/dist/data/ai/SessionInspector.verification.cjs +53 -0
- package/dist/data/ai/SessionInspector.verification.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.verification.d.ts +7 -0
- package/dist/data/ai/SessionInspector.verification.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.verification.js +53 -0
- package/dist/data/ai/SessionInspector.verification.js.map +1 -0
- package/dist/data/ai/SessionViewer.model.cjs +7 -1
- package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.model.d.ts +21 -3
- package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.model.js +7 -1
- package/dist/data/ai/SessionViewer.model.js.map +1 -1
- package/dist/data/ai/SessionViewer.unified.d.ts +13 -0
- package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
- package/dist/data/ai/index.d.ts +3 -1
- package/dist/data/ai/index.d.ts.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/devtools/useDebugStream.cjs +5 -1
- package/dist/devtools/useDebugStream.cjs.map +1 -1
- package/dist/devtools/useDebugStream.d.ts.map +1 -1
- package/dist/devtools/useDebugStream.js +6 -2
- package/dist/devtools/useDebugStream.js.map +1 -1
- package/dist/hooks/event-source-provider.cjs +9 -0
- package/dist/hooks/event-source-provider.cjs.map +1 -0
- package/dist/hooks/event-source-provider.d.ts +15 -0
- package/dist/hooks/event-source-provider.d.ts.map +1 -0
- package/dist/hooks/event-source-provider.js +9 -0
- package/dist/hooks/event-source-provider.js.map +1 -0
- package/dist/hooks/event-source.cjs +11 -0
- package/dist/hooks/event-source.cjs.map +1 -0
- package/dist/hooks/event-source.d.ts +22 -0
- package/dist/hooks/event-source.d.ts.map +1 -0
- package/dist/hooks/event-source.js +11 -0
- package/dist/hooks/event-source.js.map +1 -0
- package/dist/hooks/use-log-tail.cjs +5 -1
- package/dist/hooks/use-log-tail.cjs.map +1 -1
- package/dist/hooks/use-log-tail.d.ts.map +1 -1
- package/dist/hooks/use-log-tail.js +5 -1
- package/dist/hooks/use-log-tail.js.map +1 -1
- package/dist/hooks/use-prompts.cjs +5 -1
- package/dist/hooks/use-prompts.cjs.map +1 -1
- package/dist/hooks/use-prompts.d.ts.map +1 -1
- package/dist/hooks/use-prompts.js +6 -2
- package/dist/hooks/use-prompts.js.map +1 -1
- package/dist/hooks/use-task-run.cjs +9 -2
- package/dist/hooks/use-task-run.cjs.map +1 -1
- package/dist/hooks/use-task-run.d.ts.map +1 -1
- package/dist/hooks/use-task-run.js +10 -3
- package/dist/hooks/use-task-run.js.map +1 -1
- package/dist/hooks.cjs +4 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +4 -0
- package/dist/hooks.js.map +1 -1
- package/dist/icons/programmingCatalog.cjs +34 -34
- package/dist/icons/programmingCatalog.cjs.map +1 -1
- package/dist/icons/programmingCatalog.js +34 -34
- package/dist/icons/programmingCatalog.js.map +1 -1
- package/dist/index.cjs +9 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/monaco/MonacoEditor.cjs +4 -1
- package/dist/monaco/MonacoEditor.cjs.map +1 -1
- package/dist/monaco/MonacoEditor.d.ts +1 -1
- package/dist/monaco/MonacoEditor.d.ts.map +1 -1
- package/dist/monaco/MonacoEditor.js +4 -1
- package/dist/monaco/MonacoEditor.js.map +1 -1
- package/dist/monaco/types.d.ts +2 -1
- package/dist/monaco/types.d.ts.map +1 -1
- package/dist/overlay/CommandPalette.cjs +14 -4
- package/dist/overlay/CommandPalette.cjs.map +1 -1
- package/dist/overlay/CommandPalette.d.ts +5 -1
- package/dist/overlay/CommandPalette.d.ts.map +1 -1
- package/dist/overlay/CommandPalette.js +14 -4
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/CommandPaletteList.cjs +5 -0
- package/dist/overlay/CommandPaletteList.cjs.map +1 -1
- package/dist/overlay/CommandPaletteList.d.ts +4 -2
- package/dist/overlay/CommandPaletteList.d.ts.map +1 -1
- package/dist/overlay/CommandPaletteList.js +5 -0
- package/dist/overlay/CommandPaletteList.js.map +1 -1
- package/dist/overlay/DropdownMenu.cjs +1 -0
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.js +1 -0
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/rpc/OperationCatalog.cjs +29 -26
- package/dist/rpc/OperationCatalog.cjs.map +1 -1
- package/dist/rpc/OperationCatalog.d.ts +9 -1
- package/dist/rpc/OperationCatalog.d.ts.map +1 -1
- package/dist/rpc/OperationCatalog.js +29 -26
- package/dist/rpc/OperationCatalog.js.map +1 -1
- package/dist/rpc/OperationEntityContextPicker.cjs +62 -256
- package/dist/rpc/OperationEntityContextPicker.cjs.map +1 -1
- package/dist/rpc/OperationEntityContextPicker.d.ts.map +1 -1
- package/dist/rpc/OperationEntityContextPicker.js +64 -258
- package/dist/rpc/OperationEntityContextPicker.js.map +1 -1
- package/dist/rpc/OperationEntityPicker.cjs +185 -0
- package/dist/rpc/OperationEntityPicker.cjs.map +1 -0
- package/dist/rpc/OperationEntityPicker.d.ts +51 -0
- package/dist/rpc/OperationEntityPicker.d.ts.map +1 -0
- package/dist/rpc/OperationEntityPicker.js +185 -0
- package/dist/rpc/OperationEntityPicker.js.map +1 -0
- package/dist/rpc/OperationEntityPicker.model.cjs +39 -0
- package/dist/rpc/OperationEntityPicker.model.cjs.map +1 -0
- package/dist/rpc/OperationEntityPicker.model.d.ts +27 -0
- package/dist/rpc/OperationEntityPicker.model.d.ts.map +1 -0
- package/dist/rpc/OperationEntityPicker.model.js +39 -0
- package/dist/rpc/OperationEntityPicker.model.js.map +1 -0
- package/dist/rpc/operationCatalogRowSelection.cjs +85 -0
- package/dist/rpc/operationCatalogRowSelection.cjs.map +1 -0
- package/dist/rpc/operationCatalogRowSelection.d.ts +57 -0
- package/dist/rpc/operationCatalogRowSelection.d.ts.map +1 -0
- package/dist/rpc/operationCatalogRowSelection.js +85 -0
- package/dist/rpc/operationCatalogRowSelection.js.map +1 -0
- package/dist/rpc/operationEntityPicker.fixtures.d.ts +13 -0
- package/dist/rpc/operationEntityPicker.fixtures.d.ts.map +1 -0
- package/dist/rpc.cjs +2 -0
- package/dist/rpc.cjs.map +1 -1
- package/dist/rpc.d.ts +2 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -0
- package/dist/rpc.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +4 -1
- package/schemas/json-schema-form.schema.json +566 -0
package/README.md
CHANGED
|
@@ -90,6 +90,30 @@ const queryClient = new QueryClient();
|
|
|
90
90
|
|
|
91
91
|
A `load`-backed series is cached under `["timeseries", "load", id, range]`: **the `id` is the cache identity**, so two series that share an id share one cached response. The loader receives the widget's `range` and react-query's `AbortSignal`, which aborts when the query is cancelled (for example on unmount). URL-backed series keep their `["timeseries", requestUrl]` key. See the [timeseries guide](../../apps/docs/src/content/docs/guides/timeseries.md) for details.
|
|
92
92
|
|
|
93
|
+
### Change listeners (`x-on-change`)
|
|
94
|
+
|
|
95
|
+
A property's `x-on-change` entries act on its sibling fields while `when` holds (and `else` while it does not). `hide`/`show`, `enable`/`disable`, `require`/`optional` and `patch` are derived state, recomputed from the current value on every render. `set` and `reset` fire only when the field is edited. `patch` merges standard keywords over a sibling, so a listener can make it `readOnly` or `writeOnly`, or change its `title`, `enum` or bounds:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
"Status": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"enum": ["draft", "signed"],
|
|
101
|
+
"x-on-change": [{
|
|
102
|
+
"when": { "const": "signed" },
|
|
103
|
+
"patch": {
|
|
104
|
+
"Amount": { "readOnly": true },
|
|
105
|
+
"Rate": { "title": "Agreed rate", "minimum": 0, "maximum": 25 }
|
|
106
|
+
}
|
|
107
|
+
}]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
A patch may not change a field's shape (`type`, `properties`, `items`, `allOf`, …), its `required`-ness or its value (`default`, `const`); the form throws, pointing at the action that does. The Form listeners kitchen-sink demo has runnable examples of every action.
|
|
112
|
+
|
|
113
|
+
A `hide`/`show`/`enable`/`disable` target or a `patch` key may be a `/`-separated path into a sibling's subtree: `groups/Rates` hides a group, and `groups/Rates/Fee` hides one column of it, because an array is crossed into its `items`. An object's `x-on-load` entries have the same shape. Their `when` reads the object itself, they apply before any `x-on-change`, and they allow only the derived actions and `patch`. See the JSON Schema extensions reference in the docs app for the full grammar and ordering.
|
|
114
|
+
|
|
115
|
+
A `writeOnly: true` field is an input that is never read back: an editable form renders it normally, while a view renders nothing for it. A view is a `readOnly` form, anything under a `readOnly` object, array or map, or a field that is itself `readOnly`. `x-disabled` is not a view, so a disabled subtree keeps its controls. Use `format: "password"` to mask the value while it is edited.
|
|
116
|
+
|
|
93
117
|
## Markdown editor field
|
|
94
118
|
|
|
95
119
|
`JsonSchemaForm` fields with `format: md` — and the standalone `MdxEditorField` exported from `@flanksource/clicky-ui/mdx-editor` — render an [MDXEditor](https://mdxeditor.dev/)-backed rich-text field. Its base styles ship as a **separate** stylesheet so apps that don't use the field don't pay its weight (the editor's JavaScript is also loaded lazily, on first render). Import it once at the app root, in addition to `styles.css`:
|
package/dist/ai.cjs
CHANGED
|
@@ -57,6 +57,7 @@ const SessionViewer_header = require("./data/ai/SessionViewer.header.cjs");
|
|
|
57
57
|
const SessionInspector = require("./data/ai/SessionInspector.cjs");
|
|
58
58
|
const SessionViewer = require("./data/ai/SessionViewer.cjs");
|
|
59
59
|
const index$2 = require("./data/ai/SpecRuntimeEditor/index.cjs");
|
|
60
|
+
const SessionInspector_remote = require("./data/ai/SessionInspector.remote.cjs");
|
|
60
61
|
const ThreadPicker = require("./data/ai/ThreadPicker.cjs");
|
|
61
62
|
const ToolPreferences = require("./data/ai/ToolPreferences.cjs");
|
|
62
63
|
const unspecified = require("./data/runtime/unspecified.cjs");
|
|
@@ -70,6 +71,7 @@ const sessionCost = require("./data/ai/session-cost.cjs");
|
|
|
70
71
|
const ToolPreferences_model = require("./data/ai/ToolPreferences.model.cjs");
|
|
71
72
|
const modelCapabilities = require("./data/runtime/model-capabilities.cjs");
|
|
72
73
|
const SessionViewer_model = require("./data/ai/SessionViewer.model.cjs");
|
|
74
|
+
const SessionInspector_tabs_model = require("./data/ai/SessionInspector.tabs.model.cjs");
|
|
73
75
|
const types$2 = require("./data/ai/PromptPicker/types.cjs");
|
|
74
76
|
const context = require("./data/ai/context.cjs");
|
|
75
77
|
const chatWindowContext = require("./data/ai/chat-window-context.cjs");
|
|
@@ -191,6 +193,13 @@ exports.SessionContextMeter = SessionViewer_header.SessionContextMeter;
|
|
|
191
193
|
exports.SessionInspector = SessionInspector.SessionInspector;
|
|
192
194
|
exports.SessionViewer = SessionViewer.SessionViewer;
|
|
193
195
|
exports.SpecRuntimeEditor = index$2.SpecRuntimeEditor;
|
|
196
|
+
exports.TERMINAL_SESSION_LIFECYCLES = SessionInspector_remote.TERMINAL_SESSION_LIFECYCLES;
|
|
197
|
+
exports.fetchRemoteSession = SessionInspector_remote.fetchRemoteSession;
|
|
198
|
+
exports.isTerminalSessionLifecycle = SessionInspector_remote.isTerminalSessionLifecycle;
|
|
199
|
+
exports.mergeSessionMessages = SessionInspector_remote.mergeSessionMessages;
|
|
200
|
+
exports.openRemoteSession = SessionInspector_remote.openRemoteSession;
|
|
201
|
+
exports.sessionFollowUrl = SessionInspector_remote.sessionFollowUrl;
|
|
202
|
+
exports.useRemoteSession = SessionInspector_remote.useRemoteSession;
|
|
194
203
|
exports.ThreadPicker = ThreadPicker.ThreadPicker;
|
|
195
204
|
exports.ToolPreferences = ToolPreferences.ToolPreferences;
|
|
196
205
|
exports.UNSPECIFIED_HINT = unspecified.UNSPECIFIED_HINT;
|
|
@@ -247,6 +256,7 @@ exports.normalizeMessages = SessionViewer_model.normalizeMessages;
|
|
|
247
256
|
exports.normalizeSession = SessionViewer_model.normalizeSession;
|
|
248
257
|
exports.splitMcpTool = SessionViewer_model.splitMcpTool;
|
|
249
258
|
exports.summarizeSession = SessionViewer_model.summarizeSession;
|
|
259
|
+
exports.isSessionInspectorTab = SessionInspector_tabs_model.isSessionInspectorTab;
|
|
250
260
|
exports.isValidPromptSpecDetail = types$2.isValidPromptSpecDetail;
|
|
251
261
|
exports.makeContextId = context.makeContextId;
|
|
252
262
|
exports.parseContextPrefix = context.parseContextPrefix;
|
package/dist/ai.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ai.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/ai.js
CHANGED
|
@@ -55,6 +55,7 @@ import { SessionContextMeter } from "./data/ai/SessionViewer.header.js";
|
|
|
55
55
|
import { SessionInspector } from "./data/ai/SessionInspector.js";
|
|
56
56
|
import { SessionViewer } from "./data/ai/SessionViewer.js";
|
|
57
57
|
import { SpecRuntimeEditor } from "./data/ai/SpecRuntimeEditor/index.js";
|
|
58
|
+
import { TERMINAL_SESSION_LIFECYCLES, fetchRemoteSession, isTerminalSessionLifecycle, mergeSessionMessages, openRemoteSession, sessionFollowUrl, useRemoteSession } from "./data/ai/SessionInspector.remote.js";
|
|
58
59
|
import { ThreadPicker } from "./data/ai/ThreadPicker.js";
|
|
59
60
|
import { ToolPreferences } from "./data/ai/ToolPreferences.js";
|
|
60
61
|
import { UNSPECIFIED_HINT, UNSPECIFIED_ID, UNSPECIFIED_LABEL, UNSPECIFIED_NAME, unspecifiedHint } from "./data/runtime/unspecified.js";
|
|
@@ -68,6 +69,7 @@ import { costTotal, tokenTotal } from "./data/ai/session-cost.js";
|
|
|
68
69
|
import { effectiveToolPolicies, withUserRule } from "./data/ai/ToolPreferences.model.js";
|
|
69
70
|
import { effortOptionsForModel, reconcileModelCapabilities } from "./data/runtime/model-capabilities.js";
|
|
70
71
|
import { getSessionAction, getSessionMetadata, normalizeMessages, normalizeSession, splitMcpTool, summarizeSession } from "./data/ai/SessionViewer.model.js";
|
|
72
|
+
import { isSessionInspectorTab } from "./data/ai/SessionInspector.tabs.model.js";
|
|
71
73
|
import { isValidPromptSpecDetail } from "./data/ai/PromptPicker/types.js";
|
|
72
74
|
import { makeContextId, parseContextPrefix, serializeContext } from "./data/ai/context.js";
|
|
73
75
|
import { nextPromptId, useChatWindowManager } from "./data/ai/chat-window-context.js";
|
|
@@ -156,6 +158,7 @@ export {
|
|
|
156
158
|
SessionViewer,
|
|
157
159
|
SpecEditorDialog,
|
|
158
160
|
SpecRuntimeEditor,
|
|
161
|
+
TERMINAL_SESSION_LIFECYCLES,
|
|
159
162
|
ThreadPicker,
|
|
160
163
|
ToolPreferences,
|
|
161
164
|
ToolSchemaBrowser2 as ToolSchemaBrowser,
|
|
@@ -193,12 +196,15 @@ export {
|
|
|
193
196
|
familyById,
|
|
194
197
|
familyForMode,
|
|
195
198
|
familyForModel,
|
|
199
|
+
fetchRemoteSession,
|
|
196
200
|
formatCost,
|
|
197
201
|
getSessionAction,
|
|
198
202
|
getSessionMetadata,
|
|
199
203
|
isConflictError,
|
|
200
204
|
isDraftDirty,
|
|
201
205
|
isEventVisible,
|
|
206
|
+
isSessionInspectorTab,
|
|
207
|
+
isTerminalSessionLifecycle,
|
|
202
208
|
isValidPromptSpecDetail,
|
|
203
209
|
labelForMode,
|
|
204
210
|
layerLabel,
|
|
@@ -206,6 +212,7 @@ export {
|
|
|
206
212
|
makeContextId,
|
|
207
213
|
matchesTool,
|
|
208
214
|
mergeRuntimeSpec,
|
|
215
|
+
mergeSessionMessages,
|
|
209
216
|
modeForFamily,
|
|
210
217
|
modeOptionFor,
|
|
211
218
|
modelBelongsToFamily,
|
|
@@ -219,6 +226,7 @@ export {
|
|
|
219
226
|
normalizeToolPolicies,
|
|
220
227
|
normalizeToolPolicy,
|
|
221
228
|
normalizeToolPolicyRules,
|
|
229
|
+
openRemoteSession,
|
|
222
230
|
parseContextPrefix,
|
|
223
231
|
parsePromptRunYaml,
|
|
224
232
|
pendingApprovalRequests,
|
|
@@ -246,6 +254,7 @@ export {
|
|
|
246
254
|
selectionForMode,
|
|
247
255
|
selectionForRuntime,
|
|
248
256
|
serializeContext,
|
|
257
|
+
sessionFollowUrl,
|
|
249
258
|
sessionTone,
|
|
250
259
|
sourceLabel,
|
|
251
260
|
sourceTone,
|
|
@@ -261,6 +270,7 @@ export {
|
|
|
261
270
|
uniqueName,
|
|
262
271
|
unspecifiedHint,
|
|
263
272
|
useChatWindowManager,
|
|
273
|
+
useRemoteSession,
|
|
264
274
|
useRuntimeFamilies,
|
|
265
275
|
useRuntimePermissionCatalog,
|
|
266
276
|
useRuntimePresetResolution,
|
package/dist/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ai.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -20,6 +20,7 @@ function AccordionList({
|
|
|
20
20
|
renderActions,
|
|
21
21
|
renderMeta,
|
|
22
22
|
summary,
|
|
23
|
+
toolbar,
|
|
23
24
|
itemLabel,
|
|
24
25
|
allowReorder = false,
|
|
25
26
|
allowDrag = false,
|
|
@@ -140,7 +141,10 @@ function AccordionList({
|
|
|
140
141
|
else if (e.key === "End") focusAt(stops);
|
|
141
142
|
}
|
|
142
143
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex flex-col gap-2", className), children: [
|
|
143
|
-
summary !== void 0 && /* @__PURE__ */ jsxRuntime.
|
|
144
|
+
(summary !== void 0 || toolbar !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-accordion-summary": true, className: "flex items-center justify-between gap-2", children: [
|
|
145
|
+
summary !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: utils.cn("text-muted-foreground", jsonSchemaFormSize.labelSizeClass[size]), children: summary }),
|
|
146
|
+
toolbar !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex shrink-0 items-center", children: toolbar })
|
|
147
|
+
] }),
|
|
144
148
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
145
149
|
"div",
|
|
146
150
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccordionList.cjs","sources":["../../src/components/AccordionList.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n} from \"react\";\nimport { duplicateIndex, moveItem, removeIndex, setIndex } from \"../lib/collections\";\nimport type { SizeToken } from \"../lib/size\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiAdd, UiChevronDown, UiChevronRight, UiDotsVertical } from \"../icons\";\nimport { ItemActions } from \"./ItemActions\";\nimport { labelSizeClass } from \"./json-schema-form-size\";\n\n// AccordionList renders a list of items as one-line rows, expanding one at a\n// time into that item's own editor. It exists because a ten-field item stacked\n// in full costs ~700px of screen each, and a column of them says nothing about\n// which item is which.\n//\n// It knows nothing about what an item IS: `renderHeader` fills the collapsed\n// row, `renderBody` fills the open panel, and everything structural — the\n// disclosure, the aria pairing, roving arrow-key focus, the mutation buttons,\n// the add row — belongs to the list. Every editing capability is opt-in, so the\n// same component serves a read-only summary list and a full editor.\n//\n// \"Add\" is the list's own last row rather than a button beside it: the\n// affordance sits exactly where the next item will appear, and at zero items\n// that same row IS the empty state — one control, no separate screen.\n\n/** Handed to every slot, describing one row. */\nexport interface AccordionListItemContext<T> {\n item: T;\n index: number;\n open: boolean;\n /** Replaces this item in place, committing the whole list through `onChange`. */\n onChange: (next: T) => void;\n /** The ids the disclosure/panel pair is wired with. */\n headerId: string;\n panelId: string;\n}\n\nexport interface AccordionListProps<T> {\n items: T[];\n /** Receives the whole next list for every add, remove, duplicate, move or item edit. */\n onChange?: (next: T[]) => void;\n\n /** Row content, rendered inside the disclosure button after the chevron. */\n renderHeader: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Panel content. Rendered only while the row is open — never hidden with CSS. */\n renderBody: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Extra per-row actions, rendered before the built-in ones. */\n renderActions?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Persistent row metadata rendered between the disclosure and actions. */\n renderMeta?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Content above the list — typically the item count. */\n summary?: ReactNode;\n /** Names an item in its actions' accessible labels. Defaults to `Item <n>`. */\n itemLabel?: (ctx: { item: T; index: number }) => string;\n\n /** Offer the up/down reorder buttons. */\n allowReorder?: boolean;\n /**\n * Offer a grab handle at the head of every row, and drag-to-reorder across\n * the list. Arrow keys on a focused handle move the row too, so the gesture\n * is not mouse-only.\n */\n allowDrag?: boolean;\n /**\n * Whether a row takes part in dragging — as a source and as a destination.\n * Defaults to every row. A list holding rows that have no position of their\n * own (a filtered-out item, a soft-deleted one) says so here.\n */\n canDrag?: (ctx: { item: T; index: number }) => boolean;\n /**\n * Commits a completed drag or handle keypress. Defaults to the same move the\n * reorder buttons perform; a list whose order lives outside `items` (a\n * filtered view of a longer list) reorders the source through this instead.\n */\n onReorder?: (from: number, to: number) => void;\n /** Offer the duplicate button. */\n allowDuplicate?: boolean;\n /** Offer the remove button. */\n allowRemove?: boolean;\n /** Copy override for duplicate. Defaults to a one-level clone. */\n cloneItem?: (item: T) => T;\n /**\n * Reveal the per-row actions on hover/focus (the default). Pass false where an\n * action carries state the row has to show at rest — a visibility toggle whose\n * glyph says whether the item is hidden answers a question, not just offers a\n * click.\n */\n revealActions?: boolean;\n\n /** Seeds a new item. Supplying it is what adds the trailing add row. */\n onCreate?: () => T;\n /** Add-row text. Defaults to \"Add item\". */\n addLabel?: string;\n /** Add-row copy; grows into the empty state at zero items. */\n addDescription?: string;\n\n /** Controlled open row — an index, or null for none. */\n expanded?: number | null;\n /** Initially open row for uncontrolled usage. Defaults to none. */\n defaultExpanded?: number | null;\n onExpandedChange?: (index: number | null) => void;\n\n /** Hides the add row and every per-row action. Rows still open. */\n readOnly?: boolean;\n size?: SizeToken;\n /** Stem for the generated header/panel ids. */\n idPrefix?: string;\n /** Overrides the per-row id stem (e.g. with a JSON-Pointer instance path). */\n itemId?: (index: number) => string;\n /** Selector focused inside a freshly added row's panel. */\n focusSelector?: string;\n className?: string;\n listClassName?: string;\n rowClassName?: string;\n bodyClassName?: string;\n}\n\nconst DEFAULT_FOCUS_SELECTOR = \"[data-autofocus], input, select, textarea\";\n\nexport function AccordionList<T>({\n items,\n onChange,\n renderHeader,\n renderBody,\n renderActions,\n renderMeta,\n summary,\n itemLabel,\n allowReorder = false,\n allowDrag = false,\n canDrag,\n onReorder,\n allowDuplicate = false,\n allowRemove = false,\n revealActions = true,\n cloneItem,\n onCreate,\n addLabel = \"Add item\",\n addDescription,\n expanded: expandedProp,\n defaultExpanded = null,\n onExpandedChange,\n readOnly = false,\n size = \"md\",\n idPrefix = \"accordion\",\n itemId,\n focusSelector = DEFAULT_FOCUS_SELECTOR,\n className,\n listClassName,\n rowClassName,\n bodyClassName,\n}: AccordionListProps<T>) {\n // Which row is open is transient view state, deliberately local and never\n // persisted. The INDEX is the key, not the item: an editor typically replaces\n // the item object on every keystroke, so any identity-based map would die on\n // the first character typed. That makes it the mutators' job to keep this\n // pointing at the same row.\n const isControlled = expandedProp !== undefined;\n const [innerExpanded, setInnerExpanded] = useState<number | null>(defaultExpanded);\n const expanded = isControlled ? expandedProp : innerExpanded;\n\n // The in-flight drag: the row being dragged and the row the pointer is over.\n const [dragFrom, setDragFrom] = useState<number | null>(null);\n const [dragOver, setDragOver] = useState<number | null>(null);\n\n const rowRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const addRef = useRef<HTMLButtonElement | null>(null);\n const pendingFocus = useRef<{ kind: \"panel\" | \"row\" | \"add\"; index: number } | null>(null);\n\n useEffect(() => {\n const pending = pendingFocus.current;\n if (!pending) return;\n pendingFocus.current = null;\n if (pending.kind === \"add\") {\n addRef.current?.focus();\n return;\n }\n const row = rowRefs.current[pending.index];\n if (pending.kind === \"row\") {\n row?.focus();\n return;\n }\n // Land in the new item's first editable control, so adding an item leaves\n // the caret where the author is about to type.\n const panel = document.getElementById(panelId(pending.index));\n (panel?.querySelector<HTMLElement>(focusSelector) ?? row)?.focus();\n });\n\n function rowId(index: number): string {\n return itemId ? itemId(index) : `${idPrefix}-${index}`;\n }\n function panelId(index: number): string {\n return `${rowId(index)}-panel`;\n }\n function headerId(index: number): string {\n return `${rowId(index)}-header`;\n }\n\n function expand(next: number | null) {\n if (!isControlled) setInnerExpanded(next);\n onExpandedChange?.(next);\n }\n // A mutator moves the open row without the caller having asked to expand\n // anything, so it maps the current index rather than setting an absolute one.\n function reindexExpanded(map: (open: number | null) => number | null) {\n const next = map(expanded);\n if (next !== expanded) expand(next);\n }\n\n function commit(next: T[]) {\n onChange?.(next);\n }\n\n function add() {\n if (!onCreate) return;\n pendingFocus.current = { kind: \"panel\", index: items.length };\n expand(items.length);\n commit([...items, onCreate()]);\n }\n\n function remove(index: number) {\n reindexExpanded((open) =>\n open === index ? null : open !== null && open > index ? open - 1 : open,\n );\n pendingFocus.current =\n items.length === 1\n ? { kind: \"add\", index: 0 }\n : { kind: \"row\", index: Math.max(index - 1, 0) };\n commit(removeIndex(items, index));\n }\n\n function duplicate(index: number) {\n reindexExpanded((open) => (open !== null && open > index ? open + 1 : open));\n pendingFocus.current = { kind: \"row\", index: index + 1 };\n commit(duplicateIndex(items, index, cloneItem));\n }\n\n /** Where a drag would land, so the row can draw the insertion line. */\n function dropEdge(index: number): \"top\" | \"bottom\" | null {\n if (dragFrom === null || dragOver !== index || dragFrom === index) return null;\n return dragFrom < index ? \"bottom\" : \"top\";\n }\n\n function endDrag() {\n setDragFrom(null);\n setDragOver(null);\n }\n\n function reorder(from: number, to: number) {\n if (from === to) return;\n if (onReorder) onReorder(from, to);\n else move(from, to);\n }\n\n function move(index: number, to: number) {\n if (to < 0 || to >= items.length) return;\n // Follow the item, not the slot — otherwise moving an open row silently\n // expands whichever row took its place.\n reindexExpanded((open) => (open === index ? to : open === to ? index : open));\n pendingFocus.current = { kind: \"row\", index: to };\n commit(moveItem(items, index, to));\n }\n\n // Roving focus across the headers, with the add row as the final stop.\n function handleKeyDown(e: KeyboardEvent<HTMLButtonElement>, index: number) {\n const stops = rowRefs.current.length;\n const focusAt = (i: number) => {\n e.preventDefault();\n (i >= stops ? addRef.current : rowRefs.current[i])?.focus();\n };\n if (e.key === \"ArrowDown\") focusAt(Math.min(index + 1, stops));\n else if (e.key === \"ArrowUp\" && index > 0) focusAt(index - 1);\n else if (e.key === \"Home\") focusAt(0);\n else if (e.key === \"End\") focusAt(stops);\n }\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {summary !== undefined && (\n <p className={cn(\"text-muted-foreground\", labelSizeClass[size])}>{summary}</p>\n )}\n <div\n className={cn(\"divide-y divide-border rounded-lg border border-border\", listClassName)}\n >\n {items.map((item, i) => {\n const open = expanded === i;\n const slotCtx: AccordionListItemContext<T> = {\n item,\n index: i,\n open,\n onChange: (next) => commit(setIndex(items, i, next)),\n headerId: headerId(i),\n panelId: panelId(i),\n };\n const label = itemLabel?.({ item, index: i }) ?? `Item ${i + 1}`;\n const actions = renderActions?.(slotCtx);\n const meta = renderMeta?.(slotCtx);\n const dragEnabled = allowDrag && !readOnly && (canDrag?.({ item, index: i }) ?? true);\n const droppable = dragEnabled && dragFrom !== null && dragFrom !== i;\n const edge = droppable ? dropEdge(i) : null;\n return (\n <div key={i} className=\"group\">\n {/* The row is a plain element, NOT a button: the actions beside it\n are buttons, and nesting interactive content is invalid DOM\n with undefined click targeting. Only the disclosure toggles. */}\n <div\n data-accordion-row\n data-drop-edge={edge ?? undefined}\n onDragOver={(e) => {\n if (!droppable) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(i);\n }}\n onDrop={(e) => {\n if (!droppable || dragFrom === null) return;\n e.preventDefault();\n reorder(dragFrom, i);\n endDrag();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 hover:bg-accent/40\",\n i % 2 === 0 ? \"bg-card\" : \"bg-muted/20\",\n open && \"bg-accent/30\",\n dragFrom === i && \"opacity-40\",\n edge === \"top\" && \"shadow-[inset_0_2px_0_0_var(--color-primary)]\",\n edge === \"bottom\" && \"shadow-[inset_0_-2px_0_0_var(--color-primary)]\",\n rowClassName,\n )}\n >\n {allowDrag && !readOnly && (\n <button\n type=\"button\"\n aria-label={`Reorder ${label}`}\n title={`Reorder ${label}`}\n disabled={!dragEnabled}\n draggable={dragEnabled}\n onDragStart={(e) => {\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n // Drag the row, not the handle glyph the pointer grabbed.\n const row = e.currentTarget.closest(\"[data-accordion-row]\");\n if (row instanceof HTMLElement) e.dataTransfer.setDragImage(row, 12, 12);\n setDragFrom(i);\n }}\n onDragEnd={endDrag}\n onKeyDown={(e) => {\n const to = e.key === \"ArrowUp\" ? i - 1 : e.key === \"ArrowDown\" ? i + 1 : i;\n if (to === i || to < 0 || to >= items.length) return;\n e.preventDefault();\n reorder(i, to);\n }}\n className=\"shrink-0 cursor-grab rounded text-muted-foreground hover:text-foreground active:cursor-grabbing disabled:cursor-default disabled:opacity-30\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </button>\n )}\n <button\n type=\"button\"\n id={headerId(i)}\n aria-expanded={open}\n aria-controls={panelId(i)}\n ref={(el) => {\n rowRefs.current[i] = el;\n }}\n onKeyDown={(e) => handleKeyDown(e, i)}\n onClick={() => expand(open ? null : i)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon\n icon={open ? UiChevronDown : UiChevronRight}\n className=\"shrink-0 text-sm text-muted-foreground\"\n />\n {renderHeader(slotCtx)}\n </button>\n {meta}\n {!readOnly && (allowReorder || allowDuplicate || allowRemove || actions) && (\n <ItemActions\n label={label}\n index={i}\n count={items.length}\n size={size}\n reveal={revealActions}\n {...(actions ? { leading: actions } : {})}\n {...(allowReorder ? { onMove: (to: number) => move(i, to) } : {})}\n {...(allowDuplicate ? { onDuplicate: () => duplicate(i) } : {})}\n {...(allowRemove ? { onRemove: () => remove(i) } : {})}\n />\n )}\n </div>\n {open && (\n <div\n id={panelId(i)}\n role=\"region\"\n aria-labelledby={headerId(i)}\n className={cn(\"border-t border-border bg-muted/20 p-3\", bodyClassName)}\n >\n {renderBody(slotCtx)}\n </div>\n )}\n </div>\n );\n })}\n {!readOnly && onCreate && (\n <AddItemRow\n buttonRef={addRef}\n label={addLabel}\n empty={items.length === 0}\n onAdd={add}\n onKeyDown={(e) => handleKeyDown(e, items.length)}\n {...(addDescription ? { copy: addDescription } : {})}\n />\n )}\n </div>\n </div>\n );\n}\n\n// AddItemRow is the list's own final row. At zero items it grows to carry the\n// explanation of what an item is — so the empty state is the same control the\n// author will use, not a separate screen they have to leave.\nfunction AddItemRow({\n label,\n copy,\n empty,\n onAdd,\n onKeyDown,\n buttonRef,\n}: {\n label: string;\n copy?: string;\n empty: boolean;\n onAdd: () => void;\n onKeyDown: (e: KeyboardEvent<HTMLButtonElement>) => void;\n buttonRef: MutableRefObject<HTMLButtonElement | null>;\n}) {\n return (\n <button\n type=\"button\"\n ref={(el) => {\n buttonRef.current = el;\n }}\n onClick={onAdd}\n onKeyDown={onKeyDown}\n className={cn(\n \"group/add flex w-full items-center gap-2 px-3 py-2 text-left text-muted-foreground hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring\",\n empty && \"items-start py-5\",\n )}\n >\n {/* Holds the chevron column, so this row lines up with the ones above it. */}\n <Icon icon={UiChevronRight} className=\"shrink-0 text-sm opacity-0\" />\n <span className=\"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-dashed border-border text-foreground/60 group-hover/add:border-primary group-hover/add:text-primary\">\n <Icon icon={UiAdd} className=\"text-[13px]\" />\n </span>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm font-medium text-foreground\">{label}</span>\n {copy && (\n <span className={cn(\"text-xs\", empty ? \"max-w-xl leading-snug\" : \"truncate\")}>{copy}</span>\n )}\n </span>\n </button>\n );\n}\n"],"names":["useState","useRef","useEffect","removeIndex","duplicateIndex","moveItem","cn","jsx","labelSizeClass","jsxs","setIndex","Icon","UiDotsVertical","UiChevronDown","UiChevronRight","ItemActions","UiAdd"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AccordionList.cjs","sources":["../../src/components/AccordionList.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n} from \"react\";\nimport { duplicateIndex, moveItem, removeIndex, setIndex } from \"../lib/collections\";\nimport type { SizeToken } from \"../lib/size\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiAdd, UiChevronDown, UiChevronRight, UiDotsVertical } from \"../icons\";\nimport { ItemActions } from \"./ItemActions\";\nimport { labelSizeClass } from \"./json-schema-form-size\";\n\n// AccordionList renders a list of items as one-line rows, expanding one at a\n// time into that item's own editor. It exists because a ten-field item stacked\n// in full costs ~700px of screen each, and a column of them says nothing about\n// which item is which.\n//\n// It knows nothing about what an item IS: `renderHeader` fills the collapsed\n// row, `renderBody` fills the open panel, and everything structural — the\n// disclosure, the aria pairing, roving arrow-key focus, the mutation buttons,\n// the add row — belongs to the list. Every editing capability is opt-in, so the\n// same component serves a read-only summary list and a full editor.\n//\n// \"Add\" is the list's own last row rather than a button beside it: the\n// affordance sits exactly where the next item will appear, and at zero items\n// that same row IS the empty state — one control, no separate screen.\n\n/** Handed to every slot, describing one row. */\nexport interface AccordionListItemContext<T> {\n item: T;\n index: number;\n open: boolean;\n /** Replaces this item in place, committing the whole list through `onChange`. */\n onChange: (next: T) => void;\n /** The ids the disclosure/panel pair is wired with. */\n headerId: string;\n panelId: string;\n}\n\nexport interface AccordionListProps<T> {\n items: T[];\n /** Receives the whole next list for every add, remove, duplicate, move or item edit. */\n onChange?: (next: T[]) => void;\n\n /** Row content, rendered inside the disclosure button after the chevron. */\n renderHeader: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Panel content. Rendered only while the row is open — never hidden with CSS. */\n renderBody: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Extra per-row actions, rendered before the built-in ones. */\n renderActions?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Persistent row metadata rendered between the disclosure and actions. */\n renderMeta?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Content above the list — typically the item count. */\n summary?: ReactNode;\n /** List-level actions, right-aligned on the summary line (e.g. a view menu). */\n toolbar?: ReactNode;\n /** Names an item in its actions' accessible labels. Defaults to `Item <n>`. */\n itemLabel?: (ctx: { item: T; index: number }) => string;\n\n /** Offer the up/down reorder buttons. */\n allowReorder?: boolean;\n /**\n * Offer a grab handle at the head of every row, and drag-to-reorder across\n * the list. Arrow keys on a focused handle move the row too, so the gesture\n * is not mouse-only.\n */\n allowDrag?: boolean;\n /**\n * Whether a row takes part in dragging — as a source and as a destination.\n * Defaults to every row. A list holding rows that have no position of their\n * own (a filtered-out item, a soft-deleted one) says so here.\n */\n canDrag?: (ctx: { item: T; index: number }) => boolean;\n /**\n * Commits a completed drag or handle keypress. Defaults to the same move the\n * reorder buttons perform; a list whose order lives outside `items` (a\n * filtered view of a longer list) reorders the source through this instead.\n */\n onReorder?: (from: number, to: number) => void;\n /** Offer the duplicate button. */\n allowDuplicate?: boolean;\n /** Offer the remove button. */\n allowRemove?: boolean;\n /** Copy override for duplicate. Defaults to a one-level clone. */\n cloneItem?: (item: T) => T;\n /**\n * Reveal the per-row actions on hover/focus (the default). Pass false where an\n * action carries state the row has to show at rest — a visibility toggle whose\n * glyph says whether the item is hidden answers a question, not just offers a\n * click.\n */\n revealActions?: boolean;\n\n /** Seeds a new item. Supplying it is what adds the trailing add row. */\n onCreate?: () => T;\n /** Add-row text. Defaults to \"Add item\". */\n addLabel?: string;\n /** Add-row copy; grows into the empty state at zero items. */\n addDescription?: string;\n\n /** Controlled open row — an index, or null for none. */\n expanded?: number | null;\n /** Initially open row for uncontrolled usage. Defaults to none. */\n defaultExpanded?: number | null;\n onExpandedChange?: (index: number | null) => void;\n\n /** Hides the add row and every per-row action. Rows still open. */\n readOnly?: boolean;\n size?: SizeToken;\n /** Stem for the generated header/panel ids. */\n idPrefix?: string;\n /** Overrides the per-row id stem (e.g. with a JSON-Pointer instance path). */\n itemId?: (index: number) => string;\n /** Selector focused inside a freshly added row's panel. */\n focusSelector?: string;\n className?: string;\n listClassName?: string;\n rowClassName?: string;\n bodyClassName?: string;\n}\n\nconst DEFAULT_FOCUS_SELECTOR = \"[data-autofocus], input, select, textarea\";\n\nexport function AccordionList<T>({\n items,\n onChange,\n renderHeader,\n renderBody,\n renderActions,\n renderMeta,\n summary,\n toolbar,\n itemLabel,\n allowReorder = false,\n allowDrag = false,\n canDrag,\n onReorder,\n allowDuplicate = false,\n allowRemove = false,\n revealActions = true,\n cloneItem,\n onCreate,\n addLabel = \"Add item\",\n addDescription,\n expanded: expandedProp,\n defaultExpanded = null,\n onExpandedChange,\n readOnly = false,\n size = \"md\",\n idPrefix = \"accordion\",\n itemId,\n focusSelector = DEFAULT_FOCUS_SELECTOR,\n className,\n listClassName,\n rowClassName,\n bodyClassName,\n}: AccordionListProps<T>) {\n // Which row is open is transient view state, deliberately local and never\n // persisted. The INDEX is the key, not the item: an editor typically replaces\n // the item object on every keystroke, so any identity-based map would die on\n // the first character typed. That makes it the mutators' job to keep this\n // pointing at the same row.\n const isControlled = expandedProp !== undefined;\n const [innerExpanded, setInnerExpanded] = useState<number | null>(defaultExpanded);\n const expanded = isControlled ? expandedProp : innerExpanded;\n\n // The in-flight drag: the row being dragged and the row the pointer is over.\n const [dragFrom, setDragFrom] = useState<number | null>(null);\n const [dragOver, setDragOver] = useState<number | null>(null);\n\n const rowRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const addRef = useRef<HTMLButtonElement | null>(null);\n const pendingFocus = useRef<{ kind: \"panel\" | \"row\" | \"add\"; index: number } | null>(null);\n\n useEffect(() => {\n const pending = pendingFocus.current;\n if (!pending) return;\n pendingFocus.current = null;\n if (pending.kind === \"add\") {\n addRef.current?.focus();\n return;\n }\n const row = rowRefs.current[pending.index];\n if (pending.kind === \"row\") {\n row?.focus();\n return;\n }\n // Land in the new item's first editable control, so adding an item leaves\n // the caret where the author is about to type.\n const panel = document.getElementById(panelId(pending.index));\n (panel?.querySelector<HTMLElement>(focusSelector) ?? row)?.focus();\n });\n\n function rowId(index: number): string {\n return itemId ? itemId(index) : `${idPrefix}-${index}`;\n }\n function panelId(index: number): string {\n return `${rowId(index)}-panel`;\n }\n function headerId(index: number): string {\n return `${rowId(index)}-header`;\n }\n\n function expand(next: number | null) {\n if (!isControlled) setInnerExpanded(next);\n onExpandedChange?.(next);\n }\n // A mutator moves the open row without the caller having asked to expand\n // anything, so it maps the current index rather than setting an absolute one.\n function reindexExpanded(map: (open: number | null) => number | null) {\n const next = map(expanded);\n if (next !== expanded) expand(next);\n }\n\n function commit(next: T[]) {\n onChange?.(next);\n }\n\n function add() {\n if (!onCreate) return;\n pendingFocus.current = { kind: \"panel\", index: items.length };\n expand(items.length);\n commit([...items, onCreate()]);\n }\n\n function remove(index: number) {\n reindexExpanded((open) =>\n open === index ? null : open !== null && open > index ? open - 1 : open,\n );\n pendingFocus.current =\n items.length === 1\n ? { kind: \"add\", index: 0 }\n : { kind: \"row\", index: Math.max(index - 1, 0) };\n commit(removeIndex(items, index));\n }\n\n function duplicate(index: number) {\n reindexExpanded((open) => (open !== null && open > index ? open + 1 : open));\n pendingFocus.current = { kind: \"row\", index: index + 1 };\n commit(duplicateIndex(items, index, cloneItem));\n }\n\n /** Where a drag would land, so the row can draw the insertion line. */\n function dropEdge(index: number): \"top\" | \"bottom\" | null {\n if (dragFrom === null || dragOver !== index || dragFrom === index) return null;\n return dragFrom < index ? \"bottom\" : \"top\";\n }\n\n function endDrag() {\n setDragFrom(null);\n setDragOver(null);\n }\n\n function reorder(from: number, to: number) {\n if (from === to) return;\n if (onReorder) onReorder(from, to);\n else move(from, to);\n }\n\n function move(index: number, to: number) {\n if (to < 0 || to >= items.length) return;\n // Follow the item, not the slot — otherwise moving an open row silently\n // expands whichever row took its place.\n reindexExpanded((open) => (open === index ? to : open === to ? index : open));\n pendingFocus.current = { kind: \"row\", index: to };\n commit(moveItem(items, index, to));\n }\n\n // Roving focus across the headers, with the add row as the final stop.\n function handleKeyDown(e: KeyboardEvent<HTMLButtonElement>, index: number) {\n const stops = rowRefs.current.length;\n const focusAt = (i: number) => {\n e.preventDefault();\n (i >= stops ? addRef.current : rowRefs.current[i])?.focus();\n };\n if (e.key === \"ArrowDown\") focusAt(Math.min(index + 1, stops));\n else if (e.key === \"ArrowUp\" && index > 0) focusAt(index - 1);\n else if (e.key === \"Home\") focusAt(0);\n else if (e.key === \"End\") focusAt(stops);\n }\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {(summary !== undefined || toolbar !== undefined) && (\n <div data-accordion-summary className=\"flex items-center justify-between gap-2\">\n {summary !== undefined && (\n <p className={cn(\"text-muted-foreground\", labelSizeClass[size])}>{summary}</p>\n )}\n {toolbar !== undefined && (\n <div className=\"ml-auto flex shrink-0 items-center\">{toolbar}</div>\n )}\n </div>\n )}\n <div\n className={cn(\"divide-y divide-border rounded-lg border border-border\", listClassName)}\n >\n {items.map((item, i) => {\n const open = expanded === i;\n const slotCtx: AccordionListItemContext<T> = {\n item,\n index: i,\n open,\n onChange: (next) => commit(setIndex(items, i, next)),\n headerId: headerId(i),\n panelId: panelId(i),\n };\n const label = itemLabel?.({ item, index: i }) ?? `Item ${i + 1}`;\n const actions = renderActions?.(slotCtx);\n const meta = renderMeta?.(slotCtx);\n const dragEnabled = allowDrag && !readOnly && (canDrag?.({ item, index: i }) ?? true);\n const droppable = dragEnabled && dragFrom !== null && dragFrom !== i;\n const edge = droppable ? dropEdge(i) : null;\n return (\n <div key={i} className=\"group\">\n {/* The row is a plain element, NOT a button: the actions beside it\n are buttons, and nesting interactive content is invalid DOM\n with undefined click targeting. Only the disclosure toggles. */}\n <div\n data-accordion-row\n data-drop-edge={edge ?? undefined}\n onDragOver={(e) => {\n if (!droppable) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(i);\n }}\n onDrop={(e) => {\n if (!droppable || dragFrom === null) return;\n e.preventDefault();\n reorder(dragFrom, i);\n endDrag();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 hover:bg-accent/40\",\n i % 2 === 0 ? \"bg-card\" : \"bg-muted/20\",\n open && \"bg-accent/30\",\n dragFrom === i && \"opacity-40\",\n edge === \"top\" && \"shadow-[inset_0_2px_0_0_var(--color-primary)]\",\n edge === \"bottom\" && \"shadow-[inset_0_-2px_0_0_var(--color-primary)]\",\n rowClassName,\n )}\n >\n {allowDrag && !readOnly && (\n <button\n type=\"button\"\n aria-label={`Reorder ${label}`}\n title={`Reorder ${label}`}\n disabled={!dragEnabled}\n draggable={dragEnabled}\n onDragStart={(e) => {\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n // Drag the row, not the handle glyph the pointer grabbed.\n const row = e.currentTarget.closest(\"[data-accordion-row]\");\n if (row instanceof HTMLElement) e.dataTransfer.setDragImage(row, 12, 12);\n setDragFrom(i);\n }}\n onDragEnd={endDrag}\n onKeyDown={(e) => {\n const to = e.key === \"ArrowUp\" ? i - 1 : e.key === \"ArrowDown\" ? i + 1 : i;\n if (to === i || to < 0 || to >= items.length) return;\n e.preventDefault();\n reorder(i, to);\n }}\n className=\"shrink-0 cursor-grab rounded text-muted-foreground hover:text-foreground active:cursor-grabbing disabled:cursor-default disabled:opacity-30\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </button>\n )}\n <button\n type=\"button\"\n id={headerId(i)}\n aria-expanded={open}\n aria-controls={panelId(i)}\n ref={(el) => {\n rowRefs.current[i] = el;\n }}\n onKeyDown={(e) => handleKeyDown(e, i)}\n onClick={() => expand(open ? null : i)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon\n icon={open ? UiChevronDown : UiChevronRight}\n className=\"shrink-0 text-sm text-muted-foreground\"\n />\n {renderHeader(slotCtx)}\n </button>\n {meta}\n {!readOnly && (allowReorder || allowDuplicate || allowRemove || actions) && (\n <ItemActions\n label={label}\n index={i}\n count={items.length}\n size={size}\n reveal={revealActions}\n {...(actions ? { leading: actions } : {})}\n {...(allowReorder ? { onMove: (to: number) => move(i, to) } : {})}\n {...(allowDuplicate ? { onDuplicate: () => duplicate(i) } : {})}\n {...(allowRemove ? { onRemove: () => remove(i) } : {})}\n />\n )}\n </div>\n {open && (\n <div\n id={panelId(i)}\n role=\"region\"\n aria-labelledby={headerId(i)}\n className={cn(\"border-t border-border bg-muted/20 p-3\", bodyClassName)}\n >\n {renderBody(slotCtx)}\n </div>\n )}\n </div>\n );\n })}\n {!readOnly && onCreate && (\n <AddItemRow\n buttonRef={addRef}\n label={addLabel}\n empty={items.length === 0}\n onAdd={add}\n onKeyDown={(e) => handleKeyDown(e, items.length)}\n {...(addDescription ? { copy: addDescription } : {})}\n />\n )}\n </div>\n </div>\n );\n}\n\n// AddItemRow is the list's own final row. At zero items it grows to carry the\n// explanation of what an item is — so the empty state is the same control the\n// author will use, not a separate screen they have to leave.\nfunction AddItemRow({\n label,\n copy,\n empty,\n onAdd,\n onKeyDown,\n buttonRef,\n}: {\n label: string;\n copy?: string;\n empty: boolean;\n onAdd: () => void;\n onKeyDown: (e: KeyboardEvent<HTMLButtonElement>) => void;\n buttonRef: MutableRefObject<HTMLButtonElement | null>;\n}) {\n return (\n <button\n type=\"button\"\n ref={(el) => {\n buttonRef.current = el;\n }}\n onClick={onAdd}\n onKeyDown={onKeyDown}\n className={cn(\n \"group/add flex w-full items-center gap-2 px-3 py-2 text-left text-muted-foreground hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring\",\n empty && \"items-start py-5\",\n )}\n >\n {/* Holds the chevron column, so this row lines up with the ones above it. */}\n <Icon icon={UiChevronRight} className=\"shrink-0 text-sm opacity-0\" />\n <span className=\"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-dashed border-border text-foreground/60 group-hover/add:border-primary group-hover/add:text-primary\">\n <Icon icon={UiAdd} className=\"text-[13px]\" />\n </span>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm font-medium text-foreground\">{label}</span>\n {copy && (\n <span className={cn(\"text-xs\", empty ? \"max-w-xl leading-snug\" : \"truncate\")}>{copy}</span>\n )}\n </span>\n </button>\n );\n}\n"],"names":["useState","useRef","useEffect","removeIndex","duplicateIndex","moveItem","cn","jsxs","jsx","labelSizeClass","setIndex","Icon","UiDotsVertical","UiChevronDown","UiChevronRight","ItemActions","UiAdd"],"mappings":"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"}
|
|
@@ -25,6 +25,8 @@ export interface AccordionListProps<T> {
|
|
|
25
25
|
renderMeta?: (ctx: AccordionListItemContext<T>) => ReactNode;
|
|
26
26
|
/** Content above the list — typically the item count. */
|
|
27
27
|
summary?: ReactNode;
|
|
28
|
+
/** List-level actions, right-aligned on the summary line (e.g. a view menu). */
|
|
29
|
+
toolbar?: ReactNode;
|
|
28
30
|
/** Names an item in its actions' accessible labels. Defaults to `Item <n>`. */
|
|
29
31
|
itemLabel?: (ctx: {
|
|
30
32
|
item: T;
|
|
@@ -91,5 +93,5 @@ export interface AccordionListProps<T> {
|
|
|
91
93
|
rowClassName?: string;
|
|
92
94
|
bodyClassName?: string;
|
|
93
95
|
}
|
|
94
|
-
export declare function AccordionList<T>({ items, onChange, renderHeader, renderBody, renderActions, renderMeta, summary, itemLabel, allowReorder, allowDrag, canDrag, onReorder, allowDuplicate, allowRemove, revealActions, cloneItem, onCreate, addLabel, addDescription, expanded: expandedProp, defaultExpanded, onExpandedChange, readOnly, size, idPrefix, itemId, focusSelector, className, listClassName, rowClassName, bodyClassName, }: AccordionListProps<T>): import("react").JSX.Element;
|
|
96
|
+
export declare function AccordionList<T>({ items, onChange, renderHeader, renderBody, renderActions, renderMeta, summary, toolbar, itemLabel, allowReorder, allowDrag, canDrag, onReorder, allowDuplicate, allowRemove, revealActions, cloneItem, onCreate, addLabel, addDescription, expanded: expandedProp, defaultExpanded, onExpandedChange, readOnly, size, idPrefix, itemId, focusSelector, className, listClassName, rowClassName, bodyClassName, }: AccordionListProps<T>): import("react").JSX.Element;
|
|
95
97
|
//# sourceMappingURL=AccordionList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccordionList.d.ts","sourceRoot":"","sources":["../../src/components/AccordionList.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AccordionList.d.ts","sourceRoot":"","sources":["../../src/components/AccordionList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAsB7C,gDAAgD;AAChD,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,iFAAiF;IACjF,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,wFAAwF;IACxF,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAE/B,4EAA4E;IAC5E,YAAY,EAAE,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;IAC9D,kFAAkF;IAClF,UAAU,EAAE,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;IAC5D,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;IAChE,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;IAC7D,yDAAyD;IACzD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IAExD,yCAAyC;IACzC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;IACvD;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,kCAAkC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAElD,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAC/B,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,aAAa,EACb,UAAU,EACV,OAAO,EACP,OAAO,EACP,SAAS,EACT,YAAoB,EACpB,SAAiB,EACjB,OAAO,EACP,SAAS,EACT,cAAsB,EACtB,WAAmB,EACnB,aAAoB,EACpB,SAAS,EACT,QAAQ,EACR,QAAqB,EACrB,cAAc,EACd,QAAQ,EAAE,YAAY,EACtB,eAAsB,EACtB,gBAAgB,EAChB,QAAgB,EAChB,IAAW,EACX,QAAsB,EACtB,MAAM,EACN,aAAsC,EACtC,SAAS,EACT,aAAa,EACb,YAAY,EACZ,aAAa,GACd,EAAE,kBAAkB,CAAC,CAAC,CAAC,+BAgRvB"}
|
|
@@ -18,6 +18,7 @@ function AccordionList({
|
|
|
18
18
|
renderActions,
|
|
19
19
|
renderMeta,
|
|
20
20
|
summary,
|
|
21
|
+
toolbar,
|
|
21
22
|
itemLabel,
|
|
22
23
|
allowReorder = false,
|
|
23
24
|
allowDrag = false,
|
|
@@ -138,7 +139,10 @@ function AccordionList({
|
|
|
138
139
|
else if (e.key === "End") focusAt(stops);
|
|
139
140
|
}
|
|
140
141
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-2", className), children: [
|
|
141
|
-
summary !== void 0 && /* @__PURE__ */
|
|
142
|
+
(summary !== void 0 || toolbar !== void 0) && /* @__PURE__ */ jsxs("div", { "data-accordion-summary": true, className: "flex items-center justify-between gap-2", children: [
|
|
143
|
+
summary !== void 0 && /* @__PURE__ */ jsx("p", { className: cn("text-muted-foreground", labelSizeClass[size]), children: summary }),
|
|
144
|
+
toolbar !== void 0 && /* @__PURE__ */ jsx("div", { className: "ml-auto flex shrink-0 items-center", children: toolbar })
|
|
145
|
+
] }),
|
|
142
146
|
/* @__PURE__ */ jsxs(
|
|
143
147
|
"div",
|
|
144
148
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccordionList.js","sources":["../../src/components/AccordionList.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n} from \"react\";\nimport { duplicateIndex, moveItem, removeIndex, setIndex } from \"../lib/collections\";\nimport type { SizeToken } from \"../lib/size\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiAdd, UiChevronDown, UiChevronRight, UiDotsVertical } from \"../icons\";\nimport { ItemActions } from \"./ItemActions\";\nimport { labelSizeClass } from \"./json-schema-form-size\";\n\n// AccordionList renders a list of items as one-line rows, expanding one at a\n// time into that item's own editor. It exists because a ten-field item stacked\n// in full costs ~700px of screen each, and a column of them says nothing about\n// which item is which.\n//\n// It knows nothing about what an item IS: `renderHeader` fills the collapsed\n// row, `renderBody` fills the open panel, and everything structural — the\n// disclosure, the aria pairing, roving arrow-key focus, the mutation buttons,\n// the add row — belongs to the list. Every editing capability is opt-in, so the\n// same component serves a read-only summary list and a full editor.\n//\n// \"Add\" is the list's own last row rather than a button beside it: the\n// affordance sits exactly where the next item will appear, and at zero items\n// that same row IS the empty state — one control, no separate screen.\n\n/** Handed to every slot, describing one row. */\nexport interface AccordionListItemContext<T> {\n item: T;\n index: number;\n open: boolean;\n /** Replaces this item in place, committing the whole list through `onChange`. */\n onChange: (next: T) => void;\n /** The ids the disclosure/panel pair is wired with. */\n headerId: string;\n panelId: string;\n}\n\nexport interface AccordionListProps<T> {\n items: T[];\n /** Receives the whole next list for every add, remove, duplicate, move or item edit. */\n onChange?: (next: T[]) => void;\n\n /** Row content, rendered inside the disclosure button after the chevron. */\n renderHeader: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Panel content. Rendered only while the row is open — never hidden with CSS. */\n renderBody: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Extra per-row actions, rendered before the built-in ones. */\n renderActions?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Persistent row metadata rendered between the disclosure and actions. */\n renderMeta?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Content above the list — typically the item count. */\n summary?: ReactNode;\n /** Names an item in its actions' accessible labels. Defaults to `Item <n>`. */\n itemLabel?: (ctx: { item: T; index: number }) => string;\n\n /** Offer the up/down reorder buttons. */\n allowReorder?: boolean;\n /**\n * Offer a grab handle at the head of every row, and drag-to-reorder across\n * the list. Arrow keys on a focused handle move the row too, so the gesture\n * is not mouse-only.\n */\n allowDrag?: boolean;\n /**\n * Whether a row takes part in dragging — as a source and as a destination.\n * Defaults to every row. A list holding rows that have no position of their\n * own (a filtered-out item, a soft-deleted one) says so here.\n */\n canDrag?: (ctx: { item: T; index: number }) => boolean;\n /**\n * Commits a completed drag or handle keypress. Defaults to the same move the\n * reorder buttons perform; a list whose order lives outside `items` (a\n * filtered view of a longer list) reorders the source through this instead.\n */\n onReorder?: (from: number, to: number) => void;\n /** Offer the duplicate button. */\n allowDuplicate?: boolean;\n /** Offer the remove button. */\n allowRemove?: boolean;\n /** Copy override for duplicate. Defaults to a one-level clone. */\n cloneItem?: (item: T) => T;\n /**\n * Reveal the per-row actions on hover/focus (the default). Pass false where an\n * action carries state the row has to show at rest — a visibility toggle whose\n * glyph says whether the item is hidden answers a question, not just offers a\n * click.\n */\n revealActions?: boolean;\n\n /** Seeds a new item. Supplying it is what adds the trailing add row. */\n onCreate?: () => T;\n /** Add-row text. Defaults to \"Add item\". */\n addLabel?: string;\n /** Add-row copy; grows into the empty state at zero items. */\n addDescription?: string;\n\n /** Controlled open row — an index, or null for none. */\n expanded?: number | null;\n /** Initially open row for uncontrolled usage. Defaults to none. */\n defaultExpanded?: number | null;\n onExpandedChange?: (index: number | null) => void;\n\n /** Hides the add row and every per-row action. Rows still open. */\n readOnly?: boolean;\n size?: SizeToken;\n /** Stem for the generated header/panel ids. */\n idPrefix?: string;\n /** Overrides the per-row id stem (e.g. with a JSON-Pointer instance path). */\n itemId?: (index: number) => string;\n /** Selector focused inside a freshly added row's panel. */\n focusSelector?: string;\n className?: string;\n listClassName?: string;\n rowClassName?: string;\n bodyClassName?: string;\n}\n\nconst DEFAULT_FOCUS_SELECTOR = \"[data-autofocus], input, select, textarea\";\n\nexport function AccordionList<T>({\n items,\n onChange,\n renderHeader,\n renderBody,\n renderActions,\n renderMeta,\n summary,\n itemLabel,\n allowReorder = false,\n allowDrag = false,\n canDrag,\n onReorder,\n allowDuplicate = false,\n allowRemove = false,\n revealActions = true,\n cloneItem,\n onCreate,\n addLabel = \"Add item\",\n addDescription,\n expanded: expandedProp,\n defaultExpanded = null,\n onExpandedChange,\n readOnly = false,\n size = \"md\",\n idPrefix = \"accordion\",\n itemId,\n focusSelector = DEFAULT_FOCUS_SELECTOR,\n className,\n listClassName,\n rowClassName,\n bodyClassName,\n}: AccordionListProps<T>) {\n // Which row is open is transient view state, deliberately local and never\n // persisted. The INDEX is the key, not the item: an editor typically replaces\n // the item object on every keystroke, so any identity-based map would die on\n // the first character typed. That makes it the mutators' job to keep this\n // pointing at the same row.\n const isControlled = expandedProp !== undefined;\n const [innerExpanded, setInnerExpanded] = useState<number | null>(defaultExpanded);\n const expanded = isControlled ? expandedProp : innerExpanded;\n\n // The in-flight drag: the row being dragged and the row the pointer is over.\n const [dragFrom, setDragFrom] = useState<number | null>(null);\n const [dragOver, setDragOver] = useState<number | null>(null);\n\n const rowRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const addRef = useRef<HTMLButtonElement | null>(null);\n const pendingFocus = useRef<{ kind: \"panel\" | \"row\" | \"add\"; index: number } | null>(null);\n\n useEffect(() => {\n const pending = pendingFocus.current;\n if (!pending) return;\n pendingFocus.current = null;\n if (pending.kind === \"add\") {\n addRef.current?.focus();\n return;\n }\n const row = rowRefs.current[pending.index];\n if (pending.kind === \"row\") {\n row?.focus();\n return;\n }\n // Land in the new item's first editable control, so adding an item leaves\n // the caret where the author is about to type.\n const panel = document.getElementById(panelId(pending.index));\n (panel?.querySelector<HTMLElement>(focusSelector) ?? row)?.focus();\n });\n\n function rowId(index: number): string {\n return itemId ? itemId(index) : `${idPrefix}-${index}`;\n }\n function panelId(index: number): string {\n return `${rowId(index)}-panel`;\n }\n function headerId(index: number): string {\n return `${rowId(index)}-header`;\n }\n\n function expand(next: number | null) {\n if (!isControlled) setInnerExpanded(next);\n onExpandedChange?.(next);\n }\n // A mutator moves the open row without the caller having asked to expand\n // anything, so it maps the current index rather than setting an absolute one.\n function reindexExpanded(map: (open: number | null) => number | null) {\n const next = map(expanded);\n if (next !== expanded) expand(next);\n }\n\n function commit(next: T[]) {\n onChange?.(next);\n }\n\n function add() {\n if (!onCreate) return;\n pendingFocus.current = { kind: \"panel\", index: items.length };\n expand(items.length);\n commit([...items, onCreate()]);\n }\n\n function remove(index: number) {\n reindexExpanded((open) =>\n open === index ? null : open !== null && open > index ? open - 1 : open,\n );\n pendingFocus.current =\n items.length === 1\n ? { kind: \"add\", index: 0 }\n : { kind: \"row\", index: Math.max(index - 1, 0) };\n commit(removeIndex(items, index));\n }\n\n function duplicate(index: number) {\n reindexExpanded((open) => (open !== null && open > index ? open + 1 : open));\n pendingFocus.current = { kind: \"row\", index: index + 1 };\n commit(duplicateIndex(items, index, cloneItem));\n }\n\n /** Where a drag would land, so the row can draw the insertion line. */\n function dropEdge(index: number): \"top\" | \"bottom\" | null {\n if (dragFrom === null || dragOver !== index || dragFrom === index) return null;\n return dragFrom < index ? \"bottom\" : \"top\";\n }\n\n function endDrag() {\n setDragFrom(null);\n setDragOver(null);\n }\n\n function reorder(from: number, to: number) {\n if (from === to) return;\n if (onReorder) onReorder(from, to);\n else move(from, to);\n }\n\n function move(index: number, to: number) {\n if (to < 0 || to >= items.length) return;\n // Follow the item, not the slot — otherwise moving an open row silently\n // expands whichever row took its place.\n reindexExpanded((open) => (open === index ? to : open === to ? index : open));\n pendingFocus.current = { kind: \"row\", index: to };\n commit(moveItem(items, index, to));\n }\n\n // Roving focus across the headers, with the add row as the final stop.\n function handleKeyDown(e: KeyboardEvent<HTMLButtonElement>, index: number) {\n const stops = rowRefs.current.length;\n const focusAt = (i: number) => {\n e.preventDefault();\n (i >= stops ? addRef.current : rowRefs.current[i])?.focus();\n };\n if (e.key === \"ArrowDown\") focusAt(Math.min(index + 1, stops));\n else if (e.key === \"ArrowUp\" && index > 0) focusAt(index - 1);\n else if (e.key === \"Home\") focusAt(0);\n else if (e.key === \"End\") focusAt(stops);\n }\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {summary !== undefined && (\n <p className={cn(\"text-muted-foreground\", labelSizeClass[size])}>{summary}</p>\n )}\n <div\n className={cn(\"divide-y divide-border rounded-lg border border-border\", listClassName)}\n >\n {items.map((item, i) => {\n const open = expanded === i;\n const slotCtx: AccordionListItemContext<T> = {\n item,\n index: i,\n open,\n onChange: (next) => commit(setIndex(items, i, next)),\n headerId: headerId(i),\n panelId: panelId(i),\n };\n const label = itemLabel?.({ item, index: i }) ?? `Item ${i + 1}`;\n const actions = renderActions?.(slotCtx);\n const meta = renderMeta?.(slotCtx);\n const dragEnabled = allowDrag && !readOnly && (canDrag?.({ item, index: i }) ?? true);\n const droppable = dragEnabled && dragFrom !== null && dragFrom !== i;\n const edge = droppable ? dropEdge(i) : null;\n return (\n <div key={i} className=\"group\">\n {/* The row is a plain element, NOT a button: the actions beside it\n are buttons, and nesting interactive content is invalid DOM\n with undefined click targeting. Only the disclosure toggles. */}\n <div\n data-accordion-row\n data-drop-edge={edge ?? undefined}\n onDragOver={(e) => {\n if (!droppable) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(i);\n }}\n onDrop={(e) => {\n if (!droppable || dragFrom === null) return;\n e.preventDefault();\n reorder(dragFrom, i);\n endDrag();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 hover:bg-accent/40\",\n i % 2 === 0 ? \"bg-card\" : \"bg-muted/20\",\n open && \"bg-accent/30\",\n dragFrom === i && \"opacity-40\",\n edge === \"top\" && \"shadow-[inset_0_2px_0_0_var(--color-primary)]\",\n edge === \"bottom\" && \"shadow-[inset_0_-2px_0_0_var(--color-primary)]\",\n rowClassName,\n )}\n >\n {allowDrag && !readOnly && (\n <button\n type=\"button\"\n aria-label={`Reorder ${label}`}\n title={`Reorder ${label}`}\n disabled={!dragEnabled}\n draggable={dragEnabled}\n onDragStart={(e) => {\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n // Drag the row, not the handle glyph the pointer grabbed.\n const row = e.currentTarget.closest(\"[data-accordion-row]\");\n if (row instanceof HTMLElement) e.dataTransfer.setDragImage(row, 12, 12);\n setDragFrom(i);\n }}\n onDragEnd={endDrag}\n onKeyDown={(e) => {\n const to = e.key === \"ArrowUp\" ? i - 1 : e.key === \"ArrowDown\" ? i + 1 : i;\n if (to === i || to < 0 || to >= items.length) return;\n e.preventDefault();\n reorder(i, to);\n }}\n className=\"shrink-0 cursor-grab rounded text-muted-foreground hover:text-foreground active:cursor-grabbing disabled:cursor-default disabled:opacity-30\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </button>\n )}\n <button\n type=\"button\"\n id={headerId(i)}\n aria-expanded={open}\n aria-controls={panelId(i)}\n ref={(el) => {\n rowRefs.current[i] = el;\n }}\n onKeyDown={(e) => handleKeyDown(e, i)}\n onClick={() => expand(open ? null : i)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon\n icon={open ? UiChevronDown : UiChevronRight}\n className=\"shrink-0 text-sm text-muted-foreground\"\n />\n {renderHeader(slotCtx)}\n </button>\n {meta}\n {!readOnly && (allowReorder || allowDuplicate || allowRemove || actions) && (\n <ItemActions\n label={label}\n index={i}\n count={items.length}\n size={size}\n reveal={revealActions}\n {...(actions ? { leading: actions } : {})}\n {...(allowReorder ? { onMove: (to: number) => move(i, to) } : {})}\n {...(allowDuplicate ? { onDuplicate: () => duplicate(i) } : {})}\n {...(allowRemove ? { onRemove: () => remove(i) } : {})}\n />\n )}\n </div>\n {open && (\n <div\n id={panelId(i)}\n role=\"region\"\n aria-labelledby={headerId(i)}\n className={cn(\"border-t border-border bg-muted/20 p-3\", bodyClassName)}\n >\n {renderBody(slotCtx)}\n </div>\n )}\n </div>\n );\n })}\n {!readOnly && onCreate && (\n <AddItemRow\n buttonRef={addRef}\n label={addLabel}\n empty={items.length === 0}\n onAdd={add}\n onKeyDown={(e) => handleKeyDown(e, items.length)}\n {...(addDescription ? { copy: addDescription } : {})}\n />\n )}\n </div>\n </div>\n );\n}\n\n// AddItemRow is the list's own final row. At zero items it grows to carry the\n// explanation of what an item is — so the empty state is the same control the\n// author will use, not a separate screen they have to leave.\nfunction AddItemRow({\n label,\n copy,\n empty,\n onAdd,\n onKeyDown,\n buttonRef,\n}: {\n label: string;\n copy?: string;\n empty: boolean;\n onAdd: () => void;\n onKeyDown: (e: KeyboardEvent<HTMLButtonElement>) => void;\n buttonRef: MutableRefObject<HTMLButtonElement | null>;\n}) {\n return (\n <button\n type=\"button\"\n ref={(el) => {\n buttonRef.current = el;\n }}\n onClick={onAdd}\n onKeyDown={onKeyDown}\n className={cn(\n \"group/add flex w-full items-center gap-2 px-3 py-2 text-left text-muted-foreground hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring\",\n empty && \"items-start py-5\",\n )}\n >\n {/* Holds the chevron column, so this row lines up with the ones above it. */}\n <Icon icon={UiChevronRight} className=\"shrink-0 text-sm opacity-0\" />\n <span className=\"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-dashed border-border text-foreground/60 group-hover/add:border-primary group-hover/add:text-primary\">\n <Icon icon={UiAdd} className=\"text-[13px]\" />\n </span>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm font-medium text-foreground\">{label}</span>\n {copy && (\n <span className={cn(\"text-xs\", empty ? \"max-w-xl leading-snug\" : \"truncate\")}>{copy}</span>\n )}\n </span>\n </button>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AccordionList.js","sources":["../../src/components/AccordionList.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type MutableRefObject,\n type ReactNode,\n} from \"react\";\nimport { duplicateIndex, moveItem, removeIndex, setIndex } from \"../lib/collections\";\nimport type { SizeToken } from \"../lib/size\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiAdd, UiChevronDown, UiChevronRight, UiDotsVertical } from \"../icons\";\nimport { ItemActions } from \"./ItemActions\";\nimport { labelSizeClass } from \"./json-schema-form-size\";\n\n// AccordionList renders a list of items as one-line rows, expanding one at a\n// time into that item's own editor. It exists because a ten-field item stacked\n// in full costs ~700px of screen each, and a column of them says nothing about\n// which item is which.\n//\n// It knows nothing about what an item IS: `renderHeader` fills the collapsed\n// row, `renderBody` fills the open panel, and everything structural — the\n// disclosure, the aria pairing, roving arrow-key focus, the mutation buttons,\n// the add row — belongs to the list. Every editing capability is opt-in, so the\n// same component serves a read-only summary list and a full editor.\n//\n// \"Add\" is the list's own last row rather than a button beside it: the\n// affordance sits exactly where the next item will appear, and at zero items\n// that same row IS the empty state — one control, no separate screen.\n\n/** Handed to every slot, describing one row. */\nexport interface AccordionListItemContext<T> {\n item: T;\n index: number;\n open: boolean;\n /** Replaces this item in place, committing the whole list through `onChange`. */\n onChange: (next: T) => void;\n /** The ids the disclosure/panel pair is wired with. */\n headerId: string;\n panelId: string;\n}\n\nexport interface AccordionListProps<T> {\n items: T[];\n /** Receives the whole next list for every add, remove, duplicate, move or item edit. */\n onChange?: (next: T[]) => void;\n\n /** Row content, rendered inside the disclosure button after the chevron. */\n renderHeader: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Panel content. Rendered only while the row is open — never hidden with CSS. */\n renderBody: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Extra per-row actions, rendered before the built-in ones. */\n renderActions?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Persistent row metadata rendered between the disclosure and actions. */\n renderMeta?: (ctx: AccordionListItemContext<T>) => ReactNode;\n /** Content above the list — typically the item count. */\n summary?: ReactNode;\n /** List-level actions, right-aligned on the summary line (e.g. a view menu). */\n toolbar?: ReactNode;\n /** Names an item in its actions' accessible labels. Defaults to `Item <n>`. */\n itemLabel?: (ctx: { item: T; index: number }) => string;\n\n /** Offer the up/down reorder buttons. */\n allowReorder?: boolean;\n /**\n * Offer a grab handle at the head of every row, and drag-to-reorder across\n * the list. Arrow keys on a focused handle move the row too, so the gesture\n * is not mouse-only.\n */\n allowDrag?: boolean;\n /**\n * Whether a row takes part in dragging — as a source and as a destination.\n * Defaults to every row. A list holding rows that have no position of their\n * own (a filtered-out item, a soft-deleted one) says so here.\n */\n canDrag?: (ctx: { item: T; index: number }) => boolean;\n /**\n * Commits a completed drag or handle keypress. Defaults to the same move the\n * reorder buttons perform; a list whose order lives outside `items` (a\n * filtered view of a longer list) reorders the source through this instead.\n */\n onReorder?: (from: number, to: number) => void;\n /** Offer the duplicate button. */\n allowDuplicate?: boolean;\n /** Offer the remove button. */\n allowRemove?: boolean;\n /** Copy override for duplicate. Defaults to a one-level clone. */\n cloneItem?: (item: T) => T;\n /**\n * Reveal the per-row actions on hover/focus (the default). Pass false where an\n * action carries state the row has to show at rest — a visibility toggle whose\n * glyph says whether the item is hidden answers a question, not just offers a\n * click.\n */\n revealActions?: boolean;\n\n /** Seeds a new item. Supplying it is what adds the trailing add row. */\n onCreate?: () => T;\n /** Add-row text. Defaults to \"Add item\". */\n addLabel?: string;\n /** Add-row copy; grows into the empty state at zero items. */\n addDescription?: string;\n\n /** Controlled open row — an index, or null for none. */\n expanded?: number | null;\n /** Initially open row for uncontrolled usage. Defaults to none. */\n defaultExpanded?: number | null;\n onExpandedChange?: (index: number | null) => void;\n\n /** Hides the add row and every per-row action. Rows still open. */\n readOnly?: boolean;\n size?: SizeToken;\n /** Stem for the generated header/panel ids. */\n idPrefix?: string;\n /** Overrides the per-row id stem (e.g. with a JSON-Pointer instance path). */\n itemId?: (index: number) => string;\n /** Selector focused inside a freshly added row's panel. */\n focusSelector?: string;\n className?: string;\n listClassName?: string;\n rowClassName?: string;\n bodyClassName?: string;\n}\n\nconst DEFAULT_FOCUS_SELECTOR = \"[data-autofocus], input, select, textarea\";\n\nexport function AccordionList<T>({\n items,\n onChange,\n renderHeader,\n renderBody,\n renderActions,\n renderMeta,\n summary,\n toolbar,\n itemLabel,\n allowReorder = false,\n allowDrag = false,\n canDrag,\n onReorder,\n allowDuplicate = false,\n allowRemove = false,\n revealActions = true,\n cloneItem,\n onCreate,\n addLabel = \"Add item\",\n addDescription,\n expanded: expandedProp,\n defaultExpanded = null,\n onExpandedChange,\n readOnly = false,\n size = \"md\",\n idPrefix = \"accordion\",\n itemId,\n focusSelector = DEFAULT_FOCUS_SELECTOR,\n className,\n listClassName,\n rowClassName,\n bodyClassName,\n}: AccordionListProps<T>) {\n // Which row is open is transient view state, deliberately local and never\n // persisted. The INDEX is the key, not the item: an editor typically replaces\n // the item object on every keystroke, so any identity-based map would die on\n // the first character typed. That makes it the mutators' job to keep this\n // pointing at the same row.\n const isControlled = expandedProp !== undefined;\n const [innerExpanded, setInnerExpanded] = useState<number | null>(defaultExpanded);\n const expanded = isControlled ? expandedProp : innerExpanded;\n\n // The in-flight drag: the row being dragged and the row the pointer is over.\n const [dragFrom, setDragFrom] = useState<number | null>(null);\n const [dragOver, setDragOver] = useState<number | null>(null);\n\n const rowRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const addRef = useRef<HTMLButtonElement | null>(null);\n const pendingFocus = useRef<{ kind: \"panel\" | \"row\" | \"add\"; index: number } | null>(null);\n\n useEffect(() => {\n const pending = pendingFocus.current;\n if (!pending) return;\n pendingFocus.current = null;\n if (pending.kind === \"add\") {\n addRef.current?.focus();\n return;\n }\n const row = rowRefs.current[pending.index];\n if (pending.kind === \"row\") {\n row?.focus();\n return;\n }\n // Land in the new item's first editable control, so adding an item leaves\n // the caret where the author is about to type.\n const panel = document.getElementById(panelId(pending.index));\n (panel?.querySelector<HTMLElement>(focusSelector) ?? row)?.focus();\n });\n\n function rowId(index: number): string {\n return itemId ? itemId(index) : `${idPrefix}-${index}`;\n }\n function panelId(index: number): string {\n return `${rowId(index)}-panel`;\n }\n function headerId(index: number): string {\n return `${rowId(index)}-header`;\n }\n\n function expand(next: number | null) {\n if (!isControlled) setInnerExpanded(next);\n onExpandedChange?.(next);\n }\n // A mutator moves the open row without the caller having asked to expand\n // anything, so it maps the current index rather than setting an absolute one.\n function reindexExpanded(map: (open: number | null) => number | null) {\n const next = map(expanded);\n if (next !== expanded) expand(next);\n }\n\n function commit(next: T[]) {\n onChange?.(next);\n }\n\n function add() {\n if (!onCreate) return;\n pendingFocus.current = { kind: \"panel\", index: items.length };\n expand(items.length);\n commit([...items, onCreate()]);\n }\n\n function remove(index: number) {\n reindexExpanded((open) =>\n open === index ? null : open !== null && open > index ? open - 1 : open,\n );\n pendingFocus.current =\n items.length === 1\n ? { kind: \"add\", index: 0 }\n : { kind: \"row\", index: Math.max(index - 1, 0) };\n commit(removeIndex(items, index));\n }\n\n function duplicate(index: number) {\n reindexExpanded((open) => (open !== null && open > index ? open + 1 : open));\n pendingFocus.current = { kind: \"row\", index: index + 1 };\n commit(duplicateIndex(items, index, cloneItem));\n }\n\n /** Where a drag would land, so the row can draw the insertion line. */\n function dropEdge(index: number): \"top\" | \"bottom\" | null {\n if (dragFrom === null || dragOver !== index || dragFrom === index) return null;\n return dragFrom < index ? \"bottom\" : \"top\";\n }\n\n function endDrag() {\n setDragFrom(null);\n setDragOver(null);\n }\n\n function reorder(from: number, to: number) {\n if (from === to) return;\n if (onReorder) onReorder(from, to);\n else move(from, to);\n }\n\n function move(index: number, to: number) {\n if (to < 0 || to >= items.length) return;\n // Follow the item, not the slot — otherwise moving an open row silently\n // expands whichever row took its place.\n reindexExpanded((open) => (open === index ? to : open === to ? index : open));\n pendingFocus.current = { kind: \"row\", index: to };\n commit(moveItem(items, index, to));\n }\n\n // Roving focus across the headers, with the add row as the final stop.\n function handleKeyDown(e: KeyboardEvent<HTMLButtonElement>, index: number) {\n const stops = rowRefs.current.length;\n const focusAt = (i: number) => {\n e.preventDefault();\n (i >= stops ? addRef.current : rowRefs.current[i])?.focus();\n };\n if (e.key === \"ArrowDown\") focusAt(Math.min(index + 1, stops));\n else if (e.key === \"ArrowUp\" && index > 0) focusAt(index - 1);\n else if (e.key === \"Home\") focusAt(0);\n else if (e.key === \"End\") focusAt(stops);\n }\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n {(summary !== undefined || toolbar !== undefined) && (\n <div data-accordion-summary className=\"flex items-center justify-between gap-2\">\n {summary !== undefined && (\n <p className={cn(\"text-muted-foreground\", labelSizeClass[size])}>{summary}</p>\n )}\n {toolbar !== undefined && (\n <div className=\"ml-auto flex shrink-0 items-center\">{toolbar}</div>\n )}\n </div>\n )}\n <div\n className={cn(\"divide-y divide-border rounded-lg border border-border\", listClassName)}\n >\n {items.map((item, i) => {\n const open = expanded === i;\n const slotCtx: AccordionListItemContext<T> = {\n item,\n index: i,\n open,\n onChange: (next) => commit(setIndex(items, i, next)),\n headerId: headerId(i),\n panelId: panelId(i),\n };\n const label = itemLabel?.({ item, index: i }) ?? `Item ${i + 1}`;\n const actions = renderActions?.(slotCtx);\n const meta = renderMeta?.(slotCtx);\n const dragEnabled = allowDrag && !readOnly && (canDrag?.({ item, index: i }) ?? true);\n const droppable = dragEnabled && dragFrom !== null && dragFrom !== i;\n const edge = droppable ? dropEdge(i) : null;\n return (\n <div key={i} className=\"group\">\n {/* The row is a plain element, NOT a button: the actions beside it\n are buttons, and nesting interactive content is invalid DOM\n with undefined click targeting. Only the disclosure toggles. */}\n <div\n data-accordion-row\n data-drop-edge={edge ?? undefined}\n onDragOver={(e) => {\n if (!droppable) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(i);\n }}\n onDrop={(e) => {\n if (!droppable || dragFrom === null) return;\n e.preventDefault();\n reorder(dragFrom, i);\n endDrag();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 hover:bg-accent/40\",\n i % 2 === 0 ? \"bg-card\" : \"bg-muted/20\",\n open && \"bg-accent/30\",\n dragFrom === i && \"opacity-40\",\n edge === \"top\" && \"shadow-[inset_0_2px_0_0_var(--color-primary)]\",\n edge === \"bottom\" && \"shadow-[inset_0_-2px_0_0_var(--color-primary)]\",\n rowClassName,\n )}\n >\n {allowDrag && !readOnly && (\n <button\n type=\"button\"\n aria-label={`Reorder ${label}`}\n title={`Reorder ${label}`}\n disabled={!dragEnabled}\n draggable={dragEnabled}\n onDragStart={(e) => {\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n // Drag the row, not the handle glyph the pointer grabbed.\n const row = e.currentTarget.closest(\"[data-accordion-row]\");\n if (row instanceof HTMLElement) e.dataTransfer.setDragImage(row, 12, 12);\n setDragFrom(i);\n }}\n onDragEnd={endDrag}\n onKeyDown={(e) => {\n const to = e.key === \"ArrowUp\" ? i - 1 : e.key === \"ArrowDown\" ? i + 1 : i;\n if (to === i || to < 0 || to >= items.length) return;\n e.preventDefault();\n reorder(i, to);\n }}\n className=\"shrink-0 cursor-grab rounded text-muted-foreground hover:text-foreground active:cursor-grabbing disabled:cursor-default disabled:opacity-30\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </button>\n )}\n <button\n type=\"button\"\n id={headerId(i)}\n aria-expanded={open}\n aria-controls={panelId(i)}\n ref={(el) => {\n rowRefs.current[i] = el;\n }}\n onKeyDown={(e) => handleKeyDown(e, i)}\n onClick={() => expand(open ? null : i)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon\n icon={open ? UiChevronDown : UiChevronRight}\n className=\"shrink-0 text-sm text-muted-foreground\"\n />\n {renderHeader(slotCtx)}\n </button>\n {meta}\n {!readOnly && (allowReorder || allowDuplicate || allowRemove || actions) && (\n <ItemActions\n label={label}\n index={i}\n count={items.length}\n size={size}\n reveal={revealActions}\n {...(actions ? { leading: actions } : {})}\n {...(allowReorder ? { onMove: (to: number) => move(i, to) } : {})}\n {...(allowDuplicate ? { onDuplicate: () => duplicate(i) } : {})}\n {...(allowRemove ? { onRemove: () => remove(i) } : {})}\n />\n )}\n </div>\n {open && (\n <div\n id={panelId(i)}\n role=\"region\"\n aria-labelledby={headerId(i)}\n className={cn(\"border-t border-border bg-muted/20 p-3\", bodyClassName)}\n >\n {renderBody(slotCtx)}\n </div>\n )}\n </div>\n );\n })}\n {!readOnly && onCreate && (\n <AddItemRow\n buttonRef={addRef}\n label={addLabel}\n empty={items.length === 0}\n onAdd={add}\n onKeyDown={(e) => handleKeyDown(e, items.length)}\n {...(addDescription ? { copy: addDescription } : {})}\n />\n )}\n </div>\n </div>\n );\n}\n\n// AddItemRow is the list's own final row. At zero items it grows to carry the\n// explanation of what an item is — so the empty state is the same control the\n// author will use, not a separate screen they have to leave.\nfunction AddItemRow({\n label,\n copy,\n empty,\n onAdd,\n onKeyDown,\n buttonRef,\n}: {\n label: string;\n copy?: string;\n empty: boolean;\n onAdd: () => void;\n onKeyDown: (e: KeyboardEvent<HTMLButtonElement>) => void;\n buttonRef: MutableRefObject<HTMLButtonElement | null>;\n}) {\n return (\n <button\n type=\"button\"\n ref={(el) => {\n buttonRef.current = el;\n }}\n onClick={onAdd}\n onKeyDown={onKeyDown}\n className={cn(\n \"group/add flex w-full items-center gap-2 px-3 py-2 text-left text-muted-foreground hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring\",\n empty && \"items-start py-5\",\n )}\n >\n {/* Holds the chevron column, so this row lines up with the ones above it. */}\n <Icon icon={UiChevronRight} className=\"shrink-0 text-sm opacity-0\" />\n <span className=\"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-dashed border-border text-foreground/60 group-hover/add:border-primary group-hover/add:text-primary\">\n <Icon icon={UiAdd} className=\"text-[13px]\" />\n </span>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm font-medium text-foreground\">{label}</span>\n {copy && (\n <span className={cn(\"text-xs\", empty ? \"max-w-xl leading-snug\" : \"truncate\")}>{copy}</span>\n )}\n </span>\n </button>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ const FormLookupProvider = require("./FormLookupProvider.cjs");
|
|
|
13
13
|
const jsonSchemaFormDiscriminator = require("./json-schema-form-discriminator.cjs");
|
|
14
14
|
const jsonSchemaFormRefs = require("./json-schema-form-refs.cjs");
|
|
15
15
|
const jsonSchemaFormRender = require("./json-schema-form-render.cjs");
|
|
16
|
-
const
|
|
16
|
+
const jsonSchemaFormDefaults = require("./json-schema-form-defaults.cjs");
|
|
17
17
|
const jsonSchemaFormUtils = require("./json-schema-form-utils.cjs");
|
|
18
18
|
const jsonSchemaFormSize = require("./json-schema-form-size.cjs");
|
|
19
19
|
const jsonSchemaFormPreferences = require("./json-schema-form-preferences.cjs");
|
|
@@ -61,6 +61,7 @@ function JsonSchemaForm({
|
|
|
61
61
|
persistPreferences = true,
|
|
62
62
|
preferencesStorageKey = jsonSchemaFormPreferences.DEFAULT_PREFERENCES_STORAGE_KEY,
|
|
63
63
|
lookupFetcher,
|
|
64
|
+
expressionEvaluator,
|
|
64
65
|
applyDefaults = true
|
|
65
66
|
}) {
|
|
66
67
|
const [prefs, setPrefs] = react.useState(
|
|
@@ -77,7 +78,7 @@ function JsonSchemaForm({
|
|
|
77
78
|
};
|
|
78
79
|
const resolvedSchema = react.useMemo(() => jsonSchemaFormRefs.rehydrateRefs(schema), [schema]);
|
|
79
80
|
const effectiveValue = react.useMemo(
|
|
80
|
-
() => applyDefaults ?
|
|
81
|
+
() => applyDefaults ? jsonSchemaFormDefaults.applySchemaDefaults(resolvedSchema, value) : value,
|
|
81
82
|
[applyDefaults, resolvedSchema, value]
|
|
82
83
|
);
|
|
83
84
|
const preExtensions = pre ?? [];
|
|
@@ -86,21 +87,24 @@ function JsonSchemaForm({
|
|
|
86
87
|
schema: resolvedSchema,
|
|
87
88
|
value: effectiveValue,
|
|
88
89
|
errors,
|
|
90
|
+
viewOnly: readOnly,
|
|
89
91
|
hideReadOnlyFields,
|
|
90
92
|
hideEmpty,
|
|
91
93
|
pre: preExtensions,
|
|
92
94
|
rootValue: rootValue ?? effectiveValue,
|
|
93
95
|
instancePath,
|
|
94
96
|
...hiddenKeys ? { hiddenKeys } : {},
|
|
95
|
-
...fieldFilter.trim() ? { fieldFilter: fieldFilter.trim() } : {}
|
|
97
|
+
...fieldFilter.trim() ? { fieldFilter: fieldFilter.trim() } : {},
|
|
98
|
+
...expressionEvaluator ? { expressionEvaluator } : {}
|
|
96
99
|
}),
|
|
97
|
-
[effectiveValue, errors, fieldFilter, hiddenKeys, hideEmpty, hideReadOnlyFields, instancePath, preExtensions, resolvedSchema, rootValue]
|
|
100
|
+
[effectiveValue, errors, expressionEvaluator, fieldFilter, hiddenKeys, hideEmpty, hideReadOnlyFields, instancePath, preExtensions, readOnly, resolvedSchema, rootValue]
|
|
98
101
|
);
|
|
99
102
|
react.useEffect(() => {
|
|
100
103
|
if (!readOnly && effectiveValue !== value) onChange(effectiveValue);
|
|
101
104
|
}, [effectiveValue, onChange, readOnly, value]);
|
|
102
105
|
const ctx = {
|
|
103
106
|
readOnly,
|
|
107
|
+
viewOnly: readOnly,
|
|
104
108
|
hideReadOnlyFields,
|
|
105
109
|
hideEmpty,
|
|
106
110
|
autoSave,
|
|
@@ -115,6 +119,7 @@ function JsonSchemaForm({
|
|
|
115
119
|
errors,
|
|
116
120
|
depth: 0,
|
|
117
121
|
render: jsonSchemaFormRender.renderApi,
|
|
122
|
+
...expressionEvaluator ? { expressionEvaluator } : {},
|
|
118
123
|
...idPrefix ? { idPrefix } : {},
|
|
119
124
|
...fieldFilter.trim() ? { fieldFilter: fieldFilter.trim() } : {}
|
|
120
125
|
};
|