themelia-react-ui 1.0.0 → 1.0.1
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/CHANGELOG.md +53 -1
- package/README.md +39 -6
- package/dist/{accordion-BRWh3BM1.js → accordion-B-5Focs8.js} +2 -2
- package/dist/{accordion-BRWh3BM1.js.map → accordion-B-5Focs8.js.map} +1 -1
- package/dist/{affordance-geometry.generated-DXl-Zb3Z.js → affordance-geometry.generated-D8Jcfb0G.js} +10 -1
- package/dist/affordance-geometry.generated-D8Jcfb0G.js.map +1 -0
- package/dist/{ai-artifact-DkOArd3-.js → ai-artifact-B5tPpXnS.js} +5 -5
- package/dist/{ai-artifact-DkOArd3-.js.map → ai-artifact-B5tPpXnS.js.map} +1 -1
- package/dist/{app-shell-BH_bGcE2.js → app-shell-at8Kt4l_.js} +3 -3
- package/dist/{app-shell-BH_bGcE2.js.map → app-shell-at8Kt4l_.js.map} +1 -1
- package/dist/{attachment-DsLgNHXt.js → attachment-BO2R47wn.js} +2 -2
- package/dist/{attachment-DsLgNHXt.js.map → attachment-BO2R47wn.js.map} +1 -1
- package/dist/{auth-BDB1V_2q.js → auth-OFwWSAap.js} +4 -4
- package/dist/{auth-BDB1V_2q.js.map → auth-OFwWSAap.js.map} +1 -1
- package/dist/{avatar-CsnWaYmt.js → avatar-Bpzty83c.js} +73 -68
- package/dist/avatar-Bpzty83c.js.map +1 -0
- package/dist/base/accordion.js +1 -1
- package/dist/base/attachment.js +1 -1
- package/dist/base/avatar.js +1 -1
- package/dist/base/cards.js +2 -2
- package/dist/base/cascader.js +6 -6
- package/dist/base/choice-inputs.js +3 -2
- package/dist/base/code-block.js +1 -1
- package/dist/base/combobox.js +1 -1
- package/dist/base/command.js +1 -1
- package/dist/base/date-pickers.js +2 -2
- package/dist/base/display.js +1 -1
- package/dist/base/feedback.js +4 -3
- package/dist/base/forms-numeric.js +1 -1
- package/dist/base/forms.js +2 -2
- package/dist/base/inline-edit.js +236 -0
- package/dist/base/inline-edit.js.map +1 -0
- package/dist/base/media-gallery.js +1 -1
- package/dist/base/navigation.js +3 -3
- package/dist/base/popover-menu.js +1 -1
- package/dist/base/popover.js +1 -1
- package/dist/base/repeaters.js +4 -4
- package/dist/base/sheet.js +1 -1
- package/dist/base/sidebar.js +1 -1
- package/dist/base/signature-pad.js +3 -3
- package/dist/base/skeleton.js +1 -1
- package/dist/base/spinner.js +1 -1
- package/dist/base/structure.js +1 -1
- package/dist/base/text-inputs.js +2 -2
- package/dist/base/tree.js +1 -1
- package/dist/base/upload-queue.js +1 -1
- package/dist/base/upload.js +3 -3
- package/dist/base/value-inputs.js +1 -1
- package/dist/blocks/admin.js +6 -6
- package/dist/blocks/ai.js +6 -6
- package/dist/blocks/analytics/usage-card.js +3 -0
- package/dist/blocks/analytics.js +361 -360
- package/dist/blocks/analytics.js.map +1 -1
- package/dist/blocks/collaboration.js +1 -1
- package/dist/blocks/commerce/receipt.js +1 -1
- package/dist/blocks/commerce.js +15 -15
- package/dist/blocks/data-display.js +1 -1
- package/dist/blocks/onboarding.js +3 -3
- package/dist/blocks/planning.js +311 -0
- package/dist/blocks/planning.js.map +1 -0
- package/dist/blocks/timelines.js +1 -1
- package/dist/{cards-BMB0laLG.js → cards-Cum56XOF.js} +3 -3
- package/dist/{cards-BMB0laLG.js.map → cards-Cum56XOF.js.map} +1 -1
- package/dist/checkbox-BavDRjF7.js +34 -0
- package/dist/checkbox-BavDRjF7.js.map +1 -0
- package/dist/{choice-inputs-CavA-SGV.js → choice-inputs-CC7XqPQO.js} +166 -191
- package/dist/choice-inputs-CC7XqPQO.js.map +1 -0
- package/dist/{code-block-DIlqzoiD.js → code-block-xHKbAJDj.js} +2 -2
- package/dist/{code-block-DIlqzoiD.js.map → code-block-xHKbAJDj.js.map} +1 -1
- package/dist/{combobox-HDftpzxt.js → combobox-DEbdr-3u.js} +2 -2
- package/dist/{combobox-HDftpzxt.js.map → combobox-DEbdr-3u.js.map} +1 -1
- package/dist/{combobox-CHD750do.js → combobox-xv3yJbzl.js} +7 -7
- package/dist/{combobox-CHD750do.js.map → combobox-xv3yJbzl.js.map} +1 -1
- package/dist/{command-D0kWBZfd.js → command-C0xe7u9B.js} +2 -2
- package/dist/{command-D0kWBZfd.js.map → command-C0xe7u9B.js.map} +1 -1
- package/dist/{comments-CvYeglMS.js → comments-DviPLnwy.js} +8 -8
- package/dist/{comments-CvYeglMS.js.map → comments-DviPLnwy.js.map} +1 -1
- package/dist/components/base/cards/smart-card.types.d.ts +2 -0
- package/dist/components/base/inline-edit/index.d.ts +2 -0
- package/dist/components/base/inline-edit/inline-edit.d.ts +81 -0
- package/dist/components/base/inline-edit/inline-edit.recipes.d.ts +6 -0
- package/dist/components/base/structure/structure.recipes.d.ts +6 -0
- package/dist/components/blocks/analytics/index.d.ts +1 -0
- package/dist/components/blocks/analytics/usage-card/index.d.ts +3 -0
- package/dist/components/blocks/analytics/usage-card/types.d.ts +52 -0
- package/dist/components/blocks/analytics/usage-card/usage-card.d.ts +6 -0
- package/dist/components/blocks/analytics/usage-card/usage-card.data.d.ts +19 -0
- package/dist/components/blocks/analytics/usage-card/usage-card.strings.d.ts +22 -0
- package/dist/components/blocks/planning/index.d.ts +3 -0
- package/dist/components/blocks/planning/task-card.d.ts +3 -0
- package/dist/components/blocks/planning/task-card.strings.d.ts +15 -0
- package/dist/components/blocks/planning/task-card.types.d.ts +66 -0
- package/dist/{containers-CPNyNo1_.js → containers-6s-ucRQQ.js} +3 -2
- package/dist/containers-6s-ucRQQ.js.map +1 -0
- package/dist/{data-display-b3d1TfL4.js → data-display-cWIRdeaf.js} +8 -8
- package/dist/{data-display-b3d1TfL4.js.map → data-display-cWIRdeaf.js.map} +1 -1
- package/dist/{date-picker-DI6kPNof.js → date-picker-CA6ogMQB.js} +5 -5
- package/dist/{date-picker-DI6kPNof.js.map → date-picker-CA6ogMQB.js.map} +1 -1
- package/dist/{date-pickers-ByP6Cv2z.js → date-pickers-BPIKLdsD.js} +4 -4
- package/dist/{date-pickers-ByP6Cv2z.js.map → date-pickers-BPIKLdsD.js.map} +1 -1
- package/dist/{display-ByI5tkqy.js → display-CFM5myut.js} +3 -3
- package/dist/{display-ByI5tkqy.js.map → display-CFM5myut.js.map} +1 -1
- package/dist/features/activities.js +4 -4
- package/dist/features/ai-chat.js +4 -4
- package/dist/features/async-preview.js +4 -4
- package/dist/features/combobox.js +1 -1
- package/dist/features/comments.js +1 -1
- package/dist/features/data-view.js +5 -5
- package/dist/features/email-composer.js +11 -11
- package/dist/features/event-calendar.js +2 -2
- package/dist/features/event-calendar.js.map +1 -1
- package/dist/features/filters.js +1 -1
- package/dist/features/global-search.js +6 -6
- package/dist/features/map.js +3 -3
- package/dist/features/media-library.js +8 -8
- package/dist/features/mentions.js +1 -1
- package/dist/features/overlays.js +1 -1
- package/dist/features/products.js +43 -42
- package/dist/features/products.js.map +1 -1
- package/dist/features/questionnaire.js +107 -106
- package/dist/features/questionnaire.js.map +1 -1
- package/dist/features/resource-assignment.js +5 -5
- package/dist/features/resource.js +7 -7
- package/dist/features/rich-text-editor.js +1 -1
- package/dist/features/schema-form.js +11 -11
- package/dist/features/schema-form.js.map +1 -1
- package/dist/features/suggestions.js +2 -2
- package/dist/features/sync.js +2 -2
- package/dist/features/table.js +1 -1
- package/dist/features/theme-tweaker.js +11 -11
- package/dist/feedback-DWUkgEjR.js +233 -0
- package/dist/feedback-DWUkgEjR.js.map +1 -0
- package/dist/{field-affordance-recipes-CHdUtn2Z.js → field-affordance-recipes-IjzPrCTw.js} +2 -2
- package/dist/{field-affordance-recipes-CHdUtn2Z.js.map → field-affordance-recipes-IjzPrCTw.js.map} +1 -1
- package/dist/{filters-qtrcv1hf.js → filters-BhsUtsxW.js} +9 -9
- package/dist/{filters-qtrcv1hf.js.map → filters-BhsUtsxW.js.map} +1 -1
- package/dist/{form-field-C6W-y2hR.js → form-field-Bn7ZH1rA.js} +2 -2
- package/dist/{form-field-C6W-y2hR.js.map → form-field-Bn7ZH1rA.js.map} +1 -1
- package/dist/{form-sizing-39NohSqn.js → form-sizing-BWs70_a2.js} +4 -3
- package/dist/{form-sizing-39NohSqn.js.map → form-sizing-BWs70_a2.js.map} +1 -1
- package/dist/{forms-BF-HSbff.js → forms-B7pzS25o.js} +5 -5
- package/dist/{forms-BF-HSbff.js.map → forms-B7pzS25o.js.map} +1 -1
- package/dist/{forms-numeric-Y15Bg5Nr.js → forms-numeric-BUBxELBv.js} +18 -18
- package/dist/{forms-numeric-Y15Bg5Nr.js.map → forms-numeric-BUBxELBv.js.map} +1 -1
- package/dist/{header-B8ex54cY.js → header-C59QZyxV.js} +6 -6
- package/dist/{header-B8ex54cY.js.map → header-C59QZyxV.js.map} +1 -1
- package/dist/{hooks-D6UBVzf2.js → hooks-CM1Vp9Pe.js} +2 -2
- package/dist/{hooks-D6UBVzf2.js.map → hooks-CM1Vp9Pe.js.map} +1 -1
- package/dist/{input-DMorJiTI.js → input-WwYQZJzG.js} +3 -3
- package/dist/{input-DMorJiTI.js.map → input-WwYQZJzG.js.map} +1 -1
- package/dist/layout/app-shell.js +1 -1
- package/dist/layout/auth.js +1 -1
- package/dist/layout/containers.js +1 -1
- package/dist/layout/header.js +1 -1
- package/dist/layout/hooks.js +1 -1
- package/dist/layout/navigation.js +1 -1
- package/dist/layout/page.js +1 -1
- package/dist/layout/settings.js +1 -1
- package/dist/layout/sidebar.js +2 -2
- package/dist/layout/workspace.js +1 -1
- package/dist/layout.js +10 -10
- package/dist/lib/affordance-geometry.generated.d.ts +9 -0
- package/dist/{media-gallery-Ca4YmvBi.js → media-gallery-DhDRgnVZ.js} +2 -2
- package/dist/{media-gallery-Ca4YmvBi.js.map → media-gallery-DhDRgnVZ.js.map} +1 -1
- package/dist/{mentions-CRio8mTm.js → mentions-B8Ko-fhS.js} +4 -4
- package/dist/{mentions-CRio8mTm.js.map → mentions-B8Ko-fhS.js.map} +1 -1
- package/dist/{message-B5-6LeYl.js → message-COsdEvXT.js} +2 -2
- package/dist/{message-B5-6LeYl.js.map → message-COsdEvXT.js.map} +1 -1
- package/dist/{navigation-DqEITuD2.js → navigation-BUIn2Qxx.js} +2 -2
- package/dist/{navigation-DqEITuD2.js.map → navigation-BUIn2Qxx.js.map} +1 -1
- package/dist/{navigation-BTrEb5u4.js → navigation-DBEu4POU.js} +3 -3
- package/dist/{navigation-BTrEb5u4.js.map → navigation-DBEu4POU.js.map} +1 -1
- package/dist/{navigation-row-recipes-BFpvGPGE.js → navigation-row-recipes-GVh11KiT.js} +2 -2
- package/dist/{navigation-row-recipes-BFpvGPGE.js.map → navigation-row-recipes-GVh11KiT.js.map} +1 -1
- package/dist/{overlays-DBqV0J_V.js → overlays-B11wZka6.js} +4 -4
- package/dist/{overlays-DBqV0J_V.js.map → overlays-B11wZka6.js.map} +1 -1
- package/dist/{page-BMBGjJY8.js → page-BV6ioQg3.js} +5 -5
- package/dist/{page-BMBGjJY8.js.map → page-BV6ioQg3.js.map} +1 -1
- package/dist/{page-header-BH41wCyY.js → page-header-DQRa0jug.js} +2 -2
- package/dist/{page-header-BH41wCyY.js.map → page-header-DQRa0jug.js.map} +1 -1
- package/dist/{popover-TInGXsjg.js → popover-CoLDEg19.js} +2 -2
- package/dist/{popover-TInGXsjg.js.map → popover-CoLDEg19.js.map} +1 -1
- package/dist/{popover-menu-BzqXqE72.js → popover-menu-GLIQTJTx.js} +4 -4
- package/dist/{popover-menu-BzqXqE72.js.map → popover-menu-GLIQTJTx.js.map} +1 -1
- package/dist/progress-BOe8l_FZ.js +168 -0
- package/dist/progress-BOe8l_FZ.js.map +1 -0
- package/dist/{receipt-DYWTQGEc.js → receipt-BM8xsGFy.js} +3 -3
- package/dist/{receipt-DYWTQGEc.js.map → receipt-BM8xsGFy.js.map} +1 -1
- package/dist/{rich-text-editor-BZSJchAF.js → rich-text-editor-CoVrTiSv.js} +2 -2
- package/dist/{rich-text-editor-BZSJchAF.js.map → rich-text-editor-CoVrTiSv.js.map} +1 -1
- package/dist/services/actions.js +1 -1
- package/dist/{settings-D9f_wdIC.js → settings-BnCyyPOJ.js} +3 -3
- package/dist/{settings-D9f_wdIC.js.map → settings-BnCyyPOJ.js.map} +1 -1
- package/dist/{sheet-vOUloJ4O.js → sheet-B-q914Ad.js} +2 -2
- package/dist/{sheet-vOUloJ4O.js.map → sheet-B-q914Ad.js.map} +1 -1
- package/dist/{sidebar-C3UKkbC5.js → sidebar-C_2p3Tfj.js} +6 -6
- package/dist/{sidebar-C3UKkbC5.js.map → sidebar-C_2p3Tfj.js.map} +1 -1
- package/dist/{sidebar-CNBKFDRW.js → sidebar-DPR7fmqN.js} +4 -4
- package/dist/{sidebar-CNBKFDRW.js.map → sidebar-DPR7fmqN.js.map} +1 -1
- package/dist/{skeleton-BvRhK8y-.js → skeleton-CmT1ppqb.js} +2 -2
- package/dist/{skeleton-BvRhK8y-.js.map → skeleton-CmT1ppqb.js.map} +1 -1
- package/dist/{smart-card-B5FcKODh.js → smart-card-CYLn46e6.js} +119 -118
- package/dist/smart-card-CYLn46e6.js.map +1 -0
- package/dist/{spinner-JtlFJ7Nc.js → spinner-BY9HbUEh.js} +2 -2
- package/dist/{spinner-JtlFJ7Nc.js.map → spinner-BY9HbUEh.js.map} +1 -1
- package/dist/{structure-BvW4vG4H.js → structure-MvKVq-oL.js} +13 -13
- package/dist/{structure-BvW4vG4H.js.map → structure-MvKVq-oL.js.map} +1 -1
- package/dist/{structure.recipes-DjqnBpwz.js → structure.recipes-Bqhf9oIh.js} +2 -2
- package/dist/structure.recipes-Bqhf9oIh.js.map +1 -0
- package/dist/style.css +1 -1
- package/dist/{table-ghtAQ-JJ.js → table-BS39xnlq.js} +6 -6
- package/dist/{table-ghtAQ-JJ.js.map → table-BS39xnlq.js.map} +1 -1
- package/dist/{tabs-DY09eoZg.js → tabs-B5cNMWiP.js} +2 -2
- package/dist/{tabs-DY09eoZg.js.map → tabs-B5cNMWiP.js.map} +1 -1
- package/dist/{text-inputs-BhOcedTo.js → text-inputs-Dh9XGyiS.js} +4 -4
- package/dist/{text-inputs-BhOcedTo.js.map → text-inputs-Dh9XGyiS.js.map} +1 -1
- package/dist/{timelines-CnZ2S_Gr.js → timelines-B9ICkQcV.js} +2 -2
- package/dist/{timelines-CnZ2S_Gr.js.map → timelines-B9ICkQcV.js.map} +1 -1
- package/dist/{tree-COD9BGdU.js → tree-BdWSaoFT.js} +2 -2
- package/dist/{tree-COD9BGdU.js.map → tree-BdWSaoFT.js.map} +1 -1
- package/dist/{upload-DIzw1dx1.js → upload-D8Za5x_c.js} +4 -4
- package/dist/{upload-DIzw1dx1.js.map → upload-D8Za5x_c.js.map} +1 -1
- package/dist/{upload-queue-xRJ3qDmd.js → upload-queue-hIIYBLS7.js} +3 -3
- package/dist/{upload-queue-xRJ3qDmd.js.map → upload-queue-hIIYBLS7.js.map} +1 -1
- package/dist/usage-card-DnmnJ58D.js +458 -0
- package/dist/usage-card-DnmnJ58D.js.map +1 -0
- package/dist/{value-inputs-Co1fu-xv.js → value-inputs-DX2dbxjj.js} +7 -7
- package/dist/{value-inputs-Co1fu-xv.js.map → value-inputs-DX2dbxjj.js.map} +1 -1
- package/dist/{workspace-Cbqz4FzN.js → workspace-C1PZmk8L.js} +4 -4
- package/dist/{workspace-Cbqz4FzN.js.map → workspace-C1PZmk8L.js.map} +1 -1
- package/docs/consumer-skill/SKILL.md +120 -17
- package/docs/consumer-skill/examples/detail.tsx +7 -8
- package/docs/consumer-skill/examples/manifest.json +14 -0
- package/docs/consumer-skill/examples/planning-usage-edit.tsx +29 -0
- package/docs/consumer-skill/examples/scoped-directory.tsx +77 -0
- package/docs/consumer-skill/references/composition.md +103 -0
- package/docs/consumer-skill/references/design-contract.md +4 -3
- package/docs/consumer-skill/references/discovery.md +17 -0
- package/docs/consumer-skill/references/installation.md +20 -3
- package/docs/consumer-skill/references/integration.md +12 -0
- package/docs/consumer-skill/references/public-api.json +452 -229
- package/docs/consumer-skill/references/review.md +74 -0
- package/docs/consumer-skill/references/tokens.json +1 -1
- package/docs/consumer-skill/references/workflows.md +97 -1
- package/docs/consumer-skill/scripts/check-composition.mjs +31 -0
- package/docs/consumer-skill/scripts/composition-policy.mjs +155 -0
- package/docs/consumer-skill/scripts/discovery.mjs +6 -1
- package/docs/consumer-skill/scripts/surface-policy.mjs +22 -0
- package/package.json +24 -3
- package/dist/affordance-geometry.generated-DXl-Zb3Z.js.map +0 -1
- package/dist/avatar-CsnWaYmt.js.map +0 -1
- package/dist/choice-inputs-CavA-SGV.js.map +0 -1
- package/dist/containers-CPNyNo1_.js.map +0 -1
- package/dist/feedback-a9j9J-9A.js +0 -391
- package/dist/feedback-a9j9J-9A.js.map +0 -1
- package/dist/smart-card-B5FcKODh.js.map +0 -1
- package/dist/structure.recipes-DjqnBpwz.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-Cbqz4FzN.js","names":["Icon","React","_Fragment","Text","DisplayLabel","Stack","Progress","Value","SecondaryValue","Badge","ItemMedia","ItemDescription","ItemActions","ItemGroup","ItemContent","Item","ItemTitle","useLayoutLinkRenderer","React$1","Fragment$1","defaultWorkspaceRecordHeaderStrings","defaultWorkspaceNavStrings","value","defaultWorkspaceLocaleStripStrings","CONTENT_WIDTH_CLASS","STATUS_ICON","Check","Circle","CircleAlert","STATUS_CLASS","clampPercent","DynamicWorkspaceIcon","icon","getStatus","item","active","WorkspaceLayout","props","hasSidebar","sidebar","hasRail","rail","ariaLabel","cn","contentWidth","className","header","headerClassName","bodyClassName","sidebarClassName","mainClassName","contentClassName","railClassName","WorkspaceRecordHeader","stringsProp","strings","useStrings","hasMetadata","metadata","media","titleSlot","Heading","title","badges","status","description","index","actions","secondaryActions","headerEnd","WorkspaceNav","resolvedRenderLink","groups","group","activeId","StatusIcon","completion","handleSelect","context","renderItem","rowContent","itemClassName","linkNode","footerSlot","WorkspaceLocaleStrip","handleLocaleClick","locale","onLocaleChange","locales","activeLocale","Button"],"sources":["../src/components/layout/workspace/workspace.strings.ts","../src/components/layout/workspace/workspace.tsx"],"sourcesContent":["export interface WorkspaceRecordHeaderStrings {\n\tmetadataLabel: string;\n\tactionsLabel: string;\n\tsecondaryActionsLabel: string;\n}\n\nexport const defaultWorkspaceRecordHeaderStrings: WorkspaceRecordHeaderStrings = {\n\tmetadataLabel: 'Record metadata',\n\tactionsLabel: 'Record actions',\n\tsecondaryActionsLabel: 'Secondary actions',\n};\n\nexport interface WorkspaceNavStrings {\n\tariaLabel: string;\n\tcompletionLabel: (value: number) => string;\n\tcurrentLabel: string;\n}\n\nexport const defaultWorkspaceNavStrings: WorkspaceNavStrings = {\n\tariaLabel: 'Workspace navigation',\n\tcompletionLabel: (value) => `${value}% complete`,\n\tcurrentLabel: 'Current',\n};\n\nexport interface WorkspaceLocaleStripStrings {\n\tariaLabel: string;\n\tactiveLabel: string;\n\tcompletionLabel: (value: number) => string;\n}\n\nexport const defaultWorkspaceLocaleStripStrings: WorkspaceLocaleStripStrings = {\n\tariaLabel: 'Workspace locales',\n\tactiveLabel: 'Active',\n\tcompletionLabel: (value) => `${value}% complete`,\n};\n","import * as React from 'react';\nimport {\n\tCheck,\n\tCircle,\n\tCircleAlert,\n\ttype LucideIcon,\n\t} from 'lucide-react';\n\nimport { Badge } from '../../base/badge';\nimport { Button } from '../../base/buttons';\nimport { Progress } from '../../base/feedback';\nimport {\n\tItem,\n\tItemActions,\n\tItemContent,\n\tItemDescription,\n\tItemGroup,\n\tItemMedia,\n\tItemTitle,\n} from '../../base/item';\nimport { Stack } from '../../base/structure';\nimport { SecondaryValue, Value } from '../../primitives';\nimport { DisplayLabel, Heading, Text } from '../../typography';\nimport { useStrings } from '../../../lib/strings';\nimport { cn } from '../../../lib/utils';\n\nimport { useLayoutLinkRenderer } from '../hooks';\nimport type { LayoutIconComponent } from '../layout.types';\nimport {\n\tdefaultWorkspaceLocaleStripStrings,\n\tdefaultWorkspaceNavStrings,\n\tdefaultWorkspaceRecordHeaderStrings,\n} from './workspace.strings';\nimport type {\n\tWorkspaceContentWidth,\n\tWorkspaceLayoutProps,\n\tWorkspaceLocaleItem,\n\tWorkspaceLocaleStripProps,\n\tWorkspaceNavItem,\n\tWorkspaceNavItemStatus,\n\tWorkspaceNavProps,\n\tWorkspaceRecordHeaderProps,\n} from './workspace.types';\n\nconst CONTENT_WIDTH_CLASS: Record<WorkspaceContentWidth, string> = {\n\tdefault: '[--workspace-content-max-width:58rem]',\n\twide: '[--workspace-content-max-width:86rem]',\n\tfull: '[--workspace-content-max-width:none]',\n};\n\nconst STATUS_ICON: Record<WorkspaceNavItemStatus, LucideIcon> = {\n\tcomplete: Check,\n\tcurrent: Circle,\n\tattention: CircleAlert,\n\tdisabled: Circle,\n\tidle: Circle,\n};\n\nconst STATUS_CLASS: Record<WorkspaceNavItemStatus, string> = {\n\tcomplete: 'text-success',\n\tcurrent: 'text-primary-accent',\n\tattention: 'text-warning',\n\tdisabled: 'text-muted-foreground/40',\n\tidle: 'text-muted-foreground',\n};\n\nfunction clampPercent(value: number): number {\n\tif (!Number.isFinite(value)) return 0;\n\treturn Math.min(100, Math.max(0, Math.round(value)));\n}\n\nfunction DynamicWorkspaceIcon({\n\ticon,\n\tclassName,\n}: {\n\t/** Leading glyph. */\n\ticon?: LayoutIconComponent;\n\tclassName?: string;\n}) {\n\tif (!icon) return null;\n\tconst Icon = icon;\n\treturn <Icon className={className} aria-hidden=\"true\" />;\n}\n\nfunction getStatus(item: WorkspaceNavItem, active: boolean): WorkspaceNavItemStatus {\n\tif (item.disabled) return 'disabled';\n\tif (item.status) return item.status;\n\tif (item.warning) return 'attention';\n\tif (active) return 'current';\n\tif (item.completion !== undefined && clampPercent(item.completion) >= 100) return 'complete';\n\treturn 'idle';\n}\n\nexport const WorkspaceLayout = React.forwardRef<HTMLDivElement, WorkspaceLayoutProps>(function WorkspaceLayout({\n\theader,\n\tsidebar,\n\trail,\n\tchildren,\n\tcontentWidth = 'wide',\n\tclassName,\n\theaderClassName,\n\tbodyClassName,\n\tsidebarClassName,\n\tmainClassName,\n\tcontentClassName,\n\trailClassName,\n\tariaLabel,\n\tstyle,\n\t...props\n}, ref) {\n\tconst hasSidebar = !!sidebar;\n\tconst hasRail = !!rail;\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\n\t\t\t\t'workspace-layout--component flex h-full min-h-0 w-full flex-col overflow-hidden rounded-radius-md border border-border bg-background text-foreground',\n\t\t\t\t'[--workspace-sidebar-width:16rem] [--workspace-rail-width:20rem]',\n\t\t\t\tCONTENT_WIDTH_CLASS[contentWidth],\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tstyle={style}\n\t\t\t{...props}\n\t\t>\n\t\t\t{!!header && (\n\t\t\t\t<header className={cn('workspace-layout--header shrink-0 border-b border-border bg-background', headerClassName)}>\n\t\t\t\t\t{header}\n\t\t\t\t</header>\n\t\t\t)}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'workspace-layout--body grid min-h-0 flex-1 grid-cols-1 bg-muted/20',\n\t\t\t\t\thasSidebar && !hasRail && 'min-[80rem]:grid-cols-[var(--workspace-sidebar-width)_minmax(0,1fr)]',\n\t\t\t\t\thasSidebar && hasRail && 'min-[80rem]:grid-cols-[var(--workspace-sidebar-width)_minmax(0,1fr)] min-[96rem]:grid-cols-[var(--workspace-sidebar-width)_minmax(0,1fr)_var(--workspace-rail-width)]',\n\t\t\t\t\t!hasSidebar && hasRail && 'min-[96rem]:grid-cols-[minmax(0,1fr)_var(--workspace-rail-width)]',\n\t\t\t\t\tbodyClassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{!!hasSidebar && (\n\t\t\t\t\t<aside\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'workspace-layout--sidebar hidden min-h-0 border-r border-border bg-background min-[80rem]:block',\n\t\t\t\t\t\t\tsidebarClassName,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"h-full overflow-y-auto overscroll-contain p-space-lg\">\n\t\t\t\t\t\t\t{sidebar}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</aside>\n\t\t\t\t)}\n\t\t\t\t<main\n\t\t\t\t\ttabIndex={0}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t'workspace-layout--main min-h-0 min-w-0 overflow-y-auto overscroll-contain focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/70 focus-visible:ring-inset',\n\t\t\t\t\t\tmainClassName,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'workspace-layout--content mx-auto flex min-h-full w-full max-w-[var(--workspace-content-max-width)] flex-col gap-space-xl p-4 min-[64rem]:p-5',\n\t\t\t\t\t\t\tcontentWidth === 'full' && 'max-w-none',\n\t\t\t\t\t\t\tcontentClassName,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</div>\n\t\t\t\t</main>\n\t\t\t\t{!!hasRail && (\n\t\t\t\t\t<aside\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'workspace-layout--rail hidden min-h-0 border-l border-border bg-background min-[96rem]:block',\n\t\t\t\t\t\t\trailClassName,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"h-full overflow-y-auto overscroll-contain p-space-lg\">\n\t\t\t\t\t\t\t{rail}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</aside>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n});\n\nWorkspaceLayout.displayName = 'WorkspaceLayout';\n\nexport const WorkspaceRecordHeader = React.forwardRef<HTMLDivElement, WorkspaceRecordHeaderProps>(function WorkspaceRecordHeader({\n\ttitle,\n\tdescription,\n\tbadges,\n\tmetadata,\n\tactions,\n\tsecondaryActions,\n\tstatus,\n\tmedia,\n\theaderEnd,\n\ttitleSlot,\n\tclassName,\n\tstrings: stringsProp,\n\t...props\n}, ref) {\n\tconst strings = useStrings(defaultWorkspaceRecordHeaderStrings, stringsProp);\n\tconst hasMetadata = !!metadata?.length;\n\n\treturn (\n\t\t<Stack\n\t\t\tref={ref}\n\t\t\tdirection={{ base: 'vertical', lg: 'horizontal' }}\n\t\t\tgap=\"xl\"\n\t\t\talign={{ lg: 'start' }}\n\t\t\tjustify={{ lg: 'between' }}\n\t\t\twrap={{ base: false, lg: false }}\n\t\t\tclassName={cn(\n\t\t\t\t'workspace-record-header--component px-4 py-4 lg:px-5',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t\t<Stack direction=\"horizontal\" gap=\"lg\" align=\"start\" wrap={false} className=\"workspace-record-header--identity flex-1\">\n\t\t\t\t{!!media && (\n\t\t\t\t\t<div className=\"workspace-record-header--media flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-radius-md border border-border bg-muted/40\">\n\t\t\t\t\t\t{media}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t\t<Stack gap=\"md\" className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t<Stack direction=\"horizontal\" gap=\"md\" className=\"workspace-record-header--title-row\">\n\t\t\t\t\t\t{titleSlot ?? (\n\t\t\t\t\t\t\t<Heading tag=\"h4\" className=\"leading-tight\">\n\t\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t\t</Heading>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!!badges && <Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" className=\"workspace-record-header--badges\">{badges}</Stack>}\n\t\t\t\t\t\t\t{!!status && <div className=\"workspace-record-header--status\">{status}</div>}\n\t\t\t\t\t\t</Stack>\n\t\t\t\t\t{!!description && (\n\t\t\t\t\t\t<Text type=\"secondary\" className=\"workspace-record-header--description max-w-4xl leading-relaxed\">\n\t\t\t\t\t\t\t{description}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t)}\n\t\t\t\t\t{!!hasMetadata && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\taria-label={strings.metadataLabel}\n\t\t\t\t\t\t\tclassName=\"workspace-record-header--metadata flex flex-wrap items-center gap-x-space-xl gap-y-space-xs\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{metadata.map((item, index) => (\n\t\t\t\t\t\t\t\t<div key={item.id ?? index} className=\"flex items-baseline gap-space-sm\">\n\t\t\t\t\t\t\t\t\t<DisplayLabel>\n\t\t\t\t\t\t\t\t\t\t{item.label}\n\t\t\t\t\t\t\t\t\t</DisplayLabel>\n\t\t\t\t\t\t\t\t\t<Value weight=\"medium\" className=\"tabular-nums\">\n\t\t\t\t\t\t\t\t\t\t{item.value}\n\t\t\t\t\t\t\t\t\t</Value>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t</Stack>\n\t\t\t\t</Stack>\n\t\t\t{!!(actions || secondaryActions || headerEnd) && (\n\t\t\t\t<div className=\"workspace-record-header--actions flex shrink-0 flex-wrap items-center gap-space-md min-[64rem]:justify-end\">\n\t\t\t\t\t{!!secondaryActions && (\n\t\t\t\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" aria-label={strings.secondaryActionsLabel}>\n\t\t\t\t\t\t\t{secondaryActions}\n\t\t\t\t\t\t</Stack>\n\t\t\t\t\t)}\n\t\t\t\t\t{!!actions && (\n\t\t\t\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" aria-label={strings.actionsLabel}>\n\t\t\t\t\t\t\t{actions}\n\t\t\t\t\t\t</Stack>\n\t\t\t\t\t)}\n\t\t\t\t\t{!!headerEnd && <div className=\"workspace-record-header--end\">{headerEnd}</div>}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</Stack>\n\t);\n});\n\nWorkspaceRecordHeader.displayName = 'WorkspaceRecordHeader';\n\nexport const WorkspaceNav = React.forwardRef<HTMLElement, WorkspaceNavProps>(function WorkspaceNav({\n\tgroups,\n\tactiveId,\n\tonSelect,\n\trenderItem,\n\tfooterSlot,\n\trenderLink,\n\tclassName,\n\tstrings: stringsProp,\n\t...props\n}, ref) {\n\tconst strings = useStrings(defaultWorkspaceNavStrings, stringsProp);\n\tconst resolvedRenderLink = useLayoutLinkRenderer({ renderLink });\n\n\treturn (\n\t\t<Stack\n\t\t\trender={(\n\t\t\t\t<nav\n\t\t\t\t\tref={ref}\n\t\t\t\t\taria-label={props['aria-label'] ?? strings.ariaLabel}\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\tgap=\"xl\"\n\t\t\tclassName={cn('workspace-nav--component h-full', className)}\n\t\t>\n\t\t\t<Stack gap=\"xl\" className=\"workspace-nav--groups min-h-0 flex-1\">\n\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t<Stack key={group.id} gap=\"md\" className=\"workspace-nav--group\">\n\t\t\t\t\t\t{!!group.label && (\n\t\t\t\t\t\t\t<div className=\"workspace-nav--group-header px-space-xs\">\n\t\t\t\t\t\t\t\t<DisplayLabel>\n\t\t\t\t\t\t\t\t\t{group.label}\n\t\t\t\t\t\t\t\t</DisplayLabel>\n\t\t\t\t\t\t\t\t{!!group.description && (\n\t\t\t\t\t\t\t\t\t<Text size=\"xs\" type=\"secondary\" className=\"mt-0.5 leading-snug\">\n\t\t\t\t\t\t\t\t\t\t{group.description}\n\t\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<ItemGroup className=\"workspace-nav--items gap-space-xs\">\n\t\t\t\t\t\t\t{group.items.map((item, index) => {\n\t\t\t\t\t\t\t\tconst active = item.id === activeId;\n\t\t\t\t\t\t\t\tconst status = getStatus(item, active);\n\t\t\t\t\t\t\t\tconst StatusIcon = STATUS_ICON[status];\n\t\t\t\t\t\t\t\tconst completion = item.completion === undefined ? undefined : clampPercent(item.completion);\n\t\t\t\t\t\t\t\tconst handleSelect = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\t\t\t\t\t\t\tif (item.disabled) {\n\t\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonSelect?.(item);\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tconst context = { active, group, index, onSelect: handleSelect };\n\n\t\t\t\t\t\t\t\tif (renderItem) {\n\t\t\t\t\t\t\t\t\treturn <React.Fragment key={item.id}>{renderItem(item, context)}</React.Fragment>;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst rowContent = (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<ItemMedia variant=\"icon\" className={cn(active && 'bg-primary/10 text-primary-accent')}>\n\t\t\t\t\t\t\t\t\t\t\t{item.icon ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<DynamicWorkspaceIcon icon={item.icon} className=\"size-icon\" />\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<StatusIcon className={cn('size-icon-md', STATUS_CLASS[status])} aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ItemMedia>\n\t\t\t\t\t\t\t\t\t\t<ItemContent>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-space-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ItemTitle>{item.label}</ItemTitle>\n\t\t\t\t\t\t\t\t\t\t\t\t{active && (\n\t\t\t\t\t\t\t\t\t\t\t<Text tag=\"span\" size=\"inherit\" className=\"sr-only\">{strings.currentLabel}</Text>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{!!item.description && <ItemDescription clamp={2}>{item.description}</ItemDescription>}\n\t\t\t\t\t\t\t\t\t\t\t{completion !== undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"mt-1.5 flex items-center gap-space-md\">\n\t\t\t\t\t\t\t\t\t\t\t<Progress\n\t\t\t\t\t\t\t\t\t\t\t\tvalue={completion}\n\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label={strings.completionLabel(completion)}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<SecondaryValue size=\"xs\" className=\"tabular-nums\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{strings.completionLabel(completion)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</SecondaryValue>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ItemContent>\n\t\t\t\t\t\t\t\t\t\t{!!item.badge && <ItemActions>{item.badge}</ItemActions>}\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tconst itemClassName = cn(\n\t\t\t\t\t\t\t\t\t'workspace-nav--item w-full border border-transparent px-2 py-2 text-left transition-colors',\n\t\t\t\t\t\t\t\t\tactive && 'border-primary/20 bg-primary/5',\n\t\t\t\t\t\t\t\t\titem.warning && !active && 'border-warning/30 bg-warning/5',\n\t\t\t\t\t\t\t\t\titem.disabled && 'pointer-events-none opacity-50',\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tif (item.href) {\n\t\t\t\t\t\t\t\t\tconst linkNode = resolvedRenderLink({\n\t\t\t\t\t\t\t\t\t\thref: item.href,\n\t\t\t\t\t\t\t\t\t\tchildren: rowContent,\n\t\t\t\t\t\t\t\t\t\tclassName: 'block',\n\t\t\t\t\t\t\t\t\t\tactive,\n\t\t\t\t\t\t\t\t\t\tdisabled: item.disabled,\n\t\t\t\t\t\t\t\t\t\texternal: item.external,\n\t\t\t\t\t\t\t\t\t\ttarget: item.target,\n\t\t\t\t\t\t\t\t\t\trel: item.rel,\n\t\t\t\t\t\t\t\t\t\tonClick: handleSelect as React.MouseEventHandler<HTMLAnchorElement>,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<Item key={item.id} size=\"md\" render={linkNode as React.ReactElement} className={itemClassName}>\n\t\t\t\t\t\t\t\t\t\t\t{rowContent}\n\t\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<Item\n\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\tsize=\"md\"\n\t\t\t\t\t\t\t\t\t\trender={<button type=\"button\" onClick={handleSelect} disabled={item.disabled} />}\n\t\t\t\t\t\t\t\t\t\tclassName={itemClassName}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{rowContent}\n\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</ItemGroup>\n\t\t\t\t\t</Stack>\n\t\t\t\t))}\n\t\t\t</Stack>\n\t\t\t{!!footerSlot && <div className=\"workspace-nav--footer shrink-0 border-t border-border pt-space-lg\">{footerSlot}</div>}\n\t\t</Stack>\n\t);\n});\n\nWorkspaceNav.displayName = 'WorkspaceNav';\n\nexport const WorkspaceLocaleStrip = React.forwardRef<HTMLDivElement, WorkspaceLocaleStripProps>(function WorkspaceLocaleStrip({\n\tlocales,\n\tactiveLocale,\n\tonLocaleChange,\n\tactions,\n\tstatus,\n\tclassName,\n\tstrings: stringsProp,\n\t...props\n}, ref) {\n\tconst strings = useStrings(defaultWorkspaceLocaleStripStrings, stringsProp);\n\n\tconst handleLocaleClick = (locale: WorkspaceLocaleItem) => {\n\t\tif (locale.disabled) return;\n\t\tonLocaleChange?.(locale);\n\t};\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={cn('workspace-locale-strip--component flex flex-col gap-space-lg border-t border-border bg-muted/20 px-4 py-3 min-[64rem]:flex-row min-[64rem]:items-center min-[64rem]:justify-between min-[64rem]:px-5', className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"md\" className=\"workspace-locale-strip--locales min-w-0\" role=\"group\" aria-label={strings.ariaLabel}>\n\t\t\t\t{locales.map((locale) => {\n\t\t\t\t\tconst active = locale.id === activeLocale;\n\t\t\t\t\tconst completion = locale.completion === undefined ? undefined : clampPercent(locale.completion);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tkey={locale.id}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\ttone={active ? 'primary' : 'secondary'}\n\t\t\t\t\t\t\tbuttonStyle={active ? 'solid' : 'ghost'}\n\t\t\t\t\t\t\tonClick={() => handleLocaleClick(locale)}\n\t\t\t\t\t\t\tdisabled={locale.disabled}\n\t\t\t\t\t\t\taria-pressed={active}\n\t\t\t\t\t\t\tclassName={cn('workspace-locale-strip--item h-auto min-h-action-sm justify-start px-control-sm py-1.5')}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Stack direction=\"horizontal\" align=\"center\" gap=\"md\" render={<span />} className=\"min-w-0\">\n\t\t\t\t\t\t\t<Text tag=\"span\" size=\"inherit\" className=\"truncate text-inherit\">{locale.label}</Text>\n\t\t\t\t\t\t\t{active && <Text tag=\"span\" size=\"inherit\" className=\"sr-only\">{strings.activeLabel}</Text>}\n\t\t\t\t\t\t\t\t{completion !== undefined && (\n\t\t\t\t\t\t\t\t\t/* On an active pill the button surface is solid, so the tonal\n\t\t\t\t\t\t\t\t\t badge variants land coloured-on-coloured. Derive the nested\n\t\t\t\t\t\t\t\t\t badge from the primary foreground pair so it remains legible\n\t\t\t\t\t\t\t\t\t when dark themes intentionally lighten `--primary`. */\n\t\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\t\ttone={completion >= 100 ? 'success' : 'secondary'}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t'shrink-0',\n\t\t\t\t\t\t\t\t\t\t\tactive && 'border-primary-foreground/20 bg-primary-foreground/10 text-primary-foreground',\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{strings.completionLabel(completion)}\n\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{!!locale.status && <span className=\"shrink-0\">{locale.status}</span>}\n\t\t\t\t\t\t\t</Stack>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</Stack>\n\t\t\t{!!(status || actions) && (\n\t\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"md\" className=\"workspace-locale-strip--actions shrink-0\">\n\t\t\t\t\t{!!status && <div className=\"workspace-locale-strip--status\">{status}</div>}\n\t\t\t\t\t{!!actions && <Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" className=\"\">{actions}</Stack>}\n\t\t\t\t</Stack>\n\t\t\t)}\n\t\t</div>\n\t);\n});\n\nWorkspaceLocaleStrip.displayName = 'WorkspaceLocaleStrip';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workspace-C1PZmk8L.js","names":["Icon","React","_Fragment","Text","DisplayLabel","Stack","Progress","Value","SecondaryValue","Badge","ItemMedia","ItemDescription","ItemActions","ItemGroup","ItemContent","Item","ItemTitle","useLayoutLinkRenderer","React$1","Fragment$1","defaultWorkspaceRecordHeaderStrings","defaultWorkspaceNavStrings","value","defaultWorkspaceLocaleStripStrings","CONTENT_WIDTH_CLASS","STATUS_ICON","Check","Circle","CircleAlert","STATUS_CLASS","clampPercent","DynamicWorkspaceIcon","icon","getStatus","item","active","WorkspaceLayout","props","hasSidebar","sidebar","hasRail","rail","ariaLabel","cn","contentWidth","className","header","headerClassName","bodyClassName","sidebarClassName","mainClassName","contentClassName","railClassName","WorkspaceRecordHeader","stringsProp","strings","useStrings","hasMetadata","metadata","media","titleSlot","Heading","title","badges","status","description","index","actions","secondaryActions","headerEnd","WorkspaceNav","resolvedRenderLink","groups","group","activeId","StatusIcon","completion","handleSelect","context","renderItem","rowContent","itemClassName","linkNode","footerSlot","WorkspaceLocaleStrip","handleLocaleClick","locale","onLocaleChange","locales","activeLocale","Button"],"sources":["../src/components/layout/workspace/workspace.strings.ts","../src/components/layout/workspace/workspace.tsx"],"sourcesContent":["export interface WorkspaceRecordHeaderStrings {\n\tmetadataLabel: string;\n\tactionsLabel: string;\n\tsecondaryActionsLabel: string;\n}\n\nexport const defaultWorkspaceRecordHeaderStrings: WorkspaceRecordHeaderStrings = {\n\tmetadataLabel: 'Record metadata',\n\tactionsLabel: 'Record actions',\n\tsecondaryActionsLabel: 'Secondary actions',\n};\n\nexport interface WorkspaceNavStrings {\n\tariaLabel: string;\n\tcompletionLabel: (value: number) => string;\n\tcurrentLabel: string;\n}\n\nexport const defaultWorkspaceNavStrings: WorkspaceNavStrings = {\n\tariaLabel: 'Workspace navigation',\n\tcompletionLabel: (value) => `${value}% complete`,\n\tcurrentLabel: 'Current',\n};\n\nexport interface WorkspaceLocaleStripStrings {\n\tariaLabel: string;\n\tactiveLabel: string;\n\tcompletionLabel: (value: number) => string;\n}\n\nexport const defaultWorkspaceLocaleStripStrings: WorkspaceLocaleStripStrings = {\n\tariaLabel: 'Workspace locales',\n\tactiveLabel: 'Active',\n\tcompletionLabel: (value) => `${value}% complete`,\n};\n","import * as React from 'react';\nimport {\n\tCheck,\n\tCircle,\n\tCircleAlert,\n\ttype LucideIcon,\n\t} from 'lucide-react';\n\nimport { Badge } from '../../base/badge';\nimport { Button } from '../../base/buttons';\nimport { Progress } from '../../base/feedback';\nimport {\n\tItem,\n\tItemActions,\n\tItemContent,\n\tItemDescription,\n\tItemGroup,\n\tItemMedia,\n\tItemTitle,\n} from '../../base/item';\nimport { Stack } from '../../base/structure';\nimport { SecondaryValue, Value } from '../../primitives';\nimport { DisplayLabel, Heading, Text } from '../../typography';\nimport { useStrings } from '../../../lib/strings';\nimport { cn } from '../../../lib/utils';\n\nimport { useLayoutLinkRenderer } from '../hooks';\nimport type { LayoutIconComponent } from '../layout.types';\nimport {\n\tdefaultWorkspaceLocaleStripStrings,\n\tdefaultWorkspaceNavStrings,\n\tdefaultWorkspaceRecordHeaderStrings,\n} from './workspace.strings';\nimport type {\n\tWorkspaceContentWidth,\n\tWorkspaceLayoutProps,\n\tWorkspaceLocaleItem,\n\tWorkspaceLocaleStripProps,\n\tWorkspaceNavItem,\n\tWorkspaceNavItemStatus,\n\tWorkspaceNavProps,\n\tWorkspaceRecordHeaderProps,\n} from './workspace.types';\n\nconst CONTENT_WIDTH_CLASS: Record<WorkspaceContentWidth, string> = {\n\tdefault: '[--workspace-content-max-width:58rem]',\n\twide: '[--workspace-content-max-width:86rem]',\n\tfull: '[--workspace-content-max-width:none]',\n};\n\nconst STATUS_ICON: Record<WorkspaceNavItemStatus, LucideIcon> = {\n\tcomplete: Check,\n\tcurrent: Circle,\n\tattention: CircleAlert,\n\tdisabled: Circle,\n\tidle: Circle,\n};\n\nconst STATUS_CLASS: Record<WorkspaceNavItemStatus, string> = {\n\tcomplete: 'text-success',\n\tcurrent: 'text-primary-accent',\n\tattention: 'text-warning',\n\tdisabled: 'text-muted-foreground/40',\n\tidle: 'text-muted-foreground',\n};\n\nfunction clampPercent(value: number): number {\n\tif (!Number.isFinite(value)) return 0;\n\treturn Math.min(100, Math.max(0, Math.round(value)));\n}\n\nfunction DynamicWorkspaceIcon({\n\ticon,\n\tclassName,\n}: {\n\t/** Leading glyph. */\n\ticon?: LayoutIconComponent;\n\tclassName?: string;\n}) {\n\tif (!icon) return null;\n\tconst Icon = icon;\n\treturn <Icon className={className} aria-hidden=\"true\" />;\n}\n\nfunction getStatus(item: WorkspaceNavItem, active: boolean): WorkspaceNavItemStatus {\n\tif (item.disabled) return 'disabled';\n\tif (item.status) return item.status;\n\tif (item.warning) return 'attention';\n\tif (active) return 'current';\n\tif (item.completion !== undefined && clampPercent(item.completion) >= 100) return 'complete';\n\treturn 'idle';\n}\n\nexport const WorkspaceLayout = React.forwardRef<HTMLDivElement, WorkspaceLayoutProps>(function WorkspaceLayout({\n\theader,\n\tsidebar,\n\trail,\n\tchildren,\n\tcontentWidth = 'wide',\n\tclassName,\n\theaderClassName,\n\tbodyClassName,\n\tsidebarClassName,\n\tmainClassName,\n\tcontentClassName,\n\trailClassName,\n\tariaLabel,\n\tstyle,\n\t...props\n}, ref) {\n\tconst hasSidebar = !!sidebar;\n\tconst hasRail = !!rail;\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\taria-label={ariaLabel}\n\t\t\tclassName={cn(\n\t\t\t\t'workspace-layout--component flex h-full min-h-0 w-full flex-col overflow-hidden rounded-radius-md border border-border bg-background text-foreground',\n\t\t\t\t'[--workspace-sidebar-width:16rem] [--workspace-rail-width:20rem]',\n\t\t\t\tCONTENT_WIDTH_CLASS[contentWidth],\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tstyle={style}\n\t\t\t{...props}\n\t\t>\n\t\t\t{!!header && (\n\t\t\t\t<header className={cn('workspace-layout--header shrink-0 border-b border-border bg-background', headerClassName)}>\n\t\t\t\t\t{header}\n\t\t\t\t</header>\n\t\t\t)}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'workspace-layout--body grid min-h-0 flex-1 grid-cols-1 bg-muted/20',\n\t\t\t\t\thasSidebar && !hasRail && 'min-[80rem]:grid-cols-[var(--workspace-sidebar-width)_minmax(0,1fr)]',\n\t\t\t\t\thasSidebar && hasRail && 'min-[80rem]:grid-cols-[var(--workspace-sidebar-width)_minmax(0,1fr)] min-[96rem]:grid-cols-[var(--workspace-sidebar-width)_minmax(0,1fr)_var(--workspace-rail-width)]',\n\t\t\t\t\t!hasSidebar && hasRail && 'min-[96rem]:grid-cols-[minmax(0,1fr)_var(--workspace-rail-width)]',\n\t\t\t\t\tbodyClassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{!!hasSidebar && (\n\t\t\t\t\t<aside\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'workspace-layout--sidebar hidden min-h-0 border-r border-border bg-background min-[80rem]:block',\n\t\t\t\t\t\t\tsidebarClassName,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"h-full overflow-y-auto overscroll-contain p-space-lg\">\n\t\t\t\t\t\t\t{sidebar}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</aside>\n\t\t\t\t)}\n\t\t\t\t<main\n\t\t\t\t\ttabIndex={0}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t'workspace-layout--main min-h-0 min-w-0 overflow-y-auto overscroll-contain focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/70 focus-visible:ring-inset',\n\t\t\t\t\t\tmainClassName,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'workspace-layout--content mx-auto flex min-h-full w-full max-w-[var(--workspace-content-max-width)] flex-col gap-space-xl p-4 min-[64rem]:p-5',\n\t\t\t\t\t\t\tcontentWidth === 'full' && 'max-w-none',\n\t\t\t\t\t\t\tcontentClassName,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</div>\n\t\t\t\t</main>\n\t\t\t\t{!!hasRail && (\n\t\t\t\t\t<aside\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'workspace-layout--rail hidden min-h-0 border-l border-border bg-background min-[96rem]:block',\n\t\t\t\t\t\t\trailClassName,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"h-full overflow-y-auto overscroll-contain p-space-lg\">\n\t\t\t\t\t\t\t{rail}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</aside>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n});\n\nWorkspaceLayout.displayName = 'WorkspaceLayout';\n\nexport const WorkspaceRecordHeader = React.forwardRef<HTMLDivElement, WorkspaceRecordHeaderProps>(function WorkspaceRecordHeader({\n\ttitle,\n\tdescription,\n\tbadges,\n\tmetadata,\n\tactions,\n\tsecondaryActions,\n\tstatus,\n\tmedia,\n\theaderEnd,\n\ttitleSlot,\n\tclassName,\n\tstrings: stringsProp,\n\t...props\n}, ref) {\n\tconst strings = useStrings(defaultWorkspaceRecordHeaderStrings, stringsProp);\n\tconst hasMetadata = !!metadata?.length;\n\n\treturn (\n\t\t<Stack\n\t\t\tref={ref}\n\t\t\tdirection={{ base: 'vertical', lg: 'horizontal' }}\n\t\t\tgap=\"xl\"\n\t\t\talign={{ lg: 'start' }}\n\t\t\tjustify={{ lg: 'between' }}\n\t\t\twrap={{ base: false, lg: false }}\n\t\t\tclassName={cn(\n\t\t\t\t'workspace-record-header--component px-4 py-4 lg:px-5',\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t\t<Stack direction=\"horizontal\" gap=\"lg\" align=\"start\" wrap={false} className=\"workspace-record-header--identity flex-1\">\n\t\t\t\t{!!media && (\n\t\t\t\t\t<div className=\"workspace-record-header--media flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-radius-md border border-border bg-muted/40\">\n\t\t\t\t\t\t{media}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t\t<Stack gap=\"md\" className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t<Stack direction=\"horizontal\" gap=\"md\" className=\"workspace-record-header--title-row\">\n\t\t\t\t\t\t{titleSlot ?? (\n\t\t\t\t\t\t\t<Heading tag=\"h4\" className=\"leading-tight\">\n\t\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t\t</Heading>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!!badges && <Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" className=\"workspace-record-header--badges\">{badges}</Stack>}\n\t\t\t\t\t\t\t{!!status && <div className=\"workspace-record-header--status\">{status}</div>}\n\t\t\t\t\t\t</Stack>\n\t\t\t\t\t{!!description && (\n\t\t\t\t\t\t<Text type=\"secondary\" className=\"workspace-record-header--description max-w-4xl leading-relaxed\">\n\t\t\t\t\t\t\t{description}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t)}\n\t\t\t\t\t{!!hasMetadata && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\taria-label={strings.metadataLabel}\n\t\t\t\t\t\t\tclassName=\"workspace-record-header--metadata flex flex-wrap items-center gap-x-space-xl gap-y-space-xs\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{metadata.map((item, index) => (\n\t\t\t\t\t\t\t\t<div key={item.id ?? index} className=\"flex items-baseline gap-space-sm\">\n\t\t\t\t\t\t\t\t\t<DisplayLabel>\n\t\t\t\t\t\t\t\t\t\t{item.label}\n\t\t\t\t\t\t\t\t\t</DisplayLabel>\n\t\t\t\t\t\t\t\t\t<Value weight=\"medium\" className=\"tabular-nums\">\n\t\t\t\t\t\t\t\t\t\t{item.value}\n\t\t\t\t\t\t\t\t\t</Value>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t</Stack>\n\t\t\t\t</Stack>\n\t\t\t{!!(actions || secondaryActions || headerEnd) && (\n\t\t\t\t<div className=\"workspace-record-header--actions flex shrink-0 flex-wrap items-center gap-space-md min-[64rem]:justify-end\">\n\t\t\t\t\t{!!secondaryActions && (\n\t\t\t\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" aria-label={strings.secondaryActionsLabel}>\n\t\t\t\t\t\t\t{secondaryActions}\n\t\t\t\t\t\t</Stack>\n\t\t\t\t\t)}\n\t\t\t\t\t{!!actions && (\n\t\t\t\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" aria-label={strings.actionsLabel}>\n\t\t\t\t\t\t\t{actions}\n\t\t\t\t\t\t</Stack>\n\t\t\t\t\t)}\n\t\t\t\t\t{!!headerEnd && <div className=\"workspace-record-header--end\">{headerEnd}</div>}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</Stack>\n\t);\n});\n\nWorkspaceRecordHeader.displayName = 'WorkspaceRecordHeader';\n\nexport const WorkspaceNav = React.forwardRef<HTMLElement, WorkspaceNavProps>(function WorkspaceNav({\n\tgroups,\n\tactiveId,\n\tonSelect,\n\trenderItem,\n\tfooterSlot,\n\trenderLink,\n\tclassName,\n\tstrings: stringsProp,\n\t...props\n}, ref) {\n\tconst strings = useStrings(defaultWorkspaceNavStrings, stringsProp);\n\tconst resolvedRenderLink = useLayoutLinkRenderer({ renderLink });\n\n\treturn (\n\t\t<Stack\n\t\t\trender={(\n\t\t\t\t<nav\n\t\t\t\t\tref={ref}\n\t\t\t\t\taria-label={props['aria-label'] ?? strings.ariaLabel}\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\tgap=\"xl\"\n\t\t\tclassName={cn('workspace-nav--component h-full', className)}\n\t\t>\n\t\t\t<Stack gap=\"xl\" className=\"workspace-nav--groups min-h-0 flex-1\">\n\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t<Stack key={group.id} gap=\"md\" className=\"workspace-nav--group\">\n\t\t\t\t\t\t{!!group.label && (\n\t\t\t\t\t\t\t<div className=\"workspace-nav--group-header px-space-xs\">\n\t\t\t\t\t\t\t\t<DisplayLabel>\n\t\t\t\t\t\t\t\t\t{group.label}\n\t\t\t\t\t\t\t\t</DisplayLabel>\n\t\t\t\t\t\t\t\t{!!group.description && (\n\t\t\t\t\t\t\t\t\t<Text size=\"xs\" type=\"secondary\" className=\"mt-0.5 leading-snug\">\n\t\t\t\t\t\t\t\t\t\t{group.description}\n\t\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<ItemGroup className=\"workspace-nav--items gap-space-xs\">\n\t\t\t\t\t\t\t{group.items.map((item, index) => {\n\t\t\t\t\t\t\t\tconst active = item.id === activeId;\n\t\t\t\t\t\t\t\tconst status = getStatus(item, active);\n\t\t\t\t\t\t\t\tconst StatusIcon = STATUS_ICON[status];\n\t\t\t\t\t\t\t\tconst completion = item.completion === undefined ? undefined : clampPercent(item.completion);\n\t\t\t\t\t\t\t\tconst handleSelect = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\t\t\t\t\t\t\tif (item.disabled) {\n\t\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonSelect?.(item);\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tconst context = { active, group, index, onSelect: handleSelect };\n\n\t\t\t\t\t\t\t\tif (renderItem) {\n\t\t\t\t\t\t\t\t\treturn <React.Fragment key={item.id}>{renderItem(item, context)}</React.Fragment>;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst rowContent = (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<ItemMedia variant=\"icon\" className={cn(active && 'bg-primary/10 text-primary-accent')}>\n\t\t\t\t\t\t\t\t\t\t\t{item.icon ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<DynamicWorkspaceIcon icon={item.icon} className=\"size-icon\" />\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<StatusIcon className={cn('size-icon-md', STATUS_CLASS[status])} aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ItemMedia>\n\t\t\t\t\t\t\t\t\t\t<ItemContent>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-space-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ItemTitle>{item.label}</ItemTitle>\n\t\t\t\t\t\t\t\t\t\t\t\t{active && (\n\t\t\t\t\t\t\t\t\t\t\t<Text tag=\"span\" size=\"inherit\" className=\"sr-only\">{strings.currentLabel}</Text>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{!!item.description && <ItemDescription clamp={2}>{item.description}</ItemDescription>}\n\t\t\t\t\t\t\t\t\t\t\t{completion !== undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"mt-1.5 flex items-center gap-space-md\">\n\t\t\t\t\t\t\t\t\t\t\t<Progress\n\t\t\t\t\t\t\t\t\t\t\t\tvalue={completion}\n\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label={strings.completionLabel(completion)}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<SecondaryValue size=\"xs\" className=\"tabular-nums\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{strings.completionLabel(completion)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</SecondaryValue>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ItemContent>\n\t\t\t\t\t\t\t\t\t\t{!!item.badge && <ItemActions>{item.badge}</ItemActions>}\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tconst itemClassName = cn(\n\t\t\t\t\t\t\t\t\t'workspace-nav--item w-full border border-transparent px-2 py-2 text-left transition-colors',\n\t\t\t\t\t\t\t\t\tactive && 'border-primary/20 bg-primary/5',\n\t\t\t\t\t\t\t\t\titem.warning && !active && 'border-warning/30 bg-warning/5',\n\t\t\t\t\t\t\t\t\titem.disabled && 'pointer-events-none opacity-50',\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tif (item.href) {\n\t\t\t\t\t\t\t\t\tconst linkNode = resolvedRenderLink({\n\t\t\t\t\t\t\t\t\t\thref: item.href,\n\t\t\t\t\t\t\t\t\t\tchildren: rowContent,\n\t\t\t\t\t\t\t\t\t\tclassName: 'block',\n\t\t\t\t\t\t\t\t\t\tactive,\n\t\t\t\t\t\t\t\t\t\tdisabled: item.disabled,\n\t\t\t\t\t\t\t\t\t\texternal: item.external,\n\t\t\t\t\t\t\t\t\t\ttarget: item.target,\n\t\t\t\t\t\t\t\t\t\trel: item.rel,\n\t\t\t\t\t\t\t\t\t\tonClick: handleSelect as React.MouseEventHandler<HTMLAnchorElement>,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<Item key={item.id} size=\"md\" render={linkNode as React.ReactElement} className={itemClassName}>\n\t\t\t\t\t\t\t\t\t\t\t{rowContent}\n\t\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<Item\n\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\tsize=\"md\"\n\t\t\t\t\t\t\t\t\t\trender={<button type=\"button\" onClick={handleSelect} disabled={item.disabled} />}\n\t\t\t\t\t\t\t\t\t\tclassName={itemClassName}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{rowContent}\n\t\t\t\t\t\t\t\t\t</Item>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</ItemGroup>\n\t\t\t\t\t</Stack>\n\t\t\t\t))}\n\t\t\t</Stack>\n\t\t\t{!!footerSlot && <div className=\"workspace-nav--footer shrink-0 border-t border-border pt-space-lg\">{footerSlot}</div>}\n\t\t</Stack>\n\t);\n});\n\nWorkspaceNav.displayName = 'WorkspaceNav';\n\nexport const WorkspaceLocaleStrip = React.forwardRef<HTMLDivElement, WorkspaceLocaleStripProps>(function WorkspaceLocaleStrip({\n\tlocales,\n\tactiveLocale,\n\tonLocaleChange,\n\tactions,\n\tstatus,\n\tclassName,\n\tstrings: stringsProp,\n\t...props\n}, ref) {\n\tconst strings = useStrings(defaultWorkspaceLocaleStripStrings, stringsProp);\n\n\tconst handleLocaleClick = (locale: WorkspaceLocaleItem) => {\n\t\tif (locale.disabled) return;\n\t\tonLocaleChange?.(locale);\n\t};\n\n\treturn (\n\t\t<div\n\t\t\tref={ref}\n\t\t\tclassName={cn('workspace-locale-strip--component flex flex-col gap-space-lg border-t border-border bg-muted/20 px-4 py-3 min-[64rem]:flex-row min-[64rem]:items-center min-[64rem]:justify-between min-[64rem]:px-5', className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"md\" className=\"workspace-locale-strip--locales min-w-0\" role=\"group\" aria-label={strings.ariaLabel}>\n\t\t\t\t{locales.map((locale) => {\n\t\t\t\t\tconst active = locale.id === activeLocale;\n\t\t\t\t\tconst completion = locale.completion === undefined ? undefined : clampPercent(locale.completion);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tkey={locale.id}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\ttone={active ? 'primary' : 'secondary'}\n\t\t\t\t\t\t\tbuttonStyle={active ? 'solid' : 'ghost'}\n\t\t\t\t\t\t\tonClick={() => handleLocaleClick(locale)}\n\t\t\t\t\t\t\tdisabled={locale.disabled}\n\t\t\t\t\t\t\taria-pressed={active}\n\t\t\t\t\t\t\tclassName={cn('workspace-locale-strip--item h-auto min-h-action-sm justify-start px-control-sm py-1.5')}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Stack direction=\"horizontal\" align=\"center\" gap=\"md\" render={<span />} className=\"min-w-0\">\n\t\t\t\t\t\t\t<Text tag=\"span\" size=\"inherit\" className=\"truncate text-inherit\">{locale.label}</Text>\n\t\t\t\t\t\t\t{active && <Text tag=\"span\" size=\"inherit\" className=\"sr-only\">{strings.activeLabel}</Text>}\n\t\t\t\t\t\t\t\t{completion !== undefined && (\n\t\t\t\t\t\t\t\t\t/* On an active pill the button surface is solid, so the tonal\n\t\t\t\t\t\t\t\t\t badge variants land coloured-on-coloured. Derive the nested\n\t\t\t\t\t\t\t\t\t badge from the primary foreground pair so it remains legible\n\t\t\t\t\t\t\t\t\t when dark themes intentionally lighten `--primary`. */\n\t\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\t\ttone={completion >= 100 ? 'success' : 'secondary'}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t'shrink-0',\n\t\t\t\t\t\t\t\t\t\t\tactive && 'border-primary-foreground/20 bg-primary-foreground/10 text-primary-foreground',\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{strings.completionLabel(completion)}\n\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{!!locale.status && <span className=\"shrink-0\">{locale.status}</span>}\n\t\t\t\t\t\t\t</Stack>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</Stack>\n\t\t\t{!!(status || actions) && (\n\t\t\t\t<Stack direction=\"horizontal\" wrap align=\"center\" gap=\"md\" className=\"workspace-locale-strip--actions shrink-0\">\n\t\t\t\t\t{!!status && <div className=\"workspace-locale-strip--status\">{status}</div>}\n\t\t\t\t\t{!!actions && <Stack direction=\"horizontal\" wrap align=\"center\" gap=\"sm\" className=\"\">{actions}</Stack>}\n\t\t\t\t</Stack>\n\t\t\t)}\n\t\t</div>\n\t);\n});\n\nWorkspaceLocaleStrip.displayName = 'WorkspaceLocaleStrip';\n"],"mappings":"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"}
|
|
@@ -5,28 +5,131 @@ description: Use when building, integrating, theming or migrating a React consum
|
|
|
5
5
|
|
|
6
6
|
# Themelia React UI consumer
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Build application-specific screens from supported public owners. This skill
|
|
9
|
+
routes the shipped references, indexes, recipes and read-only helpers; it is
|
|
10
|
+
not an API replacement or a requirement to imitate one example layout.
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
## Resolve the installed contract
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
Start with [installed setup](references/installation.md). Resolve the package
|
|
15
|
+
from the app, check installed/copied guidance identity, and read matching
|
|
16
|
+
declarations. Refresh the complete skill after upgrades, including prose.
|
|
17
|
+
Read current sibling pages and app adapters; keep local product policy separate.
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- [Integration](references/integration.md): installation, CSS, state, routing, overlays and verification.
|
|
19
|
-
- [Theme extension](references/theming.md): compact tokens, scoped portals, radius and drift prevention.
|
|
20
|
-
- [Generated tokens](references/tokens.json): current structural roots/defaults, advanced settings and utility vocabulary.
|
|
21
|
-
- [Workflows](references/workflows.md): collection/navigation, detail, validated edit and destructive confirmation; callback ownership, optional peers and verification.
|
|
22
|
-
- [Typed example](examples/composition.tsx): public-import composition verified against the packed package.
|
|
19
|
+
Use [discovery](references/discovery.md) and the [public index](references/public-api.json)
|
|
20
|
+
for exact imports, symbols, responsibilities, alternatives and optional peers.
|
|
21
|
+
Search the task or exact symbol; shorten an empty query instead of guessing APIs.
|
|
22
|
+
For conventional node_modules layouts, run from the app:
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
```sh
|
|
25
|
+
node node_modules/themelia-react-ui/docs/consumer-skill/scripts/find-components.mjs record directory --limit 5
|
|
26
|
+
```
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
Inspect the chosen declaration and its re-exports before assigning props.
|
|
29
|
+
Prefer the focused published family; inspect optional peers for its whole-family
|
|
30
|
+
types as well as runtime use. Catalogue URLs and private source are not imports.
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
## Compose from the task
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
Before JSX, identify fixed context, editable criteria, the user's next decision,
|
|
35
|
+
leading information, command scope and required states. Keep this proportional
|
|
36
|
+
to the task; ordinary edits do not need an approval ceremony or planning report.
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
Choose the most complete existing owner whose lifecycle fits. Typography owns
|
|
39
|
+
display roles; primitives own formatted values; base owns controls and surfaces;
|
|
40
|
+
layout owns page regions; blocks own reusable subjects; features own workflows.
|
|
41
|
+
Compose lower owners when a feature's coordination does not fit the product.
|
|
42
|
+
|
|
43
|
+
Use Page for route identity and commands, Card for an independently framed subject,
|
|
44
|
+
ResourceCell for table identity, Item for independent entries and MetadataList for
|
|
45
|
+
facts about one subject. Compare DataView, DataTable and base Table by their actual
|
|
46
|
+
query/selection/paging contracts. Keep fixed parent scope through query clearing.
|
|
47
|
+
Use native table query, utility, bulk-action and row-action seams where they fit.
|
|
48
|
+
|
|
49
|
+
Give commands explicit tone and page/collection/record scope. Card status/count
|
|
50
|
+
badges use titleSuffix; headerStart is context, and actions use action slots.
|
|
51
|
+
Prioritize identity and decision facts; preserve access to other facts through
|
|
52
|
+
detail/disclosure rather than appending every hidden column to compact rows.
|
|
53
|
+
Product-specific columns, form arrangements and richer rows remain app decisions.
|
|
54
|
+
|
|
55
|
+
## Preserve structure, typography and appearance
|
|
56
|
+
|
|
57
|
+
Assign each gap/inset once: shell/container owns gutters, surface owns padding,
|
|
58
|
+
section owns rhythm, Stack/Grid owns sibling gaps, and typography owns no margins.
|
|
59
|
+
Use public props/slots before adding local appearance overrides. A nested framed
|
|
60
|
+
table or Card needs a distinct subject; use supported flat composition otherwise.
|
|
61
|
+
|
|
62
|
+
Use shared Heading/Text/DisplayLabel/Label/TextLink roles and value primitives such
|
|
63
|
+
as Money, Date and Email. Semantic element choice is separate from visual size.
|
|
64
|
+
Primary row text stays at the shared default; compact supporting metadata uses
|
|
65
|
+
the smaller secondary role. Density alone does not shrink primary content.
|
|
66
|
+
Native editable text keeps its responsive control recipe. For rich HTML display
|
|
67
|
+
use RichText; editing belongs to RichTextEditor. Preserve sanitization and scoped
|
|
68
|
+
content rhythm; structural UI labels do not inherit prose margins.
|
|
69
|
+
|
|
70
|
+
Keep copy textGap normal/dense, row density, control size and surface inset
|
|
71
|
+
independent. Use gap-normal/gap-dense through the headed owner or Stack/Grid;
|
|
72
|
+
avoid adding description margins. rounded-radius is the default, rounded-inner
|
|
73
|
+
is for intentional nested regions. Do not replace control geometry/typography
|
|
74
|
+
or anchored-region padding through classes, contentClassName or inline styles.
|
|
75
|
+
PopoverMenu owns routine searchable selection; custom popups use public regions.
|
|
76
|
+
|
|
77
|
+
## Integrate themes, layouts and workflows
|
|
78
|
+
|
|
79
|
+
Import themelia-react-ui/style.css once; shipped components need no source scanning.
|
|
80
|
+
Apps authoring semantic utilities process theme.css in their Tailwind v4 build.
|
|
81
|
+
Keep broad resets in the lower-priority base layer. Retheme existing semantic
|
|
82
|
+
surface/foreground pairs and structural roots; derived utilities are not new knobs.
|
|
83
|
+
CSS owns appearance, UIProvider stable defaults, callbacks per-mount app state.
|
|
84
|
+
Use ThemeScope for scoped portals, and native dir plus DirectionProvider for RTL.
|
|
85
|
+
|
|
86
|
+
Reuse public shells/sidebars/headers. Constrain app-shell height and preserve one
|
|
87
|
+
scroll owner, useful gutters, DOM/focus order and action wrapping. Check usable
|
|
88
|
+
parent width as well as viewport width; embedded layouts may need container
|
|
89
|
+
adaptation. Placement, alignment and overflow remain valid app-owned mechanics.
|
|
90
|
+
|
|
91
|
+
Keep controlled values authoritative and accept callback proposals in the parent.
|
|
92
|
+
Keep state mode fixed while mounted; choose explicit reset/remount behavior when
|
|
93
|
+
the subject changes. Stabilize lifecycle-sensitive loader/schema/column inputs
|
|
94
|
+
when their meaning is unchanged. The app owns routing, permissions, translation,
|
|
95
|
+
transport, cancellation and persistence; use href/callback/render integration.
|
|
96
|
+
|
|
97
|
+
Show relevant loading, empty, missing, error/retry, disabled, validation and success
|
|
98
|
+
states. Preserve rejected drafts and ignore superseded results. A toast alone is
|
|
99
|
+
not persistence; UI cancellation does not prove a server operation stopped.
|
|
100
|
+
Use library overlays for modal workflows and their pending/dismissal contracts.
|
|
101
|
+
Keep labels, field associations, icon-action names and translated strings meaningful.
|
|
102
|
+
In server-component apps, mount interactive families from a client wrapper;
|
|
103
|
+
use a family's published pure helpers where provided, without assuming SSR support.
|
|
104
|
+
|
|
105
|
+
## Read the relevant detail and recipe
|
|
106
|
+
|
|
107
|
+
| Task | Reference and example |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| Owner selection, Card, menu anatomy | [Composition](references/composition.md), [basic composition](examples/composition.tsx) |
|
|
110
|
+
| Display roles, formatted values and rich HTML | [Typography and rich content](references/composition.md#typography-and-rich-content) |
|
|
111
|
+
| Geometry ownership and independent variants | [Design contract](references/design-contract.md) |
|
|
112
|
+
| CSS, providers, RTL, routers, async/editor integration | [Integration](references/integration.md) |
|
|
113
|
+
| Theme roots, density, radius and portal inheritance | [Theming](references/theming.md), [generated tokens](references/tokens.json) |
|
|
114
|
+
| Collection, fixed context, detail, edit and delete | [Workflows](references/workflows.md), [directory](examples/scoped-directory.tsx), [detail](examples/detail.tsx), [edit](examples/edit.tsx), [confirmation](examples/destructive-confirm.tsx) |
|
|
115
|
+
| Application shells, navigation and parent adaptation | [Layout composition](references/composition.md#application-layouts), [workspace](examples/app-layout.tsx) |
|
|
116
|
+
| TaskCard, UsageCard, InlineEdit and persistence | [Workflow contracts](references/workflows.md#task-summaries-usage-and-inline-editing), [summary](examples/planning-usage-edit.tsx) |
|
|
117
|
+
| Email/templates, receipts, hierarchy/capture, files/messages | [Domain composition](references/composition.md#receipts), [all recipe exports and peers](examples/manifest.json) |
|
|
118
|
+
| Whole-screen review and optional source checker | [Review and checks](references/review.md) |
|
|
119
|
+
|
|
120
|
+
The manifest lists every runnable recipe, including ordinary/template emails,
|
|
121
|
+
receipt, hierarchy/guided forms, message/files and the index table. Copy only the
|
|
122
|
+
relevant recipe, adapt product labels/callbacks, and read its actual declarations.
|
|
123
|
+
|
|
124
|
+
## Verify the integrated result
|
|
125
|
+
|
|
126
|
+
Run the app's types/lint/tests/build. The optional check-composition.mjs script
|
|
127
|
+
uses app TypeScript and checks direct imports/static anatomy; errors, warnings
|
|
128
|
+
and unresolved expressions are distinct. It is not a full visual/accessibility audit.
|
|
129
|
+
Separately inspect long copy, missing values, narrow/desktop and light/dark/RTL
|
|
130
|
+
states. Open menus/overlays, execute callbacks and failure/retry, check keyboard
|
|
131
|
+
selection/dismissal/focus return and reduced motion, and report unverified limits.
|
|
132
|
+
|
|
133
|
+
This skill ships with the package and requires separate manual installation.
|
|
134
|
+
No theme-publish/skill-install CLI exists. It grants no authorization to publish,
|
|
135
|
+
deploy, message users or change other repositories.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Button } from 'themelia-react-ui/base/buttons';
|
|
2
2
|
import { Card } from 'themelia-react-ui/base/cards';
|
|
3
|
-
import {
|
|
3
|
+
import { MetadataList } from 'themelia-react-ui/blocks/data-display';
|
|
4
4
|
import { Date, Email, Money, Value } from 'themelia-react-ui/primitives';
|
|
5
|
-
import { Text } from 'themelia-react-ui/typography';
|
|
6
5
|
|
|
7
6
|
export interface AccountDetailRecord {
|
|
8
7
|
id: string; name: string; email: string | null;
|
|
@@ -18,11 +17,11 @@ export function AccountDetail({ account, onEdit, onBack }: AccountDetailProps) {
|
|
|
18
17
|
return <Card title={account.name} surface="card" headerPlacement="frame"
|
|
19
18
|
headerAction={<Button onClick={() => onEdit(account.id)}>Edit account</Button>}
|
|
20
19
|
footerSlot={<Button tone="secondary" buttonStyle="outline" onClick={onBack}>Back to accounts</Button>}>
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
<MetadataList columns={{ base: 1, md: 2 }} items={[
|
|
21
|
+
{ id: 'id', label: 'Account ID', value: <Value>{account.id}</Value> },
|
|
22
|
+
{ id: 'email', label: 'Email', value: <Email value={account.email} /> },
|
|
23
|
+
{ id: 'created', label: 'Created', value: <Date value={account.createdAt} /> },
|
|
24
|
+
{ id: 'balance', label: 'Balance', value: <Money amount={account.balanceMinor} currency={account.currency} unit="minor" /> },
|
|
25
|
+
]} />
|
|
27
26
|
</Card>;
|
|
28
27
|
}
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
"@tanstack/react-table"
|
|
20
20
|
]
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
"file": "scoped-directory.tsx",
|
|
24
|
+
"exports": ["ScopedDirectory"],
|
|
25
|
+
"phase": "optional",
|
|
26
|
+
"optionalPeers": ["@tanstack/react-table", "react-day-picker"]
|
|
27
|
+
},
|
|
22
28
|
{
|
|
23
29
|
"file": "detail.tsx",
|
|
24
30
|
"exports": [
|
|
@@ -89,6 +95,14 @@
|
|
|
89
95
|
],
|
|
90
96
|
"phase": "basic",
|
|
91
97
|
"optionalPeers": []
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"file": "planning-usage-edit.tsx",
|
|
101
|
+
"exports": [
|
|
102
|
+
"WorkspaceSummary"
|
|
103
|
+
],
|
|
104
|
+
"phase": "basic",
|
|
105
|
+
"optionalPeers": []
|
|
92
106
|
}
|
|
93
107
|
]
|
|
94
108
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { InlineEdit } from 'themelia-react-ui/base/inline-edit';
|
|
3
|
+
import { Card } from 'themelia-react-ui/base/cards';
|
|
4
|
+
import { AdaptiveGrid, Stack } from 'themelia-react-ui/base/structure';
|
|
5
|
+
import { TaskCard, type TaskCardItem } from 'themelia-react-ui/blocks/planning';
|
|
6
|
+
import { UsageCard, type UsageCardData } from 'themelia-react-ui/blocks/analytics/usage-card';
|
|
7
|
+
|
|
8
|
+
/** The application supplies persistence, authoritative tasks and usage. */
|
|
9
|
+
export function WorkspaceSummary({
|
|
10
|
+
initialName, tasks, usage, saveName, completeTask,
|
|
11
|
+
}: {
|
|
12
|
+
initialName: string;
|
|
13
|
+
tasks: readonly TaskCardItem[];
|
|
14
|
+
usage: UsageCardData;
|
|
15
|
+
saveName: (name: string, signal: AbortSignal) => Promise<void>;
|
|
16
|
+
completeTask: (id: string, completed: boolean) => void;
|
|
17
|
+
}) {
|
|
18
|
+
const [name, setName] = useState(initialName);
|
|
19
|
+
return <Stack gap="xl">
|
|
20
|
+
<Card title="Workspace">
|
|
21
|
+
<InlineEdit label="Project name" value={name} onValueChange={setName} required
|
|
22
|
+
onSave={(draft, {signal}) => saveName(draft, signal)} />
|
|
23
|
+
</Card>
|
|
24
|
+
<AdaptiveGrid minColumnWidth="md" gap="xl">
|
|
25
|
+
<TaskCard title="Release checklist" items={tasks} defaultExpanded onItemCompletedChange={completeTask} />
|
|
26
|
+
<UsageCard title="Workspace usage" data={usage} />
|
|
27
|
+
</AdaptiveGrid>
|
|
28
|
+
</Stack>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useMemo, type ReactNode } from 'react';
|
|
2
|
+
import { Button } from 'themelia-react-ui/base/buttons';
|
|
3
|
+
import { Stack } from 'themelia-react-ui/base/structure';
|
|
4
|
+
import { FilterLayout, FilterProvider, FilterType, type ActiveFilter, type FilterConfig } from 'themelia-react-ui/features/filters';
|
|
5
|
+
import { DataTable, ResourceCell, type DataTableProps } from 'themelia-react-ui/features/table';
|
|
6
|
+
import { Page } from 'themelia-react-ui/layout/page';
|
|
7
|
+
import { Email } from 'themelia-react-ui/primitives';
|
|
8
|
+
import { Text } from 'themelia-react-ui/typography';
|
|
9
|
+
|
|
10
|
+
export interface DirectoryContact { id: string; name: string; role: string; email: string | null; }
|
|
11
|
+
export type DirectoryState =
|
|
12
|
+
| { status: 'loading' }
|
|
13
|
+
| { status: 'error'; message: string }
|
|
14
|
+
| { status: 'ready'; records: DirectoryContact[] };
|
|
15
|
+
export interface ScopedDirectoryProps {
|
|
16
|
+
scope: { id: string; label: string };
|
|
17
|
+
state: DirectoryState;
|
|
18
|
+
filters: ActiveFilter[];
|
|
19
|
+
onQueryChange: (query: { scopeId: string; filters: ActiveFilter[] }) => void;
|
|
20
|
+
onRetry: () => void;
|
|
21
|
+
onBack: () => void;
|
|
22
|
+
onCreate?: () => void;
|
|
23
|
+
onOpen: (id: string) => void;
|
|
24
|
+
exportAction?: ReactNode;
|
|
25
|
+
/** App chooses from available parent width; compact identity retains contact reach. */
|
|
26
|
+
compact?: boolean;
|
|
27
|
+
// Authoritative server pagination; the app preserves scope/query in its URLs.
|
|
28
|
+
pagination?: DataTableProps<DirectoryContact, unknown>['pagination'];
|
|
29
|
+
onPageChange?: DataTableProps<DirectoryContact, unknown>['onPageChange'];
|
|
30
|
+
}
|
|
31
|
+
const criteria: FilterConfig[] = [{
|
|
32
|
+
key: 'q', label: 'Search contacts', placeholder: 'Name, email or role',
|
|
33
|
+
type: FilterType.SEARCH, icon: null, operator: 'contains', delay: 0,
|
|
34
|
+
displayConfig: { display: 'always' },
|
|
35
|
+
}];
|
|
36
|
+
const columns: DataTableProps<DirectoryContact, unknown>['columns'] = [
|
|
37
|
+
{ id: 'identity', header: 'Contact', cell: ({ row }) => <ResourceCell title={row.original.name}
|
|
38
|
+
subtitle={<Text size="xs" type="secondary">{row.original.role}</Text>} /> },
|
|
39
|
+
{ accessorKey: 'email', header: 'Email', cell: ({ row }) => <Email value={row.original.email} /> },
|
|
40
|
+
];
|
|
41
|
+
const compactColumns: DataTableProps<DirectoryContact, unknown>['columns'] = [
|
|
42
|
+
{ id: 'identity', header: 'Contact', cell: ({ row }) => <ResourceCell title={row.original.name}
|
|
43
|
+
subtitle={<Text size="xs" type="secondary">{row.original.role}</Text>}
|
|
44
|
+
metadata={<Email value={row.original.email} />} /> },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Parent scope is navigation context, never a removable filter. The app loads
|
|
48
|
+
// matching records. Its detail view keeps other operational facts available.
|
|
49
|
+
export function ScopedDirectory(props: ScopedDirectoryProps) {
|
|
50
|
+
const rowActions = useMemo<DataTableProps<DirectoryContact, unknown>['rowActions']>(() => [
|
|
51
|
+
{ id: 'open', label: 'Open contact', onClick: contact => props.onOpen(contact.id) },
|
|
52
|
+
], [props.onOpen]);
|
|
53
|
+
return <Page header={{ title: 'Contacts', headingTag: 'h1',
|
|
54
|
+
description: 'People responsible for bookings, billing and operations.',
|
|
55
|
+
onBack: props.onBack, backLabel: 'All businesses',
|
|
56
|
+
slots: { beforeTitle: <Text type="secondary">Business: {props.scope.label}</Text> },
|
|
57
|
+
actions: props.onCreate ? <Button tone="primary" onClick={props.onCreate}>New contact</Button> : undefined,
|
|
58
|
+
}}>
|
|
59
|
+
<FilterProvider filters={criteria} activeFilters={props.filters}
|
|
60
|
+
navigating={props.state.status === 'loading'}
|
|
61
|
+
onFilterChange={filters => props.onQueryChange({ scopeId: props.scope.id,
|
|
62
|
+
// A cleared search can emit ['']; it is not an applied criterion here.
|
|
63
|
+
filters: filters.filter(filter => filter.key !== 'q' || filter.value.some(value => value.trim() !== '')),
|
|
64
|
+
})}>
|
|
65
|
+
<DataTable columns={props.compact ? compactColumns : columns} data={props.state.status === 'ready' ? props.state.records : []}
|
|
66
|
+
getRowId={contact => contact.id} enableSorting={false} enableColumnVisibility={false}
|
|
67
|
+
showFullscreenToggle={false} manualPagination pagination={props.state.status === 'ready' ? props.pagination : undefined} onPageChange={props.onPageChange}
|
|
68
|
+
topbarContent={<FilterLayout variant="compact" showClearFilters />} topbarEnd={props.exportAction}
|
|
69
|
+
emptyStateMessage={props.state.status === 'loading' ? 'Loading contacts…' :
|
|
70
|
+
props.state.status === 'error' ? 'Contacts could not be loaded.' :
|
|
71
|
+
props.filters.length ? 'No contacts match these criteria.' : 'No contacts in this business yet.'}
|
|
72
|
+
emptyStateAction={props.state.status === 'error' ? <Stack gap="dense"><Text role="alert" type="error">{props.state.message}</Text>
|
|
73
|
+
<Button tone="secondary" buttonStyle="outline" onClick={props.onRetry}>Retry contacts</Button></Stack> : undefined}
|
|
74
|
+
rowActions={rowActions} />
|
|
75
|
+
</FilterProvider>
|
|
76
|
+
</Page>;
|
|
77
|
+
}
|
|
@@ -16,12 +16,100 @@ keep placement, width, wrapping and overflow with the surrounding composition.
|
|
|
16
16
|
| Reusable subject presentation | Blocks |
|
|
17
17
|
| Editing/selection/async lifecycle | Features with app callbacks |
|
|
18
18
|
|
|
19
|
+
## Compose from the task
|
|
20
|
+
|
|
21
|
+
Identify the user's next decision before selecting components. Establish fixed
|
|
22
|
+
context (account, parent entity, workspace), editable query criteria, leading
|
|
23
|
+
information, page/collection/record commands, and loading/empty/error/edit states.
|
|
24
|
+
Then assign owners. A task may require a comparison table, a compact directory,
|
|
25
|
+
a detail workspace or a form; they need different information hierarchies.
|
|
26
|
+
|
|
27
|
+
| Decision | Use when | Alternative / boundary |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| DataView | Its coordinated query, view and collection lifecycle fits | DataTable with app-owned query transport and native toolbar slots when coordination differs; base Table for presentation without TanStack behavior |
|
|
30
|
+
| ResourceCell | Identity inside a table cell | Item for independent list entries; MetadataList for label/value facts about one subject |
|
|
31
|
+
| Page / application shell | Route identity, navigation, content bounds and page commands | Card for an independent subject within that route; do not repeat the same heading merely to frame a table |
|
|
32
|
+
| Card | One subject needs a frame and headed regions | `surface="flat" padding="none"` inside an existing boundary; nested DataTable uses its own `surface="flat"` |
|
|
33
|
+
| FormField / InlineEdit | A field needs label/hint/error, or isolated save/cancel | Complete feature/overlay for a workflow; app form owns whole-record submission |
|
|
34
|
+
|
|
35
|
+
For a DataTable composition, put queries in `topbarContent`, exports or collection
|
|
36
|
+
tools in `topbarEnd`, selection commands in `bulkActions`, and record commands
|
|
37
|
+
in `rowActions`. Other arrangements are valid when the product has a distinct
|
|
38
|
+
query region; choose one owner deliberately and keep queries close to their
|
|
39
|
+
results. DataView already owns coordinated filtering; do not add a second filter
|
|
40
|
+
store around it without inspecting its contract.
|
|
41
|
+
|
|
42
|
+
Fixed scope and mutable criteria are different states. A collection reached
|
|
43
|
+
inside one business can show that business as context/navigation; query controls
|
|
44
|
+
then narrow that scope. If business is an editable filter, clearing it may widen
|
|
45
|
+
the collection. The app owns this meaning and transport. Do not accidentally
|
|
46
|
+
present fixed scope as a removable criterion or repeat it in every region.
|
|
47
|
+
|
|
48
|
+
Use primary identity plus task-critical supporting facts in compact rows.
|
|
49
|
+
When columns disappear, preserve access to other facts through detail/disclosure
|
|
50
|
+
or supported comparison controls. Avoid automatically appending every hidden
|
|
51
|
+
column to identity metadata. There is no universal row-line limit: long names,
|
|
52
|
+
accessibility needs and the task can justify richer rows. Explain the priority.
|
|
53
|
+
Custom ResourceCell metadata is caller content; use shared supporting typography
|
|
54
|
+
and CellStack where its smaller secondary-value contract fits.
|
|
55
|
+
|
|
56
|
+
Commands need explicit importance as well as scope. PageActions does not infer
|
|
57
|
+
primary tone from a label such as “New contact”; choose `tone="primary"` when
|
|
58
|
+
that is the route's main command. Permissions and destructive intent remain
|
|
59
|
+
application decisions. A back link, export and destructive command should not
|
|
60
|
+
all inherit the same visual emphasis by accident.
|
|
61
|
+
|
|
62
|
+
| Drift | Better decision |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| Same parent named in header, context strip and fixed filter | One contextual lead; repeat only where it clarifies a separate task |
|
|
65
|
+
| Every hidden column becomes identity metadata | Prioritize visible facts and retain access to the rest |
|
|
66
|
+
| Repeated route title on an otherwise untitled table Card | Route header plus native collection surface |
|
|
67
|
+
| Child and parent both add the same gutter/inset | Assign visible space once at its owner |
|
|
68
|
+
| New local controls to imitate an existing workflow | Inspect the public feature/parts/callback seams first |
|
|
69
|
+
|
|
70
|
+
See the [scoped directory recipe](../examples/scoped-directory.tsx) and
|
|
71
|
+
[workflow explanation](workflows.md#scoped-directory). Review decisions using
|
|
72
|
+
[review and checks](review.md); valid alternatives should preserve task semantics
|
|
73
|
+
and public anatomy, not imitate one screenshot.
|
|
74
|
+
|
|
19
75
|
Use the exact family declarations from public-api.json. Catalogue URLs and source folders are not import contracts.
|
|
20
76
|
|
|
77
|
+
## Typography and rich content
|
|
78
|
+
|
|
79
|
+
Choose Heading, Text, DisplayLabel, Label and TextLink from the published
|
|
80
|
+
typography family by role. Set semantic tags independently of visual variants;
|
|
81
|
+
heading order describes the document, not a desired font size. Labels associate
|
|
82
|
+
controls with stable IDs. Money, Date, Email and other value primitives own
|
|
83
|
+
formatting, locale/unit handling and empty-value fallbacks; zero is real data.
|
|
84
|
+
The app owns accounting, timezone policy and the meaning of unavailable values.
|
|
85
|
+
|
|
86
|
+
Primary row values and labels use the shared default text size. Compact metadata,
|
|
87
|
+
form hints and row descriptions use smaller secondary copy; structural panel
|
|
88
|
+
descriptions use default-sized secondary text. Typography adds no outer margins.
|
|
89
|
+
Native editable text retains its responsive field recipe. Do not resize it to
|
|
90
|
+
imitate a display label or use density to make all text smaller.
|
|
91
|
+
|
|
92
|
+
RichText from `themelia-react-ui/typography/rich-text` displays sanitized HTML,
|
|
93
|
+
with a scoped reading rhythm inherited from typography and theme roots. Use
|
|
94
|
+
`tag="div"` for HTML containing paragraphs/headings/lists; its default `p` tag
|
|
95
|
+
is appropriate only for inline content. The allowlist strips arbitrary styling,
|
|
96
|
+
embedded media and unsafe links; it is not a Markdown parser or email-layout
|
|
97
|
+
renderer. Keep outer width/padding on the surrounding structure. RichTextEditor
|
|
98
|
+
owns editing; EmailComposer owns structured email/variable/delivery contracts.
|
|
99
|
+
Read the relevant declarations and [integration](integration.md) before choosing
|
|
100
|
+
the content owner. Embedded React UI can opt out of reading rhythm through
|
|
101
|
+
`not-typeset` or `data-not-typeset` on its subtree; those opt-outs do not bypass
|
|
102
|
+
HTML sanitization or establish a separate typography system.
|
|
103
|
+
|
|
21
104
|
## Card anatomy
|
|
22
105
|
|
|
23
106
|
Card surface="card" is the ordinary bordered surface; surface="framed" retains its ring recipe; surface="flat" padding="none" composes within an existing boundary. headerPlacement="frame" places title/icon/actions above an inset body, retaining default corners and using rounded-inner on that body.
|
|
24
107
|
|
|
108
|
+
Card `gap` owns spacing between regions; `contentGap` owns direct body-child
|
|
109
|
+
spacing; `textGap` owns title/supporting-copy rhythm. Region spacing defaults to
|
|
110
|
+
`xl` (`md` for `padding="none"`). Use a semantic region gap for compact cards
|
|
111
|
+
instead of resetting padding or adding local margins.
|
|
112
|
+
|
|
25
113
|
Use titleSuffix for status/count/version badges. Long titles/suffixes wrap through the shared owner; no literal spaces/local margins to manufacture gaps. headerAction/actions are trailing controls, headerStart pre-title context. footerSlot/footer definitions own card-level actions; footerDivider owns the rule and inset. Do not simulate a footer inside children. SmartCard and ContentBlock are deprecated migration names; use Card in new code.
|
|
26
114
|
|
|
27
115
|
Reusable blocks/features stay card-free unless their explicit purpose is a card. Frame them at the composition boundary.
|
|
@@ -168,3 +256,18 @@ adaptTo="container" inside Container/PageViewport to adapt at 48rem of usable
|
|
|
168
256
|
parent width while preserving main-before-aside DOM/focus order.
|
|
169
257
|
|
|
170
258
|
See the installed [workspace recipe](../examples/app-layout.tsx).
|
|
259
|
+
|
|
260
|
+
## Task and usage summaries, editable fields
|
|
261
|
+
|
|
262
|
+
| Intent | Public owner | Composition boundary |
|
|
263
|
+
| --- | --- | --- |
|
|
264
|
+
| Task summary and expandable checklist | `TaskCard` from `themelia-react-ui/blocks/planning` | Owns one Card and its header/body/footer; supply authoritative items and callbacks. |
|
|
265
|
+
| Quotas, category/daily analytics and period selection | `UsageCard` from `themelia-react-ui/blocks/analytics/usage-card` | Owns one Card; supply data/request state and optional app footer action. |
|
|
266
|
+
| Edit one saved field in place | `InlineEdit` from `themelia-react-ui/base/inline-edit` | Owns FormField and native control/action anatomy; place it inside the app's chosen surface. |
|
|
267
|
+
|
|
268
|
+
Use Card's `gap` for region rhythm and `textGap` for title/supporting-copy rhythm;
|
|
269
|
+
neither changes Card insets. These blocks use the canonical surface, typography,
|
|
270
|
+
formatted-value and structure owners. Drawn rings and mini charts retain private
|
|
271
|
+
geometry and semantic chart/status colors. Read the
|
|
272
|
+
[workflow contracts](workflows.md#task-summaries-usage-and-inline-editing) and
|
|
273
|
+
[typed workspace recipe](../examples/planning-usage-edit.tsx) for integration.
|
|
@@ -133,9 +133,10 @@ overrides, semantic borders, and new raw spacing/control drift. The shipped
|
|
|
133
133
|
typed examples go through the same design checks as library compositions.
|
|
134
134
|
|
|
135
135
|
Those repository checks do not run automatically in a consuming application.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
The opt-in source checker in [review and checks](review.md) covers a smaller,
|
|
137
|
+
documented set of direct-import/static-anatomy rules. Run the app's lint, types,
|
|
138
|
+
build and interaction tests for broader coverage. The package does not strip
|
|
139
|
+
arbitrary CSS at runtime or install audit/skill tooling globally.
|
|
139
140
|
|
|
140
141
|
Static lint cannot prove alignment, readable wrapping, CSS authored elsewhere,
|
|
141
142
|
unknown dynamic styles, focus restoration or callback behavior. Verify long
|