@nanisoft/prism-ui 0.10.2 → 0.13.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/LICENSE +21 -21
- package/dist/.tsbuildinfo +1 -1
- package/dist/blocks/app-shell-01/app-shell.js +4 -4
- package/dist/blocks/app-shell-01/index.d.ts +2 -2
- package/dist/blocks/app-shell-01/index.js +1 -1
- package/dist/blocks/auth-form-01/auth-form.d.ts +11 -1
- package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.js +11 -8
- package/dist/blocks/auth-form-01/index.d.ts +2 -2
- package/dist/blocks/auth-form-01/index.js +1 -1
- package/dist/blocks/cta-01/cta.d.ts +26 -21
- package/dist/blocks/cta-01/cta.d.ts.map +1 -1
- package/dist/blocks/cta-01/cta.js +2 -2
- package/dist/blocks/cta-01/index.d.ts +2 -2
- package/dist/blocks/cta-01/index.js +1 -1
- package/dist/blocks/data-table-01/data-table.js +7 -7
- package/dist/blocks/data-table-01/index.d.ts +2 -2
- package/dist/blocks/data-table-01/index.js +1 -1
- package/dist/blocks/empty-state-01/empty-state.d.ts +143 -0
- package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -0
- package/dist/blocks/empty-state-01/empty-state.js +104 -0
- package/dist/blocks/empty-state-01/index.d.ts +3 -0
- package/dist/blocks/empty-state-01/index.d.ts.map +1 -0
- package/dist/blocks/empty-state-01/index.js +1 -0
- package/dist/blocks/feature-grid-01/feature-grid.d.ts +28 -1
- package/dist/blocks/feature-grid-01/feature-grid.d.ts.map +1 -1
- package/dist/blocks/feature-grid-01/feature-grid.js +34 -3
- package/dist/blocks/feature-grid-01/index.d.ts +2 -2
- package/dist/blocks/feature-grid-01/index.js +1 -1
- package/dist/blocks/hero-01/hero.d.ts +35 -15
- package/dist/blocks/hero-01/hero.d.ts.map +1 -1
- package/dist/blocks/hero-01/hero.js +14 -9
- package/dist/blocks/hero-01/index.d.ts +2 -1
- package/dist/blocks/hero-01/index.d.ts.map +1 -1
- package/dist/blocks/hero-01/index.js +1 -1
- package/dist/blocks/index.d.ts +20 -20
- package/dist/blocks/index.js +19 -19
- package/dist/blocks/instrument-panel-01/index.d.ts +2 -2
- package/dist/blocks/instrument-panel-01/index.js +1 -1
- package/dist/blocks/instrument-panel-01/instrument-panel.js +1 -1
- package/dist/blocks/logo-strip-01/index.d.ts +2 -2
- package/dist/blocks/logo-strip-01/index.js +1 -1
- package/dist/blocks/logo-strip-01/logo-strip.d.ts +1 -1
- package/dist/blocks/logo-strip-01/logo-strip.js +2 -2
- package/dist/blocks/note-grid-01/index.d.ts +2 -2
- package/dist/blocks/note-grid-01/index.js +1 -1
- package/dist/blocks/note-grid-01/note-grid.d.ts +1 -1
- package/dist/blocks/note-grid-01/note-grid.js +1 -1
- package/dist/blocks/page-header-01/index.d.ts +2 -2
- package/dist/blocks/page-header-01/index.js +1 -1
- package/dist/blocks/page-header-01/page-header.d.ts +1 -1
- package/dist/blocks/page-header-01/page-header.js +4 -4
- package/dist/blocks/pricing-01/index.d.ts +1 -1
- package/dist/blocks/pricing-01/index.js +1 -1
- package/dist/blocks/pricing-01/pricing.d.ts +8 -1
- package/dist/blocks/pricing-01/pricing.d.ts.map +1 -1
- package/dist/blocks/pricing-01/pricing.js +15 -5
- package/dist/blocks/process-flow-01/index.d.ts +3 -0
- package/dist/blocks/process-flow-01/index.d.ts.map +1 -0
- package/dist/blocks/process-flow-01/index.js +1 -0
- package/dist/blocks/process-flow-01/process-flow.d.ts +118 -0
- package/dist/blocks/process-flow-01/process-flow.d.ts.map +1 -0
- package/dist/blocks/process-flow-01/process-flow.js +71 -0
- package/dist/blocks/process-rail-01/index.d.ts +2 -2
- package/dist/blocks/process-rail-01/index.js +1 -1
- package/dist/blocks/process-rail-01/process-rail.d.ts +1 -1
- package/dist/blocks/process-rail-01/process-rail.js +2 -2
- package/dist/blocks/product-grid-01/index.d.ts +2 -2
- package/dist/blocks/product-grid-01/index.js +1 -1
- package/dist/blocks/product-grid-01/product-grid.d.ts +37 -5
- package/dist/blocks/product-grid-01/product-grid.d.ts.map +1 -1
- package/dist/blocks/product-grid-01/product-grid.js +20 -6
- package/dist/blocks/run-console-01/index.d.ts +3 -0
- package/dist/blocks/run-console-01/index.d.ts.map +1 -0
- package/dist/blocks/run-console-01/index.js +1 -0
- package/dist/blocks/run-console-01/run-console.d.ts +111 -0
- package/dist/blocks/run-console-01/run-console.d.ts.map +1 -0
- package/dist/blocks/run-console-01/run-console.js +48 -0
- package/dist/blocks/settings-panel-01/index.d.ts +2 -2
- package/dist/blocks/settings-panel-01/index.js +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.d.ts +11 -1
- package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.js +15 -10
- package/dist/blocks/site-footer/index.d.ts +2 -2
- package/dist/blocks/site-footer/index.js +1 -1
- package/dist/blocks/site-footer/site-footer.d.ts +1 -1
- package/dist/blocks/site-footer/site-footer.js +2 -2
- package/dist/blocks/site-header/index.d.ts +2 -2
- package/dist/blocks/site-header/index.js +1 -1
- package/dist/blocks/site-header/site-header.d.ts +2 -2
- package/dist/blocks/site-header/site-header.js +3 -3
- package/dist/blocks/site-navbar/index.d.ts +3 -0
- package/dist/blocks/site-navbar/index.d.ts.map +1 -0
- package/dist/blocks/site-navbar/index.js +1 -0
- package/dist/blocks/site-navbar/mobile-nav.d.ts +48 -0
- package/dist/blocks/site-navbar/mobile-nav.d.ts.map +1 -0
- package/dist/blocks/site-navbar/mobile-nav.js +35 -0
- package/dist/blocks/site-navbar/mode-toggle.d.ts +39 -0
- package/dist/blocks/site-navbar/mode-toggle.d.ts.map +1 -0
- package/dist/blocks/site-navbar/mode-toggle.js +39 -0
- package/dist/blocks/site-navbar/navbar-controls.d.ts +79 -0
- package/dist/blocks/site-navbar/navbar-controls.d.ts.map +1 -0
- package/dist/blocks/site-navbar/navbar-controls.js +40 -0
- package/dist/blocks/site-navbar/search-entry.d.ts +29 -0
- package/dist/blocks/site-navbar/search-entry.d.ts.map +1 -0
- package/dist/blocks/site-navbar/search-entry.js +52 -0
- package/dist/blocks/site-navbar/site-navbar.d.ts +212 -0
- package/dist/blocks/site-navbar/site-navbar.d.ts.map +1 -0
- package/dist/blocks/site-navbar/site-navbar.js +61 -0
- package/dist/blocks/site-navbar/sites-menu.d.ts +40 -0
- package/dist/blocks/site-navbar/sites-menu.d.ts.map +1 -0
- package/dist/blocks/site-navbar/sites-menu.js +48 -0
- package/dist/blocks/site-navbar/theme-menu.d.ts +47 -0
- package/dist/blocks/site-navbar/theme-menu.d.ts.map +1 -0
- package/dist/blocks/site-navbar/theme-menu.js +55 -0
- package/dist/blocks/stack-grid-01/index.d.ts +2 -2
- package/dist/blocks/stack-grid-01/index.js +1 -1
- package/dist/blocks/stack-grid-01/stack-grid.d.ts +42 -11
- package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -1
- package/dist/blocks/stack-grid-01/stack-grid.js +15 -7
- package/dist/blocks/stats-01/index.d.ts +9 -1
- package/dist/blocks/stats-01/index.d.ts.map +1 -1
- package/dist/blocks/stats-01/index.js +8 -1
- package/dist/blocks/stats-01/stats.d.ts +1 -1
- package/dist/blocks/stats-01/stats.js +2 -2
- package/dist/blocks/status-ledger-01/index.d.ts +2 -2
- package/dist/blocks/status-ledger-01/index.js +1 -1
- package/dist/blocks/status-ledger-01/status-ledger.d.ts +1 -1
- package/dist/blocks/status-ledger-01/status-ledger.js +2 -2
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +473 -1
- package/dist/components/index.d.ts +104 -66
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +74 -40
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/alert-dialog.d.ts +119 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.js +82 -0
- package/dist/components/ui/alert.js +1 -1
- package/dist/components/ui/aspect-ratio.d.ts +60 -0
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
- package/dist/components/ui/aspect-ratio.js +61 -0
- package/dist/components/ui/avatar.js +1 -1
- package/dist/components/ui/badge.js +1 -1
- package/dist/components/ui/breadcrumb.js +1 -1
- package/dist/components/ui/button-group.d.ts +68 -0
- package/dist/components/ui/button-group.d.ts.map +1 -0
- package/dist/components/ui/button-group.js +54 -0
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/calendar.d.ts +158 -0
- package/dist/components/ui/calendar.d.ts.map +1 -0
- package/dist/components/ui/calendar.js +216 -0
- package/dist/components/ui/card.d.ts +18 -6
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +23 -8
- package/dist/components/ui/carousel.d.ts +118 -0
- package/dist/components/ui/carousel.d.ts.map +1 -0
- package/dist/components/ui/carousel.js +152 -0
- package/dist/components/ui/chart-frame.d.ts +216 -0
- package/dist/components/ui/chart-frame.d.ts.map +1 -0
- package/dist/components/ui/chart-frame.js +286 -0
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/collapsible.d.ts +97 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/collapsible.js +54 -0
- package/dist/components/ui/combobox.d.ts +141 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/combobox.js +243 -0
- package/dist/components/ui/command-palette.d.ts +106 -0
- package/dist/components/ui/command-palette.d.ts.map +1 -0
- package/dist/components/ui/command-palette.js +176 -0
- package/dist/components/ui/command.d.ts +82 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/command.js +37 -0
- package/dist/components/ui/context-menu.d.ts +151 -0
- package/dist/components/ui/context-menu.d.ts.map +1 -0
- package/dist/components/ui/context-menu.js +97 -0
- package/dist/components/ui/cta-link.js +1 -1
- package/dist/components/ui/date-picker.d.ts +111 -0
- package/dist/components/ui/date-picker.d.ts.map +1 -0
- package/dist/components/ui/date-picker.js +74 -0
- package/dist/components/ui/diagram.d.ts +84 -15
- package/dist/components/ui/diagram.d.ts.map +1 -1
- package/dist/components/ui/diagram.js +77 -7
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/ui/diff.d.ts +105 -0
- package/dist/components/ui/diff.d.ts.map +1 -0
- package/dist/components/ui/diff.js +113 -0
- package/dist/components/ui/dropdown-menu.d.ts +18 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -1
- package/dist/components/ui/fact-list.js +1 -1
- package/dist/components/ui/field.js +1 -1
- package/dist/components/ui/form.d.ts +151 -0
- package/dist/components/ui/form.d.ts.map +1 -0
- package/dist/components/ui/form.js +73 -0
- package/dist/components/ui/hover-card.d.ts +105 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/hover-card.js +49 -0
- package/dist/components/ui/input-group.d.ts +99 -0
- package/dist/components/ui/input-group.d.ts.map +1 -0
- package/dist/components/ui/input-group.js +83 -0
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/item.d.ts +113 -0
- package/dist/components/ui/item.d.ts.map +1 -0
- package/dist/components/ui/item.js +61 -0
- package/dist/components/ui/kbd.js +1 -1
- package/dist/components/ui/label.d.ts +58 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/label.js +29 -0
- package/dist/components/ui/live-region.d.ts +9 -0
- package/dist/components/ui/live-region.d.ts.map +1 -1
- package/dist/components/ui/live-region.js +10 -2
- package/dist/components/ui/mark.js +1 -1
- package/dist/components/ui/menubar.d.ts +197 -0
- package/dist/components/ui/menubar.d.ts.map +1 -0
- package/dist/components/ui/menubar.js +109 -0
- package/dist/components/ui/meter.d.ts +121 -0
- package/dist/components/ui/meter.d.ts.map +1 -0
- package/dist/components/ui/meter.js +82 -0
- package/dist/components/ui/native-select.d.ts +72 -0
- package/dist/components/ui/native-select.d.ts.map +1 -0
- package/dist/components/ui/native-select.js +55 -0
- package/dist/components/ui/navigation-menu.d.ts +186 -0
- package/dist/components/ui/navigation-menu.d.ts.map +1 -0
- package/dist/components/ui/navigation-menu.js +94 -0
- package/dist/components/ui/number-field.d.ts +111 -0
- package/dist/components/ui/number-field.d.ts.map +1 -0
- package/dist/components/ui/number-field.js +84 -0
- package/dist/components/ui/one-time-code.d.ts +127 -0
- package/dist/components/ui/one-time-code.d.ts.map +1 -0
- package/dist/components/ui/one-time-code.js +57 -0
- package/dist/components/ui/pagination.js +1 -1
- package/dist/components/ui/popover.js +1 -1
- package/dist/components/ui/product-mark.d.ts +1 -1
- package/dist/components/ui/product-mark.js +1 -1
- package/dist/components/ui/product-switcher.d.ts +1 -1
- package/dist/components/ui/product-switcher.js +2 -2
- package/dist/components/ui/progress.js +1 -1
- package/dist/components/ui/prose.js +1 -1
- package/dist/components/ui/pulse-graph.js +1 -1
- package/dist/components/ui/pulse-series.js +1 -1
- package/dist/components/ui/radio-group.js +1 -1
- package/dist/components/ui/resizable.d.ts +129 -0
- package/dist/components/ui/resizable.d.ts.map +1 -0
- package/dist/components/ui/resizable.js +205 -0
- package/dist/components/ui/scroll-area.d.ts +97 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +68 -0
- package/dist/components/ui/search-dialog.d.ts +139 -0
- package/dist/components/ui/search-dialog.d.ts.map +1 -0
- package/dist/components/ui/search-dialog.js +178 -0
- package/dist/components/ui/section.d.ts +26 -0
- package/dist/components/ui/section.d.ts.map +1 -1
- package/dist/components/ui/section.js +32 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/separator.js +1 -1
- package/dist/components/ui/sheet.d.ts +103 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sheet.js +58 -0
- package/dist/components/ui/sidebar.d.ts +228 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/sidebar.js +172 -0
- package/dist/components/ui/signal-field.js +1 -1
- package/dist/components/ui/skeleton.js +1 -1
- package/dist/components/ui/slider.js +1 -1
- package/dist/components/ui/spinner.d.ts +79 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/spinner.js +79 -0
- package/dist/components/ui/switch.js +1 -1
- package/dist/components/ui/table-sort.d.ts +97 -0
- package/dist/components/ui/table-sort.d.ts.map +1 -0
- package/dist/components/ui/table-sort.js +102 -0
- package/dist/components/ui/table.js +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/timeline.d.ts +106 -0
- package/dist/components/ui/timeline.d.ts.map +1 -0
- package/dist/components/ui/timeline.js +111 -0
- package/dist/components/ui/toast.d.ts +219 -0
- package/dist/components/ui/toast.d.ts.map +1 -0
- package/dist/components/ui/toast.js +270 -0
- package/dist/components/ui/toggle-group.d.ts +118 -0
- package/dist/components/ui/toggle-group.d.ts.map +1 -0
- package/dist/components/ui/toggle-group.js +194 -0
- package/dist/components/ui/toggle.d.ts +73 -0
- package/dist/components/ui/toggle.d.ts.map +1 -0
- package/dist/components/ui/toggle.js +44 -0
- package/dist/components/ui/tooltip.js +1 -1
- package/dist/components/ui/tree.js +1 -1
- package/dist/components/ui/typography.js +1 -1
- package/dist/fonts/Inter-OFL.txt +93 -93
- package/dist/index.d.ts +72 -70
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -60
- package/dist/lib/rank.d.ts +69 -0
- package/dist/lib/rank.d.ts.map +1 -0
- package/dist/lib/rank.js +77 -0
- package/dist/live/index.d.ts +22 -0
- package/dist/live/index.d.ts.map +1 -0
- package/dist/live/index.js +20 -0
- package/dist/live/run-stream-01/index.d.ts +3 -0
- package/dist/live/run-stream-01/index.d.ts.map +1 -0
- package/dist/live/run-stream-01/index.js +1 -0
- package/dist/live/run-stream-01/run-stream.d.ts +125 -0
- package/dist/live/run-stream-01/run-stream.d.ts.map +1 -0
- package/dist/live/run-stream-01/run-stream.js +111 -0
- package/dist/pages/auth-page/index.d.ts +15 -2
- package/dist/pages/auth-page/index.d.ts.map +1 -1
- package/dist/pages/auth-page/index.js +8 -4
- package/dist/pages/blog-post-page/index.js +5 -5
- package/dist/pages/dashboard-page/index.d.ts +4 -4
- package/dist/pages/dashboard-page/index.js +4 -4
- package/dist/pages/docs-shell/docs-shell.d.ts +37 -4
- package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
- package/dist/pages/docs-shell/docs-shell.js +107 -16
- package/dist/pages/docs-shell/index.d.ts +2 -2
- package/dist/pages/docs-shell/index.js +1 -1
- package/dist/pages/index.d.ts +14 -14
- package/dist/pages/index.js +7 -7
- package/dist/pages/marketing-page/index.d.ts +6 -6
- package/dist/pages/marketing-page/index.js +5 -5
- package/dist/pages/not-found-page/index.js +4 -4
- package/dist/pages/settings-page/index.d.ts +3 -3
- package/dist/pages/settings-page/index.js +4 -4
- package/dist/provider/index.d.ts +4 -4
- package/dist/provider/index.js +2 -2
- package/dist/provider/provider.d.ts +1 -1
- package/dist/provider/provider.js +1 -1
- package/dist/provider/theme-script.d.ts +1 -1
- package/dist/provider/theme-script.js +1 -1
- package/dist/styles.css +689 -0
- package/package.json +9 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
3
|
+
import { cn } from '../../lib/utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* A native `<select>`, styled to match the rest of this package.
|
|
6
|
+
*
|
|
7
|
+
* **This is not a second Select, and the difference is which element is on the
|
|
8
|
+
* page.** The `Select` in this package is Base UI's: a `<button>` carrying
|
|
9
|
+
* `role="combobox"` and a popup list Base UI positions, types into and manages the
|
|
10
|
+
* keyboard for. It is a custom control, and everything it offers over the platform
|
|
11
|
+
* is the price of being one: it needs JavaScript to open, it cannot be operated by
|
|
12
|
+
* the mobile picker a reader expects when they tap a select on a phone, it is
|
|
13
|
+
* invisible to a right-click menu that offers "Save as", and it is a set of `div`s
|
|
14
|
+
* where the browser's own is one element with thirty years of platform behaviour
|
|
15
|
+
* behind it.
|
|
16
|
+
*
|
|
17
|
+
* So this Component renders the platform element. Everything the browser does with
|
|
18
|
+
* a `<select>` it still does: the wheel on Windows, the wheel on macOS and iOS,
|
|
19
|
+
* type-ahead on the first letter, the mobile picker, the form submission, and the
|
|
20
|
+
* context menu. The styling is the package's, so the control looks like its
|
|
21
|
+
* neighbours, and the appearance is suppressed so the drawn chevron is Prism's
|
|
22
|
+
* rather than the platform's.
|
|
23
|
+
*
|
|
24
|
+
* **When this is the right answer rather than the compromise it sounds like.** A
|
|
25
|
+
* native select is right when the platform's picker is better than anything this
|
|
26
|
+
* package could draw: a long list of plain strings on a phone, a form that must
|
|
27
|
+
* submit without JavaScript, a control inside a page that is already a form and
|
|
28
|
+
* gains nothing from a popup that is portalled and dismissed. It is wrong when the
|
|
29
|
+
* option needs to be more than a string, because an `<option>` holds text and
|
|
30
|
+
* nothing else: no description, no count, no state, no nested markup. That is the
|
|
31
|
+
* line, and the `Select` is on the other side of it.
|
|
32
|
+
*
|
|
33
|
+
* **The chevron is decoration and says so.** It is `aria-hidden` and it is not
|
|
34
|
+
* focusable, because the value is already announced by the platform control and a
|
|
35
|
+
* reader hearing "graphic" after every option set is a reader being told something
|
|
36
|
+
* about the drawing rather than about the value. The control is one tab stop and
|
|
37
|
+
* the browser owns its key handling entirely, which is the point.
|
|
38
|
+
*
|
|
39
|
+
* It is a server Component. A native select holds no state, runs no effect and
|
|
40
|
+
* attaches no handler, so a form of twenty of them costs no JavaScript at all,
|
|
41
|
+
* which is a real answer to the argument above rather than a footnote to it.
|
|
42
|
+
*/
|
|
43
|
+
function NativeSelect({ className, size = 'default', children, ...props }) {
|
|
44
|
+
return (_jsxs("div", { "data-slot": "native-select", className: "relative w-full", children: [_jsx("select", { "data-slot": "native-select-control", className: cn(
|
|
45
|
+
// `appearance-none` is what lets the chevron be drawn rather than
|
|
46
|
+
// inherited: the platform's own arrow is the one piece of this control
|
|
47
|
+
// that would otherwise belong to no design system, and the whole reason
|
|
48
|
+
// this Component exists is to take that over.
|
|
49
|
+
'border-input bg-background text-foreground w-full appearance-none rounded-md border ps-3 pe-9 text-sm outline-none', 'transition-[color,box-shadow,border-color] duration-fast ease-out', 'focus-visible:border-ring focus-visible:ring-ring focus-visible:ring-[3px]', 'aria-invalid:border-destructive aria-invalid:ring-destructive/20', 'disabled:cursor-not-allowed disabled:opacity-50',
|
|
50
|
+
// The font is inherited explicitly, because a `<select>` does not
|
|
51
|
+
// inherit one in every engine and a control in the platform's face
|
|
52
|
+
// beside text in Inter is a control nobody designed.
|
|
53
|
+
'font-inherit', size === 'sm' ? 'h-8' : 'h-9', className), ...props, children: children }), _jsx("span", { "data-slot": "native-select-icon", "aria-hidden": "true", className: "text-muted-foreground pointer-events-none absolute top-1/2 right-3 flex size-4 -translate-y-1/2 items-center justify-center", children: _jsx(ChevronDownIcon, { className: "size-4" }) })] }));
|
|
54
|
+
}
|
|
55
|
+
export { NativeSelect };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The props the NavigationMenu root forwards to Base UI.
|
|
4
|
+
*
|
|
5
|
+
* Declared here rather than re-exported from Base UI so no upstream type
|
|
6
|
+
* crosses the package seam (ticket 07 section 6).
|
|
7
|
+
*/
|
|
8
|
+
export interface NavigationMenuProps extends Omit<ComponentProps<'nav'>, 'defaultValue'> {
|
|
9
|
+
/**
|
|
10
|
+
* The accessible name of this region of links.
|
|
11
|
+
*
|
|
12
|
+
* Required rather than defaulted, because the root renders a `<nav>`, and a
|
|
13
|
+
* `<nav>` with no name is a landmark a screen reader announces only as
|
|
14
|
+
* "navigation". A site header's primary links and a footer link list are both
|
|
15
|
+
* `<nav>`, and a reader moving between them is told nothing about which is
|
|
16
|
+
* which. The name is the caller's word and it is usually the region: primary,
|
|
17
|
+
* product, footer.
|
|
18
|
+
*/
|
|
19
|
+
label: string;
|
|
20
|
+
/** The controlled value of the open group. */
|
|
21
|
+
value?: string | null;
|
|
22
|
+
/** The group open initially, for an uncontrolled menu. */
|
|
23
|
+
defaultValue?: string | null;
|
|
24
|
+
/** Called when the open group changes. */
|
|
25
|
+
onValueChange?: (value: string | null) => void;
|
|
26
|
+
/** Whether the groups stack. @defaultValue horizontal */
|
|
27
|
+
orientation?: 'horizontal' | 'vertical';
|
|
28
|
+
/**
|
|
29
|
+
* How long the pointer must rest on a trigger before the group opens, in
|
|
30
|
+
* milliseconds.
|
|
31
|
+
*
|
|
32
|
+
* The default is short because this is a menu of links rather than of
|
|
33
|
+
* previews: the reader is about to take one of these addresses, and a rest
|
|
34
|
+
* followed by a long wait is a rest they will conclude did nothing.
|
|
35
|
+
*
|
|
36
|
+
* @defaultValue 50
|
|
37
|
+
*/
|
|
38
|
+
delay?: number;
|
|
39
|
+
/** How long the group stays open after the pointer leaves it, in milliseconds. @defaultValue 50 */
|
|
40
|
+
closeDelay?: number;
|
|
41
|
+
}
|
|
42
|
+
/** The props NavigationMenuList forwards to Base UI. */
|
|
43
|
+
export interface NavigationMenuListProps extends ComponentProps<'ul'> {
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The props NavigationMenuItem forwards to Base UI.
|
|
47
|
+
*
|
|
48
|
+
* Declared here rather than re-exported from Base UI so no upstream type
|
|
49
|
+
* crosses the package seam (ticket 07 section 6).
|
|
50
|
+
*/
|
|
51
|
+
export interface NavigationMenuItemProps extends ComponentProps<'li'> {
|
|
52
|
+
/** The value this item contributes to the root, and the value its trigger opens. */
|
|
53
|
+
value: string;
|
|
54
|
+
}
|
|
55
|
+
/** The props NavigationMenuTrigger forwards to Base UI. */
|
|
56
|
+
export interface NavigationMenuTriggerProps extends ComponentProps<'button'> {
|
|
57
|
+
}
|
|
58
|
+
/** The props NavigationMenuContent forwards to Base UI. */
|
|
59
|
+
export interface NavigationMenuContentProps extends ComponentProps<'div'> {
|
|
60
|
+
/**
|
|
61
|
+
* Whether the closed group stays in the DOM.
|
|
62
|
+
*
|
|
63
|
+
* A navigation menu is the one overlay here a crawler reads. Every other
|
|
64
|
+
* popup is unmounted when closed, and that is right for them, but a closed
|
|
65
|
+
* navigation group holding the links of a site header is the only place a
|
|
66
|
+
* reader, a crawler and a reader with JavaScript off all see the same set of
|
|
67
|
+
* addresses, so a consumer who needs the header to read the same either way
|
|
68
|
+
* turns this on and owns the layout shift that follows.
|
|
69
|
+
*
|
|
70
|
+
* @defaultValue false
|
|
71
|
+
*/
|
|
72
|
+
keepMounted?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The props NavigationMenuLink forwards to Base UI.
|
|
76
|
+
*
|
|
77
|
+
* Declared here rather than re-exported from Base UI so no upstream type
|
|
78
|
+
* crosses the package seam (ticket 07 section 6).
|
|
79
|
+
*/
|
|
80
|
+
export interface NavigationMenuLinkProps extends ComponentProps<'a'> {
|
|
81
|
+
/**
|
|
82
|
+
* Whether this link is the page the reader is on.
|
|
83
|
+
*
|
|
84
|
+
* Required, and required for a reason a colour cannot express: a navigation
|
|
85
|
+
* menu that does not say where the reader is has made them guess. It is
|
|
86
|
+
* rendered as `aria-current="page"` rather than as a fill, so a reader who
|
|
87
|
+
* cannot distinguish the active colour still knows they are in the right
|
|
88
|
+
* place, and so the marking survives a theme change.
|
|
89
|
+
*/
|
|
90
|
+
active?: boolean;
|
|
91
|
+
/** Whether taking the link closes the group. @defaultValue false */
|
|
92
|
+
closeOnClick?: boolean;
|
|
93
|
+
}
|
|
94
|
+
/** The props NavigationMenuIcon forwards to Base UI. */
|
|
95
|
+
export interface NavigationMenuIconProps extends ComponentProps<'span'> {
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The props NavigationMenuPortal forward to Base UI.
|
|
99
|
+
*
|
|
100
|
+
* Declared here rather than re-exported from Base UI so no upstream type
|
|
101
|
+
* crosses the package seam (ticket 07 section 6).
|
|
102
|
+
*/
|
|
103
|
+
export interface NavigationMenuPortalProps extends ComponentProps<'div'> {
|
|
104
|
+
}
|
|
105
|
+
/** The props NavigationMenuPositioner forwards to Base UI. */
|
|
106
|
+
export interface NavigationMenuPositionerProps extends ComponentProps<'div'> {
|
|
107
|
+
/** The distance in pixels between the trigger and the group. @defaultValue 8 */
|
|
108
|
+
sideOffset?: number;
|
|
109
|
+
/** The offset in pixels along the alignment axis. @defaultValue 0 */
|
|
110
|
+
alignOffset?: number;
|
|
111
|
+
}
|
|
112
|
+
/** The props NavigationMenuPopup forwards to Base UI. */
|
|
113
|
+
export interface NavigationMenuPopupProps extends ComponentProps<'nav'> {
|
|
114
|
+
}
|
|
115
|
+
/** The props NavigationMenuViewport forwards to Base UI. */
|
|
116
|
+
export interface NavigationMenuViewportProps extends ComponentProps<'div'> {
|
|
117
|
+
}
|
|
118
|
+
/** The props NavigationMenuBackdrop forwards to Base UI. */
|
|
119
|
+
export interface NavigationMenuBackdropProps extends ComponentProps<'div'> {
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* A list of links with groups that expand and collapse.
|
|
123
|
+
*
|
|
124
|
+
* **It knows it holds links, and that is what every part is for.** A link can be
|
|
125
|
+
* opened in a new tab, copied by address, middle-clicked and crawled; a command
|
|
126
|
+
* cannot do any of those, and a menu that renders a command where a reader
|
|
127
|
+
* expected a destination gives them a middle-click that does nothing. So
|
|
128
|
+
* `NavigationMenuLink` is the only interactive leaf here and it is a native
|
|
129
|
+
* anchor, there is no command part to reach for by mistake, and a caller who
|
|
130
|
+
* wants commands in a header is using a Menubar or a DropdownMenu instead.
|
|
131
|
+
* `active` is a required prop on the link because a navigation menu that does
|
|
132
|
+
* not say where the reader is has made them guess.
|
|
133
|
+
*
|
|
134
|
+
* **A closed group shows nothing, and that is deliberate.** The list is a
|
|
135
|
+
* `<ul>` of triggers, and the links live in a group that is only in the document
|
|
136
|
+
* while it is open. A reader with JavaScript off, and a crawler, therefore see
|
|
137
|
+
* the triggers and nothing else, which is why `NavigationMenuContent` takes
|
|
138
|
+
* `keepMounted` for the consumer who needs the header to read the same either
|
|
139
|
+
* way and owns the layout shift that follows. Do not put a destination only in
|
|
140
|
+
* a closed group.
|
|
141
|
+
*
|
|
142
|
+
* The bar is one Tab stop: Tab moves through the triggers and into the open
|
|
143
|
+
* group, Escape closes the group and returns focus to the trigger that opened
|
|
144
|
+
* it. Use a Menubar for an application window's commands and a DropdownMenu for
|
|
145
|
+
* a menu attached to one control.
|
|
146
|
+
*/
|
|
147
|
+
declare function NavigationMenu({ className, label, orientation, ...props }: NavigationMenuProps): import("react").JSX.Element;
|
|
148
|
+
/** The row of group triggers. */
|
|
149
|
+
declare function NavigationMenuList({ className, ...props }: NavigationMenuListProps): import("react").JSX.Element;
|
|
150
|
+
/** One group: a trigger and the links it opens. */
|
|
151
|
+
declare function NavigationMenuItem({ className, ...props }: NavigationMenuItemProps): import("react").JSX.Element;
|
|
152
|
+
/**
|
|
153
|
+
* The control that opens a group, and the only Tab stop for it.
|
|
154
|
+
*
|
|
155
|
+
* It is a real button and it owns the expanded state, so a reader knows the
|
|
156
|
+
* group is open before they enter it. The chevron rotates on the open state
|
|
157
|
+
* rather than on a timer, which is the only motion here: something happened and
|
|
158
|
+
* the reader can see that it did.
|
|
159
|
+
*/
|
|
160
|
+
declare function NavigationMenuTrigger({ className, children, ...props }: NavigationMenuTriggerProps): import("react").JSX.Element;
|
|
161
|
+
/** The group of links a trigger opens. */
|
|
162
|
+
declare function NavigationMenuContent({ className, ...props }: NavigationMenuContentProps): import("react").JSX.Element;
|
|
163
|
+
/**
|
|
164
|
+
* One destination, rendered as a native anchor.
|
|
165
|
+
*
|
|
166
|
+
* This is the only interactive leaf the Component ships, and the reason is the
|
|
167
|
+
* difference between a link and a command: a link carries an address, so the
|
|
168
|
+
* browser can open it in a new tab, copy it, and a crawler can follow it. A
|
|
169
|
+
* command has none of those, and a header that renders commands where a reader
|
|
170
|
+
* expected destinations is a header whose links cannot be shared.
|
|
171
|
+
*/
|
|
172
|
+
declare function NavigationMenuLink({ className, active, ...props }: NavigationMenuLinkProps): import("react").JSX.Element;
|
|
173
|
+
/** The chevron slot, so a caller can put their own mark beside a trigger's words. */
|
|
174
|
+
declare function NavigationMenuIcon({ className, ...props }: NavigationMenuIconProps): import("react").JSX.Element;
|
|
175
|
+
/** The portal the open group is rendered into. */
|
|
176
|
+
declare function NavigationMenuPortal({ ...props }: NavigationMenuPortalProps): import("react").JSX.Element;
|
|
177
|
+
/** The positioned wrapper around the open group. */
|
|
178
|
+
declare function NavigationMenuPositioner({ className, sideOffset, alignOffset, ...props }: NavigationMenuPositionerProps): import("react").JSX.Element;
|
|
179
|
+
/** The surface the open group sits on. */
|
|
180
|
+
declare function NavigationMenuPopup({ className, ...props }: NavigationMenuPopupProps): import("react").JSX.Element;
|
|
181
|
+
/** The area one group's content is measured and animated in. */
|
|
182
|
+
declare function NavigationMenuViewport({ className, ...props }: NavigationMenuViewportProps): import("react").JSX.Element;
|
|
183
|
+
/** The scrim behind an open group, so the page behind does not read as live. */
|
|
184
|
+
declare function NavigationMenuBackdrop({ className, ...props }: NavigationMenuBackdropProps): import("react").JSX.Element;
|
|
185
|
+
export { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIcon, NavigationMenuPortal, NavigationMenuPositioner, NavigationMenuPopup, NavigationMenuViewport, NavigationMenuBackdrop, };
|
|
186
|
+
//# sourceMappingURL=navigation-menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation-menu.d.ts","sourceRoot":"","sources":["../../../src/components/ui/navigation-menu.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAI3C;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC;IACtF;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,0CAA0C;IAC1C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC9C,yDAAyD;IACzD,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAA;IACvC;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wDAAwD;AACxD,MAAM,WAAW,uBAAwB,SAAQ,cAAc,CAAC,IAAI,CAAC;CAAG;AAExE;;;;;GAKG;AACH,MAAM,WAAW,uBAAwB,SAAQ,cAAc,CAAC,IAAI,CAAC;IACnE,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAA;CACd;AAED,2DAA2D;AAC3D,MAAM,WAAW,0BAA2B,SAAQ,cAAc,CAAC,QAAQ,CAAC;CAAG;AAE/E,2DAA2D;AAC3D,MAAM,WAAW,0BAA2B,SAAQ,cAAc,CAAC,KAAK,CAAC;IACvE;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAwB,SAAQ,cAAc,CAAC,GAAG,CAAC;IAClE;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,uBAAwB,SAAQ,cAAc,CAAC,MAAM,CAAC;CAAG;AAE1E;;;;;GAKG;AACH,MAAM,WAAW,yBAA0B,SAAQ,cAAc,CAAC,KAAK,CAAC;CAAG;AAE3E,8DAA8D;AAC9D,MAAM,WAAW,6BAA8B,SAAQ,cAAc,CAAC,KAAK,CAAC;IAC1E,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,yDAAyD;AACzD,MAAM,WAAW,wBAAyB,SAAQ,cAAc,CAAC,KAAK,CAAC;CAAG;AAE1E,4DAA4D;AAC5D,MAAM,WAAW,2BAA4B,SAAQ,cAAc,CAAC,KAAK,CAAC;CAAG;AAE7E,4DAA4D;AAC5D,MAAM,WAAW,2BAA4B,SAAQ,cAAc,CAAC,KAAK,CAAC;CAAG;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iBAAS,cAAc,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,WAA0B,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,+BActG;AAED,iCAAiC;AACjC,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,+BAY3E;AAED,mDAAmD;AACnD,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,+BAQ3E;AAED;;;;;;;GAOG;AACH,iBAAS,qBAAqB,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,0BAA0B,+BAmB3F;AAED,0CAA0C;AAC1C,iBAAS,qBAAqB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,0BAA0B,+BAQjF;AAED;;;;;;;;GAQG;AACH,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,+BAcnF;AAED,qFAAqF;AACrF,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,+BAQ3E;AAED,kDAAkD;AAClD,iBAAS,oBAAoB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,yBAAyB,+BAEpE;AAED,oDAAoD;AACpD,iBAAS,wBAAwB,CAAC,EAChC,SAAS,EACT,UAAc,EACd,WAAe,EACf,GAAG,KAAK,EACT,EAAE,6BAA6B,+BAU/B;AAED,0CAA0C;AAC1C,iBAAS,mBAAmB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,wBAAwB,+BAe7E;AAED,gEAAgE;AAChE,iBAAS,sBAAsB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,2BAA2B,+BAQnF;AAED,gFAAgF;AAChF,iBAAS,sBAAsB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,2BAA2B,+BAQnF;AAED,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,CAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { NavigationMenu as NavigationMenuPrimitive } from '@base-ui/react/navigation-menu';
|
|
4
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
5
|
+
import { cn } from '../../lib/utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* A list of links with groups that expand and collapse.
|
|
8
|
+
*
|
|
9
|
+
* **It knows it holds links, and that is what every part is for.** A link can be
|
|
10
|
+
* opened in a new tab, copied by address, middle-clicked and crawled; a command
|
|
11
|
+
* cannot do any of those, and a menu that renders a command where a reader
|
|
12
|
+
* expected a destination gives them a middle-click that does nothing. So
|
|
13
|
+
* `NavigationMenuLink` is the only interactive leaf here and it is a native
|
|
14
|
+
* anchor, there is no command part to reach for by mistake, and a caller who
|
|
15
|
+
* wants commands in a header is using a Menubar or a DropdownMenu instead.
|
|
16
|
+
* `active` is a required prop on the link because a navigation menu that does
|
|
17
|
+
* not say where the reader is has made them guess.
|
|
18
|
+
*
|
|
19
|
+
* **A closed group shows nothing, and that is deliberate.** The list is a
|
|
20
|
+
* `<ul>` of triggers, and the links live in a group that is only in the document
|
|
21
|
+
* while it is open. A reader with JavaScript off, and a crawler, therefore see
|
|
22
|
+
* the triggers and nothing else, which is why `NavigationMenuContent` takes
|
|
23
|
+
* `keepMounted` for the consumer who needs the header to read the same either
|
|
24
|
+
* way and owns the layout shift that follows. Do not put a destination only in
|
|
25
|
+
* a closed group.
|
|
26
|
+
*
|
|
27
|
+
* The bar is one Tab stop: Tab moves through the triggers and into the open
|
|
28
|
+
* group, Escape closes the group and returns focus to the trigger that opened
|
|
29
|
+
* it. Use a Menubar for an application window's commands and a DropdownMenu for
|
|
30
|
+
* a menu attached to one control.
|
|
31
|
+
*/
|
|
32
|
+
function NavigationMenu({ className, label, orientation = 'horizontal', ...props }) {
|
|
33
|
+
return (_jsx(NavigationMenuPrimitive.Root, { "data-slot": "navigation-menu", "aria-label": label, orientation: orientation, className: cn('relative flex max-w-max flex-1 items-center justify-center', 'data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch', className), ...props }));
|
|
34
|
+
}
|
|
35
|
+
/** The row of group triggers. */
|
|
36
|
+
function NavigationMenuList({ className, ...props }) {
|
|
37
|
+
return (_jsx(NavigationMenuPrimitive.List, { "data-slot": "navigation-menu-list", className: cn('flex flex-1 list-none items-center gap-1', 'data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch', className), ...props }));
|
|
38
|
+
}
|
|
39
|
+
/** One group: a trigger and the links it opens. */
|
|
40
|
+
function NavigationMenuItem({ className, ...props }) {
|
|
41
|
+
return (_jsx(NavigationMenuPrimitive.Item, { "data-slot": "navigation-menu-item", className: cn('relative', className), ...props }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The control that opens a group, and the only Tab stop for it.
|
|
45
|
+
*
|
|
46
|
+
* It is a real button and it owns the expanded state, so a reader knows the
|
|
47
|
+
* group is open before they enter it. The chevron rotates on the open state
|
|
48
|
+
* rather than on a timer, which is the only motion here: something happened and
|
|
49
|
+
* the reader can see that it did.
|
|
50
|
+
*/
|
|
51
|
+
function NavigationMenuTrigger({ className, children, ...props }) {
|
|
52
|
+
return (_jsxs(NavigationMenuPrimitive.Trigger, { "data-slot": "navigation-menu-trigger", className: cn('text-foreground group inline-flex h-9 items-center gap-1.5 rounded-md px-3 text-sm font-medium whitespace-nowrap outline-none', 'transition-[color,background-color,box-shadow] duration-fast ease-out', 'hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring focus-visible:ring-[3px]', 'data-[popup-open]:bg-accent/60 data-[popup-open]:text-accent-foreground', className), ...props, children: [children, _jsx(NavigationMenuIcon, { children: _jsx(ChevronDownIcon, { className: "size-4 transition-transform duration-base ease-out group-data-[popup-open]:rotate-180" }) })] }));
|
|
53
|
+
}
|
|
54
|
+
/** The group of links a trigger opens. */
|
|
55
|
+
function NavigationMenuContent({ className, ...props }) {
|
|
56
|
+
return (_jsx(NavigationMenuPrimitive.Content, { "data-slot": "navigation-menu-content", className: cn('p-2', className), ...props }));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One destination, rendered as a native anchor.
|
|
60
|
+
*
|
|
61
|
+
* This is the only interactive leaf the Component ships, and the reason is the
|
|
62
|
+
* difference between a link and a command: a link carries an address, so the
|
|
63
|
+
* browser can open it in a new tab, copy it, and a crawler can follow it. A
|
|
64
|
+
* command has none of those, and a header that renders commands where a reader
|
|
65
|
+
* expected destinations is a header whose links cannot be shared.
|
|
66
|
+
*/
|
|
67
|
+
function NavigationMenuLink({ className, active, ...props }) {
|
|
68
|
+
return (_jsx(NavigationMenuPrimitive.Link, { "data-slot": "navigation-menu-link", active: active, className: cn('hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring focus-visible:ring-[3px] block rounded-md px-3 py-2 text-sm outline-none', 'transition-[color,background-color,box-shadow] duration-fast ease-out', 'data-[active]:bg-accent data-[active]:text-accent-foreground data-[active]:font-medium', className), ...props }));
|
|
69
|
+
}
|
|
70
|
+
/** The chevron slot, so a caller can put their own mark beside a trigger's words. */
|
|
71
|
+
function NavigationMenuIcon({ className, ...props }) {
|
|
72
|
+
return (_jsx(NavigationMenuPrimitive.Icon, { "data-slot": "navigation-menu-icon", className: cn('flex size-4 items-center justify-center', className), ...props }));
|
|
73
|
+
}
|
|
74
|
+
/** The portal the open group is rendered into. */
|
|
75
|
+
function NavigationMenuPortal({ ...props }) {
|
|
76
|
+
return _jsx(NavigationMenuPrimitive.Portal, { ...props });
|
|
77
|
+
}
|
|
78
|
+
/** The positioned wrapper around the open group. */
|
|
79
|
+
function NavigationMenuPositioner({ className, sideOffset = 8, alignOffset = 0, ...props }) {
|
|
80
|
+
return (_jsx(NavigationMenuPrimitive.Positioner, { "data-slot": "navigation-menu-positioner", sideOffset: sideOffset, alignOffset: alignOffset, className: cn('z-50', className), ...props }));
|
|
81
|
+
}
|
|
82
|
+
/** The surface the open group sits on. */
|
|
83
|
+
function NavigationMenuPopup({ className, ...props }) {
|
|
84
|
+
return (_jsx(NavigationMenuPrimitive.Popup, { "data-slot": "navigation-menu-popup", className: cn('bg-popover text-popover-foreground origin-(--transform-origin) rounded-lg border p-2 shadow-md outline-none', 'transition-[opacity,transform] duration-base ease-out', 'focus-visible:ring-ring focus-visible:ring-[3px]', 'data-[starting-style]:scale-95 data-[starting-style]:opacity-0', 'data-[ending-style]:scale-95 data-[ending-style]:opacity-0', className), ...props }));
|
|
85
|
+
}
|
|
86
|
+
/** The area one group's content is measured and animated in. */
|
|
87
|
+
function NavigationMenuViewport({ className, ...props }) {
|
|
88
|
+
return (_jsx(NavigationMenuPrimitive.Viewport, { "data-slot": "navigation-menu-viewport", className: cn('relative h-full w-full overflow-hidden', className), ...props }));
|
|
89
|
+
}
|
|
90
|
+
/** The scrim behind an open group, so the page behind does not read as live. */
|
|
91
|
+
function NavigationMenuBackdrop({ className, ...props }) {
|
|
92
|
+
return (_jsx(NavigationMenuPrimitive.Backdrop, { "data-slot": "navigation-menu-backdrop", className: cn('fixed inset-0 z-40 bg-background/40 transition-opacity duration-base ease-out data-[starting-style]:opacity-0 data-[ending-style]:opacity-0', className), ...props }));
|
|
93
|
+
}
|
|
94
|
+
export { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIcon, NavigationMenuPortal, NavigationMenuPositioner, NavigationMenuPopup, NavigationMenuViewport, NavigationMenuBackdrop, };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** The props the Number field accepts. */
|
|
2
|
+
export interface NumberFieldProps {
|
|
3
|
+
/**
|
|
4
|
+
* The accessible names of the two steppers.
|
|
5
|
+
*
|
|
6
|
+
* Required, and required as a pair, because a stepper is an icon and an icon has
|
|
7
|
+
* no name. The words are the caller's because they are sentences a reader hears,
|
|
8
|
+
* and a Component that chose them would be shipping English into every consumer's
|
|
9
|
+
* form and would get "increase" wrong for at least one of the two.
|
|
10
|
+
*/
|
|
11
|
+
labels: {
|
|
12
|
+
increment: string;
|
|
13
|
+
decrement: string;
|
|
14
|
+
};
|
|
15
|
+
/** The accessible name of the field, when no visible label is used. */
|
|
16
|
+
'aria-label'?: string;
|
|
17
|
+
/** The current value, when the field is controlled. */
|
|
18
|
+
value?: number | null;
|
|
19
|
+
/** The value the field starts at, for an uncontrolled field. */
|
|
20
|
+
defaultValue?: number | null;
|
|
21
|
+
/** Called with the value, or `null` when the field is emptied. */
|
|
22
|
+
onValueChange?: (value: number | null) => void;
|
|
23
|
+
/**
|
|
24
|
+
* The smallest value the field will hold.
|
|
25
|
+
*
|
|
26
|
+
* A value below it is clamped, by typing, by the steppers and by a paste alike.
|
|
27
|
+
* Without a minimum there is nothing to clamp against, so this and `max` are what
|
|
28
|
+
* turn a number field from a text field that happens to accept digits into a
|
|
29
|
+
* control that holds a range.
|
|
30
|
+
*/
|
|
31
|
+
min?: number;
|
|
32
|
+
/** The largest value the field will hold. */
|
|
33
|
+
max?: number;
|
|
34
|
+
/** The amount one press of a stepper changes the value by. @defaultValue 1 */
|
|
35
|
+
step?: number;
|
|
36
|
+
/**
|
|
37
|
+
* The unit shown after the number.
|
|
38
|
+
*
|
|
39
|
+
* A unit is a word in the reader's language and it is hidden from the
|
|
40
|
+
* accessibility tree, because a screen reader already says the unit when the
|
|
41
|
+
* caller puts it in the field's description. It is a prop because the Component
|
|
42
|
+
* does not know whether the reader measures in kilograms, pounds or seconds.
|
|
43
|
+
*/
|
|
44
|
+
unit?: string;
|
|
45
|
+
/** How the value is written for display: grouping, decimals, and so on. */
|
|
46
|
+
format?: Intl.NumberFormatOptions;
|
|
47
|
+
/** Whether a value must be entered before the form submits. */
|
|
48
|
+
required?: boolean;
|
|
49
|
+
/** Whether the field ignores interaction. */
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/** Whether the reader may see the value but not change it. */
|
|
52
|
+
readOnly?: boolean;
|
|
53
|
+
/** The form field name. The value is submitted under it. */
|
|
54
|
+
name?: string;
|
|
55
|
+
/** Identifies the form that owns the hidden input. */
|
|
56
|
+
form?: string;
|
|
57
|
+
/** The field's own id, so a label elsewhere can point at it. */
|
|
58
|
+
id?: string;
|
|
59
|
+
/** Layout only. */
|
|
60
|
+
className?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A numeric field that will not hold a value outside its own range.
|
|
64
|
+
*
|
|
65
|
+
* **Clamping is the Component, not the HTML.** A number field with a `max` that
|
|
66
|
+
* accepts a value past it is a field that will submit a number the caller has
|
|
67
|
+
* already said is impossible, and the browser's own range validation is no answer
|
|
68
|
+
* to that: it reports the problem after the reader has typed and leaves them with a
|
|
69
|
+
* value they cannot submit. So the value is clamped on every path that can produce
|
|
70
|
+
* one, typing, the steppers, a paste and a value that arrived from outside, and
|
|
71
|
+
* the field shows and submits only what is inside its range. Text that is not a
|
|
72
|
+
* number never becomes a value at all: the field refuses the character as it is
|
|
73
|
+
* typed, so a reader who mistypes a digit gets a shorter number rather than a
|
|
74
|
+
* number the server will reject.
|
|
75
|
+
*
|
|
76
|
+
* **It is one Component rather than a root, a group, an input and two steppers.**
|
|
77
|
+
* The range and the two ways of moving inside it are one contract, and a caller
|
|
78
|
+
* who composes the parts can leave out the steppers and get a field that clamps
|
|
79
|
+
* with no affordance to reach the middle of its own range. The parts Base UI owns
|
|
80
|
+
* are still what draw and what enforce; what this adds is the decision not to let
|
|
81
|
+
* them be rearranged into something else.
|
|
82
|
+
*
|
|
83
|
+
* **A value outside the range is clamped as it arrives rather than refused.** A
|
|
84
|
+
* controlled field whose `value` prop is out of range displays the clamped number,
|
|
85
|
+
* because the alternative is a field showing one number and submitting another.
|
|
86
|
+
* `onValueChange` is the authority: a consumer whose own state holds an
|
|
87
|
+
* out-of-range number has to take the clamped one back, and this Component will
|
|
88
|
+
* not rewrite a state it does not own.
|
|
89
|
+
*
|
|
90
|
+
* **The steppers are in the tab order.** Base UI takes them out of it, on the
|
|
91
|
+
* reasoning that the arrow keys already step the value and one field should be one
|
|
92
|
+
* Tab stop. A control that is announced and cannot be reached is worse than a
|
|
93
|
+
* second stop, so they are put back: three stops for one value, and the arrow keys
|
|
94
|
+
* still work for a reader who is typing through the form.
|
|
95
|
+
*
|
|
96
|
+
* **Empty is a value.** The field reports `null` when it is cleared, rather than
|
|
97
|
+
* zero, because zero is a number a reader may have meant and an empty field is
|
|
98
|
+
* not. A consumer that treats `null` as zero has made a decision this Component
|
|
99
|
+
* refuses to make for it.
|
|
100
|
+
*
|
|
101
|
+
* **The unit is decoration.** It is hidden from the accessibility tree and the
|
|
102
|
+
* caller describes the field once, in words, where a screen reader will reach it.
|
|
103
|
+
* Two announcements of the same unit is one too many, and the visible one is the
|
|
104
|
+
* one that was written in the reader's language by the reader's own product.
|
|
105
|
+
*
|
|
106
|
+
* Reachable by Tab, moved by the arrow keys and by its two steppers, and the whole
|
|
107
|
+
* field reads as one surface: one frame, one border, one focus colour.
|
|
108
|
+
*/
|
|
109
|
+
declare function NumberField({ labels, 'aria-label': ariaLabel, value, defaultValue, onValueChange, min, max, step, unit, format, required, disabled, readOnly, name, form, id, className, }: NumberFieldProps): import("react").JSX.Element;
|
|
110
|
+
export { NumberField };
|
|
111
|
+
//# sourceMappingURL=number-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number-field.d.ts","sourceRoot":"","sources":["../../../src/components/ui/number-field.tsx"],"names":[],"mappings":"AAOA,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,kEAAkE;IAClE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC9C;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,MAAM,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAA;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,iBAAS,WAAW,CAAC,EACnB,MAAM,EACN,YAAY,EAAE,SAAS,EACvB,KAAK,EACL,YAAmB,EACnB,aAAa,EACb,GAAG,EACH,GAAG,EACH,IAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAgB,EAChB,QAAgB,EAChB,QAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,EAAE,EACF,SAAS,GACV,EAAE,gBAAgB,+BAsFlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { NumberField as NumberFieldPrimitive } from '@base-ui/react/number-field';
|
|
4
|
+
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
|
|
5
|
+
import { cn } from '../../lib/utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* The value inside the field's own range, or nothing.
|
|
8
|
+
*
|
|
9
|
+
* Base UI clamps what the reader types and what the steppers reach, but it does
|
|
10
|
+
* not clamp a value that was already outside the range when it arrived, so this is
|
|
11
|
+
* the Component's own half of the promise. A field that accepted it would display
|
|
12
|
+
* and submit a number the caller has already said is impossible, and the browser's
|
|
13
|
+
* range validation answers that after the reader has typed, which is too late to
|
|
14
|
+
* be a control and too late to be a form.
|
|
15
|
+
*/
|
|
16
|
+
function clamp(value, min, max) {
|
|
17
|
+
if (value === null || value === undefined || !Number.isFinite(value))
|
|
18
|
+
return null;
|
|
19
|
+
let out = value;
|
|
20
|
+
if (typeof min === 'number' && out < min)
|
|
21
|
+
out = min;
|
|
22
|
+
if (typeof max === 'number' && out > max)
|
|
23
|
+
out = max;
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A numeric field that will not hold a value outside its own range.
|
|
28
|
+
*
|
|
29
|
+
* **Clamping is the Component, not the HTML.** A number field with a `max` that
|
|
30
|
+
* accepts a value past it is a field that will submit a number the caller has
|
|
31
|
+
* already said is impossible, and the browser's own range validation is no answer
|
|
32
|
+
* to that: it reports the problem after the reader has typed and leaves them with a
|
|
33
|
+
* value they cannot submit. So the value is clamped on every path that can produce
|
|
34
|
+
* one, typing, the steppers, a paste and a value that arrived from outside, and
|
|
35
|
+
* the field shows and submits only what is inside its range. Text that is not a
|
|
36
|
+
* number never becomes a value at all: the field refuses the character as it is
|
|
37
|
+
* typed, so a reader who mistypes a digit gets a shorter number rather than a
|
|
38
|
+
* number the server will reject.
|
|
39
|
+
*
|
|
40
|
+
* **It is one Component rather than a root, a group, an input and two steppers.**
|
|
41
|
+
* The range and the two ways of moving inside it are one contract, and a caller
|
|
42
|
+
* who composes the parts can leave out the steppers and get a field that clamps
|
|
43
|
+
* with no affordance to reach the middle of its own range. The parts Base UI owns
|
|
44
|
+
* are still what draw and what enforce; what this adds is the decision not to let
|
|
45
|
+
* them be rearranged into something else.
|
|
46
|
+
*
|
|
47
|
+
* **A value outside the range is clamped as it arrives rather than refused.** A
|
|
48
|
+
* controlled field whose `value` prop is out of range displays the clamped number,
|
|
49
|
+
* because the alternative is a field showing one number and submitting another.
|
|
50
|
+
* `onValueChange` is the authority: a consumer whose own state holds an
|
|
51
|
+
* out-of-range number has to take the clamped one back, and this Component will
|
|
52
|
+
* not rewrite a state it does not own.
|
|
53
|
+
*
|
|
54
|
+
* **The steppers are in the tab order.** Base UI takes them out of it, on the
|
|
55
|
+
* reasoning that the arrow keys already step the value and one field should be one
|
|
56
|
+
* Tab stop. A control that is announced and cannot be reached is worse than a
|
|
57
|
+
* second stop, so they are put back: three stops for one value, and the arrow keys
|
|
58
|
+
* still work for a reader who is typing through the form.
|
|
59
|
+
*
|
|
60
|
+
* **Empty is a value.** The field reports `null` when it is cleared, rather than
|
|
61
|
+
* zero, because zero is a number a reader may have meant and an empty field is
|
|
62
|
+
* not. A consumer that treats `null` as zero has made a decision this Component
|
|
63
|
+
* refuses to make for it.
|
|
64
|
+
*
|
|
65
|
+
* **The unit is decoration.** It is hidden from the accessibility tree and the
|
|
66
|
+
* caller describes the field once, in words, where a screen reader will reach it.
|
|
67
|
+
* Two announcements of the same unit is one too many, and the visible one is the
|
|
68
|
+
* one that was written in the reader's language by the reader's own product.
|
|
69
|
+
*
|
|
70
|
+
* Reachable by Tab, moved by the arrow keys and by its two steppers, and the whole
|
|
71
|
+
* field reads as one surface: one frame, one border, one focus colour.
|
|
72
|
+
*/
|
|
73
|
+
function NumberField({ labels, 'aria-label': ariaLabel, value, defaultValue = null, onValueChange, min, max, step = 1, unit, format, required = false, disabled = false, readOnly = false, name, form, id, className, }) {
|
|
74
|
+
return (_jsx(NumberFieldPrimitive.Root, { "data-slot": "number-field", className: cn('w-full', className), ...(value === undefined
|
|
75
|
+
? { defaultValue: clamp(defaultValue, min, max) ?? undefined }
|
|
76
|
+
: { value: clamp(value, min, max) }), onValueChange: (next) => onValueChange?.(next),
|
|
77
|
+
// The id belongs to the root, not to the input, because Base UI's steppers
|
|
78
|
+
// point their `aria-controls` at it and a dangling reference is a violation
|
|
79
|
+
// that reads, in a screen reader, as a control pointing at nothing.
|
|
80
|
+
id: id, min: min, max: max, step: step, required: required, disabled: disabled, readOnly: readOnly, name: name, ...(form === undefined ? null : { form }), ...(format === undefined ? null : { format }), children: _jsxs("div", { "data-slot": "number-field-group", className: cn('border-input bg-background focus-within:border-ring flex w-full items-stretch rounded-md border shadow-xs', 'transition-[color,box-shadow] duration-fast ease-out', 'has-[input:disabled]:opacity-50'), children: [_jsx(NumberFieldPrimitive.Input, { "data-slot": "number-field-input", "aria-label": ariaLabel, className: cn('placeholder:text-muted-foreground flex h-9 min-w-0 flex-1 border-0 bg-transparent px-3 py-1 text-base shadow-none outline-none md:text-sm', 'focus-visible:border-0 focus-visible:ring-ring focus-visible:ring-[3px]') }), unit === undefined ? null : (_jsx("span", { "data-slot": "number-field-unit", "aria-hidden": "true", className: "text-muted-foreground flex shrink-0 items-center pr-2 text-sm", children: unit })), _jsxs("div", { "data-slot": "number-field-stepper", className: "border-input flex shrink-0 flex-col border-s", children: [_jsx(NumberFieldPrimitive.Increment, { "data-slot": "number-field-increment", "aria-label": labels.increment,
|
|
81
|
+
// Put back into the tab order on purpose; see the JSDoc.
|
|
82
|
+
tabIndex: 0, className: cn('text-muted-foreground hover:bg-accent hover:text-accent-foreground flex h-1/2 items-center justify-center border-b px-2 outline-none', 'transition-colors duration-fast ease-out focus-visible:ring-ring focus-visible:ring-[3px]', 'data-[disabled]:pointer-events-none data-[disabled]:opacity-40'), children: _jsx(ChevronUpIcon, { className: "size-3.5" }) }), _jsx(NumberFieldPrimitive.Decrement, { "data-slot": "number-field-decrement", "aria-label": labels.decrement, tabIndex: 0, className: cn('text-muted-foreground hover:bg-accent hover:text-accent-foreground flex h-1/2 items-center justify-center px-2 outline-none', 'transition-colors duration-fast ease-out focus-visible:ring-ring focus-visible:ring-[3px]', 'data-[disabled]:pointer-events-none data-[disabled]:opacity-40'), children: _jsx(ChevronDownIcon, { className: "size-3.5" }) })] })] }) }));
|
|
83
|
+
}
|
|
84
|
+
export { NumberField };
|