nebula-cms 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +42 -0
- package/.github/workflows/ci.yml +31 -0
- package/.mcp.json +12 -0
- package/.prettierignore +5 -0
- package/.prettierrc.cjs +22 -0
- package/AGENTS.md +183 -0
- package/LICENSE +201 -0
- package/README.md +128 -0
- package/package.json +74 -0
- package/playground/.claude/settings.local.json +5 -0
- package/playground/astro.config.mjs +7 -0
- package/playground/node_modules/.bin/astro +21 -0
- package/playground/node_modules/.bin/rollup +21 -0
- package/playground/node_modules/.bin/tsc +21 -0
- package/playground/node_modules/.bin/tsserver +21 -0
- package/playground/node_modules/.bin/vite +21 -0
- package/playground/node_modules/.vite/_svelte_metadata.json +1 -0
- package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +80 -0
- package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +7 -0
- package/playground/node_modules/.vite/deps/_metadata.json +184 -0
- package/playground/node_modules/.vite/deps/astro___aria-query.js +6776 -0
- package/playground/node_modules/.vite/deps/astro___aria-query.js.map +7 -0
- package/playground/node_modules/.vite/deps/astro___axobject-query.js +3754 -0
- package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +7 -0
- package/playground/node_modules/.vite/deps/astro___html-escaper.js +34 -0
- package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
- package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js +1005 -0
- package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +8 -0
- package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +21 -0
- package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +223 -0
- package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js +204 -0
- package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js +27 -0
- package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js +4376 -0
- package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js +688 -0
- package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js +5099 -0
- package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +23 -0
- package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +7 -0
- package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +148 -0
- package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +7 -0
- package/playground/node_modules/.vite/deps/package.json +3 -0
- package/playground/node_modules/.vite/deps/smol-toml.js +843 -0
- package/playground/node_modules/.vite/deps/smol-toml.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte.js +55 -0
- package/playground/node_modules/.vite/deps/svelte.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte___clsx.js +9 -0
- package/playground/node_modules/.vite/deps/svelte___clsx.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_animate.js +57 -0
- package/playground/node_modules/.vite/deps/svelte_animate.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_attachments.js +15 -0
- package/playground/node_modules/.vite/deps/svelte_attachments.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_easing.js +67 -0
- package/playground/node_modules/.vite/deps/svelte_easing.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_events.js +11 -0
- package/playground/node_modules/.vite/deps/svelte_events.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_internal.js +5 -0
- package/playground/node_modules/.vite/deps/svelte_internal.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_internal_client.js +402 -0
- package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +10 -0
- package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +8 -0
- package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +8 -0
- package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +8 -0
- package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_legacy.js +35 -0
- package/playground/node_modules/.vite/deps/svelte_legacy.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_motion.js +545 -0
- package/playground/node_modules/.vite/deps/svelte_motion.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_reactivity.js +29 -0
- package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +127 -0
- package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_store.js +103 -0
- package/playground/node_modules/.vite/deps/svelte_store.js.map +7 -0
- package/playground/node_modules/.vite/deps/svelte_transition.js +208 -0
- package/playground/node_modules/.vite/deps/svelte_transition.js.map +7 -0
- package/playground/package.json +16 -0
- package/playground/pnpm-lock.yaml +3167 -0
- package/playground/src/content/authors/jane-doe.json +8 -0
- package/playground/src/content/config/build.toml +2 -0
- package/playground/src/content/courses/web-fundamentals.json +29 -0
- package/playground/src/content/docs/advanced.mdx +6 -0
- package/playground/src/content/docs/intro.md +6 -0
- package/playground/src/content/guides/getting-started.mdx +6 -0
- package/playground/src/content/posts/hello-world.md +7 -0
- package/playground/src/content/products/t-shirt.json +16 -0
- package/playground/src/content/recipes/pancakes.mdoc +8 -0
- package/playground/src/content/settings/site.yml +2 -0
- package/playground/src/content.config.ts +198 -0
- package/playground/src/env.d.ts +1 -0
- package/playground/src/pages/index.astro +11 -0
- package/playground/src/pages/nebula.astro +14 -0
- package/pnpm-workspace.yaml +2 -0
- package/scripts/subset-icons.mjs +178 -0
- package/src/astro/index.ts +295 -0
- package/src/client/Admin.svelte +283 -0
- package/src/client/components/BackendPicker.svelte +291 -0
- package/src/client/components/DraftChip.svelte +46 -0
- package/src/client/components/MetadataForm.svelte +56 -0
- package/src/client/components/ThemeToggle.svelte +18 -0
- package/src/client/components/dialogs/DeleteDraftDialog.svelte +51 -0
- package/src/client/components/dialogs/FilenameDialog.svelte +129 -0
- package/src/client/components/editor/EditorPane.svelte +227 -0
- package/src/client/components/editor/EditorTabs.svelte +81 -0
- package/src/client/components/editor/EditorToolbar.svelte +131 -0
- package/src/client/components/editor/FormatSelector.svelte +66 -0
- package/src/client/components/editor/Toolbar.svelte +17 -0
- package/src/client/components/fields/ArrayField.svelte +339 -0
- package/src/client/components/fields/ArrayItem.svelte +325 -0
- package/src/client/components/fields/BooleanField.svelte +114 -0
- package/src/client/components/fields/DateField.svelte +82 -0
- package/src/client/components/fields/EnumField.svelte +74 -0
- package/src/client/components/fields/FieldWrapper.svelte +96 -0
- package/src/client/components/fields/NumberField.svelte +99 -0
- package/src/client/components/fields/ObjectField.svelte +121 -0
- package/src/client/components/fields/SchemaField.svelte +107 -0
- package/src/client/components/fields/StringField.svelte +104 -0
- package/src/client/components/sidebar/AdminSidebar.svelte +339 -0
- package/src/client/components/sidebar/AdminSidebarSort.svelte +123 -0
- package/src/client/css/a11y.css +14 -0
- package/src/client/css/btn.css +113 -0
- package/src/client/css/dialog.css +29 -0
- package/src/client/css/field-input.css +39 -0
- package/src/client/css/reset.css +59 -0
- package/src/client/css/theme.css +77 -0
- package/src/client/index.ts +1 -0
- package/src/client/js/drafts/merge.svelte.ts +121 -0
- package/src/client/js/drafts/ops.svelte.ts +227 -0
- package/src/client/js/drafts/storage.ts +108 -0
- package/src/client/js/drafts/workers/diff.ts +40 -0
- package/src/client/js/editor/editor.svelte.ts +343 -0
- package/src/client/js/editor/languages.ts +98 -0
- package/src/client/js/editor/link-wrap.ts +45 -0
- package/src/client/js/editor/markdown-shortcuts.ts +261 -0
- package/src/client/js/handlers/admin.ts +246 -0
- package/src/client/js/state/dialogs.svelte.ts +35 -0
- package/src/client/js/state/router.svelte.ts +156 -0
- package/src/client/js/state/schema.svelte.ts +140 -0
- package/src/client/js/state/state.svelte.ts +334 -0
- package/src/client/js/state/theme.svelte.ts +173 -0
- package/src/client/js/storage/adapter.ts +102 -0
- package/src/client/js/storage/client.ts +150 -0
- package/src/client/js/storage/db.ts +36 -0
- package/src/client/js/storage/fsa.ts +110 -0
- package/src/client/js/storage/github.ts +297 -0
- package/src/client/js/storage/storage.ts +83 -0
- package/src/client/js/storage/workers/frontmatter.ts +320 -0
- package/src/client/js/storage/workers/storage.ts +177 -0
- package/src/client/js/storage/workers/toml-parser.ts +106 -0
- package/src/client/js/storage/workers/yaml-parser.ts +132 -0
- package/src/client/js/utils/file-types.ts +192 -0
- package/src/client/js/utils/format.ts +16 -0
- package/src/client/js/utils/frontmatter.ts +38 -0
- package/src/client/js/utils/schema-utils.ts +295 -0
- package/src/client/js/utils/slug.ts +18 -0
- package/src/client/js/utils/sort.ts +84 -0
- package/src/client/js/utils/stable-stringify.ts +27 -0
- package/src/client/js/utils/url-utils.ts +38 -0
- package/src/types.ts +25 -0
- package/src/virtual.d.ts +22 -0
- package/svelte.config.js +4 -0
- package/tests/astro/build.test.ts +63 -0
- package/tests/astro/index.test.ts +689 -0
- package/tests/client/components/Admin.test.ts +446 -0
- package/tests/client/components/BackendPicker.test.ts +239 -0
- package/tests/client/components/DraftChip.test.ts +53 -0
- package/tests/client/components/MetadataForm.test.ts +164 -0
- package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +91 -0
- package/tests/client/components/dialogs/FilenameDialog.test.ts +209 -0
- package/tests/client/components/dialogs/dialog-stubs.ts +19 -0
- package/tests/client/components/editor/EditorPane.test.ts +100 -0
- package/tests/client/components/editor/EditorTabs.test.ts +253 -0
- package/tests/client/components/editor/EditorToolbar.test.ts +252 -0
- package/tests/client/components/editor/fixtures.ts +31 -0
- package/tests/client/components/fields/ArrayField.test.ts +197 -0
- package/tests/client/components/fields/BooleanField.test.ts +206 -0
- package/tests/client/components/fields/DateField.test.ts +210 -0
- package/tests/client/components/fields/EnumField.test.ts +246 -0
- package/tests/client/components/fields/NumberField.test.ts +240 -0
- package/tests/client/components/fields/ObjectField.test.ts +157 -0
- package/tests/client/components/fields/SchemaField.test.ts +190 -0
- package/tests/client/components/fields/StringField.test.ts +223 -0
- package/tests/client/components/sidebar/AdminSidebar.test.ts +285 -0
- package/tests/client/components/sidebar/AdminSidebarSort.test.ts +135 -0
- package/tests/client/components/sidebar/sort-mock.ts +23 -0
- package/tests/client/js/drafts/fixtures.ts +22 -0
- package/tests/client/js/drafts/merge.test.ts +282 -0
- package/tests/client/js/drafts/ops.test.ts +658 -0
- package/tests/client/js/drafts/storage.test.ts +200 -0
- package/tests/client/js/drafts/workers/diff.test.ts +165 -0
- package/tests/client/js/editor/editor.test.ts +616 -0
- package/tests/client/js/editor/link-wrap.test.ts +225 -0
- package/tests/client/js/editor/markdown-shortcuts.test.ts +370 -0
- package/tests/client/js/handlers/admin.test.ts +467 -0
- package/tests/client/js/state/router.test.ts +619 -0
- package/tests/client/js/state/schema.test.ts +266 -0
- package/tests/client/js/state/state.test.ts +328 -0
- package/tests/client/js/storage/adapter.test.ts +115 -0
- package/tests/client/js/storage/client.test.ts +250 -0
- package/tests/client/js/storage/db.test.ts +59 -0
- package/tests/client/js/storage/fsa.test.ts +284 -0
- package/tests/client/js/storage/github.test.ts +349 -0
- package/tests/client/js/storage/mock-port.ts +95 -0
- package/tests/client/js/storage/storage.test.ts +77 -0
- package/tests/client/js/storage/workers/frontmatter.test.ts +479 -0
- package/tests/client/js/storage/workers/storage.test.ts +299 -0
- package/tests/client/js/storage/workers/toml-parser.test.ts +169 -0
- package/tests/client/js/storage/workers/yaml-parser.test.ts +168 -0
- package/tests/client/js/utils/file-types.test.ts +268 -0
- package/tests/client/js/utils/frontmatter.test.ts +87 -0
- package/tests/client/js/utils/schema-utils.test.ts +318 -0
- package/tests/client/js/utils/slug.test.ts +58 -0
- package/tests/client/js/utils/sort.test.ts +276 -0
- package/tests/client/js/utils/stable-stringify.test.ts +68 -0
- package/tests/client/js/utils/url-utils.test.ts +70 -0
- package/tests/e2e/backend-connection.test.ts +301 -0
- package/tests/e2e/draft-lifecycle.test.ts +388 -0
- package/tests/e2e/editing.test.ts +355 -0
- package/tests/e2e/github-adapter.test.ts +330 -0
- package/tests/e2e/helpers/mock-adapter.ts +166 -0
- package/tests/e2e/helpers/test-app.ts +155 -0
- package/tests/e2e/navigation.test.ts +358 -0
- package/tests/e2e/publishing.test.ts +345 -0
- package/tests/e2e/unsaved-changes.test.ts +317 -0
- package/tests/setup.ts +2 -0
- package/tests/stubs/codemirror.ts +197 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +178 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
3
|
+
import {
|
|
4
|
+
isReadOnly,
|
|
5
|
+
isNullable,
|
|
6
|
+
getLabel,
|
|
7
|
+
} from '../../js/utils/schema-utils';
|
|
8
|
+
import FieldWrapper from './FieldWrapper.svelte';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the BooleanField component, which renders a labeled checkbox for a JSON Schema boolean property.
|
|
12
|
+
*/
|
|
13
|
+
interface Props {
|
|
14
|
+
// Field name used as the input id and label fallback
|
|
15
|
+
name: string;
|
|
16
|
+
// JSON Schema node describing this field
|
|
17
|
+
schema: SchemaNode;
|
|
18
|
+
// Current field value
|
|
19
|
+
value: unknown;
|
|
20
|
+
// Whether this field is required
|
|
21
|
+
required?: boolean;
|
|
22
|
+
// Callback fired when the value changes
|
|
23
|
+
onchange: (value: boolean | null) => void;
|
|
24
|
+
// When true, visually hides FieldWrapper chrome (label/help) for inline array contexts
|
|
25
|
+
inline?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
name,
|
|
30
|
+
schema,
|
|
31
|
+
value,
|
|
32
|
+
required = false,
|
|
33
|
+
onchange,
|
|
34
|
+
inline = false,
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
// Display label — schema.title if present, otherwise title-cased name
|
|
38
|
+
const label = $derived(getLabel(schema, name));
|
|
39
|
+
|
|
40
|
+
// Checked state for the checkbox
|
|
41
|
+
const checked = $derived(typeof value === 'boolean' ? value : false);
|
|
42
|
+
|
|
43
|
+
// Whether field is read-only
|
|
44
|
+
const readOnly = $derived(isReadOnly(schema));
|
|
45
|
+
|
|
46
|
+
// Whether empty input should emit null (nullable anyOf-unwrapped types)
|
|
47
|
+
const nullable = $derived(isNullable(schema));
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handles checkbox change. Preserves null for nullable fields only while the value is already null and unchecked.
|
|
51
|
+
* @param {Event} e - The change event from the checkbox input element
|
|
52
|
+
* @return {void}
|
|
53
|
+
*/
|
|
54
|
+
function handleChange(e: Event): void {
|
|
55
|
+
const isChecked = (e.target as HTMLInputElement).checked;
|
|
56
|
+
onchange(nullable && value === null && !isChecked ? null : isChecked);
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<FieldWrapper {name} {schema} {required} hideLabel={true} {inline}>
|
|
61
|
+
<label class="field-label-wrap" for={name}>
|
|
62
|
+
<input
|
|
63
|
+
type="checkbox"
|
|
64
|
+
id={name}
|
|
65
|
+
class="field-checkbox"
|
|
66
|
+
{checked}
|
|
67
|
+
disabled={readOnly}
|
|
68
|
+
onchange={handleChange}
|
|
69
|
+
/>
|
|
70
|
+
<span class="field-label-text">
|
|
71
|
+
{label}{#if required}<span class="field-required" aria-hidden="true"
|
|
72
|
+
>*</span
|
|
73
|
+
>{/if}
|
|
74
|
+
</span>
|
|
75
|
+
</label>
|
|
76
|
+
</FieldWrapper>
|
|
77
|
+
|
|
78
|
+
<style>
|
|
79
|
+
/* Label wraps checkbox + text in a flex row — no separate label above */
|
|
80
|
+
.field-label-wrap {
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
gap: 0.5rem;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
width: fit-content;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.field-checkbox {
|
|
89
|
+
width: 1rem;
|
|
90
|
+
height: 1rem;
|
|
91
|
+
accent-color: var(--plum);
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
|
|
94
|
+
&:focus {
|
|
95
|
+
outline: 2px solid var(--plum);
|
|
96
|
+
outline-offset: -1px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
&:disabled {
|
|
100
|
+
opacity: 0.6;
|
|
101
|
+
cursor: default;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.field-label-text {
|
|
106
|
+
font-size: 0.875rem;
|
|
107
|
+
color: var(--cms-fg);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.field-required {
|
|
111
|
+
color: var(--light-red);
|
|
112
|
+
margin-left: 0.25rem;
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
3
|
+
import { isReadOnly, isNullable } from '../../js/utils/schema-utils';
|
|
4
|
+
import FieldWrapper from './FieldWrapper.svelte';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props for the DateField component, which renders a date input for a JSON Schema string property with format "date-time".
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
// Field name used as the input id and label fallback
|
|
11
|
+
name: string;
|
|
12
|
+
// JSON Schema node describing this field
|
|
13
|
+
schema: SchemaNode;
|
|
14
|
+
// Current field value — Date object or ISO string
|
|
15
|
+
value: unknown;
|
|
16
|
+
// Whether this field is required
|
|
17
|
+
required?: boolean;
|
|
18
|
+
// Callback fired when the value changes
|
|
19
|
+
onchange: (value: string | null) => void;
|
|
20
|
+
// When true, visually hides FieldWrapper chrome (label/help) for inline array contexts
|
|
21
|
+
inline?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
name,
|
|
26
|
+
schema,
|
|
27
|
+
value,
|
|
28
|
+
required = false,
|
|
29
|
+
onchange,
|
|
30
|
+
inline = false,
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Converts a Date or ISO string to YYYY-MM-DD for the date input, or empty string if unset.
|
|
35
|
+
* @param {unknown} val - The field value, which may be a Date object, ISO string, or unset
|
|
36
|
+
* @return {string} A YYYY-MM-DD formatted string for the date input, or empty string
|
|
37
|
+
*/
|
|
38
|
+
function toDateInputValue(val: unknown): string {
|
|
39
|
+
if (val instanceof Date) {
|
|
40
|
+
// Use UTC components to avoid timezone shifts converting to local date
|
|
41
|
+
const y = val.getUTCFullYear();
|
|
42
|
+
const m = String(val.getUTCMonth() + 1).padStart(2, '0');
|
|
43
|
+
const d = String(val.getUTCDate()).padStart(2, '0');
|
|
44
|
+
return `${y}-${m}-${d}`;
|
|
45
|
+
}
|
|
46
|
+
if (typeof val === 'string' && val.length >= 10) {
|
|
47
|
+
// Slice the YYYY-MM-DD portion from an ISO string (e.g. "2024-01-15T00:00:00Z")
|
|
48
|
+
return val.slice(0, 10);
|
|
49
|
+
}
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// YYYY-MM-DD string for the date input element
|
|
54
|
+
const inputValue = $derived(toDateInputValue(value));
|
|
55
|
+
|
|
56
|
+
// Whether field is read-only
|
|
57
|
+
const readOnly = $derived(isReadOnly(schema));
|
|
58
|
+
|
|
59
|
+
// Whether empty input should emit null (nullable anyOf-unwrapped types)
|
|
60
|
+
const nullable = $derived(isNullable(schema));
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handles date input change, emitting null for empty nullable fields.
|
|
64
|
+
* @param {Event} e - The input event from the date input element
|
|
65
|
+
* @return {void}
|
|
66
|
+
*/
|
|
67
|
+
function handleChange(e: Event): void {
|
|
68
|
+
const raw = (e.target as HTMLInputElement).value;
|
|
69
|
+
onchange(nullable && raw === '' ? null : raw);
|
|
70
|
+
}
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<FieldWrapper {name} {schema} {required} {inline}>
|
|
74
|
+
<input
|
|
75
|
+
type="date"
|
|
76
|
+
id={name}
|
|
77
|
+
class="field-input field-input--auto"
|
|
78
|
+
value={inputValue}
|
|
79
|
+
readonly={readOnly}
|
|
80
|
+
oninput={handleChange}
|
|
81
|
+
/>
|
|
82
|
+
</FieldWrapper>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
3
|
+
import { isReadOnly, isNullable } from '../../js/utils/schema-utils';
|
|
4
|
+
import FieldWrapper from './FieldWrapper.svelte';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props for the EnumField component, which renders a select dropdown for a JSON Schema enum property.
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
// Field name used as the select id and label fallback
|
|
11
|
+
name: string;
|
|
12
|
+
// JSON Schema node describing this field
|
|
13
|
+
schema: SchemaNode;
|
|
14
|
+
// Current field value
|
|
15
|
+
value: unknown;
|
|
16
|
+
// The enum values to render as options
|
|
17
|
+
options: unknown[];
|
|
18
|
+
// Whether this field is required
|
|
19
|
+
required?: boolean;
|
|
20
|
+
// Callback fired when the value changes
|
|
21
|
+
onchange: (value: string | null) => void;
|
|
22
|
+
// When true, visually hides FieldWrapper chrome (label/help) for inline array contexts
|
|
23
|
+
inline?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
name,
|
|
28
|
+
schema,
|
|
29
|
+
value,
|
|
30
|
+
options,
|
|
31
|
+
required = false,
|
|
32
|
+
onchange,
|
|
33
|
+
inline = false,
|
|
34
|
+
}: Props = $props();
|
|
35
|
+
|
|
36
|
+
// String representation of the current value for select binding
|
|
37
|
+
const selectedValue = $derived(value != null ? String(value) : '');
|
|
38
|
+
|
|
39
|
+
// Whether field is read-only
|
|
40
|
+
const readOnly = $derived(isReadOnly(schema));
|
|
41
|
+
|
|
42
|
+
// Whether empty selection should emit null (nullable anyOf-unwrapped types)
|
|
43
|
+
const nullable = $derived(isNullable(schema));
|
|
44
|
+
|
|
45
|
+
// Whether to show the empty placeholder option — when not required or no value is set
|
|
46
|
+
const showEmptyOption = $derived(!required || value == null);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handles select change, emitting null when empty option is selected on nullable fields.
|
|
50
|
+
* @param {Event} e - The change event from the select element
|
|
51
|
+
* @return {void}
|
|
52
|
+
*/
|
|
53
|
+
function handleChange(e: Event): void {
|
|
54
|
+
const raw = (e.target as HTMLSelectElement).value;
|
|
55
|
+
onchange(raw === '' ? (nullable ? null : '') : raw);
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<FieldWrapper {name} {schema} {required} {inline}>
|
|
60
|
+
<select
|
|
61
|
+
id={name}
|
|
62
|
+
class="field-input field-input--select"
|
|
63
|
+
value={selectedValue}
|
|
64
|
+
disabled={readOnly}
|
|
65
|
+
onchange={handleChange}
|
|
66
|
+
>
|
|
67
|
+
{#if showEmptyOption}
|
|
68
|
+
<option value="">—</option>
|
|
69
|
+
{/if}
|
|
70
|
+
{#each options as option}
|
|
71
|
+
<option value={String(option)}>{String(option)}</option>
|
|
72
|
+
{/each}
|
|
73
|
+
</select>
|
|
74
|
+
</FieldWrapper>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
3
|
+
import { getLabel } from '../../js/utils/schema-utils';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared wrapper for all form field components. Provides the label, required marker, deprecated dimming, description, and constraint text so individual field components only supply the input element via a Svelte snippet.
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
// Field name used as the input id and label fallback
|
|
11
|
+
name: string;
|
|
12
|
+
// JSON Schema node describing this field
|
|
13
|
+
schema: SchemaNode;
|
|
14
|
+
// Whether this field is required
|
|
15
|
+
required?: boolean;
|
|
16
|
+
// The input element rendered inside the wrapper
|
|
17
|
+
children: Snippet;
|
|
18
|
+
// Optional constraint text displayed after the description (e.g., "max 200", "min 0, max 100")
|
|
19
|
+
constraintText?: string;
|
|
20
|
+
// When true, hides the default label — used by BooleanField which renders its own inline checkbox+label
|
|
21
|
+
hideLabel?: boolean;
|
|
22
|
+
// When true, visually hides label and help text using sr-only for inline array contexts where the parent provides visible labels
|
|
23
|
+
inline?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
name,
|
|
28
|
+
schema,
|
|
29
|
+
required = false,
|
|
30
|
+
children,
|
|
31
|
+
constraintText,
|
|
32
|
+
hideLabel = false,
|
|
33
|
+
inline = false,
|
|
34
|
+
}: Props = $props();
|
|
35
|
+
|
|
36
|
+
// Display label — schema.title if present, otherwise title-cased name
|
|
37
|
+
const label = $derived(getLabel(schema, name));
|
|
38
|
+
|
|
39
|
+
// Description from schema
|
|
40
|
+
const description = $derived(schema['description'] as string | undefined);
|
|
41
|
+
|
|
42
|
+
// Whether field is deprecated — dims the entire field
|
|
43
|
+
const deprecated = $derived(!!(schema['deprecated'] as boolean | undefined));
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<div class="field" class:field--deprecated={deprecated}>
|
|
47
|
+
{#if !hideLabel}
|
|
48
|
+
<label class="field-label" class:sr-only={inline} for={name}>
|
|
49
|
+
{label}{#if required}<span class="field-required" aria-hidden="true"
|
|
50
|
+
>*</span
|
|
51
|
+
>{/if}
|
|
52
|
+
</label>
|
|
53
|
+
{/if}
|
|
54
|
+
|
|
55
|
+
{@render children()}
|
|
56
|
+
|
|
57
|
+
{#if description || constraintText}
|
|
58
|
+
<p class="field-help" class:sr-only={inline}>
|
|
59
|
+
{#if description}{description}{/if}
|
|
60
|
+
{#if description && constraintText} {/if}
|
|
61
|
+
{#if constraintText}<span class="field-constraint">{constraintText}</span
|
|
62
|
+
>{/if}
|
|
63
|
+
</p>
|
|
64
|
+
{/if}
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<style>
|
|
68
|
+
.field {
|
|
69
|
+
display: grid;
|
|
70
|
+
gap: 0.25rem;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Dimmed appearance for deprecated fields */
|
|
74
|
+
.field--deprecated {
|
|
75
|
+
opacity: 0.5;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.field-label {
|
|
79
|
+
font-size: 0.875rem;
|
|
80
|
+
color: var(--cms-fg);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.field-required {
|
|
84
|
+
color: var(--light-red);
|
|
85
|
+
margin-left: 0.25rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.field-help {
|
|
89
|
+
font-size: 0.75rem;
|
|
90
|
+
color: var(--cms-muted);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.field-constraint {
|
|
94
|
+
font-style: italic;
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
3
|
+
import { isReadOnly, isNullable } from '../../js/utils/schema-utils';
|
|
4
|
+
import FieldWrapper from './FieldWrapper.svelte';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props for the NumberField component, which renders a numeric input for a JSON Schema number or integer property.
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
// Field name used as the input id and label fallback
|
|
11
|
+
name: string;
|
|
12
|
+
// JSON Schema node describing this field
|
|
13
|
+
schema: SchemaNode;
|
|
14
|
+
// Current field value
|
|
15
|
+
value: unknown;
|
|
16
|
+
// Whether this field is required
|
|
17
|
+
required?: boolean;
|
|
18
|
+
// Callback fired when the value changes
|
|
19
|
+
onchange: (value: number | null) => void;
|
|
20
|
+
// When true, visually hides FieldWrapper chrome (label/help) for inline array contexts
|
|
21
|
+
inline?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
name,
|
|
26
|
+
schema,
|
|
27
|
+
value,
|
|
28
|
+
required = false,
|
|
29
|
+
onchange,
|
|
30
|
+
inline = false,
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
// Numeric value for the input, coerced from the value prop
|
|
34
|
+
const inputValue = $derived(typeof value === 'number' ? value : '');
|
|
35
|
+
|
|
36
|
+
// Min attribute: minimum takes precedence, exclusiveMinimum adds 1
|
|
37
|
+
const min = $derived.by(() => {
|
|
38
|
+
const minimum = schema['minimum'] as number | undefined;
|
|
39
|
+
const exclusiveMin = schema['exclusiveMinimum'] as number | undefined;
|
|
40
|
+
if (minimum != null) return minimum;
|
|
41
|
+
if (exclusiveMin != null) return exclusiveMin + 1;
|
|
42
|
+
return undefined;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Max attribute: maximum takes precedence, exclusiveMaximum subtracts 1
|
|
46
|
+
const max = $derived.by(() => {
|
|
47
|
+
const maximum = schema['maximum'] as number | undefined;
|
|
48
|
+
const exclusiveMax = schema['exclusiveMaximum'] as number | undefined;
|
|
49
|
+
if (maximum != null) return maximum;
|
|
50
|
+
if (exclusiveMax != null) return exclusiveMax - 1;
|
|
51
|
+
return undefined;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Step attribute from multipleOf
|
|
55
|
+
const step = $derived(schema['multipleOf'] as number | undefined);
|
|
56
|
+
|
|
57
|
+
// Whether field is read-only
|
|
58
|
+
const readOnly = $derived(isReadOnly(schema));
|
|
59
|
+
|
|
60
|
+
// Whether empty input should emit null (nullable anyOf-unwrapped types)
|
|
61
|
+
const nullable = $derived(isNullable(schema));
|
|
62
|
+
|
|
63
|
+
// Human-readable constraint summary (e.g. "min 0, max 100, step 5")
|
|
64
|
+
const constraintText = $derived.by(() => {
|
|
65
|
+
const parts: string[] = [];
|
|
66
|
+
if (min != null) parts.push(`min ${min}`);
|
|
67
|
+
if (max != null) parts.push(`max ${max}`);
|
|
68
|
+
if (step != null) parts.push(`step ${step}`);
|
|
69
|
+
return parts.length > 0 ? parts.join(', ') : undefined;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Handles input change, emitting null for empty nullable fields or 0 for non-nullable.
|
|
74
|
+
* @param {Event} e - The input event from the number input element
|
|
75
|
+
* @return {void}
|
|
76
|
+
*/
|
|
77
|
+
function handleChange(e: Event): void {
|
|
78
|
+
const raw = (e.target as HTMLInputElement).value;
|
|
79
|
+
if (raw === '') {
|
|
80
|
+
onchange(nullable ? null : 0);
|
|
81
|
+
} else {
|
|
82
|
+
onchange(parseFloat(raw));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<FieldWrapper {name} {schema} {required} {constraintText} {inline}>
|
|
88
|
+
<input
|
|
89
|
+
type="number"
|
|
90
|
+
id={name}
|
|
91
|
+
class="field-input field-input--auto"
|
|
92
|
+
value={inputValue}
|
|
93
|
+
{min}
|
|
94
|
+
{max}
|
|
95
|
+
{step}
|
|
96
|
+
readonly={readOnly}
|
|
97
|
+
oninput={handleChange}
|
|
98
|
+
/>
|
|
99
|
+
</FieldWrapper>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
3
|
+
import {
|
|
4
|
+
getProperties,
|
|
5
|
+
getRequiredFields,
|
|
6
|
+
getLabel,
|
|
7
|
+
} from '../../js/utils/schema-utils';
|
|
8
|
+
import SchemaField from './SchemaField.svelte';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the ObjectField component, which renders a grouped fieldset of child SchemaField components for a JSON Schema object property.
|
|
12
|
+
*/
|
|
13
|
+
interface Props {
|
|
14
|
+
// Property name for labeling
|
|
15
|
+
name: string;
|
|
16
|
+
// JSON Schema node describing this object
|
|
17
|
+
schema: SchemaNode;
|
|
18
|
+
// Current object value
|
|
19
|
+
value: unknown;
|
|
20
|
+
// Whether this field is required
|
|
21
|
+
required?: boolean;
|
|
22
|
+
// Callback fired when the value changes
|
|
23
|
+
onchange: (value: unknown) => void;
|
|
24
|
+
// When true, renders fields without a fieldset wrapper (used inside ArrayItem).
|
|
25
|
+
inline?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
name,
|
|
30
|
+
schema,
|
|
31
|
+
value,
|
|
32
|
+
required = false,
|
|
33
|
+
onchange,
|
|
34
|
+
inline = false,
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
// Display label from schema title or property name
|
|
38
|
+
const label = $derived(getLabel(schema, name));
|
|
39
|
+
|
|
40
|
+
// Properties map from the schema
|
|
41
|
+
const properties = $derived(getProperties(schema) ?? {});
|
|
42
|
+
|
|
43
|
+
// Required field names within this object
|
|
44
|
+
const requiredFields = $derived(getRequiredFields(schema));
|
|
45
|
+
|
|
46
|
+
// Current object value, defaulting to empty object
|
|
47
|
+
const objValue = $derived(
|
|
48
|
+
(typeof value === 'object' && value !== null ? value : {}) as Record<
|
|
49
|
+
string,
|
|
50
|
+
unknown
|
|
51
|
+
>,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Updates a single property and dispatches the full updated object via onchange.
|
|
56
|
+
* @param {string} key - The property key to update within the object
|
|
57
|
+
* @param {unknown} newValue - The new value for the given property key
|
|
58
|
+
* @return {void}
|
|
59
|
+
*/
|
|
60
|
+
function handleFieldChange(key: string, newValue: unknown): void {
|
|
61
|
+
onchange({ ...objValue, [key]: newValue });
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
{#if inline}
|
|
66
|
+
<!-- Inline mode: no fieldset wrapper, used inside ArrayItem -->
|
|
67
|
+
<div class="object-field--inline">
|
|
68
|
+
{#each Object.entries(properties) as [key, propSchema]}
|
|
69
|
+
<SchemaField
|
|
70
|
+
name={key}
|
|
71
|
+
schema={propSchema}
|
|
72
|
+
value={objValue[key]}
|
|
73
|
+
required={requiredFields.includes(key)}
|
|
74
|
+
onchange={(v) => handleFieldChange(key, v)}
|
|
75
|
+
/>
|
|
76
|
+
{/each}
|
|
77
|
+
</div>
|
|
78
|
+
{:else}
|
|
79
|
+
<fieldset class="object-field">
|
|
80
|
+
<legend class="object-field__legend">
|
|
81
|
+
{label}
|
|
82
|
+
{#if required}<span class="object-field__required">*</span>{/if}
|
|
83
|
+
</legend>
|
|
84
|
+
{#each Object.entries(properties) as [key, propSchema]}
|
|
85
|
+
<SchemaField
|
|
86
|
+
name={key}
|
|
87
|
+
schema={propSchema}
|
|
88
|
+
value={objValue[key]}
|
|
89
|
+
required={requiredFields.includes(key)}
|
|
90
|
+
onchange={(v) => handleFieldChange(key, v)}
|
|
91
|
+
/>
|
|
92
|
+
{/each}
|
|
93
|
+
</fieldset>
|
|
94
|
+
{/if}
|
|
95
|
+
|
|
96
|
+
<style>
|
|
97
|
+
.object-field {
|
|
98
|
+
border: 1px solid var(--cms-border);
|
|
99
|
+
border-radius: 4px;
|
|
100
|
+
padding: 1rem;
|
|
101
|
+
display: grid;
|
|
102
|
+
gap: 1.25rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.object-field__legend {
|
|
106
|
+
font-size: 0.875rem;
|
|
107
|
+
color: var(--cms-fg);
|
|
108
|
+
padding: 0 0.5rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.object-field__required {
|
|
112
|
+
color: var(--light-red);
|
|
113
|
+
margin-left: 0.25rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Inline mode: no border/padding, just stack the fields */
|
|
117
|
+
.object-field--inline {
|
|
118
|
+
display: grid;
|
|
119
|
+
gap: 1.25rem;
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { resolveFieldType } from '../../js/utils/schema-utils';
|
|
3
|
+
import type { SchemaNode } from '../../js/utils/schema-utils';
|
|
4
|
+
import StringField from './StringField.svelte';
|
|
5
|
+
import NumberField from './NumberField.svelte';
|
|
6
|
+
import BooleanField from './BooleanField.svelte';
|
|
7
|
+
import EnumField from './EnumField.svelte';
|
|
8
|
+
import DateField from './DateField.svelte';
|
|
9
|
+
import ArrayField from './ArrayField.svelte';
|
|
10
|
+
import ObjectField from './ObjectField.svelte';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for the SchemaField component, which resolves a JSON Schema node to the appropriate leaf field component (string, number, boolean, enum, date, array, or object).
|
|
14
|
+
*/
|
|
15
|
+
interface Props {
|
|
16
|
+
// Property name for labeling and identification
|
|
17
|
+
name: string;
|
|
18
|
+
// JSON Schema node describing this field
|
|
19
|
+
schema: SchemaNode;
|
|
20
|
+
// Current field value
|
|
21
|
+
value: unknown;
|
|
22
|
+
// Whether this field is required
|
|
23
|
+
required?: boolean;
|
|
24
|
+
// Callback fired when the value changes
|
|
25
|
+
onchange: (value: unknown) => void;
|
|
26
|
+
// When true, object fields render without a fieldset wrapper and leaf fields hide FieldWrapper chrome (used inside ArrayItem).
|
|
27
|
+
inline?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
name,
|
|
32
|
+
schema,
|
|
33
|
+
value,
|
|
34
|
+
required = false,
|
|
35
|
+
onchange,
|
|
36
|
+
inline = false,
|
|
37
|
+
}: Props = $props();
|
|
38
|
+
|
|
39
|
+
// Resolve the schema node to a field type descriptor
|
|
40
|
+
const fieldType = $derived(resolveFieldType(schema));
|
|
41
|
+
|
|
42
|
+
// For nullable anyOf schemas, merges outer annotations onto the inner type and sets _nullable so leaf fields emit null for empty values.
|
|
43
|
+
const effectiveSchema = $derived.by(() => {
|
|
44
|
+
if (Array.isArray(schema['anyOf'])) {
|
|
45
|
+
const nonNull = (schema['anyOf'] as SchemaNode[]).find(
|
|
46
|
+
(s) => s['type'] !== 'null',
|
|
47
|
+
);
|
|
48
|
+
if (nonNull) {
|
|
49
|
+
// Spread outer props (title, description, readOnly, etc.) onto the inner type; exclude anyOf to avoid recursion
|
|
50
|
+
const { anyOf: _, ...outerProps } = schema;
|
|
51
|
+
return { ...nonNull, ...outerProps, _nullable: true };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return schema;
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
{#if fieldType.kind === 'string'}
|
|
59
|
+
<StringField
|
|
60
|
+
{name}
|
|
61
|
+
schema={effectiveSchema}
|
|
62
|
+
{value}
|
|
63
|
+
{required}
|
|
64
|
+
{inline}
|
|
65
|
+
onchange={(v) => onchange(v)}
|
|
66
|
+
/>
|
|
67
|
+
{:else if fieldType.kind === 'number'}
|
|
68
|
+
<NumberField
|
|
69
|
+
{name}
|
|
70
|
+
schema={effectiveSchema}
|
|
71
|
+
{value}
|
|
72
|
+
{required}
|
|
73
|
+
{inline}
|
|
74
|
+
onchange={(v) => onchange(v)}
|
|
75
|
+
/>
|
|
76
|
+
{:else if fieldType.kind === 'boolean'}
|
|
77
|
+
<BooleanField
|
|
78
|
+
{name}
|
|
79
|
+
schema={effectiveSchema}
|
|
80
|
+
{value}
|
|
81
|
+
{inline}
|
|
82
|
+
onchange={(v) => onchange(v)}
|
|
83
|
+
/>
|
|
84
|
+
{:else if fieldType.kind === 'enum'}
|
|
85
|
+
<EnumField
|
|
86
|
+
{name}
|
|
87
|
+
schema={effectiveSchema}
|
|
88
|
+
{value}
|
|
89
|
+
{required}
|
|
90
|
+
{inline}
|
|
91
|
+
options={fieldType.options}
|
|
92
|
+
onchange={(v) => onchange(v)}
|
|
93
|
+
/>
|
|
94
|
+
{:else if fieldType.kind === 'date'}
|
|
95
|
+
<DateField
|
|
96
|
+
{name}
|
|
97
|
+
schema={effectiveSchema}
|
|
98
|
+
{value}
|
|
99
|
+
{required}
|
|
100
|
+
{inline}
|
|
101
|
+
onchange={(v) => onchange(v)}
|
|
102
|
+
/>
|
|
103
|
+
{:else if fieldType.kind === 'array'}
|
|
104
|
+
<ArrayField {name} {schema} {value} {required} {onchange} />
|
|
105
|
+
{:else if fieldType.kind === 'object'}
|
|
106
|
+
<ObjectField {name} {schema} {value} {required} {onchange} {inline} />
|
|
107
|
+
{/if}
|