@softov/scena 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/controls/command.d.ts +18 -0
- package/dist/controls/command.js +150 -0
- package/dist/controls/events.d.ts +3 -0
- package/dist/controls/events.js +43 -0
- package/dist/controls/index.d.ts +10 -0
- package/dist/controls/index.js +10 -0
- package/dist/controls/keybinding.d.ts +9 -0
- package/dist/controls/keybinding.js +94 -0
- package/dist/controls/layout.d.ts +16 -0
- package/dist/controls/layout.js +201 -0
- package/dist/controls/manifest.d.ts +21 -0
- package/dist/controls/manifest.js +219 -0
- package/dist/controls/session.d.ts +16 -0
- package/dist/controls/session.js +95 -0
- package/dist/controls/shell.d.ts +8 -0
- package/dist/controls/shell.js +48 -0
- package/dist/controls/storage/layout-local.d.ts +6 -0
- package/dist/controls/storage/layout-local.js +47 -0
- package/dist/controls/storage/session-local.d.ts +5 -0
- package/dist/controls/storage/session-local.js +38 -0
- package/dist/controls/when.d.ts +7 -0
- package/dist/controls/when.js +226 -0
- package/dist/converters/a2ui-v0.10.d.ts +23 -0
- package/dist/converters/a2ui-v0.10.js +136 -0
- package/dist/converters/index.d.ts +1 -0
- package/dist/converters/index.js +1 -0
- package/dist/core/backends/modus-backend.d.ts +13 -0
- package/dist/core/backends/modus-backend.js +115 -0
- package/dist/core/backends/storage-backend.d.ts +7 -0
- package/dist/core/backends/storage-backend.js +73 -0
- package/dist/core/backends/yjs-backend.d.ts +3 -0
- package/dist/core/backends/yjs-backend.js +44 -0
- package/dist/core/binding-resolver.d.ts +7 -0
- package/dist/core/binding-resolver.js +106 -0
- package/dist/core/component-graph.d.ts +6 -0
- package/dist/core/component-graph.js +77 -0
- package/dist/core/component-registry.d.ts +7 -0
- package/dist/core/component-registry.js +87 -0
- package/dist/core/computed-dsl.d.ts +2 -0
- package/dist/core/computed-dsl.js +267 -0
- package/dist/core/converter-registry.d.ts +2 -0
- package/dist/core/converter-registry.js +37 -0
- package/dist/core/disposable.d.ts +3 -0
- package/dist/core/disposable.js +19 -0
- package/dist/core/dynamic-resolver.d.ts +12 -0
- package/dist/core/dynamic-resolver.js +134 -0
- package/dist/core/index.d.ts +44 -0
- package/dist/core/index.js +48 -0
- package/dist/core/label.d.ts +2 -0
- package/dist/core/label.js +14 -0
- package/dist/core/mount-menu.d.ts +2 -0
- package/dist/core/mount-menu.js +23 -0
- package/dist/core/mount-surface.d.ts +15 -0
- package/dist/core/mount-surface.js +214 -0
- package/dist/core/path-resolver.d.ts +14 -0
- package/dist/core/path-resolver.js +169 -0
- package/dist/core/permissions.d.ts +2 -0
- package/dist/core/permissions.js +50 -0
- package/dist/core/reactive-store.d.ts +17 -0
- package/dist/core/reactive-store.js +496 -0
- package/dist/core/scena.d.ts +2 -0
- package/dist/core/scena.js +115 -0
- package/dist/core/surface-presentation.d.ts +6 -0
- package/dist/core/surface-presentation.js +12 -0
- package/dist/i18n/i18n-backend.d.ts +2 -0
- package/dist/i18n/i18n-backend.js +35 -0
- package/dist/i18n/registry.d.ts +32 -0
- package/dist/i18n/registry.js +200 -0
- package/dist/porta/Limen.css +50 -0
- package/dist/porta/Limen.d.ts +16 -0
- package/dist/porta/Limen.js +14 -0
- package/dist/porta/PortaContext.d.ts +10 -0
- package/dist/porta/PortaContext.js +20 -0
- package/dist/porta/PortaLock.css +14 -0
- package/dist/porta/PortaLock.d.ts +10 -0
- package/dist/porta/PortaLock.js +13 -0
- package/dist/porta/SigillumGate.d.ts +8 -0
- package/dist/porta/SigillumGate.js +7 -0
- package/dist/porta/commands.d.ts +3 -0
- package/dist/porta/commands.js +125 -0
- package/dist/porta/index.d.ts +18 -0
- package/dist/porta/index.js +22 -0
- package/dist/porta/login-form.block.d.ts +2 -0
- package/dist/porta/login-form.block.js +7 -0
- package/dist/porta/match.d.ts +2 -0
- package/dist/porta/match.js +20 -0
- package/dist/porta/provider.d.ts +59 -0
- package/dist/porta/provider.js +1 -0
- package/dist/porta/providers/examples/google-oauth.d.ts +6 -0
- package/dist/porta/providers/examples/google-oauth.js +21 -0
- package/dist/porta/providers/examples/index.d.ts +6 -0
- package/dist/porta/providers/examples/index.js +3 -0
- package/dist/porta/providers/examples/magic-link.d.ts +8 -0
- package/dist/porta/providers/examples/magic-link.js +31 -0
- package/dist/porta/providers/examples/sms-otp.d.ts +9 -0
- package/dist/porta/providers/examples/sms-otp.js +31 -0
- package/dist/porta/providers/password.d.ts +12 -0
- package/dist/porta/providers/password.js +46 -0
- package/dist/porta/register-blocks.d.ts +3 -0
- package/dist/porta/register-blocks.js +31 -0
- package/dist/porta/register.d.ts +15 -0
- package/dist/porta/register.js +31 -0
- package/dist/porta/sigillum.d.ts +19 -0
- package/dist/porta/sigillum.js +41 -0
- package/dist/react/DefaultShell.d.ts +1 -0
- package/dist/react/DefaultShell.js +37 -0
- package/dist/react/Scena.d.ts +9 -0
- package/dist/react/Scena.js +20 -0
- package/dist/react/ScenaProvider.d.ts +8 -0
- package/dist/react/ScenaProvider.js +13 -0
- package/dist/react/ShellSplitter.d.ts +13 -0
- package/dist/react/ShellSplitter.js +88 -0
- package/dist/react/SurfaceArea.d.ts +13 -0
- package/dist/react/SurfaceArea.js +75 -0
- package/dist/react/Translate.d.ts +8 -0
- package/dist/react/Translate.js +10 -0
- package/dist/react/ViewMount.d.ts +12 -0
- package/dist/react/ViewMount.js +268 -0
- package/dist/react/core.d.ts +6 -0
- package/dist/react/core.js +10 -0
- package/dist/react/hooks/useCommand.d.ts +1 -0
- package/dist/react/hooks/useCommand.js +6 -0
- package/dist/react/hooks/useI18n.d.ts +9 -0
- package/dist/react/hooks/useI18n.js +16 -0
- package/dist/react/hooks/useLabel.d.ts +2 -0
- package/dist/react/hooks/useLabel.js +19 -0
- package/dist/react/hooks/useLayout.d.ts +2 -0
- package/dist/react/hooks/useLayout.js +10 -0
- package/dist/react/hooks/useMounts.d.ts +2 -0
- package/dist/react/hooks/useMounts.js +17 -0
- package/dist/react/hooks/useStore.d.ts +3 -0
- package/dist/react/hooks/useStore.js +40 -0
- package/dist/react/hooks/useStoreLabel.d.ts +3 -0
- package/dist/react/hooks/useStoreLabel.js +25 -0
- package/dist/react/hooks/useSurfacePresentation.d.ts +4 -0
- package/dist/react/hooks/useSurfacePresentation.js +16 -0
- package/dist/react/index.d.ts +20 -0
- package/dist/react/index.js +20 -0
- package/dist/react/mount-context.d.ts +8 -0
- package/dist/react/mount-context.js +20 -0
- package/dist/react/useSurfaceResize.d.ts +39 -0
- package/dist/react/useSurfaceResize.js +135 -0
- package/dist/runtime/args-schema.d.ts +11 -0
- package/dist/runtime/args-schema.js +45 -0
- package/dist/runtime/builtins.d.ts +3 -0
- package/dist/runtime/builtins.js +193 -0
- package/dist/runtime/client-handlers.d.ts +3 -0
- package/dist/runtime/client-handlers.js +19 -0
- package/dist/runtime/in-memory-socket.d.ts +7 -0
- package/dist/runtime/in-memory-socket.js +53 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/layout-commands.d.ts +5 -0
- package/dist/runtime/layout-commands.js +548 -0
- package/dist/runtime/opener-commands.d.ts +17 -0
- package/dist/runtime/opener-commands.js +96 -0
- package/dist/runtime/surface-bridge.d.ts +15 -0
- package/dist/runtime/surface-bridge.js +116 -0
- package/dist/styles/bar.css +155 -0
- package/dist/styles/base.css +285 -0
- package/dist/styles/geometry.css +32 -0
- package/dist/styles/index.d.ts +29 -0
- package/dist/styles/index.js +109 -0
- package/dist/styles/scrollbar.css +45 -0
- package/dist/styles/surface.css +179 -0
- package/dist/styles/theme.css +3 -0
- package/dist/styles/themes/dark.css +30 -0
- package/dist/styles/themes/light.css +1 -0
- package/dist/styles/themes/tokens.css +222 -0
- package/dist/types/binding-resolver.d.ts +30 -0
- package/dist/types/binding-resolver.js +1 -0
- package/dist/types/colors.d.ts +7 -0
- package/dist/types/colors.js +59 -0
- package/dist/types/command.d.ts +62 -0
- package/dist/types/command.js +1 -0
- package/dist/types/component-graph.d.ts +72 -0
- package/dist/types/component-graph.js +40 -0
- package/dist/types/component-registry.d.ts +70 -0
- package/dist/types/component-registry.js +1 -0
- package/dist/types/converter-registry.d.ts +17 -0
- package/dist/types/converter-registry.js +1 -0
- package/dist/types/disposable.d.ts +3 -0
- package/dist/types/disposable.js +1 -0
- package/dist/types/events.d.ts +75 -0
- package/dist/types/events.js +1 -0
- package/dist/types/host.d.ts +50 -0
- package/dist/types/host.js +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.js +23 -0
- package/dist/types/keybinding.d.ts +36 -0
- package/dist/types/keybinding.js +1 -0
- package/dist/types/label.d.ts +9 -0
- package/dist/types/label.js +1 -0
- package/dist/types/layout.d.ts +117 -0
- package/dist/types/layout.js +1 -0
- package/dist/types/manifest.d.ts +152 -0
- package/dist/types/manifest.js +1 -0
- package/dist/types/mount-menu.d.ts +8 -0
- package/dist/types/mount-menu.js +1 -0
- package/dist/types/mount-surface.d.ts +87 -0
- package/dist/types/mount-surface.js +1 -0
- package/dist/types/permissions.d.ts +18 -0
- package/dist/types/permissions.js +1 -0
- package/dist/types/query.d.ts +34 -0
- package/dist/types/query.js +7 -0
- package/dist/types/reactive-store.d.ts +39 -0
- package/dist/types/reactive-store.js +1 -0
- package/dist/types/scena.d.ts +53 -0
- package/dist/types/scena.js +1 -0
- package/dist/types/scope-backend.d.ts +16 -0
- package/dist/types/scope-backend.js +1 -0
- package/dist/types/session.d.ts +39 -0
- package/dist/types/session.js +1 -0
- package/dist/types/shell.d.ts +21 -0
- package/dist/types/shell.js +1 -0
- package/dist/types/when.d.ts +10 -0
- package/dist/types/when.js +1 -0
- package/dist/ui/_utils.d.ts +8 -0
- package/dist/ui/_utils.js +53 -0
- package/dist/ui/campus/CampusMappa.d.ts +18 -0
- package/dist/ui/campus/CampusMappa.js +106 -0
- package/dist/ui/campus/CampusNodus.d.ts +25 -0
- package/dist/ui/campus/CampusNodus.js +211 -0
- package/dist/ui/campus/CampusStratum.d.ts +13 -0
- package/dist/ui/campus/CampusStratum.js +20 -0
- package/dist/ui/campus/CampusVelum.d.ts +7 -0
- package/dist/ui/campus/CampusVelum.js +12 -0
- package/dist/ui/campus/CampusView.d.ts +27 -0
- package/dist/ui/campus/CampusView.js +420 -0
- package/dist/ui/campus/context.d.ts +13 -0
- package/dist/ui/campus/context.js +12 -0
- package/dist/ui/campus/index.d.ts +14 -0
- package/dist/ui/campus/index.js +11 -0
- package/dist/ui/campus/stratum-context.d.ts +2 -0
- package/dist/ui/campus/stratum-context.js +10 -0
- package/dist/ui/campus/types.d.ts +47 -0
- package/dist/ui/campus/types.js +4 -0
- package/dist/ui/chart/BarChart.css +53 -0
- package/dist/ui/chart/BarChart.d.ts +24 -0
- package/dist/ui/chart/BarChart.js +32 -0
- package/dist/ui/chart/Donut.css +15 -0
- package/dist/ui/chart/Donut.d.ts +15 -0
- package/dist/ui/chart/Donut.js +18 -0
- package/dist/ui/chart/LineChart.css +8 -0
- package/dist/ui/chart/LineChart.d.ts +24 -0
- package/dist/ui/chart/LineChart.js +29 -0
- package/dist/ui/chart/catalog.d.ts +2 -0
- package/dist/ui/chart/catalog.js +7 -0
- package/dist/ui/chart/index.d.ts +6 -0
- package/dist/ui/chart/index.js +4 -0
- package/dist/ui/control/Button.css +7 -0
- package/dist/ui/control/Button.d.ts +17 -0
- package/dist/ui/control/Button.js +17 -0
- package/dist/ui/control/CheckBox.css +16 -0
- package/dist/ui/control/CheckBox.d.ts +15 -0
- package/dist/ui/control/CheckBox.js +23 -0
- package/dist/ui/control/Checks.css +6 -0
- package/dist/ui/control/Checks.d.ts +9 -0
- package/dist/ui/control/Checks.js +10 -0
- package/dist/ui/control/ChoicePicker.css +16 -0
- package/dist/ui/control/ChoicePicker.d.ts +25 -0
- package/dist/ui/control/ChoicePicker.js +38 -0
- package/dist/ui/control/DateTimeInput.css +1 -0
- package/dist/ui/control/DateTimeInput.d.ts +22 -0
- package/dist/ui/control/DateTimeInput.js +41 -0
- package/dist/ui/control/LocaleToggle.css +38 -0
- package/dist/ui/control/LocaleToggle.d.ts +11 -0
- package/dist/ui/control/LocaleToggle.js +23 -0
- package/dist/ui/control/ReloadButton.d.ts +6 -0
- package/dist/ui/control/ReloadButton.js +9 -0
- package/dist/ui/control/Slider.css +2 -0
- package/dist/ui/control/Slider.d.ts +21 -0
- package/dist/ui/control/Slider.js +37 -0
- package/dist/ui/control/TextField.css +0 -0
- package/dist/ui/control/TextField.d.ts +25 -0
- package/dist/ui/control/TextField.js +36 -0
- package/dist/ui/control/_field-parts.css +102 -0
- package/dist/ui/control/_field-parts.d.ts +15 -0
- package/dist/ui/control/_field-parts.js +17 -0
- package/dist/ui/control/catalog.d.ts +2 -0
- package/dist/ui/control/catalog.js +10 -0
- package/dist/ui/control/index.d.ts +13 -0
- package/dist/ui/control/index.js +11 -0
- package/dist/ui/data/DataTable.d.ts +19 -0
- package/dist/ui/data/DataTable.js +20 -0
- package/dist/ui/data/Filter.d.ts +13 -0
- package/dist/ui/data/Filter.js +56 -0
- package/dist/ui/data/List.css +16 -0
- package/dist/ui/data/List.d.ts +15 -0
- package/dist/ui/data/List.js +18 -0
- package/dist/ui/data/Listable.css +245 -0
- package/dist/ui/data/Listable.d.ts +60 -0
- package/dist/ui/data/Listable.js +209 -0
- package/dist/ui/data/Pagination.d.ts +5 -0
- package/dist/ui/data/Pagination.js +43 -0
- package/dist/ui/data/Tree.css +118 -0
- package/dist/ui/data/Tree.d.ts +45 -0
- package/dist/ui/data/Tree.js +170 -0
- package/dist/ui/data/catalog.d.ts +2 -0
- package/dist/ui/data/catalog.js +7 -0
- package/dist/ui/data/index.d.ts +11 -0
- package/dist/ui/data/index.js +7 -0
- package/dist/ui/display/AddPlaceholder.d.ts +6 -0
- package/dist/ui/display/AddPlaceholder.js +24 -0
- package/dist/ui/display/Alert.css +42 -0
- package/dist/ui/display/Alert.d.ts +14 -0
- package/dist/ui/display/Alert.js +13 -0
- package/dist/ui/display/Avatar.css +19 -0
- package/dist/ui/display/Avatar.d.ts +16 -0
- package/dist/ui/display/Avatar.js +52 -0
- package/dist/ui/display/Badge.css +26 -0
- package/dist/ui/display/Badge.d.ts +16 -0
- package/dist/ui/display/Badge.js +9 -0
- package/dist/ui/display/Card.css +15 -0
- package/dist/ui/display/Card.d.ts +11 -0
- package/dist/ui/display/Card.js +8 -0
- package/dist/ui/display/DetailContainer.d.ts +6 -0
- package/dist/ui/display/DetailContainer.js +7 -0
- package/dist/ui/display/DetailHeader.css +53 -0
- package/dist/ui/display/DetailHeader.d.ts +22 -0
- package/dist/ui/display/DetailHeader.js +8 -0
- package/dist/ui/display/DetailList.css +26 -0
- package/dist/ui/display/DetailList.d.ts +13 -0
- package/dist/ui/display/DetailList.js +5 -0
- package/dist/ui/display/DetailNotFound.d.ts +17 -0
- package/dist/ui/display/DetailNotFound.js +39 -0
- package/dist/ui/display/Divider.css +6 -0
- package/dist/ui/display/Divider.d.ts +8 -0
- package/dist/ui/display/Divider.js +16 -0
- package/dist/ui/display/Divisor.css +30 -0
- package/dist/ui/display/Divisor.d.ts +11 -0
- package/dist/ui/display/Divisor.js +43 -0
- package/dist/ui/display/Icon.css +10 -0
- package/dist/ui/display/Icon.d.ts +14 -0
- package/dist/ui/display/Icon.js +144 -0
- package/dist/ui/display/Image.css +1 -0
- package/dist/ui/display/Image.d.ts +16 -0
- package/dist/ui/display/Image.js +19 -0
- package/dist/ui/display/Markdown.css +38 -0
- package/dist/ui/display/Markdown.d.ts +8 -0
- package/dist/ui/display/Markdown.js +140 -0
- package/dist/ui/display/Progress.css +39 -0
- package/dist/ui/display/Progress.d.ts +15 -0
- package/dist/ui/display/Progress.js +17 -0
- package/dist/ui/display/SectionTitle.d.ts +6 -0
- package/dist/ui/display/SectionTitle.js +6 -0
- package/dist/ui/display/Skeleton.css +20 -0
- package/dist/ui/display/Skeleton.d.ts +11 -0
- package/dist/ui/display/Skeleton.js +11 -0
- package/dist/ui/display/Spinner.css +20 -0
- package/dist/ui/display/Spinner.d.ts +12 -0
- package/dist/ui/display/Spinner.js +12 -0
- package/dist/ui/display/Svg.d.ts +13 -0
- package/dist/ui/display/Svg.js +17 -0
- package/dist/ui/display/Text.css +20 -0
- package/dist/ui/display/Text.d.ts +13 -0
- package/dist/ui/display/Text.js +15 -0
- package/dist/ui/display/catalog.d.ts +2 -0
- package/dist/ui/display/catalog.js +12 -0
- package/dist/ui/display/index.d.ts +22 -0
- package/dist/ui/display/index.js +14 -0
- package/dist/ui/embed/HtmlEmbed.d.ts +12 -0
- package/dist/ui/embed/HtmlEmbed.js +16 -0
- package/dist/ui/embed/index.d.ts +2 -0
- package/dist/ui/embed/index.js +1 -0
- package/dist/ui/forms/DangerZone.d.ts +8 -0
- package/dist/ui/forms/DangerZone.js +31 -0
- package/dist/ui/forms/Field.css +15 -0
- package/dist/ui/forms/Field.d.ts +28 -0
- package/dist/ui/forms/Field.js +25 -0
- package/dist/ui/forms/FieldGroup.css +15 -0
- package/dist/ui/forms/FieldGroup.d.ts +7 -0
- package/dist/ui/forms/FieldGroup.js +7 -0
- package/dist/ui/forms/Form.css +14 -0
- package/dist/ui/forms/Form.d.ts +18 -0
- package/dist/ui/forms/Form.js +17 -0
- package/dist/ui/forms/FormActions.css +11 -0
- package/dist/ui/forms/FormActions.d.ts +18 -0
- package/dist/ui/forms/FormActions.js +10 -0
- package/dist/ui/forms/FormSection.css +19 -0
- package/dist/ui/forms/FormSection.d.ts +7 -0
- package/dist/ui/forms/FormSection.js +7 -0
- package/dist/ui/forms/LoginForm.css +105 -0
- package/dist/ui/forms/LoginForm.d.ts +12 -0
- package/dist/ui/forms/LoginForm.js +137 -0
- package/dist/ui/forms/SchemaForm.css +81 -0
- package/dist/ui/forms/SchemaForm.d.ts +26 -0
- package/dist/ui/forms/SchemaForm.js +186 -0
- package/dist/ui/forms/SettingsContainer.d.ts +8 -0
- package/dist/ui/forms/SettingsContainer.js +8 -0
- package/dist/ui/forms/catalog.d.ts +2 -0
- package/dist/ui/forms/catalog.js +11 -0
- package/dist/ui/forms/index.d.ts +19 -0
- package/dist/ui/forms/index.js +12 -0
- package/dist/ui/forms/schema-walk.d.ts +50 -0
- package/dist/ui/forms/schema-walk.js +116 -0
- package/dist/ui/index.d.ts +58 -0
- package/dist/ui/index.js +63 -0
- package/dist/ui/layout/BarLayout.d.ts +2 -0
- package/dist/ui/layout/BarLayout.js +18 -0
- package/dist/ui/layout/Column.css +6 -0
- package/dist/ui/layout/Column.d.ts +12 -0
- package/dist/ui/layout/Column.js +13 -0
- package/dist/ui/layout/FloatingLayout.d.ts +2 -0
- package/dist/ui/layout/FloatingLayout.js +12 -0
- package/dist/ui/layout/Grid.css +5 -0
- package/dist/ui/layout/Grid.d.ts +13 -0
- package/dist/ui/layout/Grid.js +19 -0
- package/dist/ui/layout/InlineLayout.d.ts +2 -0
- package/dist/ui/layout/InlineLayout.js +13 -0
- package/dist/ui/layout/MountTitle.d.ts +12 -0
- package/dist/ui/layout/MountTitle.js +42 -0
- package/dist/ui/layout/RailLayout.d.ts +2 -0
- package/dist/ui/layout/RailLayout.js +38 -0
- package/dist/ui/layout/Row.css +16 -0
- package/dist/ui/layout/Row.d.ts +13 -0
- package/dist/ui/layout/Row.js +21 -0
- package/dist/ui/layout/SectionHeader.d.ts +10 -0
- package/dist/ui/layout/SectionHeader.js +65 -0
- package/dist/ui/layout/SingleLayout.d.ts +2 -0
- package/dist/ui/layout/SingleLayout.js +25 -0
- package/dist/ui/layout/SpatialCard.d.ts +9 -0
- package/dist/ui/layout/SpatialCard.js +30 -0
- package/dist/ui/layout/SpatialLayout.d.ts +2 -0
- package/dist/ui/layout/SpatialLayout.js +446 -0
- package/dist/ui/layout/SplitLayout.d.ts +2 -0
- package/dist/ui/layout/SplitLayout.js +159 -0
- package/dist/ui/layout/Splitter.d.ts +12 -0
- package/dist/ui/layout/Splitter.js +64 -0
- package/dist/ui/layout/StackHeader.d.ts +9 -0
- package/dist/ui/layout/StackHeader.js +87 -0
- package/dist/ui/layout/StackLayout.d.ts +2 -0
- package/dist/ui/layout/StackLayout.js +137 -0
- package/dist/ui/layout/TabLayout.d.ts +2 -0
- package/dist/ui/layout/TabLayout.js +145 -0
- package/dist/ui/layout/TabPanelLayout.d.ts +20 -0
- package/dist/ui/layout/TabPanelLayout.js +615 -0
- package/dist/ui/layout/catalog.d.ts +2 -0
- package/dist/ui/layout/catalog.js +10 -0
- package/dist/ui/layout/index.d.ts +17 -0
- package/dist/ui/layout/index.js +17 -0
- package/dist/ui/layout/register.d.ts +3 -0
- package/dist/ui/layout/register.js +25 -0
- package/dist/ui/media/Audio.css +1 -0
- package/dist/ui/media/Audio.d.ts +12 -0
- package/dist/ui/media/Audio.js +8 -0
- package/dist/ui/media/Video.css +1 -0
- package/dist/ui/media/Video.d.ts +16 -0
- package/dist/ui/media/Video.js +9 -0
- package/dist/ui/media/catalog.d.ts +2 -0
- package/dist/ui/media/catalog.js +5 -0
- package/dist/ui/media/index.d.ts +2 -0
- package/dist/ui/media/index.js +3 -0
- package/dist/ui/menu/ActionList.css +262 -0
- package/dist/ui/menu/ActionList.d.ts +19 -0
- package/dist/ui/menu/ActionList.js +369 -0
- package/dist/ui/menu/ContainerMenu.d.ts +13 -0
- package/dist/ui/menu/ContainerMenu.js +57 -0
- package/dist/ui/menu/ContextMenu.d.ts +27 -0
- package/dist/ui/menu/ContextMenu.js +93 -0
- package/dist/ui/menu/index.d.ts +10 -0
- package/dist/ui/menu/index.js +7 -0
- package/dist/ui/menu/token.d.ts +22 -0
- package/dist/ui/menu/token.js +145 -0
- package/dist/ui/menu/useChatPicker.d.ts +24 -0
- package/dist/ui/menu/useChatPicker.js +210 -0
- package/dist/ui/navigation/Breadcrumb.css +30 -0
- package/dist/ui/navigation/Breadcrumb.d.ts +14 -0
- package/dist/ui/navigation/Breadcrumb.js +12 -0
- package/dist/ui/navigation/Link.css +13 -0
- package/dist/ui/navigation/Link.d.ts +18 -0
- package/dist/ui/navigation/Link.js +43 -0
- package/dist/ui/navigation/Tabs.css +39 -0
- package/dist/ui/navigation/Tabs.d.ts +18 -0
- package/dist/ui/navigation/Tabs.js +55 -0
- package/dist/ui/navigation/Toolbar.css +25 -0
- package/dist/ui/navigation/Toolbar.d.ts +12 -0
- package/dist/ui/navigation/Toolbar.js +21 -0
- package/dist/ui/navigation/catalog.d.ts +2 -0
- package/dist/ui/navigation/catalog.js +5 -0
- package/dist/ui/navigation/index.d.ts +8 -0
- package/dist/ui/navigation/index.js +5 -0
- package/dist/ui/overlay/Modal.css +35 -0
- package/dist/ui/overlay/Modal.d.ts +12 -0
- package/dist/ui/overlay/Modal.js +57 -0
- package/dist/ui/overlay/Popup.d.ts +12 -0
- package/dist/ui/overlay/Popup.js +74 -0
- package/dist/ui/overlay/catalog.d.ts +2 -0
- package/dist/ui/overlay/catalog.js +4 -0
- package/dist/ui/overlay/index.d.ts +1 -0
- package/dist/ui/overlay/index.js +2 -0
- package/dist/ui/register.d.ts +6 -0
- package/dist/ui/register.js +26 -0
- package/package.json +115 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useScena } from '../../react/ScenaProvider.js';
|
|
4
|
+
import { useStore } from '../../react/hooks/useStore.js';
|
|
5
|
+
import { useI18n } from '../../react/hooks/useI18n.js';
|
|
6
|
+
import { Button } from '../control/Button.js';
|
|
7
|
+
import { usePorta } from '../../porta/PortaContext.js';
|
|
8
|
+
import { SIGILLUM_PATHS } from '../../porta/sigillum.js';
|
|
9
|
+
// import { Field } from '../ui/forms/Field.js';
|
|
10
|
+
import '../control/_field-parts.css';
|
|
11
|
+
import './LoginForm.css';
|
|
12
|
+
function bindingPath(value) {
|
|
13
|
+
return value && typeof value === 'object' && 'path' in value ? value.path : undefined;
|
|
14
|
+
}
|
|
15
|
+
// Resolve a string|DataBinding label to text, reactively. `useStore(undefined)`
|
|
16
|
+
// is a no-op, so this stays a single unconditional hook. A binding (e.g.
|
|
17
|
+
// `{ path: '$/t/auth/usernameLabel' }`) follows locale switches via the $/t
|
|
18
|
+
// backend.
|
|
19
|
+
function useLabel(value, fallback = '') {
|
|
20
|
+
const bound = useStore(bindingPath(value));
|
|
21
|
+
if (bindingPath(value))
|
|
22
|
+
return bound ?? fallback;
|
|
23
|
+
return (typeof value === 'string' ? value : undefined) ?? fallback;
|
|
24
|
+
}
|
|
25
|
+
// One OAuth button — resolves its (possibly bound) provider label reactively.
|
|
26
|
+
function OAuthButton({ provider, pending, fire, t, }) {
|
|
27
|
+
const label = useLabel(provider.label, provider.id);
|
|
28
|
+
return (_jsx(Button, { label: `${provider.icon ? `${provider.icon} ` : ''}${t('auth/providerLabel', {
|
|
29
|
+
provider: label,
|
|
30
|
+
fallback: 'Continue with {provider}',
|
|
31
|
+
})}`, disabled: pending, onClick: () => fire('sigillum.signin', { providerId: provider.id, credentials: {} }) }));
|
|
32
|
+
}
|
|
33
|
+
// A field label: resolves a `{ path }` binding via the store (reactive), else
|
|
34
|
+
// renders the literal string.
|
|
35
|
+
function ProviderFieldLabel({ field }) {
|
|
36
|
+
const path = field.label && typeof field.label === 'object' && 'path' in field.label ? bindingPath(field.label) : undefined;
|
|
37
|
+
const bound = useStore(path);
|
|
38
|
+
if (path)
|
|
39
|
+
return _jsx(_Fragment, { children: bound ?? '' });
|
|
40
|
+
return _jsx(_Fragment, { children: typeof field.label === 'string' ? field.label : '' });
|
|
41
|
+
}
|
|
42
|
+
export function LoginForm({ providers: filter, allowSignup = false, allowForgotPassword = false, onSignup, onForgotPassword, }) {
|
|
43
|
+
const scena = useScena();
|
|
44
|
+
const porta = usePorta();
|
|
45
|
+
const { t } = useI18n();
|
|
46
|
+
const pending = Boolean(useStore(SIGILLUM_PATHS.pending));
|
|
47
|
+
const error = useStore(SIGILLUM_PATHS.error) ?? null;
|
|
48
|
+
const challenge = useStore(SIGILLUM_PATHS.challenge) ?? null;
|
|
49
|
+
const visible = useMemo(() => (filter ? porta.providers.filter((p) => filter.includes(p.id)) : porta.providers), [porta.providers, filter]);
|
|
50
|
+
const oauthButtons = visible.filter((p) => p.kind === 'oauth-button');
|
|
51
|
+
const formFieldsProviders = visible.filter((p) => p.kind === 'form-fields');
|
|
52
|
+
const otpProviders = visible.filter((p) => p.kind === 'otp');
|
|
53
|
+
const magicProviders = visible.filter((p) => p.kind === 'magic-link');
|
|
54
|
+
// Merge fields across all form-fields providers by name (first wins).
|
|
55
|
+
const mergedFields = useMemo(() => {
|
|
56
|
+
const out = [];
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
for (const p of formFieldsProviders) {
|
|
59
|
+
for (const f of p.fields ?? []) {
|
|
60
|
+
if (seen.has(f.name))
|
|
61
|
+
continue;
|
|
62
|
+
seen.add(f.name);
|
|
63
|
+
out.push({ field: f, ownerId: p.id });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}, [formFieldsProviders]);
|
|
68
|
+
const [fieldValues, setFieldValues] = useState({});
|
|
69
|
+
function setField(name, value) {
|
|
70
|
+
setFieldValues((prev) => ({ ...prev, [name]: value }));
|
|
71
|
+
}
|
|
72
|
+
function fire(commandId, args) {
|
|
73
|
+
void scena.commands.execute(commandId, args);
|
|
74
|
+
}
|
|
75
|
+
function onSubmit(e) {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
if (formFieldsProviders.length === 0)
|
|
78
|
+
return;
|
|
79
|
+
// When multiple form-fields providers exist, prefer 'password' if present;
|
|
80
|
+
// otherwise the first one. Apps with stricter needs can use the
|
|
81
|
+
// `providers` prop to disambiguate.
|
|
82
|
+
const target = formFieldsProviders.find((p) => p.id === 'password') ?? formFieldsProviders[0];
|
|
83
|
+
if (!target)
|
|
84
|
+
return;
|
|
85
|
+
fire('sigillum.signin', { providerId: target.id, credentials: fieldValues });
|
|
86
|
+
}
|
|
87
|
+
const hasSecondaryUi = mergedFields.length > 0 || otpProviders.length > 0 || magicProviders.length > 0;
|
|
88
|
+
// Submit button label — prefer the 'password' provider's label, else the
|
|
89
|
+
// first form provider's. Resolved reactively (string or $/t binding).
|
|
90
|
+
const submitProvider = formFieldsProviders.find((p) => p.id === 'password') ?? formFieldsProviders[0];
|
|
91
|
+
const submitLabel = useLabel(submitProvider?.label, t('auth/signIn', 'Sign in'));
|
|
92
|
+
return (_jsxs("form", { className: "porta-form", onSubmit: onSubmit, children: [oauthButtons.length > 0 ? (_jsx("div", { className: "porta-form__oauth", children: oauthButtons.map((p) => (_jsx(OAuthButton, { provider: p, pending: pending, fire: fire, t: t }, p.id))) })) : null, oauthButtons.length > 0 && hasSecondaryUi ? (_jsx("div", { className: "porta-form__divider", children: "or" })) : null, mergedFields.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "porta-form__fields", children: mergedFields.map(({ field }) => (_jsxs("label", { className: "oo-field", children: [_jsx("span", { className: "oo-field__label", children: field.label && typeof field.label === 'object' && 't' in field.label ? t(field.label.t, {
|
|
93
|
+
...field.label.args,
|
|
94
|
+
fallback: '???',
|
|
95
|
+
}) : _jsx(ProviderFieldLabel, { field: field }) }), _jsx("input", { className: "oo-input", type: field.type, name: field.name, autoComplete: field.autoComplete, required: field.required, placeholder: field.placeholder, value: fieldValues[field.name] ?? '', onChange: (e) => setField(field.name, e.currentTarget.value) })] }, field.name))) }), _jsx(Button, { type: "submit", variant: "primary", label: submitLabel, disabled: pending })] })) : null, otpProviders.map((p) => (_jsx(OtpRow, { provider: p, challenge: challenge, pending: pending, fire: fire }, p.id))), magicProviders.map((p) => (_jsx(MagicLinkRow, { provider: p, challenge: challenge, pending: pending, fire: fire }, p.id))), _jsxs("div", { className: "porta-form__footer", children: [_jsxs("span", { children: [pending ? _jsx("span", { className: "porta-form__pending", children: "Working\u2026" }) : null, error ? _jsx("span", { className: "porta-form__error", children: error }) : null] }), _jsxs("div", { className: "porta-form__aux", children: [allowForgotPassword ? (_jsx("button", { type: "button", onClick: onForgotPassword, children: t('auth/forgotPassword', 'Forgot password?') })) : null, allowSignup ? (_jsx("button", { type: "button", onClick: onSignup, children: t('auth/createAccount', 'Create account') })) : null] })] })] }));
|
|
96
|
+
}
|
|
97
|
+
function OtpRow({ provider, challenge, pending, fire, }) {
|
|
98
|
+
const { t } = useI18n('auth');
|
|
99
|
+
const label = useLabel(provider.label, provider.id);
|
|
100
|
+
const stage2 = challenge?.providerId === provider.id;
|
|
101
|
+
const digits = provider.otp?.digits ?? 6;
|
|
102
|
+
const [address, setAddress] = useState('');
|
|
103
|
+
const [code, setCode] = useState('');
|
|
104
|
+
return (_jsxs("div", { className: "porta-form__two-step", children: [_jsxs("span", { className: "porta-form__two-step-label", children: [provider.icon ? `${provider.icon} ` : '', label] }), stage2 ? (_jsxs("div", { className: "porta-form__two-step-row", children: [_jsx(DigitsInput, { digits: digits, value: code, onChange: setCode }), _jsx(Button, { label: "Verify", variant: "primary", disabled: pending || code.length !== digits, onClick: () => fire('sigillum.verifyOtp', { providerId: provider.id, code }) })] })) : (_jsxs("div", { className: "porta-form__two-step-row", children: [_jsxs("label", { className: "oo-field", children: [_jsx("span", { className: "oo-field__label", children: provider.otp?.channel === 'sms'
|
|
105
|
+
? t('phoneLabel', 'Phone number')
|
|
106
|
+
: t('emailLabel', 'Email address') }), _jsx("input", { className: "oo-input", type: provider.otp?.channel === 'sms' ? 'tel' : 'email', value: address, placeholder: provider.otp?.channel === 'sms' ? '+1 555 …' : 'you@example.com', onChange: (e) => setAddress(e.currentTarget.value) })] }), _jsx(Button, { label: provider.otp?.sendLabel ?? t('sendCode', 'Send code'), disabled: pending || !address, onClick: () => fire('sigillum.requestOtp', { providerId: provider.id, value: address }) })] }))] }));
|
|
107
|
+
}
|
|
108
|
+
function MagicLinkRow({ provider, challenge, pending, fire, }) {
|
|
109
|
+
const { t } = useI18n('auth');
|
|
110
|
+
const label = useLabel(provider.label, provider.id);
|
|
111
|
+
const stage2 = challenge?.providerId === provider.id;
|
|
112
|
+
const [address, setAddress] = useState('');
|
|
113
|
+
return (_jsxs("div", { className: "porta-form__two-step", children: [_jsxs("span", { className: "porta-form__two-step-label", children: [provider.icon ? `${provider.icon} ` : '', label] }), stage2 ? (_jsxs("span", { className: "porta-form__pending", children: ["Check your ", provider.magicLink?.channel ?? 'inbox', " for the link."] })) : (_jsxs("div", { className: "porta-form__two-step-row", children: [_jsxs("label", { className: "oo-field", children: [_jsx("span", { className: "oo-field__label", children: provider.magicLink?.channel === 'sms'
|
|
114
|
+
? t('phoneLabel', 'Phone number')
|
|
115
|
+
: t('emailLabel', 'Email address') }), _jsx("input", { className: "oo-input", type: provider.magicLink?.channel === 'sms' ? 'tel' : 'email', value: address, placeholder: provider.magicLink?.channel === 'sms' ? '+1 555 …' : 'you@example.com', onChange: (e) => setAddress(e.currentTarget.value) })] }), _jsx(Button, { label: provider.magicLink?.sendLabel ?? t('sendLink', 'Send link'), disabled: pending || !address, onClick: () => fire('sigillum.requestOtp', { providerId: provider.id, value: address }) })] }))] }));
|
|
116
|
+
}
|
|
117
|
+
function DigitsInput({ digits, value, onChange, }) {
|
|
118
|
+
const refs = useRef([]);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
refs.current = refs.current.slice(0, digits);
|
|
121
|
+
}, [digits]);
|
|
122
|
+
const chars = value.padEnd(digits, ' ').slice(0, digits).split('');
|
|
123
|
+
function setChar(idx, ch) {
|
|
124
|
+
const next = chars.slice();
|
|
125
|
+
next[idx] = ch.slice(-1) || ' ';
|
|
126
|
+
onChange(next.join('').trimEnd());
|
|
127
|
+
if (ch && idx + 1 < digits)
|
|
128
|
+
refs.current[idx + 1]?.focus();
|
|
129
|
+
}
|
|
130
|
+
return (_jsx("div", { className: "porta-form__otp-digits", children: Array.from({ length: digits }).map((_, i) => (_jsx("input", { ref: (el) => {
|
|
131
|
+
refs.current[i] = el;
|
|
132
|
+
}, className: "porta-form__otp-digit", inputMode: "numeric", maxLength: 1, value: chars[i]?.trim() ?? '', onChange: (e) => setChar(i, e.currentTarget.value), onKeyDown: (e) => {
|
|
133
|
+
if (e.key === 'Backspace' && !chars[i]?.trim() && i > 0) {
|
|
134
|
+
refs.current[i - 1]?.focus();
|
|
135
|
+
}
|
|
136
|
+
} }, i))) }));
|
|
137
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
.oo-schema-form {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--oo-spacing-md);
|
|
5
|
+
}
|
|
6
|
+
.oo-schema-form__modebar {
|
|
7
|
+
display: flex;
|
|
8
|
+
gap: var(--oo-spacing-xs);
|
|
9
|
+
align-self: flex-end;
|
|
10
|
+
}
|
|
11
|
+
.oo-schema-form__modebar button {
|
|
12
|
+
font: inherit;
|
|
13
|
+
font-size: var(--oo-font-size-xs);
|
|
14
|
+
padding: 2px var(--oo-spacing-sm);
|
|
15
|
+
border: 1px solid var(--oo-color-border);
|
|
16
|
+
border-radius: var(--oo-radius-sm);
|
|
17
|
+
background: transparent;
|
|
18
|
+
color: inherit;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
}
|
|
21
|
+
.oo-schema-form__modebar button[data-active='true'] {
|
|
22
|
+
background: var(--oo-color-active);
|
|
23
|
+
border-color: var(--oo-color-accent);
|
|
24
|
+
}
|
|
25
|
+
.oo-schema-form__modebar .oo-schema-form__reset {
|
|
26
|
+
color: var(--oo-color-warning);
|
|
27
|
+
border-color: var(--oo-color-warning);
|
|
28
|
+
}
|
|
29
|
+
.oo-schema-form__json {
|
|
30
|
+
width: 100%;
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
font-family: var(--oo-font-mono);
|
|
33
|
+
font-size: var(--oo-font-size-xs);
|
|
34
|
+
resize: vertical;
|
|
35
|
+
padding: var(--oo-spacing-sm);
|
|
36
|
+
border: 1px solid var(--oo-color-border);
|
|
37
|
+
border-radius: var(--oo-radius-sm);
|
|
38
|
+
background: var(--oo-color-canvas);
|
|
39
|
+
color: inherit;
|
|
40
|
+
}
|
|
41
|
+
.oo-schema-form__empty {
|
|
42
|
+
font-size: var(--oo-font-size-xs);
|
|
43
|
+
color: var(--oo-color-muted);
|
|
44
|
+
font-style: italic;
|
|
45
|
+
}
|
|
46
|
+
.oo-schema-form__union {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
gap: var(--oo-spacing-sm);
|
|
50
|
+
}
|
|
51
|
+
.oo-schema-form__array {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: var(--oo-spacing-xs);
|
|
55
|
+
}
|
|
56
|
+
.oo-schema-form__array-item {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: var(--oo-spacing-xs);
|
|
59
|
+
align-items: flex-start;
|
|
60
|
+
}
|
|
61
|
+
.oo-schema-form__array-control {
|
|
62
|
+
flex: 1;
|
|
63
|
+
}
|
|
64
|
+
.oo-schema-form__array-remove {
|
|
65
|
+
border: 1px solid var(--oo-color-border);
|
|
66
|
+
border-radius: var(--oo-radius-sm);
|
|
67
|
+
background: transparent;
|
|
68
|
+
color: var(--oo-color-danger);
|
|
69
|
+
font-weight: 700;
|
|
70
|
+
padding: var(--oo-spacing-xs) var(--oo-spacing-sm);
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
}
|
|
73
|
+
.oo-schema-form__array-add {
|
|
74
|
+
align-self: flex-start;
|
|
75
|
+
border: 1px solid var(--oo-color-border);
|
|
76
|
+
border-radius: var(--oo-radius-sm);
|
|
77
|
+
background: transparent;
|
|
78
|
+
color: inherit;
|
|
79
|
+
padding: var(--oo-spacing-xs) var(--oo-spacing-sm);
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ComponentType, type CSSProperties } from 'react';
|
|
2
|
+
import { type JsonSchemaField, type JsonSchemaObject } from './schema-walk.js';
|
|
3
|
+
import './SchemaForm.css';
|
|
4
|
+
export interface FormatRendererProps {
|
|
5
|
+
value: unknown;
|
|
6
|
+
onChange: (next: unknown) => void;
|
|
7
|
+
field: JsonSchemaField;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export type FormatRenderer = ComponentType<FormatRendererProps>;
|
|
11
|
+
export interface SchemaFormProps {
|
|
12
|
+
schema: JsonSchemaObject;
|
|
13
|
+
value?: Record<string, unknown>;
|
|
14
|
+
defaultValue?: Record<string, unknown>;
|
|
15
|
+
onChange?: (next: Record<string, unknown>) => void;
|
|
16
|
+
errors?: Record<string, string>;
|
|
17
|
+
namespace?: string;
|
|
18
|
+
header?: string;
|
|
19
|
+
formatRenderers?: Record<string, FormatRenderer>;
|
|
20
|
+
jsonTextRoot?: boolean;
|
|
21
|
+
jsonTextFields?: boolean;
|
|
22
|
+
baseline?: Record<string, unknown>;
|
|
23
|
+
onDirtyChange?: (dirty: boolean) => void;
|
|
24
|
+
style?: CSSProperties;
|
|
25
|
+
}
|
|
26
|
+
export declare function SchemaForm({ schema, value, defaultValue, onChange, errors, namespace, header, formatRenderers, jsonTextRoot, jsonTextFields, baseline, onDirtyChange, style, }: SchemaFormProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useWriteBack, WriteContext } from '../../react/mount-context.js';
|
|
4
|
+
import { TextField } from '../control/TextField.js';
|
|
5
|
+
import { CheckBox } from '../control/CheckBox.js';
|
|
6
|
+
import { ChoicePicker } from '../control/ChoicePicker.js';
|
|
7
|
+
import { FormContext } from './Form.js';
|
|
8
|
+
import { Field } from './Field.js';
|
|
9
|
+
import { FormSection } from './FormSection.js';
|
|
10
|
+
import { cleanDescription, deepEqual, defaultValueForSchema, effectiveType, isObjectRecord, normalizeUnionOptions, resolveArrayItemSchema, textareaRows, } from './schema-walk.js';
|
|
11
|
+
import './SchemaForm.css';
|
|
12
|
+
import { NAMED_GLYPH } from '../display/Icon.js';
|
|
13
|
+
import { Markdown } from '../display/Markdown.js';
|
|
14
|
+
function hasFormatRenderer(field, renderers) {
|
|
15
|
+
return Boolean(field.format && renderers?.[field.format]);
|
|
16
|
+
}
|
|
17
|
+
export function SchemaForm({ schema, value, defaultValue, onChange, errors, namespace, header, formatRenderers, jsonTextRoot, jsonTextFields, baseline, onDirtyChange, style, }) {
|
|
18
|
+
const writeValue = useWriteBack('value');
|
|
19
|
+
const [internal, setInternal] = useState(value ?? defaultValue ?? {});
|
|
20
|
+
const [mode, setMode] = useState('fields');
|
|
21
|
+
const [jsonText, setJsonText] = useState(() => JSON.stringify(value ?? defaultValue ?? {}, null, 2));
|
|
22
|
+
// Root shows its switch unless explicitly disabled. Nested object sections
|
|
23
|
+
// follow `jsonTextFields`, which defaults to the effective root value.
|
|
24
|
+
const showRootToggle = jsonTextRoot ?? true;
|
|
25
|
+
const childToggle = jsonTextFields ?? showRootToggle;
|
|
26
|
+
// Pristine baseline: explicit prop, else the first value this form saw.
|
|
27
|
+
const initialRef = useRef(value ?? defaultValue ?? {});
|
|
28
|
+
const baselineValue = baseline ?? initialRef.current;
|
|
29
|
+
const dirty = !deepEqual(internal, baselineValue);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
return;
|
|
33
|
+
setInternal((current) => (current === value ? current : value));
|
|
34
|
+
}, [value]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
onDirtyChange?.(dirty);
|
|
37
|
+
// Fires on dirty transitions only. `onDirtyChange` is excluded on purpose:
|
|
38
|
+
// callers routinely pass an inline arrow, and including it would re-fire
|
|
39
|
+
// the callback on every parent render.
|
|
40
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
|
+
}, [dirty]);
|
|
42
|
+
const properties = schema.properties;
|
|
43
|
+
if (!properties || Object.keys(properties).length === 0) {
|
|
44
|
+
return _jsx("div", { className: "oo-schema-form__empty", children: "No parameters defined." });
|
|
45
|
+
}
|
|
46
|
+
const requiredSet = new Set(schema.required ?? []);
|
|
47
|
+
function commit(next) {
|
|
48
|
+
setInternal(next);
|
|
49
|
+
writeValue(next); // bound/graph: writes the whole object back (no-op in plain React)
|
|
50
|
+
onChange?.(next); // controlled: direct-React parent
|
|
51
|
+
}
|
|
52
|
+
function setField(key, val) {
|
|
53
|
+
commit({ ...internal, [key]: val });
|
|
54
|
+
}
|
|
55
|
+
function enterJson() {
|
|
56
|
+
setJsonText(JSON.stringify(internal, null, 2));
|
|
57
|
+
setMode('json');
|
|
58
|
+
}
|
|
59
|
+
function reset() {
|
|
60
|
+
commit(baselineValue);
|
|
61
|
+
setJsonText(JSON.stringify(baselineValue, null, 2));
|
|
62
|
+
}
|
|
63
|
+
function renderField(key, field) {
|
|
64
|
+
// x-show-if — hide unless a sibling field matches.
|
|
65
|
+
const showIf = field['x-show-if'];
|
|
66
|
+
if (showIf && internal[showIf.field] !== showIf.value)
|
|
67
|
+
return null;
|
|
68
|
+
const type = effectiveType(field);
|
|
69
|
+
const isEnum = (field.enum?.length ?? 0) > 0 || (field['x-enumOptions']?.length ?? 0) > 0;
|
|
70
|
+
const set = (next) => setField(key, next);
|
|
71
|
+
const meta = {
|
|
72
|
+
label: field.title ?? key,
|
|
73
|
+
hint: cleanDescription(field.description),
|
|
74
|
+
required: requiredSet.has(key),
|
|
75
|
+
error: errors?.[key],
|
|
76
|
+
typeTag: isEnum ? 'enum' : type,
|
|
77
|
+
};
|
|
78
|
+
const childNamespace = namespace ? `${namespace}_${key}` : undefined;
|
|
79
|
+
// Object → a titled section with a nested form (its fields carry labels).
|
|
80
|
+
if (type === 'object' && !isEnum && !hasFormatRenderer(field, formatRenderers)) {
|
|
81
|
+
return (_jsx(FormSection, { title: meta.label ?? key, children: _jsx(SchemaForm, { schema: { type: 'object', properties: field.properties ?? {}, required: field.required ?? [] }, value: isObjectRecord(internal[key]) ? internal[key] : {}, baseline: isObjectRecord(baselineValue[key]) ? baselineValue[key] : {}, onChange: set, namespace: childNamespace, formatRenderers: formatRenderers, jsonTextRoot: childToggle }) }, key));
|
|
82
|
+
}
|
|
83
|
+
// Composite — owns no single focusable input, so Field provides the label.
|
|
84
|
+
const isComposite = normalizeUnionOptions(field).length > 0 ||
|
|
85
|
+
type === 'array' ||
|
|
86
|
+
hasFormatRenderer(field, formatRenderers);
|
|
87
|
+
if (isComposite) {
|
|
88
|
+
return (_jsx(Field, { name: key, label: meta.label, hint: meta.hint, required: meta.required, typeTag: meta.typeTag, error: meta.error, children: _jsx(SchemaControl, { field: field, value: internal[key], onChange: set, required: meta.required, error: meta.error, namespace: childNamespace, formatRenderers: formatRenderers, jsonTextFields: childToggle }) }, key));
|
|
89
|
+
}
|
|
90
|
+
// Scalar — the control owns its own <label>; pass the chrome straight in.
|
|
91
|
+
return (_jsx(SchemaControl, { field: field, value: internal[key], onChange: set, meta: meta, required: meta.required, error: meta.error, namespace: childNamespace, formatRenderers: formatRenderers }, key));
|
|
92
|
+
}
|
|
93
|
+
const rows = mode === 'fields' ? Object.entries(properties).map(([key, field]) => renderField(key, field)) : null;
|
|
94
|
+
const modebar = showRootToggle || dirty ? (_jsxs("div", { className: "oo-schema-form__modebar", children: [showRootToggle ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "data-active": mode === 'fields', onClick: () => setMode('fields'), children: "fields" }), _jsx("button", { type: "button", "data-active": mode === 'json', onClick: enterJson, children: "json" })] })) : null, dirty ? (_jsx("button", { type: "button", className: "oo-schema-form__reset", onClick: reset, children: "reset" })) : null] })) : null;
|
|
95
|
+
const body = mode === 'json' ? (_jsx("textarea", { className: "oo-schema-form__json", value: jsonText, spellCheck: false, rows: 16, onChange: (e) => {
|
|
96
|
+
setJsonText(e.target.value);
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(e.target.value);
|
|
99
|
+
if (isObjectRecord(parsed))
|
|
100
|
+
commit(parsed);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* keep editing until the JSON parses */
|
|
104
|
+
}
|
|
105
|
+
} })) : header ? (_jsx(FormSection, { title: header, children: rows })) : (rows);
|
|
106
|
+
// Suppress ambient write-back for the field controls: each control reports
|
|
107
|
+
// through its explicit onChange, and SchemaForm aggregates into one object.
|
|
108
|
+
return (_jsx(FormContext.Provider, { value: { errors, namespace }, children: _jsx(WriteContext.Provider, { value: null, children: _jsxs("div", { className: "oo-schema-form", style: style, children: [schema.description ? _jsx(Markdown, { text: schema.description }) : null, modebar, body] }) }) }));
|
|
109
|
+
}
|
|
110
|
+
function SchemaControl({ field, value, onChange, meta, required, error, namespace, formatRenderers, jsonTextFields }) {
|
|
111
|
+
const unionOptions = normalizeUnionOptions(field);
|
|
112
|
+
const [unionIndex, setUnionIndex] = useState(0);
|
|
113
|
+
// Union (oneOf / anyOf): pick a variant, then render its sub-schema.
|
|
114
|
+
if (unionOptions.length > 0) {
|
|
115
|
+
const idx = unionOptions[unionIndex] ? unionIndex : 0;
|
|
116
|
+
const current = unionOptions[idx] ?? unionOptions[0];
|
|
117
|
+
const options = unionOptions.map((opt, i) => {
|
|
118
|
+
const t = effectiveType(opt);
|
|
119
|
+
const enumLabel = opt.const !== undefined
|
|
120
|
+
? String(opt.const)
|
|
121
|
+
: opt.enum && opt.enum.length === 1
|
|
122
|
+
? opt.enum[0]
|
|
123
|
+
: opt.title ?? t;
|
|
124
|
+
return { value: String(i), label: opt.title ? `${opt.title} (${enumLabel})` : enumLabel };
|
|
125
|
+
});
|
|
126
|
+
return (_jsxs("div", { className: "oo-schema-form__union", children: [_jsx(ChoicePicker, { options: options, value: String(idx), onChange: (v) => {
|
|
127
|
+
const next = Number(v);
|
|
128
|
+
const safe = Number.isFinite(next) ? Math.max(0, Math.min(next, unionOptions.length - 1)) : 0;
|
|
129
|
+
setUnionIndex(safe);
|
|
130
|
+
onChange(defaultValueForSchema(unionOptions[safe]));
|
|
131
|
+
} }), _jsx(SchemaControl, { field: current, value: value, onChange: onChange, namespace: namespace, formatRenderers: formatRenderers, jsonTextFields: jsonTextFields })] }));
|
|
132
|
+
}
|
|
133
|
+
// Custom format renderer — checked before the built-in dispatch.
|
|
134
|
+
if (field.format && formatRenderers?.[field.format]) {
|
|
135
|
+
const Renderer = formatRenderers[field.format];
|
|
136
|
+
return _jsx(Renderer, { value: value, onChange: onChange, field: field, error: error });
|
|
137
|
+
}
|
|
138
|
+
const type = effectiveType(field);
|
|
139
|
+
// Enum / rich enum → ChoicePicker (owns its label via meta).
|
|
140
|
+
if ((field.enum && field.enum.length > 0) || (field['x-enumOptions'] && field['x-enumOptions'].length > 0)) {
|
|
141
|
+
const rich = field['x-enumOptions'];
|
|
142
|
+
const options = rich
|
|
143
|
+
? rich.map((o) => ({ value: o.value, label: o.description ? `${o.label} — ${o.description}` : o.label }))
|
|
144
|
+
: (field.enum ?? []);
|
|
145
|
+
const withPlaceholder = required
|
|
146
|
+
? options
|
|
147
|
+
: [{ value: '', label: '— select —' }, ...options];
|
|
148
|
+
return (_jsx(ChoicePicker, { options: withPlaceholder, value: String(value ?? field.default ?? ''), onChange: (v) => onChange(v), label: meta?.label, hint: meta?.hint, error: meta?.error, required: meta?.required, typeTag: meta?.typeTag }));
|
|
149
|
+
}
|
|
150
|
+
if (type === 'boolean') {
|
|
151
|
+
return (_jsx(CheckBox, { value: Boolean(value ?? field.default ?? false), onChange: (v) => onChange(v), label: meta?.label, hint: meta?.hint, error: meta?.error, required: meta?.required }));
|
|
152
|
+
}
|
|
153
|
+
if (type === 'number' || type === 'integer') {
|
|
154
|
+
return (_jsx(TextField, { type: "number", value: value !== undefined ? String(value) : '', min: field.minimum, max: field.maximum, onChange: (s) => onChange(s === '' ? undefined : Number(s)), label: meta?.label, hint: meta?.hint, error: meta?.error, required: meta?.required, typeTag: meta?.typeTag }));
|
|
155
|
+
}
|
|
156
|
+
if (type === 'color') {
|
|
157
|
+
return (_jsx(TextField, { type: "color", value: value !== undefined ? String(value) : '', min: field.minimum, max: field.maximum, onChange: (s) => onChange(s === '' ? undefined : Number(s)), label: meta?.label, hint: meta?.hint, error: meta?.error, required: meta?.required, typeTag: meta?.typeTag }));
|
|
158
|
+
}
|
|
159
|
+
if (type === 'array') {
|
|
160
|
+
return _jsx(ArrayControl, { field: field, value: value, onChange: onChange, namespace: namespace, formatRenderers: formatRenderers, jsonTextFields: jsonTextFields });
|
|
161
|
+
}
|
|
162
|
+
if (type === 'object') {
|
|
163
|
+
return (_jsx(SchemaForm, { schema: { type: 'object', properties: field.properties ?? {}, required: field.required ?? [] }, value: isObjectRecord(value) ? value : {}, onChange: (next) => onChange(next), namespace: namespace, formatRenderers: formatRenderers, jsonTextRoot: jsonTextFields }));
|
|
164
|
+
}
|
|
165
|
+
// string (default), with password + textarea variants.
|
|
166
|
+
const rows = textareaRows(field);
|
|
167
|
+
return (_jsx(TextField, { type: field.format === 'password' ? 'password' : 'text', multiline: rows !== null, rows: rows ?? undefined, value: String(value ?? field.default ?? ''), onChange: (s) => onChange(s), label: meta?.label, hint: meta?.hint, error: meta?.error, required: meta?.required, typeTag: meta?.typeTag }));
|
|
168
|
+
}
|
|
169
|
+
function ArrayControl({ field, value, onChange, namespace, formatRenderers, jsonTextFields }) {
|
|
170
|
+
const items = Array.isArray(value) ? value : [];
|
|
171
|
+
const itemSchema = resolveArrayItemSchema(field);
|
|
172
|
+
// Tuple arrays — edited as raw JSON.
|
|
173
|
+
if (!itemSchema) {
|
|
174
|
+
return (_jsx(TextField, { multiline: true, rows: 5, value: JSON.stringify(items, null, 2), onChange: (s) => {
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(s);
|
|
177
|
+
if (Array.isArray(parsed))
|
|
178
|
+
onChange(parsed);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
/* keep previous valid array */
|
|
182
|
+
}
|
|
183
|
+
} }));
|
|
184
|
+
}
|
|
185
|
+
return (_jsxs("div", { className: "oo-schema-form__array", children: [items.map((item, index) => (_jsxs("div", { className: "oo-schema-form__array-item", children: [_jsx("div", { className: "oo-schema-form__array-control", children: _jsx(SchemaControl, { field: itemSchema, value: item, onChange: (next) => onChange(items.map((it, i) => (i === index ? next : it))), namespace: namespace, formatRenderers: formatRenderers, jsonTextFields: jsonTextFields }) }), _jsx("button", { type: "button", className: "oo-schema-form__array-remove", onClick: () => onChange(items.filter((_, i) => i !== index)), "aria-label": "Remove item", children: NAMED_GLYPH.closePanel })] }, index))), _jsx("button", { type: "button", className: "oo-btn oo-schema-form__array-add", onClick: () => onChange([...items, defaultValueForSchema(itemSchema)]), children: "+ add item" })] }));
|
|
186
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { type DangerZoneProps } from './DangerZone.js';
|
|
3
|
+
export interface SettingsContainerProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
danger?: DangerZoneProps;
|
|
6
|
+
style?: CSSProperties;
|
|
7
|
+
}
|
|
8
|
+
export declare function SettingsContainer({ children, danger, style }: SettingsContainerProps): ReactNode;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DangerZone } from './DangerZone.js';
|
|
3
|
+
// Standard layout for a resource's Settings tab: the form, then an optional
|
|
4
|
+
// DangerZone. Keeps every resource's Settings tab consistent without repeating
|
|
5
|
+
// the wrapper + delete affordance.
|
|
6
|
+
export function SettingsContainer({ children, danger, style }) {
|
|
7
|
+
return (_jsxs("div", { className: "oo-settings", style: { display: 'flex', flexDirection: 'column', gap: 24, ...style }, children: [children, danger ? _jsx(DangerZone, { ...danger }) : null] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Catalog registration for form components (dynamic import — no eager load).
|
|
2
|
+
// FieldLabel / FieldError / FieldHint are internal parts of Field, not catalog
|
|
3
|
+
// components — see ./index.ts.
|
|
4
|
+
export const CATALOG_FORMS_MAP = [
|
|
5
|
+
{ component: 'Form', category: 'page', renderer: { kind: 'react', load: () => import('./Form.js').then((m) => ({ default: m.Form })) } },
|
|
6
|
+
{ component: 'Field', category: 'inline', renderer: { kind: 'react', load: () => import('./Field.js').then((m) => ({ default: m.Field })) } },
|
|
7
|
+
{ component: 'FieldGroup', category: 'inline', renderer: { kind: 'react', load: () => import('./FieldGroup.js').then((m) => ({ default: m.FieldGroup })) } },
|
|
8
|
+
{ component: 'FormSection', category: 'inline', renderer: { kind: 'react', load: () => import('./FormSection.js').then((m) => ({ default: m.FormSection })) } },
|
|
9
|
+
{ component: 'FormActions', category: 'inline', renderer: { kind: 'react', load: () => import('./FormActions.js').then((m) => ({ default: m.FormActions })) } },
|
|
10
|
+
{ component: 'SchemaForm', category: 'page', renderer: { kind: 'react', load: () => import('./SchemaForm.js').then((m) => ({ default: m.SchemaForm })) } },
|
|
11
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { Form, FormContext, useFormContext } from './Form.js';
|
|
2
|
+
export type { FormProps, FormContextValue } from './Form.js';
|
|
3
|
+
export { Field, FieldLabel, FieldError, FieldHint } from './Field.js';
|
|
4
|
+
export type { FieldProps, FieldLabelProps, FieldErrorProps, FieldHintProps } from './Field.js';
|
|
5
|
+
export { FieldGroup } from './FieldGroup.js';
|
|
6
|
+
export type { FieldGroupProps } from './FieldGroup.js';
|
|
7
|
+
export { FormSection } from './FormSection.js';
|
|
8
|
+
export type { FormSectionProps } from './FormSection.js';
|
|
9
|
+
export { FormActions } from './FormActions.js';
|
|
10
|
+
export type { FormActionsProps, FormActionItem } from './FormActions.js';
|
|
11
|
+
export { SchemaForm } from './SchemaForm.js';
|
|
12
|
+
export type { SchemaFormProps, FormatRenderer, FormatRendererProps } from './SchemaForm.js';
|
|
13
|
+
export { DangerZone } from './DangerZone.js';
|
|
14
|
+
export type { DangerZoneProps } from './DangerZone.js';
|
|
15
|
+
export { SettingsContainer } from './SettingsContainer.js';
|
|
16
|
+
export type { SettingsContainerProps } from './SettingsContainer.js';
|
|
17
|
+
export type { JsonSchemaField, JsonSchemaObject, EnumOption } from './schema-walk.js';
|
|
18
|
+
export { LoginForm } from './LoginForm.js';
|
|
19
|
+
export type { LoginFormProps } from './LoginForm.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Direct-React re-exports. Catalog registration lives in ../catalog.ts.
|
|
2
|
+
// FieldLabel / FieldError / FieldHint are internal parts of Field — exported
|
|
3
|
+
// here for hand-composed forms, but NOT registered as catalog components.
|
|
4
|
+
export { Form, FormContext, useFormContext } from './Form.js';
|
|
5
|
+
export { Field, FieldLabel, FieldError, FieldHint } from './Field.js';
|
|
6
|
+
export { FieldGroup } from './FieldGroup.js';
|
|
7
|
+
export { FormSection } from './FormSection.js';
|
|
8
|
+
export { FormActions } from './FormActions.js';
|
|
9
|
+
export { SchemaForm } from './SchemaForm.js';
|
|
10
|
+
export { DangerZone } from './DangerZone.js';
|
|
11
|
+
export { SettingsContainer } from './SettingsContainer.js';
|
|
12
|
+
export { LoginForm } from './LoginForm.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface EnumOption {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface JsonSchemaField {
|
|
7
|
+
type?: string | string[];
|
|
8
|
+
title?: string;
|
|
9
|
+
anyOf?: JsonSchemaField[];
|
|
10
|
+
oneOf?: JsonSchemaField[];
|
|
11
|
+
allOf?: JsonSchemaField[];
|
|
12
|
+
const?: unknown;
|
|
13
|
+
description?: string;
|
|
14
|
+
enum?: string[];
|
|
15
|
+
/** Rich enum options: label + optional description per option. */
|
|
16
|
+
'x-enumOptions'?: EnumOption[];
|
|
17
|
+
minimum?: number;
|
|
18
|
+
maximum?: number;
|
|
19
|
+
default?: unknown;
|
|
20
|
+
format?: string;
|
|
21
|
+
items?: JsonSchemaField | JsonSchemaField[];
|
|
22
|
+
properties?: Record<string, JsonSchemaField>;
|
|
23
|
+
required?: string[];
|
|
24
|
+
additionalProperties?: boolean;
|
|
25
|
+
/** Show this field only when a sibling field equals a value. */
|
|
26
|
+
'x-show-if'?: {
|
|
27
|
+
field: string;
|
|
28
|
+
value: unknown;
|
|
29
|
+
};
|
|
30
|
+
/** OAuth connect metadata — routed to a format renderer by the consumer. */
|
|
31
|
+
'x-oauth-slug'?: string;
|
|
32
|
+
'x-oauth-flow'?: 'pkce' | 'device';
|
|
33
|
+
/** Render this object field as a section header (FormSection). */
|
|
34
|
+
'x-header'?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface JsonSchemaObject {
|
|
37
|
+
type?: string;
|
|
38
|
+
properties?: Record<string, JsonSchemaField>;
|
|
39
|
+
required?: string[];
|
|
40
|
+
description?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function isObjectRecord(value: unknown): value is Record<string, unknown>;
|
|
43
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
44
|
+
export declare function effectiveType(field: JsonSchemaField): string;
|
|
45
|
+
export declare function resolveUnionOptions(field: JsonSchemaField): JsonSchemaField[];
|
|
46
|
+
export declare function normalizeUnionOptions(field: JsonSchemaField): JsonSchemaField[];
|
|
47
|
+
export declare function resolveArrayItemSchema(field: JsonSchemaField): JsonSchemaField | null;
|
|
48
|
+
export declare function defaultValueForSchema(field: JsonSchemaField): unknown;
|
|
49
|
+
export declare function cleanDescription(description?: string): string | undefined;
|
|
50
|
+
export declare function textareaRows(field: JsonSchemaField): number | null;
|