@flikk/ui 1.0.0-beta.12 → 1.0.0-beta.14

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 (118) hide show
  1. package/dist/components/ai/PromptInput/PromptInput.js +1 -1
  2. package/dist/components/ai/PromptInput/VoiceRecorder.js +1 -1
  3. package/dist/components/ai/PromptSuggestions/PromptSuggestion.theme.js +1 -1
  4. package/dist/components/ai/StreamingResponse/StreamingResponse.types.d.ts +7 -7
  5. package/dist/components/charts/FunnelChart/FunnelChart.js +2 -2
  6. package/dist/components/core/Accordion/Accordion.types.d.ts +1 -7
  7. package/dist/components/core/Accordion/AccordionTrigger.js +4 -6
  8. package/dist/components/core/Alert/Alert.types.d.ts +1 -1
  9. package/dist/components/core/Avatar/Avatar.js +22 -18
  10. package/dist/components/core/Avatar/Avatar.theme.js +1 -1
  11. package/dist/components/core/Avatar/Avatar.types.d.ts +9 -8
  12. package/dist/components/core/AvatarGroup/AvatarGroup.js +2 -2
  13. package/dist/components/core/Badge/Badge.js +10 -8
  14. package/dist/components/core/Badge/Badge.theme.js +14 -7
  15. package/dist/components/core/Badge/Badge.types.d.ts +3 -3
  16. package/dist/components/core/Button/Button.ripple.js +1 -1
  17. package/dist/components/core/Button/Button.theme.d.ts +1 -1
  18. package/dist/components/core/Button/Button.theme.js +5 -5
  19. package/dist/components/core/Button/Button.types.d.ts +1 -1
  20. package/dist/components/core/Calendar/CalendarEvent/CalendarEvent.types.d.ts +5 -6
  21. package/dist/components/core/Calendar/CalendarMini/CalendarMini.js +2 -2
  22. package/dist/components/core/Drawer/DrawerHeader.js +1 -1
  23. package/dist/components/core/FileTypeIcon/FileTypeIcon.d.ts +3 -0
  24. package/dist/components/core/FileTypeIcon/FileTypeIcon.js +33 -0
  25. package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.d.ts +2 -0
  26. package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.js +12 -0
  27. package/dist/components/core/FileTypeIcon/FileTypeIcon.types.d.ts +19 -0
  28. package/dist/components/core/FileTypeIcon/fileTypes.d.ts +7 -0
  29. package/dist/components/core/FileTypeIcon/fileTypes.js +30 -0
  30. package/dist/components/core/FileTypeIcon/index.d.ts +2 -0
  31. package/dist/components/core/Link/Link.js +1 -1
  32. package/dist/components/core/MenuItem/MenuItem.theme.js +1 -1
  33. package/dist/components/core/Message/Message.js +1 -1
  34. package/dist/components/core/Message/Message.theme.js +2 -2
  35. package/dist/components/core/Message/Message.types.d.ts +6 -6
  36. package/dist/components/core/Message/MessageAvatar.d.ts +2 -2
  37. package/dist/components/core/Message/MessageAvatar.js +3 -3
  38. package/dist/components/core/Modal/ModalHeader.js +1 -1
  39. package/dist/components/core/NavItem/NavItem.js +100 -52
  40. package/dist/components/core/NavItem/NavItem.theme.js +6 -3
  41. package/dist/components/core/NavItem/NavItem.types.d.ts +44 -30
  42. package/dist/components/core/NavItem/index.d.ts +1 -1
  43. package/dist/components/core/Pill/Pill.theme.js +1 -1
  44. package/dist/components/core/Popover/PopoverBody.js +19 -8
  45. package/dist/components/core/Sidebar/Sidebar.d.ts +11 -1
  46. package/dist/components/core/Sidebar/Sidebar.js +49 -16
  47. package/dist/components/core/Sidebar/Sidebar.theme.js +9 -7
  48. package/dist/components/core/Sidebar/Sidebar.types.d.ts +57 -0
  49. package/dist/components/core/Sidebar/SidebarContent.js +3 -1
  50. package/dist/components/core/Sidebar/SidebarContext.d.ts +14 -0
  51. package/dist/components/core/Sidebar/SidebarContext.js +63 -11
  52. package/dist/components/core/Sidebar/SidebarFooter.js +3 -1
  53. package/dist/components/core/Sidebar/SidebarHeader.js +6 -2
  54. package/dist/components/core/Sidebar/SidebarMobileTrigger.d.ts +3 -0
  55. package/dist/components/core/Sidebar/SidebarMobileTrigger.js +28 -0
  56. package/dist/components/core/Sidebar/SidebarNav.js +3 -2
  57. package/dist/components/core/Sidebar/SidebarNavGroup.js +4 -2
  58. package/dist/components/core/Sidebar/SidebarToggle.js +1 -1
  59. package/dist/components/core/Sidebar/SidebarUserProfile.d.ts +3 -0
  60. package/dist/components/core/Sidebar/SidebarUserProfile.js +35 -0
  61. package/dist/components/core/Sidebar/index.d.ts +3 -1
  62. package/dist/components/core/Tabs/Tabs.theme.d.ts +1 -1
  63. package/dist/components/core/Tabs/Tabs.theme.js +3 -3
  64. package/dist/components/core/Tag/Tag.d.ts +1 -1
  65. package/dist/components/core/Tag/Tag.js +2 -2
  66. package/dist/components/core/Tag/Tag.types.d.ts +1 -1
  67. package/dist/components/core/index.d.ts +1 -1
  68. package/dist/components/core/index.js +3 -1
  69. package/dist/components/data-display/Feed/Feed.animations.d.ts +40 -0
  70. package/dist/components/data-display/Feed/Feed.animations.js +26 -0
  71. package/dist/components/data-display/Feed/Feed.d.ts +5 -1
  72. package/dist/components/data-display/Feed/Feed.js +87 -60
  73. package/dist/components/data-display/Feed/Feed.theme.js +11 -13
  74. package/dist/components/data-display/Feed/Feed.types.d.ts +35 -34
  75. package/dist/components/data-display/Feed/index.d.ts +1 -1
  76. package/dist/components/data-display/GanttChart/GanttTaskBar.js +11 -9
  77. package/dist/components/data-display/GanttChart/GanttToolbar.js +1 -1
  78. package/dist/components/data-display/Timeline/TimelineMarker.js +2 -2
  79. package/dist/components/effects/3d/index.d.ts +1 -1
  80. package/dist/components/effects/AIOrb/AIOrb.d.ts +1 -0
  81. package/dist/components/effects/AIOrb/AIOrb.js +3 -2
  82. package/dist/components/effects/AIOrb/AIOrb.types.d.ts +1 -0
  83. package/dist/components/effects/AIOrb/hooks/index.d.ts +2 -0
  84. package/dist/components/effects/AIOrb/hooks/useOrbState.d.ts +20 -0
  85. package/dist/components/effects/AIOrb/hooks/useOrbState.js +27 -0
  86. package/dist/components/effects/AIOrb/index.d.ts +2 -2
  87. package/dist/components/effects/AIOrb/variants/Aura/Aura.d.ts +3 -0
  88. package/dist/components/effects/AIOrb/variants/Aura/Aura.js +75 -0
  89. package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.d.ts +28 -0
  90. package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.js +191 -0
  91. package/dist/components/effects/AIOrb/variants/Aura/Aura.types.d.ts +53 -0
  92. package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.d.ts +17 -0
  93. package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.js +149 -0
  94. package/dist/components/effects/AIOrb/variants/Aura/index.d.ts +2 -0
  95. package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.js +38 -7
  96. package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.types.d.ts +6 -0
  97. package/dist/components/effects/AIOrb/variants/Liquid/Liquid.js +34 -8
  98. package/dist/components/effects/AIOrb/variants/Liquid/Liquid.types.d.ts +6 -0
  99. package/dist/components/effects/AIOrb/variants/index.d.ts +2 -0
  100. package/dist/components/effects/CustomCursor/CustomCursor.d.ts +2 -2
  101. package/dist/components/effects/CustomCursor/CustomCursor.js +50 -47
  102. package/dist/components/effects/GlassSurface/GlassSurface.js +24 -12
  103. package/dist/components/effects/GlassSurface/GlassSurface.types.d.ts +8 -0
  104. package/dist/components/forms/Mention/Mention.js +2 -2
  105. package/dist/components/forms/RichTextEditor/RichTextEditor.js +10 -10
  106. package/dist/index.js +3 -1
  107. package/dist/styles.css +1 -1
  108. package/dist/utils/index.d.ts +1 -0
  109. package/dist/utils/paletteGenerator.d.ts +46 -0
  110. package/package.json +1 -1
  111. package/src/global.scss +1 -0
  112. package/src/styles/theme.css +50 -54
  113. package/dist/components/core/Loader/Loader.d.ts +0 -24
  114. package/dist/components/core/Loader/Loader.js +0 -57
  115. package/dist/components/core/Loader/Loader.theme.d.ts +0 -27
  116. package/dist/components/core/Loader/Loader.theme.js +0 -60
  117. package/dist/components/core/Loader/Loader.types.d.ts +0 -71
  118. package/dist/components/core/Loader/index.d.ts +0 -2
