@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 @@
1
+ {"version":3,"sources":["../src/pages/PlayDataComponents.tsx"],"sourcesContent":["/**\n * Playground page for SearchInput, EntitySelect, EntityTable, and EntityTree.\n * Accessible at /playground/data-components\n *\n * Six sections:\n * 1. Simple mode -- single freetext search, no field picker\n * 2. Faceted mode -- two-column: schema editor left, live search + output right\n * 3. EntityTable -- static sample data wired to faceted SearchInput\n * 4. Local renderers -- same data with x- prefixed custom cell renderers\n * 5. EntitySelect -- async searchable select backed by a static mock fetcher\n * 6. EntityTree -- schema-driven tree with static nested data\n */\n\nimport React, { useState, useMemo, useCallback } from \"react\";\nimport { ArrowRight, ArrowUp } from \"lucide-react\";\nimport {\n Badge,\n Button,\n Card,\n SimpleTooltip,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from \"@petrarca/sonnet-ui\";\nimport { FormTextarea } from \"@petrarca/sonnet-forms\";\nimport {\n SearchInput,\n type SearchMode,\n EntitySelect,\n EntityTable,\n createRenderer,\n EntityTree,\n type TreeNode,\n type TableUiSchema,\n} from \"@petrarca/sonnet-ui\";\nimport { JsonEditor } from \"@petrarca/sonnet-ui/json-editor\";\nimport {\n EMPTY_FILTER,\n flattenFilterExpr,\n isEmptyFilterExpr,\n serializeFilterExpr,\n type FilterExpr,\n} from \"@petrarca/sonnet-core/search\";\nimport { toJsonSchema, type JsonSchema } from \"@petrarca/sonnet-core/schema\";\nimport type { EntityOptionFetcher } from \"@petrarca/sonnet-core/entityOptions\";\nimport { z } from \"zod/v4\";\n\n// Static mock patient data for the EntitySelect playground demo.\n// Uses a local fetcher so the playground has no dependency on a live API.\nconst MOCK_PATIENTS = [\n { value: \"pat_001\", label: \"Alice Hartmann\" },\n { value: \"pat_002\", label: \"Bob Nguyen\" },\n { value: \"pat_003\", label: \"Carol Smith\" },\n { value: \"pat_004\", label: \"David Okafor\" },\n { value: \"pat_005\", label: \"Eva Larsson\" },\n { value: \"pat_006\", label: \"Frank Torres\" },\n { value: \"pat_007\", label: \"Grace Kim\" },\n { value: \"pat_008\", label: \"Henry Bauer\" },\n];\n\nconst patientFetcher: EntityOptionFetcher = ({ search } = {}) => {\n const q = search?.toLowerCase() ?? \"\";\n const results = q\n ? MOCK_PATIENTS.filter((p) => p.label.toLowerCase().includes(q))\n : MOCK_PATIENTS;\n return Promise.resolve(results);\n};\n\ninterface SampleRow {\n id: string;\n name: string;\n status: \"active\" | \"inactive\" | \"pending\";\n role: string;\n joined: string;\n}\n\nconst ALL_ROWS: SampleRow[] = [\n {\n id: \"u-001\",\n name: \"Alice Hartmann\",\n status: \"active\",\n role: \"admin\",\n joined: \"2024-01-15\",\n },\n {\n id: \"u-002\",\n name: \"Bob Nguyen\",\n status: \"active\",\n role: \"editor\",\n joined: \"2024-02-03\",\n },\n {\n id: \"u-003\",\n name: \"Carol Smith\",\n status: \"inactive\",\n role: \"viewer\",\n joined: \"2024-03-20\",\n },\n {\n id: \"u-004\",\n name: \"David Okafor\",\n status: \"pending\",\n role: \"editor\",\n joined: \"2024-04-11\",\n },\n {\n id: \"u-005\",\n name: \"Eva Larsson\",\n status: \"active\",\n role: \"admin\",\n joined: \"2024-05-08\",\n },\n {\n id: \"u-006\",\n name: \"Frank Torres\",\n status: \"inactive\",\n role: \"viewer\",\n joined: \"2024-06-22\",\n },\n {\n id: \"u-007\",\n name: \"Grace Kim\",\n status: \"active\",\n role: \"editor\",\n joined: \"2024-07-14\",\n },\n {\n id: \"u-008\",\n name: \"Henry Bauer\",\n status: \"pending\",\n role: \"viewer\",\n joined: \"2024-08-30\",\n },\n];\n\nconst STATUS_COLOR = {\n active: \"green\",\n inactive: \"violet\",\n pending: \"yellow\",\n} as const;\n\nconst PlaygroundFacets = z.object({\n search: z.string().meta({ \"x-ui-title\": \"Name\" }),\n status: z\n .enum([\"active\", \"inactive\", \"pending\"])\n .optional()\n .meta({\n \"x-ui-title\": \"Status\",\n \"x-ui-options\": {\n enumNames: {\n active: \"Active\",\n inactive: \"Inactive\",\n pending: \"Pending\",\n },\n },\n }),\n role: z.string().optional().meta({ \"x-ui-title\": \"Role\" }),\n});\n\nconst SEARCH_FACET_SCHEMA = toJsonSchema(PlaygroundFacets);\n\nconst SAMPLE_SCHEMA: JsonSchema = {\n title: \"User\",\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n title: \"Name\",\n \"x-ui-column\": { order: 0 },\n \"x-ui-cell\": \"bold\",\n },\n status: {\n type: \"string\",\n title: \"Status\",\n \"x-ui-column\": { order: 1 },\n \"x-ui-cell\": \"badge\",\n \"x-ui-cell-options\": { colors: STATUS_COLOR },\n },\n role: {\n type: \"string\",\n title: \"Role\",\n \"x-ui-column\": { order: 2 },\n \"x-ui-cell\": \"muted\",\n },\n id: {\n type: \"string\",\n title: \"ID\",\n \"x-ui-column\": { order: 3 },\n \"x-ui-cell\": \"code\",\n },\n joined: {\n type: \"string\",\n format: \"date\",\n title: \"Joined\",\n \"x-ui-column\": { order: 4 },\n },\n },\n};\n\nconst SAMPLE_UI_SCHEMA: TableUiSchema = {}; // mutable via the live schema editor below\n\n// Local renderer demo: three x- prefixed renderers using the same SampleRow data.\nconst ROLE_ICONS: Record<string, string> = {\n admin: \"A\",\n editor: \"E\",\n viewer: \"V\",\n};\n\nconst CUSTOM_RENDERERS = {\n // Extract initials from the name, display as avatar circle + name.\n \"x-play-initials\": createRenderer<SampleRow>((row) => {\n const initials = row.name\n .split(\" \")\n .map((w) => w[0])\n .join(\"\");\n return (\n <span className=\"inline-flex items-center gap-2\">\n <span className=\"inline-flex items-center justify-center h-7 w-7 rounded-full bg-muted text-xs font-medium shrink-0\">\n {initials}\n </span>\n <span className=\"font-medium\">{row.name}</span>\n </span>\n );\n }),\n\n // Role with a letter prefix read from options.icons.\n \"x-play-role-icon\": createRenderer<SampleRow>((row, options) => {\n const icons = (options.icons as Record<string, string>) ?? {};\n const icon = icons[row.role] ?? \"?\";\n return (\n <span>\n <span className=\"inline-flex items-center justify-center h-5 w-5 rounded bg-muted text-xs font-mono mr-1.5\">\n {icon}\n </span>\n {row.role}\n </span>\n );\n }),\n\n // Compute days since joined, color-coded by threshold from options.\n \"x-play-tenure\": createRenderer<SampleRow>((row, options) => {\n const threshold = (options.recentDays as number) ?? 365;\n const days = Math.floor(\n (Date.now() - new Date(row.joined).getTime()) / 86_400_000,\n );\n const isRecent = days < threshold;\n return (\n <span className={isRecent ? \"text-blue-600\" : \"text-muted-foreground\"}>\n {days}d\n </span>\n );\n }),\n};\n\nconst CUSTOM_RENDERER_SCHEMA: JsonSchema = {\n title: \"User (custom renderers)\",\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n title: \"Name\",\n \"x-ui-column\": { order: 0 },\n \"x-ui-cell\": \"x-play-initials\",\n },\n role: {\n type: \"string\",\n title: \"Role\",\n \"x-ui-column\": { order: 1 },\n \"x-ui-cell\": \"x-play-role-icon\",\n \"x-ui-cell-options\": { icons: ROLE_ICONS },\n },\n status: {\n type: \"string\",\n title: \"Status\",\n \"x-ui-column\": { order: 2 },\n \"x-ui-cell\": \"badge\",\n \"x-ui-cell-options\": { colors: STATUS_COLOR },\n },\n joined: {\n type: \"string\",\n title: \"Tenure\",\n \"x-ui-column\": { order: 3 },\n \"x-ui-cell\": \"x-play-tenure\",\n \"x-ui-cell-options\": { recentDays: 500 },\n },\n id: {\n type: \"string\",\n title: \"ID\",\n \"x-ui-column\": { order: 4 },\n \"x-ui-cell\": \"code\",\n },\n },\n};\n\n// Presets for the searchFields demo (Section 1b). Uses the same user domain\n// as the rest of the playground (name, role, status).\nconst SEARCH_FIELD_PRESETS: { label: string; fields: string[] }[] = [\n { label: \"name, role\", fields: [\"name\", \"role\"] },\n { label: \"name (single)\", fields: [\"name\"] },\n { label: \"name, role, status\", fields: [\"name\", \"role\", \"status\"] },\n];\n\n// Reusable toggle-button strip for playground controls.\n// Renders a label followed by a set of pill buttons. className applies to\n// the wrapping fragment's implicit container -- callers place it inside a\n// flex row, so use ml-* on the caller side via the containerClassName prop.\nfunction ToggleStrip<T extends string | number>({\n label,\n options,\n value,\n onChange,\n format,\n containerClassName,\n}: {\n label: string;\n options: readonly T[];\n value: T;\n onChange: (v: T) => void;\n format?: (v: T) => string;\n containerClassName?: string;\n}) {\n const fmt = format ?? String;\n return (\n <div className={`flex items-center gap-3 ${containerClassName ?? \"\"}`}>\n <span className=\"text-xs text-muted-foreground\">{label}:</span>\n {options.map((opt) => (\n <button\n key={String(opt)}\n type=\"button\"\n onClick={() => onChange(opt)}\n className={[\n \"px-3 py-1 rounded-full text-xs font-medium transition-colors\",\n value === opt\n ? \"bg-primary text-primary-foreground\"\n : \"bg-muted text-muted-foreground hover:text-foreground\",\n ].join(\" \")}\n >\n {fmt(opt)}\n </button>\n ))}\n </div>\n );\n}\n\nconst MODE_OPTIONS = [\"explicit\", \"debounce\"] as const;\nconst MODE_LABELS: Record<string, string> = {\n explicit: \"Explicit (Enter)\",\n debounce: \"Debounce (300 ms)\",\n};\nconst MIN_CHARS_OPTIONS = [0, 1, 2, 3] as const;\nconst FIELD_PRESET_INDICES = SEARCH_FIELD_PRESETS.map(\n (_, i) => i,\n) as readonly number[];\n\nfunction FilterExprInspector({ expr }: { expr: FilterExpr }) {\n return (\n <pre className=\"mt-3 rounded-md bg-muted px-4 py-3 text-xs overflow-auto max-h-40\">\n {JSON.stringify(expr, null, 2)}\n </pre>\n );\n}\n\nfunction FlatTermsInspector({ expr }: { expr: FilterExpr }) {\n const terms = flattenFilterExpr(expr);\n return (\n <pre className=\"mt-3 rounded-md bg-muted px-4 py-3 text-xs overflow-auto max-h-40\">\n {JSON.stringify(terms, null, 2)}\n </pre>\n );\n}\n\nfunction SectionHeader({\n title,\n description,\n}: {\n title: string;\n description: string;\n}) {\n return (\n <div className=\"mb-4\">\n <h2 className=\"heading-section mb-1\">{title}</h2>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n );\n}\n\n/** Playground sandbox for SearchInput, EntitySelect, and EntityTable. */\nexport default function PlayDataComponents(): React.ReactElement {\n // Section 1: simple mode\n const [simpleExpr, setSimpleExpr] = useState<FilterExpr>(EMPTY_FILTER);\n const [simpleMode, setSimpleMode] = useState<SearchMode>(\"explicit\");\n const [simpleMinChars, setSimpleMinChars] = useState(0);\n\n // Section 1b: searchFields mode (multi-field OR)\n const [fieldsExpr, setFieldsExpr] = useState<FilterExpr>(EMPTY_FILTER);\n const [fieldsMode, setFieldsMode] = useState<SearchMode>(\"explicit\");\n const [fieldsMinChars, setFieldsMinChars] = useState(0);\n const [fieldsPresetIdx, setFieldsPresetIdx] = useState(0);\n const activeSearchFields = SEARCH_FIELD_PRESETS[fieldsPresetIdx].fields;\n\n // Section 2: faceted mode with live schema editor\n const [facetedExpr, setFacetedExpr] = useState<FilterExpr>(EMPTY_FILTER);\n const [facetedSubmitCount, setFacetedSubmitCount] = useState(0);\n const handleFacetedChange = useCallback((expr: FilterExpr) => {\n setFacetedExpr(expr);\n setFacetedSubmitCount((n) => n + 1);\n }, []);\n const [schemaText, setSchemaText] = useState(() =>\n JSON.stringify(toJsonSchema(PlaygroundFacets), null, 2),\n );\n const [schemaError, setSchemaError] = useState(\"\");\n const [activeSchema, setActiveSchema] =\n useState<JsonSchema>(SEARCH_FACET_SCHEMA);\n\n const handleSchemaTextChange = useCallback((value: string) => {\n setSchemaText(value);\n try {\n JSON.parse(value);\n setSchemaError(\"\");\n } catch {\n setSchemaError(\"Invalid JSON\");\n }\n }, []);\n\n const handleLoadSchema = useCallback(() => {\n try {\n const parsed = JSON.parse(schemaText) as JsonSchema;\n setActiveSchema(parsed);\n setFacetedExpr(EMPTY_FILTER);\n setFacetedSubmitCount(-1);\n setSchemaError(\"\");\n } catch {\n setSchemaError(\"Invalid JSON -- cannot load\");\n }\n }, [schemaText]);\n\n // Section 3: schema + UI schema editors for the table.\n const [tableSchemaText, setTableSchemaText] = useState(() =>\n JSON.stringify(SAMPLE_SCHEMA, null, 2),\n );\n const [tableUiSchemaText, setTableUiSchemaText] = useState(() =>\n JSON.stringify(SAMPLE_UI_SCHEMA, null, 2),\n );\n const [tableSchemaError, setTableSchemaError] = useState(\"\");\n const [activeTableSchema, setActiveTableSchema] =\n useState<JsonSchema>(SAMPLE_SCHEMA);\n const [activeTableUiSchema, setActiveTableUiSchema] =\n useState<TableUiSchema>(SAMPLE_UI_SCHEMA);\n const [tableSchemaTab, setTableSchemaTab] = useState(\"schema\");\n\n const handleTableSchemaTextChange = useCallback((value: string) => {\n setTableSchemaText(value);\n try {\n JSON.parse(value);\n setTableSchemaError(\"\");\n } catch {\n setTableSchemaError(\"Invalid JSON\");\n }\n }, []);\n\n const handleTableUiSchemaTextChange = useCallback((value: string) => {\n setTableUiSchemaText(value);\n try {\n JSON.parse(value);\n setTableSchemaError(\"\");\n } catch {\n setTableSchemaError(\"Invalid JSON\");\n }\n }, []);\n\n const handleApplyTableSchema = useCallback(() => {\n try {\n const schema = JSON.parse(tableSchemaText) as JsonSchema;\n const uiSchema = JSON.parse(tableUiSchemaText) as TableUiSchema;\n setActiveTableSchema(schema);\n setActiveTableUiSchema(uiSchema);\n setTableSchemaError(\"\");\n } catch {\n setTableSchemaError(\"Invalid JSON -- cannot apply\");\n }\n }, [tableSchemaText, tableUiSchemaText]);\n\n // Section 3: faceted mode wired to EntityTable.\n // onChange fires only on submit -- filteredRows updates when the user presses Enter.\n const [tableExpr, setTableExpr] = useState<FilterExpr>(EMPTY_FILTER);\n\n const filteredRows = useMemo(() => {\n const terms = flattenFilterExpr(tableExpr);\n if (terms.length === 0) return ALL_ROWS;\n\n return ALL_ROWS.filter((row) =>\n terms.every(({ key, value }) => {\n const v = value.toLowerCase();\n if (key === \"status\") return row.status.toLowerCase().includes(v);\n if (key === \"role\") return row.role.toLowerCase().includes(v);\n // default: search by name\n return row.name.toLowerCase().includes(v);\n }),\n );\n }, [tableExpr]);\n\n const tableData = useMemo(\n () => ({\n data: filteredRows,\n meta: {\n total: filteredRows.length,\n limit: filteredRows.length,\n offset: 0,\n has_next: false,\n has_prev: false,\n },\n }),\n [filteredRows],\n );\n\n // Section 5: EntitySelect\n const [selectedPatient, setSelectedPatient] = useState<string | null>(null);\n\n return (\n <div className=\"w-full py-12 px-4 max-w-4xl\">\n <h1 className=\"mb-1\">Data Components</h1>\n <p className=\"text-lg text-muted-foreground mb-10\">\n Interactive sandbox for SearchInput, EntitySelect, and EntityTable.\n </p>\n\n {/* Section 1: simple mode */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"SearchInput — simple mode\"\n description=\"No fields prop. Produces a single search term under the key 'search'.\"\n />\n <div className=\"flex flex-wrap gap-4 mb-3\">\n <ToggleStrip\n label=\"Mode\"\n options={MODE_OPTIONS}\n value={simpleMode}\n onChange={(m) => {\n setSimpleMode(m);\n setSimpleExpr(EMPTY_FILTER);\n }}\n format={(m) => MODE_LABELS[m]}\n />\n <ToggleStrip\n label=\"Min chars\"\n options={MIN_CHARS_OPTIONS}\n value={simpleMinChars}\n onChange={setSimpleMinChars}\n format={(n) => (n === 0 ? \"off\" : String(n))}\n />\n </div>\n <SearchInput\n value={simpleExpr}\n onChange={setSimpleExpr}\n mode={simpleMode}\n minChars={simpleMinChars}\n placeholder=\"Search...\"\n className=\"max-w-sm\"\n />\n <FlatTermsInspector expr={simpleExpr} />\n </section>\n\n {/* Section 1b: searchFields mode */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"SearchInput — searchFields (multi-field OR)\"\n 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.\"\n />\n <div className=\"flex flex-wrap gap-4 mb-2\">\n <ToggleStrip\n label=\"Fields\"\n options={FIELD_PRESET_INDICES}\n value={fieldsPresetIdx}\n onChange={(idx) => {\n setFieldsPresetIdx(idx);\n setFieldsExpr(EMPTY_FILTER);\n }}\n format={(idx) => SEARCH_FIELD_PRESETS[idx].label}\n />\n </div>\n <div className=\"flex flex-wrap gap-4 mb-3\">\n <ToggleStrip\n label=\"Mode\"\n options={MODE_OPTIONS}\n value={fieldsMode}\n onChange={(m) => {\n setFieldsMode(m);\n setFieldsExpr(EMPTY_FILTER);\n }}\n format={(m) => MODE_LABELS[m]}\n />\n <ToggleStrip\n label=\"Min chars\"\n options={MIN_CHARS_OPTIONS}\n value={fieldsMinChars}\n onChange={setFieldsMinChars}\n format={(n) => (n === 0 ? \"off\" : String(n))}\n />\n </div>\n <SearchInput\n value={fieldsExpr}\n onChange={setFieldsExpr}\n searchFields={activeSearchFields}\n mode={fieldsMode}\n minChars={fieldsMinChars}\n placeholder={`Search by ${activeSearchFields.join(\" or \")}...`}\n className=\"max-w-sm\"\n />\n <div className=\"mt-3 space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n searchFields: [{activeSearchFields.map((f) => `\"${f}\"`).join(\", \")}]\n </p>\n <p className=\"text-xs text-muted-foreground\">FilterExpr AST:</p>\n <FilterExprInspector expr={fieldsExpr} />\n {!isEmptyFilterExpr(fieldsExpr) && (\n <>\n <p className=\"text-xs text-muted-foreground\">\n Serialized q= string:\n </p>\n <pre className=\"rounded-md bg-muted px-4 py-3 text-xs overflow-auto\">\n {serializeFilterExpr(fieldsExpr)}\n </pre>\n </>\n )}\n </div>\n </section>\n\n {/* Section 2: faceted mode -- two-column schema editor */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"SearchInput — faceted mode\"\n 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.\"\n />\n <div className=\"flex gap-4 items-start\">\n {/* Left: schema editor */}\n <Card className=\"flex-1 min-w-0 p-3 flex flex-col gap-2\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n JSON Schema\n </p>\n {schemaError && (\n <Badge variant=\"destructive\" className=\"text-xs self-start\">\n {schemaError}\n </Badge>\n )}\n <FormTextarea\n value={schemaText}\n onChange={(e) => handleSchemaTextChange(e.target.value)}\n className=\"font-mono text-xs\"\n rows={18}\n />\n </Card>\n\n {/* Load button */}\n <div className=\"flex items-center self-center\">\n <SimpleTooltip label=\"Load schema into search\">\n <Button\n variant=\"default\"\n size=\"icon\"\n onClick={handleLoadSchema}\n disabled={!!schemaError}\n >\n <ArrowRight size={18} />\n </Button>\n </SimpleTooltip>\n </div>\n\n {/* Right: live search + output */}\n <Card className=\"flex-1 min-w-0 p-3 flex flex-col gap-3\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n Live search\n </p>\n <SearchInput\n value={facetedExpr}\n onChange={handleFacetedChange}\n schema={activeSchema}\n mode=\"explicit\"\n placeholder=\"Add filter...\"\n />\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <span>onChange fired:</span>\n <span className=\"font-mono font-medium text-foreground\">\n {facetedSubmitCount}x\n </span>\n </div>\n <FilterExprInspector expr={facetedExpr} />\n </Card>\n </div>\n </section>\n\n {/* Section 3: faceted mode + EntityTable wired together */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"EntityTable with SearchInput\"\n 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.\"\n />\n <SearchInput\n value={tableExpr}\n onChange={setTableExpr}\n schema={SEARCH_FACET_SCHEMA}\n mode=\"explicit\"\n placeholder=\"Filter users... (press Enter to apply)\"\n className=\"mb-4 max-w-sm\"\n />\n <EntityTable<SampleRow>\n schema={activeTableSchema}\n uiSchema={activeTableUiSchema}\n data={tableData}\n isLoading={false}\n isError={false}\n entityLabel=\"users\"\n isFiltered={!isEmptyFilterExpr(tableExpr)}\n />\n\n {/* Schema editor below the table */}\n <div className=\"mt-4\">\n <Card className=\"border shadow-sm p-4 flex flex-col\">\n <div className=\"flex items-center justify-between mb-2\">\n <Tabs\n value={tableSchemaTab}\n onValueChange={setTableSchemaTab}\n className=\"flex flex-col flex-1\"\n >\n <div className=\"flex items-center justify-between\">\n <TabsList>\n <TabsTrigger value=\"schema\">JSON Schema</TabsTrigger>\n <TabsTrigger value=\"uischema\">UI Schema</TabsTrigger>\n </TabsList>\n <div className=\"flex items-center gap-2\">\n {tableSchemaError && (\n <Badge variant=\"destructive\" className=\"text-xs\">\n {tableSchemaError}\n </Badge>\n )}\n <SimpleTooltip label=\"Apply schema to table above\">\n <Button\n variant=\"default\"\n size=\"icon\"\n onClick={handleApplyTableSchema}\n disabled={!!tableSchemaError}\n >\n <ArrowUp size={18} />\n </Button>\n </SimpleTooltip>\n </div>\n </div>\n <TabsContent value=\"schema\" className=\"mt-2\">\n <JsonEditor\n value={tableSchemaText}\n editablePaths={[\"/\"]}\n onChange={handleTableSchemaTextChange}\n height=\"280px\"\n className=\"rounded-md border overflow-hidden\"\n />\n </TabsContent>\n <TabsContent value=\"uischema\" className=\"mt-2\">\n <JsonEditor\n value={tableUiSchemaText}\n editablePaths={[\"/\"]}\n onChange={handleTableUiSchemaTextChange}\n height=\"280px\"\n className=\"rounded-md border overflow-hidden\"\n />\n </TabsContent>\n </Tabs>\n </div>\n </Card>\n </div>\n </section>\n\n {/* Section 4: Local renderers demo */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"EntityTable with Local Renderers\"\n 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).\"\n />\n <EntityTable<SampleRow>\n schema={CUSTOM_RENDERER_SCHEMA}\n renderers={CUSTOM_RENDERERS}\n data={{\n data: ALL_ROWS,\n meta: {\n total: ALL_ROWS.length,\n limit: ALL_ROWS.length,\n offset: 0,\n has_next: false,\n has_prev: false,\n },\n }}\n isLoading={false}\n isError={false}\n entityLabel=\"users\"\n />\n </section>\n\n {/* Section 5: EntitySelect -- searchable select with mock fetcher */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"EntitySelect\"\n 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'.\"\n />\n <div className=\"flex gap-6 items-start\">\n <EntitySelect\n fetcher={patientFetcher}\n queryKey=\"playground-patients\"\n value={selectedPatient}\n onChange={setSelectedPatient}\n label=\"Patient\"\n placeholder=\"Select patient...\"\n searchPlaceholder=\"Search patients...\"\n className=\"max-w-xs\"\n />\n <Card className=\"flex-1 min-w-0 p-3\">\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">\n Selected value\n </p>\n <pre className=\"rounded-md bg-muted px-4 py-3 text-xs overflow-auto\">\n {JSON.stringify(selectedPatient, null, 2)}\n </pre>\n </Card>\n </div>\n </section>\n\n {/* Section 6: EntityTree -- schema-driven tree with static nested data */}\n <section className=\"mb-12\">\n <SectionHeader\n title=\"EntityTree\"\n 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.\"\n />\n <EntityTreeDemo />\n </section>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// EntityTree demo\n// ---------------------------------------------------------------------------\n\ninterface OrgRow extends Record<string, unknown> {\n name: string;\n type: string;\n headcount: number;\n}\n\nconst OrgSchema = z.object({\n name: z.string().meta({ title: \"Name\", \"x-ui-column\": { order: 0 } }),\n type: z.string().meta({ title: \"Type\", \"x-ui-column\": { order: 1 } }),\n headcount: z\n .number()\n .meta({ title: \"Headcount\", \"x-ui-column\": { order: 2 } }),\n});\n\nconst ORG_TREE_SCHEMA = toJsonSchema(OrgSchema);\n\nconst ORG_NODES: TreeNode<OrgRow>[] = [\n {\n id: \"eng\",\n data: { name: \"Engineering\", type: \"Division\", headcount: 120 },\n hasChildren: true,\n children: [\n {\n id: \"frontend\",\n data: { name: \"Frontend\", type: \"Team\", headcount: 18 },\n hasChildren: true,\n children: [\n {\n id: \"design-sys\",\n data: { name: \"Design System\", type: \"Squad\", headcount: 5 },\n hasChildren: false,\n },\n ],\n },\n {\n id: \"backend\",\n data: { name: \"Backend\", type: \"Team\", headcount: 32 },\n hasChildren: false,\n },\n {\n id: \"infra\",\n data: { name: \"Infrastructure\", type: \"Team\", headcount: 14 },\n hasChildren: false,\n },\n ],\n },\n {\n id: \"product\",\n data: { name: \"Product\", type: \"Division\", headcount: 45 },\n hasChildren: true,\n children: [\n {\n id: \"research\",\n data: { name: \"Research\", type: \"Team\", headcount: 8 },\n hasChildren: false,\n },\n {\n id: \"pm\",\n data: { name: \"Product Management\", type: \"Team\", headcount: 12 },\n hasChildren: false,\n },\n ],\n },\n {\n id: \"ops\",\n data: { name: \"Operations\", type: \"Division\", headcount: 30 },\n hasChildren: false,\n },\n];\n\nfunction EntityTreeDemo() {\n const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n const [activeId, setActiveId] = useState<string | null>(null);\n\n const handleExpand = (id: string) =>\n setExpandedIds((prev) => new Set([...prev, id]));\n\n const handleCollapse = (id: string) =>\n setExpandedIds((prev) => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n\n return (\n <div className=\"space-y-4\">\n <EntityTree<OrgRow>\n schema={ORG_TREE_SCHEMA}\n nodes={ORG_NODES}\n expandedIds={expandedIds}\n onExpand={handleExpand}\n onCollapse={handleCollapse}\n onNodeSelect={(node) => setActiveId(node.id)}\n onNodeEdit={(node) => setActiveId(node.id)}\n activeNodeId={activeId ?? undefined}\n selectedIds={selectedIds}\n onSelectionChange={(id, selected) =>\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (selected) next.add(id);\n else next.delete(id);\n return next;\n })\n }\n entityLabel=\"departments\"\n />\n <div className=\"flex gap-6 text-xs text-muted-foreground\">\n {activeId && (\n <span>\n Active: <span className=\"font-mono\">{activeId}</span>\n </span>\n )}\n {selectedIds.size > 0 && (\n <span>\n Selected:{\" \"}\n <span className=\"font-mono\">{[...selectedIds].join(\", \")}</span>\n </span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,160 @@
1
+ import { LucideIcon } from 'lucide-react';
2
+ import { ServiceModule } from '@petrarca/sonnet-shell';
3
+ import React$1 from 'react';
4
+
5
+ /**
6
+ * Playground entry types.
7
+ *
8
+ * A PlaygroundEntry describes a single playground page: its metadata
9
+ * for the overview tile grid and its React component for rendering.
10
+ */
11
+
12
+ /** Tailwind color names supported by the overview tile icons. */
13
+ type PlaygroundColor = "blue" | "violet" | "indigo" | "purple" | "teal" | "amber" | "emerald" | "rose" | "orange" | "cyan";
14
+ /** A single playground page registration. */
15
+ interface PlaygroundEntry {
16
+ /** Unique identifier, e.g. "sonnet.typography" or "atlas.terminology". */
17
+ id: string;
18
+ /** Display title on the overview tile. */
19
+ title: string;
20
+ /** Short description shown below the title. */
21
+ description: string;
22
+ /** Lucide icon component for the tile. */
23
+ icon: LucideIcon;
24
+ /** Tailwind color name for the icon container. */
25
+ color: PlaygroundColor;
26
+ /**
27
+ * Route path segment (relative, no leading slash).
28
+ * Appended to the playground base path, e.g. "typography" becomes
29
+ * "/playground/typography".
30
+ */
31
+ path: string;
32
+ /**
33
+ * The page component -- typically a React.lazy() import.
34
+ * Wrapped in Suspense automatically by the module factory.
35
+ */
36
+ component: React.ComponentType;
37
+ /** Optional group label for the overview grid (e.g. "Design System", "Forms"). */
38
+ group?: string;
39
+ /** Sort order within a group (lower = earlier). Defaults to 0. */
40
+ order?: number;
41
+ }
42
+ /** Options for createPlaygroundModule(). */
43
+ interface PlaygroundModuleOptions {
44
+ /** Icon rail icon. Defaults to FlaskConical. */
45
+ icon?: LucideIcon;
46
+ /** Module label. Defaults to "Playground". */
47
+ label?: string;
48
+ /** Base path. Defaults to "/playground". */
49
+ basePath?: string;
50
+ /** Overview page title. Defaults to "Developer Playground". */
51
+ overviewTitle?: string;
52
+ /** Overview page subtitle. */
53
+ overviewSubtitle?: string;
54
+ /** Initial grouping state for the overview page. Defaults to true. User can toggle at runtime. */
55
+ grouped?: boolean;
56
+ }
57
+
58
+ /**
59
+ * PlaygroundRegistry -- mutable collection of playground entries.
60
+ *
61
+ * The library ships a default instance pre-populated with the built-in
62
+ * sonnet component demos. Consumers register additional entries for
63
+ * domain-specific playgrounds before passing the registry to
64
+ * createPlaygroundModule().
65
+ */
66
+
67
+ declare class PlaygroundRegistry {
68
+ private entries;
69
+ /** Register a playground entry. Overwrites any existing entry with the same id. */
70
+ register(entry: PlaygroundEntry): void;
71
+ /** Register multiple entries at once. */
72
+ registerAll(entries: PlaygroundEntry[]): void;
73
+ /** Remove an entry by id. Returns true if it existed. */
74
+ unregister(id: string): boolean;
75
+ /** Get a single entry by id. */
76
+ get(id: string): PlaygroundEntry | undefined;
77
+ /** All registered entries, sorted by group then order. */
78
+ getAll(): PlaygroundEntry[];
79
+ /**
80
+ * Entries grouped by their group label.
81
+ * Returns an array of [groupLabel, entries[]] tuples, preserving
82
+ * insertion order for groups and sort order within each group.
83
+ */
84
+ getGrouped(): Array<[string, PlaygroundEntry[]]>;
85
+ /** Number of registered entries. */
86
+ get size(): number;
87
+ }
88
+
89
+ /**
90
+ * Create a ServiceModule for the playground.
91
+ *
92
+ * Returns a complete module definition ready to pass to
93
+ * createModuleRegistry(). Routes, navigation, and the overview page
94
+ * are all generated from the registry.
95
+ */
96
+ declare function createPlaygroundModule(registry: PlaygroundRegistry, options?: PlaygroundModuleOptions): ServiceModule;
97
+
98
+ /**
99
+ * PlaygroundOverview -- tile grid rendered from a PlaygroundRegistry.
100
+ *
101
+ * Displays cards with colored icons. Clicking a card navigates to the
102
+ * playground page. A toggle in the header lets the user switch between
103
+ * grouped (by category) and flat (all tiles) views.
104
+ */
105
+
106
+ interface PlaygroundOverviewProps {
107
+ registry: PlaygroundRegistry;
108
+ basePath: string;
109
+ title: string;
110
+ subtitle: string;
111
+ /** Initial grouping state. Defaults to true. User can toggle at runtime. */
112
+ grouped?: boolean;
113
+ }
114
+ /** Developer playground overview -- card grid linking to all registered pages. */
115
+ declare function PlaygroundOverview({ registry, basePath, title, subtitle, grouped: initialGrouped, }: PlaygroundOverviewProps): React$1.ReactElement;
116
+
117
+ /**
118
+ * Shared section wrapper for playground pages.
119
+ *
120
+ * Provides a consistent titled section with optional description,
121
+ * used across all playground demo pages.
122
+ */
123
+
124
+ interface PlaygroundSectionProps {
125
+ title: string;
126
+ description?: string;
127
+ children: React$1.ReactNode;
128
+ }
129
+ /** Titled section wrapper used across playground pages. */
130
+ declare function PlaygroundSection({ title, description, children, }: PlaygroundSectionProps): React$1.ReactElement;
131
+
132
+ declare const builtinEntries: PlaygroundEntry[];
133
+
134
+ /**
135
+ * @petrarca/sonnet-playground
136
+ *
137
+ * Playground framework and built-in component demos for the Petrarca
138
+ * Sonnet component library.
139
+ *
140
+ * Usage:
141
+ *
142
+ * import {
143
+ * createPlaygroundRegistry,
144
+ * createPlaygroundModule,
145
+ * } from "@petrarca/sonnet-playground";
146
+ *
147
+ * const playground = createPlaygroundRegistry();
148
+ * // Optionally register additional entries:
149
+ * playground.register({ id: "app.custom", ... });
150
+ *
151
+ * const playgroundModule = createPlaygroundModule(playground);
152
+ */
153
+
154
+ /**
155
+ * Convenience factory: creates a new PlaygroundRegistry pre-populated
156
+ * with all built-in sonnet component demos.
157
+ */
158
+ declare function createPlaygroundRegistry(): PlaygroundRegistry;
159
+
160
+ export { type PlaygroundColor, type PlaygroundEntry, type PlaygroundModuleOptions, PlaygroundOverview, PlaygroundRegistry, PlaygroundSection, builtinEntries, createPlaygroundModule, createPlaygroundRegistry };
package/dist/index.js ADDED
@@ -0,0 +1,328 @@
1
+ import {
2
+ PlaygroundSection
3
+ } from "./chunk-FTN2A5YL.js";
4
+
5
+ // src/PlaygroundRegistry.ts
6
+ var PlaygroundRegistry = class {
7
+ entries = /* @__PURE__ */ new Map();
8
+ /** Register a playground entry. Overwrites any existing entry with the same id. */
9
+ register(entry) {
10
+ this.entries.set(entry.id, entry);
11
+ }
12
+ /** Register multiple entries at once. */
13
+ registerAll(entries) {
14
+ for (const entry of entries) {
15
+ this.entries.set(entry.id, entry);
16
+ }
17
+ }
18
+ /** Remove an entry by id. Returns true if it existed. */
19
+ unregister(id) {
20
+ return this.entries.delete(id);
21
+ }
22
+ /** Get a single entry by id. */
23
+ get(id) {
24
+ return this.entries.get(id);
25
+ }
26
+ /** All registered entries, sorted by group then order. */
27
+ getAll() {
28
+ return [...this.entries.values()].sort((a, b) => {
29
+ const groupA = a.group ?? "";
30
+ const groupB = b.group ?? "";
31
+ if (groupA !== groupB) return groupA.localeCompare(groupB);
32
+ return (a.order ?? 0) - (b.order ?? 0);
33
+ });
34
+ }
35
+ /**
36
+ * Entries grouped by their group label.
37
+ * Returns an array of [groupLabel, entries[]] tuples, preserving
38
+ * insertion order for groups and sort order within each group.
39
+ */
40
+ getGrouped() {
41
+ const grouped = /* @__PURE__ */ new Map();
42
+ for (const entry of this.getAll()) {
43
+ const group = entry.group ?? "General";
44
+ const list = grouped.get(group) ?? [];
45
+ list.push(entry);
46
+ grouped.set(group, list);
47
+ }
48
+ return [...grouped.entries()];
49
+ }
50
+ /** Number of registered entries. */
51
+ get size() {
52
+ return this.entries.size;
53
+ }
54
+ };
55
+
56
+ // src/builtinEntries.ts
57
+ import { lazy } from "react";
58
+ import {
59
+ Type,
60
+ Palette,
61
+ Grid,
62
+ Braces,
63
+ Check,
64
+ Edit,
65
+ Package,
66
+ Table2
67
+ } from "lucide-react";
68
+ var builtinEntries = [
69
+ {
70
+ id: "sonnet.typography",
71
+ title: "Typography",
72
+ description: "Living style-guide for the typography system. Font families, type scale, weights, heading hierarchy, color tiers, and component patterns.",
73
+ icon: Type,
74
+ color: "teal",
75
+ path: "typography",
76
+ component: lazy(() => import("./PlayTypography-ID6DLFZD.js")),
77
+ group: "Design System",
78
+ order: 0
79
+ },
80
+ {
81
+ id: "sonnet.color-palette",
82
+ title: "Color Palette",
83
+ description: "Living reference for the color system. Semantic tokens, extended palette, badge colors, icon accents, and usage guidance.",
84
+ icon: Palette,
85
+ color: "violet",
86
+ path: "color-palette",
87
+ component: lazy(() => import("./PlayColorPalette-UFMTJTUB.js")),
88
+ group: "Design System",
89
+ order: 1
90
+ },
91
+ {
92
+ id: "sonnet.core-components",
93
+ title: "Core UI Components",
94
+ description: "Test all core UI components (notifications, buttons, dialogs, forms, etc.) in isolation before production use.",
95
+ icon: Grid,
96
+ color: "indigo",
97
+ path: "core-components",
98
+ component: lazy(() => import("./PlayCoreComponents-7QCDFQLT.js")),
99
+ group: "Components",
100
+ order: 0
101
+ },
102
+ {
103
+ id: "sonnet.json-editor",
104
+ title: "JSON Editor",
105
+ description: "Constrained JSON editor with selectively editable regions using JSON Pointer (RFC 6901). Readonly sections are visible but locked.",
106
+ icon: Braces,
107
+ color: "amber",
108
+ path: "json-editor",
109
+ component: lazy(() => import("./PlayJsonEditor-4L5INQPU.js")),
110
+ group: "Components",
111
+ order: 1
112
+ },
113
+ {
114
+ id: "sonnet.form-components",
115
+ title: "Form Components",
116
+ description: "Test custom Form wrapper components with labels, descriptions, and error states. Includes Input, Textarea, Number, Checkbox, Select, MultiSelect, and Tags.",
117
+ icon: Check,
118
+ color: "blue",
119
+ path: "form-components",
120
+ component: lazy(() => import("./PlayFormComponents-RJB6WZHD.js")),
121
+ group: "Forms",
122
+ order: 0
123
+ },
124
+ {
125
+ id: "sonnet.forms",
126
+ title: "Forms Renderer",
127
+ description: "Test and experiment with the JsonSchemaFormRenderer component. Try different schemas and see real-time validation.",
128
+ icon: Edit,
129
+ color: "purple",
130
+ path: "forms",
131
+ component: lazy(() => import("./PlayForms-24XX5DC6.js")),
132
+ group: "Forms",
133
+ order: 1
134
+ },
135
+ {
136
+ id: "sonnet.form-widgets",
137
+ title: "Form Widgets",
138
+ description: "Pluggable field components consumed by the Forms Renderer. Each widget maps to a JSON Schema type or format and can be swapped independently.",
139
+ icon: Package,
140
+ color: "indigo",
141
+ path: "form-widgets",
142
+ component: lazy(() => import("./PlayFormWidgets-OFV7TJKH.js")),
143
+ group: "Forms",
144
+ order: 2
145
+ },
146
+ {
147
+ id: "sonnet.data-components",
148
+ title: "Data Components",
149
+ description: "Interactive sandbox for SearchInput (simple and faceted mode) and EntityTable. Includes a live table wired to the faceted search.",
150
+ icon: Table2,
151
+ color: "teal",
152
+ path: "data-components",
153
+ component: lazy(() => import("./PlayDataComponents-GZSRTW4F.js")),
154
+ group: "Data",
155
+ order: 0
156
+ }
157
+ ];
158
+
159
+ // src/createPlaygroundModule.tsx
160
+ import { Suspense } from "react";
161
+ import { FlaskConical } from "lucide-react";
162
+
163
+ // src/PlaygroundOverview.tsx
164
+ import { useState } from "react";
165
+ import { useNavigate } from "react-router-dom";
166
+ import { LayoutGrid, List } from "lucide-react";
167
+ import { Card, Button, SimpleTooltip } from "@petrarca/sonnet-ui";
168
+ import { jsx, jsxs } from "react/jsx-runtime";
169
+ var COLOR_CLASSES = {
170
+ blue: "bg-blue-100 text-blue-600",
171
+ violet: "bg-violet-100 text-violet-600",
172
+ indigo: "bg-indigo-100 text-indigo-600",
173
+ purple: "bg-purple-100 text-purple-600",
174
+ teal: "bg-teal-100 text-teal-600",
175
+ amber: "bg-amber-100 text-amber-600",
176
+ emerald: "bg-emerald-100 text-emerald-600",
177
+ rose: "bg-rose-100 text-rose-600",
178
+ orange: "bg-orange-100 text-orange-600",
179
+ cyan: "bg-cyan-100 text-cyan-600"
180
+ };
181
+ var TILE_GRID = "grid grid-cols-1 sm:grid-cols-2 2xl:grid-cols-3 gap-6";
182
+ function PlaygroundTile({ item, onNavigate }) {
183
+ return /* @__PURE__ */ jsxs(
184
+ Card,
185
+ {
186
+ className: "cursor-pointer shadow-sm border rounded-md p-6 transition-transform duration-200 motion-safe:hover:-translate-y-1",
187
+ onClick: () => onNavigate(item.path),
188
+ children: [
189
+ /* @__PURE__ */ jsx("div", { className: "flex items-center mb-6", children: /* @__PURE__ */ jsx(
190
+ "div",
191
+ {
192
+ className: `flex items-center justify-center w-[50px] h-[50px] rounded-md ${COLOR_CLASSES[item.color] ?? COLOR_CLASSES.blue}`,
193
+ children: /* @__PURE__ */ jsx(item.icon, { size: 28 })
194
+ }
195
+ ) }),
196
+ /* @__PURE__ */ jsx("h3", { className: "heading-section mb-1", children: item.title }),
197
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: item.description })
198
+ ]
199
+ }
200
+ );
201
+ }
202
+ function PlaygroundOverview({
203
+ registry,
204
+ basePath,
205
+ title,
206
+ subtitle,
207
+ grouped: initialGrouped = true
208
+ }) {
209
+ const navigate = useNavigate();
210
+ const [grouped, setGrouped] = useState(initialGrouped);
211
+ const handleNavigate = (path) => navigate(`${basePath}/${path}`);
212
+ return /* @__PURE__ */ jsxs("div", { className: "w-full py-12 px-4", children: [
213
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-6", children: [
214
+ /* @__PURE__ */ jsxs("div", { children: [
215
+ /* @__PURE__ */ jsx("h1", { className: "mb-1", children: title }),
216
+ /* @__PURE__ */ jsx("p", { className: "text-lg text-muted-foreground", children: subtitle })
217
+ ] }),
218
+ /* @__PURE__ */ jsx(SimpleTooltip, { label: grouped ? "Show flat list" : "Group by category", children: /* @__PURE__ */ jsx(
219
+ Button,
220
+ {
221
+ variant: "ghost",
222
+ size: "icon",
223
+ onClick: () => setGrouped((v) => !v),
224
+ children: grouped ? /* @__PURE__ */ jsx(LayoutGrid, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(List, { className: "h-4 w-4" })
225
+ }
226
+ ) })
227
+ ] }),
228
+ grouped ? registry.getGrouped().map(([groupLabel, entries]) => /* @__PURE__ */ jsxs("div", { className: "mb-8", children: [
229
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wider mb-4", children: groupLabel }),
230
+ /* @__PURE__ */ jsx("div", { className: TILE_GRID, children: entries.map((item) => /* @__PURE__ */ jsx(
231
+ PlaygroundTile,
232
+ {
233
+ item,
234
+ onNavigate: handleNavigate
235
+ },
236
+ item.id
237
+ )) })
238
+ ] }, groupLabel)) : /* @__PURE__ */ jsx("div", { className: TILE_GRID, children: registry.getAll().map((item) => /* @__PURE__ */ jsx(
239
+ PlaygroundTile,
240
+ {
241
+ item,
242
+ onNavigate: handleNavigate
243
+ },
244
+ item.id
245
+ )) })
246
+ ] });
247
+ }
248
+
249
+ // src/createPlaygroundModule.tsx
250
+ import { jsx as jsx2 } from "react/jsx-runtime";
251
+ function buildNavigation(registry, basePath) {
252
+ const groups = registry.getGrouped();
253
+ const navGroups = [];
254
+ const overviewGroup = {
255
+ id: "playground-overview",
256
+ links: [{ label: "Overview", path: basePath }]
257
+ };
258
+ navGroups.push(overviewGroup);
259
+ for (const [groupLabel, entries] of groups) {
260
+ navGroups.push({
261
+ id: `playground-${groupLabel.toLowerCase().replace(/\s+/g, "-")}`,
262
+ heading: groupLabel,
263
+ links: entries.map((e) => ({
264
+ label: e.title,
265
+ path: `${basePath}/${e.path}`
266
+ }))
267
+ });
268
+ }
269
+ return navGroups;
270
+ }
271
+ function suspended(Component) {
272
+ return /* @__PURE__ */ jsx2(Suspense, { children: /* @__PURE__ */ jsx2(Component, {}) });
273
+ }
274
+ function createPlaygroundModule(registry, options = {}) {
275
+ const {
276
+ icon = FlaskConical,
277
+ label = "Playground",
278
+ basePath = "/playground",
279
+ overviewTitle = "Developer Playground",
280
+ overviewSubtitle = "Experiment with components, test features, and explore capabilities.",
281
+ grouped = true
282
+ } = options;
283
+ const routeBase = basePath.replace(/^\//, "");
284
+ const routes = [
285
+ {
286
+ path: routeBase,
287
+ element: suspended(() => /* @__PURE__ */ jsx2(
288
+ PlaygroundOverview,
289
+ {
290
+ registry,
291
+ basePath,
292
+ title: overviewTitle,
293
+ subtitle: overviewSubtitle,
294
+ grouped
295
+ }
296
+ ))
297
+ },
298
+ ...registry.getAll().map((entry) => ({
299
+ path: `${routeBase}/${entry.path}`,
300
+ element: suspended(entry.component)
301
+ }))
302
+ ];
303
+ return {
304
+ id: "playground",
305
+ label,
306
+ icon,
307
+ basePath,
308
+ pinBottom: true,
309
+ navigation: buildNavigation(registry, basePath),
310
+ routes
311
+ };
312
+ }
313
+
314
+ // src/index.ts
315
+ function createPlaygroundRegistry() {
316
+ const registry = new PlaygroundRegistry();
317
+ registry.registerAll(builtinEntries);
318
+ return registry;
319
+ }
320
+ export {
321
+ PlaygroundOverview,
322
+ PlaygroundRegistry,
323
+ PlaygroundSection,
324
+ builtinEntries,
325
+ createPlaygroundModule,
326
+ createPlaygroundRegistry
327
+ };
328
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/PlaygroundRegistry.ts","../src/builtinEntries.ts","../src/createPlaygroundModule.tsx","../src/PlaygroundOverview.tsx","../src/index.ts"],"sourcesContent":["/**\n * PlaygroundRegistry -- mutable collection of playground entries.\n *\n * The library ships a default instance pre-populated with the built-in\n * sonnet component demos. Consumers register additional entries for\n * domain-specific playgrounds before passing the registry to\n * createPlaygroundModule().\n */\nimport type { PlaygroundEntry } from \"./types\";\n\nexport class PlaygroundRegistry {\n private entries: Map<string, PlaygroundEntry> = new Map();\n\n /** Register a playground entry. Overwrites any existing entry with the same id. */\n register(entry: PlaygroundEntry): void {\n this.entries.set(entry.id, entry);\n }\n\n /** Register multiple entries at once. */\n registerAll(entries: PlaygroundEntry[]): void {\n for (const entry of entries) {\n this.entries.set(entry.id, entry);\n }\n }\n\n /** Remove an entry by id. Returns true if it existed. */\n unregister(id: string): boolean {\n return this.entries.delete(id);\n }\n\n /** Get a single entry by id. */\n get(id: string): PlaygroundEntry | undefined {\n return this.entries.get(id);\n }\n\n /** All registered entries, sorted by group then order. */\n getAll(): PlaygroundEntry[] {\n return [...this.entries.values()].sort((a, b) => {\n const groupA = a.group ?? \"\";\n const groupB = b.group ?? \"\";\n if (groupA !== groupB) return groupA.localeCompare(groupB);\n return (a.order ?? 0) - (b.order ?? 0);\n });\n }\n\n /**\n * Entries grouped by their group label.\n * Returns an array of [groupLabel, entries[]] tuples, preserving\n * insertion order for groups and sort order within each group.\n */\n getGrouped(): Array<[string, PlaygroundEntry[]]> {\n const grouped = new Map<string, PlaygroundEntry[]>();\n for (const entry of this.getAll()) {\n const group = entry.group ?? \"General\";\n const list = grouped.get(group) ?? [];\n list.push(entry);\n grouped.set(group, list);\n }\n return [...grouped.entries()];\n }\n\n /** Number of registered entries. */\n get size(): number {\n return this.entries.size;\n }\n}\n","/**\n * Built-in playground entries for the sonnet component library.\n *\n * These entries demonstrate @petrarca/sonnet-* components with static\n * mock data -- no application backend required. Each page is lazy-loaded.\n */\nimport { lazy } from \"react\";\nimport {\n Type,\n Palette,\n Grid,\n Braces,\n Check,\n Edit,\n Package,\n Table2,\n} from \"lucide-react\";\nimport type { PlaygroundEntry } from \"./types\";\n\nexport const builtinEntries: PlaygroundEntry[] = [\n {\n id: \"sonnet.typography\",\n title: \"Typography\",\n description:\n \"Living style-guide for the typography system. Font families, type scale, weights, heading hierarchy, color tiers, and component patterns.\",\n icon: Type,\n color: \"teal\",\n path: \"typography\",\n component: lazy(() => import(\"./pages/PlayTypography\")),\n group: \"Design System\",\n order: 0,\n },\n {\n id: \"sonnet.color-palette\",\n title: \"Color Palette\",\n description:\n \"Living reference for the color system. Semantic tokens, extended palette, badge colors, icon accents, and usage guidance.\",\n icon: Palette,\n color: \"violet\",\n path: \"color-palette\",\n component: lazy(() => import(\"./pages/PlayColorPalette\")),\n group: \"Design System\",\n order: 1,\n },\n {\n id: \"sonnet.core-components\",\n title: \"Core UI Components\",\n description:\n \"Test all core UI components (notifications, buttons, dialogs, forms, etc.) in isolation before production use.\",\n icon: Grid,\n color: \"indigo\",\n path: \"core-components\",\n component: lazy(() => import(\"./pages/PlayCoreComponents\")),\n group: \"Components\",\n order: 0,\n },\n {\n id: \"sonnet.json-editor\",\n title: \"JSON Editor\",\n description:\n \"Constrained JSON editor with selectively editable regions using JSON Pointer (RFC 6901). Readonly sections are visible but locked.\",\n icon: Braces,\n color: \"amber\",\n path: \"json-editor\",\n component: lazy(() => import(\"./pages/PlayJsonEditor\")),\n group: \"Components\",\n order: 1,\n },\n {\n id: \"sonnet.form-components\",\n title: \"Form Components\",\n description:\n \"Test custom Form wrapper components with labels, descriptions, and error states. Includes Input, Textarea, Number, Checkbox, Select, MultiSelect, and Tags.\",\n icon: Check,\n color: \"blue\",\n path: \"form-components\",\n component: lazy(() => import(\"./pages/PlayFormComponents\")),\n group: \"Forms\",\n order: 0,\n },\n {\n id: \"sonnet.forms\",\n title: \"Forms Renderer\",\n description:\n \"Test and experiment with the JsonSchemaFormRenderer component. Try different schemas and see real-time validation.\",\n icon: Edit,\n color: \"purple\",\n path: \"forms\",\n component: lazy(() => import(\"./pages/PlayForms\")),\n group: \"Forms\",\n order: 1,\n },\n {\n id: \"sonnet.form-widgets\",\n title: \"Form Widgets\",\n description:\n \"Pluggable field components consumed by the Forms Renderer. Each widget maps to a JSON Schema type or format and can be swapped independently.\",\n icon: Package,\n color: \"indigo\",\n path: \"form-widgets\",\n component: lazy(() => import(\"./pages/PlayFormWidgets\")),\n group: \"Forms\",\n order: 2,\n },\n {\n id: \"sonnet.data-components\",\n title: \"Data Components\",\n description:\n \"Interactive sandbox for SearchInput (simple and faceted mode) and EntityTable. Includes a live table wired to the faceted search.\",\n icon: Table2,\n color: \"teal\",\n path: \"data-components\",\n component: lazy(() => import(\"./pages/PlayDataComponents\")),\n group: \"Data\",\n order: 0,\n },\n];\n","/**\n * Factory that turns a PlaygroundRegistry into a ServiceModule.\n *\n * Generates routes (lazy + Suspense), sidebar navigation, and the\n * overview index page -- all derived from the registry entries.\n * The consumer just passes a registry and optional overrides.\n */\nimport React, { Suspense } from \"react\";\nimport { FlaskConical } from \"lucide-react\";\nimport type { ServiceModule, NavGroup } from \"@petrarca/sonnet-shell\";\nimport type { PlaygroundRegistry } from \"./PlaygroundRegistry\";\nimport type { PlaygroundModuleOptions } from \"./types\";\nimport { PlaygroundOverview } from \"./PlaygroundOverview\";\n\n/**\n * Build sidebar navigation groups from registry entries.\n *\n * Groups entries by their `group` field. Each group becomes a NavGroup\n * with a heading. Ungrouped entries go into a \"General\" group.\n */\nfunction buildNavigation(\n registry: PlaygroundRegistry,\n basePath: string,\n): NavGroup[] {\n const groups = registry.getGrouped();\n const navGroups: NavGroup[] = [];\n\n // Overview link is always first\n const overviewGroup: NavGroup = {\n id: \"playground-overview\",\n links: [{ label: \"Overview\", path: basePath }],\n };\n navGroups.push(overviewGroup);\n\n for (const [groupLabel, entries] of groups) {\n navGroups.push({\n id: `playground-${groupLabel.toLowerCase().replace(/\\s+/g, \"-\")}`,\n heading: groupLabel,\n links: entries.map((e) => ({\n label: e.title,\n path: `${basePath}/${e.path}`,\n })),\n });\n }\n\n return navGroups;\n}\n\n/** Wrap a component in Suspense for lazy loading. */\nfunction suspended(Component: React.ComponentType): React.ReactElement {\n return (\n <Suspense>\n <Component />\n </Suspense>\n );\n}\n\n/**\n * Create a ServiceModule for the playground.\n *\n * Returns a complete module definition ready to pass to\n * createModuleRegistry(). Routes, navigation, and the overview page\n * are all generated from the registry.\n */\nexport function createPlaygroundModule(\n registry: PlaygroundRegistry,\n options: PlaygroundModuleOptions = {},\n): ServiceModule {\n const {\n icon = FlaskConical,\n label = \"Playground\",\n basePath = \"/playground\",\n overviewTitle = \"Developer Playground\",\n overviewSubtitle = \"Experiment with components, test features, and explore capabilities.\",\n grouped = true,\n } = options;\n\n // Strip leading slash for route paths (React Router relative convention)\n const routeBase = basePath.replace(/^\\//, \"\");\n\n const routes = [\n {\n path: routeBase,\n element: suspended(() => (\n <PlaygroundOverview\n registry={registry}\n basePath={basePath}\n title={overviewTitle}\n subtitle={overviewSubtitle}\n grouped={grouped}\n />\n )),\n },\n ...registry.getAll().map((entry) => ({\n path: `${routeBase}/${entry.path}`,\n element: suspended(entry.component),\n })),\n ];\n\n return {\n id: \"playground\",\n label,\n icon,\n basePath,\n pinBottom: true,\n navigation: buildNavigation(registry, basePath),\n routes,\n };\n}\n","/**\n * PlaygroundOverview -- tile grid rendered from a PlaygroundRegistry.\n *\n * Displays cards with colored icons. Clicking a card navigates to the\n * playground page. A toggle in the header lets the user switch between\n * grouped (by category) and flat (all tiles) views.\n */\nimport React, { useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { LayoutGrid, List } from \"lucide-react\";\nimport { Card, Button, SimpleTooltip } from \"@petrarca/sonnet-ui\";\nimport type { PlaygroundRegistry } from \"./PlaygroundRegistry\";\nimport type { PlaygroundColor, PlaygroundEntry } from \"./types\";\n\n// Maps colour name to icon container classes (bg-*-100 text-*-600 pairing).\nconst COLOR_CLASSES: Record<PlaygroundColor, string> = {\n blue: \"bg-blue-100 text-blue-600\",\n violet: \"bg-violet-100 text-violet-600\",\n indigo: \"bg-indigo-100 text-indigo-600\",\n purple: \"bg-purple-100 text-purple-600\",\n teal: \"bg-teal-100 text-teal-600\",\n amber: \"bg-amber-100 text-amber-600\",\n emerald: \"bg-emerald-100 text-emerald-600\",\n rose: \"bg-rose-100 text-rose-600\",\n orange: \"bg-orange-100 text-orange-600\",\n cyan: \"bg-cyan-100 text-cyan-600\",\n};\n\nconst TILE_GRID = \"grid grid-cols-1 sm:grid-cols-2 2xl:grid-cols-3 gap-6\";\n\ninterface PlaygroundOverviewProps {\n registry: PlaygroundRegistry;\n basePath: string;\n title: string;\n subtitle: string;\n /** Initial grouping state. Defaults to true. User can toggle at runtime. */\n grouped?: boolean;\n}\n\ninterface TileProps {\n item: PlaygroundEntry;\n onNavigate: (path: string) => void;\n}\n\n/** A single playground tile card. */\nfunction PlaygroundTile({ item, onNavigate }: TileProps): React.ReactElement {\n return (\n <Card\n className=\"cursor-pointer shadow-sm border rounded-md p-6 transition-transform duration-200 motion-safe:hover:-translate-y-1\"\n onClick={() => onNavigate(item.path)}\n >\n <div className=\"flex items-center mb-6\">\n <div\n className={`flex items-center justify-center w-[50px] h-[50px] rounded-md ${COLOR_CLASSES[item.color] ?? COLOR_CLASSES.blue}`}\n >\n <item.icon size={28} />\n </div>\n </div>\n <h3 className=\"heading-section mb-1\">{item.title}</h3>\n <p className=\"text-sm text-muted-foreground\">{item.description}</p>\n </Card>\n );\n}\n\n/** Developer playground overview -- card grid linking to all registered pages. */\nexport function PlaygroundOverview({\n registry,\n basePath,\n title,\n subtitle,\n grouped: initialGrouped = true,\n}: PlaygroundOverviewProps): React.ReactElement {\n const navigate = useNavigate();\n const [grouped, setGrouped] = useState(initialGrouped);\n\n const handleNavigate = (path: string) => navigate(`${basePath}/${path}`);\n\n return (\n <div className=\"w-full py-12 px-4\">\n <div className=\"flex items-start justify-between mb-6\">\n <div>\n <h1 className=\"mb-1\">{title}</h1>\n <p className=\"text-lg text-muted-foreground\">{subtitle}</p>\n </div>\n <SimpleTooltip label={grouped ? \"Show flat list\" : \"Group by category\"}>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setGrouped((v) => !v)}\n >\n {grouped ? (\n <LayoutGrid className=\"h-4 w-4\" />\n ) : (\n <List className=\"h-4 w-4\" />\n )}\n </Button>\n </SimpleTooltip>\n </div>\n\n {grouped ? (\n registry.getGrouped().map(([groupLabel, entries]) => (\n <div key={groupLabel} className=\"mb-8\">\n <h2 className=\"text-sm font-medium text-muted-foreground uppercase tracking-wider mb-4\">\n {groupLabel}\n </h2>\n <div className={TILE_GRID}>\n {entries.map((item) => (\n <PlaygroundTile\n key={item.id}\n item={item}\n onNavigate={handleNavigate}\n />\n ))}\n </div>\n </div>\n ))\n ) : (\n <div className={TILE_GRID}>\n {registry.getAll().map((item) => (\n <PlaygroundTile\n key={item.id}\n item={item}\n onNavigate={handleNavigate}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * @petrarca/sonnet-playground\n *\n * Playground framework and built-in component demos for the Petrarca\n * Sonnet component library.\n *\n * Usage:\n *\n * import {\n * createPlaygroundRegistry,\n * createPlaygroundModule,\n * } from \"@petrarca/sonnet-playground\";\n *\n * const playground = createPlaygroundRegistry();\n * // Optionally register additional entries:\n * playground.register({ id: \"app.custom\", ... });\n *\n * const playgroundModule = createPlaygroundModule(playground);\n */\n\nimport { PlaygroundRegistry } from \"./PlaygroundRegistry\";\nimport { builtinEntries } from \"./builtinEntries\";\n\n// Framework\nexport { PlaygroundRegistry } from \"./PlaygroundRegistry\";\nexport { createPlaygroundModule } from \"./createPlaygroundModule\";\nexport { PlaygroundOverview } from \"./PlaygroundOverview\";\nexport { PlaygroundSection } from \"./PlaygroundSection\";\n\n// Types\nexport type {\n PlaygroundEntry,\n PlaygroundColor,\n PlaygroundModuleOptions,\n} from \"./types\";\n\n// Built-in entries (for selective use or extending)\nexport { builtinEntries } from \"./builtinEntries\";\n\n/**\n * Convenience factory: creates a new PlaygroundRegistry pre-populated\n * with all built-in sonnet component demos.\n */\nexport function createPlaygroundRegistry(): PlaygroundRegistry {\n const registry = new PlaygroundRegistry();\n registry.registerAll(builtinEntries);\n return registry;\n}\n"],"mappings":"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","names":["jsx"]}