@cueplusplus/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/charts/_token.js +39 -0
- package/dist/charts/container.d.ts +58 -0
- package/dist/charts/container.js +77 -0
- package/dist/charts/index.d.ts +5 -0
- package/dist/charts/index.js +5 -0
- package/dist/charts/ramps.d.ts +140 -0
- package/dist/charts/ramps.js +615 -0
- package/dist/charts/theme.d.ts +112 -0
- package/dist/charts/theme.js +165 -0
- package/dist/charts/tones.d.ts +111 -0
- package/dist/charts/tones.js +127 -0
- package/dist/chat/_glyphs.js +55 -0
- package/dist/chat/agent-color.d.ts +84 -0
- package/dist/chat/agent-color.js +101 -0
- package/dist/chat/agent-pile.d.ts +55 -0
- package/dist/chat/agent-pile.js +121 -0
- package/dist/chat/ask-box.d.ts +88 -0
- package/dist/chat/ask-box.js +341 -0
- package/dist/chat/composer.d.ts +66 -0
- package/dist/chat/composer.js +144 -0
- package/dist/chat/delegation-card.d.ts +60 -0
- package/dist/chat/delegation-card.js +129 -0
- package/dist/chat/index.d.ts +8 -0
- package/dist/chat/index.js +8 -0
- package/dist/chat/message-list.d.ts +39 -0
- package/dist/chat/message-list.js +105 -0
- package/dist/chat/message.d.ts +47 -0
- package/dist/chat/message.js +108 -0
- package/dist/chrome/_glyphs.js +101 -0
- package/dist/chrome/_tabs-scroll.d.ts +62 -0
- package/dist/chrome/_tabs-scroll.js +68 -0
- package/dist/chrome/_tree.d.ts +75 -0
- package/dist/chrome/_tree.js +49 -0
- package/dist/chrome/empty-state.d.ts +37 -0
- package/dist/chrome/empty-state.js +57 -0
- package/dist/chrome/eyebrow.d.ts +29 -0
- package/dist/chrome/eyebrow.js +36 -0
- package/dist/chrome/index.d.ts +17 -0
- package/dist/chrome/index.js +17 -0
- package/dist/chrome/navigation-menu.d.ts +80 -0
- package/dist/chrome/navigation-menu.js +121 -0
- package/dist/chrome/page-shell.d.ts +38 -0
- package/dist/chrome/page-shell.js +66 -0
- package/dist/chrome/panel.d.ts +55 -0
- package/dist/chrome/panel.js +67 -0
- package/dist/chrome/row.d.ts +82 -0
- package/dist/chrome/row.js +106 -0
- package/dist/chrome/scroll-area.d.ts +39 -0
- package/dist/chrome/scroll-area.js +70 -0
- package/dist/chrome/section-header.d.ts +42 -0
- package/dist/chrome/section-header.js +79 -0
- package/dist/chrome/segmented-control.d.ts +58 -0
- package/dist/chrome/segmented-control.js +76 -0
- package/dist/chrome/status-bar.d.ts +46 -0
- package/dist/chrome/status-bar.js +56 -0
- package/dist/chrome/tabs.d.ts +80 -0
- package/dist/chrome/tabs.js +176 -0
- package/dist/chrome/title-bar.d.ts +37 -0
- package/dist/chrome/title-bar.js +45 -0
- package/dist/chrome/toolbar.d.ts +46 -0
- package/dist/chrome/toolbar.js +62 -0
- package/dist/chrome/tree.d.ts +98 -0
- package/dist/chrome/tree.js +248 -0
- package/dist/color/_checker.js +14 -0
- package/dist/color/_convert.d.ts +52 -0
- package/dist/color/_convert.js +66 -0
- package/dist/color/_glyphs.js +28 -0
- package/dist/color/_thumb.js +15 -0
- package/dist/color/_value.js +28 -0
- package/dist/color/color-area.d.ts +27 -0
- package/dist/color/color-area.js +40 -0
- package/dist/color/color-field.d.ts +32 -0
- package/dist/color/color-field.js +43 -0
- package/dist/color/color-picker.d.ts +71 -0
- package/dist/color/color-picker.js +178 -0
- package/dist/color/color-slider.d.ts +31 -0
- package/dist/color/color-slider.js +51 -0
- package/dist/color/color-swatch.d.ts +32 -0
- package/dist/color/color-swatch.js +44 -0
- package/dist/color/index.d.ts +7 -0
- package/dist/color/index.js +7 -0
- package/dist/configurator/_export.d.ts +78 -0
- package/dist/configurator/_export.js +261 -0
- package/dist/configurator/_glyphs.js +92 -0
- package/dist/configurator/_overrides.d.ts +247 -0
- package/dist/configurator/_overrides.js +514 -0
- package/dist/configurator/configurator.d.ts +71 -0
- package/dist/configurator/configurator.js +279 -0
- package/dist/configurator/export-dialog.d.ts +30 -0
- package/dist/configurator/export-dialog.js +121 -0
- package/dist/configurator/index.d.ts +8 -0
- package/dist/configurator/index.js +8 -0
- package/dist/configurator/panel-sections.d.ts +86 -0
- package/dist/configurator/panel-sections.js +316 -0
- package/dist/configurator/token-editor.d.ts +74 -0
- package/dist/configurator/token-editor.js +149 -0
- package/dist/configurator/use-zoom.d.ts +87 -0
- package/dist/configurator/use-zoom.js +113 -0
- package/dist/date/_glyphs.js +34 -0
- package/dist/date/_segments.js +55 -0
- package/dist/date/_trigger.js +31 -0
- package/dist/date/calendar.d.ts +25 -0
- package/dist/date/calendar.js +125 -0
- package/dist/date/date-field.d.ts +34 -0
- package/dist/date/date-field.js +41 -0
- package/dist/date/date-picker.d.ts +69 -0
- package/dist/date/date-picker.js +89 -0
- package/dist/date/date-range-picker.d.ts +65 -0
- package/dist/date/date-range-picker.js +92 -0
- package/dist/date/index.d.ts +6 -0
- package/dist/date/index.js +6 -0
- package/dist/date/time-field.d.ts +29 -0
- package/dist/date/time-field.js +36 -0
- package/dist/dmx/_ramp.d.ts +77 -0
- package/dist/dmx/_ramp.js +169 -0
- package/dist/dmx/_readout.js +53 -0
- package/dist/dmx/_universe.d.ts +105 -0
- package/dist/dmx/_universe.js +112 -0
- package/dist/dmx/channel-matrix.d.ts +81 -0
- package/dist/dmx/channel-matrix.js +285 -0
- package/dist/dmx/dmx-bar.d.ts +50 -0
- package/dist/dmx/dmx-bar.js +107 -0
- package/dist/dmx/dmx-strip.d.ts +41 -0
- package/dist/dmx/dmx-strip.js +196 -0
- package/dist/dmx/index.d.ts +7 -0
- package/dist/dmx/index.js +7 -0
- package/dist/dmx/universe-grid.d.ts +61 -0
- package/dist/dmx/universe-grid.js +182 -0
- package/dist/flow/_frame.d.ts +52 -0
- package/dist/flow/_frame.js +38 -0
- package/dist/flow/controls.d.ts +41 -0
- package/dist/flow/controls.js +53 -0
- package/dist/flow/flow.css +121 -0
- package/dist/flow/handles.d.ts +40 -0
- package/dist/flow/handles.js +61 -0
- package/dist/flow/index.d.ts +7 -0
- package/dist/flow/index.js +7 -0
- package/dist/flow/node-card.d.ts +71 -0
- package/dist/flow/node-card.js +102 -0
- package/dist/flow/signal-edge.d.ts +60 -0
- package/dist/flow/signal-edge.js +108 -0
- package/dist/flow/use-flow-theme.d.ts +69 -0
- package/dist/flow/use-flow-theme.js +109 -0
- package/dist/forms/_chassis.d.ts +64 -0
- package/dist/forms/_chassis.js +59 -0
- package/dist/forms/_glyphs.js +143 -0
- package/dist/forms/_listbox.js +55 -0
- package/dist/forms/autocomplete.d.ts +61 -0
- package/dist/forms/autocomplete.js +88 -0
- package/dist/forms/button-group.d.ts +47 -0
- package/dist/forms/button-group.js +52 -0
- package/dist/forms/checkbox.d.ts +51 -0
- package/dist/forms/checkbox.js +98 -0
- package/dist/forms/combobox.d.ts +73 -0
- package/dist/forms/combobox.js +108 -0
- package/dist/forms/env-var-input.d.ts +74 -0
- package/dist/forms/env-var-input.js +116 -0
- package/dist/forms/field.d.ts +89 -0
- package/dist/forms/field.js +102 -0
- package/dist/forms/file-upload.d.ts +113 -0
- package/dist/forms/file-upload.js +232 -0
- package/dist/forms/index.d.ts +26 -0
- package/dist/forms/index.js +26 -0
- package/dist/forms/input-group.d.ts +31 -0
- package/dist/forms/input-group.js +54 -0
- package/dist/forms/input.d.ts +33 -0
- package/dist/forms/input.js +36 -0
- package/dist/forms/multi-select.d.ts +62 -0
- package/dist/forms/multi-select.js +107 -0
- package/dist/forms/number-field.d.ts +38 -0
- package/dist/forms/number-field.js +64 -0
- package/dist/forms/otp-field.d.ts +54 -0
- package/dist/forms/otp-field.js +59 -0
- package/dist/forms/password-input.d.ts +46 -0
- package/dist/forms/password-input.js +62 -0
- package/dist/forms/radio-group.d.ts +50 -0
- package/dist/forms/radio-group.js +64 -0
- package/dist/forms/rating.d.ts +69 -0
- package/dist/forms/rating.js +103 -0
- package/dist/forms/scrub-input.d.ts +81 -0
- package/dist/forms/scrub-input.js +249 -0
- package/dist/forms/search-input.d.ts +54 -0
- package/dist/forms/search-input.js +112 -0
- package/dist/forms/select.d.ts +75 -0
- package/dist/forms/select.js +91 -0
- package/dist/forms/slider.d.ts +44 -0
- package/dist/forms/slider.js +65 -0
- package/dist/forms/switch.d.ts +36 -0
- package/dist/forms/switch.js +71 -0
- package/dist/forms/tags-input.d.ts +69 -0
- package/dist/forms/tags-input.js +148 -0
- package/dist/forms/textarea.d.ts +27 -0
- package/dist/forms/textarea.js +37 -0
- package/dist/forms/toggle-group.d.ts +41 -0
- package/dist/forms/toggle-group.js +56 -0
- package/dist/forms/toggle.d.ts +41 -0
- package/dist/forms/toggle.js +100 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.js +104 -0
- package/dist/instruments/_ansi.js +116 -0
- package/dist/instruments/_glyphs.js +90 -0
- package/dist/instruments/_motion.js +56 -0
- package/dist/instruments/_rem.js +21 -0
- package/dist/instruments/app-window-frame.d.ts +48 -0
- package/dist/instruments/app-window-frame.js +76 -0
- package/dist/instruments/copy-button.d.ts +41 -0
- package/dist/instruments/copy-button.js +112 -0
- package/dist/instruments/data-table.d.ts +168 -0
- package/dist/instruments/data-table.js +212 -0
- package/dist/instruments/group-bar.d.ts +63 -0
- package/dist/instruments/group-bar.js +122 -0
- package/dist/instruments/index.d.ts +13 -0
- package/dist/instruments/index.js +13 -0
- package/dist/instruments/log-viewer.d.ts +74 -0
- package/dist/instruments/log-viewer.js +215 -0
- package/dist/instruments/meter.d.ts +77 -0
- package/dist/instruments/meter.js +96 -0
- package/dist/instruments/progress.d.ts +45 -0
- package/dist/instruments/progress.js +61 -0
- package/dist/instruments/seam-grid.d.ts +66 -0
- package/dist/instruments/seam-grid.js +78 -0
- package/dist/instruments/sparkline.d.ts +39 -0
- package/dist/instruments/sparkline.js +85 -0
- package/dist/instruments/stat.d.ts +35 -0
- package/dist/instruments/stat.js +59 -0
- package/dist/instruments/table.d.ts +51 -0
- package/dist/instruments/table.js +92 -0
- package/dist/instruments/terminal-frame.d.ts +89 -0
- package/dist/instruments/terminal-frame.js +256 -0
- package/dist/instruments/two-step-button.d.ts +47 -0
- package/dist/instruments/two-step-button.js +147 -0
- package/dist/instruments/usage-chart.d.ts +55 -0
- package/dist/instruments/usage-chart.js +235 -0
- package/dist/layout/_collapse.js +46 -0
- package/dist/layout/_glyphs.js +45 -0
- package/dist/layout/accordion.d.ts +52 -0
- package/dist/layout/accordion.js +81 -0
- package/dist/layout/breadcrumb.d.ts +35 -0
- package/dist/layout/breadcrumb.js +67 -0
- package/dist/layout/card.d.ts +51 -0
- package/dist/layout/card.js +76 -0
- package/dist/layout/carousel.d.ts +96 -0
- package/dist/layout/carousel.js +211 -0
- package/dist/layout/collapsible.d.ts +74 -0
- package/dist/layout/collapsible.js +126 -0
- package/dist/layout/container.d.ts +27 -0
- package/dist/layout/container.js +40 -0
- package/dist/layout/description-list.d.ts +24 -0
- package/dist/layout/description-list.js +37 -0
- package/dist/layout/grid.d.ts +40 -0
- package/dist/layout/grid.js +48 -0
- package/dist/layout/index.d.ts +15 -0
- package/dist/layout/index.js +15 -0
- package/dist/layout/item.d.ts +65 -0
- package/dist/layout/item.js +100 -0
- package/dist/layout/link.d.ts +48 -0
- package/dist/layout/link.js +63 -0
- package/dist/layout/pagination.d.ts +54 -0
- package/dist/layout/pagination.js +121 -0
- package/dist/layout/resizable.d.ts +38 -0
- package/dist/layout/resizable.js +68 -0
- package/dist/layout/sidebar.d.ts +67 -0
- package/dist/layout/sidebar.js +129 -0
- package/dist/layout/stack.d.ts +46 -0
- package/dist/layout/stack.js +62 -0
- package/dist/layout/stepper.d.ts +49 -0
- package/dist/layout/stepper.js +81 -0
- package/dist/layout/timeline.d.ts +43 -0
- package/dist/layout/timeline.js +63 -0
- package/dist/lib/aria-class.js +24 -0
- package/dist/lib/cn.js +18 -0
- package/dist/lib/compose.js +38 -0
- package/dist/lib/cva.d.ts +18 -0
- package/dist/lib/cva.js +65 -0
- package/dist/midi/_notes.d.ts +101 -0
- package/dist/midi/_notes.js +183 -0
- package/dist/midi/_ruler.d.ts +100 -0
- package/dist/midi/_ruler.js +105 -0
- package/dist/midi/_time.d.ts +107 -0
- package/dist/midi/_time.js +130 -0
- package/dist/midi/index.d.ts +9 -0
- package/dist/midi/index.js +9 -0
- package/dist/midi/musical-time-input.d.ts +48 -0
- package/dist/midi/musical-time-input.js +154 -0
- package/dist/midi/piano-keyboard.d.ts +50 -0
- package/dist/midi/piano-keyboard.js +193 -0
- package/dist/midi/spectrum-visualizer.d.ts +65 -0
- package/dist/midi/spectrum-visualizer.js +213 -0
- package/dist/midi/threshold-rail.d.ts +72 -0
- package/dist/midi/threshold-rail.js +214 -0
- package/dist/midi/timeline-ruler.d.ts +83 -0
- package/dist/midi/timeline-ruler.js +152 -0
- package/dist/overlays/_debounce.js +34 -0
- package/dist/overlays/_glyphs.js +59 -0
- package/dist/overlays/_surface.d.ts +70 -0
- package/dist/overlays/_surface.js +70 -0
- package/dist/overlays/_wheel.js +42 -0
- package/dist/overlays/alert-dialog.d.ts +67 -0
- package/dist/overlays/alert-dialog.js +117 -0
- package/dist/overlays/command-palette.d.ts +93 -0
- package/dist/overlays/command-palette.js +193 -0
- package/dist/overlays/context-menu.d.ts +64 -0
- package/dist/overlays/context-menu.js +78 -0
- package/dist/overlays/dialog.d.ts +72 -0
- package/dist/overlays/dialog.js +124 -0
- package/dist/overlays/drawer.d.ts +75 -0
- package/dist/overlays/drawer.js +151 -0
- package/dist/overlays/dropdown-menu.d.ts +79 -0
- package/dist/overlays/dropdown-menu.js +156 -0
- package/dist/overlays/hover-card.d.ts +58 -0
- package/dist/overlays/hover-card.js +91 -0
- package/dist/overlays/index.d.ts +15 -0
- package/dist/overlays/index.js +15 -0
- package/dist/overlays/info-tip.d.ts +41 -0
- package/dist/overlays/info-tip.js +45 -0
- package/dist/overlays/menubar.d.ts +72 -0
- package/dist/overlays/menubar.js +100 -0
- package/dist/overlays/popover.d.ts +66 -0
- package/dist/overlays/popover.js +114 -0
- package/dist/overlays/sheet.d.ts +78 -0
- package/dist/overlays/sheet.js +131 -0
- package/dist/overlays/toast.d.ts +106 -0
- package/dist/overlays/toast.js +146 -0
- package/dist/overlays/tooltip.d.ts +57 -0
- package/dist/overlays/tooltip.js +95 -0
- package/dist/primitives/avatar.d.ts +51 -0
- package/dist/primitives/avatar.js +93 -0
- package/dist/primitives/button.d.ts +46 -0
- package/dist/primitives/button.js +94 -0
- package/dist/primitives/chip.d.ts +47 -0
- package/dist/primitives/chip.js +152 -0
- package/dist/primitives/icon-button.d.ts +41 -0
- package/dist/primitives/icon-button.js +59 -0
- package/dist/primitives/index.d.ts +10 -0
- package/dist/primitives/index.js +10 -0
- package/dist/primitives/kbd.d.ts +17 -0
- package/dist/primitives/kbd.js +24 -0
- package/dist/primitives/separator.d.ts +25 -0
- package/dist/primitives/separator.js +30 -0
- package/dist/primitives/skeleton.d.ts +20 -0
- package/dist/primitives/skeleton.js +27 -0
- package/dist/primitives/spinner.d.ts +26 -0
- package/dist/primitives/spinner.js +31 -0
- package/dist/primitives/status-dot.d.ts +44 -0
- package/dist/primitives/status-dot.js +50 -0
- package/dist/styles.css +19 -0
- package/dist/system/density.d.ts +33 -0
- package/dist/system/density.js +49 -0
- package/dist/system/index.d.ts +8 -0
- package/dist/system/index.js +7 -0
- package/dist/system/portal.d.ts +51 -0
- package/dist/system/portal.js +122 -0
- package/dist/system/prepaint.d.ts +38 -0
- package/dist/system/prepaint.js +37 -0
- package/dist/system/theme-provider.d.ts +72 -0
- package/dist/system/theme-provider.js +252 -0
- package/dist/system/use-density.d.ts +33 -0
- package/dist/system/use-density.js +84 -0
- package/dist/system/use-isomorphic-layout-effect.js +15 -0
- package/dist/system/use-theme.d.ts +19 -0
- package/dist/system/use-theme.js +25 -0
- package/dist/theming/_presets.d.ts +11 -0
- package/dist/theming/_presets.js +487 -0
- package/dist/theming/contrast.d.ts +110 -0
- package/dist/theming/contrast.js +167 -0
- package/dist/theming/create-theme.d.ts +165 -0
- package/dist/theming/create-theme.js +442 -0
- package/dist/theming/index.d.ts +5 -0
- package/dist/theming/index.js +5 -0
- package/dist/theming/serialize.d.ts +145 -0
- package/dist/theming/serialize.js +165 -0
- package/package.json +216 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { FormControlSize } from "./_chassis.js";
|
|
2
|
+
import { ComboboxOption } from "./combobox.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
//#region src/forms/multi-select.d.ts
|
|
5
|
+
interface MultiSelectProps {
|
|
6
|
+
/** The rows, in display order. Base UI filters them against what is typed. */
|
|
7
|
+
items: readonly ComboboxOption[];
|
|
8
|
+
/** Selected values. Use with `onValueChange` for a controlled field. */
|
|
9
|
+
value?: readonly string[];
|
|
10
|
+
/** Initially selected values for an uncontrolled field. */
|
|
11
|
+
defaultValue?: readonly string[];
|
|
12
|
+
/** Called with the full selection after every add or remove. */
|
|
13
|
+
onValueChange?: (value: string[]) => void;
|
|
14
|
+
/** Shown while nothing is selected — suppressed once chips fill the field. */
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
/** Shown in place of the list when the query matches nothing. */
|
|
17
|
+
emptyMessage?: React.ReactNode;
|
|
18
|
+
/** Rung of the control ladder. Defaults to `"md"`. */
|
|
19
|
+
size?: FormControlSize;
|
|
20
|
+
/** Prevent opening and grey the field out. */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Require at least one value before the owning form submits. */
|
|
23
|
+
required?: boolean;
|
|
24
|
+
/** Name for the hidden input, when the field is part of a form. */
|
|
25
|
+
name?: string;
|
|
26
|
+
/** Id forwarded to the text input. */
|
|
27
|
+
id?: string;
|
|
28
|
+
/** Accessible name for the input, when no {@link FieldLabel} names it. */
|
|
29
|
+
"aria-label"?: string;
|
|
30
|
+
/** Id of the element naming the input. */
|
|
31
|
+
"aria-labelledby"?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Accessible name for a chip's remove button, given the row's label. Defaults
|
|
34
|
+
* to ``(label) => `Remove ${label}` ``.
|
|
35
|
+
*/
|
|
36
|
+
removeLabel?: (label: string) => string;
|
|
37
|
+
/** Extra classes for the field. */
|
|
38
|
+
className?: string;
|
|
39
|
+
/** Extra classes for the popup surface. */
|
|
40
|
+
popupClassName?: string;
|
|
41
|
+
/** Distance in px between the field and the popup. Defaults to `4`. */
|
|
42
|
+
sideOffset?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Several values out of a known set, each shown as a removable chip.
|
|
46
|
+
*
|
|
47
|
+
* The chips live *inside* the field rather than under it, which is what makes
|
|
48
|
+
* the control honest about its own size: a selection of nine trusses is visibly
|
|
49
|
+
* nine trusses tall, and nothing is hidden behind a "+6 more" the user has to
|
|
50
|
+
* open something to read. That is also why the field takes `min-h-control-*`
|
|
51
|
+
* instead of a fixed height.
|
|
52
|
+
*
|
|
53
|
+
* Backspace in an empty input removes the last chip and the chips themselves are
|
|
54
|
+
* arrow-key navigable — both come from Base UI's `Combobox.Chips`, and both are
|
|
55
|
+
* the parts hand-rolled multi-selects forget.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* <MultiSelect aria-label="Trusses" items={trusses} value={picked} onValueChange={setPicked} />
|
|
59
|
+
*/
|
|
60
|
+
declare const MultiSelect: React.ForwardRefExoticComponent<MultiSelectProps & React.RefAttributes<HTMLInputElement>>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { MultiSelect, MultiSelectProps };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { bareControlClasses, controlGroupFocusClasses, controlSurface } from "./_chassis.js";
|
|
4
|
+
import { useCuePortalProps } from "../system/portal.js";
|
|
5
|
+
import { CrossGlyph, TickGlyph } from "./_glyphs.js";
|
|
6
|
+
import { chipFieldClasses, chipRemoveClasses, listboxEmptyClasses, listboxIndicatorClasses, listboxItemClasses, listboxListClasses, listboxPopupClasses, listboxPositionerClasses, valueChipClasses } from "./_listbox.js";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { Combobox } from "@base-ui/react/combobox";
|
|
10
|
+
//#region src/forms/multi-select.tsx
|
|
11
|
+
/** Horizontal padding and type size per rung; the height comes from the chips. */
|
|
12
|
+
const FIELD_SIZES = {
|
|
13
|
+
sm: "min-h-control-sm px-(--cue-space-2) text-(length:--cue-text-label)",
|
|
14
|
+
md: "min-h-control-md px-(--cue-space-3) text-(length:--cue-text-ui)",
|
|
15
|
+
lg: "min-h-control-lg px-(--cue-space-4) text-(length:--cue-text-body)"
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Several values out of a known set, each shown as a removable chip.
|
|
19
|
+
*
|
|
20
|
+
* The chips live *inside* the field rather than under it, which is what makes
|
|
21
|
+
* the control honest about its own size: a selection of nine trusses is visibly
|
|
22
|
+
* nine trusses tall, and nothing is hidden behind a "+6 more" the user has to
|
|
23
|
+
* open something to read. That is also why the field takes `min-h-control-*`
|
|
24
|
+
* instead of a fixed height.
|
|
25
|
+
*
|
|
26
|
+
* Backspace in an empty input removes the last chip and the chips themselves are
|
|
27
|
+
* arrow-key navigable — both come from Base UI's `Combobox.Chips`, and both are
|
|
28
|
+
* the parts hand-rolled multi-selects forget.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <MultiSelect aria-label="Trusses" items={trusses} value={picked} onValueChange={setPicked} />
|
|
32
|
+
*/
|
|
33
|
+
const MultiSelect = React.forwardRef(function MultiSelect({ items, value, defaultValue, onValueChange, placeholder, emptyMessage = "No matches", size = "md", disabled, required, name, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, removeLabel = (label) => `Remove ${label}`, className, popupClassName, sideOffset = 4 }, ref) {
|
|
34
|
+
const portalProps = useCuePortalProps();
|
|
35
|
+
const resolve = React.useCallback((keys) => keys === void 0 ? void 0 : keys.map((key) => items.find((item) => item.value === key)).filter((item) => item !== void 0), [items]);
|
|
36
|
+
const handleValueChange = React.useCallback((next) => {
|
|
37
|
+
onValueChange?.(next.map((item) => item.value));
|
|
38
|
+
}, [onValueChange]);
|
|
39
|
+
return /* @__PURE__ */ jsxs(Combobox.Root, {
|
|
40
|
+
multiple: true,
|
|
41
|
+
items,
|
|
42
|
+
value: resolve(value),
|
|
43
|
+
defaultValue: resolve(defaultValue),
|
|
44
|
+
onValueChange: onValueChange === void 0 ? void 0 : handleValueChange,
|
|
45
|
+
disabled,
|
|
46
|
+
required,
|
|
47
|
+
name,
|
|
48
|
+
children: [/* @__PURE__ */ jsx(Combobox.InputGroup, {
|
|
49
|
+
"data-slot": "multi-select-group",
|
|
50
|
+
className: cn(controlSurface, FIELD_SIZES[size], controlGroupFocusClasses, className),
|
|
51
|
+
children: /* @__PURE__ */ jsx(Combobox.Chips, {
|
|
52
|
+
className: chipFieldClasses,
|
|
53
|
+
children: /* @__PURE__ */ jsx(Combobox.Value, { children: (selected) => /* @__PURE__ */ jsxs(React.Fragment, { children: [selected.map((item) => /* @__PURE__ */ jsxs(Combobox.Chip, {
|
|
54
|
+
"data-slot": "multi-select-chip",
|
|
55
|
+
className: valueChipClasses,
|
|
56
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
57
|
+
className: "truncate",
|
|
58
|
+
children: item.label
|
|
59
|
+
}), /* @__PURE__ */ jsx(Combobox.ChipRemove, {
|
|
60
|
+
"aria-label": removeLabel(item.label),
|
|
61
|
+
className: chipRemoveClasses,
|
|
62
|
+
children: /* @__PURE__ */ jsx(CrossGlyph, { className: "size-icon-sm" })
|
|
63
|
+
})]
|
|
64
|
+
}, item.value)), /* @__PURE__ */ jsx(Combobox.Input, {
|
|
65
|
+
ref,
|
|
66
|
+
id,
|
|
67
|
+
"data-slot": "multi-select-input",
|
|
68
|
+
placeholder: selected.length > 0 ? "" : placeholder,
|
|
69
|
+
"aria-label": ariaLabel,
|
|
70
|
+
"aria-labelledby": ariaLabelledBy,
|
|
71
|
+
className: cn(bareControlClasses, "h-control-sm w-auto min-w-[6ch]")
|
|
72
|
+
})] }) })
|
|
73
|
+
})
|
|
74
|
+
}), /* @__PURE__ */ jsx(Combobox.Portal, {
|
|
75
|
+
...portalProps,
|
|
76
|
+
children: /* @__PURE__ */ jsx(Combobox.Positioner, {
|
|
77
|
+
className: listboxPositionerClasses,
|
|
78
|
+
sideOffset,
|
|
79
|
+
children: /* @__PURE__ */ jsxs(Combobox.Popup, {
|
|
80
|
+
"data-slot": "multi-select-popup",
|
|
81
|
+
className: cn(listboxPopupClasses, popupClassName),
|
|
82
|
+
children: [/* @__PURE__ */ jsx(Combobox.Empty, {
|
|
83
|
+
className: listboxEmptyClasses,
|
|
84
|
+
children: emptyMessage
|
|
85
|
+
}), /* @__PURE__ */ jsx(Combobox.List, {
|
|
86
|
+
className: listboxListClasses,
|
|
87
|
+
children: (item) => /* @__PURE__ */ jsxs(Combobox.Item, {
|
|
88
|
+
value: item,
|
|
89
|
+
disabled: item.disabled,
|
|
90
|
+
"data-slot": "multi-select-item",
|
|
91
|
+
className: listboxItemClasses,
|
|
92
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
93
|
+
className: listboxIndicatorClasses,
|
|
94
|
+
children: /* @__PURE__ */ jsx(Combobox.ItemIndicator, { children: /* @__PURE__ */ jsx(TickGlyph, { className: "size-icon-sm" }) })
|
|
95
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
96
|
+
className: "truncate",
|
|
97
|
+
children: item.label
|
|
98
|
+
})]
|
|
99
|
+
}, item.value)
|
|
100
|
+
})]
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})]
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
//#endregion
|
|
107
|
+
export { MultiSelect };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { NumberField } from "@base-ui/react/number-field";
|
|
3
|
+
//#region src/forms/number-field.d.ts
|
|
4
|
+
interface NumberFieldProps extends Omit<NumberField.Root.Props, "className"> {
|
|
5
|
+
/** Extra classes, merged over the group chassis. */
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Rung of the control ladder. Defaults to `"md"`. */
|
|
8
|
+
size?: "sm" | "md";
|
|
9
|
+
/** Accessible name for the decrement button. Defaults to `"Decrease"`. */
|
|
10
|
+
decrementLabel?: string;
|
|
11
|
+
/** Accessible name for the increment button. Defaults to `"Increase"`. */
|
|
12
|
+
incrementLabel?: string;
|
|
13
|
+
/** Placeholder shown while the field is empty. */
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
/** Accessible name for the text control, when no {@link FieldLabel} names it. */
|
|
16
|
+
"aria-label"?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A numeric control with explicit − / + affordances and no native spinners.
|
|
20
|
+
*
|
|
21
|
+
* Native `<input type="number">` spinners are unusable at console density — a
|
|
22
|
+
* pair of 4px arrows that only appear on hover — so this is a text input with
|
|
23
|
+
* `inputMode="numeric"` flanked by real buttons, which is also what makes it
|
|
24
|
+
* touch-usable. Arrow keys step, Shift steps by `largeStep`, Alt by `smallStep`,
|
|
25
|
+
* and values typed out of range are clamped to `min`/`max` on blur.
|
|
26
|
+
*
|
|
27
|
+
* The group owns the rim; the input and both buttons render bare inside it, so
|
|
28
|
+
* the whole cluster reads as one field and focus feedback moves to
|
|
29
|
+
* `focus-within`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* <NumberField aria-label="Fade time" defaultValue={3} min={0} max={600} step={0.5} />
|
|
33
|
+
* @example
|
|
34
|
+
* <Field><FieldLabel>Universe</FieldLabel><NumberField min={1} max={512} /></Field>
|
|
35
|
+
*/
|
|
36
|
+
declare const NumberField$1: React.ForwardRefExoticComponent<Omit<NumberFieldProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { NumberField$1 as NumberField, NumberFieldProps };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { bareControlClasses, controlGroupFocusClasses, controlSurface } from "./_chassis.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { NumberField } from "@base-ui/react/number-field";
|
|
7
|
+
//#region src/forms/number-field.tsx
|
|
8
|
+
/** The − and + affordances: square, borderless, and quiet until hovered. */
|
|
9
|
+
const stepperClasses = "flex shrink-0 items-center justify-center border-0 bg-transparent p-0 font-mono text-fg-muted leading-none select-none outline-none transition-colors hover:bg-surface-2 hover:text-fg focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-1 focus-visible:outline-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-40";
|
|
10
|
+
/**
|
|
11
|
+
* A numeric control with explicit − / + affordances and no native spinners.
|
|
12
|
+
*
|
|
13
|
+
* Native `<input type="number">` spinners are unusable at console density — a
|
|
14
|
+
* pair of 4px arrows that only appear on hover — so this is a text input with
|
|
15
|
+
* `inputMode="numeric"` flanked by real buttons, which is also what makes it
|
|
16
|
+
* touch-usable. Arrow keys step, Shift steps by `largeStep`, Alt by `smallStep`,
|
|
17
|
+
* and values typed out of range are clamped to `min`/`max` on blur.
|
|
18
|
+
*
|
|
19
|
+
* The group owns the rim; the input and both buttons render bare inside it, so
|
|
20
|
+
* the whole cluster reads as one field and focus feedback moves to
|
|
21
|
+
* `focus-within`.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <NumberField aria-label="Fade time" defaultValue={3} min={0} max={600} step={0.5} />
|
|
25
|
+
* @example
|
|
26
|
+
* <Field><FieldLabel>Universe</FieldLabel><NumberField min={1} max={512} /></Field>
|
|
27
|
+
*/
|
|
28
|
+
const NumberField$1 = React.forwardRef(function NumberField$2({ className, size = "md", decrementLabel = "Decrease", incrementLabel = "Increase", placeholder, "aria-label": ariaLabel, ...elementProps }, ref) {
|
|
29
|
+
const rung = size === "sm" ? "sm" : "md";
|
|
30
|
+
return /* @__PURE__ */ jsx(NumberField.Root, {
|
|
31
|
+
ref,
|
|
32
|
+
"data-slot": "number-field",
|
|
33
|
+
...elementProps,
|
|
34
|
+
children: /* @__PURE__ */ jsxs(NumberField.Group, {
|
|
35
|
+
"data-slot": "number-field-group",
|
|
36
|
+
className: cn(controlSurface, "flex items-stretch overflow-hidden", rung === "sm" ? "h-control-sm text-(length:--cue-text-label)" : "h-control-md text-(length:--cue-text-ui)", controlGroupFocusClasses, className),
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx(NumberField.Decrement, {
|
|
39
|
+
"aria-label": decrementLabel,
|
|
40
|
+
className: cn(stepperClasses, rung === "sm" ? "w-control-sm" : "w-control-md"),
|
|
41
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
42
|
+
"aria-hidden": "true",
|
|
43
|
+
children: "−"
|
|
44
|
+
})
|
|
45
|
+
}),
|
|
46
|
+
/* @__PURE__ */ jsx(NumberField.Input, {
|
|
47
|
+
"aria-label": ariaLabel,
|
|
48
|
+
placeholder,
|
|
49
|
+
className: cn(bareControlClasses, "w-full px-(--cue-space-2) text-center tabular-nums", "[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none")
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsx(NumberField.Increment, {
|
|
52
|
+
"aria-label": incrementLabel,
|
|
53
|
+
className: cn(stepperClasses, rung === "sm" ? "w-control-sm" : "w-control-md"),
|
|
54
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
55
|
+
"aria-hidden": "true",
|
|
56
|
+
children: "+"
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { NumberField$1 as NumberField };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FormControlSize } from "./_chassis.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { OTPField } from "@base-ui/react/otp-field";
|
|
4
|
+
//#region src/forms/otp-field.d.ts
|
|
5
|
+
interface OTPFieldProps extends Omit<OTPField.Root.Props, "className" | "children" | "length"> {
|
|
6
|
+
/** How many characters the code has. Defaults to `6`. */
|
|
7
|
+
length?: number;
|
|
8
|
+
/** Rung of the control ladder. Defaults to `"md"`. */
|
|
9
|
+
size?: FormControlSize;
|
|
10
|
+
/**
|
|
11
|
+
* Insert a separator after every `groupSize` slots — `groupSize={3}` renders a
|
|
12
|
+
* six-character code as `123 456`, which is how people read one off a screen.
|
|
13
|
+
*/
|
|
14
|
+
groupSize?: number;
|
|
15
|
+
/** What sits between groups. Defaults to `"–"`. Always `aria-hidden`. */
|
|
16
|
+
separator?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Accessible name for the field. Names the group *and*, through a visually
|
|
19
|
+
* hidden `<label>`, the first slot — the ARIA pattern for a code field. Leave
|
|
20
|
+
* it off inside a {@link Field}, which supplies a real label.
|
|
21
|
+
*/
|
|
22
|
+
"aria-label"?: string;
|
|
23
|
+
/** Id of the first slot. Generated when omitted, so the hidden label can target it. */
|
|
24
|
+
id?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Accessible name for slot `index` (1-based here, since slot 0 is named by the
|
|
27
|
+
* field label) of `length`. Defaults to
|
|
28
|
+
* ``(index, length) => `Character ${index + 1} of ${length}` ``.
|
|
29
|
+
*/
|
|
30
|
+
slotLabel?: (index: number, length: number) => string;
|
|
31
|
+
/** Extra classes for the row. */
|
|
32
|
+
className?: string;
|
|
33
|
+
/** Extra classes for every slot. */
|
|
34
|
+
slotClassName?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A one-time code, one character per box.
|
|
38
|
+
*
|
|
39
|
+
* The boxes are not decoration: they tell the user how long the code is before
|
|
40
|
+
* they start typing, which is the whole reason this pattern beats a plain text
|
|
41
|
+
* input. Paste, arrow keys, Backspace across slot boundaries and the
|
|
42
|
+
* `one-time-code` autofill hint all come from Base UI.
|
|
43
|
+
*
|
|
44
|
+
* Slots ride the recessed chassis — sunken fill, hairline rim — so a code field
|
|
45
|
+
* reads as "type here" like every other input in the system, just squared off.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* <OTPField aria-label="Verification code" onValueComplete={submit} />
|
|
49
|
+
* @example
|
|
50
|
+
* <OTPField aria-label="Recovery code" length={8} groupSize={4} validationType="alphanumeric" />
|
|
51
|
+
*/
|
|
52
|
+
declare const OTPField$1: React.ForwardRefExoticComponent<Omit<OTPFieldProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { OTPField$1 as OTPField, OTPFieldProps };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { controlSurface } from "./_chassis.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { OTPField } from "@base-ui/react/otp-field";
|
|
7
|
+
//#region src/forms/otp-field.tsx
|
|
8
|
+
/** One square slot per rung of the control ladder. */
|
|
9
|
+
const SLOT_SIZES = {
|
|
10
|
+
sm: "size-control-sm text-(length:--cue-text-ui)",
|
|
11
|
+
md: "size-control-md text-(length:--cue-text-body)",
|
|
12
|
+
lg: "size-control-lg text-(length:--cue-text-emphasis)"
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A one-time code, one character per box.
|
|
16
|
+
*
|
|
17
|
+
* The boxes are not decoration: they tell the user how long the code is before
|
|
18
|
+
* they start typing, which is the whole reason this pattern beats a plain text
|
|
19
|
+
* input. Paste, arrow keys, Backspace across slot boundaries and the
|
|
20
|
+
* `one-time-code` autofill hint all come from Base UI.
|
|
21
|
+
*
|
|
22
|
+
* Slots ride the recessed chassis — sunken fill, hairline rim — so a code field
|
|
23
|
+
* reads as "type here" like every other input in the system, just squared off.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* <OTPField aria-label="Verification code" onValueComplete={submit} />
|
|
27
|
+
* @example
|
|
28
|
+
* <OTPField aria-label="Recovery code" length={8} groupSize={4} validationType="alphanumeric" />
|
|
29
|
+
*/
|
|
30
|
+
const OTPField$1 = React.forwardRef(function OTPField$2({ length = 6, size = "md", groupSize, separator = "–", "aria-label": ariaLabel, id, slotLabel = (index, total) => `Character ${index + 1} of ${total}`, className, slotClassName, ...elementProps }, ref) {
|
|
31
|
+
const generatedId = React.useId();
|
|
32
|
+
const fieldId = id ?? generatedId;
|
|
33
|
+
const slotClasses = cn(controlSurface, "w-auto p-0 text-center tabular-nums", SLOT_SIZES[size], slotClassName);
|
|
34
|
+
return /* @__PURE__ */ jsxs(OTPField.Root, {
|
|
35
|
+
ref,
|
|
36
|
+
id: fieldId,
|
|
37
|
+
length,
|
|
38
|
+
"aria-label": ariaLabel,
|
|
39
|
+
"data-slot": "otp-field",
|
|
40
|
+
className: cn("flex items-center gap-(--cue-space-2)", className),
|
|
41
|
+
...elementProps,
|
|
42
|
+
children: [ariaLabel === void 0 ? null : /* @__PURE__ */ jsx("label", {
|
|
43
|
+
htmlFor: fieldId,
|
|
44
|
+
className: "sr-only",
|
|
45
|
+
children: ariaLabel
|
|
46
|
+
}), Array.from({ length }, (_, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [groupSize !== void 0 && index > 0 && index % groupSize === 0 ? /* @__PURE__ */ jsx("span", {
|
|
47
|
+
"data-slot": "otp-field-separator",
|
|
48
|
+
"aria-hidden": "true",
|
|
49
|
+
className: "px-(--cue-space-1) font-mono text-fg-subtle select-none",
|
|
50
|
+
children: separator
|
|
51
|
+
}) : null, /* @__PURE__ */ jsx(OTPField.Input, {
|
|
52
|
+
"data-slot": "otp-field-slot",
|
|
53
|
+
"aria-label": index === 0 ? void 0 : slotLabel(index, length),
|
|
54
|
+
className: slotClasses
|
|
55
|
+
})] }, index))]
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
//#endregion
|
|
59
|
+
export { OTPField$1 as OTPField };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { FormControlSize } from "./_chassis.js";
|
|
2
|
+
import { InputProps } from "./input.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
//#region src/forms/password-input.d.ts
|
|
5
|
+
interface PasswordInputProps extends Omit<InputProps, "type" | "size"> {
|
|
6
|
+
/** Reveal the value. Use with `onRevealedChange` for a controlled toggle. */
|
|
7
|
+
revealed?: boolean;
|
|
8
|
+
/** Initially revealed, for an uncontrolled toggle. Defaults to `false`. */
|
|
9
|
+
defaultRevealed?: boolean;
|
|
10
|
+
/** Called with the new reveal state. */
|
|
11
|
+
onRevealedChange?: (revealed: boolean) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Offer the reveal affordance at all. Defaults to `true`. Switch it off for a
|
|
14
|
+
* value the product must never re-display — a stored secret the user is
|
|
15
|
+
* replacing rather than reading.
|
|
16
|
+
*/
|
|
17
|
+
showReveal?: boolean;
|
|
18
|
+
/** Accessible name while masked. Defaults to `"Show password"`. */
|
|
19
|
+
revealLabel?: string;
|
|
20
|
+
/** Accessible name while revealed. Defaults to `"Hide password"`. */
|
|
21
|
+
hideLabel?: string;
|
|
22
|
+
/** Rung of the control ladder. Defaults to `"md"`. */
|
|
23
|
+
size?: FormControlSize;
|
|
24
|
+
/** Extra classes for the group that wraps the input and the reveal button. */
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A masked text field with a reveal control.
|
|
29
|
+
*
|
|
30
|
+
* The reveal is a **toggle button carrying `aria-pressed`**, not a checkbox and
|
|
31
|
+
* not a bare icon: a screen-reader user needs to know both what it does and
|
|
32
|
+
* which way it currently is, and `aria-pressed` is the one attribute that says
|
|
33
|
+
* both. The label swaps with the state for the same reason.
|
|
34
|
+
*
|
|
35
|
+
* Revealing switches `type` between `password` and `text` rather than painting
|
|
36
|
+
* over the characters, so the browser's own autofill and password managers keep
|
|
37
|
+
* working.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* <PasswordInput aria-label="Service key" value={key} onValueChange={setKey} />
|
|
41
|
+
* @example
|
|
42
|
+
* <Field><FieldLabel>Token</FieldLabel><PasswordInput showReveal={false} /></Field>
|
|
43
|
+
*/
|
|
44
|
+
declare const PasswordInput: React.ForwardRefExoticComponent<Omit<PasswordInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { PasswordInput, PasswordInputProps };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { InputGroup } from "./input-group.js";
|
|
4
|
+
import { Input } from "./input.js";
|
|
5
|
+
import { EyeGlyph, EyeOffGlyph } from "./_glyphs.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
//#region src/forms/password-input.tsx
|
|
9
|
+
/**
|
|
10
|
+
* A masked text field with a reveal control.
|
|
11
|
+
*
|
|
12
|
+
* The reveal is a **toggle button carrying `aria-pressed`**, not a checkbox and
|
|
13
|
+
* not a bare icon: a screen-reader user needs to know both what it does and
|
|
14
|
+
* which way it currently is, and `aria-pressed` is the one attribute that says
|
|
15
|
+
* both. The label swaps with the state for the same reason.
|
|
16
|
+
*
|
|
17
|
+
* Revealing switches `type` between `password` and `text` rather than painting
|
|
18
|
+
* over the characters, so the browser's own autofill and password managers keep
|
|
19
|
+
* working.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <PasswordInput aria-label="Service key" value={key} onValueChange={setKey} />
|
|
23
|
+
* @example
|
|
24
|
+
* <Field><FieldLabel>Token</FieldLabel><PasswordInput showReveal={false} /></Field>
|
|
25
|
+
*/
|
|
26
|
+
const PasswordInput = React.forwardRef(function PasswordInput({ className, revealed: revealedProp, defaultRevealed = false, onRevealedChange, showReveal = true, revealLabel = "Show password", hideLabel = "Hide password", size, disabled, ...elementProps }, ref) {
|
|
27
|
+
const [uncontrolled, setUncontrolled] = React.useState(defaultRevealed);
|
|
28
|
+
const revealed = revealedProp ?? uncontrolled;
|
|
29
|
+
const toggle = React.useCallback(() => {
|
|
30
|
+
const next = !revealed;
|
|
31
|
+
if (revealedProp === void 0) setUncontrolled(next);
|
|
32
|
+
onRevealedChange?.(next);
|
|
33
|
+
}, [
|
|
34
|
+
onRevealedChange,
|
|
35
|
+
revealed,
|
|
36
|
+
revealedProp
|
|
37
|
+
]);
|
|
38
|
+
return /* @__PURE__ */ jsx(InputGroup, {
|
|
39
|
+
size,
|
|
40
|
+
className,
|
|
41
|
+
trailing: showReveal ? /* @__PURE__ */ jsx("button", {
|
|
42
|
+
type: "button",
|
|
43
|
+
"aria-pressed": revealed,
|
|
44
|
+
"aria-label": revealed ? hideLabel : revealLabel,
|
|
45
|
+
disabled,
|
|
46
|
+
onClick: toggle,
|
|
47
|
+
className: "flex shrink-0 cursor-pointer items-center justify-center rounded-(--radius-control) text-fg-subtle outline-none transition-colors hover:text-fg focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-1 focus-visible:outline-accent disabled:pointer-events-none disabled:opacity-40 aria-pressed:text-fg",
|
|
48
|
+
children: /* @__PURE__ */ jsx(revealed ? EyeOffGlyph : EyeGlyph, { className: "size-icon-md" })
|
|
49
|
+
}) : void 0,
|
|
50
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
51
|
+
ref,
|
|
52
|
+
type: revealed ? "text" : "password",
|
|
53
|
+
"data-slot": "password-input",
|
|
54
|
+
spellCheck: false,
|
|
55
|
+
disabled,
|
|
56
|
+
className: cn("[&::-ms-reveal]:hidden"),
|
|
57
|
+
...elementProps
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//#endregion
|
|
62
|
+
export { PasswordInput };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Radio } from "@base-ui/react/radio";
|
|
3
|
+
import { RadioGroup } from "@base-ui/react/radio-group";
|
|
4
|
+
//#region src/forms/radio-group.d.ts
|
|
5
|
+
interface RadioGroupProps extends Omit<RadioGroup.Props, "className"> {
|
|
6
|
+
/** Extra classes, merged over the stack defaults. */
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Lay the options out in a column (default) or a row. */
|
|
9
|
+
orientation?: "vertical" | "horizontal";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* One-of-many selection: a roving-tabindex group of {@link Radio}s sharing a
|
|
13
|
+
* single value.
|
|
14
|
+
*
|
|
15
|
+
* Arrow keys move *and* select, per the WAI-ARIA radio group pattern that Base
|
|
16
|
+
* UI implements — so the whole group is one tab stop, which is what keeps a
|
|
17
|
+
* dense settings panel navigable.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <RadioGroup aria-label="Blend mode" value={mode} onValueChange={setMode}>
|
|
21
|
+
* <Radio value="htp" label="HTP" />
|
|
22
|
+
* <Radio value="ltp" label="LTP" />
|
|
23
|
+
* </RadioGroup>
|
|
24
|
+
*/
|
|
25
|
+
declare const RadioGroup$1: React.ForwardRefExoticComponent<Omit<RadioGroupProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
26
|
+
interface RadioProps extends Omit<Radio.Root.Props, "className"> {
|
|
27
|
+
/** Extra classes, merged over the dot defaults. */
|
|
28
|
+
className?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Inline label rendered beside the dot and wrapped with it in a `<label>`, so
|
|
31
|
+
* the text is both the accessible name and part of the hit target.
|
|
32
|
+
*/
|
|
33
|
+
label?: React.ReactNode;
|
|
34
|
+
/** Rung of the density icon ladder for the dot. Defaults to `"md"`. */
|
|
35
|
+
size?: "sm" | "md";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* One option inside a {@link RadioGroup}.
|
|
39
|
+
*
|
|
40
|
+
* Round, to say "exactly one" the way a square {@link Checkbox} says "any
|
|
41
|
+
* number". Checked draws an accent rim with an accent core rather than a solid
|
|
42
|
+
* fill, so the selected option stays legible against the sunken field colour on
|
|
43
|
+
* every theme.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* <Radio value="htp" label="Highest takes precedence" />
|
|
47
|
+
*/
|
|
48
|
+
declare const Radio$1: React.ForwardRefExoticComponent<Omit<RadioProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Radio$1 as Radio, RadioGroup$1 as RadioGroup, RadioGroupProps, RadioProps };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Radio } from "@base-ui/react/radio";
|
|
6
|
+
import { RadioGroup } from "@base-ui/react/radio-group";
|
|
7
|
+
//#region src/forms/radio-group.tsx
|
|
8
|
+
/**
|
|
9
|
+
* One-of-many selection: a roving-tabindex group of {@link Radio}s sharing a
|
|
10
|
+
* single value.
|
|
11
|
+
*
|
|
12
|
+
* Arrow keys move *and* select, per the WAI-ARIA radio group pattern that Base
|
|
13
|
+
* UI implements — so the whole group is one tab stop, which is what keeps a
|
|
14
|
+
* dense settings panel navigable.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <RadioGroup aria-label="Blend mode" value={mode} onValueChange={setMode}>
|
|
18
|
+
* <Radio value="htp" label="HTP" />
|
|
19
|
+
* <Radio value="ltp" label="LTP" />
|
|
20
|
+
* </RadioGroup>
|
|
21
|
+
*/
|
|
22
|
+
const RadioGroup$1 = React.forwardRef(function RadioGroup$2({ className, orientation = "vertical", ...elementProps }, ref) {
|
|
23
|
+
return /* @__PURE__ */ jsx(RadioGroup, {
|
|
24
|
+
ref,
|
|
25
|
+
"data-slot": "radio-group",
|
|
26
|
+
"data-orientation": orientation,
|
|
27
|
+
className: cn("flex", orientation === "horizontal" ? "flex-row flex-wrap items-center gap-(--cue-space-5)" : "flex-col gap-(--cue-space-2)", className),
|
|
28
|
+
...elementProps
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* One option inside a {@link RadioGroup}.
|
|
33
|
+
*
|
|
34
|
+
* Round, to say "exactly one" the way a square {@link Checkbox} says "any
|
|
35
|
+
* number". Checked draws an accent rim with an accent core rather than a solid
|
|
36
|
+
* fill, so the selected option stays legible against the sunken field colour on
|
|
37
|
+
* every theme.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* <Radio value="htp" label="Highest takes precedence" />
|
|
41
|
+
*/
|
|
42
|
+
const Radio$1 = React.forwardRef(function Radio$2({ className, label, size = "md", ...elementProps }, ref) {
|
|
43
|
+
const labelId = React.useId();
|
|
44
|
+
const hasLabel = label !== void 0;
|
|
45
|
+
const dot = /* @__PURE__ */ jsx(Radio.Root, {
|
|
46
|
+
ref,
|
|
47
|
+
"data-slot": "radio",
|
|
48
|
+
"aria-labelledby": hasLabel ? labelId : void 0,
|
|
49
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full border border-border bg-sunken text-accent outline-none transition-colors", size === "sm" ? "size-icon-sm" : "size-icon-md", "hover:border-border-strong", "data-[checked]:border-accent", "focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-1 focus-visible:outline-accent", "data-[disabled]:pointer-events-none data-[disabled]:opacity-40", "data-[invalid]:border-danger", className),
|
|
50
|
+
...elementProps,
|
|
51
|
+
children: /* @__PURE__ */ jsx(Radio.Indicator, { className: "block size-1/2 rounded-full bg-accent data-[unchecked]:hidden" })
|
|
52
|
+
});
|
|
53
|
+
if (!hasLabel) return dot;
|
|
54
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
55
|
+
"data-slot": "radio-label",
|
|
56
|
+
className: "inline-flex cursor-pointer items-center gap-(--cue-space-3) font-mono text-(length:--cue-text-ui) text-fg-muted select-none has-[[data-disabled]]:cursor-default has-[[data-disabled]]:opacity-40",
|
|
57
|
+
children: [dot, /* @__PURE__ */ jsx("span", {
|
|
58
|
+
id: labelId,
|
|
59
|
+
children: label
|
|
60
|
+
})]
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { Radio$1 as Radio, RadioGroup$1 as RadioGroup };
|