@kerfjs/ui 5.0.0-beta.4 → 5.0.0-beta.41
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/README.md +342 -108
- package/ai/application-ui-diagnostic-ids-v1.json +100 -0
- package/ai/application-ui-profile-sync.cjs +529 -0
- package/ai/application-ui-profile.d.mts +1 -0
- package/ai/application-ui-profile.d.ts +102 -0
- package/ai/application-ui-profile.defaults.json +41 -0
- package/ai/application-ui-profile.mjs +626 -0
- package/ai/application-ui-profile.schema.json +130 -0
- package/ai/catalog-authoring.json +25 -0
- package/ai/catalog-authoring.schema.json +61 -0
- package/ai/compile-time-contracts-v1.json +132 -0
- package/ai/compile-time-contracts-v1.schema.json +51 -0
- package/ai/component-catalog-extension-v2.d.ts +7 -0
- package/ai/component-catalog-extension-v2.schema.json +7 -0
- package/ai/component-catalog-extension.schema.json +79 -0
- package/ai/component-catalog-v2-overrides.json +496 -0
- package/ai/component-catalog-v2.d.ts +90 -0
- package/ai/component-catalog-v2.json +5678 -0
- package/ai/component-catalog-v2.schema.json +298 -0
- package/ai/component-catalog.json +2694 -417
- package/ai/component-catalog.schema.json +39 -0
- package/ai/public-api-signatures-v1.md +1591 -126
- package/ai/skill.md +140 -54
- package/ai/webawesome-jsx-signatures-v1.md +2 -1
- package/analyzer/cli.mjs +49 -0
- package/analyzer/index.d.mts +46 -0
- package/analyzer/index.mjs +1086 -0
- package/analyzer/report.schema.json +66 -0
- package/dist/app-tab.d.ts +15 -4
- package/dist/app-tab.js +3 -1
- package/dist/browser/app-tab.js +1 -0
- package/dist/browser/floating-toolbar.js +3 -0
- package/dist/browser/list-action-row.js +5 -0
- package/dist/browser/list-header.js +7 -0
- package/dist/browser/list-inset-control.js +3 -0
- package/dist/browser/list-inset-text.js +3 -0
- package/dist/browser/list-item.js +5 -0
- package/dist/browser/list.js +3 -0
- package/dist/browser/pane.js +3 -0
- package/dist/browser/row.js +3 -0
- package/dist/browser/segmented-control.js +1 -0
- package/dist/browser/select.js +1 -0
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/spacer.js +3 -0
- package/dist/browser/state-banner.js +1 -0
- package/dist/browser/sunken-panel.js +3 -0
- package/dist/browser/surface-scaffold.js +3 -0
- package/dist/browser/text.js +3 -0
- package/dist/browser/toolbar-text.js +1 -0
- package/dist/browser/value-table.js +1 -0
- package/dist/catalog-resources.d.ts +35 -0
- package/dist/catalog-resources.js +22 -0
- package/dist/catalog-resources.js.map +1 -0
- package/dist/catalog.d.ts +151 -0
- package/dist/catalog.js +396 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chunk-2TYUFZUC.js +22 -0
- package/dist/chunk-2TYUFZUC.js.map +1 -0
- package/dist/chunk-6NDBE2OK.js +32 -0
- package/dist/chunk-6NDBE2OK.js.map +1 -0
- package/dist/chunk-6WTETUZ6.js +21 -0
- package/dist/chunk-6WTETUZ6.js.map +1 -0
- package/dist/chunk-AQFWBG23.js +83 -0
- package/dist/chunk-AQFWBG23.js.map +1 -0
- package/dist/chunk-B6JYRX3P.js +51 -0
- package/dist/chunk-B6JYRX3P.js.map +1 -0
- package/dist/chunk-C34NZOLJ.js +53 -0
- package/dist/chunk-C34NZOLJ.js.map +1 -0
- package/dist/chunk-CAB6NQ2T.js +102 -0
- package/dist/chunk-CAB6NQ2T.js.map +1 -0
- package/dist/chunk-CK6P5AUK.js +136 -0
- package/dist/chunk-CK6P5AUK.js.map +1 -0
- package/dist/chunk-DAO2VFTT.js +129 -0
- package/dist/chunk-DAO2VFTT.js.map +1 -0
- package/dist/chunk-DEYOGDE7.js +141 -0
- package/dist/chunk-DEYOGDE7.js.map +1 -0
- package/dist/chunk-DYBPI5O2.js +25 -0
- package/dist/chunk-DYBPI5O2.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-E34TE2ZI.js} +12 -8
- package/dist/chunk-E34TE2ZI.js.map +1 -0
- package/dist/chunk-EYALJQQD.js +25 -0
- package/dist/chunk-EYALJQQD.js.map +1 -0
- package/dist/chunk-I45QO2IZ.js +88 -0
- package/dist/chunk-I45QO2IZ.js.map +1 -0
- package/dist/chunk-I4V3UDSA.js +79 -0
- package/dist/chunk-I4V3UDSA.js.map +1 -0
- package/dist/chunk-OGTJNHRG.js +42 -0
- package/dist/chunk-OGTJNHRG.js.map +1 -0
- package/dist/chunk-OL4TC3U2.js +156 -0
- package/dist/chunk-OL4TC3U2.js.map +1 -0
- package/dist/chunk-OMSRSL3B.js +120 -0
- package/dist/chunk-OMSRSL3B.js.map +1 -0
- package/dist/chunk-R3EBGEAW.js +46 -0
- package/dist/chunk-R3EBGEAW.js.map +1 -0
- package/dist/chunk-SOK5EUF7.js +25 -0
- package/dist/chunk-SOK5EUF7.js.map +1 -0
- package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
- package/dist/chunk-SRSJO5QE.js.map +1 -0
- package/dist/chunk-T7DOWS6T.js +61 -0
- package/dist/chunk-T7DOWS6T.js.map +1 -0
- package/dist/chunk-TIGSG6BX.js +218 -0
- package/dist/chunk-TIGSG6BX.js.map +1 -0
- package/dist/chunk-UDHHKZWO.js +41 -0
- package/dist/chunk-UDHHKZWO.js.map +1 -0
- package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
- package/dist/chunk-UKKSMZYT.js.map +1 -0
- package/dist/chunk-V6GQKOX2.js +39 -0
- package/dist/chunk-V6GQKOX2.js.map +1 -0
- package/dist/chunk-V6HCTCVD.js +46 -0
- package/dist/chunk-V6HCTCVD.js.map +1 -0
- package/dist/chunk-VDUJZF5G.js +515 -0
- package/dist/chunk-VDUJZF5G.js.map +1 -0
- package/dist/chunk-VXFGMDA7.js +31 -0
- package/dist/chunk-VXFGMDA7.js.map +1 -0
- package/dist/chunk-WN52USHV.js +115 -0
- package/dist/chunk-WN52USHV.js.map +1 -0
- package/dist/chunk-WSXUPSID.js +43 -0
- package/dist/chunk-WSXUPSID.js.map +1 -0
- package/dist/chunk-X5GRDQ7V.js +50 -0
- package/dist/chunk-X5GRDQ7V.js.map +1 -0
- package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
- package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
- package/dist/chunk-ZASJJMGV.js +17 -0
- package/dist/chunk-ZASJJMGV.js.map +1 -0
- package/dist/chunk-ZPWZIVI6.js +47 -0
- package/dist/chunk-ZPWZIVI6.js.map +1 -0
- package/dist/collapsible-panel.d.ts +77 -0
- package/dist/collapsible-panel.js +84 -0
- package/dist/collapsible-panel.js.map +1 -0
- package/dist/css-values.d.ts +75 -0
- package/dist/css-values.js +3 -0
- package/dist/css-values.js.map +1 -0
- package/dist/device-class.d.ts +62 -0
- package/dist/device-class.js +103 -0
- package/dist/device-class.js.map +1 -0
- package/dist/disclosure-arrow.js +1 -1
- package/dist/divider-sides-BzB6rphT.d.ts +6 -0
- package/dist/empty-state.d.ts +3 -2
- package/dist/empty-state.js +1 -1
- package/dist/flex-alignment-CF8NLj7i.d.ts +4 -0
- package/dist/floating-toolbar.d.ts +29 -0
- package/dist/floating-toolbar.js +3 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/index.d.ts +28 -14
- package/dist/index.js +34 -23
- package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +19 -8
- package/dist/list-action-row.js +7 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/list-header.d.ts +80 -0
- package/dist/list-header.js +9 -0
- package/dist/list-header.js.map +1 -0
- package/dist/list-inset-control.d.ts +20 -0
- package/dist/list-inset-control.js +3 -0
- package/dist/list-inset-control.js.map +1 -0
- package/dist/list-inset-text.d.ts +29 -0
- package/dist/list-inset-text.js +3 -0
- package/dist/list-inset-text.js.map +1 -0
- package/dist/list-item.d.ts +45 -0
- package/dist/list-item.js +7 -0
- package/dist/list-item.js.map +1 -0
- package/dist/list.d.ts +30 -0
- package/dist/list.js +5 -0
- package/dist/list.js.map +1 -0
- package/dist/loading-spinner.js +1 -1
- package/dist/nav-stack.d.ts +41 -0
- package/dist/nav-stack.js +6 -0
- package/dist/nav-stack.js.map +1 -0
- package/dist/pane.d.ts +50 -0
- package/dist/pane.js +4 -0
- package/dist/pane.js.map +1 -0
- package/dist/resizable-region.d.ts +21 -3
- package/dist/resizable-region.js +1 -1
- package/dist/row.d.ts +28 -0
- package/dist/row.js +5 -0
- package/dist/row.js.map +1 -0
- package/dist/segmented-control.d.ts +11 -8
- package/dist/segmented-control.js +3 -1
- package/dist/select-register.d.ts +5 -1
- package/dist/select-register.js +129 -1
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +34 -8
- package/dist/select.js +3 -1
- package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
- package/dist/skeleton.d.ts +25 -0
- package/dist/skeleton.js +4 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/spacer.d.ts +16 -0
- package/dist/spacer.js +4 -0
- package/dist/spacer.js.map +1 -0
- package/dist/split-view.d.ts +43 -0
- package/dist/split-view.js +82 -0
- package/dist/split-view.js.map +1 -0
- package/dist/state-banner.d.ts +11 -5
- package/dist/state-banner.js +3 -1
- package/dist/styles/app-tab.css +26 -0
- package/dist/styles/catalog.css +582 -0
- package/dist/styles/collapsible-panel.css +212 -0
- package/dist/styles/floating-toolbar.css +38 -0
- package/dist/styles/foundation.css +80 -0
- package/dist/styles/layout.css +2 -20
- package/dist/styles/list-action-row.css +224 -0
- package/dist/styles/list-header.css +224 -0
- package/dist/styles/list-inset-control.css +23 -0
- package/dist/styles/list-inset-text.css +30 -0
- package/dist/styles/list-item.css +171 -0
- package/dist/styles/list.css +118 -0
- package/dist/styles/nav-stack.css +141 -0
- package/dist/styles/pane.css +62 -0
- package/dist/styles/resizable-region.css +158 -0
- package/dist/styles/row.css +92 -0
- package/dist/styles/segmented-control.css +10 -7
- package/dist/styles/select.css +115 -2
- package/dist/styles/skeleton.css +31 -0
- package/dist/styles/spacer.css +11 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/state-banner.css +49 -10
- package/dist/styles/styles.css +10 -5
- package/dist/styles/sunken-panel.css +22 -0
- package/dist/styles/surface-scaffold.css +51 -0
- package/dist/styles/tab-bar.css +24 -0
- package/dist/styles/tab-scaffold.css +85 -0
- package/dist/styles/text.css +6 -0
- package/dist/styles/token-search-field.css +14 -2
- package/dist/styles/toolbar-control-group.css +235 -8
- package/dist/styles/toolbar-text.css +65 -2
- package/dist/styles/toolbar.css +47 -2
- package/dist/styles/webawesome.css +171 -0
- package/dist/styles/workbench.css +187 -0
- package/dist/sunken-panel.d.ts +20 -0
- package/dist/sunken-panel.js +3 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/surface-scaffold.d.ts +26 -0
- package/dist/surface-scaffold.js +3 -0
- package/dist/surface-scaffold.js.map +1 -0
- package/dist/tab-bar.d.ts +24 -6
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +31 -0
- package/dist/tab-scaffold.js +51 -0
- package/dist/tab-scaffold.js.map +1 -0
- package/dist/text.d.ts +20 -0
- package/dist/text.js +3 -0
- package/dist/text.js.map +1 -0
- package/dist/token-search-field.d.ts +30 -13
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +33 -7
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +31 -4
- package/dist/toolbar-text.js +3 -1
- package/dist/toolbar.d.ts +10 -7
- package/dist/toolbar.js +1 -1
- package/dist/value-table.d.ts +7 -3
- package/dist/value-table.js +3 -1
- package/dist/webawesome.d.ts +1 -0
- package/dist/wire-catalog.d.ts +57 -0
- package/dist/wire-catalog.js +302 -0
- package/dist/wire-catalog.js.map +1 -0
- package/dist/wire-nav-stack.d.ts +18 -0
- package/dist/wire-nav-stack.js +228 -0
- package/dist/wire-nav-stack.js.map +1 -0
- package/dist/wire-resizable-regions.d.ts +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.d.ts +48 -0
- package/dist/wire-sidebar.js +147 -0
- package/dist/wire-sidebar.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +19 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-scaffold.d.ts +11 -0
- package/dist/wire-tab-scaffold.js +21 -0
- package/dist/wire-tab-scaffold.js.map +1 -0
- package/dist/wire-token-search-fields.d.ts +104 -4
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +44 -0
- package/dist/workbench.js +106 -0
- package/dist/workbench.js.map +1 -0
- package/docs/accessibility.md +155 -20
- package/docs/app-layouts.md +61 -0
- package/docs/catalog.md +364 -0
- package/docs/collapsible-panel.md +142 -0
- package/docs/component-contract.md +258 -28
- package/docs/component-integration.md +63 -0
- package/docs/component-selection.md +219 -57
- package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
- package/docs/design/templates/empty-state/filtered.svg +1 -0
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
- package/docs/design/templates/empty-state/with-action.svg +1 -0
- package/docs/design/templates/empty-state-dark.svg +1 -0
- package/docs/design/templates/empty-state.svg +1 -0
- package/docs/design/templates/list/compact-dark.svg +1 -0
- package/docs/design/templates/list/compact.svg +1 -0
- package/docs/design/templates/list/stack-dark.svg +1 -0
- package/docs/design/templates/list/stack.svg +1 -0
- package/docs/design/templates/list-action-row/default-dark.svg +1 -0
- package/docs/design/templates/list-action-row/default.svg +1 -0
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
- package/docs/design/templates/list-action-row/selected.svg +1 -0
- package/docs/design/templates/list-action-row-dark.svg +1 -0
- package/docs/design/templates/list-action-row.svg +1 -0
- package/docs/design/templates/list-dark.svg +1 -0
- package/docs/design/templates/list-header/action-dark.svg +1 -0
- package/docs/design/templates/list-header/action.svg +1 -0
- package/docs/design/templates/list-header/count-dark.svg +1 -0
- package/docs/design/templates/list-header/count.svg +1 -0
- package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
- package/docs/design/templates/list-header/inline-action.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
- package/docs/design/templates/list-header-dark.svg +1 -0
- package/docs/design/templates/list-header.svg +1 -0
- package/docs/design/templates/list-item/default-dark.svg +1 -0
- package/docs/design/templates/list-item/default.svg +1 -0
- package/docs/design/templates/list-item/multiline-dark.svg +1 -0
- package/docs/design/templates/list-item/multiline.svg +1 -0
- package/docs/design/templates/list-item/selected-dark.svg +1 -0
- package/docs/design/templates/list-item/selected.svg +1 -0
- package/docs/design/templates/list-item/trailing-dark.svg +1 -0
- package/docs/design/templates/list-item/trailing.svg +1 -0
- package/docs/design/templates/list-item-dark.svg +1 -0
- package/docs/design/templates/list-item.svg +1 -0
- package/docs/design/templates/list.svg +1 -0
- package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
- package/docs/design/templates/segmented-control/equal.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small.svg +1 -0
- package/docs/design/templates/segmented-control-dark.svg +1 -0
- package/docs/design/templates/segmented-control.svg +1 -0
- package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
- package/docs/design/templates/skeleton/avatar.svg +1 -0
- package/docs/design/templates/skeleton/block-dark.svg +1 -0
- package/docs/design/templates/skeleton/block.svg +1 -0
- package/docs/design/templates/skeleton/lines-dark.svg +1 -0
- package/docs/design/templates/skeleton/lines.svg +1 -0
- package/docs/design/templates/skeleton-dark.svg +1 -0
- package/docs/design/templates/skeleton.svg +1 -0
- package/docs/design/templates/state-banner/danger-dark.svg +1 -0
- package/docs/design/templates/state-banner/danger.svg +1 -0
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
- package/docs/design/templates/state-banner/info-badge.svg +1 -0
- package/docs/design/templates/state-banner/info-dark.svg +1 -0
- package/docs/design/templates/state-banner/info.svg +1 -0
- package/docs/design/templates/state-banner/success-dark.svg +1 -0
- package/docs/design/templates/state-banner/success.svg +1 -0
- package/docs/design/templates/state-banner/warning-dark.svg +1 -0
- package/docs/design/templates/state-banner/warning.svg +1 -0
- package/docs/design/templates/state-banner-dark.svg +1 -0
- package/docs/design/templates/state-banner.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
- package/docs/design/templates/tab-bar-dark.svg +1 -0
- package/docs/design/templates/tab-bar.svg +1 -0
- package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
- package/docs/design/templates/token-search-field/empty.svg +1 -0
- package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
- package/docs/design/templates/token-search-field/tokens.svg +1 -0
- package/docs/design/templates/token-search-field-dark.svg +1 -0
- package/docs/design/templates/token-search-field.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group.svg +1 -0
- package/docs/design/templates/toolbar-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default.svg +1 -0
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/large.svg +1 -0
- package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/small.svg +1 -0
- package/docs/design/templates/toolbar-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-text.svg +1 -0
- package/docs/design/templates/toolbar.svg +1 -0
- package/docs/design/templates/value-table/details-dark.svg +1 -0
- package/docs/design/templates/value-table/details.svg +1 -0
- package/docs/design/templates/value-table-dark.svg +1 -0
- package/docs/design/templates/value-table.svg +1 -0
- package/docs/design/templates.md +128 -0
- package/docs/design-philosophy.md +33 -11
- package/docs/device-class.md +54 -0
- package/docs/examples/application-ui-profile.json +60 -0
- package/docs/examples/command-palette-adapter.tsx +68 -17
- package/docs/examples/component-catalog-extension-v2.json +80 -0
- package/docs/examples/component-catalog-extension.json +45 -0
- package/docs/layout.md +169 -38
- package/docs/nav-stack.md +77 -0
- package/docs/recipes.md +64 -16
- package/docs/split-view.md +55 -0
- package/docs/sunken-panel.md +53 -0
- package/docs/surface-scaffold.md +25 -0
- package/docs/tab-scaffold.md +52 -0
- package/docs/type-contracts.md +127 -0
- package/docs/ui-analyzer.md +118 -0
- package/docs/ui-doctor.md +91 -0
- package/docs/ui-evaluator.md +122 -0
- package/docs/ux-demo.md +29 -10
- package/docs/webawesome-theme.md +82 -8
- package/docs/workbench.md +123 -0
- package/doctor/cli.mjs +115 -0
- package/doctor/config.schema.json +68 -0
- package/doctor/eslint-diagnostics.d.mts +24 -0
- package/doctor/eslint-diagnostics.mjs +24 -0
- package/doctor/index.d.mts +116 -0
- package/doctor/index.mjs +1272 -0
- package/doctor/report.schema.json +118 -0
- package/evaluator/cli.mjs +89 -0
- package/evaluator/index.d.mts +90 -0
- package/evaluator/index.mjs +1138 -0
- package/evaluator/report.schema.json +152 -0
- package/llms.txt +260 -32
- package/package.json +205 -38
- package/traversal-exclusions.mjs +27 -0
- package/ux-demo/recipes/app-shell.tsx +223 -13
- package/ux-demo/recipes/collapsible-sidebar.tsx +230 -0
- package/ux-demo/recipes/compact-toolbar.tsx +116 -12
- package/ux-demo/recipes/composer-form.tsx +120 -19
- package/ux-demo/recipes/list-detail-dialog.tsx +205 -0
- package/ux-demo/recipes/list-workspace-states.tsx +176 -9
- package/ux-demo/recipes/loaders.ts +13 -4
- package/ux-demo/recipes/loading-inspector.tsx +191 -0
- package/ux-demo/recipes/mount-recipe.ts +31 -5
- package/ux-demo/recipes/navigation-sidebar.tsx +123 -11
- package/ux-demo/recipes/navigation-stack.tsx +121 -0
- package/ux-demo/recipes/recipes.css +125 -16
- package/ux-demo/recipes/types.ts +9 -1
- package/ux-demo/recipes/workspace-header.tsx +72 -3
- package/dist/browser/dialog-header.js +0 -5
- package/dist/browser/menu-action-row.js +0 -3
- package/dist/browser/menu-header.js +0 -5
- package/dist/browser/menu-item.js +0 -3
- package/dist/browser/page-header.js +0 -3
- package/dist/chunk-2PES33HS.js +0 -13
- package/dist/chunk-2PES33HS.js.map +0 -1
- package/dist/chunk-55DATV3A.js +0 -10
- package/dist/chunk-55DATV3A.js.map +0 -1
- package/dist/chunk-7BR3DO2A.js.map +0 -1
- package/dist/chunk-7LDCTU7D.js +0 -134
- package/dist/chunk-7LDCTU7D.js.map +0 -1
- package/dist/chunk-AQNKJRFH.js +0 -79
- package/dist/chunk-AQNKJRFH.js.map +0 -1
- package/dist/chunk-CTQKI2XL.js +0 -28
- package/dist/chunk-CTQKI2XL.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-EGHL5S4P.js +0 -33
- package/dist/chunk-EGHL5S4P.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js +0 -10
- package/dist/chunk-GY5WH7TO.js.map +0 -1
- package/dist/chunk-H5AGGVU5.js +0 -75
- package/dist/chunk-H5AGGVU5.js.map +0 -1
- package/dist/chunk-K3G72I6D.js +0 -24
- package/dist/chunk-K3G72I6D.js.map +0 -1
- package/dist/chunk-M5JRI6MN.js +0 -27
- package/dist/chunk-M5JRI6MN.js.map +0 -1
- package/dist/chunk-MBR3TYNL.js +0 -14
- package/dist/chunk-MBR3TYNL.js.map +0 -1
- package/dist/chunk-MFKMHK5W.js +0 -10
- package/dist/chunk-MFKMHK5W.js.map +0 -1
- package/dist/chunk-MQIZAD4A.js.map +0 -1
- package/dist/chunk-NPPD2A6O.js +0 -40
- package/dist/chunk-NPPD2A6O.js.map +0 -1
- package/dist/chunk-R6JHHBKQ.js +0 -19
- package/dist/chunk-R6JHHBKQ.js.map +0 -1
- package/dist/chunk-S3TPM2LP.js +0 -23
- package/dist/chunk-S3TPM2LP.js.map +0 -1
- package/dist/chunk-TRNBHPNW.js.map +0 -1
- package/dist/chunk-VHW5YNIB.js +0 -17
- package/dist/chunk-VHW5YNIB.js.map +0 -1
- package/dist/chunk-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-XZXRLY5Q.js +0 -33
- package/dist/chunk-XZXRLY5Q.js.map +0 -1
- package/dist/dialog-header.d.ts +0 -15
- package/dist/dialog-header.js +0 -5
- package/dist/dialog-header.js.map +0 -1
- package/dist/menu-action-row.js +0 -4
- package/dist/menu-action-row.js.map +0 -1
- package/dist/menu-header.d.ts +0 -41
- package/dist/menu-header.js +0 -6
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.d.ts +0 -31
- package/dist/menu-item.js +0 -4
- package/dist/menu-item.js.map +0 -1
- package/dist/page-header.d.ts +0 -9
- package/dist/page-header.js +0 -3
- package/dist/page-header.js.map +0 -1
- package/dist/styles/dialog-header.css +0 -87
- package/dist/styles/menu-action-row.css +0 -160
- package/dist/styles/menu-header.css +0 -143
- package/dist/styles/menu-item.css +0 -123
- package/dist/styles/page-header.css +0 -33
- package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { delegate } from 'kerfjs';
|
|
2
|
+
|
|
3
|
+
// src/wire-tab-scaffold.ts
|
|
4
|
+
function wireTabScaffold(root, options) {
|
|
5
|
+
const section = root.matches('[data-component="tab-scaffold"]') ? root : root.querySelector('[data-component="tab-scaffold"]');
|
|
6
|
+
if (!(section instanceof HTMLElement)) return () => {
|
|
7
|
+
};
|
|
8
|
+
return delegate(
|
|
9
|
+
section,
|
|
10
|
+
"click",
|
|
11
|
+
"[data-tab-scaffold-tab]",
|
|
12
|
+
(_event, target) => {
|
|
13
|
+
const tabId = target.getAttribute("data-tab-scaffold-tab");
|
|
14
|
+
if (tabId) options.onSelect(tabId);
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { wireTabScaffold };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=wire-tab-scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/wire-tab-scaffold.ts"],"names":[],"mappings":";;;AAWO,SAAS,eAAA,CACd,MACA,OAAA,EACY;AACZ,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,iCAAiC,IAC1D,IAAA,GACA,IAAA,CAAK,cAAc,iCAAiC,CAAA;AACxD,EAAA,IAAI,EAAE,OAAA,YAAmB,WAAA,CAAA,EAAc,OAAO,MAAM;AAAA,EAAC,CAAA;AACrD,EAAA,OAAO,QAAA;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,yBAAA;AAAA,IACA,CAAC,QAAQ,MAAA,KAAW;AAClB,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,YAAA,CAAa,uBAAuB,CAAA;AACzD,MAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA;AAAA,IACnC;AAAA,GACF;AACF","file":"wire-tab-scaffold.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireTabScaffoldOptions {\n /** Invoked with the selected tab id when a bottom-bar tab is activated. */\n onSelect: (tabId: string) => void;\n}\n\n/**\n * Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its\n * id (the app then updates its controlled `active`). Returns a disposer.\n */\nexport function wireTabScaffold(\n root: Element,\n options: WireTabScaffoldOptions,\n): () => void {\n const section = root.matches('[data-component=\"tab-scaffold\"]')\n ? root\n : root.querySelector('[data-component=\"tab-scaffold\"]');\n if (!(section instanceof HTMLElement)) return () => {};\n return delegate(\n section,\n 'click',\n '[data-tab-scaffold-tab]',\n (_event, target) => {\n const tabId = target.getAttribute('data-tab-scaffold-tab');\n if (tabId) options.onSelect(tabId);\n },\n );\n}\n"]}
|
|
@@ -1,11 +1,111 @@
|
|
|
1
|
+
import { Signal } from 'kerfjs';
|
|
2
|
+
|
|
1
3
|
interface TokenSearchSubmit {
|
|
2
4
|
id: string;
|
|
3
5
|
editor: HTMLElement;
|
|
4
6
|
}
|
|
7
|
+
/** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */
|
|
8
|
+
interface TokenSearchEdit extends TokenSearchSubmit {
|
|
9
|
+
/**
|
|
10
|
+
* The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`
|
|
11
|
+
* to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)
|
|
12
|
+
* without keeping a separate `input` listener.
|
|
13
|
+
*/
|
|
14
|
+
event: InputEvent;
|
|
15
|
+
}
|
|
16
|
+
/** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */
|
|
17
|
+
interface TokenSearchTokenRemoval {
|
|
18
|
+
id: string;
|
|
19
|
+
/** The `data-token-value` of the token the app should remove from its state. */
|
|
20
|
+
value: string;
|
|
21
|
+
editor: HTMLElement;
|
|
22
|
+
/** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */
|
|
23
|
+
direction: 'backward' | 'forward';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is
|
|
27
|
+
* set; each piece defaults on once opted in. The helper never mutates app state:
|
|
28
|
+
* a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}
|
|
29
|
+
* for the caller to apply, while caret movement past a chip is a pure ephemeral
|
|
30
|
+
* mechanic the helper performs itself.
|
|
31
|
+
*/
|
|
32
|
+
interface TokenSearchKeyboardBaseOptions {
|
|
33
|
+
/**
|
|
34
|
+
* From a collapsed caret with no selection, Backspace removes the token
|
|
35
|
+
* immediately before it and Delete the token immediately after — reported via
|
|
36
|
+
* `onRemoveToken` — instead of deleting a character. Default: true.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* ArrowRight moves the caret past a trailing atomic token so text typed next
|
|
40
|
+
* lands after the chip. Default: true.
|
|
41
|
+
*/
|
|
42
|
+
moveCaretPastToken?: boolean;
|
|
43
|
+
}
|
|
44
|
+
type TokenSearchKeyboardOptions = TokenSearchKeyboardBaseOptions & ({
|
|
45
|
+
removeAdjacentToken?: true;
|
|
46
|
+
/** Apply the reported removal to your controlled state, then re-render. */
|
|
47
|
+
onRemoveToken: (removal: TokenSearchTokenRemoval) => void;
|
|
48
|
+
} | {
|
|
49
|
+
removeAdjacentToken: false;
|
|
50
|
+
onRemoveToken?: never;
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Managed collapsible behavior for the iconic TokenSearchField. Every piece is on
|
|
54
|
+
* by default; disable a specific one to own it in the app. Provide `signals` to
|
|
55
|
+
* drive app-owned `expanded` signals per field id instead of helper-created ones.
|
|
56
|
+
*/
|
|
57
|
+
interface TokenSearchCollapsibleOptions {
|
|
58
|
+
/** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */
|
|
59
|
+
expandOnActivate?: boolean;
|
|
60
|
+
/** Collapse the field when focus leaves it while it is empty. Default: true. */
|
|
61
|
+
collapseOnEmptyBlur?: boolean;
|
|
62
|
+
/** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */
|
|
63
|
+
collapseOnEscape?: boolean;
|
|
64
|
+
/** Focus the editor on expand/controlled clear and the trigger on Escape-collapse. Default: true. */
|
|
65
|
+
manageFocus?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Keep an empty field expanded when focus moves to a caller-owned surface
|
|
68
|
+
* rendered outside the field — a suggestions dropdown, date picker, or help
|
|
69
|
+
* popover shown beside it. Return true for any focus target that must NOT
|
|
70
|
+
* trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`
|
|
71
|
+
* (or any node inside one) is always exempt, so this predicate is only needed
|
|
72
|
+
* for surfaces you cannot mark declaratively.
|
|
73
|
+
*/
|
|
74
|
+
keepOpenOn?: (target: Node | null) => boolean;
|
|
75
|
+
/** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */
|
|
76
|
+
signals?: Readonly<Record<string, Signal<boolean>>>;
|
|
77
|
+
}
|
|
5
78
|
interface WireTokenSearchFieldsOptions {
|
|
6
|
-
onSubmit
|
|
79
|
+
onSubmit?: (submission: TokenSearchSubmit) => void;
|
|
80
|
+
/** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */
|
|
81
|
+
onEdit?: (edit: TokenSearchEdit) => void;
|
|
82
|
+
/** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */
|
|
83
|
+
collapsible?: boolean | TokenSearchCollapsibleOptions;
|
|
84
|
+
/** Opt-in atomic-chip keyboard behavior (off by default). */
|
|
85
|
+
keyboard?: false | TokenSearchKeyboardOptions;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to
|
|
89
|
+
* tear down. When collapsible behavior is managed, it also exposes the transient
|
|
90
|
+
* `expanded` state per field id so the app can read it in render, hand in its own
|
|
91
|
+
* signal, or drive it imperatively.
|
|
92
|
+
*/
|
|
93
|
+
interface TokenSearchFieldsHandle {
|
|
94
|
+
(): void;
|
|
95
|
+
dispose(): void;
|
|
96
|
+
/** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */
|
|
97
|
+
expanded(id: string): Signal<boolean> | undefined;
|
|
98
|
+
/** Expand the field (and, when focus is managed, focus its editor). */
|
|
99
|
+
open(id: string): void;
|
|
100
|
+
/** Collapse the field (and, when focus is managed, restore focus to its trigger). */
|
|
101
|
+
close(id: string): void;
|
|
7
102
|
}
|
|
8
|
-
/**
|
|
9
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across
|
|
105
|
+
* controlled token deletion, and (by default) manage the collapsible field's transient
|
|
106
|
+
* expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also
|
|
107
|
+
* exposes the managed `expanded` state per field id.
|
|
108
|
+
*/
|
|
109
|
+
declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, onEdit, collapsible, keyboard, }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
|
|
10
110
|
|
|
11
|
-
export { type TokenSearchSubmit, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
|
|
111
|
+
export { type TokenSearchCollapsibleOptions, type TokenSearchEdit, type TokenSearchFieldsHandle, type TokenSearchKeyboardOptions, type TokenSearchSubmit, type TokenSearchTokenRemoval, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SafeHtml } from 'kerfjs';
|
|
2
|
+
import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
|
|
3
|
+
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
4
|
+
|
|
5
|
+
/** A collapsible Workbench panel — a side rail or the bottom drawer. */
|
|
6
|
+
interface WorkbenchPanel {
|
|
7
|
+
content: KerfUiContent;
|
|
8
|
+
/** Whether the panel is currently collapsed (the app owns this). */
|
|
9
|
+
collapsed?: boolean;
|
|
10
|
+
/** Rail width, or drawer height, in px. Overrides the CSS default. */
|
|
11
|
+
size?: number;
|
|
12
|
+
/** Accessible name for the panel region. */
|
|
13
|
+
label?: string;
|
|
14
|
+
separator?: ResizableRegionSeparator;
|
|
15
|
+
collapseMotion?: ResizableRegionCollapseMotion;
|
|
16
|
+
contentOverflow?: ResizableRegionContentOverflow;
|
|
17
|
+
presentation?: ResizableRegionPresentation;
|
|
18
|
+
/** Control shown in a safe-area-aware viewport corner while collapsed. */
|
|
19
|
+
restoreControl?: SafeHtml;
|
|
20
|
+
restorePosition?: ResizableRegionRestorePosition;
|
|
21
|
+
}
|
|
22
|
+
interface WorkbenchProps {
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
/** The central work area. */
|
|
26
|
+
main: KerfUiContent;
|
|
27
|
+
leftRail?: WorkbenchPanel;
|
|
28
|
+
rightRail?: WorkbenchPanel;
|
|
29
|
+
bottomDrawer?: WorkbenchPanel;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and
|
|
34
|
+
* bottom drawer around a central work area (any absent). Collapsing snaps the
|
|
35
|
+
* panel's track to zero in one reflow while its fixed-size content slides out via
|
|
36
|
+
* a composited transform — the instant-width / sliding-content technique, so the
|
|
37
|
+
* work area relayouts once, not per frame. Bottom-drawer content stays anchored
|
|
38
|
+
* to the shell's stable bottom edge throughout that transition. The app owns
|
|
39
|
+
* each `collapsed` flag; the collapse is pure CSS (no wire). See
|
|
40
|
+
* `docs/23-app-layouts.md` §3.3.
|
|
41
|
+
*/
|
|
42
|
+
declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className, }: WorkbenchProps): SafeHtml;
|
|
43
|
+
|
|
44
|
+
export { Workbench, type WorkbenchPanel, type WorkbenchProps };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'kerfjs/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
// src/workbench.tsx
|
|
4
|
+
function Workbench({
|
|
5
|
+
id,
|
|
6
|
+
label,
|
|
7
|
+
main,
|
|
8
|
+
leftRail,
|
|
9
|
+
rightRail,
|
|
10
|
+
bottomDrawer,
|
|
11
|
+
className = ""
|
|
12
|
+
}) {
|
|
13
|
+
return /* @__PURE__ */ jsxs(
|
|
14
|
+
"section",
|
|
15
|
+
{
|
|
16
|
+
class: `kui-workbench ${className}`.trim(),
|
|
17
|
+
id,
|
|
18
|
+
"data-component": "workbench",
|
|
19
|
+
"aria-label": label,
|
|
20
|
+
children: [
|
|
21
|
+
leftRail && /* @__PURE__ */ jsx(
|
|
22
|
+
"aside",
|
|
23
|
+
{
|
|
24
|
+
class: "kui-workbench__rail kui-workbench__rail--left",
|
|
25
|
+
"data-workbench-rail": "left",
|
|
26
|
+
"data-collapsed": String(leftRail.collapsed ?? false),
|
|
27
|
+
"data-separator": leftRail.separator ?? "auto",
|
|
28
|
+
"data-collapse-motion": leftRail.collapseMotion ?? "slide",
|
|
29
|
+
"data-content-overflow": leftRail.contentOverflow ?? "clip",
|
|
30
|
+
"data-presentation": leftRail.presentation ?? "inline",
|
|
31
|
+
"aria-label": leftRail.label || void 0,
|
|
32
|
+
"aria-hidden": leftRail.presentation === "hidden" ? "true" : void 0,
|
|
33
|
+
style: leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : void 0,
|
|
34
|
+
children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: leftRail.content })
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
leftRail?.collapsed && leftRail.restoreControl && leftRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
class: "kui-workbench__restore",
|
|
41
|
+
"data-panel": "left",
|
|
42
|
+
"data-position": leftRail.restorePosition ?? "bottom-start",
|
|
43
|
+
children: leftRail.restoreControl
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { class: "kui-workbench__center", children: [
|
|
47
|
+
/* @__PURE__ */ jsx("div", { class: "kui-workbench__main", "data-workbench-main": true, children: main }),
|
|
48
|
+
bottomDrawer && /* @__PURE__ */ jsx(
|
|
49
|
+
"section",
|
|
50
|
+
{
|
|
51
|
+
class: "kui-workbench__drawer",
|
|
52
|
+
"data-workbench-drawer": true,
|
|
53
|
+
"data-collapsed": String(bottomDrawer.collapsed ?? false),
|
|
54
|
+
"data-separator": bottomDrawer.separator ?? "auto",
|
|
55
|
+
"data-collapse-motion": bottomDrawer.collapseMotion ?? "slide",
|
|
56
|
+
"data-content-overflow": bottomDrawer.contentOverflow ?? "clip",
|
|
57
|
+
"data-presentation": bottomDrawer.presentation ?? "inline",
|
|
58
|
+
"aria-label": bottomDrawer.label || void 0,
|
|
59
|
+
"aria-hidden": bottomDrawer.presentation === "hidden" ? "true" : void 0,
|
|
60
|
+
style: bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : void 0,
|
|
61
|
+
children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: bottomDrawer.content })
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
bottomDrawer?.collapsed && bottomDrawer.restoreControl && bottomDrawer.presentation !== "hidden" && /* @__PURE__ */ jsx(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
class: "kui-workbench__restore",
|
|
68
|
+
"data-panel": "bottom",
|
|
69
|
+
"data-position": bottomDrawer.restorePosition ?? "bottom-end",
|
|
70
|
+
children: bottomDrawer.restoreControl
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
] }),
|
|
74
|
+
rightRail && /* @__PURE__ */ jsx(
|
|
75
|
+
"aside",
|
|
76
|
+
{
|
|
77
|
+
class: "kui-workbench__rail kui-workbench__rail--right",
|
|
78
|
+
"data-workbench-rail": "right",
|
|
79
|
+
"data-collapsed": String(rightRail.collapsed ?? false),
|
|
80
|
+
"data-separator": rightRail.separator ?? "auto",
|
|
81
|
+
"data-collapse-motion": rightRail.collapseMotion ?? "slide",
|
|
82
|
+
"data-content-overflow": rightRail.contentOverflow ?? "clip",
|
|
83
|
+
"data-presentation": rightRail.presentation ?? "inline",
|
|
84
|
+
"aria-label": rightRail.label || void 0,
|
|
85
|
+
"aria-hidden": rightRail.presentation === "hidden" ? "true" : void 0,
|
|
86
|
+
style: rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : void 0,
|
|
87
|
+
children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: rightRail.content })
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
rightRail?.collapsed && rightRail.restoreControl && rightRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
class: "kui-workbench__restore",
|
|
94
|
+
"data-panel": "right",
|
|
95
|
+
"data-position": rightRail.restorePosition ?? "bottom-end",
|
|
96
|
+
children: rightRail.restoreControl
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { Workbench };
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=workbench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":"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","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport type {\n ResizableRegionCollapseMotion,\n ResizableRegionContentOverflow,\n ResizableRegionPresentation,\n ResizableRegionRestorePosition,\n ResizableRegionSeparator,\n} from './resizable-region.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: KerfUiContent;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n separator?: ResizableRegionSeparator;\n collapseMotion?: ResizableRegionCollapseMotion;\n contentOverflow?: ResizableRegionContentOverflow;\n presentation?: ResizableRegionPresentation;\n /** Control shown in a safe-area-aware viewport corner while collapsed. */\n restoreControl?: SafeHtml;\n restorePosition?: ResizableRegionRestorePosition;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: KerfUiContent;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. Bottom-drawer content stays anchored\n * to the shell's stable bottom edge throughout that transition. The app owns\n * each `collapsed` flag; the collapse is pure CSS (no wire). See\n * `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n className = '',\n}: WorkbenchProps) {\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n aria-label={label}\n >\n {leftRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--left\"\n data-workbench-rail=\"left\"\n data-collapsed={String(leftRail.collapsed ?? false)}\n data-separator={leftRail.separator ?? 'auto'}\n data-collapse-motion={leftRail.collapseMotion ?? 'slide'}\n data-content-overflow={leftRail.contentOverflow ?? 'clip'}\n data-presentation={leftRail.presentation ?? 'inline'}\n aria-label={leftRail.label || undefined}\n aria-hidden={leftRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n leftRail.size\n ? `--kui-workbench-rail-width: ${leftRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>\n )}\n {leftRail?.collapsed &&\n leftRail.restoreControl &&\n leftRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"left\"\n data-position={leftRail.restorePosition ?? 'bottom-start'}\n >\n {leftRail.restoreControl}\n </div>\n )}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && (\n <section\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(bottomDrawer.collapsed ?? false)}\n data-separator={bottomDrawer.separator ?? 'auto'}\n data-collapse-motion={bottomDrawer.collapseMotion ?? 'slide'}\n data-content-overflow={bottomDrawer.contentOverflow ?? 'clip'}\n data-presentation={bottomDrawer.presentation ?? 'inline'}\n aria-label={bottomDrawer.label || undefined}\n aria-hidden={\n bottomDrawer.presentation === 'hidden' ? 'true' : undefined\n }\n style={\n bottomDrawer.size\n ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">\n {bottomDrawer.content}\n </div>\n </section>\n )}\n {bottomDrawer?.collapsed &&\n bottomDrawer.restoreControl &&\n bottomDrawer.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"bottom\"\n data-position={bottomDrawer.restorePosition ?? 'bottom-end'}\n >\n {bottomDrawer.restoreControl}\n </div>\n )}\n </div>\n {rightRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--right\"\n data-workbench-rail=\"right\"\n data-collapsed={String(rightRail.collapsed ?? false)}\n data-separator={rightRail.separator ?? 'auto'}\n data-collapse-motion={rightRail.collapseMotion ?? 'slide'}\n data-content-overflow={rightRail.contentOverflow ?? 'clip'}\n data-presentation={rightRail.presentation ?? 'inline'}\n aria-label={rightRail.label || undefined}\n aria-hidden={rightRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n rightRail.size\n ? `--kui-workbench-rail-width: ${rightRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>\n )}\n {rightRail?.collapsed &&\n rightRail.restoreControl &&\n rightRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"right\"\n data-position={rightRail.restorePosition ?? 'bottom-end'}\n >\n {rightRail.restoreControl}\n </div>\n )}\n </section>\n );\n}\n"]}
|
package/docs/accessibility.md
CHANGED
|
@@ -6,18 +6,70 @@ These are normative contracts for package components and consuming applications.
|
|
|
6
6
|
|
|
7
7
|
- All controls have accessible names and visible `:focus-visible` treatment.
|
|
8
8
|
- Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
|
|
9
|
-
- `
|
|
10
|
-
- `
|
|
11
|
-
-
|
|
9
|
+
- `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
|
|
10
|
+
- `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
|
|
11
|
+
- Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
|
|
12
|
+
- `wireNavStack` moves focus into every newly active top view and remembers the last focused descendant of each mounted view for pop restoration. Mark a preferred initial heading or control with `data-nav-focus` (and `tabindex="-1"` when it is not normally tabbable); otherwise the helper uses the first enabled focusable descendant, then a programmatically focusable view-container fallback.
|
|
13
|
+
- In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; do not reduce the target to the visible icon.
|
|
12
14
|
- `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
|
|
13
|
-
- `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure.
|
|
15
|
+
- `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
|
|
14
16
|
- `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
|
|
15
17
|
- `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
|
|
16
18
|
- `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may override `--kui-disclosure-arrow-size` without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
- `List` is layout-only and adds no `list` role. Children own their native or ARIA semantics. Give a scrollable List a bounded block size, avoid nested scroll owners, and keep focused children visible while scrolling.
|
|
20
|
+
|
|
21
|
+
## Select
|
|
22
|
+
|
|
23
|
+
Supply a visible `label`, or `ariaLabel` when the surrounding interface already
|
|
24
|
+
provides visual context. A nonempty `label` takes precedence when both are
|
|
25
|
+
supplied. Kerf forwards that name through Web Awesome's internal label contract;
|
|
26
|
+
an `aria-label` on the custom-element host alone cannot name its shadow
|
|
27
|
+
combobox. With `ariaLabel` alone, Kerf visually hides the internal label without
|
|
28
|
+
adding height or spacing. This also applies to `renderSelected`: selected content
|
|
29
|
+
stays separate from the control's stable accessible name. Applications do not
|
|
30
|
+
need shadow-DOM patches or extra label styling.
|
|
31
|
+
|
|
32
|
+
Use `presentation="toolbar-borderless"` with `size="compact"` inside a
|
|
33
|
+
`ToolbarControlGroup`; set `focusRingOwner="group"` when that parent paints the
|
|
34
|
+
composed focus ring. `selectedPresentation="icon-only"` hides only the visible
|
|
35
|
+
selected label—the required `label` or `ariaLabel` still names the combobox.
|
|
36
|
+
Navigation selects can use intrinsic `presentation="navigation"` plus
|
|
37
|
+
`labelMaxWidth` for component-owned ellipsis. These props own the control's
|
|
38
|
+
appearance; its parent continues to own outer placement.
|
|
39
|
+
|
|
40
|
+
Use `hint` for persistent supporting text below the control; use
|
|
41
|
+
`placeholderText` only for the empty value shown inside the closed control.
|
|
42
|
+
Kerf passes hint text through Web Awesome's form-control contract, which renders
|
|
43
|
+
the hint part and connects the shadow combobox to it with `aria-describedby`.
|
|
44
|
+
The accessible control is that shadow combobox, not the `wa-select` wrapper;
|
|
45
|
+
inspect or test the element returned by the `combobox` role. Both the `hint`
|
|
46
|
+
attribute used by Kerf and Web Awesome's explicit `hint` slot produce a computed
|
|
47
|
+
accessible description in Chromium, Firefox, and WebKit, so applications do not
|
|
48
|
+
need an `aria-description` mirror or a shadow-DOM patch.
|
|
49
|
+
Loading placeholders keep the same visible hint while replacing the interactive
|
|
50
|
+
control with inert chrome.
|
|
51
|
+
|
|
52
|
+
`tests/unit/components.test.tsx` covers name projection and visible/hidden label
|
|
53
|
+
variants. `tests/browser/select-accessibility.spec.ts` verifies actual accessible
|
|
54
|
+
names, unchanged unlabeled geometry, keyboard selection, controlled rerenders,
|
|
55
|
+
native hint wiring, and wide/narrow presentation in Chromium, Firefox, and WebKit.
|
|
56
|
+
|
|
57
|
+
The explicit `@kerfjs/ui/select/register` import also installs the canonical
|
|
58
|
+
Select lifecycle adapter. The latest open/close request owns animation completion
|
|
59
|
+
and deferred option focus. A stale close cannot hide a reopened popup after
|
|
60
|
+
resize; stale completion events are not emitted. Preventing `wa-show` or
|
|
61
|
+
`wa-hide` retains the accepted closed/open state, and removal prevents deferred
|
|
62
|
+
activation. Native option selection, keyboard behavior, dismissal listeners,
|
|
63
|
+
and popup anchor placement remain with Web Awesome. Raw `wa-select` elements
|
|
64
|
+
without the canonical `data-component="select"` marker are unchanged.
|
|
65
|
+
`tests/unit/select-lifecycle.test.ts` and `animate-select-popup.test.ts` cover
|
|
66
|
+
repeated and out-of-order completions, cancellation, instance isolation, and
|
|
67
|
+
removal. `tests/browser/select-lifecycle.spec.ts` repeats animation/resize/reopen
|
|
68
|
+
sequences at wide and narrow widths in Chromium, Firefox, and WebKit.
|
|
69
|
+
|
|
70
|
+
## ListActionRow
|
|
71
|
+
|
|
72
|
+
`ListActionRow` renders a noninteractive visual root containing primary and
|
|
21
73
|
trailing native buttons as siblings. The primary comes first in DOM and Tab
|
|
22
74
|
order and alone receives `aria-current` or `aria-pressed`; the trailing button
|
|
23
75
|
has its own required accessible name and disabled state. Both controls carry
|
|
@@ -25,7 +77,9 @@ the row's item id for application delegation, while the root deliberately has
|
|
|
25
77
|
no action or role. `label`, `icon`, and `trailingActionIcon` are dormant visual
|
|
26
78
|
content and must not contain buttons, links, interactive roles, or other
|
|
27
79
|
controls. A trailing click, double click, context menu, Enter, or Space
|
|
28
|
-
must never activate the primary control.
|
|
80
|
+
must never activate the primary control. Selected rows keep the normal
|
|
81
|
+
foreground over their brand-tinted fill so text retains WCAG AA contrast in
|
|
82
|
+
light and dark themes. The application owns controlled
|
|
29
83
|
selection and the lifecycle, focus, and dismissal policy of any related
|
|
30
84
|
popover or context menu.
|
|
31
85
|
|
|
@@ -41,21 +95,48 @@ The handle exposes separator role, orientation, name, minimum, maximum, and curr
|
|
|
41
95
|
|
|
42
96
|
The application owns persistence and collapsed/expanded policy. Keep the last expanded size outside the component and restore it when reopening. An optional `handleIcon` replaces only decorative dormant content; it must not contain controls or interactive roles because the separator remains the sole focus and interaction owner.
|
|
43
97
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
98
|
+
## Toolbar headings
|
|
99
|
+
|
|
100
|
+
For headings and paragraphs outside toolbar identity/title zones, use `Text`.
|
|
101
|
+
It renders the selected native `h1`–`h6` or `p` element, so heading navigation
|
|
102
|
+
follows the variant directly; choose a level that preserves the document
|
|
103
|
+
outline. Native global, `data-*`, and `aria-*` attributes pass through.
|
|
104
|
+
|
|
105
|
+
Compose panel, dialog, and page headings as a plain `Toolbar`. Its leading zone
|
|
106
|
+
holds an optional icon `ToolbarControlGroup` and a direct extra-large
|
|
107
|
+
`ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
|
|
108
|
+
the title carries no native heading role, so for a **dialog or panel** the
|
|
109
|
+
application connects the title id and optional supporting-copy id to the owning
|
|
110
|
+
host through `aria-labelledby` and `aria-describedby`. For a **page or view**
|
|
111
|
+
title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
|
|
112
|
+
`role="heading"` with a matching `aria-level`, giving the view a heading landmark
|
|
113
|
+
so screen-reader heading navigation and "main heading" semantics work — the same
|
|
114
|
+
role/level pair used throughout Kerf UI. Keep the levels meaningful and
|
|
115
|
+
non-skipping within a view. Pass trailing
|
|
116
|
+
controls as a labeled `ToolbarControlGroup` when that group needs an accessible
|
|
117
|
+
name. Keep supporting copy as app-owned content below the toolbar.
|
|
118
|
+
|
|
119
|
+
## FloatingToolbar
|
|
120
|
+
|
|
121
|
+
`FloatingToolbar` is a `role="toolbar"` region with a **required** accessible
|
|
122
|
+
`label`; it floats over the content of its nearest positioned ancestor and is
|
|
123
|
+
forced to a dark color scheme, but it is **not** in the top layer, so it never
|
|
124
|
+
covers dialogs, popovers, or other overlays. Its children are the app's controls
|
|
125
|
+
(normally `ToolbarControlGroup`s), which keep their own names, focus, and
|
|
126
|
+
keyboard behavior; the application owns their actions, the toolbar's visibility,
|
|
127
|
+
and — via `position` and `--kui-floating-toolbar-inset` — where it sits.
|
|
54
128
|
|
|
55
129
|
## Tabs
|
|
56
130
|
|
|
57
131
|
`AppTab` renders one controlled tab. `TabBar` supplies the containing tab list,
|
|
58
132
|
fixed leading/trailing regions, and a horizontally scrollable strip.
|
|
133
|
+
Use `TabBar.presentation` for rail, segmented, or inspector chrome,
|
|
134
|
+
`allocation="fill"` when peers should divide the strip, and
|
|
135
|
+
`trailingPlacement="adjacent"` when an action belongs beside the final tab.
|
|
136
|
+
`AppTab` provides compact 32px, segmented, truncating-label, and icon-only
|
|
137
|
+
presentations. Icon-only tabs keep the required `name` as the tab button's
|
|
138
|
+
accessible name while visually hiding the duplicate label. These props own
|
|
139
|
+
component appearance only; the application still owns the bar's outer placement.
|
|
59
140
|
`AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
|
|
60
141
|
filtering rejects roles plus case variants of component- or wiring-owned
|
|
61
142
|
action, identity, selection, drag, drop, and component attributes. An optional
|
|
@@ -68,6 +149,16 @@ autoscroll at either horizontal edge. Edge autoscroll is direct manipulation,
|
|
|
68
149
|
stops on drop/drag end/disposal, and does not change keyboard behavior. It
|
|
69
150
|
returns a disposer.
|
|
70
151
|
|
|
152
|
+
Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
|
|
153
|
+
default: they move roving focus and select the focused tab. If selection synchronously
|
|
154
|
+
replaces the strip, focus returns to the replacement with the same bar and tab IDs. Pass
|
|
155
|
+
`activation: 'manual'` (or set `data-tab-activation="manual"` on a strip via the
|
|
156
|
+
`TabBar` `activation` prop, which overrides the option per bar) for
|
|
157
|
+
**manual activation**: arrow keys move roving focus only and the user selects
|
|
158
|
+
with Enter / Space (native on the tab button) or click. Use manual activation
|
|
159
|
+
when selecting a tab is a heavy or side-effecting action — e.g. a tab that loads
|
|
160
|
+
a project — so arrowing through the strip does not trigger it on every tab.
|
|
161
|
+
|
|
71
162
|
The application owns the ordered tab array, selection, panels, close policy,
|
|
72
163
|
routing, and persistence. On `onReorder`, synchronously render the reported
|
|
73
164
|
order so the helper can restore focus to the moved tab. Pair tabs with
|
|
@@ -97,7 +188,12 @@ and `placeTokenSearchCaret()` to restore a text caret without landing inside a
|
|
|
97
188
|
chip. Call `wireTokenSearchFields()` once at a stable root so Enter submits
|
|
98
189
|
without inserting a contenteditable line break and keyboard chip deletion
|
|
99
190
|
restores focus plus the text-relative caret after controlled rendering replaces
|
|
100
|
-
the editor
|
|
191
|
+
the editor. Restoration finishes after synchronous input listeners and before
|
|
192
|
+
another keystroke; it does not wait for an animation frame that could overwrite
|
|
193
|
+
a later selection. Select All + Backspace/Delete shares that replacement path
|
|
194
|
+
and keeps the adopted expanded signal open while managed focus is enabled.
|
|
195
|
+
Real outside focus, empty Escape, disposal, and removed fields retain their usual
|
|
196
|
+
behavior. The editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
|
|
101
197
|
the editor's `textContent` before updating application state. Leading and
|
|
102
198
|
trailing controls share the first text line's fixed vertical center and remain
|
|
103
199
|
there as the editor wraps. In `collapsible` mode, the closed state is one named
|
|
@@ -108,6 +204,45 @@ trigger into the revealed editor, and clear `expanded` only after focus leaves
|
|
|
108
204
|
the complete component. Text or tokens keep the field expanded even when that
|
|
109
205
|
transient state is false. Width animation is disabled under reduced motion.
|
|
110
206
|
|
|
207
|
+
When the field is composed with a caller-owned surface rendered outside it — a
|
|
208
|
+
suggestions listbox, date picker, or help popover — mark that surface (or wrap
|
|
209
|
+
it) with `data-token-search-keep-open`, or pass `collapsible.keepOpenOn(target)`,
|
|
210
|
+
so focus or pointer activation moving into it does not collapse an empty field.
|
|
211
|
+
The helper preserves the pointer target when a browser omits
|
|
212
|
+
`focusout.relatedTarget`, preventing the field or target from disappearing
|
|
213
|
+
between pointerdown and click. `wireTokenSearchFields`
|
|
214
|
+
can also, opt-in via `keyboard`, own atomic-chip editing keys: from a collapsed
|
|
215
|
+
caret with no selection, Backspace removes the token before the caret and Delete
|
|
216
|
+
the token after it (reported through `onRemoveToken` for the app to apply to its
|
|
217
|
+
controlled state), and ArrowRight moves the caret past a trailing chip so typed
|
|
218
|
+
text lands after it. An optional `onEdit({ id, editor, event })` — with the
|
|
219
|
+
originating `InputEvent` so a caller can gate on `inputType`/`data` — fires on every editor
|
|
220
|
+
`input`, letting a caller drop its own `input` listener; the application still
|
|
221
|
+
owns query parsing and result-count/loading announcements.
|
|
222
|
+
|
|
223
|
+
Managed clear captures the action before application handlers run, keeps the adopted
|
|
224
|
+
expanded signal open during editor replacement, and restores focus at the actual
|
|
225
|
+
mutation checkpoint before the next input task. It also returns keyboard activation
|
|
226
|
+
from the clear button to a surviving editor immediately. No animation frame owns
|
|
227
|
+
clear focus, so fast typing cannot escape to page shortcuts or lose its first letter. The app still owns clearing query/tokens and emptying
|
|
228
|
+
DOM-owned text. This focus step respects `manageFocus: false`, disposal, removed fields,
|
|
229
|
+
and focus deliberately moved to another control; no app-level reopen callback is needed.
|
|
230
|
+
|
|
231
|
+
## CollapsiblePanel / sidebar
|
|
232
|
+
|
|
233
|
+
`CollapsiblePanel` is a labeled `aside` region that is `aria-hidden` while
|
|
234
|
+
collapsed; its content slides via transform (disabled under reduced motion).
|
|
235
|
+
`CollapsiblePanelToggle` is a named button carrying `aria-expanded` and the
|
|
236
|
+
standard per-side collapse/expand glyph (`PanelLeft*` / `PanelRight*` /
|
|
237
|
+
`PanelBottom*`); place a collapse toggle inside the panel and an expand toggle in
|
|
238
|
+
an always-visible location so it is reachable while collapsed. `wireSidebar()`
|
|
239
|
+
moves focus into the panel when it opens and restores it to the trigger when it
|
|
240
|
+
closes. When a `deviceClass()` reports `compact`, an open panel becomes an overlay
|
|
241
|
+
with a dismissable backdrop, Escape and backdrop-click collapse it, and Tab is
|
|
242
|
+
trapped within it (the ARIA dialog pattern). The application owns each `collapsed`
|
|
243
|
+
signal, the panels, their sizes, and content; the wire may persist the collapsed
|
|
244
|
+
state per panel.
|
|
245
|
+
|
|
111
246
|
## Verification matrix
|
|
112
247
|
|
|
113
248
|
For each changed component, inspect default, hover, focus, disabled, selected/pressed, busy/error, long-content, wide, narrow, light, dark, increased-contrast, reduced-motion, keyboard-only, and 200%-zoom states where applicable. DOM order must match reading and focus order, with no clipping or unreachable action.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Choosing an app layout
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui` ships four opt-in, tree-shakeable whole-screen layouts plus the
|
|
4
|
+
[`device-class`](device-class.md) signal that drives their responsive behavior.
|
|
5
|
+
This guide maps a **data + interaction + device** situation to the layout to
|
|
6
|
+
reach for, and states the device-class threshold at which the presentation
|
|
7
|
+
changes. The layouts:
|
|
8
|
+
|
|
9
|
+
- [`NavStack`](nav-stack.md) — push/pop navigation (a single pane is a one-entry stack).
|
|
10
|
+
- [`SplitView`](split-view.md) — list-detail (two panes, collapsing to a stack).
|
|
11
|
+
- [`Workbench`](workbench.md) — the Xcode-like collapsible rails + drawer.
|
|
12
|
+
- [`TabScaffold`](tab-scaffold.md) — the iOS bottom tab bar (each tab a stack).
|
|
13
|
+
- [`CollapsiblePanel` + `wireSidebar`](collapsible-panel.md) — a standalone
|
|
14
|
+
collapsible rail or bottom drawer (with `CollapsiblePanelToggle`), outside a full
|
|
15
|
+
shell: the standard collapse animation, icon convention, and `wireSidebar`
|
|
16
|
+
semantics (focus, compact overlay, persistence).
|
|
17
|
+
|
|
18
|
+
Derive responsiveness from `deviceClass()`: `compact` (a handset or portrait
|
|
19
|
+
tablet) means "one pane at a time"; `atLeast('tablet')` / `atLeast('desktop')`
|
|
20
|
+
gate the roomier presentations.
|
|
21
|
+
|
|
22
|
+
## Decision matrix
|
|
23
|
+
|
|
24
|
+
| Situation | Layout | Device threshold |
|
|
25
|
+
| ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
|
|
27
|
+
| Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
|
|
28
|
+
| Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
|
|
29
|
+
| Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
|
|
30
|
+
| Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
|
|
31
|
+
|
|
32
|
+
### Worked examples
|
|
33
|
+
|
|
34
|
+
- **Settings screen (simple):** one `NavStack` entry per screen; push a subpage
|
|
35
|
+
on tap. No `SplitView`/`Workbench` — it is a single flow.
|
|
36
|
+
- **Mail (drill-down + two-pane):** `SplitView` with `list={<ThreadList/>}` and
|
|
37
|
+
`detail={<Message/>}`, `compact={device.value.compact}`,
|
|
38
|
+
`detailActive={selected != null}`. On desktop both panes show with a resizable
|
|
39
|
+
separator; on a phone it is a `NavStack` (threads → message, back clears the
|
|
40
|
+
selection).
|
|
41
|
+
- **IDE (complex tool):** `Workbench` with a left navigator rail, a right
|
|
42
|
+
inspector rail, and a bottom console drawer, each `collapsed` bound to a
|
|
43
|
+
signal. Only offer this `atLeast('desktop')`.
|
|
44
|
+
- **Social app (tabbed):** `TabScaffold` with Home / Search / Profile tabs, each
|
|
45
|
+
`content` a `NavStack`. On a tablet/desktop, render the same sections as a
|
|
46
|
+
`Workbench` left rail instead of a bottom bar.
|
|
47
|
+
|
|
48
|
+
## Dialogs
|
|
49
|
+
|
|
50
|
+
Pick the dialog's inner layout by the same complexity axis, then apply the device
|
|
51
|
+
class to how it is presented (compose with [`overlay`](../../docs/19-native-overlay-backing.md)):
|
|
52
|
+
|
|
53
|
+
- **desktop:** an inline dialog — a `SplitView` two-pane body, or a `NavStack`
|
|
54
|
+
for a wizard.
|
|
55
|
+
- **portrait tablet / handset:** present a `SplitView`/complex dialog as a
|
|
56
|
+
full-screen modal (its `compact` `NavStack` form).
|
|
57
|
+
- **landscape tablet:** a large partial-cover modal (does not need to go full
|
|
58
|
+
screen).
|
|
59
|
+
|
|
60
|
+
A `NavStack` works as a dialog body at every size — a wizard pushes and pops its
|
|
61
|
+
steps with cross-faded chrome.
|