@petrarca/sonnet-playground 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.
Files changed (41) hide show
  1. package/dist/PlayColorPalette-UFMTJTUB.js +8 -0
  2. package/dist/PlayColorPalette-UFMTJTUB.js.map +1 -0
  3. package/dist/PlayCoreComponents-7QCDFQLT.js +7 -0
  4. package/dist/PlayCoreComponents-7QCDFQLT.js.map +1 -0
  5. package/dist/PlayDataComponents-GZSRTW4F.js +7 -0
  6. package/dist/PlayDataComponents-GZSRTW4F.js.map +1 -0
  7. package/dist/PlayFormComponents-RJB6WZHD.js +7 -0
  8. package/dist/PlayFormComponents-RJB6WZHD.js.map +1 -0
  9. package/dist/PlayFormWidgets-OFV7TJKH.js +7 -0
  10. package/dist/PlayFormWidgets-OFV7TJKH.js.map +1 -0
  11. package/dist/PlayForms-24XX5DC6.js +7 -0
  12. package/dist/PlayForms-24XX5DC6.js.map +1 -0
  13. package/dist/PlayJsonEditor-4L5INQPU.js +7 -0
  14. package/dist/PlayJsonEditor-4L5INQPU.js.map +1 -0
  15. package/dist/PlayTypography-ID6DLFZD.js +8 -0
  16. package/dist/PlayTypography-ID6DLFZD.js.map +1 -0
  17. package/dist/chunk-5V27QDAY.js +617 -0
  18. package/dist/chunk-5V27QDAY.js.map +1 -0
  19. package/dist/chunk-A3IK3CVW.js +212 -0
  20. package/dist/chunk-A3IK3CVW.js.map +1 -0
  21. package/dist/chunk-CYNK2P2Q.js +411 -0
  22. package/dist/chunk-CYNK2P2Q.js.map +1 -0
  23. package/dist/chunk-FTN2A5YL.js +20 -0
  24. package/dist/chunk-FTN2A5YL.js.map +1 -0
  25. package/dist/chunk-GYKEPHHA.js +1154 -0
  26. package/dist/chunk-GYKEPHHA.js.map +1 -0
  27. package/dist/chunk-KAT24CQQ.js +125 -0
  28. package/dist/chunk-KAT24CQQ.js.map +1 -0
  29. package/dist/chunk-P7I5IDXA.js +432 -0
  30. package/dist/chunk-P7I5IDXA.js.map +1 -0
  31. package/dist/chunk-QIETZUEN.js +885 -0
  32. package/dist/chunk-QIETZUEN.js.map +1 -0
  33. package/dist/chunk-TZGJH7YS.js +837 -0
  34. package/dist/chunk-TZGJH7YS.js.map +1 -0
  35. package/dist/index.d.ts +160 -0
  36. package/dist/index.js +328 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/pages/index.d.ts +69 -0
  39. package/dist/pages/index.js +36 -0
  40. package/dist/pages/index.js.map +1 -0
  41. package/package.json +54 -0
