@passui/react 0.1.16 → 0.1.19
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 +102 -1
- package/dist/types/src/appearance/index.d.ts +2 -0
- package/dist/types/src/appearance/platform.d.ts +9 -0
- package/dist/types/src/appearance/theme.d.ts +71 -0
- package/dist/types/src/components/ambient/ambient.d.ts +12 -0
- package/dist/types/src/components/ambient/index.d.ts +1 -0
- package/dist/types/src/components/button/button-styles.d.ts +12 -0
- package/dist/types/src/components/button/button.d.ts +30 -0
- package/dist/types/src/components/button/icon-button.d.ts +10 -0
- package/dist/types/src/components/button/pending-spinner.d.ts +5 -0
- package/dist/types/src/components/button/use-pending-indicator.d.ts +2 -0
- package/dist/types/src/components/chip/chip.d.ts +7 -0
- package/dist/types/src/components/compact-date-picker/calendar-month.d.ts +25 -0
- package/dist/types/src/components/compact-date-picker/compact-date-picker.d.ts +27 -0
- package/dist/types/src/components/compact-time-picker/compact-time-picker.d.ts +26 -0
- package/dist/types/src/components/compact-time-picker/time-value.d.ts +18 -0
- package/dist/types/src/components/content-view/content-view.d.ts +48 -0
- package/dist/types/src/components/content-view/index.d.ts +1 -0
- package/dist/types/src/components/context-menu/conceal-menu-source.d.ts +2 -0
- package/dist/types/src/components/context-menu/context-menu-host.d.ts +2 -0
- package/dist/types/src/components/context-menu/context-menu-store.d.ts +57 -0
- package/dist/types/src/components/context-menu/menu-navigation.d.ts +6 -0
- package/dist/types/src/components/context-menu/menu-position.d.ts +21 -0
- package/dist/types/src/components/context-menu/menu-reveal.d.ts +22 -0
- package/dist/types/src/components/context-menu/use-context-menu.d.ts +14 -0
- package/dist/types/src/components/conversation/conversation.d.ts +43 -0
- package/dist/types/src/components/conversation/index.d.ts +1 -0
- package/dist/types/src/components/date-picker/date-picker.d.ts +16 -0
- package/dist/types/src/components/date-picker/date-value.d.ts +2 -0
- package/dist/types/src/components/dialog/dialog-host.d.ts +6 -0
- package/dist/types/src/components/dialog/dialog-store.d.ts +21 -0
- package/dist/types/src/components/dialog/dialog.d.ts +30 -0
- package/dist/types/src/components/dialog/index.d.ts +1 -0
- package/dist/types/src/components/disclosure-group/disclosure-group.d.ts +13 -0
- package/dist/types/src/components/divider/divider.d.ts +9 -0
- package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +15 -0
- package/dist/types/src/components/labeled-content/labeled-content.d.ts +19 -0
- package/dist/types/src/components/locale-switcher/index.d.ts +1 -0
- package/dist/types/src/components/locale-switcher/locale-switcher.d.ts +10 -0
- package/dist/types/src/components/menu/menu.d.ts +16 -0
- package/dist/types/src/components/picker/picker-trigger.d.ts +2 -0
- package/dist/types/src/components/picker/picker.d.ts +37 -0
- package/dist/types/src/components/popover/popover-fit.d.ts +21 -0
- package/dist/types/src/components/popover/popover.d.ts +14 -0
- package/dist/types/src/components/profile-image/profile-image.d.ts +4 -0
- package/dist/types/src/components/progress-view/progress-view.d.ts +13 -0
- package/dist/types/src/components/search-field/search-field.d.ts +2 -0
- package/dist/types/src/components/search-list/search-list.d.ts +19 -0
- package/dist/types/src/components/section/section.d.ts +15 -0
- package/dist/types/src/components/secure-field/secure-field.d.ts +4 -0
- package/dist/types/src/components/segmented-control/segmented-control.d.ts +20 -0
- package/dist/types/src/components/sheet/modal-environment.d.ts +2 -0
- package/dist/types/src/components/sheet/sheet-portal.d.ts +5 -0
- package/dist/types/src/components/sheet/sheet.d.ts +21 -0
- package/dist/types/src/components/sheet/use-sheet-focus.d.ts +3 -0
- package/dist/types/src/components/sidebar/sidebar.d.ts +18 -0
- package/dist/types/src/components/slider/slider.d.ts +14 -0
- package/dist/types/src/components/stat-tile/index.d.ts +1 -0
- package/dist/types/src/components/stat-tile/stat-tile.d.ts +15 -0
- package/dist/types/src/components/status-indicator/status-indicator.d.ts +14 -0
- package/dist/types/src/components/stepper/stepper.d.ts +19 -0
- package/dist/types/src/components/swatch-picker/swatch-picker.d.ts +17 -0
- package/dist/types/src/components/tab-bar/tab-bar.d.ts +40 -0
- package/dist/types/src/components/tab-view/tab-view.d.ts +24 -0
- package/dist/types/src/components/table/table.d.ts +10 -0
- package/dist/types/src/components/text-editor/text-editor.d.ts +2 -0
- package/dist/types/src/components/text-field/text-field.d.ts +18 -0
- package/dist/types/src/components/toast/toast-store.d.ts +47 -0
- package/dist/types/src/components/toast/toaster.d.ts +10 -0
- package/dist/types/src/components/toggle/toggle.d.ts +17 -0
- package/dist/types/src/components/tooltip/tooltip-host.d.ts +6 -0
- package/dist/types/src/components/view-controller/index.d.ts +1 -0
- package/dist/types/src/components/view-controller/view-controller.d.ts +21 -0
- package/dist/types/src/hooks/scroll/minimize-decision.d.ts +2 -0
- package/dist/types/src/hooks/use-anchored-placement.d.ts +7 -0
- package/dist/types/src/hooks/use-dismiss.d.ts +9 -0
- package/dist/types/src/hooks/use-floating-dismiss.d.ts +3 -0
- package/dist/types/src/hooks/use-media-query.d.ts +21 -0
- package/dist/types/src/hooks/use-minimize-on-scroll.d.ts +6 -0
- package/dist/types/src/hooks/use-visual-viewport.d.ts +4 -0
- package/dist/types/src/hooks/viewport/types.d.ts +19 -0
- package/dist/types/src/hooks/viewport/viewport-metrics.d.ts +3 -0
- package/dist/types/src/icons/icons.d.ts +612 -0
- package/dist/types/src/index.d.ts +58 -0
- package/dist/types/src/lib/anchored-placement.d.ts +39 -0
- package/dist/types/src/lib/class-names.d.ts +2 -0
- package/dist/types/src/lib/clipboard.d.ts +6 -0
- package/dist/types/src/lib/date.d.ts +50 -0
- package/dist/types/src/lib/floating-layers.d.ts +8 -0
- package/dist/types/src/lib/haptics.d.ts +8 -0
- package/dist/types/src/lib/language.d.ts +4 -0
- package/dist/types/src/lib/range-value.d.ts +4 -0
- package/dist/types/src/lib/screen-edges.d.ts +12 -0
- package/dist/types/src/lib/shapes.d.ts +17 -0
- package/dist/types/src/motion/transitions.d.ts +97 -0
- package/dist/types/src/tokens.d.ts +1 -0
- package/package.json +192 -40
- package/src/appearance/platform.tsx +10 -47
- package/src/appearance/theme.tsx +42 -142
- package/src/components/ambient/ambient.tsx +2 -10
- package/src/components/button/button-styles.ts +13 -31
- package/src/components/button/button.tsx +19 -14
- package/src/components/button/icon-button.tsx +14 -56
- package/src/components/button/use-pending-indicator.ts +2 -7
- package/src/components/chip/chip.tsx +8 -13
- package/src/components/compact-date-picker/calendar-month.ts +13 -57
- package/src/components/compact-date-picker/compact-date-picker.tsx +44 -123
- package/src/components/compact-time-picker/compact-time-picker.tsx +46 -130
- package/src/components/compact-time-picker/time-value.ts +4 -16
- package/src/components/content-view/content-view.tsx +13 -56
- package/src/components/context-menu/conceal-menu-source.ts +1 -4
- package/src/components/context-menu/context-menu-host.tsx +125 -195
- package/src/components/context-menu/context-menu-store.ts +3 -11
- package/src/components/context-menu/menu-navigation.ts +29 -0
- package/src/components/context-menu/menu-position.ts +6 -27
- package/src/components/context-menu/menu-reveal.ts +13 -54
- package/src/components/context-menu/use-context-menu.ts +6 -30
- package/src/components/conversation/conversation.tsx +26 -72
- package/src/components/date-picker/date-picker.tsx +8 -37
- package/src/components/date-picker/date-value.ts +5 -30
- package/src/components/dialog/dialog-host.tsx +31 -76
- package/src/components/dialog/dialog-store.ts +1 -3
- package/src/components/dialog/dialog.tsx +23 -80
- package/src/components/disclosure-group/disclosure-group.tsx +70 -0
- package/src/components/divider/divider.tsx +31 -0
- package/src/components/inline-disclosure/inline-disclosure.tsx +49 -0
- package/src/components/labeled-content/labeled-content.tsx +24 -18
- package/src/components/locale-switcher/locale-switcher.tsx +20 -70
- package/src/components/menu/menu.tsx +68 -0
- package/src/components/picker/picker-trigger.ts +10 -0
- package/src/components/picker/picker.tsx +72 -203
- package/src/components/popover/popover-fit.ts +69 -0
- package/src/components/popover/popover.tsx +94 -77
- package/src/components/profile-image/profile-image.tsx +6 -28
- package/src/components/progress-view/progress-view.tsx +65 -0
- package/src/components/search-field/search-field.tsx +27 -25
- package/src/components/search-list/search-list.tsx +43 -69
- package/src/components/section/section.tsx +18 -17
- package/src/components/secure-field/secure-field.tsx +5 -7
- package/src/components/segmented-control/segmented-control.tsx +20 -29
- package/src/components/sheet/modal-environment.ts +16 -54
- package/src/components/sheet/sheet-portal.tsx +10 -42
- package/src/components/sheet/sheet.tsx +17 -25
- package/src/components/sheet/use-sheet-focus.ts +15 -51
- package/src/components/sidebar/sidebar.tsx +27 -30
- package/src/components/slider/slider.tsx +76 -0
- package/src/components/stat-tile/stat-tile.tsx +9 -30
- package/src/components/status-indicator/status-indicator.tsx +12 -19
- package/src/components/stepper/stepper.tsx +80 -0
- package/src/components/swatch-picker/swatch-picker.tsx +19 -15
- package/src/components/tab-bar/tab-bar.tsx +59 -59
- package/src/components/tab-view/tab-view.tsx +52 -57
- package/src/components/table/table.tsx +14 -62
- package/src/components/text-editor/text-editor.tsx +2 -12
- package/src/components/text-field/text-field.tsx +11 -40
- package/src/components/toast/toast-store.ts +9 -30
- package/src/components/toast/toaster.tsx +90 -104
- package/src/components/toggle/toggle.tsx +28 -24
- package/src/components/tooltip/tooltip-host.tsx +71 -69
- package/src/components/view-controller/view-controller.tsx +4 -21
- package/src/hooks/scroll/minimize-decision.ts +1 -5
- package/src/hooks/use-anchored-placement.ts +4 -14
- package/src/hooks/use-dismiss.ts +5 -23
- package/src/hooks/use-floating-dismiss.ts +28 -0
- package/src/hooks/use-media-query.ts +12 -31
- package/src/hooks/use-minimize-on-scroll.ts +10 -34
- package/src/hooks/use-visual-viewport.ts +5 -14
- package/src/hooks/viewport/viewport-metrics.ts +4 -16
- package/src/icons/icons.tsx +167 -121
- package/src/index.ts +9 -1
- package/src/lib/anchored-placement.ts +5 -15
- package/src/lib/class-names.ts +1 -2
- package/src/lib/clipboard.ts +14 -0
- package/src/lib/date.ts +4 -15
- package/src/lib/floating-layers.ts +18 -8
- package/src/lib/haptics.ts +1 -3
- package/src/lib/language.ts +2 -7
- package/src/lib/range-value.ts +32 -0
- package/src/lib/screen-edges.ts +2 -6
- package/src/lib/shapes.ts +5 -20
- package/src/motion/transitions.ts +18 -12
- package/src/components/ambient/ambient.docs.tsx +0 -68
- package/src/components/ambient/examples/ambient-images.tsx +0 -30
- package/src/components/ambient/examples/ambient-now-playing.tsx +0 -55
- package/src/components/ambient/examples/ambient-showcase.tsx +0 -33
- package/src/components/ambient/examples/ambient-usage.tsx +0 -19
- package/src/components/button/button.docs.tsx +0 -116
- package/src/components/button/examples/button-disabled.tsx +0 -19
- package/src/components/button/examples/button-icon.tsx +0 -39
- package/src/components/button/examples/button-link.tsx +0 -12
- package/src/components/button/examples/button-pending.tsx +0 -27
- package/src/components/button/examples/button-showcase.tsx +0 -115
- package/src/components/button/examples/button-sizes.tsx +0 -12
- package/src/components/button/examples/button-usage.tsx +0 -12
- package/src/components/button/examples/button-variants.tsx +0 -15
- package/src/components/button/examples/button-with-icon.tsx +0 -32
- package/src/components/button/examples/icon-button-player.tsx +0 -38
- package/src/components/button/examples/icon-button-showcase.tsx +0 -83
- package/src/components/button/examples/icon-button-usage.tsx +0 -11
- package/src/components/button/examples/icon-button-variants.tsx +0 -39
- package/src/components/button/icon-button.docs.tsx +0 -68
- package/src/components/chip/chip.docs.tsx +0 -37
- package/src/components/chip/examples/chip-in-rows.tsx +0 -19
- package/src/components/chip/examples/chip-showcase.tsx +0 -51
- package/src/components/chip/examples/chip-tones.tsx +0 -13
- package/src/components/chip/examples/chip-usage.tsx +0 -5
- package/src/components/compact-date-picker/compact-date-picker.docs.tsx +0 -81
- package/src/components/compact-date-picker/examples/compact-date-picker-showcase.tsx +0 -35
- package/src/components/compact-date-picker/examples/compact-date-picker-usage.tsx +0 -22
- package/src/components/compact-time-picker/compact-time-picker.docs.tsx +0 -80
- package/src/components/compact-time-picker/examples/compact-time-picker-showcase.tsx +0 -31
- package/src/components/compact-time-picker/examples/compact-time-picker-usage.tsx +0 -21
- package/src/components/content-view/content-view.docs.tsx +0 -61
- package/src/components/content-view/examples/content-view-header-action.tsx +0 -33
- package/src/components/content-view/examples/content-view-showcase.tsx +0 -65
- package/src/components/content-view/examples/content-view-stats.tsx +0 -35
- package/src/components/content-view/examples/content-view-usage.tsx +0 -14
- package/src/components/context-menu/context-menu.docs.tsx +0 -92
- package/src/components/context-menu/examples/context-menu-checked.tsx +0 -37
- package/src/components/context-menu/examples/context-menu-list.tsx +0 -85
- package/src/components/context-menu/examples/context-menu-showcase.tsx +0 -56
- package/src/components/context-menu/examples/context-menu-toolbar.tsx +0 -63
- package/src/components/context-menu/examples/context-menu-usage.tsx +0 -24
- package/src/components/conversation/conversation.docs.tsx +0 -88
- package/src/components/conversation/examples/conversation-composer.tsx +0 -36
- package/src/components/conversation/examples/conversation-days.tsx +0 -48
- package/src/components/conversation/examples/conversation-showcase.tsx +0 -71
- package/src/components/conversation/examples/conversation-usage.tsx +0 -28
- package/src/components/date-picker/date-picker.docs.tsx +0 -88
- package/src/components/date-picker/examples/date-picker-locales.tsx +0 -41
- package/src/components/date-picker/examples/date-picker-past-only.tsx +0 -29
- package/src/components/date-picker/examples/date-picker-showcase.tsx +0 -49
- package/src/components/date-picker/examples/date-picker-usage.tsx +0 -22
- package/src/components/dialog/dialog.docs.tsx +0 -114
- package/src/components/dialog/examples/dialog-confirm.tsx +0 -24
- package/src/components/dialog/examples/dialog-form.tsx +0 -46
- package/src/components/dialog/examples/dialog-inline.tsx +0 -48
- package/src/components/dialog/examples/dialog-showcase.tsx +0 -53
- package/src/components/dialog/examples/dialog-usage.tsx +0 -31
- package/src/components/labeled-content/examples/labeled-content-descriptions.tsx +0 -37
- package/src/components/labeled-content/examples/labeled-content-showcase.tsx +0 -54
- package/src/components/labeled-content/examples/labeled-content-usage.tsx +0 -22
- package/src/components/labeled-content/examples/labeled-content-values.tsx +0 -26
- package/src/components/labeled-content/examples/labeled-content-wide.tsx +0 -46
- package/src/components/labeled-content/labeled-content.docs.tsx +0 -69
- package/src/components/locale-switcher/examples/locale-switcher-native-names.tsx +0 -30
- package/src/components/locale-switcher/examples/locale-switcher-showcase.tsx +0 -46
- package/src/components/locale-switcher/examples/locale-switcher-translated-page.tsx +0 -35
- package/src/components/locale-switcher/examples/locale-switcher-usage.tsx +0 -20
- package/src/components/locale-switcher/locale-switcher.docs.tsx +0 -91
- package/src/components/picker/examples/picker-details.tsx +0 -44
- package/src/components/picker/examples/picker-searchable.tsx +0 -48
- package/src/components/picker/examples/picker-showcase.tsx +0 -66
- package/src/components/picker/examples/picker-usage.tsx +0 -20
- package/src/components/picker/picker.docs.tsx +0 -68
- package/src/components/popover/examples/popover-help.tsx +0 -57
- package/src/components/popover/examples/popover-menu.tsx +0 -51
- package/src/components/popover/examples/popover-showcase.tsx +0 -61
- package/src/components/popover/examples/popover-usage.tsx +0 -31
- package/src/components/popover/popover.docs.tsx +0 -84
- package/src/components/profile-image/examples/profile-image-fallbacks.tsx +0 -29
- package/src/components/profile-image/examples/profile-image-group.tsx +0 -34
- package/src/components/profile-image/examples/profile-image-showcase.tsx +0 -30
- package/src/components/profile-image/examples/profile-image-usage.tsx +0 -14
- package/src/components/profile-image/profile-image.docs.tsx +0 -81
- package/src/components/search-field/examples/search-field-filter.tsx +0 -53
- package/src/components/search-field/examples/search-field-showcase.tsx +0 -56
- package/src/components/search-field/examples/search-field-toolbar.tsx +0 -27
- package/src/components/search-field/examples/search-field-usage.tsx +0 -18
- package/src/components/search-field/search-field.docs.tsx +0 -58
- package/src/components/search-list/examples/search-list-dropdown.tsx +0 -53
- package/src/components/search-list/examples/search-list-groups.tsx +0 -64
- package/src/components/search-list/examples/search-list-showcase.tsx +0 -67
- package/src/components/search-list/examples/search-list-usage.tsx +0 -27
- package/src/components/search-list/search-list.docs.tsx +0 -87
- package/src/components/section/examples/section-actions.tsx +0 -35
- package/src/components/section/examples/section-groups.tsx +0 -41
- package/src/components/section/examples/section-showcase.tsx +0 -63
- package/src/components/section/examples/section-usage.tsx +0 -19
- package/src/components/section/section.docs.tsx +0 -51
- package/src/components/secure-field/examples/secure-field-new-password.tsx +0 -56
- package/src/components/secure-field/examples/secure-field-reveal.tsx +0 -20
- package/src/components/secure-field/examples/secure-field-showcase.tsx +0 -62
- package/src/components/secure-field/examples/secure-field-usage.tsx +0 -19
- package/src/components/secure-field/secure-field.docs.tsx +0 -72
- package/src/components/segmented-control/examples/segmented-control-overflow.tsx +0 -28
- package/src/components/segmented-control/examples/segmented-control-showcase.tsx +0 -59
- package/src/components/segmented-control/examples/segmented-control-usage.tsx +0 -20
- package/src/components/segmented-control/examples/segmented-control-views.tsx +0 -32
- package/src/components/segmented-control/segmented-control.docs.tsx +0 -61
- package/src/components/sheet/examples/sheet-choices.tsx +0 -58
- package/src/components/sheet/examples/sheet-share.tsx +0 -51
- package/src/components/sheet/examples/sheet-showcase.tsx +0 -74
- package/src/components/sheet/examples/sheet-usage.tsx +0 -32
- package/src/components/sheet/sheet.docs.tsx +0 -75
- package/src/components/sidebar/examples/sidebar-groups.tsx +0 -133
- package/src/components/sidebar/examples/sidebar-showcase.tsx +0 -123
- package/src/components/sidebar/examples/sidebar-unread.tsx +0 -66
- package/src/components/sidebar/examples/sidebar-usage.tsx +0 -52
- package/src/components/sidebar/sidebar.docs.tsx +0 -135
- package/src/components/stat-tile/examples/stat-tile-filters.tsx +0 -32
- package/src/components/stat-tile/examples/stat-tile-grid.tsx +0 -21
- package/src/components/stat-tile/examples/stat-tile-showcase.tsx +0 -52
- package/src/components/stat-tile/examples/stat-tile-usage.tsx +0 -13
- package/src/components/stat-tile/stat-tile.docs.tsx +0 -60
- package/src/components/status-indicator/examples/status-indicator-live.tsx +0 -34
- package/src/components/status-indicator/examples/status-indicator-showcase.tsx +0 -39
- package/src/components/status-indicator/examples/status-indicator-states.tsx +0 -17
- package/src/components/status-indicator/examples/status-indicator-usage.tsx +0 -9
- package/src/components/status-indicator/status-indicator.docs.tsx +0 -43
- package/src/components/swatch-picker/examples/swatch-picker-accents.tsx +0 -42
- package/src/components/swatch-picker/examples/swatch-picker-labels.tsx +0 -45
- package/src/components/swatch-picker/examples/swatch-picker-showcase.tsx +0 -70
- package/src/components/swatch-picker/examples/swatch-picker-usage.tsx +0 -39
- package/src/components/swatch-picker/swatch-picker.docs.tsx +0 -60
- package/src/components/tab-bar/examples/tab-bar-five-tabs.tsx +0 -66
- package/src/components/tab-bar/examples/tab-bar-minimize.tsx +0 -71
- package/src/components/tab-bar/examples/tab-bar-showcase.tsx +0 -82
- package/src/components/tab-bar/examples/tab-bar-usage.tsx +0 -54
- package/src/components/tab-bar/tab-bar.docs.tsx +0 -136
- package/src/components/tab-view/examples/tab-view-keyboard.tsx +0 -39
- package/src/components/tab-view/examples/tab-view-settings.tsx +0 -56
- package/src/components/tab-view/examples/tab-view-showcase.tsx +0 -53
- package/src/components/tab-view/examples/tab-view-usage.tsx +0 -23
- package/src/components/tab-view/tab-view.docs.tsx +0 -77
- package/src/components/table/examples/table-empty.tsx +0 -53
- package/src/components/table/examples/table-invoices.tsx +0 -102
- package/src/components/table/examples/table-selection.tsx +0 -150
- package/src/components/table/examples/table-showcase.tsx +0 -115
- package/src/components/table/examples/table-usage.tsx +0 -67
- package/src/components/table/examples/table-wide.tsx +0 -75
- package/src/components/table/table.docs.tsx +0 -131
- package/src/components/text-editor/examples/text-editor-comment.tsx +0 -48
- package/src/components/text-editor/examples/text-editor-count.tsx +0 -38
- package/src/components/text-editor/examples/text-editor-showcase.tsx +0 -58
- package/src/components/text-editor/examples/text-editor-usage.tsx +0 -25
- package/src/components/text-editor/text-editor.docs.tsx +0 -61
- package/src/components/text-field/examples/text-field-icons.tsx +0 -72
- package/src/components/text-field/examples/text-field-showcase.tsx +0 -50
- package/src/components/text-field/examples/text-field-usage.tsx +0 -18
- package/src/components/text-field/examples/text-field-validation.tsx +0 -27
- package/src/components/text-field/text-field.docs.tsx +0 -118
- package/src/components/toast/examples/toast-action.tsx +0 -32
- package/src/components/toast/examples/toast-promise.tsx +0 -45
- package/src/components/toast/examples/toast-showcase.tsx +0 -49
- package/src/components/toast/examples/toast-usage.tsx +0 -13
- package/src/components/toast/toast.docs.tsx +0 -79
- package/src/components/toggle/examples/toggle-checkboxes.tsx +0 -43
- package/src/components/toggle/examples/toggle-settings.tsx +0 -31
- package/src/components/toggle/examples/toggle-showcase.tsx +0 -59
- package/src/components/toggle/examples/toggle-usage.tsx +0 -13
- package/src/components/toggle/toggle.docs.tsx +0 -69
- package/src/components/tooltip/examples/tooltip-icon-buttons.tsx +0 -27
- package/src/components/tooltip/examples/tooltip-shortcuts.tsx +0 -38
- package/src/components/tooltip/examples/tooltip-showcase.tsx +0 -44
- package/src/components/tooltip/examples/tooltip-usage.tsx +0 -13
- package/src/components/tooltip/tooltip.docs.tsx +0 -56
- package/src/components/view-controller/examples/view-controller-action.tsx +0 -27
- package/src/components/view-controller/examples/view-controller-settings.tsx +0 -31
- package/src/components/view-controller/examples/view-controller-showcase.tsx +0 -45
- package/src/components/view-controller/examples/view-controller-usage.tsx +0 -19
- package/src/components/view-controller/view-controller.docs.tsx +0 -71
- package/src/docs/index.ts +0 -3
- package/src/docs/registry.ts +0 -113
- package/src/docs/snippet.ts +0 -17
- package/src/docs/types.ts +0 -180
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ComponentProps, useId } from "react"
|
|
2
|
+
import { cn } from "../../lib/class-names"
|
|
3
|
+
import { clampRange } from "../../lib/range-value"
|
|
4
|
+
|
|
5
|
+
export interface SliderProps
|
|
6
|
+
extends Omit<
|
|
7
|
+
ComponentProps<"input">,
|
|
8
|
+
"type" | "value" | "defaultValue" | "onChange" | "min" | "max" | "step"
|
|
9
|
+
> {
|
|
10
|
+
value: number
|
|
11
|
+
onChange: (value: number) => void
|
|
12
|
+
label: string
|
|
13
|
+
min?: number
|
|
14
|
+
max?: number
|
|
15
|
+
step?: number
|
|
16
|
+
description?: string
|
|
17
|
+
/** Provides a localized, spoken description of the current value. */
|
|
18
|
+
formatValue?: (value: number) => string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Native keyboard, touch and pointer behavior with Passui's control treatment. */
|
|
22
|
+
export function Slider({
|
|
23
|
+
value,
|
|
24
|
+
onChange,
|
|
25
|
+
label,
|
|
26
|
+
min = 0,
|
|
27
|
+
max = 100,
|
|
28
|
+
step = 1,
|
|
29
|
+
description,
|
|
30
|
+
formatValue,
|
|
31
|
+
id: suppliedId,
|
|
32
|
+
className,
|
|
33
|
+
disabled,
|
|
34
|
+
...props
|
|
35
|
+
}: SliderProps) {
|
|
36
|
+
const generatedId = useId()
|
|
37
|
+
const id = suppliedId ?? generatedId
|
|
38
|
+
const upper = Math.max(min, max)
|
|
39
|
+
const current = clampRange(value, min, upper)
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={cn("flex min-w-0 flex-col gap-2", disabled && "opacity-45")}>
|
|
43
|
+
<label htmlFor={id} className="text-subheadline font-semibold text-label">
|
|
44
|
+
{label}
|
|
45
|
+
</label>
|
|
46
|
+
<input
|
|
47
|
+
{...props}
|
|
48
|
+
id={id}
|
|
49
|
+
type="range"
|
|
50
|
+
min={min}
|
|
51
|
+
max={upper}
|
|
52
|
+
step={step > 0 ? step : 1}
|
|
53
|
+
value={current}
|
|
54
|
+
disabled={disabled || upper === min}
|
|
55
|
+
aria-valuetext={formatValue?.(current)}
|
|
56
|
+
aria-describedby={description ? `${id}-description` : undefined}
|
|
57
|
+
onChange={(event) => onChange(Number(event.currentTarget.value))}
|
|
58
|
+
className={cn(
|
|
59
|
+
"h-11 w-full min-w-0 cursor-pointer appearance-none bg-transparent accent-accent",
|
|
60
|
+
"focus-visible:rounded-lg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-text",
|
|
61
|
+
"[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-separator",
|
|
62
|
+
"[&::-webkit-slider-thumb]:-mt-[7px] [&::-webkit-slider-thumb]:size-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-accent [&::-webkit-slider-thumb]:bg-elevated [&::-webkit-slider-thumb]:shadow-sm",
|
|
63
|
+
"[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-separator",
|
|
64
|
+
"[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-accent",
|
|
65
|
+
"[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-accent [&::-moz-range-thumb]:bg-elevated",
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
69
|
+
{description && (
|
|
70
|
+
<p id={`${id}-description`} className="text-footnote text-label-secondary">
|
|
71
|
+
{description}
|
|
72
|
+
</p>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
@@ -3,7 +3,7 @@ import { cn } from "../../lib/class-names"
|
|
|
3
3
|
|
|
4
4
|
export type StatTileTone = "critical"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export interface StatTileProps {
|
|
7
7
|
label: string
|
|
8
8
|
value: number
|
|
9
9
|
/** A quiet line under the number, like "3 signed in now". */
|
|
@@ -16,41 +16,20 @@ type StatTileProps = {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/** One number with its label, optionally a toggle. */
|
|
19
|
-
export function StatTile({
|
|
20
|
-
label,
|
|
21
|
-
value,
|
|
22
|
-
caption,
|
|
23
|
-
tone,
|
|
24
|
-
onClick,
|
|
25
|
-
active = false,
|
|
26
|
-
}: StatTileProps) {
|
|
19
|
+
export function StatTile({ label, value, caption, tone, onClick, active = false }: StatTileProps) {
|
|
27
20
|
const body = (
|
|
28
21
|
<>
|
|
29
|
-
<span className="text-
|
|
30
|
-
|
|
31
|
-
</span>
|
|
32
|
-
<span
|
|
33
|
-
className={cn(
|
|
34
|
-
"text-lg font-semibold tabular-nums tracking-tight",
|
|
35
|
-
tone === "critical" &&
|
|
36
|
-
value > 0 &&
|
|
37
|
-
"text-destructive"
|
|
38
|
-
)}
|
|
39
|
-
>
|
|
22
|
+
<span className="text-label-secondary">{label}</span>
|
|
23
|
+
<span className={cn("tabular-nums", tone === "critical" && value > 0 && "text-danger")}>
|
|
40
24
|
{value.toLocaleString("en-US")}
|
|
41
25
|
</span>
|
|
42
|
-
{caption &&
|
|
43
|
-
<span className="text-xs text-muted-foreground">
|
|
44
|
-
{caption}
|
|
45
|
-
</span>
|
|
46
|
-
)}
|
|
26
|
+
{caption && <span className="text-label-secondary">{caption}</span>}
|
|
47
27
|
</>
|
|
48
28
|
)
|
|
49
29
|
const className =
|
|
50
|
-
"flex min-w-0 flex-col gap-
|
|
30
|
+
"flex min-w-0 flex-col gap-2 rounded-2xl border border-separator/35 bg-elevated p-4 text-left shadow-xs"
|
|
51
31
|
|
|
52
|
-
if (!onClick)
|
|
53
|
-
return <div className={className}>{body}</div>
|
|
32
|
+
if (!onClick) return <div className={className}>{body}</div>
|
|
54
33
|
return (
|
|
55
34
|
<button
|
|
56
35
|
type="button"
|
|
@@ -58,8 +37,8 @@ export function StatTile({
|
|
|
58
37
|
onClick={onClick}
|
|
59
38
|
className={cn(
|
|
60
39
|
className,
|
|
61
|
-
"ring-1 ring-transparent transition-colors hover:bg-
|
|
62
|
-
active && "bg-
|
|
40
|
+
"ring-1 ring-transparent transition-colors hover:bg-fill/60",
|
|
41
|
+
active && "bg-fill/70 ring-label/15"
|
|
63
42
|
)}
|
|
64
43
|
>
|
|
65
44
|
{body}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react"
|
|
1
2
|
import { cn } from "../../lib/class-names"
|
|
2
3
|
|
|
3
4
|
const dots = {
|
|
@@ -6,29 +7,21 @@ const dots = {
|
|
|
6
7
|
offline: "bg-label-tertiary",
|
|
7
8
|
} as const
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}) {
|
|
10
|
+
export type StatusIndicatorState = keyof typeof dots
|
|
11
|
+
|
|
12
|
+
export interface StatusIndicatorProps extends HTMLAttributes<HTMLSpanElement> {
|
|
13
|
+
/** Visual treatment of the status dot. */
|
|
14
|
+
state: StatusIndicatorState
|
|
15
|
+
children: ReactNode
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function StatusIndicator({ state, children, className, ...props }: StatusIndicatorProps) {
|
|
18
19
|
return (
|
|
19
20
|
<span
|
|
20
|
-
className={cn(
|
|
21
|
-
"flex items-center gap-1.5 text-footnote text-label-tertiary",
|
|
22
|
-
className
|
|
23
|
-
)}
|
|
21
|
+
className={cn("flex items-center gap-1.5 text-footnote text-label-tertiary", className)}
|
|
24
22
|
{...props}
|
|
25
23
|
>
|
|
26
|
-
<span
|
|
27
|
-
className={cn(
|
|
28
|
-
"size-[7px] rounded-full",
|
|
29
|
-
dots[state]
|
|
30
|
-
)}
|
|
31
|
-
/>
|
|
24
|
+
<span aria-hidden="true" className={cn("size-[7px] rounded-full", dots[state])} />
|
|
32
25
|
{children}
|
|
33
26
|
</span>
|
|
34
27
|
)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useId } from "react"
|
|
2
|
+
import { cn } from "../../lib/class-names"
|
|
3
|
+
import { clampRange, stepRange } from "../../lib/range-value"
|
|
4
|
+
|
|
5
|
+
export interface StepperProps {
|
|
6
|
+
value: number
|
|
7
|
+
onChange: (value: number) => void
|
|
8
|
+
label: string
|
|
9
|
+
/** Localized accessible names for each action. */
|
|
10
|
+
labels: { decrement: string; increment: string }
|
|
11
|
+
min?: number
|
|
12
|
+
max?: number
|
|
13
|
+
step?: number
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
className?: string
|
|
16
|
+
/** Formats the displayed value. */
|
|
17
|
+
formatValue?: (value: number) => string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Discrete increments with disabled boundary actions and a spoken value. */
|
|
21
|
+
export function Stepper({
|
|
22
|
+
value,
|
|
23
|
+
onChange,
|
|
24
|
+
label,
|
|
25
|
+
labels,
|
|
26
|
+
min = 0,
|
|
27
|
+
max = 100,
|
|
28
|
+
step = 1,
|
|
29
|
+
disabled = false,
|
|
30
|
+
className,
|
|
31
|
+
formatValue,
|
|
32
|
+
}: StepperProps) {
|
|
33
|
+
const id = useId()
|
|
34
|
+
const upper = Math.max(min, max)
|
|
35
|
+
const current = clampRange(value, min, upper)
|
|
36
|
+
const decrement = stepRange(current, -1, min, upper, step > 0 ? step : 1)
|
|
37
|
+
const increment = stepRange(current, 1, min, upper, step > 0 ? step : 1)
|
|
38
|
+
const buttonStyle = cn(
|
|
39
|
+
"flex size-11 shrink-0 items-center justify-center rounded-xl text-title2 text-accent-text",
|
|
40
|
+
"transition-colors hover:bg-fill focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
41
|
+
"focus-visible:outline-accent-text disabled:cursor-not-allowed disabled:opacity-35"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className={cn("flex min-w-0 items-center justify-between gap-3", className)}>
|
|
46
|
+
<span id={id} className="min-w-0 text-subheadline font-semibold text-label">
|
|
47
|
+
{label}
|
|
48
|
+
</span>
|
|
49
|
+
<div className="flex shrink-0 items-center gap-1 rounded-2xl border border-separator bg-elevated p-1">
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
aria-label={labels.decrement}
|
|
53
|
+
aria-describedby={id}
|
|
54
|
+
disabled={disabled || decrement === current}
|
|
55
|
+
onClick={() => onChange(decrement)}
|
|
56
|
+
className={buttonStyle}
|
|
57
|
+
>
|
|
58
|
+
−
|
|
59
|
+
</button>
|
|
60
|
+
<output
|
|
61
|
+
aria-live="polite"
|
|
62
|
+
aria-labelledby={id}
|
|
63
|
+
className="min-w-10 text-center text-subheadline tabular-nums text-label"
|
|
64
|
+
>
|
|
65
|
+
{formatValue?.(current) ?? current}
|
|
66
|
+
</output>
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
aria-label={labels.increment}
|
|
70
|
+
aria-describedby={id}
|
|
71
|
+
disabled={disabled || increment === current}
|
|
72
|
+
onClick={() => onChange(increment)}
|
|
73
|
+
className={buttonStyle}
|
|
74
|
+
>
|
|
75
|
+
+
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
@@ -5,24 +5,30 @@ import { cn } from "../../lib/class-names"
|
|
|
5
5
|
import { haptic } from "../../lib/haptics"
|
|
6
6
|
import { spring } from "../../motion/transitions"
|
|
7
7
|
|
|
8
|
+
export interface SwatchPickerOption<T extends string> {
|
|
9
|
+
value: T
|
|
10
|
+
label: string
|
|
11
|
+
/** Swatch background color. */
|
|
12
|
+
color: string
|
|
13
|
+
/** Color of the selected checkmark. */
|
|
14
|
+
ink: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SwatchPickerProps<T extends string> {
|
|
18
|
+
value: T
|
|
19
|
+
options: ReadonlyArray<SwatchPickerOption<T>>
|
|
20
|
+
onChange: (value: T) => void
|
|
21
|
+
/** Accessible name for the group. */
|
|
22
|
+
label: string
|
|
23
|
+
}
|
|
24
|
+
|
|
8
25
|
/** Colour swatches with a check on the chosen one. */
|
|
9
26
|
export function SwatchPicker<T extends string>({
|
|
10
27
|
value,
|
|
11
28
|
options,
|
|
12
29
|
onChange,
|
|
13
30
|
label,
|
|
14
|
-
}: {
|
|
15
|
-
value: T
|
|
16
|
-
/** `ink` colours the check drawn on the swatch. */
|
|
17
|
-
options: Array<{
|
|
18
|
-
value: T
|
|
19
|
-
label: string
|
|
20
|
-
color: string
|
|
21
|
-
ink: string
|
|
22
|
-
}>
|
|
23
|
-
onChange: (value: T) => void
|
|
24
|
-
label: string
|
|
25
|
-
}) {
|
|
31
|
+
}: SwatchPickerProps<T>) {
|
|
26
32
|
const name = useId()
|
|
27
33
|
return (
|
|
28
34
|
<fieldset className="flex flex-wrap gap-3">
|
|
@@ -81,9 +87,7 @@ export function SwatchPicker<T extends string>({
|
|
|
81
87
|
<span
|
|
82
88
|
className={cn(
|
|
83
89
|
"text-caption2",
|
|
84
|
-
selected
|
|
85
|
-
? "font-semibold text-label"
|
|
86
|
-
: "text-label-tertiary"
|
|
90
|
+
selected ? "font-semibold text-label" : "text-label-tertiary"
|
|
87
91
|
)}
|
|
88
92
|
>
|
|
89
93
|
{option.label}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { motion } from "motion/react"
|
|
2
|
-
import { type ReactNode, useId } from "react"
|
|
1
|
+
import { motion, useReducedMotion } from "motion/react"
|
|
2
|
+
import { Fragment, type ReactNode, useId } from "react"
|
|
3
3
|
import { useMinimizeOnScroll } from "../../hooks/use-minimize-on-scroll"
|
|
4
4
|
import { SearchIcon } from "../../icons/icons"
|
|
5
5
|
import { cn } from "../../lib/class-names"
|
|
@@ -19,6 +19,22 @@ export interface TabBarLinkProps {
|
|
|
19
19
|
children: ReactNode
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export type TabBarMinimizeBehavior = "platform" | "scroll" | "never"
|
|
23
|
+
|
|
24
|
+
export interface TabBarProps<Tab extends TabBarTab> {
|
|
25
|
+
/** `platform` follows the platform, `scroll` always minimizes, `never` stays expanded. @default "platform" */
|
|
26
|
+
minimizeBehavior?: TabBarMinimizeBehavior
|
|
27
|
+
tabs: readonly Tab[]
|
|
28
|
+
/** Active tab ID, or null for a destination outside the bar. */
|
|
29
|
+
active: Tab["id"] | null
|
|
30
|
+
/** Renders your router's link with the supplied appearance and accessibility props. */
|
|
31
|
+
renderLink: (tab: Tab, props: TabBarLinkProps) => ReactNode
|
|
32
|
+
search?: { label: string; onPress: () => void }
|
|
33
|
+
/** Accessible name for the navigation. */
|
|
34
|
+
label: string
|
|
35
|
+
className?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
/**
|
|
23
39
|
* The app's main places a thumb's reach away, drawn as iOS 26 draws them: a
|
|
24
40
|
* floating glass capsule of tabs, with Search as its own round button at the
|
|
@@ -36,25 +52,12 @@ export function TabBar<Tab extends TabBarTab>({
|
|
|
36
52
|
label,
|
|
37
53
|
className,
|
|
38
54
|
minimizeBehavior = "platform",
|
|
39
|
-
}: {
|
|
40
|
-
minimizeBehavior?: "platform" | "scroll" | "never"
|
|
41
|
-
tabs: readonly Tab[]
|
|
42
|
-
/** Null in places the bar has no tab for. */
|
|
43
|
-
active: Tab["id"] | null
|
|
44
|
-
renderLink: (
|
|
45
|
-
tab: Tab,
|
|
46
|
-
props: TabBarLinkProps
|
|
47
|
-
) => ReactNode
|
|
48
|
-
search?: { label: string; onPress: () => void }
|
|
49
|
-
/** Names the navigation for screen readers. */
|
|
50
|
-
label: string
|
|
51
|
-
className?: string
|
|
52
|
-
}) {
|
|
55
|
+
}: TabBarProps<Tab>) {
|
|
53
56
|
// Scopes the sliding selection to this bar, so two bars on one
|
|
54
57
|
// screen (a docs page, a split view) never trade their highlight.
|
|
55
58
|
const selectionId = `${useId()}-tab-selection`
|
|
56
|
-
const
|
|
57
|
-
|
|
59
|
+
const reducedMotion = useReducedMotion()
|
|
60
|
+
const [scrolledAway, setScrolledAway] = useMinimizeOnScroll()
|
|
58
61
|
// Platform behavior keeps Android expanded; apps can opt into scroll minimization.
|
|
59
62
|
const minimized =
|
|
60
63
|
scrolledAway &&
|
|
@@ -70,8 +73,8 @@ export function TabBar<Tab extends TabBarTab>({
|
|
|
70
73
|
)}
|
|
71
74
|
>
|
|
72
75
|
<motion.div
|
|
73
|
-
layout
|
|
74
|
-
transition={spring.smooth}
|
|
76
|
+
layout={!reducedMotion}
|
|
77
|
+
transition={reducedMotion ? { duration: 0 } : spring.smooth}
|
|
75
78
|
// A tap on the shrunken capsule brings every tab back.
|
|
76
79
|
onClickCapture={(event) => {
|
|
77
80
|
if (!minimized) return
|
|
@@ -87,41 +90,41 @@ export function TabBar<Tab extends TabBarTab>({
|
|
|
87
90
|
{tabs.map((tab) => {
|
|
88
91
|
const current = tab.id === active
|
|
89
92
|
if (minimized && !current) return null
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
93
|
+
return (
|
|
94
|
+
<Fragment key={tab.id}>
|
|
95
|
+
{renderLink(tab, {
|
|
96
|
+
"aria-current": current ? "page" : undefined,
|
|
97
|
+
// Switching tabs is silent, as on Apple and Android: no haptic.
|
|
98
|
+
onClick: () => {},
|
|
99
|
+
className: cn(
|
|
100
|
+
"relative isolate flex h-11 min-w-0 flex-col items-center justify-center gap-0.5 rounded-full font-semibold text-[10px] tracking-[0.01em] no-underline transition-[color,transform] duration-fast active:scale-[0.94]",
|
|
101
|
+
minimized ? "w-11" : "flex-1 px-1",
|
|
102
|
+
current ? "text-accent-text" : "text-label-secondary"
|
|
103
|
+
),
|
|
104
|
+
children: (
|
|
105
|
+
<>
|
|
106
|
+
{current && (
|
|
107
|
+
<motion.span
|
|
108
|
+
layoutId={selectionId}
|
|
109
|
+
className="glass-lens absolute inset-0 -z-10 rounded-full"
|
|
110
|
+
transition={
|
|
111
|
+
reducedMotion ? { duration: 0 } : spring.snappy
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
{tab.icon}
|
|
116
|
+
<span
|
|
117
|
+
className={
|
|
118
|
+
minimized ? "sr-only" : "max-w-full truncate"
|
|
119
|
+
}
|
|
120
|
+
>
|
|
121
|
+
{tab.label}
|
|
122
|
+
</span>
|
|
123
|
+
</>
|
|
124
|
+
),
|
|
125
|
+
})}
|
|
126
|
+
</Fragment>
|
|
127
|
+
)
|
|
125
128
|
})}
|
|
126
129
|
</motion.div>
|
|
127
130
|
{search && (
|
|
@@ -141,8 +144,5 @@ export function TabBar<Tab extends TabBarTab>({
|
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
function onAndroid() {
|
|
144
|
-
return
|
|
145
|
-
typeof document !== "undefined" &&
|
|
146
|
-
document.documentElement.dataset.os === "android"
|
|
147
|
-
)
|
|
147
|
+
return typeof document !== "undefined" && document.documentElement.dataset.os === "android"
|
|
148
148
|
}
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useId,
|
|
3
|
-
useRef,
|
|
4
|
-
useState,
|
|
5
|
-
type ReactNode,
|
|
6
|
-
} from "react"
|
|
1
|
+
import { useId, useRef, useState, type ReactNode } from "react"
|
|
7
2
|
import { cn } from "../../lib/class-names"
|
|
8
3
|
|
|
4
|
+
export type TabViewAlign = "start" | "center"
|
|
5
|
+
|
|
6
|
+
export interface TabViewProps<Value extends string> {
|
|
7
|
+
/** Available tabs, in keyboard navigation order. */
|
|
8
|
+
options: readonly { value: Value; label: string; disabled?: boolean }[]
|
|
9
|
+
/** Initial selection when value is not controlled. */
|
|
10
|
+
defaultValue: Value
|
|
11
|
+
/** Controlled selection; omit to keep selection inside TabView. */
|
|
12
|
+
value?: Value
|
|
13
|
+
onValueChange?: (value: Value) => void
|
|
14
|
+
/** Tab alignment: `start` or `center`. @default "center" */
|
|
15
|
+
align?: TabViewAlign
|
|
16
|
+
/** Accessible name for the tab list. */
|
|
17
|
+
label: string
|
|
18
|
+
/** Renders the selected tab's panel. */
|
|
19
|
+
children: (value: Value) => ReactNode
|
|
20
|
+
className?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
/** Content tabs with native buttons, roving focus and automatic keyboard activation. */
|
|
10
24
|
export function TabView<Value extends string>({
|
|
11
25
|
options,
|
|
@@ -16,16 +30,7 @@ export function TabView<Value extends string>({
|
|
|
16
30
|
label,
|
|
17
31
|
children,
|
|
18
32
|
className,
|
|
19
|
-
}: {
|
|
20
|
-
options: readonly { value: Value; label: string }[]
|
|
21
|
-
defaultValue: Value
|
|
22
|
-
value?: Value
|
|
23
|
-
onValueChange?: (value: Value) => void
|
|
24
|
-
align?: "start" | "center"
|
|
25
|
-
label: string
|
|
26
|
-
children: (value: Value) => ReactNode
|
|
27
|
-
className?: string
|
|
28
|
-
}) {
|
|
33
|
+
}: TabViewProps<Value>) {
|
|
29
34
|
const id = useId()
|
|
30
35
|
const buttons = useRef<(HTMLButtonElement | null)[]>([])
|
|
31
36
|
const [selected, setSelected] = useState(defaultValue)
|
|
@@ -34,28 +39,18 @@ export function TabView<Value extends string>({
|
|
|
34
39
|
if (controlledValue === undefined) setSelected(next)
|
|
35
40
|
onValueChange?.(next)
|
|
36
41
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
? current
|
|
41
|
-
: options[0]?.value
|
|
42
|
+
const enabled = options.filter((option) => !option.disabled)
|
|
43
|
+
const value = enabled.some((option) => option.value === current) ? current : enabled[0]?.value
|
|
42
44
|
if (value === undefined) return null
|
|
43
45
|
|
|
44
46
|
return (
|
|
45
|
-
<div
|
|
46
|
-
className={cn(
|
|
47
|
-
"flex w-full flex-col gap-5",
|
|
48
|
-
className
|
|
49
|
-
)}
|
|
50
|
-
>
|
|
47
|
+
<div className={cn("flex w-full flex-col gap-5", className)}>
|
|
51
48
|
<div
|
|
52
49
|
role="tablist"
|
|
53
50
|
aria-label={label}
|
|
54
51
|
className={cn(
|
|
55
52
|
"inline-flex h-8 w-fit max-w-full items-center gap-5 p-[3px]",
|
|
56
|
-
align === "start"
|
|
57
|
-
? "self-start"
|
|
58
|
-
: "mx-auto justify-center"
|
|
53
|
+
align === "start" ? "self-start" : "mx-auto justify-center"
|
|
59
54
|
)}
|
|
60
55
|
>
|
|
61
56
|
{options.map((option, index) => (
|
|
@@ -66,38 +61,39 @@ export function TabView<Value extends string>({
|
|
|
66
61
|
id={`${id}-tab-${index}`}
|
|
67
62
|
aria-controls={`${id}-panel-${index}`}
|
|
68
63
|
aria-selected={option.value === value}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
64
|
+
aria-disabled={option.disabled || undefined}
|
|
65
|
+
disabled={option.disabled}
|
|
66
|
+
tabIndex={option.value === value ? 0 : -1}
|
|
72
67
|
ref={(element) => {
|
|
73
68
|
buttons.current[index] = element
|
|
74
69
|
}}
|
|
75
70
|
onClick={() => select(option.value)}
|
|
76
71
|
onKeyDown={(event) => {
|
|
77
|
-
const rtl =
|
|
78
|
-
getComputedStyle(
|
|
79
|
-
event.currentTarget
|
|
80
|
-
).direction === "rtl"
|
|
72
|
+
const rtl = getComputedStyle(event.currentTarget).direction === "rtl"
|
|
81
73
|
let next: number
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
next =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
else if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
74
|
+
let step: number
|
|
75
|
+
if (event.key === "Home") {
|
|
76
|
+
next = -1
|
|
77
|
+
step = 1
|
|
78
|
+
} else if (event.key === "End") {
|
|
79
|
+
next = options.length
|
|
80
|
+
step = -1
|
|
81
|
+
} else if (event.key === "ArrowRight") {
|
|
82
|
+
step = rtl ? -1 : 1
|
|
83
|
+
next = index
|
|
84
|
+
} else if (event.key === "ArrowLeft") {
|
|
85
|
+
step = rtl ? 1 : -1
|
|
86
|
+
next = index
|
|
87
|
+
} else return
|
|
94
88
|
event.preventDefault()
|
|
95
|
-
|
|
96
|
-
(next + options.length) %
|
|
97
|
-
options
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
for (let offset = 0; offset < options.length; offset++) {
|
|
90
|
+
next = (next + step + options.length) % options.length
|
|
91
|
+
const option = options[next]
|
|
92
|
+
if (!option || option.disabled) continue
|
|
93
|
+
select(option.value)
|
|
94
|
+
buttons.current[next]?.focus()
|
|
95
|
+
break
|
|
96
|
+
}
|
|
101
97
|
}}
|
|
102
98
|
className={cn(
|
|
103
99
|
"relative inline-flex h-[calc(100%-1px)] shrink-0 items-center justify-center border border-transparent p-0 text-sm font-medium whitespace-nowrap",
|
|
@@ -121,8 +117,7 @@ export function TabView<Value extends string>({
|
|
|
121
117
|
hidden={option.value !== value}
|
|
122
118
|
className="w-full outline-offset-4"
|
|
123
119
|
>
|
|
124
|
-
{option.value === value &&
|
|
125
|
-
children(option.value)}
|
|
120
|
+
{option.value === value && children(option.value)}
|
|
126
121
|
</div>
|
|
127
122
|
))}
|
|
128
123
|
</div>
|