@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.
Files changed (60) hide show
  1. package/README.md +120 -4
  2. package/dist/components/accordion.d.ts +17 -3
  3. package/dist/components/accordion.js +17 -3
  4. package/dist/components/button.d.ts +56 -5
  5. package/dist/components/button.js +97 -15
  6. package/dist/components/button.native-classes.d.ts +17 -0
  7. package/dist/components/button.native-classes.js +176 -0
  8. package/dist/components/button.native.d.ts +5 -2
  9. package/dist/components/button.native.js +17 -134
  10. package/dist/components/button.shared.d.ts +24 -2
  11. package/dist/components/button.shared.js +15 -1
  12. package/dist/components/collapsible.d.ts +26 -0
  13. package/dist/components/collapsible.js +40 -0
  14. package/dist/components/combobox.d.ts +3 -1
  15. package/dist/components/combobox.js +3 -2
  16. package/dist/components/date-picker-popover.d.ts +2 -0
  17. package/dist/components/date-picker-popover.js +5 -3
  18. package/dist/components/details-grid.d.ts +21 -2
  19. package/dist/components/details-grid.js +75 -20
  20. package/dist/components/field.d.ts +6 -0
  21. package/dist/components/field.js +6 -0
  22. package/dist/components/form.d.ts +43 -3
  23. package/dist/components/form.js +53 -3
  24. package/dist/components/input-control.d.ts +9 -0
  25. package/dist/components/input-control.js +10 -1
  26. package/dist/components/input.d.ts +7 -1
  27. package/dist/components/input.js +4 -4
  28. package/dist/components/label.d.ts +24 -2
  29. package/dist/components/label.js +21 -2
  30. package/dist/components/number-input.d.ts +9 -2
  31. package/dist/components/number-input.js +18 -6
  32. package/dist/components/search-field.js +1 -0
  33. package/dist/components/segmented-icon-toggle.d.ts +2 -1
  34. package/dist/components/segmented-icon-toggle.js +18 -19
  35. package/dist/components/switch.d.ts +21 -2
  36. package/dist/components/switch.js +44 -5
  37. package/dist/components/table.d.ts +16 -2
  38. package/dist/components/table.js +5 -4
  39. package/dist/components/textarea.d.ts +14 -1
  40. package/dist/components/textarea.js +11 -5
  41. package/dist/components/toggle-group.d.ts +32 -0
  42. package/dist/components/toggle-group.js +45 -0
  43. package/dist/components/toggle.d.ts +26 -0
  44. package/dist/components/toggle.js +26 -0
  45. package/dist/index.d.ts +14 -7
  46. package/dist/index.js +9 -3
  47. package/dist/kanban/column-bands.js +1 -1
  48. package/dist/kanban/index.d.ts +5 -2
  49. package/dist/kanban/index.js +4 -1
  50. package/dist/kanban/sortable-edge.d.ts +39 -0
  51. package/dist/kanban/sortable-edge.js +27 -0
  52. package/dist/kanban/sortable-interactions.d.ts +145 -0
  53. package/dist/kanban/sortable-interactions.js +503 -0
  54. package/dist/kanban/sortable-interactions.logic.d.ts +79 -0
  55. package/dist/kanban/sortable-interactions.logic.js +210 -0
  56. package/dist/kanban/subgroup-board.d.ts +27 -2
  57. package/dist/kanban/subgroup-board.js +26 -1
  58. package/dist/kanban/virtual-cell.d.ts +18 -1
  59. package/dist/kanban/virtual-cell.js +70 -17
  60. 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 { BUTTON_VARIANT_TOKENS } from "./button.shared.js";
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 shape = SIZES[size ?? "default"];
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 selected = accessibilityState?.selected === true;
131
- const label = [
132
- tokens.label,
133
- variant === "link" ? "text-foreground underline" : "",
134
- shape.label
135
- ].filter((name) => name !== "").join(" ");
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 ?? (shape.slop === 0 ? void 0 : shape.slop),
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
- export type ButtonVariant = keyof typeof BUTTON_VARIANT_TOKENS;
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) => React$1.JSX.Element;
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) ?? placeholderLabel ?? "—";
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
- declare const DetailsGrid: ({ className, ...props }: React$1.ComponentProps<"dl">) => React$1.JSX.Element;
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 };