@stll/ui 0.59.1 → 0.61.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/README.md +120 -4
- package/dist/components/accordion.d.ts +17 -3
- package/dist/components/accordion.js +17 -3
- package/dist/components/button.d.ts +56 -5
- package/dist/components/button.js +97 -15
- package/dist/components/button.native-classes.d.ts +17 -0
- package/dist/components/button.native-classes.js +176 -0
- package/dist/components/button.native.d.ts +5 -2
- package/dist/components/button.native.js +17 -134
- package/dist/components/button.shared.d.ts +24 -2
- package/dist/components/button.shared.js +15 -1
- package/dist/components/collapsible.d.ts +26 -0
- package/dist/components/collapsible.js +40 -0
- package/dist/components/combobox.d.ts +3 -1
- package/dist/components/combobox.js +3 -2
- package/dist/components/date-picker-popover.d.ts +2 -0
- package/dist/components/date-picker-popover.js +5 -3
- package/dist/components/details-grid.d.ts +21 -2
- package/dist/components/details-grid.js +75 -20
- package/dist/components/field.d.ts +6 -0
- package/dist/components/field.js +6 -0
- package/dist/components/form.d.ts +43 -3
- package/dist/components/form.js +53 -3
- package/dist/components/input-control.d.ts +9 -0
- package/dist/components/input-control.js +10 -1
- package/dist/components/input.d.ts +7 -1
- package/dist/components/input.js +4 -4
- package/dist/components/label.d.ts +24 -2
- package/dist/components/label.js +21 -2
- package/dist/components/number-input.d.ts +9 -2
- package/dist/components/number-input.js +18 -6
- package/dist/components/search-field.js +1 -0
- package/dist/components/segmented-icon-toggle.d.ts +2 -1
- package/dist/components/segmented-icon-toggle.js +18 -19
- package/dist/components/switch.d.ts +21 -2
- package/dist/components/switch.js +44 -5
- package/dist/components/table.d.ts +16 -2
- package/dist/components/table.js +5 -4
- package/dist/components/textarea.d.ts +14 -1
- package/dist/components/textarea.js +11 -5
- package/dist/components/toggle-group.d.ts +32 -0
- package/dist/components/toggle-group.js +45 -0
- package/dist/components/toggle.d.ts +26 -0
- package/dist/components/toggle.js +26 -0
- package/dist/index.d.ts +14 -7
- package/dist/index.js +9 -3
- package/dist/kanban/column-bands.js +1 -1
- package/dist/kanban/index.d.ts +5 -2
- package/dist/kanban/index.js +4 -1
- package/dist/kanban/sortable-edge.d.ts +39 -0
- package/dist/kanban/sortable-edge.js +27 -0
- package/dist/kanban/sortable-interactions.d.ts +145 -0
- package/dist/kanban/sortable-interactions.js +503 -0
- package/dist/kanban/sortable-interactions.logic.d.ts +79 -0
- package/dist/kanban/sortable-interactions.logic.js +210 -0
- package/dist/kanban/subgroup-board.d.ts +27 -2
- package/dist/kanban/subgroup-board.js +26 -1
- package/dist/kanban/virtual-cell.d.ts +18 -1
- package/dist/kanban/virtual-cell.js +70 -17
- package/package.json +17 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { BUTTON_PLAIN_VARIANT, BUTTON_VARIANT_TOKENS } from "./button.shared.js";
|
|
2
|
+
//#region src/components/button.native-classes.ts
|
|
3
|
+
/** A control's box: centred content inside the variant's border. */
|
|
4
|
+
const CONTROL = "items-center justify-center border";
|
|
5
|
+
/**
|
|
6
|
+
* Every shared size in a phone's metrics. A text size grows to the platform's
|
|
7
|
+
* 44-point touch target where the web relies on a coarse-pointer hit area; a
|
|
8
|
+
* smaller control keeps its look and reaches the target through `slop`.
|
|
9
|
+
*/
|
|
10
|
+
const SIZES = {
|
|
11
|
+
chip: {
|
|
12
|
+
box: `${CONTROL} min-h-7 gap-1.5 rounded-full px-2.5`,
|
|
13
|
+
label: "text-xs",
|
|
14
|
+
slop: 8
|
|
15
|
+
},
|
|
16
|
+
default: {
|
|
17
|
+
box: `${CONTROL} min-h-11 gap-2 rounded-lg px-4`,
|
|
18
|
+
label: "",
|
|
19
|
+
slop: 0
|
|
20
|
+
},
|
|
21
|
+
icon: {
|
|
22
|
+
box: `${CONTROL} size-11 rounded-lg`,
|
|
23
|
+
label: "",
|
|
24
|
+
slop: 0
|
|
25
|
+
},
|
|
26
|
+
"icon-lg": {
|
|
27
|
+
box: `${CONTROL} size-12 rounded-lg`,
|
|
28
|
+
label: "",
|
|
29
|
+
slop: 0
|
|
30
|
+
},
|
|
31
|
+
"icon-sm": {
|
|
32
|
+
box: `${CONTROL} size-8 rounded-lg`,
|
|
33
|
+
label: "",
|
|
34
|
+
slop: 6
|
|
35
|
+
},
|
|
36
|
+
"icon-xl": {
|
|
37
|
+
box: `${CONTROL} size-11 rounded-full`,
|
|
38
|
+
label: "",
|
|
39
|
+
slop: 0
|
|
40
|
+
},
|
|
41
|
+
"icon-2xs": {
|
|
42
|
+
box: `${CONTROL} size-5 rounded-md`,
|
|
43
|
+
label: "",
|
|
44
|
+
slop: 12
|
|
45
|
+
},
|
|
46
|
+
"icon-xs": {
|
|
47
|
+
box: `${CONTROL} size-7 rounded-md`,
|
|
48
|
+
label: "",
|
|
49
|
+
slop: 8
|
|
50
|
+
},
|
|
51
|
+
inline: {
|
|
52
|
+
box: "items-center gap-1",
|
|
53
|
+
label: "",
|
|
54
|
+
slop: 8
|
|
55
|
+
},
|
|
56
|
+
lg: {
|
|
57
|
+
box: `${CONTROL} min-h-12 gap-2 rounded-lg px-5`,
|
|
58
|
+
label: "",
|
|
59
|
+
slop: 0
|
|
60
|
+
},
|
|
61
|
+
row: {
|
|
62
|
+
box: "min-h-11 w-full items-start gap-3 py-3",
|
|
63
|
+
label: "",
|
|
64
|
+
slop: 0
|
|
65
|
+
},
|
|
66
|
+
sm: {
|
|
67
|
+
box: `${CONTROL} min-h-9 gap-1.5 rounded-lg px-3`,
|
|
68
|
+
label: "text-sm",
|
|
69
|
+
slop: 0
|
|
70
|
+
},
|
|
71
|
+
xl: {
|
|
72
|
+
box: `${CONTROL} min-h-14 gap-2 rounded-lg px-6`,
|
|
73
|
+
label: "text-lg",
|
|
74
|
+
slop: 0
|
|
75
|
+
},
|
|
76
|
+
xs: {
|
|
77
|
+
box: `${CONTROL} min-h-7 gap-1 rounded-md px-2`,
|
|
78
|
+
label: "text-sm",
|
|
79
|
+
slop: 8
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A phone has no hover: a pressed button answers with the fill the web shows
|
|
84
|
+
* on hover, or dims when it already has one. `selected` is the pressed state
|
|
85
|
+
* held (a row open beside the list, a chosen option).
|
|
86
|
+
*/
|
|
87
|
+
const PRESS = {
|
|
88
|
+
default: {
|
|
89
|
+
pressed: "active:opacity-80",
|
|
90
|
+
selected: ""
|
|
91
|
+
},
|
|
92
|
+
destructive: {
|
|
93
|
+
pressed: "active:bg-destructive/18",
|
|
94
|
+
selected: ""
|
|
95
|
+
},
|
|
96
|
+
"destructive-outline": {
|
|
97
|
+
pressed: "active:bg-destructive/4",
|
|
98
|
+
selected: ""
|
|
99
|
+
},
|
|
100
|
+
"destructive-ghost": {
|
|
101
|
+
pressed: "active:bg-destructive/10",
|
|
102
|
+
selected: "bg-destructive/10"
|
|
103
|
+
},
|
|
104
|
+
ghost: {
|
|
105
|
+
pressed: "active:bg-accent",
|
|
106
|
+
selected: "bg-accent"
|
|
107
|
+
},
|
|
108
|
+
link: {
|
|
109
|
+
pressed: "active:opacity-60",
|
|
110
|
+
selected: ""
|
|
111
|
+
},
|
|
112
|
+
muted: {
|
|
113
|
+
pressed: "active:bg-accent",
|
|
114
|
+
selected: "bg-accent"
|
|
115
|
+
},
|
|
116
|
+
"muted-destructive": {
|
|
117
|
+
pressed: "active:bg-destructive/10",
|
|
118
|
+
selected: "bg-destructive/10"
|
|
119
|
+
},
|
|
120
|
+
outline: {
|
|
121
|
+
pressed: "active:bg-accent",
|
|
122
|
+
selected: "bg-accent"
|
|
123
|
+
},
|
|
124
|
+
secondary: {
|
|
125
|
+
pressed: "active:bg-secondary/80",
|
|
126
|
+
selected: ""
|
|
127
|
+
},
|
|
128
|
+
plain: {
|
|
129
|
+
pressed: "active:opacity-80",
|
|
130
|
+
selected: "border-primary"
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* `plain` has no fill, no label colour and no size; like the web's, it does
|
|
135
|
+
* not dim when disabled, because its content says it is unavailable. It keeps
|
|
136
|
+
* a transparent border that only `selected` colours, the phone's stand-in for
|
|
137
|
+
* the web's inset ring.
|
|
138
|
+
*/
|
|
139
|
+
const PLAIN = {
|
|
140
|
+
shape: {
|
|
141
|
+
box: "rounded-lg border-2 border-transparent",
|
|
142
|
+
label: "",
|
|
143
|
+
slop: 0
|
|
144
|
+
},
|
|
145
|
+
tokens: {
|
|
146
|
+
surface: "",
|
|
147
|
+
label: ""
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
/** The Pressable's and the label's classes, and the touch slop, for a button. */
|
|
151
|
+
const nativeButtonClassNames = ({ className, inactive, selected, size, variant }) => {
|
|
152
|
+
const plain = variant === BUTTON_PLAIN_VARIANT;
|
|
153
|
+
const shape = plain ? PLAIN.shape : SIZES[size ?? "default"];
|
|
154
|
+
const tokens = plain ? PLAIN.tokens : BUTTON_VARIANT_TOKENS[variant ?? "default"];
|
|
155
|
+
const press = PRESS[variant ?? "default"];
|
|
156
|
+
const dimmed = plain ? "" : "opacity-64";
|
|
157
|
+
const join = (names) => names.filter((name) => name !== "").join(" ");
|
|
158
|
+
return {
|
|
159
|
+
label: join([
|
|
160
|
+
tokens.label,
|
|
161
|
+
variant === "link" ? "text-foreground underline" : "",
|
|
162
|
+
shape.label
|
|
163
|
+
]),
|
|
164
|
+
pressable: join([
|
|
165
|
+
plain ? "" : "flex-row",
|
|
166
|
+
tokens.surface,
|
|
167
|
+
shape.box,
|
|
168
|
+
inactive ? dimmed : press.pressed,
|
|
169
|
+
selected ? press.selected : "",
|
|
170
|
+
className ?? ""
|
|
171
|
+
]),
|
|
172
|
+
slop: shape.slop
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
//#endregion
|
|
176
|
+
export { nativeButtonClassNames };
|
|
@@ -8,8 +8,11 @@ type ButtonProps = Omit<PressableProps, "children" | "disabled"> & ButtonContrac
|
|
|
8
8
|
* The web's `Button` (./button.tsx) on a phone: the same variants and sizes,
|
|
9
9
|
* read from ./button.shared.ts, on a Pressable. A string child is set as the
|
|
10
10
|
* button's label in its variant's colour; any other child is laid out as
|
|
11
|
-
* given, which is how a row holds a glyph beside two lines of text.
|
|
11
|
+
* given, which is how a row holds a glyph beside two lines of text. A
|
|
12
|
+
* `plain` button paints no fill or label colour and ignores `size`: its
|
|
13
|
+
* children are its look, stacked as in a View, inside a border that shows
|
|
14
|
+
* only when it is selected. Its classes come from ./button.native-classes.ts.
|
|
12
15
|
*/
|
|
13
|
-
export declare const Button: ({ accessibilityState, children, className, disabled, hitSlop, icon, loading, size, variant, ...props }: ButtonProps) => import("react").JSX.Element;
|
|
16
|
+
export declare const Button: ({ accessibilityState, children, className, disabled, hitSlop, icon, loading, selected: selectedProp, size, variant: variantProp, ...props }: ButtonProps) => import("react").JSX.Element;
|
|
14
17
|
//#endregion
|
|
15
18
|
export {};
|
|
@@ -1,138 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { nativeButtonClassNames } from "./button.native-classes.js";
|
|
2
2
|
import { Loader } from "./loader.native.js";
|
|
3
3
|
import { Text as Text$1 } from "./text.native.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Pressable } from "react-native";
|
|
6
6
|
//#region src/components/button.native.tsx
|
|
7
|
-
/** A control's box: centred content inside the variant's border. */
|
|
8
|
-
const CONTROL = "items-center justify-center border";
|
|
9
|
-
/**
|
|
10
|
-
* Every shared size in a phone's metrics. A text size grows to the platform's
|
|
11
|
-
* 44-point touch target where the web relies on a coarse-pointer hit area; a
|
|
12
|
-
* smaller control keeps its look and reaches the target through `slop`.
|
|
13
|
-
*/
|
|
14
|
-
const SIZES = {
|
|
15
|
-
chip: {
|
|
16
|
-
box: `${CONTROL} min-h-7 gap-1.5 rounded-full px-2.5`,
|
|
17
|
-
label: "text-xs",
|
|
18
|
-
slop: 8
|
|
19
|
-
},
|
|
20
|
-
default: {
|
|
21
|
-
box: `${CONTROL} min-h-11 gap-2 rounded-lg px-4`,
|
|
22
|
-
label: "",
|
|
23
|
-
slop: 0
|
|
24
|
-
},
|
|
25
|
-
icon: {
|
|
26
|
-
box: `${CONTROL} size-11 rounded-lg`,
|
|
27
|
-
label: "",
|
|
28
|
-
slop: 0
|
|
29
|
-
},
|
|
30
|
-
"icon-lg": {
|
|
31
|
-
box: `${CONTROL} size-12 rounded-lg`,
|
|
32
|
-
label: "",
|
|
33
|
-
slop: 0
|
|
34
|
-
},
|
|
35
|
-
"icon-sm": {
|
|
36
|
-
box: `${CONTROL} size-8 rounded-lg`,
|
|
37
|
-
label: "",
|
|
38
|
-
slop: 6
|
|
39
|
-
},
|
|
40
|
-
"icon-xl": {
|
|
41
|
-
box: `${CONTROL} size-11 rounded-full`,
|
|
42
|
-
label: "",
|
|
43
|
-
slop: 0
|
|
44
|
-
},
|
|
45
|
-
"icon-xs": {
|
|
46
|
-
box: `${CONTROL} size-7 rounded-md`,
|
|
47
|
-
label: "",
|
|
48
|
-
slop: 8
|
|
49
|
-
},
|
|
50
|
-
lg: {
|
|
51
|
-
box: `${CONTROL} min-h-12 gap-2 rounded-lg px-5`,
|
|
52
|
-
label: "",
|
|
53
|
-
slop: 0
|
|
54
|
-
},
|
|
55
|
-
row: {
|
|
56
|
-
box: "min-h-11 w-full items-start gap-3 py-3",
|
|
57
|
-
label: "",
|
|
58
|
-
slop: 0
|
|
59
|
-
},
|
|
60
|
-
sm: {
|
|
61
|
-
box: `${CONTROL} min-h-9 gap-1.5 rounded-lg px-3`,
|
|
62
|
-
label: "text-sm",
|
|
63
|
-
slop: 0
|
|
64
|
-
},
|
|
65
|
-
xl: {
|
|
66
|
-
box: `${CONTROL} min-h-14 gap-2 rounded-lg px-6`,
|
|
67
|
-
label: "text-lg",
|
|
68
|
-
slop: 0
|
|
69
|
-
},
|
|
70
|
-
xs: {
|
|
71
|
-
box: `${CONTROL} min-h-7 gap-1 rounded-md px-2`,
|
|
72
|
-
label: "text-sm",
|
|
73
|
-
slop: 8
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* A phone has no hover: a pressed button answers with the fill the web shows
|
|
78
|
-
* on hover, or dims when it already has one. `selected` is the pressed state
|
|
79
|
-
* held (a row open beside the list, a chosen option).
|
|
80
|
-
*/
|
|
81
|
-
const PRESS = {
|
|
82
|
-
default: {
|
|
83
|
-
pressed: "active:opacity-80",
|
|
84
|
-
selected: ""
|
|
85
|
-
},
|
|
86
|
-
destructive: {
|
|
87
|
-
pressed: "active:bg-destructive/18",
|
|
88
|
-
selected: ""
|
|
89
|
-
},
|
|
90
|
-
"destructive-outline": {
|
|
91
|
-
pressed: "active:bg-destructive/4",
|
|
92
|
-
selected: ""
|
|
93
|
-
},
|
|
94
|
-
"destructive-ghost": {
|
|
95
|
-
pressed: "active:bg-destructive/10",
|
|
96
|
-
selected: "bg-destructive/10"
|
|
97
|
-
},
|
|
98
|
-
ghost: {
|
|
99
|
-
pressed: "active:bg-accent",
|
|
100
|
-
selected: "bg-accent"
|
|
101
|
-
},
|
|
102
|
-
link: {
|
|
103
|
-
pressed: "active:opacity-60",
|
|
104
|
-
selected: ""
|
|
105
|
-
},
|
|
106
|
-
muted: {
|
|
107
|
-
pressed: "active:bg-accent",
|
|
108
|
-
selected: "bg-accent"
|
|
109
|
-
},
|
|
110
|
-
outline: {
|
|
111
|
-
pressed: "active:bg-accent",
|
|
112
|
-
selected: "bg-accent"
|
|
113
|
-
},
|
|
114
|
-
secondary: {
|
|
115
|
-
pressed: "active:bg-secondary/80",
|
|
116
|
-
selected: ""
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
7
|
/**
|
|
120
8
|
* The web's `Button` (./button.tsx) on a phone: the same variants and sizes,
|
|
121
9
|
* read from ./button.shared.ts, on a Pressable. A string child is set as the
|
|
122
10
|
* button's label in its variant's colour; any other child is laid out as
|
|
123
|
-
* given, which is how a row holds a glyph beside two lines of text.
|
|
11
|
+
* given, which is how a row holds a glyph beside two lines of text. A
|
|
12
|
+
* `plain` button paints no fill or label colour and ignores `size`: its
|
|
13
|
+
* children are its look, stacked as in a View, inside a border that shows
|
|
14
|
+
* only when it is selected. Its classes come from ./button.native-classes.ts.
|
|
124
15
|
*/
|
|
125
|
-
const Button = ({ accessibilityState, children, className, disabled = false, hitSlop, icon, loading = false, size, variant, ...props }) => {
|
|
126
|
-
const
|
|
127
|
-
const tokens = BUTTON_VARIANT_TOKENS[variant ?? "default"];
|
|
128
|
-
const press = PRESS[variant ?? "default"];
|
|
16
|
+
const Button = ({ accessibilityState, children, className, disabled = false, hitSlop, icon, loading = false, selected: selectedProp, size, variant: variantProp, ...props }) => {
|
|
17
|
+
const variant = variantProp ?? (size === "inline" ? "link" : void 0);
|
|
129
18
|
const inactive = disabled || loading;
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
19
|
+
const look = nativeButtonClassNames({
|
|
20
|
+
className,
|
|
21
|
+
inactive,
|
|
22
|
+
selected: selectedProp === true || accessibilityState?.selected === true,
|
|
23
|
+
size,
|
|
24
|
+
variant
|
|
25
|
+
});
|
|
136
26
|
return /* @__PURE__ */ jsxs(Pressable, {
|
|
137
27
|
accessibilityRole: "button",
|
|
138
28
|
accessibilityState: {
|
|
@@ -140,22 +30,15 @@ const Button = ({ accessibilityState, children, className, disabled = false, hit
|
|
|
140
30
|
busy: loading,
|
|
141
31
|
disabled: inactive
|
|
142
32
|
},
|
|
143
|
-
className:
|
|
144
|
-
"flex-row",
|
|
145
|
-
tokens.surface,
|
|
146
|
-
shape.box,
|
|
147
|
-
inactive ? "opacity-64" : press.pressed,
|
|
148
|
-
selected ? press.selected : "",
|
|
149
|
-
className ?? ""
|
|
150
|
-
].filter((name) => name !== "").join(" "),
|
|
33
|
+
className: look.pressable,
|
|
151
34
|
disabled: inactive,
|
|
152
|
-
hitSlop: hitSlop ?? (
|
|
35
|
+
hitSlop: hitSlop ?? (look.slop === 0 ? void 0 : look.slop),
|
|
153
36
|
...props,
|
|
154
37
|
children: [loading ? /* @__PURE__ */ jsx(Loader, {
|
|
155
38
|
size: "sm",
|
|
156
39
|
variant: "decorative"
|
|
157
40
|
}) : icon, typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsx(Text$1, {
|
|
158
|
-
className: label,
|
|
41
|
+
className: look.label,
|
|
159
42
|
variant: "button",
|
|
160
43
|
children
|
|
161
44
|
}) : children]
|
|
@@ -34,6 +34,10 @@ export declare const BUTTON_VARIANT_TOKENS: {
|
|
|
34
34
|
readonly surface: "border-transparent";
|
|
35
35
|
readonly label: "text-muted-foreground";
|
|
36
36
|
};
|
|
37
|
+
readonly "muted-destructive": {
|
|
38
|
+
readonly surface: "border-transparent";
|
|
39
|
+
readonly label: "text-muted-foreground";
|
|
40
|
+
};
|
|
37
41
|
readonly outline: {
|
|
38
42
|
readonly surface: "border-input bg-popover";
|
|
39
43
|
readonly label: "text-foreground";
|
|
@@ -43,9 +47,19 @@ export declare const BUTTON_VARIANT_TOKENS: {
|
|
|
43
47
|
readonly label: "text-secondary-foreground";
|
|
44
48
|
};
|
|
45
49
|
};
|
|
46
|
-
|
|
50
|
+
/** A variant painted from its shared tokens above. */
|
|
51
|
+
export type ButtonTokenVariant = keyof typeof BUTTON_VARIANT_TOKENS;
|
|
52
|
+
/**
|
|
53
|
+
* `plain`: a click target whose look is its content (a card, a thumbnail, a
|
|
54
|
+
* cell, a passage). It has no tokens: neither renderer paints a box, border,
|
|
55
|
+
* type or press motion for it, and each keeps everything else its button
|
|
56
|
+
* owns (the disabled disposition, and on the web the tooltip and the focus
|
|
57
|
+
* ring). A `size` does not apply to it.
|
|
58
|
+
*/
|
|
59
|
+
export declare const BUTTON_PLAIN_VARIANT = "plain";
|
|
60
|
+
export type ButtonVariant = ButtonTokenVariant | typeof BUTTON_PLAIN_VARIANT;
|
|
47
61
|
/** Every size a button comes in, on every platform. */
|
|
48
|
-
export declare const BUTTON_SIZES: readonly ["chip", "default", "icon", "icon-lg", "icon-sm", "icon-xl", "icon-xs", "lg", "row", "sm", "xl", "xs"];
|
|
62
|
+
export declare const BUTTON_SIZES: readonly ["chip", "default", "icon", "icon-lg", "icon-sm", "icon-xl", "icon-2xs", "icon-xs", "inline", "lg", "row", "sm", "xl", "xs"];
|
|
49
63
|
export type ButtonSize = (typeof BUTTON_SIZES)[number];
|
|
50
64
|
/**
|
|
51
65
|
* The props every button renderer accepts, with one meaning. A renderer adds
|
|
@@ -60,6 +74,14 @@ export type ButtonContract = {
|
|
|
60
74
|
disabled?: boolean;
|
|
61
75
|
/** A glyph before the label. */
|
|
62
76
|
icon?: ReactNode;
|
|
77
|
+
/**
|
|
78
|
+
* The current choice in a set (a picked option, the active filter tab, the
|
|
79
|
+
* open item of a list): shows the variant's selected treatment, which is
|
|
80
|
+
* distinct from its hover or press. The accessibility state stays the
|
|
81
|
+
* caller's, because which one is right depends on the widget the button is
|
|
82
|
+
* part of.
|
|
83
|
+
*/
|
|
84
|
+
selected?: boolean | undefined;
|
|
63
85
|
children?: ReactNode;
|
|
64
86
|
};
|
|
65
87
|
//#endregion
|
|
@@ -33,6 +33,10 @@ const BUTTON_VARIANT_TOKENS = {
|
|
|
33
33
|
surface: "border-transparent",
|
|
34
34
|
label: "text-muted-foreground"
|
|
35
35
|
},
|
|
36
|
+
"muted-destructive": {
|
|
37
|
+
surface: "border-transparent",
|
|
38
|
+
label: "text-muted-foreground"
|
|
39
|
+
},
|
|
36
40
|
outline: {
|
|
37
41
|
surface: "border-input bg-popover",
|
|
38
42
|
label: "text-foreground"
|
|
@@ -42,6 +46,14 @@ const BUTTON_VARIANT_TOKENS = {
|
|
|
42
46
|
label: "text-secondary-foreground"
|
|
43
47
|
}
|
|
44
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* `plain`: a click target whose look is its content (a card, a thumbnail, a
|
|
51
|
+
* cell, a passage). It has no tokens: neither renderer paints a box, border,
|
|
52
|
+
* type or press motion for it, and each keeps everything else its button
|
|
53
|
+
* owns (the disabled disposition, and on the web the tooltip and the focus
|
|
54
|
+
* ring). A `size` does not apply to it.
|
|
55
|
+
*/
|
|
56
|
+
const BUTTON_PLAIN_VARIANT = "plain";
|
|
45
57
|
/** Every size a button comes in, on every platform. */
|
|
46
58
|
const BUTTON_SIZES = [
|
|
47
59
|
"chip",
|
|
@@ -50,7 +62,9 @@ const BUTTON_SIZES = [
|
|
|
50
62
|
"icon-lg",
|
|
51
63
|
"icon-sm",
|
|
52
64
|
"icon-xl",
|
|
65
|
+
"icon-2xs",
|
|
53
66
|
"icon-xs",
|
|
67
|
+
"inline",
|
|
54
68
|
"lg",
|
|
55
69
|
"row",
|
|
56
70
|
"sm",
|
|
@@ -58,4 +72,4 @@ const BUTTON_SIZES = [
|
|
|
58
72
|
"xs"
|
|
59
73
|
];
|
|
60
74
|
//#endregion
|
|
61
|
-
export { BUTTON_SIZES, BUTTON_VARIANT_TOKENS };
|
|
75
|
+
export { BUTTON_PLAIN_VARIANT, BUTTON_SIZES, BUTTON_VARIANT_TOKENS };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
|
|
2
|
+
//#region src/components/collapsible.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* One disclosure: a trigger that shows or hides a single panel ("Show
|
|
5
|
+
* details" under a card, a step list in a transcript). A set of sections
|
|
6
|
+
* where each heading opens its own panel is an `Accordion`.
|
|
7
|
+
*
|
|
8
|
+
* The trigger draws nothing of its own. It takes its look from what it
|
|
9
|
+
* renders, usually a `Button` (`render={<Button variant="muted" />}` for a
|
|
10
|
+
* text toggle), so a disclosure reads like every other action of its size.
|
|
11
|
+
*/
|
|
12
|
+
declare const Collapsible: (props: Collapsible$1.Root.Props) => import("react").JSX.Element;
|
|
13
|
+
declare const CollapsibleTrigger: (props: Collapsible$1.Trigger.Props) => import("react").JSX.Element;
|
|
14
|
+
/**
|
|
15
|
+
* The disclosed content. It adds no padding or type, so opening it reveals
|
|
16
|
+
* content at the rhythm of the surface around it, as a native `<details>`
|
|
17
|
+
* does. Its height eases open and shut with the accordion's timing, so the
|
|
18
|
+
* two disclosures move alike; under reduced motion it snaps.
|
|
19
|
+
*
|
|
20
|
+
* Closed, the content stays in the document as `hidden="until-found"` (as a
|
|
21
|
+
* native `<details>` keeps it), so find-in-page still reaches it and opens
|
|
22
|
+
* the panel. Pass `hiddenUntilFound={false}` to unmount it instead.
|
|
23
|
+
*/
|
|
24
|
+
declare const CollapsiblePanel: ({ className, hiddenUntilFound, ...props }: Collapsible$1.Panel.Props) => import("react").JSX.Element;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Collapsible, CollapsiblePanel, CollapsibleTrigger };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { Collapsible as Collapsible$1 } from "@base-ui/react/collapsible";
|
|
5
|
+
//#region src/components/collapsible.tsx
|
|
6
|
+
/**
|
|
7
|
+
* One disclosure: a trigger that shows or hides a single panel ("Show
|
|
8
|
+
* details" under a card, a step list in a transcript). A set of sections
|
|
9
|
+
* where each heading opens its own panel is an `Accordion`.
|
|
10
|
+
*
|
|
11
|
+
* The trigger draws nothing of its own. It takes its look from what it
|
|
12
|
+
* renders, usually a `Button` (`render={<Button variant="muted" />}` for a
|
|
13
|
+
* text toggle), so a disclosure reads like every other action of its size.
|
|
14
|
+
*/
|
|
15
|
+
const Collapsible = (props) => /* @__PURE__ */ jsx(Collapsible$1.Root, {
|
|
16
|
+
"data-slot": "collapsible",
|
|
17
|
+
...props
|
|
18
|
+
});
|
|
19
|
+
const CollapsibleTrigger = (props) => /* @__PURE__ */ jsx(Collapsible$1.Trigger, {
|
|
20
|
+
"data-slot": "collapsible-trigger",
|
|
21
|
+
...props
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* The disclosed content. It adds no padding or type, so opening it reveals
|
|
25
|
+
* content at the rhythm of the surface around it, as a native `<details>`
|
|
26
|
+
* does. Its height eases open and shut with the accordion's timing, so the
|
|
27
|
+
* two disclosures move alike; under reduced motion it snaps.
|
|
28
|
+
*
|
|
29
|
+
* Closed, the content stays in the document as `hidden="until-found"` (as a
|
|
30
|
+
* native `<details>` keeps it), so find-in-page still reaches it and opens
|
|
31
|
+
* the panel. Pass `hiddenUntilFound={false}` to unmount it instead.
|
|
32
|
+
*/
|
|
33
|
+
const CollapsiblePanel = ({ className, hiddenUntilFound = true, ...props }) => /* @__PURE__ */ jsx(Collapsible$1.Panel, {
|
|
34
|
+
className: cn("h-(--collapsible-panel-height) min-w-0 overflow-hidden transition-[height] duration-200 ease-in-out data-ending-style:h-0 data-starting-style:h-0 motion-reduce:transition-none [&[hidden]:not([hidden=until-found])]:hidden", className),
|
|
35
|
+
"data-slot": "collapsible-panel",
|
|
36
|
+
hiddenUntilFound,
|
|
37
|
+
...props
|
|
38
|
+
});
|
|
39
|
+
//#endregion
|
|
40
|
+
export { Collapsible, CollapsiblePanel, CollapsibleTrigger };
|
|
@@ -14,7 +14,9 @@ declare const ComboboxInput: ({ className, showTrigger, showClear, startAddon, s
|
|
|
14
14
|
size?: ControlSize | number;
|
|
15
15
|
ref?: React$1.Ref<HTMLInputElement>;
|
|
16
16
|
}) => React$1.JSX.Element;
|
|
17
|
-
declare const ComboboxTrigger: ({ className, ...props }: Combobox$1.Trigger.Props
|
|
17
|
+
declare const ComboboxTrigger: ({ className, variant, ...props }: Combobox$1.Trigger.Props & {
|
|
18
|
+
variant?: "plain" | "field";
|
|
19
|
+
}) => React$1.JSX.Element;
|
|
18
20
|
declare const ComboboxPopup: ({ className, children, side, sideOffset, alignOffset, align, ...props }: Combobox$1.Popup.Props & {
|
|
19
21
|
align?: Combobox$1.Positioner.Props["align"];
|
|
20
22
|
sideOffset?: Combobox$1.Positioner.Props["sideOffset"];
|
|
@@ -5,6 +5,7 @@ import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
|
|
|
5
5
|
import { ScrollArea } from "./scroll-area.js";
|
|
6
6
|
import { containedHandler } from "../hooks/use-contained-handler.js";
|
|
7
7
|
import { CONTROL_SIZE } from "../lib/control-size.js";
|
|
8
|
+
import "../lib/field-trigger.js";
|
|
8
9
|
import { Input } from "./input.js";
|
|
9
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import * as React$1 from "react";
|
|
@@ -94,8 +95,8 @@ const COMBOBOX_ACTION_SIZE_CLASS_NAMES = {
|
|
|
94
95
|
[CONTROL_SIZE.default]: "end-0.5",
|
|
95
96
|
[CONTROL_SIZE.lg]: "end-0.5"
|
|
96
97
|
};
|
|
97
|
-
const ComboboxTrigger = ({ className, ...props }) => /* @__PURE__ */ jsx(Combobox$1.Trigger, {
|
|
98
|
-
className,
|
|
98
|
+
const ComboboxTrigger = ({ className, variant = "plain", ...props }) => /* @__PURE__ */ jsx(Combobox$1.Trigger, {
|
|
99
|
+
className: cn(variant === "field" && "border-input bg-background text-foreground ring-ring/24 focus-visible:border-ring aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 dark:aria-invalid:ring-destructive/24 relative inline-flex min-h-9 w-full min-w-36 items-center justify-center gap-2 rounded-lg border px-[calc(--spacing(3)-1px)] text-start text-base shadow-xs/5 outline-none select-none not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:ring-[3px] data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-8 sm:text-sm dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none", className),
|
|
99
100
|
"data-slot": "combobox-trigger",
|
|
100
101
|
...props
|
|
101
102
|
});
|
|
@@ -39,6 +39,8 @@ type DatePickerPopoverProps = {
|
|
|
39
39
|
* the control still has height; pass a call-to-action ("Select date…") to
|
|
40
40
|
* make the empty state self-explanatory. */
|
|
41
41
|
placeholderLabel?: string;
|
|
42
|
+
/** Replaces the formatted date in the trigger, as a week range does. */
|
|
43
|
+
triggerLabel?: string;
|
|
42
44
|
clearLabel?: string;
|
|
43
45
|
defaultOpen?: boolean;
|
|
44
46
|
open?: boolean;
|
|
@@ -3,8 +3,8 @@ import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "../icons.js";
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
5
|
import { DirectionalIcon } from "./directional-icon.js";
|
|
6
|
-
import { Input } from "./input.js";
|
|
7
6
|
import { FIELD_TRIGGER_CLASS_NAME } from "../lib/field-trigger.js";
|
|
7
|
+
import { Input } from "./input.js";
|
|
8
8
|
import { getLocaleWeekInfo, getWeekendDays } from "../lib/week.js";
|
|
9
9
|
import { DATE_PICKER_MODE, formatDateTimeValue, getHourOptions, getMinuteOptions, getTimeFieldNames, joinPickerTime, localDateFromTimestamp, millisecondsUntilNextLocalDate, parseDateTimeValue, resolveCalendarViewMonth, shiftCalendarDate, splitPickerTime } from "./date-picker-popover.logic.js";
|
|
10
10
|
import { Popover, PopoverContent as PopoverPopup, PopoverTrigger } from "./popover.js";
|
|
@@ -252,8 +252,10 @@ const PICKER_SIZE_CLASS_NAMES = {
|
|
|
252
252
|
cell: "pointer-coarse:min-h-11 pointer-coarse:text-sm"
|
|
253
253
|
}
|
|
254
254
|
};
|
|
255
|
+
/** The trigger keeps its height with an em dash when nothing has a label. */
|
|
256
|
+
const firstLabel = (...labels) => labels.find((label) => label !== null && label !== void 0) ?? "—";
|
|
255
257
|
const DatePickerPopoverContent = (props) => {
|
|
256
|
-
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, clearLabel = "Clear date", defaultOpen = false, open: controlledOpen, triggerRef, focusTriggerOnOpen = false, dateInputLabel, outOfRangeLabel, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
258
|
+
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, triggerLabel, clearLabel = "Clear date", defaultOpen = false, open: controlledOpen, triggerRef, focusTriggerOnOpen = false, dateInputLabel, outOfRangeLabel, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
257
259
|
const selection = resolveSelection(mode, rawValue);
|
|
258
260
|
const value = selection.date;
|
|
259
261
|
const labels = resolvePopupLabels(props);
|
|
@@ -300,7 +302,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
300
302
|
isDayDisabled,
|
|
301
303
|
onChange
|
|
302
304
|
});
|
|
303
|
-
const displayLabel = formatSelection(locale, selection)
|
|
305
|
+
const displayLabel = firstLabel(triggerLabel, formatSelection(locale, selection), placeholderLabel);
|
|
304
306
|
const selectDay = (date) => {
|
|
305
307
|
switch (selection.mode) {
|
|
306
308
|
case DATE_PICKER_MODE.date:
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
2
|
//#region src/components/details-grid.d.ts
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* - `sheet`: a fact sheet; small uppercase labels, up to three pairs per row
|
|
5
|
+
* as the container widens.
|
|
6
|
+
* - `list`: one pair per row in body type with a muted label, for a short
|
|
7
|
+
* list inside a card or dialog (who is signed in, which organization).
|
|
8
|
+
*/
|
|
9
|
+
type DetailsVariant = "sheet" | "list";
|
|
10
|
+
/**
|
|
11
|
+
* - `horizontal`: each label beside its value. In a `list`, every label
|
|
12
|
+
* shares one column sized to the longest.
|
|
13
|
+
* - `vertical`: each label above its value, for values too long to share a
|
|
14
|
+
* row (a server address, a list of origins).
|
|
15
|
+
*/
|
|
16
|
+
type DetailsOrientation = "horizontal" | "vertical";
|
|
17
|
+
type DetailsGridProps = React$1.ComponentProps<"dl"> & {
|
|
18
|
+
orientation?: DetailsOrientation;
|
|
19
|
+
variant?: DetailsVariant;
|
|
20
|
+
};
|
|
21
|
+
declare const DetailsGrid: ({ className, orientation, variant, ...props }: DetailsGridProps) => React$1.JSX.Element;
|
|
4
22
|
type DetailsItemProps = React$1.ComponentProps<"div"> & {
|
|
5
23
|
label: React$1.ReactNode;
|
|
24
|
+
/** `wide` spans every pair column of a `sheet`. */
|
|
6
25
|
span?: "wide";
|
|
7
26
|
};
|
|
8
27
|
declare const DetailsItem: ({ label, children, span, className, ...props }: DetailsItemProps) => React$1.JSX.Element;
|
|
9
28
|
//#endregion
|
|
10
|
-
export { DetailsGrid, DetailsItem };
|
|
29
|
+
export { DetailsGrid, DetailsItem, type DetailsOrientation, type DetailsVariant };
|