@campfire-interactive/ui 0.9.0 → 0.9.2

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
@@ -41,9 +41,12 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
41
41
  * ones mostly chose styling. This is the spelling the fleet's vendored
42
42
  * shadcn buttons already use, so it needs no explaining at a call site.
43
43
  *
44
- * The child must be a single element that accepts a ref. `leadingIcon`,
45
- * `trailingIcon` and `loading` are ignored, because the child owns its own
46
- * content — put the icon inside it.
44
+ * The child must be a single element that accepts a ref. `leadingIcon` and
45
+ * `trailingIcon` work here and render inside the child, same as on a real
46
+ * button — they used to be silently dropped, which took the icon off every
47
+ * link-button in the fleet while the plain buttons beside them kept theirs.
48
+ * `loading` is still ignored: a spinner belongs on a control that is doing
49
+ * something, and a link navigates.
47
50
  */
48
51
  asChild?: boolean;
49
52
  /**
package/dist/index.js CHANGED
@@ -39,14 +39,14 @@ function cn(...values) {
39
39
  }
40
40
 
41
41
  // src/Button.tsx
42
- import { forwardRef } from "react";
42
+ import { cloneElement, forwardRef, isValidElement } from "react";
43
43
  import { Slot } from "@radix-ui/react-slot";
44
44
 
45
45
  // src/Button.css
46
- styleInject("@layer components {\n :where(.cfi-ui-btn) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--cfi-ui-space-sm);\n height: 2rem;\n padding-inline: 0.75rem;\n border: 1px solid transparent;\n border-radius: var(--cfi-ui-radius-md);\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-medium);\n line-height: normal;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background-color var(--cfi-ui-transition-fast),\n border-color var(--cfi-ui-transition-fast),\n color var(--cfi-ui-transition-fast);\n font-variant-numeric: tabular-nums;\n }\n a.cfi-ui-btn,\n a.cfi-ui-btn:hover,\n a.cfi-ui-btn:visited {\n text-decoration: none;\n color: inherit;\n }\n .cfi-ui-btn:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n }\n .cfi-ui-btn[data-loading] {\n cursor: progress;\n opacity: 1;\n }\n .cfi-ui-btn--full {\n width: 100%;\n }\n .cfi-ui-btn[data-size=sm] {\n height: 1.75rem;\n padding-inline: 0.5rem;\n gap: var(--cfi-ui-space-xs);\n }\n .cfi-ui-btn[data-size=lg] {\n height: var(--cfi-ui-control-height-md);\n padding-inline: 1rem;\n font-size: var(--cfi-ui-text-sm);\n }\n .cfi-ui-btn[data-size=icon] {\n width: var(--cfi-ui-control-height-md);\n padding-inline: 0;\n }\n .cfi-ui-btn--icon[data-size=icon] {\n aspect-ratio: 1;\n }\n .cfi-ui-btn[data-variant=primary] {\n background: var(--cfi-ui-accent);\n color: var(--cfi-ui-accent-fg);\n }\n .cfi-ui-btn[data-variant=primary]:hover:not(:disabled) {\n background: var(--cfi-ui-accent-hover);\n }\n .cfi-ui-btn[data-variant=secondary] {\n background: var(--cfi-ui-surface);\n border-color: var(--cfi-ui-border-strong);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-btn[data-variant=secondary]:hover:not(:disabled) {\n background: var(--cfi-ui-surface-hover);\n }\n .cfi-ui-btn[data-variant=ghost] {\n color: var(--cfi-ui-content);\n }\n .cfi-ui-btn[data-variant=ghost]:hover:not(:disabled) {\n background: var(--cfi-ui-surface-active);\n }\n .cfi-ui-btn[data-variant=danger] {\n background: var(--cfi-ui-error-strong);\n color: var(--cfi-ui-content-inverse);\n }\n .cfi-ui-btn[data-variant=danger]:hover:not(:disabled) {\n background: linear-gradient(rgb(0 0 0 / 0.14), rgb(0 0 0 / 0.14)), var(--cfi-ui-error-strong);\n }\n .cfi-ui-btn[data-variant=link] {\n color: var(--cfi-ui-accent);\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n .cfi-ui-btn[data-variant=link]:hover:not(:disabled) {\n text-decoration-thickness: 2px;\n }\n .cfi-ui-btn__icon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n }\n .cfi-ui-btn__icon > svg {\n width: 1em;\n height: 1em;\n display: block;\n }\n .cfi-ui-btn[data-size=lg] .cfi-ui-btn__icon > svg {\n width: 1.15em;\n height: 1.15em;\n }\n .cfi-ui-btn__label {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n min-width: 0;\n }\n .cfi-ui-btn[data-size=sm] .cfi-ui-btn__label {\n gap: var(--cfi-ui-space-xs);\n }\n .cfi-ui-btn__label > svg,\n .cfi-ui-btn > svg {\n width: 1em;\n height: 1em;\n flex: none;\n display: block;\n }\n .cfi-ui-btn__spinner {\n flex: none;\n width: 1em;\n height: 1em;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: var(--cfi-ui-radius-full);\n animation: cfi-ui-spin 620ms linear infinite;\n }\n :where(.cfi-ui-btn-group) {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n flex-wrap: wrap;\n }\n .cfi-ui-btn-group[data-align=start] {\n justify-content: flex-start;\n }\n .cfi-ui-btn-group[data-align=end] {\n justify-content: flex-end;\n }\n .cfi-ui-btn-group[data-align=between] {\n justify-content: space-between;\n width: 100%;\n }\n}\n");
46
+ styleInject("@layer components {\n :where(.cfi-ui-btn) {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--cfi-ui-space-sm);\n height: 2rem;\n padding-inline: 0.75rem;\n border: 1px solid transparent;\n border-radius: var(--cfi-ui-radius-md);\n background: transparent;\n color: inherit;\n font-family: inherit;\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-medium);\n line-height: normal;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background-color var(--cfi-ui-transition-fast),\n border-color var(--cfi-ui-transition-fast),\n color var(--cfi-ui-transition-fast);\n font-variant-numeric: tabular-nums;\n }\n a:where(.cfi-ui-btn),\n a:where(.cfi-ui-btn):hover,\n a:where(.cfi-ui-btn):visited {\n text-decoration: none;\n }\n .cfi-ui-btn:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n }\n .cfi-ui-btn[data-loading] {\n cursor: progress;\n opacity: 1;\n }\n .cfi-ui-btn--full {\n width: 100%;\n }\n .cfi-ui-btn[data-size=sm] {\n height: 1.75rem;\n padding-inline: 0.5rem;\n gap: var(--cfi-ui-space-xs);\n }\n .cfi-ui-btn[data-size=lg] {\n height: var(--cfi-ui-control-height-md);\n padding-inline: 1rem;\n font-size: var(--cfi-ui-text-sm);\n }\n .cfi-ui-btn[data-size=icon] {\n width: var(--cfi-ui-control-height-md);\n padding-inline: 0;\n }\n .cfi-ui-btn--icon[data-size=icon] {\n aspect-ratio: 1;\n }\n .cfi-ui-btn[data-variant=primary] {\n background: var(--cfi-ui-accent);\n color: var(--cfi-ui-accent-fg);\n }\n .cfi-ui-btn[data-variant=primary]:hover:not(:disabled) {\n background: var(--cfi-ui-accent-hover);\n }\n .cfi-ui-btn[data-variant=secondary] {\n background: var(--cfi-ui-surface);\n border-color: var(--cfi-ui-border-strong);\n color: var(--cfi-ui-content);\n }\n .cfi-ui-btn[data-variant=secondary]:hover:not(:disabled) {\n background: var(--cfi-ui-surface-hover);\n }\n .cfi-ui-btn[data-variant=ghost] {\n color: var(--cfi-ui-content);\n }\n .cfi-ui-btn[data-variant=ghost]:hover:not(:disabled) {\n background: var(--cfi-ui-surface-active);\n }\n .cfi-ui-btn[data-variant=danger] {\n background: var(--cfi-ui-error-strong);\n color: var(--cfi-ui-content-inverse);\n }\n .cfi-ui-btn[data-variant=danger]:hover:not(:disabled) {\n background: linear-gradient(rgb(0 0 0 / 0.14), rgb(0 0 0 / 0.14)), var(--cfi-ui-error-strong);\n }\n .cfi-ui-btn[data-variant=link] {\n color: var(--cfi-ui-accent);\n text-decoration: underline;\n text-underline-offset: 4px;\n }\n .cfi-ui-btn[data-variant=link]:hover:not(:disabled) {\n text-decoration-thickness: 2px;\n }\n .cfi-ui-btn__icon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n }\n .cfi-ui-btn__icon > svg {\n width: 1em;\n height: 1em;\n display: block;\n }\n .cfi-ui-btn[data-size=lg] .cfi-ui-btn__icon > svg {\n width: 1.15em;\n height: 1.15em;\n }\n .cfi-ui-btn__label {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n min-width: 0;\n }\n .cfi-ui-btn[data-size=sm] .cfi-ui-btn__label {\n gap: var(--cfi-ui-space-xs);\n }\n .cfi-ui-btn__label > svg,\n .cfi-ui-btn > svg {\n width: 1em;\n height: 1em;\n flex: none;\n display: block;\n }\n .cfi-ui-btn__spinner {\n flex: none;\n width: 1em;\n height: 1em;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: var(--cfi-ui-radius-full);\n animation: cfi-ui-spin 620ms linear infinite;\n }\n :where(.cfi-ui-btn-group) {\n display: flex;\n align-items: center;\n gap: var(--cfi-ui-space-sm);\n flex-wrap: wrap;\n }\n .cfi-ui-btn-group[data-align=start] {\n justify-content: flex-start;\n }\n .cfi-ui-btn-group[data-align=end] {\n justify-content: flex-end;\n }\n .cfi-ui-btn-group[data-align=between] {\n justify-content: space-between;\n width: 100%;\n }\n}\n");
47
47
 
48
48
  // src/Button.tsx
49
- import { jsx, jsxs } from "react/jsx-runtime";
49
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
50
50
  var Button = forwardRef(function Button2({
51
51
  variant = "secondary",
52
52
  size = "md",
@@ -67,7 +67,16 @@ var Button = forwardRef(function Button2({
67
67
  className: cn("cfi-ui", "cfi-ui-btn", fullWidth && "cfi-ui-btn--full", className)
68
68
  };
69
69
  if (asChild) {
70
- return /* @__PURE__ */ jsx(Slot, { ref, ...styling, ...rest, children });
70
+ const slotted = isValidElement(children) ? cloneElement(
71
+ children,
72
+ void 0,
73
+ /* @__PURE__ */ jsxs(Fragment, { children: [
74
+ leadingIcon && /* @__PURE__ */ jsx("span", { className: "cfi-ui-btn__icon", children: leadingIcon }),
75
+ /* @__PURE__ */ jsx("span", { className: "cfi-ui-btn__label", children: children.props.children }),
76
+ trailingIcon && /* @__PURE__ */ jsx("span", { className: "cfi-ui-btn__icon", children: trailingIcon })
77
+ ] })
78
+ ) : children;
79
+ return /* @__PURE__ */ jsx(Slot, { ref, ...styling, ...rest, children: slotted });
71
80
  }
