@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,397 @@
|
|
|
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 { useTypeahead } from "./use-typeahead.js";
|
|
10
|
+
import { ChevronRightIcon, CheckIcon } from "../icons/index.js";
|
|
11
|
+
const MenubarRootContext = React.createContext(null);
|
|
12
|
+
function useMenubarRoot() {
|
|
13
|
+
const ctx = React.useContext(MenubarRootContext);
|
|
14
|
+
if (!ctx)
|
|
15
|
+
throw new Error("Menubar parts must be used within <Menubar>");
|
|
16
|
+
return ctx;
|
|
17
|
+
}
|
|
18
|
+
const MenuContext = React.createContext(null);
|
|
19
|
+
function useMenu() {
|
|
20
|
+
const ctx = React.useContext(MenuContext);
|
|
21
|
+
if (!ctx)
|
|
22
|
+
throw new Error("Menu parts must be used within <MenubarMenu>");
|
|
23
|
+
return ctx;
|
|
24
|
+
}
|
|
25
|
+
function itemsOf(container) {
|
|
26
|
+
return Array.from(container.querySelectorAll(':scope > [role^="menuitem"]:not([data-disabled]), :scope > [data-slot="menubar-group"] > [role^="menuitem"]:not([data-disabled]), :scope > [data-slot="menubar-radio-group"] > [role^="menuitem"]:not([data-disabled])'));
|
|
27
|
+
}
|
|
28
|
+
function moveFocus(container, dir) {
|
|
29
|
+
const items = itemsOf(container);
|
|
30
|
+
if (!items.length)
|
|
31
|
+
return;
|
|
32
|
+
const idx = items.indexOf(document.activeElement);
|
|
33
|
+
let next = 0;
|
|
34
|
+
if (dir === "first")
|
|
35
|
+
next = 0;
|
|
36
|
+
else if (dir === "last")
|
|
37
|
+
next = items.length - 1;
|
|
38
|
+
else if (dir === "next")
|
|
39
|
+
next = idx < 0 ? 0 : (idx + 1) % items.length;
|
|
40
|
+
else
|
|
41
|
+
next = idx < 0 ? items.length - 1 : (idx - 1 + items.length) % items.length;
|
|
42
|
+
items[next]?.focus();
|
|
43
|
+
}
|
|
44
|
+
function Menubar({ className, ...props }) {
|
|
45
|
+
const [value, setValue] = React.useState(null);
|
|
46
|
+
const [active, setActive] = React.useState(false);
|
|
47
|
+
return (_jsx(MenubarRootContext.Provider, { value: { value, setValue, active, setActive }, children: _jsx("div", { role: "menubar", "data-slot": "menubar", className: cn("flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs", className), ...props }) }));
|
|
48
|
+
}
|
|
49
|
+
function MenubarMenu({ value: valueProp, children, }) {
|
|
50
|
+
const root = useMenubarRoot();
|
|
51
|
+
const generatedId = React.useId();
|
|
52
|
+
const value = valueProp ?? generatedId;
|
|
53
|
+
const triggerRef = React.useRef(null);
|
|
54
|
+
const contentId = React.useId();
|
|
55
|
+
const layers = React.useRef(new Set());
|
|
56
|
+
const open = root.value === value;
|
|
57
|
+
const setOpen = React.useCallback((next) => {
|
|
58
|
+
if (next) {
|
|
59
|
+
root.setValue(value);
|
|
60
|
+
root.setActive(true);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
if (root.value === value)
|
|
64
|
+
root.setValue(null);
|
|
65
|
+
root.setActive(false);
|
|
66
|
+
}
|
|
67
|
+
}, [root, value]);
|
|
68
|
+
return (_jsx(MenuContext.Provider, { value: { value, open, setOpen, triggerRef, contentId, layers }, children: children }));
|
|
69
|
+
}
|
|
70
|
+
function MenubarPortal({ children }) {
|
|
71
|
+
return _jsx(_Fragment, { children: children });
|
|
72
|
+
}
|
|
73
|
+
function MenubarGroup({ className, ...props }) {
|
|
74
|
+
return _jsx("div", { role: "group", "data-slot": "menubar-group", className: className, ...props });
|
|
75
|
+
}
|
|
76
|
+
function MenubarTrigger({ asChild = false, render, nativeButton: _nativeButton, children, className, onClick, onKeyDown, onMouseEnter, ...props }) {
|
|
77
|
+
const root = useMenubarRoot();
|
|
78
|
+
const { value, open, setOpen, triggerRef, contentId } = useMenu();
|
|
79
|
+
const { useSlot, slotChild } = resolveRender(asChild, render, children);
|
|
80
|
+
const Comp = useSlot ? Slot : "button";
|
|
81
|
+
return (_jsx(Comp, { ref: triggerRef, type: useSlot ? undefined : "button", "data-slot": "menubar-trigger", "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? contentId : undefined, "data-state": open ? "open" : "closed", className: cn("cursor-pointer flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted", className), onClick: (e) => {
|
|
82
|
+
onClick?.(e);
|
|
83
|
+
if (!e.defaultPrevented)
|
|
84
|
+
setOpen(!open);
|
|
85
|
+
}, onMouseEnter: (e) => {
|
|
86
|
+
onMouseEnter?.(e);
|
|
87
|
+
// When the bar is engaged, hovering a sibling trigger switches the
|
|
88
|
+
// open menu to this one without requiring another click.
|
|
89
|
+
if (!e.defaultPrevented && root.active && root.value !== value) {
|
|
90
|
+
root.setValue(value);
|
|
91
|
+
}
|
|
92
|
+
}, onKeyDown: (e) => {
|
|
93
|
+
onKeyDown?.(e);
|
|
94
|
+
if (e.defaultPrevented)
|
|
95
|
+
return;
|
|
96
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
setOpen(true);
|
|
99
|
+
}
|
|
100
|
+
else if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
|
|
101
|
+
// Move focus along the bar to the sibling trigger.
|
|
102
|
+
const bar = e.currentTarget.closest('[role="menubar"]');
|
|
103
|
+
if (!bar)
|
|
104
|
+
return;
|
|
105
|
+
const triggers = Array.from(bar.querySelectorAll('[data-slot="menubar-trigger"]'));
|
|
106
|
+
const idx = triggers.indexOf(e.currentTarget);
|
|
107
|
+
if (idx < 0)
|
|
108
|
+
return;
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
const delta = e.key === "ArrowRight" ? 1 : -1;
|
|
111
|
+
const target = triggers[(idx + delta + triggers.length) % triggers.length];
|
|
112
|
+
target?.focus();
|
|
113
|
+
if (root.active)
|
|
114
|
+
target?.click();
|
|
115
|
+
}
|
|
116
|
+
}, ...props, children: slotChild }));
|
|
117
|
+
}
|
|
118
|
+
function MenubarContent({ align = "start", alignOffset = -4, side = "bottom", sideOffset = 8, className, children, onKeyDown, ...props }) {
|
|
119
|
+
const root = useMenubarRoot();
|
|
120
|
+
const { open, setOpen, triggerRef, contentId, layers } = useMenu();
|
|
121
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
122
|
+
const { floatingRef, pos } = useFloating(triggerRef, mounted, {
|
|
123
|
+
side,
|
|
124
|
+
align,
|
|
125
|
+
sideOffset,
|
|
126
|
+
alignOffset,
|
|
127
|
+
});
|
|
128
|
+
const containerRef = React.useRef(null);
|
|
129
|
+
const { onTypeaheadKeyDown } = useTypeahead();
|
|
130
|
+
React.useEffect(() => {
|
|
131
|
+
const node = containerRef.current;
|
|
132
|
+
if (!node)
|
|
133
|
+
return;
|
|
134
|
+
layers.current.add(node);
|
|
135
|
+
return () => {
|
|
136
|
+
layers.current.delete(node);
|
|
137
|
+
};
|
|
138
|
+
}, [mounted, layers]);
|
|
139
|
+
// Focus the first item once the popup is actually visible — a
|
|
140
|
+
// visibility:hidden element can't take focus, so retry each frame until it is.
|
|
141
|
+
const didFocus = React.useRef(false);
|
|
142
|
+
React.useEffect(() => {
|
|
143
|
+
if (!open) {
|
|
144
|
+
didFocus.current = false;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
let raf = 0;
|
|
148
|
+
const tryFocus = () => {
|
|
149
|
+
const c = containerRef.current;
|
|
150
|
+
if (c && getComputedStyle(c).visibility !== "hidden") {
|
|
151
|
+
didFocus.current = true;
|
|
152
|
+
moveFocus(c, "first");
|
|
153
|
+
}
|
|
154
|
+
else if (!didFocus.current) {
|
|
155
|
+
raf = requestAnimationFrame(tryFocus);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
raf = requestAnimationFrame(tryFocus);
|
|
159
|
+
return () => cancelAnimationFrame(raf);
|
|
160
|
+
}, [open]);
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
if (!open)
|
|
163
|
+
return;
|
|
164
|
+
const onPointerDown = (e) => {
|
|
165
|
+
const t = e.target;
|
|
166
|
+
if (triggerRef.current?.contains(t))
|
|
167
|
+
return;
|
|
168
|
+
for (const layer of layers.current)
|
|
169
|
+
if (layer.contains(t))
|
|
170
|
+
return;
|
|
171
|
+
setOpen(false);
|
|
172
|
+
};
|
|
173
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
174
|
+
return () => {
|
|
175
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
176
|
+
};
|
|
177
|
+
}, [open, setOpen, triggerRef, layers]);
|
|
178
|
+
if (!mounted)
|
|
179
|
+
return null;
|
|
180
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: mergeRefs(floatingRef, presenceRef, containerRef), id: contentId, role: "menu", "aria-orientation": "vertical", tabIndex: -1, "data-slot": "menubar-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", "data-side": pos?.side ?? side, style: {
|
|
181
|
+
position: "fixed",
|
|
182
|
+
left: pos?.x ?? 0,
|
|
183
|
+
top: pos?.y ?? 0,
|
|
184
|
+
visibility: pos ? "visible" : "hidden",
|
|
185
|
+
["--transform-origin"]: pos?.transformOrigin ?? "center",
|
|
186
|
+
}, onPointerMove: (e) => {
|
|
187
|
+
const item = e.target.closest('[role^="menuitem"]:not([data-disabled])');
|
|
188
|
+
if (item && item !== document.activeElement)
|
|
189
|
+
item.focus();
|
|
190
|
+
}, onKeyDown: (e) => {
|
|
191
|
+
onKeyDown?.(e);
|
|
192
|
+
if (e.defaultPrevented)
|
|
193
|
+
return;
|
|
194
|
+
const c = containerRef.current;
|
|
195
|
+
if (e.key === "ArrowDown")
|
|
196
|
+
(e.preventDefault(), moveFocus(c, "next"));
|
|
197
|
+
else if (e.key === "ArrowUp")
|
|
198
|
+
(e.preventDefault(), moveFocus(c, "prev"));
|
|
199
|
+
else if (e.key === "Home")
|
|
200
|
+
(e.preventDefault(), moveFocus(c, "first"));
|
|
201
|
+
else if (e.key === "End")
|
|
202
|
+
(e.preventDefault(), moveFocus(c, "last"));
|
|
203
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
|
204
|
+
// Switch to the adjacent menu in the bar, keeping it open.
|
|
205
|
+
const trigger = triggerRef.current;
|
|
206
|
+
const bar = trigger?.closest('[role="menubar"]');
|
|
207
|
+
if (!bar)
|
|
208
|
+
return;
|
|
209
|
+
const triggers = Array.from(bar.querySelectorAll('[data-slot="menubar-trigger"]'));
|
|
210
|
+
const idx = triggers.indexOf(trigger);
|
|
211
|
+
if (idx < 0)
|
|
212
|
+
return;
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
const delta = e.key === "ArrowRight" ? 1 : -1;
|
|
215
|
+
triggers[(idx + delta + triggers.length) % triggers.length]?.click();
|
|
216
|
+
}
|
|
217
|
+
else if (e.key === "Escape") {
|
|
218
|
+
e.preventDefault();
|
|
219
|
+
setOpen(false);
|
|
220
|
+
triggerRef.current?.focus();
|
|
221
|
+
}
|
|
222
|
+
else if (e.key === "Tab") {
|
|
223
|
+
root.setValue(null);
|
|
224
|
+
root.setActive(false);
|
|
225
|
+
}
|
|
226
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
227
|
+
const active = document.activeElement;
|
|
228
|
+
if (active && c.contains(active) && active.getAttribute("role")?.startsWith("menuitem")) {
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
active.click();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// Printable-character typeahead: jump to the menu item whose label
|
|
235
|
+
// matches the typed buffer (WAI-ARIA menu pattern).
|
|
236
|
+
const items = itemsOf(c);
|
|
237
|
+
const idx = items.indexOf(document.activeElement);
|
|
238
|
+
const match = onTypeaheadKeyDown(e, items, idx);
|
|
239
|
+
if (match) {
|
|
240
|
+
e.preventDefault();
|
|
241
|
+
match.focus();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}, className: cn("z-50 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=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 }) }));
|
|
245
|
+
}
|
|
246
|
+
function MenubarLabel({ className, inset, ...props }) {
|
|
247
|
+
return (_jsx("div", { "data-slot": "menubar-label", "data-inset": inset ? "" : undefined, className: cn("px-2 py-1.5 text-sm font-medium data-inset:pl-8", className), ...props }));
|
|
248
|
+
}
|
|
249
|
+
function MenubarItem({ className, inset, variant = "default", disabled, onSelect, onClick, ...props }) {
|
|
250
|
+
const { setOpen } = useMenu();
|
|
251
|
+
return (_jsx("div", { role: "menuitem", tabIndex: -1, "aria-disabled": disabled || undefined, "data-slot": "menubar-item", "data-inset": inset ? "" : undefined, "data-variant": variant, "data-disabled": disabled ? "" : undefined, className: cn("group/menubar-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) => {
|
|
252
|
+
if (disabled)
|
|
253
|
+
return;
|
|
254
|
+
onClick?.(e);
|
|
255
|
+
if (e.defaultPrevented)
|
|
256
|
+
return;
|
|
257
|
+
onSelect?.();
|
|
258
|
+
setOpen(false);
|
|
259
|
+
}, ...props }));
|
|
260
|
+
}
|
|
261
|
+
const RadioGroupContext = React.createContext({});
|
|
262
|
+
function MenubarCheckboxItem({ className, children, checked, onCheckedChange, disabled, inset, onClick, ...props }) {
|
|
263
|
+
return (_jsxs("div", { role: "menuitemcheckbox", tabIndex: -1, "aria-checked": !!checked, "aria-disabled": disabled || undefined, "data-slot": "menubar-checkbox-item", "data-disabled": disabled ? "" : undefined, "data-inset": inset ? "" : undefined, className: cn("relative flex cursor-pointer items-center gap-2 rounded-md py-1.5 pr-2 pl-8 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) => {
|
|
264
|
+
if (disabled)
|
|
265
|
+
return;
|
|
266
|
+
onClick?.(e);
|
|
267
|
+
if (!e.defaultPrevented)
|
|
268
|
+
onCheckedChange?.(!checked);
|
|
269
|
+
}, ...props, children: [_jsx("span", { className: "pointer-events-none absolute left-2 flex size-4 items-center justify-center", "data-slot": "menubar-checkbox-item-indicator", children: checked ? _jsx(CheckIcon, {}) : null }), children] }));
|
|
270
|
+
}
|
|
271
|
+
function MenubarRadioGroup({ value, onValueChange, ...props }) {
|
|
272
|
+
return (_jsx(RadioGroupContext.Provider, { value: { value, onValueChange }, children: _jsx("div", { role: "group", "data-slot": "menubar-radio-group", ...props }) }));
|
|
273
|
+
}
|
|
274
|
+
function MenubarRadioItem({ className, children, value, disabled, inset, onClick, ...props }) {
|
|
275
|
+
const { value: groupValue, onValueChange } = React.useContext(RadioGroupContext);
|
|
276
|
+
const checked = groupValue === value;
|
|
277
|
+
return (_jsxs("div", { role: "menuitemradio", tabIndex: -1, "aria-checked": checked, "aria-disabled": disabled || undefined, "data-slot": "menubar-radio-item", "data-disabled": disabled ? "" : undefined, "data-inset": inset ? "" : undefined, className: cn("relative flex cursor-pointer items-center gap-2 rounded-md py-1.5 pr-2 pl-8 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) => {
|
|
278
|
+
if (disabled)
|
|
279
|
+
return;
|
|
280
|
+
onClick?.(e);
|
|
281
|
+
if (!e.defaultPrevented)
|
|
282
|
+
onValueChange?.(value);
|
|
283
|
+
}, ...props, children: [_jsx("span", { className: "pointer-events-none absolute left-2 flex size-4 items-center justify-center", "data-slot": "menubar-radio-item-indicator", children: checked ? _jsx(CheckIcon, {}) : null }), children] }));
|
|
284
|
+
}
|
|
285
|
+
function MenubarSeparator({ className, ...props }) {
|
|
286
|
+
return (_jsx("div", { role: "separator", "data-slot": "menubar-separator", className: cn("-mx-1 my-1 h-px bg-border", className), ...props }));
|
|
287
|
+
}
|
|
288
|
+
function MenubarShortcut({ className, ...props }) {
|
|
289
|
+
return (_jsx("span", { "data-slot": "menubar-shortcut", className: cn("ml-auto text-xs tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground", className), ...props }));
|
|
290
|
+
}
|
|
291
|
+
const SubContext = React.createContext(null);
|
|
292
|
+
function useSub() {
|
|
293
|
+
const ctx = React.useContext(SubContext);
|
|
294
|
+
if (!ctx)
|
|
295
|
+
throw new Error("Submenu parts must be used within <MenubarSub>");
|
|
296
|
+
return ctx;
|
|
297
|
+
}
|
|
298
|
+
function MenubarSub({ defaultOpen = false, children, }) {
|
|
299
|
+
const [open, setOpen] = React.useState(defaultOpen);
|
|
300
|
+
const triggerRef = React.useRef(null);
|
|
301
|
+
return (_jsx(SubContext.Provider, { value: { open, setOpen, triggerRef }, children: children }));
|
|
302
|
+
}
|
|
303
|
+
function MenubarSubTrigger({ className, inset, children, onClick, onKeyDown, onMouseEnter, ...props }) {
|
|
304
|
+
const { open, setOpen, triggerRef } = useSub();
|
|
305
|
+
return (_jsxs("div", { ref: triggerRef, role: "menuitem", tabIndex: -1, "aria-haspopup": "menu", "aria-expanded": open, "data-slot": "menubar-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) => {
|
|
306
|
+
onMouseEnter?.(e);
|
|
307
|
+
setOpen(true);
|
|
308
|
+
}, onClick: (e) => {
|
|
309
|
+
onClick?.(e);
|
|
310
|
+
if (!e.defaultPrevented)
|
|
311
|
+
setOpen(!open);
|
|
312
|
+
}, onKeyDown: (e) => {
|
|
313
|
+
onKeyDown?.(e);
|
|
314
|
+
if (e.defaultPrevented)
|
|
315
|
+
return;
|
|
316
|
+
if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
|
|
317
|
+
e.preventDefault();
|
|
318
|
+
setOpen(true);
|
|
319
|
+
}
|
|
320
|
+
}, ...props, children: [children, _jsx(ChevronRightIcon, { className: "ml-auto" })] }));
|
|
321
|
+
}
|
|
322
|
+
function MenubarSubContent({ className, children, onKeyDown, ...props }) {
|
|
323
|
+
const { open, setOpen, triggerRef } = useSub();
|
|
324
|
+
const { layers } = useMenu();
|
|
325
|
+
const [mounted, presenceRef] = usePresence(open);
|
|
326
|
+
const { floatingRef, pos } = useFloating(triggerRef, mounted, {
|
|
327
|
+
side: "right",
|
|
328
|
+
align: "start",
|
|
329
|
+
sideOffset: 0,
|
|
330
|
+
alignOffset: -3,
|
|
331
|
+
});
|
|
332
|
+
const containerRef = React.useRef(null);
|
|
333
|
+
React.useEffect(() => {
|
|
334
|
+
const node = containerRef.current;
|
|
335
|
+
if (!node)
|
|
336
|
+
return;
|
|
337
|
+
layers.current.add(node);
|
|
338
|
+
return () => {
|
|
339
|
+
layers.current.delete(node);
|
|
340
|
+
};
|
|
341
|
+
}, [mounted, layers]);
|
|
342
|
+
const didFocus = React.useRef(false);
|
|
343
|
+
React.useEffect(() => {
|
|
344
|
+
if (!open) {
|
|
345
|
+
didFocus.current = false;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
let raf = 0;
|
|
349
|
+
const tryFocus = () => {
|
|
350
|
+
const c = containerRef.current;
|
|
351
|
+
if (c && getComputedStyle(c).visibility !== "hidden") {
|
|
352
|
+
didFocus.current = true;
|
|
353
|
+
moveFocus(c, "first");
|
|
354
|
+
}
|
|
355
|
+
else if (!didFocus.current) {
|
|
356
|
+
raf = requestAnimationFrame(tryFocus);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
raf = requestAnimationFrame(tryFocus);
|
|
360
|
+
return () => cancelAnimationFrame(raf);
|
|
361
|
+
}, [open]);
|
|
362
|
+
if (!mounted)
|
|
363
|
+
return null;
|
|
364
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: mergeRefs(floatingRef, presenceRef, containerRef), role: "menu", tabIndex: -1, "data-slot": "menubar-sub-content", "data-state": open ? "open" : "closed", "data-open": open ? "" : undefined, "data-closed": open ? undefined : "", "data-side": pos?.side ?? "right", style: {
|
|
365
|
+
position: "fixed",
|
|
366
|
+
left: pos?.x ?? 0,
|
|
367
|
+
top: pos?.y ?? 0,
|
|
368
|
+
visibility: pos ? "visible" : "hidden",
|
|
369
|
+
["--transform-origin"]: pos?.transformOrigin ?? "center",
|
|
370
|
+
}, onPointerMove: (e) => {
|
|
371
|
+
const item = e.target.closest('[role^="menuitem"]:not([data-disabled])');
|
|
372
|
+
if (item && item !== document.activeElement)
|
|
373
|
+
item.focus();
|
|
374
|
+
}, onKeyDown: (e) => {
|
|
375
|
+
onKeyDown?.(e);
|
|
376
|
+
if (e.defaultPrevented)
|
|
377
|
+
return;
|
|
378
|
+
const c = containerRef.current;
|
|
379
|
+
if (e.key === "ArrowDown")
|
|
380
|
+
(e.preventDefault(), moveFocus(c, "next"));
|
|
381
|
+
else if (e.key === "ArrowUp")
|
|
382
|
+
(e.preventDefault(), moveFocus(c, "prev"));
|
|
383
|
+
else if (e.key === "ArrowLeft" || e.key === "Escape") {
|
|
384
|
+
e.preventDefault();
|
|
385
|
+
setOpen(false);
|
|
386
|
+
triggerRef.current?.focus();
|
|
387
|
+
}
|
|
388
|
+
else if (e.key === "Enter" || e.key === " ") {
|
|
389
|
+
const active = document.activeElement;
|
|
390
|
+
if (active && c.contains(active)) {
|
|
391
|
+
e.preventDefault();
|
|
392
|
+
active.click();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}, className: cn("z-50 min-w-32 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=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 }) }));
|
|
396
|
+
}
|
|
397
|
+
export { Menubar, MenubarPortal, MenubarMenu, MenubarTrigger, MenubarContent, MenubarGroup, MenubarSeparator, MenubarLabel, MenubarItem, MenubarShortcut, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarSub, MenubarSubTrigger, MenubarSubContent, };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
declare function NavigationMenu({ className, children, onMouseLeave, ...props }: React.ComponentProps<"nav">): React.JSX.Element;
|
|
3
|
+
declare function NavigationMenuList({ className, ...props }: React.ComponentProps<"ul">): React.JSX.Element;
|
|
4
|
+
declare function NavigationMenuItem({ className, ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
5
|
+
declare const navigationMenuTriggerStyle: (props?: import("../shadcn/lib/cva.js").CvaProps<{
|
|
6
|
+
[x: string]: Record<string, string>;
|
|
7
|
+
}> | undefined) => string;
|
|
8
|
+
declare function NavigationMenuTrigger({ className, children, onMouseEnter, onFocus, onClick, onKeyDown, ...props }: React.ComponentProps<"button">): React.JSX.Element;
|
|
9
|
+
declare function NavigationMenuContent({ className, children, onMouseEnter, onKeyDown, ...props }: React.ComponentProps<"div">): React.JSX.Element | null;
|
|
10
|
+
declare function NavigationMenuLink({ className, active, ...props }: React.ComponentProps<"a"> & {
|
|
11
|
+
active?: boolean;
|
|
12
|
+
}): React.JSX.Element;
|
|
13
|
+
/**
|
|
14
|
+
* No-op kept only to preserve the import name. The base-ui version renders an
|
|
15
|
+
* arrow indicator tied to the shared viewport; this simpler menu floats each
|
|
16
|
+
* content panel independently, so there is nothing to indicate.
|
|
17
|
+
*/
|
|
18
|
+
declare function NavigationMenuIndicator(_props: React.ComponentProps<"div">): React.ReactElement | null;
|
|
19
|
+
/**
|
|
20
|
+
* No-op kept only to preserve the import name. Positioning is handled inline by
|
|
21
|
+
* NavigationMenuContent via useFloating, so no separate positioner is needed.
|
|
22
|
+
*/
|
|
23
|
+
declare function NavigationMenuPositioner(_props: React.ComponentProps<"div">): React.ReactElement | null;
|
|
24
|
+
export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, navigationMenuTriggerStyle, NavigationMenuPositioner, };
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva } from "../shadcn/lib/cva.js";
|
|
5
|
+
import { cn } from "../shadcn/lib/utils.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 { ChevronDownIcon } from "../icons/index.js";
|
|
11
|
+
const NavigationMenuContext = React.createContext(null);
|
|
12
|
+
function useNavigationMenu() {
|
|
13
|
+
const ctx = React.useContext(NavigationMenuContext);
|
|
14
|
+
if (!ctx)
|
|
15
|
+
throw new Error("NavigationMenu parts must be used within <NavigationMenu>");
|
|
16
|
+
return ctx;
|
|
17
|
+
}
|
|
18
|
+
const NavigationMenuItemContext = React.createContext(null);
|
|
19
|
+
function useNavigationMenuItem() {
|
|
20
|
+
const ctx = React.useContext(NavigationMenuItemContext);
|
|
21
|
+
if (!ctx)
|
|
22
|
+
throw new Error("NavigationMenu trigger/content must be used within <NavigationMenuItem>");
|
|
23
|
+
return ctx;
|
|
24
|
+
}
|
|
25
|
+
function NavigationMenu({ className, children, onMouseLeave, ...props }) {
|
|
26
|
+
const [value, setValue] = useControllableState({
|
|
27
|
+
defaultValue: null,
|
|
28
|
+
});
|
|
29
|
+
const closeTimer = React.useRef(null);
|
|
30
|
+
const cancelClose = React.useCallback(() => {
|
|
31
|
+
if (closeTimer.current !== null) {
|
|
32
|
+
window.clearTimeout(closeTimer.current);
|
|
33
|
+
closeTimer.current = null;
|
|
34
|
+
}
|
|
35
|
+
}, []);
|
|
36
|
+
const scheduleClose = React.useCallback(() => {
|
|
37
|
+
cancelClose();
|
|
38
|
+
closeTimer.current = window.setTimeout(() => setValue(null), 150);
|
|
39
|
+
}, [cancelClose, setValue]);
|
|
40
|
+
const open = React.useCallback((v) => {
|
|
41
|
+
cancelClose();
|
|
42
|
+
setValue(v);
|
|
43
|
+
}, [cancelClose, setValue]);
|
|
44
|
+
React.useEffect(() => () => cancelClose(), [cancelClose]);
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
if (value === null)
|
|
47
|
+
return;
|
|
48
|
+
const onKeyDown = (e) => {
|
|
49
|
+
if (e.key === "Escape")
|
|
50
|
+
setValue(null);
|
|
51
|
+
};
|
|
52
|
+
document.addEventListener("keydown", onKeyDown);
|
|
53
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
54
|
+
}, [value, setValue]);
|
|
55
|
+
return (_jsx(NavigationMenuContext.Provider, { value: { value, setValue, open, scheduleClose, cancelClose }, children: _jsx("nav", { "data-slot": "navigation-menu", "data-orientation": "horizontal", className: cn("group/navigation-menu relative flex max-w-max flex-1 items-center justify-center", className), onMouseEnter: cancelClose, onMouseLeave: (e) => {
|
|
56
|
+
onMouseLeave?.(e);
|
|
57
|
+
if (!e.defaultPrevented)
|
|
58
|
+
scheduleClose();
|
|
59
|
+
}, ...props, children: children }) }));
|
|
60
|
+
}
|
|
61
|
+
function NavigationMenuList({ className, ...props }) {
|
|
62
|
+
return (_jsx("ul", { "data-slot": "navigation-menu-list", className: cn("group flex flex-1 list-none items-center justify-center gap-0", className), ...props }));
|
|
63
|
+
}
|
|
64
|
+
function NavigationMenuItem({ className, ...props }) {
|
|
65
|
+
const value = React.useId();
|
|
66
|
+
// One id + content ref per item so multiple triggers/panels don't collide
|
|
67
|
+
// when wiring aria-controls and focusing into the right portaled panel.
|
|
68
|
+
const contentId = React.useId();
|
|
69
|
+
const triggerRef = React.useRef(null);
|
|
70
|
+
const contentRef = React.useRef(null);
|
|
71
|
+
return (_jsx(NavigationMenuItemContext.Provider, { value: { value, triggerRef, contentId, contentRef }, children: _jsx("li", { "data-slot": "navigation-menu-item", className: cn("relative", className), ...props }) }));
|
|
72
|
+
}
|
|
73
|
+
// Tabbable descendants, in DOM order, used to move focus into the open content
|
|
74
|
+
// panel. Kept local (not imported) so this file stays self-contained.
|
|
75
|
+
const FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
76
|
+
const navigationMenuTriggerStyle = cva("cursor-pointer group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted");
|
|
77
|
+
function NavigationMenuTrigger({ className, children, onMouseEnter, onFocus, onClick, onKeyDown, ...props }) {
|
|
78
|
+
const { value, setValue, open } = useNavigationMenu();
|
|
79
|
+
const { value: itemValue, triggerRef, contentId, contentRef } = useNavigationMenuItem();
|
|
80
|
+
const isOpen = value === itemValue;
|
|
81
|
+
/**
|
|
82
|
+
* Move keyboard focus into the open content panel so its links are
|
|
83
|
+
* reachable. The panel is portaled and opens on focus/hover, but without
|
|
84
|
+
* this a keyboard user can never enter it (WCAG 2.1.1 keyboard trap-out).
|
|
85
|
+
* ArrowDown (and Enter) open if needed, then focus the first focusable child.
|
|
86
|
+
*/
|
|
87
|
+
const handleKeyDown = (e) => {
|
|
88
|
+
onKeyDown?.(e);
|
|
89
|
+
if (e.defaultPrevented)
|
|
90
|
+
return;
|
|
91
|
+
if (e.key !== "ArrowDown" && e.key !== "Enter")
|
|
92
|
+
return;
|
|
93
|
+
// Enter still toggles via onClick; only intercept it to enter an open panel.
|
|
94
|
+
if (e.key === "Enter" && !isOpen)
|
|
95
|
+
return;
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
open(itemValue);
|
|
98
|
+
// The panel mounts this tick and is positioned a frame or two later (it
|
|
99
|
+
// starts visibility:hidden until useFloating measures it, and focus() on a
|
|
100
|
+
// hidden node is a no-op). Retry across a few frames until it is visible,
|
|
101
|
+
// bounded so a panel that never opens can't spin forever.
|
|
102
|
+
let frames = 0;
|
|
103
|
+
const focusFirst = () => {
|
|
104
|
+
const node = contentRef.current;
|
|
105
|
+
if (!node || getComputedStyle(node).visibility === "hidden") {
|
|
106
|
+
if (frames++ < 20)
|
|
107
|
+
requestAnimationFrame(focusFirst);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const first = node.querySelector(FOCUSABLE_SELECTOR);
|
|
111
|
+
(first ?? node).focus();
|
|
112
|
+
};
|
|
113
|
+
requestAnimationFrame(focusFirst);
|
|
114
|
+
};
|
|
115
|
+
return (_jsxs("button", { ref: triggerRef, type: "button", "data-slot": "navigation-menu-trigger", "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": isOpen ? contentId : undefined, "data-state": isOpen ? "open" : "closed", "data-popup-open": isOpen ? "" : undefined, "data-open": isOpen ? "" : undefined, className: cn(navigationMenuTriggerStyle(), "group", className), onMouseEnter: (e) => {
|
|
116
|
+
onMouseEnter?.(e);
|
|
117
|
+
if (!e.defaultPrevented)
|
|
118
|
+
open(itemValue);
|
|
119
|
+
}, onFocus: (e) => {
|
|
120
|
+
onFocus?.(e);
|
|
121
|
+
if (!e.defaultPrevented)
|
|
122
|
+
open(itemValue);
|
|
123
|
+
}, onClick: (e) => {
|
|
124
|
+
onClick?.(e);
|
|
125
|
+
if (!e.defaultPrevented)
|
|
126
|
+
setValue(isOpen ? null : itemValue);
|
|
127
|
+
}, onKeyDown: handleKeyDown, ...props, children: [children, " ", _jsx(ChevronDownIcon, { className: "relative top-px ml-1 size-3 transition duration-300 group-data-open:rotate-180", "aria-hidden": "true" })] }));
|
|
128
|
+
}
|
|
129
|
+
function NavigationMenuContent({ className, children, onMouseEnter, onKeyDown, ...props }) {
|
|
130
|
+
const { value, setValue, cancelClose, scheduleClose } = useNavigationMenu();
|
|
131
|
+
const { value: itemValue, triggerRef, contentId, contentRef } = useNavigationMenuItem();
|
|
132
|
+
const isOpen = value === itemValue;
|
|
133
|
+
const [mounted, presenceRef] = usePresence(isOpen);
|
|
134
|
+
const { floatingRef, pos } = useFloating(triggerRef, mounted, {
|
|
135
|
+
side: "bottom",
|
|
136
|
+
align: "start",
|
|
137
|
+
sideOffset: 8,
|
|
138
|
+
alignOffset: 0,
|
|
139
|
+
});
|
|
140
|
+
const setRef = React.useCallback((node) => {
|
|
141
|
+
floatingRef.current = node;
|
|
142
|
+
presenceRef.current = node;
|
|
143
|
+
contentRef.current = node;
|
|
144
|
+
}, [floatingRef, presenceRef, contentRef]);
|
|
145
|
+
/**
|
|
146
|
+
* Restore focus to the trigger whenever the panel closes while focus is still
|
|
147
|
+
* inside it (root-level Escape, mouse-leave timeout, etc.). Without this the
|
|
148
|
+
* panel unmounts and keyboard focus is stranded on a detached node (a11y).
|
|
149
|
+
* If focus already moved elsewhere (e.g. an outside click), leave it alone.
|
|
150
|
+
*/
|
|
151
|
+
React.useEffect(() => {
|
|
152
|
+
if (isOpen)
|
|
153
|
+
return;
|
|
154
|
+
const node = contentRef.current;
|
|
155
|
+
const trigger = triggerRef.current;
|
|
156
|
+
if (trigger && node && node.contains(document.activeElement))
|
|
157
|
+
trigger.focus();
|
|
158
|
+
}, [isOpen, contentRef, triggerRef]);
|
|
159
|
+
/**
|
|
160
|
+
* Escape closes the menu and returns focus to the trigger. The root already
|
|
161
|
+
* listens for Escape to clear the value, but only this panel knows to restore
|
|
162
|
+
* focus — otherwise keyboard focus would be stranded on a now-unmounted node.
|
|
163
|
+
*/
|
|
164
|
+
const handleKeyDown = (e) => {
|
|
165
|
+
onKeyDown?.(e);
|
|
166
|
+
if (e.defaultPrevented || e.key !== "Escape")
|
|
167
|
+
return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
setValue(null);
|
|
170
|
+
triggerRef.current?.focus();
|
|
171
|
+
};
|
|
172
|
+
if (!mounted)
|
|
173
|
+
return null;
|
|
174
|
+
return (_jsx(Portal, { children: _jsx("div", { ref: setRef, id: contentId, role: "menu", tabIndex: -1, "data-slot": "navigation-menu-content", "data-state": isOpen ? "open" : "closed", "data-open": isOpen ? "" : undefined, "data-closed": isOpen ? undefined : "", "data-side": pos?.side ?? "bottom", style: {
|
|
175
|
+
position: "fixed",
|
|
176
|
+
left: pos?.x ?? 0,
|
|
177
|
+
top: pos?.y ?? 0,
|
|
178
|
+
visibility: pos ? "visible" : "hidden",
|
|
179
|
+
["--transform-origin"]: pos?.transformOrigin ?? "center",
|
|
180
|
+
}, onKeyDown: handleKeyDown, onMouseEnter: (e) => {
|
|
181
|
+
onMouseEnter?.(e);
|
|
182
|
+
cancelClose();
|
|
183
|
+
}, onMouseLeave: scheduleClose, className: cn("z-50 w-auto origin-(--transform-origin) rounded-md bg-popover p-2 text-popover-foreground shadow ring-1 ring-foreground/10 duration-200 outline-none 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 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none", className), ...props, children: children }) }));
|
|
184
|
+
}
|
|
185
|
+
function NavigationMenuLink({ className, active, ...props }) {
|
|
186
|
+
return (_jsx("a", { "data-slot": "navigation-menu-link", "data-active": active ? "true" : undefined, className: cn("cursor-pointer flex items-center gap-1.5 rounded-md p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-sm data-[active=true]:bg-muted/50 data-[active=true]:hover:bg-muted data-[active=true]:focus:bg-muted [&_svg:not([class*='size-'])]:size-4", className), ...props }));
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* No-op kept only to preserve the import name. The base-ui version renders an
|
|
190
|
+
* arrow indicator tied to the shared viewport; this simpler menu floats each
|
|
191
|
+
* content panel independently, so there is nothing to indicate.
|
|
192
|
+
*/
|
|
193
|
+
function NavigationMenuIndicator(_props) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* No-op kept only to preserve the import name. Positioning is handled inline by
|
|
198
|
+
* NavigationMenuContent via useFloating, so no separate positioner is needed.
|
|
199
|
+
*/
|
|
200
|
+
function NavigationMenuPositioner(_props) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, navigationMenuTriggerStyle, NavigationMenuPositioner, };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
declare function Pagination({ className, ...props }: React.ComponentProps<"nav">): React.JSX.Element;
|
|
4
|
+
declare function PaginationContent({ className, ...props }: React.ComponentProps<"ul">): React.JSX.Element;
|
|
5
|
+
declare function PaginationItem({ ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
6
|
+
type PaginationLinkProps = {
|
|
7
|
+
isActive?: boolean;
|
|
8
|
+
} & Pick<React.ComponentProps<typeof Button>, "size"> & React.ComponentProps<"a">;
|
|
9
|
+
declare function PaginationLink({ className, isActive, size, onKeyDown, ...props }: PaginationLinkProps): React.JSX.Element;
|
|
10
|
+
declare function PaginationPrevious({ className, text, ...props }: React.ComponentProps<typeof PaginationLink> & {
|
|
11
|
+
text?: string;
|
|
12
|
+
}): React.JSX.Element;
|
|
13
|
+
declare function PaginationNext({ className, text, ...props }: React.ComponentProps<typeof PaginationLink> & {
|
|
14
|
+
text?: string;
|
|
15
|
+
}): React.JSX.Element;
|
|
16
|
+
declare function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
|
17
|
+
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, };
|