@@ -0,0 +1,837 @@
1
+ // src/pages/PlayDataComponents.tsx
2
+ import { useState, useMemo, useCallback } from "react";
3
+ import { ArrowRight, ArrowUp } from "lucide-react";
4
+ import {
5
+ Badge,
6
+ Button,
7
+ Card,
8
+ SimpleTooltip,
9
+ Tabs,
10
+ TabsContent,
11
+ TabsList,
12
+ TabsTrigger
13
+ } from "@petrarca/sonnet-ui";
14
+ import { FormTextarea } from "@petrarca/sonnet-forms";
15
+ import {
16
+ SearchInput,
17
+ EntitySelect,
18
+ EntityTable,
19
+ createRenderer,
20
+ EntityTree
21
+ } from "@petrarca/sonnet-ui";
22
+ import { JsonEditor } from "@petrarca/sonnet-ui/json-editor";
23
+ import {
24
+ EMPTY_FILTER,
25
+ flattenFilterExpr,
26
+ isEmptyFilterExpr,
27
+ serializeFilterExpr
28
+ } from "@petrarca/sonnet-core/search";
29
+ import { toJsonSchema } from "@petrarca/sonnet-core/schema";
30
+ import { z } from "zod/v4";
31
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
32
+ var MOCK_PATIENTS = [
33
+ { value: "pat_001", label: "Alice Hartmann" },
34
+ { value: "pat_002", label: "Bob Nguyen" },
35
+ { value: "pat_003", label: "Carol Smith" },
36
+ { value: "pat_004", label: "David Okafor" },
37
+ { value: "pat_005", label: "Eva Larsson" },
38
+ { value: "pat_006", label: "Frank Torres" },
39
+ { value: "pat_007", label: "Grace Kim" },
40
+ { value: "pat_008", label: "Henry Bauer" }
41
+ ];
42
+ var patientFetcher = ({ search } = {}) => {
43
+ const q = search?.toLowerCase() ?? "";
44
+ const results = q ? MOCK_PATIENTS.filter((p) => p.label.toLowerCase().includes(q)) : MOCK_PATIENTS;
45
+ return Promise.resolve(results);
46
+ };
47
+ var ALL_ROWS = [
48
+ {
49
+ id: "u-001",
50
+ name: "Alice Hartmann",
51
+ status: "active",
52
+ role: "admin",
53
+ joined: "2024-01-15"
54
+ },
55
+ {
56
+ id: "u-002",
57
+ name: "Bob Nguyen",
58
+ status: "active",
59
+ role: "editor",
60
+ joined: "2024-02-03"
61
+ },
62
+ {
63
+ id: "u-003",
64
+ name: "Carol Smith",
65
+ status: "inactive",
66
+ role: "viewer",
67
+ joined: "2024-03-20"
68
+ },
69
+ {
70
+ id: "u-004",
71
+ name: "David Okafor",
72
+ status: "pending",
73
+ role: "editor",
74
+ joined: "2024-04-11"
75
+ },
76
+ {
77
+ id: "u-005",
78
+ name: "Eva Larsson",
79
+ status: "active",
80
+ role: "admin",
81
+ joined: "2024-05-08"
82
+ },
83
+ {
84
+ id: "u-006",
85
+ name: "Frank Torres",
86
+ status: "inactive",
87
+ role: "viewer",
88
+ joined: "2024-06-22"
89
+ },
90
+ {
91
+ id: "u-007",
92
+ name: "Grace Kim",
93
+ status: "active",
94
+ role: "editor",
95
+ joined: "2024-07-14"
96
+ },
97
+ {
98
+ id: "u-008",
99
+ name: "Henry Bauer",
100
+ status: "pending",
101
+ role: "viewer",
102
+ joined: "2024-08-30"
103
+ }
104
+ ];
105
+ var STATUS_COLOR = {
106
+ active: "green",
107
+ inactive: "violet",
108
+ pending: "yellow"
109
+ };
110
+ var PlaygroundFacets = z.object({
111
+ search: z.string().meta({ "x-ui-title": "Name" }),
112
+ status: z.enum(["active", "inactive", "pending"]).optional().meta({
113
+ "x-ui-title": "Status",
114
+ "x-ui-options": {
115
+ enumNames: {
116
+ active: "Active",
117
+ inactive: "Inactive",
118
+ pending: "Pending"
119
+ }
120
+ }
121
+ }),
122
+ role: z.string().optional().meta({ "x-ui-title": "Role" })
123
+ });
124
+ var SEARCH_FACET_SCHEMA = toJsonSchema(PlaygroundFacets);
125
+ var SAMPLE_SCHEMA = {
126
+ title: "User",
127
+ type: "object",
128
+ properties: {
129
+ name: {
130
+ type: "string",
131
+ title: "Name",
132
+ "x-ui-column": { order: 0 },
133
+ "x-ui-cell": "bold"
134
+ },
135
+ status: {
136
+ type: "string",
137
+ title: "Status",
138
+ "x-ui-column": { order: 1 },
139
+ "x-ui-cell": "badge",
140
+ "x-ui-cell-options": { colors: STATUS_COLOR }
141
+ },
142
+ role: {
143
+ type: "string",
144
+ title: "Role",
145
+ "x-ui-column": { order: 2 },
146
+ "x-ui-cell": "muted"
147
+ },
148
+ id: {
149
+ type: "string",
150
+ title: "ID",
151
+ "x-ui-column": { order: 3 },
152
+ "x-ui-cell": "code"
153
+ },
154
+ joined: {
155
+ type: "string",
156
+ format: "date",
157
+ title: "Joined",
158
+ "x-ui-column": { order: 4 }
159
+ }
160
+ }
161
+ };
162
+ var SAMPLE_UI_SCHEMA = {};
163
+ var ROLE_ICONS = {
164
+ admin: "A",
165
+ editor: "E",
166
+ viewer: "V"
167
+ };
168
+ var CUSTOM_RENDERERS = {
169
+ // Extract initials from the name, display as avatar circle + name.
170
+ "x-play-initials": createRenderer((row) => {
171
+ const initials = row.name.split(" ").map((w) => w[0]).join("");
172
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
173
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center h-7 w-7 rounded-full bg-muted text-xs font-medium shrink-0", children: initials }),
174
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: row.name })
175
+ ] });
176
+ }),
177
+ // Role with a letter prefix read from options.icons.
178
+ "x-play-role-icon": createRenderer((row, options) => {
179
+ const icons = options.icons ?? {};
180
+ const icon = icons[row.role] ?? "?";
181
+ return /* @__PURE__ */ jsxs("span", { children: [
182
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center h-5 w-5 rounded bg-muted text-xs font-mono mr-1.5", children: icon }),
183
+ row.role
184
+ ] });
185
+ }),
186
+ // Compute days since joined, color-coded by threshold from options.
187
+ "x-play-tenure": createRenderer((row, options) => {
188
+ const threshold = options.recentDays ?? 365;
189
+ const days = Math.floor(
190
+ (Date.now() - new Date(row.joined).getTime()) / 864e5
191
+ );
192
+ const isRecent = days < threshold;
193
+ return /* @__PURE__ */ jsxs("span", { className: isRecent ? "text-blue-600" : "text-muted-foreground", children: [
194
+ days,
195
+ "d"
196
+ ] });
197
+ })
198
+ };
199
+ var CUSTOM_RENDERER_SCHEMA = {
200
+ title: "User (custom renderers)",
201
+ type: "object",
202
+ properties: {
203
+ name: {
204
+ type: "string",
205
+ title: "Name",
206
+ "x-ui-column": { order: 0 },
207
+ "x-ui-cell": "x-play-initials"
208
+ },
209
+ role: {
210
+ type: "string",
211
+ title: "Role",
212
+ "x-ui-column": { order: 1 },
213
+ "x-ui-cell": "x-play-role-icon",
214
+ "x-ui-cell-options": { icons: ROLE_ICONS }
215
+ },
216
+ status: {
217
+ type: "string",
218
+ title: "Status",
219
+ "x-ui-column": { order: 2 },
220
+ "x-ui-cell": "badge",
221
+ "x-ui-cell-options": { colors: STATUS_COLOR }
222
+ },
223
+ joined: {
224
+ type: "string",
225
+ title: "Tenure",
226
+ "x-ui-column": { order: 3 },
227
+ "x-ui-cell": "x-play-tenure",
228
+ "x-ui-cell-options": { recentDays: 500 }
229
+ },
230
+ id: {
231
+ type: "string",
232
+ title: "ID",
233
+ "x-ui-column": { order: 4 },
234
+ "x-ui-cell": "code"
235
+ }
236
+ }
237
+ };
238
+ var SEARCH_FIELD_PRESETS = [
239
+ { label: "name, role", fields: ["name", "role"] },
240
+ { label: "name (single)", fields: ["name"] },
241
+ { label: "name, role, status", fields: ["name", "role", "status"] }
242
+ ];
243
+ function ToggleStrip({
244
+ label,
245
+ options,
246
+ value,
247
+ onChange,
248
+ format,
249
+ containerClassName
250
+ }) {
251
+ const fmt = format ?? String;
252
+ return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 ${containerClassName ?? ""}`, children: [
253
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
254
+ label,
255
+ ":"
256
+ ] }),
257
+ options.map((opt) => /* @__PURE__ */ jsx(
258
+ "button",
259
+ {
260
+ type: "button",
261
+ onClick: () => onChange(opt),
262
+ className: [
263
+ "px-3 py-1 rounded-full text-xs font-medium transition-colors",
264
+ value === opt ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
265
+ ].join(" "),
266
+ children: fmt(opt)
267
+ },
268
+ String(opt)
269
+ ))
270
+ ] });
271
+ }
272
+ var MODE_OPTIONS = ["explicit", "debounce"];
273
+ var MODE_LABELS = {
274
+ explicit: "Explicit (Enter)",
275
+ debounce: "Debounce (300 ms)"
276
+ };
277
+ var MIN_CHARS_OPTIONS = [0, 1, 2, 3];
278
+ var FIELD_PRESET_INDICES = SEARCH_FIELD_PRESETS.map(
279
+ (_, i) => i
280
+ );
281
+ function FilterExprInspector({ expr }) {
282
+ return /* @__PURE__ */ jsx("pre", { className: "mt-3 rounded-md bg-muted px-4 py-3 text-xs overflow-auto max-h-40", children: JSON.stringify(expr, null, 2) });
283
+ }
284
+ function FlatTermsInspector({ expr }) {
285
+ const terms = flattenFilterExpr(expr);
286
+ return /* @__PURE__ */ jsx("pre", { className: "mt-3 rounded-md bg-muted px-4 py-3 text-xs overflow-auto max-h-40", children: JSON.stringify(terms, null, 2) });
287
+ }
288
+ function SectionHeader({
289
+ title,
290
+ description
291
+ }) {
292
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
293
+ /* @__PURE__ */ jsx("h2", { className: "heading-section mb-1", children: title }),
294
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
295
+ ] });
296
+ }
297
+ function PlayDataComponents() {
298
+ const [simpleExpr, setSimpleExpr] = useState(EMPTY_FILTER);
299
+ const [simpleMode, setSimpleMode] = useState("explicit");
300
+ const [simpleMinChars, setSimpleMinChars] = useState(0);
301
+ const [fieldsExpr, setFieldsExpr] = useState(EMPTY_FILTER);
302
+ const [fieldsMode, setFieldsMode] = useState("explicit");
303
+ const [fieldsMinChars, setFieldsMinChars] = useState(0);
304
+ const [fieldsPresetIdx, setFieldsPresetIdx] = useState(0);
305
+ const activeSearchFields = SEARCH_FIELD_PRESETS[fieldsPresetIdx].fields;
306
+ const [facetedExpr, setFacetedExpr] = useState(EMPTY_FILTER);
307
+ const [facetedSubmitCount, setFacetedSubmitCount] = useState(0);
308
+ const handleFacetedChange = useCallback((expr) => {
309
+ setFacetedExpr(expr);
310
+ setFacetedSubmitCount((n) => n + 1);
311
+ }, []);
312
+ const [schemaText, setSchemaText] = useState(
313
+ () => JSON.stringify(toJsonSchema(PlaygroundFacets), null, 2)
314
+ );
315
+ const [schemaError, setSchemaError] = useState("");
316
+ const [activeSchema, setActiveSchema] = useState(SEARCH_FACET_SCHEMA);
317
+ const handleSchemaTextChange = useCallback((value) => {
318
+ setSchemaText(value);
319
+ try {
320
+ JSON.parse(value);
321
+ setSchemaError("");
322
+ } catch {
323
+ setSchemaError("Invalid JSON");
324
+ }
325
+ }, []);
326
+ const handleLoadSchema = useCallback(() => {
327
+ try {
328
+ const parsed = JSON.parse(schemaText);
329
+ setActiveSchema(parsed);
330
+ setFacetedExpr(EMPTY_FILTER);
331
+ setFacetedSubmitCount(-1);
332
+ setSchemaError("");
333
+ } catch {
334
+ setSchemaError("Invalid JSON -- cannot load");
335
+ }
336
+ }, [schemaText]);
337
+ const [tableSchemaText, setTableSchemaText] = useState(
338
+ () => JSON.stringify(SAMPLE_SCHEMA, null, 2)
339
+ );
340
+ const [tableUiSchemaText, setTableUiSchemaText] = useState(
341
+ () => JSON.stringify(SAMPLE_UI_SCHEMA, null, 2)
342
+ );
343
+ const [tableSchemaError, setTableSchemaError] = useState("");
344
+ const [activeTableSchema, setActiveTableSchema] = useState(SAMPLE_SCHEMA);
345
+ const [activeTableUiSchema, setActiveTableUiSchema] = useState(SAMPLE_UI_SCHEMA);
346
+ const [tableSchemaTab, setTableSchemaTab] = useState("schema");
347
+ const handleTableSchemaTextChange = useCallback((value) => {
348
+ setTableSchemaText(value);
349
+ try {
350
+ JSON.parse(value);
351
+ setTableSchemaError("");
352
+ } catch {
353
+ setTableSchemaError("Invalid JSON");
354
+ }
355
+ }, []);
356
+ const handleTableUiSchemaTextChange = useCallback((value) => {
357
+ setTableUiSchemaText(value);
358
+ try {
359
+ JSON.parse(value);
360
+ setTableSchemaError("");
361
+ } catch {
362
+ setTableSchemaError("Invalid JSON");
363
+ }
364
+ }, []);
365
+ const handleApplyTableSchema = useCallback(() => {
366
+ try {
367
+ const schema = JSON.parse(tableSchemaText);
368
+ const uiSchema = JSON.parse(tableUiSchemaText);
369
+ setActiveTableSchema(schema);
370
+ setActiveTableUiSchema(uiSchema);
371
+ setTableSchemaError("");
372
+ } catch {
373
+ setTableSchemaError("Invalid JSON -- cannot apply");
374
+ }
375
+ }, [tableSchemaText, tableUiSchemaText]);
376
+ const [tableExpr, setTableExpr] = useState(EMPTY_FILTER);
377
+ const filteredRows = useMemo(() => {
378
+ const terms = flattenFilterExpr(tableExpr);
379
+ if (terms.length === 0) return ALL_ROWS;
380
+ return ALL_ROWS.filter(
381
+ (row) => terms.every(({ key, value }) => {
382
+ const v = value.toLowerCase();
383
+ if (key === "status") return row.status.toLowerCase().includes(v);
384
+ if (key === "role") return row.role.toLowerCase().includes(v);
385
+ return row.name.toLowerCase().includes(v);
386
+ })
387
+ );
388
+ }, [tableExpr]);
389
+ const tableData = useMemo(
390
+ () => ({
391
+ data: filteredRows,
392
+ meta: {
393
+ total: filteredRows.length,
394
+ limit: filteredRows.length,
395
+ offset: 0,
396
+ has_next: false,
397
+ has_prev: false
398
+ }
399
+ }),
400
+ [filteredRows]
401
+ );
402
+ const [selectedPatient, setSelectedPatient] = useState(null);
403
+ return /* @__PURE__ */ jsxs("div", { className: "w-full py-12 px-4 max-w-4xl", children: [
404
+ /* @__PURE__ */ jsx("h1", { className: "mb-1", children: "Data Components" }),
405
+ /* @__PURE__ */ jsx("p", { className: "text-lg text-muted-foreground mb-10", children: "Interactive sandbox for SearchInput, EntitySelect, and EntityTable." }),
406
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
407
+ /* @__PURE__ */ jsx(
408
+ SectionHeader,
409
+ {
410
+ title: "SearchInput \u2014 simple mode",
411
+ description: "No fields prop. Produces a single search term under the key 'search'."
412
+ }
413
+ ),
414
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-4 mb-3", children: [
415
+ /* @__PURE__ */ jsx(
416
+ ToggleStrip,
417
+ {
418
+ label: "Mode",
419
+ options: MODE_OPTIONS,
420
+ value: simpleMode,
421
+ onChange: (m) => {
422
+ setSimpleMode(m);
423
+ setSimpleExpr(EMPTY_FILTER);
424
+ },
425
+ format: (m) => MODE_LABELS[m]
426
+ }
427
+ ),
428
+ /* @__PURE__ */ jsx(
429
+ ToggleStrip,
430
+ {
431
+ label: "Min chars",
432
+ options: MIN_CHARS_OPTIONS,
433
+ value: simpleMinChars,
434
+ onChange: setSimpleMinChars,
435
+ format: (n) => n === 0 ? "off" : String(n)
436
+ }
437
+ )
438
+ ] }),
439
+ /* @__PURE__ */ jsx(
440
+ SearchInput,
441
+ {
442
+ value: simpleExpr,
443
+ onChange: setSimpleExpr,
444
+ mode: simpleMode,
445
+ minChars: simpleMinChars,
446
+ placeholder: "Search...",
447
+ className: "max-w-sm"
448
+ }
449
+ ),
450
+ /* @__PURE__ */ jsx(FlatTermsInspector, { expr: simpleExpr })
451
+ ] }),
452
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
453
+ /* @__PURE__ */ jsx(
454
+ SectionHeader,
455
+ {
456
+ title: "SearchInput \u2014 searchFields (multi-field OR)",
457
+ description: "Simple mode with searchFields prop. Typing text produces a FilterOr with one term per field. buildListParams serializes it as a q= expression for the server. Toggle field sets, mode, and minChars to explore different behaviours."
458
+ }
459
+ ),
460
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-4 mb-2", children: /* @__PURE__ */ jsx(
461
+ ToggleStrip,
462
+ {
463
+ label: "Fields",
464
+ options: FIELD_PRESET_INDICES,
465
+ value: fieldsPresetIdx,
466
+ onChange: (idx) => {
467
+ setFieldsPresetIdx(idx);
468
+ setFieldsExpr(EMPTY_FILTER);
469
+ },
470
+ format: (idx) => SEARCH_FIELD_PRESETS[idx].label
471
+ }
472
+ ) }),
473
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-4 mb-3", children: [
474
+ /* @__PURE__ */ jsx(
475
+ ToggleStrip,
476
+ {
477
+ label: "Mode",
478
+ options: MODE_OPTIONS,
479
+ value: fieldsMode,
480
+ onChange: (m) => {
481
+ setFieldsMode(m);
482
+ setFieldsExpr(EMPTY_FILTER);
483
+ },
484
+ format: (m) => MODE_LABELS[m]
485
+ }
486
+ ),
487
+ /* @__PURE__ */ jsx(
488
+ ToggleStrip,
489
+ {
490
+ label: "Min chars",
491
+ options: MIN_CHARS_OPTIONS,
492
+ value: fieldsMinChars,
493
+ onChange: setFieldsMinChars,
494
+ format: (n) => n === 0 ? "off" : String(n)
495
+ }
496
+ )
497
+ ] }),
498
+ /* @__PURE__ */ jsx(
499
+ SearchInput,
500
+ {
501
+ value: fieldsExpr,
502
+ onChange: setFieldsExpr,
503
+ searchFields: activeSearchFields,
504
+ mode: fieldsMode,
505
+ minChars: fieldsMinChars,
506
+ placeholder: `Search by ${activeSearchFields.join(" or ")}...`,
507
+ className: "max-w-sm"
508
+ }
509
+ ),
510
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-2", children: [
511
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
512
+ "searchFields: [",
513
+ activeSearchFields.map((f) => `"${f}"`).join(", "),
514
+ "]"
515
+ ] }),
516
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "FilterExpr AST:" }),
517
+ /* @__PURE__ */ jsx(FilterExprInspector, { expr: fieldsExpr }),
518
+ !isEmptyFilterExpr(fieldsExpr) && /* @__PURE__ */ jsxs(Fragment, { children: [
519
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Serialized q= string:" }),
520
+ /* @__PURE__ */ jsx("pre", { className: "rounded-md bg-muted px-4 py-3 text-xs overflow-auto", children: serializeFilterExpr(fieldsExpr) })
521
+ ] })
522
+ ] })
523
+ ] }),
524
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
525
+ /* @__PURE__ */ jsx(
526
+ SectionHeader,
527
+ {
528
+ title: "SearchInput \u2014 faceted mode",
529
+ description: "Edit the JSON Schema on the left and load it into the search input. Add chips by picking a field and typing a value. Press Enter on empty input to fire onChange."
530
+ }
531
+ ),
532
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4 items-start", children: [
533
+ /* @__PURE__ */ jsxs(Card, { className: "flex-1 min-w-0 p-3 flex flex-col gap-2", children: [
534
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "JSON Schema" }),
535
+ schemaError && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs self-start", children: schemaError }),
536
+ /* @__PURE__ */ jsx(
537
+ FormTextarea,
538
+ {
539
+ value: schemaText,
540
+ onChange: (e) => handleSchemaTextChange(e.target.value),
541
+ className: "font-mono text-xs",
542
+ rows: 18
543
+ }
544
+ )
545
+ ] }),
546
+ /* @__PURE__ */ jsx("div", { className: "flex items-center self-center", children: /* @__PURE__ */ jsx(SimpleTooltip, { label: "Load schema into search", children: /* @__PURE__ */ jsx(
547
+ Button,
548
+ {
549
+ variant: "default",
550
+ size: "icon",
551
+ onClick: handleLoadSchema,
552
+ disabled: !!schemaError,
553
+ children: /* @__PURE__ */ jsx(ArrowRight, { size: 18 })
554
+ }
555
+ ) }) }),
556
+ /* @__PURE__ */ jsxs(Card, { className: "flex-1 min-w-0 p-3 flex flex-col gap-3", children: [
557
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Live search" }),
558
+ /* @__PURE__ */ jsx(
559
+ SearchInput,
560
+ {
561
+ value: facetedExpr,
562
+ onChange: handleFacetedChange,
563
+ schema: activeSchema,
564
+ mode: "explicit",
565
+ placeholder: "Add filter..."
566
+ }
567
+ ),
568
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
569
+ /* @__PURE__ */ jsx("span", { children: "onChange fired:" }),
570
+ /* @__PURE__ */ jsxs("span", { className: "font-mono font-medium text-foreground", children: [
571
+ facetedSubmitCount,
572
+ "x"
573
+ ] })
574
+ ] }),
575
+ /* @__PURE__ */ jsx(FilterExprInspector, { expr: facetedExpr })
576
+ ] })
577
+ ] })
578
+ ] }),
579
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
580
+ /* @__PURE__ */ jsx(
581
+ SectionHeader,
582
+ {
583
+ title: "EntityTable with SearchInput",
584
+ description: "Faceted search wired to EntityTable. onChange fires only on submit (Enter on empty input) -- table filters when the user presses Enter. Edit the JSON Schema or UI Schema below and press Apply to see column changes live."
585
+ }
586
+ ),
587
+ /* @__PURE__ */ jsx(
588
+ SearchInput,
589
+ {
590
+ value: tableExpr,
591
+ onChange: setTableExpr,
592
+ schema: SEARCH_FACET_SCHEMA,
593
+ mode: "explicit",
594
+ placeholder: "Filter users... (press Enter to apply)",
595
+ className: "mb-4 max-w-sm"
596
+ }
597
+ ),
598
+ /* @__PURE__ */ jsx(
599
+ EntityTable,
600
+ {
601
+ schema: activeTableSchema,
602
+ uiSchema: activeTableUiSchema,
603
+ data: tableData,
604
+ isLoading: false,
605
+ isError: false,
606
+ entityLabel: "users",
607
+ isFiltered: !isEmptyFilterExpr(tableExpr)
608
+ }
609
+ ),
610
+ /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Card, { className: "border shadow-sm p-4 flex flex-col", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxs(
611
+ Tabs,
612
+ {
613
+ value: tableSchemaTab,
614
+ onValueChange: setTableSchemaTab,
615
+ className: "flex flex-col flex-1",
616
+ children: [
617
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
618
+ /* @__PURE__ */ jsxs(TabsList, { children: [
619
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "schema", children: "JSON Schema" }),
620
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "uischema", children: "UI Schema" })
621
+ ] }),
622
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
623
+ tableSchemaError && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: tableSchemaError }),
624
+ /* @__PURE__ */ jsx(SimpleTooltip, { label: "Apply schema to table above", children: /* @__PURE__ */ jsx(
625
+ Button,
626
+ {
627
+ variant: "default",
628
+ size: "icon",
629
+ onClick: handleApplyTableSchema,
630
+ disabled: !!tableSchemaError,
631
+ children: /* @__PURE__ */ jsx(ArrowUp, { size: 18 })
632
+ }
633
+ ) })
634
+ ] })
635
+ ] }),
636
+ /* @__PURE__ */ jsx(TabsContent, { value: "schema", className: "mt-2", children: /* @__PURE__ */ jsx(
637
+ JsonEditor,
638
+ {
639
+ value: tableSchemaText,
640
+ editablePaths: ["/"],
641
+ onChange: handleTableSchemaTextChange,
642
+ height: "280px",
643
+ className: "rounded-md border overflow-hidden"
644
+ }
645
+ ) }),
646
+ /* @__PURE__ */ jsx(TabsContent, { value: "uischema", className: "mt-2", children: /* @__PURE__ */ jsx(
647
+ JsonEditor,
648
+ {
649
+ value: tableUiSchemaText,
650
+ editablePaths: ["/"],
651
+ onChange: handleTableUiSchemaTextChange,
652
+ height: "280px",
653
+ className: "rounded-md border overflow-hidden"
654
+ }
655
+ ) })
656
+ ]
657
+ }
658
+ ) }) }) })
659
+ ] }),
660
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
661
+ /* @__PURE__ */ jsx(
662
+ SectionHeader,
663
+ {
664
+ title: "EntityTable with Local Renderers",
665
+ description: "Same sample data rendered with x- prefixed local renderers passed via the renderers prop. The schema is pure data -- renderer functions live in code and are resolved by name. Three custom renderers: x-play-initials (avatar from name), x-play-role-icon (letter prefix from options), x-play-tenure (computed days with threshold)."
666
+ }
667
+ ),
668
+ /* @__PURE__ */ jsx(
669
+ EntityTable,
670
+ {
671
+ schema: CUSTOM_RENDERER_SCHEMA,
672
+ renderers: CUSTOM_RENDERERS,
673
+ data: {
674
+ data: ALL_ROWS,
675
+ meta: {
676
+ total: ALL_ROWS.length,
677
+ limit: ALL_ROWS.length,
678
+ offset: 0,
679
+ has_next: false,
680
+ has_prev: false
681
+ }
682
+ },
683
+ isLoading: false,
684
+ isError: false,
685
+ entityLabel: "users"
686
+ }
687
+ )
688
+ ] }),
689
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
690
+ /* @__PURE__ */ jsx(
691
+ SectionHeader,
692
+ {
693
+ title: "EntitySelect",
694
+ description: "Searchable single-select backed by a static mock patient fetcher. Search text is filtered client-side. Available as a form widget via x-ui-widget: 'entity-select'."
695
+ }
696
+ ),
697
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-6 items-start", children: [
698
+ /* @__PURE__ */ jsx(
699
+ EntitySelect,
700
+ {
701
+ fetcher: patientFetcher,
702
+ queryKey: "playground-patients",
703
+ value: selectedPatient,
704
+ onChange: setSelectedPatient,
705
+ label: "Patient",
706
+ placeholder: "Select patient...",
707
+ searchPlaceholder: "Search patients...",
708
+ className: "max-w-xs"
709
+ }
710
+ ),
711
+ /* @__PURE__ */ jsxs(Card, { className: "flex-1 min-w-0 p-3", children: [
712
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Selected value" }),
713
+ /* @__PURE__ */ jsx("pre", { className: "rounded-md bg-muted px-4 py-3 text-xs overflow-auto", children: JSON.stringify(selectedPatient, null, 2) })
714
+ ] })
715
+ ] })
716
+ ] }),
717
+ /* @__PURE__ */ jsxs("section", { className: "mb-12", children: [
718
+ /* @__PURE__ */ jsx(
719
+ SectionHeader,
720
+ {
721
+ title: "EntityTree",
722
+ description: "Schema-driven tree component built on TreeView. Columns are resolved from a JSON Schema using the same resolveColumns pipeline as EntityTable. Supports expand/collapse, row selection, loading/error/empty states, and custom cell renderers."
723
+ }
724
+ ),
725
+ /* @__PURE__ */ jsx(EntityTreeDemo, {})
726
+ ] })
727
+ ] });
728
+ }
729
+ var OrgSchema = z.object({
730
+ name: z.string().meta({ title: "Name", "x-ui-column": { order: 0 } }),
731
+ type: z.string().meta({ title: "Type", "x-ui-column": { order: 1 } }),
732
+ headcount: z.number().meta({ title: "Headcount", "x-ui-column": { order: 2 } })
733
+ });
734
+ var ORG_TREE_SCHEMA = toJsonSchema(OrgSchema);
735
+ var ORG_NODES = [
736
+ {
737
+ id: "eng",
738
+ data: { name: "Engineering", type: "Division", headcount: 120 },
739
+ hasChildren: true,
740
+ children: [
741
+ {
742
+ id: "frontend",
743
+ data: { name: "Frontend", type: "Team", headcount: 18 },
744
+ hasChildren: true,
745
+ children: [
746
+ {
747
+ id: "design-sys",
748
+ data: { name: "Design System", type: "Squad", headcount: 5 },
749
+ hasChildren: false
750
+ }
751
+ ]
752
+ },
753
+ {
754
+ id: "backend",
755
+ data: { name: "Backend", type: "Team", headcount: 32 },
756
+ hasChildren: false
757
+ },
758
+ {
759
+ id: "infra",
760
+ data: { name: "Infrastructure", type: "Team", headcount: 14 },
761
+ hasChildren: false
762
+ }
763
+ ]
764
+ },
765
+ {
766
+ id: "product",
767
+ data: { name: "Product", type: "Division", headcount: 45 },
768
+ hasChildren: true,
769
+ children: [
770
+ {
771
+ id: "research",
772
+ data: { name: "Research", type: "Team", headcount: 8 },
773
+ hasChildren: false
774
+ },
775
+ {
776
+ id: "pm",
777
+ data: { name: "Product Management", type: "Team", headcount: 12 },
778
+ hasChildren: false
779
+ }
780
+ ]
781
+ },
782
+ {
783
+ id: "ops",
784
+ data: { name: "Operations", type: "Division", headcount: 30 },
785
+ hasChildren: false
786
+ }
787
+ ];
788
+ function EntityTreeDemo() {
789
+ const [expandedIds, setExpandedIds] = useState(/* @__PURE__ */ new Set());
790
+ const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
791
+ const [activeId, setActiveId] = useState(null);
792
+ const handleExpand = (id) => setExpandedIds((prev) => /* @__PURE__ */ new Set([...prev, id]));
793
+ const handleCollapse = (id) => setExpandedIds((prev) => {
794
+ const next = new Set(prev);
795
+ next.delete(id);
796
+ return next;
797
+ });
798
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
799
+ /* @__PURE__ */ jsx(
800
+ EntityTree,
801
+ {
802
+ schema: ORG_TREE_SCHEMA,
803
+ nodes: ORG_NODES,
804
+ expandedIds,
805
+ onExpand: handleExpand,
806
+ onCollapse: handleCollapse,
807
+ onNodeSelect: (node) => setActiveId(node.id),
808
+ onNodeEdit: (node) => setActiveId(node.id),
809
+ activeNodeId: activeId ?? void 0,
810
+ selectedIds,
811
+ onSelectionChange: (id, selected) => setSelectedIds((prev) => {
812
+ const next = new Set(prev);
813
+ if (selected) next.add(id);
814
+ else next.delete(id);
815
+ return next;
816
+ }),
817
+ entityLabel: "departments"
818
+ }
819
+ ),
820
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-6 text-xs text-muted-foreground", children: [
821
+ activeId && /* @__PURE__ */ jsxs("span", { children: [
822
+ "Active: ",
823
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: activeId })
824
+ ] }),
825
+ selectedIds.size > 0 && /* @__PURE__ */ jsxs("span", { children: [
826
+ "Selected:",
827
+ " ",
828
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: [...selectedIds].join(", ") })
829
+ ] })
830
+ ] })
831
+ ] });
832
+ }
833
+
834
+ export {
835
+ PlayDataComponents
836
+ };
837
+ //# sourceMappingURL=chunk-TZGJH7YS.js.map