@k8ordo/ui 1.0.0 → 2.1.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 +206 -171
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- 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 +33 -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 +14 -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 -3
- 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 +7 -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 +14 -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 +8 -11
- package/dist/components/ai/conversation/conversation.mjs +39 -40
- package/dist/components/ai/conversation/conversation.module.mjs +11 -0
- package/dist/components/ai/conversation/index.d.mts +15 -2
- package/dist/components/ai/conversation/index.mjs +8 -1
- package/dist/components/ai/index.d.mts +8 -10
- package/dist/components/ai/index.mjs +7 -5
- package/dist/components/ai/message/index.d.mts +39 -2
- package/dist/components/ai/message/index.mjs +12 -1
- package/dist/components/ai/message/message.css +71 -0
- package/dist/components/ai/message/message.d.mts +33 -5
- package/dist/components/ai/message/message.mjs +98 -12
- package/dist/components/ai/message/message.module.mjs +18 -0
- package/dist/components/ai/prompt-input/index.d.mts +31 -2
- package/dist/components/ai/prompt-input/index.mjs +10 -1
- package/dist/components/ai/prompt-input/prompt-input.css +101 -0
- package/dist/components/ai/prompt-input/prompt-input.d.mts +15 -8
- package/dist/components/ai/prompt-input/prompt-input.mjs +152 -42
- package/dist/components/ai/prompt-input/prompt-input.module.mjs +15 -0
- package/dist/components/ai/reasoning/reasoning.css +16 -0
- package/dist/components/ai/reasoning/reasoning.d.mts +2 -3
- package/dist/components/ai/reasoning/reasoning.mjs +5 -5
- package/dist/components/ai/reasoning/reasoning.module.mjs +8 -0
- package/dist/components/ai/response/response.css +453 -0
- package/dist/components/ai/response/response.d.mts +4 -6
- 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 +13 -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 +13 -2
- package/dist/components/ai/suggestion/index.mjs +7 -1
- package/dist/components/ai/suggestion/suggestion.css +32 -0
- package/dist/components/ai/suggestion/suggestion.d.mts +3 -6
- package/dist/components/ai/suggestion/suggestion.mjs +8 -11
- 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 +11 -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/tool-invocation.css +55 -0
- package/dist/components/ai/tool-invocation/tool-invocation.d.mts +5 -5
- 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 -9
- 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/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 +2 -0
- package/dist/components/buttons/icon-button/icon-button.css +81 -0
- package/dist/components/buttons/icon-button/icon-button.d.mts +39 -12
- package/dist/components/buttons/icon-button/icon-button.mjs +71 -58
- 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/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 +18 -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 -3
- 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/badge/badge.css +160 -0
- package/dist/components/data-display/badge/badge.d.mts +2 -3
- 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/card/card.css +44 -0
- package/dist/components/data-display/card/card.d.mts +2 -3
- 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/carousel/carousel.css +83 -0
- package/dist/components/data-display/carousel/carousel.d.mts +11 -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 -3
- 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 -3
- package/dist/components/data-display/code/find-all-colors.mjs +0 -161
- package/dist/components/data-display/code-block/code-block.css +161 -0
- package/dist/components/data-display/code-block/code-block.d.mts +12 -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 +9 -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 +2 -0
- package/dist/components/data-display/data-table/data-table.css +74 -0
- package/dist/components/data-display/data-table/data-table.d.mts +31 -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 +2 -0
- package/dist/components/data-display/heading/heading.css +99 -0
- package/dist/components/data-display/heading/heading.d.mts +2 -3
- 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/kbd/index.d.mts +2 -0
- package/dist/components/data-display/kbd/index.mjs +2 -0
- package/dist/components/data-display/kbd/kbd.css +21 -0
- package/dist/components/data-display/kbd/kbd.d.mts +8 -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 +2 -0
- package/dist/components/data-display/prose/prose.css +253 -0
- package/dist/components/data-display/prose/prose.d.mts +5 -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/table.css +107 -0
- package/dist/components/data-display/table/table.d.mts +10 -9
- 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 +2 -0
- package/dist/components/data-display/tree/tree.css +84 -0
- package/dist/components/data-display/tree/tree.d.mts +20 -0
- package/dist/components/data-display/tree/tree.mjs +151 -0
- package/dist/components/data-display/tree/tree.module.mjs +15 -0
- package/dist/components/feedback/alert/alert.css +42 -0
- package/dist/components/feedback/alert/alert.d.mts +3 -4
- package/dist/components/feedback/alert/alert.mjs +20 -23
- package/dist/components/feedback/alert/alert.module.mjs +14 -0
- package/dist/components/feedback/callout/callout.css +38 -0
- package/dist/components/feedback/callout/callout.d.mts +10 -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 +2 -0
- package/dist/components/feedback/empty-state/empty-state.css +36 -0
- package/dist/components/feedback/empty-state/empty-state.d.mts +10 -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 +2 -0
- package/dist/components/feedback/progress/progress.css +78 -0
- package/dist/components/feedback/progress/progress.d.mts +5 -5
- package/dist/components/feedback/progress/progress.mjs +17 -3
- package/dist/components/feedback/progress/progress.module.mjs +12 -0
- package/dist/components/feedback/skeleton/skeleton.css +64 -0
- package/dist/components/feedback/skeleton/skeleton.d.mts +2 -3
- package/dist/components/feedback/skeleton/skeleton.mjs +19 -2
- package/dist/components/feedback/skeleton/skeleton.module.mjs +17 -0
- package/dist/components/feedback/spinner/spinner.css +40 -0
- package/dist/components/feedback/spinner/spinner.d.mts +2 -3
- 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 -3
- package/dist/components/form/autocomplete/autocomplete.css +154 -0
- package/dist/components/form/autocomplete/autocomplete.d.mts +2 -3
- package/dist/components/form/autocomplete/autocomplete.mjs +70 -46
- package/dist/components/form/autocomplete/autocomplete.module.mjs +21 -0
- package/dist/components/form/calendar/calendar.css +98 -0
- package/dist/components/form/calendar/calendar.d.mts +14 -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 +2 -0
- package/dist/components/form/checkbox/checkbox.css +90 -0
- package/dist/components/form/checkbox/checkbox.d.mts +5 -3
- package/dist/components/form/checkbox/checkbox.mjs +30 -27
- package/dist/components/form/checkbox/checkbox.module.mjs +13 -0
- package/dist/components/form/checkbox-card/checkbox-card.css +138 -0
- package/dist/components/form/checkbox-card/checkbox-card.d.mts +3 -4
- 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-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 +5 -3
- package/dist/components/form/color-picker/color-picker.css +172 -0
- package/dist/components/form/color-picker/color-picker.d.mts +28 -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 +2 -0
- package/dist/components/form/combobox/combobox.css +143 -0
- package/dist/components/form/combobox/combobox.d.mts +32 -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 +2 -0
- package/dist/components/form/date-field/date-field.css +31 -0
- package/dist/components/form/date-field/date-field.d.mts +8 -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 +2 -0
- package/dist/components/form/date-picker/date-picker.css +90 -0
- package/dist/components/form/date-picker/date-picker.d.mts +12 -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 +2 -0
- package/dist/components/form/file-field/file-field.css +88 -0
- package/dist/components/form/file-field/file-field.d.mts +18 -19
- package/dist/components/form/file-field/file-field.mjs +130 -42
- package/dist/components/form/file-field/file-field.module.mjs +17 -0
- package/dist/components/form/file-field/index.d.mts +20 -2
- package/dist/components/form/file-field/index.mjs +9 -1
- package/dist/components/form/form/form.css +11 -0
- package/dist/components/form/form/form.d.mts +4 -4
- package/dist/components/form/form/form.mjs +2 -1
- package/dist/components/form/form/form.module.mjs +5 -0
- package/dist/components/form/form-control/form-control.css +43 -0
- package/dist/components/form/form-control/form-control.d.mts +2 -3
- 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/number-field/cast.d.mts +2 -3
- package/dist/components/form/number-field/cast.mjs +3 -22
- package/dist/components/form/number-field/number-field.css +122 -0
- package/dist/components/form/number-field/number-field.d.mts +10 -10
- 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/password-input.css +88 -0
- package/dist/components/form/password-input/password-input.d.mts +2 -3
- 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/radio.css +89 -0
- package/dist/components/form/radio/radio.d.mts +4 -3
- package/dist/components/form/radio/radio.mjs +16 -19
- package/dist/components/form/radio/radio.module.mjs +16 -0
- package/dist/components/form/radio-card/radio-card.css +147 -0
- package/dist/components/form/radio-card/radio-card.d.mts +4 -4
- 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 +2 -0
- package/dist/components/form/range-slider/range-slider.css +67 -0
- package/dist/components/form/range-slider/range-slider.d.mts +30 -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/select.css +74 -0
- package/dist/components/form/select/select.d.mts +2 -3
- package/dist/components/form/select/select.mjs +25 -8
- package/dist/components/form/select/select.module.mjs +12 -0
- 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 -7
- package/dist/components/form/slider/slider.mjs +44 -11
- package/dist/components/form/slider/slider.module.mjs +12 -0
- package/dist/components/form/switch/switch.css +103 -0
- package/dist/components/form/switch/switch.d.mts +2 -3
- 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/text-field.css +41 -0
- package/dist/components/form/text-field/text-field.d.mts +4 -5
- 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/textarea.css +50 -0
- package/dist/components/form/textarea/textarea.d.mts +3 -3
- 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 -61
- 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 +62 -29
- package/dist/components/index.mjs +36 -14
- 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/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 +21 -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/separator.css +37 -0
- package/dist/components/layout/separator/separator.d.mts +2 -3
- package/dist/components/layout/separator/separator.mjs +12 -9
- package/dist/components/layout/separator/separator.module.mjs +12 -0
- 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 -3
- package/dist/components/navigation/anchor/anchor.mjs +5 -2
- package/dist/components/navigation/anchor/anchor.module.mjs +8 -0
- package/dist/components/navigation/breadcrumb/breadcrumb.css +72 -0
- package/dist/components/navigation/breadcrumb/breadcrumb.d.mts +5 -6
- 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/pagination.css +36 -0
- package/dist/components/navigation/pagination/pagination.d.mts +2 -3
- 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 +31 -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 +2 -0
- package/dist/components/navigation/stepper/stepper.css +151 -0
- package/dist/components/navigation/stepper/stepper.d.mts +19 -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 +2 -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 +19 -2
- package/dist/components/navigation/tabs/index.mjs +9 -1
- package/dist/components/navigation/tabs/tabs.css +70 -0
- package/dist/components/navigation/tabs/tabs.d.mts +16 -19
- package/dist/components/navigation/tabs/tabs.mjs +52 -49
- 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 +2 -0
- package/dist/components/observers/resize/index.d.mts +2 -0
- package/dist/components/observers/resize/index.mjs +2 -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 +24 -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 +2 -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 +12 -17
- package/dist/components/overlays/dialog/dialog.mjs +11 -13
- package/dist/components/overlays/dialog/dialog.module.mjs +10 -0
- package/dist/components/overlays/dialog/index.d.mts +17 -2
- package/dist/components/overlays/dialog/index.mjs +8 -1
- 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/dropdown-menu/dropdown-menu.d.mts +19 -26
- package/dist/components/overlays/dropdown-menu/dropdown-menu.mjs +12 -20
- package/dist/components/overlays/dropdown-menu/hooks.d.mts +7 -7
- package/dist/components/overlays/dropdown-menu/hooks.mjs +3 -3
- package/dist/components/overlays/dropdown-menu/index.d.mts +30 -2
- package/dist/components/overlays/dropdown-menu/index.mjs +12 -1
- 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 +16 -16
- 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 +27 -2
- package/dist/components/overlays/list-box/index.mjs +9 -1
- package/dist/components/overlays/list-box/list-box.css +74 -0
- package/dist/components/overlays/list-box/list-box.d.mts +20 -23
- package/dist/components/overlays/list-box/list-box.mjs +17 -21
- package/dist/components/overlays/list-box/list-box.module.mjs +10 -0
- 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 -5
- package/dist/components/overlays/popover/escape-stack.d.mts +2 -3
- package/dist/components/overlays/popover/hooks.d.mts +7 -8
- package/dist/components/overlays/popover/index.d.mts +25 -2
- package/dist/components/overlays/popover/index.mjs +8 -1
- package/dist/components/overlays/popover/popover.css +51 -0
- package/dist/components/overlays/popover/popover.d.mts +18 -20
- package/dist/components/overlays/popover/popover.mjs +13 -12
- 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 +22 -2
- package/dist/components/overlays/tooltip/index.mjs +8 -1
- package/dist/components/overlays/tooltip/tooltip.css +15 -0
- package/dist/components/overlays/tooltip/tooltip.d.mts +19 -24
- package/dist/components/overlays/tooltip/tooltip.mjs +5 -9
- 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 -3
- package/dist/helpers/chain.mjs +0 -23
- package/dist/helpers/create-safe-context.d.mts +2 -3
- 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 -3
- 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 -3
- package/dist/hooks/controllable-state/index.mjs +9 -11
- package/dist/hooks/index.d.mts +2 -23
- package/dist/hooks/index.mjs +2 -22
- 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 +40 -47
- package/dist/index.mjs +36 -42
- 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 +292 -34
- package/dist/integrations/_shared/renderers.module.mjs +14 -0
- package/dist/integrations/_shared/schemas.d.mts +388 -104
- package/dist/integrations/_shared/schemas.mjs +165 -27
- package/dist/integrations/ai-sdk/index.d.mts +2 -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 -18
- 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 -7
- 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/{hooks/focus-trap/index.d.mts → internal/focus-trap.d.mts} +6 -7
- package/dist/{hooks/focus-trap/index.mjs → internal/focus-trap.mjs} +5 -5
- 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 -2534
- 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 +340 -158
- package/docs/llms.txt +18 -10
- package/docs/props.generated.json +2157 -292
- package/docs/references/ai-chat.md +396 -113
- package/docs/references/color.md +258 -113
- package/docs/references/components.md +1891 -461
- package/docs/references/generative-ui.md +163 -34
- package/docs/references/interaction-design.md +103 -73
- package/docs/references/spatial-design.md +112 -90
- package/docs/references/types.md +101 -0
- package/docs/references/typography.md +182 -73
- package/package.json +71 -70
- 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/hash/index.d.mts +0 -4
- package/dist/hooks/hash/index.mjs +0 -25
- 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/local-storage/index.d.mts +0 -4
- package/dist/hooks/local-storage/index.mjs +0 -46
- 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/session-storage/index.d.mts +0 -4
- package/dist/hooks/session-storage/index.mjs +0 -46
- 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 -120
- package/docs/references/hooks.md +0 -314
|
@@ -1,20 +1,77 @@
|
|
|
1
|
-
# k8ordo
|
|
1
|
+
# @k8ordo/ui component catalog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Importing
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
|
-
//
|
|
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
|
-
// プロバイダー(アプリルートで1回)
|
|
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
|
|
18
|
+
included: `Dialog.Root`, `Tabs.Root`, `Popover.Trigger`, and the rest are
|
|
19
|
+
composed outside the client modules, so the parts resolve on the server. Only
|
|
20
|
+
the interactive parts carry `'use client'`.
|
|
21
|
+
|
|
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
|
|
18
75
|
|
|
19
76
|
### Button
|
|
20
77
|
|
|
@@ -31,81 +88,100 @@ import { Button } from '@k8ordo/ui';
|
|
|
31
88
|
disabled={false}
|
|
32
89
|
isActive={false}
|
|
33
90
|
>
|
|
34
|
-
|
|
91
|
+
Button
|
|
35
92
|
</Button>
|
|
36
93
|
```
|
|
37
94
|
|
|
38
95
|
Props:
|
|
39
96
|
|
|
40
97
|
- `children`: `ReactNode`
|
|
41
|
-
- `color`: `'primary'` | `'secondary'` | `'base'
|
|
98
|
+
- `color`: `'primary'` | `'secondary'` | `'base'` (default: `'primary'`)
|
|
42
99
|
- `endIcon`: `ReactNode`
|
|
43
|
-
- `fullWidth`: `boolean
|
|
44
|
-
- `isActive`: `boolean
|
|
100
|
+
- `fullWidth`: `boolean` (default: `false`)
|
|
101
|
+
- `isActive`: `boolean` (default: `false`)
|
|
45
102
|
- `onAction`: `() => void | Promise<void>`
|
|
46
|
-
- `renderItem`: `(props:
|
|
47
|
-
- `size`: `'sm'` | `'md'` | `'lg'
|
|
103
|
+
- `renderItem`: `(props: ButtonRenderItemProps) => ReactNode`
|
|
104
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
48
105
|
- `startIcon`: `ReactNode`
|
|
49
|
-
- `type`: `'button'` | `'submit'
|
|
50
|
-
- `variant`: `'solid'` | `'outline'` | `'skeleton'
|
|
51
|
-
|
|
52
|
-
|
|
106
|
+
- `type`: `'button'` | `'submit'` (default: `'button'`)
|
|
107
|
+
- `variant`: `'solid'` | `'outline'` | `'skeleton'` (default: `'solid'`)
|
|
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>`.
|
|
53
124
|
|
|
54
125
|
```tsx
|
|
55
126
|
<Button
|
|
56
127
|
color="base"
|
|
57
128
|
variant="outline"
|
|
58
|
-
renderItem={({
|
|
59
|
-
<a
|
|
129
|
+
renderItem={({ children, disabled: _disabled, type: _type, ...props }) => (
|
|
130
|
+
<a href="/page" {...props}>
|
|
60
131
|
{children}
|
|
61
132
|
</a>
|
|
62
133
|
)}
|
|
63
134
|
>
|
|
64
|
-
|
|
135
|
+
Link
|
|
65
136
|
</Button>
|
|
66
137
|
```
|
|
67
138
|
|
|
68
139
|
### IconButton
|
|
69
140
|
|
|
70
|
-
|
|
141
|
+
An icon-only button. Styling is controlled by the `color` prop.
|
|
71
142
|
|
|
72
143
|
```tsx
|
|
73
144
|
import { IconButton } from '@k8ordo/ui';
|
|
74
145
|
|
|
75
|
-
<IconButton label="
|
|
146
|
+
<IconButton label="Close" color="transparent" size="md">
|
|
76
147
|
<CloseIcon />
|
|
77
148
|
</IconButton>;
|
|
78
149
|
```
|
|
79
150
|
|
|
80
151
|
Props:
|
|
81
152
|
|
|
82
|
-
- `label`: `string
|
|
153
|
+
- `label`: `string` (required)
|
|
83
154
|
- `children`: `ReactNode`
|
|
84
|
-
- `color`: `'transparent'` | `'base'` | `'primary'` | `'secondary'
|
|
155
|
+
- `color`: `'transparent'` | `'base'` | `'primary'` | `'secondary'` (default: `'transparent'`)
|
|
85
156
|
- `onAction`: `() => void | Promise<void>`
|
|
86
|
-
- `renderItem`: `(props:
|
|
87
|
-
- `size`: `'sm'` | `'md'` | `'lg'
|
|
88
|
-
- `tooltipDisabled`: `boolean
|
|
89
|
-
- `tooltipPlacement`: `Placement
|
|
90
|
-
|
|
91
|
-
|
|
157
|
+
- `renderItem`: `(props: IconButtonRenderItemProps) => ReactNode`
|
|
158
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
159
|
+
- `tooltipDisabled`: `boolean` (default: `false`)
|
|
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.
|
|
165
|
+
|
|
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.
|
|
92
172
|
|
|
93
173
|
```tsx
|
|
94
174
|
<IconButton
|
|
95
175
|
color="base"
|
|
96
|
-
label="
|
|
176
|
+
label="Mail"
|
|
97
177
|
renderItem={({
|
|
98
|
-
className,
|
|
99
178
|
children,
|
|
100
|
-
|
|
179
|
+
disabled: _disabled,
|
|
101
180
|
triggerProps,
|
|
181
|
+
type: _type,
|
|
182
|
+
...props
|
|
102
183
|
}) => (
|
|
103
|
-
<a
|
|
104
|
-
aria-label={ariaLabel}
|
|
105
|
-
className={className}
|
|
106
|
-
href="/contact"
|
|
107
|
-
{...triggerProps}
|
|
108
|
-
>
|
|
184
|
+
<a href="/contact" {...props} {...triggerProps}>
|
|
109
185
|
{children}
|
|
110
186
|
</a>
|
|
111
187
|
)}
|
|
@@ -114,80 +190,117 @@ Props:
|
|
|
114
190
|
</IconButton>
|
|
115
191
|
```
|
|
116
192
|
|
|
117
|
-
###
|
|
193
|
+
### CopyButton
|
|
118
194
|
|
|
119
|
-
|
|
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.
|
|
120
201
|
|
|
121
202
|
```tsx
|
|
122
|
-
import {
|
|
203
|
+
import { CopyButton } from '@k8ordo/ui';
|
|
204
|
+
|
|
205
|
+
<CopyButton value={css} label="Copy CSS" size="sm" />;
|
|
123
206
|
|
|
124
|
-
|
|
125
|
-
<
|
|
126
|
-
|
|
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
|
+
/>;
|
|
127
215
|
```
|
|
128
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
|
+
|
|
129
222
|
Props:
|
|
130
223
|
|
|
131
|
-
- `
|
|
132
|
-
- `
|
|
133
|
-
- `
|
|
134
|
-
- `
|
|
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'`)
|
|
135
229
|
|
|
136
|
-
|
|
230
|
+
### Toolbar
|
|
137
231
|
|
|
138
|
-
|
|
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.
|
|
139
237
|
|
|
140
|
-
|
|
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.
|
|
141
244
|
|
|
142
245
|
```tsx
|
|
143
|
-
import {
|
|
144
|
-
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
</
|
|
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>;
|
|
151
270
|
```
|
|
152
271
|
|
|
153
|
-
Props (
|
|
154
|
-
|
|
155
|
-
- `children`: `ReactNode`
|
|
156
|
-
|
|
157
|
-
Props (Accordion.Item):
|
|
272
|
+
Props (Root):
|
|
158
273
|
|
|
159
274
|
- `children`: `ReactNode`
|
|
160
|
-
- `
|
|
161
|
-
- `
|
|
162
|
-
- `
|
|
163
|
-
|
|
164
|
-
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`.
|
|
165
278
|
|
|
166
|
-
|
|
279
|
+
Props (Toolbar.Item):
|
|
167
280
|
|
|
168
|
-
|
|
281
|
+
- `renderItem`: `(props: ToolbarItemProps) => ReactElement` (required)
|
|
169
282
|
|
|
170
|
-
|
|
283
|
+
## Navigation
|
|
171
284
|
|
|
172
285
|
### Breadcrumb
|
|
173
286
|
|
|
174
|
-
|
|
287
|
+
A breadcrumb trail, as a compound component.
|
|
175
288
|
|
|
176
289
|
```tsx
|
|
177
290
|
import { Breadcrumb } from '@k8ordo/ui';
|
|
178
291
|
|
|
179
292
|
<Breadcrumb.List>
|
|
180
293
|
<Breadcrumb.Item>
|
|
181
|
-
<Breadcrumb.Link href="/"
|
|
294
|
+
<Breadcrumb.Link href="/">Home</Breadcrumb.Link>
|
|
182
295
|
</Breadcrumb.Item>
|
|
183
296
|
<Breadcrumb.Separator />
|
|
184
297
|
<Breadcrumb.Item>
|
|
185
|
-
<Breadcrumb.Link href="/products"
|
|
298
|
+
<Breadcrumb.Link href="/products">Products</Breadcrumb.Link>
|
|
186
299
|
</Breadcrumb.Item>
|
|
187
300
|
<Breadcrumb.Separator />
|
|
188
301
|
<Breadcrumb.Item>
|
|
189
302
|
<Breadcrumb.Link href="/products/1" current>
|
|
190
|
-
|
|
303
|
+
Details
|
|
191
304
|
</Breadcrumb.Link>
|
|
192
305
|
</Breadcrumb.Item>
|
|
193
306
|
</Breadcrumb.List>;
|
|
@@ -196,14 +309,17 @@ import { Breadcrumb } from '@k8ordo/ui';
|
|
|
196
309
|
Props (Breadcrumb.List):
|
|
197
310
|
|
|
198
311
|
- `children`: `ReactNode`
|
|
199
|
-
- `size`: `'sm'` | `'md'` | `'lg'
|
|
312
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
200
313
|
|
|
201
314
|
Props (Breadcrumb.Link):
|
|
202
315
|
|
|
203
|
-
- `href`: `T
|
|
316
|
+
- `href`: `T` (required)
|
|
204
317
|
- `children`: `ReactNode`
|
|
205
|
-
- `current`: `boolean
|
|
206
|
-
- `renderAnchor`: `(props: RenderBreadcrumbAnchorProps<T>) => ReactNode
|
|
318
|
+
- `current`: `boolean` (default: `false`)
|
|
319
|
+
- `renderAnchor`: `(props: RenderBreadcrumbAnchorProps<T>) => ReactNode` (default: `defaultRenderBreadcrumbAnchor`)
|
|
320
|
+
|
|
321
|
+
`Breadcrumb.Link`'s `renderAnchor` receives `href`, `className`, and `children`;
|
|
322
|
+
see [Render props](#render-props).
|
|
207
323
|
|
|
208
324
|
Props (Breadcrumb.Item):
|
|
209
325
|
|
|
@@ -211,7 +327,7 @@ Props (Breadcrumb.Item):
|
|
|
211
327
|
|
|
212
328
|
### Pagination
|
|
213
329
|
|
|
214
|
-
|
|
330
|
+
Pagination. It shows previous/next buttons and the current page only — there is no list of page numbers.
|
|
215
331
|
|
|
216
332
|
```tsx
|
|
217
333
|
import { Pagination } from '@k8ordo/ui';
|
|
@@ -221,206 +337,294 @@ import { Pagination } from '@k8ordo/ui';
|
|
|
221
337
|
|
|
222
338
|
Props:
|
|
223
339
|
|
|
224
|
-
- `currentPage`: `number
|
|
225
|
-
- `onChange`: `(page: number) => void
|
|
226
|
-
- `totalPages`: `number
|
|
340
|
+
- `currentPage`: `number` (required)
|
|
341
|
+
- `onChange`: `(page: number) => void` (required)
|
|
342
|
+
- `totalPages`: `number` (required)
|
|
227
343
|
- `aria-label`: `string`
|
|
228
|
-
- `disabled`: `boolean
|
|
344
|
+
- `disabled`: `boolean` (default: `false`)
|
|
229
345
|
- `nextLabel`: `string`
|
|
230
346
|
- `prevLabel`: `string`
|
|
231
347
|
- `ref`: `Ref<HTMLElement>`
|
|
348
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `children`.
|
|
232
349
|
|
|
233
|
-
###
|
|
350
|
+
### Stepper
|
|
234
351
|
|
|
235
|
-
|
|
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`.
|
|
236
358
|
|
|
237
|
-
|
|
238
|
-
|
|
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.
|
|
239
367
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<Tabs.Tab id="tab1">タブ1</Tabs.Tab>
|
|
243
|
-
<Tabs.Tab id="tab2">タブ2</Tabs.Tab>
|
|
244
|
-
</Tabs.List>
|
|
245
|
-
<Tabs.Panel id="tab1">パネル1</Tabs.Panel>
|
|
246
|
-
<Tabs.Panel id="tab2">パネル2</Tabs.Panel>
|
|
247
|
-
</Tabs.Root>;
|
|
248
|
-
```
|
|
368
|
+
In a multi-step form (see `@k8ordo/form`'s guide), keep every step mounted and
|
|
369
|
+
drive `value` from the step you show.
|
|
249
370
|
|
|
250
|
-
|
|
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
|
+
```
|
|
251
386
|
|
|
252
|
-
|
|
253
|
-
- `children`: `ReactNode`
|
|
254
|
-
- `defaultSelectedId`: `string` | `null`(既定: `null`)
|
|
255
|
-
- `onChange`: `(id: string) => void`
|
|
256
|
-
- `selectedId`: `string`
|
|
387
|
+
Props:
|
|
257
388
|
|
|
258
|
-
|
|
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`.
|
|
259
397
|
|
|
260
|
-
|
|
261
|
-
- `children`: `ReactNode`
|
|
398
|
+
### SideNav
|
|
262
399
|
|
|
263
|
-
|
|
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.
|
|
264
407
|
|
|
265
|
-
|
|
266
|
-
|
|
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
|
+
```
|
|
267
427
|
|
|
268
|
-
Props (
|
|
428
|
+
Props (SideNav.Root):
|
|
269
429
|
|
|
270
|
-
- `
|
|
430
|
+
- `label`: `string` (required)
|
|
271
431
|
- `children`: `ReactNode`
|
|
432
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `aria-label`.
|
|
272
433
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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`.
|
|
276
437
|
|
|
277
438
|
```tsx
|
|
278
|
-
|
|
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
|
+
```
|
|
279
451
|
|
|
280
|
-
|
|
281
|
-
<Card width="full" variant="shadow">
|
|
282
|
-
<div className="p-6">コンテンツ</div>
|
|
283
|
-
</Card>
|
|
452
|
+
Props (SideNav.Group):
|
|
284
453
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
<div className="p-6">コンテンツ</div>
|
|
288
|
-
</Card>
|
|
289
|
-
```
|
|
454
|
+
- `title`: `string` (required)
|
|
455
|
+
- `children`: `ReactNode`
|
|
290
456
|
|
|
291
|
-
Props:
|
|
457
|
+
Props (SideNav.Sub):
|
|
292
458
|
|
|
459
|
+
- `title`: `string` (required)
|
|
293
460
|
- `children`: `ReactNode`
|
|
294
|
-
- `
|
|
295
|
-
- `variant`: `'shadow'` | `'outline'`(既定: `'shadow'`)
|
|
296
|
-
- `width`: `'full'` | `'fit'`(既定: `'full'`)
|
|
461
|
+
- `defaultOpen`: `boolean` (default: `false`)
|
|
297
462
|
|
|
298
|
-
|
|
463
|
+
Props (SideNav.Link):
|
|
299
464
|
|
|
300
|
-
|
|
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`.
|
|
301
476
|
|
|
302
477
|
```tsx
|
|
303
|
-
import {
|
|
478
|
+
import { TableOfContents } from '@k8ordo/ui';
|
|
304
479
|
|
|
305
|
-
<
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
+
/>;
|
|
309
490
|
```
|
|
310
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
|
+
|
|
311
504
|
Props:
|
|
312
505
|
|
|
313
|
-
- `
|
|
314
|
-
- `
|
|
506
|
+
- `items`: `readonly TableOfContentsItem[]` (required)
|
|
507
|
+
- `label`: `string`
|
|
508
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `children` / `aria-labelledby`.
|
|
315
509
|
|
|
316
|
-
###
|
|
510
|
+
### Tabs
|
|
317
511
|
|
|
318
|
-
|
|
512
|
+
Tab switching, as a compound component.
|
|
319
513
|
|
|
320
514
|
```tsx
|
|
321
|
-
import {
|
|
515
|
+
import { Tabs } from '@k8ordo/ui';
|
|
322
516
|
|
|
323
|
-
<
|
|
324
|
-
<
|
|
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>;
|
|
325
525
|
```
|
|
326
526
|
|
|
327
|
-
|
|
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.
|
|
328
530
|
|
|
329
|
-
-
|
|
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.
|
|
330
536
|
|
|
331
|
-
|
|
537
|
+
Props (Tabs.Root):
|
|
332
538
|
|
|
333
|
-
|
|
539
|
+
- `ids`: `[string, ...string[]]` (required)
|
|
540
|
+
- `children`: `ReactNode`
|
|
541
|
+
- `defaultSelectedId`: `string` | `null` (default: `null`)
|
|
542
|
+
- `onChange`: `(id: string) => void`
|
|
543
|
+
- `selectedId`: `string`
|
|
334
544
|
|
|
335
|
-
|
|
336
|
-
import { Stack } from '@k8ordo/ui';
|
|
545
|
+
Props (Tabs.List):
|
|
337
546
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
<Card>2</Card>
|
|
341
|
-
</Stack>
|
|
547
|
+
- `label`: `string` (required)
|
|
548
|
+
- `children`: `ReactNode`
|
|
342
549
|
|
|
343
|
-
|
|
344
|
-
<Heading level="h2">タイトル</Heading>
|
|
345
|
-
<Button>操作</Button>
|
|
346
|
-
</Stack>
|
|
347
|
-
```
|
|
550
|
+
Props (Tabs.Panel):
|
|
348
551
|
|
|
349
|
-
|
|
552
|
+
- `id`: `string` (required)
|
|
553
|
+
- `children`: `ReactNode`
|
|
350
554
|
|
|
351
|
-
|
|
555
|
+
Props (Tabs.Tab):
|
|
556
|
+
|
|
557
|
+
- `id`: `string` (required)
|
|
352
558
|
- `children`: `ReactNode`
|
|
353
|
-
- `direction`: `'row'` | `'column'`(既定: `'column'`)
|
|
354
|
-
- `gap`: `GapSize`(既定: `'md'`)
|
|
355
|
-
- `justify`: `'start'` | `'center'` | `'end'` | `'between'`
|
|
356
|
-
- `padding`: `PaddingSize`
|
|
357
559
|
|
|
358
|
-
###
|
|
560
|
+
### Anchor
|
|
359
561
|
|
|
360
|
-
|
|
562
|
+
A text link. External links automatically get a new-tab icon.
|
|
361
563
|
|
|
362
564
|
```tsx
|
|
363
|
-
import {
|
|
364
|
-
|
|
365
|
-
<Grid cols={3} gap="md">
|
|
366
|
-
<Card>1</Card>
|
|
367
|
-
<Card>2</Card>
|
|
368
|
-
<Card>3</Card>
|
|
369
|
-
</Grid>
|
|
565
|
+
import { Anchor } from '@k8ordo/ui';
|
|
370
566
|
|
|
371
|
-
<
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
))}
|
|
375
|
-
</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>
|
|
376
570
|
```
|
|
377
571
|
|
|
378
572
|
Props:
|
|
379
573
|
|
|
380
|
-
- `children`: `ReactNode`
|
|
381
|
-
- `
|
|
382
|
-
- `
|
|
383
|
-
- `
|
|
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).
|
|
384
582
|
|
|
385
|
-
##
|
|
583
|
+
## Forms
|
|
386
584
|
|
|
387
|
-
|
|
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.
|
|
388
586
|
|
|
389
|
-
`ref`
|
|
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"`.
|
|
588
|
+
|
|
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.
|
|
390
592
|
|
|
391
593
|
### Form
|
|
392
594
|
|
|
393
|
-
`<form
|
|
595
|
+
A wrapper for `<form>`. `action` accepts a Server Action (`(formData) => …`) or a URL string.
|
|
394
596
|
|
|
395
597
|
```tsx
|
|
396
598
|
import { Button, Form, FormControl, TextField } from '@k8ordo/ui';
|
|
397
599
|
|
|
398
600
|
<Form action={submitAction}>
|
|
399
601
|
<FormControl
|
|
400
|
-
label="
|
|
602
|
+
label="Email"
|
|
401
603
|
required
|
|
402
604
|
renderInput={(props) => <TextField {...props} name="email" />}
|
|
403
605
|
/>
|
|
404
|
-
<Button type="submit"
|
|
606
|
+
<Button type="submit">Submit</Button>
|
|
405
607
|
</Form>;
|
|
406
608
|
```
|
|
407
609
|
|
|
408
610
|
Props:
|
|
409
611
|
|
|
410
|
-
- `children`: `ReactNode
|
|
612
|
+
- `children`: `ReactNode` (required)
|
|
411
613
|
- `action`: `((formData: FormData) => void | Promise<void>)` | `string`
|
|
614
|
+
- `ref`: `Ref<HTMLFormElement>`
|
|
615
|
+
- Other props are forwarded to `FormHTMLAttributes<HTMLFormElement>`, except `className` / `style`.
|
|
412
616
|
|
|
413
617
|
### FormControl
|
|
414
618
|
|
|
415
|
-
|
|
619
|
+
A wrapper for a form field. It unifies the label, help text, and error display.
|
|
416
620
|
|
|
417
621
|
```tsx
|
|
418
622
|
import { FormControl, TextField } from '@k8ordo/ui';
|
|
419
623
|
|
|
420
624
|
<FormControl
|
|
421
|
-
label="
|
|
422
|
-
errorText="
|
|
423
|
-
helpText="
|
|
625
|
+
label="Email address"
|
|
626
|
+
errorText="This field is required"
|
|
627
|
+
helpText="Enter your work email address"
|
|
424
628
|
required
|
|
425
629
|
renderInput={(props) => (
|
|
426
630
|
<TextField {...props} placeholder="example@mail.com" />
|
|
@@ -430,19 +634,20 @@ import { FormControl, TextField } from '@k8ordo/ui';
|
|
|
430
634
|
|
|
431
635
|
Props:
|
|
432
636
|
|
|
433
|
-
- `label`: `string
|
|
434
|
-
- `renderInput`: `(props: { id: string; 'aria-describedby': string | undefined; 'aria-labelledby': string; disabled: boolean; invalid: boolean; required: boolean; }) => ReactElement
|
|
435
|
-
- `disabled`: `boolean
|
|
637
|
+
- `label`: `string` (required)
|
|
638
|
+
- `renderInput`: `(props: { id: string; 'aria-describedby': string | undefined; 'aria-labelledby': string; disabled: boolean; invalid: boolean; required: boolean; }) => ReactElement` (required)
|
|
639
|
+
- `disabled`: `boolean` (default: `false`)
|
|
436
640
|
- `errorText`: `string`
|
|
437
641
|
- `helpText`: `string`
|
|
438
|
-
- `invalid`: `boolean
|
|
439
|
-
- `labelAs`: `'label'` | `'legend'
|
|
642
|
+
- `invalid`: `boolean` (default: `false`)
|
|
643
|
+
- `labelAs`: `'label'` | `'legend'` (default: `'label'`)
|
|
440
644
|
- `ref`: `Ref<HTMLElement>`
|
|
441
|
-
- `required`: `boolean
|
|
645
|
+
- `required`: `boolean` (default: `false`)
|
|
646
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style` / `children`.
|
|
442
647
|
|
|
443
|
-
`renderInput`
|
|
648
|
+
`renderInput` receives `{ id, 'aria-describedby', 'aria-labelledby', disabled, invalid, required }`.
|
|
444
649
|
|
|
445
|
-
|
|
650
|
+
The wrapper element depends on `labelAs`. `'label'` (the default) gives `<div>` + `<label htmlFor>`; `'legend'` gives `<fieldset>` + `<legend>`. `<fieldset>` is used only for `legend`, so that a single field never becomes an unnamed group. Pass `labelAs="legend"` when wrapping a group input such as `Radio` or `CheckboxGroup`.
|
|
446
651
|
|
|
447
652
|
### TextField
|
|
448
653
|
|
|
@@ -457,16 +662,23 @@ import { TextField } from '@k8ordo/ui';
|
|
|
457
662
|
<TextField id="email" value={value} onChange={onChange}
|
|
458
663
|
invalid={false} disabled={false} required={false} />
|
|
459
664
|
|
|
460
|
-
// type
|
|
665
|
+
// type can be passed too (default: "text")
|
|
461
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" />
|
|
462
670
|
```
|
|
463
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
|
+
|
|
464
675
|
Props:
|
|
465
676
|
|
|
466
677
|
- `children`: `ReactNode`
|
|
467
|
-
- `invalid`: `boolean
|
|
678
|
+
- `invalid`: `boolean` (default: `false`)
|
|
468
679
|
- `ref`: `Ref<HTMLInputElement>`
|
|
469
|
-
- `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`.
|
|
470
682
|
|
|
471
683
|
### Textarea
|
|
472
684
|
|
|
@@ -485,44 +697,187 @@ import { Textarea } from '@k8ordo/ui';
|
|
|
485
697
|
|
|
486
698
|
Props:
|
|
487
699
|
|
|
488
|
-
- `autoResize`: `boolean
|
|
700
|
+
- `autoResize`: `boolean` (default: `false`)
|
|
489
701
|
- `children`: `ReactNode`
|
|
490
|
-
- `fullHeight`: `boolean
|
|
491
|
-
- `invalid`: `boolean
|
|
702
|
+
- `fullHeight`: `boolean` (default: `false`)
|
|
703
|
+
- `invalid`: `boolean` (default: `false`)
|
|
492
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.
|
|
493
711
|
|
|
494
712
|
### NumberField
|
|
495
713
|
|
|
496
714
|
```tsx
|
|
497
715
|
import { NumberField } from '@k8ordo/ui';
|
|
498
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);
|
|
499
730
|
<NumberField
|
|
500
731
|
id="quantity"
|
|
501
732
|
min={0}
|
|
502
733
|
max={100}
|
|
503
734
|
value={value}
|
|
504
|
-
onChange={
|
|
735
|
+
onChange={setValue}
|
|
505
736
|
invalid={false}
|
|
506
737
|
disabled={false}
|
|
507
738
|
required={false}
|
|
508
739
|
/>;
|
|
509
740
|
```
|
|
510
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
|
+
|
|
511
768
|
Props:
|
|
512
769
|
|
|
513
|
-
- `defaultValue`: `
|
|
514
|
-
- `invalid`: `boolean
|
|
515
|
-
- `max`: `number
|
|
516
|
-
- `min`: `number
|
|
517
|
-
- `onChange`: `(value: number) => void`
|
|
518
|
-
- `precision`: `number
|
|
770
|
+
- `defaultValue`: `number` | `string`
|
|
771
|
+
- `invalid`: `boolean` (default: `false`)
|
|
772
|
+
- `max`: `number` | `string`
|
|
773
|
+
- `min`: `number` | `string`
|
|
774
|
+
- `onChange`: `(value: number | null) => void`
|
|
775
|
+
- `precision`: `number`
|
|
519
776
|
- `ref`: `Ref<HTMLInputElement>`
|
|
520
|
-
- `step`: `number
|
|
521
|
-
- `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`
|
|
522
877
|
|
|
523
878
|
### PasswordInput
|
|
524
879
|
|
|
525
|
-
|
|
880
|
+
A password input, with a show/hide toggle.
|
|
526
881
|
|
|
527
882
|
```tsx
|
|
528
883
|
import { PasswordInput } from '@k8ordo/ui';
|
|
@@ -534,8 +889,8 @@ import { PasswordInput } from '@k8ordo/ui';
|
|
|
534
889
|
invalid={false}
|
|
535
890
|
disabled={false}
|
|
536
891
|
required={false}
|
|
537
|
-
showLabel="
|
|
538
|
-
hideLabel="
|
|
892
|
+
showLabel="Show"
|
|
893
|
+
hideLabel="Hide"
|
|
539
894
|
/>;
|
|
540
895
|
```
|
|
541
896
|
|
|
@@ -543,9 +898,13 @@ Props:
|
|
|
543
898
|
|
|
544
899
|
- `children`: `ReactNode`
|
|
545
900
|
- `hideLabel`: `string`
|
|
546
|
-
- `invalid`: `boolean
|
|
901
|
+
- `invalid`: `boolean` (default: `false`)
|
|
547
902
|
- `ref`: `Ref<HTMLInputElement>`
|
|
548
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.
|
|
549
908
|
|
|
550
909
|
### Select
|
|
551
910
|
|
|
@@ -555,8 +914,8 @@ import { Select } from '@k8ordo/ui';
|
|
|
555
914
|
<Select
|
|
556
915
|
id="category"
|
|
557
916
|
options={[
|
|
558
|
-
{ value: '1', label: '
|
|
559
|
-
{ value: '2', label: '
|
|
917
|
+
{ value: '1', label: 'Option 1' },
|
|
918
|
+
{ value: '2', label: 'Option 2' },
|
|
560
919
|
]}
|
|
561
920
|
value={value}
|
|
562
921
|
onChange={onChange}
|
|
@@ -568,14 +927,33 @@ import { Select } from '@k8ordo/ui';
|
|
|
568
927
|
|
|
569
928
|
Props:
|
|
570
929
|
|
|
571
|
-
- `options`: `readonly Option[]
|
|
930
|
+
- `options`: `readonly Option[]` (required)
|
|
572
931
|
- `children`: `ReactNode`
|
|
573
|
-
- `invalid`: `boolean
|
|
932
|
+
- `invalid`: `boolean` (default: `false`)
|
|
574
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.
|
|
575
940
|
|
|
576
941
|
### Autocomplete
|
|
577
942
|
|
|
578
|
-
|
|
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.
|
|
579
957
|
|
|
580
958
|
```tsx
|
|
581
959
|
import { Autocomplete } from '@k8ordo/ui';
|
|
@@ -593,96 +971,227 @@ import { Autocomplete } from '@k8ordo/ui';
|
|
|
593
971
|
|
|
594
972
|
Props:
|
|
595
973
|
|
|
596
|
-
- `id`: `string
|
|
597
|
-
- `options`: `readonly Option[]
|
|
598
|
-
- `defaultValue`: `
|
|
599
|
-
- `invalid`: `boolean
|
|
974
|
+
- `id`: `string` (required)
|
|
975
|
+
- `options`: `readonly Option[]` (required)
|
|
976
|
+
- `defaultValue`: `string[]`
|
|
977
|
+
- `invalid`: `boolean` (default: `false`)
|
|
600
978
|
- `onChange`: `(value: string[]) => void`
|
|
601
979
|
- `ref`: `Ref<HTMLInputElement>`
|
|
602
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`.
|
|
603
1086
|
|
|
604
1087
|
### Checkbox
|
|
605
1088
|
|
|
606
|
-
|
|
1089
|
+
The label is passed as the `label` prop, not as children. `onChange` is `(checked, event)`.
|
|
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`.
|
|
607
1092
|
|
|
608
1093
|
```tsx
|
|
609
1094
|
import { Checkbox } from '@k8ordo/ui';
|
|
610
1095
|
|
|
611
1096
|
// Controlled
|
|
612
|
-
<Checkbox checked={checked} label="
|
|
1097
|
+
<Checkbox checked={checked} label="I agree" onChange={onChange} />
|
|
613
1098
|
|
|
614
1099
|
// Uncontrolled
|
|
615
|
-
<Checkbox defaultChecked label="
|
|
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" />
|
|
616
1104
|
```
|
|
617
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
|
+
|
|
618
1111
|
Props:
|
|
619
1112
|
|
|
620
|
-
- `label`: `string
|
|
1113
|
+
- `label`: `string` (required)
|
|
621
1114
|
- `checked`: `boolean`
|
|
622
|
-
- `defaultChecked`: `
|
|
1115
|
+
- `defaultChecked`: `boolean`
|
|
1116
|
+
- `indeterminate`: `boolean` (default: `false`)
|
|
1117
|
+
- `invalid`: `boolean` (default: `false`)
|
|
623
1118
|
- `itemValue`: `string`
|
|
1119
|
+
- `labelHidden`: `boolean` (default: `false`)
|
|
624
1120
|
- `onChange`: `(checked: boolean, event: ChangeEvent<HTMLInputElement>) => void`
|
|
625
1121
|
- `ref`: `Ref<HTMLInputElement>`
|
|
1122
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style` / `value` / `children`.
|
|
626
1123
|
|
|
627
1124
|
### CheckboxGroup
|
|
628
1125
|
|
|
629
|
-
|
|
1126
|
+
A group of checkboxes. The children are `CheckboxGroup.Item` (= `Checkbox`), and `itemValue` is required.
|
|
1127
|
+
|
|
1128
|
+
The group's selection lives in `value` / `onChange` (`string[]`). That is a different thing from a lone `Checkbox` holding a boolean in `checked` — do not conflate them.
|
|
630
1129
|
|
|
631
|
-
|
|
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.
|
|
632
1131
|
|
|
633
|
-
|
|
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.
|
|
634
1135
|
|
|
635
1136
|
```tsx
|
|
636
1137
|
import { CheckboxGroup } from '@k8ordo/ui';
|
|
637
1138
|
|
|
638
|
-
<p id="interests-label"
|
|
1139
|
+
<p id="interests-label">Areas of interest</p>
|
|
639
1140
|
<CheckboxGroup.Root
|
|
640
1141
|
aria-labelledby="interests-label"
|
|
641
1142
|
name="interests"
|
|
642
1143
|
value={values}
|
|
643
1144
|
onChange={setValues}
|
|
644
1145
|
>
|
|
645
|
-
<CheckboxGroup.Item itemValue="music" label="
|
|
646
|
-
<CheckboxGroup.Item itemValue="movie" label="
|
|
1146
|
+
<CheckboxGroup.Item itemValue="music" label="Music" />
|
|
1147
|
+
<CheckboxGroup.Item itemValue="movie" label="Film" />
|
|
647
1148
|
</CheckboxGroup.Root>;
|
|
648
1149
|
```
|
|
649
1150
|
|
|
650
1151
|
Props (CheckboxGroup.Item):
|
|
651
1152
|
|
|
652
|
-
- `label`: `string
|
|
1153
|
+
- `label`: `string` (required)
|
|
653
1154
|
- `checked`: `boolean`
|
|
654
|
-
- `defaultChecked`: `
|
|
1155
|
+
- `defaultChecked`: `boolean`
|
|
1156
|
+
- `indeterminate`: `boolean` (default: `false`)
|
|
1157
|
+
- `invalid`: `boolean` (default: `false`)
|
|
655
1158
|
- `itemValue`: `string`
|
|
1159
|
+
- `labelHidden`: `boolean` (default: `false`)
|
|
656
1160
|
- `onChange`: `(checked: boolean, event: ChangeEvent<HTMLInputElement>) => void`
|
|
657
1161
|
- `ref`: `Ref<HTMLInputElement>`
|
|
1162
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `className` / `style` / `value` / `children`.
|
|
658
1163
|
|
|
659
1164
|
Props (CheckboxGroup.Root):
|
|
660
1165
|
|
|
661
|
-
- `aria-labelledby`: `string
|
|
662
|
-
- `name`: `string
|
|
1166
|
+
- `aria-labelledby`: `string` (required)
|
|
1167
|
+
- `name`: `string` (required)
|
|
663
1168
|
- `children`: `ReactNode`
|
|
664
|
-
- `defaultValue`: `
|
|
665
|
-
- `invalid`: `boolean
|
|
1169
|
+
- `defaultValue`: `string[]`
|
|
1170
|
+
- `invalid`: `boolean` (default: `false`)
|
|
666
1171
|
- `onChange`: `(value: string[]) => void`
|
|
667
1172
|
- `ref`: `Ref<HTMLFieldSetElement>`
|
|
668
1173
|
- `value`: `string[]`
|
|
1174
|
+
- Other props are forwarded to `FieldsetHTMLAttributes<HTMLFieldSetElement>`, except `className` / `style` / `role`.
|
|
669
1175
|
|
|
670
1176
|
### CheckboxCard
|
|
671
1177
|
|
|
672
|
-
|
|
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.
|
|
673
1181
|
|
|
674
1182
|
```tsx
|
|
675
1183
|
import { CheckboxCard } from '@k8ordo/ui';
|
|
676
1184
|
|
|
677
1185
|
<CheckboxCard
|
|
1186
|
+
aria-labelledby="plan-checkbox"
|
|
678
1187
|
name="plan"
|
|
679
1188
|
disabled={false}
|
|
680
1189
|
options={[
|
|
681
|
-
{ value: 'basic', label: '
|
|
1190
|
+
{ value: 'basic', label: 'Basic', description: '$9 / month' },
|
|
682
1191
|
{
|
|
683
1192
|
value: 'pro',
|
|
684
|
-
label: '
|
|
685
|
-
description: '
|
|
1193
|
+
label: 'Pro',
|
|
1194
|
+
description: '$19 / month',
|
|
686
1195
|
visual: <Icon />,
|
|
687
1196
|
},
|
|
688
1197
|
]}
|
|
@@ -693,13 +1202,14 @@ import { CheckboxCard } from '@k8ordo/ui';
|
|
|
693
1202
|
|
|
694
1203
|
Props:
|
|
695
1204
|
|
|
696
|
-
- `aria-labelledby`: `string
|
|
697
|
-
- `options`: `readonly CheckboxCardOption[]
|
|
698
|
-
- `defaultValue`: `
|
|
699
|
-
- `invalid`: `boolean
|
|
1205
|
+
- `aria-labelledby`: `string` (required)
|
|
1206
|
+
- `options`: `readonly CheckboxCardOption[]` (required)
|
|
1207
|
+
- `defaultValue`: `string[]`
|
|
1208
|
+
- `invalid`: `boolean` (default: `false`)
|
|
700
1209
|
- `onChange`: `(value: string[]) => void`
|
|
701
1210
|
- `ref`: `Ref<HTMLFieldSetElement>`
|
|
702
1211
|
- `value`: `string[]`
|
|
1212
|
+
- Other props are forwarded to `FieldsetHTMLAttributes<HTMLFieldSetElement>`, except `className` / `style` / `children` / `role`.
|
|
703
1213
|
|
|
704
1214
|
### Radio
|
|
705
1215
|
|
|
@@ -711,8 +1221,8 @@ import { Radio } from '@k8ordo/ui';
|
|
|
711
1221
|
name="example"
|
|
712
1222
|
onChange={onChange}
|
|
713
1223
|
options={[
|
|
714
|
-
{ value: 'a', label: '
|
|
715
|
-
{ value: 'b', label: '
|
|
1224
|
+
{ value: 'a', label: 'Choice A' },
|
|
1225
|
+
{ value: 'b', label: 'Choice B' },
|
|
716
1226
|
]}
|
|
717
1227
|
value={value}
|
|
718
1228
|
/>;
|
|
@@ -720,17 +1230,21 @@ import { Radio } from '@k8ordo/ui';
|
|
|
720
1230
|
|
|
721
1231
|
Props:
|
|
722
1232
|
|
|
723
|
-
- `aria-labelledby`: `string
|
|
724
|
-
- `options`: `readonly Option[]
|
|
725
|
-
- `
|
|
1233
|
+
- `aria-labelledby`: `string` (required)
|
|
1234
|
+
- `options`: `readonly Option[]` (required)
|
|
1235
|
+
- `defaultValue`: `string`
|
|
1236
|
+
- `disabled`: `boolean` (default: `false`)
|
|
1237
|
+
- `invalid`: `boolean` (default: `false`)
|
|
726
1238
|
- `name`: `string`
|
|
727
1239
|
- `onChange`: `(value: string, event: ChangeEvent<HTMLInputElement>) => void`
|
|
728
1240
|
- `ref`: `Ref<HTMLDivElement>`
|
|
1241
|
+
- `required`: `boolean` (default: `false`)
|
|
729
1242
|
- `value`: `string`
|
|
1243
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `role` / `className` / `style` / `children`.
|
|
730
1244
|
|
|
731
1245
|
### RadioCard
|
|
732
1246
|
|
|
733
|
-
|
|
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.
|
|
734
1248
|
|
|
735
1249
|
```tsx
|
|
736
1250
|
import { RadioCard } from '@k8ordo/ui';
|
|
@@ -740,11 +1254,11 @@ import { RadioCard } from '@k8ordo/ui';
|
|
|
740
1254
|
name="plan"
|
|
741
1255
|
disabled={false}
|
|
742
1256
|
options={[
|
|
743
|
-
{ value: 'basic', label: '
|
|
1257
|
+
{ value: 'basic', label: 'Basic', description: '$9 / month' },
|
|
744
1258
|
{
|
|
745
1259
|
value: 'pro',
|
|
746
|
-
label: '
|
|
747
|
-
description: '
|
|
1260
|
+
label: 'Pro',
|
|
1261
|
+
description: '$19 / month',
|
|
748
1262
|
visual: <Icon />,
|
|
749
1263
|
},
|
|
750
1264
|
]}
|
|
@@ -755,17 +1269,21 @@ import { RadioCard } from '@k8ordo/ui';
|
|
|
755
1269
|
|
|
756
1270
|
Props:
|
|
757
1271
|
|
|
758
|
-
- `aria-labelledby`: `string
|
|
759
|
-
- `options`: `readonly RadioCardOption[]
|
|
760
|
-
- `defaultValue`: `
|
|
761
|
-
- `invalid`: `boolean
|
|
1272
|
+
- `aria-labelledby`: `string` (required)
|
|
1273
|
+
- `options`: `readonly RadioCardOption[]` (required)
|
|
1274
|
+
- `defaultValue`: `string`
|
|
1275
|
+
- `invalid`: `boolean` (default: `false`)
|
|
762
1276
|
- `onChange`: `(value: string) => void`
|
|
763
1277
|
- `ref`: `Ref<HTMLFieldSetElement>`
|
|
1278
|
+
- `required`: `boolean` (default: `false`)
|
|
764
1279
|
- `value`: `string`
|
|
1280
|
+
- Other props are forwarded to `FieldsetHTMLAttributes<HTMLFieldSetElement>`, except `className` / `style` / `children` / `role`.
|
|
765
1281
|
|
|
766
1282
|
### Slider
|
|
767
1283
|
|
|
768
|
-
|
|
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'`.
|
|
769
1287
|
|
|
770
1288
|
```tsx
|
|
771
1289
|
import { Slider } from '@k8ordo/ui';
|
|
@@ -784,18 +1302,62 @@ import { Slider } from '@k8ordo/ui';
|
|
|
784
1302
|
|
|
785
1303
|
Props:
|
|
786
1304
|
|
|
787
|
-
- `defaultValue`: `
|
|
788
|
-
- `invalid`: `boolean
|
|
789
|
-
- `max`: `number
|
|
790
|
-
- `min`: `number
|
|
1305
|
+
- `defaultValue`: `number` | `string`
|
|
1306
|
+
- `invalid`: `boolean` (default: `false`)
|
|
1307
|
+
- `max`: `number` | `string`
|
|
1308
|
+
- `min`: `number` | `string`
|
|
791
1309
|
- `onChange`: `(value: number) => void`
|
|
792
1310
|
- `ref`: `Ref<HTMLInputElement>`
|
|
793
|
-
- `step`: `number
|
|
1311
|
+
- `step`: `number` | `'any'` (default: `1`)
|
|
794
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`.
|
|
795
1357
|
|
|
796
1358
|
### Switch
|
|
797
1359
|
|
|
798
|
-
|
|
1360
|
+
A toggle switch.
|
|
799
1361
|
|
|
800
1362
|
```tsx
|
|
801
1363
|
import { Switch } from '@k8ordo/ui';
|
|
@@ -804,7 +1366,7 @@ import { Switch } from '@k8ordo/ui';
|
|
|
804
1366
|
checked={checked}
|
|
805
1367
|
disabled={false}
|
|
806
1368
|
invalid={false}
|
|
807
|
-
label="
|
|
1369
|
+
label="Enable notifications"
|
|
808
1370
|
onChange={onChange}
|
|
809
1371
|
required={false}
|
|
810
1372
|
/>;
|
|
@@ -812,16 +1374,18 @@ import { Switch } from '@k8ordo/ui';
|
|
|
812
1374
|
|
|
813
1375
|
Props:
|
|
814
1376
|
|
|
815
|
-
- `label`: `string
|
|
1377
|
+
- `label`: `string` (required)
|
|
816
1378
|
- `checked`: `boolean`
|
|
817
|
-
- `defaultChecked`: `
|
|
818
|
-
- `invalid`: `boolean
|
|
1379
|
+
- `defaultChecked`: `boolean`
|
|
1380
|
+
- `invalid`: `boolean` (default: `false`)
|
|
819
1381
|
- `onChange`: `(checked: boolean, event: ChangeEvent<HTMLInputElement>) => void`
|
|
820
1382
|
- `ref`: `Ref<HTMLInputElement>`
|
|
1383
|
+
- Other props are forwarded to `InputHTMLAttributes<HTMLInputElement>`, except `type` / `role` / `className` / `style` / `value` / `children`.
|
|
821
1384
|
|
|
822
1385
|
### FileField
|
|
823
1386
|
|
|
824
|
-
|
|
1387
|
+
File upload, as a composite pattern. A string `defaultValue` (the type
|
|
1388
|
+
`@k8ordo/form`'s derived attributes carry) is accepted and ignored.
|
|
825
1389
|
|
|
826
1390
|
```tsx
|
|
827
1391
|
import { FileField } from '@k8ordo/ui';
|
|
@@ -830,7 +1394,7 @@ import { FileField } from '@k8ordo/ui';
|
|
|
830
1394
|
<FileField.Trigger
|
|
831
1395
|
renderItem={({ onClick, disabled }) => (
|
|
832
1396
|
<Button onClick={onClick} disabled={disabled}>
|
|
833
|
-
|
|
1397
|
+
Choose files
|
|
834
1398
|
</Button>
|
|
835
1399
|
)}
|
|
836
1400
|
/>
|
|
@@ -838,15 +1402,53 @@ import { FileField } from '@k8ordo/ui';
|
|
|
838
1402
|
</FileField.Root>;
|
|
839
1403
|
```
|
|
840
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
|
+
|
|
841
1438
|
Props (Root):
|
|
842
1439
|
|
|
843
1440
|
- `children`: `ReactNode`
|
|
844
|
-
- `defaultValue`: `File[]`
|
|
845
|
-
- `invalid`: `boolean
|
|
1441
|
+
- `defaultValue`: `File[]` | `string`
|
|
1442
|
+
- `invalid`: `boolean` (default: `false`)
|
|
846
1443
|
- `maxFiles`: `number`
|
|
847
1444
|
- `onChange`: `(files: FileList | null, event?: ChangeEvent<HTMLInputElement>) => void`
|
|
848
1445
|
- `ref`: `Ref<HTMLInputElement>`
|
|
849
|
-
- `webkitDirectory`: `boolean
|
|
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`
|
|
850
1452
|
|
|
851
1453
|
Props (FileField.ItemList):
|
|
852
1454
|
|
|
@@ -855,37 +1457,38 @@ Props (FileField.ItemList):
|
|
|
855
1457
|
|
|
856
1458
|
Props (FileField.Trigger):
|
|
857
1459
|
|
|
858
|
-
- `renderItem`: `(props: { onClick: () => void; disabled: boolean; invalid: boolean; }) => ReactElement
|
|
1460
|
+
- `renderItem`: `(props: { onClick: () => void; disabled: boolean; invalid: boolean; }) => ReactElement` (required)
|
|
859
1461
|
|
|
860
|
-
##
|
|
1462
|
+
## Data display
|
|
861
1463
|
|
|
862
1464
|
### Heading
|
|
863
1465
|
|
|
864
|
-
|
|
1466
|
+
A semantic heading. The `level` prop selects the HTML element.
|
|
865
1467
|
|
|
866
1468
|
```tsx
|
|
867
1469
|
import { Heading } from '@k8ordo/ui';
|
|
868
1470
|
|
|
869
|
-
<Heading level="h1"
|
|
870
|
-
<Heading level="h2"
|
|
871
|
-
<Heading level="h3"
|
|
1471
|
+
<Heading level="h1">Page title</Heading>
|
|
1472
|
+
<Heading level="h2">Section heading</Heading>
|
|
1473
|
+
<Heading level="h3">Subsection</Heading>
|
|
872
1474
|
```
|
|
873
1475
|
|
|
874
1476
|
Props:
|
|
875
1477
|
|
|
876
|
-
- `level`: `'h1'` | `'h2'` | `'h3'` | `'h4'` | `'h5'` | `'h6'
|
|
1478
|
+
- `level`: `'h1'` | `'h2'` | `'h3'` | `'h4'` | `'h5'` | `'h6'` (required)
|
|
877
1479
|
- `children`: `ReactNode`
|
|
878
1480
|
- `lineClamp`: `1` | `2` | `3` | `4` | `5` | `6`
|
|
1481
|
+
- Other props are forwarded to `HTMLAttributes<HTMLHeadingElement>`, except `className` / `style`.
|
|
879
1482
|
|
|
880
1483
|
### Avatar
|
|
881
1484
|
|
|
882
|
-
|
|
1485
|
+
A user avatar.
|
|
883
1486
|
|
|
884
1487
|
```tsx
|
|
885
1488
|
import { Avatar } from '@k8ordo/ui';
|
|
886
1489
|
|
|
887
|
-
<Avatar src="/avatar.jpg" alt="
|
|
888
|
-
<Avatar name="
|
|
1490
|
+
<Avatar src="/avatar.jpg" alt="User name" size="md" />
|
|
1491
|
+
<Avatar name="Ada Lovelace" fallback="A" size="lg" />
|
|
889
1492
|
<Avatar color="primary" icon={<AssistantIcon />} name="AI" size="sm" />
|
|
890
1493
|
```
|
|
891
1494
|
|
|
@@ -893,36 +1496,38 @@ Props:
|
|
|
893
1496
|
|
|
894
1497
|
- `alt`: `string`
|
|
895
1498
|
- `children`: `ReactNode`
|
|
896
|
-
- `color`: `'base'` | `'primary'` | `'secondary'
|
|
1499
|
+
- `color`: `'base'` | `'primary'` | `'secondary'` (default: `'base'`)
|
|
897
1500
|
- `fallback`: `string`
|
|
898
1501
|
- `icon`: `ReactNode`
|
|
899
1502
|
- `name`: `string`
|
|
900
|
-
- `size`: `'sm'` | `'md'` | `'lg'
|
|
1503
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
901
1504
|
- `src`: `string`
|
|
1505
|
+
- Other props are forwarded to `HTMLAttributes<HTMLSpanElement>`, except `role` / `aria-label` / `className` / `style`.
|
|
902
1506
|
|
|
903
1507
|
### Badge
|
|
904
1508
|
|
|
905
|
-
|
|
1509
|
+
A status badge.
|
|
906
1510
|
|
|
907
1511
|
```tsx
|
|
908
1512
|
import { Badge } from '@k8ordo/ui';
|
|
909
1513
|
|
|
910
|
-
<Badge label="
|
|
911
|
-
<Badge label="
|
|
912
|
-
<Badge label="
|
|
1514
|
+
<Badge label="New" tone="info" variant="solid" />
|
|
1515
|
+
<Badge label="Done" tone="success" variant="outline" />
|
|
1516
|
+
<Badge label="Filter" interactive />
|
|
913
1517
|
```
|
|
914
1518
|
|
|
915
1519
|
Props:
|
|
916
1520
|
|
|
917
|
-
- `label`: `string
|
|
918
|
-
- `interactive`: `
|
|
919
|
-
- `size`: `
|
|
920
|
-
- `tone`: `
|
|
921
|
-
- `variant`: `
|
|
1521
|
+
- `label`: `string` (required)
|
|
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`.
|
|
922
1527
|
|
|
923
1528
|
### Code
|
|
924
1529
|
|
|
925
|
-
|
|
1530
|
+
Inline code.
|
|
926
1531
|
|
|
927
1532
|
```tsx
|
|
928
1533
|
import { Code } from '@k8ordo/ui';
|
|
@@ -932,11 +1537,155 @@ import { Code } from '@k8ordo/ui';
|
|
|
932
1537
|
|
|
933
1538
|
Props:
|
|
934
1539
|
|
|
935
|
-
- `children`: `string
|
|
1540
|
+
- `children`: `string` (required)
|
|
1541
|
+
- Other props are forwarded to `HTMLAttributes<HTMLElement>`, except `className` / `style`.
|
|
936
1542
|
|
|
937
|
-
###
|
|
1543
|
+
### CodeBlock
|
|
938
1544
|
|
|
939
|
-
|
|
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`.
|
|
1683
|
+
|
|
1684
|
+
### Table
|
|
1685
|
+
|
|
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.
|
|
940
1689
|
|
|
941
1690
|
```tsx
|
|
942
1691
|
import { Table } from '@k8ordo/ui';
|
|
@@ -944,13 +1693,13 @@ import { Table } from '@k8ordo/ui';
|
|
|
944
1693
|
<Table.Root>
|
|
945
1694
|
<Table.Head>
|
|
946
1695
|
<Table.Row>
|
|
947
|
-
<Table.HeaderCell
|
|
948
|
-
<Table.HeaderCell align="right"
|
|
1696
|
+
<Table.HeaderCell>Name</Table.HeaderCell>
|
|
1697
|
+
<Table.HeaderCell align="right">Amount</Table.HeaderCell>
|
|
949
1698
|
</Table.Row>
|
|
950
1699
|
</Table.Head>
|
|
951
1700
|
<Table.Body>
|
|
952
1701
|
<Table.Row interactive>
|
|
953
|
-
<Table.Cell
|
|
1702
|
+
<Table.Cell>Product A</Table.Cell>
|
|
954
1703
|
<Table.Cell align="right">¥1,000</Table.Cell>
|
|
955
1704
|
</Table.Row>
|
|
956
1705
|
</Table.Body>
|
|
@@ -960,59 +1709,309 @@ import { Table } from '@k8ordo/ui';
|
|
|
960
1709
|
Props (Table.Body):
|
|
961
1710
|
|
|
962
1711
|
- `children`: `ReactNode`
|
|
1712
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableSectionElement>`, except `className` / `style`.
|
|
963
1713
|
|
|
964
1714
|
Props (Table.Caption):
|
|
965
1715
|
|
|
966
1716
|
- `children`: `ReactNode`
|
|
1717
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableCaptionElement>`, except `className` / `style`.
|
|
967
1718
|
|
|
968
1719
|
Props (Table.Cell):
|
|
969
1720
|
|
|
970
|
-
- `align`: `CellAlign
|
|
1721
|
+
- `align`: `CellAlign` (default: `'left'`)
|
|
971
1722
|
- `children`: `ReactNode`
|
|
972
|
-
- `color`: `'base'` | `'mute'
|
|
1723
|
+
- `color`: `'base'` | `'mute'` (default: `'base'`)
|
|
1724
|
+
- Other props are forwarded to `TdHTMLAttributes<HTMLTableCellElement>`, except `className` / `style`.
|
|
973
1725
|
|
|
974
1726
|
Props (Table.EmptyState):
|
|
975
1727
|
|
|
976
|
-
- `
|
|
977
|
-
- `
|
|
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>`.
|
|
978
1734
|
|
|
979
1735
|
Props (Table.Head):
|
|
980
1736
|
|
|
981
1737
|
- `children`: `ReactNode`
|
|
1738
|
+
- Other props are forwarded to `HTMLAttributes<HTMLTableSectionElement>`, except `className` / `style`.
|
|
982
1739
|
|
|
983
1740
|
Props (Table.HeaderCell):
|
|
984
1741
|
|
|
985
|
-
- `align`: `CellAlign
|
|
1742
|
+
- `align`: `CellAlign` (default: `'left'`)
|
|
986
1743
|
- `children`: `ReactNode`
|
|
987
|
-
- `scope`: `'col'` | `'row'` | `'colgroup'` | `'rowgroup'
|
|
1744
|
+
- `scope`: `'col'` | `'row'` | `'colgroup'` | `'rowgroup'` (default: `'col'`)
|
|
1745
|
+
- Other props are forwarded to `ThHTMLAttributes<HTMLTableCellElement>`, except `className` / `style`.
|
|
988
1746
|
|
|
989
1747
|
Props (Table.Root):
|
|
990
1748
|
|
|
991
1749
|
- `children`: `ReactNode`
|
|
1750
|
+
- Other props are forwarded to `TableHTMLAttributes<HTMLTableElement>`, except `className` / `style`.
|
|
992
1751
|
|
|
993
1752
|
Props (Table.Row):
|
|
994
1753
|
|
|
995
1754
|
- `children`: `ReactNode`
|
|
996
|
-
- `interactive`: `boolean
|
|
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
|
+
- Under `.writing-v` the nodes run right to left, so Left / Right move between
|
|
1861
|
+
them and Down / Up open and close.
|
|
1862
|
+
|
|
1863
|
+
Props:
|
|
1864
|
+
|
|
1865
|
+
- `items`: `readonly TreeItem[]` (required)
|
|
1866
|
+
- `label`: `string` (required)
|
|
1867
|
+
- `defaultExpandedIds`: `readonly string[]`
|
|
1868
|
+
- `defaultSelectedId`: `string` | `null` (default: `null`)
|
|
1869
|
+
- `expandedIds`: `readonly string[]`
|
|
1870
|
+
- `onChange`: `(id: string) => void`
|
|
1871
|
+
- `onExpandedChange`: `(ids: readonly string[]) => void`
|
|
1872
|
+
- `selectedId`: `string` | `null`
|
|
1873
|
+
- Other props are forwarded to `HTMLAttributes<HTMLUListElement>`, except `children` / `className` / `style` / `role` / `aria-label`.
|
|
1874
|
+
|
|
1875
|
+
### Accordion
|
|
997
1876
|
|
|
998
|
-
|
|
1877
|
+
A collapsible section, as a compound component.
|
|
1878
|
+
|
|
1879
|
+
```tsx
|
|
1880
|
+
import { Accordion } from '@k8ordo/ui';
|
|
1881
|
+
|
|
1882
|
+
<Accordion.Root>
|
|
1883
|
+
<Accordion.Item>
|
|
1884
|
+
<Accordion.Button>Section 1</Accordion.Button>
|
|
1885
|
+
<Accordion.Panel>Content</Accordion.Panel>
|
|
1886
|
+
</Accordion.Item>
|
|
1887
|
+
</Accordion.Root>;
|
|
1888
|
+
```
|
|
1889
|
+
|
|
1890
|
+
Props (Accordion.Button):
|
|
1891
|
+
|
|
1892
|
+
- `children`: `ReactNode`
|
|
1893
|
+
|
|
1894
|
+
Props (Accordion.Item):
|
|
1895
|
+
|
|
1896
|
+
- `children`: `ReactNode`
|
|
1897
|
+
- `defaultOpen`: `boolean` (default: `false`)
|
|
1898
|
+
- `isOpen`: `boolean`
|
|
1899
|
+
- `onChange`: `(isOpen: boolean) => void`
|
|
1900
|
+
|
|
1901
|
+
Props (Accordion.Panel):
|
|
1902
|
+
|
|
1903
|
+
- `children`: `ReactNode`
|
|
1904
|
+
|
|
1905
|
+
Props (Accordion.Root):
|
|
1906
|
+
|
|
1907
|
+
- `children`: `ReactNode`
|
|
1908
|
+
|
|
1909
|
+
### Card
|
|
1910
|
+
|
|
1911
|
+
A card that groups content.
|
|
1912
|
+
|
|
1913
|
+
```tsx
|
|
1914
|
+
import { Card } from '@k8ordo/ui';
|
|
1915
|
+
|
|
1916
|
+
// Static card. The card has no padding of its own
|
|
1917
|
+
<Card width="full" variant="shadow">
|
|
1918
|
+
<div style={{ padding: 'calc(var(--spacing) * 6)' }}>Content</div>
|
|
1919
|
+
</Card>
|
|
1920
|
+
|
|
1921
|
+
// Clickable card (scales to 1.02 on hover and to 0.98 while pressed)
|
|
1922
|
+
<Card variant="outline" interactive>
|
|
1923
|
+
<div style={{ padding: 'calc(var(--spacing) * 6)' }}>Content</div>
|
|
1924
|
+
</Card>
|
|
1925
|
+
```
|
|
1926
|
+
|
|
1927
|
+
Props:
|
|
1928
|
+
|
|
1929
|
+
- `children`: `ReactNode`
|
|
1930
|
+
- `interactive`: `boolean` (default: `false`)
|
|
1931
|
+
- `variant`: `'shadow'` | `'outline'` (default: `'shadow'`)
|
|
1932
|
+
- `width`: `'full'` | `'fit'` (default: `'full'`)
|
|
1933
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
1934
|
+
|
|
1935
|
+
## Feedback
|
|
999
1936
|
|
|
1000
1937
|
### Alert
|
|
1001
1938
|
|
|
1939
|
+
Reports a status: the result of something the user did, or a state the
|
|
1940
|
+
application is in. It is a live region (`role="alert"` for `warning` and
|
|
1941
|
+
`error`, `role="status"` otherwise), so a screen reader announces it when it
|
|
1942
|
+
appears. For a note that is part of the content, use `Callout`.
|
|
1943
|
+
|
|
1002
1944
|
```tsx
|
|
1003
1945
|
import { Alert } from '@k8ordo/ui';
|
|
1004
1946
|
|
|
1005
|
-
<Alert tone="info" message="
|
|
1006
|
-
<Alert tone="error" message={['
|
|
1947
|
+
<Alert tone="info" message="An informational message" />
|
|
1948
|
+
<Alert tone="error" message={['Error 1', 'Error 2']} />
|
|
1007
1949
|
```
|
|
1008
1950
|
|
|
1009
1951
|
Props:
|
|
1010
1952
|
|
|
1011
|
-
- `message`: `string` | `string[]
|
|
1012
|
-
- `tone`: `Status
|
|
1953
|
+
- `message`: `string` | `string[]` (required)
|
|
1954
|
+
- `tone`: `Status` (required)
|
|
1013
1955
|
- `action`: `AlertAction`
|
|
1014
1956
|
- `closeLabel`: `string`
|
|
1015
1957
|
- `onClose`: `() => void`
|
|
1958
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `role` / `className` / `style`.
|
|
1959
|
+
|
|
1960
|
+
`action` is an `AlertAction`, `{ label: string; renderItem: (props: { children: ReactNode }) => ReactNode }`.
|
|
1961
|
+
`renderItem` receives `label` as `children`; render your own button or link
|
|
1962
|
+
around it.
|
|
1963
|
+
|
|
1964
|
+
### Callout
|
|
1965
|
+
|
|
1966
|
+
A note that sits in the content: a tip beside a paragraph, a pitfall in a
|
|
1967
|
+
guide, a dated-content notice at the top of an article. Unlike `Alert`, it is
|
|
1968
|
+
not a live region (`role="note"`), so a page that renders one, or a client
|
|
1969
|
+
navigation that brings one in, does not have it announced as a warning. The
|
|
1970
|
+
body is `children`, so it can hold paragraphs, `Code`, and links; `label` is an
|
|
1971
|
+
optional bold line above it.
|
|
1972
|
+
|
|
1973
|
+
```tsx
|
|
1974
|
+
import { Callout, Code } from '@k8ordo/ui';
|
|
1975
|
+
|
|
1976
|
+
<Callout label="Pitfall" tone="warning">
|
|
1977
|
+
<p>
|
|
1978
|
+
Passing <Code>value</Code> makes the field controlled, and a form reset no
|
|
1979
|
+
longer restores it.
|
|
1980
|
+
</p>
|
|
1981
|
+
</Callout>;
|
|
1982
|
+
```
|
|
1983
|
+
|
|
1984
|
+
Props:
|
|
1985
|
+
|
|
1986
|
+
- `children`: `ReactNode` (required)
|
|
1987
|
+
- `tone`: `Status` (required)
|
|
1988
|
+
- `label`: `string`
|
|
1989
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `role` / `className` / `style`.
|
|
1990
|
+
|
|
1991
|
+
### EmptyState
|
|
1992
|
+
|
|
1993
|
+
What a list, a table, or a search shows when there is nothing in it: a title,
|
|
1994
|
+
an optional description and icon, and an optional action. Inside a table, use
|
|
1995
|
+
`Table.EmptyState`, which puts the same content in a row spanning the columns.
|
|
1996
|
+
|
|
1997
|
+
```tsx
|
|
1998
|
+
import { Button, EmptyState, TableIcon } from '@k8ordo/ui';
|
|
1999
|
+
|
|
2000
|
+
<EmptyState
|
|
2001
|
+
action={<Button onClick={clearFilters}>Clear filters</Button>}
|
|
2002
|
+
description="Try removing a filter."
|
|
2003
|
+
icon={<TableIcon size="lg" />}
|
|
2004
|
+
title="No matching posts"
|
|
2005
|
+
/>;
|
|
2006
|
+
```
|
|
2007
|
+
|
|
2008
|
+
Props:
|
|
2009
|
+
|
|
2010
|
+
- `title`: `string` (required)
|
|
2011
|
+
- `action`: `ReactNode`
|
|
2012
|
+
- `description`: `ReactNode`
|
|
2013
|
+
- `icon`: `ReactNode`
|
|
2014
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `className` / `style`.
|
|
1016
2015
|
|
|
1017
2016
|
### Toast
|
|
1018
2017
|
|
|
@@ -1021,21 +2020,26 @@ import { useToast } from '@k8ordo/ui';
|
|
|
1021
2020
|
|
|
1022
2021
|
const { open, close, closeAll } = useToast();
|
|
1023
2022
|
|
|
1024
|
-
open('success', '
|
|
1025
|
-
open('error', '
|
|
2023
|
+
open('success', 'Saved');
|
|
2024
|
+
open('error', 'Something went wrong');
|
|
2025
|
+
|
|
2026
|
+
const syncingId = open('info', 'Syncing…', {
|
|
2027
|
+
duration: Number.POSITIVE_INFINITY,
|
|
2028
|
+
});
|
|
2029
|
+
close(syncingId);
|
|
1026
2030
|
```
|
|
1027
2031
|
|
|
1028
|
-
`ToastProvider`
|
|
2032
|
+
`ToastProvider` is already inside `UIProvider`, so no extra wrapper is needed.
|
|
1029
2033
|
|
|
1030
|
-
`useToast()`
|
|
2034
|
+
What `useToast()` returns:
|
|
1031
2035
|
|
|
1032
|
-
- `open`: `(tone: Status, message: string, options?: ToastOptions) =>
|
|
1033
|
-
- `close`: `(id: string) => void`
|
|
2036
|
+
- `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`.)
|
|
2037
|
+
- `close`: `(id: string) => void` (closes only the toast with the id `open` returned)
|
|
1034
2038
|
- `closeAll`: `() => void`
|
|
1035
2039
|
|
|
1036
2040
|
### ToastProvider
|
|
1037
2041
|
|
|
1038
|
-
|
|
2042
|
+
Wrap explicitly inside `UIProvider` only when you want to change where toasts appear or which Portal root they use.
|
|
1039
2043
|
|
|
1040
2044
|
```tsx
|
|
1041
2045
|
import { ToastProvider } from '@k8ordo/ui';
|
|
@@ -1048,43 +2052,53 @@ import { ToastProvider } from '@k8ordo/ui';
|
|
|
1048
2052
|
Props:
|
|
1049
2053
|
|
|
1050
2054
|
- `children`: `ReactNode`
|
|
1051
|
-
- `portalRef`: `RefObject<HTMLElement | null
|
|
1052
|
-
- `position`: `'fixed'` | `'absolute'
|
|
2055
|
+
- `portalRef`: `RefObject<HTMLElement | null>` (default: `null`)
|
|
2056
|
+
- `position`: `'fixed'` | `'absolute'` (default: `'fixed'`)
|
|
1053
2057
|
|
|
1054
2058
|
### Progress
|
|
1055
2059
|
|
|
2060
|
+
Leave `value` out when how far along it is cannot be known: the bar then slides
|
|
2061
|
+
back and forth, has no `aria-valuenow`, and is named `label` (the built-in
|
|
2062
|
+
`loading` wording when omitted). With reduced motion it stops sliding and
|
|
2063
|
+
pulses across the whole track instead.
|
|
2064
|
+
|
|
1056
2065
|
```tsx
|
|
1057
2066
|
import { Progress } from '@k8ordo/ui';
|
|
1058
2067
|
|
|
1059
|
-
<Progress value={50}
|
|
1060
|
-
<Progress value={
|
|
2068
|
+
<Progress value={50} />
|
|
2069
|
+
<Progress value={150} max={200} min={100} label="Progress" />
|
|
2070
|
+
|
|
2071
|
+
// Progress that cannot be measured
|
|
2072
|
+
<Progress label="Uploading" />
|
|
1061
2073
|
```
|
|
1062
2074
|
|
|
1063
2075
|
Props:
|
|
1064
2076
|
|
|
1065
|
-
- `max`: `number`(必須)
|
|
1066
|
-
- `value`: `number`(必須)
|
|
1067
2077
|
- `label`: `string`
|
|
1068
|
-
- `
|
|
2078
|
+
- `max`: `number` (default: `100`)
|
|
2079
|
+
- `min`: `number` (default: `0`)
|
|
2080
|
+
- `value`: `number`
|
|
2081
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `className` / `style`.
|
|
1069
2082
|
|
|
1070
2083
|
### Spinner
|
|
1071
2084
|
|
|
1072
|
-
|
|
2085
|
+
A loading spinner.
|
|
1073
2086
|
|
|
1074
2087
|
```tsx
|
|
1075
2088
|
import { Spinner } from '@k8ordo/ui';
|
|
1076
2089
|
|
|
1077
|
-
<Spinner size="md" label="
|
|
2090
|
+
<Spinner size="md" label="Loading" />;
|
|
1078
2091
|
```
|
|
1079
2092
|
|
|
1080
2093
|
Props:
|
|
1081
2094
|
|
|
1082
2095
|
- `label`: `string`
|
|
1083
|
-
- `size`: `'sm'` | `'md'` | `'lg'
|
|
2096
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
2097
|
+
- Other props are forwarded to `OutputHTMLAttributes<HTMLOutputElement>`, except `children` / `aria-label` / `className` / `style`.
|
|
1084
2098
|
|
|
1085
2099
|
### Skeleton
|
|
1086
2100
|
|
|
1087
|
-
|
|
2101
|
+
A content placeholder.
|
|
1088
2102
|
|
|
1089
2103
|
```tsx
|
|
1090
2104
|
import { Skeleton } from '@k8ordo/ui';
|
|
@@ -1096,21 +2110,22 @@ import { Skeleton } from '@k8ordo/ui';
|
|
|
1096
2110
|
|
|
1097
2111
|
Props:
|
|
1098
2112
|
|
|
1099
|
-
- `animate`: `boolean
|
|
1100
|
-
- `shape`: `'rect'` | `'circle'
|
|
1101
|
-
- `size`: `'sm'` | `'md'` | `'lg'
|
|
2113
|
+
- `animate`: `boolean` (default: `true`)
|
|
2114
|
+
- `shape`: `'rect'` | `'circle'` (default: `'rect'`)
|
|
2115
|
+
- `size`: `'sm'` | `'md'` | `'lg'` (default: `'md'`)
|
|
2116
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `children` / `className` / `style`.
|
|
1102
2117
|
|
|
1103
|
-
##
|
|
2118
|
+
## Overlays
|
|
1104
2119
|
|
|
1105
2120
|
### Modal
|
|
1106
2121
|
|
|
1107
|
-
|
|
2122
|
+
The base overlay component. It uses the `<dialog>` element.
|
|
1108
2123
|
|
|
1109
2124
|
```tsx
|
|
1110
2125
|
import { Modal } from '@k8ordo/ui';
|
|
1111
2126
|
|
|
1112
2127
|
<Modal isOpen={open} onClose={onClose} side="center">
|
|
1113
|
-
|
|
2128
|
+
Content
|
|
1114
2129
|
</Modal>;
|
|
1115
2130
|
```
|
|
1116
2131
|
|
|
@@ -1124,27 +2139,27 @@ Props:
|
|
|
1124
2139
|
- `isOpen`: `boolean`
|
|
1125
2140
|
- `onClose`: `() => void`
|
|
1126
2141
|
- `ref`: `Ref<HTMLDialogElement>`
|
|
1127
|
-
- `side`: `ModalSide
|
|
2142
|
+
- `side`: `ModalSide` (default: `'center'`)
|
|
1128
2143
|
|
|
1129
|
-
|
|
2144
|
+
Name resolution goes `aria-label` / `aria-labelledby` first, then the heading registered by a `Dialog.Root` inside. With neither, the dialog is unnamed — so pass `aria-label` when you place content directly without a `Dialog`.
|
|
1130
2145
|
|
|
1131
2146
|
```tsx
|
|
1132
|
-
<Modal aria-label="
|
|
2147
|
+
<Modal aria-label="Image preview" isOpen={open} onClose={onClose}>
|
|
1133
2148
|
<img alt="" src={src} />
|
|
1134
2149
|
</Modal>
|
|
1135
2150
|
```
|
|
1136
2151
|
|
|
1137
2152
|
### Dialog
|
|
1138
2153
|
|
|
1139
|
-
|
|
2154
|
+
A compound component, used together with Modal.
|
|
1140
2155
|
|
|
1141
2156
|
```tsx
|
|
1142
2157
|
import { Modal, Dialog } from '@k8ordo/ui';
|
|
1143
2158
|
|
|
1144
2159
|
<Modal isOpen={open} onClose={onClose}>
|
|
1145
2160
|
<Dialog.Root>
|
|
1146
|
-
<Dialog.Header title="
|
|
1147
|
-
<Dialog.Content
|
|
2161
|
+
<Dialog.Header title="Confirm" onClose={onClose} />
|
|
2162
|
+
<Dialog.Content>Content</Dialog.Content>
|
|
1148
2163
|
</Dialog.Root>
|
|
1149
2164
|
</Modal>;
|
|
1150
2165
|
```
|
|
@@ -1155,8 +2170,8 @@ Props (Dialog.Content):
|
|
|
1155
2170
|
|
|
1156
2171
|
Props (Dialog.Header):
|
|
1157
2172
|
|
|
1158
|
-
- `onClose`: `() => void
|
|
1159
|
-
- `title`: `ReactNode
|
|
2173
|
+
- `onClose`: `() => void` (required)
|
|
2174
|
+
- `title`: `ReactNode` (required)
|
|
1160
2175
|
|
|
1161
2176
|
Props (Dialog.Root):
|
|
1162
2177
|
|
|
@@ -1166,38 +2181,85 @@ Props (Dialog.Root):
|
|
|
1166
2181
|
- `role`: `'dialog'` | `'alertdialog'`
|
|
1167
2182
|
- `tabIndex`: `number`
|
|
1168
2183
|
|
|
2184
|
+
### CommandPalette
|
|
2185
|
+
|
|
2186
|
+
A command palette: a `Modal` holding a search field and the list of commands it
|
|
2187
|
+
filters. Typing matches each item's `label` and `keywords`, case-insensitively.
|
|
2188
|
+
`ArrowDown` / `ArrowUp` move through the matches and wrap around, `Enter` or a
|
|
2189
|
+
click runs the item's `onSelect` after closing the palette, and `Escape` closes
|
|
2190
|
+
it; focus stays in the search field throughout (the WAI-ARIA combobox, with the
|
|
2191
|
+
list always shown). Items that share a `group` are gathered under that heading,
|
|
2192
|
+
in the order the group first appears, and `shortcut` keys are drawn with `Kbd`.
|
|
2193
|
+
Each opening starts from an empty search. Open and close it like `Modal`
|
|
2194
|
+
(`isOpen` / `defaultOpen` / `onClose`); a key such as ⌘K / Ctrl+K is the
|
|
2195
|
+
application's to wire.
|
|
2196
|
+
|
|
2197
|
+
```tsx
|
|
2198
|
+
import { CommandPalette } from '@k8ordo/ui';
|
|
2199
|
+
|
|
2200
|
+
<CommandPalette
|
|
2201
|
+
isOpen={isOpen}
|
|
2202
|
+
onClose={() => setIsOpen(false)}
|
|
2203
|
+
items={[
|
|
2204
|
+
{
|
|
2205
|
+
id: 'new-file',
|
|
2206
|
+
label: 'New file',
|
|
2207
|
+
group: 'File',
|
|
2208
|
+
shortcut: ['⌘', 'N'],
|
|
2209
|
+
onSelect: createFile,
|
|
2210
|
+
},
|
|
2211
|
+
{
|
|
2212
|
+
id: 'theme',
|
|
2213
|
+
label: 'Toggle theme',
|
|
2214
|
+
keywords: ['dark', 'light'],
|
|
2215
|
+
onSelect: toggleTheme,
|
|
2216
|
+
},
|
|
2217
|
+
]}
|
|
2218
|
+
/>;
|
|
2219
|
+
```
|
|
2220
|
+
|
|
2221
|
+
Props:
|
|
2222
|
+
|
|
2223
|
+
- `items`: `readonly CommandPaletteItem[]` (required)
|
|
2224
|
+
- `aria-label`: `string`
|
|
2225
|
+
- `defaultOpen`: `boolean`
|
|
2226
|
+
- `isOpen`: `boolean`
|
|
2227
|
+
- `onClose`: `() => void`
|
|
2228
|
+
- `placeholder`: `string`
|
|
2229
|
+
- `ref`: `Ref<HTMLDialogElement>`
|
|
2230
|
+
|
|
1169
2231
|
### Drawer
|
|
1170
2232
|
|
|
1171
|
-
|
|
2233
|
+
A side panel. It uses Modal internally.
|
|
1172
2234
|
|
|
1173
2235
|
```tsx
|
|
1174
2236
|
import { Drawer } from '@k8ordo/ui';
|
|
1175
2237
|
|
|
1176
|
-
<Drawer title="
|
|
1177
|
-
|
|
2238
|
+
<Drawer title="Menu" isOpen={open} onClose={onClose} side="right">
|
|
2239
|
+
Content
|
|
1178
2240
|
</Drawer>;
|
|
1179
2241
|
```
|
|
1180
2242
|
|
|
1181
2243
|
Props:
|
|
1182
2244
|
|
|
1183
|
-
- `title`: `ReactNode
|
|
2245
|
+
- `title`: `ReactNode` (required)
|
|
1184
2246
|
- `children`: `ReactNode`
|
|
1185
2247
|
- `defaultOpen`: `boolean`
|
|
1186
2248
|
- `isOpen`: `boolean`
|
|
1187
2249
|
- `onClose`: `() => void`
|
|
1188
|
-
- `side`: `DrawerSide
|
|
2250
|
+
- `side`: `DrawerSide` (default: `'right'`)
|
|
1189
2251
|
|
|
1190
2252
|
### Popover
|
|
1191
2253
|
|
|
1192
|
-
CSS Anchor Positioning
|
|
2254
|
+
A popover built on CSS Anchor Positioning, as a compound component.
|
|
1193
2255
|
|
|
1194
2256
|
```tsx
|
|
1195
2257
|
import { Popover } from '@k8ordo/ui';
|
|
1196
2258
|
|
|
1197
2259
|
<Popover.Root placement="bottom">
|
|
1198
|
-
<Popover.Trigger renderItem={(props) => <Button {...props}
|
|
2260
|
+
<Popover.Trigger renderItem={(props) => <Button {...props}>Open</Button>} />
|
|
1199
2261
|
<Popover.Content
|
|
1200
|
-
renderItem={(props) => <div {...props}
|
|
2262
|
+
renderItem={(props) => <div {...props}>Popover content</div>}
|
|
1201
2263
|
/>
|
|
1202
2264
|
</Popover.Root>;
|
|
1203
2265
|
```
|
|
@@ -1205,48 +2267,48 @@ import { Popover } from '@k8ordo/ui';
|
|
|
1205
2267
|
Props (Root):
|
|
1206
2268
|
|
|
1207
2269
|
- `children`: `ReactNode`
|
|
1208
|
-
- `closeOnClickAway`: `boolean
|
|
1209
|
-
- `defaultOpen`: `boolean
|
|
1210
|
-
- `flipDisabled`: `boolean
|
|
2270
|
+
- `closeOnClickAway`: `boolean` (default: `true`)
|
|
2271
|
+
- `defaultOpen`: `boolean` (default: `false`)
|
|
2272
|
+
- `flipDisabled`: `boolean` (default: `false`)
|
|
1211
2273
|
- `isOpen`: `boolean`
|
|
1212
2274
|
- `onChange`: `(isOpen: boolean) => void`
|
|
1213
|
-
- `placement`: `Placement
|
|
1214
|
-
- `role`: `'dialog'` | `'menu'` | `'listbox'
|
|
1215
|
-
- `trapFocus`: `boolean
|
|
2275
|
+
- `placement`: `Placement` (default: `'bottom-start'`)
|
|
2276
|
+
- `role`: `'dialog'` | `'menu'` | `'listbox'` (default: `'menu'`)
|
|
2277
|
+
- `trapFocus`: `boolean` (default: `true`)
|
|
1216
2278
|
|
|
1217
|
-
Escape
|
|
2279
|
+
Escape closes **only the innermost one** when they are nested.
|
|
1218
2280
|
|
|
1219
2281
|
Props (Popover.Content):
|
|
1220
2282
|
|
|
1221
|
-
- `renderItem`: `(props: PopoverContentProps) => ReactElement
|
|
1222
|
-
- `animation`: `'scale'` | `'fade'
|
|
2283
|
+
- `renderItem`: `(props: PopoverContentProps) => ReactElement` (required)
|
|
2284
|
+
- `animation`: `'scale'` | `'fade'` (default: `'scale'`)
|
|
1223
2285
|
|
|
1224
2286
|
Props (Popover.Trigger):
|
|
1225
2287
|
|
|
1226
|
-
- `renderItem`: `(props: PopoverTriggerProps) => ReactElement
|
|
2288
|
+
- `renderItem`: `(props: PopoverTriggerProps) => ReactElement` (required)
|
|
1227
2289
|
|
|
1228
2290
|
### Tooltip
|
|
1229
2291
|
|
|
1230
|
-
|
|
2292
|
+
A tooltip, as a compound component.
|
|
1231
2293
|
|
|
1232
2294
|
```tsx
|
|
1233
2295
|
import { Tooltip } from '@k8ordo/ui';
|
|
1234
2296
|
|
|
1235
2297
|
<Tooltip.Root placement="top">
|
|
1236
|
-
<Tooltip.Trigger renderItem={(props) => <Button {...props}
|
|
1237
|
-
<Tooltip.Content
|
|
2298
|
+
<Tooltip.Trigger renderItem={(props) => <Button {...props}>Hover</Button>} />
|
|
2299
|
+
<Tooltip.Content>A hint</Tooltip.Content>
|
|
1238
2300
|
</Tooltip.Root>;
|
|
1239
2301
|
```
|
|
1240
2302
|
|
|
1241
2303
|
Props (Root):
|
|
1242
2304
|
|
|
1243
2305
|
- `children`: `ReactNode`
|
|
1244
|
-
- `closeDelay`: `number
|
|
2306
|
+
- `closeDelay`: `number` (default: `150`)
|
|
1245
2307
|
- `defaultOpen`: `boolean`
|
|
1246
2308
|
- `isOpen`: `boolean`
|
|
1247
2309
|
- `onChange`: `(isOpen: boolean) => void`
|
|
1248
|
-
- `openDelay`: `number
|
|
1249
|
-
- `placement`: `Placement
|
|
2310
|
+
- `openDelay`: `number` (default: `0`)
|
|
2311
|
+
- `placement`: `Placement` (default: `'bottom'`)
|
|
1250
2312
|
|
|
1251
2313
|
Props (Tooltip.Content):
|
|
1252
2314
|
|
|
@@ -1254,20 +2316,20 @@ Props (Tooltip.Content):
|
|
|
1254
2316
|
|
|
1255
2317
|
Props (Tooltip.Trigger):
|
|
1256
2318
|
|
|
1257
|
-
- `renderItem`: `(props: TooltipTriggerProps) => ReactElement
|
|
2319
|
+
- `renderItem`: `(props: TooltipTriggerProps) => ReactElement` (required)
|
|
1258
2320
|
|
|
1259
2321
|
### DropdownMenu
|
|
1260
2322
|
|
|
1261
|
-
|
|
2323
|
+
A dropdown menu, as a compound component.
|
|
1262
2324
|
|
|
1263
2325
|
```tsx
|
|
1264
2326
|
import { DropdownMenu } from '@k8ordo/ui';
|
|
1265
2327
|
|
|
1266
2328
|
<DropdownMenu.Root>
|
|
1267
|
-
<DropdownMenu.Trigger label="
|
|
2329
|
+
<DropdownMenu.Trigger label="Menu" />
|
|
1268
2330
|
<DropdownMenu.Content>
|
|
1269
|
-
<DropdownMenu.Item label="
|
|
1270
|
-
<DropdownMenu.Item label="
|
|
2331
|
+
<DropdownMenu.Item label="Item 1" onAction={handleClick} />
|
|
2332
|
+
<DropdownMenu.Item label="Item 2" onAction={handleClick} />
|
|
1271
2333
|
</DropdownMenu.Content>
|
|
1272
2334
|
</DropdownMenu.Root>;
|
|
1273
2335
|
```
|
|
@@ -1278,12 +2340,12 @@ Props (Root):
|
|
|
1278
2340
|
- `defaultOpen`: `boolean`
|
|
1279
2341
|
- `isOpen`: `boolean`
|
|
1280
2342
|
- `onChange`: `(isOpen: boolean) => void`
|
|
1281
|
-
- `placement`: `Placement
|
|
2343
|
+
- `placement`: `Placement` (default: `'bottom-start'`)
|
|
1282
2344
|
|
|
1283
|
-
Trigger
|
|
2345
|
+
Trigger variants:
|
|
1284
2346
|
|
|
1285
|
-
- `DropdownMenu.Trigger`:
|
|
1286
|
-
- `DropdownMenu.IconTrigger`:
|
|
2347
|
+
- `DropdownMenu.Trigger`: text-based (`label`, `size`, `variant`)
|
|
2348
|
+
- `DropdownMenu.IconTrigger`: icon-based (`icon`, `label`)
|
|
1287
2349
|
|
|
1288
2350
|
Props (DropdownMenu.Content):
|
|
1289
2351
|
|
|
@@ -1291,30 +2353,90 @@ Props (DropdownMenu.Content):
|
|
|
1291
2353
|
|
|
1292
2354
|
Props (DropdownMenu.IconTrigger):
|
|
1293
2355
|
|
|
1294
|
-
- `icon`: `ReactNode
|
|
1295
|
-
- `label`: `string
|
|
2356
|
+
- `icon`: `ReactNode` (required)
|
|
2357
|
+
- `label`: `string` (required)
|
|
1296
2358
|
|
|
1297
2359
|
Props (DropdownMenu.Item):
|
|
1298
2360
|
|
|
1299
|
-
- `label`: `string
|
|
1300
|
-
- `onAction`: `() => void
|
|
2361
|
+
- `label`: `string` (required)
|
|
2362
|
+
- `onAction`: `() => void` (required)
|
|
1301
2363
|
|
|
1302
|
-
`DropdownMenu.SubMenu`
|
|
2364
|
+
`DropdownMenu.SubMenu` is a nested menu. Hovering the `label` row, or opening it from the keyboard, opens the child menu to the right.
|
|
1303
2365
|
|
|
1304
2366
|
Props (DropdownMenu.SubMenu):
|
|
1305
2367
|
|
|
1306
|
-
- `label`: `string
|
|
2368
|
+
- `label`: `string` (required)
|
|
1307
2369
|
- `children`: `ReactNode`
|
|
1308
2370
|
|
|
1309
2371
|
Props (DropdownMenu.Trigger):
|
|
1310
2372
|
|
|
1311
|
-
- `label`: `string
|
|
1312
|
-
- `size`: `ComponentProps<typeof Button>['size']
|
|
1313
|
-
- `variant`: `ComponentProps<typeof Button>['variant']
|
|
2373
|
+
- `label`: `string` (required)
|
|
2374
|
+
- `size`: `ComponentProps<typeof Button>['size']` (default: `'md'`)
|
|
2375
|
+
- `variant`: `ComponentProps<typeof Button>['variant']` (default: `'solid'`)
|
|
2376
|
+
|
|
2377
|
+
### ContextMenu
|
|
2378
|
+
|
|
2379
|
+
A menu opened by right-clicking (or by the keyboard's context-menu key or
|
|
2380
|
+
Shift+F10) on an area, shown at the pointer. The inside is `DropdownMenu`'s —
|
|
2381
|
+
`ContextMenu.Content`, `ContextMenu.Item` (`label` / `onAction`), and
|
|
2382
|
+
`ContextMenu.SubMenu` behave exactly like theirs — and only how it opens and
|
|
2383
|
+
where differ. When it closes from inside (an item, Escape), focus returns to
|
|
2384
|
+
the element that had it before it opened; closing by clicking elsewhere leaves
|
|
2385
|
+
focus where the click put it.
|
|
2386
|
+
|
|
2387
|
+
`ContextMenu.Trigger` fills a slot: `renderItem` receives `onContextMenu` to
|
|
2388
|
+
put on the area. To open it from the keyboard, the area (or something in it)
|
|
2389
|
+
has to be able to take focus.
|
|
2390
|
+
|
|
2391
|
+
```tsx
|
|
2392
|
+
import { ContextMenu } from '@k8ordo/ui';
|
|
2393
|
+
|
|
2394
|
+
<ContextMenu.Root>
|
|
2395
|
+
<ContextMenu.Trigger
|
|
2396
|
+
renderItem={(props) => (
|
|
2397
|
+
<div {...props} tabIndex={0}>
|
|
2398
|
+
report.pdf
|
|
2399
|
+
</div>
|
|
2400
|
+
)}
|
|
2401
|
+
/>
|
|
2402
|
+
<ContextMenu.Content>
|
|
2403
|
+
<ContextMenu.Item label="Rename" onAction={rename} />
|
|
2404
|
+
<ContextMenu.SubMenu label="Move to">
|
|
2405
|
+
<ContextMenu.Item label="Archive" onAction={archive} />
|
|
2406
|
+
</ContextMenu.SubMenu>
|
|
2407
|
+
<ContextMenu.Item label="Delete" onAction={remove} />
|
|
2408
|
+
</ContextMenu.Content>
|
|
2409
|
+
</ContextMenu.Root>;
|
|
2410
|
+
```
|
|
2411
|
+
|
|
2412
|
+
Props (Root):
|
|
2413
|
+
|
|
2414
|
+
- `children`: `ReactNode`
|
|
2415
|
+
- `defaultOpen`: `boolean`
|
|
2416
|
+
- `isOpen`: `boolean`
|
|
2417
|
+
- `onChange`: `(isOpen: boolean) => void`
|
|
2418
|
+
|
|
2419
|
+
Props (ContextMenu.Content):
|
|
2420
|
+
|
|
2421
|
+
- `children`: `ReactNode`
|
|
2422
|
+
|
|
2423
|
+
Props (ContextMenu.Item):
|
|
2424
|
+
|
|
2425
|
+
- `label`: `string` (required)
|
|
2426
|
+
- `onAction`: `() => void` (required)
|
|
2427
|
+
|
|
2428
|
+
Props (ContextMenu.SubMenu):
|
|
2429
|
+
|
|
2430
|
+
- `label`: `string` (required)
|
|
2431
|
+
- `children`: `ReactNode`
|
|
2432
|
+
|
|
2433
|
+
Props (ContextMenu.Trigger):
|
|
2434
|
+
|
|
2435
|
+
- `renderItem`: `(props: { onContextMenu: MouseEventHandler<HTMLElement>; }) => ReactElement` (required)
|
|
1314
2436
|
|
|
1315
2437
|
### ListBox
|
|
1316
2438
|
|
|
1317
|
-
|
|
2439
|
+
List selection, as a compound component. The choices use the same `Option` (`{ value, label }`) as `Select`.
|
|
1318
2440
|
|
|
1319
2441
|
```tsx
|
|
1320
2442
|
import { ListBox } from '@k8ordo/ui';
|
|
@@ -1322,31 +2444,31 @@ import { ListBox } from '@k8ordo/ui';
|
|
|
1322
2444
|
<ListBox.Root
|
|
1323
2445
|
onChange={onChange}
|
|
1324
2446
|
options={[
|
|
1325
|
-
{ value: '1', label: '
|
|
1326
|
-
{ value: '2', label: '
|
|
2447
|
+
{ value: '1', label: 'Option 1' },
|
|
2448
|
+
{ value: '2', label: 'Option 2' },
|
|
1327
2449
|
]}
|
|
1328
2450
|
value={value}
|
|
1329
2451
|
>
|
|
1330
|
-
<ListBox.Trigger label="
|
|
2452
|
+
<ListBox.Trigger label="Items per page" size="md" />
|
|
1331
2453
|
<ListBox.Content />
|
|
1332
2454
|
</ListBox.Root>;
|
|
1333
2455
|
```
|
|
1334
2456
|
|
|
1335
2457
|
Props (Root):
|
|
1336
2458
|
|
|
1337
|
-
- `options`: `readonly Option[]
|
|
2459
|
+
- `options`: `readonly Option[]` (required)
|
|
1338
2460
|
- `children`: `ReactNode`
|
|
1339
2461
|
- `defaultValue`: `Option['value']`
|
|
1340
2462
|
- `onChange`: `(value: Option['value']) => void`
|
|
1341
|
-
- `placement`: `Placement
|
|
2463
|
+
- `placement`: `Placement` (default: `'bottom'`)
|
|
1342
2464
|
- `value`: `Option['value']`
|
|
1343
2465
|
|
|
1344
|
-
Trigger
|
|
2466
|
+
Trigger variants:
|
|
1345
2467
|
|
|
1346
|
-
- `ListBox.Trigger`:
|
|
1347
|
-
- `ListBox.IconTrigger`:
|
|
2468
|
+
- `ListBox.Trigger`: text-based (`size`, `label`)
|
|
2469
|
+
- `ListBox.IconTrigger`: icon-based (`size`, `icon`, `label`)
|
|
1348
2470
|
|
|
1349
|
-
`label`
|
|
2471
|
+
Passing `label` makes the trigger's accessible name "label + current value". Omit it and only the current value is used, so pass it when there is no heading nearby.
|
|
1350
2472
|
|
|
1351
2473
|
Props (Content):
|
|
1352
2474
|
|
|
@@ -1354,113 +2476,421 @@ Props (Content):
|
|
|
1354
2476
|
|
|
1355
2477
|
Props (ListBox.IconTrigger):
|
|
1356
2478
|
|
|
1357
|
-
- `icon`: `ReactElement
|
|
2479
|
+
- `icon`: `ReactElement` (required)
|
|
1358
2480
|
- `label`: `string`
|
|
1359
|
-
- `size`: `ComponentProps<typeof Button>['size']
|
|
2481
|
+
- `size`: `ComponentProps<typeof Button>['size']` (default: `'md'`)
|
|
1360
2482
|
|
|
1361
2483
|
Props (ListBox.Trigger):
|
|
1362
2484
|
|
|
1363
2485
|
- `label`: `string`
|
|
1364
|
-
- `size`: `ComponentProps<typeof Button>['size']
|
|
2486
|
+
- `size`: `ComponentProps<typeof Button>['size']` (default: `'md'`)
|
|
1365
2487
|
|
|
1366
|
-
##
|
|
2488
|
+
## Layout
|
|
1367
2489
|
|
|
1368
|
-
###
|
|
2490
|
+
### Separator
|
|
1369
2491
|
|
|
1370
|
-
|
|
2492
|
+
A dividing rule.
|
|
1371
2493
|
|
|
1372
2494
|
```tsx
|
|
1373
|
-
import {
|
|
2495
|
+
import { Separator } from '@k8ordo/ui';
|
|
1374
2496
|
|
|
1375
|
-
<
|
|
1376
|
-
|
|
1377
|
-
|
|
2497
|
+
<Separator />
|
|
2498
|
+
<Separator color="mute" />
|
|
2499
|
+
<Separator color="subtle" />
|
|
2500
|
+
<Separator orientation="vertical" />
|
|
1378
2501
|
```
|
|
1379
2502
|
|
|
1380
2503
|
Props:
|
|
1381
2504
|
|
|
2505
|
+
- `color`: `'base'` | `'mute'` | `'subtle'` (default: `'base'`)
|
|
2506
|
+
- `orientation`: `'horizontal'` | `'vertical'` (default: `'horizontal'`)
|
|
2507
|
+
- Other props are forwarded to `HTMLAttributes<HTMLSpanElement>`, except `children` / `role` / `aria-orientation` / `className` / `style`.
|
|
2508
|
+
|
|
2509
|
+
### Stack
|
|
2510
|
+
|
|
2511
|
+
Lays children out along one axis. Pick `gap` from the spacing tokens.
|
|
2512
|
+
|
|
2513
|
+
```tsx
|
|
2514
|
+
import { Stack } from '@k8ordo/ui';
|
|
2515
|
+
|
|
2516
|
+
<Stack gap="lg">
|
|
2517
|
+
<Card>1</Card>
|
|
2518
|
+
<Card>2</Card>
|
|
2519
|
+
</Stack>
|
|
2520
|
+
|
|
2521
|
+
<Stack direction="row" justify="between" align="center">
|
|
2522
|
+
<Heading level="h2">Title</Heading>
|
|
2523
|
+
<Button>Action</Button>
|
|
2524
|
+
</Stack>
|
|
2525
|
+
```
|
|
2526
|
+
|
|
2527
|
+
Props:
|
|
2528
|
+
|
|
2529
|
+
- `align`: `'start'` | `'center'` | `'end'` | `'stretch'`
|
|
1382
2530
|
- `children`: `ReactNode`
|
|
1383
|
-
- `
|
|
2531
|
+
- `direction`: `'row'` | `'column'` (default: `'column'`)
|
|
2532
|
+
- `gap`: `GapSize` (default: `'md'`)
|
|
2533
|
+
- `justify`: `'start'` | `'center'` | `'end'` | `'between'`
|
|
2534
|
+
- `padding`: `PaddingSize`
|
|
2535
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
1384
2536
|
|
|
1385
|
-
###
|
|
2537
|
+
### Grid
|
|
1386
2538
|
|
|
1387
|
-
|
|
2539
|
+
A grid layout. Passing `'auto-fill'` or `'auto-fit'` to `cols` wraps with `minItemSize` as the lower bound.
|
|
1388
2540
|
|
|
1389
2541
|
```tsx
|
|
1390
|
-
import {
|
|
2542
|
+
import { Grid } from '@k8ordo/ui';
|
|
1391
2543
|
|
|
1392
|
-
<
|
|
2544
|
+
<Grid cols={3} gap="md">
|
|
2545
|
+
<Card>1</Card>
|
|
2546
|
+
<Card>2</Card>
|
|
2547
|
+
<Card>3</Card>
|
|
2548
|
+
</Grid>
|
|
2549
|
+
|
|
2550
|
+
<Grid cols="auto-fill" minItemSize={64}>
|
|
2551
|
+
{items.map((item) => (
|
|
2552
|
+
<Card key={item.id}>{item.name}</Card>
|
|
2553
|
+
))}
|
|
2554
|
+
</Grid>
|
|
1393
2555
|
```
|
|
1394
2556
|
|
|
1395
2557
|
Props:
|
|
1396
2558
|
|
|
1397
2559
|
- `children`: `ReactNode`
|
|
1398
|
-
- `
|
|
2560
|
+
- `cols`: `1` | `2` | `3` | `4` | `5` | `6` | `'auto-fill'` | `'auto-fit'` (default: `'auto-fill'`)
|
|
2561
|
+
- `gap`: `GapSize` (default: `'md'`)
|
|
2562
|
+
- `minItemSize`: `24` | `32` | `40` | `48` | `64` | `80` (default: `48`)
|
|
2563
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2564
|
+
|
|
2565
|
+
### ResizablePanels
|
|
2566
|
+
|
|
2567
|
+
Two panes with a divider between them that the user drags or moves with the
|
|
2568
|
+
arrow keys (the WAI-ARIA window splitter). `value` is the first pane's share in
|
|
2569
|
+
percent, kept between `min` and `max`; the second pane takes the rest. The root
|
|
2570
|
+
fills its parent, so give that parent a size — a vertical split needs a height.
|
|
2571
|
+
Each pane scrolls on its own.
|
|
2572
|
+
|
|
2573
|
+
The divider is a focusable `separator` whose value is the first pane's size and
|
|
2574
|
+
whose `aria-controls` points at it. The arrow keys move it the way they point
|
|
2575
|
+
on screen — left/right when the panes sit side by side, up/down when they are
|
|
2576
|
+
stacked — so in a right-to-left page `ArrowLeft` widens a first pane that sits
|
|
2577
|
+
on the right; `Home` / `End` jump to `min` / `max`. `orientation="horizontal"`
|
|
2578
|
+
lays the panes along the line of text, so in vertical writing mode they stack.
|
|
2579
|
+
Name the divider after the first pane with `aria-labelledby` when it has a
|
|
2580
|
+
heading; otherwise it falls back to `resizablePanelsHandle` from the dictionary.
|
|
2581
|
+
|
|
2582
|
+
```tsx
|
|
2583
|
+
import { ResizablePanels } from '@k8ordo/ui';
|
|
2584
|
+
|
|
2585
|
+
<div style={{ height: '24rem' }}>
|
|
2586
|
+
<ResizablePanels.Root defaultValue={25}>
|
|
2587
|
+
<ResizablePanels.Panel>
|
|
2588
|
+
<h2 id="files">Files</h2>
|
|
2589
|
+
</ResizablePanels.Panel>
|
|
2590
|
+
<ResizablePanels.Handle aria-labelledby="files" />
|
|
2591
|
+
<ResizablePanels.Panel>…</ResizablePanels.Panel>
|
|
2592
|
+
</ResizablePanels.Root>
|
|
2593
|
+
</div>;
|
|
2594
|
+
```
|
|
2595
|
+
|
|
2596
|
+
Props (Root):
|
|
2597
|
+
|
|
2598
|
+
- `children`: `ReactNode`
|
|
2599
|
+
- `defaultValue`: `number` (default: `50`)
|
|
2600
|
+
- `max`: `number` (default: `90`)
|
|
2601
|
+
- `min`: `number` (default: `10`)
|
|
2602
|
+
- `onChange`: `(value: number) => void`
|
|
2603
|
+
- `orientation`: `'horizontal'` | `'vertical'` (default: `'horizontal'`)
|
|
2604
|
+
- `ref`: `Ref<HTMLDivElement>`
|
|
2605
|
+
- `step`: `number` (default: `5`)
|
|
2606
|
+
- `value`: `number`
|
|
2607
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2608
|
+
|
|
2609
|
+
Props (ResizablePanels.Panel):
|
|
2610
|
+
|
|
2611
|
+
- `children`: `ReactNode`
|
|
2612
|
+
- `ref`: `Ref<HTMLDivElement>`
|
|
2613
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style`.
|
|
2614
|
+
|
|
2615
|
+
Props (ResizablePanels.Handle):
|
|
2616
|
+
|
|
2617
|
+
- _No props of its own._
|
|
2618
|
+
- Other props are forwarded to `HTMLAttributes<HTMLDivElement>`, except `className` / `style` / `role` / `children` / `tabIndex` / `aria-orientation` / `aria-valuenow` / `aria-valuemin` / `aria-valuemax` / `aria-controls`.
|
|
2619
|
+
|
|
2620
|
+
## Observers
|
|
2621
|
+
|
|
2622
|
+
Components that watch their children and report back without adding a wrapper
|
|
2623
|
+
element. They are built on React's Fragment refs, so whatever the children
|
|
2624
|
+
render is what gets observed — including host elements that mount later.
|
|
2625
|
+
|
|
2626
|
+
### InView
|
|
2627
|
+
|
|
2628
|
+
Reports whether its children are inside the viewport, or inside `root`.
|
|
2629
|
+
|
|
2630
|
+
```tsx
|
|
2631
|
+
import { InView } from '@k8ordo/ui';
|
|
2632
|
+
|
|
2633
|
+
const [isInView, setIsInView] = useState(false);
|
|
2634
|
+
|
|
2635
|
+
<InView onChange={setIsInView}>
|
|
2636
|
+
<section>…</section>
|
|
2637
|
+
</InView>;
|
|
2638
|
+
|
|
2639
|
+
// inside a scroll container, and only until it has been seen once
|
|
2640
|
+
const [container, setContainer] = useState<HTMLElement | null>(null);
|
|
2641
|
+
|
|
2642
|
+
<div ref={setContainer} style={{ overflowY: 'auto' }}>
|
|
2643
|
+
<InView once onChange={reveal} root={container} rootMargin="0px 0px 24px 0px">
|
|
2644
|
+
<img alt="" src="…" />
|
|
2645
|
+
</InView>
|
|
2646
|
+
</div>;
|
|
2647
|
+
```
|
|
2648
|
+
|
|
2649
|
+
- `onChange` reports the state as soon as a host element is observed, then again
|
|
2650
|
+
each time it flips. The same value is never reported twice in a row, even
|
|
2651
|
+
when `root` changes and the observer is re-created.
|
|
2652
|
+
- With several host elements, `isInView` is `true` while **any** of them
|
|
2653
|
+
intersects, and it follows children that mount or unmount later. While the
|
|
2654
|
+
children render no host element, `onChange` is not called at all — the first
|
|
2655
|
+
report comes once one mounts.
|
|
2656
|
+
- `once` stops observing after the first `true`.
|
|
2657
|
+
- Hold `root` in state, not a `RefObject`: the observer has to be re-created
|
|
2658
|
+
once the element exists.
|
|
2659
|
+
|
|
2660
|
+
Props:
|
|
2661
|
+
|
|
2662
|
+
- `children`: `ReactNode` (required)
|
|
2663
|
+
- `onChange`: `(isInView: boolean) => void` (required)
|
|
2664
|
+
- `once`: `boolean` (default: `false`)
|
|
2665
|
+
- `root`: `Element` | `null` (default: `null`)
|
|
2666
|
+
- `rootMargin`: `string` (default: `'0px'`)
|
|
2667
|
+
- `threshold`: `number` (default: `0`)
|
|
2668
|
+
|
|
2669
|
+
### Resize
|
|
2670
|
+
|
|
2671
|
+
Calls `onChange` when the size of its children changes.
|
|
2672
|
+
|
|
2673
|
+
```tsx
|
|
2674
|
+
import { Resize } from '@k8ordo/ui';
|
|
2675
|
+
|
|
2676
|
+
<Resize onChange={remeasure}>
|
|
2677
|
+
<div>…</div>
|
|
2678
|
+
</Resize>;
|
|
2679
|
+
```
|
|
2680
|
+
|
|
2681
|
+
- `onChange` is also called once when observation starts, as a native
|
|
2682
|
+
`ResizeObserver` is.
|
|
2683
|
+
- It takes no argument; read what you need from the DOM in the handler.
|
|
2684
|
+
|
|
2685
|
+
Props:
|
|
1399
2686
|
|
|
1400
|
-
|
|
2687
|
+
- `children`: `ReactNode` (required)
|
|
2688
|
+
- `onChange`: `() => void` (required)
|
|
1401
2689
|
|
|
1402
|
-
|
|
2690
|
+
## Icons
|
|
1403
2691
|
|
|
1404
|
-
|
|
2692
|
+
Decorative icons, all from the root entry. Each renders an `<svg>` with
|
|
2693
|
+
`aria-hidden` and `focusable="false"`, so the accessible name belongs to the
|
|
2694
|
+
control around it — an `IconButton`'s `label`, for example.
|
|
2695
|
+
|
|
2696
|
+
```tsx
|
|
2697
|
+
import { AlertIcon, ChevronIcon, CloseIcon } from '@k8ordo/ui';
|
|
2698
|
+
|
|
2699
|
+
<CloseIcon />
|
|
2700
|
+
<ChevronIcon direction="down" size="sm" />
|
|
2701
|
+
<AlertIcon status="warning" />
|
|
2702
|
+
```
|
|
2703
|
+
|
|
2704
|
+
Every icon takes one optional prop and nothing else:
|
|
2705
|
+
|
|
2706
|
+
- `size`: `'xs'` | `'sm'` | `'md'` | `'lg'` | `'xl'` | `'2xl'` | `'3xl'` (default: `'md'`)
|
|
2707
|
+
|
|
2708
|
+
Two of them also require a prop that picks the glyph:
|
|
2709
|
+
|
|
2710
|
+
- `ChevronIcon`: `direction`: `Direction` (`'up'` | `'down'` | `'left'` | `'right'`)
|
|
2711
|
+
- `AlertIcon`: `status`: `Status` (`'success'` | `'info'` | `'warning'` | `'error'`)
|
|
2712
|
+
|
|
2713
|
+
<!-- generated:icons -->
|
|
2714
|
+
|
|
2715
|
+
The icons: `AccessibilityIcon`, `AIIcon`, `AlertIcon`, `AssistantIcon`,
|
|
2716
|
+
`AtomIcon`, `BadIcon`, `BlogIcon`, `BoringIcon`, `CheckIcon`, `ChevronIcon`,
|
|
2717
|
+
`ChromeIcon`, `CloseIcon`, `CodeXmlIcon`, `ColorContrastIcon`, `ColorInfoIcon`,
|
|
2718
|
+
`ColorScaleIcon`, `CopyIcon`, `DarkModeIcon`, `DifficultIcon`, `EasyIcon`,
|
|
2719
|
+
`EdgeIcon`, `ExternalLinkIcon`, `FirefoxIcon`, `FlaskIcon`, `ForkIcon`,
|
|
2720
|
+
`FormIcon`, `FullscreenIcon`, `GitHubIcon`, `GoodIcon`, `HistoryIcon`,
|
|
2721
|
+
`HorizontalWritingIcon`, `InformativeIcon`, `InterestingIcon`, `LightModeIcon`,
|
|
2722
|
+
`LinkIcon`, `ListIcon`, `LocationIcon`, `LockIcon`, `LockOpenIcon`, `LogoIcon`,
|
|
2723
|
+
`MailIcon`, `MinusIcon`, `MixedColorIcon`, `NavigationMenuIcon`, `NewsIcon`,
|
|
2724
|
+
`PackageIcon`, `PaletteIcon`, `PlusIcon`, `PrepareIcon`, `PublishDateIcon`,
|
|
2725
|
+
`QiitaIcon`, `RefreshIcon`, `RSSIcon`, `SafariIcon`, `SendIcon`, `ShallowIcon`,
|
|
2726
|
+
`ShieldCheckIcon`, `SlideIcon`, `SparklesIcon`, `SquircleIcon`, `SubscribeIcon`,
|
|
2727
|
+
`TableIcon`, `TagIcon`, `TwitterIcon`, `UpdateDateIcon`, `VerticalWritingIcon`,
|
|
2728
|
+
`ViewIcon`, `ViewOffIcon`.
|
|
2729
|
+
|
|
2730
|
+
<!-- /generated:icons -->
|
|
2731
|
+
|
|
2732
|
+
`Logo` is the mark `LogoIcon` draws, as a bare `<svg>` without the sizing: it
|
|
2733
|
+
takes `className`, `aria-hidden`, and `focusable` (`Partial<IconRenderProps>`)
|
|
2734
|
+
instead of `size`. The types `BaseIconProps` (`{ size }`) and
|
|
2735
|
+
`IconRenderProps` (`{ className: string; 'aria-hidden': true; focusable: 'false' }`)
|
|
2736
|
+
are exported as well.
|
|
2737
|
+
|
|
2738
|
+
## Providers
|
|
2739
|
+
|
|
2740
|
+
### UIProvider
|
|
2741
|
+
|
|
2742
|
+
Wrap the app root once. It includes ToastProvider. The components' own wording needs no provider: it follows `@k8ordo/i18n` (i18n, below).
|
|
1405
2743
|
|
|
1406
2744
|
```tsx
|
|
1407
2745
|
import { UIProvider } from '@k8ordo/ui';
|
|
1408
|
-
import { en } from '@k8ordo/ui/i18n';
|
|
1409
2746
|
|
|
1410
|
-
<UIProvider
|
|
2747
|
+
<UIProvider>
|
|
1411
2748
|
<App />
|
|
1412
2749
|
</UIProvider>;
|
|
1413
2750
|
```
|
|
1414
2751
|
|
|
1415
|
-
|
|
2752
|
+
Props:
|
|
2753
|
+
|
|
2754
|
+
- `children`: `ReactNode`
|
|
2755
|
+
|
|
2756
|
+
### PortalRootProvider
|
|
2757
|
+
|
|
2758
|
+
A context that shares a portal container with your own `createPortal` calls;
|
|
2759
|
+
read it with `usePortalRoot()`. The library's overlays do not read it — they
|
|
2760
|
+
open in the top layer (`<dialog>`, the Popover API), and toasts go where
|
|
2761
|
+
`ToastProvider`'s `portalRef` points. `Modal` passes its own `<dialog>` as the
|
|
2762
|
+
value, so a portal placed through `usePortalRoot()` inside a modal stays in it.
|
|
1416
2763
|
|
|
1417
2764
|
```tsx
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
</
|
|
2765
|
+
import { PortalRootProvider, usePortalRoot } from '@k8ordo/ui';
|
|
2766
|
+
|
|
2767
|
+
<PortalRootProvider value={containerRef}>{children}</PortalRootProvider>;
|
|
1421
2768
|
```
|
|
1422
2769
|
|
|
1423
|
-
|
|
2770
|
+
Props:
|
|
2771
|
+
|
|
2772
|
+
- `value`: `RefObject<HTMLElement | null>` (required)
|
|
2773
|
+
- `children`: `ReactNode`
|
|
2774
|
+
|
|
2775
|
+
## i18n (message dictionary)
|
|
2776
|
+
|
|
2777
|
+
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.
|
|
2778
|
+
|
|
2779
|
+
- 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.
|
|
2780
|
+
- An application that defines no set gets **English**, whatever its URL starts with, so the server and the browser agree.
|
|
2781
|
+
- The module defining the set has to be loaded in the browser as well; where no set is defined the components speak English.
|
|
2782
|
+
|
|
2783
|
+
`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
2784
|
|
|
1425
2785
|
```tsx
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
2786
|
+
import { en, registerMessages } from '@k8ordo/ui/i18n';
|
|
2787
|
+
import type { Messages } from '@k8ordo/ui/i18n';
|
|
2788
|
+
|
|
2789
|
+
const fr: Messages = { close: 'Fermer' /* …every key */ };
|
|
2790
|
+
registerMessages('fr', fr);
|
|
2791
|
+
|
|
2792
|
+
registerMessages('en', { ...en, close: 'Dismiss' });
|
|
1429
2793
|
```
|
|
1430
2794
|
|
|
1431
|
-
|
|
2795
|
+
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`.
|
|
2796
|
+
|
|
2797
|
+
### Resolution order
|
|
1432
2798
|
|
|
1433
|
-
|
|
2799
|
+
**Component prop > registered dictionary > built-in dictionary**.
|
|
1434
2800
|
|
|
1435
|
-
`Spinner`
|
|
2801
|
+
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.
|
|
1436
2802
|
|
|
1437
2803
|
```tsx
|
|
1438
|
-
//
|
|
2804
|
+
// Whatever the locale, this one Spinner reads 「保存中」
|
|
1439
2805
|
<Spinner label="保存中" />
|
|
1440
2806
|
```
|
|
1441
2807
|
|
|
1442
|
-
###
|
|
2808
|
+
### Exports
|
|
1443
2809
|
|
|
1444
2810
|
```tsx
|
|
1445
|
-
import {
|
|
2811
|
+
import {
|
|
2812
|
+
en,
|
|
2813
|
+
getMessages,
|
|
2814
|
+
ja,
|
|
2815
|
+
messageUsage,
|
|
2816
|
+
registerMessages,
|
|
2817
|
+
type Messages,
|
|
2818
|
+
} from '@k8ordo/ui/i18n';
|
|
1446
2819
|
```
|
|
1447
2820
|
|
|
1448
|
-
|
|
2821
|
+
### Reading the wording in your own elements
|
|
1449
2822
|
|
|
1450
|
-
|
|
2823
|
+
`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.
|
|
1451
2824
|
|
|
1452
|
-
|
|
2825
|
+
```tsx
|
|
2826
|
+
import { getMessages } from '@k8ordo/ui/i18n';
|
|
2827
|
+
|
|
2828
|
+
function DismissButton({ onDismiss }) {
|
|
2829
|
+
const { close } = getMessages();
|
|
2830
|
+
return (
|
|
2831
|
+
<button aria-label={close} onClick={onDismiss} type="button">
|
|
2832
|
+
×
|
|
2833
|
+
</button>
|
|
2834
|
+
);
|
|
2835
|
+
}
|
|
2836
|
+
```
|
|
1453
2837
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
2838
|
+
### Key list
|
|
2839
|
+
|
|
2840
|
+
Every key in the `Messages` type, grouped by the components that draw it —
|
|
2841
|
+
the same mapping `messageUsage` exports. All values are `string`.
|
|
2842
|
+
|
|
2843
|
+
<!-- generated:message-keys -->
|
|
2844
|
+
|
|
2845
|
+
- Alert / Dialog / Drawer / Response: `close`
|
|
2846
|
+
- FormControl: `required`
|
|
2847
|
+
- Spinner / Progress / Combobox: `loading`
|
|
2848
|
+
- Avatar: `avatar`
|
|
2849
|
+
- Code: `color`
|
|
2850
|
+
- Alert / Callout: `alertSuccess`, `alertInfo`, `alertWarning`, `alertError`
|
|
2851
|
+
- ToastProvider: `toastRegion`
|
|
2852
|
+
- CopyButton / Message.Copy: `copy`
|
|
2853
|
+
- CopyButton / CodeBlock / Message.Copy: `copied`, `copyFailed`
|
|
2854
|
+
- Autocomplete: `autocompletePlaceholder`, `autocompleteRemoveTag`, `autocompleteClear`, `autocompleteEmpty`
|
|
2855
|
+
- Combobox: `comboboxToggle`, `comboboxEmpty`, `comboboxFailed`
|
|
2856
|
+
- FileField: `fileFieldRemove`
|
|
2857
|
+
- FileField.Dropzone / FileField (generative UI): `fileFieldTrigger`, `fileFieldDrop`
|
|
2858
|
+
- NumberField: `numberFieldIncrement`, `numberFieldDecrement`, `numberFieldRangeUnderflow` (`{min}` is replaced), `numberFieldRangeOverflow` (`{max}` is replaced)
|
|
2859
|
+
- RangeSlider: `rangeSliderStart`, `rangeSliderEnd`
|
|
2860
|
+
- Calendar / DatePicker: `calendarPreviousMonth`, `calendarNextMonth`
|
|
2861
|
+
- DatePicker: `datePickerOpen`, `datePickerDialog`
|
|
2862
|
+
- ColorPicker: `colorPickerHue`, `colorPickerSaturation`, `colorPickerLightness`, `colorPickerSwatches`, `colorPickerOpen`
|
|
2863
|
+
- PasswordInput: `passwordShow`, `passwordHide`
|
|
2864
|
+
- ListBox: `listBoxPlaceholder`
|
|
2865
|
+
- Breadcrumb: `breadcrumb`
|
|
2866
|
+
- Tabs (generative UI): `tabList`
|
|
2867
|
+
- Pagination: `paginationLabel`, `paginationPrevious`, `paginationNext`
|
|
2868
|
+
- Stepper: `stepperComplete`
|
|
2869
|
+
- DataTable: `dataTableColumns`, `dataTableSelectAll`, `dataTableSelectRow`
|
|
2870
|
+
- CodeBlock: `codeBlockCopy`
|
|
2871
|
+
- Carousel: `carousel`, `carouselPrevious`, `carouselNext`
|
|
2872
|
+
- Carousel.Slide: `carouselSlide`
|
|
2873
|
+
- ResizablePanels.Handle: `resizablePanelsHandle`
|
|
2874
|
+
- TableOfContents: `tableOfContents`
|
|
2875
|
+
- CommandPalette: `commandPalette`, `commandPaletteSearch`, `commandPaletteEmpty`
|
|
2876
|
+
- Conversation.Messages: `chat`
|
|
2877
|
+
- Conversation.ScrollButton: `scrollToLatest`
|
|
2878
|
+
- Reasoning: `reasoning`, `reasoningStreaming`
|
|
2879
|
+
- Suggestion.List: `suggestions`
|
|
2880
|
+
- PromptInput: `send`, `stop`
|
|
2881
|
+
- PromptInput.Attach: `attach`
|
|
2882
|
+
- Attachment.List / PromptInput.Attachments: `attachments`
|
|
2883
|
+
- PromptInput.Attachments: `attachmentRemove`
|
|
2884
|
+
- Attachment.Item / PromptInput.Attachments: `attachmentImage`
|
|
2885
|
+
- Source.List: `sources`
|
|
2886
|
+
- Message.Actions: `messageActions`
|
|
2887
|
+
- Message.Regenerate: `regenerate`
|
|
2888
|
+
- Message.Feedback: `feedbackPositive`, `feedbackNegative`
|
|
2889
|
+
- ToolInvocation: `toolInput`, `toolOutput`, `toolError`, `toolDenied`, `toolApprovalRequest`, `toolApprove`, `toolDeny`
|
|
2890
|
+
- Response: `responseCopied`, `responseCopyCode`, `responseCopyLink`, `responseCopyTable`, `responseCopyTableAsCsv`, `responseCopyTableAsMarkdown`, `responseCopyTableAsTsv`, `responseDownloadDiagram`, `responseDownloadDiagramAsMmd`, `responseDownloadDiagramAsPng`, `responseDownloadDiagramAsSvg`, `responseDownloadFile`, `responseDownloadImage`, `responseDownloadTable`, `responseDownloadTableAsCsv`, `responseDownloadTableAsMarkdown`, `responseExitFullscreen`, `responseViewFullscreen`, `responseImageNotAvailable`, `responseOpenExternalLink`, `responseExternalLinkWarning`, `responseOpenLink`
|
|
2891
|
+
|
|
2892
|
+
<!-- /generated:message-keys -->
|
|
2893
|
+
|
|
2894
|
+
`fileFieldTrigger` is the button text of an empty `FileField.Dropzone`, and
|
|
2895
|
+
with `tabList` it is also what the generative-UI renderers fall back to when a
|
|
2896
|
+
spec leaves the trigger text or the tab-list name out.
|