themelia-react-ui 2.0.5 → 2.0.6
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 +76 -0
- package/MIGRATION.md +119 -0
- package/README.md +13 -6
- package/dist/{accordion-B9MHG0cT.js → accordion-0tQUZ20r.js} +7 -7
- package/dist/accordion-0tQUZ20r.js.map +1 -0
- package/dist/{action-menu-Dr8K-ghE.js → action-menu-C0gjzLyE.js} +7 -7
- package/dist/{action-menu-Dr8K-ghE.js.map → action-menu-C0gjzLyE.js.map} +1 -1
- package/dist/{ai-artifact-C58tQcBy.js → ai-artifact-CGGpQryR.js} +172 -157
- package/dist/ai-artifact-CGGpQryR.js.map +1 -0
- package/dist/{app-shell-B_IuNWOo.js → app-shell-EHuuRDGy.js} +5 -5
- package/dist/{app-shell-B_IuNWOo.js.map → app-shell-EHuuRDGy.js.map} +1 -1
- package/dist/{attachment-Dg_69lPS.js → attachment-BCpA4e7i.js} +8 -8
- package/dist/{attachment-Dg_69lPS.js.map → attachment-BCpA4e7i.js.map} +1 -1
- package/dist/{auth-DTkkshS8.js → auth-B0girIk_.js} +15 -15
- package/dist/auth-B0girIk_.js.map +1 -0
- package/dist/{avatar-CeZaFmNH.js → avatar-B9Jnp-T-.js} +3 -3
- package/dist/{avatar-CeZaFmNH.js.map → avatar-B9Jnp-T-.js.map} +1 -1
- package/dist/{badge-xIiV_E7f.js → badge-wWauMgAt.js} +3 -3
- package/dist/{badge-xIiV_E7f.js.map → badge-wWauMgAt.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 +1 -1
- 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 +4 -4
- package/dist/base/forms-numeric.js +1 -1
- package/dist/base/forms.js +2 -2
- package/dist/base/inline-edit.js +8 -8
- 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 +195 -188
- package/dist/base/repeaters.js.map +1 -1
- package/dist/base/sheet.js +1 -1
- package/dist/base/sidebar.js +2 -2
- package/dist/base/signature-pad.js +7 -7
- package/dist/base/skeleton.js +1 -1
- package/dist/base/spinner.js +5 -5
- package/dist/base/spinner.js.map +1 -1
- 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 +1 -1
- 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-Cb8cuvyy.js → batch-action-bar-BoqWIIpV.js} +5 -5
- package/dist/{batch-action-bar-Cb8cuvyy.js.map → batch-action-bar-BoqWIIpV.js.map} +1 -1
- package/dist/blocks/admin.js +17 -17
- package/dist/blocks/ai.js +227 -221
- package/dist/blocks/ai.js.map +1 -1
- package/dist/blocks/analytics/usage-card.js +1 -1
- package/dist/blocks/analytics.js +424 -449
- 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 +225 -220
- 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-J-K2kl4c.js → breadcrumbs-Bp9Ke7kd.js} +4 -4
- package/dist/breadcrumbs-Bp9Ke7kd.js.map +1 -0
- package/dist/{button-S2Cubiiz.js → button-ChBovTx0.js} +2 -2
- package/dist/{button-S2Cubiiz.js.map → button-ChBovTx0.js.map} +1 -1
- package/dist/{buttons-BedGfygC.js → buttons-DeW0bQeK.js} +6 -6
- package/dist/{buttons-BedGfygC.js.map → buttons-DeW0bQeK.js.map} +1 -1
- package/dist/{cards-rNX1M4Xn.js → cards-CNXnmvV5.js} +8 -8
- package/dist/{cards-rNX1M4Xn.js.map → cards-CNXnmvV5.js.map} +1 -1
- package/dist/{cascader-B1MiqkBq.js → cascader-CrAl9zD5.js} +15 -15
- package/dist/{cascader-B1MiqkBq.js.map → cascader-CrAl9zD5.js.map} +1 -1
- package/dist/{chart-Darg_0Kv.js → chart-I7ROnsl8.js} +5 -5
- package/dist/{chart-Darg_0Kv.js.map → chart-I7ROnsl8.js.map} +1 -1
- package/dist/{checkbox-fflqmODR.js → checkbox-MoyHIAPs.js} +3 -3
- package/dist/{checkbox-fflqmODR.js.map → checkbox-MoyHIAPs.js.map} +1 -1
- package/dist/{choice-inputs-zUgBZfE4.js → choice-inputs-DHGtx0QV.js} +122 -110
- package/dist/choice-inputs-DHGtx0QV.js.map +1 -0
- package/dist/{code-block-BzmM9Eig.js → code-block-BSJyq9e-.js} +7 -7
- package/dist/{code-block-BzmM9Eig.js.map → code-block-BSJyq9e-.js.map} +1 -1
- package/dist/{collapsible-BkAI_XD4.js → collapsible-BzJ91LUG.js} +3 -3
- package/dist/{collapsible-BkAI_XD4.js.map → collapsible-BzJ91LUG.js.map} +1 -1
- package/dist/{collection-content-gDh1v6O4.js → collection-content-CSnwtOTH.js} +3 -3
- package/dist/{collection-content-gDh1v6O4.js.map → collection-content-CSnwtOTH.js.map} +1 -1
- package/dist/{combobox-BEx0vJgF.js → combobox-Bcvu5J8O.js} +11 -10
- package/dist/{combobox-BEx0vJgF.js.map → combobox-Bcvu5J8O.js.map} +1 -1
- package/dist/combobox-C2JiSQPc.js +915 -0
- package/dist/combobox-C2JiSQPc.js.map +1 -0
- package/dist/{command-CrlPIL6m.js → command-9e52NPgK.js} +6 -6
- package/dist/{command-CrlPIL6m.js.map → command-9e52NPgK.js.map} +1 -1
- package/dist/{comments-Dmpp0YIc.js → comments-BsgErEqd.js} +33 -28
- package/dist/comments-BsgErEqd.js.map +1 -0
- package/dist/components/base/choice-inputs/choice-types.d.ts +4 -0
- package/dist/components/base/choice-inputs/pill-radio-group.d.ts +7 -6
- package/dist/components/base/combobox/combobox-field.d.ts +2 -2
- package/dist/components/base/date-pickers/date-picker-internals.d.ts +3 -1
- package/dist/components/base/date-pickers/datepicker.types.d.ts +10 -4
- package/dist/components/base/date-pickers/time-picker.d.ts +17 -3
- package/dist/components/base/feedback/state-display.d.ts +5 -1
- package/dist/components/base/feedback/state-display.strings.d.ts +9 -0
- package/dist/components/base/forms/form-field.d.ts +7 -2
- package/dist/components/base/forms-numeric/coordinates-input.d.ts +4 -1
- package/dist/components/base/forms-numeric/decimal-input.d.ts +2 -2
- package/dist/components/base/forms-numeric/dimensions-input.d.ts +4 -1
- package/dist/components/base/forms-numeric/numeric-composite-field.d.ts +12 -0
- package/dist/components/base/forms-numeric/weight-input.d.ts +6 -1
- package/dist/components/base/navigation/language-switcher.d.ts +12 -4
- package/dist/components/base/repeaters/key-value-editor.d.ts +2 -1
- package/dist/components/base/sidebar/sidebar.d.ts +2 -0
- package/dist/components/base/upload/avatar-upload.d.ts +2 -1
- package/dist/components/base/upload/image-upload.d.ts +2 -1
- package/dist/components/blocks/ai/ai-citation/types.d.ts +2 -0
- package/dist/components/blocks/ai/ai-reasoning/types.d.ts +2 -0
- package/dist/components/blocks/ai/ai-sources/types.d.ts +2 -0
- package/dist/components/blocks/ai/ai-tool-call/types.d.ts +2 -0
- package/dist/components/blocks/analytics/metric/metric.d.ts +4 -0
- package/dist/components/blocks/analytics/metric-comparison/metric-comparison.d.ts +2 -0
- package/dist/components/blocks/analytics/metric-grid/metric-grid.d.ts +4 -5
- package/dist/components/blocks/analytics/types.d.ts +2 -2
- package/dist/components/blocks/commerce/order-status/types.d.ts +4 -0
- package/dist/components/blocks/data-display/empty-state/empty-state.types.d.ts +4 -3
- package/dist/components/features/activities/activities.types.d.ts +18 -11
- package/dist/components/features/activities/hooks/use-activity-resources.d.ts +2 -1
- package/dist/components/features/ai-chat/hooks/use-ai-chat-scroll.d.ts +1 -1
- package/dist/components/features/appointment-calendar/appointment-calendar.d.ts +1 -1
- package/dist/components/features/appointment-calendar/appointment-calendar.types.d.ts +4 -2
- package/dist/components/features/combobox/async-resource-combobox.d.ts +2 -2
- package/dist/components/features/combobox/combobox.d.ts +2 -2
- package/dist/components/features/combobox/hooks/use-combobox-core.d.ts +2 -1
- package/dist/components/features/combobox/hooks/use-combobox-state.d.ts +1 -0
- package/dist/components/features/combobox/hooks/use-load-more.d.ts +1 -0
- package/dist/components/features/combobox/types.d.ts +37 -8
- package/dist/components/features/email-composer/types.d.ts +3 -1
- package/dist/components/features/filters/facets/date-facet.d.ts +4 -2
- package/dist/components/features/filters/facets/range-facet.d.ts +12 -0
- package/dist/components/features/filters/filters.types.d.ts +3 -3
- package/dist/components/features/filters/partials/date-filter-content.d.ts +3 -2
- package/dist/components/features/filters/partials/filter-popover-content.d.ts +4 -2
- package/dist/components/features/filters/partials/range-filter-content.d.ts +12 -0
- package/dist/components/features/filters/utils/filter-utils.d.ts +5 -1
- package/dist/components/features/media-library/hooks/use-media-library.d.ts +1 -1
- package/dist/components/features/media-library/media-library.d.ts +1 -1
- package/dist/components/features/media-library/media-library.strings.d.ts +6 -0
- package/dist/components/features/media-library/media-library.types.d.ts +36 -8
- package/dist/components/features/media-library/partials/media-library-partials.d.ts +3 -3
- package/dist/components/features/mentions/hooks/use-mentions.d.ts +16 -1
- package/dist/components/features/mentions/partials/mention-inline-suggestions.d.ts +5 -1
- package/dist/components/features/mentions/utils/split-html-by-mentions.d.ts +1 -7
- package/dist/components/features/overlays/hooks/use-overlay-visibility.d.ts +2 -2
- package/dist/components/features/products/products.types.d.ts +2 -2
- package/dist/components/features/questionnaire/questionnaire.types.d.ts +3 -0
- package/dist/components/features/resource/resource.d.ts +1 -1
- package/dist/components/features/resource/resource.types.d.ts +2 -0
- package/dist/components/features/rich-text-editor/form-rich-text-editor.d.ts +1 -1
- package/dist/components/features/rich-text-editor/partials/caret-helpers.d.ts +1 -1
- package/dist/components/features/rich-text-editor/partials/editor-field-aria.d.ts +14 -0
- package/dist/components/features/rich-text-editor/partials/rich-text-editor-toolbar.d.ts +3 -1
- package/dist/components/features/rich-text-editor/rich-text-editor.recipes.d.ts +1 -1
- package/dist/components/features/rich-text-editor/rich-text-editor.types.d.ts +17 -3
- package/dist/components/features/schema-form/hooks/use-schema-form.d.ts +5 -1
- package/dist/components/features/schema-form/partials/schema-form-field.d.ts +3 -1
- package/dist/components/features/schema-form/schema-form.utils.d.ts +1 -1
- package/dist/components/features/suggestions/suggestions.types.d.ts +1 -1
- package/dist/components/features/table/data-table.d.ts +1 -1
- package/dist/components/features/table/index.d.ts +1 -0
- package/dist/components/features/table/table-body.d.ts +1 -1
- package/dist/components/features/table/table-helpers.d.ts +6 -3
- package/dist/components/features/table/table.strings.d.ts +3 -0
- package/dist/components/features/table/table.types.d.ts +17 -1
- package/dist/components/layout/header/header.types.d.ts +3 -2
- package/dist/components/layout/layout.types.d.ts +5 -3
- package/dist/components/layout/navigation/side-nav.d.ts +3 -2
- package/dist/components/layout/normalize-layout-path.d.ts +1 -0
- package/dist/components/layout/settings/settings.d.ts +1 -1
- package/dist/components/layout/sidebar/app-sidebar.d.ts +1 -1
- package/dist/components/layout/sidebar/sidebar-navigation-rail.d.ts +3 -1
- package/dist/components/layout/sidebar/sidebar.context.d.ts +1 -0
- package/dist/components/layout/sidebar/sidebar.types.d.ts +2 -0
- package/dist/components/primitives/link.d.ts +3 -2
- package/dist/components/primitives/money.d.ts +1 -1
- package/dist/components/primitives/number.d.ts +1 -1
- package/dist/components/typography/rich-text/rich-text-tree.d.ts +7 -5
- package/dist/{containers-CIxwbZt5.js → containers-CSdc32h9.js} +2 -2
- package/dist/{containers-CIxwbZt5.js.map → containers-CSdc32h9.js.map} +1 -1
- package/dist/{copyable-DIznBqiT.js → copyable-3fdHfMR3.js} +4 -4
- package/dist/{copyable-DIznBqiT.js.map → copyable-3fdHfMR3.js.map} +1 -1
- package/dist/{data-display-BdgCAw8T.js → data-display-Cmj-xZYl.js} +16 -15
- package/dist/data-display-Cmj-xZYl.js.map +1 -0
- package/dist/{date-Cq8ioE4o.js → date-Br0FicVv.js} +4 -4
- package/dist/{date-Cq8ioE4o.js.map → date-Br0FicVv.js.map} +1 -1
- package/dist/date-picker-CyQlFYsP.js +1617 -0
- package/dist/date-picker-CyQlFYsP.js.map +1 -0
- package/dist/date-pickers-tj2MXmXd.js +185 -0
- package/dist/date-pickers-tj2MXmXd.js.map +1 -0
- package/dist/{display-KsZLw9Ov.js → display-CT_N2a3w.js} +7 -7
- package/dist/{display-KsZLw9Ov.js.map → display-CT_N2a3w.js.map} +1 -1
- package/dist/{dropdown-menu-CLs8Lihj.js → dropdown-menu-C08iIugv.js} +6 -6
- package/dist/{dropdown-menu-CLs8Lihj.js.map → dropdown-menu-C08iIugv.js.map} +1 -1
- package/dist/features/activities.js +283 -270
- package/dist/features/activities.js.map +1 -1
- package/dist/features/ai-chat.js +40 -27
- package/dist/features/ai-chat.js.map +1 -1
- package/dist/features/appointment-calendar.js +126 -126
- package/dist/features/appointment-calendar.js.map +1 -1
- package/dist/features/async-preview.js +9 -9
- 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 +24 -19
- package/dist/features/email-composer.js.map +1 -1
- package/dist/features/event-calendar.js +129 -125
- package/dist/features/event-calendar.js.map +1 -1
- package/dist/features/filters.js +2 -2
- package/dist/features/global-search.js +16 -20
- package/dist/features/global-search.js.map +1 -1
- package/dist/features/kanban.js +18 -28
- package/dist/features/kanban.js.map +1 -1
- package/dist/features/map/style.css +15 -0
- package/dist/features/map.js +331 -314
- package/dist/features/map.js.map +1 -1
- package/dist/features/media-library.js +1044 -866
- package/dist/features/media-library.js.map +1 -1
- package/dist/features/mentions.js +2 -2
- package/dist/features/overlays.js +1 -1
- package/dist/features/products.js +978 -964
- package/dist/features/products.js.map +1 -1
- package/dist/features/questionnaire.js +159 -152
- package/dist/features/questionnaire.js.map +1 -1
- package/dist/features/resource-assignment.js +8 -8
- package/dist/features/resource.js +74 -71
- package/dist/features/resource.js.map +1 -1
- package/dist/features/rich-text-editor.js +28 -19
- package/dist/features/rich-text-editor.js.map +1 -1
- package/dist/features/schema-form.js +297 -256
- package/dist/features/schema-form.js.map +1 -1
- package/dist/features/suggestions.js +57 -50
- package/dist/features/suggestions.js.map +1 -1
- package/dist/features/sync.js +3 -3
- package/dist/features/table.js +1 -1
- package/dist/features/theme-tweaker.js +400 -399
- package/dist/features/theme-tweaker.js.map +1 -1
- package/dist/feedback-Dzo1xtAU.js +246 -0
- package/dist/feedback-Dzo1xtAU.js.map +1 -0
- package/dist/{field-affordance-recipes-DWcNcOjh.js → field-affordance-recipes-D6H7tVJi.js} +2 -2
- package/dist/{field-affordance-recipes-DWcNcOjh.js.map → field-affordance-recipes-D6H7tVJi.js.map} +1 -1
- package/dist/field-control-attributes-B6i335wr.js +18 -0
- package/dist/field-control-attributes-B6i335wr.js.map +1 -0
- package/dist/{field-state-recipes-BVA2CWHo.js → field-state-recipes-7sza8QbC.js} +4 -2
- package/dist/{field-state-recipes-BVA2CWHo.js.map → field-state-recipes-7sza8QbC.js.map} +1 -1
- package/dist/{filters-DzgSJpMK.js → filters-Dl80yYtN.js} +763 -605
- package/dist/filters-Dl80yYtN.js.map +1 -0
- package/dist/form-field-CAPdwdnZ.js +74 -0
- package/dist/form-field-CAPdwdnZ.js.map +1 -0
- package/dist/{form-sizing-Bs2qFUeE.js → form-sizing-CgcrUbLU.js} +2 -2
- package/dist/{form-sizing-Bs2qFUeE.js.map → form-sizing-CgcrUbLU.js.map} +1 -1
- package/dist/{forms-C0RGnJTe.js → forms-B3W8CH7-.js} +8 -8
- package/dist/{forms-C0RGnJTe.js.map → forms-B3W8CH7-.js.map} +1 -1
- package/dist/forms-numeric-CwjQJe-O.js +991 -0
- package/dist/forms-numeric-CwjQJe-O.js.map +1 -0
- package/dist/{header-DMMxG__r.js → header-ZIIsRKxP.js} +113 -113
- package/dist/header-ZIIsRKxP.js.map +1 -0
- package/dist/hooks/use-files.d.ts +2 -0
- package/dist/{hooks-DB6CJ4MR.js → hooks-BitHSh-Y.js} +3 -3
- package/dist/{hooks-DB6CJ4MR.js.map → hooks-BitHSh-Y.js.map} +1 -1
- package/dist/{icon-badge-DZF-YjQp.js → icon-badge-npxF1SLX.js} +2 -2
- package/dist/{icon-badge-DZF-YjQp.js.map → icon-badge-npxF1SLX.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/{input-CvyDQlY7.js → input-7KAc7XQL.js} +6 -6
- package/dist/{input-CvyDQlY7.js.map → input-7KAc7XQL.js.map} +1 -1
- package/dist/{input-DMBwAZmr.js → input-DIREVYHs.js} +3 -3
- package/dist/{input-DMBwAZmr.js.map → input-DIREVYHs.js.map} +1 -1
- package/dist/{interaction-recipes-XR9ZpVYL.js → interaction-recipes-4wZ2tvfb.js} +2 -2
- package/dist/{interaction-recipes-XR9ZpVYL.js.map → interaction-recipes-4wZ2tvfb.js.map} +1 -1
- package/dist/{item-DjCcM18E.js → item-FfWnaYST.js} +99 -98
- package/dist/item-FfWnaYST.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 +11 -11
- package/dist/layout.types-E_GAApzO.js +30 -0
- package/dist/layout.types-E_GAApzO.js.map +1 -0
- package/dist/lib/field-control-attributes.d.ts +11 -0
- package/dist/lib/field-state-recipes.d.ts +2 -0
- package/dist/lib/format.d.ts +1 -0
- package/dist/lib/surface-recipes.d.ts +1 -1
- package/dist/lib/token-merge.generated.d.ts +6 -6
- package/dist/lib/utils.js +1 -1
- package/dist/{media-gallery-C-vChPmE.js → media-gallery-C81DAnKI.js} +6 -6
- package/dist/{media-gallery-C-vChPmE.js.map → media-gallery-C81DAnKI.js.map} +1 -1
- package/dist/{mentions-0_4nWuI-.js → mentions-qOnedSt4.js} +236 -167
- package/dist/mentions-qOnedSt4.js.map +1 -0
- package/dist/message-BJHnn43M.js +135 -0
- package/dist/message-BJHnn43M.js.map +1 -0
- package/dist/{metadata-list-C3MkU-Om.js → metadata-list-XkqZ_UXH.js} +11 -11
- package/dist/{metadata-list-C3MkU-Om.js.map → metadata-list-XkqZ_UXH.js.map} +1 -1
- package/dist/{navigation-DaRjthVk.js → navigation-BgtPJAhi.js} +119 -119
- package/dist/navigation-BgtPJAhi.js.map +1 -0
- package/dist/{navigation-C90-oQFq.js → navigation-Bt-v7rWP.js} +52 -46
- package/dist/navigation-Bt-v7rWP.js.map +1 -0
- package/dist/normalize-layout-path-BmEgg6fu.js +14 -0
- package/dist/normalize-layout-path-BmEgg6fu.js.map +1 -0
- package/dist/{number-CRU3kGuk.js → number-CYqBcrSj.js} +78 -86
- package/dist/number-CYqBcrSj.js.map +1 -0
- package/dist/{overflow-tab-bar-IVFVQFV6.js → overflow-tab-bar-BQCA2Zn2.js} +7 -7
- package/dist/{overflow-tab-bar-IVFVQFV6.js.map → overflow-tab-bar-BQCA2Zn2.js.map} +1 -1
- package/dist/{overlays-BSgCtM2v.js → overlays-CC8zI3Du.js} +264 -260
- package/dist/overlays-CC8zI3Du.js.map +1 -0
- package/dist/{page-DUUKDAjo.js → page-BoQ1VaFJ.js} +13 -13
- package/dist/{page-DUUKDAjo.js.map → page-BoQ1VaFJ.js.map} +1 -1
- package/dist/{page-header-commands-7P3sfE9u.js → page-header-commands-DIKspJfx.js} +4 -4
- package/dist/{page-header-commands-7P3sfE9u.js.map → page-header-commands-DIKspJfx.js.map} +1 -1
- package/dist/{pagination-CgCjElfu.js → pagination-Br8TdV9A.js} +4 -4
- package/dist/{pagination-CgCjElfu.js.map → pagination-Br8TdV9A.js.map} +1 -1
- package/dist/{popover-B85RenIl.js → popover-k8TdUcAt.js} +10 -10
- package/dist/popover-k8TdUcAt.js.map +1 -0
- package/dist/{popover-menu-DlnQ2zlR.js → popover-menu-yn-o0kyA.js} +7 -7
- package/dist/{popover-menu-DlnQ2zlR.js.map → popover-menu-yn-o0kyA.js.map} +1 -1
- package/dist/{popover.recipes-DdiEp0rd.js → popover.recipes-DJ0AkUan.js} +2 -2
- package/dist/{popover.recipes-DdiEp0rd.js.map → popover.recipes-DJ0AkUan.js.map} +1 -1
- package/dist/{portal-recipes-BpsV_iRw.js → portal-recipes-DyUXqUtD.js} +2 -2
- package/dist/{portal-recipes-BpsV_iRw.js.map → portal-recipes-DyUXqUtD.js.map} +1 -1
- package/dist/{primitives-ByWgGUgt.js → primitives-BkkQjYl7.js} +22 -17
- package/dist/primitives-BkkQjYl7.js.map +1 -0
- package/dist/primitives.js +4 -4
- package/dist/{progress-BJhQP9WX.js → progress-DZFXXzPs.js} +4 -4
- package/dist/{progress-BJhQP9WX.js.map → progress-DZFXXzPs.js.map} +1 -1
- package/dist/{receipt-tOc7B3hw.js → receipt-C7LhOg7Q.js} +6 -6
- package/dist/{receipt-tOc7B3hw.js.map → receipt-C7LhOg7Q.js.map} +1 -1
- package/dist/rich-text-BnTzZjIo.js +45 -0
- package/dist/rich-text-BnTzZjIo.js.map +1 -0
- package/dist/rich-text-editor-CAurDwT5.js +1273 -0
- package/dist/rich-text-editor-CAurDwT5.js.map +1 -0
- package/dist/{rich-text-editor-toolbar-laT3tpBT.js → rich-text-editor-toolbar-DbXig7Jk.js} +36 -35
- package/dist/rich-text-editor-toolbar-DbXig7Jk.js.map +1 -0
- package/dist/rich-text-tree-BqyQ_7-R.js +66 -0
- package/dist/rich-text-tree-BqyQ_7-R.js.map +1 -0
- package/dist/{sanitize-html-DsQmI48u.js → sanitize-html-BWkH4dLM.js} +29 -16
- package/dist/sanitize-html-BWkH4dLM.js.map +1 -0
- package/dist/{scroll-area-B0dB2cBi.js → scroll-area-Bj6CRUif.js} +2 -2
- package/dist/{scroll-area-B0dB2cBi.js.map → scroll-area-Bj6CRUif.js.map} +1 -1
- package/dist/{separator-Bb17I4pu.js → separator-B9ewiCGO.js} +2 -2
- package/dist/{separator-Bb17I4pu.js.map → separator-B9ewiCGO.js.map} +1 -1
- package/dist/{separator-Ct_WSWTJ.js → separator-DM6gs2vu.js} +3 -3
- package/dist/{separator-Ct_WSWTJ.js.map → separator-DM6gs2vu.js.map} +1 -1
- package/dist/services/actions.js +1 -1
- package/dist/settings-DZPHgpx4.js +155 -0
- package/dist/settings-DZPHgpx4.js.map +1 -0
- package/dist/{sheet-BGPERY55.js → sheet-C4DHsgLk.js} +6 -6
- package/dist/{sheet-BGPERY55.js.map → sheet-C4DHsgLk.js.map} +1 -1
- package/dist/sidebar-C8BnDwTH.js +709 -0
- package/dist/sidebar-C8BnDwTH.js.map +1 -0
- package/dist/{sidebar-f9gRWXIR.js → sidebar-CmD6a-HK.js} +16 -13
- package/dist/sidebar-CmD6a-HK.js.map +1 -0
- package/dist/{skeleton-BapcJd9C.js → skeleton-DDgdhmu9.js} +5 -5
- package/dist/{skeleton-BapcJd9C.js.map → skeleton-DDgdhmu9.js.map} +1 -1
- package/dist/{smart-card-CW0hjetE.js → smart-card-B6vLxqa_.js} +8 -8
- package/dist/{smart-card-CW0hjetE.js.map → smart-card-B6vLxqa_.js.map} +1 -1
- package/dist/{structure-DuxKl954.js → structure-DZJ2wbAU.js} +2 -2
- package/dist/{structure-DuxKl954.js.map → structure-DZJ2wbAU.js.map} +1 -1
- package/dist/style.css +1 -1
- package/dist/{surface-recipes-BKRVW8Os.js → surface-recipes-C8mNycaY.js} +2 -2
- package/dist/{surface-recipes-BKRVW8Os.js.map → surface-recipes-C8mNycaY.js.map} +1 -1
- package/dist/{table-CFOWY9pQ.js → table-CBonSdTp.js} +534 -486
- package/dist/table-CBonSdTp.js.map +1 -0
- package/dist/{table-B4TE7iOV.js → table-CXSPHH25.js} +75 -71
- package/dist/table-CXSPHH25.js.map +1 -0
- package/dist/{tabs-e7ko9S2o.js → tabs-C4Kr_hCr.js} +50 -49
- package/dist/tabs-C4Kr_hCr.js.map +1 -0
- package/dist/{text-inputs-B1culiSK.js → text-inputs-jdkS3ReG.js} +9 -9
- package/dist/{text-inputs-B1culiSK.js.map → text-inputs-jdkS3ReG.js.map} +1 -1
- package/dist/{timelines-D3Sl9ueq.js → timelines-CoPwdJP0.js} +10 -10
- package/dist/{timelines-D3Sl9ueq.js.map → timelines-CoPwdJP0.js.map} +1 -1
- package/dist/{tooltip-8wz5S_Zr.js → tooltip-31NwIpTo.js} +4 -4
- package/dist/{tooltip-8wz5S_Zr.js.map → tooltip-31NwIpTo.js.map} +1 -1
- package/dist/{tree-CzUJHnFs.js → tree-CeYVzdFE.js} +6 -6
- package/dist/{tree-CzUJHnFs.js.map → tree-CeYVzdFE.js.map} +1 -1
- package/dist/typography/rich-text.js +1 -1
- package/dist/{typography-BQnine1k.js → typography-C7-YMW1S.js} +2 -2
- package/dist/{typography-BQnine1k.js.map → typography-C7-YMW1S.js.map} +1 -1
- package/dist/typography.js +1 -1
- package/dist/{upload-BOW6U2tI.js → upload-Hdm5gObf.js} +303 -291
- package/dist/upload-Hdm5gObf.js.map +1 -0
- package/dist/{upload-queue-qUXn1ykN.js → upload-queue-D0sYPuqo.js} +8 -8
- package/dist/{upload-queue-qUXn1ykN.js.map → upload-queue-D0sYPuqo.js.map} +1 -1
- package/dist/{usage-card-BeSLY0i0.js → usage-card-9s8hLDfn.js} +9 -9
- package/dist/{usage-card-BeSLY0i0.js.map → usage-card-9s8hLDfn.js.map} +1 -1
- package/dist/{utils-B6kKb8jB.js → utils-DhuoqFy4.js} +31 -37
- package/dist/utils-DhuoqFy4.js.map +1 -0
- package/dist/{value-inputs-skdVB2zh.js → value-inputs-D3aPgq9H.js} +15 -15
- package/dist/{value-inputs-skdVB2zh.js.map → value-inputs-D3aPgq9H.js.map} +1 -1
- package/dist/{value-typography-uAmWUAL1.js → value-typography-uSzzG9Gf.js} +32 -26
- package/dist/value-typography-uSzzG9Gf.js.map +1 -0
- package/dist/{workspace-DHeHv0qw.js → workspace-CC8Vchiu.js} +12 -12
- package/dist/{workspace-DHeHv0qw.js.map → workspace-CC8Vchiu.js.map} +1 -1
- package/docs/consumer-skill/examples/edit.tsx +3 -3
- package/docs/consumer-skill/references/agents-md.md +19 -19
- package/docs/consumer-skill/references/analytics.md +15 -0
- package/docs/consumer-skill/references/component-contracts.json +14724 -13057
- package/docs/consumer-skill/references/composition.md +21 -4
- package/docs/consumer-skill/references/design-contract.md +1 -0
- package/docs/consumer-skill/references/installation.md +51 -8
- package/docs/consumer-skill/references/integration.md +24 -1
- package/docs/consumer-skill/references/public-api.json +172 -34
- package/docs/consumer-skill/references/review.md +51 -11
- package/docs/consumer-skill/references/rhf-workflows.md +72 -0
- package/docs/consumer-skill/references/theming.md +7 -2
- package/docs/consumer-skill/references/tokens.json +1 -1
- package/docs/consumer-skill/scripts/composition-policy.mjs +144 -30
- package/docs/consumer-skill/scripts/setup-skills.mjs +46 -9
- package/package.json +4 -2
- package/templates/admin-shell-template.tsx +1 -2
- package/templates/customers-template.tsx +18 -16
- package/dist/accordion-B9MHG0cT.js.map +0 -1
- package/dist/ai-artifact-C58tQcBy.js.map +0 -1
- package/dist/auth-DTkkshS8.js.map +0 -1
- package/dist/breadcrumbs-J-K2kl4c.js.map +0 -1
- package/dist/choice-inputs-zUgBZfE4.js.map +0 -1
- package/dist/combobox-BwzoXFSG.js +0 -884
- package/dist/combobox-BwzoXFSG.js.map +0 -1
- package/dist/comments-Dmpp0YIc.js.map +0 -1
- package/dist/data-display-BdgCAw8T.js.map +0 -1
- package/dist/date-picker-RlIzKom_.js +0 -1551
- package/dist/date-picker-RlIzKom_.js.map +0 -1
- package/dist/date-pickers-DY-CpXS1.js +0 -179
- package/dist/date-pickers-DY-CpXS1.js.map +0 -1
- package/dist/feedback-Dm4izqFn.js +0 -240
- package/dist/feedback-Dm4izqFn.js.map +0 -1
- package/dist/filters-DzgSJpMK.js.map +0 -1
- package/dist/form-field-BriWAXyg.js +0 -63
- package/dist/form-field-BriWAXyg.js.map +0 -1
- package/dist/forms-numeric-DIhghX1Q.js +0 -910
- package/dist/forms-numeric-DIhghX1Q.js.map +0 -1
- package/dist/header-DMMxG__r.js.map +0 -1
- package/dist/item-DjCcM18E.js.map +0 -1
- package/dist/layout.types-Cn7aUzJp.js +0 -20
- package/dist/layout.types-Cn7aUzJp.js.map +0 -1
- package/dist/mentions-0_4nWuI-.js.map +0 -1
- package/dist/message-g8p7eVtN.js +0 -128
- package/dist/message-g8p7eVtN.js.map +0 -1
- package/dist/navigation-C90-oQFq.js.map +0 -1
- package/dist/navigation-DaRjthVk.js.map +0 -1
- package/dist/number-CRU3kGuk.js.map +0 -1
- package/dist/overlays-BSgCtM2v.js.map +0 -1
- package/dist/popover-B85RenIl.js.map +0 -1
- package/dist/primitives-ByWgGUgt.js.map +0 -1
- package/dist/rich-text-Dth5E882.js +0 -78
- package/dist/rich-text-Dth5E882.js.map +0 -1
- package/dist/rich-text-editor-C1fOjBTd.js +0 -1195
- package/dist/rich-text-editor-C1fOjBTd.js.map +0 -1
- package/dist/rich-text-editor-toolbar-laT3tpBT.js.map +0 -1
- package/dist/safe-image-src-C6kQSMBZ.js +0 -17
- package/dist/safe-image-src-C6kQSMBZ.js.map +0 -1
- package/dist/sanitize-html-DsQmI48u.js.map +0 -1
- package/dist/settings-Bar5wiSo.js +0 -167
- package/dist/settings-Bar5wiSo.js.map +0 -1
- package/dist/sidebar-D-JYMMbY.js +0 -686
- package/dist/sidebar-D-JYMMbY.js.map +0 -1
- package/dist/sidebar-f9gRWXIR.js.map +0 -1
- package/dist/table-B4TE7iOV.js.map +0 -1
- package/dist/table-CFOWY9pQ.js.map +0 -1
- package/dist/tabs-e7ko9S2o.js.map +0 -1
- package/dist/upload-BOW6U2tI.js.map +0 -1
- package/dist/utils-B6kKb8jB.js.map +0 -1
- package/dist/value-typography-uAmWUAL1.js.map +0 -1
|
@@ -42,7 +42,10 @@ just name components or repeat an archetype.
|
|
|
42
42
|
|
|
43
43
|
These criteria allow different product layouts. Do not impose a universal card
|
|
44
44
|
count, row-line limit or one-column form rule. Emphasis is the exception: keep
|
|
45
|
-
one
|
|
45
|
+
one leading primary command per task/surface, so the next action stays obvious.
|
|
46
|
+
Independent forms can each own a primary submit. Fix a warning by reviewing its
|
|
47
|
+
scope and owner; a DOM-lookup submit handler or flattening every FormSection
|
|
48
|
+
does not establish a better composition.
|
|
46
49
|
|
|
47
50
|
## Done means
|
|
48
51
|
|
|
@@ -104,11 +107,20 @@ disabled 50% treatment remain valid; component disabled props keep that dimming
|
|
|
104
107
|
with its owner.
|
|
105
108
|
|
|
106
109
|
Standalone compact Cards and Page gutter removal also produce review warnings.
|
|
107
|
-
`uncontained-table-in-card`
|
|
108
|
-
|
|
110
|
+
`uncontained-table-in-card` follows tables through passive wrappers and known
|
|
111
|
+
slots in padded Card/FormSection/explicit ChartCard surfaces. It also warns on
|
|
112
|
+
tables in card/chart footers: place substantial tables in a body or peer layout
|
|
113
|
+
region, or deliberately use Card padding="none" and a flat table.
|
|
114
|
+
`form-actions-bar-placement` reviews a FormActionsBar under a padded surface or
|
|
115
|
+
in a footer that already owns geometry; put action nodes directly in the footer
|
|
116
|
+
or use an edge-to-edge bar in a zero-padding host. Both remain warnings because
|
|
117
|
+
custom hosts need contextual review.
|
|
109
118
|
`detached-filter-layout` recommends DataTable topbarContent for a sibling filter
|
|
110
119
|
strip, or DataView for the complete workflow. `wide-single-column-form` suggests
|
|
111
|
-
Page narrow for a simple form lacking explicit width or aside.
|
|
120
|
+
Page narrow for a simple form lacking explicit width or aside. On mixed/tabbed
|
|
121
|
+
routes, constrain only a form panel with Container maxWidth="md" gutter="none";
|
|
122
|
+
`dynamic-page-measure` warns about expressions that can resize the route header
|
|
123
|
+
along with the body. Deliberate responsive sizing remains valid. These contextual
|
|
112
124
|
warnings follow qualified import bindings; unrelated same-name components are
|
|
113
125
|
not evidence. Deprecated resource shells use the existing deprecated-owner
|
|
114
126
|
warning and direct new routes to Page.
|
|
@@ -120,9 +132,24 @@ For PageHeader's `slots.actions`, wrapping or returning the callback's
|
|
|
120
132
|
keyboard, overflow and action semantics. Inspect the compact menu below 40rem of
|
|
121
133
|
header width, including in a narrow parent on desktop: every command, including
|
|
122
134
|
primary, belongs in one More actions menu.
|
|
123
|
-
A Page with more than one solid primary command
|
|
124
|
-
|
|
125
|
-
|
|
135
|
+
A Page with more than one solid primary command in the same task scope warns,
|
|
136
|
+
counting `header.primaryAction`, solid Button tone="primary", and
|
|
137
|
+
SubmitStateButton's default primary. Native forms (including a public owner's
|
|
138
|
+
native form `render`) and overlays own separate command scopes; exclusive
|
|
139
|
+
conditional branches use their maximum count. Local submits stay with their
|
|
140
|
+
form. `remote-header-form-submit` reviews header handlers that call
|
|
141
|
+
document.getElementById and requestSubmit, including same-file callback values;
|
|
142
|
+
a deliberate single full-page editor can keep its header submit with native
|
|
143
|
+
validation and appropriate form association.
|
|
144
|
+
|
|
145
|
+
`heavy-cell-disclosure` reviews Accordion, SmartAccordion, DataTable and CodeBlock
|
|
146
|
+
inside statically visible DataTable `columns.cell` renderers, separately from
|
|
147
|
+
column headers and portaled record details. Prefer a summary plus rowActions
|
|
148
|
+
opening an overlay or detail route; intentional accessible inline disclosure is
|
|
149
|
+
valid. `single-option-choice-group` suggests boolean controls for one static
|
|
150
|
+
choice, without banning two-option sets or filtered real choice domains.
|
|
151
|
+
|
|
152
|
+
Raw palette classes such as
|
|
126
153
|
`bg-blue-500`, `text-gray-600` or `border-white` warn on any element; use a
|
|
127
154
|
semantic surface/foreground pair or a status tone. A Card title icon and an icon on
|
|
128
155
|
more than one MetadataList fact warn as decoration; keep a glyph only when it
|
|
@@ -133,7 +160,9 @@ native button passed as a package owner's `render`, such as an Item row
|
|
|
133
160
|
rendered as a button, is that owner's root and does not warn.
|
|
134
161
|
Verified Card-owning Metric/card-grid, UsageCard and TaskCard compositions also
|
|
135
162
|
participate in nested-surface and peer-gap review. Metric without its card variant,
|
|
136
|
-
compact MetricGrid, flat TaskCard, ChartCard and Receipt do not establish
|
|
163
|
+
compact MetricGrid, flat TaskCard, default ChartCard and Receipt do not establish
|
|
164
|
+
a frame. FormSection has a Card by default; ChartCard establishes one only with
|
|
165
|
+
an explicit non-flat surface. Unknown surface values/spreads are not proof of a frame.
|
|
137
166
|
Direct framed/default Card peers, including GridCell-wrapped peers, in a statically
|
|
138
167
|
small Stack/Grid gap also warn; omitted gaps use the actual component defaults.
|
|
139
168
|
Review visible ring clearance and the application's ordinary surface policy.
|
|
@@ -148,9 +177,20 @@ Use existing app lint/typechecking for wider language, utility and accessibility
|
|
|
148
177
|
coverage. The script is deliberately small and does not replace them.
|
|
149
178
|
|
|
150
179
|
Coverage is limited to direct public imports, aliases/namespaces, native paragraph/
|
|
151
|
-
heading copy and same-file static classes/styles/props
|
|
152
|
-
|
|
153
|
-
|
|
180
|
+
heading copy and same-file static classes/styles/props/JSX slot values. Paired
|
|
181
|
+
and self-closing elements retain their slot owner; opaque app components and
|
|
182
|
+
known portals form separate regions. Inline cell renderers are inspected when
|
|
183
|
+
their JSX is available, without executing application code. Dynamic geometry,
|
|
184
|
+
classes and spread values are disclosed as unresolved where inspected;
|
|
185
|
+
cross-file wrappers, external CSS and runtime composition require review. Do not
|
|
186
|
+
treat an empty report as proof of complete compliance or taste. Test source
|
|
187
|
+
rules through analyzer findings, and UI behavior through executed outcomes;
|
|
188
|
+
regex assertions over app JSX cannot establish either visual quality or callbacks.
|
|
189
|
+
Local helpers invoked to produce JSX (for example `footerSlot={renderFooter()}`)
|
|
190
|
+
are not evaluated. Dynamically constructed whole header objects are disclosed
|
|
191
|
+
as unresolved; review their action scopes separately. Native ReactDOM
|
|
192
|
+
createPortal calls retain their lexical binding and start a separate rendered
|
|
193
|
+
region, like the supported public overlay owners.
|
|
154
194
|
|
|
155
195
|
## Rendered and interaction evidence
|
|
156
196
|
|
|
@@ -17,6 +17,27 @@ The complete [order edit page](../examples/rhf-edit-page.tsx) composes:
|
|
|
17
17
|
|
|
18
18
|
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.
|
|
19
19
|
|
|
20
|
+
Choose each section's frame from its actual parent. Independent FormSections on
|
|
21
|
+
the page can keep their default card surface; flattening a section whose content
|
|
22
|
+
already consists of Cards does not require flattening unrelated sibling forms.
|
|
23
|
+
|
|
24
|
+
`FormActionsBar` owns a top divider, opaque background and surface inset. Use
|
|
25
|
+
`leading` for status and `trailing` (or `children`) for action nodes. In
|
|
26
|
+
`Card padding="none"`, flat FormSections keep their body inset and the bar sits
|
|
27
|
+
edge-to-edge after them. In an ordinary Card's `footerSlot` or FormSection's
|
|
28
|
+
`footer`, place the buttons directly: the host already owns the footer geometry.
|
|
29
|
+
`SubmitStateButton` defaults to `type="submit"` and primary tone; pass
|
|
30
|
+
`submitting`, `dirty` and `disableWhenPristine` for pending/pristine behavior.
|
|
31
|
+
|
|
32
|
+
Independent forms own independent submit commands. Keep a lookup beside its
|
|
33
|
+
input and settings saves with their edited fields; page command guidance does
|
|
34
|
+
not require moving these into `Page.header`. For a single full-page editor, a
|
|
35
|
+
deliberate header submit can use native form association (`type="submit"` and
|
|
36
|
+
`form` on a Button in a supported slot). Preserve browser validation and one
|
|
37
|
+
submit lifecycle; routine `document.getElementById(...).requestSubmit()` handlers
|
|
38
|
+
add unnecessary coupling. Tests should execute validation, pending, error/draft
|
|
39
|
+
retention and accepted persistence rather than assert a particular source string.
|
|
40
|
+
|
|
20
41
|
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.
|
|
21
42
|
|
|
22
43
|
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.
|
|
@@ -40,12 +61,63 @@ Mount an editor with `key={record.id}` when switching records. `defaultValues` e
|
|
|
40
61
|
| SliderField (public slider name) | scalar `value: number` | `onValueChange(value)`; `inputRef`, `onBlur` |
|
|
41
62
|
| ColorInput | `value: string` | `onValueChange(value)`; input `ref`, `onBlur` |
|
|
42
63
|
|
|
64
|
+
Set DatePicker's `mode` explicitly when binding its callback. `mode="single"`
|
|
65
|
+
narrows the output to `DateOutput`; range and multiple modes have their own
|
|
66
|
+
value/output shapes. The runtime's single-mode default does not narrow the
|
|
67
|
+
unified callback's contextual TypeScript type.
|
|
68
|
+
|
|
69
|
+
Bind an empty controlled date field with `value={field.value ?? undefined}`.
|
|
70
|
+
An external reset to that empty value clears single, multiple, range and
|
|
71
|
+
month/year selections. Do not also pass `defaultValue` unless restoring that
|
|
72
|
+
fallback is intentional; RHF's `defaultValues` owns the saved form baseline.
|
|
73
|
+
|
|
43
74
|
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.
|
|
44
75
|
|
|
45
76
|
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.
|
|
46
77
|
|
|
78
|
+
Use `FormRichTextEditor` from `themelia-react-ui/features/rich-text-editor`
|
|
79
|
+
inside an RHF `FormProvider`. Pass its field name and wrap it in `FormField`
|
|
80
|
+
with the field's validation message. The binding handles touch, focus,
|
|
81
|
+
invalid and disabled state. Both editor modes use the same field association.
|
|
82
|
+
Native form data includes the committed HTML for enabled, named editors.
|
|
83
|
+
Switch from HTML source to rich text to commit a source draft.
|
|
84
|
+
|
|
85
|
+
WeightInput, DimensionsInput and CoordinatesInput can bind `field.onChange`
|
|
86
|
+
through `onValueChange` and `field.onBlur` through `onBlur`. Their legacy
|
|
87
|
+
event-shaped callbacks still receive the same value. Native `name` submits
|
|
88
|
+
one numeric string, using `widthxheightxdepth` for dimensions and
|
|
89
|
+
`latitude,longitude` for coordinates. The React callback retains the
|
|
90
|
+
selected string or object format.
|
|
91
|
+
|
|
92
|
+
TimePicker accepts the same direct `value` and `onValueChange` binding
|
|
93
|
+
and submits a named 24-hour value in either mode. Bind `field.onBlur`
|
|
94
|
+
to report leaving the complete picker, including its open popover.
|
|
95
|
+
|
|
96
|
+
### Empty selections
|
|
97
|
+
|
|
98
|
+
| Owner | Empty value |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| PillRadioGroup with `allowClear` | `null` |
|
|
101
|
+
| Combobox and AsyncResourceCombobox | `null` |
|
|
102
|
+
| MultiCombobox | An empty array |
|
|
103
|
+
| Select and RoundingModeSelect with `allowClear` | `undefined` |
|
|
104
|
+
| DateTimeInput | `undefined` |
|
|
105
|
+
| TimePicker | A controlled `null` shows an empty time. Editing emits aligned strings. |
|
|
106
|
+
|
|
107
|
+
Normalize nullable callbacks at the form boundary when the app uses one
|
|
108
|
+
empty value. An explicit `value={undefined}` keeps PillRadioGroup controlled.
|
|
109
|
+
Omit `value` to use its `defaultValue`. A shared clear sentinel would require
|
|
110
|
+
a major release.
|
|
111
|
+
|
|
47
112
|
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.
|
|
48
113
|
|
|
114
|
+
FormField honors an explicit `htmlFor` target already declared in descendant JSX,
|
|
115
|
+
including arrays/fragments, without copying its ID or name onto the outer wrapper.
|
|
116
|
+
It does not inspect custom component bodies. If a compound creates its nested
|
|
117
|
+
controls internally, put it inside Stack or a native container and assign the
|
|
118
|
+
target ID and supporting-text attributes to the actual control. Direct controls
|
|
119
|
+
without an ID retain automatic association.
|
|
120
|
+
|
|
49
121
|
## Transport lifecycle
|
|
50
122
|
|
|
51
123
|
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.
|
|
@@ -4,6 +4,10 @@ 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
|
+
Set the application's document font and page background/foreground through the
|
|
8
|
+
[host CSS setup](installation.md#set-the-application-canvas). Importing package
|
|
9
|
+
styles defines theme roles without painting the document or resetting app markup.
|
|
10
|
+
|
|
7
11
|
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
12
|
|
|
9
13
|
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.
|
|
@@ -66,9 +70,10 @@ control heights and copy gaps keep their independent roots.
|
|
|
66
70
|
| Actions versus fields | `--height-action` versus `--height-control`; default size uses the root times density-scale |
|
|
67
71
|
| Native field text inset | `--control-x` times space-scale; icons/add-ons use shared derived clearance |
|
|
68
72
|
| Collection inline inset | `--row-x` times space-scale |
|
|
69
|
-
|
|
|
73
|
+
| Item/table row block inset | `--row-y` times density-scale; compact/comfortable use 0.75/1.25 multipliers where supported |
|
|
74
|
+
| Anchored collection block inset | Default/comfortable use 0.75/1 × `--row-y` × density-scale; compact uses the spacing-scaled 2xs step |
|
|
70
75
|
| Surface tiers | `--surface-x` / `--surface-y`, times space-scale; xs/sm/md/lg use 1/2, 2/3, 1 and 4/3 |
|
|
71
|
-
| Menu header/footer inline inset |
|
|
76
|
+
| Menu header/footer inline inset | Compact bands use the md spacing step; plain header/footer copy adds the xs step. Leading controls and actions stay on the band edge. |
|
|
72
77
|
|
|
73
78
|
Rich display popovers use the surface roots (24px by default); compact menu/list shells use the md spacing role (8px), scaled once by --space-scale. Row inline padding and density stay independent.
|
|
74
79
|
|
|
@@ -44,6 +44,8 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
44
44
|
const families = new Map(index.families.map(owner => [owner.import, owner]));
|
|
45
45
|
const bindings = new Map();
|
|
46
46
|
const namespaces = new Map();
|
|
47
|
+
const portalBindings = new Set();
|
|
48
|
+
const portalNamespaces = new Set();
|
|
47
49
|
const declarations = new Map();
|
|
48
50
|
const findings = [];
|
|
49
51
|
const unresolved = [];
|
|
@@ -135,14 +137,23 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
135
137
|
if (names && ts.isNamespaceImport(names)) namespaces.set(symbolFor(names.name), specifier);
|
|
136
138
|
}
|
|
137
139
|
function gather(node) {
|
|
140
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier) && node.moduleSpecifier.text === 'react-dom') {
|
|
141
|
+
const names = node.importClause?.namedBindings;
|
|
142
|
+
if (names && ts.isNamedImports(names)) for (const item of names.elements) if ((item.propertyName ?? item.name).text === 'createPortal') portalBindings.add(symbolFor(item.name));
|
|
143
|
+
if (names && ts.isNamespaceImport(names)) portalNamespaces.add(symbolFor(names.name));
|
|
144
|
+
if (node.importClause?.name) portalNamespaces.add(symbolFor(node.importClause.name));
|
|
145
|
+
}
|
|
138
146
|
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier) && importsModal(node, ts)) add(node, 'raw-overlay-primitive', 'warning', `${node.moduleSpecifier.text} builds a second dialog system. ${overlayRemedy}`);
|
|
139
147
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) declarations.set(symbolFor(node.name), node.initializer);
|
|
148
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.body) declarations.set(symbolFor(node.name), node);
|
|
140
149
|
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) importOwner(node, node.moduleSpecifier.text);
|
|
141
150
|
if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteralLike(node.argument.literal)) importOwner(node, node.argument.literal.text);
|
|
142
151
|
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && ts.isStringLiteralLike(node.arguments[0])) importOwner(node, node.arguments[0].text);
|
|
143
152
|
ts.forEachChild(node, gather);
|
|
144
153
|
}
|
|
145
154
|
gather(source);
|
|
155
|
+
const portalCall = node => ts.isCallExpression(node) && (ts.isIdentifier(node.expression) && portalBindings.has(symbolFor(node.expression))
|
|
156
|
+
|| ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === 'createPortal' && ts.isIdentifier(node.expression.expression) && portalNamespaces.has(symbolFor(node.expression.expression)));
|
|
146
157
|
for (const diagnostic of source.parseDiagnostics) add(source, 'syntax', 'error', ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
|
|
147
158
|
const ownerFor = tag => {
|
|
148
159
|
if (ts.isIdentifier(tag)) return bindings.get(symbolFor(tag));
|
|
@@ -157,16 +168,23 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
157
168
|
const expression = ts.isJsxExpression(attribute.initializer) ? attribute.initializer.expression : attribute.initializer;
|
|
158
169
|
return [[attribute.name.getText(source), expression]];
|
|
159
170
|
});
|
|
171
|
+
const unknownSpread = (expression, visiting = new Set()) => {
|
|
172
|
+
const value = resolve(expression);
|
|
173
|
+
if (!value || !ts.isObjectLiteralExpression(value) || visiting.has(value)) return true;
|
|
174
|
+
const next = new Set(visiting).add(value);
|
|
175
|
+
return value.properties.some(property => ts.isSpreadAssignment(property) && unknownSpread(property.expression, next));
|
|
176
|
+
};
|
|
160
177
|
const isOwner = (owner, suffix, names) => owner?.import === `${index.package}/${suffix}` && names.includes(owner.symbol);
|
|
161
178
|
const framedOwner = (owner, props) => {
|
|
162
|
-
if (literal(props.get('surface')) === 'flat') return false;
|
|
163
|
-
if (isOwner(owner, 'base/cards', ['Card', 'SmartCard']) || isOwner(owner, 'features/table', ['DataTable']) || isOwner(owner, 'features/data-view', ['DataView'])) return true;
|
|
179
|
+
if (props.has('surface') && (literal(props.get('surface')) === undefined || literal(props.get('surface')) === 'flat')) return false;
|
|
180
|
+
if (isOwner(owner, 'base/cards', ['Card', 'SmartCard']) || isOwner(owner, 'base/forms', ['FormSection']) || isOwner(owner, 'features/table', ['DataTable']) || isOwner(owner, 'features/data-view', ['DataView'])) return true;
|
|
181
|
+
if (isOwner(owner, 'blocks/analytics', ['ChartCard']) || isOwner(owner, 'base/display', ['ContentBlock'])) return props.has('surface');
|
|
164
182
|
if (isOwner(owner, 'blocks/analytics', ['Metric'])) return literal(props.get('variant')) === 'card';
|
|
165
183
|
if (isOwner(owner, 'blocks/analytics', ['MetricGrid'])) return !props.has('variant') || literal(props.get('variant')) === 'card';
|
|
166
184
|
return isOwner(owner, 'blocks/analytics/usage-card', ['UsageCard']) || isOwner(owner, 'blocks/analytics', ['UsageCard']) || isOwner(owner, 'blocks/planning', ['TaskCard']);
|
|
167
185
|
};
|
|
168
186
|
// Examine lexical JSX only. Names ending in Card are not evidence of framing;
|
|
169
|
-
// ChartCard is card-free; Receipt's
|
|
187
|
+
// ChartCard is card-free by default; Receipt's paper has no ordinary Card frame.
|
|
170
188
|
const contentElements = element => {
|
|
171
189
|
const found = [];
|
|
172
190
|
function collect(child) {
|
|
@@ -260,25 +278,43 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
260
278
|
};
|
|
261
279
|
// tone="primary" is solid unless a buttonStyle says otherwise; untoned Buttons are neutral.
|
|
262
280
|
const solidPrimary = (opening, owner) => {
|
|
263
|
-
|
|
281
|
+
const submit = isOwner(owner, 'base/forms', ['SubmitStateButton']);
|
|
282
|
+
if (!submit && !isOwner(owner, 'base/buttons', ['Button'])) return false;
|
|
264
283
|
const props = new Map(attributes(opening));
|
|
265
284
|
const style = props.has('buttonStyle') ? literal(props.get('buttonStyle')) : 'solid';
|
|
266
|
-
return literal(props.get('tone')) === 'primary' && style === 'solid';
|
|
285
|
+
return (literal(props.get('tone')) ?? (submit && !props.has('tone') ? 'primary' : undefined)) === 'primary' && style === 'solid';
|
|
286
|
+
};
|
|
287
|
+
const formRoot = opening => {
|
|
288
|
+
if (opening.tagName.getText(source) === 'form') return true;
|
|
289
|
+
const render = resolve(new Map(attributes(opening)).get('render'));
|
|
290
|
+
return render && (ts.isJsxElement(render) ? render.openingElement : ts.isJsxSelfClosingElement(render) ? render : undefined)?.tagName.getText(source) === 'form';
|
|
267
291
|
};
|
|
268
|
-
//
|
|
292
|
+
// A form owns its submit command; overlays and exclusive branches are not
|
|
293
|
+
// simultaneously competing page commands. Follow same-file slot values only.
|
|
269
294
|
const solidPrimaryCount = root => {
|
|
270
|
-
|
|
271
|
-
|
|
295
|
+
const count = (expression, visiting = new Set()) => {
|
|
296
|
+
if (!expression) return 0;
|
|
297
|
+
const child = resolve(expression) ?? expression;
|
|
298
|
+
if (visiting.has(child)) return 0;
|
|
299
|
+
const next = new Set(visiting).add(child);
|
|
300
|
+
if (portalCall(child)) return 0;
|
|
301
|
+
if (ts.isConditionalExpression(child)) return Math.max(count(child.whenTrue, next), count(child.whenFalse, next));
|
|
302
|
+
if (ts.isBinaryExpression(child) && [ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(child.operatorToken.kind)) return Math.max(count(child.left, next), count(child.right, next));
|
|
272
303
|
if (ts.isJsxElement(child) || ts.isJsxSelfClosingElement(child)) {
|
|
273
304
|
const opening = ts.isJsxElement(child) ? child.openingElement : child;
|
|
274
305
|
const owner = ownerFor(opening.tagName);
|
|
275
|
-
if (isOwner(owner, 'features/overlays', overlayOwners)) return;
|
|
276
|
-
|
|
306
|
+
if (isOwner(owner, 'features/overlays', overlayOwners) || formRoot(opening) || !owner && /^[A-Z]/.test(opening.tagName.getText(source))) return 0;
|
|
307
|
+
const props = new Map(attributes(opening));
|
|
308
|
+
if (resolve(props.get('hidden'))?.kind === ts.SyntaxKind.TrueKeyword || props.has('hidden') && props.get('hidden') === undefined) return 0;
|
|
309
|
+
const children = ts.isJsxElement(child) ? child.children.reduce((total, item) => total + count(item, next), 0) : 0;
|
|
310
|
+
const slots = [...props].filter(([name]) => !['header', 'render'].includes(name)).reduce((total, [, value]) => total + count(value, next), 0);
|
|
311
|
+
return Number(solidPrimary(opening, owner)) + children + slots;
|
|
277
312
|
}
|
|
278
|
-
|
|
313
|
+
let total = 0;
|
|
314
|
+
ts.forEachChild(child, item => { total += count(item, next); });
|
|
315
|
+
return total;
|
|
279
316
|
};
|
|
280
|
-
|
|
281
|
-
return count;
|
|
317
|
+
return count(root);
|
|
282
318
|
};
|
|
283
319
|
const peerCards = element => {
|
|
284
320
|
if (!ts.isJsxElement(element.parent)) return new Set();
|
|
@@ -322,18 +358,78 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
322
358
|
return smallPeerGaps.has(row ?? gap) || smallPeerGaps.has(column ?? gap);
|
|
323
359
|
});
|
|
324
360
|
};
|
|
325
|
-
|
|
361
|
+
const separateRegion = owner => isOwner(owner, 'features/overlays', overlayOwners)
|
|
362
|
+
|| isOwner(owner, 'base/popover', ['PopoverContent'])
|
|
363
|
+
|| isOwner(owner, 'base/navigation', ['DropdownMenuContent', 'DropdownMenuSubContent', 'ContextMenuContent', 'ContextMenuSubContent'])
|
|
364
|
+
|| isOwner(owner, 'base/combobox', ['ComboboxContent', 'ComboboxPopup'])
|
|
365
|
+
|| isOwner(owner, 'base/choice-inputs', ['SelectPopupContent']);
|
|
366
|
+
function regionParents(element, parents, context, slot) {
|
|
367
|
+
if (slot === 'render') return parents;
|
|
368
|
+
if (separateRegion(context?.owner) || !context && /^[A-Z]/.test(element.tagName.getText(source))) parents = [];
|
|
369
|
+
return context ? [...parents, { ...context, slot }] : parents;
|
|
370
|
+
}
|
|
371
|
+
function visitValue(value, parents, visiting) {
|
|
372
|
+
if (value) visit(resolve(value) ?? value, parents, visiting);
|
|
373
|
+
}
|
|
374
|
+
function visit(node, parents = [], visiting = new Set()) {
|
|
375
|
+
if (visiting.has(node)) return;
|
|
376
|
+
const next = new Set(visiting).add(node);
|
|
326
377
|
if (ts.isJsxElement(node)) {
|
|
327
378
|
const element = node.openingElement;
|
|
328
379
|
const context = inspect(element, parents);
|
|
329
|
-
for (const child of node.children) visit(child,
|
|
330
|
-
|
|
331
|
-
|
|
380
|
+
for (const child of node.children) visit(child, regionParents(element, parents, context, 'children'), next);
|
|
381
|
+
for (const [slot, value] of attributes(element)) visitValue(value, regionParents(element, parents, context, slot), next);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (ts.isJsxSelfClosingElement(node)) {
|
|
385
|
+
const context = inspect(node, parents);
|
|
386
|
+
for (const [slot, value] of attributes(node)) visitValue(value, regionParents(node, parents, context, slot), next);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (ts.isJsxExpression(node)) { visitValue(node.expression, parents, next); return; }
|
|
390
|
+
if (ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node) || ts.isMethodDeclaration(node)) {
|
|
391
|
+
const member = ts.isIdentifier(node.name) || ts.isStringLiteralLike(node.name) ? node.name.text : undefined;
|
|
392
|
+
// columns contains header/footer renderers as well as cells. Only a cell
|
|
393
|
+
// owns the table-row height addressed by heavy-cell-disclosure.
|
|
394
|
+
const nextParents = parents.map(parent => parent.slot === 'columns' && ['cell', 'header', 'footer'].includes(member) ? {...parent, member} : parent);
|
|
395
|
+
if (ts.isMethodDeclaration(node)) { if (node.body) visit(node.body, nextParents, next); }
|
|
396
|
+
else visitValue(ts.isShorthandPropertyAssignment(node) ? node.name : node.initializer, nextParents, next);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (portalCall(node)) {
|
|
400
|
+
for (const [index, value] of node.arguments.entries()) visitValue(value, index === 0 ? [] : parents, next);
|
|
332
401
|
return;
|
|
333
402
|
}
|
|
334
|
-
|
|
335
|
-
ts.forEachChild(node, child => visit(child, parents));
|
|
403
|
+
ts.forEachChild(node, child => visit(child, parents, next));
|
|
336
404
|
}
|
|
405
|
+
const surfaceOwner = owner => isOwner(owner, 'base/cards', ['Card', 'SmartCard', 'CardFooter']) || isOwner(owner, 'base/forms', ['FormSection']) || isOwner(owner, 'blocks/analytics', ['ChartCard']) || isOwner(owner, 'base/display', ['ContentBlock']);
|
|
406
|
+
const footerRegion = parent => surfaceOwner(parent.owner) && (['footerSlot', 'footer'].includes(parent.slot) || parent.owner.symbol === 'CardFooter');
|
|
407
|
+
const paddedRegion = parent => {
|
|
408
|
+
if (!surfaceOwner(parent.owner) || parent.unknownProps) return false;
|
|
409
|
+
if (parent.props.has('padding')) {
|
|
410
|
+
const padding = literal(parent.props.get('padding'));
|
|
411
|
+
return padding !== undefined && padding !== 'none';
|
|
412
|
+
}
|
|
413
|
+
// Flat removes the frame, not Card/FormSection's ordinary region insets.
|
|
414
|
+
// ChartCard and ContentBlock instead default to no inset when card-free.
|
|
415
|
+
return isOwner(parent.owner, 'base/cards', ['Card', 'SmartCard', 'CardFooter']) || isOwner(parent.owner, 'base/forms', ['FormSection']) || parent.framed;
|
|
416
|
+
};
|
|
417
|
+
const remoteSubmit = expression => {
|
|
418
|
+
const calls = new Set();
|
|
419
|
+
const walk = (node, visiting = new Set()) => {
|
|
420
|
+
if (!node) return;
|
|
421
|
+
const value = resolve(node) ?? node;
|
|
422
|
+
if (visiting.has(value)) return;
|
|
423
|
+
const next = new Set(visiting).add(value);
|
|
424
|
+
if (ts.isCallExpression(value) && ts.isPropertyAccessExpression(value.expression)) {
|
|
425
|
+
if (value.expression.name.text === 'requestSubmit') calls.add('submit');
|
|
426
|
+
if (value.expression.name.text === 'getElementById' && value.expression.expression.getText(source) === 'document' && !symbolFor(value.expression.expression)) calls.add('lookup');
|
|
427
|
+
}
|
|
428
|
+
ts.forEachChild(value, child => walk(child, next));
|
|
429
|
+
};
|
|
430
|
+
walk(expression);
|
|
431
|
+
return calls.size === 2;
|
|
432
|
+
};
|
|
337
433
|
// A native element passed as a package owner's `render` is that owner's root, such as
|
|
338
434
|
// an Item row rendered as a button; the owner keeps its focus, hover and row anatomy.
|
|
339
435
|
const renderRoots = new Set();
|
|
@@ -375,9 +471,10 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
375
471
|
add(node, 'public-symbol', 'error', `${owner.import} does not export ${owner.symbol}.`);
|
|
376
472
|
return;
|
|
377
473
|
}
|
|
378
|
-
const unresolvedBeforeProps = unresolved.length;
|
|
379
474
|
const props = new Map(attributes(node));
|
|
380
|
-
|
|
475
|
+
// Other reviews can have already disclosed this spread. Deduplication of
|
|
476
|
+
// unresolved output must not turn an unknown value into a known default.
|
|
477
|
+
const unknownProps = node.attributes.properties.some(attribute => ts.isJsxSpreadAttribute(attribute) && unknownSpread(attribute.expression));
|
|
381
478
|
const family = owner.import.split('/base/')[1];
|
|
382
479
|
const anatomy = surfaceParts[family]?.test(owner.symbol);
|
|
383
480
|
const forwarder = surfaceForwarders[family] === owner.symbol;
|
|
@@ -410,7 +507,7 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
410
507
|
if ((isOwner(owner, 'base/cards', ['Card', 'SmartCard']) || isOwner(owner, 'base/display', ['ContentBlock'])) && props.get('icon')) add(node, 'decorative-icon', 'warning', `${owner.symbol}: a title icon is decoration unless it encodes state or a brand; let the title name the subject.`);
|
|
411
508
|
const facts = isOwner(owner, 'base/metadata-list', ['MetadataList']) || isOwner(owner, 'blocks/data-display', ['MetadataList']) ? resolve(props.get('items')) : undefined;
|
|
412
509
|
if (facts && ts.isArrayLiteralExpression(facts) && facts.elements.filter(item => properties(item, false).some(([name]) => name === 'icon')).length > 1) add(node, 'decorative-icon', 'warning', 'MetadataList: an icon on every fact repeats what its label says; keep labels and drop the icons.');
|
|
413
|
-
const framed = framedOwner(owner, props);
|
|
510
|
+
const framed = !unknownProps && framedOwner(owner, props);
|
|
414
511
|
if (isOwner(owner, 'base/cards', ['Card', 'SmartCard'])) {
|
|
415
512
|
for (const slot of ['headerAction', 'headerStart']) if (slotBadge(props.get(slot))) add(node, 'card-badge-placement', 'error', `${owner.symbol}: put status/count/version badges in titleSuffix; ${slot} owns ${slot === 'headerAction' ? 'commands' : 'pre-title context'}.`);
|
|
416
513
|
const contents = contentElements(node);
|
|
@@ -423,22 +520,36 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
423
520
|
}
|
|
424
521
|
if (isOwner(owner, 'base/feedback', ['Empty'])) add(node, 'reinvented-empty-state', 'warning', 'Empty exposes lower-level anatomy. Compare EmptyState from blocks/data-display for a routine no-data/no-results region; keep Empty when custom anatomy is required.');
|
|
425
522
|
if (isOwner(owner, 'base/sheet', ['Sheet', 'SheetContent']) || isOwner(owner, 'base/display', ['Dialog', 'DialogContent', 'AlertDialog', 'AlertDialogContent'])) add(node, 'raw-overlay-primitive', 'warning', 'This lower-level modal needs application lifecycle coordination. Prefer ActionDialog, ConfirmDialog or ActionDrawer from features/overlays when their workflow fits.');
|
|
426
|
-
if (isOwner(owner, '
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
523
|
+
if (isOwner(owner, 'base/forms', ['FormActionsBar'])) {
|
|
524
|
+
const footer = parents.findLast(footerRegion);
|
|
525
|
+
const padded = parents.findLast(paddedRegion);
|
|
526
|
+
if (footer || padded) add(node, 'form-actions-bar-placement', 'warning', `FormActionsBar owns a divider, background and inset; ${footer ? `${footer.owner.symbol}.${footer.slot} already owns footer geometry` : `${padded.owner.symbol} already owns body padding`}. Put action buttons directly in the footer slot, or place the bar at the end of the form inside Card padding="none" with flat FormSection siblings. Review intentional custom hosts.`);
|
|
527
|
+
}
|
|
528
|
+
if (isOwner(owner, 'features/table', ['DataTable']) || isOwner(owner, 'base/table', ['Table'])) {
|
|
529
|
+
const footer = parents.findLast(footerRegion);
|
|
530
|
+
const padded = parents.findLast(paddedRegion);
|
|
531
|
+
if (footer || padded) add(node, 'uncontained-table-in-card', 'warning', `${owner.symbol} is inside ${footer ? `${footer.owner.symbol}.${footer.slot}` : `a padded ${padded.owner.symbol}`}. Keep substantial tables in a body or peer region; a deliberate Card host uses padding="none" with a flat table. Review the actual surface and inset ownership.`);
|
|
532
|
+
}
|
|
533
|
+
if ((isOwner(owner, 'base/accordion', ['Accordion', 'SmartAccordion']) || isOwner(owner, 'features/table', ['DataTable']) || isOwner(owner, 'base/code-block', ['CodeBlock'])) && parents.some(parent => isOwner(parent.owner, 'features/table', ['DataTable']) && parent.slot === 'columns' && parent.member === 'cell')) {
|
|
534
|
+
add(node, 'heavy-cell-disclosure', 'warning', `${owner.symbol} in a DataTable cell can disrupt row scanning. Prefer a compact summary with rowActions opening an ActionDrawer, ActionDialog or detail route; retain an intentional inline disclosure after checking expanded rows and keyboard access.`);
|
|
535
|
+
}
|
|
536
|
+
if (isOwner(owner, 'base/choice-inputs', ['CardCheckboxGroup', 'CardRadioGroup', 'ListRadioGroup', 'PillRadioGroup'])) {
|
|
537
|
+
const options = resolve(props.get('options'));
|
|
538
|
+
if (options && ts.isArrayLiteralExpression(options) && options.elements.length === 1 && !ts.isSpreadElement(options.elements[0])) add(node, 'single-option-choice-group', 'warning', `${owner.symbol} has one static option. For a boolean setting prefer Checkbox, Switch, ToggleField or SwitchCard; retain a genuine choice set whose available options happen to contain one item.`);
|
|
432
539
|
}
|
|
433
540
|
const routeOwner = isOwner(owner, 'layout/page', ['Page']) || isOwner(owner, 'layout', ['Page']);
|
|
434
541
|
if (routeOwner) {
|
|
542
|
+
for (const name of ['narrow', 'maxWidth']) {
|
|
543
|
+
const value = resolve(props.get(name));
|
|
544
|
+
if (value && (ts.isConditionalExpression(value) || ts.isBinaryExpression(value) || ts.isPrefixUnaryExpression(value))) add(props.get(name), 'dynamic-page-measure', 'warning', `Page ${name} depends on an expression and may resize the header and tabs when sections change. Keep the route measure stable and constrain a focused tab panel with Container maxWidth="md" gutter="none"; responsive route sizing can be intentional.`);
|
|
545
|
+
}
|
|
435
546
|
const children = regionElements(node);
|
|
436
547
|
const contents = regionElements(node, true);
|
|
437
548
|
const tables = contents.filter(child => isOwner(child.owner, 'features/table', ['DataTable']));
|
|
438
549
|
for (const child of contents) if (isOwner(child.owner, 'features/filters', ['FilterLayout']) && tables.some(table => table.scope === child.scope && (!child.subject || table.subject === child.subject))) add(child.node, 'detached-filter-layout', 'warning', 'Pass FilterLayout into DataTable topbarContent with variant="compact", or use DataView so filters and table chrome share one surface.');
|
|
439
550
|
const hasAsideSlot = properties(props.get('slots'), false).some(([name]) => name === 'aside');
|
|
440
551
|
const onlyForms = children.length > 0 && children.every(child => child.tag === 'form' || isOwner(child.owner, 'base/forms', ['FormSection']));
|
|
441
|
-
if (onlyForms && !unknownProps && !['narrow', 'maxWidth', 'aside'].some(name => props.has(name)) && !hasAsideSlot) add(node, 'wide-single-column-form', 'warning', '
|
|
552
|
+
if (onlyForms && !unknownProps && !['narrow', 'maxWidth', 'aside'].some(name => props.has(name)) && !hasAsideSlot) add(node, 'wide-single-column-form', 'warning', 'Review this single-column form measure: a focused form can use Page narrow (48rem centered), an explicit maxWidth, or a supporting aside. In a mixed/tabbed route, keep Page stable and constrain only the form panel with Container maxWidth="md" gutter="none".');
|
|
442
553
|
}
|
|
443
554
|
if (owner.symbol === 'Page' && literal(props.get('gutter')) === 'none' && !parents.some(parent => parent.framed)) add(node, 'page-gutter', 'warning', 'Page gutter="none" delegates inline padding to a real parent. PageViewport owns scrolling, not padding; verify the parent inset.');
|
|
444
555
|
if (owner.symbol === 'Card' && framed && literal(props.get('padding')) === 'sm' && !parents.some(parent => parent.framed)) add(node, 'compact-card', 'warning', 'Standalone Card padding="sm" halves the normal inset. Omit padding for ordinary subjects; retain sm only for an intentional compact composition.');
|
|
@@ -447,6 +558,8 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
447
558
|
if (framed && parents.some(parent => parent.framed)) add(node, 'nested-surface', 'warning', `${owner.symbol} is inside a framed owner. Review subject boundaries; use surface="flat" when the parent owns the frame.`);
|
|
448
559
|
const header = properties(props.get('header'), false);
|
|
449
560
|
const pageOwner = owner.import === `${index.package}/layout/page` || owner.import === `${index.package}/layout`;
|
|
561
|
+
const headerValue = resolve(props.get('header'));
|
|
562
|
+
if (routeOwner && props.has('header') && headerValue && !ts.isObjectLiteralExpression(headerValue)) unknown(props.get('header'));
|
|
450
563
|
// Every header owner takes role-based commands; its free-form regions hold
|
|
451
564
|
// non-command controls only.
|
|
452
565
|
const commandHeader = (pageOwner && owner.symbol === 'PageHeader') || isOwner(owner, 'base/navigation', ['PageHeading']) || isOwner(owner, 'features/resource', ['ResourceHeader', 'ResourceIndexShell', 'ResourceShowShell', 'TabbedResourceShell']) || isOwner(owner, 'layout/settings', ['SettingsShell', 'AsideNavShell']) || isOwner(owner, 'layout', ['SettingsShell', 'AsideNavShell']) || isOwner(owner, 'layout/workspace', ['WorkspaceRecordHeader']);
|
|
@@ -454,11 +567,12 @@ export function analyzeComposition(text, { filename, index, ts }) {
|
|
|
454
567
|
const elementSecondary = isOwner(owner, 'layout/workspace', ['WorkspaceRecordHeader']) ? props.get('secondaryActions') : undefined;
|
|
455
568
|
if (pageOwner && owner.symbol === 'Page') {
|
|
456
569
|
const primaries = solidPrimaryCount(ts.isJsxSelfClosingElement(node) ? node : node.parent) + (new Map(header).has('primaryAction') ? 1 : 0);
|
|
457
|
-
if (primaries > 1) add(node, 'multiple-primary-commands', 'warning', `Page has ${primaries} solid primary commands. Keep one
|
|
570
|
+
if (primaries > 1) add(node, 'multiple-primary-commands', 'warning', `Page has ${primaries} solid primary commands in the same task scope. Keep one leading command per task/surface; forms keep their local SubmitStateButton or FormActionsBar. Use header.primaryAction for route-level commands and secondaryActions for supporting route commands.`);
|
|
458
571
|
}
|
|
459
572
|
if ((headerActions && commandButtons(headerActions)) || (elementSecondary && commandButtons(elementSecondary))) add(node, 'page-command-roles', 'warning', `${owner.symbol}: buttons, command rows and menus in the header actions region choose their own emphasis, order and overflow. Pass commands as primaryAction (one) and ordered secondaryActions; keep actions for non-command controls.`);
|
|
460
573
|
const headerProps = pageOwner && owner.symbol === 'Page' ? new Map(header) : pageOwner && owner.symbol === 'PageHeader' ? props : undefined;
|
|
461
574
|
if (headerProps) {
|
|
575
|
+
for (const name of ['primaryAction', 'secondaryActions']) if (headerProps.has(name) && remoteSubmit(headerProps.get(name))) add(headerProps.get(name), 'remote-header-form-submit', 'warning', `${owner.symbol}: ${name} submits a form through document.getElementById(...).requestSubmit(). Keep task-local submission beside its fields or in the form footer. A single full-page editor can deliberately own a header submit; preserve native validation and avoid duplicate commands.`);
|
|
462
576
|
if (headerProps.has('titleIcon') && !headerProps.has('titleIconHref') && !headerProps.has('onTitleIconClick')) add(headerProps.get('titleIcon') ?? node, 'decorative-icon', 'warning', `${owner.symbol}: titleIcon decorates the page title; let the title name the page, or link the icon with titleIconHref.`);
|
|
463
577
|
const hasIcon = action => properties(action, false).some(([name]) => name === 'icon');
|
|
464
578
|
const secondary = resolve(headerProps.get('secondaryActions'));
|