@vitavision/ui 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -161,7 +161,10 @@ export declare function CountRun({ counts, className }: {
161
161
  * destroys something.
162
162
  */
163
163
  type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
164
- /** A button's height and padding. `md` is the comfortable density's size, `sm` the compact one's. */
164
+ /**
165
+ * A control's height and padding, shared by `Button`, `SegmentedControl` and `ToggleChip`. For a
166
+ * button, `md` is the comfortable density's size and `sm` the compact one's.
167
+ */
165
168
  type ButtonSize = "sm" | "md";
166
169
  /**
167
170
  * The classes that make a control look like a button, for the one element that has to be
@@ -890,8 +893,11 @@ export declare function PoseInput({ value, onValueChange, rotationView, translat
890
893
  * or `defaultValue` while `value` is `""` — and choosing the segment that matches
891
894
  * `defaultValue` reports `""`, so the default stays defined in one place. Each segment
892
895
  * carries `data-state="checked"` or `"unchecked"`; the group carries `data-disabled`.
896
+ *
897
+ * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or
898
+ * `Button`, pass `size="md"` and the strip's outer height matches theirs.
893
899
  */
894
- export declare function SegmentedControl({ value, defaultValue, options, onValueChange, disabled, "aria-label": ariaLabel, className }: {
900
+ export declare function SegmentedControl({ value, defaultValue, options, onValueChange, disabled, "aria-label": ariaLabel, size, className }: {
895
901
  /** `""` when the field is untouched. */
896
902
  value: string;
897
903
  /** What applies when nothing is sent. Highlighted while `value` is `""`. */
@@ -907,6 +913,13 @@ export declare function SegmentedControl({ value, defaultValue, options, onValue
907
913
  disabled?: boolean | undefined;
908
914
  /** Names the radio group. */
909
915
  "aria-label"?: string | undefined;
916
+ /**
917
+ * The strip's height: `sm` (the default) is the toolbar size, about 30px with `text-xs`
918
+ * labels; `md` is exactly 32px (`h-8`) outside, border included, with `text-sm` labels, the
919
+ * height of a comfortable-density `Input` or `Button`. Not derived from the density in
920
+ * force, so a consumer that never passes it renders as it always did.
921
+ */
922
+ size?: ButtonSize | undefined;
910
923
  /** Merged with the strip's own classes through `cn`. */
911
924
  className?: string | undefined;
912
925
  }): import("react").JSX.Element;
@@ -1091,8 +1104,11 @@ export declare function Checkbox({ checked, onCheckedChange, onClick, label, des
1091
1104
  * colour: a toolbar-sized `Switch` (`role="switch"`, `aria-checked`).
1092
1105
  *
1093
1106
  * Controlled. State is exposed as `data-state` (`checked`/`unchecked`).
1107
+ *
1108
+ * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or
1109
+ * `Button`, pass `size="md"` and the chip's outer height matches theirs.
1094
1110
  */
1095
- export declare function ToggleChip({ checked, onCheckedChange, children, swatch, disabled, title, className }: {
1111
+ export declare function ToggleChip({ checked, onCheckedChange, children, swatch, disabled, title, size, className }: {
1096
1112
  /** Whether the layer is shown. */
1097
1113
  checked: boolean;
1098
1114
  /** Called with the new state. */
@@ -1105,6 +1121,14 @@ export declare function ToggleChip({ checked, onCheckedChange, children, swatch,
1105
1121
  disabled?: boolean | undefined;
1106
1122
  /** Why the control is disabled, or what the layer is. Never the only explanation. */
1107
1123
  title?: string | undefined;
1124
+ /**
1125
+ * The chip's height: `sm` (the default) is the toolbar size, about 26px with `text-xs`
1126
+ * text; `md` is exactly 32px (`h-8`) outside, border included, with `text-sm` text and a
1127
+ * slightly larger swatch, the height of a comfortable-density `Input` or `Button`. Not
1128
+ * derived from the density in force, so a consumer that never passes it renders as it
1129
+ * always did.
1130
+ */
1131
+ size?: ButtonSize | undefined;
1108
1132
  /** Merged with the chip's own classes through `cn`. */
1109
1133
  className?: string | undefined;
1110
1134
  }): import("react").JSX.Element;
package/dist/index.js CHANGED
@@ -353,7 +353,7 @@ const VARIANT_CLASSES = {
353
353
  ghost: "text-fg-muted hover:bg-raised hover:text-fg",
354
354
  danger: "bg-defect/10 text-defect ring-1 ring-inset ring-defect/30 hover:bg-defect/15"
355
355
  };
356
- const SIZE_CLASSES = {
356
+ const SIZE_CLASSES$1 = {
357
357
  sm: "h-7 gap-1.5 px-2.5 text-xs",
358
358
  md: "h-8 gap-2 px-3 text-sm"
359
359
  };
@@ -367,7 +367,7 @@ const SIZE_CLASSES = {
367
367
  * @returns The class string.
368
368
  */
369
369
  function buttonClasses({ variant = "secondary", size = "md", className } = {}) {
370
- return cn("inline-flex shrink-0 items-center justify-center rounded-control font-medium transition-colors", "disabled:cursor-not-allowed disabled:opacity-45", SIZE_CLASSES[size], VARIANT_CLASSES[variant], focusRing, className);
370
+ return cn("inline-flex shrink-0 items-center justify-center rounded-control font-medium transition-colors", "disabled:cursor-not-allowed disabled:opacity-45", SIZE_CLASSES$1[size], VARIANT_CLASSES[variant], focusRing, className);
371
371
  }
372
372
  function ButtonIcon({ icon }) {
373
373
  return /* @__PURE__ */ jsx("span", {
@@ -1621,6 +1621,16 @@ function PoseInput({ value, onValueChange, rotationView, translationUnit = "m",
1621
1621
  //#endregion
1622
1622
  //#region src/components/ui/SegmentedControl.tsx
1623
1623
  /**
1624
+ * The segment's own box, by size. `sm` is the toolbar size and the default. `md` sets the
1625
+ * height explicitly rather than leaving it to padding plus line-height: the strip around it
1626
+ * is `h-8` with a 1px border and `p-0.5`, which leaves 26px of content box, and `h-full`
1627
+ * fills exactly that whatever the font's line height turns out to be.
1628
+ */
1629
+ const SEGMENT_SIZE_CLASSES = {
1630
+ sm: "px-2.5 py-1 text-xs",
1631
+ md: "flex h-full items-center px-3.5 text-sm"
1632
+ };
1633
+ /**
1624
1634
  * A choice among two or three, shown as a strip of segments (native radios in one group, so
1625
1635
  * arrow keys move between them).
1626
1636
  *
@@ -1628,8 +1638,11 @@ function PoseInput({ value, onValueChange, rotationView, translationUnit = "m",
1628
1638
  * or `defaultValue` while `value` is `""` — and choosing the segment that matches
1629
1639
  * `defaultValue` reports `""`, so the default stays defined in one place. Each segment
1630
1640
  * carries `data-state="checked"` or `"unchecked"`; the group carries `data-disabled`.
1641
+ *
1642
+ * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or
1643
+ * `Button`, pass `size="md"` and the strip's outer height matches theirs.
1631
1644
  */
1632
- function SegmentedControl({ value, defaultValue, options, onValueChange, disabled = false, "aria-label": ariaLabel, className }) {
1645
+ function SegmentedControl({ value, defaultValue, options, onValueChange, disabled = false, "aria-label": ariaLabel, size = "sm", className }) {
1633
1646
  const nameId = useId();
1634
1647
  const described = useFieldDescription();
1635
1648
  const effective = value === "" ? defaultValue : value;
@@ -1638,12 +1651,12 @@ function SegmentedControl({ value, defaultValue, options, onValueChange, disable
1638
1651
  "aria-label": ariaLabel,
1639
1652
  "aria-describedby": described["aria-describedby"],
1640
1653
  "data-disabled": disabled ? "" : void 0,
1641
- className: cn("inline-flex w-fit max-w-full shrink-0 items-center gap-0.5 rounded-control border border-line-strong bg-raised p-0.5", disabled && "opacity-50", className),
1654
+ className: cn("inline-flex w-fit max-w-full shrink-0 items-center gap-0.5 rounded-control border border-line-strong bg-raised p-0.5", size === "md" && "h-8", disabled && "opacity-50", className),
1642
1655
  children: options.map((option) => {
1643
1656
  const checked = option.value === effective;
1644
1657
  return /* @__PURE__ */ jsxs("label", {
1645
1658
  "data-state": checked ? "checked" : "unchecked",
1646
- className: cn("relative cursor-pointer whitespace-nowrap rounded-[0.3rem] px-2.5 py-1 text-xs font-medium transition-colors", "has-focus-visible:outline-2 has-focus-visible:outline-offset-1 has-focus-visible:outline-signal", checked ? "bg-surface text-fg shadow-sm" : "text-fg-muted hover:text-fg", disabled && "cursor-not-allowed"),
1659
+ className: cn("relative cursor-pointer whitespace-nowrap rounded-[0.3rem]", SEGMENT_SIZE_CLASSES[size], "font-medium transition-colors", "has-focus-visible:outline-2 has-focus-visible:outline-offset-1 has-focus-visible:outline-signal", checked ? "bg-surface text-fg shadow-sm" : "text-fg-muted hover:text-fg", disabled && "cursor-not-allowed"),
1647
1660
  children: [/* @__PURE__ */ jsx("input", {
1648
1661
  type: "radio",
1649
1662
  name: nameId,
@@ -1901,12 +1914,28 @@ function Checkbox({ checked, onCheckedChange, onClick, label, description, disab
1901
1914
  //#endregion
1902
1915
  //#region src/components/ui/ToggleChip.tsx
1903
1916
  /**
1917
+ * The chip's box, by size. `sm` is the toolbar size and the default. `md` sets the height
1918
+ * explicitly (`h-8`, border included) rather than leaving it to padding plus line-height.
1919
+ */
1920
+ const SIZE_CLASSES = {
1921
+ sm: "px-2 py-1 text-xs",
1922
+ md: "h-8 px-3 text-sm"
1923
+ };
1924
+ /** The swatch dot, by size: a little larger beside `text-sm` so it keeps its weight against the label. */
1925
+ const SWATCH_SIZE_CLASSES = {
1926
+ sm: "size-2",
1927
+ md: "size-2.5"
1928
+ };
1929
+ /**
1904
1930
  * A dense on/off chip for a layer drawn on screen, with an optional swatch in the layer's
1905
1931
  * colour: a toolbar-sized `Switch` (`role="switch"`, `aria-checked`).
1906
1932
  *
1907
1933
  * Controlled. State is exposed as `data-state` (`checked`/`unchecked`).
1934
+ *
1935
+ * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or
1936
+ * `Button`, pass `size="md"` and the chip's outer height matches theirs.
1908
1937
  */
1909
- function ToggleChip({ checked, onCheckedChange, children, swatch, disabled = false, title, className }) {
1938
+ function ToggleChip({ checked, onCheckedChange, children, swatch, disabled = false, title, size = "sm", className }) {
1910
1939
  return /* @__PURE__ */ jsxs("button", {
1911
1940
  type: "button",
1912
1941
  role: "switch",
@@ -1915,10 +1944,10 @@ function ToggleChip({ checked, onCheckedChange, children, swatch, disabled = fal
1915
1944
  disabled,
1916
1945
  title,
1917
1946
  onClick: () => onCheckedChange(!checked),
1918
- className: cn("inline-flex items-center gap-1.5 rounded-control border px-2 py-1 text-xs whitespace-nowrap transition-colors", checked ? "border-line-strong bg-raised text-fg" : "border-line bg-transparent text-fg-muted hover:text-fg", "disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:text-fg-muted", focusRing, className),
1947
+ className: cn("inline-flex items-center gap-1.5 rounded-control border", SIZE_CLASSES[size], "whitespace-nowrap transition-colors", checked ? "border-line-strong bg-raised text-fg" : "border-line bg-transparent text-fg-muted hover:text-fg", "disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:text-fg-muted", focusRing, className),
1919
1948
  children: [swatch !== void 0 && /* @__PURE__ */ jsx("span", {
1920
1949
  "aria-hidden": true,
1921
- className: cn("size-2 shrink-0 rounded-full border transition-opacity", checked ? "opacity-100" : "opacity-30"),
1950
+ className: cn(SWATCH_SIZE_CLASSES[size], "shrink-0 rounded-full border transition-opacity", checked ? "opacity-100" : "opacity-30"),
1922
1951
  style: {
1923
1952
  backgroundColor: swatch,
1924
1953
  borderColor: swatch