@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,127 @@
|
|
|
1
|
+
/** How the code is drawn in groups, for a code the reader sees written in groups. */
|
|
2
|
+
export interface OneTimeCodeGrouping {
|
|
3
|
+
/** The zero-based indices of the segments a separator is drawn after. */
|
|
4
|
+
readonly after: readonly number[];
|
|
5
|
+
/** What the separator is. The caller's, because it is read by a person. */
|
|
6
|
+
readonly separator: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/** The props the One time code accepts. */
|
|
9
|
+
export interface OneTimeCodeProps {
|
|
10
|
+
/**
|
|
11
|
+
* The accessible name of the code.
|
|
12
|
+
*
|
|
13
|
+
* Required. A row of six single-character fields with no name is six anonymous
|
|
14
|
+
* fields, and a reader cannot say out loud which one they are in.
|
|
15
|
+
*/
|
|
16
|
+
label: string;
|
|
17
|
+
/** How many characters the code has. */
|
|
18
|
+
length: number;
|
|
19
|
+
/**
|
|
20
|
+
* The characters the code may contain.
|
|
21
|
+
*
|
|
22
|
+
* The caller's decision because it is theirs: a numeric code, an alphabet-only
|
|
23
|
+
* recovery code and an alphanumeric one-time token are three different things
|
|
24
|
+
* and a verification service decides which. A character outside the set is
|
|
25
|
+
* refused as it is typed or pasted rather than accepted and refused later, so
|
|
26
|
+
* the field never holds a value the server will reject.
|
|
27
|
+
*/
|
|
28
|
+
characters?: 'numeric' | 'alpha' | 'alphanumeric' | 'none';
|
|
29
|
+
/**
|
|
30
|
+
* A normaliser, run after the character set has been applied.
|
|
31
|
+
*
|
|
32
|
+
* For the rules the closed set cannot express: folding a pasted code to lower
|
|
33
|
+
* case, stripping a leading country code. It must be idempotent, because the
|
|
34
|
+
* field normalises again on every edit and again on the way out of one.
|
|
35
|
+
*/
|
|
36
|
+
normalize?: (value: string) => string;
|
|
37
|
+
/**
|
|
38
|
+
* The accessible name of one segment, receiving its zero-based index.
|
|
39
|
+
*
|
|
40
|
+
* Without it every segment is named by the field's own label, which is enough to
|
|
41
|
+
* satisfy the form but not to tell a reader which of the six they are in. This
|
|
42
|
+
* Component does not know the reader's words for a position, so it asks.
|
|
43
|
+
*/
|
|
44
|
+
segmentLabel?: (index: number) => string;
|
|
45
|
+
/** How the code is drawn in groups, when it is read in groups. */
|
|
46
|
+
grouping?: OneTimeCodeGrouping;
|
|
47
|
+
/** The code, when the field is controlled. */
|
|
48
|
+
value?: string;
|
|
49
|
+
/** The code the field starts with, for an uncontrolled field. */
|
|
50
|
+
defaultValue?: string;
|
|
51
|
+
/** Called with the code as it changes. */
|
|
52
|
+
onValueChange?: (value: string) => void;
|
|
53
|
+
/** Called once every segment is filled, including by a paste that fills them all. */
|
|
54
|
+
onComplete?: (value: string) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Called with the text the reader attempted when part of it was refused.
|
|
57
|
+
*
|
|
58
|
+
* The attempted text, not a verdict. Whether a refused character deserves a word
|
|
59
|
+
* to the reader is the caller's decision, and this Component ships no sentence
|
|
60
|
+
* into a consumer's product.
|
|
61
|
+
*/
|
|
62
|
+
onInvalid?: (attempted: string) => void;
|
|
63
|
+
/** Whether the field submits its form as soon as the code is complete. */
|
|
64
|
+
autoSubmit?: boolean;
|
|
65
|
+
/** Whether the characters are hidden as they are typed. */
|
|
66
|
+
mask?: boolean;
|
|
67
|
+
/** Whether the field ignores interaction. */
|
|
68
|
+
disabled?: boolean;
|
|
69
|
+
/** Whether a code must be entered before the form submits. */
|
|
70
|
+
required?: boolean;
|
|
71
|
+
/** Whether the reader may see the code but not change it. */
|
|
72
|
+
readOnly?: boolean;
|
|
73
|
+
/** The form field name. The code is submitted under it. */
|
|
74
|
+
name?: string;
|
|
75
|
+
/** Identifies the form that owns the hidden input. */
|
|
76
|
+
form?: string;
|
|
77
|
+
/** The first segment's id. The rest derive from it. */
|
|
78
|
+
id?: string;
|
|
79
|
+
/** Layout only. */
|
|
80
|
+
className?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A verification code entered one segment at a time.
|
|
84
|
+
*
|
|
85
|
+
* **A paste of the whole code lands in every segment.** A reader who has the code
|
|
86
|
+
* on their clipboard has already done the hard part, and a field that accepted one
|
|
87
|
+
* character per paste would make them retype it one box at a time, which is what
|
|
88
|
+
* they are on their phone to avoid. So a paste into the first segment is
|
|
89
|
+
* distributed across the ones after it and `onComplete` fires once, from the paste,
|
|
90
|
+
* rather than after six keystrokes.
|
|
91
|
+
*
|
|
92
|
+
* **A refused character is refused, not stored.** Whitespace is dropped and
|
|
93
|
+
* anything outside `characters` is refused, whether it was typed or pasted, so
|
|
94
|
+
* "123 456" pastes as `123456` and `12345a6` pastes as `123456`. The alternative,
|
|
95
|
+
* accepting the text and rejecting it at the server, gives the reader a field
|
|
96
|
+
* that looks right and a form that does not submit.
|
|
97
|
+
*
|
|
98
|
+
* **The code is one value, not six.** The segments are six views of one string of
|
|
99
|
+
* `length` characters, so Backspace removes a character from the string and the
|
|
100
|
+
* characters after it close up: on `123456`, Backspace in the third segment leaves
|
|
101
|
+
* `12456`. Six boxes that each had to be emptied by hand would need a rule for
|
|
102
|
+
* what Backspace on an empty box means, and every version of that rule is wrong
|
|
103
|
+
* for somebody. The rule that does nothing traps a reader who is correcting a
|
|
104
|
+
* digit; the rule that clears the box before deletes a character they did not ask
|
|
105
|
+
* to delete.
|
|
106
|
+
*
|
|
107
|
+
* **The character set, the normaliser and the words are all the caller's.** Which
|
|
108
|
+
* characters a code may contain is decided by the service that issued it, folding
|
|
109
|
+
* case is a rule only the caller knows, and every segment's accessible name is a
|
|
110
|
+
* phrase. Nothing here is a table of words or a regular expression.
|
|
111
|
+
*
|
|
112
|
+
* **The first segment is named by a real `<label>`, and it is hidden.** Base UI
|
|
113
|
+
* strips `aria-label` from that one segment on purpose: it is the segment the
|
|
114
|
+
* platform autofills, and the label element is what names the autofill prompt as
|
|
115
|
+
* well as the field. This Component renders that label visually hidden, because a
|
|
116
|
+
* label above a row of six boxes is the caller's to place. Pass `id` and put your
|
|
117
|
+
* own `Label` on the first segment, and this one steps aside.
|
|
118
|
+
*
|
|
119
|
+
* **One Tab stop for the whole code.** A reader reaches the first segment and the
|
|
120
|
+
* rest follow as they type, so the code is one stop in a form rather than six, and
|
|
121
|
+
* the segments are a named group rather than six fields in a row. It is built on
|
|
122
|
+
* Base UI's OTP field, which owns the segmentation, the clamping to `length` and
|
|
123
|
+
* the autofill hint the platform needs for a one-time code.
|
|
124
|
+
*/
|
|
125
|
+
declare function OneTimeCode({ label, length, characters, normalize, segmentLabel, grouping, value, defaultValue, onValueChange, onComplete, onInvalid, autoSubmit, mask, disabled, required, readOnly, name, form, id, className, }: OneTimeCodeProps): import("react").JSX.Element;
|
|
126
|
+
export { OneTimeCode };
|
|
127
|
+
//# sourceMappingURL=one-time-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"one-time-code.d.ts","sourceRoot":"","sources":["../../../src/components/ui/one-time-code.tsx"],"names":[],"mappings":"AAOA,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAA;CACpC;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,cAAc,GAAG,MAAM,CAAA;IAC1D;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACxC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,qFAAqF;IACrF,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,iBAAS,WAAW,CAAC,EACnB,KAAK,EACL,MAAM,EACN,UAAsB,EACtB,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,aAAa,EACb,UAAU,EACV,SAAS,EACT,UAAkB,EAClB,IAAY,EACZ,QAAgB,EAChB,QAAgB,EAChB,QAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,EAAE,EACF,SAAS,GACV,EAAE,gBAAgB,+BA0ElB;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { OTPField as OTPFieldPrimitive } from '@base-ui/react/otp-field';
|
|
4
|
+
import { Fragment, useId } from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* A verification code entered one segment at a time.
|
|
8
|
+
*
|
|
9
|
+
* **A paste of the whole code lands in every segment.** A reader who has the code
|
|
10
|
+
* on their clipboard has already done the hard part, and a field that accepted one
|
|
11
|
+
* character per paste would make them retype it one box at a time, which is what
|
|
12
|
+
* they are on their phone to avoid. So a paste into the first segment is
|
|
13
|
+
* distributed across the ones after it and `onComplete` fires once, from the paste,
|
|
14
|
+
* rather than after six keystrokes.
|
|
15
|
+
*
|
|
16
|
+
* **A refused character is refused, not stored.** Whitespace is dropped and
|
|
17
|
+
* anything outside `characters` is refused, whether it was typed or pasted, so
|
|
18
|
+
* "123 456" pastes as `123456` and `12345a6` pastes as `123456`. The alternative,
|
|
19
|
+
* accepting the text and rejecting it at the server, gives the reader a field
|
|
20
|
+
* that looks right and a form that does not submit.
|
|
21
|
+
*
|
|
22
|
+
* **The code is one value, not six.** The segments are six views of one string of
|
|
23
|
+
* `length` characters, so Backspace removes a character from the string and the
|
|
24
|
+
* characters after it close up: on `123456`, Backspace in the third segment leaves
|
|
25
|
+
* `12456`. Six boxes that each had to be emptied by hand would need a rule for
|
|
26
|
+
* what Backspace on an empty box means, and every version of that rule is wrong
|
|
27
|
+
* for somebody. The rule that does nothing traps a reader who is correcting a
|
|
28
|
+
* digit; the rule that clears the box before deletes a character they did not ask
|
|
29
|
+
* to delete.
|
|
30
|
+
*
|
|
31
|
+
* **The character set, the normaliser and the words are all the caller's.** Which
|
|
32
|
+
* characters a code may contain is decided by the service that issued it, folding
|
|
33
|
+
* case is a rule only the caller knows, and every segment's accessible name is a
|
|
34
|
+
* phrase. Nothing here is a table of words or a regular expression.
|
|
35
|
+
*
|
|
36
|
+
* **The first segment is named by a real `<label>`, and it is hidden.** Base UI
|
|
37
|
+
* strips `aria-label` from that one segment on purpose: it is the segment the
|
|
38
|
+
* platform autofills, and the label element is what names the autofill prompt as
|
|
39
|
+
* well as the field. This Component renders that label visually hidden, because a
|
|
40
|
+
* label above a row of six boxes is the caller's to place. Pass `id` and put your
|
|
41
|
+
* own `Label` on the first segment, and this one steps aside.
|
|
42
|
+
*
|
|
43
|
+
* **One Tab stop for the whole code.** A reader reaches the first segment and the
|
|
44
|
+
* rest follow as they type, so the code is one stop in a form rather than six, and
|
|
45
|
+
* the segments are a named group rather than six fields in a row. It is built on
|
|
46
|
+
* Base UI's OTP field, which owns the segmentation, the clamping to `length` and
|
|
47
|
+
* the autofill hint the platform needs for a one-time code.
|
|
48
|
+
*/
|
|
49
|
+
function OneTimeCode({ label, length, characters = 'numeric', normalize, segmentLabel, grouping, value, defaultValue, onValueChange, onComplete, onInvalid, autoSubmit = false, mask = false, disabled = false, required = false, readOnly = false, name, form, id, className, }) {
|
|
50
|
+
const generated = useId();
|
|
51
|
+
const firstId = id ?? generated;
|
|
52
|
+
const boundary = (index) => grouping?.after.includes(index) === true;
|
|
53
|
+
return (_jsxs(OTPFieldPrimitive.Root, { "data-slot": "one-time-code", className: cn('flex items-center justify-center gap-2', className), length: length, validationType: characters, ...(normalize === undefined ? null : { normalizeValue: normalize }), ...(value === undefined ? {} : { value }), ...(defaultValue === undefined ? {} : { defaultValue }), onValueChange: (next) => onValueChange?.(next), onValueComplete: (next) => onComplete?.(next), onValueInvalid: (attempted) => onInvalid?.(attempted), autoSubmit: autoSubmit, mask: mask, disabled: disabled, required: required, readOnly: readOnly, name: name, ...(form === undefined ? null : { form }), id: firstId, "aria-label": label, children: [id === undefined ? (_jsx("label", { htmlFor: firstId, className: "sr-only", children: label })) : null, Array.from({ length }, (_, index) => (_jsxs(Fragment, { children: [_jsx(OTPFieldPrimitive.Input, { "data-slot": "one-time-code-input", ...(index === 0
|
|
54
|
+
? null
|
|
55
|
+
: { 'aria-label': segmentLabel === undefined ? label : segmentLabel(index) }), className: cn('border-input bg-background text-foreground h-11 w-9 rounded-md border text-center text-base tabular-nums outline-none', 'transition-[color,box-shadow] duration-fast ease-out', 'focus-visible:border-ring focus-visible:ring-ring focus-visible:ring-[3px]', 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50', 'pointer-coarse:size-11') }), boundary(index) ? (_jsx("span", { "data-slot": "one-time-code-separator", "aria-hidden": "true", className: "text-muted-foreground shrink-0 text-base", children: grouping?.separator })) : null] }, index)))] }));
|
|
56
|
+
}
|
|
57
|
+
export { OneTimeCode };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
|
|
3
|
-
import { cn } from '../../lib/utils';
|
|
3
|
+
import { cn } from '../../lib/utils.js';
|
|
4
4
|
/**
|
|
5
5
|
* The control that moves between pages of a paged collection.
|
|
6
6
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { Popover as PopoverPrimitive } from '@base-ui/react/popover';
|
|
4
|
-
import { cn } from '../../lib/utils';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
5
|
/**
|
|
6
6
|
* A floating surface opened by a trigger.
|
|
7
7
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentProps } from 'react';
|
|
2
|
-
import { type ProductMarkProps, type ProductMarkSize } from './product-mark';
|
|
2
|
+
import { type ProductMarkProps, type ProductMarkSize } from './product-mark.js';
|
|
3
3
|
/**
|
|
4
4
|
* One product in the set a switcher moves between.
|
|
5
5
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { ProductMark } from './product-mark';
|
|
3
|
-
import { cn } from '../../lib/utils';
|
|
2
|
+
import { ProductMark } from './product-mark.js';
|
|
3
|
+
import { cn } from '../../lib/utils.js';
|
|
4
4
|
/**
|
|
5
5
|
* A switch between the members of a set of products.
|
|
6
6
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { Progress as ProgressPrimitive } from '@base-ui/react/progress';
|
|
4
|
-
import { cn } from '../../lib/utils';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
5
|
/**
|
|
6
6
|
* A bar that reports how far a task has progressed.
|
|
7
7
|
*
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { Radio as RadioPrimitive } from '@base-ui/react/radio';
|
|
4
4
|
import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group';
|
|
5
5
|
import { CircleIcon } from 'lucide-react';
|
|
6
|
-
import { cn } from '../../lib/utils';
|
|
6
|
+
import { cn } from '../../lib/utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* A set of radio items where exactly one may be chosen.
|
|
9
9
|
*
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The divider's position, as a percentage along the group's main axis.
|
|
4
|
+
*
|
|
5
|
+
* A percentage rather than a pixel count because the group is a proportion of
|
|
6
|
+
* whatever the page gives it, and a divider measured in pixels is wrong the
|
|
7
|
+
* moment the window is resized or the group is put in a narrower column. A
|
|
8
|
+
* percentage is wrong for one case and that case is a pane whose content must
|
|
9
|
+
* not reflow, and a caller who needs one is measuring their content rather than
|
|
10
|
+
* asking for a split.
|
|
11
|
+
*/
|
|
12
|
+
type Position = number;
|
|
13
|
+
/** How the panes are laid out, and therefore which pair of arrows moves a divider. */
|
|
14
|
+
type ResizableOrientation = 'horizontal' | 'vertical';
|
|
15
|
+
/** The props the Resizable group accepts. */
|
|
16
|
+
export interface ResizableProps extends Omit<ComponentProps<'div'>, 'onChange'> {
|
|
17
|
+
/**
|
|
18
|
+
* The accessible name of the group.
|
|
19
|
+
*
|
|
20
|
+
* Required rather than defaulted, because a resizable is announced as two
|
|
21
|
+
* panes with a divider between them, and two unnamed panes side by side are a
|
|
22
|
+
* page where a reader is told "region" twice and cannot tell which is which.
|
|
23
|
+
*/
|
|
24
|
+
label: string;
|
|
25
|
+
/** Whether the panes sit side by side or stacked. @defaultValue horizontal */
|
|
26
|
+
orientation?: ResizableOrientation;
|
|
27
|
+
/**
|
|
28
|
+
* The divider's starting position, as a percentage along the main axis.
|
|
29
|
+
* @defaultValue 50
|
|
30
|
+
*/
|
|
31
|
+
defaultPosition?: Position;
|
|
32
|
+
/**
|
|
33
|
+
* The controlled divider position, as a percentage along the main axis.
|
|
34
|
+
*
|
|
35
|
+
* The position is a prop and a callback rather than something the group keeps
|
|
36
|
+
* to itself, and that is the decision this Component makes: **a split is
|
|
37
|
+
* worth remembering, and this Component will not remember it for you.** A
|
|
38
|
+
* reader who drags a sidebar to where their work fits and comes back to a
|
|
39
|
+
* reset split has been told the preference did not count, and the honest fix is
|
|
40
|
+
* the caller reading this and persisting it, because a design system cannot
|
|
41
|
+
* know a reader's storage, cannot know which of a reader's panes this one is,
|
|
42
|
+
* and cannot know whether a second reader of the same account wants the same
|
|
43
|
+
* number. Pass the stored value back as `position` and this is a controlled
|
|
44
|
+
* group with no storage of its own anywhere.
|
|
45
|
+
*/
|
|
46
|
+
position?: Position;
|
|
47
|
+
/** Called when the divider moves, with the new position. */
|
|
48
|
+
onPositionChange?: (position: Position) => void;
|
|
49
|
+
/** Whether the divider ignores user interaction. @defaultValue false */
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/** The panes and the divider between them. */
|
|
52
|
+
children?: ReactNode;
|
|
53
|
+
}
|
|
54
|
+
/** The props ResizablePanel accepts. */
|
|
55
|
+
export interface ResizablePanelProps extends ComponentProps<'div'> {
|
|
56
|
+
/** The pane's share of the group, as a percentage. @defaultValue 50 */
|
|
57
|
+
size?: Position;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Two panes with a divider between them that both a pointer and a keyboard move.
|
|
61
|
+
*
|
|
62
|
+
* **The divider is the Component, and everything about it is a decision a
|
|
63
|
+
* consumer would otherwise make badly.** It is an ARIA separator that is
|
|
64
|
+
* focusable, so the split is operable without a pointer at all, and it reports
|
|
65
|
+
* `aria-valuenow` as the position along the split, so a reader is told where the
|
|
66
|
+
* divider is rather than only being able to feel it. Arrow keys move it by a
|
|
67
|
+
* small step, Home takes it to one end and End to the other, and both are
|
|
68
|
+
* clamped, so a reader who holds the key stops at the end rather than pushing a
|
|
69
|
+
* pane to nothing and being unable to get it back.
|
|
70
|
+
*
|
|
71
|
+
* **It remembers nothing across a reload, and that is deliberate.** The position
|
|
72
|
+
* is a prop and a callback, so a caller who wants a reader's split to survive a
|
|
73
|
+
* reload persists it and passes it back, and a design system that wrote to a
|
|
74
|
+
* reader's storage unasked would be making a claim about the product it does not
|
|
75
|
+
* know. The failure this prevents is a sidebar that silently resets, which is the
|
|
76
|
+
* same class of defect as a control that forgets a setting the reader chose and
|
|
77
|
+
* nobody can explain.
|
|
78
|
+
*
|
|
79
|
+
* **A divider has a name and it is the caller's word.** There is no default,
|
|
80
|
+
* because a default would be the same word on every divider on every page and
|
|
81
|
+
* would be true of none of them.
|
|
82
|
+
*
|
|
83
|
+
* This is the one Component here that is not composed on a Base UI primitive,
|
|
84
|
+
* because Base UI 1.8.0 ships none: the package has no `Resizable` in it, so the
|
|
85
|
+
* separator role, the focusable-divider keyboard model and the drag are authored
|
|
86
|
+
* here rather than wrapped. Every other overlay in this batch is composed, and a
|
|
87
|
+
* hand-rolled focus trap or portal would be a second and worse answer; a divider
|
|
88
|
+
* is none of those five things.
|
|
89
|
+
*/
|
|
90
|
+
declare function Resizable({ className, label, orientation, defaultPosition, position: positionProp, onPositionChange, disabled, ...props }: ResizableProps): import("react").JSX.Element;
|
|
91
|
+
/**
|
|
92
|
+
* One pane of the split.
|
|
93
|
+
*
|
|
94
|
+
* `size` is a prop rather than something computed from the content, because a
|
|
95
|
+
* pane whose width depends on what is in it is how a split ends up reporting a
|
|
96
|
+
* different position after a reload than before one, and the divider is measured
|
|
97
|
+
* against a box the reader cannot predict.
|
|
98
|
+
*/
|
|
99
|
+
declare function ResizablePanel({ className, size, style, ...props }: ResizablePanelProps): import("react").JSX.Element;
|
|
100
|
+
/**
|
|
101
|
+
* The divider, and the whole of the Component's behaviour.
|
|
102
|
+
*
|
|
103
|
+
* A focusable ARIA separator with a value, so the split is operable and
|
|
104
|
+
* announced: `role="separator"`, `aria-valuenow` for the position, and
|
|
105
|
+
* `aria-orientation` for the axis, which is what tells a reader whether the left
|
|
106
|
+
* and right arrows or the up and down ones are the ones to press.
|
|
107
|
+
*/
|
|
108
|
+
declare function ResizableHandle({ className, label, onKeyDown, ...props }: ResizableHandleProps): import("react").JSX.Element;
|
|
109
|
+
/**
|
|
110
|
+
* The props ResizableHandle accepts.
|
|
111
|
+
*
|
|
112
|
+
* The `label` is required for the same reason the group's is, and it is a prop
|
|
113
|
+
* rather than a default because there is no honest default: a divider named
|
|
114
|
+
* "Resize" in a page with a file tree, a preview and a comment thread tells a
|
|
115
|
+
* reader nothing about which one they are holding, and the words that would
|
|
116
|
+
* tell them are the words for that product's panes.
|
|
117
|
+
*/
|
|
118
|
+
export interface ResizableHandleProps extends Omit<ComponentProps<'div'>, 'onChange'> {
|
|
119
|
+
/**
|
|
120
|
+
* The accessible name of this divider.
|
|
121
|
+
*
|
|
122
|
+
* Required. A divider with no name is announced as "separator", and a page
|
|
123
|
+
* with three of them is a page where a screen reader user cannot tell which
|
|
124
|
+
* one they are about to move.
|
|
125
|
+
*/
|
|
126
|
+
label: string;
|
|
127
|
+
}
|
|
128
|
+
export { Resizable, ResizablePanel, ResizableHandle };
|
|
129
|
+
//# sourceMappingURL=resizable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizable.d.ts","sourceRoot":"","sources":["../../../src/components/ui/resizable.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,cAAc,EAGnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AAId;;;;;;;;;GASG;AACH,KAAK,QAAQ,GAAG,MAAM,CAAA;AAEtB,sFAAsF;AACtF,KAAK,oBAAoB,GAAG,YAAY,GAAG,UAAU,CAAA;AAgDrD,6CAA6C;AAC7C,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC7E;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC;;;OAGG;IACH,eAAe,CAAC,EAAE,QAAQ,CAAA;IAC1B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC/C,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED,wCAAwC;AACxC,MAAM,WAAW,mBAAoB,SAAQ,cAAc,CAAC,KAAK,CAAC;IAChE,uEAAuE;IACvE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,KAAK,EACL,WAA0B,EAC1B,eAAoB,EACpB,QAAQ,EAAE,YAAY,EACtB,gBAAgB,EAChB,QAAgB,EAChB,GAAG,KAAK,EACT,EAAE,cAAc,+BA0FhB;AAED;;;;;;;GAOG;AACH,iBAAS,cAAc,CAAC,EAAE,SAAS,EAAE,IAAS,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,+BAoBrF;AAED;;;;;;;GAOG;AACH,iBAAS,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,oBAAoB,+BA8FvF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnF;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAA;CACd;AAED,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,CAAA"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
/** The window the divider may travel in, as a percentage of the group. */
|
|
6
|
+
const MIN = 10;
|
|
7
|
+
const MAX = 90;
|
|
8
|
+
/** How far one arrow-key press moves the divider, as a percentage of the group. */
|
|
9
|
+
const STEP = 5;
|
|
10
|
+
const ResizableContext = createContext(null);
|
|
11
|
+
/**
|
|
12
|
+
* The group context, which a pane and a divider are both children of.
|
|
13
|
+
*
|
|
14
|
+
* The part name is a machine value and lowercase on purpose: it reaches a
|
|
15
|
+
* developer in a console and never a reader, and the gate that refuses shipped
|
|
16
|
+
* copy cannot tell a part's own name from a sentence. The diagnostic is the
|
|
17
|
+
* house's own shape, an error thrown when a caller has composed a part outside
|
|
18
|
+
* the group it belongs to, and the useful half of it is what to do about it.
|
|
19
|
+
*/
|
|
20
|
+
function useResizableContext(part) {
|
|
21
|
+
const context = useContext(ResizableContext);
|
|
22
|
+
if (context === null) {
|
|
23
|
+
throw new Error(`Resizable: the ${part} must be rendered inside a Resizable, because the split is owned by the group and neither part can know it on its own.`);
|
|
24
|
+
}
|
|
25
|
+
return context;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Two panes with a divider between them that both a pointer and a keyboard move.
|
|
29
|
+
*
|
|
30
|
+
* **The divider is the Component, and everything about it is a decision a
|
|
31
|
+
* consumer would otherwise make badly.** It is an ARIA separator that is
|
|
32
|
+
* focusable, so the split is operable without a pointer at all, and it reports
|
|
33
|
+
* `aria-valuenow` as the position along the split, so a reader is told where the
|
|
34
|
+
* divider is rather than only being able to feel it. Arrow keys move it by a
|
|
35
|
+
* small step, Home takes it to one end and End to the other, and both are
|
|
36
|
+
* clamped, so a reader who holds the key stops at the end rather than pushing a
|
|
37
|
+
* pane to nothing and being unable to get it back.
|
|
38
|
+
*
|
|
39
|
+
* **It remembers nothing across a reload, and that is deliberate.** The position
|
|
40
|
+
* is a prop and a callback, so a caller who wants a reader's split to survive a
|
|
41
|
+
* reload persists it and passes it back, and a design system that wrote to a
|
|
42
|
+
* reader's storage unasked would be making a claim about the product it does not
|
|
43
|
+
* know. The failure this prevents is a sidebar that silently resets, which is the
|
|
44
|
+
* same class of defect as a control that forgets a setting the reader chose and
|
|
45
|
+
* nobody can explain.
|
|
46
|
+
*
|
|
47
|
+
* **A divider has a name and it is the caller's word.** There is no default,
|
|
48
|
+
* because a default would be the same word on every divider on every page and
|
|
49
|
+
* would be true of none of them.
|
|
50
|
+
*
|
|
51
|
+
* This is the one Component here that is not composed on a Base UI primitive,
|
|
52
|
+
* because Base UI 1.8.0 ships none: the package has no `Resizable` in it, so the
|
|
53
|
+
* separator role, the focusable-divider keyboard model and the drag are authored
|
|
54
|
+
* here rather than wrapped. Every other overlay in this batch is composed, and a
|
|
55
|
+
* hand-rolled focus trap or portal would be a second and worse answer; a divider
|
|
56
|
+
* is none of those five things.
|
|
57
|
+
*/
|
|
58
|
+
function Resizable({ className, label, orientation = 'horizontal', defaultPosition = 50, position: positionProp, onPositionChange, disabled = false, ...props }) {
|
|
59
|
+
const [internal, setInternal] = useState(defaultPosition);
|
|
60
|
+
const [dragging, setDragging] = useState(false);
|
|
61
|
+
const group = useRef(null);
|
|
62
|
+
const { ref: callerRef, ...rest } = props;
|
|
63
|
+
// Both refs, rather than mine and then the caller's. The drag measures the
|
|
64
|
+
// pointer against this element's box, so a caller who forwarded a ref and
|
|
65
|
+
// silently replaced ours would get a divider the keyboard moves and the pointer
|
|
66
|
+
// does not, which is a split that only half works.
|
|
67
|
+
const setGroup = useCallback((element) => {
|
|
68
|
+
group.current = element;
|
|
69
|
+
if (typeof callerRef === 'function')
|
|
70
|
+
callerRef(element);
|
|
71
|
+
else if (callerRef !== null && callerRef !== undefined)
|
|
72
|
+
callerRef.current = element;
|
|
73
|
+
}, [callerRef]);
|
|
74
|
+
const controlled = positionProp !== undefined;
|
|
75
|
+
const position = controlled ? positionProp : internal;
|
|
76
|
+
/**
|
|
77
|
+
* The position every move is measured from, kept in a ref rather than read
|
|
78
|
+
* from the rendered value.
|
|
79
|
+
*
|
|
80
|
+
* This is the whole of the keyboard model and it is not a style choice. A
|
|
81
|
+
* reader who holds ArrowRight produces four key events before React has
|
|
82
|
+
* re-rendered, and a move that read the rendered position each time would see
|
|
83
|
+
* the same number four times and travel one step instead of four. The divider
|
|
84
|
+
* would then feel broken to exactly the reader using it the way it is meant to
|
|
85
|
+
* be used, and a drag would run behind the pointer.
|
|
86
|
+
*/
|
|
87
|
+
const latest = useRef(position);
|
|
88
|
+
latest.current = position;
|
|
89
|
+
const moveTo = useCallback((next) => {
|
|
90
|
+
if (disabled)
|
|
91
|
+
return;
|
|
92
|
+
const clamped = Math.min(MAX, Math.max(MIN, next));
|
|
93
|
+
latest.current = clamped;
|
|
94
|
+
if (!controlled)
|
|
95
|
+
setInternal(clamped);
|
|
96
|
+
onPositionChange?.(clamped);
|
|
97
|
+
}, [controlled, disabled, onPositionChange]);
|
|
98
|
+
const moveBy = useCallback((delta) => {
|
|
99
|
+
if (disabled)
|
|
100
|
+
return;
|
|
101
|
+
moveTo(latest.current + delta);
|
|
102
|
+
}, [disabled, moveTo]);
|
|
103
|
+
const value = useMemo(() => ({
|
|
104
|
+
orientation,
|
|
105
|
+
position,
|
|
106
|
+
moveTo,
|
|
107
|
+
moveBy,
|
|
108
|
+
min: MIN,
|
|
109
|
+
max: MAX,
|
|
110
|
+
disabled,
|
|
111
|
+
dragging,
|
|
112
|
+
setDragging,
|
|
113
|
+
groupElement: () => group.current,
|
|
114
|
+
}), [orientation, position, moveTo, moveBy, disabled, dragging]);
|
|
115
|
+
return (_jsx(ResizableContext.Provider, { value: value, children: _jsx("div", { "data-slot": "resizable", role: "group", "aria-label": label, "data-orientation": orientation, "data-dragging": dragging ? 'true' : undefined, ref: setGroup, className: cn('flex h-full w-full', orientation === 'vertical' ? 'flex-col' : 'flex-row', 'data-[dragging]:select-none', className), ...rest }) }));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* One pane of the split.
|
|
119
|
+
*
|
|
120
|
+
* `size` is a prop rather than something computed from the content, because a
|
|
121
|
+
* pane whose width depends on what is in it is how a split ends up reporting a
|
|
122
|
+
* different position after a reload than before one, and the divider is measured
|
|
123
|
+
* against a box the reader cannot predict.
|
|
124
|
+
*/
|
|
125
|
+
function ResizablePanel({ className, size = 50, style, ...props }) {
|
|
126
|
+
const { orientation, disabled } = useResizableContext('panel');
|
|
127
|
+
return (_jsx("div", { "data-slot": "resizable-panel", "data-orientation": orientation, "data-disabled": disabled ? 'true' : undefined, style: {
|
|
128
|
+
flexBasis: `${size}%`,
|
|
129
|
+
flexGrow: 0,
|
|
130
|
+
flexShrink: 0,
|
|
131
|
+
minWidth: 0,
|
|
132
|
+
minHeight: 0,
|
|
133
|
+
...style,
|
|
134
|
+
}, className: cn('overflow-hidden', className), ...props }));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The divider, and the whole of the Component's behaviour.
|
|
138
|
+
*
|
|
139
|
+
* A focusable ARIA separator with a value, so the split is operable and
|
|
140
|
+
* announced: `role="separator"`, `aria-valuenow` for the position, and
|
|
141
|
+
* `aria-orientation` for the axis, which is what tells a reader whether the left
|
|
142
|
+
* and right arrows or the up and down ones are the ones to press.
|
|
143
|
+
*/
|
|
144
|
+
function ResizableHandle({ className, label, onKeyDown, ...props }) {
|
|
145
|
+
const { orientation, position, moveTo, moveBy, min, max, disabled, dragging, setDragging, groupElement, } = useResizableContext('divider');
|
|
146
|
+
const last = useRef(null);
|
|
147
|
+
const onPointerDown = (event) => {
|
|
148
|
+
if (disabled || event.button !== 0)
|
|
149
|
+
return;
|
|
150
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
151
|
+
// The pointer's own coordinate, and not the divider's, because a drag is a
|
|
152
|
+
// distance travelled and the first event is a press rather than a move.
|
|
153
|
+
last.current = orientation === 'horizontal' ? event.clientX : event.clientY;
|
|
154
|
+
setDragging(true);
|
|
155
|
+
};
|
|
156
|
+
const onPointerMove = (event) => {
|
|
157
|
+
if (last.current === null)
|
|
158
|
+
return;
|
|
159
|
+
const box = groupElement()?.getBoundingClientRect();
|
|
160
|
+
if (box === undefined)
|
|
161
|
+
return;
|
|
162
|
+
// Measured along the group's own axis, so a horizontal split follows the x
|
|
163
|
+
// and a vertical one follows the y, and a caller that passes the wrong
|
|
164
|
+
// `orientation` cannot get a divider that tracks the wrong coordinate.
|
|
165
|
+
const here = orientation === 'horizontal' ? event.clientX : event.clientY;
|
|
166
|
+
const delta = here - last.current;
|
|
167
|
+
const travel = orientation === 'horizontal' ? box.width : box.height;
|
|
168
|
+
if (travel === 0)
|
|
169
|
+
return;
|
|
170
|
+
moveBy((delta / travel) * 100);
|
|
171
|
+
last.current = here;
|
|
172
|
+
};
|
|
173
|
+
const endDrag = (event) => {
|
|
174
|
+
if (last.current === null)
|
|
175
|
+
return;
|
|
176
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
177
|
+
last.current = null;
|
|
178
|
+
setDragging(false);
|
|
179
|
+
};
|
|
180
|
+
const onKey = (event) => {
|
|
181
|
+
if (disabled)
|
|
182
|
+
return;
|
|
183
|
+
const decrease = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';
|
|
184
|
+
const increase = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';
|
|
185
|
+
if (event.key === decrease)
|
|
186
|
+
moveBy(-STEP);
|
|
187
|
+
else if (event.key === increase)
|
|
188
|
+
moveBy(STEP);
|
|
189
|
+
else if (event.key === 'Home')
|
|
190
|
+
moveTo(min);
|
|
191
|
+
else if (event.key === 'End')
|
|
192
|
+
moveTo(max);
|
|
193
|
+
else
|
|
194
|
+
return;
|
|
195
|
+
// Only the keys this Component owns are prevented, so a reader who binds
|
|
196
|
+
// something else to the divider still gets it.
|
|
197
|
+
event.preventDefault();
|
|
198
|
+
event.stopPropagation();
|
|
199
|
+
};
|
|
200
|
+
return (_jsx("div", { "data-slot": "resizable-handle", role: "separator", "aria-label": label, "aria-orientation": orientation, "aria-valuenow": Math.round(position), "aria-valuemin": min, "aria-valuemax": max, "aria-disabled": disabled ? 'true' : undefined, tabIndex: disabled ? -1 : 0, "data-orientation": orientation, "data-dragging": dragging ? 'true' : undefined, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: endDrag, onPointerCancel: endDrag, onKeyDown: (event) => {
|
|
201
|
+
onKey(event);
|
|
202
|
+
onKeyDown?.(event);
|
|
203
|
+
}, className: cn('bg-border relative shrink-0 touch-none outline-none', orientation === 'vertical' ? 'h-px w-full cursor-row-resize' : 'h-full w-px cursor-col-resize', 'transition-colors duration-fast ease-out', 'hover:bg-ring focus-visible:bg-ring data-[dragging]:bg-ring', 'focus-visible:ring-ring focus-visible:ring-[3px]', 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50', className), ...props }));
|
|
204
|
+
}
|
|
205
|
+
export { Resizable, ResizablePanel, ResizableHandle };
|