@@ -512,7 +512,7 @@ className, textareaClassName, footerClassName, theme = {},
512
512
  return (jsx(Button, { iconOnly: true, className: cn(mergedTheme.submitButtonStyle, extraClassName), size: buttonSize, variant: buttonVariant, color: buttonColor, onClick: handleSubmit, disabled: !canSubmit, state: isLoading ? "loading" : "default", "aria-label": submitButtonLabel, title: submitShortcut ? `Submit (${submitShortcut})` : undefined, showReflection: buttonVariant === "filled" ? false : undefined, children: jsx(AnimatePresence, { mode: "wait", initial: false, children: jsx(motion.span, { variants: iconSwapVariants, initial: "initial", animate: "animate", exit: "exit", className: "flex items-center justify-center", children: submitButtonIcon }, "submit") }) }));
513
513
  };
514
514
  // ========== Render ==========
515
- return (jsxs("div", { className: cn("min-w-xl mx-auto", className), ...props, children: [jsx("input", { ref: fileInputRef, type: "file", accept: acceptedFileTypes.join(","), multiple: true, onChange: (e) => {
515
+ return (jsxs("div", { className: cn("w-full mx-auto", className), ...props, children: [jsx("input", { ref: fileInputRef, type: "file", accept: acceptedFileTypes.join(","), multiple: true, onChange: (e) => {
516
516
  if (e.target.files)
517
517
  processFiles(e.target.files);
518
518
  e.target.value = "";
@@ -170,7 +170,7 @@ const VoiceRecorder = ({ onComplete, onCancel, maxDuration, className, }) => {
170
170
  setRecordingState("recording");
171
171
  }
172
172
  }, [recordingState, clearTimer, startTimer]);
173
- return (jsxs("div", { className: cn("flex items-center justify-between w-full", className), children: [jsx(Button, { variant: "ghost", color: "neutral", size: "sm", className: "rounded-full", onClick: handleCancel, "aria-label": "Cancel recording", children: "Cancel" }), jsxs("div", { className: "flex items-center gap-2.5", children: [jsx(motion.div, { className: "size-2.5 rounded-full bg-[var(--color-danger)]", animate: recordingState === "recording"
173
+ return (jsxs("div", { className: cn("flex items-center justify-between w-full", className), children: [jsx(Button, { variant: "soft", color: "neutral", size: "sm", className: "rounded-full", onClick: handleCancel, "aria-label": "Cancel recording", children: "Cancel" }), jsxs("div", { className: "flex items-center gap-2.5", children: [jsx(motion.div, { className: "size-2.5 rounded-full bg-[var(--color-danger)]", animate: recordingState === "recording"
174
174
  ? { opacity: [1, 0.3, 1] }
175
175
  : { opacity: 0.5 }, transition: recordingState === "recording"
176
176
  ? { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
@@ -1,5 +1,5 @@
1
1
  const promptSuggestionTheme = {
2
- baseStyle: "inline-flex items-center gap-2 py-2 px-3.5 rounded-full " +
2
+ baseStyle: "inline-flex items-center gap-2 py-1 px-3 rounded-full " +
3
3
  "bg-white dark:bg-[var(--color-background)] " +
4
4
  "border border-[var(--color-border)] " +
5
5
  "text-base font-medium text-[var(--color-text-primary)] " +
@@ -2,16 +2,16 @@
2
2
  * StreamingResponse Component Types
3
3
  * Type definitions for the StreamingResponse component
4
4
  */
5
- import type { ReactNode } from 'react';
6
- import type { SSEFormat, SSEParser } from '../../../utils/sseFormatParsers';
7
- import type { StreamingError, RetryConfig } from '../../../utils';
8
- import type { MessageRole } from '../../core/Message';
5
+ import type { ReactNode } from "react";
6
+ import type { SSEFormat, SSEParser } from "../../../utils/sseFormatParsers";
7
+ import type { StreamingError, RetryConfig } from "../../../utils";
8
+ import type { MessageRole } from "../../core/Message";
9
9
  /**
10
10
  * Avatar configuration for Message component
11
11
  */
12
12
  export interface MessageAvatarProps {
13
13
  src?: string;
14
- alt: string;
14
+ name: string;
15
15
  fallback?: ReactNode;
16
16
  }
17
17
  /**
@@ -21,7 +21,7 @@ export interface MarkdownOptions {
21
21
  /** Allow HTML in markdown (default: false) */
22
22
  allowHtml?: boolean;
23
23
  /** Syntax highlighting theme (default: 'light') */
24
- syntaxTheme?: 'light' | 'dark';
24
+ syntaxTheme?: "light" | "dark";
25
25
  /** Custom code block renderer */
26
26
  renderCodeBlock?: (code: string, language: string) => ReactNode;
27
27
  }
@@ -32,7 +32,7 @@ export interface StreamingResponseProps {
32
32
  /** The SSE endpoint URL */
33
33
  url: string;
34
34
  /** HTTP method (default: 'POST') */
35
- method?: 'GET' | 'POST';
35
+ method?: "GET" | "POST";
36
36
  /** Request headers */
37
37
  headers?: Record<string, string>;
38
38
  /** Request body (for POST requests) */
@@ -430,7 +430,7 @@ const FunnelChart = ({ data, height = 400, width = 600, showPercentages = true,
430
430
  animate: "visible",
431
431
  custom: index,
432
432
  }
433
- : {}), children: jsxs(Badge, { color: isGoodConversion ? "success" : "warning", variant: "ghost", size: "sm", pill: true, children: ["\u2193 ", stage.conversionRate.toFixed(1), "%"] }) }, `conversion-${stage.name}`));
433
+ : {}), children: jsxs(Badge, { color: isGoodConversion ? "success" : "warning", variant: "soft", size: "sm", pill: true, children: ["\u2193 ", stage.conversionRate.toFixed(1), "%"] }) }, `conversion-${stage.name}`));
434
434
  }), orientation === "vertical" &&
435
435
  showConversionRate &&
436
436
  stages.map((stage, index) => {
@@ -448,7 +448,7 @@ const FunnelChart = ({ data, height = 400, width = 600, showPercentages = true,
448
448
  animate: "visible",
449
449
  custom: index,
450
450
  }
451
- : {}), children: jsxs(Badge, { color: isGoodConversion ? "success" : "warning", variant: "ghost", size: "sm", pill: true, children: [stage.conversionRate.toFixed(0), "%"] }) }, `conversion-${stage.name}`));
451
+ : {}), children: jsxs(Badge, { color: isGoodConversion ? "success" : "warning", variant: "soft", size: "sm", pill: true, children: [stage.conversionRate.toFixed(0), "%"] }) }, `conversion-${stage.name}`));
452
452
  }), showSummary && summaryPosition !== "none" && stages.length > 1 && (jsxs(motion.div, { className: cn("absolute bg-[var(--color-background)] rounded-lg shadow-sm border border-[var(--color-border)] px-3 py-2 pointer-events-none", getSummaryPositionClasses(summaryPosition)), ...(animate
453
453
  ? {
454
454
  variants: summaryVariants,
@@ -92,7 +92,7 @@ export interface AccordionTriggerProps extends React.HTMLAttributes<HTMLButtonEl
92
92
  className?: string;
93
93
  /**
94
94
  * Override the default indicator (chevron) icon.
95
- * Set to `null` to hide it entirely — equivalent to `showChevron={false}`.
95
+ * Set to `null` to hide it entirely.
96
96
  */
97
97
  chevron?: React.ReactNode | null;
98
98
  /**
@@ -108,12 +108,6 @@ export interface AccordionTriggerProps extends React.HTMLAttributes<HTMLButtonEl
108
108
  * @default "right"
109
109
  */
110
110
  chevronPosition?: "left" | "right";
111
- /**
112
- * Set to `false` to hide the chevron icon entirely.
113
- * Equivalent to passing `chevron={null}`.
114
- * @default true
115
- */
116
- showChevron?: boolean;
117
111
  /**
118
112
  * @deprecated Use `chevronRotation` instead.
119
113
  */
@@ -7,14 +7,14 @@ import { motion } from 'motion/react';
7
7
  import { PlusIcon } from '@heroicons/react/24/outline';
8
8
  import { createTriggerIconVariants } from './Accordion.animations.js';
9
9
 
10
- const AccordionTrigger = React__default.forwardRef(({ children, className, chevron, chevronRotation, iconRotation, chevronPosition = "right", showChevron = true, ...props }, ref) => {
10
+ const AccordionTrigger = React__default.forwardRef(({ children, className, chevron, chevronRotation, iconRotation, chevronPosition = "right", ...props }, ref) => {
11
11
  var _a;
12
12
  const { theme } = useAccordionContext();
13
13
  const { isOpen, toggle, triggerId, contentId } = useAccordionItemContext();
14
14
  const resolvedChevronRotation = (_a = chevronRotation !== null && chevronRotation !== void 0 ? chevronRotation : iconRotation) !== null && _a !== void 0 ? _a : 45;
15
- // Resolve the indicator icon, respecting showChevron and chevron overrides
15
+ // Resolve the indicator icon
16
16
  const defaultChevronIcon = jsx(PlusIcon, { className: "size-4", strokeWidth: 2 });
17
- const resolvedChevron = !showChevron || chevron === null
17
+ const resolvedChevron = chevron === null
18
18
  ? null
19
19
  : chevron !== undefined
20
20
  ? chevron
@@ -22,9 +22,7 @@ const AccordionTrigger = React__default.forwardRef(({ children, className, chevr
22
22
  ? null // rendered in the left slot instead
23
23
  : defaultChevronIcon;
24
24
  // When chevronPosition="left", render the default animated chevron before children
25
- const useDefaultOnLeft = showChevron && chevron === null
26
- ? false
27
- : showChevron && chevronPosition === "left" && chevron === undefined;
25
+ const useDefaultOnLeft = chevron !== null && chevronPosition === "left" && chevron === undefined;
28
26
  const animatedChevron = (rotation, extraClass) => (jsx(motion.div, { initial: false, animate: isOpen ? "open" : "closed", variants: createTriggerIconVariants(rotation), className: cn("flex items-center justify-center shrink-0", extraClass), children: defaultChevronIcon }));
29
27
  return (jsxs("button", { ref: ref, type: "button", id: triggerId, className: cn(theme === null || theme === void 0 ? void 0 : theme.triggerStyle, className), "aria-expanded": isOpen, "aria-controls": contentId, "data-state": isOpen ? "open" : "closed", onClick: toggle, ...props, children: [useDefaultOnLeft && animatedChevron(resolvedChevronRotation), children, resolvedChevron && (jsx(motion.div, { initial: false, animate: isOpen ? "open" : "closed", variants: createTriggerIconVariants(resolvedChevronRotation), className: "flex items-center justify-center shrink-0 ml-auto", children: resolvedChevron }))] }));
30
28
  });
@@ -45,7 +45,7 @@ export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"
45
45
  showIcon?: boolean;
46
46
  /**
47
47
  * Avatar to display (takes precedence over icons)
48
- * Example: avatar={<Avatar src="..." alt="User name" size="md" />}
48
+ * Example: avatar={<Avatar src="..." name="User name" size="md" />}
49
49
  */
50
50
  avatar?: ReactNode;
51
51
  /**
@@ -4,12 +4,14 @@ import { cn } from '../../../utils/cn.js';
4
4
  import { avatarTheme } from './Avatar.theme.js';
5
5
  import { Tooltip } from '../Tooltip/Tooltip.js';
6
6
  import '../Tooltip/Tooltip.animations.js';
7
+ import { UserIcon } from '@heroicons/react/24/solid';
7
8
 
8
- const Avatar = React__default.forwardRef(({ src, alt = "Avatar", // Default alt text for better accessibility
9
- size = "md", status = "none", fallback, className, role, lazyLoad = true, retryCount = 0, retryDelay = 1000, contextualLabel, groupPosition, showTooltip = false, ...props }, ref) => {
9
+ const Avatar = React__default.forwardRef(({ src, name, size = "md", status = "none", fallback, className, role, lazyLoad = true, retryCount = 0, retryDelay = 1000, contextualLabel, groupPosition, showTooltip = false, initialsCount = 2, ...props }, ref) => {
10
10
  const [imageError, setImageError] = useState(false);
11
11
  const [retries, setRetries] = useState(0);
12
12
  const retryTimerRef = useRef(null);
13
+ // Normalize null to undefined for internal use
14
+ const resolvedName = name !== null && name !== void 0 ? name : undefined;
13
15
  // Clear timeout on unmount to prevent memory leaks
14
16
  useEffect(() => {
15
17
  return () => {
@@ -43,21 +45,23 @@ size = "md", status = "none", fallback, className, role, lazyLoad = true, retryC
43
45
  }, [retries, retryCount, retryDelay]);
44
46
  // Handle fallback content
45
47
  const getFallbackContent = () => {
48
+ // 1. Explicit fallback always wins
46
49
  if (fallback) {
47
50
  return fallback;
48
51
  }
49
- // If no fallback provided and no alt text, use placeholder
50
- if (alt === "Avatar") {
51
- return jsx("span", { "aria-hidden": "true", children: "?" });
52
+ // 2. Generate initials from name
53
+ if (resolvedName) {
54
+ const maxInitials = size === "xs" ? 1 : initialsCount;
55
+ const initials = resolvedName
56
+ .split(" ")
57
+ .map((part) => part[0])
58
+ .slice(0, maxInitials)
59
+ .join("")
60
+ .toUpperCase();
61
+ return jsx("span", { "aria-hidden": "true", children: initials });
52
62
  }
53
- // Use initials from alt text
54
- const initials = alt
55
- .split(" ")
56
- .map((part) => part[0])
57
- .slice(0, 1)
58
- .join("")
59
- .toUpperCase();
60
- return jsx("span", { "aria-hidden": "true", children: initials });
63
+ // 3. Default placeholder icon
64
+ return jsx(UserIcon, { className: "w-1/2 h-1/2" });
61
65
  };
62
66
  // Determine status indicator size based on avatar size
63
67
  const getStatusSize = () => {
@@ -78,15 +82,15 @@ size = "md", status = "none", fallback, className, role, lazyLoad = true, retryC
78
82
  };
79
83
  // Create full accessibility label including context if provided
80
84
  const accessibilityLabel = contextualLabel
81
- ? `${alt}${contextualLabel}`
82
- : alt;
85
+ ? `${resolvedName !== null && resolvedName !== void 0 ? resolvedName : "Avatar"}${contextualLabel}`
86
+ : (resolvedName !== null && resolvedName !== void 0 ? resolvedName : "Avatar");
83
87
  // Determine appropriate ARIA role
84
88
  const ariaRole = role || (src ? undefined : "img");
85
89
  // Avatar content to potentially wrap in tooltip
86
- const avatarContent = (jsxs("div", { ref: ref, className: cn("relative inline-flex avatar-wrapper", groupPosition), role: ariaRole === "presentation" ? "presentation" : undefined, "aria-label": ariaRole === "presentation" ? undefined : accessibilityLabel, "data-size": size, "data-status": status, "data-error": imageError ? "true" : "false", ...props, children: [jsx("div", { className: cn(avatarTheme.baseStyle, avatarTheme.sizes[size], className), "aria-hidden": ariaRole === "presentation" ? "true" : undefined, "data-size": size, children: src && !imageError ? (jsx("img", { src: src, alt: alt, className: "size-full object-cover object-top", onError: handleImageError, loading: lazyLoad ? "lazy" : undefined, "aria-hidden": ariaRole === "presentation" ? "true" : undefined, "data-loaded": "true" })) : (jsx("div", { className: "avatar-fallback", children: getFallbackContent() })) }), status !== "none" && (jsx("span", { className: cn("absolute rounded-full ring-2 ring-[var(--color-background)]", getStatusSize(), avatarTheme.statuses[status]), "aria-label": `Status: ${status}`, role: "status", "data-status": status }))] }));
90
+ const avatarContent = (jsxs("div", { ref: ref, className: cn("relative inline-flex avatar-wrapper", groupPosition), role: ariaRole === "presentation" ? "presentation" : undefined, "aria-label": ariaRole === "presentation" ? undefined : accessibilityLabel, "data-size": size, "data-status": status, "data-error": imageError ? "true" : "false", ...props, children: [jsx("div", { className: cn(avatarTheme.baseStyle, avatarTheme.sizes[size], className), "aria-hidden": ariaRole === "presentation" ? "true" : undefined, "data-size": size, children: src && !imageError ? (jsx("img", { src: src, alt: resolvedName !== null && resolvedName !== void 0 ? resolvedName : "Avatar", className: "size-full object-cover object-top", onError: handleImageError, loading: lazyLoad ? "lazy" : undefined, "aria-hidden": ariaRole === "presentation" ? "true" : undefined, "data-loaded": "true" })) : (jsx("div", { className: "avatar-fallback", children: getFallbackContent() })) }), status !== "none" && (jsx("span", { className: cn("absolute rounded-full ring-2 ring-[var(--color-background)]", getStatusSize(), avatarTheme.statuses[status]), "aria-label": `Status: ${status}`, role: "status", "data-status": status }))] }));
87
91
  // Conditionally wrap in tooltip
88
- if (showTooltip && alt && alt !== "Avatar") {
89
- return (jsx(Tooltip, { content: alt, placement: "top", children: avatarContent }));
92
+ if (showTooltip && resolvedName) {
93
+ return (jsx(Tooltip, { content: resolvedName, placement: "top", children: avatarContent }));
90
94
  }
91
95
  return avatarContent;
92
96
  });
@@ -3,7 +3,7 @@
3
3
  */
4
4
  const avatarTheme = {
5
5
  // Base styles for the avatar element
6
- baseStyle: "relative inline-flex items-center justify-center overflow-hidden bg-neutral-200 text-[var(--color-text-primary)] font-semibold tracking-tighter rounded-[var(--avatar-radius)] ring-2 ring-[var(--color-primary)]/10 border border-[var(--border)] transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 glass-effect",
6
+ baseStyle: "relative inline-flex items-center justify-center overflow-hidden bg-neutral-200 text-[var(--color-text-primary)] font-bold tracking-tighter rounded-[var(--avatar-radius)] border border-[var(--color-border)] transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2",
7
7
  // Size variants using CSS variables
8
8
  sizes: {
9
9
  xs: "size-[var(--avatar-size-xs)] text-xs",
@@ -1,17 +1,17 @@
1
- import { ReactNode } from 'react';
2
- export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
3
- export type AvatarStatus = 'online' | 'offline' | 'busy' | 'away' | 'none';
1
+ import { ReactNode } from "react";
2
+ export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl";
3
+ export type AvatarStatus = "online" | "offline" | "busy" | "away" | "none";
4
4
  export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
5
5
  /**
6
6
  * The source URL for the avatar image
7
7
  */
8
8
  src?: string;
9
9
  /**
10
- * Alternative text for the avatar image.
11
- * Should describe the person or entity represented by the avatar.
12
- * @default "Avatar"
10
+ * The name of the person or entity represented by the avatar.
11
+ * Used to generate initials for the fallback and as alt text for the image.
12
+ * Accepts `null` for compatibility with nullable data sources.
13
13
  */
14
- alt?: string;
14
+ name?: string | null;
15
15
  /**
16
16
  * The size of the avatar
17
17
  * @default "md"
@@ -23,7 +23,8 @@ export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
23
23
  */
24
24
  status?: AvatarStatus;
25
25
  /**
26
- * Fallback content to display when image fails to load or no src is provided
26
+ * Fallback content to display when image fails to load or no src is provided.
27
+ * Overrides the default placeholder icon and auto-generated initials.
27
28
  */
28
29
  fallback?: ReactNode;
29
30
  /**
@@ -18,7 +18,7 @@ const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "
18
18
  const directionClass = direction === "row-reverse" ? "flex-row-reverse" : "flex-row";
19
19
  const totalPeople = avatars.length;
20
20
  const groupDescription = avatars
21
- .map((avatar) => avatar.alt || "User")
21
+ .map((avatar) => avatar.name || "User")
22
22
  .join(", ");
23
23
  return {
24
24
  visibleAvatars,
@@ -49,7 +49,7 @@ const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "
49
49
  // Ensure that groupPosition is not passed to avoid conflicts
50
50
  groupPosition: undefined }) }, index));
51
51
  }), overflowCount > 0 && (jsx("div", { className: cn(spacingClass, enableAnimations &&
52
- "transition-transform duration-200 hover:scale-110"), "data-overflow-count": overflowCount, children: overflowIndicator ? (overflowIndicator(overflowCount)) : (jsx(Avatar, { size: size, alt: `+${overflowCount} more`, fallback: jsxs("span", { children: ["+", overflowCount] }), className: avatarGroupTheme.overflowAvatarStyle, role: "img", "aria-label": `+ ${overflowCount} more users` })) }))] }));
52
+ "transition-transform duration-200 hover:scale-110"), "data-overflow-count": overflowCount, children: overflowIndicator ? (overflowIndicator(overflowCount)) : (jsx(Avatar, { size: size, name: `+${overflowCount} more`, fallback: jsxs("span", { children: ["+", overflowCount] }), className: avatarGroupTheme.overflowAvatarStyle, role: "img", "aria-label": `+ ${overflowCount} more users` })) }))] }));
53
53
  });
54
54
  // Set display name for dev tools and Storybook
55
55
  AvatarGroup.displayName = "AvatarGroup";
@@ -90,9 +90,9 @@ const Badge = React__default.memo(React__default.forwardRef(({ children, variant
90
90
  ...(_c = badgeTheme.variantColors) === null || _c === void 0 ? void 0 : _c.outline,
91
91
  ...(_d = customTheme.variantColors) === null || _d === void 0 ? void 0 : _d.outline,
92
92
  },
93
- ghost: {
94
- ...(_e = badgeTheme.variantColors) === null || _e === void 0 ? void 0 : _e.ghost,
95
- ...(_f = customTheme.variantColors) === null || _f === void 0 ? void 0 : _f.ghost,
93
+ soft: {
94
+ ...(_e = badgeTheme.variantColors) === null || _e === void 0 ? void 0 : _e.soft,
95
+ ...(_f = customTheme.variantColors) === null || _f === void 0 ? void 0 : _f.soft,
96
96
  },
97
97
  },
98
98
  sizes: {
@@ -164,14 +164,16 @@ const Badge = React__default.memo(React__default.forwardRef(({ children, variant
164
164
  // Apply negative margin for overlap effect (scales with badge size)
165
165
  const avatarMargin = pill
166
166
  ? {
167
- sm: "-ml-1.5", // Small pill: minimal overlap
168
- md: "-ml-1.5", // Medium pill: moderate overlap
167
+ xs: "-ml-1",
168
+ sm: "-ml-1.5",
169
+ md: "-ml-1.5",
169
170
  }[size]
170
171
  : {
171
- sm: "-ml-1.5", // Small default: minimal overlap
172
- md: "-ml-1", // Medium default: less overlap (has border ring)
172
+ xs: "-ml-1",
173
+ sm: "-ml-1.5",
174
+ md: "-ml-1",
173
175
  }[size];
174
- return (jsx(Avatar, { src: avatar.src, alt: avatar.alt, fallback: avatar.fallback, initialsCount: avatar.initialsCount, className: cn((_a = theme.avatarSizes) === null || _a === void 0 ? void 0 : _a[size], avatarMargin, "border-0", pill ? "rounded-full" : "rounded-md ring-0") }));
176
+ return (jsx(Avatar, { src: avatar.src, name: avatar.name, fallback: avatar.fallback, initialsCount: avatar.initialsCount, className: cn((_a = theme.avatarSizes) === null || _a === void 0 ? void 0 : _a[size], avatarMargin, "border-0", pill ? "rounded-full" : "rounded-md ring-0") }));
175
177
  }
176
178
  if (iconStart) {
177
179
  return (jsx("span", { className: cn("inline-flex items-center justify-center flex-shrink-0", (_b = theme.iconSizes) === null || _b === void 0 ? void 0 : _b[size]), "aria-hidden": "true", children: iconStart }));
@@ -6,12 +6,12 @@ import { semanticBgColors } from '../../../utils/colorUtils.js';
6
6
  */
7
7
  const badgeTheme = {
8
8
  // Base styles applied to all badges
9
- baseStyle: "inline-flex items-center font-semibold select-none whitespace-nowrap transition-all duration-300 h-fit",
9
+ baseStyle: "inline-flex items-center align-middle select-none whitespace-nowrap transition-all duration-300 h-fit",
10
10
  // Visual style variants (structure only, no colors)
11
11
  variants: {
12
12
  filled: "",
13
13
  outline: "bg-transparent border",
14
- ghost: "ring-transparent border-0",
14
+ soft: "ring-transparent border-0",
15
15
  },
16
16
  // Color styles organized PER VARIANT
17
17
  // This structure enables reliable dark: variants and className overrides
@@ -45,9 +45,9 @@ const badgeTheme = {
45
45
  danger: "text-[var(--color-danger)] border-[var(--color-border)] " +
46
46
  "dark:text-[var(--color-danger-400)]",
47
47
  },
48
- // Ghost variant - subtle background tints
49
- ghost: {
50
- neutral: "bg-[var(--color-neutral-200)] text-[var(--color-text-secondary)] " +
48
+ // Soft variant - subtle background tints
49
+ soft: {
50
+ neutral: "bg-[var(--color-neutral-200)] text-[var(--color-text-primary)] " +
51
51
  "dark:bg-[var(--color-neutral-900)] ",
52
52
  primary: "bg-[var(--color-primary-100)] text-[var(--color-primary-700)] " +
53
53
  "dark:bg-[var(--color-primary-600)]/30 dark:text-[var(--color-primary-400)]",
@@ -63,36 +63,43 @@ const badgeTheme = {
63
63
  },
64
64
  // Flat size structure (no nested objects)
65
65
  sizes: {
66
- sm: "text-sm px-1.5 py-0.5 gap-1",
67
- md: "text-base px-2 py-0.5 gap-1",
66
+ xs: "text-xs px-1 py-0 gap-1 font-semibold",
67
+ sm: "text-sm px-1.5 py-0 gap-1 font-semibold",
68
+ md: "text-sm px-2 py-0.25 gap-1 font-semibold",
68
69
  },
69
70
  // Border radius per size (using CSS variable)
70
71
  radiusSizes: {
72
+ xs: "rounded-[calc(var(--badge-radius)*0.5)]",
71
73
  sm: "rounded-[calc(var(--badge-radius)*0.75)]",
72
74
  md: "rounded-[var(--badge-radius)]",
73
75
  },
74
76
  // Additional padding for pill shape
75
77
  pillSizes: {
78
+ xs: "px-1.5",
76
79
  sm: "px-2",
77
80
  md: "px-2.5",
78
81
  },
79
82
  // Icon sizes
80
83
  iconSizes: {
84
+ xs: "size-2.5",
81
85
  sm: "size-3",
82
86
  md: "size-3.5",
83
87
  },
84
88
  // Avatar sizes (base sizes without negative margin)
85
89
  avatarSizes: {
90
+ xs: "size-3.5",
86
91
  sm: "size-4",
87
92
  md: "size-4",
88
93
  },
89
94
  // Dismiss button container sizes
90
95
  dismissButtonSizes: {
96
+ xs: "size-3",
91
97
  sm: "size-3.5",
92
98
  md: "size-4",
93
99
  },
94
100
  // Dismiss icon sizes (inside button)
95
101
  dismissIconSizes: {
102
+ xs: "size-2",
96
103
  sm: "size-2.5",
97
104
  md: "size-3",
98
105
  },
@@ -1,8 +1,8 @@
1
1
  import React, { ReactNode, MouseEvent } from "react";
2
2
  import { SemanticColor } from "../../../utils/colorUtils";
3
- export type BadgeVariant = "filled" | "outline" | "ghost";
3
+ export type BadgeVariant = "filled" | "outline" | "soft";
4
4
  export type BadgeColor = SemanticColor;
5
- export type BadgeSize = "sm" | "md";
5
+ export type BadgeSize = "xs" | "sm" | "md";
6
6
  /**
7
7
  * Theme configuration for Badge component
8
8
  */
@@ -122,7 +122,7 @@ export interface BadgeProps extends Omit<React.HTMLAttributes<HTMLSpanElement>,
122
122
  */
123
123
  avatar?: {
124
124
  src?: string;
125
- alt: string;
125
+ name: string;
126
126
  fallback?: ReactNode;
127
127
  initialsCount?: number;
128
128
  };
@@ -120,7 +120,7 @@ function getRippleColorClass(variant, color) {
120
120
  if (variant === "filled") {
121
121
  return "bg-white/30";
122
122
  }
123
- // For outline/ghost buttons, use color-matched ripple
123
+ // For outline/soft buttons, use color-matched ripple
124
124
  switch (color) {
125
125
  case "primary":
126
126
  return "bg-[var(--color-primary)]/20";
@@ -9,7 +9,7 @@ export interface ButtonTheme {
9
9
  variantColors: {
10
10
  filled: Record<ButtonColor, string>;
11
11
  outline: Record<ButtonColor, string>;
12
- ghost: Record<ButtonColor, string>;
12
+ soft: Record<ButtonColor, string>;
13
13
  link: Record<ButtonColor, string>;
14
14
  };
15
15
  sizes: Record<ButtonSize, {
@@ -8,7 +8,7 @@ const buttonTheme = {
8
8
  variantStyles: {
9
9
  filled: "border hover:brightness-[0.9] dark:hover:brightness-[1.15]",
10
10
  outline: "bg-transparent border",
11
- ghost: "border-transparent",
11
+ soft: "border-transparent",
12
12
  link: "bg-transparent border-transparent",
13
13
  },
14
14
  // Color styles organized BY VARIANT for reliable dark: prefixes
@@ -66,8 +66,8 @@ const buttonTheme = {
66
66
  "dark:text-[var(--color-danger-300)] dark:border-[var(--color-danger-400)] " +
67
67
  "dark:hover:bg-[var(--color-danger-900)]/30",
68
68
  },
69
- // Ghost variant - subtle background tints
70
- ghost: {
69
+ // Soft variant - subtle background tints
70
+ soft: {
71
71
  neutral: "text-[var(--color-text-primary)] bg-[var(--color-neutral-100)] " +
72
72
  "hover:bg-[var(--color-neutral-200)] " +
73
73
  "focus-visible:outline-[var(--color-text-secondary)] " +
@@ -124,9 +124,9 @@ const buttonTheme = {
124
124
  // Size variants
125
125
  sizes: {
126
126
  sm: {
127
- default: "font-semibold min-h-[var(--button-min-h-sm)] px-[var(--button-px-sm)] py-[var(--button-py-sm)] text-base",
127
+ default: "font-semibold min-h-[var(--button-min-h-sm)] px-[var(--button-px-sm)] py-[var(--button-py-sm)] text-sm",
128
128
  iconOnly: "size-[var(--button-icon-size-sm)] p-[var(--button-icon-p-sm)]",
129
- link: "font-semibold p-0 text-base h-fit",
129
+ link: "font-semibold p-0 text-sm h-fit",
130
130
  },
131
131
  md: {
132
132
  default: "font-semibold min-h-[var(--button-min-h-md)] px-[var(--button-px-md)] py-[var(--button-py-md)] text-base",
@@ -51,6 +51,6 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
51
51
  darkMode?: boolean;
52
52
  }
53
53
  export type ButtonColor = SemanticColor;
54
- export type ButtonVariant = "filled" | "outline" | "ghost" | "link";
54
+ export type ButtonVariant = "filled" | "outline" | "soft" | "link";
55
55
  export type ButtonSize = "sm" | "md" | "lg";
56
56
  export type ButtonState = "default" | "disabled" | "error" | "loading";
@@ -1,7 +1,7 @@
1
- import { HTMLAttributes } from 'react';
2
- import { CalendarEventThemeOverrides } from './CalendarEvent.theme';
3
- import { CalendarEvent as CalendarEventType, GridConfig, EventPosition } from '../../../../utils';
4
- export type CalendarEventVariant = 'daily' | 'weekly' | 'monthly';
1
+ import { HTMLAttributes } from "react";
2
+ import { CalendarEventThemeOverrides } from "./CalendarEvent.theme";
3
+ import { CalendarEvent as CalendarEventType, GridConfig, EventPosition } from "../../../../utils";
4
+ export type CalendarEventVariant = "daily" | "weekly" | "monthly";
5
5
  export interface CalendarEventProps extends HTMLAttributes<HTMLDivElement> {
6
6
  variant?: CalendarEventVariant;
7
7
  event: CalendarEventType & {
@@ -9,7 +9,6 @@ export interface CalendarEventProps extends HTMLAttributes<HTMLDivElement> {
9
9
  attendees?: Array<{
10
10
  name: string;
11
11
  src: string;
12
- alt: string;
13
12
  showTooltip?: boolean;
14
13
  }>;
15
14
  };
@@ -20,5 +19,5 @@ export interface CalendarEventProps extends HTMLAttributes<HTMLDivElement> {
20
19
  gridInterval?: number;
21
20
  startOffset?: number;
22
21
  theme?: CalendarEventThemeOverrides;
23
- onEventClick?: (eventData: CalendarEventProps['event']) => void;
22
+ onEventClick?: (eventData: CalendarEventProps["event"]) => void;
24
23
  }
@@ -195,7 +195,7 @@ const CalendarMini = React__default.forwardRef(({ selectedDate, currentMonth, on
195
195
  };
196
196
  const MonthRow = enableAnimations ? motion.div : "div";
197
197
  const DateItem = enableAnimations ? motion.button : "button";
198
- return (jsxs("div", { ref: ref, className: cn(mergedTheme.baseStyle, className), role: "region", "aria-label": "Mini calendar", ...rest, children: [jsx(Button, { iconOnly: true, color: "neutral", variant: "ghost", className: "!flex-shrink !flex-grow-0 !w-fit self-stretch !h-auto !min-h-0 px-0.5", onClick: handlePrevMonth, "aria-label": "Previous month", children: jsx(ChevronLeftIcon, { className: "size-5 text-[var(--color-text-primary)]" }) }), jsxs("div", { className: "flex flex-col gap-1 relative", children: [jsxs("div", { className: "flex items-center gap-1", children: [jsx("div", { className: "flex flex-col relative overflow-hidden flex-1", style: {
198
+ return (jsxs("div", { ref: ref, className: cn(mergedTheme.baseStyle, className), role: "region", "aria-label": "Mini calendar", ...rest, children: [jsx(Button, { iconOnly: true, color: "neutral", variant: "soft", className: "!flex-shrink !flex-grow-0 !w-fit self-stretch !h-auto !min-h-0 px-0.5", onClick: handlePrevMonth, "aria-label": "Previous month", children: jsx(ChevronLeftIcon, { className: "size-5 text-[var(--color-text-primary)]" }) }), jsxs("div", { className: "flex flex-col gap-1 relative", children: [jsxs("div", { className: "flex items-center gap-1", children: [jsx("div", { className: "flex flex-col relative overflow-hidden flex-1", style: {
199
199
  maskImage: "linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)",
200
200
  WebkitMaskImage: "linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)",
201
201
  }, children: jsx("div", { ref: monthScrollRef, className: cn(mergedTheme.monthRowStyle), role: "tablist", "aria-label": "Month selection", children: jsx(AnimatePresence, { mode: "popLayout", initial: false, custom: direction, children: jsx(MonthRow, { custom: direction, ...(enableAnimations && {
@@ -243,7 +243,7 @@ const CalendarMini = React__default.forwardRef(({ selectedDate, currentMonth, on
243
243
  }), "aria-selected": isSelected, "aria-current": isToday ? "date" : undefined, "aria-disabled": disabled, type: "button", ...(enableAnimations && {
244
244
  whileTap: !disabled ? { scale: 0.95 } : undefined,
245
245
  }), children: [jsx("div", { className: "text-base font-medium opacity-60", children: formatDayName(date) }), jsx("time", { dateTime: date.toISOString(), className: cn("font-semibold"), children: formatDateNumber(date) })] }, date.toISOString()));
246
- }) }, `${startDate.getTime()}-dates`) }) }) })] }), jsx(Button, { iconOnly: true, color: "neutral", variant: "ghost", className: "!flex-shrink !flex-grow-0 !w-fit self-stretch !h-auto !min-h-0 px-0.5", onClick: handleNextMonth, "aria-label": "Next month", children: jsx(ChevronRightIcon, { className: "size-5 text-[var(--color-text-primary)]" }) })] }));
246
+ }) }, `${startDate.getTime()}-dates`) }) }) })] }), jsx(Button, { iconOnly: true, color: "neutral", variant: "soft", className: "!flex-shrink !flex-grow-0 !w-fit self-stretch !h-auto !min-h-0 px-0.5", onClick: handleNextMonth, "aria-label": "Next month", children: jsx(ChevronRightIcon, { className: "size-5 text-[var(--color-text-primary)]" }) })] }));
247
247
  });
248
248
  CalendarMini.displayName = "CalendarMini";
249
249
 
@@ -8,7 +8,7 @@ import { XMarkIcon } from '@heroicons/react/24/outline';
8
8
  const DrawerHeader = ({ children, icon, showCloseButton, className, ...props }) => {
9
9
  const { onClose, showCloseButton: contextShowCloseButton, theme, } = useDrawerContext();
10
10
  const shouldShowCloseButton = showCloseButton !== null && showCloseButton !== void 0 ? showCloseButton : contextShowCloseButton;
11
- return (jsxs("div", { className: cn(theme.header, className), ...props, children: [jsxs("div", { className: cn(theme.headerContent), children: [icon && (jsx("div", { className: "flex-shrink-0 text-[var(--color-text-secondary)]", children: icon })), jsx("div", { className: "flex-1 min-w-0", children: children })] }), shouldShowCloseButton && (jsx(Button, { color: "neutral", variant: "ghost", iconOnly: true, size: "sm", onClick: onClose, "aria-label": "Close drawer", className: "rounded-full shrink-0", children: jsx(XMarkIcon, { className: "size-5" }) }))] }));
11
+ return (jsxs("div", { className: cn(theme.header, className), ...props, children: [jsxs("div", { className: cn(theme.headerContent), children: [icon && (jsx("div", { className: "flex-shrink-0 text-[var(--color-text-secondary)]", children: icon })), jsx("div", { className: "flex-1 min-w-0", children: children })] }), shouldShowCloseButton && (jsx(Button, { color: "neutral", variant: "soft", iconOnly: true, size: "sm", onClick: onClose, "aria-label": "Close drawer", className: "rounded-full shrink-0", children: jsx(XMarkIcon, { className: "size-5" }) }))] }));
12
12
  };
13
13
  DrawerHeader.displayName = "Drawer.Header";
14
14
 
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { FileTypeIconProps } from './FileTypeIcon.types';
3
+ export declare const FileTypeIcon: React.ForwardRefExoticComponent<Omit<FileTypeIconProps, "ref"> & React.RefAttributes<SVGSVGElement>>;
@@ -0,0 +1,33 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import React__default from 'react';
3
+ import { cn } from '../../../utils/cn.js';
4
+ import { fileTypeIconTheme } from './FileTypeIcon.theme.js';
5
+ import { fileTypes } from './fileTypes.js';
6
+
7
+ const FileTypeIcon = React__default.forwardRef(({ type, size = 'md', colorMode = 'color', className, theme: customTheme, ...props }, ref) => {
8
+ var _a;
9
+ const fileType = fileTypes[type];
10
+ if (!fileType) {
11
+ if (process.env.NODE_ENV !== 'production') {
12
+ console.warn(`[FileTypeIcon]: Unknown type "${type}". Available types: ${Object.keys(fileTypes).join(', ')}`);
13
+ }
14
+ return null;
15
+ }
16
+ const theme = {
17
+ ...fileTypeIconTheme,
18
+ ...customTheme,
19
+ sizes: { ...fileTypeIconTheme.sizes, ...customTheme === null || customTheme === void 0 ? void 0 : customTheme.sizes },
20
+ };
21
+ const resolvedSize = typeof size === 'number' ? size : (_a = theme.sizes[size]) !== null && _a !== void 0 ? _a : 24;
22
+ const strokeColor = colorMode === 'color' ? '#D5D7DA' : 'currentColor';
23
+ const strokeOpacity = colorMode === 'color' ? 1 : 0.3;
24
+ const badgeColor = colorMode === 'color' ? fileType.color : 'currentColor';
25
+ const labelText = fileType.label;
26
+ const badgeWidth = Math.round(labelText.length * 6 + 8);
27
+ const badgeX = 1;
28
+ const textX = badgeX + badgeWidth / 2;
29
+ return (jsxs("svg", { ref: ref, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 40 40", width: resolvedSize, height: resolvedSize, fill: "none", role: "img", "aria-label": `${fileType.label} file`, "data-type": type, "data-color-mode": colorMode, "data-size": size, className: cn(theme.baseStyle, className), ...props, children: [jsx("path", { d: "M11 0.75H27C27.1212 0.75 27.2375 0.798088 27.3232 0.883789L38.1162 11.6768C38.2019 11.7625 38.25 11.8788 38.25 12V36C38.25 37.7949 36.7949 39.25 35 39.25H11C9.20507 39.25 7.75 37.7949 7.75 36V4C7.75 2.20507 9.20508 0.75 11 0.75Z", stroke: strokeColor, strokeWidth: "1.5", opacity: strokeOpacity }), jsx("path", { d: "M27 0.5V8C27 10.2091 28.7909 12 31 12H38.5", stroke: strokeColor, strokeWidth: "1.5", opacity: strokeOpacity }), jsx("rect", { x: badgeX, y: 18, width: badgeWidth, height: 16, rx: 2, fill: badgeColor, opacity: colorMode === 'color' ? 1 : 0.15 }), jsx("text", { x: textX, y: 26, textAnchor: "middle", dominantBaseline: "central", fill: colorMode === 'color' ? 'white' : 'currentColor', fontSize: 9.5, fontWeight: 700, fontFamily: "Arial, Helvetica, sans-serif", letterSpacing: 0.3, children: labelText })] }));
30
+ });
31
+ FileTypeIcon.displayName = 'FileTypeIcon';
32
+
33
+ export { FileTypeIcon };
@@ -0,0 +1,2 @@
1
+ import { FileTypeIconTheme } from './FileTypeIcon.types';
2
+ export declare const fileTypeIconTheme: FileTypeIconTheme;
@@ -0,0 +1,12 @@
1
+ const fileTypeIconTheme = {
2
+ baseStyle: 'inline-block shrink-0',
3
+ sizes: {
4
+ xs: 16,
5
+ sm: 20,
6
+ md: 24,
7
+ lg: 32,
8
+ xl: 40,
9
+ },
10
+ };
11
+
12
+ export { fileTypeIconTheme };