@kanzo-tech/ui 0.0.1-alpha
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 +60 -0
- package/dist/analytics.d.ts +36 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +155 -0
- package/dist/analytics.js.map +1 -0
- package/dist/charts/arrow.d.ts +37 -0
- package/dist/charts/arrow.d.ts.map +1 -0
- package/dist/charts/arrow.js +29 -0
- package/dist/charts/arrow.js.map +1 -0
- package/dist/charts/chart-axes.d.ts +59 -0
- package/dist/charts/chart-axes.d.ts.map +1 -0
- package/dist/charts/chart-axes.js +39 -0
- package/dist/charts/chart-axes.js.map +1 -0
- package/dist/charts/chart-config.d.ts +52 -0
- package/dist/charts/chart-config.d.ts.map +1 -0
- package/dist/charts/chart-config.js +43 -0
- package/dist/charts/chart-config.js.map +1 -0
- package/dist/charts/chart-inputs.d.ts +174 -0
- package/dist/charts/chart-inputs.d.ts.map +1 -0
- package/dist/charts/chart-inputs.js +382 -0
- package/dist/charts/chart-inputs.js.map +1 -0
- package/dist/charts/chart-interactors.d.ts +75 -0
- package/dist/charts/chart-interactors.d.ts.map +1 -0
- package/dist/charts/chart-interactors.js +35 -0
- package/dist/charts/chart-interactors.js.map +1 -0
- package/dist/charts/chart-legend.d.ts +38 -0
- package/dist/charts/chart-legend.d.ts.map +1 -0
- package/dist/charts/chart-legend.js +49 -0
- package/dist/charts/chart-legend.js.map +1 -0
- package/dist/charts/chart-marks.d.ts +124 -0
- package/dist/charts/chart-marks.d.ts.map +1 -0
- package/dist/charts/chart-marks.js +109 -0
- package/dist/charts/chart-marks.js.map +1 -0
- package/dist/charts/chart-root.d.ts +90 -0
- package/dist/charts/chart-root.d.ts.map +1 -0
- package/dist/charts/chart-root.js +162 -0
- package/dist/charts/chart-root.js.map +1 -0
- package/dist/charts/chart-spec.d.ts +126 -0
- package/dist/charts/chart-spec.d.ts.map +1 -0
- package/dist/charts/chart-spec.js +133 -0
- package/dist/charts/chart-spec.js.map +1 -0
- package/dist/charts/chart-stat.d.ts +23 -0
- package/dist/charts/chart-stat.d.ts.map +1 -0
- package/dist/charts/chart-stat.js +24 -0
- package/dist/charts/chart-stat.js.map +1 -0
- package/dist/charts/id-set-client.d.ts +42 -0
- package/dist/charts/id-set-client.d.ts.map +1 -0
- package/dist/charts/id-set-client.js +43 -0
- package/dist/charts/id-set-client.js.map +1 -0
- package/dist/charts/mosaic-provider.d.ts +70 -0
- package/dist/charts/mosaic-provider.d.ts.map +1 -0
- package/dist/charts/mosaic-provider.js +53 -0
- package/dist/charts/mosaic-provider.js.map +1 -0
- package/dist/charts/query-client.d.ts +22 -0
- package/dist/charts/query-client.d.ts.map +1 -0
- package/dist/charts/query-client.js +26 -0
- package/dist/charts/query-client.js.map +1 -0
- package/dist/charts/tokenized-plot.d.ts +26 -0
- package/dist/charts/tokenized-plot.d.ts.map +1 -0
- package/dist/charts/tokenized-plot.js +41 -0
- package/dist/charts/tokenized-plot.js.map +1 -0
- package/dist/charts/use-chart-query.d.ts +30 -0
- package/dist/charts/use-chart-query.d.ts.map +1 -0
- package/dist/charts/use-chart-query.js +19 -0
- package/dist/charts/use-chart-query.js.map +1 -0
- package/dist/composites/CodeEditor.d.ts +64 -0
- package/dist/composites/CodeEditor.d.ts.map +1 -0
- package/dist/composites/CodeEditor.js +521 -0
- package/dist/composites/CodeEditor.js.map +1 -0
- package/dist/composites/MadeWith.d.ts +39 -0
- package/dist/composites/MadeWith.d.ts.map +1 -0
- package/dist/composites/MadeWith.js +30 -0
- package/dist/composites/MadeWith.js.map +1 -0
- package/dist/composites/Preferences.d.ts +155 -0
- package/dist/composites/Preferences.d.ts.map +1 -0
- package/dist/composites/Preferences.js +501 -0
- package/dist/composites/Preferences.js.map +1 -0
- package/dist/composites/Questionnaire.d.ts +215 -0
- package/dist/composites/Questionnaire.d.ts.map +1 -0
- package/dist/composites/Questionnaire.js +422 -0
- package/dist/composites/Questionnaire.js.map +1 -0
- package/dist/composites/SidebarIdentity.d.ts +63 -0
- package/dist/composites/SidebarIdentity.d.ts.map +1 -0
- package/dist/composites/SidebarIdentity.js +103 -0
- package/dist/composites/SidebarIdentity.js.map +1 -0
- package/dist/composites/code-editor-search.d.ts +40 -0
- package/dist/composites/code-editor-search.d.ts.map +1 -0
- package/dist/composites/code-editor-search.js +212 -0
- package/dist/composites/code-editor-search.js.map +1 -0
- package/dist/composites/sidebar.d.ts +129 -0
- package/dist/composites/sidebar.d.ts.map +1 -0
- package/dist/composites/sidebar.js +671 -0
- package/dist/composites/sidebar.js.map +1 -0
- package/dist/composites/theme-notice.d.ts +54 -0
- package/dist/composites/theme-notice.d.ts.map +1 -0
- package/dist/composites/theme-notice.js +19 -0
- package/dist/composites/theme-notice.js.map +1 -0
- package/dist/editor.d.ts +3 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +6 -0
- package/dist/editor.js.map +1 -0
- package/dist/guard-corpus.d.ts +63 -0
- package/dist/guard-corpus.d.ts.map +1 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +696 -0
- package/dist/index.js.map +1 -0
- package/dist/layouts/section.d.ts +166 -0
- package/dist/layouts/section.d.ts.map +1 -0
- package/dist/layouts/section.js +153 -0
- package/dist/layouts/section.js.map +1 -0
- package/dist/layouts/shell.d.ts +128 -0
- package/dist/layouts/shell.d.ts.map +1 -0
- package/dist/layouts/shell.js +107 -0
- package/dist/layouts/shell.js.map +1 -0
- package/dist/lib/cn.d.ts +4 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/cn.js +9 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/is-active-path.d.ts +13 -0
- package/dist/lib/is-active-path.d.ts.map +1 -0
- package/dist/lib/is-active-path.js +7 -0
- package/dist/lib/is-active-path.js.map +1 -0
- package/dist/lib/theme-tick.d.ts +37 -0
- package/dist/lib/theme-tick.d.ts.map +1 -0
- package/dist/lib/theme-tick.js +30 -0
- package/dist/lib/theme-tick.js.map +1 -0
- package/dist/lib/token-color.d.ts +74 -0
- package/dist/lib/token-color.d.ts.map +1 -0
- package/dist/lib/token-color.js +35 -0
- package/dist/lib/token-color.js.map +1 -0
- package/dist/lib/use-is-mobile.d.ts +2 -0
- package/dist/lib/use-is-mobile.d.ts.map +1 -0
- package/dist/lib/use-is-mobile.js +17 -0
- package/dist/lib/use-is-mobile.js.map +1 -0
- package/dist/shark-parity.divergences.d.ts +104 -0
- package/dist/shark-parity.divergences.d.ts.map +1 -0
- package/dist/simples/FacetFilter.d.ts +57 -0
- package/dist/simples/FacetFilter.d.ts.map +1 -0
- package/dist/simples/FacetFilter.js +167 -0
- package/dist/simples/FacetFilter.js.map +1 -0
- package/dist/simples/FieldArray.d.ts +42 -0
- package/dist/simples/FieldArray.d.ts.map +1 -0
- package/dist/simples/FieldArray.js +76 -0
- package/dist/simples/FieldArray.js.map +1 -0
- package/dist/simples/Link.d.ts +38 -0
- package/dist/simples/Link.d.ts.map +1 -0
- package/dist/simples/Link.js +24 -0
- package/dist/simples/Link.js.map +1 -0
- package/dist/simples/accordion.d.ts +8 -0
- package/dist/simples/accordion.d.ts.map +1 -0
- package/dist/simples/accordion.js +78 -0
- package/dist/simples/accordion.js.map +1 -0
- package/dist/simples/action-bar.d.ts +101 -0
- package/dist/simples/action-bar.d.ts.map +1 -0
- package/dist/simples/action-bar.js +195 -0
- package/dist/simples/action-bar.js.map +1 -0
- package/dist/simples/alert-dialog.d.ts +24 -0
- package/dist/simples/alert-dialog.d.ts.map +1 -0
- package/dist/simples/alert-dialog.js +43 -0
- package/dist/simples/alert-dialog.js.map +1 -0
- package/dist/simples/alert.d.ts +36 -0
- package/dist/simples/alert.d.ts.map +1 -0
- package/dist/simples/alert.js +123 -0
- package/dist/simples/alert.js.map +1 -0
- package/dist/simples/avatar.d.ts +37 -0
- package/dist/simples/avatar.d.ts.map +1 -0
- package/dist/simples/avatar.js +133 -0
- package/dist/simples/avatar.js.map +1 -0
- package/dist/simples/badge.d.ts +66 -0
- package/dist/simples/badge.d.ts.map +1 -0
- package/dist/simples/badge.js +138 -0
- package/dist/simples/badge.js.map +1 -0
- package/dist/simples/breadcrumb.d.ts +19 -0
- package/dist/simples/breadcrumb.d.ts.map +1 -0
- package/dist/simples/breadcrumb.js +107 -0
- package/dist/simples/breadcrumb.js.map +1 -0
- package/dist/simples/button-group.d.ts +19 -0
- package/dist/simples/button-group.d.ts.map +1 -0
- package/dist/simples/button-group.js +69 -0
- package/dist/simples/button-group.js.map +1 -0
- package/dist/simples/button.d.ts +148 -0
- package/dist/simples/button.d.ts.map +1 -0
- package/dist/simples/button.js +157 -0
- package/dist/simples/button.js.map +1 -0
- package/dist/simples/calendar.d.ts +45 -0
- package/dist/simples/calendar.d.ts.map +1 -0
- package/dist/simples/calendar.js +342 -0
- package/dist/simples/calendar.js.map +1 -0
- package/dist/simples/card.d.ts +43 -0
- package/dist/simples/card.d.ts.map +1 -0
- package/dist/simples/card.js +151 -0
- package/dist/simples/card.js.map +1 -0
- package/dist/simples/checkbox.d.ts +25 -0
- package/dist/simples/checkbox.d.ts.map +1 -0
- package/dist/simples/checkbox.js +103 -0
- package/dist/simples/checkbox.js.map +1 -0
- package/dist/simples/client-only.d.ts +3 -0
- package/dist/simples/client-only.d.ts.map +1 -0
- package/dist/simples/client-only.js +7 -0
- package/dist/simples/client-only.js.map +1 -0
- package/dist/simples/clipboard.d.ts +11 -0
- package/dist/simples/clipboard.d.ts.map +1 -0
- package/dist/simples/clipboard.js +99 -0
- package/dist/simples/clipboard.js.map +1 -0
- package/dist/simples/collapsible.d.ts +8 -0
- package/dist/simples/collapsible.d.ts.map +1 -0
- package/dist/simples/collapsible.js +79 -0
- package/dist/simples/collapsible.js.map +1 -0
- package/dist/simples/color-picker.d.ts +62 -0
- package/dist/simples/color-picker.d.ts.map +1 -0
- package/dist/simples/color-picker.js +391 -0
- package/dist/simples/color-picker.js.map +1 -0
- package/dist/simples/combobox.d.ts +66 -0
- package/dist/simples/combobox.d.ts.map +1 -0
- package/dist/simples/combobox.js +230 -0
- package/dist/simples/combobox.js.map +1 -0
- package/dist/simples/command.d.ts +45 -0
- package/dist/simples/command.d.ts.map +1 -0
- package/dist/simples/command.js +180 -0
- package/dist/simples/command.js.map +1 -0
- package/dist/simples/data-list.d.ts +25 -0
- package/dist/simples/data-list.d.ts.map +1 -0
- package/dist/simples/data-list.js +62 -0
- package/dist/simples/data-list.js.map +1 -0
- package/dist/simples/date-picker.d.ts +16 -0
- package/dist/simples/date-picker.d.ts.map +1 -0
- package/dist/simples/date-picker.js +117 -0
- package/dist/simples/date-picker.js.map +1 -0
- package/dist/simples/diagnostic.d.ts +138 -0
- package/dist/simples/diagnostic.d.ts.map +1 -0
- package/dist/simples/diagnostic.js +288 -0
- package/dist/simples/diagnostic.js.map +1 -0
- package/dist/simples/dialog.d.ts +100 -0
- package/dist/simples/dialog.d.ts.map +1 -0
- package/dist/simples/dialog.js +267 -0
- package/dist/simples/dialog.js.map +1 -0
- package/dist/simples/download-trigger.d.ts +3 -0
- package/dist/simples/download-trigger.d.ts.map +1 -0
- package/dist/simples/download-trigger.js +7 -0
- package/dist/simples/download-trigger.js.map +1 -0
- package/dist/simples/editable.d.ts +23 -0
- package/dist/simples/editable.d.ts.map +1 -0
- package/dist/simples/editable.js +70 -0
- package/dist/simples/editable.js.map +1 -0
- package/dist/simples/field.d.ts +58 -0
- package/dist/simples/field.d.ts.map +1 -0
- package/dist/simples/field.js +285 -0
- package/dist/simples/field.js.map +1 -0
- package/dist/simples/file-upload.d.ts +19 -0
- package/dist/simples/file-upload.d.ts.map +1 -0
- package/dist/simples/file-upload.js +192 -0
- package/dist/simples/file-upload.js.map +1 -0
- package/dist/simples/float.d.ts +45 -0
- package/dist/simples/float.d.ts.map +1 -0
- package/dist/simples/float.js +40 -0
- package/dist/simples/float.js.map +1 -0
- package/dist/simples/floating-panel.d.ts +25 -0
- package/dist/simples/floating-panel.d.ts.map +1 -0
- package/dist/simples/floating-panel.js +95 -0
- package/dist/simples/floating-panel.js.map +1 -0
- package/dist/simples/highlight.d.ts +6 -0
- package/dist/simples/highlight.d.ts.map +1 -0
- package/dist/simples/highlight.js +24 -0
- package/dist/simples/highlight.js.map +1 -0
- package/dist/simples/hover-card.d.ts +11 -0
- package/dist/simples/hover-card.d.ts.map +1 -0
- package/dist/simples/hover-card.js +83 -0
- package/dist/simples/hover-card.js.map +1 -0
- package/dist/simples/image-cropper.d.ts +17 -0
- package/dist/simples/image-cropper.d.ts.map +1 -0
- package/dist/simples/image-cropper.js +139 -0
- package/dist/simples/image-cropper.js.map +1 -0
- package/dist/simples/input-group.d.ts +116 -0
- package/dist/simples/input-group.d.ts.map +1 -0
- package/dist/simples/input-group.js +245 -0
- package/dist/simples/input-group.js.map +1 -0
- package/dist/simples/input.d.ts +26 -0
- package/dist/simples/input.d.ts.map +1 -0
- package/dist/simples/input.js +60 -0
- package/dist/simples/input.js.map +1 -0
- package/dist/simples/item.d.ts +72 -0
- package/dist/simples/item.d.ts.map +1 -0
- package/dist/simples/item.js +180 -0
- package/dist/simples/item.js.map +1 -0
- package/dist/simples/json-tree-view.d.ts +7 -0
- package/dist/simples/json-tree-view.d.ts.map +1 -0
- package/dist/simples/json-tree-view.js +71 -0
- package/dist/simples/json-tree-view.js.map +1 -0
- package/dist/simples/kbd.d.ts +25 -0
- package/dist/simples/kbd.d.ts.map +1 -0
- package/dist/simples/kbd.js +50 -0
- package/dist/simples/kbd.js.map +1 -0
- package/dist/simples/listbox.d.ts +46 -0
- package/dist/simples/listbox.d.ts.map +1 -0
- package/dist/simples/listbox.js +218 -0
- package/dist/simples/listbox.js.map +1 -0
- package/dist/simples/menu.d.ts +60 -0
- package/dist/simples/menu.d.ts.map +1 -0
- package/dist/simples/menu.js +294 -0
- package/dist/simples/menu.js.map +1 -0
- package/dist/simples/native-select.d.ts +36 -0
- package/dist/simples/native-select.d.ts.map +1 -0
- package/dist/simples/native-select.js +77 -0
- package/dist/simples/native-select.js.map +1 -0
- package/dist/simples/number-input.d.ts +35 -0
- package/dist/simples/number-input.d.ts.map +1 -0
- package/dist/simples/number-input.js +142 -0
- package/dist/simples/number-input.js.map +1 -0
- package/dist/simples/pagination.d.ts +11 -0
- package/dist/simples/pagination.d.ts.map +1 -0
- package/dist/simples/pagination.js +78 -0
- package/dist/simples/pagination.js.map +1 -0
- package/dist/simples/password-input.d.ts +23 -0
- package/dist/simples/password-input.d.ts.map +1 -0
- package/dist/simples/password-input.js +90 -0
- package/dist/simples/password-input.js.map +1 -0
- package/dist/simples/pin-input.d.ts +30 -0
- package/dist/simples/pin-input.d.ts.map +1 -0
- package/dist/simples/pin-input.js +71 -0
- package/dist/simples/pin-input.js.map +1 -0
- package/dist/simples/popover.d.ts +42 -0
- package/dist/simples/popover.d.ts.map +1 -0
- package/dist/simples/popover.js +188 -0
- package/dist/simples/popover.js.map +1 -0
- package/dist/simples/progress.d.ts +23 -0
- package/dist/simples/progress.d.ts.map +1 -0
- package/dist/simples/progress.js +82 -0
- package/dist/simples/progress.js.map +1 -0
- package/dist/simples/prose.d.ts +17 -0
- package/dist/simples/prose.d.ts.map +1 -0
- package/dist/simples/prose.js +18 -0
- package/dist/simples/prose.js.map +1 -0
- package/dist/simples/radio-group.d.ts +23 -0
- package/dist/simples/radio-group.d.ts.map +1 -0
- package/dist/simples/radio-group.js +149 -0
- package/dist/simples/radio-group.js.map +1 -0
- package/dist/simples/rating.d.ts +10 -0
- package/dist/simples/rating.d.ts.map +1 -0
- package/dist/simples/rating.js +81 -0
- package/dist/simples/rating.js.map +1 -0
- package/dist/simples/resizable.d.ts +16 -0
- package/dist/simples/resizable.d.ts.map +1 -0
- package/dist/simples/resizable.js +57 -0
- package/dist/simples/resizable.js.map +1 -0
- package/dist/simples/scroll-area.d.ts +23 -0
- package/dist/simples/scroll-area.d.ts.map +1 -0
- package/dist/simples/scroll-area.js +88 -0
- package/dist/simples/scroll-area.js.map +1 -0
- package/dist/simples/segment-group.d.ts +27 -0
- package/dist/simples/segment-group.d.ts.map +1 -0
- package/dist/simples/segment-group.js +129 -0
- package/dist/simples/segment-group.js.map +1 -0
- package/dist/simples/select.d.ts +34 -0
- package/dist/simples/select.d.ts.map +1 -0
- package/dist/simples/select.js +258 -0
- package/dist/simples/select.js.map +1 -0
- package/dist/simples/separator.d.ts +12 -0
- package/dist/simples/separator.d.ts.map +1 -0
- package/dist/simples/separator.js +27 -0
- package/dist/simples/separator.js.map +1 -0
- package/dist/simples/sheet.d.ts +94 -0
- package/dist/simples/sheet.d.ts.map +1 -0
- package/dist/simples/sheet.js +172 -0
- package/dist/simples/sheet.js.map +1 -0
- package/dist/simples/show.d.ts +10 -0
- package/dist/simples/show.d.ts.map +1 -0
- package/dist/simples/show.js +9 -0
- package/dist/simples/show.js.map +1 -0
- package/dist/simples/skeleton.d.ts +3 -0
- package/dist/simples/skeleton.d.ts.map +1 -0
- package/dist/simples/skeleton.js +23 -0
- package/dist/simples/skeleton.js.map +1 -0
- package/dist/simples/skip-nav.d.ts +54 -0
- package/dist/simples/skip-nav.d.ts.map +1 -0
- package/dist/simples/skip-nav.js +42 -0
- package/dist/simples/skip-nav.js.map +1 -0
- package/dist/simples/slider.d.ts +29 -0
- package/dist/simples/slider.d.ts.map +1 -0
- package/dist/simples/slider.js +189 -0
- package/dist/simples/slider.js.map +1 -0
- package/dist/simples/spinner.d.ts +2 -0
- package/dist/simples/spinner.d.ts.map +1 -0
- package/dist/simples/spinner.js +20 -0
- package/dist/simples/spinner.js.map +1 -0
- package/dist/simples/stat-tile.d.ts +31 -0
- package/dist/simples/stat-tile.d.ts.map +1 -0
- package/dist/simples/stat-tile.js +64 -0
- package/dist/simples/stat-tile.js.map +1 -0
- package/dist/simples/status.d.ts +48 -0
- package/dist/simples/status.d.ts.map +1 -0
- package/dist/simples/status.js +57 -0
- package/dist/simples/status.js.map +1 -0
- package/dist/simples/steps.d.ts +20 -0
- package/dist/simples/steps.d.ts.map +1 -0
- package/dist/simples/steps.js +171 -0
- package/dist/simples/steps.js.map +1 -0
- package/dist/simples/suggestions.d.ts +43 -0
- package/dist/simples/suggestions.d.ts.map +1 -0
- package/dist/simples/suggestions.js +38 -0
- package/dist/simples/suggestions.js.map +1 -0
- package/dist/simples/swatch.d.ts +73 -0
- package/dist/simples/swatch.d.ts.map +1 -0
- package/dist/simples/swatch.js +35 -0
- package/dist/simples/swatch.js.map +1 -0
- package/dist/simples/switch.d.ts +5 -0
- package/dist/simples/switch.d.ts.map +1 -0
- package/dist/simples/switch.js +71 -0
- package/dist/simples/switch.js.map +1 -0
- package/dist/simples/table.d.ts +55 -0
- package/dist/simples/table.d.ts.map +1 -0
- package/dist/simples/table.js +166 -0
- package/dist/simples/table.js.map +1 -0
- package/dist/simples/tabs.d.ts +55 -0
- package/dist/simples/tabs.d.ts.map +1 -0
- package/dist/simples/tabs.js +133 -0
- package/dist/simples/tabs.js.map +1 -0
- package/dist/simples/tags-input.d.ts +64 -0
- package/dist/simples/tags-input.d.ts.map +1 -0
- package/dist/simples/tags-input.js +228 -0
- package/dist/simples/tags-input.js.map +1 -0
- package/dist/simples/textarea.d.ts +6 -0
- package/dist/simples/textarea.d.ts.map +1 -0
- package/dist/simples/textarea.js +34 -0
- package/dist/simples/textarea.js.map +1 -0
- package/dist/simples/toast.d.ts +20 -0
- package/dist/simples/toast.d.ts.map +1 -0
- package/dist/simples/toast.js +127 -0
- package/dist/simples/toast.js.map +1 -0
- package/dist/simples/toggle-group.d.ts +20 -0
- package/dist/simples/toggle-group.d.ts.map +1 -0
- package/dist/simples/toggle-group.js +82 -0
- package/dist/simples/toggle-group.js.map +1 -0
- package/dist/simples/toggle.d.ts +35 -0
- package/dist/simples/toggle.d.ts.map +1 -0
- package/dist/simples/toggle.js +57 -0
- package/dist/simples/toggle.js.map +1 -0
- package/dist/simples/tooltip.d.ts +8 -0
- package/dist/simples/tooltip.d.ts.map +1 -0
- package/dist/simples/tooltip.js +80 -0
- package/dist/simples/tooltip.js.map +1 -0
- package/dist/simples/tour.d.ts +81 -0
- package/dist/simples/tour.d.ts.map +1 -0
- package/dist/simples/tour.js +253 -0
- package/dist/simples/tour.js.map +1 -0
- package/dist/simples/tree-view.d.ts +64 -0
- package/dist/simples/tree-view.d.ts.map +1 -0
- package/dist/simples/tree-view.js +300 -0
- package/dist/simples/tree-view.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/table/data-table-content.d.ts +18 -0
- package/dist/table/data-table-content.d.ts.map +1 -0
- package/dist/table/data-table-content.js +53 -0
- package/dist/table/data-table-content.js.map +1 -0
- package/dist/table/data-table-pagination.d.ts +8 -0
- package/dist/table/data-table-pagination.d.ts.map +1 -0
- package/dist/table/data-table-pagination.js +65 -0
- package/dist/table/data-table-pagination.js.map +1 -0
- package/dist/table/data-table-root.d.ts +10 -0
- package/dist/table/data-table-root.d.ts.map +1 -0
- package/dist/table/data-table-root.js +21 -0
- package/dist/table/data-table-root.js.map +1 -0
- package/dist/table/data-table-toolbar.d.ts +44 -0
- package/dist/table/data-table-toolbar.d.ts.map +1 -0
- package/dist/table/data-table-toolbar.js +87 -0
- package/dist/table/data-table-toolbar.js.map +1 -0
- package/dist/table/select-column.d.ts +16 -0
- package/dist/table/select-column.d.ts.map +1 -0
- package/dist/table/select-column.js +39 -0
- package/dist/table/select-column.js.map +1 -0
- package/dist/table/sortableHeader.d.ts +25 -0
- package/dist/table/sortableHeader.d.ts.map +1 -0
- package/dist/table/sortableHeader.js +43 -0
- package/dist/table/sortableHeader.js.map +1 -0
- package/dist/table/use-data-table.d.ts +40 -0
- package/dist/table/use-data-table.d.ts.map +1 -0
- package/dist/table/use-data-table.js +65 -0
- package/dist/table/use-data-table.js.map +1 -0
- package/dist/table.d.ts +15 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +22 -0
- package/dist/table.js.map +1 -0
- package/dist/theme/KanzoTheme.d.ts +74 -0
- package/dist/theme/KanzoTheme.d.ts.map +1 -0
- package/dist/theme/KanzoTheme.js +61 -0
- package/dist/theme/KanzoTheme.js.map +1 -0
- package/dist/theme/KanzoThemeProvider.d.ts +144 -0
- package/dist/theme/KanzoThemeProvider.d.ts.map +1 -0
- package/dist/theme/KanzoThemeProvider.js +271 -0
- package/dist/theme/KanzoThemeProvider.js.map +1 -0
- package/dist/theme/theme-context.d.ts +146 -0
- package/dist/theme/theme-context.d.ts.map +1 -0
- package/dist/theme/theme-context.js +18 -0
- package/dist/theme/theme-context.js.map +1 -0
- package/dist/theme/theme-script.d.ts +17 -0
- package/dist/theme/theme-script.d.ts.map +1 -0
- package/dist/theme/theme-script.js +16 -0
- package/dist/theme/theme-script.js.map +1 -0
- package/package.json +201 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Questionnaire.js","sources":["../../src/composites/Questionnaire.tsx"],"sourcesContent":["\"use client\";\n\nimport { ark } from \"@ark-ui/react/factory\";\nimport { useFieldsetContext, type UseFieldsetContext } from \"@ark-ui/react/fieldset\";\nimport { useSteps, type UseStepsReturn } from \"@ark-ui/react/steps\";\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\nimport { cn } from \"../lib/cn.js\";\nimport { Button, type ButtonProps } from \"../simples/button.js\";\nimport { Checkbox, CheckboxGroup } from \"../simples/checkbox.js\";\nimport {\n FieldLegend,\n FieldSet,\n FieldSetError,\n FieldSetHelper,\n FieldTitle,\n} from \"../simples/field.js\";\nimport { Input } from \"../simples/input.js\";\nimport { ItemContent, ItemDescription } from \"../simples/item.js\";\nimport { Kbd } from \"../simples/kbd.js\";\nimport {\n RadioGroup,\n RadioGroupCard,\n RadioGroupIndicator,\n RadioGroupText,\n} from \"../simples/radio-group.js\";\nimport { Textarea } from \"../simples/textarea.js\";\n\n/**\n * Questionnaire — one question at a time, with progress, validation and navigation.\n *\n * <Questionnaire onSubmit={…}>\n * <QuestionnaireProgress />\n * <QuestionnaireItem name=\"…\" required>\n * <QuestionnaireTitle /> <QuestionnaireDescription />\n * <QuestionnaireChoices>\n * <QuestionnaireChoice description=\"…\" value=\"…\">…</QuestionnaireChoice>…\n * </QuestionnaireChoices>\n * <QuestionnaireError />\n * </QuestionnaireItem>\n * <QuestionnaireActions>\n * <QuestionnairePrevious /> <QuestionnaireSkip /> <QuestionnaireNext /> <QuestionnaireSubmit />\n * </QuestionnaireActions>\n * </Questionnaire>\n *\n * **The root owns the order, the active question, the answers and the navigation; the parent owns\n * dismissal, persistence and branching.** Which is why there is no `open`, no storage and no\n * `onDismiss` here: a questionnaire that decided when it appears could not be put in a dialog, a\n * sidebar and a page, and every product wants it in a different one.\n *\n * **No navigation state machine is written here.** Ark's `useSteps` already owns the step index,\n * `hasNextStep` / `hasPrevStep`, `percent`, and — the part that is easy to miss — the *guards*:\n * `isStepValid` blocks `STEP.NEXT` and fires `onStepInvalid`, and `isStepSkippable` overrides it.\n * A required question blocking Next and a skippable one not blocking are therefore two callbacks,\n * not two branches. None of Ark's Steps *parts* are used: `getContentProps` declares\n * `role=\"tabpanel\"` and points `aria-labelledby` at a trigger, and this renders no tablist for\n * either to belong to.\n *\n * **A question is a `FieldSet`.** That is what carries the ARIA rather than anything hand-rolled:\n * Ark's fieldset names the group from its own legend, describes it from the helper and error text,\n * and marks the error `aria-live=\"polite\"` — so `QuestionnaireError` announces itself on the group\n * that failed with no `role=\"alert\"` of ours anywhere.\n */\n\nexport type QuestionnaireAnswer = string | string[];\nexport type QuestionnaireAnswers = Record<string, QuestionnaireAnswer>;\n\n/** What the root reads off an item's own props to steer the machine. */\nexport interface QuestionnaireQuestion {\n multiple: boolean;\n name: string;\n required: boolean;\n skippable: boolean;\n}\n\ninterface QuestionnaireContextValue {\n answers: QuestionnaireAnswers;\n /** The index whose Next was refused, or `null`. One at a time: only one question is on screen. */\n blocked: number | null;\n indexOf: (name: string) => number;\n questions: QuestionnaireQuestion[];\n setAnswer: (name: string, answer: QuestionnaireAnswer) => void;\n shortcuts: boolean;\n steps: UseStepsReturn;\n}\n\ninterface QuestionnaireItemContextValue {\n current: boolean;\n index: number;\n /** This item's own declaration — what a part of the caller's can read without the scan. */\n question: QuestionnaireQuestion;\n /** The key printed on a choice's `Kbd`, or `undefined` when shortcuts are off. */\n shortcutOf: (value: string) => string | undefined;\n}\n\nconst QuestionnaireContext = React.createContext<QuestionnaireContextValue | null>(null);\nconst QuestionnaireItemContext = React.createContext<QuestionnaireItemContextValue | null>(null);\n\nexport function useQuestionnaire(): QuestionnaireContextValue {\n const context = React.useContext(QuestionnaireContext);\n if (!context) throw new Error(\"useQuestionnaire must be used inside <Questionnaire>\");\n return context;\n}\n\nexport function useQuestionnaireItem(): QuestionnaireItemContextValue {\n const context = React.useContext(QuestionnaireItemContext);\n if (!context) throw new Error(\"This part must be used inside <QuestionnaireItem>\");\n return context;\n}\n\n/**\n * Every descendant of `node` rendered as `type`, in source order.\n *\n * The root needs the ordered questions and an item needs its ordered choices *before* either\n * renders — the machine is constructed with a `count` and a validity callback keyed by index, and a\n * registration effect would hand it a count of zero on the first pass (`percent` is `step / count`,\n * so that is `NaN` on screen). Reading the elements is synchronous and cannot disagree with what is\n * rendered a moment later.\n *\n * It descends through arrays, fragments and any element's children, which covers a list built with\n * `.map()` and a question wrapped in a `<div>`. What it cannot see through is a *component* of the\n * caller's that returns items, because its children do not exist until it renders.\n */\nfunction collect(node: React.ReactNode, type: React.ElementType): React.ReactElement[] {\n const found: React.ReactElement[] = [];\n const walk = (current: React.ReactNode) => {\n React.Children.forEach(current, (child) => {\n if (!React.isValidElement(child)) return;\n if (child.type === type) {\n found.push(child);\n return;\n }\n walk((child.props as { children?: React.ReactNode }).children);\n });\n };\n walk(node);\n return found;\n}\n\nfunction questionsOf(children: React.ReactNode): QuestionnaireQuestion[] {\n return collect(children, QuestionnaireItem).map((item) => {\n const props = item.props as QuestionnaireItemProps;\n return {\n multiple: props.multiple === true,\n name: props.name,\n required: props.required === true,\n skippable: props.skippable === true,\n };\n });\n}\n\n/**\n * Whether a question lets navigation past it: unasked-for, or answered with a non-blank string or a\n * non-empty selection.\n */\nfunction answered(question: QuestionnaireQuestion | undefined, answers: QuestionnaireAnswers): boolean {\n if (!question?.required) return true;\n const answer = answers[question.name];\n return Array.isArray(answer) ? answer.length > 0 : (answer ?? \"\").trim().length > 0;\n}\n\nexport interface QuestionnaireProps\n extends Omit<React.ComponentProps<typeof ark.form>, \"onSubmit\"> {\n /** Answers to start from — a resumed session, or a question already decided elsewhere. */\n defaultAnswers?: QuestionnaireAnswers;\n /** Which question to open on. Persistence is the parent's, so resuming is this plus the answers. */\n defaultStep?: number;\n onAnswersChange?: (answers: QuestionnaireAnswers) => void;\n onStepChange?: (details: { step: number }) => void;\n /** Called with the whole payload when the last question is submitted and passes validation. */\n onSubmit?: (answers: QuestionnaireAnswers) => void;\n /**\n * Answer the visible question from the keyboard: `1`–`9`, by the choice's position, printed on a\n * `Kbd` beside it so the key is visible rather than folklore.\n *\n * **Opt-in — there is no default.** This registers a `window` keydown listener on bare, unmodified\n * keys, and a design system must not claim those in its host's keymap without being asked. Typing\n * in a text control is ignored; a focused radio or checkbox is not, which is the case that makes\n * the difference (both are `<input>` elements, and treating them as typing kills the shortcuts the\n * moment somebody clicks an option).\n */\n shortcuts?: boolean;\n}\n\nexport function Questionnaire(props: QuestionnaireProps) {\n const {\n children,\n className,\n defaultAnswers,\n defaultStep = 0,\n onAnswersChange,\n onStepChange,\n onSubmit,\n shortcuts = false,\n slot,\n ...rest\n } = props;\n\n const [answers, setAnswers] = React.useState<QuestionnaireAnswers>(defaultAnswers ?? {});\n const [blocked, setBlocked] = React.useState<number | null>(null);\n const questions = questionsOf(children);\n\n const steps = useSteps({\n // `|| 1`: zag divides by `count` for `percent`, so an empty questionnaire would paint `NaN%`.\n count: questions.length || 1,\n defaultStep,\n isStepSkippable: (index) => questions[index]?.skippable === true,\n isStepValid: (index) => answered(questions[index], answers),\n onStepChange: (details) => {\n setBlocked(null);\n onStepChange?.(details);\n },\n onStepInvalid: (details) => setBlocked(details.step),\n });\n\n const setAnswer = (name: string, answer: QuestionnaireAnswer) => {\n const next = { ...answers, [name]: answer };\n setBlocked(null);\n setAnswers(next);\n onAnswersChange?.(next);\n };\n\n // The machine has no \"complete\" event to send, so the last question's guard is spelled out —\n // from the machine's own two accessors, so the POLICY still lives in one place. Sending\n // `STEP.NEXT` instead would leave the form on a step past its last question with every item\n // hidden, which is a state a parent that keeps the form mounted has to undo.\n const submit = () => {\n const index = steps.value;\n if (!steps.isStepSkippable(index) && !steps.isStepValid(index)) {\n setBlocked(index);\n return;\n }\n onSubmit?.(answers);\n };\n\n const context: QuestionnaireContextValue = {\n answers,\n blocked,\n indexOf: (name) => questions.findIndex((question) => question.name === name),\n questions,\n setAnswer,\n shortcuts,\n steps,\n };\n\n return (\n <QuestionnaireContext.Provider value={context}>\n <ark.form\n className={cn(\"flex w-full min-w-0 flex-col gap-4\", className)}\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n {...rest}\n data-slot={slot ?? \"questionnaire\"}\n >\n {children}\n </ark.form>\n </QuestionnaireContext.Provider>\n );\n}\n\n/**\n * How far along we are, as a sentence rather than a bar: *Question 2 of 4*.\n *\n * **ARIA contract.** `role=\"progressbar\"` with `aria-valuemin` / `aria-valuenow` / `aria-valuemax`\n * counting questions, not percent, and `aria-valuetext` carrying the same sentence the eye reads;\n * `aria-live=\"polite\"` announces the move. `progressbar` is a leaf role with no keyboard contract,\n * so nothing is promised here that is not delivered.\n *\n * Ark's `Progress` is not used, and that is not an oversight: zag puts `role=\"progressbar\"` on the\n * *track*, so a progress with no painted track has no role at all. The machine can only be kept by\n * painting the bar the reference does not have.\n *\n * Children override the sentence, which is how it is translated — `useQuestionnaire()` carries the\n * numbers.\n */\nexport const QuestionnaireProgress = (props: React.ComponentProps<typeof ark.div>) => {\n const { children, className, slot, ...rest } = props;\n const { steps } = useQuestionnaire();\n\n const position = steps.value + 1;\n const reading = `Question ${position} of ${steps.count}`;\n\n return (\n <ark.div\n aria-live=\"polite\"\n aria-valuemax={steps.count}\n aria-valuemin={1}\n aria-valuenow={position}\n aria-valuetext={reading}\n // `min-w-[14ch]` and `min-h-[1lh]` reserve the line so the block below it does not shift as\n // the reading widens or while a translation is absent.\n className={cn(\n \"min-h-[1lh] w-fit min-w-[14ch]\",\n \"font-medium text-muted-foreground text-xs tabular-nums\",\n className,\n )}\n role=\"progressbar\"\n {...rest}\n data-slot={slot ?? \"questionnaire-progress\"}\n >\n {children ?? reading}\n </ark.div>\n );\n};\n\nexport interface QuestionnaireItemProps extends React.ComponentProps<typeof FieldSet> {\n /** Accumulate several answers instead of one — a `CheckboxGroup` rather than a `RadioGroup`. */\n multiple?: boolean;\n /** The key this question's answer is filed under. Unique within the questionnaire. */\n name: string;\n /** Refuse Next until it is answered. */\n required?: boolean;\n /** Offer a Skip, and let Next through unanswered. Overrides `required`, the way Ark's guard does. */\n skippable?: boolean;\n}\n\nexport function QuestionnaireItem(props: QuestionnaireItemProps) {\n const { children, className, multiple, name, required, skippable, slot, ...rest } = props;\n const { answers, blocked, indexOf, setAnswer, shortcuts, steps } = useQuestionnaire();\n\n const index = indexOf(name);\n const current = index === steps.value;\n const question: QuestionnaireQuestion = {\n multiple: multiple === true,\n name,\n required: required === true,\n skippable: skippable === true,\n };\n const values = choiceValues(children);\n\n // No dependency array on purpose: the handler reads the answers and the choice list of THIS\n // render, and a listener held across an answer would toggle against a stale array. One\n // add/remove per render is cheaper than the ref dance that would avoid it.\n React.useEffect(() => {\n if (!current || !shortcuts || values.length === 0) return;\n\n const onKey = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey) return;\n const active = document.activeElement;\n // A radio and a checkbox are `<input>` too, and Ark keeps focus on the hidden one — so\n // \"focus is in an input\" would switch the shortcuts off as soon as a choice was clicked.\n const typing =\n active instanceof HTMLElement &&\n (active.isContentEditable ||\n active.tagName === \"TEXTAREA\" ||\n (active instanceof HTMLInputElement &&\n active.type !== \"checkbox\" &&\n active.type !== \"radio\"));\n if (typing) return;\n\n const hit = values[Number(event.key) - 1];\n if (hit === undefined) return;\n event.preventDefault();\n\n if (!question.multiple) {\n setAnswer(name, hit);\n return;\n }\n const chosen = answers[name];\n const list = Array.isArray(chosen) ? chosen : [];\n setAnswer(\n name,\n list.includes(hit) ? list.filter((value) => value !== hit) : [...list, hit],\n );\n };\n\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n });\n\n const item: QuestionnaireItemContextValue = {\n current,\n index,\n question,\n shortcutOf: (value) => {\n const position = values.indexOf(value);\n // Only the first nine: `10` is two keystrokes and there is no chord worth inventing for a\n // tenth option — a question with that many is a `Select`, not a questionnaire.\n return shortcuts && position >= 0 && position < 9 ? String(position + 1) : undefined;\n },\n };\n\n return (\n <QuestionnaireItemContext.Provider value={item}>\n <FieldSet\n aria-current={current ? \"step\" : undefined}\n className={cn(\"min-w-0 gap-4\", className)}\n hidden={!current}\n invalid={blocked === index}\n {...rest}\n slot={slot ?? \"questionnaire-item\"}\n >\n {children}\n </FieldSet>\n </QuestionnaireItemContext.Provider>\n );\n}\n\n/** One item's choices in source order — which is also the order the shortcut keys are handed out. */\nfunction choiceValues(children: React.ReactNode): string[] {\n return collect(children, QuestionnaireChoice).map(\n (choice) => (choice.props as QuestionnaireChoiceProps).value,\n );\n}\n\n/**\n * The question itself. A `<legend>`, which is what names the group Ark builds around it.\n *\n * The margin is not the fieldset's `gap`: a `<legend>` is rendered into the fieldset's border and\n * is not a flex item, so `gap` never reaches it. It sits flush against its own description and\n * opens up to `mb-4` when there is none — delete either half and the two spellings of this\n * question's heading stop agreeing.\n */\nexport const QuestionnaireTitle = (props: React.ComponentProps<typeof FieldLegend>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <FieldLegend\n className={cn(\n \"mb-0 font-heading text-pretty leading-snug\",\n \"[&:not(:has(~[data-slot=questionnaire-description]))]:mb-4\",\n className,\n )}\n {...rest}\n slot={slot ?? \"questionnaire-title\"}\n />\n );\n};\n\nexport const QuestionnaireDescription = (props: React.ComponentProps<typeof FieldSetHelper>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <FieldSetHelper\n className={cn(\"text-pretty\", className)}\n {...rest}\n slot={slot ?? \"questionnaire-description\"}\n />\n );\n};\n\n/**\n * Why Next was refused. Renders only while the fieldset is invalid, which is Ark's rule and not\n * ours — and Ark gives it `aria-live=\"polite\"` and hangs it off the group's `aria-describedby`.\n */\nexport const QuestionnaireError = (props: React.ComponentProps<typeof FieldSetError>) => {\n const { children, slot, ...rest } = props;\n\n return (\n <FieldSetError {...rest} slot={slot ?? \"questionnaire-error\"}>\n {children ?? \"Answer this to continue.\"}\n </FieldSetError>\n );\n};\n\n/**\n * The item's options — a radio group, or a checkbox group where the item is `multiple`.\n *\n * One component and not two, because the choice between them is the item's own `multiple` and a\n * caller who had to pick would be stating it twice.\n */\nexport type QuestionnaireChoicesProps = Omit<\n React.ComponentProps<typeof ark.div>,\n \"defaultValue\" | \"value\"\n>;\n\nexport const QuestionnaireChoices = (props: QuestionnaireChoicesProps) => {\n const { children, className, slot, ...rest } = props;\n const { answers, setAnswer } = useQuestionnaire();\n const { question } = useQuestionnaireItem();\n const { multiple, name } = question;\n const answer = answers[name];\n\n if (multiple) {\n return (\n <CheckboxGroup\n className={cn(\"gap-2\", className)}\n onValueChange={(value) => setAnswer(name, value)}\n value={Array.isArray(answer) ? answer : []}\n {...rest}\n slot={slot ?? \"questionnaire-choices\"}\n >\n {children}\n </CheckboxGroup>\n );\n }\n\n return (\n <RadioGroup\n className={cn(\"gap-2\", className)}\n onValueChange={(details) => setAnswer(name, details.value ?? \"\")}\n value={typeof answer === \"string\" ? answer : null}\n {...rest}\n slot={slot ?? \"questionnaire-choices\"}\n >\n {children}\n </RadioGroup>\n );\n};\n\n/**\n * The card a choice is drawn as, in one recipe for both arms.\n *\n * `shell` is the border-and-state half. `RadioGroupCard` already *is* that shell, so the single\n * arm inherits it and only the multiple arm asks for it — a `Checkbox` has no card of its own. The\n * values are `simples/radio-group.tsx`'s, which is where the ΔE figures behind the hover and\n * selected fills are recorded and dated; they are restated here and decided there.\n *\n * **The control sits at the end**, which is where the reference puts it: a choice card is read as a\n * sentence and answered at the margin, and a control at the start makes a column of radios with\n * text hanging off them. `Checkbox` renders its control before its children and has to, so the\n * multiple arm reorders in CSS; the single arm simply writes the indicator last.\n *\n * `min-h-[44px]` is in pixels, not `rem`: WCAG 2.5.8 states its bar in CSS pixels, and every `rem`\n * here resolves against a root the density axis sets (14px at compact), so a `rem` floor is the\n * failure dressed as the fix.\n */\nconst questionnaireChoiceVariants = tv({\n base: [\n \"group/questionnaire-choice\",\n \"relative flex w-full min-h-[44px] items-start gap-3\",\n \"cursor-pointer select-none rounded-lg px-3.5 py-3\",\n \"text-start text-sm\",\n \"transition-colors\",\n // The control is 16px against a 20px title line, so it drops 2px to sit on that line rather\n // than on the top of a two-line stack.\n \"*:data-[slot=checkbox-control]:mt-0.5 *:data-[slot=radio-group-indicator]:mt-0.5\",\n ],\n variants: {\n shell: {\n true: [\n \"border border-input bg-transparent\",\n \"hover:bg-foreground/14\",\n \"data-[state=checked]:border-primary data-[state=checked]:bg-primary/17\",\n \"data-focus-visible:border-primary data-focus-visible:ring-[3px] data-focus-visible:ring-ring\",\n \"data-invalid:border-destructive\",\n \"data-disabled:pointer-events-none data-disabled:opacity-64\",\n // See the docblock: the control is the last thing in the row, and `Checkbox` renders it\n // first.\n \"*:data-[slot=checkbox-control]:order-last\",\n ],\n },\n },\n defaultVariants: {\n shell: false,\n },\n});\n\n// `ark.label`, not `ark.div`: both arms render the control's own `<label>` as the card, so that is\n// the element a caller's props and `ref` land on.\nexport interface QuestionnaireChoiceProps\n extends Omit<React.ComponentProps<typeof ark.label>, \"value\"> {\n /**\n * The muted second line — what the option means, rather than what it is called.\n *\n * A `string` and not a node, following `CardHeader`'s `title` / `description`: the label is\n * already this part's children, and the control has to *name* itself off an element this\n * component renders, so a second children slot would have to be found by splitting them.\n */\n description?: string;\n /** What this choice contributes to the answer. */\n value: string;\n}\n\n/**\n * One option, drawn as a card: the label and its description, then the shortcut key as a chip,\n * then the control at the margin.\n *\n * The card is the control's own `<label>` in both arms, so the whole of it is the target. What\n * differs is where the accessible name is read from: Ark points a radio's hidden input at its\n * `ItemText`, and a checkbox's at the label part we do not render — so the multiple arm names\n * itself explicitly off the title instead.\n *\n * `Item` is not the card here, deliberately. It declares `role=\"listitem\"`, which inside a\n * `radiogroup` is a list item with no list, and `asChild`-ing it onto the control would put that\n * role on the control itself — the failure `Item`'s own docblock describes.\n */\nexport const QuestionnaireChoice = (props: QuestionnaireChoiceProps) => {\n const { children, className, description, slot, value, ...rest } = props;\n const { question, shortcutOf } = useQuestionnaireItem();\n const key = shortcutOf(value);\n const labelId = React.useId();\n\n const body = (\n <>\n <ItemContent slot=\"questionnaire-choice-label\">\n {question.multiple ? (\n <FieldTitle id={labelId} slot=\"questionnaire-choice-title\">\n {children}\n </FieldTitle>\n ) : (\n <RadioGroupText slot=\"questionnaire-choice-title\">{children}</RadioGroupText>\n )}\n\n {description ? (\n <ItemDescription slot=\"questionnaire-choice-description\">{description}</ItemDescription>\n ) : null}\n </ItemContent>\n\n {/* No fill of its own. It used to paint `bg-background`, which is a second opinion about\n what is behind the card — on a selected one it punched a page-coloured hole through the\n tint. Transparent, it takes whatever the card is showing. */}\n {key ? (\n <Kbd\n aria-hidden\n className=\"mt-0.5 size-5 shrink-0 rounded-md bg-transparent text-muted-foreground\"\n variant=\"outline\"\n >\n {key}\n </Kbd>\n ) : null}\n </>\n );\n\n if (question.multiple) {\n return (\n <Checkbox\n aria-labelledby={labelId}\n className={cn(questionnaireChoiceVariants({ shell: true }), className)}\n value={value}\n {...rest}\n slot={slot ?? \"questionnaire-choice\"}\n >\n {body}\n </Checkbox>\n );\n }\n\n return (\n <RadioGroupCard\n className={cn(questionnaireChoiceVariants(), className)}\n value={value}\n {...rest}\n slot={slot ?? \"questionnaire-choice\"}\n >\n {body}\n <RadioGroupIndicator />\n </RadioGroupCard>\n );\n};\n\n/**\n * A freeform answer following a list of choices is the last row of that list, not a field below it\n * — so it closes the item's `gap-4` back down to the `gap-2` between the cards.\n */\nconst freeform = \"[[data-slot=questionnaire-choices]+&]:-mt-2\";\n\n/**\n * The freeform arm — a single-line answer filed under the item's name.\n *\n * A fieldset's legend names the GROUP, not a control inside it, so the input is pointed at the\n * legend explicitly. Without this the question is announced when focus enters the group and the\n * control itself is unnamed.\n */\nexport const QuestionnaireInput = (props: React.ComponentProps<typeof Input>) => {\n const { className, slot, ...rest } = props;\n const { answers, setAnswer } = useQuestionnaire();\n const { question } = useQuestionnaireItem();\n const fieldset: UseFieldsetContext | undefined = useFieldsetContext();\n const answer = answers[question.name];\n\n return (\n <Input\n aria-labelledby={fieldset?.ids.legend}\n className={cn(freeform, className)}\n onChange={(event) => setAnswer(question.name, event.target.value)}\n value={typeof answer === \"string\" ? answer : \"\"}\n {...rest}\n slot={slot ?? \"questionnaire-input\"}\n />\n );\n};\n\n/** The same binding over a `Textarea`, for the answer that runs to a paragraph. */\nexport const QuestionnaireTextarea = (props: React.ComponentProps<typeof Textarea>) => {\n const { className, slot, ...rest } = props;\n const { answers, setAnswer } = useQuestionnaire();\n const { question } = useQuestionnaireItem();\n const fieldset: UseFieldsetContext | undefined = useFieldsetContext();\n const answer = answers[question.name];\n\n return (\n <Textarea\n aria-labelledby={fieldset?.ids.legend}\n className={cn(freeform, className)}\n onChange={(event) => setAnswer(question.name, event.target.value)}\n value={typeof answer === \"string\" ? answer : \"\"}\n {...rest}\n slot={slot ?? \"questionnaire-textarea\"}\n />\n );\n};\n\nexport interface QuestionnaireActionsProps\n extends Omit<React.ComponentProps<typeof ark.div>, \"aria-label\" | \"aria-labelledby\"> {\n /** Names the group, the way a run of independent controls needs naming. */\n \"aria-label\"?: string;\n}\n\n/**\n * The navigation row: Back at the start, Skip and Next/Submit at the end.\n *\n * Three fixed columns rather than a `ButtonGroup`, and that is the point of it — `ButtonGroup`\n * collapses the inner radii and overlaps its children by a pixel, which is right for a cluster\n * acting on one thing and wrong for two ends of a journey. The columns hold their places while\n * Back and Skip come and go, so nothing slides sideways between questions.\n */\nexport const QuestionnaireActions = (props: QuestionnaireActionsProps) => {\n const { \"aria-label\": label = \"Questionnaire navigation\", className, slot, ...rest } = props;\n\n return (\n <ark.div\n aria-label={label}\n className={cn(\n \"grid w-full grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-2\",\n className,\n )}\n role=\"group\"\n {...rest}\n data-slot={slot ?? \"questionnaire-actions\"}\n />\n );\n};\n\n/**\n * Drawn only where there is somewhere to go back to.\n *\n * Absent rather than disabled: the first question's row is one solid Next and nothing else, and a\n * greyed control that has never been available says nothing a reader can use.\n */\nexport const QuestionnairePrevious = (props: ButtonProps) => {\n const { children, className, slot, ...rest } = props;\n const { steps } = useQuestionnaire();\n\n if (!steps.hasPrevStep) return null;\n\n return (\n <Button\n className={cn(\"col-start-1 row-start-1 justify-self-start\", className)}\n onClick={() => steps.goToPrevStep()}\n variant=\"outline\"\n {...rest}\n slot={slot ?? \"questionnaire-previous\"}\n >\n {children ?? \"Back\"}\n </Button>\n );\n};\n\n/** Drawn only where the visible question declared itself skippable — elsewhere it would lie. */\nexport const QuestionnaireSkip = (props: ButtonProps) => {\n const { children, className, slot, ...rest } = props;\n const { questions, steps } = useQuestionnaire();\n\n if (questions[steps.value]?.skippable !== true) return null;\n\n return (\n <Button\n className={cn(\"col-start-2 row-start-1 justify-self-end\", className)}\n onClick={() => steps.goToNextStep()}\n variant=\"ghost\"\n {...rest}\n slot={slot ?? \"questionnaire-skip\"}\n >\n {children ?? \"Skip\"}\n </Button>\n );\n};\n\n/**\n * Advance, or reveal why not.\n *\n * Never disabled on an unanswered question: a disabled control says nothing about what is missing,\n * and the machine's `onStepInvalid` is what puts the message on screen. It stands down on the last\n * question so that `QuestionnaireSubmit` can take the same place in the cluster.\n */\nexport const QuestionnaireNext = (props: ButtonProps) => {\n const { children, className, slot, ...rest } = props;\n const { steps } = useQuestionnaire();\n\n if (steps.value >= steps.count - 1) return null;\n\n return (\n <Button\n className={cn(\"col-start-3 row-start-1 justify-self-end\", className)}\n onClick={() => steps.goToNextStep()}\n {...rest}\n slot={slot ?? \"questionnaire-next\"}\n >\n {children ?? \"Next\"}\n </Button>\n );\n};\n\n/** The other half of that pair: drawn only on the last question, and it submits the form. */\nexport const QuestionnaireSubmit = (props: ButtonProps) => {\n const { children, className, slot, ...rest } = props;\n const { steps } = useQuestionnaire();\n\n if (steps.value < steps.count - 1) return null;\n\n return (\n <Button\n className={cn(\"col-start-3 row-start-1 justify-self-end\", className)}\n type=\"submit\"\n {...rest}\n slot={slot ?? \"questionnaire-submit\"}\n >\n {children ?? \"Submit\"}\n </Button>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA+FA;AAGO;AACL;AACA;AACA;AACF;AAEO;AACL;AACA;AACA;AACF;AAeA;AACE;AAEE;AACE;AACA;AACE;AACA;AAAA;AAEF;AAA6D;AAAA;AAC9D;AAEH;AAEF;AAEA;AACE;AACE;AACA;AAAO;AACwB;AACjB;AACiB;AACE;AAAA;AAGrC;AAMA;AACE;AACA;AACA;AACF;AAyBO;AACL;AAAM;AACJ;AACA;AACA;AACc;AACd;AACA;AACA;AACY;AACZ;AACG;AAOkB;AAAA;AAEM;AAC3B;;AAC4B;AAAgC;AAAA;AACF;AAExD;AACe;AACjB;AACmD;AAInD;AACA;AAEkB;AAQlB;AACA;AACE;AACA;AAAA;AAEF;AAAW;AAG8B;AACzC;AACA;AAC2E;AAC3E;AACA;AACA;AACA;AAGF;AAEI;AAAK;AAAJ;AAC8D;AACnD;AAER;AACA;AACF;AACI;AACe;AAElB;AAAA;AAIT;AAiBO;AACL;AAMA;AACE;AAAK;AAAJ;AACW;AACW;AACN;AACA;AACC;AAGL;AACT;AACA;AACA;AAAA;AAEG;AACD;AACe;AAEN;AAAA;AAGnB;AAaO;AACL;AAKwC;AACf;AACvB;AACuB;AACE;AAO3B;AACE;AAEA;AACE;AACA;AAUA;AAEA;AACA;AAGA;AACE;AACA;AAAA;AAEF;AAEA;AAAA;AACE;AAC0E;AAAA;AAI9E;AACwD;AAG1D;AAA4C;AAC1C;AACA;AACA;AAEE;AAGA;AAA2E;AAC7E;AAGF;AAEI;AAAC;AAAA;AACkC;AACO;AAC/B;AACY;AACjB;AACU;AAEb;AAAA;AAIT;AAGA;AACE;AAA8C;AACW;AAE3D;AAUO;AACL;AAEA;AACE;AAAC;AAAA;AACY;AACT;AACA;AACA;AAAA;AAEE;AACU;AAAA;AAGpB;AAGE;AAEA;AACE;AAAC;AAAA;AACuC;AAClC;AACU;AAAA;AAGpB;AAOE;AAEA;AAKF;AAcE;AAMA;AAEI;AAAC;AAAA;AACiC;AACe;AACP;AACpC;AACU;AAEb;AAAA;AAML;AAAC;AAAA;AACiC;AAC+B;AAClB;AACzC;AACU;AAEb;AAAA;AAGP;AAmBuC;AAC/B;AACJ;AACA;AACA;AACA;AACA;AAAA;AAAA;AAGA;AAAA;AAEQ;AACD;AACC;AACJ;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAGA;AAAA;AACF;AACF;AAEe;AACR;AAEX;AAgCE;AAOI;AACG;AAK6D;AAK1D;AACN;AAME;AAAC;AAAA;AACY;AACD;AACF;AAEP;AAAA;AAED;AAIR;AAEI;AAAC;AAAA;AACkB;AACoD;AACrE;AACI;AACU;AAEb;AAAA;AAML;AAAC;AAAA;AACuD;AACtD;AACI;AACU;AAEb;AAAA;AACoB;AAAA;AAAA;AAG3B;AAgBE;AAMA;AACE;AAAC;AAAA;AACgC;AACE;AAC+B;AACnB;AACzC;AACU;AAAA;AAGpB;AAIE;AAMA;AACE;AAAC;AAAA;AACgC;AACE;AAC+B;AACnB;AACzC;AACU;AAAA;AAGpB;AAiBE;AAEA;AACE;AAAK;AAAJ;AACa;AACD;AACT;AACA;AAAA;AAEG;AACD;AACe;AAAA;AAGzB;AASE;AAGA;AAGE;AAAC;AAAA;AACsE;AAChD;AACb;AACJ;AACU;AAED;AAAA;AAGnB;;AAIE;AAGA;AAGE;AAAC;AAAA;AACoE;AAC9C;AACb;AACJ;AACU;AAED;AAAA;AAGnB;AAUE;AAGA;AAGE;AAAC;AAAA;AACoE;AAC9C;AACjB;AACU;AAED;AAAA;AAGnB;AAIE;AAGA;AAGE;AAAC;AAAA;AACoE;AAC9D;AACD;AACU;AAED;AAAA;AAGnB;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
import { Avatar } from '../simples/avatar.js';
|
|
3
|
+
export interface SidebarIdentityProps extends ComponentProps<"div"> {
|
|
4
|
+
/** Gate the `group-data-[collapsible=icon]` variants (false for the portaled menu header). */
|
|
5
|
+
responsive?: boolean;
|
|
6
|
+
/** Whether the sidebar is currently collapsed (drives the avatar's semantic size). */
|
|
7
|
+
collapsed?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* SidebarIdentity — the collapse-aware "avatar/icon + name + subtitle" block a sidebar header or
|
|
11
|
+
* footer is built from. When `responsive`, the text column and gap
|
|
12
|
+
* collapse in the sidebar's icon state and the avatar grows to fill the 32px square (its
|
|
13
|
+
* `data-size` grows to `md` too, so badges/icons stay correctly scaled).
|
|
14
|
+
*
|
|
15
|
+
* The content is CHILDREN, not an `IdentityData` object prop. A record of `ReactNode`s
|
|
16
|
+
* (`label`/`description`/`avatarUrl`/`fallback`/`icon`) is a layout tree written as an
|
|
17
|
+
* attribute: you cannot reorder it, wrap a region, spread props onto one, or use `asChild` —
|
|
18
|
+
* and the avatar could only ever be an `<img src>` plus initials, never a badge or a status
|
|
19
|
+
* dot. Composition gives all of that back, and matches how every simple in this library
|
|
20
|
+
* already works — `CardHeader`, not `<Card header={…} />`.
|
|
21
|
+
*
|
|
22
|
+
* <SidebarIdentity responsive collapsed={collapsed}>
|
|
23
|
+
* <SidebarIdentityAvatar>
|
|
24
|
+
* <AvatarImage src={url} alt={name} />
|
|
25
|
+
* <AvatarFallback>{initials}</AvatarFallback>
|
|
26
|
+
* </SidebarIdentityAvatar>
|
|
27
|
+
* <SidebarIdentityText>
|
|
28
|
+
* <SidebarIdentityLabel>{name}</SidebarIdentityLabel>
|
|
29
|
+
* <SidebarIdentityDescription>{email}</SidebarIdentityDescription>
|
|
30
|
+
* </SidebarIdentityText>
|
|
31
|
+
* </SidebarIdentity>
|
|
32
|
+
*/
|
|
33
|
+
export declare function SidebarIdentity({ responsive, collapsed, className, children, slot, ...rest }: SidebarIdentityProps): import("react").JSX.Element;
|
|
34
|
+
export declare namespace SidebarIdentity {
|
|
35
|
+
var displayName: string;
|
|
36
|
+
}
|
|
37
|
+
/** A square icon tile instead of an avatar (instances / workspaces / orgs). */
|
|
38
|
+
export declare function SidebarIdentityIcon({ className, slot, ...rest }: ComponentProps<"div">): import("react").JSX.Element;
|
|
39
|
+
export declare namespace SidebarIdentityIcon {
|
|
40
|
+
var displayName: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The avatar (people). Takes `AvatarImage` / `AvatarFallback` as children; its `size` is
|
|
44
|
+
* derived from the sidebar state, so it is not overridable here.
|
|
45
|
+
*/
|
|
46
|
+
export declare function SidebarIdentityAvatar({ slot, ...rest }: Omit<ComponentProps<typeof Avatar>, "size">): import("react").JSX.Element;
|
|
47
|
+
export declare namespace SidebarIdentityAvatar {
|
|
48
|
+
var displayName: string;
|
|
49
|
+
}
|
|
50
|
+
/** The text column. Hidden entirely in the sidebar's icon state when `responsive`. */
|
|
51
|
+
export declare function SidebarIdentityText({ className, slot, ...rest }: ComponentProps<"div">): import("react").JSX.Element;
|
|
52
|
+
export declare namespace SidebarIdentityText {
|
|
53
|
+
var displayName: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function SidebarIdentityLabel({ className, slot, ...rest }: ComponentProps<"span">): import("react").JSX.Element;
|
|
56
|
+
export declare namespace SidebarIdentityLabel {
|
|
57
|
+
var displayName: string;
|
|
58
|
+
}
|
|
59
|
+
export declare function SidebarIdentityDescription({ className, slot, ...rest }: ComponentProps<"span">): import("react").JSX.Element;
|
|
60
|
+
export declare namespace SidebarIdentityDescription {
|
|
61
|
+
var displayName: string;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=SidebarIdentity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SidebarIdentity.d.ts","sourceRoot":"","sources":["../../src/composites/SidebarIdentity.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAc9C,MAAM,WAAW,oBAAqB,SAAQ,cAAc,CAAC,KAAK,CAAC;IACjE,8FAA8F;IAC9F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,UAAkB,EAClB,SAAiB,EACjB,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,GAAG,IAAI,EACR,EAAE,oBAAoB,+BAmBtB;yBA1Be,eAAe;;;AA6B/B,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAatF;yBAbe,mBAAmB;;;AAgBnC;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,MAAM,CAAC,+BAGnG;yBAHe,qBAAqB;;;AAMrC,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,+BAatF;yBAbe,mBAAmB;;;AAgBnC,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,MAAM,CAAC,+BAQxF;yBARe,oBAAoB;;;AAWpC,wBAAgB,0BAA0B,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,MAAM,CAAC,+BAQ9F;yBARe,0BAA0B"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
3
|
+
import { useContext as s, createContext as c } from "react";
|
|
4
|
+
import { Avatar as p } from "../simples/avatar.js";
|
|
5
|
+
import { cn as n } from "../lib/cn.js";
|
|
6
|
+
const d = c({
|
|
7
|
+
responsive: !1,
|
|
8
|
+
collapsed: !1
|
|
9
|
+
});
|
|
10
|
+
function m({
|
|
11
|
+
responsive: e = !1,
|
|
12
|
+
collapsed: t = !1,
|
|
13
|
+
className: i,
|
|
14
|
+
children: a,
|
|
15
|
+
slot: o,
|
|
16
|
+
...l
|
|
17
|
+
}) {
|
|
18
|
+
return /* @__PURE__ */ r(d.Provider, { value: { responsive: e, collapsed: t }, children: /* @__PURE__ */ r(
|
|
19
|
+
"div",
|
|
20
|
+
{
|
|
21
|
+
className: n(
|
|
22
|
+
"flex min-w-0 flex-1 items-center gap-2 text-start",
|
|
23
|
+
// Collapsed, the avatar/tile (32px) is slightly larger than the ghost button's content
|
|
24
|
+
// box (32px minus its 1px transparent border), so `justify-start` would left-align it and
|
|
25
|
+
// clip the right edge — reading as off-centre. Centre it so the overflow is symmetric.
|
|
26
|
+
e && "group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:gap-0",
|
|
27
|
+
i
|
|
28
|
+
),
|
|
29
|
+
...l,
|
|
30
|
+
"data-slot": o ?? "sidebar-identity",
|
|
31
|
+
children: a
|
|
32
|
+
}
|
|
33
|
+
) });
|
|
34
|
+
}
|
|
35
|
+
m.displayName = "SidebarIdentity";
|
|
36
|
+
function y({ className: e, slot: t, ...i }) {
|
|
37
|
+
const { responsive: a } = s(d);
|
|
38
|
+
return /* @__PURE__ */ r(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
className: n(
|
|
42
|
+
"flex size-6 shrink-0 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground [&_svg]:size-4",
|
|
43
|
+
a && "group-data-[collapsible=icon]:size-8",
|
|
44
|
+
e
|
|
45
|
+
),
|
|
46
|
+
...i,
|
|
47
|
+
"data-slot": t ?? "sidebar-identity-icon"
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
y.displayName = "SidebarIdentityIcon";
|
|
52
|
+
function b({ slot: e, ...t }) {
|
|
53
|
+
const { responsive: i, collapsed: a } = s(d);
|
|
54
|
+
return /* @__PURE__ */ r(p, { size: i && a ? "md" : "sm", ...t, slot: e ?? "sidebar-identity-avatar" });
|
|
55
|
+
}
|
|
56
|
+
b.displayName = "SidebarIdentityAvatar";
|
|
57
|
+
function u({ className: e, slot: t, ...i }) {
|
|
58
|
+
const { responsive: a } = s(d);
|
|
59
|
+
return /* @__PURE__ */ r(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
className: n(
|
|
63
|
+
"flex min-w-0 flex-col leading-tight",
|
|
64
|
+
a && "group-data-[collapsible=icon]:hidden",
|
|
65
|
+
e
|
|
66
|
+
),
|
|
67
|
+
...i,
|
|
68
|
+
"data-slot": t ?? "sidebar-identity-text"
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
u.displayName = "SidebarIdentityText";
|
|
73
|
+
function f({ className: e, slot: t, ...i }) {
|
|
74
|
+
return /* @__PURE__ */ r(
|
|
75
|
+
"span",
|
|
76
|
+
{
|
|
77
|
+
className: n("truncate text-sm font-medium", e),
|
|
78
|
+
...i,
|
|
79
|
+
"data-slot": t ?? "sidebar-identity-label"
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
f.displayName = "SidebarIdentityLabel";
|
|
84
|
+
function x({ className: e, slot: t, ...i }) {
|
|
85
|
+
return /* @__PURE__ */ r(
|
|
86
|
+
"span",
|
|
87
|
+
{
|
|
88
|
+
className: n("truncate text-xs text-muted-foreground", e),
|
|
89
|
+
...i,
|
|
90
|
+
"data-slot": t ?? "sidebar-identity-description"
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
x.displayName = "SidebarIdentityDescription";
|
|
95
|
+
export {
|
|
96
|
+
m as SidebarIdentity,
|
|
97
|
+
b as SidebarIdentityAvatar,
|
|
98
|
+
x as SidebarIdentityDescription,
|
|
99
|
+
y as SidebarIdentityIcon,
|
|
100
|
+
f as SidebarIdentityLabel,
|
|
101
|
+
u as SidebarIdentityText
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=SidebarIdentity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SidebarIdentity.js","sources":["../../src/composites/SidebarIdentity.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, type ComponentProps } from \"react\";\nimport { Avatar } from \"../simples/avatar.js\";\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * `responsive`/`collapsed` are behaviour, so they stay props on the root — but the parts need\n * them too (the avatar's semantic size, the text column's collapse). Context carries them\n * down instead of the caller repeating them on every part. This context is why the file is\n * `\"use client\"`.\n */\nconst IdentityCtx = createContext<{ responsive: boolean; collapsed: boolean }>({\n responsive: false,\n collapsed: false,\n});\n\nexport interface SidebarIdentityProps extends ComponentProps<\"div\"> {\n /** Gate the `group-data-[collapsible=icon]` variants (false for the portaled menu header). */\n responsive?: boolean;\n /** Whether the sidebar is currently collapsed (drives the avatar's semantic size). */\n collapsed?: boolean;\n}\n\n/**\n * SidebarIdentity — the collapse-aware \"avatar/icon + name + subtitle\" block a sidebar header or\n * footer is built from. When `responsive`, the text column and gap\n * collapse in the sidebar's icon state and the avatar grows to fill the 32px square (its\n * `data-size` grows to `md` too, so badges/icons stay correctly scaled).\n *\n * The content is CHILDREN, not an `IdentityData` object prop. A record of `ReactNode`s\n * (`label`/`description`/`avatarUrl`/`fallback`/`icon`) is a layout tree written as an\n * attribute: you cannot reorder it, wrap a region, spread props onto one, or use `asChild` —\n * and the avatar could only ever be an `<img src>` plus initials, never a badge or a status\n * dot. Composition gives all of that back, and matches how every simple in this library\n * already works — `CardHeader`, not `<Card header={…} />`.\n *\n * <SidebarIdentity responsive collapsed={collapsed}>\n * <SidebarIdentityAvatar>\n * <AvatarImage src={url} alt={name} />\n * <AvatarFallback>{initials}</AvatarFallback>\n * </SidebarIdentityAvatar>\n * <SidebarIdentityText>\n * <SidebarIdentityLabel>{name}</SidebarIdentityLabel>\n * <SidebarIdentityDescription>{email}</SidebarIdentityDescription>\n * </SidebarIdentityText>\n * </SidebarIdentity>\n */\nexport function SidebarIdentity({\n responsive = false,\n collapsed = false,\n className,\n children,\n slot,\n ...rest\n}: SidebarIdentityProps) {\n return (\n <IdentityCtx.Provider value={{ responsive, collapsed }}>\n <div\n className={cn(\n \"flex min-w-0 flex-1 items-center gap-2 text-start\",\n // Collapsed, the avatar/tile (32px) is slightly larger than the ghost button's content\n // box (32px minus its 1px transparent border), so `justify-start` would left-align it and\n // clip the right edge — reading as off-centre. Centre it so the overflow is symmetric.\n responsive && \"group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:gap-0\",\n className,\n )}\n {...rest}\n data-slot={slot ?? \"sidebar-identity\"}\n >\n {children}\n </div>\n </IdentityCtx.Provider>\n );\n}\nSidebarIdentity.displayName = \"SidebarIdentity\";\n\n/** A square icon tile instead of an avatar (instances / workspaces / orgs). */\nexport function SidebarIdentityIcon({ className, slot, ...rest }: ComponentProps<\"div\">) {\n const { responsive } = useContext(IdentityCtx);\n return (\n <div\n className={cn(\n \"flex size-6 shrink-0 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground [&_svg]:size-4\",\n responsive && \"group-data-[collapsible=icon]:size-8\",\n className,\n )}\n {...rest}\n data-slot={slot ?? \"sidebar-identity-icon\"}\n />\n );\n}\nSidebarIdentityIcon.displayName = \"SidebarIdentityIcon\";\n\n/**\n * The avatar (people). Takes `AvatarImage` / `AvatarFallback` as children; its `size` is\n * derived from the sidebar state, so it is not overridable here.\n */\nexport function SidebarIdentityAvatar({ slot, ...rest }: Omit<ComponentProps<typeof Avatar>, \"size\">) {\n const { responsive, collapsed } = useContext(IdentityCtx);\n return <Avatar size={responsive && collapsed ? \"md\" : \"sm\"} {...rest} slot={slot ?? \"sidebar-identity-avatar\"} />;\n}\nSidebarIdentityAvatar.displayName = \"SidebarIdentityAvatar\";\n\n/** The text column. Hidden entirely in the sidebar's icon state when `responsive`. */\nexport function SidebarIdentityText({ className, slot, ...rest }: ComponentProps<\"div\">) {\n const { responsive } = useContext(IdentityCtx);\n return (\n <div\n className={cn(\n \"flex min-w-0 flex-col leading-tight\",\n responsive && \"group-data-[collapsible=icon]:hidden\",\n className,\n )}\n {...rest}\n data-slot={slot ?? \"sidebar-identity-text\"}\n />\n );\n}\nSidebarIdentityText.displayName = \"SidebarIdentityText\";\n\nexport function SidebarIdentityLabel({ className, slot, ...rest }: ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\"truncate text-sm font-medium\", className)}\n {...rest}\n data-slot={slot ?? \"sidebar-identity-label\"}\n />\n );\n}\nSidebarIdentityLabel.displayName = \"SidebarIdentityLabel\";\n\nexport function SidebarIdentityDescription({ className, slot, ...rest }: ComponentProps<\"span\">) {\n return (\n <span\n className={cn(\"truncate text-xs text-muted-foreground\", className)}\n {...rest}\n data-slot={slot ?? \"sidebar-identity-description\"}\n />\n );\n}\nSidebarIdentityDescription.displayName = \"SidebarIdentityDescription\";\n"],"names":["SidebarIdentity","SidebarIdentityIcon","SidebarIdentityAvatar","SidebarIdentityText","SidebarIdentityLabel","SidebarIdentityDescription"],"mappings":";;;;;AAYA;AAA+E;AACjE;AAEd;AAiCO;AAAyB;AACjB;AACD;AACZ;AACA;AACA;AAEF;AACE;AAEI;AAAC;AAAA;AACY;AACT;AAAA;AAAA;AAAA;AAIc;AACd;AAAA;AAEE;AACe;AAElB;AAAA;AAIT;AACAA;AAGO;AACL;AACA;AACE;AAAC;AAAA;AACY;AACT;AACc;AACd;AAAA;AAEE;AACe;AAAA;AAGzB;AACAC;AAMO;AACL;AACA;AACF;AACAC;AAGO;AACL;AACA;AACE;AAAC;AAAA;AACY;AACT;AACc;AACd;AAAA;AAEE;AACe;AAAA;AAGzB;AACAC;AAEO;AACL;AACE;AAAC;AAAA;AACwD;AACnD;AACe;AAAA;AAGzB;AACAC;AAEO;AACL;AACE;AAAC;AAAA;AACkE;AAC7D;AACe;AAAA;AAGzB;AACAC;;;;;;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Extension } from '@codemirror/state';
|
|
2
|
+
import { Panel } from '@codemirror/view';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
/**
|
|
5
|
+
* The find/replace panel, built from this library's own controls.
|
|
6
|
+
*
|
|
7
|
+
* `@codemirror/search` lets the panel be replaced wholesale — `search({ createPanel })`, where a
|
|
8
|
+
* `Panel` is an object with a `dom` — so the editor hands CodeMirror an empty element and renders
|
|
9
|
+
* into it with `createPortal`. **The search itself is not reimplemented:** `setSearchQuery`,
|
|
10
|
+
* `findNext`, `findPrevious`, `replaceNext`, `replaceAll` and `getSearchQuery` are all public, and
|
|
11
|
+
* they are the whole of the behaviour here. What changes is who draws the form.
|
|
12
|
+
*
|
|
13
|
+
* It buys the one thing a CSS override could not: the panel stops being the piece of the editor
|
|
14
|
+
* with its own typography, its own buttons and its own focus ring. Six selectors went with it —
|
|
15
|
+
* `.cm-panel.cm-search`, its font-size reset, the two `.cm-textfield` rules and the two for
|
|
16
|
+
* `button[name=close]`. **`.cm-textfield` and `.cm-button` themselves stay**, unscoped, because
|
|
17
|
+
* `gotoLine`'s dialog is still CodeMirror's own and because `codemirror-dark-parity.test.ts` reads
|
|
18
|
+
* that list: they are what stands between a dark page and CodeMirror's `&light` white box.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* How a `Panel` is opened as React. `CodeEditor` owns the implementation because the portals have
|
|
22
|
+
* to be children of its tree — a panel rendered from anywhere else loses the theme context, the
|
|
23
|
+
* error boundary and every provider above it.
|
|
24
|
+
*/
|
|
25
|
+
export interface ReactPanelHost {
|
|
26
|
+
panel: (node: ReactNode, opts?: {
|
|
27
|
+
top?: boolean;
|
|
28
|
+
}) => Panel;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `@codemirror/search`, drawn by `CodeSearchPanel`.
|
|
32
|
+
*
|
|
33
|
+
* The `update` hook is the panel's only channel back into React: CodeMirror gives a `Panel` a
|
|
34
|
+
* `ViewUpdate` on every transaction, and re-rendering the form on each keystroke of the document
|
|
35
|
+
* would be absurd — so it compares the query and `readOnly` and notifies only when one of them
|
|
36
|
+
* moved. `SearchQuery.eq` is what makes that cheap, and it is also what keeps our own
|
|
37
|
+
* `setSearchQuery` from looping back through the component.
|
|
38
|
+
*/
|
|
39
|
+
export declare const kanzoSearch: (host: ReactPanelHost) => Extension;
|
|
40
|
+
//# sourceMappingURL=code-editor-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-editor-search.d.ts","sourceRoot":"","sources":["../../src/composites/code-editor-search.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAmB,KAAK,KAAK,EAAoB,MAAM,kBAAkB,CAAC;AAYjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,KAAK,CAAC;CAC7D;AA6LD;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,cAAc,KAAG,SA+B/C,CAAC"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as r, jsxs as o, Fragment as w } from "react/jsx-runtime";
|
|
3
|
+
import { search as k, getSearchQuery as u, findPrevious as v, findNext as y, selectMatches as O, closeSearchPanel as P, replaceNext as b, replaceAll as W, SearchQuery as D, setSearchQuery as F } from "@codemirror/search";
|
|
4
|
+
import { runScopeHandlers as q } from "@codemirror/view";
|
|
5
|
+
import { CaseSensitiveIcon as G, WholeWordIcon as N, RegexIcon as Q, ChevronUpIcon as T, ChevronDownIcon as A, TextSelectIcon as E, XIcon as K, ReplaceIcon as j, ReplaceAllIcon as B } from "lucide-react";
|
|
6
|
+
import { useState as C, useRef as S, useEffect as x } from "react";
|
|
7
|
+
import { Button as c } from "../simples/button.js";
|
|
8
|
+
import { ButtonGroup as z } from "../simples/button-group.js";
|
|
9
|
+
import { InputGroup as I, InputGroupInput as R, InputGroupAddon as M } from "../simples/input-group.js";
|
|
10
|
+
import { Toggle as m } from "../simples/toggle.js";
|
|
11
|
+
const _ = (h) => {
|
|
12
|
+
const { view: a, subscribe: t } = h, [n, s] = C(() => u(a.state)), [g, l] = C(() => a.state.readOnly), i = S(null), p = S(null);
|
|
13
|
+
x(
|
|
14
|
+
() => t(() => {
|
|
15
|
+
s(u(a.state)), l(a.state.readOnly);
|
|
16
|
+
}),
|
|
17
|
+
[t, a]
|
|
18
|
+
), x(() => {
|
|
19
|
+
var e;
|
|
20
|
+
(e = i.current) == null || e.select();
|
|
21
|
+
}, []);
|
|
22
|
+
const d = (e) => {
|
|
23
|
+
const f = new D({
|
|
24
|
+
search: n.search,
|
|
25
|
+
caseSensitive: n.caseSensitive,
|
|
26
|
+
literal: n.literal,
|
|
27
|
+
regexp: n.regexp,
|
|
28
|
+
replace: n.replace,
|
|
29
|
+
wholeWord: n.wholeWord,
|
|
30
|
+
...e
|
|
31
|
+
});
|
|
32
|
+
f.eq(n) || (s(f), a.dispatch({ effects: F.of(f) }));
|
|
33
|
+
};
|
|
34
|
+
return (
|
|
35
|
+
// Two rows on ONE grid, not two flex rows. The fields have to end on the same line and the
|
|
36
|
+
// clusters beside them have to start on it, and the two clusters are a different number of
|
|
37
|
+
// buttons — so that column has to be sized by the wider of them, which is what a shared grid
|
|
38
|
+
// track does and what a pair of flex rows cannot.
|
|
39
|
+
//
|
|
40
|
+
// `font-sans` is the point of the file: everything inside `.cm-editor` inherits the monospace
|
|
41
|
+
// stack, so a panel that says nothing renders its labels in code type.
|
|
42
|
+
/* @__PURE__ */ o(
|
|
43
|
+
"div",
|
|
44
|
+
{
|
|
45
|
+
className: "grid grid-cols-[1fr_auto_auto] items-center gap-1.5 p-1.5 font-sans",
|
|
46
|
+
"data-slot": "code-editor-search",
|
|
47
|
+
onKeyDown: (e) => {
|
|
48
|
+
if (q(a, e.nativeEvent, "search-panel")) {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
e.key === "Enter" && (e.target === i.current ? (e.preventDefault(), (e.shiftKey ? v : y)(a)) : e.target === p.current && (e.preventDefault(), b(a)));
|
|
53
|
+
},
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ o(I, { size: "sm", children: [
|
|
56
|
+
/* @__PURE__ */ r(
|
|
57
|
+
R,
|
|
58
|
+
{
|
|
59
|
+
"aria-label": "Find",
|
|
60
|
+
"main-field": "true",
|
|
61
|
+
onChange: (e) => d({ search: e.currentTarget.value }),
|
|
62
|
+
placeholder: "Find",
|
|
63
|
+
ref: i,
|
|
64
|
+
size: "sm",
|
|
65
|
+
value: n.search
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ o(M, { align: "inline-end", children: [
|
|
69
|
+
/* @__PURE__ */ r(
|
|
70
|
+
m,
|
|
71
|
+
{
|
|
72
|
+
"aria-label": "Match case",
|
|
73
|
+
onPressedChange: (e) => d({ caseSensitive: e }),
|
|
74
|
+
pressed: n.caseSensitive,
|
|
75
|
+
size: "sm",
|
|
76
|
+
children: /* @__PURE__ */ r(G, {})
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
/* @__PURE__ */ r(
|
|
80
|
+
m,
|
|
81
|
+
{
|
|
82
|
+
"aria-label": "Whole word",
|
|
83
|
+
onPressedChange: (e) => d({ wholeWord: e }),
|
|
84
|
+
pressed: n.wholeWord,
|
|
85
|
+
size: "sm",
|
|
86
|
+
children: /* @__PURE__ */ r(N, {})
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
/* @__PURE__ */ r(
|
|
90
|
+
m,
|
|
91
|
+
{
|
|
92
|
+
"aria-label": "Regular expression",
|
|
93
|
+
onPressedChange: (e) => d({ regexp: e }),
|
|
94
|
+
pressed: n.regexp,
|
|
95
|
+
size: "sm",
|
|
96
|
+
children: /* @__PURE__ */ r(Q, {})
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
] })
|
|
100
|
+
] }),
|
|
101
|
+
/* @__PURE__ */ o(z, { "aria-label": "Find", children: [
|
|
102
|
+
/* @__PURE__ */ r(
|
|
103
|
+
c,
|
|
104
|
+
{
|
|
105
|
+
"aria-label": "Previous match",
|
|
106
|
+
onClick: () => v(a),
|
|
107
|
+
size: "icon-lg",
|
|
108
|
+
variant: "outline",
|
|
109
|
+
children: /* @__PURE__ */ r(T, {})
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
/* @__PURE__ */ r(
|
|
113
|
+
c,
|
|
114
|
+
{
|
|
115
|
+
"aria-label": "Next match",
|
|
116
|
+
onClick: () => y(a),
|
|
117
|
+
size: "icon-lg",
|
|
118
|
+
variant: "outline",
|
|
119
|
+
children: /* @__PURE__ */ r(A, {})
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
/* @__PURE__ */ r(
|
|
123
|
+
c,
|
|
124
|
+
{
|
|
125
|
+
"aria-label": "Select all matches",
|
|
126
|
+
onClick: () => O(a),
|
|
127
|
+
size: "icon-lg",
|
|
128
|
+
variant: "outline",
|
|
129
|
+
children: /* @__PURE__ */ r(E, {})
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
] }),
|
|
133
|
+
/* @__PURE__ */ r(
|
|
134
|
+
c,
|
|
135
|
+
{
|
|
136
|
+
"aria-label": "Close",
|
|
137
|
+
onClick: () => P(a),
|
|
138
|
+
size: "icon-lg",
|
|
139
|
+
variant: "ghost",
|
|
140
|
+
children: /* @__PURE__ */ r(K, {})
|
|
141
|
+
}
|
|
142
|
+
),
|
|
143
|
+
!g && /* @__PURE__ */ o(w, { children: [
|
|
144
|
+
/* @__PURE__ */ r(I, { size: "sm", children: /* @__PURE__ */ r(
|
|
145
|
+
R,
|
|
146
|
+
{
|
|
147
|
+
"aria-label": "Replace",
|
|
148
|
+
onChange: (e) => d({ replace: e.currentTarget.value }),
|
|
149
|
+
placeholder: "Replace",
|
|
150
|
+
ref: p,
|
|
151
|
+
size: "sm",
|
|
152
|
+
value: n.replace
|
|
153
|
+
}
|
|
154
|
+
) }),
|
|
155
|
+
/* @__PURE__ */ o(z, { "aria-label": "Replace", children: [
|
|
156
|
+
/* @__PURE__ */ r(
|
|
157
|
+
c,
|
|
158
|
+
{
|
|
159
|
+
"aria-label": "Replace",
|
|
160
|
+
onClick: () => b(a),
|
|
161
|
+
size: "icon-lg",
|
|
162
|
+
variant: "outline",
|
|
163
|
+
children: /* @__PURE__ */ r(j, {})
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
/* @__PURE__ */ r(
|
|
167
|
+
c,
|
|
168
|
+
{
|
|
169
|
+
"aria-label": "Replace all",
|
|
170
|
+
onClick: () => W(a),
|
|
171
|
+
size: "icon-lg",
|
|
172
|
+
variant: "outline",
|
|
173
|
+
children: /* @__PURE__ */ r(B, {})
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
] })
|
|
177
|
+
] })
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
}, re = (h) => k({
|
|
183
|
+
createPanel: (a) => {
|
|
184
|
+
const t = /* @__PURE__ */ new Set();
|
|
185
|
+
let n = u(a.state), s = a.state.readOnly;
|
|
186
|
+
return {
|
|
187
|
+
...h.panel(
|
|
188
|
+
/* @__PURE__ */ r(
|
|
189
|
+
_,
|
|
190
|
+
{
|
|
191
|
+
subscribe: (l) => (t.add(l), () => {
|
|
192
|
+
t.delete(l);
|
|
193
|
+
}),
|
|
194
|
+
view: a
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
{ top: !0 }
|
|
198
|
+
),
|
|
199
|
+
update: (l) => {
|
|
200
|
+
const i = u(l.state);
|
|
201
|
+
if (!(i.eq(n) && l.state.readOnly === s)) {
|
|
202
|
+
n = i, s = l.state.readOnly;
|
|
203
|
+
for (const p of t) p();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
export {
|
|
210
|
+
re as kanzoSearch
|
|
211
|
+
};
|
|
212
|
+
//# sourceMappingURL=code-editor-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-editor-search.js","sources":["../../src/composites/code-editor-search.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n SearchQuery,\n closeSearchPanel,\n findNext,\n findPrevious,\n getSearchQuery,\n replaceAll,\n replaceNext,\n search,\n selectMatches,\n setSearchQuery,\n} from \"@codemirror/search\";\nimport type { Extension } from \"@codemirror/state\";\nimport { type EditorView, type Panel, runScopeHandlers } from \"@codemirror/view\";\nimport {\n CaseSensitiveIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n RegexIcon,\n ReplaceAllIcon,\n ReplaceIcon,\n TextSelectIcon,\n WholeWordIcon,\n XIcon,\n} from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { Button } from \"../simples/button\";\nimport { ButtonGroup } from \"../simples/button-group\";\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupInput,\n} from \"../simples/input-group\";\nimport { Toggle } from \"../simples/toggle\";\n\n/**\n * The find/replace panel, built from this library's own controls.\n *\n * `@codemirror/search` lets the panel be replaced wholesale — `search({ createPanel })`, where a\n * `Panel` is an object with a `dom` — so the editor hands CodeMirror an empty element and renders\n * into it with `createPortal`. **The search itself is not reimplemented:** `setSearchQuery`,\n * `findNext`, `findPrevious`, `replaceNext`, `replaceAll` and `getSearchQuery` are all public, and\n * they are the whole of the behaviour here. What changes is who draws the form.\n *\n * It buys the one thing a CSS override could not: the panel stops being the piece of the editor\n * with its own typography, its own buttons and its own focus ring. Six selectors went with it —\n * `.cm-panel.cm-search`, its font-size reset, the two `.cm-textfield` rules and the two for\n * `button[name=close]`. **`.cm-textfield` and `.cm-button` themselves stay**, unscoped, because\n * `gotoLine`'s dialog is still CodeMirror's own and because `codemirror-dark-parity.test.ts` reads\n * that list: they are what stands between a dark page and CodeMirror's `&light` white box.\n */\n\n/**\n * How a `Panel` is opened as React. `CodeEditor` owns the implementation because the portals have\n * to be children of its tree — a panel rendered from anywhere else loses the theme context, the\n * error boundary and every provider above it.\n */\nexport interface ReactPanelHost {\n panel: (node: ReactNode, opts?: { top?: boolean }) => Panel;\n}\n\ninterface CodeSearchPanelProps {\n view: EditorView;\n /** Fires when the query or `readOnly` changed under us — see `kanzoSearch`. */\n subscribe: (fn: () => void) => () => void;\n}\n\nconst CodeSearchPanel = (props: CodeSearchPanelProps) => {\n const { view, subscribe } = props;\n\n const [query, setQuery] = useState(() => getSearchQuery(view.state));\n const [readOnly, setReadOnly] = useState(() => view.state.readOnly);\n const searchField = useRef<HTMLInputElement>(null);\n const replaceField = useRef<HTMLInputElement>(null);\n\n // The query is not ours alone: Mod-F over a selection seeds it while the panel is already open,\n // and `selectSelectionMatches` writes it too. Both arrive as a `setSearchQuery` effect.\n useEffect(\n () =>\n subscribe(() => {\n setQuery(getSearchQuery(view.state));\n setReadOnly(view.state.readOnly);\n }),\n [subscribe, view]\n );\n\n // CodeMirror focuses the field itself only when the panel is REOPENED (it looks for\n // `[main-field]`). On the first open it calls the panel's `mount()`, which runs before React has\n // rendered anything into the element — so the first focus is ours.\n useEffect(() => {\n searchField.current?.select();\n }, []);\n\n const commit = (patch: Partial<ConstructorParameters<typeof SearchQuery>[0]>) => {\n const next = new SearchQuery({\n search: query.search,\n caseSensitive: query.caseSensitive,\n literal: query.literal,\n regexp: query.regexp,\n replace: query.replace,\n wholeWord: query.wholeWord,\n ...patch,\n });\n if (next.eq(query)) return;\n setQuery(next);\n view.dispatch({ effects: setSearchQuery.of(next) });\n };\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n // The panel is inside the editor but outside its `contenteditable`, so the editor's own key\n // handling never sees these. `search-panel` is the scope `searchKeymap` registers for exactly\n // this: Escape closes, Mod-G and F3 step, Mod-F refocuses.\n if (runScopeHandlers(view, e.nativeEvent, \"search-panel\")) {\n e.preventDefault();\n return;\n }\n if (e.key !== \"Enter\") return;\n if (e.target === searchField.current) {\n e.preventDefault();\n (e.shiftKey ? findPrevious : findNext)(view);\n } else if (e.target === replaceField.current) {\n e.preventDefault();\n replaceNext(view);\n }\n };\n\n return (\n // Two rows on ONE grid, not two flex rows. The fields have to end on the same line and the\n // clusters beside them have to start on it, and the two clusters are a different number of\n // buttons — so that column has to be sized by the wider of them, which is what a shared grid\n // track does and what a pair of flex rows cannot.\n //\n // `font-sans` is the point of the file: everything inside `.cm-editor` inherits the monospace\n // stack, so a panel that says nothing renders its labels in code type.\n <div\n className=\"grid grid-cols-[1fr_auto_auto] items-center gap-1.5 p-1.5 font-sans\"\n data-slot=\"code-editor-search\"\n onKeyDown={onKeyDown}\n >\n <InputGroup size=\"sm\">\n <InputGroupInput\n aria-label=\"Find\"\n main-field=\"true\"\n onChange={(e) => commit({ search: e.currentTarget.value })}\n placeholder=\"Find\"\n ref={searchField}\n size=\"sm\"\n value={query.search}\n />\n <InputGroupAddon align=\"inline-end\">\n <Toggle\n aria-label=\"Match case\"\n onPressedChange={(pressed) => commit({ caseSensitive: pressed })}\n pressed={query.caseSensitive}\n size=\"sm\"\n >\n <CaseSensitiveIcon />\n </Toggle>\n <Toggle\n aria-label=\"Whole word\"\n onPressedChange={(pressed) => commit({ wholeWord: pressed })}\n pressed={query.wholeWord}\n size=\"sm\"\n >\n <WholeWordIcon />\n </Toggle>\n <Toggle\n aria-label=\"Regular expression\"\n onPressedChange={(pressed) => commit({ regexp: pressed })}\n pressed={query.regexp}\n size=\"sm\"\n >\n <RegexIcon />\n </Toggle>\n </InputGroupAddon>\n </InputGroup>\n <ButtonGroup aria-label=\"Find\">\n <Button\n aria-label=\"Previous match\"\n onClick={() => findPrevious(view)}\n size=\"icon-lg\"\n variant=\"outline\"\n >\n <ChevronUpIcon />\n </Button>\n <Button\n aria-label=\"Next match\"\n onClick={() => findNext(view)}\n size=\"icon-lg\"\n variant=\"outline\"\n >\n <ChevronDownIcon />\n </Button>\n <Button\n aria-label=\"Select all matches\"\n onClick={() => selectMatches(view)}\n size=\"icon-lg\"\n variant=\"outline\"\n >\n <TextSelectIcon />\n </Button>\n </ButtonGroup>\n <Button\n aria-label=\"Close\"\n onClick={() => closeSearchPanel(view)}\n size=\"icon-lg\"\n variant=\"ghost\"\n >\n <XIcon />\n </Button>\n {/* A read-only document has nothing to replace, and CodeMirror's own panel drops the row\n for the same reason. The third column stays empty on this row. */}\n {!readOnly && (\n <>\n <InputGroup size=\"sm\">\n <InputGroupInput\n aria-label=\"Replace\"\n onChange={(e) => commit({ replace: e.currentTarget.value })}\n placeholder=\"Replace\"\n ref={replaceField}\n size=\"sm\"\n value={query.replace}\n />\n </InputGroup>\n <ButtonGroup aria-label=\"Replace\">\n <Button\n aria-label=\"Replace\"\n onClick={() => replaceNext(view)}\n size=\"icon-lg\"\n variant=\"outline\"\n >\n <ReplaceIcon />\n </Button>\n <Button\n aria-label=\"Replace all\"\n onClick={() => replaceAll(view)}\n size=\"icon-lg\"\n variant=\"outline\"\n >\n <ReplaceAllIcon />\n </Button>\n </ButtonGroup>\n </>\n )}\n </div>\n );\n};\n\n/**\n * `@codemirror/search`, drawn by `CodeSearchPanel`.\n *\n * The `update` hook is the panel's only channel back into React: CodeMirror gives a `Panel` a\n * `ViewUpdate` on every transaction, and re-rendering the form on each keystroke of the document\n * would be absurd — so it compares the query and `readOnly` and notifies only when one of them\n * moved. `SearchQuery.eq` is what makes that cheap, and it is also what keeps our own\n * `setSearchQuery` from looping back through the component.\n */\nexport const kanzoSearch = (host: ReactPanelHost): Extension =>\n search({\n createPanel: (view) => {\n const listeners = new Set<() => void>();\n let query = getSearchQuery(view.state);\n let readOnly = view.state.readOnly;\n\n const panel = host.panel(\n <CodeSearchPanel\n subscribe={(fn) => {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n }}\n view={view}\n />,\n { top: true }\n );\n\n return {\n ...panel,\n update: (u) => {\n const next = getSearchQuery(u.state);\n if (next.eq(query) && u.state.readOnly === readOnly) return;\n query = next;\n readOnly = u.state.readOnly;\n for (const fn of listeners) fn();\n },\n };\n },\n });\n"],"names":[],"mappings":";;;;;;;;;;AAsEA;AACE;AASA;AAAA;AAGM;AAC+B;AAChC;AACa;;AAOhB;AAAqB;AAGvB;AACE;AAA6B;AACb;AACO;AACN;AACD;AACC;AACE;AACd;AAEL;AAEkD;AAqBpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQE;AAAC;AAAA;AACW;AACA;AAxBZ;AACE;AACA;AAAA;AAEF;AAMkB;AAEpB;AAeI;AACE;AAAA;AAAC;AAAA;AACY;AACA;AAC8C;AAC7C;AACP;AACA;AACQ;AAAA;AAAA;AAGb;AAAA;AAAC;AAAA;AACY;AACoD;AAChD;AACV;AAEc;AAAA;AAAA;AAErB;AAAC;AAAA;AACY;AACgD;AAC5C;AACV;AAEU;AAAA;AAAA;AAEjB;AAAC;AAAA;AACY;AAC6C;AACzC;AACV;AAEM;AAAA;AAAA;AAEf;AACF;AAEE;AAAA;AAAC;AAAA;AACY;AACqB;AAC3B;AACG;AAEO;AAAA;AAAA;AAEjB;AAAC;AAAA;AACY;AACiB;AACvB;AACG;AAES;AAAA;AAAA;AAEnB;AAAC;AAAA;AACY;AACsB;AAC5B;AACG;AAEQ;AAAA;AAAA;AAEpB;AACA;AAAC;AAAA;AACY;AACyB;AAC/B;AACG;AAED;AAAA;AAAA;AAML;AACE;AAAC;AAAA;AACY;AAC+C;AAC9C;AACP;AACA;AACQ;AAAA;AAEjB;AAEE;AAAA;AAAC;AAAA;AACY;AACoB;AAC1B;AACG;AAEK;AAAA;AAAA;AAEf;AAAC;AAAA;AACY;AACmB;AACzB;AACG;AAEQ;AAAA;AAAA;AAEpB;AACF;AAAA;AAAA;AAAA;AAIR;AAYS;AAEH;AACA;AAgBA;AAAO;AAbY;AACjB;AAAC;AAAA;AAIK;AAAmB;AACrB;AAEF;AAAA;AAAA;AAEK;AAAK;AAMV;AACA;AACA;AAEA;AAA4B;AAAA;AAC9B;AAAA;AAGN;;;;"}
|