@stevederico/skateboard-ui 3.11.0 → 4.0.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 +17 -0
- package/README.md +37 -31
- 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 -48
- 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 +345 -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 +234 -0
- package/dist/ui/dropdown-menu.d.ts +61 -0
- package/dist/ui/dropdown-menu.js +323 -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 +384 -0
- package/dist/ui/navigation-menu.d.ts +24 -0
- package/dist/ui/navigation-menu.js +134 -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 +68 -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 +54 -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 +216 -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 +82 -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 +38 -0
- package/dist/ui/use-floating.js +128 -0
- package/dist/ui/use-presence.d.ts +8 -0
- package/dist/ui/use-presence.js +48 -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,345 @@
|
|
|
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 { ChevronRightIcon, CheckIcon } from "../icons/index.js";
|
|
10
|
+
const MenuContext = React.createContext(null);
|
|
11
|
+
function useMenu() {
|
|
12
|
+
const ctx = React.useContext(MenuContext);
|
|
13
|
+
if (!ctx)
|
|
14
|
+
throw new Error("ContextMenu parts must be used within <ContextMenu>");
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
function itemsOf(container) {
|
|
18
|
+
return Array.from(container.querySelectorAll(':scope > [role^="menuitem"]:not([data-disabled]), :scope > [data-slot="context-menu-group"] > [role^="menuitem"]:not([data-disabled]), :scope > [data-slot="context-menu-radio-group"] > [role^="menuitem"]:not([data-disabled])'));
|
|
19
|
+
}
|
|
20
|
+
function moveFocus(container, dir) {
|
|
21
|
+
const items = itemsOf(container);
|
|
22
|
+
if (!items.length)
|
|
23
|
+
return;
|
|
24
|
+
const idx = items.indexOf(document.activeElement);
|
|
25
|
+
let next = 0;
|
|
26
|
+
if (dir === "first")
|
|
27
|
+
next = 0;
|
|
28
|
+
else if (dir === "last")
|
|
29
|
+
next = items.length - 1;
|
|
30
|
+
else if (dir === "next")
|
|
31
|
+
next = idx < 0 ? 0 : (idx + 1) % items.length;
|
|
32
|
+
else
|
|
33
|
+
next = idx < 0 ? items.length - 1 : (idx - 1 + items.length) % items.length;
|
|
34
|
+
items[next]?.focus();
|
|
35
|
+
}
|
|
36
|
+
function ContextMenu({ children }) {
|
|
37
|
+
const [open, setOpen] = React.useState(false);
|
|
38
|
+
const [point, setPoint] = React.useState({ x: 0, y: 0 });
|
|
39
|
+
const contentId = React.useId();
|
|
40
|
+
const layers = React.useRef(new Set());
|
|
41
|
+
const openAt = React.useCallback((p) => {
|
|
42
|
+
setPoint(p);
|
|
43
|
+
setOpen(true);
|
|
44
|
+
}, []);
|
|
45
|
+
return (_jsx(MenuContext.Provider, { value: { open, setOpen, point, openAt, contentId, layers }, children: children }));
|
|
46
|
+
}
|
|
47
|
+
function ContextMenuPortal({ children }) {
|
|
48
|
+
return _jsx(_Fragment, { children: children });
|
|
49
|
+
}
|
|
50
|
+
function ContextMenuTrigger({ asChild = false, render, nativeButton: _nativeButton, children, className, onContextMenu, ...props }) {
|
|
51
|
+
const { open, openAt, contentId } = useMenu();
|
|
52
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
53
|
+
const Comp = useSlot ? Slot : "div";
|
|
54
|
+
return (_jsx(Comp, { "data-slot": "context-menu-trigger", "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? contentId : undefined, "data-state": open ? "open" : "closed", className: cn("select-none", className), onContextMenu: (e) => {
|
|
55
|
+
onContextMenu?.(e);
|
|
56
|
+
if (e.defaultPrevented)
|
|
57
|
+
return;
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
openAt({ x: e.clientX, y: e.clientY });
|
|
60
|
+
}, ...props, children: slotChild }));
|
|
61
|
+
}
|
|
62
|
+
function ContextMenuContent({ className, children, onKeyDown, ...props }) {
|
|
63
|
+
const { open, setOpen, point, contentId, layers } = useMenu();
|
|
64
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
65
|
+
const containerRef = React.useRef(null);
|
|
66
|
+
// Position the menu at the cursor, clamped into the viewport once the
|
|
67
|
+
// popup has measurable dimensions.
|
|
68
|
+
const [pos, setPos] = React.useState(null);
|
|
69
|
+
React.useLayoutEffect(() => {
|
|
70
|
+
if (!mounted) {
|
|
71
|
+
setPos(null);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
let raf = 0;
|
|
75
|
+
const measure = () => {
|
|
76
|
+
const node = containerRef.current;
|
|
77
|
+
// The popup is portaled and may attach a frame late; retry until it does,
|
|
78
|
+
// otherwise pos stays null and the menu is stuck visibility:hidden.
|
|
79
|
+
if (!node) {
|
|
80
|
+
raf = requestAnimationFrame(measure);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const w = node.offsetWidth;
|
|
84
|
+
const h = node.offsetHeight;
|
|
85
|
+
const PAD = 8;
|
|
86
|
+
const vw = window.innerWidth;
|
|
87
|
+
const vh = window.innerHeight;
|
|
88
|
+
// Flip toward the side with room, then clamp.
|
|
89
|
+
let x = point.x;
|
|
90
|
+
let y = point.y;
|
|
91
|
+
if (x + w + PAD > vw)
|
|
92
|
+
x = Math.max(PAD, point.x - w);
|
|
93
|
+
if (y + h + PAD > vh)
|
|
94
|
+
y = Math.max(PAD, point.y - h);
|
|
95
|
+
x = Math.max(PAD, Math.min(x, vw - w - PAD));
|
|
96
|
+
y = Math.max(PAD, Math.min(y, vh - h - PAD));
|
|
97
|
+
setPos({ x, y });
|
|
98
|
+
};
|
|
99
|
+
measure();
|
|
100
|
+
window.addEventListener("scroll", measure, true);
|
|
101
|
+
window.addEventListener("resize", measure);
|
|
102
|
+
return () => {
|
|
103
|
+
cancelAnimationFrame(raf);
|
|
104
|
+
window.removeEventListener("scroll", measure, true);
|
|
105
|
+
window.removeEventListener("resize", measure);
|
|
106
|
+
};
|
|
107
|
+
}, [mounted, point]);
|
|
108
|
+
React.useEffect(() => {
|
|
109
|
+
const node = containerRef.current;
|
|
110
|
+
if (!node)
|
|
111
|
+
return;
|
|
112
|
+
layers.current.add(node);
|
|
113
|
+
return () => {
|
|
114
|
+
layers.current.delete(node);
|
|
115
|
+
};
|
|
116
|
+
}, [mounted, layers]);
|
|
117
|
+
// Focus the first item once the popup is actually visible — a
|
|
118
|
+
// visibility:hidden element can't take focus, so retry each frame until it is.
|
|
119
|
+
const didFocus = React.useRef(false);
|
|
120
|
+
React.useEffect(() => {
|
|
121
|
+
if (!open) {
|
|
122
|
+
didFocus.current = false;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
let raf = 0;
|
|
126
|
+
const tryFocus = () => {
|
|
127
|
+
const c = containerRef.current;
|
|
128
|
+
if (c && getComputedStyle(c).visibility !== "hidden") {
|
|
129
|
+
didFocus.current = true;
|
|
130
|
+
moveFocus(c, "first");
|
|
131
|
+
}
|
|
132
|
+
else if (!didFocus.current) {
|
|
133
|
+
raf = requestAnimationFrame(tryFocus);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
raf = requestAnimationFrame(tryFocus);
|
|
137
|
+
return () => cancelAnimationFrame(raf);
|
|
138
|
+
}, [open]);
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
if (!open)
|
|
141
|
+
return;
|
|
142
|
+
const onPointerDown = (e) => {
|
|
143
|
+
const t = e.target;
|
|
144
|
+
for (const layer of layers.current)
|
|
145
|
+
if (layer.contains(t))
|
|
146
|
+
return;
|
|
147
|
+
setOpen(false);
|
|
148
|
+
};
|
|
149
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
150
|
+
return () => {
|
|
151
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
152
|
+
};
|
|
153
|
+
}, [open, setOpen, layers]);
|
|
154
|
+
if (!mounted)
|
|
155
|
+
return null;
|
|
156
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: mergeRefs(presenceRef, containerRef), id: contentId, role: "menu", "aria-orientation": "vertical", tabIndex: -1, "data-slot": "context-menu-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", style: {
|
|
157
|
+
position: "fixed",
|
|
158
|
+
left: pos?.x ?? point.x,
|
|
159
|
+
top: pos?.y ?? point.y,
|
|
160
|
+
visibility: pos ? "visible" : "hidden",
|
|
161
|
+
["--transform-origin"]: "top left",
|
|
162
|
+
}, onPointerMove: (e) => {
|
|
163
|
+
const item = e.target.closest('[role^="menuitem"]:not([data-disabled])');
|
|
164
|
+
if (item && item !== document.activeElement)
|
|
165
|
+
item.focus();
|
|
166
|
+
}, onKeyDown: (e) => {
|
|
167
|
+
onKeyDown?.(e);
|
|
168
|
+
if (e.defaultPrevented)
|
|
169
|
+
return;
|
|
170
|
+
const c = containerRef.current;
|
|
171
|
+
if (e.key === "ArrowDown")
|
|
172
|
+
(e.preventDefault(), moveFocus(c, "next"));
|
|
173
|
+
else if (e.key === "ArrowUp")
|
|
174
|
+
(e.preventDefault(), moveFocus(c, "prev"));
|
|
175
|
+
else if (e.key === "Home")
|
|
176
|
+
(e.preventDefault(), moveFocus(c, "first"));
|
|
177
|
+
else if (e.key === "End")
|
|
178
|
+
(e.preventDefault(), moveFocus(c, "last"));
|
|
179
|
+
else if (e.key === "Escape") {
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
setOpen(false);
|
|
182
|
+
}
|
|
183
|
+
else if (e.key === "Tab") {
|
|
184
|
+
setOpen(false);
|
|
185
|
+
}
|
|
186
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
187
|
+
const active = document.activeElement;
|
|
188
|
+
if (active && c.contains(active) && active.getAttribute("role")?.startsWith("menuitem")) {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
active.click();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}, className: cn("z-50 max-h-(--available-height) min-w-36 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=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 }) }));
|
|
194
|
+
}
|
|
195
|
+
function ContextMenuGroup({ className, ...props }) {
|
|
196
|
+
return _jsx("div", { role: "group", "data-slot": "context-menu-group", className: className, ...props });
|
|
197
|
+
}
|
|
198
|
+
function ContextMenuLabel({ className, inset, ...props }) {
|
|
199
|
+
return (_jsx("div", { "data-slot": "context-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 }));
|
|
200
|
+
}
|
|
201
|
+
function ContextMenuItem({ className, inset, variant = "default", disabled, onSelect, onClick, ...props }) {
|
|
202
|
+
const { setOpen } = useMenu();
|
|
203
|
+
return (_jsx("div", { role: "menuitem", tabIndex: -1, "aria-disabled": disabled || undefined, "data-slot": "context-menu-item", "data-inset": inset ? "" : undefined, "data-variant": variant, "data-disabled": disabled ? "" : undefined, className: cn("group/context-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 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 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive", className), onClick: (e) => {
|
|
204
|
+
if (disabled)
|
|
205
|
+
return;
|
|
206
|
+
onClick?.(e);
|
|
207
|
+
if (e.defaultPrevented)
|
|
208
|
+
return;
|
|
209
|
+
onSelect?.();
|
|
210
|
+
setOpen(false);
|
|
211
|
+
}, ...props }));
|
|
212
|
+
}
|
|
213
|
+
const RadioGroupContext = React.createContext({});
|
|
214
|
+
function ContextMenuCheckboxItem({ className, children, checked, onCheckedChange, disabled, inset, onClick, ...props }) {
|
|
215
|
+
return (_jsxs("div", { role: "menuitemcheckbox", tabIndex: -1, "aria-checked": !!checked, "aria-disabled": disabled || undefined, "data-slot": "context-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 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) => {
|
|
216
|
+
if (disabled)
|
|
217
|
+
return;
|
|
218
|
+
onClick?.(e);
|
|
219
|
+
if (!e.defaultPrevented)
|
|
220
|
+
onCheckedChange?.(!checked);
|
|
221
|
+
}, ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex items-center justify-center", "data-slot": "context-menu-checkbox-item-indicator", children: checked ? _jsx(CheckIcon, {}) : null }), children] }));
|
|
222
|
+
}
|
|
223
|
+
function ContextMenuRadioGroup({ value, onValueChange, ...props }) {
|
|
224
|
+
return (_jsx(RadioGroupContext.Provider, { value: { value, onValueChange }, children: _jsx("div", { role: "group", "data-slot": "context-menu-radio-group", ...props }) }));
|
|
225
|
+
}
|
|
226
|
+
function ContextMenuRadioItem({ className, children, value, disabled, inset, onClick, ...props }) {
|
|
227
|
+
const { value: groupValue, onValueChange } = React.useContext(RadioGroupContext);
|
|
228
|
+
const checked = groupValue === value;
|
|
229
|
+
return (_jsxs("div", { role: "menuitemradio", tabIndex: -1, "aria-checked": checked, "aria-disabled": disabled || undefined, "data-slot": "context-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 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) => {
|
|
230
|
+
if (disabled)
|
|
231
|
+
return;
|
|
232
|
+
onClick?.(e);
|
|
233
|
+
if (!e.defaultPrevented)
|
|
234
|
+
onValueChange?.(value);
|
|
235
|
+
}, ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex items-center justify-center", "data-slot": "context-menu-radio-item-indicator", children: checked ? _jsx(CheckIcon, {}) : null }), children] }));
|
|
236
|
+
}
|
|
237
|
+
function ContextMenuSeparator({ className, ...props }) {
|
|
238
|
+
return (_jsx("div", { role: "separator", "data-slot": "context-menu-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
|
|
239
|
+
}
|
|
240
|
+
function ContextMenuShortcut({ className, ...props }) {
|
|
241
|
+
return (_jsx("span", { "data-slot": "context-menu-shortcut", className: cn("ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground", className), ...props }));
|
|
242
|
+
}
|
|
243
|
+
const SubContext = React.createContext(null);
|
|
244
|
+
function useSub() {
|
|
245
|
+
const ctx = React.useContext(SubContext);
|
|
246
|
+
if (!ctx)
|
|
247
|
+
throw new Error("Submenu parts must be used within <ContextMenuSub>");
|
|
248
|
+
return ctx;
|
|
249
|
+
}
|
|
250
|
+
function ContextMenuSub({ defaultOpen = false, children, }) {
|
|
251
|
+
const [open, setOpen] = React.useState(defaultOpen);
|
|
252
|
+
const triggerRef = React.useRef(null);
|
|
253
|
+
return (_jsx(SubContext.Provider, { value: { open, setOpen, triggerRef }, children: children }));
|
|
254
|
+
}
|
|
255
|
+
function ContextMenuSubTrigger({ className, inset, children, onClick, onKeyDown, onMouseEnter, ...props }) {
|
|
256
|
+
const { open, setOpen, triggerRef } = useSub();
|
|
257
|
+
return (_jsxs("div", { ref: triggerRef, role: "menuitem", tabIndex: -1, "aria-haspopup": "menu", "aria-expanded": open, "data-slot": "context-menu-sub-trigger", "data-inset": inset ? "" : undefined, "data-popup-open": open ? "" : undefined, "data-open": open ? "" : undefined, className: cn("flex cursor-pointer items-center 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) => {
|
|
258
|
+
onMouseEnter?.(e);
|
|
259
|
+
setOpen(true);
|
|
260
|
+
}, onClick: (e) => {
|
|
261
|
+
onClick?.(e);
|
|
262
|
+
if (!e.defaultPrevented)
|
|
263
|
+
setOpen(!open);
|
|
264
|
+
}, onKeyDown: (e) => {
|
|
265
|
+
onKeyDown?.(e);
|
|
266
|
+
if (e.defaultPrevented)
|
|
267
|
+
return;
|
|
268
|
+
if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
setOpen(true);
|
|
271
|
+
}
|
|
272
|
+
}, ...props, children: [children, _jsx(ChevronRightIcon, { className: "ml-auto" })] }));
|
|
273
|
+
}
|
|
274
|
+
function ContextMenuSubContent({ className, children, onKeyDown, ...props }) {
|
|
275
|
+
const { open, setOpen, triggerRef } = useSub();
|
|
276
|
+
const { layers } = useMenu();
|
|
277
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
278
|
+
const { floatingRef, pos } = useFloating(triggerRef, mounted, {
|
|
279
|
+
side: "right",
|
|
280
|
+
align: "start",
|
|
281
|
+
sideOffset: 0,
|
|
282
|
+
alignOffset: -3,
|
|
283
|
+
});
|
|
284
|
+
const containerRef = React.useRef(null);
|
|
285
|
+
React.useEffect(() => {
|
|
286
|
+
const node = containerRef.current;
|
|
287
|
+
if (!node)
|
|
288
|
+
return;
|
|
289
|
+
layers.current.add(node);
|
|
290
|
+
return () => {
|
|
291
|
+
layers.current.delete(node);
|
|
292
|
+
};
|
|
293
|
+
}, [mounted, layers]);
|
|
294
|
+
const didFocus = React.useRef(false);
|
|
295
|
+
React.useEffect(() => {
|
|
296
|
+
if (!open) {
|
|
297
|
+
didFocus.current = false;
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
let raf = 0;
|
|
301
|
+
const tryFocus = () => {
|
|
302
|
+
const c = containerRef.current;
|
|
303
|
+
if (c && getComputedStyle(c).visibility !== "hidden") {
|
|
304
|
+
didFocus.current = true;
|
|
305
|
+
moveFocus(c, "first");
|
|
306
|
+
}
|
|
307
|
+
else if (!didFocus.current) {
|
|
308
|
+
raf = requestAnimationFrame(tryFocus);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
raf = requestAnimationFrame(tryFocus);
|
|
312
|
+
return () => cancelAnimationFrame(raf);
|
|
313
|
+
}, [open]);
|
|
314
|
+
if (!mounted)
|
|
315
|
+
return null;
|
|
316
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: mergeRefs(floatingRef, presenceRef, containerRef), role: "menu", tabIndex: -1, "data-slot": "context-menu-sub-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", "data-side": pos?.side ?? "right", style: {
|
|
317
|
+
position: "fixed",
|
|
318
|
+
left: pos?.x ?? 0,
|
|
319
|
+
top: pos?.y ?? 0,
|
|
320
|
+
visibility: pos ? "visible" : "hidden",
|
|
321
|
+
["--transform-origin"]: pos?.transformOrigin ?? "center",
|
|
322
|
+
}, onKeyDown: (e) => {
|
|
323
|
+
onKeyDown?.(e);
|
|
324
|
+
if (e.defaultPrevented)
|
|
325
|
+
return;
|
|
326
|
+
const c = containerRef.current;
|
|
327
|
+
if (e.key === "ArrowDown")
|
|
328
|
+
(e.preventDefault(), moveFocus(c, "next"));
|
|
329
|
+
else if (e.key === "ArrowUp")
|
|
330
|
+
(e.preventDefault(), moveFocus(c, "prev"));
|
|
331
|
+
else if (e.key === "ArrowLeft" || e.key === "Escape") {
|
|
332
|
+
e.preventDefault();
|
|
333
|
+
setOpen(false);
|
|
334
|
+
triggerRef.current?.focus();
|
|
335
|
+
}
|
|
336
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
337
|
+
const active = document.activeElement;
|
|
338
|
+
if (active && c.contains(active)) {
|
|
339
|
+
e.preventDefault();
|
|
340
|
+
active.click();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}, 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 }) }));
|
|
344
|
+
}
|
|
345
|
+
export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup, };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface DialogProps {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
defaultOpen?: boolean;
|
|
5
|
+
onOpenChange?: (open: boolean) => void;
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Self-contained dialog built on the native `<dialog>` element: focus trapping,
|
|
10
|
+
* Escape, top-layer stacking, and the `::backdrop` come from the platform, so no
|
|
11
|
+
* hand-rolled focus trap or portal is needed.
|
|
12
|
+
*/
|
|
13
|
+
declare function Dialog({ open, defaultOpen, onOpenChange, children }: DialogProps): React.JSX.Element;
|
|
14
|
+
declare function DialogTrigger({ asChild, render, nativeButton: _nativeButton, children, onClick, ...props }: React.ComponentProps<"button"> & {
|
|
15
|
+
asChild?: boolean;
|
|
16
|
+
render?: React.ReactElement;
|
|
17
|
+
nativeButton?: boolean;
|
|
18
|
+
}): React.JSX.Element;
|
|
19
|
+
declare function DialogClose({ asChild, render, nativeButton: _nativeButton, children, onClick, ...props }: React.ComponentProps<"button"> & {
|
|
20
|
+
asChild?: boolean;
|
|
21
|
+
render?: React.ReactElement;
|
|
22
|
+
nativeButton?: boolean;
|
|
23
|
+
}): React.JSX.Element;
|
|
24
|
+
/** No-op wrappers kept for API parity with the Base UI version. The native
|
|
25
|
+
* `<dialog>` provides the portal/top-layer and the dim via `::backdrop`. */
|
|
26
|
+
declare function DialogPortal({ children }: {
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
}): React.JSX.Element;
|
|
29
|
+
declare function DialogOverlay(): null;
|
|
30
|
+
export interface DialogContentProps extends React.ComponentProps<"div"> {
|
|
31
|
+
showCloseButton?: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare function DialogContent({ className, children, showCloseButton, ...props }: DialogContentProps): React.JSX.Element;
|
|
34
|
+
declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
35
|
+
declare function DialogFooter({ className, showCloseButton, children, ...props }: React.ComponentProps<"div"> & {
|
|
36
|
+
showCloseButton?: boolean;
|
|
37
|
+
}): React.JSX.Element;
|
|
38
|
+
declare function DialogTitle({ className, ...props }: React.ComponentProps<"h2">): React.JSX.Element;
|
|
39
|
+
declare function DialogDescription({ className, ...props }: React.ComponentProps<"p">): React.JSX.Element;
|
|
40
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
|
|
@@ -0,0 +1,109 @@
|
|
|
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 { Button } from "./button.js";
|
|
6
|
+
import { Slot, resolveRender } from "./slot.js";
|
|
7
|
+
import { XIcon } from "../icons/index.js";
|
|
8
|
+
import { useControllableState } from "./use-controllable-state.js";
|
|
9
|
+
const DialogContext = React.createContext(null);
|
|
10
|
+
function useDialog() {
|
|
11
|
+
const ctx = React.useContext(DialogContext);
|
|
12
|
+
if (!ctx)
|
|
13
|
+
throw new Error("Dialog parts must be used within <Dialog>");
|
|
14
|
+
return ctx;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Self-contained dialog built on the native `<dialog>` element: focus trapping,
|
|
18
|
+
* Escape, top-layer stacking, and the `::backdrop` come from the platform, so no
|
|
19
|
+
* hand-rolled focus trap or portal is needed.
|
|
20
|
+
*/
|
|
21
|
+
function Dialog({ open, defaultOpen = false, onOpenChange, children }) {
|
|
22
|
+
const [isOpen, setOpen] = useControllableState({
|
|
23
|
+
value: open,
|
|
24
|
+
defaultValue: defaultOpen,
|
|
25
|
+
onChange: onOpenChange,
|
|
26
|
+
});
|
|
27
|
+
const titleId = React.useId();
|
|
28
|
+
const descriptionId = React.useId();
|
|
29
|
+
return (_jsx(DialogContext.Provider, { value: { open: isOpen, setOpen, titleId, descriptionId }, children: children }));
|
|
30
|
+
}
|
|
31
|
+
function DialogTrigger({ asChild = false, render, nativeButton: _nativeButton, children, onClick, ...props }) {
|
|
32
|
+
const { open, setOpen } = useDialog();
|
|
33
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
34
|
+
const Comp = useSlot ? Slot : "button";
|
|
35
|
+
return (_jsx(Comp, { type: useSlot ? undefined : "button", "data-slot": "dialog-trigger", "aria-haspopup": "dialog", "aria-expanded": open, "data-state": open ? "open" : "closed", onClick: (e) => {
|
|
36
|
+
onClick?.(e);
|
|
37
|
+
if (!e.defaultPrevented)
|
|
38
|
+
setOpen(true);
|
|
39
|
+
}, ...props, children: slotChild }));
|
|
40
|
+
}
|
|
41
|
+
function DialogClose({ asChild = false, render, nativeButton: _nativeButton, children, onClick, ...props }) {
|
|
42
|
+
const { setOpen } = useDialog();
|
|
43
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
44
|
+
const Comp = useSlot ? Slot : "button";
|
|
45
|
+
return (_jsx(Comp, { type: useSlot ? undefined : "button", "data-slot": "dialog-close", onClick: (e) => {
|
|
46
|
+
onClick?.(e);
|
|
47
|
+
if (!e.defaultPrevented)
|
|
48
|
+
setOpen(false);
|
|
49
|
+
}, ...props, children: slotChild }));
|
|
50
|
+
}
|
|
51
|
+
/** No-op wrappers kept for API parity with the Base UI version. The native
|
|
52
|
+
* `<dialog>` provides the portal/top-layer and the dim via `::backdrop`. */
|
|
53
|
+
function DialogPortal({ children }) {
|
|
54
|
+
return _jsx(_Fragment, { children: children });
|
|
55
|
+
}
|
|
56
|
+
function DialogOverlay() {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function DialogContent({ className, children, showCloseButton = true, ...props }) {
|
|
60
|
+
const { open, setOpen, titleId, descriptionId } = useDialog();
|
|
61
|
+
const ref = React.useRef(null);
|
|
62
|
+
const pointerDownOnBackdrop = React.useRef(false);
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
const node = ref.current;
|
|
65
|
+
if (!node)
|
|
66
|
+
return;
|
|
67
|
+
if (open && !node.open)
|
|
68
|
+
node.showModal();
|
|
69
|
+
else if (!open && node.open)
|
|
70
|
+
node.close();
|
|
71
|
+
}, [open]);
|
|
72
|
+
// Lock background scroll while open.
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
if (!open)
|
|
75
|
+
return;
|
|
76
|
+
const prev = document.body.style.overflow;
|
|
77
|
+
document.body.style.overflow = "hidden";
|
|
78
|
+
return () => {
|
|
79
|
+
document.body.style.overflow = prev;
|
|
80
|
+
};
|
|
81
|
+
}, [open]);
|
|
82
|
+
return (_jsx("dialog", { ref: ref, "aria-labelledby": titleId, "aria-describedby": descriptionId, "data-slot": "dialog-overlay", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", onCancel: (e) => {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
setOpen(false);
|
|
85
|
+
}, onClose: () => setOpen(false), onPointerDown: (e) => {
|
|
86
|
+
pointerDownOnBackdrop.current = e.target === ref.current;
|
|
87
|
+
}, onClick: (e) => {
|
|
88
|
+
// Close only when the press both started and ended on the backdrop —
|
|
89
|
+
// a drag that began inside the content (e.g. text selection) must not.
|
|
90
|
+
if (e.target === ref.current && pointerDownOnBackdrop.current) {
|
|
91
|
+
setOpen(false);
|
|
92
|
+
}
|
|
93
|
+
}, className: "group/dialog m-0 grid max-h-none max-w-none place-items-center bg-transparent p-4 backdrop:bg-black/10 backdrop:duration-100 supports-backdrop-filter:backdrop:backdrop-blur-xs open:fixed open:inset-0 open:size-full", children: open ? (_jsxs("div", { "data-slot": "dialog-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, className: cn("relative grid w-full max-w-[calc(100%-2rem)] gap-6 rounded-xl bg-background p-6 text-sm text-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95", className), ...props, children: [children, showCloseButton && (_jsx(DialogClose, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: "absolute top-4 right-4", size: "icon-sm", children: [_jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: "Close" })] }) }))] })) : null }));
|
|
94
|
+
}
|
|
95
|
+
function DialogHeader({ className, ...props }) {
|
|
96
|
+
return (_jsx("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2", className), ...props }));
|
|
97
|
+
}
|
|
98
|
+
function DialogFooter({ className, showCloseButton = false, children, ...props }) {
|
|
99
|
+
return (_jsxs("div", { "data-slot": "dialog-footer", className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props, children: [children, showCloseButton && (_jsx(DialogClose, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Close" }) }))] }));
|
|
100
|
+
}
|
|
101
|
+
function DialogTitle({ className, ...props }) {
|
|
102
|
+
const { titleId } = useDialog();
|
|
103
|
+
return (_jsx("h2", { id: titleId, "data-slot": "dialog-title", className: cn("leading-none font-medium", className), ...props }));
|
|
104
|
+
}
|
|
105
|
+
function DialogDescription({ className, ...props }) {
|
|
106
|
+
const { descriptionId } = useDialog();
|
|
107
|
+
return (_jsx("p", { id: descriptionId, "data-slot": "dialog-description", className: cn("text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground", className), ...props }));
|
|
108
|
+
}
|
|
109
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface DrawerProps {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
defaultOpen?: boolean;
|
|
5
|
+
onOpenChange?: (open: boolean) => void;
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Self-contained bottom drawer on native `<dialog>` (focus trap, Escape,
|
|
10
|
+
* top-layer, ::backdrop) with vaul-style drag-to-dismiss: direct translate when
|
|
11
|
+
* dragging down, logarithmic rubber-band damping when dragging up, dismiss past
|
|
12
|
+
* 25% height or 0.4 px/ms velocity, otherwise snap back.
|
|
13
|
+
*/
|
|
14
|
+
declare function Drawer({ open, defaultOpen, onOpenChange, children }: DrawerProps): React.JSX.Element;
|
|
15
|
+
declare function DrawerTrigger({ asChild, render, nativeButton: _nativeButton, children, onClick, ...props }: React.ComponentProps<"button"> & {
|
|
16
|
+
asChild?: boolean;
|
|
17
|
+
render?: React.ReactElement;
|
|
18
|
+
nativeButton?: boolean;
|
|
19
|
+
}): React.JSX.Element;
|
|
20
|
+
declare function DrawerClose({ asChild, render, nativeButton: _nativeButton, children, onClick, ...props }: React.ComponentProps<"button"> & {
|
|
21
|
+
asChild?: boolean;
|
|
22
|
+
render?: React.ReactElement;
|
|
23
|
+
nativeButton?: boolean;
|
|
24
|
+
}): React.JSX.Element;
|
|
25
|
+
/** No-op wrappers kept for API parity — the native `<dialog>` provides the
|
|
26
|
+
* top-layer portal and the dim via `::backdrop`. */
|
|
27
|
+
declare function DrawerPortal({ children }: {
|
|
28
|
+
children?: React.ReactNode;
|
|
29
|
+
}): React.JSX.Element;
|
|
30
|
+
declare function DrawerOverlay(): null;
|
|
31
|
+
declare function DrawerContent({ className, children, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, ...props }: React.ComponentProps<"dialog">): React.JSX.Element | null;
|
|
32
|
+
declare function DrawerHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
33
|
+
declare function DrawerFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
34
|
+
declare function DrawerTitle({ className, ...props }: React.ComponentProps<"h2">): React.JSX.Element;
|
|
35
|
+
declare function DrawerDescription({ className, ...props }: React.ComponentProps<"p">): React.JSX.Element;
|
|
36
|
+
export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, };
|