@delacour/react-native-ui 0.1.0-alpha.3
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 +192 -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 +435 -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 +232 -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 +245 -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 +148 -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 +95 -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 +276 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +199 -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 +100 -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 +33 -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 +78 -0
- package/src/components/field/field.tsx +144 -0
- package/src/components/field/field.types.ts +15 -0
- package/src/components/field/field.variants.test.ts +216 -0
- package/src/components/field/field.variants.ts +136 -0
- package/src/components/field/index.ts +21 -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 +64 -0
- package/src/components/pressable/index.ts +9 -0
- package/src/components/pressable/pressable.tsx +256 -0
- package/src/components/pressable/pressable.variants.test.ts +99 -0
- package/src/components/pressable/pressable.variants.ts +63 -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 +247 -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 +581 -0
- package/src/components/radio/radio.variants.ts +272 -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 +48 -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 +161 -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 +804 -0
- package/src/components/slider/slider.variants.ts +607 -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 +286 -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 +672 -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,480 @@
|
|
|
1
|
+
import type { VariantProps } from "tailwind-variants";
|
|
2
|
+
import { tv } from "../../lib/tv";
|
|
3
|
+
import type { PressableFeedback } from "../pressable/pressable.variants";
|
|
4
|
+
|
|
5
|
+
export const BUTTON_VARIANTS = [
|
|
6
|
+
"primary",
|
|
7
|
+
"secondary",
|
|
8
|
+
"tertiary",
|
|
9
|
+
"outline",
|
|
10
|
+
"ghost",
|
|
11
|
+
"destructive",
|
|
12
|
+
"destructive-soft",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
/** Sizes that hold a label. Horizontal padding; the width comes from the content. */
|
|
16
|
+
export const BUTTON_LABEL_SIZES = ["sm", "md", "lg"] as const;
|
|
17
|
+
|
|
18
|
+
/** Sizes with a square footprint, for a button whose only content is an icon. */
|
|
19
|
+
export const BUTTON_ICON_SIZES = ["icon-sm", "icon-md", "icon-lg"] as const;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Every value `size` accepts.
|
|
23
|
+
*
|
|
24
|
+
* Derived rather than written out — a tuple key is not a class, so composing one
|
|
25
|
+
* costs nothing. The class strings below are a different matter and are spelled
|
|
26
|
+
* in full; see the note on the `size` axis.
|
|
27
|
+
*/
|
|
28
|
+
export const BUTTON_SIZES = [...BUTTON_LABEL_SIZES, ...BUTTON_ICON_SIZES] as const;
|
|
29
|
+
|
|
30
|
+
export const BUTTON_SPINNER_PLACEMENTS = ["start", "end", "only"] as const;
|
|
31
|
+
|
|
32
|
+
export const BUTTON_GROUP_ORIENTATIONS = ["horizontal", "vertical"] as const;
|
|
33
|
+
|
|
34
|
+
export const BUTTON_GROUP_POSITIONS = ["first", "middle", "last", "only"] as const;
|
|
35
|
+
|
|
36
|
+
export type ButtonVariant = (typeof BUTTON_VARIANTS)[number];
|
|
37
|
+
export type ButtonLabelSize = (typeof BUTTON_LABEL_SIZES)[number];
|
|
38
|
+
export type ButtonIconSize = (typeof BUTTON_ICON_SIZES)[number];
|
|
39
|
+
export type ButtonSize = (typeof BUTTON_SIZES)[number];
|
|
40
|
+
export type ButtonSpinnerPlacement = (typeof BUTTON_SPINNER_PLACEMENTS)[number];
|
|
41
|
+
export type ButtonGroupOrientation = (typeof BUTTON_GROUP_ORIENTATIONS)[number];
|
|
42
|
+
export type ButtonGroupPosition = (typeof BUTTON_GROUP_POSITIONS)[number];
|
|
43
|
+
|
|
44
|
+
/** A member's place in its group, or `none` for a button standing on its own. */
|
|
45
|
+
export type ButtonGroupSlotPosition = ButtonGroupPosition | "none";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The axis a group's separator runs along — a rule crosses the run.
|
|
49
|
+
*
|
|
50
|
+
* Typed on the group's own union rather than on `SeparatorOrientation`, which
|
|
51
|
+
* lives in `separator.tsx` beside a React Native import and would make this file
|
|
52
|
+
* unparseable by `bun test`. The two vocabularies are the same two words, and a
|
|
53
|
+
* test asserts this map still names one of them.
|
|
54
|
+
*/
|
|
55
|
+
export const BUTTON_GROUP_SEPARATOR_ORIENTATION: Record<ButtonGroupOrientation, ButtonGroupOrientation> = {
|
|
56
|
+
horizontal: "vertical",
|
|
57
|
+
vertical: "horizontal",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** What a button presses with on its own, and what it presses with joined. */
|
|
61
|
+
export const BUTTON_FEEDBACK: PressableFeedback = "scale";
|
|
62
|
+
export const BUTTON_GROUP_FEEDBACK: PressableFeedback = "fade";
|
|
63
|
+
|
|
64
|
+
/** Theme token whose value gives icons and text their colour on each surface. */
|
|
65
|
+
export const BUTTON_FOREGROUND_TOKEN: Record<ButtonVariant, string> = {
|
|
66
|
+
primary: "primary-foreground",
|
|
67
|
+
secondary: "secondary-foreground",
|
|
68
|
+
tertiary: "tertiary-foreground",
|
|
69
|
+
outline: "foreground",
|
|
70
|
+
ghost: "foreground",
|
|
71
|
+
destructive: "destructive-foreground",
|
|
72
|
+
"destructive-soft": "destructive-soft-foreground",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Styling for every part of a button.
|
|
77
|
+
*
|
|
78
|
+
* One slotted `tv()` rather than a call per part, so `size` and `variant` are
|
|
79
|
+
* declared once.
|
|
80
|
+
*
|
|
81
|
+
* A size names tokens rather than raw utilities: `h-button-md` for the height,
|
|
82
|
+
* `text-button-md` for the label, `rounded-button-md` for the corner,
|
|
83
|
+
* `size-icon-md` for a composed glyph. The values live in `tokens.css`, so a
|
|
84
|
+
* button's icon and the spinner that replaces it resolve to the same edge
|
|
85
|
+
* length by construction rather than by two numbers happening to agree. `cn`
|
|
86
|
+
* has to know these token names — see `lib/cn.ts`. The root holds no `text-*`
|
|
87
|
+
* utility: a React Native `View` does not cascade colour to a `Text`
|
|
88
|
+
* descendant, so label colour lives on the `label` slot and icon colour is
|
|
89
|
+
* resolved from {@link BUTTON_FOREGROUND_TOKEN}.
|
|
90
|
+
*
|
|
91
|
+
* The corner belongs to the size axis and the group axis together, and to
|
|
92
|
+
* nothing else — not the base, not a variant — so exactly one corner
|
|
93
|
+
* *statement* ever reaches the root and a caller's `rounded-lg` has a single
|
|
94
|
+
* thing to beat. Each step is half its own height, which draws a capsule, and a
|
|
95
|
+
* circle on the square steps.
|
|
96
|
+
*
|
|
97
|
+
* A joined member **replaces** that corner with a per-side pair rather than
|
|
98
|
+
* layering a squaring class on top of it. Layering would in fact render
|
|
99
|
+
* correctly — Uniwind arbitrates a className string per style property in token
|
|
100
|
+
* order, and React Native puts a per-corner radius above the uniform one — but
|
|
101
|
+
* tailwind-merge deletes a side class the moment any all-corner class is emitted
|
|
102
|
+
* after it, and the `size` branch emitting one is exactly that case. Correctness
|
|
103
|
+
* would then rest on the key order of this object, one reorder away from a
|
|
104
|
+
* silent regression.
|
|
105
|
+
*
|
|
106
|
+
* A horizontal member squares on the inline axis (`rounded-s-*` / `rounded-e-*`,
|
|
107
|
+
* which flip under RTL); a vertical one squares top and bottom, where there is
|
|
108
|
+
* no logical form and no flip to want. **Never mix the two on one member** —
|
|
109
|
+
* React Native resolves a physical corner above a logical one, so a stray
|
|
110
|
+
* `rounded-t-*` would silently outrank a `rounded-s-*` beside it.
|
|
111
|
+
*
|
|
112
|
+
* A seamed member overlaps the one before it by a point, so two adjacent borders
|
|
113
|
+
* draw as one hairline instead of two. That is a negative margin rather than a
|
|
114
|
+
* dropped border on purpose: a margin moves the box and changes nothing about
|
|
115
|
+
* it, while `border-s-0` would make the member a point narrower than its
|
|
116
|
+
* neighbours and shift its centred content half a point — and would only be
|
|
117
|
+
* right for the variants that draw a visible border, which the group cannot know
|
|
118
|
+
* because a member may name its own.
|
|
119
|
+
*
|
|
120
|
+
* `overflow-hidden` on the root is load-bearing rather than tidiness — a pressed
|
|
121
|
+
* button fades to the edge of its own box, and the fade is clipped to the same
|
|
122
|
+
* corner the button draws.
|
|
123
|
+
*
|
|
124
|
+
* Free of React Native imports so it stays unit-testable — `bun test` cannot
|
|
125
|
+
* parse React Native's Flow-typed source. See AGENTS.md.
|
|
126
|
+
*/
|
|
127
|
+
export const buttonVariants = tv({
|
|
128
|
+
slots: {
|
|
129
|
+
root: "flex-row items-center justify-center gap-2 overflow-hidden border border-transparent",
|
|
130
|
+
label: "text-center font-semibold",
|
|
131
|
+
startContent: "items-center justify-center",
|
|
132
|
+
endContent: "items-center justify-center",
|
|
133
|
+
/** Edge length an `Icon` or `Spinner` composed into the button inherits. */
|
|
134
|
+
icon: "",
|
|
135
|
+
/**
|
|
136
|
+
* A `Button.Group`'s box.
|
|
137
|
+
*
|
|
138
|
+
* Holds no `gap` and no `overflow-hidden`, and both absences are
|
|
139
|
+
* load-bearing. A gap is the seam this component exists to close. A clip
|
|
140
|
+
* would square off the very corners the position compounds just rounded,
|
|
141
|
+
* and would have to restate the group's own corner to avoid it — while the
|
|
142
|
+
* members already square themselves, which is the whole point.
|
|
143
|
+
*
|
|
144
|
+
* Paints no state of its own either: a disabled group publishes
|
|
145
|
+
* `isDisabled` and each member fades itself, so a group and its members
|
|
146
|
+
* cannot compound `opacity-50` down to a quarter.
|
|
147
|
+
*/
|
|
148
|
+
group: "",
|
|
149
|
+
/** Wraps a group's rule. `shrink-0`, or a row of `flex-1` members squeezes it to nothing. */
|
|
150
|
+
groupSeparator: "shrink-0",
|
|
151
|
+
},
|
|
152
|
+
variants: {
|
|
153
|
+
variant: {
|
|
154
|
+
primary: { root: "bg-primary", label: "text-primary-foreground" },
|
|
155
|
+
secondary: { root: "bg-secondary", label: "text-secondary-foreground" },
|
|
156
|
+
tertiary: { root: "bg-tertiary", label: "text-tertiary-foreground" },
|
|
157
|
+
outline: { root: "border-border bg-transparent", label: "text-foreground" },
|
|
158
|
+
ghost: { root: "border-transparent bg-transparent", label: "text-foreground" },
|
|
159
|
+
destructive: { root: "bg-destructive", label: "text-destructive-foreground" },
|
|
160
|
+
"destructive-soft": { root: "bg-destructive-soft", label: "text-destructive-soft-foreground" },
|
|
161
|
+
},
|
|
162
|
+
// Six steps in two families, and a square is a step rather than a flag on
|
|
163
|
+
// one. `icon-md` is `md` with its horizontal padding traded for a width
|
|
164
|
+
// off the same token, so the two can never fight each other in the merge
|
|
165
|
+
// the way a boolean crossed with a size had to.
|
|
166
|
+
//
|
|
167
|
+
// Written out rather than built. Tailwind scans source text, so a class
|
|
168
|
+
// assembled at runtime — `h-button-${step}` — is never compiled and
|
|
169
|
+
// silently draws nothing; see `styles/tokens.ts`. The tuples above are
|
|
170
|
+
// keys, not classes, which is why those may be composed and these may not.
|
|
171
|
+
//
|
|
172
|
+
// A square carries no `gap`: it holds one child. That is also what keeps
|
|
173
|
+
// each row on one line, which is the whole readability of the table.
|
|
174
|
+
//
|
|
175
|
+
// The square steps are reachable only through `size`, never through
|
|
176
|
+
// loading. A definite width defeats the parent's `alignItems: stretch` —
|
|
177
|
+
// a stretch-aligned child with a definite cross size resolves to
|
|
178
|
+
// cross-*start* — so a full-width button that squared itself the moment
|
|
179
|
+
// work began would snap to a small box flush against the left edge. That
|
|
180
|
+
// is why `spinnerPlacement="only"` swaps the content and leaves the
|
|
181
|
+
// footprint exactly as the caller sized it.
|
|
182
|
+
size: {
|
|
183
|
+
sm: { root: "h-button-sm gap-1.5 px-3", label: "text-button-sm", icon: "size-icon-sm" },
|
|
184
|
+
md: { root: "h-button-md px-4", label: "text-button-md", icon: "size-icon-md" },
|
|
185
|
+
lg: { root: "h-button-lg px-5", label: "text-button-lg", icon: "size-icon-lg" },
|
|
186
|
+
"icon-sm": { root: "h-button-sm w-button-sm", label: "text-button-sm", icon: "size-icon-sm" },
|
|
187
|
+
"icon-md": { root: "h-button-md w-button-md", label: "text-button-md", icon: "size-icon-md" },
|
|
188
|
+
"icon-lg": { root: "h-button-lg w-button-lg", label: "text-button-lg", icon: "size-icon-lg" },
|
|
189
|
+
},
|
|
190
|
+
/** Which way a `Button.Group` runs. Only the `group` slot reads it on its own. */
|
|
191
|
+
orientation: {
|
|
192
|
+
horizontal: { group: "flex-row self-start" },
|
|
193
|
+
vertical: { group: "flex-col" },
|
|
194
|
+
},
|
|
195
|
+
// `middle` is the one position whose corner needs neither the size nor the
|
|
196
|
+
// orientation, so it lands here rather than in six compound cells that
|
|
197
|
+
// would all say the same word.
|
|
198
|
+
groupPosition: { none: {}, first: {}, middle: { root: "rounded-none" }, last: {}, only: {} },
|
|
199
|
+
// The empty `false` branches are load-bearing typing, not placeholders.
|
|
200
|
+
// `tv` derives the prop type from the declared keys, so a map with only
|
|
201
|
+
// `true` types the prop as `true` rather than `boolean` and rejects
|
|
202
|
+
// `buttonVariants({ isLoading })`. The compound variants below would match
|
|
203
|
+
// without them — `tv` compares against `defaultVariants` plus props and
|
|
204
|
+
// never reads this map.
|
|
205
|
+
isSeamed: { true: {}, false: {} },
|
|
206
|
+
isDisabled: { true: { root: "opacity-50" }, false: {} },
|
|
207
|
+
isLoading: { true: {}, false: {} },
|
|
208
|
+
isDimmedWhileLoading: { true: {}, false: {} },
|
|
209
|
+
},
|
|
210
|
+
compoundVariants: [
|
|
211
|
+
// Loading is not a disabled state. The button keeps full contrast — the
|
|
212
|
+
// spinner already says the press landed — unless the caller opts in.
|
|
213
|
+
{ isLoading: true, isDimmedWhileLoading: true, class: { root: "opacity-50" } },
|
|
214
|
+
// A lone button and a lone member draw the same corner, so the two positions
|
|
215
|
+
// share a cell. Each step pairs its label size with its square one: a corner
|
|
216
|
+
// is half the button's height, and an `icon-*` step is the same height as the
|
|
217
|
+
// label step it is named for.
|
|
218
|
+
{ groupPosition: ["none", "only"], size: ["sm", "icon-sm"], class: { root: "rounded-button-sm" } },
|
|
219
|
+
{ groupPosition: ["none", "only"], size: ["md", "icon-md"], class: { root: "rounded-button-md" } },
|
|
220
|
+
{ groupPosition: ["none", "only"], size: ["lg", "icon-lg"], class: { root: "rounded-button-lg" } },
|
|
221
|
+
// Each cell names all four corners, so none is left to a default and a
|
|
222
|
+
// uniform radius could not leak into one.
|
|
223
|
+
{
|
|
224
|
+
groupPosition: "first",
|
|
225
|
+
orientation: "horizontal",
|
|
226
|
+
size: ["sm", "icon-sm"],
|
|
227
|
+
class: { root: "rounded-s-button-sm rounded-e-none" },
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
groupPosition: "first",
|
|
231
|
+
orientation: "horizontal",
|
|
232
|
+
size: ["md", "icon-md"],
|
|
233
|
+
class: { root: "rounded-s-button-md rounded-e-none" },
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
groupPosition: "first",
|
|
237
|
+
orientation: "horizontal",
|
|
238
|
+
size: ["lg", "icon-lg"],
|
|
239
|
+
class: { root: "rounded-s-button-lg rounded-e-none" },
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
groupPosition: "last",
|
|
243
|
+
orientation: "horizontal",
|
|
244
|
+
size: ["sm", "icon-sm"],
|
|
245
|
+
class: { root: "rounded-e-button-sm rounded-s-none" },
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
groupPosition: "last",
|
|
249
|
+
orientation: "horizontal",
|
|
250
|
+
size: ["md", "icon-md"],
|
|
251
|
+
class: { root: "rounded-e-button-md rounded-s-none" },
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
groupPosition: "last",
|
|
255
|
+
orientation: "horizontal",
|
|
256
|
+
size: ["lg", "icon-lg"],
|
|
257
|
+
class: { root: "rounded-e-button-lg rounded-s-none" },
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
groupPosition: "first",
|
|
261
|
+
orientation: "vertical",
|
|
262
|
+
size: ["sm", "icon-sm"],
|
|
263
|
+
class: { root: "rounded-t-button-sm rounded-b-none" },
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
groupPosition: "first",
|
|
267
|
+
orientation: "vertical",
|
|
268
|
+
size: ["md", "icon-md"],
|
|
269
|
+
class: { root: "rounded-t-button-md rounded-b-none" },
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
groupPosition: "first",
|
|
273
|
+
orientation: "vertical",
|
|
274
|
+
size: ["lg", "icon-lg"],
|
|
275
|
+
class: { root: "rounded-t-button-lg rounded-b-none" },
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
groupPosition: "last",
|
|
279
|
+
orientation: "vertical",
|
|
280
|
+
size: ["sm", "icon-sm"],
|
|
281
|
+
class: { root: "rounded-b-button-sm rounded-t-none" },
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
groupPosition: "last",
|
|
285
|
+
orientation: "vertical",
|
|
286
|
+
size: ["md", "icon-md"],
|
|
287
|
+
class: { root: "rounded-b-button-md rounded-t-none" },
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
groupPosition: "last",
|
|
291
|
+
orientation: "vertical",
|
|
292
|
+
size: ["lg", "icon-lg"],
|
|
293
|
+
class: { root: "rounded-b-button-lg rounded-t-none" },
|
|
294
|
+
},
|
|
295
|
+
// Written out per orientation rather than built from the axis, because
|
|
296
|
+
// Tailwind's scanner is static: a class assembled at runtime is never
|
|
297
|
+
// compiled and Uniwind's store has nothing to look up. See AGENTS.md.
|
|
298
|
+
{ isSeamed: true, orientation: "horizontal", class: { root: "-ms-px" } },
|
|
299
|
+
{ isSeamed: true, orientation: "vertical", class: { root: "-mt-px" } },
|
|
300
|
+
],
|
|
301
|
+
defaultVariants: {
|
|
302
|
+
variant: "primary",
|
|
303
|
+
size: "md",
|
|
304
|
+
orientation: "horizontal",
|
|
305
|
+
groupPosition: "none",
|
|
306
|
+
isSeamed: false,
|
|
307
|
+
isDisabled: false,
|
|
308
|
+
isLoading: false,
|
|
309
|
+
isDimmedWhileLoading: false,
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
/** What the spinner does to a button's children, once loading and placement are folded together. */
|
|
314
|
+
export type ButtonLayout = {
|
|
315
|
+
/** The spinner has replaced the children entirely. */
|
|
316
|
+
isSpinnerOnly: boolean;
|
|
317
|
+
/** Side the spinner is composed onto, or null when no spinner is shown. */
|
|
318
|
+
spinnerSide: "start" | "end" | null;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Folds `isLoading` and `spinnerPlacement` into the facts the content needs.
|
|
323
|
+
*
|
|
324
|
+
* It says nothing about the footprint, and that is the point: `only` swaps the
|
|
325
|
+
* content out and leaves the button exactly the size the caller asked for. A
|
|
326
|
+
* square is a `size`, so `only` cannot reach it — see the note on the `size`
|
|
327
|
+
* axis for the `alignItems: stretch` failure that rules out earning one here.
|
|
328
|
+
*
|
|
329
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
330
|
+
*/
|
|
331
|
+
export function resolveButtonLayout({
|
|
332
|
+
isLoading = false,
|
|
333
|
+
spinnerPlacement = "start",
|
|
334
|
+
}: {
|
|
335
|
+
isLoading?: boolean;
|
|
336
|
+
spinnerPlacement?: ButtonSpinnerPlacement;
|
|
337
|
+
}): ButtonLayout {
|
|
338
|
+
if (!isLoading) return { isSpinnerOnly: false, spinnerSide: null };
|
|
339
|
+
if (spinnerPlacement === "only") return { isSpinnerOnly: true, spinnerSide: null };
|
|
340
|
+
return { isSpinnerOnly: false, spinnerSide: spinnerPlacement };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Index of the child the spinner should replace, or null to insert one.
|
|
345
|
+
*
|
|
346
|
+
* A loading button swaps its icon for the spinner rather than showing both.
|
|
347
|
+
* Adding one would push the label sideways the moment work started and pull it
|
|
348
|
+
* back when it finished — and with the spinner and the icon drawn at the same
|
|
349
|
+
* `size-icon-*` token, replacing costs no layout at all.
|
|
350
|
+
*
|
|
351
|
+
* Only the child at the named edge is a candidate — the first at `start`, the
|
|
352
|
+
* last at `end`. An icon on the other side is left alone and the spinner is
|
|
353
|
+
* inserted at the edge instead: `spinnerPlacement` says where the caller wants
|
|
354
|
+
* the spinner, so taking the one icon a button holds regardless of which side
|
|
355
|
+
* it sits on would draw the spinner opposite the side that was asked for.
|
|
356
|
+
*
|
|
357
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
358
|
+
*/
|
|
359
|
+
export function resolveSpinnerSwapIndex(isIcon: readonly boolean[], side: "start" | "end"): number | null {
|
|
360
|
+
const index = side === "start" ? 0 : isIcon.length - 1;
|
|
361
|
+
return isIcon[index] === true ? index : null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The labelled step a size is built on — `icon-md` is `md`'s height.
|
|
366
|
+
*
|
|
367
|
+
* A square size is its labelled step with the horizontal padding traded for a
|
|
368
|
+
* width off the same token, so the two share a height and therefore a corner.
|
|
369
|
+
* Anything that needs the step rather than the shape reads it through here
|
|
370
|
+
* rather than slicing the prefix at the call site: a control joining a
|
|
371
|
+
* `Button.Group` has to line up with the run without having a square form of its
|
|
372
|
+
* own, which is exactly what an `Input` does.
|
|
373
|
+
*
|
|
374
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
375
|
+
*/
|
|
376
|
+
export function resolveButtonSizeStep(size: ButtonSize): ButtonLabelSize {
|
|
377
|
+
return (size.startsWith("icon-") ? size.slice("icon-".length) : size) as ButtonLabelSize;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* The size a member draws at, once its group has had its say.
|
|
382
|
+
*
|
|
383
|
+
* A group owns the *step* outright — members of different heights do not join —
|
|
384
|
+
* but not the *shape*. Those are one axis since a square footprint became a size
|
|
385
|
+
* rather than a flag, so resolving the whole prop to the group's value would
|
|
386
|
+
* make a square member impossible inside a run: the icon button at the end of a
|
|
387
|
+
* split button would silently grow a label's padding and lose its width.
|
|
388
|
+
*
|
|
389
|
+
* So the step comes from the group and the shape from the member, falling back
|
|
390
|
+
* to the group's own shape when the member names no size. `icon-md` inside a
|
|
391
|
+
* `sm` group is `icon-sm` — square, and the same height as everything beside it.
|
|
392
|
+
*
|
|
393
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
394
|
+
*/
|
|
395
|
+
export function resolveGroupedButtonSize(own: ButtonSize | undefined, group: ButtonSize | undefined): ButtonSize {
|
|
396
|
+
if (group === undefined) return own ?? "md";
|
|
397
|
+
const step = resolveButtonSizeStep(group);
|
|
398
|
+
const isSquare = own === undefined ? group.startsWith("icon-") : own.startsWith("icon-");
|
|
399
|
+
return (isSquare ? `icon-${step}` : step) as ButtonSize;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Where each child sits among the group's *members*.
|
|
404
|
+
*
|
|
405
|
+
* `null` is "not a member". A `Button.Group.Separator` between two buttons is a
|
|
406
|
+
* rule, not a segment, so the buttons either side of one are still the first and
|
|
407
|
+
* the last and keep their rounded outer corners. A single member is `only` and
|
|
408
|
+
* draws the corner it would have alone, which is what makes a group rendering
|
|
409
|
+
* one button conditionally look no different from the button on its own.
|
|
410
|
+
*
|
|
411
|
+
* Takes an array of "is this child a member" rather than the children
|
|
412
|
+
* themselves, so it stays free of React and reachable from `bun test` — the
|
|
413
|
+
* trade {@link resolveSpinnerSwapIndex} already makes. React Native has no
|
|
414
|
+
* sibling selector, so this is the only place a member's corner can be decided.
|
|
415
|
+
*
|
|
416
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
417
|
+
*/
|
|
418
|
+
export function resolveGroupPositions(isMember: readonly boolean[]): (ButtonGroupPosition | null)[] {
|
|
419
|
+
const count = isMember.reduce((total, member) => (member ? total + 1 : total), 0);
|
|
420
|
+
let seen = 0;
|
|
421
|
+
|
|
422
|
+
return isMember.map((member) => {
|
|
423
|
+
if (!member) return null;
|
|
424
|
+
const index = seen++;
|
|
425
|
+
if (count === 1) return "only";
|
|
426
|
+
if (index === 0) return "first";
|
|
427
|
+
if (index === count - 1) return "last";
|
|
428
|
+
return "middle";
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Which members overlap the one before them, so a shared border draws once.
|
|
434
|
+
*
|
|
435
|
+
* Only where two members are *adjacent*. A member following a
|
|
436
|
+
* `Button.Group.Separator` must not overlap: the rule is one point wide, and a
|
|
437
|
+
* one-point overlap would slide straight over it and delete the divider the
|
|
438
|
+
* caller asked for.
|
|
439
|
+
*
|
|
440
|
+
* A separate walk from {@link resolveGroupPositions} because it asks a different
|
|
441
|
+
* question — "what is immediately before me" rather than "how many of us are
|
|
442
|
+
* there" — and folding both into one return type would hand two callers a record
|
|
443
|
+
* where each wants one field.
|
|
444
|
+
*
|
|
445
|
+
* `isMember[-1]` is `undefined`, so the first child falls out as `false` without
|
|
446
|
+
* a case of its own.
|
|
447
|
+
*
|
|
448
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
449
|
+
*/
|
|
450
|
+
export function resolveGroupSeams(isMember: readonly boolean[]): boolean[] {
|
|
451
|
+
return isMember.map((member, index) => member && isMember[index - 1] === true);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Settles how a press moves the button, from the three places it can come from.
|
|
456
|
+
*
|
|
457
|
+
* **Nearest wins** — the button's own prop, then its group's, then the built-in
|
|
458
|
+
* default: the ladder `Radio` and `Input.Group` already run. `??` rather than
|
|
459
|
+
* `||`, so an explicit `none` is honoured instead of read as an absence.
|
|
460
|
+
*
|
|
461
|
+
* A joined member falls back to `fade` rather than `scale`, and that is not a
|
|
462
|
+
* taste call. A scaling member pulls its own edges in by a point and a half
|
|
463
|
+
* while its neighbours hold still, so the seam the group exists to close tears
|
|
464
|
+
* open for the length of the press. `fade` moves no geometry.
|
|
465
|
+
*
|
|
466
|
+
* A caller still gets `scale` by asking for it, on one button or on a whole
|
|
467
|
+
* group. `pressedScale` beats both, because `resolvePressedState` lets an
|
|
468
|
+
* explicit value win on the axis it names — see `pressable.variants.ts`.
|
|
469
|
+
*
|
|
470
|
+
* Pure, so the whole matrix is reachable from `bun test`. See AGENTS.md.
|
|
471
|
+
*/
|
|
472
|
+
export function resolveButtonFeedback(
|
|
473
|
+
own: PressableFeedback | undefined,
|
|
474
|
+
group: PressableFeedback | undefined,
|
|
475
|
+
isGrouped: boolean
|
|
476
|
+
): PressableFeedback {
|
|
477
|
+
return own ?? group ?? (isGrouped ? BUTTON_GROUP_FEEDBACK : BUTTON_FEEDBACK);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export type ButtonVariantProps = VariantProps<typeof buttonVariants>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export { Button, type ButtonProps } from "./button";
|
|
2
|
+
export {
|
|
3
|
+
type ButtonContextValue,
|
|
4
|
+
type ButtonGroupContextValue,
|
|
5
|
+
type ButtonGroupItemContextValue,
|
|
6
|
+
ButtonGroupItemProvider,
|
|
7
|
+
ButtonGroupProvider,
|
|
8
|
+
ButtonProvider,
|
|
9
|
+
useButton,
|
|
10
|
+
useButtonContext,
|
|
11
|
+
useButtonGroup,
|
|
12
|
+
useButtonGroupContext,
|
|
13
|
+
useButtonGroupItem,
|
|
14
|
+
useButtonGroupItemContext,
|
|
15
|
+
} from "./button.context";
|
|
16
|
+
export type { ButtonSlotProps } from "./button.types";
|
|
17
|
+
export {
|
|
18
|
+
BUTTON_FEEDBACK,
|
|
19
|
+
BUTTON_FOREGROUND_TOKEN,
|
|
20
|
+
BUTTON_GROUP_FEEDBACK,
|
|
21
|
+
BUTTON_GROUP_ORIENTATIONS,
|
|
22
|
+
BUTTON_GROUP_POSITIONS,
|
|
23
|
+
BUTTON_GROUP_SEPARATOR_ORIENTATION,
|
|
24
|
+
BUTTON_ICON_SIZES,
|
|
25
|
+
BUTTON_LABEL_SIZES,
|
|
26
|
+
BUTTON_SIZES,
|
|
27
|
+
BUTTON_SPINNER_PLACEMENTS,
|
|
28
|
+
BUTTON_VARIANTS,
|
|
29
|
+
type ButtonGroupOrientation,
|
|
30
|
+
type ButtonGroupPosition,
|
|
31
|
+
type ButtonGroupSlotPosition,
|
|
32
|
+
type ButtonIconSize,
|
|
33
|
+
type ButtonLabelSize,
|
|
34
|
+
type ButtonLayout,
|
|
35
|
+
type ButtonSize,
|
|
36
|
+
type ButtonSpinnerPlacement,
|
|
37
|
+
type ButtonVariant,
|
|
38
|
+
type ButtonVariantProps,
|
|
39
|
+
buttonVariants,
|
|
40
|
+
resolveButtonFeedback,
|
|
41
|
+
resolveButtonLayout,
|
|
42
|
+
resolveButtonSizeStep,
|
|
43
|
+
resolveGroupedButtonSize,
|
|
44
|
+
resolveGroupPositions,
|
|
45
|
+
resolveGroupSeams,
|
|
46
|
+
} from "./button.variants";
|
|
47
|
+
export type { ButtonGroupProps } from "./button-group";
|
|
48
|
+
export type { ButtonGroupSeparatorProps } from "./button-group-separator";
|
|
49
|
+
export type { ButtonGroupTextProps } from "./button-group-text";
|
|
50
|
+
export type { ButtonLabelProps } from "./button-label";
|