@petrarca/sonnet-playground 0.6.0 → 0.8.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/README.md +63 -101
- package/dist/PlayAuth-SOEYPOOL.js +8 -0
- package/dist/PlayForms-U2TWUX5L.js +7 -0
- package/dist/PlayGraph-M4KQSFI7.js +7 -0
- package/dist/PlaySchemaDesigner-S4DBB53G.js +7 -0
- package/dist/PlaySchemaDesigner-S4DBB53G.js.map +1 -0
- package/dist/{chunk-6VQJFBTM.js → chunk-2XMWOZ5O.js} +1 -1
- package/dist/chunk-2XMWOZ5O.js.map +1 -0
- package/dist/{chunk-ZSUCFEB6.js → chunk-BRH3FFFT.js} +182 -18
- package/dist/chunk-BRH3FFFT.js.map +1 -0
- package/dist/{chunk-C2P3UQMZ.js → chunk-E5X7GVLU.js} +2 -2
- package/dist/chunk-E5X7GVLU.js.map +1 -0
- package/dist/chunk-RYCRENYO.js +401 -0
- package/dist/chunk-RYCRENYO.js.map +1 -0
- package/dist/index.d.ts +20 -10
- package/dist/index.js +50 -5
- package/dist/index.js.map +1 -1
- package/dist/pages/index.d.ts +3 -1
- package/dist/pages/index.js +7 -3
- package/package.json +7 -7
- package/dist/PlayForms-2G7PROCH.js +0 -7
- package/dist/PlayGraph-M7UE5MZC.js +0 -7
- package/dist/PlaySchemaDesigner-ZM4F7KBU.js +0 -7
- package/dist/chunk-6VQJFBTM.js.map +0 -1
- package/dist/chunk-C2P3UQMZ.js.map +0 -1
- package/dist/chunk-ZSUCFEB6.js.map +0 -1
- /package/dist/{PlayForms-2G7PROCH.js.map → PlayAuth-SOEYPOOL.js.map} +0 -0
- /package/dist/{PlayGraph-M7UE5MZC.js.map → PlayForms-U2TWUX5L.js.map} +0 -0
- /package/dist/{PlaySchemaDesigner-ZM4F7KBU.js.map → PlayGraph-M4KQSFI7.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
# @petrarca/sonnet-playground
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The living reference for the Petrarca Sonnet component library: a registry of
|
|
4
|
+
demonstration pages and a factory that turns it into a `ShellModule`. It is a
|
|
5
|
+
library, not a hosted site -- you mount it in your own application and get a
|
|
6
|
+
working component reference inside your own build
|
|
7
|
+
([ADR-0005](../../docs/adr/0005-the-playground-is-the-living-documentation.md)).
|
|
5
8
|
|
|
6
|
-
## What
|
|
9
|
+
## What it provides
|
|
7
10
|
|
|
8
|
-
**
|
|
9
|
-
|
|
11
|
+
**A registry.** `PlaygroundRegistry` and `createPlaygroundRegistry`, which
|
|
12
|
+
returns one pre-populated with the built-in entries. Register your own pages
|
|
13
|
+
beside them, grouped under a label you choose.
|
|
10
14
|
|
|
11
|
-
**
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
**A module factory.** `createPlaygroundModule` turns a registry into a
|
|
16
|
+
`ShellModule` with an overview page, generated routes and navigation, ready to
|
|
17
|
+
pass to `createModuleRegistry`.
|
|
14
18
|
|
|
15
|
-
**
|
|
16
|
-
|
|
19
|
+
**Twelve built-in pages**, each lazily loaded: Typography and Color Palette
|
|
20
|
+
under Design System; Core UI Components, Advanced Components and JSON Editor
|
|
21
|
+
under Components; Form Components, Forms Renderer and Form Widgets under Forms;
|
|
22
|
+
Schema Designer under Schemas; Data Components under Data; Graph Visualization
|
|
23
|
+
under Graph; Shell API under Shell.
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
**Page building blocks.** `PlaygroundSection` for consistent section chrome,
|
|
26
|
+
`PlaygroundOverview` for the tile grid, and the playground settings provider,
|
|
27
|
+
hook and panel.
|
|
19
28
|
|
|
20
29
|
## Install
|
|
21
30
|
|
|
@@ -23,120 +32,73 @@ used by all playground pages.
|
|
|
23
32
|
pnpm add @petrarca/sonnet-playground
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
That single install pulls in all six other `@petrarca/sonnet-*` packages --
|
|
36
|
+
`sonnet-core`, `sonnet-ui`, `sonnet-forms`, `sonnet-shell`, `sonnet-graph` and
|
|
37
|
+
`sonnet-schema-designer` -- as regular dependencies, because the pages import
|
|
38
|
+
from every one of them.
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// src/modules/playground/index.ts
|
|
35
|
-
import {
|
|
36
|
-
createPlaygroundRegistry,
|
|
37
|
-
createPlaygroundModule,
|
|
38
|
-
} from "@petrarca/sonnet-playground";
|
|
39
|
-
|
|
40
|
-
const registry = createPlaygroundRegistry();
|
|
41
|
-
|
|
42
|
-
// Optionally add app-specific pages:
|
|
43
|
-
registry.register({
|
|
44
|
-
id: "app.my-page",
|
|
45
|
-
title: "My Page",
|
|
46
|
-
description: "Custom demo page.",
|
|
47
|
-
icon: SomeIcon,
|
|
48
|
-
color: "teal",
|
|
49
|
-
path: "my-page",
|
|
50
|
-
component: React.lazy(() => import("./pages/MyPage")),
|
|
51
|
-
group: "App",
|
|
52
|
-
order: 0,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
export default createPlaygroundModule(registry);
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
// src/modules/registry.ts
|
|
60
|
-
import { createModuleRegistry } from "@petrarca/sonnet-shell";
|
|
61
|
-
import home from "./home";
|
|
62
|
-
import playground from "./playground";
|
|
63
|
-
|
|
64
|
-
export const registry = createModuleRegistry([
|
|
65
|
-
home,
|
|
66
|
-
playground, // pinBottom: true is set automatically by createPlaygroundModule
|
|
67
|
-
]);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
`createPlaygroundModule` returns a `ShellModule` — pass it directly to
|
|
71
|
-
`createModuleRegistry` alongside your app's own modules.
|
|
72
|
-
|
|
73
|
-
---
|
|
40
|
+
| Peer | Range | Needed for |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `react`, `react-dom` | `>=19` | Everything |
|
|
43
|
+
| `react-router-dom` | `>=7` | The generated routes and the layout outlet |
|
|
44
|
+
| `tailwindcss` | `>=3` | Every page's styling |
|
|
74
45
|
|
|
75
|
-
|
|
46
|
+
Those dependencies carry further peers of their own, and some that are only
|
|
47
|
+
optional down there are required here -- the graph page needs every renderer
|
|
48
|
+
peer, and the JSON editor page needs CodeMirror. Getting that wrong shows up as
|
|
49
|
+
one page throwing rather than as an install error. See
|
|
50
|
+
[getting started](docs/usage/getting-started.md).
|
|
76
51
|
|
|
77
|
-
|
|
78
|
-
`registry.register()` before passing the registry to `createPlaygroundModule`.
|
|
79
|
-
Pages appear in the overview tile grid grouped under your chosen `group` label.
|
|
52
|
+
## Usage
|
|
80
53
|
|
|
81
54
|
```ts
|
|
82
55
|
// src/modules/playground/index.ts
|
|
83
56
|
import { lazy } from "react";
|
|
84
|
-
import {
|
|
57
|
+
import { Boxes } from "lucide-react";
|
|
85
58
|
import {
|
|
86
|
-
createPlaygroundRegistry,
|
|
87
59
|
createPlaygroundModule,
|
|
60
|
+
createPlaygroundRegistry,
|
|
88
61
|
} from "@petrarca/sonnet-playground";
|
|
89
62
|
|
|
90
63
|
const registry = createPlaygroundRegistry();
|
|
91
64
|
|
|
92
|
-
// Register app-specific pages
|
|
93
65
|
registry.register({
|
|
94
|
-
id: "app.
|
|
95
|
-
title: "
|
|
96
|
-
description: "Interactive demo of
|
|
97
|
-
icon:
|
|
66
|
+
id: "app.widgets",
|
|
67
|
+
title: "Widgets",
|
|
68
|
+
description: "Interactive demo of the Widget components.",
|
|
69
|
+
icon: Boxes,
|
|
98
70
|
color: "teal",
|
|
99
|
-
path: "
|
|
100
|
-
component: lazy(() => import("./pages/
|
|
101
|
-
group: "App Components",
|
|
71
|
+
path: "widgets",
|
|
72
|
+
component: lazy(() => import("./pages/PlayWidgets")),
|
|
73
|
+
group: "App Components",
|
|
102
74
|
order: 0,
|
|
103
75
|
});
|
|
104
76
|
|
|
105
77
|
export default createPlaygroundModule(registry);
|
|
106
78
|
```
|
|
107
79
|
|
|
108
|
-
|
|
109
|
-
|
|
80
|
+
`createPlaygroundModule` returns a `ShellModule`, so it goes into
|
|
81
|
+
`createModuleRegistry` alongside your own modules. Its options are `icon`,
|
|
82
|
+
`label`, `basePath`, `overviewTitle`, `overviewSubtitle` and `grouped`; the
|
|
83
|
+
defaults are `FlaskConical`, `"Playground"`, `"/playground"`,
|
|
84
|
+
`"Developer Playground"`, `"Experiment with components, test features, and
|
|
85
|
+
explore capabilities."` and `true`.
|
|
110
86
|
|
|
111
|
-
|
|
112
|
-
|
|
87
|
+
Individual built-in pages are available from
|
|
88
|
+
`@petrarca/sonnet-playground/pages` when you want one on a route of your own.
|
|
113
89
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
## Pages subpath
|
|
117
|
-
|
|
118
|
-
Individual built-in pages are available at `@petrarca/sonnet-playground/pages`
|
|
119
|
-
for consumers who want to cherry-pick or re-export them:
|
|
120
|
-
|
|
121
|
-
```tsx
|
|
122
|
-
import { PlayTypography, PlayColorPalette } from "@petrarca/sonnet-playground/pages";
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## createPlaygroundModule options
|
|
128
|
-
|
|
129
|
-
| Option | Type | Default |
|
|
130
|
-
|---|---|---|
|
|
131
|
-
| `icon` | `LucideIcon` | `FlaskConical` |
|
|
132
|
-
| `label` | `string` | `"Playground"` |
|
|
133
|
-
| `basePath` | `string` | `"/playground"` |
|
|
134
|
-
| `overviewTitle` | `string` | `"Developer Playground"` |
|
|
135
|
-
| `overviewSubtitle` | `string` | `""` |
|
|
136
|
-
| `grouped` | `boolean` | `true` |
|
|
90
|
+
## Documentation
|
|
137
91
|
|
|
138
|
-
|
|
92
|
+
- [Usage guides](docs/usage/index.md) -- getting started,
|
|
93
|
+
[adding pages](docs/usage/adding-pages.md) and
|
|
94
|
+
[embedding](docs/usage/embedding.md).
|
|
95
|
+
- [Design docs](docs/design/index.md) -- the registry model and the page
|
|
96
|
+
contract.
|
|
97
|
+
- [sonnet-shell: getting started](../sonnet-shell/docs/usage/getting-started.md)
|
|
98
|
+
-- the application frame this module plugs into.
|
|
99
|
+
- [Workspace documentation](../../docs/README.md) -- how the packages layer and
|
|
100
|
+
the architecture decision records.
|
|
139
101
|
|
|
140
102
|
## License
|
|
141
103
|
|
|
142
|
-
See [LICENSE.md](../../LICENSE.md).
|
|
104
|
+
Apache 2.0. See [LICENSE.md](../../LICENSE.md).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pages/PlaySchemaDesigner.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n SchemaDesigner,\n type SchemaDesignerCapabilities,\n} from \"@petrarca/sonnet-schema-designer\";\nimport { FormSelect } from \"@petrarca/sonnet-forms\";\nimport type { JsonSchema, UISchema } from \"@petrarca/sonnet-core/schema\";\n\nconst SAMPLE_SCHEMA: JsonSchema = {\n title: \"Patient\",\n type: \"object\",\n properties: {\n first_name: { type: \"string\", title: \"First name\", minLength: 1 },\n last_name: { type: \"string\", title: \"Last name\", minLength: 1 },\n date_of_birth: { type: \"string\", format: \"date\", title: \"Date of birth\" },\n gender: {\n type: \"string\",\n title: \"Gender\",\n enum: [\"male\", \"female\", \"other\", \"unknown\"],\n },\n active: { type: \"boolean\", title: \"Active\" },\n contact: {\n type: \"object\",\n title: \"Contact\",\n properties: {\n email: { type: \"string\", format: \"email\", title: \"Email\" },\n phone: { type: \"string\", title: \"Phone\" },\n },\n required: [\"email\"],\n },\n addresses: {\n type: \"array\",\n title: \"Addresses\",\n minItems: 1,\n maxItems: 3,\n items: {\n type: \"object\",\n properties: {\n street: { type: \"string\", title: \"Street\" },\n city: { type: \"string\", title: \"City\" },\n zip: { type: \"string\", title: \"ZIP\" },\n country: { type: \"string\", title: \"Country\" },\n },\n required: [\"street\", \"city\"],\n },\n },\n },\n required: [\"first_name\", \"last_name\"],\n};\n\n// An audit-event-like schema: a fixed envelope (actor, targets) with three\n// extensible metadata containers. Used to demo scoped editing.\nconst AUDIT_SCHEMA: JsonSchema = {\n title: \"AuditEvent\",\n type: \"object\",\n properties: {\n actor: {\n type: \"object\",\n title: \"Actor\",\n properties: {\n type: { type: \"string\", title: \"Type\" },\n id: { type: \"string\", title: \"ID\" },\n metadata: {\n type: \"object\",\n title: \"Actor metadata\",\n properties: { role: { type: \"string\", title: \"Role\" } },\n },\n },\n },\n targets: {\n type: \"array\",\n title: \"Targets\",\n items: {\n type: \"object\",\n properties: {\n type: { type: \"string\", title: \"Type\" },\n id: { type: \"string\", title: \"ID\" },\n metadata: {\n type: \"object\",\n title: \"Target metadata\",\n properties: { ward: { type: \"string\", title: \"Ward\" } },\n },\n },\n },\n },\n metadata: {\n type: \"object\",\n title: \"Metadata\",\n properties: { reason: { type: \"string\", title: \"Reason\" } },\n },\n },\n};\n\n// A schema exercising every logic construct the designer can author: a oneOf\n// discriminated variant (payment method), if/then/else conditionals (authored\n// as allOf members), a const-tagged field, and a nullable anyOf optional field.\n// x-ui-order places each conditional field right after the property it depends\n// on (see docs/design/logic-authoring.md in the schema-designer package,\n// ordering note).\nconst LOGIC_SCHEMA: JsonSchema = {\n title: \"Checkout\",\n type: \"object\",\n // A reusable definition referenced by $ref (shows read-only expansion).\n $defs: {\n Address: {\n type: \"object\",\n properties: {\n street: { type: \"string\", title: \"Street\" },\n city: { type: \"string\", title: \"City\" },\n zip: { type: \"string\", title: \"ZIP\" },\n },\n },\n },\n \"x-ui-order\": [\n \"schema_version\",\n \"amount\",\n \"requires_approval\",\n \"currency\",\n \"expedited\",\n \"delivery_window\",\n \"gift_wrap\",\n \"gift_message\",\n \"shipping_address\",\n \"note\",\n \"payment\",\n ],\n properties: {\n // A $ref to the shared Address definition (read-only in the tree).\n shipping_address: { $ref: \"#/$defs/Address\", title: \"Shipping address\" },\n // A fixed value carried by every document (const authoring).\n schema_version: {\n type: \"string\",\n title: \"Schema version\",\n const: \"checkout/v1\",\n },\n amount: { type: \"number\", title: \"Amount\", minimum: 0 },\n currency: {\n type: \"string\",\n title: \"Currency\",\n enum: [\"EUR\", \"USD\", \"GBP\"],\n },\n expedited: { type: \"boolean\", title: \"Expedited\" },\n gift_wrap: { type: \"boolean\", title: \"Gift wrap\" },\n // Declarative visibility rule (x-ui-rule): declared at its own position,\n // shown only when gift_wrap is checked. Contrast with the if/then/else\n // fields above, which are structurally added by their conditionals.\n gift_message: {\n type: \"string\",\n title: \"Gift message\",\n \"x-ui-rule\": {\n effect: \"show\",\n condition: { scope: \"/gift_wrap\", schema: { const: true } },\n },\n },\n // Nullable optional (anyOf [T, null]) -- the renderer unwraps to T.\n note: {\n title: \"Note (optional)\",\n anyOf: [{ type: \"string\" }, { type: \"null\" }],\n },\n // A discriminated union (oneOf): the payment method.\n payment: {\n type: \"object\",\n title: \"Payment method\",\n discriminator: { propertyName: \"kind\" },\n oneOf: [\n {\n title: \"Card\",\n properties: {\n kind: { const: \"card\" },\n card_number: { type: \"string\", title: \"Card number\" },\n cvc: { type: \"string\", title: \"CVC\" },\n },\n },\n {\n title: \"Bank transfer\",\n properties: {\n kind: { const: \"bank\" },\n iban: { type: \"string\", title: \"IBAN\" },\n },\n },\n {\n title: \"Cash\",\n properties: { kind: { const: \"cash\" } },\n },\n ],\n },\n },\n required: [\"amount\", \"currency\"],\n // Conditionals (if/then/else) authored as allOf members.\n allOf: [\n {\n // Large amounts require an approval reference.\n if: {\n properties: { amount: { exclusiveMinimum: 1000 } },\n required: [\"amount\"],\n },\n then: {\n properties: {\n requires_approval: {\n type: \"string\",\n title: \"Approval reference\",\n },\n },\n required: [\"requires_approval\"],\n },\n },\n {\n // Expedited orders ask for a delivery window; otherwise none.\n if: {\n properties: { expedited: { const: true } },\n required: [\"expedited\"],\n },\n then: {\n properties: {\n delivery_window: {\n type: \"string\",\n title: \"Delivery window\",\n enum: [\"morning\", \"afternoon\", \"evening\"],\n },\n },\n },\n },\n ],\n};\n\nconst AUDIT_EDITABLE_PATHS = [\n \"/properties/metadata/properties\",\n \"/properties/actor/properties/metadata/properties\",\n \"/properties/targets/items/properties/metadata/properties\",\n];\n\ntype ProfileKey = \"full\" | \"data\" | \"scoped\" | \"structure\" | \"logic\";\n\ninterface Profile {\n label: string;\n description: string;\n schema: JsonSchema;\n capabilities?: SchemaDesignerCapabilities;\n readOnly?: boolean;\n}\n\nconst PROFILES: Record<ProfileKey, Profile> = {\n full: {\n label: \"Full (schema + uiSchema)\",\n description: \"Default: author the schema and its form presentation hints.\",\n schema: SAMPLE_SCHEMA,\n capabilities: { previews: [\"form\", \"table\", \"json\", \"capabilities\"] },\n },\n data: {\n label: \"Data schema only\",\n description:\n \"Pure JSON Schema (audit, terminology): no UI/Combined tabs, JSON preview only.\",\n schema: SAMPLE_SCHEMA,\n capabilities: { mode: \"data\", previews: [\"json\", \"capabilities\"] },\n },\n scoped: {\n label: \"Scoped (audit event)\",\n description:\n \"Fixed envelope; only the three metadata containers are editable.\",\n schema: AUDIT_SCHEMA,\n capabilities: {\n mode: \"data\",\n editablePaths: AUDIT_EDITABLE_PATHS,\n previews: [\"json\", \"capabilities\"],\n },\n },\n structure: {\n label: \"Structure only (no preview)\",\n description: \"Tree + inspector only; the center preview pane is hidden.\",\n schema: SAMPLE_SCHEMA,\n capabilities: { mode: \"data\", previews: [] },\n },\n logic: {\n label: \"Logic (variants + conditionals)\",\n description:\n \"Shows every logic construct: a oneOf variant, if/then/else conditionals, a const field, and a nullable optional.\",\n schema: LOGIC_SCHEMA,\n capabilities: { previews: [\"form\", \"table\", \"json\", \"capabilities\"] },\n },\n};\n\n/**\n * PlaySchemaDesigner -- exercises SchemaDesigner: visually author a JsonSchema\n * (and its uiSchema presentation hints), with a live preview. The profile\n * switcher presets the capability profile (data/full triage, scoped editing,\n * read-only); the in-designer Capabilities tab tweaks it live.\n */\nexport default function PlaySchemaDesigner() {\n const [profileKey, setProfileKey] = useState<ProfileKey>(\"full\");\n const profile = PROFILES[profileKey];\n\n const [schema, setSchema] = useState<JsonSchema>(profile.schema);\n const [uiSchema, setUiSchema] = useState<Record<string, UISchema>>({});\n\n const selectProfile = (key: ProfileKey) => {\n setProfileKey(key);\n setSchema(PROFILES[key].schema);\n setUiSchema({});\n };\n\n return (\n <div className=\"w-full flex flex-col gap-3 h-[calc(100vh-8rem)]\">\n <div>\n <h1>Schema Designer</h1>\n <p className=\"text-sm text-muted-foreground mb-3\">\n Visually author a JSON Schema and its UI presentation hints. Edits\n flow through onChange as a valid schema + uiSchema.\n </p>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <label\n htmlFor=\"capability-profile\"\n className=\"shrink-0 text-sm font-medium\"\n >\n Capability profile\n </label>\n <div className=\"w-72\">\n <FormSelect\n id=\"capability-profile\"\n name=\"profile\"\n value={profileKey}\n options={(Object.keys(PROFILES) as ProfileKey[]).map((k) => ({\n value: k,\n label: PROFILES[k].label,\n }))}\n onChange={(v) => selectProfile((v as ProfileKey) ?? \"full\")}\n clearable={false}\n />\n </div>\n <p className=\"text-sm text-muted-foreground\">{profile.description}</p>\n </div>\n\n <div style={{ flex: 1, minHeight: 0 }}>\n <SchemaDesigner\n key={profileKey}\n schema={schema}\n uiSchema={uiSchema}\n capabilities={profile.capabilities}\n readOnly={profile.readOnly}\n onChange={(nextSchema, nextUiSchema) => {\n setSchema(nextSchema);\n setUiSchema(nextUiSchema);\n }}\n />\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -21,7 +21,8 @@ import { JsonEditor } from "@petrarca/sonnet-ui/json-editor";
|
|
|
21
21
|
import {
|
|
22
22
|
ArrowRight,
|
|
23
23
|
ChevronLeft,
|
|
24
|
-
ChevronRight as ChevronRightIcon
|
|
24
|
+
ChevronRight as ChevronRightIcon,
|
|
25
|
+
ChevronDown
|
|
25
26
|
} from "lucide-react";
|
|
26
27
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
27
28
|
var SAMPLE_SCHEMA = {
|
|
@@ -50,6 +51,64 @@ var SAMPLE_SCHEMA = {
|
|
|
50
51
|
},
|
|
51
52
|
required: ["name", "npi"]
|
|
52
53
|
};
|
|
54
|
+
var VALIDATION_SCHEMA = {
|
|
55
|
+
title: "Validation showcase",
|
|
56
|
+
type: "object",
|
|
57
|
+
// Cross-field / form validator (registered by the app): confirmEmail == email.
|
|
58
|
+
"x-ui-validate": ["emailsMatch"],
|
|
59
|
+
properties: {
|
|
60
|
+
email: {
|
|
61
|
+
type: "string",
|
|
62
|
+
title: "Email",
|
|
63
|
+
format: "email",
|
|
64
|
+
description: "Built-in: format: email"
|
|
65
|
+
},
|
|
66
|
+
confirmEmail: {
|
|
67
|
+
type: "string",
|
|
68
|
+
title: "Confirm email",
|
|
69
|
+
format: "email",
|
|
70
|
+
description: "Cross-field: must match Email (x-ui-validate on the object)"
|
|
71
|
+
},
|
|
72
|
+
website: {
|
|
73
|
+
type: "string",
|
|
74
|
+
title: "Website",
|
|
75
|
+
format: "uri",
|
|
76
|
+
description: "Built-in: format: uri"
|
|
77
|
+
},
|
|
78
|
+
zip: {
|
|
79
|
+
type: "string",
|
|
80
|
+
title: "ZIP code",
|
|
81
|
+
pattern: "^[0-9]{5}$",
|
|
82
|
+
description: "Built-in: pattern (five digits)"
|
|
83
|
+
},
|
|
84
|
+
age: {
|
|
85
|
+
type: "integer",
|
|
86
|
+
title: "Age",
|
|
87
|
+
minimum: 18,
|
|
88
|
+
maximum: 120,
|
|
89
|
+
description: "Built-in: minimum / maximum"
|
|
90
|
+
},
|
|
91
|
+
plan: {
|
|
92
|
+
type: "string",
|
|
93
|
+
title: "Plan",
|
|
94
|
+
enum: ["free", "pro", "enterprise"],
|
|
95
|
+
description: "Built-in: enum"
|
|
96
|
+
},
|
|
97
|
+
slug: {
|
|
98
|
+
type: "string",
|
|
99
|
+
title: "Slug",
|
|
100
|
+
"x-ui-validate": "slug",
|
|
101
|
+
description: "Custom: x-ui-validate: 'slug' (registered by the app)"
|
|
102
|
+
},
|
|
103
|
+
handle: {
|
|
104
|
+
type: "string",
|
|
105
|
+
title: "Handle",
|
|
106
|
+
"x-ui-validate": "uniqueHandle",
|
|
107
|
+
description: "Async: remote uniqueness check -- try 'taken'"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
required: ["email"]
|
|
111
|
+
};
|
|
53
112
|
var NESTED_OBJECT_SCHEMA = {
|
|
54
113
|
title: "Patient",
|
|
55
114
|
type: "object",
|
|
@@ -402,6 +461,7 @@ var SECTIONS_LAYOUT_UISCHEMA = {
|
|
|
402
461
|
};
|
|
403
462
|
var sampleSchemas = {
|
|
404
463
|
flat: SAMPLE_SCHEMA,
|
|
464
|
+
validation: VALIDATION_SCHEMA,
|
|
405
465
|
nested_object: NESTED_OBJECT_SCHEMA,
|
|
406
466
|
one_of: ONEOF_SCHEMA,
|
|
407
467
|
array_of_objects: ARRAY_OF_OBJECTS_SCHEMA,
|
|
@@ -495,9 +555,16 @@ function usePlayFormsState() {
|
|
|
495
555
|
const [dataError, setDataError] = useState("");
|
|
496
556
|
const [activeTab, setActiveTab] = useState("schema");
|
|
497
557
|
const [deferStateUpdates, setDeferStateUpdates] = useState(false);
|
|
558
|
+
const [validationDisplay, setValidationDisplay] = useState("onSubmit");
|
|
498
559
|
const [dataCollapsed, setDataCollapsed] = useState(false);
|
|
560
|
+
const [errorsCollapsed, setErrorsCollapsed] = useState(false);
|
|
561
|
+
const [injectServerError, setInjectServerError] = useState(false);
|
|
499
562
|
const [lastDiff, setLastDiff] = useState(null);
|
|
500
563
|
const toggleDataPanel = useCallback(() => setDataCollapsed((v) => !v), []);
|
|
564
|
+
const toggleErrorsPanel = useCallback(
|
|
565
|
+
() => setErrorsCollapsed((v) => !v),
|
|
566
|
+
[]
|
|
567
|
+
);
|
|
501
568
|
const loadSchemaPreset = useCallback((value) => {
|
|
502
569
|
if (!value || !sampleSchemas[value]) return;
|
|
503
570
|
const text = JSON.stringify(sampleSchemas[value], null, 2);
|
|
@@ -533,7 +600,10 @@ function usePlayFormsState() {
|
|
|
533
600
|
dataError,
|
|
534
601
|
activeTab,
|
|
535
602
|
deferStateUpdates,
|
|
603
|
+
validationDisplay,
|
|
536
604
|
dataCollapsed,
|
|
605
|
+
errorsCollapsed,
|
|
606
|
+
injectServerError,
|
|
537
607
|
lastDiff,
|
|
538
608
|
setSchemaText,
|
|
539
609
|
setUiSchemaText,
|
|
@@ -548,7 +618,10 @@ function usePlayFormsState() {
|
|
|
548
618
|
setDataError,
|
|
549
619
|
setActiveTab,
|
|
550
620
|
setDeferStateUpdates,
|
|
621
|
+
setValidationDisplay,
|
|
551
622
|
toggleDataPanel,
|
|
623
|
+
toggleErrorsPanel,
|
|
624
|
+
setInjectServerError,
|
|
552
625
|
setLastDiff,
|
|
553
626
|
loadSchemaPreset,
|
|
554
627
|
loadUiSchemaPreset
|
|
@@ -725,8 +798,37 @@ function SchemaEditorColumn({
|
|
|
725
798
|
}
|
|
726
799
|
);
|
|
727
800
|
}
|
|
801
|
+
var DEMO_SERVER_ERRORS = [
|
|
802
|
+
{ pointer: "/name", message: "Server: this name is already taken" },
|
|
803
|
+
{ pointer: "", message: "Server: could not save (example form-level error)" }
|
|
804
|
+
];
|
|
805
|
+
var DEMO_VALIDATORS = {
|
|
806
|
+
field: {
|
|
807
|
+
slug: (value) => /^[a-z0-9-]+$/.test(String(value ?? "")) ? null : "Lowercase letters, digits and hyphens only"
|
|
808
|
+
},
|
|
809
|
+
form: {
|
|
810
|
+
// Cross-field: confirmEmail must equal email. Error attaches to the
|
|
811
|
+
// confirmEmail field by pointer.
|
|
812
|
+
emailsMatch: (data) => data.email && data.confirmEmail && data.email !== data.confirmEmail ? [{ pointer: "/confirmEmail", message: "Emails do not match" }] : []
|
|
813
|
+
},
|
|
814
|
+
// Declared in the `asyncField` namespace so the engine knows it is async and
|
|
815
|
+
// fires it only on value change. Demo: a simulated remote uniqueness check
|
|
816
|
+
// (200ms) where "taken" is unavailable.
|
|
817
|
+
asyncField: {
|
|
818
|
+
uniqueHandle: async (value) => {
|
|
819
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
820
|
+
return String(value ?? "") === "taken" ? "Handle is already taken (async check)" : null;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
var VALIDATION_DISPLAY_OPTIONS = [
|
|
825
|
+
{ value: "onSubmit", label: "Errors: on submit" },
|
|
826
|
+
{ value: "onTouched", label: "Errors: on blur" },
|
|
827
|
+
{ value: "always", label: "Errors: always" }
|
|
828
|
+
];
|
|
728
829
|
var SCHEMA_PRESET_OPTIONS = [
|
|
729
830
|
{ value: "flat", label: "Practitioner (flat fields)" },
|
|
831
|
+
{ value: "validation", label: "Validation showcase (built-in + custom)" },
|
|
730
832
|
{ value: "nested_object", label: "Patient (nested object)" },
|
|
731
833
|
{ value: "one_of", label: "Payment (oneOf variant)" },
|
|
732
834
|
{ value: "array_of_objects", label: "Medication Request (array)" },
|
|
@@ -791,9 +893,9 @@ function FormColumn({
|
|
|
791
893
|
flexDirection: "column"
|
|
792
894
|
},
|
|
793
895
|
children: [
|
|
794
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
795
|
-
/* @__PURE__ */
|
|
796
|
-
|
|
896
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
897
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
898
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "Rendered Form" }),
|
|
797
899
|
/* @__PURE__ */ jsx(
|
|
798
900
|
Badge,
|
|
799
901
|
{
|
|
@@ -801,21 +903,53 @@ function FormColumn({
|
|
|
801
903
|
className: "text-xs",
|
|
802
904
|
children: state.isValid ? "Valid" : "Invalid"
|
|
803
905
|
}
|
|
804
|
-
),
|
|
805
|
-
/* @__PURE__ */ jsx(
|
|
806
|
-
FormCheckbox,
|
|
807
|
-
{
|
|
808
|
-
label: "Defer state updates (change on blur)",
|
|
809
|
-
checked: state.deferStateUpdates,
|
|
810
|
-
onChange: (checked) => {
|
|
811
|
-
if (checked !== "indeterminate") {
|
|
812
|
-
state.setDeferStateUpdates(checked);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
906
|
)
|
|
817
907
|
] }),
|
|
818
|
-
|
|
908
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border p-2", children: [
|
|
909
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-1.5", children: "Options" }),
|
|
910
|
+
/* @__PURE__ */ jsxs(SimpleGroup, { gap: "md", align: "center", wrap: "wrap", children: [
|
|
911
|
+
/* @__PURE__ */ jsx(
|
|
912
|
+
FormSelect,
|
|
913
|
+
{
|
|
914
|
+
label: false,
|
|
915
|
+
"aria-label": "Validation display timing",
|
|
916
|
+
options: VALIDATION_DISPLAY_OPTIONS,
|
|
917
|
+
value: state.validationDisplay,
|
|
918
|
+
onChange: (value) => {
|
|
919
|
+
if (value) {
|
|
920
|
+
state.setValidationDisplay(value);
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
className: "text-xs",
|
|
924
|
+
wrapperClassName: "w-44"
|
|
925
|
+
}
|
|
926
|
+
),
|
|
927
|
+
/* @__PURE__ */ jsx(
|
|
928
|
+
FormCheckbox,
|
|
929
|
+
{
|
|
930
|
+
label: "Change on blur",
|
|
931
|
+
checked: state.deferStateUpdates,
|
|
932
|
+
onChange: (checked) => {
|
|
933
|
+
if (checked !== "indeterminate") {
|
|
934
|
+
state.setDeferStateUpdates(checked);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
),
|
|
939
|
+
/* @__PURE__ */ jsx(
|
|
940
|
+
FormCheckbox,
|
|
941
|
+
{
|
|
942
|
+
label: "Inject server error",
|
|
943
|
+
checked: state.injectServerError,
|
|
944
|
+
onChange: (checked) => {
|
|
945
|
+
if (checked !== "indeterminate") {
|
|
946
|
+
state.setInjectServerError(checked);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
)
|
|
951
|
+
] })
|
|
952
|
+
] })
|
|
819
953
|
] }),
|
|
820
954
|
/* @__PURE__ */ jsxs(
|
|
821
955
|
"div",
|
|
@@ -836,7 +970,11 @@ function FormColumn({
|
|
|
836
970
|
onCancel: handleCancel,
|
|
837
971
|
onValidationChange: handleValidationChange,
|
|
838
972
|
showActions: true,
|
|
973
|
+
allowPristineSave: true,
|
|
839
974
|
deferStateUpdates: state.deferStateUpdates,
|
|
975
|
+
validationDisplay: state.validationDisplay,
|
|
976
|
+
validators: DEMO_VALIDATORS,
|
|
977
|
+
extraErrors: state.injectServerError ? DEMO_SERVER_ERRORS : void 0,
|
|
840
978
|
saveLabel: "Save Changes",
|
|
841
979
|
cancelLabel: "Reset",
|
|
842
980
|
showExtraProperties: true,
|
|
@@ -862,6 +1000,31 @@ function FormColumn({
|
|
|
862
1000
|
}
|
|
863
1001
|
);
|
|
864
1002
|
}
|
|
1003
|
+
function ErrorsCard({
|
|
1004
|
+
state
|
|
1005
|
+
}) {
|
|
1006
|
+
if (state.validationErrors.length === 0) return null;
|
|
1007
|
+
return /* @__PURE__ */ jsxs(Card, { className: "border shadow-sm shrink-0 flex flex-col overflow-hidden", children: [
|
|
1008
|
+
/* @__PURE__ */ jsxs(
|
|
1009
|
+
"button",
|
|
1010
|
+
{
|
|
1011
|
+
type: "button",
|
|
1012
|
+
onClick: state.toggleErrorsPanel,
|
|
1013
|
+
className: "flex items-center gap-2 px-4 py-3 text-left rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
1014
|
+
children: [
|
|
1015
|
+
state.errorsCollapsed ? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1016
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-destructive", children: [
|
|
1017
|
+
"Errors (",
|
|
1018
|
+
state.validationErrors.length,
|
|
1019
|
+
")"
|
|
1020
|
+
] }),
|
|
1021
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto text-[11px] font-medium text-muted-foreground", children: "via onValidationChange" })
|
|
1022
|
+
]
|
|
1023
|
+
}
|
|
1024
|
+
),
|
|
1025
|
+
!state.errorsCollapsed && /* @__PURE__ */ jsx("div", { className: "px-4 pb-4 space-y-0.5", children: state.validationErrors.map((err, idx) => /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: err }, idx)) })
|
|
1026
|
+
] });
|
|
1027
|
+
}
|
|
865
1028
|
function DataColumn({
|
|
866
1029
|
state
|
|
867
1030
|
}) {
|
|
@@ -881,6 +1044,7 @@ function DataColumn({
|
|
|
881
1044
|
transition: "flex 200ms ease"
|
|
882
1045
|
},
|
|
883
1046
|
children: [
|
|
1047
|
+
!state.dataCollapsed && /* @__PURE__ */ jsx(ErrorsCard, { state }),
|
|
884
1048
|
/* @__PURE__ */ jsxs(
|
|
885
1049
|
Card,
|
|
886
1050
|
{
|
|
@@ -974,4 +1138,4 @@ function PlayForms() {
|
|
|
974
1138
|
export {
|
|
975
1139
|
PlayForms
|
|
976
1140
|
};
|
|
977
|
-
//# sourceMappingURL=chunk-
|
|
1141
|
+
//# sourceMappingURL=chunk-BRH3FFFT.js.map
|