@stevederico/skateboard-ui 3.12.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +37 -31
- package/dist/components/core/Command.js +33 -7
- package/dist/shadcn/ui/accordion.d.ts +1 -6
- package/dist/shadcn/ui/accordion.js +3 -17
- package/dist/shadcn/ui/alert-dialog.d.ts +1 -18
- package/dist/shadcn/ui/alert-dialog.js +3 -43
- package/dist/shadcn/ui/alert.d.ts +1 -13
- package/dist/shadcn/ui/alert.js +3 -28
- package/dist/shadcn/ui/aspect-ratio.d.ts +1 -4
- package/dist/shadcn/ui/aspect-ratio.js +3 -8
- package/dist/shadcn/ui/avatar.d.ts +1 -11
- package/dist/shadcn/ui/avatar.js +3 -24
- package/dist/shadcn/ui/badge.d.ts +1 -14
- package/dist/shadcn/ui/badge.js +3 -33
- package/dist/shadcn/ui/breadcrumb.d.ts +1 -10
- package/dist/shadcn/ui/breadcrumb.js +3 -37
- package/dist/shadcn/ui/button-group.d.ts +1 -13
- package/dist/shadcn/ui/button-group.js +3 -36
- package/dist/shadcn/ui/button.d.ts +1 -34
- package/dist/shadcn/ui/button.js +3 -53
- package/dist/shadcn/ui/calendar.d.ts +1 -13
- package/dist/shadcn/ui/calendar.js +3 -75
- package/dist/shadcn/ui/card.d.ts +1 -11
- package/dist/shadcn/ui/card.js +3 -25
- package/dist/shadcn/ui/checkbox.d.ts +1 -3
- package/dist/shadcn/ui/checkbox.js +3 -9
- package/dist/shadcn/ui/collapsible.d.ts +1 -5
- package/dist/shadcn/ui/collapsible.js +3 -13
- package/dist/shadcn/ui/command.d.ts +1 -19
- package/dist/shadcn/ui/command.js +3 -35
- package/dist/shadcn/ui/context-menu.d.ts +1 -29
- package/dist/shadcn/ui/context-menu.js +3 -52
- package/dist/shadcn/ui/dialog.d.ts +1 -17
- package/dist/shadcn/ui/dialog.js +3 -38
- package/dist/shadcn/ui/drawer.d.ts +1 -13
- package/dist/shadcn/ui/drawer.js +3 -196
- package/dist/shadcn/ui/dropdown-menu.d.ts +1 -29
- package/dist/shadcn/ui/dropdown-menu.js +3 -52
- package/dist/shadcn/ui/empty.d.ts +1 -14
- package/dist/shadcn/ui/empty.js +3 -33
- package/dist/shadcn/ui/field.d.ts +1 -28
- package/dist/shadcn/ui/field.js +3 -68
- package/dist/shadcn/ui/hover-card.d.ts +1 -5
- package/dist/shadcn/ui/hover-card.js +3 -14
- package/dist/shadcn/ui/input-group.d.ts +1 -28
- package/dist/shadcn/ui/input-group.js +3 -58
- package/dist/shadcn/ui/input.d.ts +1 -3
- package/dist/shadcn/ui/input.js +3 -8
- package/dist/shadcn/ui/item.d.ts +1 -34
- package/dist/shadcn/ui/item.js +3 -79
- package/dist/shadcn/ui/kbd.d.ts +1 -3
- package/dist/shadcn/ui/kbd.js +3 -9
- package/dist/shadcn/ui/label.d.ts +1 -3
- package/dist/shadcn/ui/label.js +3 -8
- package/dist/shadcn/ui/menubar.d.ts +1 -29
- package/dist/shadcn/ui/menubar.js +3 -57
- package/dist/shadcn/ui/navigation-menu.d.ts +1 -13
- package/dist/shadcn/ui/navigation-menu.js +3 -31
- package/dist/shadcn/ui/pagination.d.ts +1 -17
- package/dist/shadcn/ui/pagination.js +3 -27
- package/dist/shadcn/ui/popover.d.ts +1 -9
- package/dist/shadcn/ui/popover.js +3 -24
- package/dist/shadcn/ui/progress.d.ts +1 -7
- package/dist/shadcn/ui/progress.js +3 -20
- package/dist/shadcn/ui/radio-group.d.ts +1 -5
- package/dist/shadcn/ui/radio-group.js +3 -12
- package/dist/shadcn/ui/scroll-area.d.ts +1 -5
- package/dist/shadcn/ui/scroll-area.js +3 -12
- package/dist/shadcn/ui/select.d.ts +1 -15
- package/dist/shadcn/ui/select.js +3 -35
- package/dist/shadcn/ui/separator.d.ts +1 -3
- package/dist/shadcn/ui/separator.js +3 -8
- package/dist/shadcn/ui/sheet.d.ts +1 -14
- package/dist/shadcn/ui/sheet.js +3 -38
- package/dist/shadcn/ui/sidebar.d.ts +1 -70
- package/dist/shadcn/ui/sidebar.js +3 -256
- package/dist/shadcn/ui/skeleton.d.ts +1 -2
- package/dist/shadcn/ui/skeleton.js +3 -6
- package/dist/shadcn/ui/slider.d.ts +1 -3
- package/dist/shadcn/ui/slider.js +3 -12
- package/dist/shadcn/ui/spinner.d.ts +1 -2
- package/dist/shadcn/ui/spinner.js +3 -7
- package/dist/shadcn/ui/switch.d.ts +1 -5
- package/dist/shadcn/ui/switch.js +3 -8
- package/dist/shadcn/ui/table.d.ts +1 -10
- package/dist/shadcn/ui/table.js +3 -29
- package/dist/shadcn/ui/tabs.d.ts +1 -13
- package/dist/shadcn/ui/tabs.js +3 -29
- package/dist/shadcn/ui/textarea.d.ts +1 -3
- package/dist/shadcn/ui/textarea.js +3 -7
- package/dist/shadcn/ui/toggle-group.d.ts +1 -11
- package/dist/shadcn/ui/toggle-group.js +3 -25
- package/dist/shadcn/ui/toggle.d.ts +1 -15
- package/dist/shadcn/ui/toggle.js +3 -26
- package/dist/shadcn/ui/tooltip.d.ts +1 -6
- package/dist/shadcn/ui/tooltip.js +3 -17
- package/dist/ui/accordion.d.ts +21 -0
- package/dist/ui/accordion.js +67 -0
- package/dist/ui/alert-dialog.d.ts +32 -0
- package/dist/ui/alert-dialog.js +102 -0
- package/dist/ui/alert.d.ts +13 -0
- package/dist/ui/alert.js +28 -0
- package/dist/ui/aspect-ratio.d.ts +5 -0
- package/dist/ui/aspect-ratio.js +9 -0
- package/dist/ui/avatar.d.ts +14 -0
- package/dist/ui/avatar.js +49 -0
- package/dist/ui/badge.d.ts +22 -0
- package/dist/ui/badge.js +26 -0
- package/dist/ui/breadcrumb.d.ts +13 -0
- package/dist/ui/breadcrumb.js +29 -0
- package/dist/ui/button-group.d.ts +21 -0
- package/dist/ui/button-group.js +29 -0
- package/dist/ui/button.d.ts +38 -0
- package/dist/ui/button.js +49 -0
- package/dist/ui/calendar.d.ts +13 -0
- package/dist/ui/calendar.js +75 -0
- package/dist/ui/card.d.ts +11 -0
- package/dist/ui/card.js +25 -0
- package/dist/ui/checkbox.d.ts +17 -0
- package/dist/ui/checkbox.js +25 -0
- package/dist/ui/collapsible.d.ts +11 -0
- package/dist/ui/collapsible.js +40 -0
- package/dist/ui/command.d.ts +19 -0
- package/dist/ui/command.js +35 -0
- package/dist/ui/context-menu.d.ts +53 -0
- package/dist/ui/context-menu.js +372 -0
- package/dist/ui/dialog.d.ts +40 -0
- package/dist/ui/dialog.js +109 -0
- package/dist/ui/drawer.d.ts +36 -0
- package/dist/ui/drawer.js +243 -0
- package/dist/ui/dropdown-menu.d.ts +61 -0
- package/dist/ui/dropdown-menu.js +347 -0
- package/dist/ui/empty.d.ts +14 -0
- package/dist/ui/empty.js +33 -0
- package/dist/ui/field.d.ts +28 -0
- package/dist/ui/field.js +68 -0
- package/dist/ui/hover-card.d.ts +32 -0
- package/dist/ui/hover-card.js +119 -0
- package/dist/ui/input-group.d.ts +28 -0
- package/dist/ui/input-group.js +58 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +7 -0
- package/dist/ui/item.d.ts +41 -0
- package/dist/ui/item.js +69 -0
- package/dist/ui/kbd.d.ts +4 -0
- package/dist/ui/kbd.js +10 -0
- package/dist/ui/label.d.ts +3 -0
- package/dist/ui/label.js +8 -0
- package/dist/ui/menubar.d.ts +59 -0
- package/dist/ui/menubar.js +397 -0
- package/dist/ui/navigation-menu.d.ts +24 -0
- package/dist/ui/navigation-menu.js +203 -0
- package/dist/ui/pagination.d.ts +17 -0
- package/dist/ui/pagination.js +36 -0
- package/dist/ui/popover.d.ts +25 -0
- package/dist/ui/popover.js +126 -0
- package/dist/ui/portal.d.ts +7 -0
- package/dist/ui/portal.js +12 -0
- package/dist/ui/progress.d.ts +15 -0
- package/dist/ui/progress.js +36 -0
- package/dist/ui/radio-group.d.ts +17 -0
- package/dist/ui/radio-group.js +66 -0
- package/dist/ui/scroll-area.d.ts +9 -0
- package/dist/ui/scroll-area.js +72 -0
- package/dist/ui/select.d.ts +37 -0
- package/dist/ui/select.js +226 -0
- package/dist/ui/separator.d.ts +14 -0
- package/dist/ui/separator.js +13 -0
- package/dist/ui/sheet.d.ts +31 -0
- package/dist/ui/sheet.js +121 -0
- package/dist/ui/sidebar.d.ts +86 -0
- package/dist/ui/sidebar.js +211 -0
- package/dist/ui/skeleton.d.ts +3 -0
- package/dist/ui/skeleton.js +7 -0
- package/dist/ui/slider.d.ts +18 -0
- package/dist/ui/slider.js +94 -0
- package/dist/ui/slot.d.ts +27 -0
- package/dist/ui/slot.js +62 -0
- package/dist/ui/spinner.d.ts +3 -0
- package/dist/ui/spinner.js +8 -0
- package/dist/ui/switch.d.ts +17 -0
- package/dist/ui/switch.js +23 -0
- package/dist/ui/table.d.ts +10 -0
- package/dist/ui/table.js +29 -0
- package/dist/ui/tabs.d.ts +29 -0
- package/dist/ui/tabs.js +96 -0
- package/dist/ui/textarea.d.ts +3 -0
- package/dist/ui/textarea.js +7 -0
- package/dist/ui/toggle-group.d.ts +23 -0
- package/dist/ui/toggle-group.js +52 -0
- package/dist/ui/toggle.d.ts +25 -0
- package/dist/ui/toggle.js +41 -0
- package/dist/ui/tooltip.d.ts +25 -0
- package/dist/ui/tooltip.js +90 -0
- package/dist/ui/use-controllable-state.d.ts +10 -0
- package/dist/ui/use-controllable-state.js +17 -0
- package/dist/ui/use-dismiss.d.ts +10 -0
- package/dist/ui/use-dismiss.js +51 -0
- package/dist/ui/use-floating.d.ts +42 -0
- package/dist/ui/use-floating.js +136 -0
- package/dist/ui/use-presence.d.ts +8 -0
- package/dist/ui/use-presence.js +48 -0
- package/dist/ui/use-typeahead.d.ts +19 -0
- package/dist/ui/use-typeahead.js +64 -0
- package/package.json +6 -1
- package/dist/shadcn/lib/base-ui/LICENSE +0 -21
- package/dist/shadcn/lib/base-ui/_chunk-000kmre8.js +0 -150
- package/dist/shadcn/lib/base-ui/_chunk-1vw45v38.js +0 -64
- package/dist/shadcn/lib/base-ui/_chunk-23rx7haq.js +0 -81
- package/dist/shadcn/lib/base-ui/_chunk-242gh8ph.js +0 -230
- package/dist/shadcn/lib/base-ui/_chunk-26cc610z.js +0 -6
- package/dist/shadcn/lib/base-ui/_chunk-2z044bba.js +0 -5890
- package/dist/shadcn/lib/base-ui/_chunk-3cpd1vjz.js +0 -13
- package/dist/shadcn/lib/base-ui/_chunk-3enq1vat.js +0 -82
- package/dist/shadcn/lib/base-ui/_chunk-3xpke33f.js +0 -23
- package/dist/shadcn/lib/base-ui/_chunk-451nqgsa.js +0 -6
- package/dist/shadcn/lib/base-ui/_chunk-4maf6pas.js +0 -11
- package/dist/shadcn/lib/base-ui/_chunk-4zhfq4d3.js +0 -16
- package/dist/shadcn/lib/base-ui/_chunk-5gaqyne5.js +0 -21
- package/dist/shadcn/lib/base-ui/_chunk-5tt5hk59.js +0 -4
- package/dist/shadcn/lib/base-ui/_chunk-5tze5c8q.js +0 -13
- package/dist/shadcn/lib/base-ui/_chunk-5xmdvndx.js +0 -225
- package/dist/shadcn/lib/base-ui/_chunk-6adaer33.js +0 -435
- package/dist/shadcn/lib/base-ui/_chunk-6ejf1z1r.js +0 -9
- package/dist/shadcn/lib/base-ui/_chunk-6kqramh9.js +0 -27
- package/dist/shadcn/lib/base-ui/_chunk-7b9dc493.js +0 -14
- package/dist/shadcn/lib/base-ui/_chunk-8a9vv8am.js +0 -89
- package/dist/shadcn/lib/base-ui/_chunk-8ctgmf06.js +0 -129
- package/dist/shadcn/lib/base-ui/_chunk-8ganmsbg.js +0 -54
- package/dist/shadcn/lib/base-ui/_chunk-8xaqxgck.js +0 -15
- package/dist/shadcn/lib/base-ui/_chunk-95tf2rxq.js +0 -62
- package/dist/shadcn/lib/base-ui/_chunk-9x63vfqj.js +0 -70
- package/dist/shadcn/lib/base-ui/_chunk-ase0ydtt.js +0 -9
- package/dist/shadcn/lib/base-ui/_chunk-b0nc8wq7.js +0 -34
- package/dist/shadcn/lib/base-ui/_chunk-b5ya6xds.js +0 -10
- package/dist/shadcn/lib/base-ui/_chunk-b6dkjkbw.js +0 -43
- package/dist/shadcn/lib/base-ui/_chunk-c2bz952e.js +0 -19
- package/dist/shadcn/lib/base-ui/_chunk-cdgfsr3q.js +0 -15
- package/dist/shadcn/lib/base-ui/_chunk-cdj8cpx5.js +0 -347
- package/dist/shadcn/lib/base-ui/_chunk-cgptgywc.js +0 -104
- package/dist/shadcn/lib/base-ui/_chunk-cwvtvwc7.js +0 -35
- package/dist/shadcn/lib/base-ui/_chunk-dan0mva4.js +0 -23
- package/dist/shadcn/lib/base-ui/_chunk-dzvjwv25.js +0 -0
- package/dist/shadcn/lib/base-ui/_chunk-e13rsb6b.js +0 -184
- package/dist/shadcn/lib/base-ui/_chunk-e56mpvk1.js +0 -118
- package/dist/shadcn/lib/base-ui/_chunk-e6k2ecs7.js +0 -48
- package/dist/shadcn/lib/base-ui/_chunk-ebyxgtb4.js +0 -29
- package/dist/shadcn/lib/base-ui/_chunk-f2wttwrf.js +0 -39
- package/dist/shadcn/lib/base-ui/_chunk-fdzctasg.js +0 -38
- package/dist/shadcn/lib/base-ui/_chunk-ffpjy0ta.js +0 -39
- package/dist/shadcn/lib/base-ui/_chunk-fqry7pew.js +0 -603
- package/dist/shadcn/lib/base-ui/_chunk-ghn1tzt8.js +0 -84
- package/dist/shadcn/lib/base-ui/_chunk-gy0bpkmx.js +0 -12
- package/dist/shadcn/lib/base-ui/_chunk-hm5h9vsk.js +0 -16
- package/dist/shadcn/lib/base-ui/_chunk-htdj1jps.js +0 -833
- package/dist/shadcn/lib/base-ui/_chunk-j29xjete.js +0 -146
- package/dist/shadcn/lib/base-ui/_chunk-j3qkyd10.js +0 -50
- package/dist/shadcn/lib/base-ui/_chunk-ktvsd5k6.js +0 -2386
- package/dist/shadcn/lib/base-ui/_chunk-kw8nnq00.js +0 -546
- package/dist/shadcn/lib/base-ui/_chunk-m307wpdj.js +0 -18
- package/dist/shadcn/lib/base-ui/_chunk-mnd0j7v9.js +0 -15
- package/dist/shadcn/lib/base-ui/_chunk-n11h505c.js +0 -32
- package/dist/shadcn/lib/base-ui/_chunk-p0e2qacz.js +0 -26
- package/dist/shadcn/lib/base-ui/_chunk-pv7b791x.js +0 -106
- package/dist/shadcn/lib/base-ui/_chunk-q5cg71p7.js +0 -83
- package/dist/shadcn/lib/base-ui/_chunk-qbezxj1g.js +0 -385
- package/dist/shadcn/lib/base-ui/_chunk-qmsqz6nt.js +0 -15
- package/dist/shadcn/lib/base-ui/_chunk-rrh8rt4v.js +0 -65
- package/dist/shadcn/lib/base-ui/_chunk-s5pwkz8v.js +0 -25
- package/dist/shadcn/lib/base-ui/_chunk-svxv97ph.js +0 -218
- package/dist/shadcn/lib/base-ui/_chunk-t7ppm3t0.js +0 -68
- package/dist/shadcn/lib/base-ui/_chunk-vg16xxmb.js +0 -52
- package/dist/shadcn/lib/base-ui/_chunk-vgkfmtbe.js +0 -21
- package/dist/shadcn/lib/base-ui/_chunk-watha94s.js +0 -14
- package/dist/shadcn/lib/base-ui/_chunk-wdqynnjf.js +0 -35
- package/dist/shadcn/lib/base-ui/_chunk-wx2nxg0p.js +0 -15
- package/dist/shadcn/lib/base-ui/_chunk-x11e1k9r.js +0 -0
- package/dist/shadcn/lib/base-ui/_chunk-x8xehj6d.js +0 -302
- package/dist/shadcn/lib/base-ui/_chunk-xcqbtm2f.js +0 -28
- package/dist/shadcn/lib/base-ui/_chunk-ytnp24gq.js +0 -328
- package/dist/shadcn/lib/base-ui/_chunk-yzr7eg4b.js +0 -6
- package/dist/shadcn/lib/base-ui/_chunk-zk4mtm9m.js +0 -9
- package/dist/shadcn/lib/base-ui/accordion.d.ts +0 -447
- package/dist/shadcn/lib/base-ui/accordion.js +0 -630
- package/dist/shadcn/lib/base-ui/alert-dialog.d.ts +0 -1467
- package/dist/shadcn/lib/base-ui/alert-dialog.js +0 -98
- package/dist/shadcn/lib/base-ui/avatar.d.ts +0 -123
- package/dist/shadcn/lib/base-ui/avatar.js +0 -235
- package/dist/shadcn/lib/base-ui/button.d.ts +0 -84
- package/dist/shadcn/lib/base-ui/button.js +0 -51
- package/dist/shadcn/lib/base-ui/checkbox.d.ts +0 -341
- package/dist/shadcn/lib/base-ui/checkbox.js +0 -501
- package/dist/shadcn/lib/base-ui/collapsible.d.ts +0 -291
- package/dist/shadcn/lib/base-ui/collapsible.js +0 -264
- package/dist/shadcn/lib/base-ui/context-menu.d.ts +0 -2213
- package/dist/shadcn/lib/base-ui/context-menu.js +0 -323
- package/dist/shadcn/lib/base-ui/dialog.d.ts +0 -1448
- package/dist/shadcn/lib/base-ui/dialog.js +0 -79
- package/dist/shadcn/lib/base-ui/input.d.ts +0 -506
- package/dist/shadcn/lib/base-ui/input.js +0 -1027
- package/dist/shadcn/lib/base-ui/menu.d.ts +0 -2275
- package/dist/shadcn/lib/base-ui/menu.js +0 -60
- package/dist/shadcn/lib/base-ui/menubar.d.ts +0 -48
- package/dist/shadcn/lib/base-ui/menubar.js +0 -156
- package/dist/shadcn/lib/base-ui/merge-props.d.ts +0 -70
- package/dist/shadcn/lib/base-ui/merge-props.js +0 -15
- package/dist/shadcn/lib/base-ui/navigation-menu.d.ts +0 -825
- package/dist/shadcn/lib/base-ui/navigation-menu.js +0 -1849
- package/dist/shadcn/lib/base-ui/popover.d.ts +0 -1743
- package/dist/shadcn/lib/base-ui/popover.js +0 -1089
- package/dist/shadcn/lib/base-ui/preview-card.d.ts +0 -1608
- package/dist/shadcn/lib/base-ui/preview-card.js +0 -919
- package/dist/shadcn/lib/base-ui/progress.d.ts +0 -172
- package/dist/shadcn/lib/base-ui/progress.js +0 -272
- package/dist/shadcn/lib/base-ui/radio-group.d.ts +0 -260
- package/dist/shadcn/lib/base-ui/radio-group.js +0 -239
- package/dist/shadcn/lib/base-ui/radio.d.ts +0 -176
- package/dist/shadcn/lib/base-ui/radio.js +0 -387
- package/dist/shadcn/lib/base-ui/scroll-area.d.ts +0 -241
- package/dist/shadcn/lib/base-ui/scroll-area.js +0 -1055
- package/dist/shadcn/lib/base-ui/select.d.ts +0 -1266
- package/dist/shadcn/lib/base-ui/select.js +0 -2450
- package/dist/shadcn/lib/base-ui/separator.d.ts +0 -76
- package/dist/shadcn/lib/base-ui/separator.js +0 -11
- package/dist/shadcn/lib/base-ui/slider.d.ts +0 -551
- package/dist/shadcn/lib/base-ui/slider.js +0 -1583
- package/dist/shadcn/lib/base-ui/switch.d.ts +0 -313
- package/dist/shadcn/lib/base-ui/switch.js +0 -326
- package/dist/shadcn/lib/base-ui/tabs.d.ts +0 -406
- package/dist/shadcn/lib/base-ui/tabs.js +0 -915
- package/dist/shadcn/lib/base-ui/toggle-group.d.ts +0 -235
- package/dist/shadcn/lib/base-ui/toggle-group.js +0 -145
- package/dist/shadcn/lib/base-ui/toggle.d.ts +0 -224
- package/dist/shadcn/lib/base-ui/toggle.js +0 -127
- package/dist/shadcn/lib/base-ui/tooltip.d.ts +0 -1656
- package/dist/shadcn/lib/base-ui/tooltip.js +0 -920
- package/dist/shadcn/lib/base-ui/use-render.d.ts +0 -87
- package/dist/shadcn/lib/base-ui/use-render.js +0 -15
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../shadcn/lib/utils.js";
|
|
5
|
+
import { Slot, mergeRefs, resolveRender } from "./slot.js";
|
|
6
|
+
import { useControllableState } from "./use-controllable-state.js";
|
|
7
|
+
import { usePresence } from "./use-presence.js";
|
|
8
|
+
// Drag thresholds and animation timing ported from vaul (MIT — Emil Kowalski).
|
|
9
|
+
// https://github.com/emilkowalski/vaul/blob/main/src/constants.ts
|
|
10
|
+
const VELOCITY_THRESHOLD = 0.4;
|
|
11
|
+
const CLOSE_THRESHOLD = 0.25;
|
|
12
|
+
const TRANSITION = "transform 500ms cubic-bezier(0.32, 0.72, 0, 1)";
|
|
13
|
+
function dampenValue(v) {
|
|
14
|
+
return 8 * (Math.log(v + 1) - 2);
|
|
15
|
+
}
|
|
16
|
+
// Ported from vaul/src/index.tsx `shouldDrag`. Skips drag when the pointer is
|
|
17
|
+
// inside a scrollable child that isn't at scrollTop=0 (lets content scroll
|
|
18
|
+
// naturally), on a <select>, on a [data-no-drag] subtree, or when text is
|
|
19
|
+
// highlighted.
|
|
20
|
+
function shouldDrag(el, popup) {
|
|
21
|
+
let element = el;
|
|
22
|
+
if (window.getSelection()?.toString())
|
|
23
|
+
return false;
|
|
24
|
+
while (element && element !== popup) {
|
|
25
|
+
if (element.tagName === "SELECT")
|
|
26
|
+
return false;
|
|
27
|
+
if (element.hasAttribute?.("data-no-drag") || element.closest?.("[data-no-drag]"))
|
|
28
|
+
return false;
|
|
29
|
+
if (element.scrollHeight > element.clientHeight && element.scrollTop !== 0)
|
|
30
|
+
return false;
|
|
31
|
+
element = element.parentNode;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const DrawerContext = React.createContext(null);
|
|
36
|
+
function useDrawer() {
|
|
37
|
+
const ctx = React.useContext(DrawerContext);
|
|
38
|
+
if (!ctx)
|
|
39
|
+
throw new Error("Drawer parts must be used within <Drawer>");
|
|
40
|
+
return ctx;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Self-contained bottom drawer on native `<dialog>` (focus trap, Escape,
|
|
44
|
+
* top-layer, ::backdrop) with vaul-style drag-to-dismiss: direct translate when
|
|
45
|
+
* dragging down, logarithmic rubber-band damping when dragging up, dismiss past
|
|
46
|
+
* 25% height or 0.4 px/ms velocity, otherwise snap back.
|
|
47
|
+
*/
|
|
48
|
+
function Drawer({ open, defaultOpen = false, onOpenChange, children }) {
|
|
49
|
+
const [isOpen, setOpen] = useControllableState({
|
|
50
|
+
value: open,
|
|
51
|
+
defaultValue: defaultOpen,
|
|
52
|
+
onChange: onOpenChange,
|
|
53
|
+
});
|
|
54
|
+
const titleId = React.useId();
|
|
55
|
+
const descriptionId = React.useId();
|
|
56
|
+
return (_jsx(DrawerContext.Provider, { value: { open: isOpen, setOpen, titleId, descriptionId }, children: children }));
|
|
57
|
+
}
|
|
58
|
+
function DrawerTrigger({ asChild = false, render, nativeButton: _nativeButton, children, onClick, ...props }) {
|
|
59
|
+
const { open, setOpen } = useDrawer();
|
|
60
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
61
|
+
const Comp = useSlot ? Slot : "button";
|
|
62
|
+
return (_jsx(Comp, { type: useSlot ? undefined : "button", "data-slot": "drawer-trigger", "aria-haspopup": "dialog", "data-state": open ? "open" : "closed", onClick: (e) => {
|
|
63
|
+
onClick?.(e);
|
|
64
|
+
if (!e.defaultPrevented)
|
|
65
|
+
setOpen(true);
|
|
66
|
+
}, ...props, children: slotChild }));
|
|
67
|
+
}
|
|
68
|
+
function DrawerClose({ asChild = false, render, nativeButton: _nativeButton, children, onClick, ...props }) {
|
|
69
|
+
const { setOpen } = useDrawer();
|
|
70
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
71
|
+
const Comp = useSlot ? Slot : "button";
|
|
72
|
+
return (_jsx(Comp, { type: useSlot ? undefined : "button", "data-slot": "drawer-close", onClick: (e) => {
|
|
73
|
+
onClick?.(e);
|
|
74
|
+
if (!e.defaultPrevented)
|
|
75
|
+
setOpen(false);
|
|
76
|
+
}, ...props, children: slotChild }));
|
|
77
|
+
}
|
|
78
|
+
/** No-op wrappers kept for API parity — the native `<dialog>` provides the
|
|
79
|
+
* top-layer portal and the dim via `::backdrop`. */
|
|
80
|
+
function DrawerPortal({ children }) {
|
|
81
|
+
return _jsx(_Fragment, { children: children });
|
|
82
|
+
}
|
|
83
|
+
function DrawerOverlay() {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function DrawerContent({ className, children, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, ...props }) {
|
|
87
|
+
const { open, setOpen, titleId, descriptionId } = useDrawer();
|
|
88
|
+
const ref = React.useRef(null);
|
|
89
|
+
const pointerDownOnBackdrop = React.useRef(false);
|
|
90
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
91
|
+
const dragState = React.useRef({ dragging: false, startY: 0, height: 0, startTime: 0 });
|
|
92
|
+
// A drag-dismiss already slides the panel out via an inline transform, so the
|
|
93
|
+
// CSS `animate-out` slide must be suppressed for that path — otherwise its
|
|
94
|
+
// keyframes would override the transform and snap the panel back into view.
|
|
95
|
+
// Programmatic closes (backdrop, Escape, controlled) keep the CSS animation.
|
|
96
|
+
const dismissingByDrag = React.useRef(false);
|
|
97
|
+
React.useEffect(() => {
|
|
98
|
+
const node = ref.current;
|
|
99
|
+
if (!node)
|
|
100
|
+
return;
|
|
101
|
+
if (mounted && !node.open)
|
|
102
|
+
node.showModal();
|
|
103
|
+
else if (!mounted && node.open)
|
|
104
|
+
node.close();
|
|
105
|
+
}, [mounted]);
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
if (!mounted)
|
|
108
|
+
return;
|
|
109
|
+
const prev = document.body.style.overflow;
|
|
110
|
+
document.body.style.overflow = "hidden";
|
|
111
|
+
return () => {
|
|
112
|
+
document.body.style.overflow = prev;
|
|
113
|
+
};
|
|
114
|
+
}, [mounted]);
|
|
115
|
+
// Clear any drag-leftover inline transform whenever we (re)open.
|
|
116
|
+
React.useEffect(() => {
|
|
117
|
+
if (open && ref.current) {
|
|
118
|
+
ref.current.style.transition = "";
|
|
119
|
+
ref.current.style.transform = "";
|
|
120
|
+
dismissingByDrag.current = false;
|
|
121
|
+
}
|
|
122
|
+
}, [open]);
|
|
123
|
+
const snapBack = React.useCallback(() => {
|
|
124
|
+
const el = ref.current;
|
|
125
|
+
if (!el)
|
|
126
|
+
return;
|
|
127
|
+
el.style.transition = TRANSITION;
|
|
128
|
+
el.style.transform = "";
|
|
129
|
+
}, []);
|
|
130
|
+
const dismiss = React.useCallback(() => {
|
|
131
|
+
const el = ref.current;
|
|
132
|
+
if (!el) {
|
|
133
|
+
setOpen(false);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
dismissingByDrag.current = true;
|
|
137
|
+
el.style.transition = TRANSITION;
|
|
138
|
+
el.style.transform = "translate3d(0, 100%, 0)";
|
|
139
|
+
window.setTimeout(() => setOpen(false), 500);
|
|
140
|
+
}, [setOpen]);
|
|
141
|
+
const handlePointerDown = (event) => {
|
|
142
|
+
pointerDownOnBackdrop.current = event.target === ref.current;
|
|
143
|
+
onPointerDown?.(event);
|
|
144
|
+
if (event.defaultPrevented)
|
|
145
|
+
return;
|
|
146
|
+
const el = ref.current;
|
|
147
|
+
const target = event.target;
|
|
148
|
+
if (!el || !shouldDrag(target, el))
|
|
149
|
+
return;
|
|
150
|
+
target?.setPointerCapture?.(event.pointerId);
|
|
151
|
+
dragState.current = {
|
|
152
|
+
dragging: true,
|
|
153
|
+
startY: event.clientY,
|
|
154
|
+
height: el.getBoundingClientRect().height || 0,
|
|
155
|
+
startTime: event.timeStamp,
|
|
156
|
+
};
|
|
157
|
+
el.style.transition = "none";
|
|
158
|
+
};
|
|
159
|
+
const handlePointerMove = (event) => {
|
|
160
|
+
onPointerMove?.(event);
|
|
161
|
+
if (event.defaultPrevented)
|
|
162
|
+
return;
|
|
163
|
+
const state = dragState.current;
|
|
164
|
+
const el = ref.current;
|
|
165
|
+
if (!state.dragging || !el)
|
|
166
|
+
return;
|
|
167
|
+
const delta = event.clientY - state.startY;
|
|
168
|
+
if (delta >= 0) {
|
|
169
|
+
// Dragging down toward close — direct translate
|
|
170
|
+
el.style.transform = `translate3d(0, ${delta}px, 0)`;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Dragging up away from close — logarithmic rubber-band damping (vaul-style)
|
|
174
|
+
el.style.transform = `translate3d(0, ${-dampenValue(-delta)}px, 0)`;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const handlePointerUp = (event) => {
|
|
178
|
+
onPointerUp?.(event);
|
|
179
|
+
if (event.defaultPrevented)
|
|
180
|
+
return;
|
|
181
|
+
const state = dragState.current;
|
|
182
|
+
if (!state.dragging)
|
|
183
|
+
return;
|
|
184
|
+
state.dragging = false;
|
|
185
|
+
const delta = event.clientY - state.startY;
|
|
186
|
+
const elapsed = Math.max(1, event.timeStamp - state.startTime);
|
|
187
|
+
const velocity = Math.abs(delta) / elapsed;
|
|
188
|
+
if (delta < 0) {
|
|
189
|
+
snapBack();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (velocity > VELOCITY_THRESHOLD || delta >= state.height * CLOSE_THRESHOLD) {
|
|
193
|
+
dismiss();
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
snapBack();
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const handlePointerCancel = (event) => {
|
|
200
|
+
onPointerCancel?.(event);
|
|
201
|
+
if (event.defaultPrevented)
|
|
202
|
+
return;
|
|
203
|
+
if (!dragState.current.dragging)
|
|
204
|
+
return;
|
|
205
|
+
dragState.current.dragging = false;
|
|
206
|
+
snapBack();
|
|
207
|
+
};
|
|
208
|
+
if (!mounted)
|
|
209
|
+
return null;
|
|
210
|
+
return (_jsxs("dialog", { ...props, ref: mergeRefs(ref, presenceRef), "aria-labelledby": titleId, "aria-describedby": descriptionId, "data-slot": "drawer-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", onCancel: (e) => {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
setOpen(false);
|
|
213
|
+
}, onClose: () => setOpen(false), onClick: (e) => {
|
|
214
|
+
if (e.target === ref.current &&
|
|
215
|
+
pointerDownOnBackdrop.current &&
|
|
216
|
+
!dragState.current.dragging)
|
|
217
|
+
setOpen(false);
|
|
218
|
+
}, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel, className: cn(
|
|
219
|
+
// touch-none lets the pointer-drag own the vertical gesture instead of the
|
|
220
|
+
// browser claiming it for native scroll (which would fire pointercancel).
|
|
221
|
+
// top-auto/m-0/w-full/max-w-none clear the native <dialog> UA inset and
|
|
222
|
+
// sizing so the drawer pins to the bottom edge at full width.
|
|
223
|
+
"fixed inset-x-0 top-auto bottom-0 z-50 m-0 flex max-h-[80vh] w-full max-w-none touch-none flex-col rounded-t-xl border-t border-border bg-background p-0 text-sm outline-none backdrop:bg-black/10 backdrop:transition-opacity backdrop:duration-250 supports-backdrop-filter:backdrop:backdrop-blur-xs", open
|
|
224
|
+
? "animate-in fade-in-0 slide-in-from-bottom-52 duration-300"
|
|
225
|
+
: dismissingByDrag.current
|
|
226
|
+
? ""
|
|
227
|
+
: "animate-out fade-out-0 slide-out-to-bottom-52 duration-300", className), children: [_jsx("div", { className: "mx-auto mt-4 h-1.5 w-[100px] shrink-0 rounded-full bg-muted" }), children] }));
|
|
228
|
+
}
|
|
229
|
+
function DrawerHeader({ className, ...props }) {
|
|
230
|
+
return (_jsx("div", { "data-slot": "drawer-header", className: cn("flex flex-col gap-0.5 p-4 text-center md:gap-1.5 md:text-left", className), ...props }));
|
|
231
|
+
}
|
|
232
|
+
function DrawerFooter({ className, ...props }) {
|
|
233
|
+
return (_jsx("div", { "data-slot": "drawer-footer", className: cn("mt-auto flex flex-col gap-2 p-4", className), ...props }));
|
|
234
|
+
}
|
|
235
|
+
function DrawerTitle({ className, ...props }) {
|
|
236
|
+
const { titleId } = useDrawer();
|
|
237
|
+
return (_jsx("h2", { id: titleId, "data-slot": "drawer-title", className: cn("font-medium text-foreground", className), ...props }));
|
|
238
|
+
}
|
|
239
|
+
function DrawerDescription({ className, ...props }) {
|
|
240
|
+
const { descriptionId } = useDrawer();
|
|
241
|
+
return (_jsx("p", { id: descriptionId, "data-slot": "drawer-description", className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
242
|
+
}
|
|
243
|
+
export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type Side, type Align } from "./use-floating.js";
|
|
3
|
+
export interface DropdownMenuProps {
|
|
4
|
+
open?: boolean;
|
|
5
|
+
defaultOpen?: boolean;
|
|
6
|
+
onOpenChange?: (open: boolean) => void;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
declare function DropdownMenu({ open, defaultOpen, onOpenChange, children, }: DropdownMenuProps): React.JSX.Element;
|
|
10
|
+
declare function DropdownMenuPortal({ children }: {
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
}): React.JSX.Element;
|
|
13
|
+
declare function DropdownMenuTrigger({ asChild, render, nativeButton: _nativeButton, children, onClick, onKeyDown, ...props }: React.ComponentProps<"button"> & {
|
|
14
|
+
asChild?: boolean;
|
|
15
|
+
render?: React.ReactElement;
|
|
16
|
+
nativeButton?: boolean;
|
|
17
|
+
}): React.JSX.Element;
|
|
18
|
+
export interface DropdownMenuContentProps extends React.ComponentProps<"div"> {
|
|
19
|
+
side?: Side;
|
|
20
|
+
align?: Align;
|
|
21
|
+
sideOffset?: number;
|
|
22
|
+
alignOffset?: number;
|
|
23
|
+
}
|
|
24
|
+
declare function DropdownMenuContent({ align, alignOffset, side, sideOffset, className, children, onKeyDown, ...props }: DropdownMenuContentProps): React.JSX.Element | null;
|
|
25
|
+
declare function DropdownMenuGroup({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
26
|
+
declare function DropdownMenuLabel({ className, inset, ...props }: React.ComponentProps<"div"> & {
|
|
27
|
+
inset?: boolean;
|
|
28
|
+
}): React.JSX.Element;
|
|
29
|
+
export interface DropdownMenuItemProps extends React.ComponentProps<"div"> {
|
|
30
|
+
inset?: boolean;
|
|
31
|
+
variant?: "default" | "destructive";
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
onSelect?: () => void;
|
|
34
|
+
}
|
|
35
|
+
declare function DropdownMenuItem({ className, inset, variant, disabled, onSelect, onClick, ...props }: DropdownMenuItemProps): React.JSX.Element;
|
|
36
|
+
declare function DropdownMenuCheckboxItem({ className, children, checked, onCheckedChange, disabled, inset, onClick, ...props }: Omit<React.ComponentProps<"div">, "onChange"> & {
|
|
37
|
+
checked?: boolean;
|
|
38
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
inset?: boolean;
|
|
41
|
+
}): React.JSX.Element;
|
|
42
|
+
declare function DropdownMenuRadioGroup({ value, onValueChange, ...props }: React.ComponentProps<"div"> & {
|
|
43
|
+
value?: string;
|
|
44
|
+
onValueChange?: (value: string) => void;
|
|
45
|
+
}): React.JSX.Element;
|
|
46
|
+
declare function DropdownMenuRadioItem({ className, children, value, disabled, inset, onClick, ...props }: Omit<React.ComponentProps<"div">, "value"> & {
|
|
47
|
+
value: string;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
inset?: boolean;
|
|
50
|
+
}): React.JSX.Element;
|
|
51
|
+
declare function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
52
|
+
declare function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
|
53
|
+
declare function DropdownMenuSub({ defaultOpen, children, }: {
|
|
54
|
+
defaultOpen?: boolean;
|
|
55
|
+
children?: React.ReactNode;
|
|
56
|
+
}): React.JSX.Element;
|
|
57
|
+
declare function DropdownMenuSubTrigger({ className, inset, children, onClick, onKeyDown, onMouseEnter, ...props }: React.ComponentProps<"div"> & {
|
|
58
|
+
inset?: boolean;
|
|
59
|
+
}): React.JSX.Element;
|
|
60
|
+
declare function DropdownMenuSubContent({ className, children, onKeyDown, ...props }: React.ComponentProps<"div">): React.JSX.Element | null;
|
|
61
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../shadcn/lib/utils.js";
|
|
5
|
+
import { Slot, mergeRefs, resolveRender } from "./slot.js";
|
|
6
|
+
import { Portal } from "./portal.js";
|
|
7
|
+
import { useFloating } from "./use-floating.js";
|
|
8
|
+
import { usePresence } from "./use-presence.js";
|
|
9
|
+
import { useControllableState } from "./use-controllable-state.js";
|
|
10
|
+
import { useTypeahead } from "./use-typeahead.js";
|
|
11
|
+
import { ChevronRightIcon, CheckIcon } from "../icons/index.js";
|
|
12
|
+
const MenuContext = React.createContext(null);
|
|
13
|
+
function useMenu() {
|
|
14
|
+
const ctx = React.useContext(MenuContext);
|
|
15
|
+
if (!ctx)
|
|
16
|
+
throw new Error("Menu parts must be used within <DropdownMenu>");
|
|
17
|
+
return ctx;
|
|
18
|
+
}
|
|
19
|
+
function itemsOf(container) {
|
|
20
|
+
return Array.from(container.querySelectorAll(':scope > [role^="menuitem"]:not([data-disabled]), :scope > [data-slot="dropdown-menu-group"] > [role^="menuitem"]:not([data-disabled]), :scope > [data-slot="dropdown-menu-radio-group"] > [role^="menuitem"]:not([data-disabled])'));
|
|
21
|
+
}
|
|
22
|
+
function moveFocus(container, dir) {
|
|
23
|
+
const items = itemsOf(container);
|
|
24
|
+
if (!items.length)
|
|
25
|
+
return;
|
|
26
|
+
const idx = items.indexOf(document.activeElement);
|
|
27
|
+
let next = 0;
|
|
28
|
+
if (dir === "first")
|
|
29
|
+
next = 0;
|
|
30
|
+
else if (dir === "last")
|
|
31
|
+
next = items.length - 1;
|
|
32
|
+
else if (dir === "next")
|
|
33
|
+
next = idx < 0 ? 0 : (idx + 1) % items.length;
|
|
34
|
+
else
|
|
35
|
+
next = idx < 0 ? items.length - 1 : (idx - 1 + items.length) % items.length;
|
|
36
|
+
items[next]?.focus();
|
|
37
|
+
}
|
|
38
|
+
function DropdownMenu({ open, defaultOpen = false, onOpenChange, children, }) {
|
|
39
|
+
const [isOpen, setOpen] = useControllableState({
|
|
40
|
+
value: open,
|
|
41
|
+
defaultValue: defaultOpen,
|
|
42
|
+
onChange: onOpenChange,
|
|
43
|
+
});
|
|
44
|
+
const triggerRef = React.useRef(null);
|
|
45
|
+
const contentId = React.useId();
|
|
46
|
+
const layers = React.useRef(new Set());
|
|
47
|
+
return (_jsx(MenuContext.Provider, { value: { open: isOpen, setOpen, triggerRef, contentId, layers }, children: children }));
|
|
48
|
+
}
|
|
49
|
+
function DropdownMenuPortal({ children }) {
|
|
50
|
+
return _jsx(_Fragment, { children: children });
|
|
51
|
+
}
|
|
52
|
+
function DropdownMenuTrigger({ asChild = false, render, nativeButton: _nativeButton, children, onClick, onKeyDown, ...props }) {
|
|
53
|
+
const { open, setOpen, triggerRef, contentId } = useMenu();
|
|
54
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
55
|
+
const Comp = useSlot ? Slot : "button";
|
|
56
|
+
return (_jsx(Comp, { ref: triggerRef, type: useSlot ? undefined : "button", "data-slot": "dropdown-menu-trigger", "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? contentId : undefined, "data-state": open ? "open" : "closed", onClick: (e) => {
|
|
57
|
+
onClick?.(e);
|
|
58
|
+
if (!e.defaultPrevented)
|
|
59
|
+
setOpen(!open);
|
|
60
|
+
}, onKeyDown: (e) => {
|
|
61
|
+
onKeyDown?.(e);
|
|
62
|
+
if (e.defaultPrevented)
|
|
63
|
+
return;
|
|
64
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
setOpen(true);
|
|
67
|
+
}
|
|
68
|
+
}, ...props, children: slotChild }));
|
|
69
|
+
}
|
|
70
|
+
function DropdownMenuContent({ align = "start", alignOffset = 0, side = "bottom", sideOffset = 4, className, children, onKeyDown, ...props }) {
|
|
71
|
+
const { open, setOpen, triggerRef, contentId, layers } = useMenu();
|
|
72
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
73
|
+
const { floatingRef, pos } = useFloating(triggerRef, mounted, {
|
|
74
|
+
side,
|
|
75
|
+
align,
|
|
76
|
+
sideOffset,
|
|
77
|
+
alignOffset,
|
|
78
|
+
});
|
|
79
|
+
const containerRef = React.useRef(null);
|
|
80
|
+
const { onTypeaheadKeyDown } = useTypeahead();
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
const node = containerRef.current;
|
|
83
|
+
if (!node)
|
|
84
|
+
return;
|
|
85
|
+
layers.current.add(node);
|
|
86
|
+
return () => {
|
|
87
|
+
layers.current.delete(node);
|
|
88
|
+
};
|
|
89
|
+
}, [mounted, layers]);
|
|
90
|
+
// Focus the first item once the popup is actually visible — a
|
|
91
|
+
// visibility:hidden element can't take focus, so retry each frame until it is.
|
|
92
|
+
const didFocus = React.useRef(false);
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
if (!open) {
|
|
95
|
+
didFocus.current = false;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let raf = 0;
|
|
99
|
+
const tryFocus = () => {
|
|
100
|
+
const c = containerRef.current;
|
|
101
|
+
if (c && getComputedStyle(c).visibility !== "hidden") {
|
|
102
|
+
didFocus.current = true;
|
|
103
|
+
moveFocus(c, "first");
|
|
104
|
+
}
|
|
105
|
+
else if (!didFocus.current) {
|
|
106
|
+
raf = requestAnimationFrame(tryFocus);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
raf = requestAnimationFrame(tryFocus);
|
|
110
|
+
return () => cancelAnimationFrame(raf);
|
|
111
|
+
}, [open]);
|
|
112
|
+
React.useEffect(() => {
|
|
113
|
+
if (!open)
|
|
114
|
+
return;
|
|
115
|
+
const onPointerDown = (e) => {
|
|
116
|
+
const t = e.target;
|
|
117
|
+
if (triggerRef.current?.contains(t))
|
|
118
|
+
return;
|
|
119
|
+
for (const layer of layers.current)
|
|
120
|
+
if (layer.contains(t))
|
|
121
|
+
return;
|
|
122
|
+
setOpen(false);
|
|
123
|
+
};
|
|
124
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
125
|
+
return () => {
|
|
126
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
127
|
+
};
|
|
128
|
+
}, [open, setOpen, triggerRef, layers]);
|
|
129
|
+
if (!mounted)
|
|
130
|
+
return null;
|
|
131
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: mergeRefs(floatingRef, presenceRef, containerRef), id: contentId, role: "menu", "aria-orientation": "vertical", tabIndex: -1, "data-slot": "dropdown-menu-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", "data-side": pos?.side ?? side, style: {
|
|
132
|
+
position: "fixed",
|
|
133
|
+
left: pos?.x ?? 0,
|
|
134
|
+
top: pos?.y ?? 0,
|
|
135
|
+
visibility: pos ? "visible" : "hidden",
|
|
136
|
+
["--transform-origin"]: pos?.transformOrigin ?? "center",
|
|
137
|
+
["--available-height"]: pos ? `${pos.availableHeight}px` : undefined,
|
|
138
|
+
}, onPointerMove: (e) => {
|
|
139
|
+
const item = e.target.closest('[role^="menuitem"]:not([data-disabled])');
|
|
140
|
+
if (item && item !== document.activeElement)
|
|
141
|
+
item.focus();
|
|
142
|
+
}, onKeyDown: (e) => {
|
|
143
|
+
onKeyDown?.(e);
|
|
144
|
+
if (e.defaultPrevented)
|
|
145
|
+
return;
|
|
146
|
+
const c = containerRef.current;
|
|
147
|
+
if (e.key === "ArrowDown")
|
|
148
|
+
(e.preventDefault(), moveFocus(c, "next"));
|
|
149
|
+
else if (e.key === "ArrowUp")
|
|
150
|
+
(e.preventDefault(), moveFocus(c, "prev"));
|
|
151
|
+
else if (e.key === "Home")
|
|
152
|
+
(e.preventDefault(), moveFocus(c, "first"));
|
|
153
|
+
else if (e.key === "End")
|
|
154
|
+
(e.preventDefault(), moveFocus(c, "last"));
|
|
155
|
+
else if (e.key === "Escape") {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
setOpen(false);
|
|
158
|
+
triggerRef.current?.focus();
|
|
159
|
+
}
|
|
160
|
+
else if (e.key === "Tab") {
|
|
161
|
+
setOpen(false);
|
|
162
|
+
}
|
|
163
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
164
|
+
const active = document.activeElement;
|
|
165
|
+
if (active && c.contains(active) && active.getAttribute("role")?.startsWith("menuitem")) {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
active.click();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// WAI-ARIA typeahead: printable chars jump to the matching item.
|
|
172
|
+
const items = itemsOf(c);
|
|
173
|
+
const idx = items.indexOf(document.activeElement);
|
|
174
|
+
const match = onTypeaheadKeyDown(e, items, idx);
|
|
175
|
+
if (match) {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
match.focus();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}, className: cn("z-50 max-h-(--available-height) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props, children: children }) }));
|
|
181
|
+
}
|
|
182
|
+
function DropdownMenuGroup({ className, ...props }) {
|
|
183
|
+
return _jsx("div", { role: "group", "data-slot": "dropdown-menu-group", className: className, ...props });
|
|
184
|
+
}
|
|
185
|
+
function DropdownMenuLabel({ className, inset, ...props }) {
|
|
186
|
+
return (_jsx("div", { "data-slot": "dropdown-menu-label", "data-inset": inset ? "" : undefined, className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground data-inset:pl-8", className), ...props }));
|
|
187
|
+
}
|
|
188
|
+
function DropdownMenuItem({ className, inset, variant = "default", disabled, onSelect, onClick, ...props }) {
|
|
189
|
+
const { setOpen } = useMenu();
|
|
190
|
+
return (_jsx("div", { role: "menuitem", tabIndex: -1, "aria-disabled": disabled || undefined, "data-slot": "dropdown-menu-item", "data-inset": inset ? "" : undefined, "data-variant": variant, "data-disabled": disabled ? "" : undefined, className: cn("group/dropdown-menu-item relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", className), onClick: (e) => {
|
|
191
|
+
if (disabled)
|
|
192
|
+
return;
|
|
193
|
+
onClick?.(e);
|
|
194
|
+
if (e.defaultPrevented)
|
|
195
|
+
return;
|
|
196
|
+
onSelect?.();
|
|
197
|
+
setOpen(false);
|
|
198
|
+
}, ...props }));
|
|
199
|
+
}
|
|
200
|
+
const RadioGroupContext = React.createContext({});
|
|
201
|
+
function DropdownMenuCheckboxItem({ className, children, checked, onCheckedChange, disabled, inset, onClick, ...props }) {
|
|
202
|
+
return (_jsxs("div", { role: "menuitemcheckbox", tabIndex: -1, "aria-checked": !!checked, "aria-disabled": disabled || undefined, "data-slot": "dropdown-menu-checkbox-item", "data-disabled": disabled ? "" : undefined, "data-inset": inset ? "" : undefined, className: cn("relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), onClick: (e) => {
|
|
203
|
+
if (disabled)
|
|
204
|
+
return;
|
|
205
|
+
onClick?.(e);
|
|
206
|
+
if (!e.defaultPrevented)
|
|
207
|
+
onCheckedChange?.(!checked);
|
|
208
|
+
}, ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex items-center justify-center", "data-slot": "dropdown-menu-checkbox-item-indicator", children: checked ? _jsx(CheckIcon, {}) : null }), children] }));
|
|
209
|
+
}
|
|
210
|
+
function DropdownMenuRadioGroup({ value, onValueChange, ...props }) {
|
|
211
|
+
return (_jsx(RadioGroupContext.Provider, { value: { value, onValueChange }, children: _jsx("div", { role: "group", "data-slot": "dropdown-menu-radio-group", ...props }) }));
|
|
212
|
+
}
|
|
213
|
+
function DropdownMenuRadioItem({ className, children, value, disabled, inset, onClick, ...props }) {
|
|
214
|
+
const { value: groupValue, onValueChange } = React.useContext(RadioGroupContext);
|
|
215
|
+
const checked = groupValue === value;
|
|
216
|
+
return (_jsxs("div", { role: "menuitemradio", tabIndex: -1, "aria-checked": checked, "aria-disabled": disabled || undefined, "data-slot": "dropdown-menu-radio-item", "data-disabled": disabled ? "" : undefined, "data-inset": inset ? "" : undefined, className: cn("relative flex cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), onClick: (e) => {
|
|
217
|
+
if (disabled)
|
|
218
|
+
return;
|
|
219
|
+
onClick?.(e);
|
|
220
|
+
if (!e.defaultPrevented)
|
|
221
|
+
onValueChange?.(value);
|
|
222
|
+
}, ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex items-center justify-center", "data-slot": "dropdown-menu-radio-item-indicator", children: checked ? _jsx(CheckIcon, {}) : null }), children] }));
|
|
223
|
+
}
|
|
224
|
+
function DropdownMenuSeparator({ className, ...props }) {
|
|
225
|
+
return (_jsx("div", { role: "separator", "data-slot": "dropdown-menu-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
|
|
226
|
+
}
|
|
227
|
+
function DropdownMenuShortcut({ className, ...props }) {
|
|
228
|
+
return (_jsx("span", { "data-slot": "dropdown-menu-shortcut", className: cn("ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground", className), ...props }));
|
|
229
|
+
}
|
|
230
|
+
const SubContext = React.createContext(null);
|
|
231
|
+
function useSub() {
|
|
232
|
+
const ctx = React.useContext(SubContext);
|
|
233
|
+
if (!ctx)
|
|
234
|
+
throw new Error("Submenu parts must be used within <DropdownMenuSub>");
|
|
235
|
+
return ctx;
|
|
236
|
+
}
|
|
237
|
+
function DropdownMenuSub({ defaultOpen = false, children, }) {
|
|
238
|
+
const [open, setOpen] = React.useState(defaultOpen);
|
|
239
|
+
const triggerRef = React.useRef(null);
|
|
240
|
+
return (_jsx(SubContext.Provider, { value: { open, setOpen, triggerRef }, children: children }));
|
|
241
|
+
}
|
|
242
|
+
function DropdownMenuSubTrigger({ className, inset, children, onClick, onKeyDown, onMouseEnter, ...props }) {
|
|
243
|
+
const { open, setOpen, triggerRef } = useSub();
|
|
244
|
+
return (_jsxs("div", { ref: triggerRef, role: "menuitem", tabIndex: -1, "aria-haspopup": "menu", "aria-expanded": open, "data-slot": "dropdown-menu-sub-trigger", "data-inset": inset ? "" : undefined, "data-popup-open": open ? "" : undefined, "data-open": open ? "" : undefined, className: cn("flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), onMouseEnter: (e) => {
|
|
245
|
+
onMouseEnter?.(e);
|
|
246
|
+
setOpen(true);
|
|
247
|
+
}, onClick: (e) => {
|
|
248
|
+
onClick?.(e);
|
|
249
|
+
if (!e.defaultPrevented)
|
|
250
|
+
setOpen(!open);
|
|
251
|
+
}, onKeyDown: (e) => {
|
|
252
|
+
onKeyDown?.(e);
|
|
253
|
+
if (e.defaultPrevented)
|
|
254
|
+
return;
|
|
255
|
+
if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
setOpen(true);
|
|
258
|
+
}
|
|
259
|
+
}, ...props, children: [children, _jsx(ChevronRightIcon, { className: "ml-auto" })] }));
|
|
260
|
+
}
|
|
261
|
+
function DropdownMenuSubContent({ className, children, onKeyDown, ...props }) {
|
|
262
|
+
const { open, setOpen, triggerRef } = useSub();
|
|
263
|
+
const { layers } = useMenu();
|
|
264
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
265
|
+
const { floatingRef, pos } = useFloating(triggerRef, mounted, {
|
|
266
|
+
side: "right",
|
|
267
|
+
align: "start",
|
|
268
|
+
sideOffset: 0,
|
|
269
|
+
alignOffset: -3,
|
|
270
|
+
});
|
|
271
|
+
const containerRef = React.useRef(null);
|
|
272
|
+
const { onTypeaheadKeyDown } = useTypeahead();
|
|
273
|
+
React.useEffect(() => {
|
|
274
|
+
const node = containerRef.current;
|
|
275
|
+
if (!node)
|
|
276
|
+
return;
|
|
277
|
+
layers.current.add(node);
|
|
278
|
+
return () => {
|
|
279
|
+
layers.current.delete(node);
|
|
280
|
+
};
|
|
281
|
+
}, [mounted, layers]);
|
|
282
|
+
const didFocus = React.useRef(false);
|
|
283
|
+
React.useEffect(() => {
|
|
284
|
+
if (!open) {
|
|
285
|
+
didFocus.current = false;
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
let raf = 0;
|
|
289
|
+
const tryFocus = () => {
|
|
290
|
+
const c = containerRef.current;
|
|
291
|
+
if (c && getComputedStyle(c).visibility !== "hidden") {
|
|
292
|
+
didFocus.current = true;
|
|
293
|
+
moveFocus(c, "first");
|
|
294
|
+
}
|
|
295
|
+
else if (!didFocus.current) {
|
|
296
|
+
raf = requestAnimationFrame(tryFocus);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
raf = requestAnimationFrame(tryFocus);
|
|
300
|
+
return () => cancelAnimationFrame(raf);
|
|
301
|
+
}, [open]);
|
|
302
|
+
if (!mounted)
|
|
303
|
+
return null;
|
|
304
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: mergeRefs(floatingRef, presenceRef, containerRef), role: "menu", tabIndex: -1, "data-slot": "dropdown-menu-sub-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", "data-side": pos?.side ?? "right", style: {
|
|
305
|
+
position: "fixed",
|
|
306
|
+
left: pos?.x ?? 0,
|
|
307
|
+
top: pos?.y ?? 0,
|
|
308
|
+
visibility: pos ? "visible" : "hidden",
|
|
309
|
+
["--transform-origin"]: pos?.transformOrigin ?? "center",
|
|
310
|
+
}, onPointerMove: (e) => {
|
|
311
|
+
const item = e.target.closest('[role^="menuitem"]:not([data-disabled])');
|
|
312
|
+
if (item && item !== document.activeElement)
|
|
313
|
+
item.focus();
|
|
314
|
+
}, onKeyDown: (e) => {
|
|
315
|
+
onKeyDown?.(e);
|
|
316
|
+
if (e.defaultPrevented)
|
|
317
|
+
return;
|
|
318
|
+
const c = containerRef.current;
|
|
319
|
+
if (e.key === "ArrowDown")
|
|
320
|
+
(e.preventDefault(), moveFocus(c, "next"));
|
|
321
|
+
else if (e.key === "ArrowUp")
|
|
322
|
+
(e.preventDefault(), moveFocus(c, "prev"));
|
|
323
|
+
else if (e.key === "ArrowLeft" || e.key === "Escape") {
|
|
324
|
+
e.preventDefault();
|
|
325
|
+
setOpen(false);
|
|
326
|
+
triggerRef.current?.focus();
|
|
327
|
+
}
|
|
328
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
329
|
+
const active = document.activeElement;
|
|
330
|
+
if (active && c.contains(active)) {
|
|
331
|
+
e.preventDefault();
|
|
332
|
+
active.click();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// WAI-ARIA typeahead: printable chars jump to the matching item.
|
|
337
|
+
const items = itemsOf(c);
|
|
338
|
+
const idx = items.indexOf(document.activeElement);
|
|
339
|
+
const match = onTypeaheadKeyDown(e, items, idx);
|
|
340
|
+
if (match) {
|
|
341
|
+
e.preventDefault();
|
|
342
|
+
match.focus();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}, className: cn("z-50 min-w-[96px] origin-(--transform-origin) overflow-hidden rounded-md bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props, children: children }) }));
|
|
346
|
+
}
|
|
347
|
+
export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type VariantProps } from "../shadcn/lib/cva.js";
|
|
2
|
+
declare function Empty({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
3
|
+
declare function EmptyHeader({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
4
|
+
declare const emptyMediaVariants: (props?: import("../shadcn/lib/cva.js").CvaProps<{
|
|
5
|
+
variant: {
|
|
6
|
+
default: string;
|
|
7
|
+
icon: string;
|
|
8
|
+
};
|
|
9
|
+
}> | undefined) => string;
|
|
10
|
+
declare function EmptyMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>): import("react").JSX.Element;
|
|
11
|
+
declare function EmptyTitle({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
12
|
+
declare function EmptyDescription({ className, ...props }: React.ComponentProps<"p">): import("react").JSX.Element;
|
|
13
|
+
declare function EmptyContent({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
14
|
+
export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia, };
|