@delacour/react-native-ui 0.1.0-alpha.20260925053522
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/README.md +113 -0
- package/package.json +125 -0
- package/src/components/accordion/AGENTS.md +193 -0
- package/src/components/accordion/accordion-content.tsx +125 -0
- package/src/components/accordion/accordion-description.tsx +17 -0
- package/src/components/accordion/accordion-indicator.tsx +104 -0
- package/src/components/accordion/accordion-item.tsx +78 -0
- package/src/components/accordion/accordion-title.tsx +18 -0
- package/src/components/accordion/accordion-trigger.tsx +148 -0
- package/src/components/accordion/accordion.context.tsx +153 -0
- package/src/components/accordion/accordion.tsx +253 -0
- package/src/components/accordion/accordion.types.ts +11 -0
- package/src/components/accordion/accordion.variants.test.ts +434 -0
- package/src/components/accordion/accordion.variants.ts +358 -0
- package/src/components/accordion/index.ts +45 -0
- package/src/components/badge/AGENTS.md +83 -0
- package/src/components/badge/badge-close-button.tsx +48 -0
- package/src/components/badge/badge-end-content.tsx +15 -0
- package/src/components/badge/badge-label.tsx +24 -0
- package/src/components/badge/badge-start-content.tsx +16 -0
- package/src/components/badge/badge.context.tsx +64 -0
- package/src/components/badge/badge.tsx +192 -0
- package/src/components/badge/badge.types.ts +10 -0
- package/src/components/badge/badge.variants.test.ts +269 -0
- package/src/components/badge/badge.variants.ts +238 -0
- package/src/components/badge/index.ts +17 -0
- package/src/components/bottom-sheet/AGENTS.md +242 -0
- package/src/components/bottom-sheet/bottom-sheet-background.tsx +35 -0
- package/src/components/bottom-sheet/bottom-sheet-close.tsx +60 -0
- package/src/components/bottom-sheet/bottom-sheet-container.tsx +246 -0
- package/src/components/bottom-sheet/bottom-sheet-content.tsx +66 -0
- package/src/components/bottom-sheet/bottom-sheet-description.tsx +29 -0
- package/src/components/bottom-sheet/bottom-sheet-footer.tsx +150 -0
- package/src/components/bottom-sheet/bottom-sheet-handle.tsx +47 -0
- package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +180 -0
- package/src/components/bottom-sheet/bottom-sheet-portal.tsx +72 -0
- package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +105 -0
- package/src/components/bottom-sheet/bottom-sheet-title.tsx +24 -0
- package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +73 -0
- package/src/components/bottom-sheet/bottom-sheet.context.tsx +135 -0
- package/src/components/bottom-sheet/bottom-sheet.tsx +125 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +293 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +220 -0
- package/src/components/bottom-sheet/index.ts +40 -0
- package/src/components/bottom-sheet/use-bottom-sheet-input.ts +125 -0
- package/src/components/button/AGENTS.md +217 -0
- package/src/components/button/button-end-content.tsx +15 -0
- package/src/components/button/button-group-separator.tsx +37 -0
- package/src/components/button/button-group-text.tsx +101 -0
- package/src/components/button/button-group.tsx +158 -0
- package/src/components/button/button-label.tsx +24 -0
- package/src/components/button/button-start-content.tsx +15 -0
- package/src/components/button/button.context.tsx +187 -0
- package/src/components/button/button.tsx +273 -0
- package/src/components/button/button.types.ts +10 -0
- package/src/components/button/button.variants.test.ts +811 -0
- package/src/components/button/button.variants.ts +480 -0
- package/src/components/button/index.ts +50 -0
- package/src/components/chart/AGENTS.md +210 -0
- package/src/components/chart/chart-area.tsx +93 -0
- package/src/components/chart/chart-bar.tsx +103 -0
- package/src/components/chart/chart-bars.tsx +75 -0
- package/src/components/chart/chart-candlestick.tsx +60 -0
- package/src/components/chart/chart-grid.tsx +33 -0
- package/src/components/chart/chart-legend.tsx +41 -0
- package/src/components/chart/chart-line.tsx +50 -0
- package/src/components/chart/chart-scatter.tsx +35 -0
- package/src/components/chart/chart-tooltip-dot.tsx +70 -0
- package/src/components/chart/chart-tooltip-x.tsx +121 -0
- package/src/components/chart/chart-tooltip-y.tsx +55 -0
- package/src/components/chart/chart-tooltip.tsx +133 -0
- package/src/components/chart/chart-x-axis.tsx +27 -0
- package/src/components/chart/chart-y-axis.tsx +21 -0
- package/src/components/chart/chart.context.tsx +127 -0
- package/src/components/chart/chart.tsx +448 -0
- package/src/components/chart/chart.types.ts +108 -0
- package/src/components/chart/chart.variants.test.ts +893 -0
- package/src/components/chart/chart.variants.ts +726 -0
- package/src/components/chart/index.ts +103 -0
- package/src/components/chart/pie-chart-center.tsx +40 -0
- package/src/components/chart/pie-chart-label.tsx +56 -0
- package/src/components/chart/pie-chart-slice.tsx +45 -0
- package/src/components/chart/pie-chart-tooltip.tsx +85 -0
- package/src/components/chart/pie-chart.context.tsx +77 -0
- package/src/components/chart/pie-chart.tsx +247 -0
- package/src/components/chart/use-chart-font.ts +25 -0
- package/src/components/chart/use-chart-palette.ts +38 -0
- package/src/components/checkbox/AGENTS.md +185 -0
- package/src/components/checkbox/checkbox-box.tsx +152 -0
- package/src/components/checkbox/checkbox-group.tsx +107 -0
- package/src/components/checkbox/checkbox-label.tsx +34 -0
- package/src/components/checkbox/checkbox.context.tsx +136 -0
- package/src/components/checkbox/checkbox.tsx +258 -0
- package/src/components/checkbox/checkbox.types.ts +14 -0
- package/src/components/checkbox/checkbox.variants.test.ts +634 -0
- package/src/components/checkbox/checkbox.variants.ts +484 -0
- package/src/components/checkbox/index.ts +42 -0
- package/src/components/field/AGENTS.md +112 -0
- package/src/components/field/field-content.tsx +24 -0
- package/src/components/field/field-description.tsx +29 -0
- package/src/components/field/field-error.tsx +61 -0
- package/src/components/field/field-group.tsx +22 -0
- package/src/components/field/field-label.tsx +47 -0
- package/src/components/field/field-legend.tsx +34 -0
- package/src/components/field/field-separator.tsx +81 -0
- package/src/components/field/field-set.tsx +22 -0
- package/src/components/field/field.context.tsx +95 -0
- package/src/components/field/field.tsx +157 -0
- package/src/components/field/field.types.ts +15 -0
- package/src/components/field/field.variants.test.ts +240 -0
- package/src/components/field/field.variants.ts +156 -0
- package/src/components/field/index.ts +22 -0
- package/src/components/icon/AGENTS.md +104 -0
- package/src/components/icon/icon.context.tsx +29 -0
- package/src/components/icon/icon.tsx +92 -0
- package/src/components/icon/icon.variants.test.ts +111 -0
- package/src/components/icon/icon.variants.ts +79 -0
- package/src/components/icon/index.ts +11 -0
- package/src/components/input/AGENTS.md +126 -0
- package/src/components/input/index.ts +27 -0
- package/src/components/input/input-group-decorator.tsx +84 -0
- package/src/components/input/input-group-prefix.tsx +16 -0
- package/src/components/input/input-group-suffix.tsx +15 -0
- package/src/components/input/input-group.tsx +140 -0
- package/src/components/input/input.context.tsx +87 -0
- package/src/components/input/input.tsx +184 -0
- package/src/components/input/input.types.ts +10 -0
- package/src/components/input/input.variants.test.ts +369 -0
- package/src/components/input/input.variants.ts +329 -0
- package/src/components/list-group/AGENTS.md +67 -0
- package/src/components/list-group/index.ts +20 -0
- package/src/components/list-group/list-group-item-content.tsx +10 -0
- package/src/components/list-group/list-group-item-description.tsx +12 -0
- package/src/components/list-group/list-group-item-prefix.tsx +30 -0
- package/src/components/list-group/list-group-item-suffix.tsx +55 -0
- package/src/components/list-group/list-group-item-title.tsx +17 -0
- package/src/components/list-group/list-group-item.tsx +87 -0
- package/src/components/list-group/list-group.context.tsx +65 -0
- package/src/components/list-group/list-group.tsx +116 -0
- package/src/components/list-group/list-group.types.ts +14 -0
- package/src/components/list-group/list-group.variants.test.ts +223 -0
- package/src/components/list-group/list-group.variants.ts +107 -0
- package/src/components/pressable/AGENTS.md +74 -0
- package/src/components/pressable/index.ts +9 -0
- package/src/components/pressable/pressable.tsx +261 -0
- package/src/components/pressable/pressable.variants.test.ts +128 -0
- package/src/components/pressable/pressable.variants.ts +80 -0
- package/src/components/provider/AGENTS.md +90 -0
- package/src/components/provider/index.ts +1 -0
- package/src/components/provider/provider.tsx +105 -0
- package/src/components/radio/AGENTS.md +245 -0
- package/src/components/radio/index.ts +35 -0
- package/src/components/radio/radio-group.tsx +126 -0
- package/src/components/radio/radio-indicator.tsx +91 -0
- package/src/components/radio/radio-label.tsx +39 -0
- package/src/components/radio/radio.context.tsx +132 -0
- package/src/components/radio/radio.tsx +215 -0
- package/src/components/radio/radio.variants.test.ts +580 -0
- package/src/components/radio/radio.variants.ts +271 -0
- package/src/components/screen/AGENTS.md +287 -0
- package/src/components/screen/index.ts +85 -0
- package/src/components/screen/screen-chat-list.tsx +485 -0
- package/src/components/screen/screen-content.tsx +69 -0
- package/src/components/screen/screen-debug.ts +43 -0
- package/src/components/screen/screen-error.tsx +61 -0
- package/src/components/screen/screen-flat-list.tsx +88 -0
- package/src/components/screen/screen-footer-background.tsx +60 -0
- package/src/components/screen/screen-footer.tsx +170 -0
- package/src/components/screen/screen-header.tsx +36 -0
- package/src/components/screen/screen-legend-list.tsx +105 -0
- package/src/components/screen/screen-list-component.tsx +23 -0
- package/src/components/screen/screen-loading.tsx +50 -0
- package/src/components/screen/screen-navbar-back-button.tsx +79 -0
- package/src/components/screen/screen-navbar-background.tsx +45 -0
- package/src/components/screen/screen-navbar-subtitle.tsx +26 -0
- package/src/components/screen/screen-navbar-title.tsx +31 -0
- package/src/components/screen/screen-navbar.tsx +154 -0
- package/src/components/screen/screen-root.tsx +38 -0
- package/src/components/screen/screen-scroll-area.tsx +116 -0
- package/src/components/screen/screen-scroll-shadow.tsx +186 -0
- package/src/components/screen/screen-section-list.tsx +81 -0
- package/src/components/screen/screen-view.tsx +59 -0
- package/src/components/screen/screen.context.tsx +167 -0
- package/src/components/screen/screen.tsx +114 -0
- package/src/components/screen/screen.types.ts +66 -0
- package/src/components/screen/screen.variants.test.ts +602 -0
- package/src/components/screen/screen.variants.ts +475 -0
- package/src/components/screen/use-screen-scroll-insets.ts +218 -0
- package/src/components/separator/AGENTS.md +30 -0
- package/src/components/separator/index.ts +8 -0
- package/src/components/separator/separator.tsx +85 -0
- package/src/components/slider/AGENTS.md +274 -0
- package/src/components/slider/index.ts +50 -0
- package/src/components/slider/slider-fill.tsx +68 -0
- package/src/components/slider/slider-output.tsx +55 -0
- package/src/components/slider/slider-thumb.tsx +193 -0
- package/src/components/slider/slider-track.tsx +233 -0
- package/src/components/slider/slider.context.tsx +118 -0
- package/src/components/slider/slider.tsx +321 -0
- package/src/components/slider/slider.types.ts +26 -0
- package/src/components/slider/slider.variants.test.ts +856 -0
- package/src/components/slider/slider.variants.ts +661 -0
- package/src/components/spinner/AGENTS.md +73 -0
- package/src/components/spinner/index.ts +17 -0
- package/src/components/spinner/spinner-arc.tsx +90 -0
- package/src/components/spinner/spinner-content.tsx +63 -0
- package/src/components/spinner/spinner.context.tsx +52 -0
- package/src/components/spinner/spinner.tsx +128 -0
- package/src/components/spinner/spinner.variants.test.ts +273 -0
- package/src/components/spinner/spinner.variants.ts +187 -0
- package/src/components/switch/AGENTS.md +213 -0
- package/src/components/switch/index.ts +42 -0
- package/src/components/switch/switch-content.tsx +111 -0
- package/src/components/switch/switch-end-content.tsx +18 -0
- package/src/components/switch/switch-start-content.tsx +20 -0
- package/src/components/switch/switch-thumb.tsx +102 -0
- package/src/components/switch/switch.context.tsx +79 -0
- package/src/components/switch/switch.tsx +423 -0
- package/src/components/switch/switch.types.ts +14 -0
- package/src/components/switch/switch.variants.test.ts +570 -0
- package/src/components/switch/switch.variants.ts +511 -0
- package/src/components/tabs/AGENTS.md +287 -0
- package/src/components/tabs/index.ts +69 -0
- package/src/components/tabs/tabs-content.tsx +65 -0
- package/src/components/tabs/tabs-indicator.tsx +97 -0
- package/src/components/tabs/tabs-label.tsx +62 -0
- package/src/components/tabs/tabs-list.tsx +139 -0
- package/src/components/tabs/tabs-pager.tsx +59 -0
- package/src/components/tabs/tabs-scroll-view.tsx +171 -0
- package/src/components/tabs/tabs-separator.tsx +73 -0
- package/src/components/tabs/tabs-trigger.tsx +210 -0
- package/src/components/tabs/tabs.context.tsx +294 -0
- package/src/components/tabs/tabs.tsx +435 -0
- package/src/components/tabs/tabs.types.ts +13 -0
- package/src/components/tabs/tabs.variants.test.ts +1020 -0
- package/src/components/tabs/tabs.variants.ts +670 -0
- package/src/components/text/AGENTS.md +95 -0
- package/src/components/text/index.ts +25 -0
- package/src/components/text/text.context.tsx +60 -0
- package/src/components/text/text.tsx +251 -0
- package/src/components/text/text.variants.test.ts +422 -0
- package/src/components/text/text.variants.ts +282 -0
- package/src/display-name.test.ts +145 -0
- package/src/docs.test.ts +98 -0
- package/src/expo/navigation-theme.tsx +58 -0
- package/src/hooks/use-controllable-state.ts +45 -0
- package/src/hooks/use-keyboard-state-sync.tsx +147 -0
- package/src/hooks/use-navigation-theme.ts +78 -0
- package/src/hooks/use-theme-color.ts +44 -0
- package/src/icons/central.ts +1 -0
- package/src/lib/cn.test.ts +136 -0
- package/src/lib/cn.ts +29 -0
- package/src/lib/color.test.ts +80 -0
- package/src/lib/color.ts +79 -0
- package/src/lib/compose-refs.test.ts +64 -0
- package/src/lib/compose-refs.ts +37 -0
- package/src/lib/keyboard-animation.test.ts +33 -0
- package/src/lib/keyboard-animation.ts +28 -0
- package/src/lib/merge-props.test.ts +82 -0
- package/src/lib/merge-props.ts +47 -0
- package/src/lib/navigation-theme.test.ts +49 -0
- package/src/lib/navigation-theme.ts +53 -0
- package/src/lib/slot.tsx +45 -0
- package/src/lib/tv.ts +20 -0
- package/src/styles/base.css +2 -0
- package/src/styles/geometry.test.ts +117 -0
- package/src/styles/index.css +3 -0
- package/src/styles/theme-tokens.test.ts +252 -0
- package/src/styles/theme.css +447 -0
- package/src/styles/tokens.css +132 -0
- package/src/styles/tokens.test.ts +181 -0
- package/src/styles/tokens.ts +86 -0
- package/src/uniwind-env.d.ts +1 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import type { VariantProps } from "tailwind-variants";
|
|
2
|
+
import { tv } from "../../lib/tv";
|
|
3
|
+
import type { IconSize } from "../icon/icon.variants";
|
|
4
|
+
|
|
5
|
+
/** What a switch that is on means. Badge's, Checkbox's and Slider's set. */
|
|
6
|
+
export const SWITCH_COLORS = ["default", "primary", "success", "warning", "destructive", "info"] as const;
|
|
7
|
+
|
|
8
|
+
export const SWITCH_SIZES = ["sm", "md", "lg"] as const;
|
|
9
|
+
|
|
10
|
+
export type SwitchColor = (typeof SWITCH_COLORS)[number];
|
|
11
|
+
export type SwitchSize = (typeof SWITCH_SIZES)[number];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The axes a switch falls back to when nothing names one.
|
|
15
|
+
*
|
|
16
|
+
* Named once because two places read them — `defaultVariants` below and
|
|
17
|
+
* {@link resolveSwitchAxes}, which runs before `tv` is ever called. A test pins
|
|
18
|
+
* the pair, since a drift between them is a switch that renders at one size and
|
|
19
|
+
* reports another.
|
|
20
|
+
*/
|
|
21
|
+
export const SWITCH_DEFAULT_COLOR: SwitchColor = "default";
|
|
22
|
+
export const SWITCH_DEFAULT_SIZE: SwitchSize = "md";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The step every glyph on this control is drawn at — inside the knob, or at
|
|
26
|
+
* either end of the track. One step rather than two, so a tick inside the thumb
|
|
27
|
+
* and a tick behind it are the same mark. The `glyph` slot writes it out and a
|
|
28
|
+
* test pins the two together.
|
|
29
|
+
*/
|
|
30
|
+
export const SWITCH_CONTENT_ICON_STEP: Record<SwitchSize, IconSize> = { sm: "xs", md: "sm", lg: "md" };
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* How far the knob sits inside the track, on every side, in points.
|
|
34
|
+
*
|
|
35
|
+
* One number rather than two. Horizontally it is written into the `thumb`,
|
|
36
|
+
* `startContent` and `endContent` slots as `left-0.75` / `right-0.75` and
|
|
37
|
+
* subtracted twice by {@link switchTravel}, so a slot and this constant
|
|
38
|
+
* disagreeing is a knob that stops short of the far edge at every size — a test
|
|
39
|
+
* pins the class against it.
|
|
40
|
+
*
|
|
41
|
+
* Vertically it is never written at all. The track is `justify-center`, so an
|
|
42
|
+
* absolutely positioned child with no vertical inset is centred by its parent —
|
|
43
|
+
* `Slider`'s rule, where the track centres the thumb and the thumb carries no
|
|
44
|
+
* offset of its own. On that axis this is a *relationship* a test pins,
|
|
45
|
+
* `(track height − knob height) / 2`, rather than a value anything reads.
|
|
46
|
+
*
|
|
47
|
+
* It is also what makes the two capsules concentric: both are `rounded-full`, so
|
|
48
|
+
* each radius is half its own height and the difference between them is exactly
|
|
49
|
+
* this inset. That is the subtraction `Checkbox`'s fill makes against its
|
|
50
|
+
* border, arrived at by construction rather than by a second number.
|
|
51
|
+
*
|
|
52
|
+
* Three rather than two because two left the knob looking wedged against the
|
|
53
|
+
* ends — a rounded rectangle needs a little more air around it than a disc,
|
|
54
|
+
* whose curve already reads as clearance.
|
|
55
|
+
*/
|
|
56
|
+
export const SWITCH_THUMB_INSET = 3;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The spring the thumb settles on after a tap or a release.
|
|
60
|
+
*
|
|
61
|
+
* **Critically damped on purpose**, where every other spring in this package
|
|
62
|
+
* overshoots a little. A wider thumb travels a shorter distance inside a track
|
|
63
|
+
* that clips it, so an overshoot has nowhere to go: the knob would visibly
|
|
64
|
+
* squash against the end of its own capsule on every toggle. A test pins the
|
|
65
|
+
* damping ratio at or above one, so a retune cannot quietly reintroduce it.
|
|
66
|
+
*/
|
|
67
|
+
export const SWITCH_THUMB_SPRING = { damping: 26, mass: 0.4, stiffness: 400 } as const;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The spring the whole control settles on when it is pressed and released.
|
|
71
|
+
*
|
|
72
|
+
* Deliberately `Pressable`'s own `PRESS_SPRING`: a switch responding to a touch
|
|
73
|
+
* is the same event as a button responding to one, and two springs a frame apart
|
|
74
|
+
* would read as two things happening. `Slider` copies it for the same reason.
|
|
75
|
+
*/
|
|
76
|
+
export const SWITCH_PRESS_SPRING = { damping: 18, mass: 0.4, stiffness: 320 } as const;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* How far the whole control shrinks while a finger is on it.
|
|
80
|
+
*
|
|
81
|
+
* The **track** scales, not the thumb — the track is the outermost node, so it
|
|
82
|
+
* is the one thing `overflow-hidden` cannot clip, and scaling the knob inside a
|
|
83
|
+
* capsule that crops it would take a bite out of the knob rather than acknowledge
|
|
84
|
+
* the press. It is also the only press feedback this control has: until a drag
|
|
85
|
+
* actually moves the thumb, nothing else says the touch landed.
|
|
86
|
+
*/
|
|
87
|
+
export const SWITCH_PRESS_ANIMATION = { restScale: 1, pressedScale: 0.96 } as const;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* How far a release may have travelled and still count as a tap, in points.
|
|
91
|
+
*
|
|
92
|
+
* A finger never leaves a control perfectly still, so "did not move" has to be a
|
|
93
|
+
* tolerance rather than zero. Below it the release toggles whatever the state
|
|
94
|
+
* was; above it the position and the velocity decide, and a drag that went out
|
|
95
|
+
* and came back therefore commits nothing.
|
|
96
|
+
*/
|
|
97
|
+
export const SWITCH_TAP_SLOP = 4;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The release speed that decides the outcome on its own, in points per second.
|
|
101
|
+
*
|
|
102
|
+
* A flick is a statement of intent: let go fast enough and the switch goes the
|
|
103
|
+
* way the finger was going, however short of half way it stopped. Without it a
|
|
104
|
+
* quick flick from the left edge would spring back, because the thumb never got
|
|
105
|
+
* past the middle — the one case where position is the wrong question.
|
|
106
|
+
*/
|
|
107
|
+
export const SWITCH_FLING_VELOCITY = 500;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Theme token the track fades **to** at each colour.
|
|
111
|
+
*
|
|
112
|
+
* `default` names `foreground` rather than `primary`, and the two are the same
|
|
113
|
+
* value in this theme today — the situation `Badge` documents for its neutral
|
|
114
|
+
* end and `Slider` repeats. `foreground` is the page's ink and `primary` is the
|
|
115
|
+
* brand's action colour; collapsing them would be the drift, not the
|
|
116
|
+
* duplication, since an app that re-themes `primary` to blue wants
|
|
117
|
+
* `color="primary"` blue and `color="default"` still ink.
|
|
118
|
+
*/
|
|
119
|
+
export const SWITCH_TRACK_TOKEN: Record<SwitchColor, string> = {
|
|
120
|
+
default: "foreground",
|
|
121
|
+
primary: "primary",
|
|
122
|
+
success: "success",
|
|
123
|
+
warning: "warning",
|
|
124
|
+
destructive: "destructive",
|
|
125
|
+
info: "info",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The track a switch that is off wears.
|
|
130
|
+
*
|
|
131
|
+
* `input` rather than `secondary`, and it is the token that makes the knob
|
|
132
|
+
* legible without a border: it is the chrome a field's own box wears, a step
|
|
133
|
+
* darker than the page in light and a step lighter in dark, so a knob painted
|
|
134
|
+
* the page's own colour reads against it at either end of the theme. `secondary`
|
|
135
|
+
* sits too close to the surface behind it for that.
|
|
136
|
+
*/
|
|
137
|
+
export const SWITCH_TRACK_REST_TOKEN = "input";
|
|
138
|
+
|
|
139
|
+
/** The track, on or off, once the switch is reporting an invalid value. */
|
|
140
|
+
export const SWITCH_INVALID_TRACK_TOKEN = "destructive";
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Theme token the thumb fades **to** at each colour.
|
|
144
|
+
*
|
|
145
|
+
* Every entry is the `-foreground` of its own {@link SWITCH_TRACK_TOKEN} entry,
|
|
146
|
+
* which is rule 11 doing its job: a single pale knob would be unreadable on
|
|
147
|
+
* `warning`, whose foreground is near-black, so the knob follows the surface it
|
|
148
|
+
* sits on. `default` is the exception the theme forces — there is no
|
|
149
|
+
* `--foreground-foreground`, and `background` is what content drawn on the
|
|
150
|
+
* page's ink actually is. A test pins the whole map against the track's rather
|
|
151
|
+
* than trusting two tables to stay in step.
|
|
152
|
+
*/
|
|
153
|
+
export const SWITCH_THUMB_TOKEN: Record<SwitchColor, string> = {
|
|
154
|
+
default: "background",
|
|
155
|
+
primary: "primary-foreground",
|
|
156
|
+
success: "success-foreground",
|
|
157
|
+
warning: "warning-foreground",
|
|
158
|
+
destructive: "destructive-foreground",
|
|
159
|
+
info: "info-foreground",
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/** The thumb a switch that is off wears. */
|
|
163
|
+
export const SWITCH_THUMB_REST_TOKEN = "background";
|
|
164
|
+
|
|
165
|
+
/** The thumb, on or off, once the switch is reporting an invalid value. */
|
|
166
|
+
export const SWITCH_INVALID_THUMB_TOKEN = "destructive-foreground";
|
|
167
|
+
|
|
168
|
+
/** The colour a glyph inside `Switch.EndContent` takes — it sits on the *off* track. */
|
|
169
|
+
export const SWITCH_CONTENT_REST_TOKEN = "secondary-foreground";
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The `text-*` class a `Text` inside a content layer takes at each colour.
|
|
173
|
+
*
|
|
174
|
+
* A colour that has to reach a glyph is a token; a colour that has to reach a
|
|
175
|
+
* `Text` is a class, because text colour goes on the `Text` and a `View` does
|
|
176
|
+
* not cascade it (rule 1). So this map exists beside {@link SWITCH_THUMB_TOKEN}
|
|
177
|
+
* rather than being derived from it: Tailwind's scanner is static, so a runtime
|
|
178
|
+
* `text-${token}` is never compiled and would silently draw nothing. A test pins
|
|
179
|
+
* every entry against the token it must agree with, which is the difference
|
|
180
|
+
* between a coupling that is checked and one that is merely hoped for.
|
|
181
|
+
*/
|
|
182
|
+
export const SWITCH_CONTENT_TEXT_CLASS: Record<SwitchColor, string> = {
|
|
183
|
+
default: "text-background",
|
|
184
|
+
primary: "text-primary-foreground",
|
|
185
|
+
success: "text-success-foreground",
|
|
186
|
+
warning: "text-warning-foreground",
|
|
187
|
+
destructive: "text-destructive-foreground",
|
|
188
|
+
info: "text-info-foreground",
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** What a `Text` inside `Switch.EndContent` takes — it sits on the *off* track. */
|
|
192
|
+
export const SWITCH_CONTENT_REST_TEXT_CLASS = "text-secondary-foreground";
|
|
193
|
+
|
|
194
|
+
/** What either content layer takes once the switch is reporting an invalid value. */
|
|
195
|
+
export const SWITCH_INVALID_CONTENT_TEXT_CLASS = "text-destructive-foreground";
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Styling for every part of a switch.
|
|
199
|
+
*
|
|
200
|
+
* One slotted `tv()` rather than a call per part, because `switch-thumb.tsx` and
|
|
201
|
+
* the two content parts cannot import the root without closing a cycle
|
|
202
|
+
* (AGENTS.md rule 3) yet all three read the same `size`.
|
|
203
|
+
*
|
|
204
|
+
* **It describes geometry, and almost no colour.** Every colour on this control
|
|
205
|
+
* fades between two token *values* off the one `progress` — the track, the thumb
|
|
206
|
+
* and both content layers — and a colour being interpolated cannot be a class,
|
|
207
|
+
* which is the rule `Checkbox`'s border already follows. So there is no `color`
|
|
208
|
+
* axis here at all: the maps above are where the colour lives, and the base
|
|
209
|
+
* classes are only the resting appearance the animated styles start from. Adding
|
|
210
|
+
* a `bg-*` per colour here would be a second source for one surface, which is
|
|
211
|
+
* how a class and a style end up disagreeing for a frame on every toggle.
|
|
212
|
+
*
|
|
213
|
+
* **The track's height and the touch padding ride in the same size cell**,
|
|
214
|
+
* because they are one number: they sum to 44pt at every size. Split them across
|
|
215
|
+
* two variants and a retune of the track silently shrinks the target — the trap
|
|
216
|
+
* `Slider` names, and a test asserts the sum rather than the parts.
|
|
217
|
+
*
|
|
218
|
+
* **The thumb is as wide as the track is tall, and shorter than it.** It is a
|
|
219
|
+
* rounded rectangle lying on its side, not a disc — which is why it stopped
|
|
220
|
+
* reading `--spacing-icon-*` and became plain spacing steps. That is the move
|
|
221
|
+
* `Slider`'s handle already made, and for the same reason: the icon scale is
|
|
222
|
+
* where a *glyph* belongs, and a knob is the body of the control rather than a
|
|
223
|
+
* mark drawn on it. `Checkbox` still reads that scale because its square really
|
|
224
|
+
* is a glyph in a box.
|
|
225
|
+
*
|
|
226
|
+
* Three relationships hold at every size and a test pins each, so the ladder can
|
|
227
|
+
* be retuned without the test becoming a transcript of it: the thumb's width is
|
|
228
|
+
* the track's height, its height is that less twice
|
|
229
|
+
* {@link SWITCH_THUMB_INSET}, and the two capsules are therefore concentric.
|
|
230
|
+
*
|
|
231
|
+
* **The knob draws no border and no shadow.** A border is a second line drawn
|
|
232
|
+
* where there is already a boundary — the knob is a solid block against a track
|
|
233
|
+
* of a different colour — and against a saturated track it reads as a dark ring
|
|
234
|
+
* rather than as definition. `Slider`'s handle dropped its own for the same
|
|
235
|
+
* reason. Contrast at rest comes from the track's colour instead, which is why
|
|
236
|
+
* {@link SWITCH_TRACK_REST_TOKEN} is `input`.
|
|
237
|
+
*
|
|
238
|
+
* **A content layer is as wide as the thumb's travel, not as wide as the
|
|
239
|
+
* thumb.** It occupies exactly the space the knob vacates at its own end — which
|
|
240
|
+
* is what the travel *is* — so the two layers and the thumb tile the track
|
|
241
|
+
* instead of overlapping. Size them like the knob and they reach under it: the
|
|
242
|
+
* far layer's text is then clipped by a knob drawn on top of it, visible only at
|
|
243
|
+
* the size where the text is longest. They keep the thumb's *height*, so a glyph
|
|
244
|
+
* at either end sits on the knob's own centre line.
|
|
245
|
+
*
|
|
246
|
+
* **Every slot that can hold a caller's content centres it.** The thumb and both
|
|
247
|
+
* content layers are boxes a glyph is dropped into, and a `View` lays a child out
|
|
248
|
+
* at flex-start by default — so a glyph in a knob that did not centre sits up
|
|
249
|
+
* against its top-left corner, inside a circle, which reads as a mispositioned
|
|
250
|
+
* icon rather than as a missing class. A test sweeps all three.
|
|
251
|
+
*
|
|
252
|
+
* **`overflow-hidden` on the track is load-bearing, not tidiness.** A caller's
|
|
253
|
+
* `Switch.StartContent` is arbitrary content sitting inside a fully rounded
|
|
254
|
+
* capsule, and without the clip a wide glyph paints over the track's own edge.
|
|
255
|
+
* It is also why the thumb does **not** grow when grabbed the way a `Slider`'s
|
|
256
|
+
* does: a scaled thumb would be clipped by the very same rule, and a knob
|
|
257
|
+
* already following the finger does not need a second signal.
|
|
258
|
+
*
|
|
259
|
+
* **The fade lands on `touchArea`, the one slot no animated style touches.**
|
|
260
|
+
* `track` and `thumb` both carry a `useAnimatedStyle`, and while neither writes
|
|
261
|
+
* `opacity` today, a disabled treatment that would break the moment one did is a
|
|
262
|
+
* treatment waiting to fail silently — the failure mode `Pressable` once had
|
|
263
|
+
* with a class on its own animated node.
|
|
264
|
+
*
|
|
265
|
+
* **`self-start` is load-bearing.** A switch is sized by its own geometry, and
|
|
266
|
+
* inside a gap column every child is stretch-aligned by default — without it the
|
|
267
|
+
* touch area spans the screen and a tap far from the pill toggles it.
|
|
268
|
+
*
|
|
269
|
+
* No slot worn by a `View` carries `text-*`; a glyph's colour is a token handed
|
|
270
|
+
* to an `IconDefaultsProvider`. See AGENTS.md rule 1.
|
|
271
|
+
*
|
|
272
|
+
* Free of React Native imports so it stays unit-testable — `bun test` cannot
|
|
273
|
+
* parse React Native's Flow-typed source. See AGENTS.md.
|
|
274
|
+
*/
|
|
275
|
+
export const switchVariants = tv({
|
|
276
|
+
slots: {
|
|
277
|
+
/** The transparent box the drag is claimed on. Padded on the cross axis only. */
|
|
278
|
+
touchArea: "self-start items-center justify-center",
|
|
279
|
+
/** The capsule. Its colour is an animated style, never a class. */
|
|
280
|
+
track: "relative justify-center overflow-hidden rounded-full bg-input",
|
|
281
|
+
/** The knob. Its position and its colour are one animated style. */
|
|
282
|
+
thumb: "absolute left-0.75 items-center justify-center rounded-full bg-background",
|
|
283
|
+
/** Behind the thumb at the leading edge. Revealed as the switch turns on. */
|
|
284
|
+
startContent: "absolute left-0.75 items-center justify-center",
|
|
285
|
+
/** Behind the thumb at the trailing edge. Revealed as the switch turns off. */
|
|
286
|
+
endContent: "absolute right-0.75 items-center justify-center",
|
|
287
|
+
/** Edge length any glyph on this control inherits — inside the knob, or at either end. */
|
|
288
|
+
glyph: "",
|
|
289
|
+
/** Handed to a `TextClassProvider`, never worn by a `View`. Its colour is added per end. */
|
|
290
|
+
contentText: "font-semibold",
|
|
291
|
+
},
|
|
292
|
+
variants: {
|
|
293
|
+
size: {
|
|
294
|
+
sm: {
|
|
295
|
+
touchArea: "py-2.5",
|
|
296
|
+
track: "h-6 w-11",
|
|
297
|
+
thumb: "h-4.5 w-6",
|
|
298
|
+
startContent: "h-4.5 w-3.5",
|
|
299
|
+
endContent: "h-4.5 w-3.5",
|
|
300
|
+
glyph: "size-icon-xs",
|
|
301
|
+
contentText: "text-[8px]",
|
|
302
|
+
},
|
|
303
|
+
md: {
|
|
304
|
+
touchArea: "py-2",
|
|
305
|
+
track: "h-7 w-13",
|
|
306
|
+
thumb: "h-5.5 w-7",
|
|
307
|
+
startContent: "h-5.5 w-4.5",
|
|
308
|
+
endContent: "h-5.5 w-4.5",
|
|
309
|
+
glyph: "size-icon-sm",
|
|
310
|
+
contentText: "text-[10px]",
|
|
311
|
+
},
|
|
312
|
+
lg: {
|
|
313
|
+
touchArea: "py-1",
|
|
314
|
+
track: "h-9 w-17",
|
|
315
|
+
thumb: "h-7.5 w-9",
|
|
316
|
+
startContent: "h-7.5 w-6.5",
|
|
317
|
+
endContent: "h-7.5 w-6.5",
|
|
318
|
+
glyph: "size-icon-md",
|
|
319
|
+
contentText: "text-[10px]",
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
// The empty `false` branch is load-bearing typing, not a placeholder. `tv`
|
|
323
|
+
// derives the prop type from the declared keys, so a map with only `true`
|
|
324
|
+
// types the prop as `true` rather than `boolean`.
|
|
325
|
+
isDisabled: { true: { touchArea: "opacity-50" }, false: {} },
|
|
326
|
+
},
|
|
327
|
+
defaultVariants: {
|
|
328
|
+
size: SWITCH_DEFAULT_SIZE,
|
|
329
|
+
isDisabled: false,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
/** What a switch was given at its own call site. */
|
|
334
|
+
export type SwitchOwnAxes = {
|
|
335
|
+
color?: SwitchColor;
|
|
336
|
+
size?: SwitchSize;
|
|
337
|
+
isDisabled?: boolean;
|
|
338
|
+
isInvalid?: boolean;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/** What an enclosing `Field` publishes, or null outside one. */
|
|
342
|
+
export type SwitchFieldAxes = { isDisabled?: boolean; isInvalid?: boolean };
|
|
343
|
+
|
|
344
|
+
/** Every axis settled, ready to hand to {@link switchVariants} and to context. */
|
|
345
|
+
export type SwitchAxes = {
|
|
346
|
+
color: SwitchColor;
|
|
347
|
+
size: SwitchSize;
|
|
348
|
+
isDisabled: boolean;
|
|
349
|
+
isInvalid: boolean;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Settles a switch's axes from the two places they can come from.
|
|
354
|
+
*
|
|
355
|
+
* A two-rung ladder rather than `Checkbox`'s three, because there is no
|
|
356
|
+
* `Switch.Group`: the switch's own props first, an enclosing `Field` last. A
|
|
357
|
+
* switch inside a disabled `Field` dims with nothing said at the call site,
|
|
358
|
+
* while `isDisabled={false}` still opts that one control out — `??` throughout
|
|
359
|
+
* and never `||`, so an explicit `false` is a value rather than an absence.
|
|
360
|
+
*
|
|
361
|
+
* A `Field` reaches the two *state* axes only. It cannot acquire a paint axis by
|
|
362
|
+
* accident, and a test pins that.
|
|
363
|
+
*
|
|
364
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
365
|
+
*/
|
|
366
|
+
export function resolveSwitchAxes({ own, field }: { own?: SwitchOwnAxes; field?: SwitchFieldAxes | null }): SwitchAxes {
|
|
367
|
+
return {
|
|
368
|
+
color: own?.color ?? SWITCH_DEFAULT_COLOR,
|
|
369
|
+
size: own?.size ?? SWITCH_DEFAULT_SIZE,
|
|
370
|
+
isDisabled: own?.isDisabled ?? field?.isDisabled ?? false,
|
|
371
|
+
isInvalid: own?.isInvalid ?? field?.isInvalid ?? false,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** The two ends the track's colour interpolates between. */
|
|
376
|
+
export function resolveSwitchTrackTokens({ color, isInvalid }: { color: SwitchColor; isInvalid: boolean }): {
|
|
377
|
+
rest: string;
|
|
378
|
+
active: string;
|
|
379
|
+
} {
|
|
380
|
+
if (isInvalid) return { rest: SWITCH_INVALID_TRACK_TOKEN, active: SWITCH_INVALID_TRACK_TOKEN };
|
|
381
|
+
return { rest: SWITCH_TRACK_REST_TOKEN, active: SWITCH_TRACK_TOKEN[color] };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The two ends the thumb's colour interpolates between.
|
|
386
|
+
*
|
|
387
|
+
* An **invalid** switch returns destructive at both ends, on the track and on the
|
|
388
|
+
* knob, so there is nothing to fade — the colour is the signal the value is
|
|
389
|
+
* wrong, and it has to be there before the switch is turned on as much as after.
|
|
390
|
+
* The precedence `Checkbox` sets on its border.
|
|
391
|
+
*/
|
|
392
|
+
export function resolveSwitchThumbTokens({ color, isInvalid }: { color: SwitchColor; isInvalid: boolean }): {
|
|
393
|
+
rest: string;
|
|
394
|
+
active: string;
|
|
395
|
+
} {
|
|
396
|
+
if (isInvalid) return { rest: SWITCH_INVALID_THUMB_TOKEN, active: SWITCH_INVALID_THUMB_TOKEN };
|
|
397
|
+
return { rest: SWITCH_THUMB_REST_TOKEN, active: SWITCH_THUMB_TOKEN[color] };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Whether the caller wrote a `Switch.Thumb` of their own.
|
|
402
|
+
*
|
|
403
|
+
* The root composes one in when they did not, which is what makes `<Switch />`
|
|
404
|
+
* on its own a complete control — `Radio`'s rule for its indicator, and for the
|
|
405
|
+
* same reason: a switch has exactly one thumb, so it can know that a caller who
|
|
406
|
+
* wrote none wants the default rather than an empty capsule.
|
|
407
|
+
*
|
|
408
|
+
* Takes an array of "is this child a thumb" rather than the children themselves,
|
|
409
|
+
* so it stays free of React and reachable from `bun test` — the trade
|
|
410
|
+
* `resolveIndicatorPlacement` already makes.
|
|
411
|
+
*/
|
|
412
|
+
export function hasThumbChild(isThumb: readonly boolean[]): boolean {
|
|
413
|
+
return isThumb.some(Boolean);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* How far the thumb can travel, in points.
|
|
418
|
+
*
|
|
419
|
+
* A measured `0` means **not measured yet**, never "a track with no length", so
|
|
420
|
+
* this is floored rather than allowed to go negative: the thumb sits at the
|
|
421
|
+
* leading edge until both layouts have reported. `Slider` guards the same way.
|
|
422
|
+
*/
|
|
423
|
+
export function switchTravel({
|
|
424
|
+
trackWidth,
|
|
425
|
+
thumbWidth,
|
|
426
|
+
inset,
|
|
427
|
+
}: {
|
|
428
|
+
trackWidth: number;
|
|
429
|
+
thumbWidth: number;
|
|
430
|
+
/** {@link SWITCH_THUMB_INSET}. Taken off both ends. */
|
|
431
|
+
inset: number;
|
|
432
|
+
}): number {
|
|
433
|
+
"worklet";
|
|
434
|
+
const travel = trackWidth - thumbWidth - inset * 2;
|
|
435
|
+
return travel > 0 ? travel : 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Where a release leaves the switch.
|
|
440
|
+
*
|
|
441
|
+
* Three questions in order, and the order is the design. A release whose finger
|
|
442
|
+
* barely moved **in any direction** is a tap, so it toggles whatever the state
|
|
443
|
+
* was — this is what lets one `Gesture.Pan()` serve both gestures rather than
|
|
444
|
+
* racing a `Tap` against it. `distance` is deliberately not the along-track
|
|
445
|
+
* translation: a vertical swipe that began on the switch moves nothing
|
|
446
|
+
* horizontally, and reading only that axis would turn every attempt to scroll
|
|
447
|
+
* past the control into a toggle. A release travelling fast is a **flick**, and
|
|
448
|
+
* goes the way the finger was going however short of half way it stopped.
|
|
449
|
+
* Anything else is settled by position.
|
|
450
|
+
*
|
|
451
|
+
* Marked `"worklet"` and written flat — it calls nothing, not even to clamp.
|
|
452
|
+
* A module-scope worklet is rewritten into a factory call that runs at import
|
|
453
|
+
* time in source order, so one calling a sibling works only while the sibling
|
|
454
|
+
* happens to be declared first, and a tidy-up that reorders the file crashes the
|
|
455
|
+
* UI thread. See AGENTS.md under **Slider**.
|
|
456
|
+
*
|
|
457
|
+
* Pure, so `bun test` reaches the whole matrix.
|
|
458
|
+
*/
|
|
459
|
+
export function resolveSwitchRelease({
|
|
460
|
+
progress,
|
|
461
|
+
distance,
|
|
462
|
+
velocity,
|
|
463
|
+
wasSelected,
|
|
464
|
+
}: {
|
|
465
|
+
progress: number;
|
|
466
|
+
/** How far the finger moved, in points, on whichever axis moved further. */
|
|
467
|
+
distance: number;
|
|
468
|
+
velocity: number;
|
|
469
|
+
wasSelected: boolean;
|
|
470
|
+
}): boolean {
|
|
471
|
+
"worklet";
|
|
472
|
+
if (distance < SWITCH_TAP_SLOP) return !wasSelected;
|
|
473
|
+
if (velocity > SWITCH_FLING_VELOCITY) return true;
|
|
474
|
+
if (velocity < -SWITCH_FLING_VELOCITY) return false;
|
|
475
|
+
return progress > 0.5;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Which end of the track a content layer sits at. */
|
|
479
|
+
export type SwitchContentPlacement = "start" | "end";
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* The colour a content layer's glyphs and text take.
|
|
483
|
+
*
|
|
484
|
+
* The two ends sit on different surfaces, and that is the whole of the
|
|
485
|
+
* decision: `Switch.StartContent` is revealed when the switch is **on**, so it
|
|
486
|
+
* is drawn on the coloured track and takes that colour's `-foreground`;
|
|
487
|
+
* `Switch.EndContent` is revealed when it is **off**, so it is drawn on the
|
|
488
|
+
* resting track and takes `secondary`'s. Invalid outranks both, the precedence
|
|
489
|
+
* `Checkbox` sets on its border.
|
|
490
|
+
*
|
|
491
|
+
* Returns a token for the glyph and a class for the text, because those are what
|
|
492
|
+
* an `IconDefaultsProvider` and a `TextClassProvider` respectively take — the
|
|
493
|
+
* asymmetry AGENTS.md's **Theming** section sets out.
|
|
494
|
+
*
|
|
495
|
+
* Pure, so `bun test` reaches the whole matrix.
|
|
496
|
+
*/
|
|
497
|
+
export function resolveSwitchContentTreatment({
|
|
498
|
+
color,
|
|
499
|
+
isInvalid,
|
|
500
|
+
placement,
|
|
501
|
+
}: {
|
|
502
|
+
color: SwitchColor;
|
|
503
|
+
isInvalid: boolean;
|
|
504
|
+
placement: SwitchContentPlacement;
|
|
505
|
+
}): { color: string; textClass: string } {
|
|
506
|
+
if (isInvalid) return { color: SWITCH_INVALID_THUMB_TOKEN, textClass: SWITCH_INVALID_CONTENT_TEXT_CLASS };
|
|
507
|
+
if (placement === "end") return { color: SWITCH_CONTENT_REST_TOKEN, textClass: SWITCH_CONTENT_REST_TEXT_CLASS };
|
|
508
|
+
return { color: SWITCH_THUMB_TOKEN[color], textClass: SWITCH_CONTENT_TEXT_CLASS[color] };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export type SwitchVariantProps = VariantProps<typeof switchVariants>;
|