themelia-react-ui 2.0.3 → 2.0.4
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 +50 -0
- package/README.md +65 -28
- package/dist/{accordion-Cjb6sx0G.js → accordion-96o0pN62.js} +5 -5
- package/dist/{accordion-Cjb6sx0G.js.map → accordion-96o0pN62.js.map} +1 -1
- package/dist/action-menu-CHgs8vmj.js +281 -0
- package/dist/action-menu-CHgs8vmj.js.map +1 -0
- package/dist/{affordance-geometry.generated-tF36bREk.js → affordance-geometry.generated-B8yGOr8-.js} +7 -1
- package/dist/affordance-geometry.generated-B8yGOr8-.js.map +1 -0
- package/dist/{ai-artifact-DvTwDVyL.js → ai-artifact-BZUGwBq6.js} +277 -276
- package/dist/ai-artifact-BZUGwBq6.js.map +1 -0
- package/dist/{app-shell-C4FhvCqF.js → app-shell-D63ATkIK.js} +84 -83
- package/dist/app-shell-D63ATkIK.js.map +1 -0
- package/dist/{attachment-BxDfL2mw.js → attachment-DkoqHPPl.js} +7 -7
- package/dist/{attachment-BxDfL2mw.js.map → attachment-DkoqHPPl.js.map} +1 -1
- package/dist/{auth-BGua8ouh.js → auth-D2FVZ1MH.js} +10 -10
- package/dist/{auth-BGua8ouh.js.map → auth-D2FVZ1MH.js.map} +1 -1
- package/dist/{avatar-DAPlDvhJ.js → avatar-DWO_HHhB.js} +5 -5
- package/dist/{avatar-DAPlDvhJ.js.map → avatar-DWO_HHhB.js.map} +1 -1
- package/dist/{badge-HpFqoqSY.js → badge-DErMA7ul.js} +4 -4
- package/dist/{badge-HpFqoqSY.js.map → badge-DErMA7ul.js.map} +1 -1
- package/dist/base/accordion.js +1 -1
- package/dist/base/action-menu.js +1 -1
- package/dist/base/attachment.js +1 -1
- package/dist/base/avatar.js +1 -1
- package/dist/base/badge.js +1 -1
- package/dist/base/batch-action-bar.js +1 -1
- package/dist/base/buttons.js +1 -1
- package/dist/base/cards.js +2 -2
- package/dist/base/carousel.js +3 -3
- package/dist/base/cascader.js +13 -13
- package/dist/base/chart.js +1 -1
- package/dist/base/choice-inputs.js +2 -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/copyable.js +1 -1
- package/dist/base/date-pickers.js +2 -2
- package/dist/base/display.js +6 -6
- package/dist/base/feedback.js +3 -3
- package/dist/base/forms-numeric.js +1 -1
- package/dist/base/forms.js +3 -3
- package/dist/base/inline-edit.js +10 -10
- package/dist/base/item.js +1 -1
- package/dist/base/media-gallery.js +1 -1
- package/dist/base/metadata-list.js +1 -1
- package/dist/base/navigation.js +6 -6
- package/dist/base/popover-menu.js +1 -1
- package/dist/base/popover.js +1 -1
- package/dist/base/qr-code.js +1 -1
- package/dist/base/repeaters.js +9 -9
- package/dist/base/sheet.js +1 -1
- package/dist/base/sidebar.js +1 -1
- package/dist/base/signature-pad.js +6 -6
- package/dist/base/skeleton.js +1 -1
- package/dist/base/spinner.js +4 -4
- package/dist/base/structure.js +1 -1
- package/dist/base/table.js +1 -1
- package/dist/base/text-inputs.js +2 -2
- package/dist/base/toaster.js +3 -3
- 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/{batch-action-bar-DnUJ21NQ.js → batch-action-bar-CdfIkCsV.js} +4 -4
- package/dist/{batch-action-bar-DnUJ21NQ.js.map → batch-action-bar-CdfIkCsV.js.map} +1 -1
- package/dist/blocks/admin.js +20 -20
- package/dist/blocks/admin.js.map +1 -1
- package/dist/blocks/ai.js +15 -15
- package/dist/blocks/analytics/usage-card.js +1 -1
- package/dist/blocks/analytics.js +17 -17
- 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 +570 -567
- package/dist/blocks/commerce.js.map +1 -1
- package/dist/blocks/data-display.js +2 -2
- package/dist/blocks/onboarding.js +5 -5
- package/dist/blocks/planning.js +11 -11
- package/dist/blocks/timelines.js +1 -1
- package/dist/{breadcrumbs-C-oqs1sQ.js → breadcrumbs-B-WFKecD.js} +4 -4
- package/dist/{breadcrumbs-C-oqs1sQ.js.map → breadcrumbs-B-WFKecD.js.map} +1 -1
- package/dist/{button-DVzspB_G.js → button-DkmK-z8E.js} +2 -2
- package/dist/{button-DVzspB_G.js.map → button-DkmK-z8E.js.map} +1 -1
- package/dist/{buttons-DPZekJFu.js → buttons-BbfzgczK.js} +8 -8
- package/dist/{buttons-DPZekJFu.js.map → buttons-BbfzgczK.js.map} +1 -1
- package/dist/{cards-BaBZR-AU.js → cards-4JTWqvVe.js} +10 -10
- package/dist/cards-4JTWqvVe.js.map +1 -0
- package/dist/{chart-ZOYyK2xG.js → chart-C19flDQb.js} +4 -4
- package/dist/{chart-ZOYyK2xG.js.map → chart-C19flDQb.js.map} +1 -1
- package/dist/{checkbox-AWIhNhmo.js → checkbox-CzYUTPeJ.js} +9 -5
- package/dist/checkbox-CzYUTPeJ.js.map +1 -0
- package/dist/{choice-inputs-CNHvARCJ.js → choice-inputs-JLtij2y4.js} +182 -176
- package/dist/choice-inputs-JLtij2y4.js.map +1 -0
- package/dist/{code-block-DeneBJR_.js → code-block-CGNaUtAE.js} +7 -7
- package/dist/{code-block-DeneBJR_.js.map → code-block-CGNaUtAE.js.map} +1 -1
- package/dist/{collapsible-C-_l4hpy.js → collapsible-Do4G0zcC.js} +3 -3
- package/dist/{collapsible-C-_l4hpy.js.map → collapsible-Do4G0zcC.js.map} +1 -1
- package/dist/{collection-content-By56pLog.js → collection-content-C0e8SsTm.js} +3 -3
- package/dist/{collection-content-By56pLog.js.map → collection-content-C0e8SsTm.js.map} +1 -1
- package/dist/{combobox-Dxo2fAYQ.js → combobox-CBJDs6w_.js} +12 -12
- package/dist/{combobox-Dxo2fAYQ.js.map → combobox-CBJDs6w_.js.map} +1 -1
- package/dist/{combobox-DaHESuiL.js → combobox-hbhSS18O.js} +7 -7
- package/dist/{combobox-DaHESuiL.js.map → combobox-hbhSS18O.js.map} +1 -1
- package/dist/{command-C25VMRII.js → command-D8NrBj-b.js} +7 -7
- package/dist/{command-C25VMRII.js.map → command-D8NrBj-b.js.map} +1 -1
- package/dist/{comments-DD0DUPwJ.js → comments-Di4w0H_S.js} +383 -382
- package/dist/comments-Di4w0H_S.js.map +1 -0
- package/dist/components/base/action-menu/action-menu.d.ts +2 -0
- package/dist/components/base/cards/card.recipes.d.ts +12 -5
- package/dist/components/base/cards/smart-card.types.d.ts +12 -7
- package/dist/components/base/choice-inputs/checkbox.d.ts +7 -2
- package/dist/components/base/choice-inputs/switch-card.d.ts +7 -0
- package/dist/components/base/choice-inputs/toggle-field.d.ts +7 -0
- package/dist/components/base/date-pickers/datepicker.types.d.ts +7 -0
- package/dist/components/base/forms/controlled-form-field.d.ts +6 -6
- package/dist/components/base/forms/form-field.d.ts +5 -3
- package/dist/components/base/forms/index.d.ts +1 -0
- package/dist/components/base/forms/server-errors.d.ts +16 -0
- package/dist/components/base/forms/workflow.d.ts +5 -2
- package/dist/components/base/metadata-list/metadata-list.d.ts +3 -1
- package/dist/components/base/navigation/page-header/page-header.d.ts +4 -2
- package/dist/components/base/structure/composition.recipes.d.ts +17 -0
- package/dist/components/base/text-inputs/hooks/use-form-field-state.d.ts +7 -2
- package/dist/components/base/text-inputs/input.d.ts +4 -2
- package/dist/components/base/text-inputs/textarea.d.ts +4 -2
- package/dist/components/base/value-inputs/date-time-input.d.ts +2 -1
- package/dist/components/base/value-inputs/slider.d.ts +5 -1
- package/dist/components/base/value-inputs/tags-input.d.ts +5 -1
- package/dist/components/typography/text.styles.d.ts +26 -1
- package/dist/composition.recipes-COdiFhxB.js +19 -0
- package/dist/composition.recipes-COdiFhxB.js.map +1 -0
- package/dist/containers-CRmHf7GP.js +104 -0
- package/dist/containers-CRmHf7GP.js.map +1 -0
- package/dist/{copyable-ClBuk_UK.js → copyable-CnGCZdX4.js} +4 -4
- package/dist/{copyable-ClBuk_UK.js.map → copyable-CnGCZdX4.js.map} +1 -1
- package/dist/{data-display-DT_khPl_.js → data-display-D4oujtx0.js} +239 -238
- package/dist/data-display-D4oujtx0.js.map +1 -0
- package/dist/{date-DCrsDTZc.js → date-Q7jnXxpk.js} +5 -5
- package/dist/{date-DCrsDTZc.js.map → date-Q7jnXxpk.js.map} +1 -1
- package/dist/{date-picker-B2K3ucWn.js → date-picker-zOt1oNss.js} +102 -90
- package/dist/date-picker-zOt1oNss.js.map +1 -0
- package/dist/date-pickers-cj8IXbDC.js +179 -0
- package/dist/date-pickers-cj8IXbDC.js.map +1 -0
- package/dist/{display-hAc1HjpI.js → display-BB4BL_-K.js} +8 -8
- package/dist/{display-hAc1HjpI.js.map → display-BB4BL_-K.js.map} +1 -1
- package/dist/{dropdown-menu-Cl4X7L5S.js → dropdown-menu-CjY8Vt7W.js} +8 -8
- package/dist/{dropdown-menu-Cl4X7L5S.js.map → dropdown-menu-CjY8Vt7W.js.map} +1 -1
- package/dist/features/activities.js +11 -11
- package/dist/features/ai-chat.js +9 -9
- package/dist/features/appointment-calendar.js +222 -220
- package/dist/features/appointment-calendar.js.map +1 -1
- package/dist/features/async-preview.js +8 -8
- package/dist/features/combobox.js +1 -1
- package/dist/features/comments.js +1 -1
- package/dist/features/data-view.js +10 -10
- package/dist/features/email-composer.js +275 -274
- package/dist/features/email-composer.js.map +1 -1
- package/dist/features/event-calendar.js +199 -198
- package/dist/features/event-calendar.js.map +1 -1
- package/dist/features/filters.js +1 -1
- package/dist/features/global-search.js +12 -12
- package/dist/features/kanban.js +4 -4
- package/dist/features/map.js +7 -7
- package/dist/features/media-library.js +623 -622
- package/dist/features/media-library.js.map +1 -1
- package/dist/features/mentions.js +1 -1
- package/dist/features/overlays.js +1 -1
- package/dist/features/products.js +973 -969
- package/dist/features/products.js.map +1 -1
- package/dist/features/questionnaire.js +12 -12
- package/dist/features/resource-assignment.js +8 -8
- package/dist/features/resource.js +113 -112
- package/dist/features/resource.js.map +1 -1
- package/dist/features/rich-text-editor.js +2 -2
- package/dist/features/schema-form.js +254 -253
- package/dist/features/schema-form.js.map +1 -1
- package/dist/features/suggestions.js +3 -3
- package/dist/features/sync.js +3 -3
- package/dist/features/table.js +1 -1
- package/dist/features/theme-tweaker.js +19 -19
- package/dist/features/theme-tweaker.js.map +1 -1
- package/dist/{feedback-BYIhtitP.js → feedback-DZNL5Lg1.js} +9 -9
- package/dist/{feedback-BYIhtitP.js.map → feedback-DZNL5Lg1.js.map} +1 -1
- package/dist/{field-affordance-recipes-DZhnIL_Y.js → field-affordance-recipes-DjwGx8eM.js} +2 -2
- package/dist/{field-affordance-recipes-DZhnIL_Y.js.map → field-affordance-recipes-DjwGx8eM.js.map} +1 -1
- package/dist/{filters-DJVAveHA.js → filters-BcT-uoFy.js} +19 -19
- package/dist/{filters-DJVAveHA.js.map → filters-BcT-uoFy.js.map} +1 -1
- package/dist/{form-field-CwH9y77i.js → form-field-CsgRmdit.js} +14 -8
- package/dist/form-field-CsgRmdit.js.map +1 -0
- package/dist/{form-sizing-BdP_Mf5p.js → form-sizing-NXgjkmBG.js} +3 -3
- package/dist/{form-sizing-BdP_Mf5p.js.map → form-sizing-NXgjkmBG.js.map} +1 -1
- package/dist/{forms-B9G_xwzi.js → forms-BV5m5aQF.js} +83 -65
- package/dist/forms-BV5m5aQF.js.map +1 -0
- package/dist/{forms-numeric-B_jraJaQ.js → forms-numeric-Cd33AKbD.js} +288 -288
- package/dist/forms-numeric-Cd33AKbD.js.map +1 -0
- package/dist/{header-BSfIEWbI.js → header-BA697l4a.js} +14 -14
- package/dist/{header-BSfIEWbI.js.map → header-BA697l4a.js.map} +1 -1
- package/dist/hooks/useFormErrors.d.ts +1 -1
- package/dist/{hooks-ClH9thn4.js → hooks-BQ-L7Uu2.js} +2 -2
- package/dist/{hooks-ClH9thn4.js.map → hooks-BQ-L7Uu2.js.map} +1 -1
- package/dist/{icon-badge-DYKIwvOV.js → icon-badge-COWbCCWu.js} +3 -3
- package/dist/{icon-badge-DYKIwvOV.js.map → icon-badge-COWbCCWu.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/input-Cdw4k0fb.js +289 -0
- package/dist/input-Cdw4k0fb.js.map +1 -0
- package/dist/{input-CZw8iU99.js → input-DOPL5_QE.js} +3 -3
- package/dist/{input-CZw8iU99.js.map → input-DOPL5_QE.js.map} +1 -1
- package/dist/item-BvZ2DR_x.js +277 -0
- package/dist/item-BvZ2DR_x.js.map +1 -0
- 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 +6 -0
- package/dist/lib/ui-provider/types.d.ts +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/{media-gallery-3zIVLMWH.js → media-gallery-CSl5W1Fy.js} +6 -6
- package/dist/{media-gallery-3zIVLMWH.js.map → media-gallery-CSl5W1Fy.js.map} +1 -1
- package/dist/{mentions-CGlEJ15c.js → mentions-RKOQaaIB.js} +8 -8
- package/dist/{mentions-CGlEJ15c.js.map → mentions-RKOQaaIB.js.map} +1 -1
- package/dist/{message-DZ_ruyUV.js → message-INU2xsxu.js} +4 -4
- package/dist/{message-DZ_ruyUV.js.map → message-INU2xsxu.js.map} +1 -1
- package/dist/metadata-list-qVV7AVxa.js +443 -0
- package/dist/metadata-list-qVV7AVxa.js.map +1 -0
- package/dist/{navigation-BzhHI3lo.js → navigation-BQNcg1jT.js} +8 -8
- package/dist/{navigation-BzhHI3lo.js.map → navigation-BQNcg1jT.js.map} +1 -1
- package/dist/{navigation-Bnotn4AQ.js → navigation-DLF3E1IC.js} +7 -7
- package/dist/{navigation-Bnotn4AQ.js.map → navigation-DLF3E1IC.js.map} +1 -1
- package/dist/{navigation-row-recipes-BFLCk_Aq.js → navigation-row-recipes-WZEs-hYo.js} +2 -2
- package/dist/{navigation-row-recipes-BFLCk_Aq.js.map → navigation-row-recipes-WZEs-hYo.js.map} +1 -1
- package/dist/{number-DbV1Dija.js → number-E_OiIfJz.js} +5 -5
- package/dist/{number-DbV1Dija.js.map → number-E_OiIfJz.js.map} +1 -1
- package/dist/{overflow-tab-bar-CWY7Nogn.js → overflow-tab-bar-CrbqMteP.js} +107 -105
- package/dist/overflow-tab-bar-CrbqMteP.js.map +1 -0
- package/dist/{overlays-tMkF-VAN.js → overlays-BlKynmde.js} +228 -227
- package/dist/overlays-BlKynmde.js.map +1 -0
- package/dist/page-CIwNUtGr.js +227 -0
- package/dist/page-CIwNUtGr.js.map +1 -0
- package/dist/page-header-commands-DFMlNYzN.js +128 -0
- package/dist/page-header-commands-DFMlNYzN.js.map +1 -0
- package/dist/{pagination-BL25hu2s.js → pagination-Ch8X_47u.js} +5 -5
- package/dist/{pagination-BL25hu2s.js.map → pagination-Ch8X_47u.js.map} +1 -1
- package/dist/{popover-menu-Cn9rUkdc.js → popover-menu-BYaCdFhf.js} +6 -6
- package/dist/{popover-menu-Cn9rUkdc.js.map → popover-menu-BYaCdFhf.js.map} +1 -1
- package/dist/{popover-gnTOhbFM.js → popover-uwzX2vo7.js} +4 -4
- package/dist/{popover-gnTOhbFM.js.map → popover-uwzX2vo7.js.map} +1 -1
- package/dist/{primitives-BFK08r9M.js → primitives-Du64iDbb.js} +6 -6
- package/dist/{primitives-BFK08r9M.js.map → primitives-Du64iDbb.js.map} +1 -1
- package/dist/primitives.js +4 -4
- package/dist/{progress-B1VnCT0K.js → progress-BvF2oCdV.js} +5 -5
- package/dist/{progress-B1VnCT0K.js.map → progress-BvF2oCdV.js.map} +1 -1
- package/dist/{receipt-BVSOH3Xo.js → receipt-D789Jwyy.js} +6 -6
- package/dist/{receipt-BVSOH3Xo.js.map → receipt-D789Jwyy.js.map} +1 -1
- package/dist/{rich-text-GOUyKQ-s.js → rich-text-CLJEf76s.js} +4 -4
- package/dist/{rich-text-GOUyKQ-s.js.map → rich-text-CLJEf76s.js.map} +1 -1
- package/dist/{rich-text-editor-cRLC-eBK.js → rich-text-editor-Cm1dpUpQ.js} +10 -10
- package/dist/{rich-text-editor-cRLC-eBK.js.map → rich-text-editor-Cm1dpUpQ.js.map} +1 -1
- package/dist/{rich-text-editor-toolbar-DfXg0-VC.js → rich-text-editor-toolbar-L__e4x47.js} +4 -4
- package/dist/{rich-text-editor-toolbar-DfXg0-VC.js.map → rich-text-editor-toolbar-L__e4x47.js.map} +1 -1
- package/dist/{scroll-area-DcI-M1UT.js → scroll-area-La5RXsEk.js} +2 -2
- package/dist/{scroll-area-DcI-M1UT.js.map → scroll-area-La5RXsEk.js.map} +1 -1
- package/dist/{separator-Bom7NsGO.js → separator-DAEV0w65.js} +2 -2
- package/dist/{separator-Bom7NsGO.js.map → separator-DAEV0w65.js.map} +1 -1
- package/dist/{separator-BDV-Y648.js → separator-GNfPG-90.js} +3 -3
- package/dist/{separator-BDV-Y648.js.map → separator-GNfPG-90.js.map} +1 -1
- package/dist/services/actions.js +1 -1
- package/dist/settings-BA2iboic.js +174 -0
- package/dist/settings-BA2iboic.js.map +1 -0
- package/dist/{sheet-ijlsjq-n.js → sheet-DoAxTb2J.js} +7 -7
- package/dist/{sheet-ijlsjq-n.js.map → sheet-DoAxTb2J.js.map} +1 -1
- package/dist/{sidebar-DkTO4VTO.js → sidebar-ByqKR_W5.js} +13 -13
- package/dist/{sidebar-DkTO4VTO.js.map → sidebar-ByqKR_W5.js.map} +1 -1
- package/dist/{sidebar-Db6gkh3U.js → sidebar-D2iSnGJ7.js} +11 -11
- package/dist/{sidebar-Db6gkh3U.js.map → sidebar-D2iSnGJ7.js.map} +1 -1
- package/dist/{skeleton-Byjnt7qr.js → skeleton-CUj8O5Bd.js} +3 -3
- package/dist/{skeleton-Byjnt7qr.js.map → skeleton-CUj8O5Bd.js.map} +1 -1
- package/dist/smart-card-CqkzQsK-.js +614 -0
- package/dist/smart-card-CqkzQsK-.js.map +1 -0
- package/dist/{structure-BmisBK-3.js → structure-C0V0_Fyf.js} +3 -3
- package/dist/{structure-BmisBK-3.js.map → structure-C0V0_Fyf.js.map} +1 -1
- package/dist/{structure.recipes-EPz60cU8.js → structure.recipes-BB4-n58V.js} +2 -2
- package/dist/{structure.recipes-EPz60cU8.js.map → structure.recipes-BB4-n58V.js.map} +1 -1
- package/dist/style.css +1 -1
- package/dist/{table-DsfzoUia.js → table-CpvnzJn9.js} +3 -3
- package/dist/{table-DsfzoUia.js.map → table-CpvnzJn9.js.map} +1 -1
- package/dist/{table-BweFy_P9.js → table-DH7rozUv.js} +19 -19
- package/dist/{table-BweFy_P9.js.map → table-DH7rozUv.js.map} +1 -1
- package/dist/{tabs-CDocW5D1.js → tabs-eqVsJZWO.js} +7 -7
- package/dist/{tabs-CDocW5D1.js.map → tabs-eqVsJZWO.js.map} +1 -1
- package/dist/{text-inputs-f9Ia-aCi.js → text-inputs-DXItiR0O.js} +101 -98
- package/dist/text-inputs-DXItiR0O.js.map +1 -0
- package/dist/{text.styles-g4He6UIW.js → text.styles-B64My8RZ.js} +25 -10
- package/dist/text.styles-B64My8RZ.js.map +1 -0
- package/dist/theme.css +152 -112
- package/dist/{timelines-CwM_9IhD.js → timelines-DmtztAY_.js} +10 -10
- package/dist/{timelines-CwM_9IhD.js.map → timelines-DmtztAY_.js.map} +1 -1
- package/dist/{tooltip-yiRJUFjp.js → tooltip-B4hjKtoV.js} +3 -3
- package/dist/{tooltip-yiRJUFjp.js.map → tooltip-B4hjKtoV.js.map} +1 -1
- package/dist/{tree-WnKQzwbq.js → tree-CV11PURP.js} +8 -8
- package/dist/{tree-WnKQzwbq.js.map → tree-CV11PURP.js.map} +1 -1
- package/dist/typography/rich-text.js +1 -1
- package/dist/typography-CSyJrh28.js +117 -0
- package/dist/typography-CSyJrh28.js.map +1 -0
- package/dist/typography.js +1 -1
- package/dist/{ui-provider-C_U0Xma4.js → ui-provider-CQCuCBcy.js} +3 -3
- package/dist/ui-provider-CQCuCBcy.js.map +1 -0
- package/dist/ui-provider.js +1 -1
- package/dist/{upload-CXyxmQBP.js → upload-C9SA0X-8.js} +11 -11
- package/dist/{upload-CXyxmQBP.js.map → upload-C9SA0X-8.js.map} +1 -1
- package/dist/{upload-queue-Boscubn9.js → upload-queue-DhQJiSRc.js} +9 -9
- package/dist/{upload-queue-Boscubn9.js.map → upload-queue-DhQJiSRc.js.map} +1 -1
- package/dist/{usage-card-BhO5eqkX.js → usage-card-DVTRDmGz.js} +134 -133
- package/dist/usage-card-DVTRDmGz.js.map +1 -0
- package/dist/{utils-BRhFDwuU.js → utils-CF3c95Io.js} +68 -5
- package/dist/utils-CF3c95Io.js.map +1 -0
- package/dist/value-inputs-C0o70p-U.js +980 -0
- package/dist/value-inputs-C0o70p-U.js.map +1 -0
- package/dist/{value-typography-BP8OG6h5.js → value-typography-ClupF0Gz.js} +2 -2
- package/dist/{value-typography-BP8OG6h5.js.map → value-typography-ClupF0Gz.js.map} +1 -1
- package/dist/workspace-9EFVXUyk.js +410 -0
- package/dist/workspace-9EFVXUyk.js.map +1 -0
- package/docs/consumer-skill/SKILL.md +14 -129
- package/docs/consumer-skill/examples/app-layout.tsx +22 -12
- package/docs/consumer-skill/examples/authentication.tsx +77 -0
- package/docs/consumer-skill/examples/edit.tsx +30 -38
- package/docs/consumer-skill/examples/manifest.json +51 -1
- package/docs/consumer-skill/examples/rhf-bindings.tsx +86 -0
- package/docs/consumer-skill/examples/rhf-edit-page.tsx +141 -0
- package/docs/consumer-skill/examples/rhf-overlay-form.tsx +92 -0
- package/docs/consumer-skill/examples/rhf-transport.tsx +28 -0
- package/docs/consumer-skill/references/agents-md.md +17 -7
- package/docs/consumer-skill/references/authentication.md +42 -0
- package/docs/consumer-skill/references/component-contracts.json +319824 -0
- package/docs/consumer-skill/references/composition-mapping.md +92 -0
- package/docs/consumer-skill/references/composition.md +43 -19
- package/docs/consumer-skill/references/design-contract.md +32 -0
- package/docs/consumer-skill/references/discovery.md +84 -7
- package/docs/consumer-skill/references/installation.md +40 -36
- package/docs/consumer-skill/references/integration.md +6 -2
- package/docs/consumer-skill/references/public-api.json +1519 -65
- package/docs/consumer-skill/references/review.md +9 -4
- package/docs/consumer-skill/references/rhf-workflows.md +86 -0
- package/docs/consumer-skill/references/screen-archetypes.md +22 -0
- package/docs/consumer-skill/references/theming.md +8 -9
- package/docs/consumer-skill/references/tokens.json +12 -3
- package/docs/consumer-skill/references/workflows.md +2 -1
- package/docs/consumer-skill/scripts/composition-schema.d.mts +2 -0
- package/docs/consumer-skill/scripts/composition-schema.mjs +32 -0
- package/docs/consumer-skill/scripts/composition.mjs +198 -0
- package/docs/consumer-skill/scripts/discovery.mjs +42 -10
- package/docs/consumer-skill/scripts/inspect-component.mjs +11 -0
- package/docs/consumer-skill/scripts/inspect-tokens.mjs +11 -0
- package/docs/consumer-skill/scripts/inspection.mjs +138 -0
- package/docs/consumer-skill/scripts/map-composition.mjs +12 -0
- package/docs/consumer-skill/scripts/setup-skills.mjs +76 -0
- package/docs/consumer-skills/themelia-components/SKILL.md +30 -0
- package/docs/consumer-skills/themelia-composition/SKILL.md +35 -0
- package/docs/consumer-skills/themelia-forms/SKILL.md +12 -0
- package/docs/consumer-skills/themelia-foundation/SKILL.md +14 -0
- package/docs/consumer-skills/themelia-tokenization/SKILL.md +35 -0
- package/package.json +4 -2
- package/templates/README.md +39 -0
- package/templates/admin-shell-template.tsx +382 -0
- package/templates/analytics-operations-template.tsx +376 -0
- package/templates/customers-template.tsx +509 -0
- package/templates/index.ts +29 -0
- package/templates/orders-template.tsx +1102 -0
- package/templates/products-template.tsx +834 -0
- package/templates/user-permissions-template.tsx +450 -0
- package/dist/action-menu-CllU52Mm.js +0 -280
- package/dist/action-menu-CllU52Mm.js.map +0 -1
- package/dist/affordance-geometry.generated-tF36bREk.js.map +0 -1
- package/dist/ai-artifact-DvTwDVyL.js.map +0 -1
- package/dist/app-shell-C4FhvCqF.js.map +0 -1
- package/dist/cards-BaBZR-AU.js.map +0 -1
- package/dist/checkbox-AWIhNhmo.js.map +0 -1
- package/dist/choice-inputs-CNHvARCJ.js.map +0 -1
- package/dist/comments-DD0DUPwJ.js.map +0 -1
- package/dist/containers-Mu1LMGV1.js +0 -103
- package/dist/containers-Mu1LMGV1.js.map +0 -1
- package/dist/data-display-DT_khPl_.js.map +0 -1
- package/dist/date-picker-B2K3ucWn.js.map +0 -1
- package/dist/date-pickers-Ds8gLaoP.js +0 -175
- package/dist/date-pickers-Ds8gLaoP.js.map +0 -1
- package/dist/form-field-CwH9y77i.js.map +0 -1
- package/dist/forms-B9G_xwzi.js.map +0 -1
- package/dist/forms-numeric-B_jraJaQ.js.map +0 -1
- package/dist/input-DQdEd0YD.js +0 -263
- package/dist/input-DQdEd0YD.js.map +0 -1
- package/dist/item-K5_ghvze.js +0 -275
- package/dist/item-K5_ghvze.js.map +0 -1
- package/dist/metadata-list-8N16ZMW_.js +0 -405
- package/dist/metadata-list-8N16ZMW_.js.map +0 -1
- package/dist/overflow-tab-bar-CWY7Nogn.js.map +0 -1
- package/dist/overlays-tMkF-VAN.js.map +0 -1
- package/dist/page-CL9YeH_V.js +0 -224
- package/dist/page-CL9YeH_V.js.map +0 -1
- package/dist/page-header-commands-DOb6WsfD.js +0 -126
- package/dist/page-header-commands-DOb6WsfD.js.map +0 -1
- package/dist/settings-ybnjVwbd.js +0 -171
- package/dist/settings-ybnjVwbd.js.map +0 -1
- package/dist/smart-card-DiC5aac3.js +0 -604
- package/dist/smart-card-DiC5aac3.js.map +0 -1
- package/dist/text-inputs-f9Ia-aCi.js.map +0 -1
- package/dist/text.styles-g4He6UIW.js.map +0 -1
- package/dist/typography-UTnveOEy.js +0 -117
- package/dist/typography-UTnveOEy.js.map +0 -1
- package/dist/ui-provider-C_U0Xma4.js.map +0 -1
- package/dist/usage-card-BhO5eqkX.js.map +0 -1
- package/dist/utils-BRhFDwuU.js.map +0 -1
- package/dist/value-inputs-TaX4OjNk.js +0 -963
- package/dist/value-inputs-TaX4OjNk.js.map +0 -1
- package/dist/workspace-B-OEm3Kf.js +0 -408
- package/dist/workspace-B-OEm3Kf.js.map +0 -1
|
@@ -24,21 +24,26 @@ one solid primary command per page or surface, so the next action stays obvious.
|
|
|
24
24
|
|
|
25
25
|
## Done means
|
|
26
26
|
|
|
27
|
-
UI work is
|
|
28
|
-
|
|
27
|
+
UI work is complete only with three separate proofs: source/API checks, inspected
|
|
28
|
+
rendered appearance and executed interactions. The source checker below must
|
|
29
|
+
report no errors on changed files, and the app's typecheck, lint and tests must
|
|
30
|
+
pass. Inspect the actual narrow/wide and light/dark states relevant to the change,
|
|
31
|
+
including opened surfaces; execute the affected callbacks, keyboard/focus and
|
|
32
|
+
error/retry paths. Record any missing proof as an unverified limit. Run the checker, fix
|
|
29
33
|
the owner or prop each finding names, and run it again; after three rounds, stop
|
|
30
34
|
and report what remains. Warnings are review findings: fix each one or keep it
|
|
31
35
|
with a reason. This is the agent's completion condition, not the build's; app CI
|
|
32
36
|
may stay non-blocking on warnings. The [project instructions
|
|
33
37
|
block](agents-md.md) states the same condition in the app's `AGENTS.md`.
|
|
34
38
|
|
|
35
|
-
Hand back
|
|
39
|
+
Hand back a short evidence record, so skipped checks and missing proof are visible:
|
|
36
40
|
|
|
37
41
|
1. Owners used for the route, its commands and each data region.
|
|
38
42
|
2. Checker: run or not run, with its error and warning counts.
|
|
39
43
|
3. Typecheck, lint and tests: each run or not run, and why.
|
|
40
44
|
4. Kept warnings, each with file:line and its reason.
|
|
41
|
-
5.
|
|
45
|
+
5. Rendered: widths/themes/states measured and images actually inspected, with judgment.
|
|
46
|
+
6. Interactions: actions and resulting states executed, including relevant focus, cancellation, loading, error/retry and validation; list missing coverage.
|
|
42
47
|
|
|
43
48
|
## Source checker
|
|
44
49
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# React Hook Form workflows
|
|
2
|
+
|
|
3
|
+
Use `react-hook-form` for form state in create/edit pages, settings and overlays. Import chrome from `themelia-react-ui/base/forms`, text controls from `base/text-inputs`, choices from `base/choice-inputs`, dates from `base/date-pickers` and structured values from `base/value-inputs`. RHF is optional at package level, but the whole `base/forms` family requires its peer, including presentation-only FormField imports. A schema resolver is an application choice, not another required library dependency.
|
|
4
|
+
|
|
5
|
+
## Screen and state ownership
|
|
6
|
+
|
|
7
|
+
The complete [order edit page](../examples/rhf-edit-page.tsx) composes:
|
|
8
|
+
|
|
9
|
+
`Page render={<form ... />}` owns the native form, containing `DirtyStateBanner / ErrorSummary → TwoColumnLayout → FormSection → ControlledFormField → sticky FormActionsBar / SubmitStateButton`.
|
|
10
|
+
|
|
11
|
+
Page owns the route title/gutters and TwoColumnLayout owns peer rhythm. FormSection owns its surface and field gaps; do not wrap a framed FormSection in another Card. Inside an already padded boundary, use `surface="flat" padding="none"`. Inside an unpadded boundary, a flat FormSection keeps its ordinary inset; flat surface alone does not remove padding. `handleSubmit` validates and awaits persistence. `isSubmitting` drives the submit button and disabled fields; `isDirty` drives dirty feedback and pristine submit protection. An in-flight ref also blocks duplicate same-tick requests. Native form validation is disabled in these RHF examples so the schema/rules produce one consistent error source.
|
|
12
|
+
|
|
13
|
+
Page forwards `render` to its existing Container, so rendering it as the form preserves one width, gutter and query-boundary owner. Its body already spaces direct subjects; no extra Container or Stack is needed around the layout and action bar. `TwoColumnLayout adaptTo="container"` adapts to that Page width. A standalone form outside Page needs its own deliberate query boundary when it selects container adaptation.
|
|
14
|
+
|
|
15
|
+
Use `useFieldArray`'s stable `field.id` as the React key, preserve names such as `lines.${index}.quantity`, and set complete append defaults. Derive totals from `useWatch`; do not persist a second total that can drift. Collect nested client/server errors into ErrorSummary while keeping each field's local message. Use `setFocus` or summary links for focusable controls when the app's error-summary design requires it.
|
|
16
|
+
|
|
17
|
+
Mount an editor with `key={record.id}` when switching records. `defaultValues` establishes the saved baseline. On accepted success call `reset(authoritativeSavedValues)` to clear dirty state and keep server normalization. `reset()` discards to that baseline. Failed validation, transport failure or cancellation keeps the draft. Do not reset on every background cache refresh. If the product allows editing during a request, reconcile the accepted response with later edits explicitly; these recipes disable editing while saving.
|
|
18
|
+
|
|
19
|
+
## Typed control bindings
|
|
20
|
+
|
|
21
|
+
[TypedControlBindings](../examples/rhf-bindings.tsx) typechecks every supported binding without `any`, control casts or a generic spread adapter. A public control's event contract is part of its API:
|
|
22
|
+
|
|
23
|
+
| Control | Controlled state | Change and RHF focus/touch binding |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| Input / Textarea | `value: string` | `onValueChange(value)` → `field.onChange`; `ref`, `onBlur` |
|
|
26
|
+
| Select | `value: string` | `onValueChange(value)` → `field.onChange(value ?? '')`; trigger `ref`, `onBlur` |
|
|
27
|
+
| Switch / Checkbox | `checked: boolean` | `onValueChange(checked)` → `field.onChange`; `ref`, `onBlur` |
|
|
28
|
+
| ToggleField / SwitchCard | `value: boolean` | `onValueChange(checked)`; `controlRef`, `onBlur`; their own label owns the control name |
|
|
29
|
+
| DatePicker (single) | `value: Date \| undefined` | `onValueChange(output)` → `field.onChange(output.date ?? null)`; `triggerRef`, `onBlur` |
|
|
30
|
+
| DateTimeInput | ISO `value: string \| undefined` | `onValueChange(value)` → `field.onChange(value ?? '')`; `triggerRef`, `onBlur` |
|
|
31
|
+
| PhoneInput | `value: string` | native `onChange(event.target.value)`; input `ref`, `onBlur` |
|
|
32
|
+
| TagsInput | `value: string[]` (or typed objects) | `onValueChange(values)`; `inputRef`, `onBlur` |
|
|
33
|
+
| SliderField (public slider name) | scalar `value: number` | `onValueChange(value)`; `inputRef`, `onBlur` |
|
|
34
|
+
| ColorInput | `value: string` | `onValueChange(value)`; input `ref`, `onBlur` |
|
|
35
|
+
|
|
36
|
+
For numeric Input fields normalize `event.target.valueAsNumber` according to the schema (an empty field is NaN); keep numeric validation explicit. Date-only fields need an application serialization policy; do not turn a local calendar date into a UTC instant accidentally. DateTimeInput uses ISO instants. Use `modal={false}` on pickers inside modal forms to avoid competing focus traps.
|
|
37
|
+
|
|
38
|
+
ControlledFormField accepts a ReactNode error override or a name-based error getter, falling back to RHF validation. Its render callback's error argument remains string/undefined for compatibility. A direct supported control receives the generated label/control ID and supporting-text association automatically; omit routine `htmlFor`/`id` pairs. Each mount stays unique even when forms share field names. ToggleField and SwitchCard own their visible labels and forward the automatic supporting description to their editable control; omit an outer duplicate label.
|
|
39
|
+
|
|
40
|
+
Compound children own their nested association. SliderField's `id` and forwarded `ref` name its group, while `inputRef` names the native range. The binding recipe uses visible copy with an ID, `aria-labelledby` on SliderField and `supportingTextId`/`aria-describedby` for its hint or error. Keep explicit IDs per mount when a passive wrapper separates chrome from the editable control; do not point `htmlFor` at a group or infer one common ref target across control families.
|
|
41
|
+
|
|
42
|
+
## Transport lifecycle
|
|
43
|
+
|
|
44
|
+
The app owns transport. RHF's submit callback must return a Promise spanning the complete request; otherwise `isSubmitting` drops early and repeated submissions become possible. The [callback bridge](../examples/rhf-transport.tsx) records success/errors/cancellation, then settles only from `onFinish`. It has no framework import or runtime dependency.
|
|
45
|
+
|
|
46
|
+
For Laravel/Inertia, adapt the application's typed router and page props at this seam:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
const submit = form.handleSubmit(async values => {
|
|
50
|
+
const result = await awaitCallbackSave(values, (data, lifecycle) => {
|
|
51
|
+
router.put(`/orders/${order.id}`, data, {
|
|
52
|
+
preserveScroll: true,
|
|
53
|
+
onSuccess: page => lifecycle.onSuccess(readSavedOrder(page)),
|
|
54
|
+
onError: lifecycle.onError,
|
|
55
|
+
onCancel: lifecycle.onCancel,
|
|
56
|
+
onFinish: lifecycle.onFinish,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
if (result.ok) form.reset(result.values);
|
|
60
|
+
else {
|
|
61
|
+
applyServerErrors(form.setError, result.errors, {
|
|
62
|
+
fields: ['reference', 'email', 'notes', 'priority', 'notify',
|
|
63
|
+
...values.lines.flatMap((_, index) => [
|
|
64
|
+
`lines.${index}.product` as const,
|
|
65
|
+
`lines.${index}.quantity` as const,
|
|
66
|
+
`lines.${index}.priceMinor` as const,
|
|
67
|
+
])],
|
|
68
|
+
});
|
|
69
|
+
if (result.message) form.setError('root.server', { message: result.message });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`router`, `order` and `readSavedOrder(page)` belong to the app; readSavedOrder validates/reads its typed authoritative response. Import `applyServerErrors` from `themelia-react-ui/base/forms` and `awaitCallbackSave` from the copied bridge. The helper accepts unknown payloads, maps only string/string-array messages to an explicit typed FieldPath allowlist, and sends unknown keys to `root.server`. Include current array paths. Avoid casting arbitrary server keys to `keyof FormValues`; a server key may be unknown or a nested path. Client validation, Laravel errors and transport errors then render through one RHF error state. Handle rejection with try/catch as in the complete edit recipe; do not swallow errors or reset a failed draft.
|
|
75
|
+
|
|
76
|
+
For TanStack Start/Query, pass an app-owned async function: `onSave={async values => { const saved = await saveOrder(values); await invalidateOrders(); return { ok: true, values: saved }; }}`. If server validation returns a structured result, return `{ok:false, errors, message}` instead. Await the server function/mutateAsync Promise and any cache work required before the form becomes editable. Do not pass a fire-and-forget `mutate` callback. A router/framework adapter does not replace RHF's frontend state ownership.
|
|
77
|
+
|
|
78
|
+
## Overlay create and edit
|
|
79
|
+
|
|
80
|
+
The complete [customer drawer](../examples/rhf-overlay-form.tsx) uses ActionDrawer and useOverlayVisibility from `features/overlays`. ActionDialog uses the same formId/loading contract for shorter forms. Opening explicitly calls `reset(recordSnapshot)` (or complete empty defaults); onSubmit owns validation and persistence. The overlay footer's `formId` calls requestSubmit on that actual form. Do not combine it with onConfirm/onAsyncConfirm, which would introduce a second submit path.
|
|
81
|
+
|
|
82
|
+
Pass `loading={isSubmitting}` so confirm/cancel/close/Escape/outside dismissal follow the pending lock. A failed request shows ErrorSummary and field errors while the drawer stays open with its values. Success resets to the returned record, publishes it through onSaved, and hides the controlled overlay. Cancelling without saving leaves the committed record unchanged; reopening begins a new snapshot. If a product requires drafts across dismissal, store drafts outside the overlay and make that policy explicit rather than resetting on open.
|
|
83
|
+
|
|
84
|
+
## Verify the real workflow
|
|
85
|
+
|
|
86
|
+
Run the bundled composition checker and the app's type/lint/test scripts. Then execute required-field errors, field focus, typed edits, arrays/reorder/removal, computed totals, duplicate-submit protection, server errors, retry with retained draft, accepted reset and discard. Open the actual date/select/tag surfaces and drawer; check keyboard/dismissal/focus return and light/dark at narrow/wide widths. Static typechecks prove the bindings and exports, not an application's server behavior.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Screen archetypes
|
|
2
|
+
|
|
3
|
+
Choose by the user's job, then inspect the exact declaration with the [finder](discovery.md).
|
|
4
|
+
The [complete shipped blueprint index](../../../templates/README.md) includes
|
|
5
|
+
the application shell and every domain example. These are source assets at the
|
|
6
|
+
installed package's `templates/`; complete skill copying places them under the
|
|
7
|
+
shared skill's `templates/` and rebases the links below.
|
|
8
|
+
|
|
9
|
+
| Screen | Main owner and regions | Starting blueprint | Application-owned state |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| Index / directory | Page → fixed scope/context → FilterLayout → DataView/DataTable; maxWidth="full" when comparison needs it | [Scoped directory](../examples/scoped-directory.tsx), [index table](../examples/index-table.tsx) | URL/query, facets, paging, selection, bulk commands, loading/empty/error/retry |
|
|
12
|
+
| Detail workspace | Page title/status/commands/tabs above TwoColumnLayout; subject Cards in main, facts/support in aside | [Orders](../../../templates/orders-template.tsx), [customers](../../../templates/customers-template.tsx), [record detail](../examples/detail.tsx) | Record snapshot, section/tab, permitted commands, overlay drafts, accepted server results |
|
|
13
|
+
| Settings / edit | `Page render={<form ... />}` → dirty/error feedback → TwoColumnLayout → FormSection → ControlledFormField → sticky FormActionsBar | [RHF edit page](../examples/rhf-edit-page.tsx), [RHF drawer](../examples/rhf-overlay-form.tsx) | RHF values, rules/schema, arrays, computed totals, transport/errors, saved baseline |
|
|
14
|
+
| Product editor | Page → product subjects and publishing/support regions → variant editor workflow | [Products](../../../templates/products-template.tsx) | Product snapshot, option/variant drafts, publishing settings, validation and persistence |
|
|
15
|
+
| User access / permissions | Page → profile and role context → editable permission groups → save/discard feedback | [User permissions](../../../templates/user-permissions-template.tsx) | Role presets, permission draft, permitted commands, accepted-save/discard baseline and server enforcement |
|
|
16
|
+
| Analytics overview | Page period control → numeric MetricBar/MetricGrid → comparison/chart Cards → operations/usage regions | [Operations analytics](../../../templates/analytics-operations-template.tsx), [analytics states](../examples/analytics.tsx) | Period/query, metric formatting, accepted series, independent loading/error/retry states |
|
|
17
|
+
|
|
18
|
+
Page owns its h1, gutters and direct-child rhythm; TwoColumnLayout owns the column split and peer-region rhythm. Stack/Grid own nested sibling gaps. Card owns framing and aligned header/body/footer insets. Authentication uses [AuthShell](authentication.md), and content sites use [Container/Section](../examples/website.tsx).
|
|
19
|
+
|
|
20
|
+
Headers carry one primary command and ordered secondary commands; period selects and similar non-command controls use header actions. Put destructive commands behind ConfirmDialog. Longer create/edit work belongs in ActionDrawer; a short focused form uses ActionDialog. Preserve a snapshot until save succeeds. Templates are copyable application sources, not package component entrypoints.
|
|
21
|
+
|
|
22
|
+
Read only the relevant detail: [composition ownership](composition.md), [workflow states](workflows.md), [RHF transport and bindings](rhf-workflows.md), or [analytics contracts](analytics.md). The [review checklist](review.md) separates static checks from opened-surface and interaction evidence.
|
|
@@ -4,7 +4,7 @@ The [design contract](design-contract.md) records which geometry changes togethe
|
|
|
4
4
|
and which variant choices remain independent. Retheme shared responsibilities;
|
|
5
5
|
avoid overriding individual components to imitate another density or inset.
|
|
6
6
|
|
|
7
|
-
Use shadcn semantic surfaces/foregrounds: background, card, popover, primary, secondary, muted, accent, destructive, border, input, ring, charts and sidebar. Central success/info/warning foreground pairs express states; never overload primary or statuses as decoration. `--input` is the
|
|
7
|
+
Use shadcn semantic surfaces/foregrounds: background, card, popover, primary, secondary, muted, accent, destructive, border, input, ring, charts and sidebar. Central success/info/warning foreground pairs express states; never overload primary or statuses as decoration. `--input` is the shared resting control boundary: fields, select and date triggers, checkboxes, radios, switch tracks and outline buttons draw it. Its defaults follow shadcn neutral: `oklch(0.922 0 0)` in light mode and `oklch(1 0 0 / 15%)` in dark mode. `--border` remains the surface/divider role, and `--ring` owns the stronger focus indicator. No extra border variable or component opacity is needed. Resting borders have no blanket 3:1 requirement in this palette; focus retains its separate tested 3:1 contract. When rebranding, review resting, focused and invalid states on the actual surfaces; an application requiring stronger boundaries can override the existing `--input` role.
|
|
8
8
|
|
|
9
9
|
tokens.json distinguishes 23 structural roots, advanced typography/status settings, and derived utility spellings. A utility is not another public variable. Keep overrides in one stylesheet at root or ThemeScope, without copying computed CSS into JavaScript.
|
|
10
10
|
|
|
@@ -15,8 +15,8 @@ surface, use Text/Value `type="inherit"` and Label `className="text-inherit"`;
|
|
|
15
15
|
standalone typography keeps its documented defaults. Keep secondary, link, status
|
|
16
16
|
and inverse roles explicit, and use a secondary text role instead of fading text
|
|
17
17
|
with opacity. Component disabled props own dimming; an extra faded wrapper can
|
|
18
|
-
multiply it. Brand colors
|
|
19
|
-
|
|
18
|
+
multiply it. Brand colors should preserve the shared resting `--input` palette
|
|
19
|
+
and `--ring` focus role; review deliberate replacements on every host surface.
|
|
20
20
|
|
|
21
21
|
The default `--radius` is 0.75rem (12px). One public root drives two shared
|
|
22
22
|
roles; component owners choose the role, so ordinary callers add no radius class.
|
|
@@ -50,8 +50,8 @@ replacing individual components' owned corners.
|
|
|
50
50
|
--radius: 0.75rem;
|
|
51
51
|
--height-action: 2.5rem;
|
|
52
52
|
--height-control: 2.5rem;
|
|
53
|
-
--surface-x: 1.
|
|
54
|
-
--surface-y: 1.
|
|
53
|
+
--surface-x: 1.5rem;
|
|
54
|
+
--surface-y: 1.5rem;
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -65,9 +65,8 @@ under their intentionally tighter leading, so independently enlarged type can
|
|
|
65
65
|
produce different fitted heights even with equal action/control roots. Check
|
|
66
66
|
type size, leading and height roots together when exact custom pairing matters.
|
|
67
67
|
|
|
68
|
-
Both surface roots default to 1.
|
|
69
|
-
|
|
70
|
-
36px. Page's default large gutters inherit that same recipe. Row insets,
|
|
68
|
+
Both surface roots default to 1.5rem. At a 16px root font and space-scale 1,
|
|
69
|
+
the xs/sm/md/lg surface tiers are 12/16/24/32px. Page's default large gutters inherit that same recipe. Row insets,
|
|
71
70
|
control heights and copy gaps keep their independent roots.
|
|
72
71
|
|
|
73
72
|
| Shared geometry | Root / derivation |
|
|
@@ -76,7 +75,7 @@ control heights and copy gaps keep their independent roots.
|
|
|
76
75
|
| Native field text inset | `--control-x` times space-scale; icons/add-ons use shared derived clearance |
|
|
77
76
|
| Collection inline inset | `--row-x` times space-scale |
|
|
78
77
|
| Collection block inset | `--row-y` times density-scale; compact/comfortable use 0.75/1.25 multipliers |
|
|
79
|
-
|
|
|
78
|
+
| Surface tiers | `--surface-x` / `--surface-y`, times space-scale; xs/sm/md/lg use 1/2, 2/3, 1 and 4/3 |
|
|
80
79
|
| Menu header/footer inline inset | Half surface-x plus row-x, then space-scale; no additional public token |
|
|
81
80
|
|
|
82
81
|
Use discrete inset, density and size props for the component responsibility described in [composition](composition.md). CSS scales are independent continuous theme settings. Primary row text does not shrink to xs merely because a density is compact. Public Input and Command search retain normal desktop editable text; coarse-pointer devices use text-base at mobile and tablet widths. Typography components remain margin-free.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "themelia-react-ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"structural": [
|
|
5
5
|
{
|
|
6
6
|
"name": "--space-scale",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"name": "--surface-x",
|
|
52
|
-
"default": "1.
|
|
52
|
+
"default": "1.5rem",
|
|
53
53
|
"purpose": "Default inline inset for cards and multi-content surfaces."
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"name": "--surface-y",
|
|
57
|
-
"default": "1.
|
|
57
|
+
"default": "1.5rem",
|
|
58
58
|
"purpose": "Default block inset for cards and multi-content surfaces."
|
|
59
59
|
},
|
|
60
60
|
{
|
|
@@ -561,30 +561,39 @@
|
|
|
561
561
|
"py-row-y",
|
|
562
562
|
"py-row-y-lg",
|
|
563
563
|
"p-surface-xs",
|
|
564
|
+
"p-surface-sm",
|
|
564
565
|
"p-surface",
|
|
565
566
|
"p-surface-lg",
|
|
566
567
|
"px-surface-xs",
|
|
568
|
+
"px-surface-sm",
|
|
567
569
|
"px-surface",
|
|
568
570
|
"px-surface-lg",
|
|
569
571
|
"py-surface-xs",
|
|
572
|
+
"py-surface-sm",
|
|
570
573
|
"py-surface",
|
|
571
574
|
"py-surface-lg",
|
|
572
575
|
"pt-surface-xs",
|
|
576
|
+
"pt-surface-sm",
|
|
573
577
|
"pt-surface",
|
|
574
578
|
"pt-surface-lg",
|
|
575
579
|
"pb-surface-xs",
|
|
580
|
+
"pb-surface-sm",
|
|
576
581
|
"pb-surface",
|
|
577
582
|
"pb-surface-lg",
|
|
578
583
|
"ps-surface-xs",
|
|
584
|
+
"ps-surface-sm",
|
|
579
585
|
"ps-surface",
|
|
580
586
|
"ps-surface-lg",
|
|
581
587
|
"pe-surface-xs",
|
|
588
|
+
"pe-surface-sm",
|
|
582
589
|
"pe-surface",
|
|
583
590
|
"pe-surface-lg",
|
|
584
591
|
"pl-surface-xs",
|
|
592
|
+
"pl-surface-sm",
|
|
585
593
|
"pl-surface",
|
|
586
594
|
"pl-surface-lg",
|
|
587
595
|
"pr-surface-xs",
|
|
596
|
+
"pr-surface-sm",
|
|
588
597
|
"pr-surface",
|
|
589
598
|
"pr-surface-lg"
|
|
590
599
|
]
|
|
@@ -122,7 +122,8 @@ definitions; the app then chooses appropriate destination focus.
|
|
|
122
122
|
Use `Page` from `themelia-react-ui/layout/page` for the record route's identity,
|
|
123
123
|
back navigation, content bounds, commands and section tabs. Pass the record's
|
|
124
124
|
main command as `header.primaryAction`, supporting commands as
|
|
125
|
-
`header.secondaryActions` and sections as `header.tabs`;
|
|
125
|
+
`header.secondaryActions` and separately selected/routed sections as `header.tabs`;
|
|
126
|
+
continuous sections can stay in the body. One status badge goes in
|
|
126
127
|
`header.titleBadges`. `PageHeader` serves a custom shell; `PageActions` is
|
|
127
128
|
deprecated. The [layout recipe](../examples/app-layout.tsx) demonstrates that
|
|
128
129
|
route boundary; the [detail recipe](../examples/detail.tsx) demonstrates the
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const record = value => value !== null && typeof value === 'object' && !Array.isArray(value) && [Object.prototype, null].includes(Object.getPrototypeOf(value));
|
|
2
|
+
const fail = (condition, message) => { if (!condition) throw new Error(`Invalid composition mapping: ${message}`); };
|
|
3
|
+
const name = value => typeof value === 'string' && /^[A-Za-z][A-Za-z0-9]*$/.test(value);
|
|
4
|
+
const uniqueList = (value, valid, maximum) => Array.isArray(value) && value.length <= maximum && value.every(valid) && new Set(value).size === value.length;
|
|
5
|
+
const scalar = value => value === null || typeof value === 'boolean' || (typeof value === 'string' && value.length <= 500) || (typeof value === 'number' && Number.isFinite(value));
|
|
6
|
+
const kinds = ['shell', 'page', 'layout', 'surface', 'content', 'workflow'];
|
|
7
|
+
const placements = ['region', 'cell', 'overlay'];
|
|
8
|
+
const surfaces = ['none', 'parent', 'self', 'variant'];
|
|
9
|
+
|
|
10
|
+
/** One literal metadata shape shared by authored catalogue and copied tools. */
|
|
11
|
+
export function validateMappings(mappings) {
|
|
12
|
+
fail(Array.isArray(mappings) && mappings.length <= 64, 'expected an array of at most 64 mappings');
|
|
13
|
+
for (const mapping of mappings) {
|
|
14
|
+
fail(record(mapping), 'expected a plain mapping object');
|
|
15
|
+
fail(Object.keys(mapping).every(key => ['symbol', 'roles', 'kind', 'placement', 'surface', 'slots', 'props', 'routeSlot'].includes(key)), 'unknown mapping field');
|
|
16
|
+
fail(typeof mapping.symbol === 'string' && /^[A-Z][A-Za-z0-9]*$/.test(mapping.symbol), 'symbol must be an exact public component name');
|
|
17
|
+
fail(uniqueList(mapping.roles, value => typeof value === 'string' && /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(value), 8) && mapping.roles.length > 0, `${mapping.symbol}: use 1..8 distinct kebab-case roles`);
|
|
18
|
+
fail(kinds.includes(mapping.kind), `${mapping.symbol}: invalid kind`);
|
|
19
|
+
fail(placements.includes(mapping.placement), `${mapping.symbol}: invalid placement`);
|
|
20
|
+
fail(surfaces.includes(mapping.surface), `${mapping.symbol}: invalid surface`);
|
|
21
|
+
if (mapping.slots !== undefined) fail(uniqueList(mapping.slots, name, 16), `${mapping.symbol}: invalid or duplicate slots`);
|
|
22
|
+
if (mapping.routeSlot !== undefined) fail(mapping.kind === 'shell' && name(mapping.routeSlot) && (mapping.slots ?? []).includes(mapping.routeSlot), `${mapping.symbol}: routeSlot must name a mapped shell insertion slot`);
|
|
23
|
+
if (mapping.props !== undefined) {
|
|
24
|
+
fail(record(mapping.props) && Object.keys(mapping.props).length <= 16, `${mapping.symbol}: props must be a small literal object`);
|
|
25
|
+
for (const [key, value] of Object.entries(mapping.props)) {
|
|
26
|
+
fail(/^[A-Za-z][\w-]*$/.test(key) && !['__proto__', 'constructor', 'prototype'].includes(key) && scalar(value), `${mapping.symbol}: invalid literal prop ${key}`);
|
|
27
|
+
fail(!(mapping.slots ?? []).includes(key), `${mapping.symbol}: ${key} cannot be both an insertion slot and a fixed prop`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return mappings;
|
|
32
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {validateIndex} from './discovery.mjs';
|
|
2
|
+
import {validateComponentContracts} from './inspection.mjs';
|
|
3
|
+
import {validateMappings} from './composition-schema.mjs';
|
|
4
|
+
|
|
5
|
+
const fail = (condition, message) => { if (!condition) throw new Error(message); };
|
|
6
|
+
const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
7
|
+
const unique = values => [...new Set(values)].sort(compare);
|
|
8
|
+
const patterns = ['record-detail', 'collection', 'edit', 'analytics', 'embedded'];
|
|
9
|
+
const roleKey = value => value.trim().toLowerCase().replace(/\s+/g, '-');
|
|
10
|
+
const identity = mapping => `${mapping.import}#${mapping.symbol}`;
|
|
11
|
+
|
|
12
|
+
function publicProps(contract, contracts) {
|
|
13
|
+
const inherited = contracts.nativeProfiles[contract.inheritedProfile] ?? {};
|
|
14
|
+
return Object.assign(Object.create(null), Object.fromEntries(Object.entries(inherited).map(([key, value]) => [key, typeof value === 'string' ? contracts.nativeProperties[value] : value])), contract.props);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Resolve authored rows against the same qualified contracts as API inspection. */
|
|
18
|
+
export function validateCompositionMappings(index, contracts) {
|
|
19
|
+
validateIndex(index);
|
|
20
|
+
validateComponentContracts(contracts, index);
|
|
21
|
+
const byContract = new Map(contracts.contracts.map(contract => [identity(contract), contract]));
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
const mappings = [];
|
|
24
|
+
for (const family of index.families) for (const guidance of family.guidance) {
|
|
25
|
+
for (const mapping of validateMappings(guidance.composition?.mappings ?? [])) {
|
|
26
|
+
const key = `${family.import}#${mapping.symbol}`;
|
|
27
|
+
fail(!seen.has(key), `Duplicate composition mapping: ${key}`); seen.add(key);
|
|
28
|
+
fail(family.symbols.includes(mapping.symbol), `Composition mapping is not a public export: ${key}`);
|
|
29
|
+
fail(!Object.hasOwn(family.deprecated ?? {}, mapping.symbol), `Composition mapping uses a deprecated export: ${key}`);
|
|
30
|
+
const contract = byContract.get(key);
|
|
31
|
+
fail(contract, `Composition mapping has no callable component contract: ${key}`);
|
|
32
|
+
const props = publicProps(contract, contracts);
|
|
33
|
+
for (const slot of mapping.slots ?? []) {
|
|
34
|
+
fail(props[slot], `Unknown composition slot ${slot} on ${key}`);
|
|
35
|
+
fail(/\b(?:ReactNode|ReactElement)\b|JSX\.Element/.test(props[slot].type), `Composition slot ${slot} on ${key} is not a documented React insertion prop`);
|
|
36
|
+
}
|
|
37
|
+
for (const prop of Object.keys(mapping.props ?? {})) fail(props[prop], `Unknown composition prop ${prop} on ${key}`);
|
|
38
|
+
const recipes = (family.recipes ?? []).filter(recipe => recipe.symbols.includes(mapping.symbol));
|
|
39
|
+
mappings.push({...mapping, import: family.import, declaration: family.declaration, optionalPeers: family.optionalPeers,
|
|
40
|
+
guidance, nextReads: {declaration: family.declaration,
|
|
41
|
+
inspect: `node scripts/inspect-component.mjs ${mapping.symbol} --import ${family.import}`,
|
|
42
|
+
references: unique([...(family.references ?? []), ...recipes.flatMap(recipe => recipe.references)]),
|
|
43
|
+
examples: recipes.map(({file, exports, optionalPeers = []}) => ({file, exports, optionalPeers})),
|
|
44
|
+
}});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return mappings.sort((a, b) => compare(identity(a), identity(b)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateOptions(options) {
|
|
51
|
+
const value = {...options, main: options.main ?? [], aside: options.aside ?? [], limit: options.limit ?? 3};
|
|
52
|
+
fail(Number.isInteger(value.limit) && value.limit >= 1 && value.limit <= 10, 'Invalid limit: use 1..10');
|
|
53
|
+
fail(Number(value.roles === true) + Number(value.role !== undefined) + Number(value.pattern !== undefined) === 1, 'Choose one mode: --roles, --role or --pattern');
|
|
54
|
+
for (const region of ['main', 'aside']) fail(Array.isArray(value[region]) && value[region].length <= 24 && value[region].every(need => typeof need === 'string' && need.trim().length > 0 && need.length <= 300), `Invalid ${region} needs; use up to 24 explicit role tags or qualified selectors`);
|
|
55
|
+
if (value.pattern !== undefined) {
|
|
56
|
+
fail(patterns.includes(value.pattern), `Invalid pattern: choose ${patterns.join(', ')}`);
|
|
57
|
+
fail(value.main.length + value.aside.length > 0, 'A plan needs at least one --main or --aside requirement');
|
|
58
|
+
} else {
|
|
59
|
+
fail(value.main.length + value.aside.length === 0, 'Main/aside needs require --pattern');
|
|
60
|
+
if (value.role !== undefined) fail(typeof value.role === 'string' && value.role.trim().length > 0, 'A nonempty role is required');
|
|
61
|
+
}
|
|
62
|
+
if (value.parent !== undefined) fail(value.pattern === 'embedded' && typeof value.parent === 'string' && /^[A-Z][A-Za-z0-9]*$/.test(value.parent), '--parent must be an exact component name with --pattern embedded');
|
|
63
|
+
if (value.publicImport !== undefined) fail(value.parent !== undefined && typeof value.publicImport === 'string' && value.publicImport.length > 0, '--import qualifies an explicit embedded --parent');
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function parseCompositionArguments(args) {
|
|
68
|
+
const options = {main: [], aside: [], limit: 3};
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
for (let index = 0; index < args.length; index++) {
|
|
71
|
+
const arg = args[index];
|
|
72
|
+
fail(['--roles', '--role', '--pattern', '--main', '--aside', '--parent', '--import', '--limit'].includes(arg), `Unknown argument: ${arg}`);
|
|
73
|
+
fail(['--main', '--aside'].includes(arg) || !seen.has(arg), `Duplicate argument: ${arg}`); seen.add(arg);
|
|
74
|
+
if (arg === '--roles') { options.roles = true; continue; }
|
|
75
|
+
const value = args[++index];
|
|
76
|
+
fail(typeof value === 'string' && value.trim() && !value.startsWith('--'), `Missing value for ${arg}`);
|
|
77
|
+
if (arg === '--main' || arg === '--aside') options[arg.slice(2)].push(value.trim());
|
|
78
|
+
else if (arg === '--limit') { fail(/^[1-9]\d*$/.test(value), 'Invalid limit: use 1..10'); options.limit = Number(value); }
|
|
79
|
+
else options[arg === '--import' ? 'publicImport' : arg.slice(2)] = value.trim();
|
|
80
|
+
}
|
|
81
|
+
validateOptions(options);
|
|
82
|
+
return options;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function candidate(mapping) {
|
|
86
|
+
return {import: mapping.import, symbol: mapping.symbol, roles: mapping.roles, kind: mapping.kind,
|
|
87
|
+
placement: mapping.placement, surface: mapping.surface, slots: mapping.slots ?? [], props: mapping.props ?? {},
|
|
88
|
+
...(mapping.routeSlot ? {routeSlot: mapping.routeSlot} : {}),
|
|
89
|
+
optionalPeers: mapping.optionalPeers, applicationOwns: mapping.guidance.appOwned,
|
|
90
|
+
chooseWhen: mapping.guidance.chooseWhen, avoidWhen: mapping.guidance.avoidWhen, nextReads: mapping.nextReads};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Exact roles and explicit regions yield an advisory structure, never guessed APIs. */
|
|
94
|
+
export function mapComposition(index, contracts, rawOptions) {
|
|
95
|
+
const options = validateOptions(rawOptions);
|
|
96
|
+
const mappings = validateCompositionMappings(index, contracts);
|
|
97
|
+
const base = {package: index.package, version: index.version, advisory: true,
|
|
98
|
+
baselinePeers: unique((index.peers ?? []).filter(peer => !peer.optional).map(peer => peer.name)),
|
|
99
|
+
peerScope: 'requiredPeers covers selected-family optional peers; baselinePeers apply to every consumer'};
|
|
100
|
+
const matches = request => request.includes('#')
|
|
101
|
+
? mappings.filter(mapping => identity(mapping) === request)
|
|
102
|
+
: /^[A-Z][A-Za-z0-9]*$/.test(request) || /[\/#]/.test(request) ? []
|
|
103
|
+
: mappings.filter(mapping => mapping.roles.includes(roleKey(request)));
|
|
104
|
+
if (options.roles) {
|
|
105
|
+
const roles = unique(mappings.flatMap(mapping => mapping.roles)).map(role => ({role, owners: mappings.filter(mapping => mapping.roles.includes(role)).length}));
|
|
106
|
+
return {...base, mode: 'roles', total: roles.length, roles};
|
|
107
|
+
}
|
|
108
|
+
if (options.role !== undefined) {
|
|
109
|
+
const found = matches(options.role);
|
|
110
|
+
return {...base, mode: 'lookup', status: found.length ? 'mapped' : 'unresolved', query: options.role, total: found.length,
|
|
111
|
+
results: found.slice(0, options.limit).map(candidate), omittedResults: Math.max(0, found.length - options.limit)};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const unresolved = [], warnings = [], selected = [];
|
|
115
|
+
const node = (mapping, decision = 'none') => {
|
|
116
|
+
selected.push(mapping);
|
|
117
|
+
return {import: mapping.import, symbol: mapping.symbol, kind: mapping.kind, surface: mapping.surface,
|
|
118
|
+
surfaceDecision: decision, props: {...mapping.props}, slots: {}};
|
|
119
|
+
};
|
|
120
|
+
const issue = (request, region, reason, found = []) => unresolved.push({request, region, reason, totalCandidates: found.length, candidates: found.slice(0, options.limit).map(candidate)});
|
|
121
|
+
const resolve = (request, region, found = matches(request)) => {
|
|
122
|
+
if (found.length !== 1) { issue(request, region, found.length ? 'Role is ambiguous; select an exact import#symbol or a more specific mapped role.' : 'No exact mapping covers this requirement. Use --roles or the component finder; do not infer an API.', found); return null; }
|
|
123
|
+
return found[0];
|
|
124
|
+
};
|
|
125
|
+
const insertion = (mapping, slot, request, region) => {
|
|
126
|
+
if (!(mapping.slots ?? []).includes(slot)) { issue(request, region, `${mapping.symbol} has no mapped ${slot} insertion slot. Inspect its API rather than nesting into an invented prop.`, [mapping]); return false; }
|
|
127
|
+
return true;
|
|
128
|
+
};
|
|
129
|
+
const structure = (role, kind, slots, region = 'structure') => {
|
|
130
|
+
const mapping = resolve(role, region);
|
|
131
|
+
if (!mapping) return null;
|
|
132
|
+
if (mapping.kind !== kind || mapping.placement !== 'region' || (kind === 'surface' && mapping.surface !== 'self')) {
|
|
133
|
+
issue(role, region, `The ${role} mapping does not establish the required ${kind} boundary. Inspect the authored mapping.`, [mapping]); return null;
|
|
134
|
+
}
|
|
135
|
+
if (!slots.every(slot => insertion(mapping, slot, role, region))) return null;
|
|
136
|
+
return mapping;
|
|
137
|
+
};
|
|
138
|
+
let rootMapping = null, root = null, host = null, mainSlot = 'main', asideSlot = 'aside';
|
|
139
|
+
if (options.pattern === 'embedded') {
|
|
140
|
+
if (options.parent) {
|
|
141
|
+
rootMapping = resolve(options.parent, 'structure', mappings.filter(mapping => mapping.symbol === options.parent && (!options.publicImport || mapping.import === options.publicImport)));
|
|
142
|
+
if (rootMapping && rootMapping.placement !== 'region') { issue(options.parent, 'structure', 'This parent belongs to a renderer or overlay boundary, not a subject region.', [rootMapping]); rootMapping = null; }
|
|
143
|
+
if (rootMapping) {
|
|
144
|
+
root = host = node(rootMapping, rootMapping.surface === 'self' ? 'existing-surface' : rootMapping.surface === 'variant' ? 'inspect-variant-host' : 'existing-host');
|
|
145
|
+
mainSlot = (rootMapping.slots ?? []).includes('children') ? 'children' : 'main';
|
|
146
|
+
if (rootMapping.surface === 'variant') warnings.push({code: 'variant-host-surface', message: `${rootMapping.symbol} may already provide framing in its current variant or state. Inspect its contract and existing ancestors before composing another surface.`});
|
|
147
|
+
}
|
|
148
|
+
} else warnings.push({code: 'host-ownership-unverified', message: 'Embedded requirements retain the caller\'s host. Confirm its insertion slot, framing, padding and gaps; no Page or new surface was added.'});
|
|
149
|
+
} else {
|
|
150
|
+
rootMapping = structure('route', 'page', ['children']);
|
|
151
|
+
if (rootMapping) root = host = node(rootMapping);
|
|
152
|
+
mainSlot = 'children';
|
|
153
|
+
if (rootMapping && options.aside.length) {
|
|
154
|
+
const layout = structure('main-support-layout', 'layout', ['main', 'aside']);
|
|
155
|
+
if (layout && insertion(rootMapping, 'children', 'main-support-layout', 'structure')) {
|
|
156
|
+
host = node(layout); root.slots.children = [host]; rootMapping = layout; mainSlot = 'main';
|
|
157
|
+
} else host = null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let resolved = 0;
|
|
162
|
+
const regions = {main: [], aside: []};
|
|
163
|
+
for (const region of ['main', 'aside']) for (const request of options[region]) {
|
|
164
|
+
const mapping = resolve(request, region);
|
|
165
|
+
if (!mapping) continue;
|
|
166
|
+
if (mapping.placement !== 'region') { issue(request, region, mapping.placement === 'cell' ? 'This owner belongs in a table/collection renderer, not directly in a subject region.' : 'This owner belongs at the app/feature overlay boundary; open it through callbacks.', [mapping]); continue; }
|
|
167
|
+
const slot = region === 'main' ? mainSlot : asideSlot;
|
|
168
|
+
if (['page', 'shell'].includes(mapping.kind) && !(options.pattern === 'embedded' && rootMapping?.routeSlot === slot && mapping.kind === 'page')) { issue(request, region, 'A page or shell is not an independent content subject. Only an explicit route-host slot may receive a Page; keep one route title/gutter owner.', [mapping]); continue; }
|
|
169
|
+
if ((!host && options.pattern !== 'embedded') || (!host && options.parent)) { issue(request, region, 'The structural owner is unresolved, so this requirement has not been placed.', [mapping]); continue; }
|
|
170
|
+
if (host && !insertion(rootMapping, slot, request, region)) continue;
|
|
171
|
+
let child;
|
|
172
|
+
const insideSurface = rootMapping?.surface === 'self';
|
|
173
|
+
if (mapping.surface === 'parent' && host && !insideSurface && options.pattern !== 'embedded') {
|
|
174
|
+
const frame = structure('subject-surface', 'surface', ['children'], region);
|
|
175
|
+
if (!frame) continue;
|
|
176
|
+
child = node(frame, 'recommended-frame');
|
|
177
|
+
child.slots.children = [node(mapping, 'uses-parent')];
|
|
178
|
+
} else {
|
|
179
|
+
child = node(mapping, mapping.surface === 'self' ? 'owns-surface' : mapping.surface === 'parent' ? insideSurface ? 'uses-parent' : 'caller-surface-unverified' : mapping.surface === 'variant' ? 'inspect-variant' : 'none');
|
|
180
|
+
}
|
|
181
|
+
if (mapping.surface === 'variant') warnings.push({code: 'variant-surface', request, region, message: `${mapping.symbol} owns framing only in some variants or states. Inspect its contract before adding a surface.`});
|
|
182
|
+
if (mapping.surface === 'parent' && options.pattern === 'embedded' && host && !insideSurface) warnings.push({code: 'host-surface-unverified', request, region, message: 'The explicit host does not establish a known surface boundary. Confirm its ancestors before adding framing; no extra Card was added.'});
|
|
183
|
+
if (insideSurface && mapping.surface === 'self') warnings.push({code: 'nested-surface-review', request, region, message: `${mapping.symbol} already owns a surface. Review its supported embedded variant or use an unframed host; no props were guessed.`});
|
|
184
|
+
else if (rootMapping?.surface === 'variant' && mapping.surface === 'self') warnings.push({code: 'nested-surface-review', request, region, message: `${mapping.symbol} owns a surface and its host may also frame it. Inspect both variants before keeping nested surfaces; no embedded props were guessed.`});
|
|
185
|
+
regions[region].push(child);
|
|
186
|
+
if (host) (host.slots[slot] ??= []).push(child);
|
|
187
|
+
resolved++;
|
|
188
|
+
}
|
|
189
|
+
const requested = options.main.length + options.aside.length;
|
|
190
|
+
const selectedMappings = [...new Map(selected.map(mapping => [identity(mapping), mapping])).values()];
|
|
191
|
+
if (options.pattern === 'edit') warnings.push({code: 'native-form-required', message: 'Provide the application-owned native form, validation and awaited submit/save/discard lifecycle. This structural plan does not create form state or transport.'});
|
|
192
|
+
return {...base, mode: 'plan', status: unresolved.length ? resolved ? 'partial' : 'unresolved' : 'mapped', pattern: options.pattern, patternScope: 'structural-context-only',
|
|
193
|
+
tree: root, ...(root ? {} : {regions}), coverage: {requested, resolved, unresolved: requested - resolved}, unresolved,
|
|
194
|
+
requiredPeers: unique(selectedMappings.flatMap(mapping => mapping.optionalPeers)),
|
|
195
|
+
applicationOwns: unique(selectedMappings.flatMap(mapping => mapping.guidance.appOwned)), warnings,
|
|
196
|
+
nextReads: selectedMappings.map(mapping => ({import: mapping.import, symbol: mapping.symbol, ...mapping.nextReads})),
|
|
197
|
+
limits: 'This is an advisory role/slot map. Supply application data, state, callbacks and content; inspect APIs and execute visual/interaction checks before delivery.'};
|
|
198
|
+
}
|