72
81
  return /* @__PURE__ */ jsxs(
73
82
  "button",
@@ -110,7 +119,7 @@ import { forwardRef as forwardRef3 } from "react";
110
119
  import { ChevronDown } from "lucide-react";
111
120
 
112
121
  // src/Menu.tsx
113
- import { Fragment, forwardRef as forwardRef2 } from "react";
122
+ import { Fragment as Fragment2, forwardRef as forwardRef2 } from "react";
114
123
  import * as RadixMenu from "@radix-ui/react-dropdown-menu";
115
124
  import { Check } from "lucide-react";
116
125
 
@@ -193,7 +202,7 @@ var MenuSubContent = forwardRef2(function MenuSubContent2({ className, ...rest }
193
202
  function Menu({ trigger, items, align = "end", side = "bottom", className }) {
194
203
  return /* @__PURE__ */ jsxs2(MenuRoot, { children: [
195
204
  /* @__PURE__ */ jsx2(MenuTrigger, { asChild: true, children: trigger }),
196
- /* @__PURE__ */ jsx2(MenuContent, { align, side, className, children: items.map((item) => /* @__PURE__ */ jsxs2(Fragment, { children: [
205
+ /* @__PURE__ */ jsx2(MenuContent, { align, side, className, children: items.map((item) => /* @__PURE__ */ jsxs2(Fragment2, { children: [
197
206
  item.separatorBefore && /* @__PURE__ */ jsx2(MenuSeparator, {}),
198
207
  /* @__PURE__ */ jsx2(
199
208
  MenuItem,
@@ -650,13 +659,13 @@ var Textarea = forwardRef8(function Textarea2({ invalid, className, rows = 3, ..
650
659
  });
651
660
 
652
661
  // src/Field.tsx
653
- import { Children, cloneElement, createContext, isValidElement, useContext, useId } from "react";
662
+ import { Children, cloneElement as cloneElement2, createContext, isValidElement as isValidElement2, useContext, useId } from "react";
654
663
 
655
664
  // src/Field.css
656
665
  styleInject('@layer components {\n .cfi-ui-label {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-ui-space-3xs);\n font-size: var(--cfi-ui-text-sm);\n font-weight: var(--cfi-ui-weight-medium);\n color: var(--cfi-ui-content);\n line-height: var(--cfi-ui-leading-normal);\n }\n .cfi-ui-label__required {\n color: var(--cfi-ui-error);\n font-weight: var(--cfi-ui-weight-semibold);\n }\n .cfi-ui-field {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--cfi-ui-space-xs);\n }\n .cfi-ui-field__label {\n justify-self: start;\n }\n .cfi-ui-field__body {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--cfi-ui-space-xs);\n min-width: 0;\n }\n .cfi-ui-field__hint,\n .cfi-ui-field__error {\n margin: 0;\n font-size: var(--cfi-ui-text-xs);\n line-height: var(--cfi-ui-leading-normal);\n }\n .cfi-ui-field__hint {\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-field__error {\n color: var(--cfi-ui-error-text);\n font-weight: var(--cfi-ui-weight-medium);\n }\n .cfi-ui-field[data-orientation=horizontal] {\n grid-template-columns: minmax(8rem, 12rem) 1fr;\n align-items: start;\n gap: var(--cfi-ui-space-md);\n }\n .cfi-ui-field[data-orientation=horizontal] .cfi-ui-field__label {\n padding-top: 0.4375rem;\n }\n @media (max-width: 640px) {\n .cfi-ui-field[data-orientation=horizontal] {\n grid-template-columns: 1fr;\n gap: var(--cfi-ui-space-xs);\n }\n .cfi-ui-field[data-orientation=horizontal] .cfi-ui-field__label {\n padding-top: 0;\n }\n }\n .cfi-ui-fieldset {\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n }\n .cfi-ui-fieldset__legend {\n padding: 0 0 var(--cfi-ui-space-sm);\n font-size: var(--cfi-ui-text-xs);\n font-weight: var(--cfi-ui-weight-semibold);\n letter-spacing: var(--cfi-ui-tracking-caps);\n text-transform: uppercase;\n color: var(--cfi-ui-content-muted);\n }\n .cfi-ui-fieldset__body {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--cfi-ui-space-md);\n }\n .cfi-ui-fieldset[data-columns="2"] .cfi-ui-fieldset__body {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n .cfi-ui-fieldset[data-columns="3"] .cfi-ui-fieldset__body {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n }\n @media (max-width: 720px) {\n .cfi-ui-fieldset[data-columns="2"] .cfi-ui-fieldset__body,\n .cfi-ui-fieldset[data-columns="3"] .cfi-ui-fieldset__body {\n grid-template-columns: 1fr;\n }\n }\n}\n');
657
666
 
658
667
  // src/Field.tsx
659
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
668
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
660
669
  function Label3({ required, className, children, ...rest }) {
661
670
  return /* @__PURE__ */ jsxs10("label", { className: cn("cfi-ui", "cfi-ui-label", className), ...rest, children: [
662
671
  children,
@@ -693,11 +702,11 @@ function Field({
693
702
  }
694
703
  function FieldControl({ children }) {
695
704
  const ctx = useContext(FieldContext);
696
- if (!ctx) return /* @__PURE__ */ jsx10(Fragment2, { children });
705
+ if (!ctx) return /* @__PURE__ */ jsx10(Fragment3, { children });
697
706
  const only = Children.count(children) === 1 ? Children.toArray(children)[0] : null;
698
- if (!only || !isValidElement(only)) return /* @__PURE__ */ jsx10(Fragment2, { children });
707
+ if (!only || !isValidElement2(only)) return /* @__PURE__ */ jsx10(Fragment3, { children });
699
708
  const props = only.props;
700
- return cloneElement(only, {
709
+ return cloneElement2(only, {
701
710
  id: props.id ?? ctx.controlId,
702
711
  // A control that already declares its own description keeps it — the
703
712
  // field's id is appended rather than replacing it.
@@ -1078,7 +1087,7 @@ function CreatableSelect({
1078
1087
  }
1079
1088
 
1080
1089
  // src/Combobox.tsx
1081
- import { Fragment as Fragment3, useEffect, useId as useId2, useMemo as useMemo3, useRef, useState as useState3 } from "react";
1090
+ import { Fragment as Fragment4, useEffect, useId as useId2, useMemo as useMemo3, useRef, useState as useState3 } from "react";
1082
1091
  import { Check as Check4, ChevronDown as ChevronDown3, Search, X as X3 } from "lucide-react";
1083
1092
 
1084
1093
  // src/Combobox.css
@@ -1302,7 +1311,7 @@ function Combobox({
1302
1311
  option.value
1303
1312
  );
1304
1313
  });
1305
- if (!run.group) return /* @__PURE__ */ jsx15(Fragment3, { children: rows }, `ungrouped-${runIndex}`);
1314
+ if (!run.group) return /* @__PURE__ */ jsx15(Fragment4, { children: rows }, `ungrouped-${runIndex}`);
1306
1315
  return /* @__PURE__ */ jsxs15("div", { role: "group", "aria-label": run.group, children: [
1307
1316
  /* @__PURE__ */ jsx15("p", { className: "cfi-ui-combobox__group-label", "aria-hidden": "true", children: run.group }),
1308
1317
  rows
@@ -1320,7 +1329,7 @@ function Combobox({
1320
1329
  // src/Tooltip.tsx
1321
1330
  import { forwardRef as forwardRef12 } from "react";
1322
1331
  import * as RadixTooltip from "@radix-ui/react-tooltip";
1323
- import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
1332
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
1324
1333
  var TooltipProvider = RadixTooltip.Provider;
1325
1334
  var TooltipRoot = RadixTooltip.Root;
1326
1335
  var TooltipTrigger = RadixTooltip.Trigger;
@@ -1342,7 +1351,7 @@ var TooltipContent = forwardRef12(function TooltipContent2({ className, children
1342
1351
  ) });
1343
1352
  });
1344
1353
  function Tooltip({ content, side = "top", align = "center", delay = 300, disabled, children }) {
1345
- if (disabled || content == null || content === "") return /* @__PURE__ */ jsx16(Fragment4, { children });
1354
+ if (disabled || content == null || content === "") return /* @__PURE__ */ jsx16(Fragment5, { children });
1346
1355
  return /* @__PURE__ */ jsx16(RadixTooltip.Provider, { delayDuration: delay, children: /* @__PURE__ */ jsxs16(TooltipRoot, { children: [
1347
1356
  /* @__PURE__ */ jsx16(TooltipTrigger, { asChild: true, children }),
1348
1357
  /* @__PURE__ */ jsx16(TooltipContent, { side, align, children: content })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campfire-interactive/ui",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Shared UI primitives for the Campfire Suite — Radix behavior, plain CSS styled entirely from the design tokens, no Tailwind required",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",