@passui/react 0.1.46 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -109
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/dist/accordion.d.ts +15 -0
- package/dist/accordion.js +93 -0
- package/dist/alert-dialog.d.ts +26 -0
- package/dist/alert-dialog.js +142 -0
- package/dist/alert.d.ts +10 -0
- package/dist/alert.js +28 -0
- package/dist/{types/src/components/ambient/ambient.d.ts → ambient.d.ts} +4 -4
- package/dist/ambient.js +22 -0
- package/dist/{types/src/appearance → appearance}/platform.d.ts +3 -3
- package/dist/appearance/platform.js +43 -0
- package/dist/appearance/theme.d.ts +71 -0
- package/dist/appearance/theme.js +236 -0
- package/dist/appearance.d.ts +3 -0
- package/dist/appearance.js +3 -0
- package/dist/{types/src/components/aspect-ratio/aspect-ratio.d.ts → aspect-ratio.d.ts} +4 -4
- package/dist/aspect-ratio.js +17 -0
- package/dist/avatar.d.ts +5 -0
- package/dist/avatar.js +43 -0
- package/dist/badge.d.ts +8 -0
- package/dist/badge.js +21 -0
- package/dist/breadcrumb.d.ts +13 -0
- package/dist/breadcrumb.js +63 -0
- package/dist/button-group.d.ts +5 -0
- package/dist/button-group.js +14 -0
- package/dist/button.d.ts +21 -0
- package/dist/button.js +58 -0
- package/dist/calendar.d.ts +35 -0
- package/dist/calendar.js +225 -0
- package/dist/card.d.ts +9 -0
- package/dist/card.js +54 -0
- package/dist/chart.d.ts +30 -0
- package/dist/chart.js +226 -0
- package/dist/checkbox.d.ts +13 -0
- package/dist/checkbox.js +53 -0
- package/dist/{types/src/lib/clipboard.d.ts → clipboard.d.ts} +1 -1
- package/dist/clipboard.js +16 -0
- package/dist/cn.d.ts +2 -0
- package/dist/cn.js +10 -0
- package/dist/code-block.d.ts +34 -0
- package/dist/code-block.js +87 -0
- package/dist/collapsible.d.ts +13 -0
- package/dist/collapsible.js +49 -0
- package/dist/combobox.d.ts +25 -0
- package/dist/combobox.js +112 -0
- package/dist/command.d.ts +22 -0
- package/dist/command.js +129 -0
- package/dist/components/toast/toast-store.d.ts +60 -0
- package/dist/components/toast/toast-store.js +96 -0
- package/dist/{types/src/components → components}/toast/toaster.d.ts +4 -4
- package/dist/components/toast/toaster.js +345 -0
- package/dist/context-menu.d.ts +2 -0
- package/dist/context-menu.js +2 -0
- package/dist/conversation.d.ts +48 -0
- package/dist/conversation.js +202 -0
- package/dist/date-time-picker.d.ts +50 -0
- package/dist/date-time-picker.js +235 -0
- package/dist/date.d.ts +50 -0
- package/dist/date.js +77 -0
- package/dist/dialog.d.ts +21 -0
- package/dist/dialog.js +113 -0
- package/dist/drawer.d.ts +30 -0
- package/dist/drawer.js +140 -0
- package/dist/dropdown-menu.d.ts +56 -0
- package/dist/dropdown-menu.js +295 -0
- package/dist/empty.d.ts +11 -0
- package/dist/empty.js +51 -0
- package/dist/field.d.ts +16 -0
- package/dist/field.js +47 -0
- package/{src/hooks/scroll/minimize-decision.ts → dist/hooks/scroll/minimize-decision.js} +3 -5
- package/dist/{types/src/hooks → hooks}/use-anchored-placement.d.ts +2 -2
- package/dist/hooks/use-anchored-placement.js +33 -0
- package/dist/{types/src/hooks → hooks}/use-minimize-on-scroll.d.ts +1 -1
- package/dist/hooks/use-minimize-on-scroll.js +59 -0
- package/dist/{types/src/hooks → hooks}/use-visual-viewport.d.ts +2 -2
- package/{src/hooks/use-visual-viewport.ts → dist/hooks/use-visual-viewport.js} +20 -35
- package/dist/hooks/viewport/types.d.ts +7 -0
- package/dist/hooks/viewport/viewport-metrics.js +13 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +80 -0
- package/dist/input-group.d.ts +17 -0
- package/dist/input-group.js +55 -0
- package/dist/input-otp.d.ts +13 -0
- package/dist/input-otp.js +36 -0
- package/dist/input.d.ts +4 -0
- package/dist/input.js +14 -0
- package/dist/item.d.ts +49 -0
- package/dist/item.js +142 -0
- package/dist/kbd.d.ts +4 -0
- package/dist/kbd.js +86 -0
- package/dist/label.d.ts +3 -0
- package/dist/label.js +12 -0
- package/dist/labeled-content.d.ts +24 -0
- package/dist/labeled-content.js +41 -0
- package/dist/{types/src/lib/language.d.ts → language.d.ts} +1 -1
- package/dist/language.js +11 -0
- package/dist/layout.d.ts +24 -0
- package/dist/layout.js +36 -0
- package/dist/{types/src/lib → lib}/anchored-placement.d.ts +27 -27
- package/dist/lib/anchored-placement.js +42 -0
- package/dist/lib/calendar-month.js +90 -0
- package/dist/lib/cx.js +6 -0
- package/dist/lib/drawer.math.js +29 -0
- package/dist/lib/floating-layers.d.ts +3 -0
- package/dist/lib/floating-layers.js +25 -0
- package/dist/{types/src/lib → lib}/haptics.d.ts +1 -1
- package/dist/lib/haptics.js +13 -0
- package/dist/lib/liquid-step.js +33 -0
- package/dist/lib/page-scale.js +58 -0
- package/dist/lib/portal.js +12 -0
- package/dist/lib/presence.js +53 -0
- package/dist/lib/range-value.js +25 -0
- package/dist/lib/render.d.ts +8 -0
- package/dist/lib/render.js +42 -0
- package/dist/lib/rolling-digit.js +83 -0
- package/dist/{types/src/lib → lib}/screen-edges.d.ts +5 -5
- package/dist/lib/screen-edges.js +30 -0
- package/dist/lib/swipe-touch-action.js +34 -0
- package/dist/lib/visual-viewport.js +27 -0
- package/dist/lib/water-drop.js +412 -0
- package/dist/lib/wheel.js +125 -0
- package/dist/list.d.ts +26 -0
- package/dist/list.js +80 -0
- package/dist/locales.d.ts +18 -0
- package/dist/locales.js +36 -0
- package/dist/marker.d.ts +13 -0
- package/dist/marker.js +33 -0
- package/dist/motion/transitions.d.ts +170 -0
- package/dist/motion/transitions.js +156 -0
- package/dist/pagination.d.ts +30 -0
- package/dist/pagination.js +117 -0
- package/dist/popover.d.ts +26 -0
- package/dist/popover.js +101 -0
- package/dist/progress.d.ts +9 -0
- package/dist/progress.js +24 -0
- package/dist/questionnaire.d.ts +46 -0
- package/dist/questionnaire.js +133 -0
- package/dist/radio-group.d.ts +21 -0
- package/dist/radio-group.js +67 -0
- package/dist/search-field.d.ts +18 -0
- package/dist/search-field.js +45 -0
- package/dist/segmented-control.d.ts +16 -0
- package/dist/segmented-control.js +25 -0
- package/dist/select.d.ts +24 -0
- package/dist/select.js +107 -0
- package/dist/separator.d.ts +9 -0
- package/dist/separator.js +17 -0
- package/dist/{types/src/lib/shapes.d.ts → shapes.d.ts} +2 -2
- package/dist/shapes.js +65 -0
- package/dist/sheet.d.ts +24 -0
- package/dist/sheet.js +141 -0
- package/dist/sidebar.d.ts +32 -0
- package/dist/sidebar.js +85 -0
- package/dist/skeleton.d.ts +3 -0
- package/dist/skeleton.js +13 -0
- package/dist/slider.d.ts +9 -0
- package/dist/slider.js +42 -0
- package/dist/slosh-slider.d.ts +21 -0
- package/dist/slosh-slider.js +205 -0
- package/dist/spinner.d.ts +3 -0
- package/dist/spinner.js +20 -0
- package/dist/stat-tile.d.ts +20 -0
- package/dist/stat-tile.js +53 -0
- package/dist/stepper.d.ts +22 -0
- package/dist/stepper.js +98 -0
- package/dist/swatch-picker.d.ts +22 -0
- package/dist/swatch-picker.js +70 -0
- package/dist/switch.d.ts +10 -0
- package/dist/switch.js +44 -0
- package/dist/tab-bar.d.ts +15 -0
- package/dist/tab-bar.js +34 -0
- package/dist/{types/src/components/table/table.d.ts → table.d.ts} +3 -3
- package/dist/table.js +93 -0
- package/dist/tabs.d.ts +32 -0
- package/dist/tabs.js +131 -0
- package/dist/text.d.ts +25 -0
- package/dist/text.js +44 -0
- package/dist/textarea.d.ts +3 -0
- package/dist/textarea.js +13 -0
- package/dist/toast.d.ts +3 -0
- package/dist/toast.js +3 -0
- package/dist/toggle-group.d.ts +20 -0
- package/dist/toggle-group.js +68 -0
- package/dist/toggle.d.ts +24 -0
- package/dist/toggle.js +37 -0
- package/dist/tokens.d.ts +1 -0
- package/dist/tooltip.d.ts +21 -0
- package/dist/tooltip.js +61 -0
- package/dist/trace.d.ts +14 -0
- package/dist/trace.js +54 -0
- package/dist/{types/src/hooks/use-dismiss.d.ts → use-dismiss.d.ts} +3 -3
- package/dist/use-dismiss.js +38 -0
- package/dist/{types/src/hooks/use-media-query.d.ts → use-media-query.d.ts} +12 -12
- package/dist/use-media-query.js +56 -0
- package/dist/use-os.d.ts +12 -0
- package/dist/use-os.js +17 -0
- package/dist/wake-slider.d.ts +15 -0
- package/dist/wake-slider.js +174 -0
- package/package.json +122 -253
- package/src/styles/code-block.css +4 -0
- package/src/styles/globals.css +5 -0
- package/src/styles/motion-controls.css +10 -0
- package/src/styles/page-scale.css +27 -0
- package/src/styles/styles.css +4 -2
- package/dist/types/src/appearance/index.d.ts +0 -2
- package/dist/types/src/appearance/theme.d.ts +0 -71
- package/dist/types/src/components/accordion/accordion.d.ts +0 -23
- package/dist/types/src/components/ambient/index.d.ts +0 -1
- package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +0 -19
- package/dist/types/src/components/button/button-styles.d.ts +0 -12
- package/dist/types/src/components/button/button.d.ts +0 -32
- package/dist/types/src/components/button/icon-button.d.ts +0 -10
- package/dist/types/src/components/button/pending-spinner.d.ts +0 -5
- package/dist/types/src/components/button/use-pending-indicator.d.ts +0 -2
- package/dist/types/src/components/chip/chip.d.ts +0 -10
- package/dist/types/src/components/code-block/code-block.d.ts +0 -13
- package/dist/types/src/components/combo-box/combo-box.d.ts +0 -9
- package/dist/types/src/components/command/command.d.ts +0 -18
- package/dist/types/src/components/compact-date-picker/calendar-month.d.ts +0 -25
- package/dist/types/src/components/compact-date-picker/compact-date-picker.d.ts +0 -30
- package/dist/types/src/components/compact-time-picker/compact-time-picker.d.ts +0 -29
- package/dist/types/src/components/compact-time-picker/time-value.d.ts +0 -18
- package/dist/types/src/components/content-unavailable-view/content-unavailable-view.d.ts +0 -12
- package/dist/types/src/components/content-view/content-view.d.ts +0 -51
- package/dist/types/src/components/content-view/index.d.ts +0 -1
- package/dist/types/src/components/context-menu/conceal-menu-source.d.ts +0 -2
- package/dist/types/src/components/context-menu/context-menu-host.d.ts +0 -2
- package/dist/types/src/components/context-menu/context-menu-store.d.ts +0 -62
- package/dist/types/src/components/context-menu/menu-navigation.d.ts +0 -6
- package/dist/types/src/components/context-menu/menu-position.d.ts +0 -21
- package/dist/types/src/components/context-menu/menu-reveal.d.ts +0 -26
- package/dist/types/src/components/context-menu/use-context-menu.d.ts +0 -14
- package/dist/types/src/components/conversation/conversation.d.ts +0 -48
- package/dist/types/src/components/conversation/index.d.ts +0 -1
- package/dist/types/src/components/date-picker/date-picker.d.ts +0 -19
- package/dist/types/src/components/date-picker/date-value.d.ts +0 -2
- package/dist/types/src/components/dialog/dialog-host.d.ts +0 -6
- package/dist/types/src/components/dialog/dialog-store.d.ts +0 -23
- package/dist/types/src/components/dialog/dialog.d.ts +0 -33
- package/dist/types/src/components/dialog/index.d.ts +0 -1
- package/dist/types/src/components/disclosure-group/disclosure-group.d.ts +0 -18
- package/dist/types/src/components/divider/divider.d.ts +0 -9
- package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +0 -15
- package/dist/types/src/components/keyboard-shortcut/keyboard-shortcut.d.ts +0 -8
- package/dist/types/src/components/labeled-content/labeled-content.d.ts +0 -22
- package/dist/types/src/components/locale-switcher/index.d.ts +0 -1
- package/dist/types/src/components/locale-switcher/locale-switcher.d.ts +0 -13
- package/dist/types/src/components/menu/menu.d.ts +0 -16
- package/dist/types/src/components/morph/index.d.ts +0 -2
- package/dist/types/src/components/morph/morph.d.ts +0 -32
- package/dist/types/src/components/morph/use-morph-disclosure.d.ts +0 -13
- package/dist/types/src/components/one-time-code/one-time-code.d.ts +0 -26
- package/dist/types/src/components/picker/picker-trigger.d.ts +0 -3
- package/dist/types/src/components/picker/picker.d.ts +0 -46
- package/dist/types/src/components/popover/anchored-surface.d.ts +0 -17
- package/dist/types/src/components/popover/index.d.ts +0 -41
- package/dist/types/src/components/popover/popover-content.d.ts +0 -32
- package/dist/types/src/components/popover/popover-fit.d.ts +0 -21
- package/dist/types/src/components/popover/popover-root.d.ts +0 -22
- package/dist/types/src/components/popover/popover-trigger.d.ts +0 -23
- package/dist/types/src/components/profile-image/profile-image.d.ts +0 -9
- package/dist/types/src/components/progress-view/progress-view.d.ts +0 -13
- package/dist/types/src/components/progress-view/use-liquid-progress.d.ts +0 -3
- package/dist/types/src/components/radio-group/radio-group.d.ts +0 -20
- package/dist/types/src/components/search-field/expanding-search-field.d.ts +0 -5
- package/dist/types/src/components/search-field/search-field.d.ts +0 -9
- package/dist/types/src/components/search-list/search-list.d.ts +0 -22
- package/dist/types/src/components/section/section.d.ts +0 -18
- package/dist/types/src/components/secure-field/secure-field.d.ts +0 -4
- package/dist/types/src/components/segmented-control/segmented-control.d.ts +0 -25
- package/dist/types/src/components/sheet/modal-environment.d.ts +0 -2
- package/dist/types/src/components/sheet/sheet-portal.d.ts +0 -5
- package/dist/types/src/components/sheet/sheet.d.ts +0 -26
- package/dist/types/src/components/sheet/use-sheet-focus.d.ts +0 -3
- package/dist/types/src/components/sidebar/sidebar.d.ts +0 -24
- package/dist/types/src/components/skeleton/skeleton.d.ts +0 -9
- package/dist/types/src/components/slider/slider.d.ts +0 -17
- package/dist/types/src/components/slosh-slider/liquid-step.d.ts +0 -6
- package/dist/types/src/components/slosh-slider/slosh-slider.d.ts +0 -21
- package/dist/types/src/components/stat-tile/index.d.ts +0 -1
- package/dist/types/src/components/stat-tile/stat-tile.d.ts +0 -20
- package/dist/types/src/components/status-indicator/status-indicator.d.ts +0 -17
- package/dist/types/src/components/stepper/rolling-digit.d.ts +0 -11
- package/dist/types/src/components/stepper/stepper.d.ts +0 -22
- package/dist/types/src/components/swatch-picker/swatch-picker.d.ts +0 -22
- package/dist/types/src/components/tab-bar/tab-bar.d.ts +0 -43
- package/dist/types/src/components/tab-view/tab-view.d.ts +0 -27
- package/dist/types/src/components/text-editor/text-editor.d.ts +0 -9
- package/dist/types/src/components/text-field/text-field.d.ts +0 -33
- package/dist/types/src/components/toast/toast-store.d.ts +0 -52
- package/dist/types/src/components/toggle/toggle.d.ts +0 -22
- package/dist/types/src/components/tooltip/tooltip-host.d.ts +0 -7
- package/dist/types/src/components/view-controller/index.d.ts +0 -1
- package/dist/types/src/components/view-controller/view-controller.d.ts +0 -21
- package/dist/types/src/components/wake-slider/wake-slider.d.ts +0 -15
- package/dist/types/src/hooks/scroll/minimize-decision.d.ts +0 -2
- package/dist/types/src/hooks/use-anchored-disclosure.d.ts +0 -9
- package/dist/types/src/hooks/use-floating-dismiss.d.ts +0 -3
- package/dist/types/src/hooks/viewport/types.d.ts +0 -19
- package/dist/types/src/hooks/viewport/viewport-metrics.d.ts +0 -3
- package/dist/types/src/icons/icons.d.ts +0 -616
- package/dist/types/src/index.d.ts +0 -73
- package/dist/types/src/lib/class-names.d.ts +0 -2
- package/dist/types/src/lib/date.d.ts +0 -50
- package/dist/types/src/lib/floating-layers.d.ts +0 -8
- package/dist/types/src/lib/range-value.d.ts +0 -4
- package/dist/types/src/machines/popover.machine.d.ts +0 -30
- package/dist/types/src/motion/transitions.d.ts +0 -170
- package/src/appearance/index.ts +0 -2
- package/src/appearance/platform.tsx +0 -102
- package/src/appearance/theme.tsx +0 -467
- package/src/components/accordion/accordion.tsx +0 -70
- package/src/components/ambient/ambient.tsx +0 -35
- package/src/components/ambient/index.ts +0 -1
- package/src/components/aspect-ratio/aspect-ratio.tsx +0 -27
- package/src/components/breadcrumb/breadcrumb.tsx +0 -73
- package/src/components/button/button-styles.ts +0 -49
- package/src/components/button/button.tsx +0 -107
- package/src/components/button/icon-button.tsx +0 -33
- package/src/components/button/pending-spinner.tsx +0 -14
- package/src/components/button/use-pending-indicator.ts +0 -22
- package/src/components/chip/chip.tsx +0 -45
- package/src/components/code-block/code-block.tsx +0 -48
- package/src/components/combo-box/combo-box.tsx +0 -13
- package/src/components/command/command.tsx +0 -149
- package/src/components/compact-date-picker/calendar-month.ts +0 -109
- package/src/components/compact-date-picker/compact-date-picker.tsx +0 -337
- package/src/components/compact-time-picker/compact-time-picker.tsx +0 -321
- package/src/components/compact-time-picker/time-value.ts +0 -58
- package/src/components/content-unavailable-view/content-unavailable-view.tsx +0 -78
- package/src/components/content-view/content-view.tsx +0 -215
- package/src/components/content-view/index.ts +0 -1
- package/src/components/context-menu/conceal-menu-source.ts +0 -30
- package/src/components/context-menu/context-menu-host.tsx +0 -459
- package/src/components/context-menu/context-menu-store.ts +0 -167
- package/src/components/context-menu/menu-navigation.ts +0 -36
- package/src/components/context-menu/menu-position.ts +0 -38
- package/src/components/context-menu/menu-reveal.ts +0 -42
- package/src/components/context-menu/use-context-menu.ts +0 -73
- package/src/components/conversation/conversation.tsx +0 -309
- package/src/components/conversation/index.ts +0 -1
- package/src/components/date-picker/date-picker.tsx +0 -106
- package/src/components/date-picker/date-value.ts +0 -22
- package/src/components/dialog/dialog-host.tsx +0 -119
- package/src/components/dialog/dialog-store.ts +0 -66
- package/src/components/dialog/dialog.tsx +0 -211
- package/src/components/dialog/index.ts +0 -1
- package/src/components/disclosure-group/disclosure-group.tsx +0 -108
- package/src/components/divider/divider.tsx +0 -33
- package/src/components/inline-disclosure/inline-disclosure.tsx +0 -53
- package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +0 -32
- package/src/components/labeled-content/labeled-content.tsx +0 -84
- package/src/components/locale-switcher/index.ts +0 -1
- package/src/components/locale-switcher/locale-switcher.tsx +0 -311
- package/src/components/menu/menu.tsx +0 -85
- package/src/components/morph/index.ts +0 -2
- package/src/components/morph/morph.tsx +0 -258
- package/src/components/morph/use-morph-disclosure.ts +0 -27
- package/src/components/one-time-code/one-time-code.tsx +0 -234
- package/src/components/picker/picker-trigger.ts +0 -22
- package/src/components/picker/picker.tsx +0 -404
- package/src/components/popover/anchored-surface.tsx +0 -264
- package/src/components/popover/index.ts +0 -50
- package/src/components/popover/popover-content.tsx +0 -334
- package/src/components/popover/popover-fit.ts +0 -74
- package/src/components/popover/popover-root.tsx +0 -97
- package/src/components/popover/popover-trigger.tsx +0 -71
- package/src/components/profile-image/profile-image.tsx +0 -96
- package/src/components/progress-view/progress-view.tsx +0 -72
- package/src/components/progress-view/use-liquid-progress.ts +0 -43
- package/src/components/radio-group/radio-group.tsx +0 -86
- package/src/components/search-field/expanding-search-field.tsx +0 -197
- package/src/components/search-field/search-field.tsx +0 -66
- package/src/components/search-list/search-list.tsx +0 -276
- package/src/components/section/section.tsx +0 -75
- package/src/components/secure-field/secure-field.tsx +0 -8
- package/src/components/segmented-control/segmented-control.tsx +0 -157
- package/src/components/sheet/modal-environment.ts +0 -82
- package/src/components/sheet/sheet-portal.tsx +0 -62
- package/src/components/sheet/sheet.tsx +0 -165
- package/src/components/sheet/use-sheet-focus.ts +0 -109
- package/src/components/sidebar/sidebar.tsx +0 -102
- package/src/components/skeleton/skeleton.tsx +0 -38
- package/src/components/slider/slider.tsx +0 -134
- package/src/components/slosh-slider/liquid-step.ts +0 -43
- package/src/components/slosh-slider/slosh-slider.tsx +0 -294
- package/src/components/stat-tile/index.ts +0 -1
- package/src/components/stat-tile/stat-tile.tsx +0 -90
- package/src/components/status-indicator/status-indicator.tsx +0 -54
- package/src/components/stepper/rolling-digit.tsx +0 -91
- package/src/components/stepper/stepper.tsx +0 -136
- package/src/components/swatch-picker/swatch-picker.tsx +0 -138
- package/src/components/tab-bar/tab-bar.tsx +0 -182
- package/src/components/tab-view/tab-view.tsx +0 -155
- package/src/components/table/table.tsx +0 -151
- package/src/components/text-editor/text-editor.tsx +0 -42
- package/src/components/text-field/text-field.tsx +0 -200
- package/src/components/toast/toast-store.ts +0 -164
- package/src/components/toast/toaster.tsx +0 -407
- package/src/components/toggle/toggle.tsx +0 -320
- package/src/components/tooltip/tooltip-host.tsx +0 -178
- package/src/components/view-controller/index.ts +0 -1
- package/src/components/view-controller/view-controller.tsx +0 -75
- package/src/components/wake-slider/wake-slider.tsx +0 -260
- package/src/hooks/use-anchored-disclosure.ts +0 -23
- package/src/hooks/use-anchored-placement.ts +0 -40
- package/src/hooks/use-dismiss.ts +0 -44
- package/src/hooks/use-floating-dismiss.ts +0 -30
- package/src/hooks/use-media-query.ts +0 -94
- package/src/hooks/use-minimize-on-scroll.ts +0 -77
- package/src/hooks/viewport/types.ts +0 -19
- package/src/hooks/viewport/viewport-metrics.ts +0 -19
- package/src/icons/icons.tsx +0 -458
- package/src/index.ts +0 -107
- package/src/lib/anchored-placement.ts +0 -91
- package/src/lib/class-names.ts +0 -21
- package/src/lib/clipboard.ts +0 -15
- package/src/lib/date.ts +0 -86
- package/src/lib/floating-layers.ts +0 -53
- package/src/lib/haptics.ts +0 -23
- package/src/lib/language.ts +0 -16
- package/src/lib/range-value.ts +0 -38
- package/src/lib/screen-edges.ts +0 -44
- package/src/lib/shapes.ts +0 -59
- package/src/machines/popover.machine.ts +0 -51
- package/src/motion/transitions.ts +0 -97
- package/src/tokens.ts +0 -1
- /package/dist/{types/src/tokens.d.ts → tokens.js} +0 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
2
|
+
// src/lib/water-drop.ts
|
|
3
|
+
/** Fallback spring when the motion tokens are not loaded: a soft settle with a little overshoot. */
|
|
4
|
+
const OPEN_EASING = "cubic-bezier(0.2, 0.9, 0.25, 1.12)";
|
|
5
|
+
const OPEN_MS = 480;
|
|
6
|
+
/** Closing is quicker and never overshoots, so the glass does not shrink past its trigger. */
|
|
7
|
+
const CLOSE_EASING = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
8
|
+
const CLOSE_MS = 320;
|
|
9
|
+
/** The trigger's own face gives way as the drop leaves it. */
|
|
10
|
+
const HIDE_MS = 140;
|
|
11
|
+
/** Frames to wait for the positioner to place the panel before measuring it anyway. */
|
|
12
|
+
const SETTLE_FRAMES = 8;
|
|
13
|
+
/** Whether the browser can run the morph. jsdom, old engines and Reduce Motion cannot or should not. */
|
|
14
|
+
function canMorph() {
|
|
15
|
+
if (typeof window === "undefined") return false;
|
|
16
|
+
if (typeof Element === "undefined") return false;
|
|
17
|
+
if (typeof Element.prototype.animate !== "function") return false;
|
|
18
|
+
return !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
19
|
+
}
|
|
20
|
+
const OPPOSITE = {
|
|
21
|
+
top: "bottom",
|
|
22
|
+
bottom: "top",
|
|
23
|
+
left: "right",
|
|
24
|
+
right: "left"
|
|
25
|
+
};
|
|
26
|
+
const isSide = (value) => value === "top" || value === "bottom" || value === "left" || value === "right";
|
|
27
|
+
/**
|
|
28
|
+
* Positioning for a panel that opens on its trigger instead of beside it. The
|
|
29
|
+
* water drop swells out of the trigger, so the panel's near edge belongs on the
|
|
30
|
+
* trigger's near edge, not a gap away: iOS lays the menu over the button and
|
|
31
|
+
* grows it from there. Pass the gap the machine adds so it cancels out.
|
|
32
|
+
*
|
|
33
|
+
* Works on all four sides: above and below the anchor is a flat line on the
|
|
34
|
+
* trigger's top or bottom edge; left and right it is a line on the left or
|
|
35
|
+
* right edge, as tall as the trigger so start and end alignment still hold.
|
|
36
|
+
*
|
|
37
|
+
* Give it to a machine's `positioning.getAnchorRect`. It stands down (`null`
|
|
38
|
+
* means the trigger's own rectangle) wherever the morph does not run, so the
|
|
39
|
+
* panel then sits beside the trigger as usual.
|
|
40
|
+
*/
|
|
41
|
+
function anchorOnTrigger(placement, gap, state) {
|
|
42
|
+
return (element) => {
|
|
43
|
+
const asked = placement.split("-")[0];
|
|
44
|
+
if (!element || !isSide(asked) || !canMorph()) return null;
|
|
45
|
+
const side = state?.flipped ? OPPOSITE[asked] : asked;
|
|
46
|
+
const rect = element.getBoundingClientRect();
|
|
47
|
+
switch (side) {
|
|
48
|
+
case "bottom": {
|
|
49
|
+
const shift = state?.shift?.() ?? 0;
|
|
50
|
+
return {
|
|
51
|
+
x: rect.left,
|
|
52
|
+
y: rect.top - gap + shift,
|
|
53
|
+
width: rect.width,
|
|
54
|
+
height: 0
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
case "top": return {
|
|
58
|
+
x: rect.left,
|
|
59
|
+
y: rect.bottom + gap,
|
|
60
|
+
width: rect.width,
|
|
61
|
+
height: 0
|
|
62
|
+
};
|
|
63
|
+
case "right": return {
|
|
64
|
+
x: rect.left - gap,
|
|
65
|
+
y: rect.top,
|
|
66
|
+
width: 0,
|
|
67
|
+
height: rect.height
|
|
68
|
+
};
|
|
69
|
+
case "left": return {
|
|
70
|
+
x: rect.right + gap,
|
|
71
|
+
y: rect.top,
|
|
72
|
+
width: 0,
|
|
73
|
+
height: rect.height
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** The side a machine placed its panel on, from the positioner's props. */
|
|
79
|
+
function positionerSide(props) {
|
|
80
|
+
const side = props["data-side"];
|
|
81
|
+
return typeof side === "string" ? side : void 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `anchorOnTrigger` that survives a flip. The machine decides the side after the
|
|
85
|
+
* anchor is read, so a panel pushed to the other side of the trigger would float
|
|
86
|
+
* a gap away from it. Pass the result to `useAnchorFollow` once the machine's
|
|
87
|
+
* api exists.
|
|
88
|
+
*/
|
|
89
|
+
function useTriggerAnchor(placement, gap) {
|
|
90
|
+
return useMemo(() => {
|
|
91
|
+
const state = {};
|
|
92
|
+
return {
|
|
93
|
+
getAnchorRect: anchorOnTrigger(placement, gap, state),
|
|
94
|
+
state,
|
|
95
|
+
base: placement.split("-")[0] ?? placement
|
|
96
|
+
};
|
|
97
|
+
}, [placement, gap]);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Re-aims the anchor at the trigger's opposite edge the first time the machine
|
|
101
|
+
* opens on the other side, then repositions. The panel is still hidden while
|
|
102
|
+
* the drop waits for it to settle, so the correction is never seen. It flips at
|
|
103
|
+
* most once per opening, so a panel that fits neither side cannot loop.
|
|
104
|
+
*/
|
|
105
|
+
function useAnchorFollow(anchor, open, side, reposition) {
|
|
106
|
+
const again = useRef(reposition);
|
|
107
|
+
again.current = reposition;
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (!open) {
|
|
110
|
+
anchor.state.flipped = false;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (anchor.state.flipped || !isSide(side) || side === anchor.base) return;
|
|
114
|
+
anchor.state.flipped = true;
|
|
115
|
+
again.current();
|
|
116
|
+
}, [
|
|
117
|
+
anchor,
|
|
118
|
+
open,
|
|
119
|
+
side
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* How far to move a downward list so its selected option lands on the trigger's
|
|
124
|
+
* value, as macOS and iOS pickers do. Measured from the open list, which is
|
|
125
|
+
* already in the DOM when the machine positions it. Returns 0 (the list sits
|
|
126
|
+
* below the trigger) when nothing is selected or the list scrolls, because a
|
|
127
|
+
* scrolled list cannot keep the option and the trigger together.
|
|
128
|
+
*/
|
|
129
|
+
function alignSelectedOption(trigger, list, selector = "[role=\"option\"][data-state=\"checked\"]") {
|
|
130
|
+
const option = list?.querySelector(selector);
|
|
131
|
+
if (!trigger || !list || !option) return 0;
|
|
132
|
+
if (list.scrollHeight > list.clientHeight + 1) return 0;
|
|
133
|
+
const optionCenter = option.offsetTop - list.offsetTop + option.offsetHeight / 2;
|
|
134
|
+
return trigger.getBoundingClientRect().height / 2 - optionCenter;
|
|
135
|
+
}
|
|
136
|
+
/** Reads an element's viewport rectangle and the radius it is drawn with, capped to a full pill. */
|
|
137
|
+
function readBox(el) {
|
|
138
|
+
const rect = el.getBoundingClientRect();
|
|
139
|
+
const corner = getComputedStyle(el).borderTopLeftRadius;
|
|
140
|
+
const pill = Math.min(rect.width, rect.height) / 2;
|
|
141
|
+
const raw = Number.parseFloat(corner);
|
|
142
|
+
const radius = corner.includes("%") ? pill : Number.isFinite(raw) ? raw : 0;
|
|
143
|
+
return {
|
|
144
|
+
left: rect.left,
|
|
145
|
+
top: rect.top,
|
|
146
|
+
width: rect.width,
|
|
147
|
+
height: rect.height,
|
|
148
|
+
radius: Math.min(radius, pill)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** One animation frame of the drop: where it sits, how big it is and how round. */
|
|
152
|
+
function boxKeyframe(box) {
|
|
153
|
+
return {
|
|
154
|
+
transform: `translate(${box.left}px, ${box.top}px)`,
|
|
155
|
+
width: `${box.width}px`,
|
|
156
|
+
height: `${box.height}px`,
|
|
157
|
+
borderRadius: `${box.radius}px`
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** Converts a CSS time such as `0.5s` or `450ms` to milliseconds. */
|
|
161
|
+
function toMs(value, fallback) {
|
|
162
|
+
const text = value.trim();
|
|
163
|
+
const n = Number.parseFloat(text);
|
|
164
|
+
if (!Number.isFinite(n)) return fallback;
|
|
165
|
+
return text.endsWith("ms") ? n : text.endsWith("s") ? n * 1e3 : fallback;
|
|
166
|
+
}
|
|
167
|
+
/** The smooth spring from the motion tokens, falling back when they are absent. */
|
|
168
|
+
function openSpring(el) {
|
|
169
|
+
const style = getComputedStyle(el);
|
|
170
|
+
return {
|
|
171
|
+
easing: style.getPropertyValue("--spring-smooth").trim() || OPEN_EASING,
|
|
172
|
+
ms: toMs(style.getPropertyValue("--spring-smooth-duration"), OPEN_MS)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Glass that grows from the source. It sits just under the panel's positioner, above any backdrop. */
|
|
176
|
+
function makeGhost(panel, material) {
|
|
177
|
+
const positioner = panel.parentElement;
|
|
178
|
+
const ghost = document.createElement("div");
|
|
179
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
180
|
+
ghost.setAttribute("data-slot", "water-drop");
|
|
181
|
+
ghost.className = material;
|
|
182
|
+
Object.assign(ghost.style, {
|
|
183
|
+
position: "fixed",
|
|
184
|
+
left: "0",
|
|
185
|
+
top: "0",
|
|
186
|
+
margin: "0",
|
|
187
|
+
boxSizing: "border-box",
|
|
188
|
+
pointerEvents: "none",
|
|
189
|
+
zIndex: getComputedStyle(positioner).zIndex
|
|
190
|
+
});
|
|
191
|
+
positioner.parentNode?.insertBefore(ghost, positioner);
|
|
192
|
+
return ghost;
|
|
193
|
+
}
|
|
194
|
+
/** Half the size of the bead of glass a point origin starts as and lands on. */
|
|
195
|
+
const POINT_RADIUS = 6;
|
|
196
|
+
/** A small round bead centered on a point, where a context menu's glass starts and lands. */
|
|
197
|
+
function pointBox(x, y) {
|
|
198
|
+
return {
|
|
199
|
+
left: x - POINT_RADIUS,
|
|
200
|
+
top: y - POINT_RADIUS,
|
|
201
|
+
width: 12,
|
|
202
|
+
height: 12,
|
|
203
|
+
radius: POINT_RADIUS
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The water drop. A panel does not pop in at a distance from its trigger: the
|
|
208
|
+
* trigger's own glass swells into the panel, and on close it drops back into
|
|
209
|
+
* the trigger, like the three dot menu in iOS Notes.
|
|
210
|
+
*
|
|
211
|
+
* Only a separate layer of glass changes size, so the panel's content never
|
|
212
|
+
* stretches. The content fades in once the glass has nearly arrived and out
|
|
213
|
+
* before it leaves. Pass the returned hooks to `usePresence`.
|
|
214
|
+
*/
|
|
215
|
+
function useWaterDrop(options) {
|
|
216
|
+
const latest = useRef(options);
|
|
217
|
+
latest.current = options;
|
|
218
|
+
const controller = useMemo(() => createController(latest), []);
|
|
219
|
+
useEffect(() => controller.dispose, [controller]);
|
|
220
|
+
return controller;
|
|
221
|
+
}
|
|
222
|
+
function createController(latest) {
|
|
223
|
+
let animations = [];
|
|
224
|
+
let ghosts = [];
|
|
225
|
+
let frame = 0;
|
|
226
|
+
/** The element the glass grows from, when there is one. */
|
|
227
|
+
const source = () => {
|
|
228
|
+
const id = latest.current.sourceId;
|
|
229
|
+
return id ? document.getElementById(id) : null;
|
|
230
|
+
};
|
|
231
|
+
/** Stops everything this controller started, which also hands the trigger back. */
|
|
232
|
+
const reset = () => {
|
|
233
|
+
cancelAnimationFrame(frame);
|
|
234
|
+
frame = 0;
|
|
235
|
+
for (const animation of animations) animation.cancel();
|
|
236
|
+
animations = [];
|
|
237
|
+
for (const ghost of ghosts) ghost.remove();
|
|
238
|
+
ghosts = [];
|
|
239
|
+
};
|
|
240
|
+
const track = (animation) => {
|
|
241
|
+
animations.push(animation);
|
|
242
|
+
return animation;
|
|
243
|
+
};
|
|
244
|
+
/** Where the glass starts and lands: the source's box, or a flat line on its bottom edge. */
|
|
245
|
+
const sourceBox = (from) => {
|
|
246
|
+
const box = readBox(from);
|
|
247
|
+
return latest.current.keepSource ? {
|
|
248
|
+
...box,
|
|
249
|
+
top: box.top + box.height,
|
|
250
|
+
height: 0
|
|
251
|
+
} : box;
|
|
252
|
+
};
|
|
253
|
+
/** Where the glass starts and lands: the source element, or else a bead at the point. */
|
|
254
|
+
const origin = () => {
|
|
255
|
+
const el = source();
|
|
256
|
+
if (el) return {
|
|
257
|
+
el,
|
|
258
|
+
box: sourceBox(el)
|
|
259
|
+
};
|
|
260
|
+
const at = latest.current.point?.();
|
|
261
|
+
return at ? {
|
|
262
|
+
el: null,
|
|
263
|
+
box: pointBox(at.x, at.y)
|
|
264
|
+
} : null;
|
|
265
|
+
};
|
|
266
|
+
/** Drops the ghost once its fade ends. A cancelled fade rejects, which is fine. */
|
|
267
|
+
const retireOnFinish = (fade, ghost) => {
|
|
268
|
+
fade.finished.then(() => {
|
|
269
|
+
ghost.remove();
|
|
270
|
+
ghosts = ghosts.filter((g) => g !== ghost);
|
|
271
|
+
}, () => {});
|
|
272
|
+
};
|
|
273
|
+
const open = (panel, from) => {
|
|
274
|
+
const { easing, ms } = openSpring(panel);
|
|
275
|
+
const content = track(panel.animate([
|
|
276
|
+
{ opacity: 0 },
|
|
277
|
+
{
|
|
278
|
+
opacity: 0,
|
|
279
|
+
offset: .45
|
|
280
|
+
},
|
|
281
|
+
{ opacity: 1 }
|
|
282
|
+
], {
|
|
283
|
+
duration: ms,
|
|
284
|
+
easing: "linear",
|
|
285
|
+
fill: "both"
|
|
286
|
+
}));
|
|
287
|
+
content.pause();
|
|
288
|
+
let previous = "";
|
|
289
|
+
let frames = 0;
|
|
290
|
+
const settle = () => {
|
|
291
|
+
frame = 0;
|
|
292
|
+
const rect = panel.getBoundingClientRect();
|
|
293
|
+
const key = `${rect.left},${rect.top},${rect.width},${rect.height}`;
|
|
294
|
+
if (!(rect.width > 0 && rect.bottom > 0 && key === previous) && ++frames < SETTLE_FRAMES) {
|
|
295
|
+
previous = key;
|
|
296
|
+
frame = requestAnimationFrame(settle);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
const ghost = makeGhost(panel, latest.current.material);
|
|
301
|
+
ghosts.push(ghost);
|
|
302
|
+
track(ghost.animate([boxKeyframe(from.box), boxKeyframe(readBox(panel))], {
|
|
303
|
+
duration: ms,
|
|
304
|
+
easing,
|
|
305
|
+
fill: "forwards"
|
|
306
|
+
}));
|
|
307
|
+
const fade = track(ghost.animate([
|
|
308
|
+
{ opacity: 1 },
|
|
309
|
+
{
|
|
310
|
+
opacity: 1,
|
|
311
|
+
offset: .7
|
|
312
|
+
},
|
|
313
|
+
{ opacity: 0 }
|
|
314
|
+
], {
|
|
315
|
+
duration: ms,
|
|
316
|
+
easing: "linear",
|
|
317
|
+
fill: "forwards"
|
|
318
|
+
}));
|
|
319
|
+
retireOnFinish(fade, ghost);
|
|
320
|
+
if (from.el && !latest.current.keepSource) track(from.el.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
321
|
+
duration: HIDE_MS,
|
|
322
|
+
easing: "ease-out",
|
|
323
|
+
fill: "forwards"
|
|
324
|
+
}));
|
|
325
|
+
content.play();
|
|
326
|
+
content.onfinish = () => content.cancel();
|
|
327
|
+
} catch {
|
|
328
|
+
reset();
|
|
329
|
+
panel.removeAttribute("data-morph");
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
frame = requestAnimationFrame(settle);
|
|
333
|
+
};
|
|
334
|
+
return {
|
|
335
|
+
enter(panel) {
|
|
336
|
+
reset();
|
|
337
|
+
const from = origin();
|
|
338
|
+
if (!from || !canMorph()) return;
|
|
339
|
+
panel.setAttribute("data-morph", "");
|
|
340
|
+
try {
|
|
341
|
+
open(panel, from);
|
|
342
|
+
} catch {
|
|
343
|
+
reset();
|
|
344
|
+
panel.removeAttribute("data-morph");
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
exit(panel) {
|
|
348
|
+
if (!panel.hasAttribute("data-morph")) return;
|
|
349
|
+
const to = origin();
|
|
350
|
+
const live = ghosts[0];
|
|
351
|
+
const start = readBox(live ?? panel);
|
|
352
|
+
const opacity = Number.parseFloat(getComputedStyle(panel).opacity);
|
|
353
|
+
reset();
|
|
354
|
+
if (!to) {
|
|
355
|
+
panel.animate([{ opacity }, { opacity: 0 }], {
|
|
356
|
+
duration: 160,
|
|
357
|
+
fill: "forwards"
|
|
358
|
+
});
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const ghost = makeGhost(panel, latest.current.material);
|
|
363
|
+
ghosts.push(ghost);
|
|
364
|
+
track(ghost.animate([boxKeyframe(start), boxKeyframe(to.box)], {
|
|
365
|
+
duration: CLOSE_MS,
|
|
366
|
+
easing: CLOSE_EASING,
|
|
367
|
+
fill: "forwards"
|
|
368
|
+
}));
|
|
369
|
+
const fade = track(ghost.animate([
|
|
370
|
+
{ opacity: 1 },
|
|
371
|
+
{
|
|
372
|
+
opacity: 1,
|
|
373
|
+
offset: .75
|
|
374
|
+
},
|
|
375
|
+
{ opacity: 0 }
|
|
376
|
+
], {
|
|
377
|
+
duration: CLOSE_MS,
|
|
378
|
+
easing: "linear",
|
|
379
|
+
fill: "forwards"
|
|
380
|
+
}));
|
|
381
|
+
retireOnFinish(fade, ghost);
|
|
382
|
+
track(panel.animate([
|
|
383
|
+
{ opacity },
|
|
384
|
+
{
|
|
385
|
+
opacity: 0,
|
|
386
|
+
offset: .3
|
|
387
|
+
},
|
|
388
|
+
{ opacity: 0 }
|
|
389
|
+
], {
|
|
390
|
+
duration: CLOSE_MS,
|
|
391
|
+
easing: "linear",
|
|
392
|
+
fill: "forwards"
|
|
393
|
+
}));
|
|
394
|
+
if (to.el && !latest.current.keepSource) track(to.el.animate([
|
|
395
|
+
{ opacity: 0 },
|
|
396
|
+
{
|
|
397
|
+
opacity: 0,
|
|
398
|
+
offset: .7
|
|
399
|
+
},
|
|
400
|
+
{ opacity: 1 }
|
|
401
|
+
], {
|
|
402
|
+
duration: CLOSE_MS,
|
|
403
|
+
easing: "linear"
|
|
404
|
+
}));
|
|
405
|
+
} catch {
|
|
406
|
+
reset();
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
dispose: reset
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
export { alignSelectedOption, anchorOnTrigger, boxKeyframe, canMorph, pointBox, positionerSide, readBox, toMs, useAnchorFollow, useTriggerAnchor, useWaterDrop };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "./cx.js";
|
|
3
|
+
import { haptic } from "./haptics.js";
|
|
4
|
+
import { useEffect, useId, useRef } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
/**
|
|
7
|
+
* A snapping column of choices, like the wheels in iOS date pickers: scroll,
|
|
8
|
+
* flick or tap a row and it settles in the middle. Rows fade and shrink away
|
|
9
|
+
* from the middle, and each row passed ticks the haptic. Up and Down arrows,
|
|
10
|
+
* Home and End also move it, so it works without a pointer.
|
|
11
|
+
*/
|
|
12
|
+
function Wheel({ options, value, onValueChange, label, className }) {
|
|
13
|
+
const id = useId();
|
|
14
|
+
const scroller = useRef(null);
|
|
15
|
+
const frame = useRef(0);
|
|
16
|
+
const settle = useRef(0);
|
|
17
|
+
const lastTick = useRef(-1);
|
|
18
|
+
const index = Math.max(0, options.findIndex((option) => option.value === value));
|
|
19
|
+
const latest = useRef({
|
|
20
|
+
options,
|
|
21
|
+
index,
|
|
22
|
+
onValueChange
|
|
23
|
+
});
|
|
24
|
+
latest.current = {
|
|
25
|
+
options,
|
|
26
|
+
index,
|
|
27
|
+
onValueChange
|
|
28
|
+
};
|
|
29
|
+
/** Fades and shrinks each row by its distance from the middle. */
|
|
30
|
+
const shape = () => {
|
|
31
|
+
const node = scroller.current;
|
|
32
|
+
if (!node) return;
|
|
33
|
+
const middle = node.scrollTop / 32;
|
|
34
|
+
const rows = node.children;
|
|
35
|
+
const from = Math.max(0, Math.floor(middle) - 4);
|
|
36
|
+
const to = Math.min(rows.length - 1, Math.ceil(middle) + 4);
|
|
37
|
+
for (let row = from; row <= to; row++) {
|
|
38
|
+
const element = rows[row];
|
|
39
|
+
const distance = Math.abs(row - middle);
|
|
40
|
+
element.style.opacity = String(Math.max(.2, 1 - distance * .28));
|
|
41
|
+
element.style.transform = `scale(${1 - Math.min(distance, 3) * .05})`;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const goTo = (target, smooth) => {
|
|
45
|
+
scroller.current?.scrollTo({
|
|
46
|
+
top: target * 32,
|
|
47
|
+
behavior: smooth ? "smooth" : "instant"
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const node = scroller.current;
|
|
52
|
+
if (!node) return;
|
|
53
|
+
if (Math.round(node.scrollTop / 32) !== index) goTo(index, node.scrollTop > 0);
|
|
54
|
+
shape();
|
|
55
|
+
}, [index]);
|
|
56
|
+
useEffect(() => () => {
|
|
57
|
+
cancelAnimationFrame(frame.current);
|
|
58
|
+
clearTimeout(settle.current);
|
|
59
|
+
}, []);
|
|
60
|
+
const onScroll = () => {
|
|
61
|
+
cancelAnimationFrame(frame.current);
|
|
62
|
+
frame.current = requestAnimationFrame(shape);
|
|
63
|
+
const node = scroller.current;
|
|
64
|
+
if (!node) return;
|
|
65
|
+
const row = Math.round(node.scrollTop / 32);
|
|
66
|
+
if (row !== lastTick.current) {
|
|
67
|
+
if (lastTick.current !== -1) haptic("selection");
|
|
68
|
+
lastTick.current = row;
|
|
69
|
+
}
|
|
70
|
+
clearTimeout(settle.current);
|
|
71
|
+
settle.current = window.setTimeout(() => {
|
|
72
|
+
const { options: all, index: current, onValueChange: emit } = latest.current;
|
|
73
|
+
const chosen = all[Math.min(all.length - 1, Math.max(0, row))];
|
|
74
|
+
if (chosen && !chosen.disabled && row !== current) emit(chosen.value);
|
|
75
|
+
}, 90);
|
|
76
|
+
};
|
|
77
|
+
const onKeyDown = (event) => {
|
|
78
|
+
const step = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : event.key === "PageDown" ? 5 : event.key === "PageUp" ? -5 : 0;
|
|
79
|
+
const target = event.key === "Home" ? 0 : event.key === "End" ? options.length - 1 : Math.min(options.length - 1, Math.max(0, index + step));
|
|
80
|
+
if (!step && event.key !== "Home" && event.key !== "End") return;
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
const option = options[target];
|
|
83
|
+
if (option && !option.disabled) onValueChange(option.value);
|
|
84
|
+
};
|
|
85
|
+
const edge = 96;
|
|
86
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
87
|
+
"data-slot": "wheel",
|
|
88
|
+
className: cx("relative", className),
|
|
89
|
+
style: { height: 224 },
|
|
90
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
91
|
+
"aria-hidden": "true",
|
|
92
|
+
className: "bg-fill/70 pointer-events-none absolute inset-x-0 rounded-xl",
|
|
93
|
+
style: {
|
|
94
|
+
top: edge,
|
|
95
|
+
height: 32
|
|
96
|
+
}
|
|
97
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
98
|
+
ref: scroller,
|
|
99
|
+
role: "listbox",
|
|
100
|
+
tabIndex: 0,
|
|
101
|
+
"aria-label": label,
|
|
102
|
+
"aria-activedescendant": `${id}-${index}`,
|
|
103
|
+
onScroll,
|
|
104
|
+
onKeyDown,
|
|
105
|
+
className: "no-scrollbar focus-visible:ring-accent/70 relative h-full snap-y snap-mandatory overflow-y-auto overscroll-contain rounded-xl [mask-image:linear-gradient(transparent,black_28%,black_72%,transparent)] outline-none focus-visible:ring-2",
|
|
106
|
+
style: { paddingBlock: edge },
|
|
107
|
+
children: options.map((option, row) => /* @__PURE__ */ jsx("div", {
|
|
108
|
+
id: `${id}-${row}`,
|
|
109
|
+
role: "option",
|
|
110
|
+
"aria-selected": row === index,
|
|
111
|
+
"aria-disabled": option.disabled || void 0,
|
|
112
|
+
tabIndex: -1,
|
|
113
|
+
onClick: () => {
|
|
114
|
+
if (option.disabled) return;
|
|
115
|
+
onValueChange(option.value);
|
|
116
|
+
goTo(row, true);
|
|
117
|
+
},
|
|
118
|
+
className: cx("text-footnote text-label flex cursor-pointer snap-center items-center justify-center px-3 tabular-nums select-none", row === index && "font-semibold", option.disabled && "pointer-events-none opacity-30"),
|
|
119
|
+
style: { height: 32 },
|
|
120
|
+
children: option.label
|
|
121
|
+
}, option.value))
|
|
122
|
+
})]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
export { Wheel };
|
package/dist/list.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RenderProp } from "./lib/render.js";
|
|
2
|
+
import { ComponentProps, ReactNode } from "react";
|
|
3
|
+
/** iOS inset grouped list: rows on one rounded plate with hairlines inset from the leading edge. */
|
|
4
|
+
export declare function List({ className, ...props }: ComponentProps<"ul">): import("react").JSX.Element;
|
|
5
|
+
export interface ListSectionProps extends Omit<ComponentProps<"section">, "title"> {
|
|
6
|
+
/** Small caps heading above the plate. */
|
|
7
|
+
title?: ReactNode;
|
|
8
|
+
/** Quiet explanation below the plate. */
|
|
9
|
+
footer?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/** A titled group: heading, the list plate, and a footer note. */
|
|
12
|
+
export declare function ListSection({ title, footer, className, children, ...props }: ListSectionProps): import("react").JSX.Element;
|
|
13
|
+
export interface ListItemProps extends Omit<ComponentProps<"li">, "title"> {
|
|
14
|
+
/** An icon or avatar at the start. The hairline insets past it. */
|
|
15
|
+
leading?: ReactNode;
|
|
16
|
+
title: ReactNode;
|
|
17
|
+
description?: ReactNode;
|
|
18
|
+
/** A value, switch or badge at the end. */
|
|
19
|
+
trailing?: ReactNode;
|
|
20
|
+
/** Shows the disclosure chevron, for rows that open something. */
|
|
21
|
+
chevron?: boolean;
|
|
22
|
+
/** Makes the whole row your own element, such as a router link. */
|
|
23
|
+
render?: RenderProp;
|
|
24
|
+
}
|
|
25
|
+
/** One row. With `render`, `href` or `onClick` the whole row presses like iOS. */
|
|
26
|
+
export declare function ListItem({ leading, title, description, trailing, chevron, render, className, ...props }: ListItemProps): import("react").JSX.Element;
|
package/dist/list.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { cx } from "./lib/cx.js";
|
|
2
|
+
import { mergeProps, renderPart } from "./lib/render.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
// src/components/list.tsx
|
|
5
|
+
/** iOS inset grouped list: rows on one rounded plate with hairlines inset from the leading edge. */
|
|
6
|
+
function List({ className, ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
8
|
+
"data-slot": "list",
|
|
9
|
+
className: cx("inset-group squircle m-0 list-none p-0 [--squircle-radius:1.25rem]", className),
|
|
10
|
+
...props
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** A titled group: heading, the list plate, and a footer note. */
|
|
14
|
+
function ListSection({ title, footer, className, children, ...props }) {
|
|
15
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
16
|
+
"data-slot": "list-section",
|
|
17
|
+
className: cx("flex flex-col gap-1.5", className),
|
|
18
|
+
...props,
|
|
19
|
+
children: [
|
|
20
|
+
title ? /* @__PURE__ */ jsx("h3", {
|
|
21
|
+
className: "text-footnote text-label-secondary px-4 font-normal tracking-wide uppercase",
|
|
22
|
+
children: title
|
|
23
|
+
}) : null,
|
|
24
|
+
children,
|
|
25
|
+
footer ? /* @__PURE__ */ jsx("p", {
|
|
26
|
+
className: "text-footnote text-label-secondary px-4",
|
|
27
|
+
children: footer
|
|
28
|
+
}) : null
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** One row. With `render`, `href` or `onClick` the whole row presses like iOS. */
|
|
33
|
+
function ListItem({ leading, title, description, trailing, chevron, render, className, ...props }) {
|
|
34
|
+
const row = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
35
|
+
leading ? /* @__PURE__ */ jsx("span", {
|
|
36
|
+
className: "text-accent-text flex size-8 shrink-0 items-center justify-center [&_svg]:size-5",
|
|
37
|
+
children: leading
|
|
38
|
+
}) : null,
|
|
39
|
+
/* @__PURE__ */ jsxs("span", {
|
|
40
|
+
className: "flex min-w-0 flex-1 flex-col",
|
|
41
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
42
|
+
className: "text-body truncate",
|
|
43
|
+
children: title
|
|
44
|
+
}), description ? /* @__PURE__ */ jsx("span", {
|
|
45
|
+
className: "text-footnote text-label-secondary truncate",
|
|
46
|
+
children: description
|
|
47
|
+
}) : null]
|
|
48
|
+
}),
|
|
49
|
+
trailing ? /* @__PURE__ */ jsx("span", {
|
|
50
|
+
className: "text-body text-label-secondary shrink-0",
|
|
51
|
+
children: trailing
|
|
52
|
+
}) : null,
|
|
53
|
+
chevron ? /* @__PURE__ */ jsx("svg", {
|
|
54
|
+
viewBox: "0 0 16 16",
|
|
55
|
+
"aria-hidden": "true",
|
|
56
|
+
className: "text-label-tertiary size-4 shrink-0",
|
|
57
|
+
fill: "none",
|
|
58
|
+
stroke: "currentColor",
|
|
59
|
+
strokeWidth: "2",
|
|
60
|
+
strokeLinecap: "round",
|
|
61
|
+
strokeLinejoin: "round",
|
|
62
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 3 5 5-5 5" })
|
|
63
|
+
}) : null
|
|
64
|
+
] });
|
|
65
|
+
const rowClass = "relative flex min-h-11 w-full items-center gap-3 px-4 py-2 text-left text-label outline-none transition-colors duration-(--duration-instant) after:absolute after:right-0 after:bottom-0 after:left-4 after:h-px after:bg-separator";
|
|
66
|
+
const inset = leading ? "after:left-14" : "";
|
|
67
|
+
return /* @__PURE__ */ jsx("li", {
|
|
68
|
+
"data-slot": "list-item",
|
|
69
|
+
className: cx("relative [&:last-child>*]:after:hidden", className),
|
|
70
|
+
...props,
|
|
71
|
+
children: render ? renderPart("a", mergeProps({
|
|
72
|
+
className: cx(rowClass, inset, "hover:bg-fill/70 active:bg-fill"),
|
|
73
|
+
children: row
|
|
74
|
+
}, {}), render) : /* @__PURE__ */ jsx("div", {
|
|
75
|
+
className: cx(rowClass, inset),
|
|
76
|
+
children: row
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export { List, ListItem, ListSection };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type LocalesSize = "sm" | "md" | "lg";
|
|
2
|
+
export type LocalesProps<L extends string> = {
|
|
3
|
+
/** Language tags to offer, such as `["en", "fr"]`. Each is named in its own language. */
|
|
4
|
+
locales: readonly L[];
|
|
5
|
+
locale: L;
|
|
6
|
+
onSelect: (locale: L) => void;
|
|
7
|
+
/** Accessible name for the control. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Width of the control. @default "md" */
|
|
10
|
+
size?: LocalesSize;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* A language list: the current language on a globe trigger, every choice named
|
|
15
|
+
* in its own language. It sits in the page where you put it and opens like a
|
|
16
|
+
* Select, so keyboard, typeahead and dismissal come from the same parts.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Locales<L extends string>({ locales, locale, onSelect, label, size, className }: LocalesProps<L>): import("react").JSX.Element;
|
package/dist/locales.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "./lib/cx.js";
|
|
3
|
+
import { getNativeLanguageName } from "./language.js";
|
|
4
|
+
import { Select } from "./select.js";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { GlobeIcon } from "@passui/icons/globe";
|
|
8
|
+
// src/components/locales.tsx
|
|
9
|
+
const widths = {
|
|
10
|
+
sm: "w-36",
|
|
11
|
+
md: "w-44",
|
|
12
|
+
lg: "w-56"
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A language list: the current language on a globe trigger, every choice named
|
|
16
|
+
* in its own language. It sits in the page where you put it and opens like a
|
|
17
|
+
* Select, so keyboard, typeahead and dismissal come from the same parts.
|
|
18
|
+
*/
|
|
19
|
+
function Locales({ locales, locale, onSelect, label, size = "md", className }) {
|
|
20
|
+
const items = useMemo(() => Array.from(locales, (value) => ({
|
|
21
|
+
value,
|
|
22
|
+
label: getNativeLanguageName(value)
|
|
23
|
+
})), [locales]);
|
|
24
|
+
return /* @__PURE__ */ jsx(Select, {
|
|
25
|
+
"data-slot": "locales",
|
|
26
|
+
"aria-label": label,
|
|
27
|
+
icon: /* @__PURE__ */ jsx(GlobeIcon, { size: 16 }),
|
|
28
|
+
items,
|
|
29
|
+
value: locale,
|
|
30
|
+
onValueChange: (next) => {
|
|
31
|
+
if (next && next !== locale) onSelect(next);
|
|
32
|
+
},
|
|
33
|
+
className: cx(widths[size], className)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export { Locales };
|