@epicbagel/saas-kit 1.27.0 → 1.29.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.
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import {
3
+ CountBadge
4
+ } from "./chunk-H7TETFLB.js";
5
+ import {
6
+ cn
7
+ } from "./chunk-G6SRSWXB.js";
8
+
9
+ // src/primitives/chip-tabs.tsx
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ function ChipTabs({
12
+ items,
13
+ value,
14
+ onChange,
15
+ size = "sm",
16
+ variant = "soft",
17
+ trailing,
18
+ className,
19
+ marginBottom = "none",
20
+ placement = "inline",
21
+ disabled = false,
22
+ guidedValue,
23
+ label,
24
+ labelledBy
25
+ }) {
26
+ const padClass = size === "xs" ? "px-1.5 py-0.5" : size === "md" ? "px-3.5 py-2" : "px-2 py-1";
27
+ const textClass = size === "xs" ? "text-[11px]" : size === "md" ? "text-sm" : "text-xs";
28
+ const gapClass = size === "md" ? "gap-2" : "gap-1";
29
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center", gapClass, marginBottom === "lg" && "mb-6", placement === "panel" && "border-b px-3 py-2", className), children: [
30
+ /* @__PURE__ */ jsx(
31
+ "div",
32
+ {
33
+ className: cn("flex min-w-0 flex-wrap", gapClass),
34
+ ...label || labelledBy ? { role: "group", "aria-label": labelledBy ? void 0 : label, "aria-labelledby": labelledBy } : {},
35
+ children: items.map((item) => {
36
+ const active = item.id === value;
37
+ return /* @__PURE__ */ jsxs(
38
+ "button",
39
+ {
40
+ type: "button",
41
+ "aria-pressed": active,
42
+ disabled,
43
+ onClick: () => onChange(item.id),
44
+ className: cn(
45
+ "rounded-md transition-colors inline-flex items-center gap-1 disabled:pointer-events-none disabled:opacity-60",
46
+ textClass,
47
+ padClass,
48
+ variant === "outline" && "border",
49
+ item.id === guidedValue && "ring-2 ring-destructive/60 ring-offset-2 ring-offset-background animate-pulse-once motion-reduce:animate-none",
50
+ active && variant === "soft" && "bg-accent text-accent-foreground font-medium",
51
+ !active && variant === "soft" && "text-muted-foreground hover:bg-accent/50",
52
+ active && variant === "outline" && "border-primary bg-primary/10 text-foreground font-medium",
53
+ !active && variant === "outline" && "border-muted-foreground/50 bg-background text-muted-foreground hover:border-foreground/60 hover:text-foreground"
54
+ ),
55
+ children: [
56
+ item.icon != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "contents", children: item.icon }),
57
+ item.label,
58
+ item.count != null && item.count > 0 && /* @__PURE__ */ jsx(CountBadge, { value: item.count, variant: "secondary" })
59
+ ]
60
+ },
61
+ item.id
62
+ );
63
+ })
64
+ }
65
+ ),
66
+ trailing && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1", children: trailing })
67
+ ] });
68
+ }
69
+
70
+ export {
71
+ ChipTabs
72
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
+
3
+
4
+ var _chunkVGTINL5Qcjs = require('./chunk-VGTINL5Q.cjs');
5
+
6
+
7
+ var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
8
+
9
+ // src/primitives/chip-tabs.tsx
10
+ var _jsxruntime = require('react/jsx-runtime');
11
+ function ChipTabs({
12
+ items,
13
+ value,
14
+ onChange,
15
+ size = "sm",
16
+ variant = "soft",
17
+ trailing,
18
+ className,
19
+ marginBottom = "none",
20
+ placement = "inline",
21
+ disabled = false,
22
+ guidedValue,
23
+ label,
24
+ labelledBy
25
+ }) {
26
+ const padClass = size === "xs" ? "px-1.5 py-0.5" : size === "md" ? "px-3.5 py-2" : "px-2 py-1";
27
+ const textClass = size === "xs" ? "text-[11px]" : size === "md" ? "text-sm" : "text-xs";
28
+ const gapClass = size === "md" ? "gap-2" : "gap-1";
29
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex items-center", gapClass, marginBottom === "lg" && "mb-6", placement === "panel" && "border-b px-3 py-2", className), children: [
30
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
31
+ "div",
32
+ {
33
+ className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex min-w-0 flex-wrap", gapClass),
34
+ ...label || labelledBy ? { role: "group", "aria-label": labelledBy ? void 0 : label, "aria-labelledby": labelledBy } : {},
35
+ children: items.map((item) => {
36
+ const active = item.id === value;
37
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
38
+ "button",
39
+ {
40
+ type: "button",
41
+ "aria-pressed": active,
42
+ disabled,
43
+ onClick: () => onChange(item.id),
44
+ className: _chunkKWLSBTB2cjs.cn.call(void 0,
45
+ "rounded-md transition-colors inline-flex items-center gap-1 disabled:pointer-events-none disabled:opacity-60",
46
+ textClass,
47
+ padClass,
48
+ variant === "outline" && "border",
49
+ item.id === guidedValue && "ring-2 ring-destructive/60 ring-offset-2 ring-offset-background animate-pulse-once motion-reduce:animate-none",
50
+ active && variant === "soft" && "bg-accent text-accent-foreground font-medium",
51
+ !active && variant === "soft" && "text-muted-foreground hover:bg-accent/50",
52
+ active && variant === "outline" && "border-primary bg-primary/10 text-foreground font-medium",
53
+ !active && variant === "outline" && "border-muted-foreground/50 bg-background text-muted-foreground hover:border-foreground/60 hover:text-foreground"
54
+ ),
55
+ children: [
56
+ item.icon != null && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "aria-hidden": "true", className: "contents", children: item.icon }),
57
+ item.label,
58
+ item.count != null && item.count > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVGTINL5Qcjs.CountBadge, { value: item.count, variant: "secondary" })
59
+ ]
60
+ },
61
+ item.id
62
+ );
63
+ })
64
+ }
65
+ ),
66
+ trailing && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "ml-auto flex items-center gap-1", children: trailing })
67
+ ] });
68
+ }
69
+
70
+
71
+
72
+ exports.ChipTabs = ChipTabs;
@@ -24,19 +24,21 @@ function PersonAvatar({
24
24
  size,
25
25
  className,
26
26
  fallbackClassName,
27
- fallbackSize = "default"
27
+ fallbackSize = "default",
28
+ decorative = false
28
29
  }) {
29
30
  return /* @__PURE__ */ jsxs(
30
31
  Avatar,
31
32
  {
32
33
  className: cn("shrink-0 overflow-hidden rounded-full bg-muted", className),
33
34
  style: size != null ? { width: size, height: size } : void 0,
35
+ ...decorative ? { "aria-hidden": true } : {},
34
36
  children: [
35
37
  photoUrl ? /* @__PURE__ */ jsx(
36
38
  AvatarImage,
37
39
  {
38
40
  src: photoUrl,
39
- alt: name,
41
+ alt: decorative ? "" : name,
40
42
  className: "h-full w-full object-cover"
41
43
  }
42
44
  ) : null,
@@ -24,19 +24,21 @@ function PersonAvatar({
24
24
  size,
25
25
  className,
26
26
  fallbackClassName,
27
- fallbackSize = "default"
27
+ fallbackSize = "default",
28
+ decorative = false
28
29
  }) {
29
30
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
30
31
  _chunkWABCLRBRcjs.Avatar,
31
32
  {
32
33
  className: _chunkKWLSBTB2cjs.cn.call(void 0, "shrink-0 overflow-hidden rounded-full bg-muted", className),
33
34
  style: size != null ? { width: size, height: size } : void 0,
35
+ ...decorative ? { "aria-hidden": true } : {},
34
36
  children: [
35
37
  photoUrl ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
36
38
  _chunkWABCLRBRcjs.AvatarImage,
37
39
  {
38
40
  src: photoUrl,
39
- alt: name,
41
+ alt: decorative ? "" : name,
40
42
  className: "h-full w-full object-cover"
41
43
  }
42
44
  ) : null,
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }"use client";
2
2
 
3
3
 
4
- var _chunkKGFL3VWYcjs = require('../chunk-KGFL3VWY.cjs');
4
+ var _chunkPPYHNJMVcjs = require('../chunk-PPYHNJMV.cjs');
5
5
 
6
6
 
7
7
  var _chunkPQUSJEXLcjs = require('../chunk-PQUSJEXL.cjs');
@@ -40,7 +40,7 @@ function ActivityFeedToolbar({
40
40
  }) {
41
41
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex flex-col gap-2 rounded-lg border bg-card p-2 lg:flex-row lg:items-center", className), children: [
42
42
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
43
- _chunkKGFL3VWYcjs.ChipTabs,
43
+ _chunkPPYHNJMVcjs.ChipTabs,
44
44
  {
45
45
  items: categories,
46
46
  value: category,
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  ChipTabs
4
- } from "../chunk-QD63LB2F.js";
4
+ } from "../chunk-MP64UGRF.js";
5
5
  import {
6
6
  Input
7
7
  } from "../chunk-QMA3WVB7.js";
@@ -20,7 +20,7 @@ var _chunkM4V7OJNFcjs = require('../chunk-M4V7OJNF.cjs');
20
20
  var _chunkVMXKT67Ecjs = require('../chunk-VMXKT67E.cjs');
21
21
 
22
22
 
23
- var _chunkU7XNNLVPcjs = require('../chunk-U7XNNLVP.cjs');
23
+ var _chunkWP7CKIPEcjs = require('../chunk-WP7CKIPE.cjs');
24
24
 
25
25
 
26
26
 
@@ -190,7 +190,7 @@ function authorLines(messages, markers) {
190
190
  });
191
191
  }
192
192
  function ChatAuthorLine({ author, align }) {
193
- const avatar = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkU7XNNLVPcjs.PersonAvatar, { name: author.name, photoUrl: author.avatarUrl, size: 20, fallbackClassName: "text-[9px]" });
193
+ const avatar = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWP7CKIPEcjs.PersonAvatar, { name: author.name, photoUrl: author.avatarUrl, size: 20, fallbackClassName: "text-[9px]" });
194
194
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { "data-slot": "chat-author", className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex items-center gap-2 text-xs", align === "end" ? "self-end" : "self-start"), children: [
195
195
  align === "start" ? avatar : null,
196
196
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "font-medium text-foreground", children: author.name }),
@@ -924,7 +924,7 @@ function MobileReplySuggestions({
924
924
  _chunk2BHI5ZWJcjs.DrawerContent,
925
925
  {
926
926
  "aria-describedby": void 0,
927
- className: "max-h-[min(85dvh,40rem)] overflow-hidden pb-[env(safe-area-inset-bottom)]",
927
+ className: "max-h-[min(85dvh,40rem)] overflow-hidden pb-[env(safe-area-inset-bottom)] md:mx-auto md:w-full md:max-w-xl",
928
928
  onOpenAutoFocus: (event) => {
929
929
  event.preventDefault();
930
930
  _optionalChain([closeRef, 'access', _11 => _11.current, 'optionalAccess', _12 => _12.focus, 'call', _13 => _13({ preventScroll: true })]);
@@ -999,6 +999,19 @@ function ExamplePromptsLoading({ label, className }) {
999
999
  function ChatError({ children, className }) {
1000
1000
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { "data-slot": "chat-error", role: "alert", className: _chunkKWLSBTB2cjs.cn.call(void 0, "mt-2 text-xs text-destructive", className), children });
1001
1001
  }
1002
+ var SHORT_VIEWPORT_HEIGHT = 900;
1003
+ function useIsShortViewport() {
1004
+ const [short, setShort] = _react.useState.call(void 0, false);
1005
+ _react.useEffect.call(void 0, () => {
1006
+ if (typeof window === "undefined") return;
1007
+ const mql = window.matchMedia(`(max-height: ${SHORT_VIEWPORT_HEIGHT - 1}px)`);
1008
+ const onChange = () => setShort(window.innerHeight < SHORT_VIEWPORT_HEIGHT);
1009
+ mql.addEventListener("change", onChange);
1010
+ onChange();
1011
+ return () => mql.removeEventListener("change", onChange);
1012
+ }, []);
1013
+ return short;
1014
+ }
1002
1015
  var Chat = _react.forwardRef.call(void 0, function Chat2({
1003
1016
  messages,
1004
1017
  onSend,
@@ -1127,7 +1140,8 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
1127
1140
  );
1128
1141
  const application = layout === "application";
1129
1142
  const workspace = layout === "workspace";
1130
- const compactSuggestions = mobileCompactSuggestions && isMobile;
1143
+ const shortViewport = useIsShortViewport();
1144
+ const compactSuggestions = mobileCompactSuggestions && (isMobile || shortViewport);
1131
1145
  const showEmptyFileDropzone = emptyFileDropzone && Boolean(onFilesSelected) && attachments.length === 0;
1132
1146
  const liveVoiceActive = voiceSession && voiceSession.state !== "disconnected" && voiceSession.state !== "error";
1133
1147
  const showEmptyPending = !conversationStarted && pending && Boolean(emptyPendingContent) && !liveVoiceActive;
@@ -402,7 +402,10 @@ interface ChatProps {
402
402
  replySuggestions?: ReadonlyArray<string>;
403
403
  /** The reply suggestions' accessible name. Defaults to "Suggested replies". */
404
404
  replySuggestionsLabel?: string;
405
- /** Below 768px, show replies inside the composer as the first reply and a bottom-sheet picker. Desktop chips are unchanged. */
405
+ /**
406
+ * Below 768px wide, or 900px tall, show replies inside the composer as the
407
+ * first reply and a bottom-sheet picker. Taller desktop windows keep chips.
408
+ */
406
409
  mobileCompactSuggestions?: boolean;
407
410
  /** The mobile picker's opener, given the number of additional replies. Defaults to `+N more`. */
408
411
  replySuggestionsMoreLabel?: (count: number) => string;
@@ -402,7 +402,10 @@ interface ChatProps {
402
402
  replySuggestions?: ReadonlyArray<string>;
403
403
  /** The reply suggestions' accessible name. Defaults to "Suggested replies". */
404
404
  replySuggestionsLabel?: string;
405
- /** Below 768px, show replies inside the composer as the first reply and a bottom-sheet picker. Desktop chips are unchanged. */
405
+ /**
406
+ * Below 768px wide, or 900px tall, show replies inside the composer as the
407
+ * first reply and a bottom-sheet picker. Taller desktop windows keep chips.
408
+ */
406
409
  mobileCompactSuggestions?: boolean;
407
410
  /** The mobile picker's opener, given the number of additional replies. Defaults to `+N more`. */
408
411
  replySuggestionsMoreLabel?: (count: number) => string;
@@ -20,7 +20,7 @@ import {
20
20
  } from "../chunk-QZBPZLAB.js";
21
21
  import {
22
22
  PersonAvatar
23
- } from "../chunk-NXFZULS3.js";
23
+ } from "../chunk-T5AFBLP2.js";
24
24
  import {
25
25
  Marker,
26
26
  MarkerContent
@@ -924,7 +924,7 @@ function MobileReplySuggestions({
924
924
  DrawerContent,
925
925
  {
926
926
  "aria-describedby": void 0,
927
- className: "max-h-[min(85dvh,40rem)] overflow-hidden pb-[env(safe-area-inset-bottom)]",
927
+ className: "max-h-[min(85dvh,40rem)] overflow-hidden pb-[env(safe-area-inset-bottom)] md:mx-auto md:w-full md:max-w-xl",
928
928
  onOpenAutoFocus: (event) => {
929
929
  event.preventDefault();
930
930
  closeRef.current?.focus({ preventScroll: true });
@@ -999,6 +999,19 @@ function ExamplePromptsLoading({ label, className }) {
999
999
  function ChatError({ children, className }) {
1000
1000
  return /* @__PURE__ */ jsx("p", { "data-slot": "chat-error", role: "alert", className: cn("mt-2 text-xs text-destructive", className), children });
1001
1001
  }
1002
+ var SHORT_VIEWPORT_HEIGHT = 900;
1003
+ function useIsShortViewport() {
1004
+ const [short, setShort] = useState(false);
1005
+ useEffect(() => {
1006
+ if (typeof window === "undefined") return;
1007
+ const mql = window.matchMedia(`(max-height: ${SHORT_VIEWPORT_HEIGHT - 1}px)`);
1008
+ const onChange = () => setShort(window.innerHeight < SHORT_VIEWPORT_HEIGHT);
1009
+ mql.addEventListener("change", onChange);
1010
+ onChange();
1011
+ return () => mql.removeEventListener("change", onChange);
1012
+ }, []);
1013
+ return short;
1014
+ }
1002
1015
  var Chat = forwardRef(function Chat2({
1003
1016
  messages,
1004
1017
  onSend,
@@ -1127,7 +1140,8 @@ var Chat = forwardRef(function Chat2({
1127
1140
  );
1128
1141
  const application = layout === "application";
1129
1142
  const workspace = layout === "workspace";
1130
- const compactSuggestions = mobileCompactSuggestions && isMobile;
1143
+ const shortViewport = useIsShortViewport();
1144
+ const compactSuggestions = mobileCompactSuggestions && (isMobile || shortViewport);
1131
1145
  const showEmptyFileDropzone = emptyFileDropzone && Boolean(onFilesSelected) && attachments.length === 0;
1132
1146
  const liveVoiceActive = voiceSession && voiceSession.state !== "disconnected" && voiceSession.state !== "error";
1133
1147
  const showEmptyPending = !conversationStarted && pending && Boolean(emptyPendingContent) && !liveVoiceActive;
@@ -1,10 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
2
 
3
3
 
4
- var _chunkKGFL3VWYcjs = require('../chunk-KGFL3VWY.cjs');
4
+ var _chunkPPYHNJMVcjs = require('../chunk-PPYHNJMV.cjs');
5
5
  require('../chunk-VGTINL5Q.cjs');
6
6
  require('../chunk-I6GUOEBO.cjs');
7
7
  require('../chunk-KWLSBTB2.cjs');
8
8
 
9
9
 
10
- exports.ChipTabs = _chunkKGFL3VWYcjs.ChipTabs;
10
+ exports.ChipTabs = _chunkPPYHNJMVcjs.ChipTabs;
@@ -6,7 +6,7 @@ interface ChipTabItem {
6
6
  label: ReactNode;
7
7
  /** Optional count rendered after the label in a neutral count badge. */
8
8
  count?: number;
9
- /** Optional leading icon. */
9
+ /** Optional leading icon. Decorative: hidden from screen readers, since the label names the chip. */
10
10
  icon?: ReactNode;
11
11
  }
12
12
  interface ChipTabsProps {
@@ -27,13 +27,24 @@ interface ChipTabsProps {
27
27
  disabled?: boolean;
28
28
  /** Visually guides attention to one option without selecting it. */
29
29
  guidedValue?: string;
30
+ /**
31
+ * The row's accessible name ("3 people shared from this search"): the
32
+ * chips become a labelled group. Or point at a visible caption with
33
+ * `labelledBy`.
34
+ */
35
+ label?: string;
36
+ /** The id of an element whose text names the row, in place of `label`. */
37
+ labelledBy?: string;
30
38
  }
31
39
  /**
32
40
  * Pill-style tab strip — toggle row of chips with active/inactive states
33
41
  * and an optional trailing actions slot. Distinct from `Tabs` (which
34
42
  * uses an underlined trigger row for page-level navigation). Use this
35
43
  * for compact filter / view selectors inside popovers, sidebars, etc.
44
+ *
45
+ * Each chip is a toggle button (`aria-pressed`), so a screen reader says
46
+ * which one is selected; `label` or `labelledBy` names the row as a group.
36
47
  */
37
- declare function ChipTabs({ items, value, onChange, size, variant, trailing, className, marginBottom, placement, disabled, guidedValue, }: ChipTabsProps): react_jsx_runtime.JSX.Element;
48
+ declare function ChipTabs({ items, value, onChange, size, variant, trailing, className, marginBottom, placement, disabled, guidedValue, label, labelledBy, }: ChipTabsProps): react_jsx_runtime.JSX.Element;
38
49
 
39
50
  export { type ChipTabItem, ChipTabs, type ChipTabsProps };
@@ -6,7 +6,7 @@ interface ChipTabItem {
6
6
  label: ReactNode;
7
7
  /** Optional count rendered after the label in a neutral count badge. */
8
8
  count?: number;
9
- /** Optional leading icon. */
9
+ /** Optional leading icon. Decorative: hidden from screen readers, since the label names the chip. */
10
10
  icon?: ReactNode;
11
11
  }
12
12
  interface ChipTabsProps {
@@ -27,13 +27,24 @@ interface ChipTabsProps {
27
27
  disabled?: boolean;
28
28
  /** Visually guides attention to one option without selecting it. */
29
29
  guidedValue?: string;
30
+ /**
31
+ * The row's accessible name ("3 people shared from this search"): the
32
+ * chips become a labelled group. Or point at a visible caption with
33
+ * `labelledBy`.
34
+ */
35
+ label?: string;
36
+ /** The id of an element whose text names the row, in place of `label`. */
37
+ labelledBy?: string;
30
38
  }
31
39
  /**
32
40
  * Pill-style tab strip — toggle row of chips with active/inactive states
33
41
  * and an optional trailing actions slot. Distinct from `Tabs` (which
34
42
  * uses an underlined trigger row for page-level navigation). Use this
35
43
  * for compact filter / view selectors inside popovers, sidebars, etc.
44
+ *
45
+ * Each chip is a toggle button (`aria-pressed`), so a screen reader says
46
+ * which one is selected; `label` or `labelledBy` names the row as a group.
36
47
  */
37
- declare function ChipTabs({ items, value, onChange, size, variant, trailing, className, marginBottom, placement, disabled, guidedValue, }: ChipTabsProps): react_jsx_runtime.JSX.Element;
48
+ declare function ChipTabs({ items, value, onChange, size, variant, trailing, className, marginBottom, placement, disabled, guidedValue, label, labelledBy, }: ChipTabsProps): react_jsx_runtime.JSX.Element;
38
49
 
39
50
  export { type ChipTabItem, ChipTabs, type ChipTabsProps };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  ChipTabs
4
- } from "../chunk-QD63LB2F.js";
4
+ } from "../chunk-MP64UGRF.js";
5
5
  import "../chunk-H7TETFLB.js";
6
6
  import "../chunk-D4ZL2OEQ.js";
7
7
  import "../chunk-G6SRSWXB.js";
@@ -2,10 +2,10 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkU7XNNLVPcjs = require('../chunk-U7XNNLVP.cjs');
5
+ var _chunkWP7CKIPEcjs = require('../chunk-WP7CKIPE.cjs');
6
6
  require('../chunk-WABCLRBR.cjs');
7
7
  require('../chunk-KWLSBTB2.cjs');
8
8
 
9
9
 
10
10
 
11
- exports.PersonAvatar = _chunkU7XNNLVPcjs.PersonAvatar; exports.initials = _chunkU7XNNLVPcjs.initials;
11
+ exports.PersonAvatar = _chunkWP7CKIPEcjs.PersonAvatar; exports.initials = _chunkWP7CKIPEcjs.initials;
@@ -20,6 +20,11 @@ interface PersonAvatarProps {
20
20
  /** Class for the initials fallback — e.g. a smaller type scale. */
21
21
  fallbackClassName?: string;
22
22
  fallbackSize?: "default" | "xl";
23
+ /**
24
+ * Beside the person's name already (a chip, a list row): hidden from
25
+ * screen readers, so the name isn't read twice or as initials.
26
+ */
27
+ decorative?: boolean;
23
28
  }
24
29
  /**
25
30
  * A person's avatar: their photo if we have one, their initials if we don't.
@@ -28,6 +33,6 @@ interface PersonAvatarProps {
28
33
  * coloured tile masks a missing photo by pretending to be one, while plain
29
34
  * initials make a missing photo obvious.
30
35
  */
31
- declare function PersonAvatar({ name, photoUrl, size, className, fallbackClassName, fallbackSize, }: PersonAvatarProps): react_jsx_runtime.JSX.Element;
36
+ declare function PersonAvatar({ name, photoUrl, size, className, fallbackClassName, fallbackSize, decorative, }: PersonAvatarProps): react_jsx_runtime.JSX.Element;
32
37
 
33
38
  export { PersonAvatar, type PersonAvatarProps, initials };
@@ -20,6 +20,11 @@ interface PersonAvatarProps {
20
20
  /** Class for the initials fallback — e.g. a smaller type scale. */
21
21
  fallbackClassName?: string;
22
22
  fallbackSize?: "default" | "xl";
23
+ /**
24
+ * Beside the person's name already (a chip, a list row): hidden from
25
+ * screen readers, so the name isn't read twice or as initials.
26
+ */
27
+ decorative?: boolean;
23
28
  }
24
29
  /**
25
30
  * A person's avatar: their photo if we have one, their initials if we don't.
@@ -28,6 +33,6 @@ interface PersonAvatarProps {
28
33
  * coloured tile masks a missing photo by pretending to be one, while plain
29
34
  * initials make a missing photo obvious.
30
35
  */
31
- declare function PersonAvatar({ name, photoUrl, size, className, fallbackClassName, fallbackSize, }: PersonAvatarProps): react_jsx_runtime.JSX.Element;
36
+ declare function PersonAvatar({ name, photoUrl, size, className, fallbackClassName, fallbackSize, decorative, }: PersonAvatarProps): react_jsx_runtime.JSX.Element;
32
37
 
33
38
  export { PersonAvatar, type PersonAvatarProps, initials };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PersonAvatar,
4
4
  initials
5
- } from "../chunk-NXFZULS3.js";
5
+ } from "../chunk-T5AFBLP2.js";
6
6
  import "../chunk-3OWGL4Q2.js";
7
7
  import "../chunk-G6SRSWXB.js";
8
8
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicbagel/saas-kit",
3
- "version": "1.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "Accessible React primitives, layout components and design tokens for SaaS apps, built on Radix UI and Tailwind CSS v4.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -1,62 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
-
3
-
4
- var _chunkVGTINL5Qcjs = require('./chunk-VGTINL5Q.cjs');
5
-
6
-
7
- var _chunkKWLSBTB2cjs = require('./chunk-KWLSBTB2.cjs');
8
-
9
- // src/primitives/chip-tabs.tsx
10
- var _jsxruntime = require('react/jsx-runtime');
11
- function ChipTabs({
12
- items,
13
- value,
14
- onChange,
15
- size = "sm",
16
- variant = "soft",
17
- trailing,
18
- className,
19
- marginBottom = "none",
20
- placement = "inline",
21
- disabled = false,
22
- guidedValue
23
- }) {
24
- const padClass = size === "xs" ? "px-1.5 py-0.5" : size === "md" ? "px-3.5 py-2" : "px-2 py-1";
25
- const textClass = size === "xs" ? "text-[11px]" : size === "md" ? "text-sm" : "text-xs";
26
- const gapClass = size === "md" ? "gap-2" : "gap-1";
27
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex items-center", gapClass, marginBottom === "lg" && "mb-6", placement === "panel" && "border-b px-3 py-2", className), children: [
28
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex min-w-0 flex-wrap", gapClass), children: items.map((item) => {
29
- const active = item.id === value;
30
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
31
- "button",
32
- {
33
- type: "button",
34
- disabled,
35
- onClick: () => onChange(item.id),
36
- className: _chunkKWLSBTB2cjs.cn.call(void 0,
37
- "rounded-md transition-colors inline-flex items-center gap-1 disabled:pointer-events-none disabled:opacity-60",
38
- textClass,
39
- padClass,
40
- variant === "outline" && "border",
41
- item.id === guidedValue && "ring-2 ring-destructive/60 ring-offset-2 ring-offset-background animate-pulse-once motion-reduce:animate-none",
42
- active && variant === "soft" && "bg-accent text-accent-foreground font-medium",
43
- !active && variant === "soft" && "text-muted-foreground hover:bg-accent/50",
44
- active && variant === "outline" && "border-primary bg-primary/10 text-foreground font-medium",
45
- !active && variant === "outline" && "border-muted-foreground/50 bg-background text-muted-foreground hover:border-foreground/60 hover:text-foreground"
46
- ),
47
- children: [
48
- item.icon,
49
- item.label,
50
- item.count != null && item.count > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVGTINL5Qcjs.CountBadge, { value: item.count, variant: "secondary" })
51
- ]
52
- },
53
- item.id
54
- );
55
- }) }),
56
- trailing && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "ml-auto flex items-center gap-1", children: trailing })
57
- ] });
58
- }
59
-
60
-
61
-
62
- exports.ChipTabs = ChipTabs;
@@ -1,62 +0,0 @@
1
- "use client";
2
- import {
3
- CountBadge
4
- } from "./chunk-H7TETFLB.js";
5
- import {
6
- cn
7
- } from "./chunk-G6SRSWXB.js";
8
-
9
- // src/primitives/chip-tabs.tsx
10
- import { jsx, jsxs } from "react/jsx-runtime";
11
- function ChipTabs({
12
- items,
13
- value,
14
- onChange,
15
- size = "sm",
16
- variant = "soft",
17
- trailing,
18
- className,
19
- marginBottom = "none",
20
- placement = "inline",
21
- disabled = false,
22
- guidedValue
23
- }) {
24
- const padClass = size === "xs" ? "px-1.5 py-0.5" : size === "md" ? "px-3.5 py-2" : "px-2 py-1";
25
- const textClass = size === "xs" ? "text-[11px]" : size === "md" ? "text-sm" : "text-xs";
26
- const gapClass = size === "md" ? "gap-2" : "gap-1";
27
- return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center", gapClass, marginBottom === "lg" && "mb-6", placement === "panel" && "border-b px-3 py-2", className), children: [
28
- /* @__PURE__ */ jsx("div", { className: cn("flex min-w-0 flex-wrap", gapClass), children: items.map((item) => {
29
- const active = item.id === value;
30
- return /* @__PURE__ */ jsxs(
31
- "button",
32
- {
33
- type: "button",
34
- disabled,
35
- onClick: () => onChange(item.id),
36
- className: cn(
37
- "rounded-md transition-colors inline-flex items-center gap-1 disabled:pointer-events-none disabled:opacity-60",
38
- textClass,
39
- padClass,
40
- variant === "outline" && "border",
41
- item.id === guidedValue && "ring-2 ring-destructive/60 ring-offset-2 ring-offset-background animate-pulse-once motion-reduce:animate-none",
42
- active && variant === "soft" && "bg-accent text-accent-foreground font-medium",
43
- !active && variant === "soft" && "text-muted-foreground hover:bg-accent/50",
44
- active && variant === "outline" && "border-primary bg-primary/10 text-foreground font-medium",
45
- !active && variant === "outline" && "border-muted-foreground/50 bg-background text-muted-foreground hover:border-foreground/60 hover:text-foreground"
46
- ),
47
- children: [
48
- item.icon,
49
- item.label,
50
- item.count != null && item.count > 0 && /* @__PURE__ */ jsx(CountBadge, { value: item.count, variant: "secondary" })
51
- ]
52
- },
53
- item.id
54
- );
55
- }) }),
56
- trailing && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1", children: trailing })
57
- ] });
58
- }
59
-
60
- export {
61
- ChipTabs
62
- };