@k8ordo/ui 2.0.0 → 2.2.0
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 +197 -187
- package/dist/components/_internal/focus-ring.css +37 -0
- package/dist/components/_internal/focus-ring.module.mjs +9 -0
- package/dist/components/_internal/form-value.d.mts +34 -0
- package/dist/components/_internal/form-value.mjs +91 -0
- package/dist/components/_internal/high-contrast.css +18 -0
- package/dist/components/_internal/high-contrast.module.mjs +5 -0
- package/dist/components/_internal/ignore-changes.d.mts +20 -0
- package/dist/components/_internal/ignore-changes.mjs +37 -0
- package/dist/components/_internal/nav-link.css +78 -0
- package/dist/components/_internal/nav-link.d.mts +5 -0
- package/dist/components/_internal/nav-link.mjs +8 -0
- package/dist/components/_internal/nav-link.module.mjs +14 -0
- package/dist/components/_internal/sr-only.css +17 -0
- package/dist/components/_internal/sr-only.module.mjs +5 -0
- package/dist/components/_internal/status-tone.css +37 -0
- package/dist/components/_internal/status-tone.d.mts +20 -0
- package/dist/components/_internal/status-tone.mjs +22 -0
- package/dist/components/_internal/status-tone.module.mjs +14 -0
- package/dist/components/ai/_internal/attachment-preview.css +75 -0
- package/dist/components/ai/_internal/attachment-preview.d.mts +15 -0
- package/dist/components/ai/_internal/attachment-preview.mjs +50 -0
- package/dist/components/ai/_internal/attachment-preview.module.mjs +14 -0
- package/dist/components/ai/_internal/collapsible.css +62 -0
- package/dist/components/ai/_internal/collapsible.d.mts +4 -2
- package/dist/components/ai/_internal/collapsible.mjs +42 -33
- package/dist/components/ai/_internal/collapsible.module.mjs +12 -0
- package/dist/components/ai/_internal/icons.d.mts +8 -0
- package/dist/components/ai/_internal/icons.mjs +14 -0
- package/dist/components/ai/_internal/streaming-cursor.css +28 -0
- package/dist/components/ai/_internal/streaming-cursor.d.mts +2 -3
- package/dist/components/ai/_internal/streaming-cursor.mjs +2 -1
- package/dist/components/ai/_internal/streaming-cursor.module.mjs +8 -0
- package/dist/components/ai/attachment/attachment.css +17 -0
- package/dist/components/ai/attachment/attachment.d.mts +15 -0
- package/dist/components/ai/attachment/attachment.mjs +25 -0
- package/dist/components/ai/attachment/attachment.module.mjs +8 -0
- package/dist/components/ai/attachment/index.d.mts +13 -0
- package/dist/components/ai/attachment/index.mjs +8 -0
- package/dist/components/ai/conversation/conversation.css +71 -0
- package/dist/components/ai/conversation/conversation.d.mts +4 -4
- package/dist/components/ai/conversation/conversation.mjs +38 -34
- package/dist/components/ai/conversation/conversation.module.mjs +11 -0
- package/dist/components/ai/conversation/index.d.mts +2 -3
- package/dist/components/ai/index.d.mts +4 -2
- package/dist/components/ai/index.mjs +3 -1
- package/dist/components/ai/message/index.d.mts +29 -3
- package/dist/components/ai/message/index.mjs +7 -2
- package/dist/components/ai/message/message.css +71 -0
- package/dist/components/ai/message/message.d.mts +34 -3
- package/dist/components/ai/message/message.mjs +99 -9
- package/dist/components/ai/message/message.module.mjs +18 -0
- package/dist/components/ai/prompt-input/index.d.mts +11 -4
- package/dist/components/ai/prompt-input/index.mjs +3 -1
- package/dist/components/ai/prompt-input/prompt-input.css +101 -0
- package/dist/components/ai/prompt-input/prompt-input.d.mts +15 -5
- package/dist/components/ai/prompt-input/prompt-input.mjs +152 -37
- package/dist/components/ai/prompt-input/prompt-input.module.mjs +15 -0
- package/dist/components/ai/reasoning/index.mjs +1 -2
- package/dist/components/ai/reasoning/reasoning.css +16 -0
- package/dist/components/ai/reasoning/reasoning.d.mts +2 -2
- package/dist/components/ai/reasoning/reasoning.mjs +5 -5
- package/dist/components/ai/reasoning/reasoning.module.mjs +8 -0
- package/dist/components/ai/response/index.mjs +1 -2
- package/dist/components/ai/response/response.css +453 -0
- package/dist/components/ai/response/response.d.mts +4 -5
- package/dist/components/ai/response/response.mjs +20 -15
- package/dist/components/ai/response/response.module.mjs +8 -0
- package/dist/components/ai/source/index.d.mts +12 -0
- package/dist/components/ai/source/index.mjs +8 -0
- package/dist/components/ai/source/source.css +54 -0
- package/dist/components/ai/source/source.d.mts +14 -0
- package/dist/components/ai/source/source.mjs +51 -0
- package/dist/components/ai/source/source.module.mjs +12 -0
- package/dist/components/ai/suggestion/index.d.mts +2 -3
- package/dist/components/ai/suggestion/suggestion.css +32 -0
- package/dist/components/ai/suggestion/suggestion.d.mts +3 -3
- package/dist/components/ai/suggestion/suggestion.mjs +7 -6
- package/dist/components/ai/suggestion/suggestion.module.mjs +8 -0
- package/dist/components/ai/tool-invocation/approval-bar.css +26 -0
- package/dist/components/ai/tool-invocation/approval-bar.d.mts +12 -0
- package/dist/components/ai/tool-invocation/approval-bar.mjs +59 -0
- package/dist/components/ai/tool-invocation/approval-bar.module.mjs +9 -0
- package/dist/components/ai/tool-invocation/index.mjs +1 -2
- package/dist/components/ai/tool-invocation/tool-invocation.css +55 -0
- package/dist/components/ai/tool-invocation/tool-invocation.d.mts +5 -4
- package/dist/components/ai/tool-invocation/tool-invocation.mjs +39 -18
- package/dist/components/ai/tool-invocation/tool-invocation.module.mjs +15 -0
- package/dist/components/ai/types.d.mts +21 -4
- package/dist/components/buttons/button/button.css +158 -0
- package/dist/components/buttons/button/button.d.mts +32 -8
- package/dist/components/buttons/button/button.mjs +46 -36
- package/dist/components/buttons/button/button.module.mjs +23 -0
- package/dist/components/buttons/button/index.d.mts +2 -2
- package/dist/components/buttons/button/index.mjs +1 -2
- package/dist/components/buttons/copy-button/copy-button.css +15 -0
- package/dist/components/buttons/copy-button/copy-button.d.mts +10 -0
- package/dist/components/buttons/copy-button/copy-button.mjs +70 -0
- package/dist/components/buttons/copy-button/copy-button.module.mjs +8 -0
- package/dist/components/buttons/copy-button/index.d.mts +2 -0
- package/dist/components/buttons/copy-button/index.mjs +1 -0
- package/dist/components/buttons/icon-button/icon-button.css +81 -0
- package/dist/components/buttons/icon-button/icon-button.d.mts +39 -11
- package/dist/components/buttons/icon-button/icon-button.mjs +70 -57
- package/dist/components/buttons/icon-button/icon-button.module.mjs +15 -0
- package/dist/components/buttons/icon-button/index.d.mts +2 -2
- package/dist/components/buttons/icon-button/index.mjs +1 -2
- package/dist/components/buttons/toolbar/index.d.mts +14 -0
- package/dist/components/buttons/toolbar/index.mjs +9 -0
- package/dist/components/buttons/toolbar/toolbar.css +30 -0
- package/dist/components/buttons/toolbar/toolbar.d.mts +19 -0
- package/dist/components/buttons/toolbar/toolbar.mjs +111 -0
- package/dist/components/buttons/toolbar/toolbar.module.mjs +9 -0
- package/dist/components/data-display/accordion/accordion-button.css +48 -0
- package/dist/components/data-display/accordion/accordion-button.d.mts +2 -3
- package/dist/components/data-display/accordion/accordion-button.mjs +5 -4
- package/dist/components/data-display/accordion/accordion-button.module.mjs +9 -0
- package/dist/components/data-display/accordion/accordion-item.css +11 -0
- package/dist/components/data-display/accordion/accordion-item.d.mts +2 -3
- package/dist/components/data-display/accordion/accordion-item.mjs +2 -1
- package/dist/components/data-display/accordion/accordion-item.module.mjs +5 -0
- package/dist/components/data-display/accordion/accordion-panel.css +9 -0
- package/dist/components/data-display/accordion/accordion-panel.d.mts +2 -3
- package/dist/components/data-display/accordion/accordion-panel.mjs +2 -2
- package/dist/components/data-display/accordion/accordion-panel.module.mjs +5 -0
- package/dist/components/data-display/accordion/accordion.css +19 -0
- package/dist/components/data-display/accordion/accordion.d.mts +2 -3
- package/dist/components/data-display/accordion/accordion.mjs +2 -1
- package/dist/components/data-display/accordion/accordion.module.mjs +5 -0
- package/dist/components/data-display/accordion/context.d.mts +3 -3
- package/dist/components/data-display/accordion/index.d.mts +2 -3
- package/dist/components/data-display/avatar/avatar.css +95 -0
- package/dist/components/data-display/avatar/avatar.d.mts +2 -2
- package/dist/components/data-display/avatar/avatar.mjs +22 -6
- package/dist/components/data-display/avatar/avatar.module.mjs +18 -0
- package/dist/components/data-display/avatar/index.mjs +1 -2
- package/dist/components/data-display/badge/badge.css +160 -0
- package/dist/components/data-display/badge/badge.d.mts +2 -2
- package/dist/components/data-display/badge/badge.mjs +30 -5
- package/dist/components/data-display/badge/badge.module.mjs +21 -0
- package/dist/components/data-display/badge/index.mjs +1 -2
- package/dist/components/data-display/card/card.css +44 -0
- package/dist/components/data-display/card/card.d.mts +2 -2
- package/dist/components/data-display/card/card.mjs +12 -2
- package/dist/components/data-display/card/card.module.mjs +12 -0
- package/dist/components/data-display/card/index.mjs +1 -2
- package/dist/components/data-display/carousel/carousel.css +83 -0
- package/dist/components/data-display/carousel/carousel.d.mts +12 -0
- package/dist/components/data-display/carousel/carousel.mjs +186 -0
- package/dist/components/data-display/carousel/carousel.module.mjs +18 -0
- package/dist/components/data-display/carousel/index.d.mts +11 -0
- package/dist/components/data-display/carousel/index.mjs +8 -0
- package/dist/components/data-display/code/code.css +37 -0
- package/dist/components/data-display/code/code.d.mts +2 -2
- package/dist/components/data-display/code/code.mjs +7 -6
- package/dist/components/data-display/code/code.module.mjs +9 -0
- package/dist/components/data-display/code/find-all-colors.d.mts +2 -2
- package/dist/components/data-display/code/find-all-colors.mjs +0 -161
- package/dist/components/data-display/code/index.mjs +1 -2
- package/dist/components/data-display/code-block/code-block.css +161 -0
- package/dist/components/data-display/code-block/code-block.d.mts +13 -0
- package/dist/components/data-display/code-block/code-block.mjs +44 -0
- package/dist/components/data-display/code-block/code-block.module.mjs +11 -0
- package/dist/components/data-display/code-block/highlight.d.mts +10 -0
- package/dist/components/data-display/code-block/highlight.mjs +54 -0
- package/dist/components/data-display/code-block/index.d.mts +2 -0
- package/dist/components/data-display/code-block/index.mjs +1 -0
- package/dist/components/data-display/data-table/data-table.css +74 -0
- package/dist/components/data-display/data-table/data-table.d.mts +32 -0
- package/dist/components/data-display/data-table/data-table.mjs +155 -0
- package/dist/components/data-display/data-table/data-table.module.mjs +14 -0
- package/dist/components/data-display/data-table/index.d.mts +2 -0
- package/dist/components/data-display/data-table/index.mjs +1 -0
- package/dist/components/data-display/heading/heading.css +99 -0
- package/dist/components/data-display/heading/heading.d.mts +2 -2
- package/dist/components/data-display/heading/heading.mjs +15 -14
- package/dist/components/data-display/heading/heading.module.mjs +20 -0
- package/dist/components/data-display/heading/index.mjs +1 -2
- package/dist/components/data-display/kbd/index.d.mts +2 -0
- package/dist/components/data-display/kbd/index.mjs +1 -0
- package/dist/components/data-display/kbd/kbd.css +21 -0
- package/dist/components/data-display/kbd/kbd.d.mts +9 -0
- package/dist/components/data-display/kbd/kbd.mjs +17 -0
- package/dist/components/data-display/kbd/kbd.module.mjs +5 -0
- package/dist/components/data-display/prose/index.d.mts +2 -0
- package/dist/components/data-display/prose/index.mjs +1 -0
- package/dist/components/data-display/prose/prose.css +253 -0
- package/dist/components/data-display/prose/prose.d.mts +6 -0
- package/dist/components/data-display/prose/prose.mjs +10 -0
- package/dist/components/data-display/prose/prose.module.mjs +5 -0
- package/dist/components/data-display/table/index.mjs +1 -2
- package/dist/components/data-display/table/table.css +107 -0
- package/dist/components/data-display/table/table.d.mts +10 -8
- package/dist/components/data-display/table/table.mjs +26 -15
- package/dist/components/data-display/table/table.module.mjs +22 -0
- package/dist/components/data-display/tree/index.d.mts +2 -0
- package/dist/components/data-display/tree/index.mjs +1 -0
- package/dist/components/data-display/tree/tree.css +70 -0
- package/dist/components/data-display/tree/tree.d.mts +21 -0
- package/dist/components/data-display/tree/tree.mjs +144 -0
- package/dist/components/data-display/tree/tree.module.mjs +14 -0
- package/dist/components/feedback/alert/alert.css +42 -0
- package/dist/components/feedback/alert/alert.d.mts +3 -3
- package/dist/components/feedback/alert/alert.mjs +20 -23
- package/dist/components/feedback/alert/alert.module.mjs +14 -0
- package/dist/components/feedback/alert/index.mjs +1 -2
- package/dist/components/feedback/callout/callout.css +38 -0
- package/dist/components/feedback/callout/callout.d.mts +11 -0
- package/dist/components/feedback/callout/callout.mjs +38 -0
- package/dist/components/feedback/callout/callout.module.mjs +11 -0
- package/dist/components/feedback/callout/index.d.mts +2 -0
- package/dist/components/feedback/callout/index.mjs +1 -0
- package/dist/components/feedback/empty-state/empty-state.css +36 -0
- package/dist/components/feedback/empty-state/empty-state.d.mts +11 -0
- package/dist/components/feedback/empty-state/empty-state.mjs +27 -0
- package/dist/components/feedback/empty-state/empty-state.module.mjs +11 -0
- package/dist/components/feedback/empty-state/index.d.mts +2 -0
- package/dist/components/feedback/empty-state/index.mjs +1 -0
- package/dist/components/feedback/progress/index.mjs +1 -2
- package/dist/components/feedback/progress/progress.css +78 -0
- package/dist/components/feedback/progress/progress.d.mts +5 -4
- package/dist/components/feedback/progress/progress.mjs +17 -3
- package/dist/components/feedback/progress/progress.module.mjs +12 -0
- package/dist/components/feedback/skeleton/index.mjs +1 -2
- package/dist/components/feedback/skeleton/skeleton.css +64 -0
- package/dist/components/feedback/skeleton/skeleton.d.mts +2 -2
- package/dist/components/feedback/skeleton/skeleton.mjs +19 -2
- package/dist/components/feedback/skeleton/skeleton.module.mjs +17 -0
- package/dist/components/feedback/spinner/index.mjs +1 -2
- package/dist/components/feedback/spinner/spinner.css +40 -0
- package/dist/components/feedback/spinner/spinner.d.mts +2 -2
- package/dist/components/feedback/spinner/spinner.mjs +13 -7
- package/dist/components/feedback/spinner/spinner.module.mjs +12 -0
- package/dist/components/feedback/toast/context.d.mts +8 -9
- package/dist/components/feedback/toast/provider.css +69 -0
- package/dist/components/feedback/toast/provider.d.mts +2 -3
- package/dist/components/feedback/toast/provider.mjs +17 -13
- package/dist/components/feedback/toast/provider.module.mjs +12 -0
- package/dist/components/feedback/toast/toast.d.mts +2 -2
- package/dist/components/form/autocomplete/autocomplete.css +154 -0
- package/dist/components/form/autocomplete/autocomplete.d.mts +2 -2
- package/dist/components/form/autocomplete/autocomplete.mjs +70 -46
- package/dist/components/form/autocomplete/autocomplete.module.mjs +21 -0
- package/dist/components/form/autocomplete/index.mjs +1 -2
- package/dist/components/form/calendar/calendar.css +98 -0
- package/dist/components/form/calendar/calendar.d.mts +15 -0
- package/dist/components/form/calendar/calendar.mjs +182 -0
- package/dist/components/form/calendar/calendar.module.mjs +18 -0
- package/dist/components/form/calendar/index.d.mts +2 -0
- package/dist/components/form/calendar/index.mjs +1 -0
- package/dist/components/form/checkbox/checkbox.css +90 -0
- package/dist/components/form/checkbox/checkbox.d.mts +4 -2
- package/dist/components/form/checkbox/checkbox.mjs +29 -27
- package/dist/components/form/checkbox/checkbox.module.mjs +13 -0
- package/dist/components/form/checkbox/index.mjs +1 -2
- package/dist/components/form/checkbox-card/checkbox-card.css +138 -0
- package/dist/components/form/checkbox-card/checkbox-card.d.mts +3 -3
- package/dist/components/form/checkbox-card/checkbox-card.mjs +17 -23
- package/dist/components/form/checkbox-card/checkbox-card.module.mjs +17 -0
- package/dist/components/form/checkbox-card/index.mjs +1 -2
- package/dist/components/form/checkbox-group/checkbox-group.css +15 -0
- package/dist/components/form/checkbox-group/checkbox-group.d.mts +7 -5
- package/dist/components/form/checkbox-group/checkbox-group.mjs +12 -16
- package/dist/components/form/checkbox-group/checkbox-group.module.mjs +8 -0
- package/dist/components/form/checkbox-group/index.d.mts +4 -3
- package/dist/components/form/color-picker/color-picker.css +172 -0
- package/dist/components/form/color-picker/color-picker.d.mts +29 -0
- package/dist/components/form/color-picker/color-picker.mjs +239 -0
- package/dist/components/form/color-picker/color-picker.module.mjs +20 -0
- package/dist/components/form/color-picker/index.d.mts +2 -0
- package/dist/components/form/color-picker/index.mjs +1 -0
- package/dist/components/form/combobox/combobox.css +143 -0
- package/dist/components/form/combobox/combobox.d.mts +33 -0
- package/dist/components/form/combobox/combobox.mjs +278 -0
- package/dist/components/form/combobox/combobox.module.mjs +19 -0
- package/dist/components/form/combobox/index.d.mts +2 -0
- package/dist/components/form/combobox/index.mjs +1 -0
- package/dist/components/form/date-field/date-field.css +31 -0
- package/dist/components/form/date-field/date-field.d.mts +9 -0
- package/dist/components/form/date-field/date-field.mjs +20 -0
- package/dist/components/form/date-field/date-field.module.mjs +5 -0
- package/dist/components/form/date-field/index.d.mts +2 -0
- package/dist/components/form/date-field/index.mjs +1 -0
- package/dist/components/form/date-picker/date-picker.css +90 -0
- package/dist/components/form/date-picker/date-picker.d.mts +13 -0
- package/dist/components/form/date-picker/date-picker.mjs +77 -0
- package/dist/components/form/date-picker/date-picker.module.mjs +14 -0
- package/dist/components/form/date-picker/index.d.mts +2 -0
- package/dist/components/form/date-picker/index.mjs +1 -0
- package/dist/components/form/file-field/file-field.css +88 -0
- package/dist/components/form/file-field/file-field.d.mts +9 -6
- package/dist/components/form/file-field/file-field.mjs +130 -37
- package/dist/components/form/file-field/file-field.module.mjs +17 -0
- package/dist/components/form/file-field/index.d.mts +5 -3
- package/dist/components/form/file-field/index.mjs +2 -1
- package/dist/components/form/form/form.css +11 -0
- package/dist/components/form/form/form.d.mts +4 -3
- package/dist/components/form/form/form.mjs +2 -1
- package/dist/components/form/form/form.module.mjs +5 -0
- package/dist/components/form/form/index.mjs +1 -2
- package/dist/components/form/form-control/form-control.css +43 -0
- package/dist/components/form/form-control/form-control.d.mts +2 -2
- package/dist/components/form/form-control/form-control.mjs +11 -10
- package/dist/components/form/form-control/form-control.module.mjs +12 -0
- package/dist/components/form/form-control/index.mjs +1 -2
- package/dist/components/form/number-field/cast.d.mts +2 -3
- package/dist/components/form/number-field/cast.mjs +3 -22
- package/dist/components/form/number-field/index.mjs +1 -2
- package/dist/components/form/number-field/number-field.css +122 -0
- package/dist/components/form/number-field/number-field.d.mts +10 -9
- package/dist/components/form/number-field/number-field.mjs +126 -54
- package/dist/components/form/number-field/number-field.module.mjs +10 -0
- package/dist/components/form/password-input/index.mjs +1 -2
- package/dist/components/form/password-input/password-input.css +88 -0
- package/dist/components/form/password-input/password-input.d.mts +2 -2
- package/dist/components/form/password-input/password-input.mjs +15 -12
- package/dist/components/form/password-input/password-input.module.mjs +12 -0
- package/dist/components/form/radio/index.mjs +1 -2
- package/dist/components/form/radio/radio.css +89 -0
- package/dist/components/form/radio/radio.d.mts +3 -2
- package/dist/components/form/radio/radio.mjs +15 -19
- package/dist/components/form/radio/radio.module.mjs +16 -0
- package/dist/components/form/radio-card/index.mjs +1 -2
- package/dist/components/form/radio-card/radio-card.css +147 -0
- package/dist/components/form/radio-card/radio-card.d.mts +4 -3
- package/dist/components/form/radio-card/radio-card.mjs +17 -23
- package/dist/components/form/radio-card/radio-card.module.mjs +19 -0
- package/dist/components/form/range-slider/index.d.mts +2 -0
- package/dist/components/form/range-slider/index.mjs +1 -0
- package/dist/components/form/range-slider/range-slider.css +67 -0
- package/dist/components/form/range-slider/range-slider.d.mts +31 -0
- package/dist/components/form/range-slider/range-slider.mjs +135 -0
- package/dist/components/form/range-slider/range-slider.module.mjs +14 -0
- package/dist/components/form/select/index.mjs +1 -2
- package/dist/components/form/select/select.css +74 -0
- package/dist/components/form/select/select.d.mts +2 -2
- package/dist/components/form/select/select.mjs +25 -8
- package/dist/components/form/select/select.module.mjs +12 -0
- package/dist/components/form/slider/index.mjs +1 -2
- package/dist/components/form/slider/range-input-class.css +97 -0
- package/dist/components/form/slider/range-input-class.d.mts +7 -0
- package/dist/components/form/slider/range-input-class.mjs +10 -0
- package/dist/components/form/slider/range-input-class.module.mjs +8 -0
- package/dist/components/form/slider/slider.css +55 -0
- package/dist/components/form/slider/slider.d.mts +6 -6
- package/dist/components/form/slider/slider.mjs +44 -11
- package/dist/components/form/slider/slider.module.mjs +12 -0
- package/dist/components/form/switch/index.mjs +1 -2
- package/dist/components/form/switch/switch.css +103 -0
- package/dist/components/form/switch/switch.d.mts +2 -2
- package/dist/components/form/switch/switch.mjs +14 -17
- package/dist/components/form/switch/switch.module.mjs +16 -0
- package/dist/components/form/text-field/index.mjs +1 -2
- package/dist/components/form/text-field/text-field.css +41 -0
- package/dist/components/form/text-field/text-field.d.mts +4 -4
- package/dist/components/form/text-field/text-field.mjs +2 -3
- package/dist/components/form/text-field/text-field.module.mjs +5 -0
- package/dist/components/form/textarea/index.mjs +1 -2
- package/dist/components/form/textarea/textarea.css +50 -0
- package/dist/components/form/textarea/textarea.d.mts +3 -2
- package/dist/components/form/textarea/textarea.mjs +6 -20
- package/dist/components/form/textarea/textarea.module.mjs +9 -0
- package/dist/components/icons/base.css +40 -0
- package/dist/components/icons/base.d.mts +4 -5
- package/dist/components/icons/base.mjs +10 -10
- package/dist/components/icons/base.module.mjs +13 -0
- package/dist/components/icons/browsers.d.mts +5 -6
- package/dist/components/icons/color-scale.d.mts +2 -3
- package/dist/components/icons/github-mark.d.mts +2 -3
- package/dist/components/icons/logo.css +17 -0
- package/dist/components/icons/logo.d.mts +3 -4
- package/dist/components/icons/logo.mjs +4 -3
- package/dist/components/icons/logo.module.mjs +9 -0
- package/dist/components/icons/lucide-deep.d.mts +3 -1
- package/dist/components/icons/lucide-imports.d.mts +3 -1
- package/dist/components/icons/lucide-imports.mjs +3 -1
- package/dist/components/icons/lucide.d.mts +60 -60
- package/dist/components/icons/qiita.d.mts +2 -3
- package/dist/components/icons/twitter.css +9 -0
- package/dist/components/icons/twitter.d.mts +2 -3
- package/dist/components/icons/twitter.mjs +2 -1
- package/dist/components/icons/twitter.module.mjs +5 -0
- package/dist/components/icons/vertical-writing.d.mts +2 -3
- package/dist/components/index.d.mts +48 -9
- package/dist/components/index.mjs +32 -10
- package/dist/components/layout/_shared/gap.css +25 -0
- package/dist/components/layout/_shared/gap.d.mts +8 -9
- package/dist/components/layout/_shared/gap.mjs +6 -5
- package/dist/components/layout/_shared/gap.module.mjs +11 -0
- package/dist/components/layout/_shared/padding.css +25 -0
- package/dist/components/layout/_shared/padding.d.mts +8 -9
- package/dist/components/layout/_shared/padding.mjs +6 -5
- package/dist/components/layout/_shared/padding.module.mjs +11 -0
- package/dist/components/layout/grid/grid.css +93 -0
- package/dist/components/layout/grid/grid.d.mts +5 -6
- package/dist/components/layout/grid/grid.mjs +21 -20
- package/dist/components/layout/grid/grid.module.mjs +25 -0
- package/dist/components/layout/grid/index.mjs +1 -2
- package/dist/components/layout/resizable-panels/index.d.mts +18 -0
- package/dist/components/layout/resizable-panels/index.mjs +9 -0
- package/dist/components/layout/resizable-panels/resizable-panels.css +77 -0
- package/dist/components/layout/resizable-panels/resizable-panels.d.mts +22 -0
- package/dist/components/layout/resizable-panels/resizable-panels.mjs +164 -0
- package/dist/components/layout/resizable-panels/resizable-panels.module.mjs +16 -0
- package/dist/components/layout/separator/index.mjs +1 -2
- package/dist/components/layout/separator/separator.css +37 -0
- package/dist/components/layout/separator/separator.d.mts +2 -2
- package/dist/components/layout/separator/separator.mjs +12 -9
- package/dist/components/layout/separator/separator.module.mjs +12 -0
- package/dist/components/layout/stack/index.mjs +1 -2
- package/dist/components/layout/stack/stack.css +49 -0
- package/dist/components/layout/stack/stack.d.mts +3 -4
- package/dist/components/layout/stack/stack.mjs +11 -10
- package/dist/components/layout/stack/stack.module.mjs +17 -0
- package/dist/components/navigation/anchor/anchor.css +29 -0
- package/dist/components/navigation/anchor/anchor.d.mts +2 -2
- package/dist/components/navigation/anchor/anchor.mjs +5 -2
- package/dist/components/navigation/anchor/anchor.module.mjs +8 -0
- package/dist/components/navigation/anchor/index.mjs +1 -2
- package/dist/components/navigation/breadcrumb/breadcrumb.css +72 -0
- package/dist/components/navigation/breadcrumb/breadcrumb.d.mts +5 -5
- package/dist/components/navigation/breadcrumb/breadcrumb.mjs +15 -9
- package/dist/components/navigation/breadcrumb/breadcrumb.module.mjs +14 -0
- package/dist/components/navigation/breadcrumb/index.d.mts +2 -3
- package/dist/components/navigation/pagination/index.mjs +1 -2
- package/dist/components/navigation/pagination/pagination.css +36 -0
- package/dist/components/navigation/pagination/pagination.d.mts +2 -2
- package/dist/components/navigation/pagination/pagination.mjs +9 -8
- package/dist/components/navigation/pagination/pagination.module.mjs +11 -0
- package/dist/components/navigation/side-nav/index.d.mts +16 -0
- package/dist/components/navigation/side-nav/index.mjs +10 -0
- package/dist/components/navigation/side-nav/side-nav.css +73 -0
- package/dist/components/navigation/side-nav/side-nav.d.mts +32 -0
- package/dist/components/navigation/side-nav/side-nav.mjs +62 -0
- package/dist/components/navigation/side-nav/side-nav.module.mjs +11 -0
- package/dist/components/navigation/stepper/index.d.mts +2 -0
- package/dist/components/navigation/stepper/index.mjs +1 -0
- package/dist/components/navigation/stepper/stepper.css +151 -0
- package/dist/components/navigation/stepper/stepper.d.mts +20 -0
- package/dist/components/navigation/stepper/stepper.mjs +87 -0
- package/dist/components/navigation/stepper/stepper.module.mjs +29 -0
- package/dist/components/navigation/table-of-contents/index.d.mts +2 -0
- package/dist/components/navigation/table-of-contents/index.mjs +1 -0
- package/dist/components/navigation/table-of-contents/table-of-contents.css +17 -0
- package/dist/components/navigation/table-of-contents/table-of-contents.d.mts +12 -0
- package/dist/components/navigation/table-of-contents/table-of-contents.mjs +94 -0
- package/dist/components/navigation/table-of-contents/table-of-contents.module.mjs +8 -0
- package/dist/components/navigation/tabs/index.d.mts +2 -3
- package/dist/components/navigation/tabs/tabs.css +70 -0
- package/dist/components/navigation/tabs/tabs.d.mts +5 -6
- package/dist/components/navigation/tabs/tabs.mjs +51 -42
- package/dist/components/navigation/tabs/tabs.module.mjs +12 -0
- package/dist/components/observers/fragment-instance.d.mts +1 -0
- package/dist/components/observers/in-view/in-view.d.mts +11 -0
- package/dist/components/observers/in-view/in-view.mjs +60 -0
- package/dist/components/observers/in-view/index.d.mts +2 -0
- package/dist/components/observers/in-view/index.mjs +1 -0
- package/dist/components/observers/resize/index.d.mts +2 -0
- package/dist/components/observers/resize/index.mjs +1 -0
- package/dist/components/observers/resize/resize.d.mts +7 -0
- package/dist/components/observers/resize/resize.mjs +25 -0
- package/dist/components/overlays/_internal/modal-dialog-context.d.mts +4 -5
- package/dist/components/overlays/_internal/use-can-hover.d.mts +2 -3
- package/dist/components/overlays/_internal/use-hover-intent.d.mts +3 -4
- package/dist/components/overlays/_internal/use-list-navigation.d.mts +17 -11
- package/dist/components/overlays/_internal/use-list-navigation.mjs +42 -26
- package/dist/components/overlays/command-palette/command-palette.css +91 -0
- package/dist/components/overlays/command-palette/command-palette.d.mts +25 -0
- package/dist/components/overlays/command-palette/command-palette.mjs +139 -0
- package/dist/components/overlays/command-palette/command-palette.module.mjs +14 -0
- package/dist/components/overlays/command-palette/index.d.mts +2 -0
- package/dist/components/overlays/command-palette/index.mjs +1 -0
- package/dist/components/overlays/context-menu/context-menu.d.mts +13 -0
- package/dist/components/overlays/context-menu/context-menu.mjs +79 -0
- package/dist/components/overlays/context-menu/index.d.mts +21 -0
- package/dist/components/overlays/context-menu/index.mjs +13 -0
- package/dist/components/overlays/dialog/dialog.css +31 -0
- package/dist/components/overlays/dialog/dialog.d.mts +4 -5
- package/dist/components/overlays/dialog/dialog.mjs +10 -7
- package/dist/components/overlays/dialog/dialog.module.mjs +10 -0
- package/dist/components/overlays/dialog/index.d.mts +2 -3
- package/dist/components/overlays/drawer/drawer.css +48 -0
- package/dist/components/overlays/drawer/drawer.d.mts +2 -3
- package/dist/components/overlays/drawer/drawer.mjs +11 -10
- package/dist/components/overlays/drawer/drawer.module.mjs +13 -0
- package/dist/components/overlays/drawer/index.mjs +1 -2
- package/dist/components/overlays/dropdown-menu/dropdown-menu.d.mts +7 -10
- package/dist/components/overlays/dropdown-menu/dropdown-menu.mjs +10 -9
- package/dist/components/overlays/dropdown-menu/hooks.d.mts +7 -6
- package/dist/components/overlays/dropdown-menu/hooks.mjs +3 -3
- package/dist/components/overlays/dropdown-menu/index.d.mts +8 -10
- package/dist/components/overlays/dropdown-menu/shared.css +51 -0
- package/dist/components/overlays/dropdown-menu/shared.d.mts +3 -6
- package/dist/components/overlays/dropdown-menu/shared.mjs +4 -6
- package/dist/components/overlays/dropdown-menu/shared.module.mjs +8 -0
- package/dist/components/overlays/dropdown-menu/sub-menu.css +28 -0
- package/dist/components/overlays/dropdown-menu/sub-menu.d.mts +3 -6
- package/dist/components/overlays/dropdown-menu/sub-menu.mjs +15 -15
- package/dist/components/overlays/dropdown-menu/sub-menu.module.mjs +9 -0
- package/dist/components/overlays/dropdown-menu/use-safe-triangle.d.mts +2 -3
- package/dist/components/overlays/list-box/hooks.d.mts +6 -5
- package/dist/components/overlays/list-box/hooks.mjs +5 -4
- package/dist/components/overlays/list-box/index.d.mts +2 -3
- package/dist/components/overlays/list-box/list-box.css +74 -0
- package/dist/components/overlays/list-box/list-box.d.mts +5 -6
- package/dist/components/overlays/list-box/list-box.mjs +15 -13
- package/dist/components/overlays/list-box/list-box.module.mjs +10 -0
- package/dist/components/overlays/modal/index.mjs +1 -2
- package/dist/components/overlays/modal/modal.css +119 -0
- package/dist/components/overlays/modal/modal.d.mts +2 -3
- package/dist/components/overlays/modal/modal.mjs +14 -11
- package/dist/components/overlays/modal/modal.module.mjs +11 -0
- package/dist/components/overlays/popover/anchor-positioning.d.mts +4 -4
- package/dist/components/overlays/popover/escape-stack.d.mts +2 -3
- package/dist/components/overlays/popover/hooks.d.mts +7 -7
- package/dist/components/overlays/popover/index.d.mts +2 -2
- package/dist/components/overlays/popover/popover.css +51 -0
- package/dist/components/overlays/popover/popover.d.mts +4 -4
- package/dist/components/overlays/popover/popover.mjs +11 -5
- package/dist/components/overlays/popover/popover.module.mjs +11 -0
- package/dist/components/overlays/tooltip/bridge.css +68 -0
- package/dist/components/overlays/tooltip/bridge.d.mts +2 -3
- package/dist/components/overlays/tooltip/bridge.mjs +7 -21
- package/dist/components/overlays/tooltip/bridge.module.mjs +11 -0
- package/dist/components/overlays/tooltip/index.d.mts +2 -2
- package/dist/components/overlays/tooltip/tooltip.css +15 -0
- package/dist/components/overlays/tooltip/tooltip.d.mts +5 -6
- package/dist/components/overlays/tooltip/tooltip.mjs +3 -2
- package/dist/components/overlays/tooltip/tooltip.module.mjs +5 -0
- package/dist/components/providers/index.d.mts +1 -2
- package/dist/components/providers/portal-root.d.mts +9 -7
- package/dist/components/providers/portal-root.mjs +7 -7
- package/dist/components/providers/ui-provider.d.mts +2 -6
- package/dist/components/providers/ui-provider.mjs +1 -5
- package/dist/helpers/chain.d.mts +2 -2
- package/dist/helpers/chain.mjs +0 -23
- package/dist/helpers/create-safe-context.d.mts +2 -2
- package/dist/helpers/create-safe-context.mjs +0 -17
- package/dist/helpers/index.d.mts +1 -2
- package/dist/helpers/index.mjs +2 -3
- package/dist/helpers/merge-props.d.mts +2 -2
- package/dist/helpers/merge-props.mjs +2 -37
- package/dist/helpers/merge-refs.d.mts +2 -3
- package/dist/helpers/merge-refs.mjs +0 -32
- package/dist/hooks/click-away/index.d.mts +2 -3
- package/dist/hooks/controllable-state/index.d.mts +2 -2
- package/dist/hooks/controllable-state/index.mjs +9 -11
- package/dist/hooks/index.d.mts +2 -20
- package/dist/hooks/index.mjs +2 -19
- package/dist/hooks/writing-mode/index.d.mts +15 -5
- package/dist/hooks/writing-mode/index.mjs +21 -14
- package/dist/i18n/current.d.mts +23 -0
- package/dist/i18n/current.mjs +43 -0
- package/dist/i18n/en.d.mts +2 -3
- package/dist/i18n/en.mjs +46 -0
- package/dist/i18n/index.d.mts +3 -2
- package/dist/i18n/index.mjs +4 -3
- package/dist/i18n/ja.d.mts +2 -3
- package/dist/i18n/ja.mjs +46 -0
- package/dist/i18n/messages.d.mts +81 -3
- package/dist/i18n/usage.d.mts +8 -0
- package/dist/i18n/usage.mjs +131 -0
- package/dist/index.d.mts +29 -34
- package/dist/index.mjs +32 -35
- package/dist/integrations/_shared/openui-defs.d.mts +3 -4
- package/dist/integrations/_shared/openui-defs.mjs +123 -54
- package/dist/integrations/_shared/renderers.css +50 -0
- package/dist/integrations/_shared/renderers.d.mts +71 -52
- package/dist/integrations/_shared/renderers.mjs +288 -30
- package/dist/integrations/_shared/renderers.module.mjs +14 -0
- package/dist/integrations/_shared/schemas.d.mts +384 -102
- package/dist/integrations/_shared/schemas.mjs +165 -27
- package/dist/integrations/ai-sdk/index.d.mts +2 -2
- package/dist/integrations/ai-sdk/index.mjs +1 -2
- package/dist/integrations/ai-sdk/map-parts.d.mts +25 -7
- package/dist/integrations/ai-sdk/map-parts.mjs +28 -3
- package/dist/integrations/json-render/catalog.d.mts +336 -17
- package/dist/integrations/json-render/catalog.mjs +120 -57
- package/dist/integrations/json-render/registry.d.mts +3 -4
- package/dist/integrations/json-render/registry.mjs +38 -4
- package/dist/integrations/openui/form-views.d.mts +23 -18
- package/dist/integrations/openui/form-views.mjs +27 -3
- package/dist/integrations/openui/library.d.mts +59 -6
- package/dist/integrations/openui/library.mjs +18 -3
- package/dist/integrations/openui/prompt.d.mts +2 -3
- package/dist/internal/accepts-file.d.mts +8 -0
- package/dist/internal/accepts-file.mjs +19 -0
- package/dist/internal/carries-files.d.mts +3 -0
- package/dist/internal/carries-files.mjs +4 -0
- package/dist/internal/clamp.d.mts +2 -3
- package/dist/internal/clamp.mjs +0 -11
- package/dist/internal/commit-input-value.d.mts +10 -0
- package/dist/internal/commit-input-value.mjs +15 -0
- package/dist/internal/dropped-files.d.mts +3 -0
- package/dist/internal/dropped-files.mjs +8 -0
- package/dist/internal/focus-trap.d.mts +5 -6
- package/dist/internal/focus-trap.mjs +4 -4
- package/dist/internal/hex-color.d.mts +22 -0
- package/dist/internal/hex-color.mjs +53 -0
- package/dist/internal/iso-date.d.mts +39 -0
- package/dist/internal/iso-date.mjs +98 -0
- package/dist/internal/to-precision.d.mts +2 -3
- package/dist/internal/to-precision.mjs +0 -8
- package/dist/styles/index.css +513 -2526
- package/dist/styles/tailwind.css +217 -17
- package/dist/styles/tokens.d.mts +1 -2
- package/dist/styles/tokens.mjs +1 -2
- package/dist/types/variables.d.mts +7 -9
- package/docs/GUIDE.md +239 -72
- package/docs/llms.txt +11 -7
- package/docs/props.generated.json +2156 -309
- package/docs/references/ai-chat.md +345 -62
- package/docs/references/color.md +222 -78
- package/docs/references/components.md +1667 -242
- package/docs/references/generative-ui.md +126 -17
- package/docs/references/interaction-design.md +65 -35
- package/docs/references/spatial-design.md +83 -63
- package/docs/references/types.md +101 -0
- package/docs/references/typography.md +159 -51
- package/package.json +65 -63
- package/dist/components/_internal/focus-ring.d.mts +0 -8
- package/dist/components/_internal/focus-ring.mjs +0 -8
- package/dist/components/layout/scroll-linked/index.d.mts +0 -2
- package/dist/components/layout/scroll-linked/index.mjs +0 -2
- package/dist/components/layout/scroll-linked/scroll-linked.d.mts +0 -14
- package/dist/components/layout/scroll-linked/scroll-linked.mjs +0 -59
- package/dist/helpers/cn.d.mts +0 -5
- package/dist/helpers/cn.mjs +0 -27
- package/dist/hooks/breakpoint/index.d.mts +0 -5
- package/dist/hooks/breakpoint/index.mjs +0 -24
- package/dist/hooks/client/index.d.mts +0 -4
- package/dist/hooks/client/index.mjs +0 -6
- package/dist/hooks/clipboard/index.d.mts +0 -7
- package/dist/hooks/clipboard/index.mjs +0 -15
- package/dist/hooks/debounced-transition/index.d.mts +0 -5
- package/dist/hooks/debounced-transition/index.mjs +0 -49
- package/dist/hooks/deferred-debounce/index.d.mts +0 -4
- package/dist/hooks/deferred-debounce/index.mjs +0 -9
- package/dist/hooks/disclosure/index.d.mts +0 -10
- package/dist/hooks/disclosure/index.mjs +0 -20
- package/dist/hooks/hover/index.d.mts +0 -12
- package/dist/hooks/hover/index.mjs +0 -19
- package/dist/hooks/intersection-observer/index.d.mts +0 -3
- package/dist/hooks/intersection-observer/index.mjs +0 -3
- package/dist/hooks/intersection-observer/use-in-view.d.mts +0 -11
- package/dist/hooks/intersection-observer/use-in-view.mjs +0 -18
- package/dist/hooks/intersection-observer/use-intersection-observer.d.mts +0 -11
- package/dist/hooks/intersection-observer/use-intersection-observer.mjs +0 -33
- package/dist/hooks/interval/index.d.mts +0 -4
- package/dist/hooks/interval/index.mjs +0 -19
- package/dist/hooks/resize/index.d.mts +0 -8
- package/dist/hooks/resize/index.mjs +0 -24
- package/dist/hooks/scroll-direction/index.d.mts +0 -13
- package/dist/hooks/scroll-direction/index.mjs +0 -67
- package/dist/hooks/scroll-lock/index.d.mts +0 -9
- package/dist/hooks/scroll-lock/index.mjs +0 -59
- package/dist/hooks/step/index.d.mts +0 -15
- package/dist/hooks/step/index.mjs +0 -39
- package/dist/hooks/timeout/index.d.mts +0 -4
- package/dist/hooks/timeout/index.mjs +0 -19
- package/dist/hooks/window-resize/index.d.mts +0 -11
- package/dist/hooks/window-resize/index.mjs +0 -21
- package/dist/hooks/window-size/index.d.mts +0 -8
- package/dist/hooks/window-size/index.mjs +0 -31
- package/dist/i18n/context.d.mts +0 -9
- package/dist/i18n/context.mjs +0 -19
- package/dist/styles/base.css +0 -315
- package/dist/styles/tokens.css +0 -380
- package/dist/styles/tokens.generated.d.mts +0 -840
- package/dist/styles/tokens.generated.mjs +0 -813
- package/dist/styles/utilities.css +0 -62
- package/docs/references/helpers.md +0 -223
- package/docs/references/hooks.md +0 -301
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
## Importing
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
|
-
// The stylesheet (required). In a Tailwind CSS 4 project,
|
|
7
|
-
//
|
|
6
|
+
// The stylesheet (required). In a Tailwind CSS 4 project, a CSS file
|
|
7
|
+
// imports 'tailwindcss' and then '@k8ordo/ui/tailwind.css' instead
|
|
8
8
|
import '@k8ordo/ui/styles.css';
|
|
9
|
-
|
|
10
9
|
// The provider (once, at the app root)
|
|
11
10
|
import { UIProvider } from '@k8ordo/ui';
|
|
12
|
-
|
|
13
11
|
// Components (all from the root entry)
|
|
14
12
|
import { Button, Card, TextField } from '@k8ordo/ui';
|
|
13
|
+
// Except CodeBlock, which highlights on the server and has its own entry
|
|
14
|
+
import { CodeBlock } from '@k8ordo/ui/code-block';
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Every component can be rendered from a Server Component, compound ones
|
|
@@ -19,7 +19,59 @@ included: `Dialog.Root`, `Tabs.Root`, `Popover.Trigger`, and the rest are
|
|
|
19
19
|
composed outside the client modules, so the parts resolve on the server. Only
|
|
20
20
|
the interactive parts carry `'use client'`.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Render props
|
|
23
|
+
|
|
24
|
+
Two different contracts are spelled `render*`, and mixing them up is the usual
|
|
25
|
+
source of surprise.
|
|
26
|
+
|
|
27
|
+
**Replacing the element** — `renderItem` on `Button` and `IconButton`,
|
|
28
|
+
`renderAnchor` on `Anchor`, `Breadcrumb.Link`, and `SideNav.Link`. The component computes
|
|
29
|
+
everything and hands back the exact props it would have put on its own element,
|
|
30
|
+
so substituting an `<a>`, a framework `<Link>`, or your own button loses
|
|
31
|
+
nothing. For `Button` and `IconButton` that is the resolved `className`, the
|
|
32
|
+
composed `children` (icons and the pending spinner included), the click handler,
|
|
33
|
+
the disabled and pending state, the `ref`, and every `aria-*` / `data-*` /
|
|
34
|
+
native attribute the caller passed in. Spread the bag onto whatever you render.
|
|
35
|
+
|
|
36
|
+
- Handlers and the `ref` are typed for `HTMLElement` rather than for the
|
|
37
|
+
element the component would have rendered, so the bag spreads onto any tag.
|
|
38
|
+
- `disabled` and `type` are the only members that exist on `<button>` alone.
|
|
39
|
+
Destructure them away when you render something else — `aria-disabled` in the
|
|
40
|
+
same bag carries the state, and the supplied `onClick` already does nothing
|
|
41
|
+
and calls `preventDefault()` while disabled, so a disabled link will not
|
|
42
|
+
navigate. The examples below name them `_disabled` / `_type` so the discarded
|
|
43
|
+
bindings pass a `no-unused-vars` rule.
|
|
44
|
+
- `disabled` is only the `disabled` you passed. While an action is pending (see
|
|
45
|
+
[Button](#button)), the bag carries `aria-disabled` and `aria-busy` but
|
|
46
|
+
`disabled` stays `false`, so the pressed element keeps focus; the supplied
|
|
47
|
+
`onClick` is what ignores presses meanwhile.
|
|
48
|
+
- Spreading onto a real `<button>` is exact, but write `type` on the element
|
|
49
|
+
anyway: the `button-has-type` lint rule cannot see a `type` that arrives
|
|
50
|
+
through a spread.
|
|
51
|
+
|
|
52
|
+
The link components own less, so their bags are smaller:
|
|
53
|
+
|
|
54
|
+
- `Anchor` hands back `href`, `className`, `children` (with the new-tab icon on
|
|
55
|
+
an external link), `target` / `rel` on an external link, and every other
|
|
56
|
+
anchor attribute the caller passed, plus `kind` (`'internal'` |
|
|
57
|
+
`'external'`). `kind` is not an attribute: destructure it away before
|
|
58
|
+
spreading, or use it to choose between a framework `<Link>` and a plain `<a>`.
|
|
59
|
+
There is no `ref` and no disabled state.
|
|
60
|
+
- `Breadcrumb.Link` hands back `href`, `className`, and `children` only — it
|
|
61
|
+
takes no other attributes. A `current` link renders
|
|
62
|
+
`<span aria-current="page">` and does not call `renderAnchor`.
|
|
63
|
+
- `SideNav.Link` hands back `href`, `className`, `children`, `aria-current`
|
|
64
|
+
(`'page'` on the current link), and every other anchor attribute the caller
|
|
65
|
+
passed. A current link is still a link.
|
|
66
|
+
|
|
67
|
+
**Filling a slot** — `renderInput` on `FormControl`, `renderItem` on
|
|
68
|
+
`Popover.Trigger`, `Tooltip.Trigger`, `FileField.Trigger`, and `Alert`'s
|
|
69
|
+
`action`. Here the component owns no element of its own. It computes wiring —
|
|
70
|
+
ids, ARIA relationships, open/close handlers — and you supply the element that
|
|
71
|
+
wiring belongs to. Those bags carry only the wiring, and they are required
|
|
72
|
+
rather than optional.
|
|
73
|
+
|
|
74
|
+
## Buttons
|
|
23
75
|
|
|
24
76
|
### Button
|
|
25
77
|
|
|
@@ -48,20 +100,34 @@ Props:
|
|
|
48
100
|
- `fullWidth`: `boolean` (default: `false`)
|
|
49
101
|
- `isActive`: `boolean` (default: `false`)
|
|
50
102
|
- `onAction`: `() => void | Promise<void>`
|
|
51
|
-
- `renderItem`: `(props:
|
|
103
|
+
- `renderItem`: `(props: ButtonRenderItemProps) => ReactNode`
|
|
52
104
|
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
53
105
|
- `startIcon`: `ReactNode`
|
|
54
106
|
- `type`: `'button'` | `'submit'` (default: `'button'`)
|
|
55
107
|
- `variant`: `'solid'` | `'outline'` | `'skeleton'` (default: `'solid'`)
|
|
56
|
-
|
|
57
|
-
|
|
108
|
+
- Other props are forwarded to `ComponentPropsWithRef<'button'>`, except `className` / `style`.
|
|
109
|
+
|
|
110
|
+
`onAction` runs inside a transition. While the promise it returns is pending —
|
|
111
|
+
and, with `type="submit"`, while the form's action is pending — the button shows
|
|
112
|
+
a spinner, carries `aria-busy` and `aria-disabled`, and ignores presses,
|
|
113
|
+
including the implicit submission of pressing Enter in a field. It does not
|
|
114
|
+
become natively `disabled`: Chromium moves focus to `body` when the focused
|
|
115
|
+
element is disabled, so a keyboard user who pressed the button would lose their
|
|
116
|
+
place. The `disabled` you pass is native, and takes the button out of the tab
|
|
117
|
+
order.
|
|
118
|
+
|
|
119
|
+
`renderItem` replaces the `<button>`; see [Render props](#render-props) for the
|
|
120
|
+
contract. It receives `className`, the composed `children`, `ref`, `type`,
|
|
121
|
+
`disabled`, `aria-disabled`, `aria-busy`, `onClick`, and every other attribute
|
|
122
|
+
passed to `Button`. Use it to render a link — the same shape works for things
|
|
123
|
+
like Next.js's `<Link>`.
|
|
58
124
|
|
|
59
125
|
```tsx
|
|
60
126
|
<Button
|
|
61
127
|
color="base"
|
|
62
128
|
variant="outline"
|
|
63
|
-
renderItem={({
|
|
64
|
-
<a
|
|
129
|
+
renderItem={({ children, disabled: _disabled, type: _type, ...props }) => (
|
|
130
|
+
<a href="/page" {...props}>
|
|
65
131
|
{children}
|
|
66
132
|
</a>
|
|
67
133
|
)}
|
|
@@ -88,29 +154,34 @@ Props:
|
|
|
88
154
|
- `children`: `ReactNode`
|
|
89
155
|
- `color`: `'transparent'` | `'base'` | `'primary'` | `'secondary'` (default: `'transparent'`)
|
|
90
156
|
- `onAction`: `() => void | Promise<void>`
|
|
91
|
-
- `renderItem`: `(props:
|
|
157
|
+
- `renderItem`: `(props: IconButtonRenderItemProps) => ReactNode`
|
|
92
158
|
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
93
159
|
- `tooltipDisabled`: `boolean` (default: `false`)
|
|
94
160
|
- `tooltipPlacement`: `Placement` (default: `'top'`)
|
|
161
|
+
- Other props are forwarded to `ComponentPropsWithRef<'button'>`, except `type` / `className` / `style`.
|
|
162
|
+
|
|
163
|
+
`onAction` keeps the button busy the way `Button`'s does — `aria-busy`,
|
|
164
|
+
`aria-disabled`, presses ignored, focus kept — without the spinner.
|
|
95
165
|
|
|
96
|
-
|
|
166
|
+
`renderItem` replaces the `<button>` under the same contract as `Button`'s,
|
|
167
|
+
with one addition: the tooltip wiring is kept in a nested `triggerProps` so it
|
|
168
|
+
stays free of any element type. Spread it too, and `label` shows as a tooltip on
|
|
169
|
+
hover and focus. `triggerProps` also carries the merged `ref` and any
|
|
170
|
+
`onMouseEnter` / `onMouseLeave` / `onFocus` / `onBlur` the caller passed, so the
|
|
171
|
+
flat part of the bag holds neither.
|
|
97
172
|
|
|
98
173
|
```tsx
|
|
99
174
|
<IconButton
|
|
100
175
|
color="base"
|
|
101
176
|
label="Mail"
|
|
102
177
|
renderItem={({
|
|
103
|
-
className,
|
|
104
178
|
children,
|
|
105
|
-
|
|
179
|
+
disabled: _disabled,
|
|
106
180
|
triggerProps,
|
|
181
|
+
type: _type,
|
|
182
|
+
...props
|
|
107
183
|
}) => (
|
|
108
|
-
<a
|
|
109
|
-
aria-label={ariaLabel}
|
|
110
|
-
className={className}
|
|
111
|
-
href="/contact"
|
|
112
|
-
{...triggerProps}
|
|
113
|
-
>
|
|
184
|
+
<a href="/contact" {...props} {...triggerProps}>
|
|
114
185
|
{children}
|
|
115
186
|
</a>
|
|
116
187
|
)}
|
|
@@ -119,60 +190,97 @@ Use the `renderItem` prop to render it as a link. Spreading `triggerProps` onto
|
|
|
119
190
|
</IconButton>
|
|
120
191
|
```
|
|
121
192
|
|
|
122
|
-
###
|
|
193
|
+
### CopyButton
|
|
123
194
|
|
|
124
|
-
A text
|
|
195
|
+
A button that copies text to the clipboard and shows that it did: its icon
|
|
196
|
+
turns into a check for two seconds (an error icon if the write fails), and a
|
|
197
|
+
live region beside it announces `copied` / `copyFailed` from the dictionary. The
|
|
198
|
+
button's name stays the same throughout. By default it is an outline `Button`
|
|
199
|
+
with its `label` as text; `iconOnly` makes it a transparent `IconButton` whose
|
|
200
|
+
`label` is the tooltip.
|
|
125
201
|
|
|
126
202
|
```tsx
|
|
127
|
-
import {
|
|
203
|
+
import { CopyButton } from '@k8ordo/ui';
|
|
128
204
|
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
205
|
+
<CopyButton value={css} label="Copy CSS" size="sm" />;
|
|
206
|
+
|
|
207
|
+
// Icon only, e.g. in the header of a code block
|
|
208
|
+
<CopyButton value={code} label="Copy code" iconOnly size="sm" />;
|
|
209
|
+
|
|
210
|
+
// Build the text when the button is pressed: read the DOM, or fetch it
|
|
211
|
+
<CopyButton
|
|
212
|
+
label="Copy as Markdown"
|
|
213
|
+
value={async () => (await fetch(`/blog/${slug}.md`)).text()}
|
|
214
|
+
/>;
|
|
132
215
|
```
|
|
133
216
|
|
|
217
|
+
`value` may be a function, and it may return a `Promise`. It is called on the
|
|
218
|
+
click, and the promise goes to the clipboard as it is (a `ClipboardItem`), so the
|
|
219
|
+
write starts inside the click even when the text arrives later. Safari refuses
|
|
220
|
+
a write that begins after an `await`.
|
|
221
|
+
|
|
134
222
|
Props:
|
|
135
223
|
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
138
|
-
- `
|
|
139
|
-
- `
|
|
224
|
+
- `value`: `string` | `(() => string | Promise<string>)` (required)
|
|
225
|
+
- `disabled`: `boolean` (default: `false`)
|
|
226
|
+
- `iconOnly`: `boolean` (default: `false`)
|
|
227
|
+
- `label`: `string`
|
|
228
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
140
229
|
|
|
141
|
-
|
|
230
|
+
### Toolbar
|
|
142
231
|
|
|
143
|
-
|
|
232
|
+
A group of controls that takes one Tab stop (the WAI-ARIA toolbar). Arrow keys
|
|
233
|
+
move between the items — left/right when `orientation` is `horizontal`, up/down
|
|
234
|
+
when `vertical`, and in vertical writing mode the other pair, since the row
|
|
235
|
+
itself runs vertically — `Home` / `End` jump to the ends, and disabled items are
|
|
236
|
+
skipped. Tab leaves the toolbar, and coming back lands on the item last focused.
|
|
144
237
|
|
|
145
|
-
|
|
238
|
+
Each item is a slot: `Toolbar.Item`'s `renderItem` receives `ref`, `tabIndex`,
|
|
239
|
+
and `onFocus` (`ToolbarItemProps`) to spread onto a `Button`, an `IconButton`,
|
|
240
|
+
or any other button. A toggle item sets `aria-pressed`; the toolbar shades a
|
|
241
|
+
pressed item. The root needs a name (`aria-label` or `aria-labelledby`).
|
|
242
|
+
Items are meant to be buttons: a control that uses the arrow keys itself (a text
|
|
243
|
+
field, a slider) would fight the toolbar for them.
|
|
146
244
|
|
|
147
245
|
```tsx
|
|
148
|
-
import {
|
|
149
|
-
|
|
150
|
-
<
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
</
|
|
246
|
+
import { IconButton, Toolbar } from '@k8ordo/ui';
|
|
247
|
+
|
|
248
|
+
<Toolbar.Root aria-label="Formatting">
|
|
249
|
+
<Toolbar.Item
|
|
250
|
+
renderItem={(props) => (
|
|
251
|
+
<IconButton {...props} label="Copy">
|
|
252
|
+
<CopyIcon />
|
|
253
|
+
</IconButton>
|
|
254
|
+
)}
|
|
255
|
+
/>
|
|
256
|
+
<Toolbar.Separator />
|
|
257
|
+
<Toolbar.Item
|
|
258
|
+
renderItem={(props) => (
|
|
259
|
+
<IconButton
|
|
260
|
+
{...props}
|
|
261
|
+
aria-pressed={isList}
|
|
262
|
+
label="Bulleted list"
|
|
263
|
+
onClick={toggleList}
|
|
264
|
+
>
|
|
265
|
+
<ListIcon />
|
|
266
|
+
</IconButton>
|
|
267
|
+
)}
|
|
268
|
+
/>
|
|
269
|
+
</Toolbar.Root>;
|
|
156
270
|
```
|
|
157
271
|
|
|
158
|
-
Props (
|
|
159
|
-
|
|
160
|
-
- `children`: `ReactNode`
|
|
161
|
-
|
|
162
|
-
Props (Accordion.Item):
|
|
272
|
+
Props (Root):
|
|
163
273
|
|
|
164
274
|
- `children`: `ReactNode`
|
|
165
|
-
- `
|
|
166
|
-
- `
|
|
167
|
-
- `
|
|
168
|
-
|
|
169
|
-
Props (Accordion.Panel):
|
|
275
|
+
- `orientation`: `'horizontal'` | `'vertical'` (default: `'horizontal'`)
|
|
276
|
+
- `ref`: `Ref<HTMLDivElement>`
|
|
277
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style` / `role` / `aria-orientation`.
|
|
170
278
|
|
|
171
|
-
|
|
279
|
+
Props (Toolbar.Item):
|
|
172
280
|
|
|
173
|
-
|
|
281
|
+
- `renderItem`: `(props: ToolbarItemProps) => ReactElement` (required)
|
|
174
282
|
|
|
175
|
-
|
|
283
|
+
## Navigation
|
|
176
284
|
|
|
177
285
|
### Breadcrumb
|
|
178
286
|
|
|
@@ -210,6 +318,9 @@ Props (Breadcrumb.Link):
|
|
|
210
318
|
- `current`: `boolean` (default: `false`)
|
|
211
319
|
- `renderAnchor`: `(props: RenderBreadcrumbAnchorProps<T>) => ReactNode` (default: `defaultRenderBreadcrumbAnchor`)
|
|
212
320
|
|
|
321
|
+
`Breadcrumb.Link`'s `renderAnchor` receives `href`, `className`, and `children`;
|
|
322
|
+
see [Render props](#render-props).
|
|
323
|
+
|
|
213
324
|
Props (Breadcrumb.Item):
|
|
214
325
|
|
|
215
326
|
- `children`: `ReactNode`
|
|
@@ -234,164 +345,250 @@ Props:
|
|
|
234
345
|
- `nextLabel`: `string`
|
|
235
346
|
- `prevLabel`: `string`
|
|
236
347
|
- `ref`: `Ref<HTMLElement>`
|
|
348
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `children`.
|
|
237
349
|
|
|
238
|
-
###
|
|
350
|
+
### Stepper
|
|
239
351
|
|
|
240
|
-
|
|
352
|
+
The steps of a process in order, as an `<ol>`: done steps show a check and are
|
|
353
|
+
read with the built-in `stepperComplete` wording, and the current step carries
|
|
354
|
+
`aria-current="step"`. `value` is the index of the current step (0-based);
|
|
355
|
+
`steps.length` marks every step done. With `interactive`, done steps become
|
|
356
|
+
buttons that go back to them (`onChange` receives the index); steps ahead are
|
|
357
|
+
never buttons. Name the list with `aria-label`.
|
|
241
358
|
|
|
242
|
-
|
|
243
|
-
|
|
359
|
+
`orientation` is logical: `horizontal` lays the steps along the line and
|
|
360
|
+
`vertical` stacks them, so inside `writing-v` they run top to bottom and right
|
|
361
|
+
to left respectively, with the connecting lines drawn to match and the step
|
|
362
|
+
numbers kept upright. In `horizontal`, every step takes an equal share of the
|
|
363
|
+
width: the first row holds only the step markers and the lines between them,
|
|
364
|
+
and each step's label and description sit under its marker, wrapping within
|
|
365
|
+
that share. The lines keep their length however long the labels are, and a
|
|
366
|
+
narrow container wraps the text rather than squeezing the lines.
|
|
244
367
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
<Tabs.Tab id="tab1">Tab 1</Tabs.Tab>
|
|
248
|
-
<Tabs.Tab id="tab2">Tab 2</Tabs.Tab>
|
|
249
|
-
</Tabs.List>
|
|
250
|
-
<Tabs.Panel id="tab1">Panel 1</Tabs.Panel>
|
|
251
|
-
<Tabs.Panel id="tab2">Panel 2</Tabs.Panel>
|
|
252
|
-
</Tabs.Root>;
|
|
253
|
-
```
|
|
368
|
+
In a multi-step form (see `@k8ordo/form`'s guide), keep every step mounted and
|
|
369
|
+
drive `value` from the step you show.
|
|
254
370
|
|
|
255
|
-
|
|
371
|
+
```tsx
|
|
372
|
+
import { Stepper } from '@k8ordo/ui';
|
|
373
|
+
|
|
374
|
+
<Stepper
|
|
375
|
+
aria-label="Sign-up"
|
|
376
|
+
interactive
|
|
377
|
+
onChange={setStep}
|
|
378
|
+
steps={[
|
|
379
|
+
{ label: 'Plan', description: 'Pick what fits' },
|
|
380
|
+
{ label: 'Payment' },
|
|
381
|
+
{ label: 'Review' },
|
|
382
|
+
]}
|
|
383
|
+
value={step}
|
|
384
|
+
/>;
|
|
385
|
+
```
|
|
256
386
|
|
|
257
|
-
|
|
258
|
-
- `children`: `ReactNode`
|
|
259
|
-
- `defaultSelectedId`: `string` | `null` (default: `null`)
|
|
260
|
-
- `onChange`: `(id: string) => void`
|
|
261
|
-
- `selectedId`: `string`
|
|
387
|
+
Props:
|
|
262
388
|
|
|
263
|
-
|
|
389
|
+
- `steps`: `readonly StepperStep[]` (required)
|
|
390
|
+
- `defaultValue`: `number`
|
|
391
|
+
- `interactive`: `boolean` (default: `false`)
|
|
392
|
+
- `onChange`: `(value: number) => void`
|
|
393
|
+
- `orientation`: `'horizontal'` | `'vertical'` (default: `'horizontal'`)
|
|
394
|
+
- `ref`: `Ref<HTMLOListElement>`
|
|
395
|
+
- `value`: `number`
|
|
396
|
+
- Other props are forwarded to `OlHTMLAttributes<HTMLOListElement>`, except `className` / `style` / `children`.
|
|
264
397
|
|
|
265
|
-
|
|
266
|
-
- `children`: `ReactNode`
|
|
398
|
+
### SideNav
|
|
267
399
|
|
|
268
|
-
|
|
400
|
+
Side navigation: groups of links, each under a small title, with the page being
|
|
401
|
+
shown marked by a bar (`aria-current="page"`). The library has no router, so
|
|
402
|
+
you say which link is current, and swap the `<a>` for your router's link with
|
|
403
|
+
`renderAnchor` — it replaces the element, and its bag holds `href`,
|
|
404
|
+
`className`, `children`, `aria-current`, and every other anchor attribute you
|
|
405
|
+
passed (an `onClick` that closes a drawer, say). Every part renders from a
|
|
406
|
+
Server Component.
|
|
269
407
|
|
|
270
|
-
|
|
271
|
-
|
|
408
|
+
```tsx
|
|
409
|
+
import { SideNav } from '@k8ordo/ui';
|
|
410
|
+
|
|
411
|
+
<SideNav.Root label="Components">
|
|
412
|
+
<SideNav.Group title="Buttons">
|
|
413
|
+
<SideNav.Link current={pathname === '/button'} href="/button">
|
|
414
|
+
Button
|
|
415
|
+
</SideNav.Link>
|
|
416
|
+
<SideNav.Link
|
|
417
|
+
href="/icon-button"
|
|
418
|
+
renderAnchor={({ children, ...props }) => (
|
|
419
|
+
<Link {...props}>{children}</Link>
|
|
420
|
+
)}
|
|
421
|
+
>
|
|
422
|
+
IconButton
|
|
423
|
+
</SideNav.Link>
|
|
424
|
+
</SideNav.Group>
|
|
425
|
+
</SideNav.Root>;
|
|
426
|
+
```
|
|
272
427
|
|
|
273
|
-
Props (
|
|
428
|
+
Props (SideNav.Root):
|
|
274
429
|
|
|
275
|
-
- `
|
|
430
|
+
- `label`: `string` (required)
|
|
276
431
|
- `children`: `ReactNode`
|
|
432
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `aria-label`.
|
|
277
433
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
434
|
+
A group can hold a level that opens and closes, `SideNav.Sub`, to make the
|
|
435
|
+
navigation a tree. It is a `<details>`, so it opens and closes without
|
|
436
|
+
JavaScript; open the one holding the current page with `defaultOpen`.
|
|
281
437
|
|
|
282
438
|
```tsx
|
|
283
|
-
|
|
439
|
+
<SideNav.Group title="Components">
|
|
440
|
+
<SideNav.Link href="/components">Overview</SideNav.Link>
|
|
441
|
+
<SideNav.Sub defaultOpen title="Buttons">
|
|
442
|
+
<SideNav.Link current href="/button">
|
|
443
|
+
Button
|
|
444
|
+
</SideNav.Link>
|
|
445
|
+
</SideNav.Sub>
|
|
446
|
+
<SideNav.Sub title="Navigation">
|
|
447
|
+
<SideNav.Link href="/tabs">Tabs</SideNav.Link>
|
|
448
|
+
</SideNav.Sub>
|
|
449
|
+
</SideNav.Group>
|
|
450
|
+
```
|
|
284
451
|
|
|
285
|
-
|
|
286
|
-
<Card width="full" variant="shadow">
|
|
287
|
-
<div className="p-6">Content</div>
|
|
288
|
-
</Card>
|
|
452
|
+
Props (SideNav.Group):
|
|
289
453
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
<div className="p-6">Content</div>
|
|
293
|
-
</Card>
|
|
294
|
-
```
|
|
454
|
+
- `title`: `string` (required)
|
|
455
|
+
- `children`: `ReactNode`
|
|
295
456
|
|
|
296
|
-
Props:
|
|
457
|
+
Props (SideNav.Sub):
|
|
297
458
|
|
|
459
|
+
- `title`: `string` (required)
|
|
298
460
|
- `children`: `ReactNode`
|
|
299
|
-
- `
|
|
300
|
-
- `variant`: `'shadow'` | `'outline'` (default: `'shadow'`)
|
|
301
|
-
- `width`: `'full'` | `'fit'` (default: `'full'`)
|
|
461
|
+
- `defaultOpen`: `boolean` (default: `false`)
|
|
302
462
|
|
|
303
|
-
|
|
463
|
+
Props (SideNav.Link):
|
|
304
464
|
|
|
305
|
-
|
|
465
|
+
- `children`: `ReactNode` (required)
|
|
466
|
+
- `href`: `T` (required)
|
|
467
|
+
- `current`: `boolean` (default: `false`)
|
|
468
|
+
- `renderAnchor`: `(props: RenderSideNavAnchorProps<T>) => ReactNode` (default: `defaultRenderAnchor`)
|
|
469
|
+
- Other props are forwarded to `AnchorHTMLAttributes<HTMLAnchorElement>`, except `className` / `style` / `aria-current`.
|
|
470
|
+
|
|
471
|
+
### TableOfContents
|
|
472
|
+
|
|
473
|
+
The contents of the page, with the heading being read marked
|
|
474
|
+
(`aria-current="location"`). Pass the headings as a tree of `{ id, label,
|
|
475
|
+
children? }`; each item links to `#id`.
|
|
306
476
|
|
|
307
477
|
```tsx
|
|
308
|
-
import {
|
|
478
|
+
import { TableOfContents } from '@k8ordo/ui';
|
|
309
479
|
|
|
310
|
-
<
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
480
|
+
<TableOfContents
|
|
481
|
+
items={[
|
|
482
|
+
{ id: 'install', label: 'Install' },
|
|
483
|
+
{
|
|
484
|
+
id: 'usage',
|
|
485
|
+
label: 'Usage',
|
|
486
|
+
children: [{ id: 'usage-basic', label: 'Basics' }],
|
|
487
|
+
},
|
|
488
|
+
]}
|
|
489
|
+
/>;
|
|
314
490
|
```
|
|
315
491
|
|
|
492
|
+
- The heading being read is the last one whose start has passed the heading's
|
|
493
|
+
own `scroll-margin-block-start`: set the margin your sticky header needs on
|
|
494
|
+
the headings, and a heading reached from the contents becomes the current
|
|
495
|
+
one. At the end of the document, the last heading is current even if its
|
|
496
|
+
section is too short to reach that line.
|
|
497
|
+
- It follows the document's scroll: the headings are expected to scroll with
|
|
498
|
+
the page, and `items` to be in document order. It measures on scroll, on
|
|
499
|
+
resize, and when the page's size changes. In a vertical document it reads
|
|
500
|
+
from right to left (`vertical-rl`) or left to right (`vertical-lr`).
|
|
501
|
+
- `label` replaces the title (`tableOfContents` in the message dictionary),
|
|
502
|
+
which also names the `nav`.
|
|
503
|
+
|
|
316
504
|
Props:
|
|
317
505
|
|
|
318
|
-
- `
|
|
319
|
-
- `
|
|
506
|
+
- `items`: `readonly TableOfContentsItem[]` (required)
|
|
507
|
+
- `label`: `string`
|
|
508
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `children` / `aria-labelledby`.
|
|
320
509
|
|
|
321
|
-
###
|
|
510
|
+
### Tabs
|
|
322
511
|
|
|
323
|
-
|
|
512
|
+
Tab switching, as a compound component.
|
|
324
513
|
|
|
325
514
|
```tsx
|
|
326
|
-
import {
|
|
515
|
+
import { Tabs } from '@k8ordo/ui';
|
|
327
516
|
|
|
328
|
-
<
|
|
329
|
-
<
|
|
517
|
+
<Tabs.Root ids={['tab1', 'tab2']}>
|
|
518
|
+
<Tabs.List label="Tabs">
|
|
519
|
+
<Tabs.Tab id="tab1">Tab 1</Tabs.Tab>
|
|
520
|
+
<Tabs.Tab id="tab2">Tab 2</Tabs.Tab>
|
|
521
|
+
</Tabs.List>
|
|
522
|
+
<Tabs.Panel id="tab1">Panel 1</Tabs.Panel>
|
|
523
|
+
<Tabs.Panel id="tab2">Panel 2</Tabs.Panel>
|
|
524
|
+
</Tabs.Root>;
|
|
330
525
|
```
|
|
331
526
|
|
|
332
|
-
|
|
527
|
+
Selecting a tab is a transition: the panels cross-fade through React's
|
|
528
|
+
`<ViewTransition>` (off under `prefers-reduced-motion`), and a panel that
|
|
529
|
+
suspends keeps the current one on screen until it is ready.
|
|
333
530
|
|
|
334
|
-
-
|
|
531
|
+
The keys follow the WAI-ARIA tabs pattern, and focusing a tab selects it.
|
|
532
|
+
`ArrowLeft` / `ArrowRight` move to the previous / next tab, wrapping at the
|
|
533
|
+
ends, and swap under `dir="rtl"`; in vertical writing mode the list is
|
|
534
|
+
vertical and `ArrowUp` / `ArrowDown` do the same. `Home` / `End` go to the
|
|
535
|
+
first / last tab.
|
|
335
536
|
|
|
336
|
-
|
|
537
|
+
Props (Tabs.Root):
|
|
337
538
|
|
|
338
|
-
|
|
539
|
+
- `ids`: `[string, ...string[]]` (required)
|
|
540
|
+
- `children`: `ReactNode`
|
|
541
|
+
- `defaultSelectedId`: `string` | `null` (default: `null`)
|
|
542
|
+
- `onChange`: `(id: string) => void`
|
|
543
|
+
- `selectedId`: `string`
|
|
339
544
|
|
|
340
|
-
|
|
341
|
-
import { Stack } from '@k8ordo/ui';
|
|
545
|
+
Props (Tabs.List):
|
|
342
546
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
<Card>2</Card>
|
|
346
|
-
</Stack>
|
|
547
|
+
- `label`: `string` (required)
|
|
548
|
+
- `children`: `ReactNode`
|
|
347
549
|
|
|
348
|
-
|
|
349
|
-
<Heading level="h2">Title</Heading>
|
|
350
|
-
<Button>Action</Button>
|
|
351
|
-
</Stack>
|
|
352
|
-
```
|
|
550
|
+
Props (Tabs.Panel):
|
|
353
551
|
|
|
354
|
-
|
|
552
|
+
- `id`: `string` (required)
|
|
553
|
+
- `children`: `ReactNode`
|
|
355
554
|
|
|
356
|
-
|
|
555
|
+
Props (Tabs.Tab):
|
|
556
|
+
|
|
557
|
+
- `id`: `string` (required)
|
|
357
558
|
- `children`: `ReactNode`
|
|
358
|
-
- `direction`: `'row'` | `'column'` (default: `'column'`)
|
|
359
|
-
- `gap`: `GapSize` (default: `'md'`)
|
|
360
|
-
- `justify`: `'start'` | `'center'` | `'end'` | `'between'`
|
|
361
|
-
- `padding`: `PaddingSize`
|
|
362
559
|
|
|
363
|
-
###
|
|
560
|
+
### Anchor
|
|
364
561
|
|
|
365
|
-
A
|
|
562
|
+
A text link. External links automatically get a new-tab icon.
|
|
366
563
|
|
|
367
564
|
```tsx
|
|
368
|
-
import {
|
|
369
|
-
|
|
370
|
-
<Grid cols={3} gap="md">
|
|
371
|
-
<Card>1</Card>
|
|
372
|
-
<Card>2</Card>
|
|
373
|
-
<Card>3</Card>
|
|
374
|
-
</Grid>
|
|
565
|
+
import { Anchor } from '@k8ordo/ui';
|
|
375
566
|
|
|
376
|
-
<
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
))}
|
|
380
|
-
</Grid>
|
|
567
|
+
<Anchor href="https://example.com">External link</Anchor>
|
|
568
|
+
<Anchor href="/about">Internal link</Anchor>
|
|
569
|
+
<Anchor href="/docs" openInNewTab>Open in a new tab</Anchor>
|
|
381
570
|
```
|
|
382
571
|
|
|
383
572
|
Props:
|
|
384
573
|
|
|
385
|
-
- `children`: `ReactNode`
|
|
386
|
-
- `
|
|
387
|
-
- `
|
|
388
|
-
- `
|
|
574
|
+
- `children`: `ReactNode` (required)
|
|
575
|
+
- `href`: `T` (required)
|
|
576
|
+
- `openInNewTab`: `boolean` (default: `false`)
|
|
577
|
+
- `renderAnchor`: `(props: RenderAnchorProps<T>) => ReactNode` (default: `defaultRenderAnchor`)
|
|
578
|
+
- Other props are forwarded to `AnchorHTMLAttributes<HTMLAnchorElement>`, except `target` / `rel` / `className` / `style`.
|
|
579
|
+
|
|
580
|
+
`renderAnchor` replaces the `<a>`; its bag is described under
|
|
581
|
+
[Render props](#render-props).
|
|
389
582
|
|
|
390
583
|
## Forms
|
|
391
584
|
|
|
392
|
-
Form components are used together with `FormControl`'s `renderInput` pattern. Every form component supports both controlled and uncontrolled use.
|
|
585
|
+
Form components are used together with `FormControl`'s `renderInput` pattern. Every form component except `FileField` supports both controlled and uncontrolled use; `FileField.Root` takes only `defaultValue` and keeps the selected files itself.
|
|
586
|
+
|
|
587
|
+
`ref` reaches the real element (`input` / `textarea` / `select` / `fieldset`). `FileField` uses a ref internally but composes it with yours, so the `ref` you pass still reaches the element. `Radio` (a group that renders several inputs) puts its `ref` on the radiogroup `<div>`, and `FormControl` (a wrapper) on its wrapper element — a `<div>`, or a `<fieldset>` with `labelAs="legend"`.
|
|
393
588
|
|
|
394
|
-
|
|
589
|
+
An uncontrolled field keeps its value in the DOM, so a form reset — `form.reset()`, a reset button, or React resetting the form after an action — puts it back to its `defaultValue`, and a `defaultValue` that changes after mount (the values a failed submission echoes back) is where the next reset goes. A value a component changes in code (a stepper, a chosen option, a removed file) is announced with an `input` event, so a form library listening on the `<form>` hears it like typing.
|
|
590
|
+
|
|
591
|
+
Every field takes the attributes `@k8ordo/form`'s `formFields` derives as they are — spread `field.input` after `FormControl`'s props. Its guide's "Working with @k8ordo/ui" section has one example per component.
|
|
395
592
|
|
|
396
593
|
### Form
|
|
397
594
|
|
|
@@ -414,6 +611,8 @@ Props:
|
|
|
414
611
|
|
|
415
612
|
- `children`: `ReactNode` (required)
|
|
416
613
|
- `action`: `((formData: FormData) => void | Promise<void>)` | `string`
|
|
614
|
+
- `ref`: `Ref<HTMLFormElement>`
|
|
615
|
+
- Other props are forwarded to `FormHTMLAttributes<HTMLFormElement>`, except `className` / `style`.
|
|
417
616
|
|
|
418
617
|
### FormControl
|
|
419
618
|
|
|
@@ -444,6 +643,7 @@ Props:
|
|
|
444
643
|
- `labelAs`: `'label'` | `'legend'` (default: `'label'`)
|
|
445
644
|
- `ref`: `Ref<HTMLElement>`
|
|
446
645
|
- `required`: `boolean` (default: `false`)
|
|
646
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `children`.
|
|
447
647
|
|
|
448
648
|
`renderInput` receives `{ id, 'aria-describedby', 'aria-labelledby', disabled, invalid, required }`.
|
|
449
649
|
|
|
@@ -464,14 +664,21 @@ import { TextField } from '@k8ordo/ui';
|
|
|
464
664
|
|
|
465
665
|
// type can be passed too (default: "text")
|
|
466
666
|
<TextField id="tel" type="tel" inputMode="numeric" />
|
|
667
|
+
|
|
668
|
+
// date and time inputs render as the browser's own pickers
|
|
669
|
+
<TextField id="birthday" type="date" />
|
|
467
670
|
```
|
|
468
671
|
|
|
672
|
+
`type` also accepts any string, so the `type` `@k8ordo/form` derives
|
|
673
|
+
(`email`, `url`, `date`, `time`, `datetime-local`, …) spreads as it is.
|
|
674
|
+
|
|
469
675
|
Props:
|
|
470
676
|
|
|
471
677
|
- `children`: `ReactNode`
|
|
472
678
|
- `invalid`: `boolean` (default: `false`)
|
|
473
679
|
- `ref`: `Ref<HTMLInputElement>`
|
|
474
|
-
- `type`: `
|
|
680
|
+
- `type`: `'date'` | `'datetime-local'` | `'email'` | `'month'` | `'search'` | `'tel'` | `'text'` | `'time'` | `'url'` | `'week'` | `(string & Record<never, never>)` (default: `'text'`)
|
|
681
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `className` / `style`.
|
|
475
682
|
|
|
476
683
|
### Textarea
|
|
477
684
|
|
|
@@ -495,35 +702,178 @@ Props:
|
|
|
495
702
|
- `fullHeight`: `boolean` (default: `false`)
|
|
496
703
|
- `invalid`: `boolean` (default: `false`)
|
|
497
704
|
- `ref`: `Ref<HTMLTextAreaElement>`
|
|
705
|
+
- `type`: `string`
|
|
706
|
+
- Other props are forwarded to `TextareaHTMLAttributes<HTMLTextAreaElement>`, except `className` / `style`.
|
|
707
|
+
|
|
708
|
+
`type` is accepted and dropped: a `<textarea>` has none, and the attributes
|
|
709
|
+
`@k8ordo/form` derives for a string carry `type="text"`. A `pattern` is not
|
|
710
|
+
checked by the browser on a `<textarea>` either.
|
|
498
711
|
|
|
499
712
|
### NumberField
|
|
500
713
|
|
|
501
714
|
```tsx
|
|
502
715
|
import { NumberField } from '@k8ordo/ui';
|
|
503
716
|
|
|
717
|
+
// Uncontrolled: starts empty unless defaultValue is given
|
|
718
|
+
<NumberField
|
|
719
|
+
id="quantity"
|
|
720
|
+
name="quantity"
|
|
721
|
+
min={0}
|
|
722
|
+
max={100}
|
|
723
|
+
invalid={false}
|
|
724
|
+
disabled={false}
|
|
725
|
+
required={false}
|
|
726
|
+
/>;
|
|
727
|
+
|
|
728
|
+
// Controlled: null is an empty field
|
|
729
|
+
const [value, setValue] = useState<number | null>(null);
|
|
504
730
|
<NumberField
|
|
505
731
|
id="quantity"
|
|
506
732
|
min={0}
|
|
507
733
|
max={100}
|
|
508
734
|
value={value}
|
|
509
|
-
onChange={
|
|
735
|
+
onChange={setValue}
|
|
510
736
|
invalid={false}
|
|
511
737
|
disabled={false}
|
|
512
738
|
required={false}
|
|
513
739
|
/>;
|
|
514
740
|
```
|
|
515
741
|
|
|
742
|
+
An empty field is `null`, never `0`. While empty the input submits `''` and
|
|
743
|
+
has no `aria-valuenow`, and leaving it keeps it empty; clearing the field
|
|
744
|
+
reports `null` to `onChange`. The first ArrowUp / ArrowDown or stepper press on
|
|
745
|
+
an empty field fills in `0`, or the nearer of `min` / `max` when `0` is out of
|
|
746
|
+
range. `required` reaches the input itself, so an empty required field fails
|
|
747
|
+
native validation.
|
|
748
|
+
|
|
749
|
+
The input is `type="text"` (so it can format to `precision` and step with the
|
|
750
|
+
arrow keys), which the browser does not range-check — so the field does it: a
|
|
751
|
+
value below `min` or above `max` is reported with `setCustomValidity`, using
|
|
752
|
+
the `numberFieldRangeUnderflow` / `numberFieldRangeOverflow` wording. Leaving the
|
|
753
|
+
field clamps the value into range. A message set by someone else on the same
|
|
754
|
+
input — a form library's cross-field rule — is left alone.
|
|
755
|
+
|
|
756
|
+
`min`, `max`, and an uncontrolled `defaultValue` also take strings, and `step`
|
|
757
|
+
takes `'any'`, which is what `@k8ordo/form` derives. `precision` defaults to the
|
|
758
|
+
number of decimals in `step`; with `step="any"` the value is not rounded.
|
|
759
|
+
|
|
760
|
+
A value the field writes itself — an arrow key, a stepper press, formatting on
|
|
761
|
+
blur — is announced with one `input` event, and `onChange` is still called once.
|
|
762
|
+
|
|
763
|
+
A form reset — `form.reset()`, a reset button, or React resetting the form after
|
|
764
|
+
an action — puts an uncontrolled field back to `defaultValue` (or to empty) and
|
|
765
|
+
reports that value to `onChange`. A controlled field keeps its `value`; reset
|
|
766
|
+
your own state from the form's `onReset`.
|
|
767
|
+
|
|
516
768
|
Props:
|
|
517
769
|
|
|
518
|
-
- `defaultValue`: `
|
|
770
|
+
- `defaultValue`: `number` | `string`
|
|
519
771
|
- `invalid`: `boolean` (default: `false`)
|
|
520
|
-
- `max`: `number`
|
|
521
|
-
- `min`: `number`
|
|
522
|
-
- `onChange`: `(value: number) => void`
|
|
523
|
-
- `precision`: `number`
|
|
772
|
+
- `max`: `number` | `string`
|
|
773
|
+
- `min`: `number` | `string`
|
|
774
|
+
- `onChange`: `(value: number | null) => void`
|
|
775
|
+
- `precision`: `number`
|
|
524
776
|
- `ref`: `Ref<HTMLInputElement>`
|
|
525
|
-
- `step`: `number` (default: `1`)
|
|
526
|
-
- `value`: `number`
|
|
777
|
+
- `step`: `number` | `'any'` (default: `1`)
|
|
778
|
+
- `value`: `number` | `null`
|
|
779
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `role` / `className` / `style` / `children`.
|
|
780
|
+
|
|
781
|
+
### DateField
|
|
782
|
+
|
|
783
|
+
A native `<input type="date">` styled like `TextField`. The value is a
|
|
784
|
+
`YYYY-MM-DD` string (`''` when empty), and the browser checks `min` / `max` /
|
|
785
|
+
`required` itself. `onChange` is the native event, as with `TextField`.
|
|
786
|
+
|
|
787
|
+
It accepts what `@k8ordo/form` derives from `z.iso.date()` as is: spread the
|
|
788
|
+
field's `input` and nothing needs to be taken out. `type` is always `date`, so a
|
|
789
|
+
`type` in the spread does not replace it.
|
|
790
|
+
|
|
791
|
+
```tsx
|
|
792
|
+
import { DateField, FormControl } from '@k8ordo/ui';
|
|
793
|
+
|
|
794
|
+
const eventDate = form.field('eventDate'); // z.iso.date()
|
|
795
|
+
|
|
796
|
+
<FormControl
|
|
797
|
+
errorText={eventDate.error}
|
|
798
|
+
invalid={eventDate.invalid}
|
|
799
|
+
label="Date"
|
|
800
|
+
required={eventDate.required}
|
|
801
|
+
renderInput={(props) => <DateField {...props} {...eventDate.input} />}
|
|
802
|
+
/>;
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
Props:
|
|
806
|
+
|
|
807
|
+
- `invalid`: `boolean` (default: `false`)
|
|
808
|
+
- `ref`: `Ref<HTMLInputElement>`
|
|
809
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `className` / `style` / `type` / `children`.
|
|
810
|
+
|
|
811
|
+
### DatePicker
|
|
812
|
+
|
|
813
|
+
`DateField` with a button that opens `Calendar` in a popover. The field is the
|
|
814
|
+
same native `<input type="date">`, so typing a date, `name`, `required`, and
|
|
815
|
+
`min` / `max` all work as they do on `DateField`, and spreading a derived
|
|
816
|
+
`@k8ordo/form` field works the same way. `onChange` takes the value
|
|
817
|
+
(`YYYY-MM-DD`, `''` when cleared), not the event, because a date picked from the
|
|
818
|
+
calendar has no input event of its own.
|
|
819
|
+
|
|
820
|
+
Picking a date writes it into the input and dispatches an `input` event, so a
|
|
821
|
+
form sees it exactly as if it had been typed (dirty state, rules, and clearing
|
|
822
|
+
an error). The popover closes and focus returns to the calendar button.
|
|
823
|
+
|
|
824
|
+
Firefox draws its own calendar button inside every `<input type="date">` and
|
|
825
|
+
offers no way to hide it, so there the field shows two calendar buttons: the
|
|
826
|
+
browser's and this component's. Chromium and Safari show only this one.
|
|
827
|
+
|
|
828
|
+
```tsx
|
|
829
|
+
import { DatePicker } from '@k8ordo/ui';
|
|
830
|
+
|
|
831
|
+
<DatePicker
|
|
832
|
+
aria-label="Check-in"
|
|
833
|
+
min="2026-01-01"
|
|
834
|
+
name="checkIn"
|
|
835
|
+
onChange={setCheckIn}
|
|
836
|
+
value={checkIn}
|
|
837
|
+
/>;
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
Props:
|
|
841
|
+
|
|
842
|
+
- `defaultValue`: `string`
|
|
843
|
+
- `invalid`: `boolean` (default: `false`)
|
|
844
|
+
- `onChange`: `(value: string) => void`
|
|
845
|
+
- `ref`: `Ref<HTMLInputElement>`
|
|
846
|
+
- `value`: `string`
|
|
847
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `className` / `style` / `type` / `children`.
|
|
848
|
+
|
|
849
|
+
### Calendar
|
|
850
|
+
|
|
851
|
+
A month grid for picking one day (the WAI-ARIA date picker grid). The value is
|
|
852
|
+
a `YYYY-MM-DD` string. Arrow keys move by day and week, `Home` / `End` to the
|
|
853
|
+
ends of the week, `PageUp` / `PageDown` by month (with `Shift`, by year), and
|
|
854
|
+
`Enter` / `Space` select. Days outside `min` / `max` stay focusable but cannot
|
|
855
|
+
be selected.
|
|
856
|
+
|
|
857
|
+
Month and weekday names, and the first day of the week, follow the same locale
|
|
858
|
+
as the built-in wording (i18n, below). Today is marked with
|
|
859
|
+
`aria-current="date"` in the visitor's time zone, which only the browser knows,
|
|
860
|
+
so the calendar renders in the browser alone: the server writes an empty box of
|
|
861
|
+
the same size. It submits nothing; inside a form, use `DatePicker` or
|
|
862
|
+
`DateField`.
|
|
863
|
+
|
|
864
|
+
```tsx
|
|
865
|
+
import { Calendar } from '@k8ordo/ui';
|
|
866
|
+
|
|
867
|
+
<Calendar defaultValue="2026-09-25" max="2026-12-31" onChange={setDay} />;
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
Props:
|
|
871
|
+
|
|
872
|
+
- `defaultValue`: `string`
|
|
873
|
+
- `max`: `string`
|
|
874
|
+
- `min`: `string`
|
|
875
|
+
- `onChange`: `(value: string) => void`
|
|
876
|
+
- `value`: `string` | `null`
|
|
527
877
|
|
|
528
878
|
### PasswordInput
|
|
529
879
|
|
|
@@ -551,6 +901,10 @@ Props:
|
|
|
551
901
|
- `invalid`: `boolean` (default: `false`)
|
|
552
902
|
- `ref`: `Ref<HTMLInputElement>`
|
|
553
903
|
- `showLabel`: `string`
|
|
904
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style`.
|
|
905
|
+
|
|
906
|
+
A spread `type="password"` (what `@k8ordo/form` derives for a password) does not
|
|
907
|
+
override the show/hide toggle.
|
|
554
908
|
|
|
555
909
|
### Select
|
|
556
910
|
|
|
@@ -577,10 +931,29 @@ Props:
|
|
|
577
931
|
- `children`: `ReactNode`
|
|
578
932
|
- `invalid`: `boolean` (default: `false`)
|
|
579
933
|
- `ref`: `Ref<HTMLSelectElement>`
|
|
934
|
+
- Other props are forwarded to `SelectHTMLAttributes<HTMLSelectElement>`, except `className` / `style`.
|
|
935
|
+
|
|
936
|
+
React applies a `<select>`'s `defaultValue` only when it mounts; `Select` also
|
|
937
|
+
applies a later one, so the next reset — React's after a form action included —
|
|
938
|
+
goes back to it. With `required`, put a placeholder option whose `value` is
|
|
939
|
+
`''` first; leaving it selected fails validation.
|
|
580
940
|
|
|
581
941
|
### Autocomplete
|
|
582
942
|
|
|
583
|
-
A multi-select autocomplete
|
|
943
|
+
A multi-select autocomplete over a fixed list of options, shown as removable
|
|
944
|
+
tags. `value` and `onChange` are `string[]`. To pick one option, or to search a
|
|
945
|
+
list that lives on a server, use `Combobox`.
|
|
946
|
+
|
|
947
|
+
Typing filters `options` by label. `ArrowDown` / `ArrowUp` open the list and
|
|
948
|
+
move through it, `Enter` adds or removes the active option, `Backspace` in an
|
|
949
|
+
empty field removes the last tag, and `Escape` closes the list. Keys pressed
|
|
950
|
+
while an IME is composing belong to the IME.
|
|
951
|
+
|
|
952
|
+
With a `name`, the selection is submitted through a visually hidden
|
|
953
|
+
`<select multiple>`: one entry per selected value, and `required` means at
|
|
954
|
+
least one. It is always present — nothing selected included — so native
|
|
955
|
+
validation and a form library's rules reach it, and when a form moves focus to
|
|
956
|
+
it after a failure, focus goes on to the text input.
|
|
584
957
|
|
|
585
958
|
```tsx
|
|
586
959
|
import { Autocomplete } from '@k8ordo/ui';
|
|
@@ -600,16 +973,123 @@ Props:
|
|
|
600
973
|
|
|
601
974
|
- `id`: `string` (required)
|
|
602
975
|
- `options`: `readonly Option[]` (required)
|
|
603
|
-
- `defaultValue`: `
|
|
976
|
+
- `defaultValue`: `string[]`
|
|
604
977
|
- `invalid`: `boolean` (default: `false`)
|
|
605
978
|
- `onChange`: `(value: string[]) => void`
|
|
606
979
|
- `ref`: `Ref<HTMLInputElement>`
|
|
607
980
|
- `value`: `string[]`
|
|
981
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `role` / `className` / `style` / `children` / `autoComplete` / `aria-autocomplete` / `aria-controls` / `aria-expanded` / `aria-activedescendant`.
|
|
982
|
+
|
|
983
|
+
### ColorPicker
|
|
984
|
+
|
|
985
|
+
A color field whose value is `#rrggbb`. The value lives in a text input that
|
|
986
|
+
carries `name`, so it submits and resets like any other field; hue, saturation,
|
|
987
|
+
and lightness sliders and optional `swatches` (toggle buttons named by their
|
|
988
|
+
`label`) write into it and announce the change with an `input` event, as if it
|
|
989
|
+
had been typed. The color chip beside the text input is a native
|
|
990
|
+
`<input type="color">`, named by the built-in `colorPickerOpen` wording: pressing
|
|
991
|
+
it opens the browser's own color picker, which writes into the text input the
|
|
992
|
+
same way, while it is dragged too. It carries no `name`, so only the text input
|
|
993
|
+
submits; with no color yet it opens on the sliders' gray. The chip, sliders,
|
|
994
|
+
and swatches are all disabled while the field is `disabled`, `readOnly`, or its
|
|
995
|
+
form is pending. Typing reports a color only once it has six digits; on blur and
|
|
996
|
+
on Enter the text is tidied to lowercase `#rrggbb`, and a three-digit `#f80` is
|
|
997
|
+
expanded. Emptying the field reports `''`. Spread `@k8ordo/form`'s `input` onto
|
|
998
|
+
it as is: a `.regex()` in the schema arrives as `pattern` and replaces the
|
|
999
|
+
built-in `#[0-9a-fA-F]{6}`. The panel stays horizontal inside vertical writing
|
|
1000
|
+
mode.
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
import { ColorPicker, FormControl } from '@k8ordo/ui';
|
|
1004
|
+
|
|
1005
|
+
<FormControl
|
|
1006
|
+
label="Accent color"
|
|
1007
|
+
renderInput={(props) => (
|
|
1008
|
+
<ColorPicker
|
|
1009
|
+
{...props}
|
|
1010
|
+
defaultValue="#0d9488"
|
|
1011
|
+
name="accent"
|
|
1012
|
+
swatches={[
|
|
1013
|
+
{ value: '#0d9488', label: 'Teal' },
|
|
1014
|
+
{ value: '#f97316', label: 'Orange' },
|
|
1015
|
+
]}
|
|
1016
|
+
/>
|
|
1017
|
+
)}
|
|
1018
|
+
/>;
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
Props:
|
|
1022
|
+
|
|
1023
|
+
- `defaultValue`: `string`
|
|
1024
|
+
- `invalid`: `boolean` (default: `false`)
|
|
1025
|
+
- `onChange`: `(value: string) => void`
|
|
1026
|
+
- `ref`: `Ref<HTMLInputElement>`
|
|
1027
|
+
- `swatches`: `readonly ColorPickerSwatch[]`
|
|
1028
|
+
- `type`: `string`
|
|
1029
|
+
- `value`: `string`
|
|
1030
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `className` / `style` / `children`.
|
|
1031
|
+
|
|
1032
|
+
### Combobox
|
|
1033
|
+
|
|
1034
|
+
A text field with a list of options, for picking one (the WAI-ARIA combobox
|
|
1035
|
+
with list autocomplete and manual selection). `value` and `onChange` are the
|
|
1036
|
+
chosen option's `value`, `''` when nothing is chosen. To pick several from a
|
|
1037
|
+
fixed list, use `Autocomplete`.
|
|
1038
|
+
|
|
1039
|
+
Typing opens the list; nothing is chosen until an option is picked with a click
|
|
1040
|
+
or `Enter`, so leaving half-typed text puts the chosen option's label back, and
|
|
1041
|
+
leaving the field empty clears the choice. `ArrowDown` / `ArrowUp` open the list
|
|
1042
|
+
and move through it (`Alt+ArrowDown` opens it without entering it), `Escape`
|
|
1043
|
+
closes it and, pressed again, drops what was typed. Keys pressed while an IME is
|
|
1044
|
+
composing belong to the IME.
|
|
1045
|
+
|
|
1046
|
+
Without `search`, typing filters `options` by label. With `search`, typing
|
|
1047
|
+
calls it for the options instead — each call gets a `signal` that aborts when
|
|
1048
|
+
the text changes again, so pass it to `fetch` — and `options` becomes the list
|
|
1049
|
+
shown before anything is typed and where the current value's label is looked
|
|
1050
|
+
up. While a search runs, the list is marked busy; a failed one says so with
|
|
1051
|
+
`comboboxFailed`, an empty result with `comboboxEmpty`.
|
|
1052
|
+
|
|
1053
|
+
With a `name`, the choice is submitted through a visually hidden `<select>`, the
|
|
1054
|
+
same way `Autocomplete` submits, so `required`, a form library's rules, reset,
|
|
1055
|
+
and moving focus to the field after a failure all work on it.
|
|
1056
|
+
|
|
1057
|
+
```tsx
|
|
1058
|
+
import { Combobox } from '@k8ordo/ui';
|
|
1059
|
+
|
|
1060
|
+
<Combobox aria-label="Prefecture" name="prefecture" options={prefectures} />;
|
|
1061
|
+
|
|
1062
|
+
// Options from a server
|
|
1063
|
+
<Combobox
|
|
1064
|
+
aria-label="City"
|
|
1065
|
+
name="city"
|
|
1066
|
+
search={async (query, { signal }) => {
|
|
1067
|
+
const response = await fetch(`/api/cities?q=${encodeURIComponent(query)}`, {
|
|
1068
|
+
signal,
|
|
1069
|
+
});
|
|
1070
|
+
return response.json();
|
|
1071
|
+
}}
|
|
1072
|
+
/>;
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
Props:
|
|
1076
|
+
|
|
1077
|
+
- `defaultValue`: `string`
|
|
1078
|
+
- `invalid`: `boolean` (default: `false`)
|
|
1079
|
+
- `onChange`: `(value: string) => void`
|
|
1080
|
+
- `options`: `readonly Option[]`
|
|
1081
|
+
- `ref`: `Ref<HTMLInputElement>`
|
|
1082
|
+
- `search`: `ComboboxSearch`
|
|
1083
|
+
- `type`: `string`
|
|
1084
|
+
- `value`: `string`
|
|
1085
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `role` / `className` / `style` / `children` / `autoComplete` / `aria-autocomplete` / `aria-controls` / `aria-expanded` / `aria-activedescendant`.
|
|
608
1086
|
|
|
609
1087
|
### Checkbox
|
|
610
1088
|
|
|
611
1089
|
The label is passed as the `label` prop, not as children. `onChange` is `(checked, event)`.
|
|
612
1090
|
|
|
1091
|
+
`itemValue` is the input's `value`: the string a checked box submits under its `name`. Without it the box renders no `value` and submits the browser's default, `on`. There is no `value` prop — on a checkbox it is easily mistaken for the checked state, which is `checked`.
|
|
1092
|
+
|
|
613
1093
|
```tsx
|
|
614
1094
|
import { Checkbox } from '@k8ordo/ui';
|
|
615
1095
|
|
|
@@ -618,17 +1098,28 @@ import { Checkbox } from '@k8ordo/ui';
|
|
|
618
1098
|
|
|
619
1099
|
// Uncontrolled
|
|
620
1100
|
<Checkbox defaultChecked label="I agree" />
|
|
1101
|
+
|
|
1102
|
+
// Submits inStock=true when checked, instead of inStock=on
|
|
1103
|
+
<Checkbox itemValue="true" label="In stock only" name="inStock" />
|
|
621
1104
|
```
|
|
622
1105
|
|
|
1106
|
+
`indeterminate` shows the mixed state — some of a set selected — and sets the
|
|
1107
|
+
input's `indeterminate` property, so it is announced as partly checked.
|
|
1108
|
+
`labelHidden` keeps `label` as the accessible name but does not draw it, for a
|
|
1109
|
+
place with no room for text such as a table cell.
|
|
1110
|
+
|
|
623
1111
|
Props:
|
|
624
1112
|
|
|
625
1113
|
- `label`: `string` (required)
|
|
626
1114
|
- `checked`: `boolean`
|
|
627
|
-
- `defaultChecked`: `
|
|
1115
|
+
- `defaultChecked`: `boolean`
|
|
1116
|
+
- `indeterminate`: `boolean` (default: `false`)
|
|
628
1117
|
- `invalid`: `boolean` (default: `false`)
|
|
629
1118
|
- `itemValue`: `string`
|
|
1119
|
+
- `labelHidden`: `boolean` (default: `false`)
|
|
630
1120
|
- `onChange`: `(checked: boolean, event: ChangeEvent<HTMLInputElement>) => void`
|
|
631
1121
|
- `ref`: `Ref<HTMLInputElement>`
|
|
1122
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style` / `value` / `children`.
|
|
632
1123
|
|
|
633
1124
|
### CheckboxGroup
|
|
634
1125
|
|
|
@@ -638,6 +1129,10 @@ The group's selection lives in `value` / `onChange` (`string[]`). That is a diff
|
|
|
638
1129
|
|
|
639
1130
|
It renders a `fieldset[role="group"]`, so `aria-labelledby` is required. Convey that the group is required through the referenced label element (for example `FormControl`'s required marker). `role="group"` does not allow `aria-required`, so do not put it on the group.
|
|
640
1131
|
|
|
1132
|
+
Uncontrolled, the selection lives in the checkboxes themselves: `onChange`
|
|
1133
|
+
receives the checked values in document order, and a form reset puts the boxes
|
|
1134
|
+
back to `defaultValue` without leaving a stale selection behind.
|
|
1135
|
+
|
|
641
1136
|
```tsx
|
|
642
1137
|
import { CheckboxGroup } from '@k8ordo/ui';
|
|
643
1138
|
|
|
@@ -657,31 +1152,38 @@ Props (CheckboxGroup.Item):
|
|
|
657
1152
|
|
|
658
1153
|
- `label`: `string` (required)
|
|
659
1154
|
- `checked`: `boolean`
|
|
660
|
-
- `defaultChecked`: `
|
|
1155
|
+
- `defaultChecked`: `boolean`
|
|
1156
|
+
- `indeterminate`: `boolean` (default: `false`)
|
|
661
1157
|
- `invalid`: `boolean` (default: `false`)
|
|
662
1158
|
- `itemValue`: `string`
|
|
1159
|
+
- `labelHidden`: `boolean` (default: `false`)
|
|
663
1160
|
- `onChange`: `(checked: boolean, event: ChangeEvent<HTMLInputElement>) => void`
|
|
664
1161
|
- `ref`: `Ref<HTMLInputElement>`
|
|
1162
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style` / `value` / `children`.
|
|
665
1163
|
|
|
666
1164
|
Props (CheckboxGroup.Root):
|
|
667
1165
|
|
|
668
1166
|
- `aria-labelledby`: `string` (required)
|
|
669
1167
|
- `name`: `string` (required)
|
|
670
1168
|
- `children`: `ReactNode`
|
|
671
|
-
- `defaultValue`: `
|
|
1169
|
+
- `defaultValue`: `string[]`
|
|
672
1170
|
- `invalid`: `boolean` (default: `false`)
|
|
673
1171
|
- `onChange`: `(value: string[]) => void`
|
|
674
1172
|
- `ref`: `Ref<HTMLFieldSetElement>`
|
|
675
1173
|
- `value`: `string[]`
|
|
1174
|
+
- Other props are forwarded to `FieldsetHTMLAttributes<HTMLFieldSetElement>`, except `className` / `style` / `role`.
|
|
676
1175
|
|
|
677
1176
|
### CheckboxCard
|
|
678
1177
|
|
|
679
|
-
A card-styled checkbox.
|
|
1178
|
+
A card-styled checkbox. Uncontrolled, the selection and its look follow the
|
|
1179
|
+
checkboxes themselves (`:checked`), so a form reset restores both; `onChange`
|
|
1180
|
+
receives the checked values in document order.
|
|
680
1181
|
|
|
681
1182
|
```tsx
|
|
682
1183
|
import { CheckboxCard } from '@k8ordo/ui';
|
|
683
1184
|
|
|
684
1185
|
<CheckboxCard
|
|
1186
|
+
aria-labelledby="plan-checkbox"
|
|
685
1187
|
name="plan"
|
|
686
1188
|
disabled={false}
|
|
687
1189
|
options={[
|
|
@@ -702,11 +1204,12 @@ Props:
|
|
|
702
1204
|
|
|
703
1205
|
- `aria-labelledby`: `string` (required)
|
|
704
1206
|
- `options`: `readonly CheckboxCardOption[]` (required)
|
|
705
|
-
- `defaultValue`: `
|
|
1207
|
+
- `defaultValue`: `string[]`
|
|
706
1208
|
- `invalid`: `boolean` (default: `false`)
|
|
707
1209
|
- `onChange`: `(value: string[]) => void`
|
|
708
1210
|
- `ref`: `Ref<HTMLFieldSetElement>`
|
|
709
1211
|
- `value`: `string[]`
|
|
1212
|
+
- Other props are forwarded to `FieldsetHTMLAttributes<HTMLFieldSetElement>`, except `className` / `style` / `children` / `role`.
|
|
710
1213
|
|
|
711
1214
|
### Radio
|
|
712
1215
|
|
|
@@ -729,16 +1232,19 @@ Props:
|
|
|
729
1232
|
|
|
730
1233
|
- `aria-labelledby`: `string` (required)
|
|
731
1234
|
- `options`: `readonly Option[]` (required)
|
|
1235
|
+
- `defaultValue`: `string`
|
|
732
1236
|
- `disabled`: `boolean` (default: `false`)
|
|
733
1237
|
- `invalid`: `boolean` (default: `false`)
|
|
734
1238
|
- `name`: `string`
|
|
735
1239
|
- `onChange`: `(value: string, event: ChangeEvent<HTMLInputElement>) => void`
|
|
736
1240
|
- `ref`: `Ref<HTMLDivElement>`
|
|
1241
|
+
- `required`: `boolean` (default: `false`)
|
|
737
1242
|
- `value`: `string`
|
|
1243
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `role` / `className` / `style` / `children`.
|
|
738
1244
|
|
|
739
1245
|
### RadioCard
|
|
740
1246
|
|
|
741
|
-
A card-styled radio button. Real `input[type="radio"]` elements sit inside a `fieldset[role="radiogroup"]`, so arrow-key roving and single selection are left to the browser. Reach them from tests with `getByRole('radio', { checked })`.
|
|
1247
|
+
A card-styled radio button. Real `input[type="radio"]` elements sit inside a `fieldset[role="radiogroup"]`, so arrow-key roving and single selection are left to the browser. Reach them from tests with `getByRole('radio', { checked })`. `required` goes to every radio, and an uncontrolled selection's look follows `:checked`, so a form reset restores it.
|
|
742
1248
|
|
|
743
1249
|
```tsx
|
|
744
1250
|
import { RadioCard } from '@k8ordo/ui';
|
|
@@ -765,15 +1271,19 @@ Props:
|
|
|
765
1271
|
|
|
766
1272
|
- `aria-labelledby`: `string` (required)
|
|
767
1273
|
- `options`: `readonly RadioCardOption[]` (required)
|
|
768
|
-
- `defaultValue`: `
|
|
1274
|
+
- `defaultValue`: `string`
|
|
769
1275
|
- `invalid`: `boolean` (default: `false`)
|
|
770
1276
|
- `onChange`: `(value: string) => void`
|
|
771
1277
|
- `ref`: `Ref<HTMLFieldSetElement>`
|
|
1278
|
+
- `required`: `boolean` (default: `false`)
|
|
772
1279
|
- `value`: `string`
|
|
1280
|
+
- Other props are forwarded to `FieldsetHTMLAttributes<HTMLFieldSetElement>`, except `className` / `style` / `children` / `role`.
|
|
773
1281
|
|
|
774
1282
|
### Slider
|
|
775
1283
|
|
|
776
|
-
A range slider.
|
|
1284
|
+
A range slider. Uncontrolled, the value lives in the input, so a form reset
|
|
1285
|
+
puts it back to `defaultValue` and the filled track follows. `min`, `max`, and
|
|
1286
|
+
an uncontrolled `defaultValue` also take strings, and `step` takes `'any'`.
|
|
777
1287
|
|
|
778
1288
|
```tsx
|
|
779
1289
|
import { Slider } from '@k8ordo/ui';
|
|
@@ -792,14 +1302,58 @@ import { Slider } from '@k8ordo/ui';
|
|
|
792
1302
|
|
|
793
1303
|
Props:
|
|
794
1304
|
|
|
795
|
-
- `defaultValue`: `
|
|
1305
|
+
- `defaultValue`: `number` | `string`
|
|
796
1306
|
- `invalid`: `boolean` (default: `false`)
|
|
797
|
-
- `max`: `number`
|
|
798
|
-
- `min`: `number`
|
|
1307
|
+
- `max`: `number` | `string`
|
|
1308
|
+
- `min`: `number` | `string`
|
|
799
1309
|
- `onChange`: `(value: number) => void`
|
|
800
1310
|
- `ref`: `Ref<HTMLInputElement>`
|
|
801
|
-
- `step`: `number` (default: `1`)
|
|
1311
|
+
- `step`: `number` | `'any'` (default: `1`)
|
|
802
1312
|
- `value`: `number`
|
|
1313
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style` / `children`.
|
|
1314
|
+
|
|
1315
|
+
### RangeSlider
|
|
1316
|
+
|
|
1317
|
+
A slider with two thumbs for picking a range (the WAI-ARIA multi-thumb slider).
|
|
1318
|
+
Each thumb is a real `<input type="range">`, so the keyboard behaves as the
|
|
1319
|
+
browser's own slider does, and neither thumb can pass the other: the lower
|
|
1320
|
+
thumb's `aria-valuemax` is the upper value and the upper thumb's
|
|
1321
|
+
`aria-valuemin` the lower one. `value` / `defaultValue` / `onChange` carry the
|
|
1322
|
+
pair `[lower, upper]`.
|
|
1323
|
+
|
|
1324
|
+
`name` is a pair too: the two thumbs submit as two form fields. Uncontrolled,
|
|
1325
|
+
the thumbs keep their values in the DOM, so a form's reset and its dirty check
|
|
1326
|
+
(value against default value) work on them as on any input. The whole slider is
|
|
1327
|
+
a `role="group"`: name it with `aria-label` or `aria-labelledby`
|
|
1328
|
+
(`FormControl`'s `renderInput` props work), and each thumb is read as that name
|
|
1329
|
+
followed by the built-in `rangeSliderStart` / `rangeSliderEnd` wording.
|
|
1330
|
+
|
|
1331
|
+
```tsx
|
|
1332
|
+
import { RangeSlider } from '@k8ordo/ui';
|
|
1333
|
+
|
|
1334
|
+
<RangeSlider
|
|
1335
|
+
aria-label="Price"
|
|
1336
|
+
defaultValue={[20, 80]}
|
|
1337
|
+
max={100}
|
|
1338
|
+
min={0}
|
|
1339
|
+
name={['priceMin', 'priceMax']}
|
|
1340
|
+
/>;
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
Props:
|
|
1344
|
+
|
|
1345
|
+
- `defaultValue`: `readonly [number, number]`
|
|
1346
|
+
- `disabled`: `boolean` (default: `false`)
|
|
1347
|
+
- `invalid`: `boolean` (default: `false`)
|
|
1348
|
+
- `max`: `number` (default: `100`)
|
|
1349
|
+
- `min`: `number` (default: `0`)
|
|
1350
|
+
- `name`: `readonly [string, string]`
|
|
1351
|
+
- `onChange`: `(value: [number, number]) => void`
|
|
1352
|
+
- `ref`: `Ref<HTMLDivElement>`
|
|
1353
|
+
- `required`: `boolean` (default: `false`)
|
|
1354
|
+
- `step`: `number` (default: `1`)
|
|
1355
|
+
- `value`: `readonly [number, number]`
|
|
1356
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style` / `children` / `role`.
|
|
803
1357
|
|
|
804
1358
|
### Switch
|
|
805
1359
|
|
|
@@ -822,14 +1376,16 @@ Props:
|
|
|
822
1376
|
|
|
823
1377
|
- `label`: `string` (required)
|
|
824
1378
|
- `checked`: `boolean`
|
|
825
|
-
- `defaultChecked`: `
|
|
1379
|
+
- `defaultChecked`: `boolean`
|
|
826
1380
|
- `invalid`: `boolean` (default: `false`)
|
|
827
1381
|
- `onChange`: `(checked: boolean, event: ChangeEvent<HTMLInputElement>) => void`
|
|
828
1382
|
- `ref`: `Ref<HTMLInputElement>`
|
|
1383
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `role` / `className` / `style` / `value` / `children`.
|
|
829
1384
|
|
|
830
1385
|
### FileField
|
|
831
1386
|
|
|
832
|
-
File upload, as a composite pattern.
|
|
1387
|
+
File upload, as a composite pattern. A string `defaultValue` (the type
|
|
1388
|
+
`@k8ordo/form`'s derived attributes carry) is accepted and ignored.
|
|
833
1389
|
|
|
834
1390
|
```tsx
|
|
835
1391
|
import { FileField } from '@k8ordo/ui';
|
|
@@ -846,15 +1402,53 @@ import { FileField } from '@k8ordo/ui';
|
|
|
846
1402
|
</FileField.Root>;
|
|
847
1403
|
```
|
|
848
1404
|
|
|
1405
|
+
`FileField.Dropzone` is an area files can be dropped onto. Left empty, it holds
|
|
1406
|
+
the built-in `fileFieldDrop` wording and a "choose files" button, so the field
|
|
1407
|
+
stays usable by keyboard; pass children to lay it out yourself (put a
|
|
1408
|
+
`FileField.Trigger` inside). Dropped files are added exactly like picked ones:
|
|
1409
|
+
they respect `multiple` and `maxFiles`, land in the input so they are
|
|
1410
|
+
submitted, and are announced with an `input` event so a form sees the change.
|
|
1411
|
+
A dropped folder is skipped (choose folders through the picker with
|
|
1412
|
+
`webkitDirectory`). The browser applies `accept` only to the picker, so the
|
|
1413
|
+
field applies it to dropped files itself, by the same rules: a file it does not
|
|
1414
|
+
match is left out, and a drop with nothing it matches changes nothing — no
|
|
1415
|
+
`onChange`, no `input` event.
|
|
1416
|
+
|
|
1417
|
+
The input holds exactly what `ItemList` lists, so what is listed is what is
|
|
1418
|
+
submitted:
|
|
1419
|
+
|
|
1420
|
+
- With `multiple` or `webkitDirectory`, each pick or drop adds to the list, up
|
|
1421
|
+
to `maxFiles`, and the input is rewritten to the whole list — the browser
|
|
1422
|
+
alone would keep only the files just picked. A rewrite is announced with an
|
|
1423
|
+
`input` event.
|
|
1424
|
+
- Removing a file from `ItemList` removes it from the input.
|
|
1425
|
+
- A `File[]` `defaultValue` is submitted as well as listed, and a form reset
|
|
1426
|
+
puts both back to it (to an empty list without one).
|
|
1427
|
+
- `onChange` receives that whole list — not only the files just picked or
|
|
1428
|
+
dropped — after every pick, drop, and removal. Only a pick passes the
|
|
1429
|
+
`event`.
|
|
1430
|
+
|
|
1431
|
+
```tsx
|
|
1432
|
+
<FileField.Root accept="image/*" multiple name="photos">
|
|
1433
|
+
<FileField.Dropzone />
|
|
1434
|
+
<FileField.ItemList clearable />
|
|
1435
|
+
</FileField.Root>
|
|
1436
|
+
```
|
|
1437
|
+
|
|
849
1438
|
Props (Root):
|
|
850
1439
|
|
|
851
1440
|
- `children`: `ReactNode`
|
|
852
|
-
- `defaultValue`: `File[]`
|
|
1441
|
+
- `defaultValue`: `File[]` | `string`
|
|
853
1442
|
- `invalid`: `boolean` (default: `false`)
|
|
854
1443
|
- `maxFiles`: `number`
|
|
855
1444
|
- `onChange`: `(files: FileList | null, event?: ChangeEvent<HTMLInputElement>) => void`
|
|
856
1445
|
- `ref`: `Ref<HTMLInputElement>`
|
|
857
1446
|
- `webkitDirectory`: `boolean` (default: `false`)
|
|
1447
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style` / `value`.
|
|
1448
|
+
|
|
1449
|
+
Props (FileField.Dropzone):
|
|
1450
|
+
|
|
1451
|
+
- `children`: `ReactNode`
|
|
858
1452
|
|
|
859
1453
|
Props (FileField.ItemList):
|
|
860
1454
|
|
|
@@ -869,7 +1463,7 @@ Props (FileField.Trigger):
|
|
|
869
1463
|
|
|
870
1464
|
### Heading
|
|
871
1465
|
|
|
872
|
-
A semantic heading. The `
|
|
1466
|
+
A semantic heading. The `level` prop selects the HTML element.
|
|
873
1467
|
|
|
874
1468
|
```tsx
|
|
875
1469
|
import { Heading } from '@k8ordo/ui';
|
|
@@ -884,6 +1478,7 @@ Props:
|
|
|
884
1478
|
- `level`: `'h1'` | `'h2'` | `'h3'` | `'h4'` | `'h5'` | `'h6'` (required)
|
|
885
1479
|
- `children`: `ReactNode`
|
|
886
1480
|
- `lineClamp`: `1` | `2` | `3` | `4` | `5` | `6`
|
|
1481
|
+
- Other props are forwarded to `HTMLAttributes<HTMLHeadingElement>`, except `className` / `style`.
|
|
887
1482
|
|
|
888
1483
|
### Avatar
|
|
889
1484
|
|
|
@@ -907,6 +1502,7 @@ Props:
|
|
|
907
1502
|
- `name`: `string`
|
|
908
1503
|
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
909
1504
|
- `src`: `string`
|
|
1505
|
+
- Other props are forwarded to `HTMLAttributes<HTMLSpanElement>`, except `role` / `aria-label` / `className` / `style`.
|
|
910
1506
|
|
|
911
1507
|
### Badge
|
|
912
1508
|
|
|
@@ -923,10 +1519,11 @@ import { Badge } from '@k8ordo/ui';
|
|
|
923
1519
|
Props:
|
|
924
1520
|
|
|
925
1521
|
- `label`: `string` (required)
|
|
926
|
-
- `interactive`: `
|
|
927
|
-
- `size`: `
|
|
928
|
-
- `tone`: `
|
|
929
|
-
- `variant`: `
|
|
1522
|
+
- `interactive`: `boolean`
|
|
1523
|
+
- `size`: `'sm'` | `'md'` | `'lg'`
|
|
1524
|
+
- `tone`: `'neutral'` | `'info'` | `'success'` | `'warning'` | `'error'`
|
|
1525
|
+
- `variant`: `'solid'` | `'outline'`
|
|
1526
|
+
- Other props are forwarded to `ButtonHTMLAttributes<HTMLButtonElement> | HTMLAttributes<HTMLSpanElement>`, except `children` / `className` / `style` / `type`.
|
|
930
1527
|
|
|
931
1528
|
### Code
|
|
932
1529
|
|
|
@@ -941,10 +1538,154 @@ import { Code } from '@k8ordo/ui';
|
|
|
941
1538
|
Props:
|
|
942
1539
|
|
|
943
1540
|
- `children`: `string` (required)
|
|
1541
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style`.
|
|
1542
|
+
|
|
1543
|
+
### CodeBlock
|
|
1544
|
+
|
|
1545
|
+
A block of code, highlighted on the server with shiki, with a copy button.
|
|
1546
|
+
It is an async Server Component on its own subpath, `@k8ordo/ui/code-block`,
|
|
1547
|
+
and imports `server-only`: the highlighter never reaches the browser, and
|
|
1548
|
+
importing it from a Client Component fails the build. Only the copy button is a
|
|
1549
|
+
client module.
|
|
1550
|
+
|
|
1551
|
+
```tsx
|
|
1552
|
+
import { CodeBlock } from '@k8ordo/ui/code-block';
|
|
1553
|
+
|
|
1554
|
+
<CodeBlock code={source} lang="tsx" title="save.tsx" />;
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
- `lang` is any language shiki bundles (`tsx`, `bash`, `css`, …). A name it
|
|
1558
|
+
does not know renders as plain text rather than failing, so a Markdown fence
|
|
1559
|
+
can pass its info string through as it is. The header shows `title` when
|
|
1560
|
+
given (as the figure's `figcaption`), and the language otherwise.
|
|
1561
|
+
- It looks like the code blocks on k8o's blog: one surface in `bg-surface`
|
|
1562
|
+
with no border, the language (or `title`, in monospace) as a small label at
|
|
1563
|
+
its top, and the copy button over its top corner. The syntax colors are
|
|
1564
|
+
shiki's `one-light` in light and `plastic` in dark, unchanged, written as
|
|
1565
|
+
`light-dark()` so they follow the `color-scheme` the stylesheet sets with
|
|
1566
|
+
`.dark`. Some of them, the comments among them, fall short of 4.5:1 on the
|
|
1567
|
+
surface; the library's own accessibility checks exempt the code's text from
|
|
1568
|
+
the contrast rule and nothing else. Stretched by its parent, the block gives
|
|
1569
|
+
the extra height to the code, never to the label.
|
|
1570
|
+
- `marks` marks lines by their 1-based number, with a bar and a tint in the
|
|
1571
|
+
status tokens: `highlight`, `add` (drawn with a `+`), or `remove` (drawn
|
|
1572
|
+
with a `-`). `callouts` puts a note under a line
|
|
1573
|
+
(an array puts several, in order) as a bubble pointing up at it, indented
|
|
1574
|
+
like the line, and tints the line it points at. A line can carry both; the
|
|
1575
|
+
note's tint wins. Neither is part of the copied text: the button copies
|
|
1576
|
+
`code` exactly.
|
|
1577
|
+
|
|
1578
|
+
```tsx
|
|
1579
|
+
<CodeBlock
|
|
1580
|
+
callouts={{ 3: 'Guard the division' }}
|
|
1581
|
+
code={source}
|
|
1582
|
+
lang="ts"
|
|
1583
|
+
marks={{ 2: 'remove', 3: 'add' }}
|
|
1584
|
+
/>
|
|
1585
|
+
```
|
|
1586
|
+
|
|
1587
|
+
- Inside a `.writing-v` tree it stays a horizontal island.
|
|
1588
|
+
- It cannot render in a generative-UI spec, which renders on the client; see
|
|
1589
|
+
[generative-ui](generative-ui.md).
|
|
1590
|
+
|
|
1591
|
+
Props:
|
|
1592
|
+
|
|
1593
|
+
- `code`: `string` (required)
|
|
1594
|
+
- `callouts`: `Readonly<Record<number, string | readonly string[]>>`
|
|
1595
|
+
- `lang`: `string` (default: `'text'`)
|
|
1596
|
+
- `marks`: `Readonly<Record<number, 'highlight' | 'add' | 'remove'>>`
|
|
1597
|
+
- `title`: `string`
|
|
1598
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `children` / `className` / `style`.
|
|
1599
|
+
|
|
1600
|
+
### Kbd
|
|
1601
|
+
|
|
1602
|
+
One keyboard key, drawn as a key cap. A shortcut is several `Kbd` side by side,
|
|
1603
|
+
one per key. When the key is a symbol a screen reader would not say usefully
|
|
1604
|
+
(`⌘`, `⇧`), pass `label`: the symbol stays on screen and the label is what is
|
|
1605
|
+
read out.
|
|
1606
|
+
|
|
1607
|
+
```tsx
|
|
1608
|
+
import { Kbd } from '@k8ordo/ui';
|
|
1609
|
+
|
|
1610
|
+
<Kbd>Esc</Kbd>
|
|
1611
|
+
|
|
1612
|
+
<Kbd label="Command">⌘</Kbd>
|
|
1613
|
+
<Kbd>K</Kbd>
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
Props:
|
|
1617
|
+
|
|
1618
|
+
- `children`: `string` (required)
|
|
1619
|
+
- `label`: `string`
|
|
1620
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style`.
|
|
1621
|
+
|
|
1622
|
+
### Carousel
|
|
1623
|
+
|
|
1624
|
+
Slides that scroll along the inline axis, snapping one slide at a time, with
|
|
1625
|
+
previous and next buttons under them. It is built on scroll snapping, so a
|
|
1626
|
+
trackpad, a touch swipe, and the arrow keys (the track takes focus) all move it
|
|
1627
|
+
as well; the buttons move one slide per press and are disabled at either end.
|
|
1628
|
+
|
|
1629
|
+
```tsx
|
|
1630
|
+
import { Carousel } from '@k8ordo/ui';
|
|
1631
|
+
|
|
1632
|
+
<Carousel.Root label="Featured posts" slideSize="md">
|
|
1633
|
+
{posts.map((post) => (
|
|
1634
|
+
<Carousel.Slide key={post.id} label={post.title}>
|
|
1635
|
+
<PostCard post={post} />
|
|
1636
|
+
</Carousel.Slide>
|
|
1637
|
+
))}
|
|
1638
|
+
</Carousel.Root>;
|
|
1639
|
+
```
|
|
1640
|
+
|
|
1641
|
+
`slideSize` sets how much of the track one slide takes: `full` (one at a time),
|
|
1642
|
+
`lg` (the next one peeks in), `md` (two), `sm` (three). With `full` and `lg` the
|
|
1643
|
+
current position is shown as `2 / 5`; with several slides in view there is no
|
|
1644
|
+
single current slide, so no position is shown. The region is announced as a
|
|
1645
|
+
carousel and each slide as a slide (`aria-roledescription`); give a slide a
|
|
1646
|
+
`label` when its content has a title. The slides follow the writing mode, so
|
|
1647
|
+
under `.writing-v` the track scrolls vertically. There is no autoplay.
|
|
1648
|
+
|
|
1649
|
+
Props (Carousel.Root):
|
|
1650
|
+
|
|
1651
|
+
- `label`: `string` (required)
|
|
1652
|
+
- `children`: `ReactNode`
|
|
1653
|
+
- `slideSize`: `'full'` | `'lg'` | `'md'` | `'sm'` (default: `'full'`)
|
|
1654
|
+
|
|
1655
|
+
Props (Carousel.Slide):
|
|
1656
|
+
|
|
1657
|
+
- `children`: `ReactNode`
|
|
1658
|
+
- `label`: `string`
|
|
1659
|
+
|
|
1660
|
+
### Prose
|
|
1661
|
+
|
|
1662
|
+
A container that puts the typesetting of body text back — for Markdown or MDX
|
|
1663
|
+
rendered to HTML. Only bare elements (no `class`) are typeset, so components
|
|
1664
|
+
placed inside keep their own look; the spacing between blocks applies to
|
|
1665
|
+
everything. Tuned for Japanese: loose leading, emphasis dots for `em`, and a
|
|
1666
|
+
one-character paragraph indent in vertical writing. See
|
|
1667
|
+
[Typography](typography.md#long-form-text-prose) for what it sets.
|
|
1668
|
+
|
|
1669
|
+
```tsx
|
|
1670
|
+
import { Prose } from '@k8ordo/ui';
|
|
1671
|
+
|
|
1672
|
+
<article>
|
|
1673
|
+
<Prose>
|
|
1674
|
+
<MDXContent components={{ pre: MyCodeBlock }} />
|
|
1675
|
+
</Prose>
|
|
1676
|
+
</article>;
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
Props:
|
|
1680
|
+
|
|
1681
|
+
- `children`: `ReactNode`
|
|
1682
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
944
1683
|
|
|
945
1684
|
### Table
|
|
946
1685
|
|
|
947
|
-
A data table, as a compound component.
|
|
1686
|
+
A data table, as a compound component. `Table.EmptyState` is the row to put in
|
|
1687
|
+
`Table.Body` when there are no rows: it spans `colSpan` columns and draws an
|
|
1688
|
+
`EmptyState` with the rest of its props.
|
|
948
1689
|
|
|
949
1690
|
```tsx
|
|
950
1691
|
import { Table } from '@k8ordo/ui';
|
|
@@ -968,45 +1709,241 @@ import { Table } from '@k8ordo/ui';
|
|
|
968
1709
|
Props (Table.Body):
|
|
969
1710
|
|
|
970
1711
|
- `children`: `ReactNode`
|
|
1712
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableSectionElement>`, except `className` / `style`.
|
|
971
1713
|
|
|
972
1714
|
Props (Table.Caption):
|
|
973
1715
|
|
|
974
1716
|
- `children`: `ReactNode`
|
|
1717
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableCaptionElement>`, except `className` / `style`.
|
|
975
1718
|
|
|
976
1719
|
Props (Table.Cell):
|
|
977
1720
|
|
|
978
1721
|
- `align`: `CellAlign` (default: `'left'`)
|
|
979
1722
|
- `children`: `ReactNode`
|
|
980
1723
|
- `color`: `'base'` | `'mute'` (default: `'base'`)
|
|
1724
|
+
- Other props are forwarded to `TdHTMLAttributes<HTMLTableCellElement>`, except `className` / `style`.
|
|
981
1725
|
|
|
982
1726
|
Props (Table.EmptyState):
|
|
983
1727
|
|
|
984
|
-
- `children`: `ReactNode` (required)
|
|
985
1728
|
- `colSpan`: `number` (required)
|
|
1729
|
+
- `title`: `string` (required)
|
|
1730
|
+
- `action`: `ReactNode`
|
|
1731
|
+
- `description`: `ReactNode`
|
|
1732
|
+
- `icon`: `ReactNode`
|
|
1733
|
+
- Other props are forwarded to `ComponentProps<typeof EmptyState>`.
|
|
1734
|
+
|
|
1735
|
+
Props (Table.Head):
|
|
1736
|
+
|
|
1737
|
+
- `children`: `ReactNode`
|
|
1738
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableSectionElement>`, except `className` / `style`.
|
|
1739
|
+
|
|
1740
|
+
Props (Table.HeaderCell):
|
|
1741
|
+
|
|
1742
|
+
- `align`: `CellAlign` (default: `'left'`)
|
|
1743
|
+
- `children`: `ReactNode`
|
|
1744
|
+
- `scope`: `'col'` | `'row'` | `'colgroup'` | `'rowgroup'` (default: `'col'`)
|
|
1745
|
+
- Other props are forwarded to `ThHTMLAttributes<HTMLTableCellElement>`, except `className` / `style`.
|
|
1746
|
+
|
|
1747
|
+
Props (Table.Root):
|
|
1748
|
+
|
|
1749
|
+
- `children`: `ReactNode`
|
|
1750
|
+
- Other props are forwarded to `TableHTMLAttributes<HTMLTableElement>`, except `className` / `style`.
|
|
1751
|
+
|
|
1752
|
+
Props (Table.Row):
|
|
1753
|
+
|
|
1754
|
+
- `children`: `ReactNode`
|
|
1755
|
+
- `interactive`: `boolean` (default: `false`)
|
|
1756
|
+
- `selected`: `boolean` (default: `false`)
|
|
1757
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableRowElement>`, except `className` / `style`.
|
|
1758
|
+
|
|
1759
|
+
### DataTable
|
|
1760
|
+
|
|
1761
|
+
A table with sorting, row selection, and column visibility. Every piece of state
|
|
1762
|
+
is **controlled** and owned by the caller, so it can live anywhere — component
|
|
1763
|
+
state, or the URL through `@k8ordo/state`'s url slot, which keeps the sort and
|
|
1764
|
+
the page in a shareable link. It takes functions (`cell`, `getRowId`, the
|
|
1765
|
+
handlers), so render it from the Client Component that owns that state.
|
|
1766
|
+
|
|
1767
|
+
```tsx
|
|
1768
|
+
'use client';
|
|
1769
|
+
import { DataTable, type DataTableSort } from '@k8ordo/ui';
|
|
1770
|
+
|
|
1771
|
+
const [sort, setSort] = useState<DataTableSort | null>(null);
|
|
1772
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
1773
|
+
const [hiddenColumnIds, setHiddenColumnIds] = useState<string[]>([]);
|
|
1774
|
+
|
|
1775
|
+
<DataTable
|
|
1776
|
+
columns={[
|
|
1777
|
+
{ id: 'name', header: 'Name', cell: (m) => m.name, sortable: true },
|
|
1778
|
+
{ id: 'role', header: 'Role', cell: (m) => m.role, hideable: false },
|
|
1779
|
+
]}
|
|
1780
|
+
getRowId={(m) => m.id}
|
|
1781
|
+
hiddenColumnIds={hiddenColumnIds}
|
|
1782
|
+
label="Members"
|
|
1783
|
+
onHiddenColumnIdsChange={setHiddenColumnIds}
|
|
1784
|
+
onSelectedIdsChange={setSelectedIds}
|
|
1785
|
+
onSortChange={setSort}
|
|
1786
|
+
rows={sortMembers(members, sort)}
|
|
1787
|
+
selectedIds={selectedIds}
|
|
1788
|
+
sort={sort}
|
|
1789
|
+
/>;
|
|
1790
|
+
```
|
|
1791
|
+
|
|
1792
|
+
- A column is a `DataTableColumn<Row>`: `id`, `header` (text), `cell(row)`
|
|
1793
|
+
(what to draw), and optionally `align`, `sortable`, and `hideable`.
|
|
1794
|
+
- **It does not sort, filter, or page.** It draws `rows` in the order given, so
|
|
1795
|
+
the same component works when the server sorts. `sort` is
|
|
1796
|
+
`{ columnId, direction: 'ascending' | 'descending' } | null`; a sortable
|
|
1797
|
+
header cycles ascending → descending → unsorted and carries `aria-sort`.
|
|
1798
|
+
Put `Pagination` under it for pages.
|
|
1799
|
+
- Each feature appears only when you pass its handler: sort buttons on the
|
|
1800
|
+
`sortable` columns with `onSortChange`, a checkbox column with
|
|
1801
|
+
`onSelectedIdsChange`, and a "Columns" menu (every column but those with
|
|
1802
|
+
`hideable: false`) with `onHiddenColumnIdsChange`.
|
|
1803
|
+
- The header checkbox selects or clears the rows on screen and shows the mixed
|
|
1804
|
+
state when some are selected (`Checkbox`'s `indeterminate`). The first column
|
|
1805
|
+
is the row header (`th scope="row"`), and each row's checkbox is named after
|
|
1806
|
+
it ("Select row Aoki").
|
|
1807
|
+
- `emptyState` is drawn in a row spanning the columns when `rows` is empty —
|
|
1808
|
+
pass an `EmptyState`.
|
|
1809
|
+
- Selected rows use `Table.Row`'s `selected`. For bulk actions, render your own
|
|
1810
|
+
bar above the table from `selectedIds`.
|
|
1811
|
+
|
|
1812
|
+
Props:
|
|
1813
|
+
|
|
1814
|
+
- `columns`: `ReadonlyArray<DataTableColumn<Row>>` (required)
|
|
1815
|
+
- `getRowId`: `(row: Row) => string` (required)
|
|
1816
|
+
- `label`: `string` (required)
|
|
1817
|
+
- `rows`: `readonly Row[]` (required)
|
|
1818
|
+
- `emptyState`: `ReactNode`
|
|
1819
|
+
- `hiddenColumnIds`: `readonly string[]` (default: `NONE`)
|
|
1820
|
+
- `onHiddenColumnIdsChange`: `(ids: string[]) => void`
|
|
1821
|
+
- `onSelectedIdsChange`: `(ids: string[]) => void`
|
|
1822
|
+
- `onSortChange`: `(sort: DataTableSort | null) => void`
|
|
1823
|
+
- `selectedIds`: `readonly string[]` (default: `NONE`)
|
|
1824
|
+
- `sort`: `DataTableSort` | `null` (default: `null`)
|
|
1825
|
+
|
|
1826
|
+
### Tree
|
|
1827
|
+
|
|
1828
|
+
A hierarchy whose parents open and close — files and folders, an outline —
|
|
1829
|
+
with the WAI-ARIA tree view keyboard model. Pass the nodes as a tree of
|
|
1830
|
+
`{ id, label, icon?, children? }`.
|
|
1831
|
+
|
|
1832
|
+
```tsx
|
|
1833
|
+
import { Tree } from '@k8ordo/ui';
|
|
1834
|
+
|
|
1835
|
+
<Tree
|
|
1836
|
+
defaultExpandedIds={['src']}
|
|
1837
|
+
items={[
|
|
1838
|
+
{
|
|
1839
|
+
id: 'src',
|
|
1840
|
+
label: 'src',
|
|
1841
|
+
children: [{ id: 'index', label: 'index.ts' }],
|
|
1842
|
+
},
|
|
1843
|
+
{ id: 'readme', label: 'README.md' },
|
|
1844
|
+
]}
|
|
1845
|
+
label="Files"
|
|
1846
|
+
onChange={(id) => open(id)}
|
|
1847
|
+
/>;
|
|
1848
|
+
```
|
|
1849
|
+
|
|
1850
|
+
- Which parents are open is controllable (`expandedIds` / `defaultExpandedIds`
|
|
1851
|
+
/ `onExpandedChange`), and so is the selected node (`selectedId` /
|
|
1852
|
+
`defaultSelectedId` / `onChange`, called with the node's `id`).
|
|
1853
|
+
- Keyboard: Down / Up move between the visible nodes; Right opens a closed
|
|
1854
|
+
parent, then moves to its first child; Left closes an open parent, or moves
|
|
1855
|
+
to the parent; Home / End go to the first / last visible node; Enter or Space
|
|
1856
|
+
selects; a character moves to the next node whose label starts with it.
|
|
1857
|
+
- Clicking a node selects it and, on a parent, opens or closes it.
|
|
1858
|
+
- One node takes Tab at a time (roving tabindex): the focused one, else the
|
|
1859
|
+
selected one, else the first.
|
|
1860
|
+
- The visible nodes render as one flat list of `treeitem`s, with no nested
|
|
1861
|
+
`group`: the depth is `aria-level`, and the place among siblings is
|
|
1862
|
+
`aria-posinset` / `aria-setsize`. A node's accessible name is its own label.
|
|
1863
|
+
- Under `.writing-v` the nodes run right to left, so Left / Right move between
|
|
1864
|
+
them and Down / Up open and close.
|
|
1865
|
+
|
|
1866
|
+
Props:
|
|
1867
|
+
|
|
1868
|
+
- `items`: `readonly TreeItem[]` (required)
|
|
1869
|
+
- `label`: `string` (required)
|
|
1870
|
+
- `defaultExpandedIds`: `readonly string[]`
|
|
1871
|
+
- `defaultSelectedId`: `string` | `null` (default: `null`)
|
|
1872
|
+
- `expandedIds`: `readonly string[]`
|
|
1873
|
+
- `onChange`: `(id: string) => void`
|
|
1874
|
+
- `onExpandedChange`: `(ids: readonly string[]) => void`
|
|
1875
|
+
- `selectedId`: `string` | `null`
|
|
1876
|
+
- Other props are forwarded to `HTMLAttributes<HTMLUListElement>`, except `children` / `className` / `style` / `role` / `aria-label`.
|
|
1877
|
+
|
|
1878
|
+
### Accordion
|
|
1879
|
+
|
|
1880
|
+
A collapsible section, as a compound component.
|
|
1881
|
+
|
|
1882
|
+
```tsx
|
|
1883
|
+
import { Accordion } from '@k8ordo/ui';
|
|
1884
|
+
|
|
1885
|
+
<Accordion.Root>
|
|
1886
|
+
<Accordion.Item>
|
|
1887
|
+
<Accordion.Button>Section 1</Accordion.Button>
|
|
1888
|
+
<Accordion.Panel>Content</Accordion.Panel>
|
|
1889
|
+
</Accordion.Item>
|
|
1890
|
+
</Accordion.Root>;
|
|
1891
|
+
```
|
|
1892
|
+
|
|
1893
|
+
Props (Accordion.Button):
|
|
1894
|
+
|
|
1895
|
+
- `children`: `ReactNode`
|
|
986
1896
|
|
|
987
|
-
Props (
|
|
1897
|
+
Props (Accordion.Item):
|
|
988
1898
|
|
|
989
1899
|
- `children`: `ReactNode`
|
|
1900
|
+
- `defaultOpen`: `boolean` (default: `false`)
|
|
1901
|
+
- `isOpen`: `boolean`
|
|
1902
|
+
- `onChange`: `(isOpen: boolean) => void`
|
|
990
1903
|
|
|
991
|
-
Props (
|
|
1904
|
+
Props (Accordion.Panel):
|
|
992
1905
|
|
|
993
|
-
- `align`: `CellAlign` (default: `'left'`)
|
|
994
1906
|
- `children`: `ReactNode`
|
|
995
|
-
- `scope`: `'col'` | `'row'` | `'colgroup'` | `'rowgroup'` (default: `'col'`)
|
|
996
1907
|
|
|
997
|
-
Props (
|
|
1908
|
+
Props (Accordion.Root):
|
|
998
1909
|
|
|
999
1910
|
- `children`: `ReactNode`
|
|
1000
1911
|
|
|
1001
|
-
|
|
1912
|
+
### Card
|
|
1913
|
+
|
|
1914
|
+
A card that groups content.
|
|
1915
|
+
|
|
1916
|
+
```tsx
|
|
1917
|
+
import { Card } from '@k8ordo/ui';
|
|
1918
|
+
|
|
1919
|
+
// Static card. The card has no padding of its own
|
|
1920
|
+
<Card width="full" variant="shadow">
|
|
1921
|
+
<div style={{ padding: 'calc(var(--spacing) * 6)' }}>Content</div>
|
|
1922
|
+
</Card>
|
|
1923
|
+
|
|
1924
|
+
// Clickable card (scales to 1.02 on hover and to 0.98 while pressed)
|
|
1925
|
+
<Card variant="outline" interactive>
|
|
1926
|
+
<div style={{ padding: 'calc(var(--spacing) * 6)' }}>Content</div>
|
|
1927
|
+
</Card>
|
|
1928
|
+
```
|
|
1929
|
+
|
|
1930
|
+
Props:
|
|
1002
1931
|
|
|
1003
1932
|
- `children`: `ReactNode`
|
|
1004
1933
|
- `interactive`: `boolean` (default: `false`)
|
|
1934
|
+
- `variant`: `'shadow'` | `'outline'` (default: `'shadow'`)
|
|
1935
|
+
- `width`: `'full'` | `'fit'` (default: `'full'`)
|
|
1936
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
1005
1937
|
|
|
1006
1938
|
## Feedback
|
|
1007
1939
|
|
|
1008
1940
|
### Alert
|
|
1009
1941
|
|
|
1942
|
+
Reports a status: the result of something the user did, or a state the
|
|
1943
|
+
application is in. It is a live region (`role="alert"` for `warning` and
|
|
1944
|
+
`error`, `role="status"` otherwise), so a screen reader announces it when it
|
|
1945
|
+
appears. For a note that is part of the content, use `Callout`.
|
|
1946
|
+
|
|
1010
1947
|
```tsx
|
|
1011
1948
|
import { Alert } from '@k8ordo/ui';
|
|
1012
1949
|
|
|
@@ -1021,6 +1958,63 @@ Props:
|
|
|
1021
1958
|
- `action`: `AlertAction`
|
|
1022
1959
|
- `closeLabel`: `string`
|
|
1023
1960
|
- `onClose`: `() => void`
|
|
1961
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `role` / `className` / `style`.
|
|
1962
|
+
|
|
1963
|
+
`action` is an `AlertAction`, `{ label: string; renderItem: (props: { children: ReactNode }) => ReactNode }`.
|
|
1964
|
+
`renderItem` receives `label` as `children`; render your own button or link
|
|
1965
|
+
around it.
|
|
1966
|
+
|
|
1967
|
+
### Callout
|
|
1968
|
+
|
|
1969
|
+
A note that sits in the content: a tip beside a paragraph, a pitfall in a
|
|
1970
|
+
guide, a dated-content notice at the top of an article. Unlike `Alert`, it is
|
|
1971
|
+
not a live region (`role="note"`), so a page that renders one, or a client
|
|
1972
|
+
navigation that brings one in, does not have it announced as a warning. The
|
|
1973
|
+
body is `children`, so it can hold paragraphs, `Code`, and links; `label` is an
|
|
1974
|
+
optional bold line above it.
|
|
1975
|
+
|
|
1976
|
+
```tsx
|
|
1977
|
+
import { Callout, Code } from '@k8ordo/ui';
|
|
1978
|
+
|
|
1979
|
+
<Callout label="Pitfall" tone="warning">
|
|
1980
|
+
<p>
|
|
1981
|
+
Passing <Code>value</Code> makes the field controlled, and a form reset no
|
|
1982
|
+
longer restores it.
|
|
1983
|
+
</p>
|
|
1984
|
+
</Callout>;
|
|
1985
|
+
```
|
|
1986
|
+
|
|
1987
|
+
Props:
|
|
1988
|
+
|
|
1989
|
+
- `children`: `ReactNode` (required)
|
|
1990
|
+
- `tone`: `Status` (required)
|
|
1991
|
+
- `label`: `string`
|
|
1992
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `role` / `className` / `style`.
|
|
1993
|
+
|
|
1994
|
+
### EmptyState
|
|
1995
|
+
|
|
1996
|
+
What a list, a table, or a search shows when there is nothing in it: a title,
|
|
1997
|
+
an optional description and icon, and an optional action. Inside a table, use
|
|
1998
|
+
`Table.EmptyState`, which puts the same content in a row spanning the columns.
|
|
1999
|
+
|
|
2000
|
+
```tsx
|
|
2001
|
+
import { Button, EmptyState, TableIcon } from '@k8ordo/ui';
|
|
2002
|
+
|
|
2003
|
+
<EmptyState
|
|
2004
|
+
action={<Button onClick={clearFilters}>Clear filters</Button>}
|
|
2005
|
+
description="Try removing a filter."
|
|
2006
|
+
icon={<TableIcon size="lg" />}
|
|
2007
|
+
title="No matching posts"
|
|
2008
|
+
/>;
|
|
2009
|
+
```
|
|
2010
|
+
|
|
2011
|
+
Props:
|
|
2012
|
+
|
|
2013
|
+
- `title`: `string` (required)
|
|
2014
|
+
- `action`: `ReactNode`
|
|
2015
|
+
- `description`: `ReactNode`
|
|
2016
|
+
- `icon`: `ReactNode`
|
|
2017
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `className` / `style`.
|
|
1024
2018
|
|
|
1025
2019
|
### Toast
|
|
1026
2020
|
|
|
@@ -1031,14 +2025,19 @@ const { open, close, closeAll } = useToast();
|
|
|
1031
2025
|
|
|
1032
2026
|
open('success', 'Saved');
|
|
1033
2027
|
open('error', 'Something went wrong');
|
|
2028
|
+
|
|
2029
|
+
const syncingId = open('info', 'Syncing…', {
|
|
2030
|
+
duration: Number.POSITIVE_INFINITY,
|
|
2031
|
+
});
|
|
2032
|
+
close(syncingId);
|
|
1034
2033
|
```
|
|
1035
2034
|
|
|
1036
2035
|
`ToastProvider` is already inside `UIProvider`, so no extra wrapper is needed.
|
|
1037
2036
|
|
|
1038
2037
|
What `useToast()` returns:
|
|
1039
2038
|
|
|
1040
|
-
- `open`: `(tone: Status, message: string, options?: ToastOptions) =>
|
|
1041
|
-
- `close`: `(id: string) => void`
|
|
2039
|
+
- `open`: `(tone: Status, message: string, options?: ToastOptions) => string` (returns the toast's id; `ToastOptions` is `{ duration?: number; action?: ToastAction }`. `duration` is in milliseconds and defaults to `5000`; `Number.POSITIVE_INFINITY` keeps the toast until it is closed. `ToastAction` is the same type as `AlertAction`.)
|
|
2040
|
+
- `close`: `(id: string) => void` (closes only the toast with the id `open` returned)
|
|
1042
2041
|
- `closeAll`: `() => void`
|
|
1043
2042
|
|
|
1044
2043
|
### ToastProvider
|
|
@@ -1061,19 +2060,28 @@ Props:
|
|
|
1061
2060
|
|
|
1062
2061
|
### Progress
|
|
1063
2062
|
|
|
2063
|
+
Leave `value` out when how far along it is cannot be known: the bar then slides
|
|
2064
|
+
back and forth, has no `aria-valuenow`, and is named `label` (the built-in
|
|
2065
|
+
`loading` wording when omitted). With reduced motion it stops sliding and
|
|
2066
|
+
pulses across the whole track instead.
|
|
2067
|
+
|
|
1064
2068
|
```tsx
|
|
1065
2069
|
import { Progress } from '@k8ordo/ui';
|
|
1066
2070
|
|
|
1067
|
-
<Progress value={50}
|
|
1068
|
-
<Progress value={
|
|
2071
|
+
<Progress value={50} />
|
|
2072
|
+
<Progress value={150} max={200} min={100} label="Progress" />
|
|
2073
|
+
|
|
2074
|
+
// Progress that cannot be measured
|
|
2075
|
+
<Progress label="Uploading" />
|
|
1069
2076
|
```
|
|
1070
2077
|
|
|
1071
2078
|
Props:
|
|
1072
2079
|
|
|
1073
|
-
- `max`: `number` (required)
|
|
1074
|
-
- `value`: `number` (required)
|
|
1075
2080
|
- `label`: `string`
|
|
2081
|
+
- `max`: `number` (default: `100`)
|
|
1076
2082
|
- `min`: `number` (default: `0`)
|
|
2083
|
+
- `value`: `number`
|
|
2084
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `className` / `style`.
|
|
1077
2085
|
|
|
1078
2086
|
### Spinner
|
|
1079
2087
|
|
|
@@ -1089,6 +2097,7 @@ Props:
|
|
|
1089
2097
|
|
|
1090
2098
|
- `label`: `string`
|
|
1091
2099
|
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
2100
|
+
- Other props are forwarded to `OutputHTMLAttributes<HTMLOutputElement>`, except `children` / `aria-label` / `className` / `style`.
|
|
1092
2101
|
|
|
1093
2102
|
### Skeleton
|
|
1094
2103
|
|
|
@@ -1107,6 +2116,7 @@ Props:
|
|
|
1107
2116
|
- `animate`: `boolean` (default: `true`)
|
|
1108
2117
|
- `shape`: `'rect'` | `'circle'` (default: `'rect'`)
|
|
1109
2118
|
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
2119
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `className` / `style`.
|
|
1110
2120
|
|
|
1111
2121
|
## Overlays
|
|
1112
2122
|
|
|
@@ -1174,6 +2184,53 @@ Props (Dialog.Root):
|
|
|
1174
2184
|
- `role`: `'dialog'` | `'alertdialog'`
|
|
1175
2185
|
- `tabIndex`: `number`
|
|
1176
2186
|
|
|
2187
|
+
### CommandPalette
|
|
2188
|
+
|
|
2189
|
+
A command palette: a `Modal` holding a search field and the list of commands it
|
|
2190
|
+
filters. Typing matches each item's `label` and `keywords`, case-insensitively.
|
|
2191
|
+
`ArrowDown` / `ArrowUp` move through the matches and wrap around, `Enter` or a
|
|
2192
|
+
click runs the item's `onSelect` after closing the palette, and `Escape` closes
|
|
2193
|
+
it; focus stays in the search field throughout (the WAI-ARIA combobox, with the
|
|
2194
|
+
list always shown). Items that share a `group` are gathered under that heading,
|
|
2195
|
+
in the order the group first appears, and `shortcut` keys are drawn with `Kbd`.
|
|
2196
|
+
Each opening starts from an empty search. Open and close it like `Modal`
|
|
2197
|
+
(`isOpen` / `defaultOpen` / `onClose`); a key such as ⌘K / Ctrl+K is the
|
|
2198
|
+
application's to wire.
|
|
2199
|
+
|
|
2200
|
+
```tsx
|
|
2201
|
+
import { CommandPalette } from '@k8ordo/ui';
|
|
2202
|
+
|
|
2203
|
+
<CommandPalette
|
|
2204
|
+
isOpen={isOpen}
|
|
2205
|
+
onClose={() => setIsOpen(false)}
|
|
2206
|
+
items={[
|
|
2207
|
+
{
|
|
2208
|
+
id: 'new-file',
|
|
2209
|
+
label: 'New file',
|
|
2210
|
+
group: 'File',
|
|
2211
|
+
shortcut: ['⌘', 'N'],
|
|
2212
|
+
onSelect: createFile,
|
|
2213
|
+
},
|
|
2214
|
+
{
|
|
2215
|
+
id: 'theme',
|
|
2216
|
+
label: 'Toggle theme',
|
|
2217
|
+
keywords: ['dark', 'light'],
|
|
2218
|
+
onSelect: toggleTheme,
|
|
2219
|
+
},
|
|
2220
|
+
]}
|
|
2221
|
+
/>;
|
|
2222
|
+
```
|
|
2223
|
+
|
|
2224
|
+
Props:
|
|
2225
|
+
|
|
2226
|
+
- `items`: `readonly CommandPaletteItem[]` (required)
|
|
2227
|
+
- `aria-label`: `string`
|
|
2228
|
+
- `defaultOpen`: `boolean`
|
|
2229
|
+
- `isOpen`: `boolean`
|
|
2230
|
+
- `onClose`: `() => void`
|
|
2231
|
+
- `placeholder`: `string`
|
|
2232
|
+
- `ref`: `Ref<HTMLDialogElement>`
|
|
2233
|
+
|
|
1177
2234
|
### Drawer
|
|
1178
2235
|
|
|
1179
2236
|
A side panel. It uses Modal internally.
|
|
@@ -1320,6 +2377,66 @@ Props (DropdownMenu.Trigger):
|
|
|
1320
2377
|
- `size`: `ComponentProps<typeof Button>['size']` (default: `'md'`)
|
|
1321
2378
|
- `variant`: `ComponentProps<typeof Button>['variant']` (default: `'solid'`)
|
|
1322
2379
|
|
|
2380
|
+
### ContextMenu
|
|
2381
|
+
|
|
2382
|
+
A menu opened by right-clicking (or by the keyboard's context-menu key or
|
|
2383
|
+
Shift+F10) on an area, shown at the pointer. The inside is `DropdownMenu`'s —
|
|
2384
|
+
`ContextMenu.Content`, `ContextMenu.Item` (`label` / `onAction`), and
|
|
2385
|
+
`ContextMenu.SubMenu` behave exactly like theirs — and only how it opens and
|
|
2386
|
+
where differ. When it closes from inside (an item, Escape), focus returns to
|
|
2387
|
+
the element that had it before it opened; closing by clicking elsewhere leaves
|
|
2388
|
+
focus where the click put it.
|
|
2389
|
+
|
|
2390
|
+
`ContextMenu.Trigger` fills a slot: `renderItem` receives `onContextMenu` to
|
|
2391
|
+
put on the area. To open it from the keyboard, the area (or something in it)
|
|
2392
|
+
has to be able to take focus.
|
|
2393
|
+
|
|
2394
|
+
```tsx
|
|
2395
|
+
import { ContextMenu } from '@k8ordo/ui';
|
|
2396
|
+
|
|
2397
|
+
<ContextMenu.Root>
|
|
2398
|
+
<ContextMenu.Trigger
|
|
2399
|
+
renderItem={(props) => (
|
|
2400
|
+
<div {...props} tabIndex={0}>
|
|
2401
|
+
report.pdf
|
|
2402
|
+
</div>
|
|
2403
|
+
)}
|
|
2404
|
+
/>
|
|
2405
|
+
<ContextMenu.Content>
|
|
2406
|
+
<ContextMenu.Item label="Rename" onAction={rename} />
|
|
2407
|
+
<ContextMenu.SubMenu label="Move to">
|
|
2408
|
+
<ContextMenu.Item label="Archive" onAction={archive} />
|
|
2409
|
+
</ContextMenu.SubMenu>
|
|
2410
|
+
<ContextMenu.Item label="Delete" onAction={remove} />
|
|
2411
|
+
</ContextMenu.Content>
|
|
2412
|
+
</ContextMenu.Root>;
|
|
2413
|
+
```
|
|
2414
|
+
|
|
2415
|
+
Props (Root):
|
|
2416
|
+
|
|
2417
|
+
- `children`: `ReactNode`
|
|
2418
|
+
- `defaultOpen`: `boolean`
|
|
2419
|
+
- `isOpen`: `boolean`
|
|
2420
|
+
- `onChange`: `(isOpen: boolean) => void`
|
|
2421
|
+
|
|
2422
|
+
Props (ContextMenu.Content):
|
|
2423
|
+
|
|
2424
|
+
- `children`: `ReactNode`
|
|
2425
|
+
|
|
2426
|
+
Props (ContextMenu.Item):
|
|
2427
|
+
|
|
2428
|
+
- `label`: `string` (required)
|
|
2429
|
+
- `onAction`: `() => void` (required)
|
|
2430
|
+
|
|
2431
|
+
Props (ContextMenu.SubMenu):
|
|
2432
|
+
|
|
2433
|
+
- `label`: `string` (required)
|
|
2434
|
+
- `children`: `ReactNode`
|
|
2435
|
+
|
|
2436
|
+
Props (ContextMenu.Trigger):
|
|
2437
|
+
|
|
2438
|
+
- `renderItem`: `(props: { onContextMenu: MouseEventHandler<HTMLElement>; }) => ReactElement` (required)
|
|
2439
|
+
|
|
1323
2440
|
### ListBox
|
|
1324
2441
|
|
|
1325
2442
|
List selection, as a compound component. The choices use the same `Option` (`{ value, label }`) as `Select`.
|
|
@@ -1371,11 +2488,261 @@ Props (ListBox.Trigger):
|
|
|
1371
2488
|
- `label`: `string`
|
|
1372
2489
|
- `size`: `ComponentProps<typeof Button>['size']` (default: `'md'`)
|
|
1373
2490
|
|
|
2491
|
+
## Layout
|
|
2492
|
+
|
|
2493
|
+
### Separator
|
|
2494
|
+
|
|
2495
|
+
A dividing rule.
|
|
2496
|
+
|
|
2497
|
+
```tsx
|
|
2498
|
+
import { Separator } from '@k8ordo/ui';
|
|
2499
|
+
|
|
2500
|
+
<Separator />
|
|
2501
|
+
<Separator color="mute" />
|
|
2502
|
+
<Separator color="subtle" />
|
|
2503
|
+
<Separator orientation="vertical" />
|
|
2504
|
+
```
|
|
2505
|
+
|
|
2506
|
+
Props:
|
|
2507
|
+
|
|
2508
|
+
- `color`: `'base'` | `'mute'` | `'subtle'` (default: `'base'`)
|
|
2509
|
+
- `orientation`: `'horizontal'` | `'vertical'` (default: `'horizontal'`)
|
|
2510
|
+
- Other props are forwarded to `HTMLAttributes<HTMLSpanElement>`, except `children` / `role` / `aria-orientation` / `className` / `style`.
|
|
2511
|
+
|
|
2512
|
+
### Stack
|
|
2513
|
+
|
|
2514
|
+
Lays children out along one axis. Pick `gap` from the spacing tokens.
|
|
2515
|
+
|
|
2516
|
+
```tsx
|
|
2517
|
+
import { Stack } from '@k8ordo/ui';
|
|
2518
|
+
|
|
2519
|
+
<Stack gap="lg">
|
|
2520
|
+
<Card>1</Card>
|
|
2521
|
+
<Card>2</Card>
|
|
2522
|
+
</Stack>
|
|
2523
|
+
|
|
2524
|
+
<Stack direction="row" justify="between" align="center">
|
|
2525
|
+
<Heading level="h2">Title</Heading>
|
|
2526
|
+
<Button>Action</Button>
|
|
2527
|
+
</Stack>
|
|
2528
|
+
```
|
|
2529
|
+
|
|
2530
|
+
Props:
|
|
2531
|
+
|
|
2532
|
+
- `align`: `'start'` | `'center'` | `'end'` | `'stretch'`
|
|
2533
|
+
- `children`: `ReactNode`
|
|
2534
|
+
- `direction`: `'row'` | `'column'` (default: `'column'`)
|
|
2535
|
+
- `gap`: `GapSize` (default: `'md'`)
|
|
2536
|
+
- `justify`: `'start'` | `'center'` | `'end'` | `'between'`
|
|
2537
|
+
- `padding`: `PaddingSize`
|
|
2538
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2539
|
+
|
|
2540
|
+
### Grid
|
|
2541
|
+
|
|
2542
|
+
A grid layout. Passing `'auto-fill'` or `'auto-fit'` to `cols` wraps with `minItemSize` as the lower bound.
|
|
2543
|
+
|
|
2544
|
+
```tsx
|
|
2545
|
+
import { Grid } from '@k8ordo/ui';
|
|
2546
|
+
|
|
2547
|
+
<Grid cols={3} gap="md">
|
|
2548
|
+
<Card>1</Card>
|
|
2549
|
+
<Card>2</Card>
|
|
2550
|
+
<Card>3</Card>
|
|
2551
|
+
</Grid>
|
|
2552
|
+
|
|
2553
|
+
<Grid cols="auto-fill" minItemSize={64}>
|
|
2554
|
+
{items.map((item) => (
|
|
2555
|
+
<Card key={item.id}>{item.name}</Card>
|
|
2556
|
+
))}
|
|
2557
|
+
</Grid>
|
|
2558
|
+
```
|
|
2559
|
+
|
|
2560
|
+
Props:
|
|
2561
|
+
|
|
2562
|
+
- `children`: `ReactNode`
|
|
2563
|
+
- `cols`: `1` | `2` | `3` | `4` | `5` | `6` | `'auto-fill'` | `'auto-fit'` (default: `'auto-fill'`)
|
|
2564
|
+
- `gap`: `GapSize` (default: `'md'`)
|
|
2565
|
+
- `minItemSize`: `24` | `32` | `40` | `48` | `64` | `80` (default: `48`)
|
|
2566
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2567
|
+
|
|
2568
|
+
### ResizablePanels
|
|
2569
|
+
|
|
2570
|
+
Two panes with a divider between them that the user drags or moves with the
|
|
2571
|
+
arrow keys (the WAI-ARIA window splitter). `value` is the first pane's share in
|
|
2572
|
+
percent, kept between `min` and `max`; the second pane takes the rest. The root
|
|
2573
|
+
fills its parent, so give that parent a size — a vertical split needs a height.
|
|
2574
|
+
Each pane scrolls on its own.
|
|
2575
|
+
|
|
2576
|
+
The divider is a focusable `separator` whose value is the first pane's size and
|
|
2577
|
+
whose `aria-controls` points at it. The arrow keys move it the way they point
|
|
2578
|
+
on screen — left/right when the panes sit side by side, up/down when they are
|
|
2579
|
+
stacked — so in a right-to-left page `ArrowLeft` widens a first pane that sits
|
|
2580
|
+
on the right; `Home` / `End` jump to `min` / `max`. `orientation="horizontal"`
|
|
2581
|
+
lays the panes along the line of text, so in vertical writing mode they stack.
|
|
2582
|
+
Name the divider after the first pane with `aria-labelledby` when it has a
|
|
2583
|
+
heading; otherwise it falls back to `resizablePanelsHandle` from the dictionary.
|
|
2584
|
+
|
|
2585
|
+
```tsx
|
|
2586
|
+
import { ResizablePanels } from '@k8ordo/ui';
|
|
2587
|
+
|
|
2588
|
+
<div style={{ height: '24rem' }}>
|
|
2589
|
+
<ResizablePanels.Root defaultValue={25}>
|
|
2590
|
+
<ResizablePanels.Panel>
|
|
2591
|
+
<h2 id="files">Files</h2>
|
|
2592
|
+
</ResizablePanels.Panel>
|
|
2593
|
+
<ResizablePanels.Handle aria-labelledby="files" />
|
|
2594
|
+
<ResizablePanels.Panel>…</ResizablePanels.Panel>
|
|
2595
|
+
</ResizablePanels.Root>
|
|
2596
|
+
</div>;
|
|
2597
|
+
```
|
|
2598
|
+
|
|
2599
|
+
Props (Root):
|
|
2600
|
+
|
|
2601
|
+
- `children`: `ReactNode`
|
|
2602
|
+
- `defaultValue`: `number` (default: `50`)
|
|
2603
|
+
- `max`: `number` (default: `90`)
|
|
2604
|
+
- `min`: `number` (default: `10`)
|
|
2605
|
+
- `onChange`: `(value: number) => void`
|
|
2606
|
+
- `orientation`: `'horizontal'` | `'vertical'` (default: `'horizontal'`)
|
|
2607
|
+
- `ref`: `Ref<HTMLDivElement>`
|
|
2608
|
+
- `step`: `number` (default: `5`)
|
|
2609
|
+
- `value`: `number`
|
|
2610
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2611
|
+
|
|
2612
|
+
Props (ResizablePanels.Panel):
|
|
2613
|
+
|
|
2614
|
+
- `children`: `ReactNode`
|
|
2615
|
+
- `ref`: `Ref<HTMLDivElement>`
|
|
2616
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2617
|
+
|
|
2618
|
+
Props (ResizablePanels.Handle):
|
|
2619
|
+
|
|
2620
|
+
- _No props of its own._
|
|
2621
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style` / `role` / `children` / `tabIndex` / `aria-orientation` / `aria-valuenow` / `aria-valuemin` / `aria-valuemax` / `aria-controls`.
|
|
2622
|
+
|
|
2623
|
+
## Observers
|
|
2624
|
+
|
|
2625
|
+
Components that watch their children and report back without adding a wrapper
|
|
2626
|
+
element. They are built on React's Fragment refs, so whatever the children
|
|
2627
|
+
render is what gets observed — including host elements that mount later.
|
|
2628
|
+
|
|
2629
|
+
### InView
|
|
2630
|
+
|
|
2631
|
+
Reports whether its children are inside the viewport, or inside `root`.
|
|
2632
|
+
|
|
2633
|
+
```tsx
|
|
2634
|
+
import { InView } from '@k8ordo/ui';
|
|
2635
|
+
|
|
2636
|
+
const [isInView, setIsInView] = useState(false);
|
|
2637
|
+
|
|
2638
|
+
<InView onChange={setIsInView}>
|
|
2639
|
+
<section>…</section>
|
|
2640
|
+
</InView>;
|
|
2641
|
+
|
|
2642
|
+
// inside a scroll container, and only until it has been seen once
|
|
2643
|
+
const [container, setContainer] = useState<HTMLElement | null>(null);
|
|
2644
|
+
|
|
2645
|
+
<div ref={setContainer} style={{ overflowY: 'auto' }}>
|
|
2646
|
+
<InView once onChange={reveal} root={container} rootMargin="0px 0px 24px 0px">
|
|
2647
|
+
<img alt="" src="…" />
|
|
2648
|
+
</InView>
|
|
2649
|
+
</div>;
|
|
2650
|
+
```
|
|
2651
|
+
|
|
2652
|
+
- `onChange` reports the state as soon as a host element is observed, then again
|
|
2653
|
+
each time it flips. The same value is never reported twice in a row, even
|
|
2654
|
+
when `root` changes and the observer is re-created.
|
|
2655
|
+
- With several host elements, `isInView` is `true` while **any** of them
|
|
2656
|
+
intersects, and it follows children that mount or unmount later. While the
|
|
2657
|
+
children render no host element, `onChange` is not called at all — the first
|
|
2658
|
+
report comes once one mounts.
|
|
2659
|
+
- `once` stops observing after the first `true`.
|
|
2660
|
+
- Hold `root` in state, not a `RefObject`: the observer has to be re-created
|
|
2661
|
+
once the element exists.
|
|
2662
|
+
|
|
2663
|
+
Props:
|
|
2664
|
+
|
|
2665
|
+
- `children`: `ReactNode` (required)
|
|
2666
|
+
- `onChange`: `(isInView: boolean) => void` (required)
|
|
2667
|
+
- `once`: `boolean` (default: `false`)
|
|
2668
|
+
- `root`: `Element` | `null` (default: `null`)
|
|
2669
|
+
- `rootMargin`: `string` (default: `'0px'`)
|
|
2670
|
+
- `threshold`: `number` (default: `0`)
|
|
2671
|
+
|
|
2672
|
+
### Resize
|
|
2673
|
+
|
|
2674
|
+
Calls `onChange` when the size of its children changes.
|
|
2675
|
+
|
|
2676
|
+
```tsx
|
|
2677
|
+
import { Resize } from '@k8ordo/ui';
|
|
2678
|
+
|
|
2679
|
+
<Resize onChange={remeasure}>
|
|
2680
|
+
<div>…</div>
|
|
2681
|
+
</Resize>;
|
|
2682
|
+
```
|
|
2683
|
+
|
|
2684
|
+
- `onChange` is also called once when observation starts, as a native
|
|
2685
|
+
`ResizeObserver` is.
|
|
2686
|
+
- It takes no argument; read what you need from the DOM in the handler.
|
|
2687
|
+
|
|
2688
|
+
Props:
|
|
2689
|
+
|
|
2690
|
+
- `children`: `ReactNode` (required)
|
|
2691
|
+
- `onChange`: `() => void` (required)
|
|
2692
|
+
|
|
2693
|
+
## Icons
|
|
2694
|
+
|
|
2695
|
+
Decorative icons, all from the root entry. Each renders an `<svg>` with
|
|
2696
|
+
`aria-hidden` and `focusable="false"`, so the accessible name belongs to the
|
|
2697
|
+
control around it — an `IconButton`'s `label`, for example.
|
|
2698
|
+
|
|
2699
|
+
```tsx
|
|
2700
|
+
import { AlertIcon, ChevronIcon, CloseIcon } from '@k8ordo/ui';
|
|
2701
|
+
|
|
2702
|
+
<CloseIcon />
|
|
2703
|
+
<ChevronIcon direction="down" size="sm" />
|
|
2704
|
+
<AlertIcon status="warning" />
|
|
2705
|
+
```
|
|
2706
|
+
|
|
2707
|
+
Every icon takes one optional prop and nothing else:
|
|
2708
|
+
|
|
2709
|
+
- `size`: `'xs'` | `'sm'` | `'md'` | `'lg'` | `'xl'` | `'2xl'` | `'3xl'` (default: `'md'`)
|
|
2710
|
+
|
|
2711
|
+
Two of them also require a prop that picks the glyph:
|
|
2712
|
+
|
|
2713
|
+
- `ChevronIcon`: `direction`: `Direction` (`'up'` | `'down'` | `'left'` | `'right'`)
|
|
2714
|
+
- `AlertIcon`: `status`: `Status` (`'success'` | `'info'` | `'warning'` | `'error'`)
|
|
2715
|
+
|
|
2716
|
+
<!-- generated:icons -->
|
|
2717
|
+
|
|
2718
|
+
The icons: `AccessibilityIcon`, `AIIcon`, `AlertIcon`, `AssistantIcon`,
|
|
2719
|
+
`AtomIcon`, `BadIcon`, `BlogIcon`, `BoringIcon`, `CheckIcon`, `ChevronIcon`,
|
|
2720
|
+
`ChromeIcon`, `CloseIcon`, `CodeXmlIcon`, `ColorContrastIcon`, `ColorInfoIcon`,
|
|
2721
|
+
`ColorScaleIcon`, `CopyIcon`, `DarkModeIcon`, `DifficultIcon`, `EasyIcon`,
|
|
2722
|
+
`EdgeIcon`, `ExternalLinkIcon`, `FirefoxIcon`, `FlaskIcon`, `ForkIcon`,
|
|
2723
|
+
`FormIcon`, `FullscreenIcon`, `GitHubIcon`, `GoodIcon`, `HistoryIcon`,
|
|
2724
|
+
`HorizontalWritingIcon`, `InformativeIcon`, `InterestingIcon`, `LightModeIcon`,
|
|
2725
|
+
`LinkIcon`, `ListIcon`, `LocationIcon`, `LockIcon`, `LockOpenIcon`, `LogoIcon`,
|
|
2726
|
+
`MailIcon`, `MinusIcon`, `MixedColorIcon`, `NavigationMenuIcon`, `NewsIcon`,
|
|
2727
|
+
`PackageIcon`, `PaletteIcon`, `PlusIcon`, `PrepareIcon`, `PublishDateIcon`,
|
|
2728
|
+
`QiitaIcon`, `RefreshIcon`, `RSSIcon`, `SafariIcon`, `SendIcon`, `ShallowIcon`,
|
|
2729
|
+
`ShieldCheckIcon`, `SlideIcon`, `SparklesIcon`, `SquircleIcon`, `SubscribeIcon`,
|
|
2730
|
+
`TableIcon`, `TagIcon`, `TwitterIcon`, `UpdateDateIcon`, `VerticalWritingIcon`,
|
|
2731
|
+
`ViewIcon`, `ViewOffIcon`.
|
|
2732
|
+
|
|
2733
|
+
<!-- /generated:icons -->
|
|
2734
|
+
|
|
2735
|
+
`Logo` is the mark `LogoIcon` draws, as a bare `<svg>` without the sizing: it
|
|
2736
|
+
takes `className`, `aria-hidden`, and `focusable` (`Partial<IconRenderProps>`)
|
|
2737
|
+
instead of `size`. The types `BaseIconProps` (`{ size }`) and
|
|
2738
|
+
`IconRenderProps` (`{ className: string; 'aria-hidden': true; focusable: 'false' }`)
|
|
2739
|
+
are exported as well.
|
|
2740
|
+
|
|
1374
2741
|
## Providers
|
|
1375
2742
|
|
|
1376
2743
|
### UIProvider
|
|
1377
2744
|
|
|
1378
|
-
Wrap the app root once. It includes ToastProvider
|
|
2745
|
+
Wrap the app root once. It includes ToastProvider. The components' own wording needs no provider: it follows `@k8ordo/i18n` (i18n, below).
|
|
1379
2746
|
|
|
1380
2747
|
```tsx
|
|
1381
2748
|
import { UIProvider } from '@k8ordo/ui';
|
|
@@ -1388,11 +2755,14 @@ import { UIProvider } from '@k8ordo/ui';
|
|
|
1388
2755
|
Props:
|
|
1389
2756
|
|
|
1390
2757
|
- `children`: `ReactNode`
|
|
1391
|
-
- `messages`: `Partial<Messages>`
|
|
1392
2758
|
|
|
1393
2759
|
### PortalRootProvider
|
|
1394
2760
|
|
|
1395
|
-
|
|
2761
|
+
A context that shares a portal container with your own `createPortal` calls;
|
|
2762
|
+
read it with `usePortalRoot()`. The library's overlays do not read it — they
|
|
2763
|
+
open in the top layer (`<dialog>`, the Popover API), and toasts go where
|
|
2764
|
+
`ToastProvider`'s `portalRef` points. `Modal` passes its own `<dialog>` as the
|
|
2765
|
+
value, so a portal placed through `usePortalRoot()` inside a modal stays in it.
|
|
1396
2766
|
|
|
1397
2767
|
```tsx
|
|
1398
2768
|
import { PortalRootProvider, usePortalRoot } from '@k8ordo/ui';
|
|
@@ -1402,73 +2772,128 @@ import { PortalRootProvider, usePortalRoot } from '@k8ordo/ui';
|
|
|
1402
2772
|
|
|
1403
2773
|
Props:
|
|
1404
2774
|
|
|
2775
|
+
- `value`: `RefObject<HTMLElement | null>` (required)
|
|
1405
2776
|
- `children`: `ReactNode`
|
|
1406
|
-
- `value`: `RefObject<HTMLElement | null>`
|
|
1407
2777
|
|
|
1408
2778
|
## i18n (message dictionary)
|
|
1409
2779
|
|
|
1410
|
-
The wording components own internally (close, required, loading, …) comes from a dictionary
|
|
2780
|
+
The wording components own internally (close, required, loading, …) comes from a dictionary, picked by `@k8ordo/i18n`'s current locale. There is no provider and nothing to pass.
|
|
1411
2781
|
|
|
1412
|
-
|
|
2782
|
+
- An application that defines its locale set with `defineLocales` gets the locale its messages render in: the one the URL names, or the set's default.
|
|
2783
|
+
- An application that defines no set gets **English**, whatever its URL starts with, so the server and the browser agree.
|
|
2784
|
+
- The module defining the set has to be loaded in the browser as well; where no set is defined the components speak English.
|
|
1413
2785
|
|
|
1414
|
-
|
|
1415
|
-
import { UIProvider } from '@k8ordo/ui';
|
|
1416
|
-
import { en } from '@k8ordo/ui/i18n';
|
|
1417
|
-
|
|
1418
|
-
<UIProvider messages={en}>
|
|
1419
|
-
<App />
|
|
1420
|
-
</UIProvider>;
|
|
1421
|
-
```
|
|
1422
|
-
|
|
1423
|
-
To replace only part of it, spread the dictionary and override those keys (`Partial<Messages>`, so you need not fill in every key).
|
|
2786
|
+
`ja` and `en` ship with the library. Register any other locale — or replace a built-in one — with `registerMessages`, next to where the set is defined:
|
|
1424
2787
|
|
|
1425
2788
|
```tsx
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
</UIProvider>
|
|
1429
|
-
```
|
|
2789
|
+
import { en, registerMessages } from '@k8ordo/ui/i18n';
|
|
2790
|
+
import type { Messages } from '@k8ordo/ui/i18n';
|
|
1430
2791
|
|
|
1431
|
-
|
|
2792
|
+
const fr: Messages = { close: 'Fermer' /* …every key */ };
|
|
2793
|
+
registerMessages('fr', fr);
|
|
1432
2794
|
|
|
1433
|
-
|
|
1434
|
-
<UIProvider messages={{ close: '閉じる(Esc)' }}>
|
|
1435
|
-
<App />
|
|
1436
|
-
</UIProvider>
|
|
2795
|
+
registerMessages('en', { ...en, close: 'Dismiss' });
|
|
1437
2796
|
```
|
|
1438
2797
|
|
|
2798
|
+
A regional tag without a dictionary of its own (`en-US`) reads its language's (`en`). Rendering in a locale nothing has text for throws, naming the locale and `registerMessages`.
|
|
2799
|
+
|
|
1439
2800
|
### Resolution order
|
|
1440
2801
|
|
|
1441
|
-
**Component prop >
|
|
2802
|
+
**Component prop > registered dictionary > built-in dictionary**.
|
|
1442
2803
|
|
|
1443
2804
|
A component with a wording prop of its own — `Spinner`'s `label`, `Alert`'s `closeLabel`, `PasswordInput`'s `showLabel` / `hideLabel`, `Pagination`'s `prevLabel` / `nextLabel` — takes that prop over the dictionary.
|
|
1444
2805
|
|
|
1445
2806
|
```tsx
|
|
1446
|
-
//
|
|
2807
|
+
// Whatever the locale, this one Spinner reads 「保存中」
|
|
1447
2808
|
<Spinner label="保存中" />
|
|
1448
2809
|
```
|
|
1449
2810
|
|
|
1450
2811
|
### Exports
|
|
1451
2812
|
|
|
1452
2813
|
```tsx
|
|
1453
|
-
import {
|
|
2814
|
+
import {
|
|
2815
|
+
en,
|
|
2816
|
+
getMessages,
|
|
2817
|
+
ja,
|
|
2818
|
+
messageUsage,
|
|
2819
|
+
registerMessages,
|
|
2820
|
+
type Messages,
|
|
2821
|
+
} from '@k8ordo/ui/i18n';
|
|
1454
2822
|
```
|
|
1455
2823
|
|
|
1456
|
-
|
|
2824
|
+
### Reading the wording in your own elements
|
|
2825
|
+
|
|
2826
|
+
`getMessages` returns the wording in effect: the dictionary for the current locale, a registered one before a built-in one. Read from it in an element you draw through `renderItem`, or in a component of your own that sits beside the library, and it follows the same language and replacements as the components do. It is not a hook, so a Server Component calls it too.
|
|
2827
|
+
|
|
2828
|
+
```tsx
|
|
2829
|
+
import { getMessages } from '@k8ordo/ui/i18n';
|
|
2830
|
+
|
|
2831
|
+
function DismissButton({ onDismiss }) {
|
|
2832
|
+
const { close } = getMessages();
|
|
2833
|
+
return (
|
|
2834
|
+
<button aria-label={close} onClick={onDismiss} type="button">
|
|
2835
|
+
×
|
|
2836
|
+
</button>
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
```
|
|
1457
2840
|
|
|
1458
2841
|
### Key list
|
|
1459
2842
|
|
|
1460
|
-
Every key in the `Messages` type
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
2843
|
+
Every key in the `Messages` type, grouped by the components that draw it —
|
|
2844
|
+
the same mapping `messageUsage` exports. All values are `string`.
|
|
2845
|
+
|
|
2846
|
+
<!-- generated:message-keys -->
|
|
2847
|
+
|
|
2848
|
+
- Alert / Dialog / Drawer / Response: `close`
|
|
2849
|
+
- FormControl: `required`
|
|
2850
|
+
- Spinner / Progress / Combobox: `loading`
|
|
2851
|
+
- Avatar: `avatar`
|
|
2852
|
+
- Code: `color`
|
|
2853
|
+
- Alert / Callout: `alertSuccess`, `alertInfo`, `alertWarning`, `alertError`
|
|
2854
|
+
- ToastProvider: `toastRegion`
|
|
2855
|
+
- CopyButton / Message.Copy: `copy`
|
|
2856
|
+
- CopyButton / CodeBlock / Message.Copy: `copied`, `copyFailed`
|
|
2857
|
+
- Autocomplete: `autocompletePlaceholder`, `autocompleteRemoveTag`, `autocompleteClear`, `autocompleteEmpty`
|
|
2858
|
+
- Combobox: `comboboxToggle`, `comboboxEmpty`, `comboboxFailed`
|
|
2859
|
+
- FileField: `fileFieldRemove`
|
|
2860
|
+
- FileField.Dropzone / FileField (generative UI): `fileFieldTrigger`, `fileFieldDrop`
|
|
2861
|
+
- NumberField: `numberFieldIncrement`, `numberFieldDecrement`, `numberFieldRangeUnderflow` (`{min}` is replaced), `numberFieldRangeOverflow` (`{max}` is replaced)
|
|
2862
|
+
- RangeSlider: `rangeSliderStart`, `rangeSliderEnd`
|
|
2863
|
+
- Calendar / DatePicker: `calendarPreviousMonth`, `calendarNextMonth`
|
|
2864
|
+
- DatePicker: `datePickerOpen`, `datePickerDialog`
|
|
2865
|
+
- ColorPicker: `colorPickerHue`, `colorPickerSaturation`, `colorPickerLightness`, `colorPickerSwatches`, `colorPickerOpen`
|
|
2866
|
+
- PasswordInput: `passwordShow`, `passwordHide`
|
|
2867
|
+
- ListBox: `listBoxPlaceholder`
|
|
2868
|
+
- Breadcrumb: `breadcrumb`
|
|
2869
|
+
- Tabs (generative UI): `tabList`
|
|
2870
|
+
- Pagination: `paginationLabel`, `paginationPrevious`, `paginationNext`
|
|
2871
|
+
- Stepper: `stepperComplete`
|
|
2872
|
+
- DataTable: `dataTableColumns`, `dataTableSelectAll`, `dataTableSelectRow`
|
|
2873
|
+
- CodeBlock: `codeBlockCopy`
|
|
2874
|
+
- Carousel: `carousel`, `carouselPrevious`, `carouselNext`
|
|
2875
|
+
- Carousel.Slide: `carouselSlide`
|
|
2876
|
+
- ResizablePanels.Handle: `resizablePanelsHandle`
|
|
2877
|
+
- TableOfContents: `tableOfContents`
|
|
2878
|
+
- CommandPalette: `commandPalette`, `commandPaletteSearch`, `commandPaletteEmpty`
|
|
2879
|
+
- Conversation.Messages: `chat`
|
|
2880
|
+
- Conversation.ScrollButton: `scrollToLatest`
|
|
2881
|
+
- Reasoning: `reasoning`, `reasoningStreaming`
|
|
2882
|
+
- Suggestion.List: `suggestions`
|
|
2883
|
+
- PromptInput: `send`, `stop`
|
|
2884
|
+
- PromptInput.Attach: `attach`
|
|
2885
|
+
- Attachment.List / PromptInput.Attachments: `attachments`
|
|
2886
|
+
- PromptInput.Attachments: `attachmentRemove`
|
|
2887
|
+
- Attachment.Item / PromptInput.Attachments: `attachmentImage`
|
|
2888
|
+
- Source.List: `sources`
|
|
2889
|
+
- Message.Actions: `messageActions`
|
|
2890
|
+
- Message.Regenerate: `regenerate`
|
|
2891
|
+
- Message.Feedback: `feedbackPositive`, `feedbackNegative`
|
|
2892
|
+
- ToolInvocation: `toolInput`, `toolOutput`, `toolError`, `toolDenied`, `toolApprovalRequest`, `toolApprove`, `toolDeny`
|
|
2893
|
+
- Response: `responseCopied`, `responseCopyCode`, `responseCopyLink`, `responseCopyTable`, `responseCopyTableAsCsv`, `responseCopyTableAsMarkdown`, `responseCopyTableAsTsv`, `responseDownloadDiagram`, `responseDownloadDiagramAsMmd`, `responseDownloadDiagramAsPng`, `responseDownloadDiagramAsSvg`, `responseDownloadFile`, `responseDownloadImage`, `responseDownloadTable`, `responseDownloadTableAsCsv`, `responseDownloadTableAsMarkdown`, `responseExitFullscreen`, `responseViewFullscreen`, `responseImageNotAvailable`, `responseOpenExternalLink`, `responseExternalLinkWarning`, `responseOpenLink`
|
|
2894
|
+
|
|
2895
|
+
<!-- /generated:message-keys -->
|
|
2896
|
+
|
|
2897
|
+
`fileFieldTrigger` is the button text of an empty `FileField.Dropzone`, and
|
|
2898
|
+
with `tabList` it is also what the generative-UI renderers fall back to when a
|
|
2899
|
+
spec leaves the trigger text or the tab-list name out.
|