@tribepad/themis 1.6.8 → 1.6.10

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/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.6.10] - 2026-06-15
9
+
10
+ ### Added
11
+ - **`Tabs` stepper variant: explicit per-step `completed` + optional `sublabel` on `Tab`.** The stepper previously inferred "completed" purely from position (any step before the selected one rendered a checkmark). The new optional `completed` prop overrides that inference, so a non-linear flow (e.g. the EOS blueprint editor) can mark a later step done while an earlier one is still outstanding — and can show the current step itself as completed. The new optional `sublabel` prop renders a small secondary line beneath a step's label (e.g. "Current step"). Both props apply to the `stepper` variant only and are ignored elsewhere; index-based inference is unchanged when `completed` is omitted, so this is backwards-compatible.
12
+
13
+ ## [1.6.9] - 2026-06-12
14
+
15
+ ### Changed
16
+ - **Dependency refresh (no API changes).** Runtime deps: `react-aria` 3.49.0, `react-aria-components` 1.18.0, `react-stately` 3.47.0, `@internationalized/date` 3.12.2, `tailwind-merge` 3.6.0. Dev/tooling: Storybook 10.4.4, Vite 8.0.16, Vitest 4.1.8, ESLint 10.4.1, typescript-eslint 8.61.0, happy-dom 20.10.2, motion 12.40.0, lucide-react 1.18.0, zod 4.4.3, and friends. TypeScript intentionally stays on 5.9.x.
17
+ - **Tests adapted to react-aria 3.49's pressable handling**: label-click simulation no longer toggles Checkbox/Switch under test DOMs (native label forwarding is unaffected in real browsers — tests now assert the label association explicitly and click the control by role), and `Switch` no longer toggles on Enter (spec-correct: Space is the ARIA activation key).
18
+
8
19
  ## [1.6.8] - 2026-06-10
9
20
 
10
21
  ### Added
@@ -72,7 +72,7 @@ export declare const accordionContentInnerVariants: (props?: ({} & import("class
72
72
  * ...
73
73
  * </Accordion>
74
74
  */
75
- declare function AccordionRoot({ type, collapsible, expandedKeys, defaultExpandedKeys, onExpandedChange, isDisabled, disabledKeys, className, children, ...props }: AccordionProps): import("react/jsx-runtime").JSX.Element;
75
+ declare function AccordionRoot({ type, collapsible, expandedKeys, defaultExpandedKeys, onExpandedChange, isDisabled, disabledKeys, className, children, ...props }: AccordionProps): import("react").JSX.Element;
76
76
  declare namespace AccordionRoot {
77
77
  var displayName: string;
78
78
  }
@@ -89,7 +89,7 @@ declare namespace AccordionRoot {
89
89
  * <AccordionContent>This content is for members only.</AccordionContent>
90
90
  * </AccordionItem>
91
91
  */
92
- declare function AccordionItemComponent({ id, isDisabled: isDisabledProp, className, children, ...props }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
92
+ declare function AccordionItemComponent({ id, isDisabled: isDisabledProp, className, children, ...props }: AccordionItemProps): import("react").JSX.Element;
93
93
  declare namespace AccordionItemComponent {
94
94
  var displayName: string;
95
95
  }
@@ -117,11 +117,11 @@ declare namespace AccordionItemComponent {
117
117
  * Click to expand
118
118
  * </AccordionTrigger>
119
119
  */
120
- declare function AccordionTriggerComponent({ as: headingLevel, icon, hideIcon, className, children, ...props }: AccordionTriggerProps): import("react/jsx-runtime").JSX.Element;
120
+ declare function AccordionTriggerComponent({ as: headingLevel, icon, hideIcon, className, children, ...props }: AccordionTriggerProps): import("react").JSX.Element;
121
121
  declare namespace AccordionTriggerComponent {
122
122
  var displayName: string;
123
123
  }
124
- declare function AccordionContentComponent({ preserveState, className, children, ...props }: AccordionContentProps): import("react/jsx-runtime").JSX.Element;
124
+ declare function AccordionContentComponent({ preserveState, className, children, ...props }: AccordionContentProps): import("react").JSX.Element;
125
125
  declare namespace AccordionContentComponent {
126
126
  var displayName: string;
127
127
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Accordion.d.ts","sourceRoot":"","sources":["../../../src/elements/Accordion/Accordion.tsx"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EAEtB,MAAM,mBAAmB,CAAC;AAoB3B;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,2FAG5B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,2FAUjC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,2FAuBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;8EAoBpC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;8EAgBpC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,6BAA6B,2FAGxC,CAAC;AA0CH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,iBAAS,aAAa,CAAC,EACrB,IAAe,EACf,WAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,UAAkB,EAClB,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,2CAmChB;kBA9CQ,aAAa;;;AAkDtB;;;;;;;;;;;;GAYG;AACH,iBAAS,sBAAsB,CAAC,EAC9B,EAAE,EACF,UAAU,EAAE,cAAc,EAC1B,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,kBAAkB,2CAkBpB;kBAxBQ,sBAAsB;;;AA4B/B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iBAAS,yBAAyB,CAAC,EACjC,EAAE,EAAE,YAAmB,EACvB,IAAI,EACJ,QAAgB,EAChB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,2CA0BvB;kBAjCQ,yBAAyB;;;AAqFlC,iBAAS,yBAAyB,CAAC,EACjC,aAAqB,EACrB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,2CAgBvB;kBArBQ,yBAAyB;;;AA6BlC;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,sBAAgB,CAAC;AACvC,eAAO,MAAM,aAAa,+BAAyB,CAAC;AACpD,eAAO,MAAM,gBAAgB,kCAA4B,CAAC;AAC1D,eAAO,MAAM,gBAAgB,kCAA4B,CAAC;AAG1D,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,GACtB,CAAC"}
1
+ {"version":3,"file":"Accordion.d.ts","sourceRoot":"","sources":["../../../src/elements/Accordion/Accordion.tsx"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EAEtB,MAAM,mBAAmB,CAAC;AAoB3B;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,2FAG5B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,2FAUjC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,2FAuBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;8EAoBpC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;8EAgBpC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,6BAA6B,2FAGxC,CAAC;AA0CH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,iBAAS,aAAa,CAAC,EACrB,IAAe,EACf,WAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,UAAkB,EAClB,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,+BAmChB;kBA9CQ,aAAa;;;AAkDtB;;;;;;;;;;;;GAYG;AACH,iBAAS,sBAAsB,CAAC,EAC9B,EAAE,EACF,UAAU,EAAE,cAAc,EAC1B,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,kBAAkB,+BAkBpB;kBAxBQ,sBAAsB;;;AA4B/B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iBAAS,yBAAyB,CAAC,EACjC,EAAE,EAAE,YAAmB,EACvB,IAAI,EACJ,QAAgB,EAChB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,+BA0BvB;kBAjCQ,yBAAyB;;;AAqFlC,iBAAS,yBAAyB,CAAC,EACjC,aAAqB,EACrB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,+BAgBvB;kBArBQ,yBAAyB;;;AA6BlC;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,sBAAgB,CAAC;AACvC,eAAO,MAAM,aAAa,+BAAyB,CAAC;AACpD,eAAO,MAAM,gBAAgB,kCAA4B,CAAC;AAC1D,eAAO,MAAM,gBAAgB,kCAA4B,CAAC;AAG1D,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,GACtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Avatar/Avatar.tsx","../../../src/elements/Avatar/AvatarGroup.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/Avatar/Avatar.types.ts"],"names":["cn","inputs","twMerge","clsx","getInitials","name","parts","firstPart","lastPart","first","last","avatarOuterVariants","cva","avatarVisualVariants","Avatar","memo","forwardRef","src","alt","size","shape","status","onPress","className","id","dataTestId","ariaLabel","ariaLabelledBy","ariaDescribedBy","_groupIndex","ref","loadingState","setLoadingState","useState","handleImageLoad","useCallback","handleImageError","initials","isInteractive","computedAriaLabel","groupBrightness","visualContent","jsxs","jsx","AriaButton","OVERLAP_CLASS_BY_SIZE","avatarOverflowVariants","avatarGroupVariants","AvatarGroup","max","children","childArray","Children","totalCount","visibleCount","overflowCount","isGroupInteractive","overlapClass","child","isValidElement","visibleAvatars","index","childProps","zIndex","individualOnPress","cloneElement","overflowIndicator","groupContent","Fragment","BaseComponentPropsSchema","z","AvatarSizeSchema","AvatarShapeSchema","AvatarStatusSchema","AVATAR_OVERLAP_BY_SIZE","AvatarPropsSchema","AvatarGroupPropsSchema"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Accessible avatar with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant)\n * - Layer 2: Visual Avatar (configurable size, shape, status ring)\n * - Layer 3: Content (image, initials, skeleton shimmer)\n *\n * @see plan.md for implementation details\n * @see avatar-prd.md for requirements\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useState, useCallback, type ReactElement } from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type {\n AvatarProps,\n AvatarSize,\n AvatarShape,\n AvatarStatus,\n AvatarLoadingState,\n} from './Avatar.types';\n\n/**\n * Extract initials from a name\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\nfunction getInitials(name: string | undefined): string {\n if (!name || !name.trim()) return '';\n\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n\n const firstPart = parts[0];\n if (parts.length === 1) {\n return firstPart ? firstPart.charAt(0).toUpperCase() : '';\n }\n\n // First and last initials (max 2 characters)\n const lastPart = parts[parts.length - 1];\n const first = firstPart ? firstPart.charAt(0).toUpperCase() : '';\n const last = lastPart ? lastPart.charAt(0).toUpperCase() : '';\n return first + last;\n}\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n */\nexport const avatarOuterVariants = cva(\n 'relative inline-flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'min-h-[44px] min-w-[44px]',\n default: 'min-h-[44px] min-w-[44px]',\n lg: 'min-h-[48px] min-w-[48px]',\n },\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-105',\n ],\n false: '',\n },\n },\n defaultVariants: {\n size: 'default',\n interactive: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual avatar appearance\n * Provides the visual appearance with configurable size, shape, and status ring\n */\nexport const avatarVisualVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'overflow-hidden',\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'font-medium',\n 'select-none',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n status: {\n online: 'ring-[3px] ring-[var(--status-online,#22c55e)]',\n offline: 'ring-[3px] ring-[var(--status-offline,#6b7280)]',\n busy: 'ring-[3px] ring-[var(--status-busy,#ef4444)]',\n away: 'ring-[3px] ring-[var(--status-away,#eab308)]',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Displays user avatar with image or initials fallback\n *\n * Layer 1: Touch Target - 44x44px WCAG AAA compliant\n * Layer 2: Visual Avatar - configurable appearance\n * Layer 3: Content - image, initials, or loading skeleton\n */\nconst Avatar = memo(\n forwardRef<HTMLDivElement, AvatarProps>(\n (\n {\n src,\n alt,\n name,\n size = 'default',\n shape = 'circle',\n status,\n onPress,\n className,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n _groupIndex,\n },\n ref\n ): ReactElement => {\n // Loading state machine: loading → loaded | error\n const [loadingState, setLoadingState] = useState<AvatarLoadingState>(\n src ? 'loading' : 'error'\n );\n\n const handleImageLoad = useCallback(() => {\n setLoadingState('loaded');\n }, []);\n\n const handleImageError = useCallback(() => {\n setLoadingState('error');\n }, []);\n\n const initials = getInitials(name);\n const isInteractive = Boolean(onPress);\n\n // Build aria-label for actionable avatars with status\n const computedAriaLabel =\n ariaLabel ||\n (isInteractive && status ? `${alt}, ${status}` : undefined);\n\n // Calculate brightness for group darkening effect\n // First avatar (index 0) = 100%, each subsequent gets 5% darker\n const groupBrightness =\n _groupIndex !== undefined ? 1 - _groupIndex * 0.05 : undefined;\n\n // Common visual layer content\n const visualContent = (\n <div\n data-testid=\"avatar-visual\"\n className={cn(avatarVisualVariants({ size, shape, status }))}\n style={groupBrightness !== undefined ? { filter: `brightness(${groupBrightness})` } : undefined}\n >\n {/* Layer 3: Content */}\n\n {/* Skeleton shimmer while loading */}\n {loadingState === 'loading' && (\n <div\n data-testid=\"avatar-skeleton\"\n className={cn(\n 'absolute inset-0 animate-pulse bg-[var(--accent-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg'\n )}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Image (hidden during loading, removed on error) */}\n {src && loadingState !== 'error' && (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- onLoad/onError are lifecycle events, not interactions\n <img\n src={src}\n alt={isInteractive ? '' : alt}\n onLoad={handleImageLoad}\n onError={handleImageError}\n className={cn(\n 'h-full w-full object-cover',\n loadingState === 'loading' && 'invisible'\n )}\n />\n )}\n\n {/* Initials fallback (shown when no src or on error) */}\n {loadingState === 'error' && initials && (\n <span aria-hidden=\"true\">{initials}</span>\n )}\n </div>\n );\n\n // Render as button if interactive\n if (isInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={computedAriaLabel || alt}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(\n avatarOuterVariants({ size, interactive: true }),\n className\n )}\n >\n {visualContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(\n avatarOuterVariants({ size, interactive: false }),\n className\n )}\n >\n {visualContent}\n </div>\n );\n }\n )\n);\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar };\nexport type { AvatarProps, AvatarSize, AvatarShape, AvatarStatus };\n","'use client';\n\n/**\n * AvatarGroup Component\n * Displays multiple avatars with overlap, overflow indicator, and optional actionability\n *\n * Features:\n * - Overlapping display with configurable overlap\n * - Overflow indicator (+X) when exceeding max\n * - 2px border for visual separation\n * - First avatar on top (highest z-index)\n * - Mutually exclusive actions (group OR individual, not both)\n * - Shape/size inheritance to children\n *\n * @see plan.md Clarifications 1, 2, 5, 6, 7\n * @see avatar-prd.md US-5, US-7\n */\n\nimport {\n forwardRef,\n memo,\n Children,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type { AvatarGroupProps, AvatarSize, AvatarShape } from './Avatar.types';\n\n/**\n * Overlap class by size (proportional)\n * sm: 8px (-ml-2), default: 12px (-ml-3), lg: 16px (-ml-4)\n */\nconst OVERLAP_CLASS_BY_SIZE: Record<AvatarSize, string> = {\n sm: '-ml-2',\n default: '-ml-3',\n lg: '-ml-4',\n};\n\n/**\n * Overflow indicator variants (the +X badge)\n */\nconst avatarOverflowVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'bg-[var(--secondary)]',\n 'text-[var(--secondary-foreground)]',\n 'font-semibold',\n 'select-none',\n 'border-2 border-[var(--page-background)]',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Group outer variants\n */\nconst avatarGroupVariants = cva('inline-flex items-center', {\n variants: {\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n ],\n false: '',\n },\n },\n defaultVariants: {\n interactive: false,\n },\n});\n\n/**\n * AvatarGroup Component\n * Groups multiple Avatar components with overlap and overflow handling\n */\nconst AvatarGroup = memo(\n forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n max = 3,\n shape = 'circle',\n size = 'default',\n onPress,\n className,\n children,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n },\n ref\n ): ReactElement => {\n const childArray = Children.toArray(children);\n const totalCount = childArray.length;\n const visibleCount = Math.min(totalCount, max);\n const overflowCount = totalCount - visibleCount;\n\n const isGroupInteractive = Boolean(onPress);\n const overlapClass = OVERLAP_CLASS_BY_SIZE[size];\n\n // Warn about mutually exclusive actions (Clarification 2)\n // Uses !== 'production' to work in both development and test modes\n if (process.env.NODE_ENV !== 'production' && isGroupInteractive) {\n Children.forEach(children, (child) => {\n if (\n isValidElement(child) &&\n (child.props as { onPress?: unknown }).onPress\n ) {\n console.warn(\n '[AvatarGroup] onPress on individual avatars is ignored when AvatarGroup has onPress. ' +\n 'Actions are mutually exclusive - use group onPress OR individual onPress, not both.'\n );\n }\n });\n }\n\n // Build visible avatars with proper styling\n const visibleAvatars = childArray.slice(0, visibleCount).map((child, index) => {\n if (!isValidElement(child)) return null;\n\n // Clone child with inherited props and group styling\n const childProps = child.props as {\n size?: AvatarSize;\n shape?: AvatarShape;\n onPress?: unknown;\n className?: string;\n };\n\n // First avatar has highest z-index (Clarification 5)\n const zIndex = visibleCount - index;\n\n // Determine if individual avatar is interactive\n // If group has onPress, individual onPress is ignored\n const individualOnPress = isGroupInteractive\n ? undefined\n : childProps.onPress;\n\n return (\n <div\n key={index}\n data-testid=\"avatar-group-item\"\n className={cn(\n 'relative',\n // 2px border for visual separation (Clarification 1 & 7)\n 'border-2 border-[var(--page-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg',\n // Overlap negative margin (except first)\n index > 0 && overlapClass,\n // Hover lift animation when group is NOT actionable\n // Individual avatars get lift effect regardless of their own onPress\n !isGroupInteractive && [\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-[1.15]',\n 'motion-safe:hover:z-10',\n ]\n )}\n style={{ zIndex }}\n >\n {cloneElement(child as ReactElement, {\n size,\n shape,\n // Individual onPress is ignored if group has onPress\n onPress: individualOnPress,\n // Pass group index for progressive darkening\n _groupIndex: index,\n // Remove outer wrapper styling since we handle it\n className: cn(\n childProps.className,\n // Remove min dimensions since group item handles touch target\n '!min-h-0 !min-w-0'\n ),\n } as Partial<typeof childProps>)}\n </div>\n );\n });\n\n // Overflow indicator (+X)\n const overflowIndicator =\n overflowCount > 0 ? (\n <div\n data-testid=\"avatar-overflow\"\n className={cn(\n avatarOverflowVariants({ size, shape }),\n overlapClass\n )}\n style={{ zIndex: 0 }}\n aria-hidden=\"true\"\n >\n +{overflowCount}\n </div>\n ) : null;\n\n // Auto-generate aria-label if not provided\n const computedAriaLabel =\n ariaLabel ||\n `${totalCount} ${totalCount === 1 ? 'member' : 'members'}`;\n\n // Group content\n const groupContent = (\n <>\n {visibleAvatars}\n {overflowIndicator}\n </>\n );\n\n // Render as button if group is interactive\n // Button inherently has role=\"button\", aria-label provides group context\n if (isGroupInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId}\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(avatarGroupVariants({ interactive: true }), className)}\n >\n {groupContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div with role=\"group\"\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId}\n role=\"group\"\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(avatarGroupVariants({ interactive: false }), className)}\n >\n {groupContent}\n </div>\n );\n }\n )\n);\n\nAvatarGroup.displayName = 'AvatarGroup';\n\nexport { AvatarGroup, avatarOverflowVariants };\nexport type { AvatarGroupProps };\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type { ReactNode } from 'react';\nimport type { PressEvent } from 'react-aria-components';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * Avatar size schema\n *\n * Size mappings (visual | touch target | overlap):\n * - sm: 32px | 44x44px | 8px\n * - default: 44px | 44x44px | 12px\n * - lg: 48px | 48x48px | 16px\n *\n * @see plan.md Clarification 6 (Overlap Scale)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 44x44px touch targets)\n */\nexport const AvatarSizeSchema = z.enum(['sm', 'default', 'lg']);\nexport type AvatarSize = z.infer<typeof AvatarSizeSchema>;\n\n/**\n * Avatar shape schema\n * - circle: Fully rounded (rounded-full)\n * - rounded: Rounded corners (rounded-lg)\n */\nexport const AvatarShapeSchema = z.enum(['circle', 'rounded']);\nexport type AvatarShape = z.infer<typeof AvatarShapeSchema>;\n\n/**\n * Avatar status schema for ring indicators\n *\n * Status ring colors:\n * - online: green (#22c55e)\n * - offline: gray (#6b7280)\n * - busy: red (#ef4444)\n * - away: yellow (#eab308)\n */\nexport const AvatarStatusSchema = z.enum(['online', 'offline', 'busy', 'away']);\nexport type AvatarStatus = z.infer<typeof AvatarStatusSchema>;\n\n/**\n * Overlap values by size (in pixels)\n * @see plan.md Clarification 6 (Overlap Scale)\n */\nexport const AVATAR_OVERLAP_BY_SIZE: Record<AvatarSize, number> = {\n sm: 8,\n default: 12,\n lg: 16,\n};\n\n/**\n * Avatar props schema extending BaseComponentProps\n *\n * @see React Aria Button: https://react-aria.adobe.com/Button\n * @see ShadCN Avatar: https://ui.shadcn.com/docs/components/avatar\n * @see plan.md Phase 1 (Type System)\n * @see constitution.md Principle IV (Accessibility First)\n */\nexport const AvatarPropsSchema = BaseComponentPropsSchema.extend({\n /** Image source URL */\n src: z.string().optional(),\n\n /**\n * Alt text for the avatar image (required for accessibility)\n * When actionable, this is used for the aria-label\n */\n alt: z.string().min(1, 'alt is required for accessibility'),\n\n /**\n * Name used to generate initials fallback\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\n name: z.string().optional(),\n\n /** Visual size variant (touch target always 44x44px min for sm/default) */\n size: AvatarSizeSchema.optional().default('default'),\n\n /** Shape variant */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /** Status ring indicator */\n status: AvatarStatusSchema.optional(),\n\n /**\n * Click handler for actionable avatar\n * When provided, renders as Button with 3-layer architecture\n * @see plan.md Clarification 2 (mutually exclusive with group onPress)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarProps {\n className?: string;\n children?: unknown;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'aria-hidden'?: boolean;\n 'data-testid'?: string;\n src?: string;\n alt: string;\n name?: string;\n size?: AvatarSize;\n shape?: AvatarShape;\n status?: AvatarStatus;\n onPress?: (e: PressEvent) => void;\n /** @internal Set by AvatarGroup, do not use directly */\n _groupIndex?: number;\n}\n\n/**\n * Avatar group props schema extending BaseComponentProps\n *\n * @see plan.md Clarification 1 (Group Border)\n * @see plan.md Clarification 5 (Stack Order - first on top)\n * @see plan.md Clarification 7 (Border Scope - groups only)\n */\nexport const AvatarGroupPropsSchema = BaseComponentPropsSchema.extend({\n /**\n * Maximum avatars to display before overflow (+X indicator)\n * Default: 3\n */\n max: z.number().int().positive().optional().default(3),\n\n /**\n * Shape inherited by all child avatars\n * Individual avatar shape props are ignored in groups\n */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /**\n * Size inherited by all child avatars\n * Individual avatar size props are ignored in groups\n * Also determines overlap spacing (sm: 8px, default: 12px, lg: 16px)\n */\n size: AvatarSizeSchema.optional().default('default'),\n\n /**\n * Click handler for the entire group\n * When provided, individual avatar onPress handlers are ignored\n * @see plan.md Clarification 2 (Mutually exclusive)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarGroupProps {\n className?: string;\n children?: ReactNode;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'data-testid'?: string;\n max?: number;\n shape?: AvatarShape;\n size?: AvatarSize;\n onPress?: (e: PressEvent) => void;\n}\n\n/**\n * Internal loading state for avatar images\n * @see plan.md Clarification 4 (Loading State)\n */\nexport type AvatarLoadingState = 'loading' | 'loaded' | 'error';\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Avatar/Avatar.tsx","../../../src/elements/Avatar/AvatarGroup.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/Avatar/Avatar.types.ts"],"names":["cn","inputs","twMerge","clsx","getInitials","name","parts","firstPart","lastPart","first","last","avatarOuterVariants","cva","avatarVisualVariants","Avatar","memo","forwardRef","src","alt","size","shape","status","onPress","className","id","dataTestId","ariaLabel","ariaLabelledBy","ariaDescribedBy","_groupIndex","ref","loadingState","setLoadingState","useState","handleImageLoad","useCallback","handleImageError","initials","isInteractive","computedAriaLabel","groupBrightness","visualContent","jsxs","jsx","AriaButton","OVERLAP_CLASS_BY_SIZE","avatarOverflowVariants","avatarGroupVariants","AvatarGroup","max","children","childArray","Children","totalCount","visibleCount","overflowCount","isGroupInteractive","overlapClass","child","isValidElement","visibleAvatars","index","childProps","zIndex","individualOnPress","cloneElement","overflowIndicator","groupContent","Fragment","BaseComponentPropsSchema","z","AvatarSizeSchema","AvatarShapeSchema","AvatarStatusSchema","AVATAR_OVERLAP_BY_SIZE","AvatarPropsSchema","AvatarGroupPropsSchema"],"mappings":"gRAcO,SAASA,CAAAA,CAAAA,GAAMC,CAAAA,CAA8B,CAClD,OAAOC,qBAAAA,CAAQC,SAAAA,CAAKF,CAAM,CAAC,CAC7B,CCkBA,SAASG,EAAAA,CAAYC,CAAAA,CAAkC,CACrD,GAAI,CAACA,CAAAA,EAAQ,CAACA,CAAAA,CAAK,IAAA,EAAK,CAAG,OAAO,EAAA,CAElC,IAAMC,CAAAA,CAAQD,CAAAA,CAAK,IAAA,EAAK,CAAE,MAAM,KAAK,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CACrD,GAAIC,CAAAA,CAAM,MAAA,GAAW,CAAA,CAAG,OAAO,EAAA,CAE/B,IAAMC,CAAAA,CAAYD,CAAAA,CAAM,CAAC,CAAA,CACzB,GAAIA,CAAAA,CAAM,MAAA,GAAW,CAAA,CACnB,OAAOC,CAAAA,CAAYA,CAAAA,CAAU,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,CAAI,EAAA,CAIzD,IAAMC,CAAAA,CAAWF,CAAAA,CAAMA,CAAAA,CAAM,MAAA,CAAS,CAAC,CAAA,CACjCG,CAAAA,CAAQF,CAAAA,CAAYA,CAAAA,CAAU,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,CAAI,EAAA,CACxDG,CAAAA,CAAOF,CAAAA,CAAWA,CAAAA,CAAS,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,CAAI,EAAA,CAC3D,OAAOC,CAAAA,CAAQC,CACjB,CAOO,IAAMC,CAAAA,CAAsBC,0BAAAA,CACjC,kDAAA,CACA,CACE,QAAA,CAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,2BAAA,CACJ,OAAA,CAAS,2BAAA,CACT,EAAA,CAAI,2BACN,CAAA,CACA,WAAA,CAAa,CACX,IAAA,CAAM,CACJ,gBAAA,CACA,4BAAA,CACA,sBAAA,CACA,kCAAA,CACA,6BAAA,CACA,oDAAA,CACA,kCAAA,CACA,0BAAA,CACA,sBAAA,CACA,6BACF,CAAA,CACA,KAAA,CAAO,EACT,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,SAAA,CACN,WAAA,CAAa,KACf,CACF,CACF,CAAA,CAMaC,CAAAA,CAAuBD,0BAAAA,CAClC,CACE,kDAAA,CACA,iBAAA,CACA,+BAAA,CACA,iCAAA,CACA,aAAA,CACA,aACF,CAAA,CACA,CACE,QAAA,CAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,iBAAA,CACJ,OAAA,CAAS,mBAAA,CACT,EAAA,CAAI,qBACN,CAAA,CACA,KAAA,CAAO,CACL,MAAA,CAAQ,cAAA,CACR,OAAA,CAAS,YACX,CAAA,CACA,MAAA,CAAQ,CACN,MAAA,CAAQ,gDAAA,CACR,OAAA,CAAS,iDAAA,CACT,IAAA,CAAM,8CAAA,CACN,IAAA,CAAM,8CACR,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,SAAA,CACN,KAAA,CAAO,QACT,CACF,CACF,CAAA,CAUME,CAAAA,CAASC,UAAAA,CACbC,gBAAAA,CACE,CACE,CACE,GAAA,CAAAC,CAAAA,CACA,GAAA,CAAAC,CAAAA,CACA,IAAA,CAAAb,EACA,IAAA,CAAAc,CAAAA,CAAO,SAAA,CACP,KAAA,CAAAC,CAAAA,CAAQ,QAAA,CACR,MAAA,CAAAC,CAAAA,CACA,OAAA,CAAAC,CAAAA,CACA,SAAA,CAAAC,CAAAA,CACA,EAAA,CAAAC,CAAAA,CACA,aAAA,CAAeC,CAAAA,CACf,YAAA,CAAcC,CAAAA,CACd,iBAAA,CAAmBC,CAAAA,CACnB,kBAAA,CAAoBC,CAAAA,CACpB,WAAA,CAAAC,CACF,CAAA,CACAC,CAAAA,GACiB,CAEjB,GAAM,CAACC,CAAAA,CAAcC,CAAe,CAAA,CAAIC,eACtChB,CAAAA,CAAM,SAAA,CAAY,OACpB,CAAA,CAEMiB,CAAAA,CAAkBC,iBAAAA,CAAY,IAAM,CACxCH,CAAAA,CAAgB,QAAQ,EAC1B,CAAA,CAAG,EAAE,CAAA,CAECI,CAAAA,CAAmBD,iBAAAA,CAAY,IAAM,CACzCH,CAAAA,CAAgB,OAAO,EACzB,CAAA,CAAG,EAAE,CAAA,CAECK,CAAAA,CAAWjC,EAAAA,CAAYC,CAAI,CAAA,CAC3BiC,CAAAA,CAAgB,CAAA,CAAQhB,CAAAA,CAGxBiB,CAAAA,CACJb,CAAAA,GACCY,CAAAA,EAAiBjB,CAAAA,CAAS,CAAA,EAAGH,CAAG,CAAA,EAAA,EAAKG,CAAM,CAAA,CAAA,CAAK,MAAA,CAAA,CAI7CmB,CAAAA,CACJX,CAAAA,GAAgB,MAAA,CAAY,CAAA,CAAIA,CAAAA,CAAc,GAAA,CAAO,MAAA,CAGjDY,CAAAA,CACJC,eAAAA,CAAC,KAAA,CAAA,CACC,aAAA,CAAY,eAAA,CACZ,SAAA,CAAW1C,CAAAA,CAAGa,CAAAA,CAAqB,CAAE,IAAA,CAAAM,CAAAA,CAAM,KAAA,CAAAC,CAAAA,CAAO,MAAA,CAAAC,CAAO,CAAC,CAAC,CAAA,CAC3D,KAAA,CAAOmB,CAAAA,GAAoB,MAAA,CAAY,CAAE,MAAA,CAAQ,CAAA,WAAA,EAAcA,CAAe,CAAA,CAAA,CAAI,CAAA,CAAI,MAAA,CAKrF,QAAA,CAAA,CAAAT,CAAAA,GAAiB,SAAA,EAChBY,cAAAA,CAAC,KAAA,CAAA,CACC,aAAA,CAAY,iBAAA,CACZ,SAAA,CAAW3C,CAAAA,CACT,8DAAA,CACAoB,CAAAA,GAAU,QAAA,CAAW,cAAA,CAAiB,YACxC,CAAA,CACA,aAAA,CAAY,MAAA,CACd,CAAA,CAIDH,CAAAA,EAAOc,IAAiB,OAAA,EAEvBY,cAAAA,CAAC,KAAA,CAAA,CACC,GAAA,CAAK1B,CAAAA,CACL,GAAA,CAAKqB,CAAAA,CAAgB,EAAA,CAAKpB,CAAAA,CAC1B,MAAA,CAAQgB,CAAAA,CACR,OAAA,CAASE,CAAAA,CACT,SAAA,CAAWpC,CAAAA,CACT,4BAAA,CACA+B,CAAAA,GAAiB,SAAA,EAAa,WAChC,CAAA,CACF,CAAA,CAIDA,CAAAA,GAAiB,OAAA,EAAWM,CAAAA,EAC3BM,cAAAA,CAAC,MAAA,CAAA,CAAK,aAAA,CAAY,MAAA,CAAQ,QAAA,CAAAN,CAAAA,CAAS,CAAA,CAAA,CAEvC,CAAA,CAIF,OAAIC,CAAAA,CAEAK,cAAAA,CAACC,0BAAAA,CAAA,CACC,GAAA,CAAKd,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,EAAc,cAAA,CAC3B,YAAA,CAAYc,CAAAA,EAAqBrB,CAAAA,CACjC,iBAAA,CAAiBS,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,OAAA,CAASN,CAAAA,CACT,SAAA,CAAWtB,CAAAA,CACTW,CAAAA,CAAoB,CAAE,IAAA,CAAAQ,CAAAA,CAAM,WAAA,CAAa,IAAK,CAAC,CAAA,CAC/CI,CACF,CAAA,CAEC,QAAA,CAAAkB,CAAAA,CACH,CAAA,CAMFE,cAAAA,CAAC,KAAA,CAAA,CACC,GAAA,CAAKb,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,EAAc,cAAA,CAC3B,YAAA,CAAYC,CAAAA,CACZ,iBAAA,CAAiBC,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,SAAA,CAAW5B,CAAAA,CACTW,CAAAA,CAAoB,CAAE,IAAA,CAAAQ,CAAAA,CAAM,WAAA,CAAa,KAAM,CAAC,CAAA,CAChDI,CACF,CAAA,CAEC,QAAA,CAAAkB,CAAAA,CACH,CAEJ,CACF,CACF,EAEA3B,CAAAA,CAAO,WAAA,CAAc,QAAA,CCxOrB,IAAM+B,EAAAA,CAAoD,CACxD,EAAA,CAAI,OAAA,CACJ,OAAA,CAAS,OAAA,CACT,EAAA,CAAI,OACN,CAAA,CAKMC,CAAAA,CAAyBlC,0BAAAA,CAC7B,CACE,kDAAA,CACA,uBAAA,CACA,oCAAA,CACA,eAAA,CACA,aAAA,CACA,0CACF,CAAA,CACA,CACE,QAAA,CAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,iBAAA,CACJ,OAAA,CAAS,mBAAA,CACT,EAAA,CAAI,qBACN,EACA,KAAA,CAAO,CACL,MAAA,CAAQ,cAAA,CACR,OAAA,CAAS,YACX,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,SAAA,CACN,KAAA,CAAO,QACT,CACF,CACF,CAAA,CAKMmC,CAAAA,CAAsBnC,0BAAAA,CAAI,0BAAA,CAA4B,CAC1D,QAAA,CAAU,CACR,WAAA,CAAa,CACX,IAAA,CAAM,CACJ,gBAAA,CACA,4BAAA,CACA,sBAAA,CACA,kCAAA,CACA,6BAAA,CACA,oDACF,CAAA,CACA,KAAA,CAAO,EACT,CACF,CAAA,CACA,eAAA,CAAiB,CACf,WAAA,CAAa,KACf,CACF,CAAC,CAAA,CAMKoC,CAAAA,CAAcjC,UAAAA,CAClBC,gBAAAA,CACE,CACE,CACE,GAAA,CAAAiC,CAAAA,CAAM,CAAA,CACN,KAAA,CAAA7B,CAAAA,CAAQ,QAAA,CACR,IAAA,CAAAD,CAAAA,CAAO,SAAA,CACP,OAAA,CAAAG,CAAAA,CACA,SAAA,CAAAC,CAAAA,CACA,QAAA,CAAA2B,EACA,EAAA,CAAA1B,CAAAA,CACA,aAAA,CAAeC,CAAAA,CACf,YAAA,CAAcC,CAAAA,CACd,iBAAA,CAAmBC,CAAAA,CACnB,kBAAA,CAAoBC,CACtB,CAAA,CACAE,CAAAA,GACiB,CACjB,IAAMqB,CAAAA,CAAaC,cAAAA,CAAS,OAAA,CAAQF,CAAQ,CAAA,CACtCG,CAAAA,CAAaF,CAAAA,CAAW,MAAA,CACxBG,CAAAA,CAAe,IAAA,CAAK,GAAA,CAAID,CAAAA,CAAYJ,CAAG,CAAA,CACvCM,CAAAA,CAAgBF,CAAAA,CAAaC,CAAAA,CAE7BE,CAAAA,CAAqB,EAAQlC,CAAAA,CAC7BmC,CAAAA,CAAeZ,EAAAA,CAAsB1B,CAAI,CAAA,CAI3C,OAAA,CAAQ,GAAA,CAAI,QAAA,GAAa,YAAA,EAAgBqC,CAAAA,EAC3CJ,cAAAA,CAAS,OAAA,CAAQF,CAAAA,CAAWQ,CAAAA,EAAU,CAElCC,oBAAAA,CAAeD,CAAK,CAAA,EACnBA,CAAAA,CAAM,KAAA,CAAgC,OAAA,EAEvC,OAAA,CAAQ,IAAA,CACN,0KAEF,EAEJ,CAAC,CAAA,CAIH,IAAME,CAAAA,CAAiBT,CAAAA,CAAW,KAAA,CAAM,CAAA,CAAGG,CAAY,CAAA,CAAE,GAAA,CAAI,CAACI,CAAAA,CAAOG,CAAAA,GAAU,CAC7E,GAAI,CAACF,oBAAAA,CAAeD,CAAK,CAAA,CAAG,OAAO,IAAA,CAGnC,IAAMI,CAAAA,CAAaJ,CAAAA,CAAM,KAAA,CAQnBK,CAAAA,CAAST,CAAAA,CAAeO,CAAAA,CAIxBG,CAAAA,CAAoBR,CAAAA,CACtB,MAAA,CACAM,CAAAA,CAAW,OAAA,CAEf,OACEnB,cAAAA,CAAC,KAAA,CAAA,CAEC,aAAA,CAAY,mBAAA,CACZ,SAAA,CAAW3C,CAAAA,CACT,UAAA,CAEA,0CAAA,CACAoB,CAAAA,GAAU,QAAA,CAAW,cAAA,CAAiB,YAAA,CAEtCyC,CAAAA,CAAQ,CAAA,EAAKJ,CAAAA,CAGb,CAACD,CAAAA,EAAsB,CACrB,kCAAA,CACA,0BAAA,CACA,sBAAA,CACA,gCAAA,CACA,wBACF,CACF,CAAA,CACA,KAAA,CAAO,CAAE,MAAA,CAAAO,CAAO,CAAA,CAEf,QAAA,CAAAE,kBAAAA,CAAaP,CAAAA,CAAuB,CACnC,IAAA,CAAAvC,CAAAA,CACA,KAAA,CAAAC,CAAAA,CAEA,QAAS4C,CAAAA,CAET,WAAA,CAAaH,CAAAA,CAEb,SAAA,CAAW7D,CAAAA,CACT8D,CAAAA,CAAW,SAAA,CAEX,mBACF,CACF,CAA+B,CAAA,CAAA,CAlC1BD,CAmCP,CAEJ,CAAC,CAAA,CAGKK,CAAAA,CACJX,CAAAA,CAAgB,CAAA,CACdb,eAAAA,CAAC,KAAA,CAAA,CACC,aAAA,CAAY,iBAAA,CACZ,SAAA,CAAW1C,CAAAA,CACT8C,CAAAA,CAAuB,CAAE,IAAA,CAAA3B,CAAAA,CAAM,KAAA,CAAAC,CAAM,CAAC,CAAA,CACtCqC,CACF,CAAA,CACA,KAAA,CAAO,CAAE,MAAA,CAAQ,CAAE,CAAA,CACnB,aAAA,CAAY,MAAA,CACb,QAAA,CAAA,CAAA,GAAA,CACGF,CAAAA,CAAAA,CACJ,CAAA,CACE,IAAA,CAGAhB,CAAAA,CACJb,CAAAA,EACA,CAAA,EAAG2B,CAAU,CAAA,CAAA,EAAIA,CAAAA,GAAe,CAAA,CAAI,QAAA,CAAW,SAAS,CAAA,CAAA,CAGpDc,CAAAA,CACJzB,eAAAA,CAAA0B,mBAAAA,CAAA,CACG,QAAA,CAAA,CAAAR,CAAAA,CACAM,CAAAA,CAAAA,CACH,CAAA,CAKF,OAAIV,CAAAA,CAEAb,eAACC,0BAAAA,CAAA,CACC,GAAA,CAAKd,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,CACb,YAAA,CAAYc,CAAAA,CACZ,iBAAA,CAAiBZ,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,OAAA,CAASN,CAAAA,CACT,SAAA,CAAWtB,CAAAA,CAAG+C,CAAAA,CAAoB,CAAE,WAAA,CAAa,IAAK,CAAC,CAAA,CAAGxB,CAAS,CAAA,CAElE,QAAA,CAAA4C,CAAAA,CACH,CAAA,CAMFxB,cAAAA,CAAC,KAAA,CAAA,CACC,GAAA,CAAKb,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,CACb,IAAA,CAAK,OAAA,CACL,YAAA,CAAYc,CAAAA,CACZ,iBAAA,CAAiBZ,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,SAAA,CAAW5B,CAAAA,CAAG+C,CAAAA,CAAoB,CAAE,WAAA,CAAa,KAAM,CAAC,CAAA,CAAGxB,CAAS,CAAA,CAEnE,QAAA,CAAA4C,CAAAA,CACH,CAEJ,CACF,CACF,EAEAnB,CAAAA,CAAY,WAAA,CAAc,aAAA,CCpQnB,IAAMqB,CAAAA,CAA2BC,KAAAA,CAAE,MAAA,CAAO,CAE/C,SAAA,CAAWA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CAG/B,QAAA,CAAUA,KAAAA,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAC3B,EAAA,CAAIA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CAGxB,YAAA,CAAcA,KAAAA,CAAE,QAAO,CAAE,QAAA,EAAS,CAClC,iBAAA,CAAmBA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CACvC,kBAAA,CAAoBA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CACxC,WAAA,CAAaA,KAAAA,CAAE,IAAA,CAAK,CAAC,KAAA,CAAO,QAAA,CAAU,WAAW,CAAC,CAAA,CAAE,QAAA,EAAS,CAC7D,aAAA,CAAeA,KAAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS,CAGpC,cAAeA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAC5B,CAAC,CAAA,CCVM,IAAMC,CAAAA,CAAmBD,KAAAA,CAAE,IAAA,CAAK,CAAC,IAAA,CAAM,SAAA,CAAW,IAAI,CAAC,CAAA,CAQjDE,CAAAA,CAAoBF,KAAAA,CAAE,IAAA,CAAK,CAAC,QAAA,CAAU,SAAS,CAAC,CAAA,CAYhDG,CAAAA,CAAqBH,KAAAA,CAAE,IAAA,CAAK,CAAC,QAAA,CAAU,SAAA,CAAW,MAAA,CAAQ,MAAM,CAAC,CAAA,CAOjEI,EAAAA,CAAqD,CAChE,EAAA,CAAI,CAAA,CACJ,OAAA,CAAS,EAAA,CACT,EAAA,CAAI,EACN,CAAA,CAUaC,EAAAA,CAAoBN,CAAAA,CAAyB,MAAA,CAAO,CAE/D,GAAA,CAAKC,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CAMzB,GAAA,CAAKA,KAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAA,CAAG,mCAAmC,CAAA,CAQ1D,IAAA,CAAMA,KAAAA,CAAE,QAAO,CAAE,QAAA,EAAS,CAG1B,IAAA,CAAMC,CAAAA,CAAiB,QAAA,EAAS,CAAE,OAAA,CAAQ,SAAS,CAAA,CAGnD,KAAA,CAAOC,CAAAA,CAAkB,QAAA,EAAS,CAAE,OAAA,CAAQ,QAAQ,CAAA,CAGpD,MAAA,CAAQC,CAAAA,CAAmB,QAAA,EAAS,CAOpC,OAAA,CAASH,KAAAA,CAAE,QAAA,EAAS,CAAE,QAAA,EACxB,CAAC,CAAA,CA6BYM,EAAAA,CAAyBP,CAAAA,CAAyB,MAAA,CAAO,CAKpE,GAAA,CAAKC,KAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA,CAMrD,KAAA,CAAOE,CAAAA,CAAkB,QAAA,EAAS,CAAE,OAAA,CAAQ,QAAQ,CAAA,CAOpD,IAAA,CAAMD,CAAAA,CAAiB,QAAA,EAAS,CAAE,OAAA,CAAQ,SAAS,CAAA,CAOnD,OAAA,CAASD,KAAAA,CAAE,QAAA,EAAS,CAAE,QAAA,EACxB,CAAC","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Accessible avatar with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant)\n * - Layer 2: Visual Avatar (configurable size, shape, status ring)\n * - Layer 3: Content (image, initials, skeleton shimmer)\n *\n * @see plan.md for implementation details\n * @see avatar-prd.md for requirements\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useState, useCallback, type ReactElement } from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type {\n AvatarProps,\n AvatarSize,\n AvatarShape,\n AvatarStatus,\n AvatarLoadingState,\n} from './Avatar.types';\n\n/**\n * Extract initials from a name\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\nfunction getInitials(name: string | undefined): string {\n if (!name || !name.trim()) return '';\n\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n\n const firstPart = parts[0];\n if (parts.length === 1) {\n return firstPart ? firstPart.charAt(0).toUpperCase() : '';\n }\n\n // First and last initials (max 2 characters)\n const lastPart = parts[parts.length - 1];\n const first = firstPart ? firstPart.charAt(0).toUpperCase() : '';\n const last = lastPart ? lastPart.charAt(0).toUpperCase() : '';\n return first + last;\n}\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n */\nexport const avatarOuterVariants = cva(\n 'relative inline-flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'min-h-[44px] min-w-[44px]',\n default: 'min-h-[44px] min-w-[44px]',\n lg: 'min-h-[48px] min-w-[48px]',\n },\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-105',\n ],\n false: '',\n },\n },\n defaultVariants: {\n size: 'default',\n interactive: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual avatar appearance\n * Provides the visual appearance with configurable size, shape, and status ring\n */\nexport const avatarVisualVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'overflow-hidden',\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'font-medium',\n 'select-none',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n status: {\n online: 'ring-[3px] ring-[var(--status-online,#22c55e)]',\n offline: 'ring-[3px] ring-[var(--status-offline,#6b7280)]',\n busy: 'ring-[3px] ring-[var(--status-busy,#ef4444)]',\n away: 'ring-[3px] ring-[var(--status-away,#eab308)]',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Displays user avatar with image or initials fallback\n *\n * Layer 1: Touch Target - 44x44px WCAG AAA compliant\n * Layer 2: Visual Avatar - configurable appearance\n * Layer 3: Content - image, initials, or loading skeleton\n */\nconst Avatar = memo(\n forwardRef<HTMLDivElement, AvatarProps>(\n (\n {\n src,\n alt,\n name,\n size = 'default',\n shape = 'circle',\n status,\n onPress,\n className,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n _groupIndex,\n },\n ref\n ): ReactElement => {\n // Loading state machine: loading → loaded | error\n const [loadingState, setLoadingState] = useState<AvatarLoadingState>(\n src ? 'loading' : 'error'\n );\n\n const handleImageLoad = useCallback(() => {\n setLoadingState('loaded');\n }, []);\n\n const handleImageError = useCallback(() => {\n setLoadingState('error');\n }, []);\n\n const initials = getInitials(name);\n const isInteractive = Boolean(onPress);\n\n // Build aria-label for actionable avatars with status\n const computedAriaLabel =\n ariaLabel ||\n (isInteractive && status ? `${alt}, ${status}` : undefined);\n\n // Calculate brightness for group darkening effect\n // First avatar (index 0) = 100%, each subsequent gets 5% darker\n const groupBrightness =\n _groupIndex !== undefined ? 1 - _groupIndex * 0.05 : undefined;\n\n // Common visual layer content\n const visualContent = (\n <div\n data-testid=\"avatar-visual\"\n className={cn(avatarVisualVariants({ size, shape, status }))}\n style={groupBrightness !== undefined ? { filter: `brightness(${groupBrightness})` } : undefined}\n >\n {/* Layer 3: Content */}\n\n {/* Skeleton shimmer while loading */}\n {loadingState === 'loading' && (\n <div\n data-testid=\"avatar-skeleton\"\n className={cn(\n 'absolute inset-0 animate-pulse bg-[var(--accent-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg'\n )}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Image (hidden during loading, removed on error) */}\n {src && loadingState !== 'error' && (\n \n <img\n src={src}\n alt={isInteractive ? '' : alt}\n onLoad={handleImageLoad}\n onError={handleImageError}\n className={cn(\n 'h-full w-full object-cover',\n loadingState === 'loading' && 'invisible'\n )}\n />\n )}\n\n {/* Initials fallback (shown when no src or on error) */}\n {loadingState === 'error' && initials && (\n <span aria-hidden=\"true\">{initials}</span>\n )}\n </div>\n );\n\n // Render as button if interactive\n if (isInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={computedAriaLabel || alt}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(\n avatarOuterVariants({ size, interactive: true }),\n className\n )}\n >\n {visualContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(\n avatarOuterVariants({ size, interactive: false }),\n className\n )}\n >\n {visualContent}\n </div>\n );\n }\n )\n);\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar };\nexport type { AvatarProps, AvatarSize, AvatarShape, AvatarStatus };\n","'use client';\n\n/**\n * AvatarGroup Component\n * Displays multiple avatars with overlap, overflow indicator, and optional actionability\n *\n * Features:\n * - Overlapping display with configurable overlap\n * - Overflow indicator (+X) when exceeding max\n * - 2px border for visual separation\n * - First avatar on top (highest z-index)\n * - Mutually exclusive actions (group OR individual, not both)\n * - Shape/size inheritance to children\n *\n * @see plan.md Clarifications 1, 2, 5, 6, 7\n * @see avatar-prd.md US-5, US-7\n */\n\nimport {\n forwardRef,\n memo,\n Children,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type { AvatarGroupProps, AvatarSize, AvatarShape } from './Avatar.types';\n\n/**\n * Overlap class by size (proportional)\n * sm: 8px (-ml-2), default: 12px (-ml-3), lg: 16px (-ml-4)\n */\nconst OVERLAP_CLASS_BY_SIZE: Record<AvatarSize, string> = {\n sm: '-ml-2',\n default: '-ml-3',\n lg: '-ml-4',\n};\n\n/**\n * Overflow indicator variants (the +X badge)\n */\nconst avatarOverflowVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'bg-[var(--secondary)]',\n 'text-[var(--secondary-foreground)]',\n 'font-semibold',\n 'select-none',\n 'border-2 border-[var(--page-background)]',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Group outer variants\n */\nconst avatarGroupVariants = cva('inline-flex items-center', {\n variants: {\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n ],\n false: '',\n },\n },\n defaultVariants: {\n interactive: false,\n },\n});\n\n/**\n * AvatarGroup Component\n * Groups multiple Avatar components with overlap and overflow handling\n */\nconst AvatarGroup = memo(\n forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n max = 3,\n shape = 'circle',\n size = 'default',\n onPress,\n className,\n children,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n },\n ref\n ): ReactElement => {\n const childArray = Children.toArray(children);\n const totalCount = childArray.length;\n const visibleCount = Math.min(totalCount, max);\n const overflowCount = totalCount - visibleCount;\n\n const isGroupInteractive = Boolean(onPress);\n const overlapClass = OVERLAP_CLASS_BY_SIZE[size];\n\n // Warn about mutually exclusive actions (Clarification 2)\n // Uses !== 'production' to work in both development and test modes\n if (process.env.NODE_ENV !== 'production' && isGroupInteractive) {\n Children.forEach(children, (child) => {\n if (\n isValidElement(child) &&\n (child.props as { onPress?: unknown }).onPress\n ) {\n console.warn(\n '[AvatarGroup] onPress on individual avatars is ignored when AvatarGroup has onPress. ' +\n 'Actions are mutually exclusive - use group onPress OR individual onPress, not both.'\n );\n }\n });\n }\n\n // Build visible avatars with proper styling\n const visibleAvatars = childArray.slice(0, visibleCount).map((child, index) => {\n if (!isValidElement(child)) return null;\n\n // Clone child with inherited props and group styling\n const childProps = child.props as {\n size?: AvatarSize;\n shape?: AvatarShape;\n onPress?: unknown;\n className?: string;\n };\n\n // First avatar has highest z-index (Clarification 5)\n const zIndex = visibleCount - index;\n\n // Determine if individual avatar is interactive\n // If group has onPress, individual onPress is ignored\n const individualOnPress = isGroupInteractive\n ? undefined\n : childProps.onPress;\n\n return (\n <div\n key={index}\n data-testid=\"avatar-group-item\"\n className={cn(\n 'relative',\n // 2px border for visual separation (Clarification 1 & 7)\n 'border-2 border-[var(--page-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg',\n // Overlap negative margin (except first)\n index > 0 && overlapClass,\n // Hover lift animation when group is NOT actionable\n // Individual avatars get lift effect regardless of their own onPress\n !isGroupInteractive && [\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-[1.15]',\n 'motion-safe:hover:z-10',\n ]\n )}\n style={{ zIndex }}\n >\n {cloneElement(child as ReactElement, {\n size,\n shape,\n // Individual onPress is ignored if group has onPress\n onPress: individualOnPress,\n // Pass group index for progressive darkening\n _groupIndex: index,\n // Remove outer wrapper styling since we handle it\n className: cn(\n childProps.className,\n // Remove min dimensions since group item handles touch target\n '!min-h-0 !min-w-0'\n ),\n } as Partial<typeof childProps>)}\n </div>\n );\n });\n\n // Overflow indicator (+X)\n const overflowIndicator =\n overflowCount > 0 ? (\n <div\n data-testid=\"avatar-overflow\"\n className={cn(\n avatarOverflowVariants({ size, shape }),\n overlapClass\n )}\n style={{ zIndex: 0 }}\n aria-hidden=\"true\"\n >\n +{overflowCount}\n </div>\n ) : null;\n\n // Auto-generate aria-label if not provided\n const computedAriaLabel =\n ariaLabel ||\n `${totalCount} ${totalCount === 1 ? 'member' : 'members'}`;\n\n // Group content\n const groupContent = (\n <>\n {visibleAvatars}\n {overflowIndicator}\n </>\n );\n\n // Render as button if group is interactive\n // Button inherently has role=\"button\", aria-label provides group context\n if (isGroupInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId}\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(avatarGroupVariants({ interactive: true }), className)}\n >\n {groupContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div with role=\"group\"\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId}\n role=\"group\"\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(avatarGroupVariants({ interactive: false }), className)}\n >\n {groupContent}\n </div>\n );\n }\n )\n);\n\nAvatarGroup.displayName = 'AvatarGroup';\n\nexport { AvatarGroup, avatarOverflowVariants };\nexport type { AvatarGroupProps };\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type { ReactNode } from 'react';\nimport type { PressEvent } from 'react-aria-components';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * Avatar size schema\n *\n * Size mappings (visual | touch target | overlap):\n * - sm: 32px | 44x44px | 8px\n * - default: 44px | 44x44px | 12px\n * - lg: 48px | 48x48px | 16px\n *\n * @see plan.md Clarification 6 (Overlap Scale)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 44x44px touch targets)\n */\nexport const AvatarSizeSchema = z.enum(['sm', 'default', 'lg']);\nexport type AvatarSize = z.infer<typeof AvatarSizeSchema>;\n\n/**\n * Avatar shape schema\n * - circle: Fully rounded (rounded-full)\n * - rounded: Rounded corners (rounded-lg)\n */\nexport const AvatarShapeSchema = z.enum(['circle', 'rounded']);\nexport type AvatarShape = z.infer<typeof AvatarShapeSchema>;\n\n/**\n * Avatar status schema for ring indicators\n *\n * Status ring colors:\n * - online: green (#22c55e)\n * - offline: gray (#6b7280)\n * - busy: red (#ef4444)\n * - away: yellow (#eab308)\n */\nexport const AvatarStatusSchema = z.enum(['online', 'offline', 'busy', 'away']);\nexport type AvatarStatus = z.infer<typeof AvatarStatusSchema>;\n\n/**\n * Overlap values by size (in pixels)\n * @see plan.md Clarification 6 (Overlap Scale)\n */\nexport const AVATAR_OVERLAP_BY_SIZE: Record<AvatarSize, number> = {\n sm: 8,\n default: 12,\n lg: 16,\n};\n\n/**\n * Avatar props schema extending BaseComponentProps\n *\n * @see React Aria Button: https://react-aria.adobe.com/Button\n * @see ShadCN Avatar: https://ui.shadcn.com/docs/components/avatar\n * @see plan.md Phase 1 (Type System)\n * @see constitution.md Principle IV (Accessibility First)\n */\nexport const AvatarPropsSchema = BaseComponentPropsSchema.extend({\n /** Image source URL */\n src: z.string().optional(),\n\n /**\n * Alt text for the avatar image (required for accessibility)\n * When actionable, this is used for the aria-label\n */\n alt: z.string().min(1, 'alt is required for accessibility'),\n\n /**\n * Name used to generate initials fallback\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\n name: z.string().optional(),\n\n /** Visual size variant (touch target always 44x44px min for sm/default) */\n size: AvatarSizeSchema.optional().default('default'),\n\n /** Shape variant */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /** Status ring indicator */\n status: AvatarStatusSchema.optional(),\n\n /**\n * Click handler for actionable avatar\n * When provided, renders as Button with 3-layer architecture\n * @see plan.md Clarification 2 (mutually exclusive with group onPress)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarProps {\n className?: string;\n children?: unknown;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'aria-hidden'?: boolean;\n 'data-testid'?: string;\n src?: string;\n alt: string;\n name?: string;\n size?: AvatarSize;\n shape?: AvatarShape;\n status?: AvatarStatus;\n onPress?: (e: PressEvent) => void;\n /** @internal Set by AvatarGroup, do not use directly */\n _groupIndex?: number;\n}\n\n/**\n * Avatar group props schema extending BaseComponentProps\n *\n * @see plan.md Clarification 1 (Group Border)\n * @see plan.md Clarification 5 (Stack Order - first on top)\n * @see plan.md Clarification 7 (Border Scope - groups only)\n */\nexport const AvatarGroupPropsSchema = BaseComponentPropsSchema.extend({\n /**\n * Maximum avatars to display before overflow (+X indicator)\n * Default: 3\n */\n max: z.number().int().positive().optional().default(3),\n\n /**\n * Shape inherited by all child avatars\n * Individual avatar shape props are ignored in groups\n */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /**\n * Size inherited by all child avatars\n * Individual avatar size props are ignored in groups\n * Also determines overlap spacing (sm: 8px, default: 12px, lg: 16px)\n */\n size: AvatarSizeSchema.optional().default('default'),\n\n /**\n * Click handler for the entire group\n * When provided, individual avatar onPress handlers are ignored\n * @see plan.md Clarification 2 (Mutually exclusive)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarGroupProps {\n className?: string;\n children?: ReactNode;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'data-testid'?: string;\n max?: number;\n shape?: AvatarShape;\n size?: AvatarSize;\n onPress?: (e: PressEvent) => void;\n}\n\n/**\n * Internal loading state for avatar images\n * @see plan.md Clarification 4 (Loading State)\n */\nexport type AvatarLoadingState = 'loading' | 'loaded' | 'error';\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Avatar/Avatar.tsx","../../../src/elements/Avatar/AvatarGroup.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/Avatar/Avatar.types.ts"],"names":["cn","inputs","twMerge","clsx","getInitials","name","parts","firstPart","lastPart","first","last","avatarOuterVariants","cva","avatarVisualVariants","Avatar","memo","forwardRef","src","alt","size","shape","status","onPress","className","id","dataTestId","ariaLabel","ariaLabelledBy","ariaDescribedBy","_groupIndex","ref","loadingState","setLoadingState","useState","handleImageLoad","useCallback","handleImageError","initials","isInteractive","computedAriaLabel","groupBrightness","visualContent","jsxs","jsx","AriaButton","OVERLAP_CLASS_BY_SIZE","avatarOverflowVariants","avatarGroupVariants","AvatarGroup","max","children","childArray","Children","totalCount","visibleCount","overflowCount","isGroupInteractive","overlapClass","child","isValidElement","visibleAvatars","index","childProps","zIndex","individualOnPress","cloneElement","overflowIndicator","groupContent","Fragment","BaseComponentPropsSchema","z","AvatarSizeSchema","AvatarShapeSchema","AvatarStatusSchema","AVATAR_OVERLAP_BY_SIZE","AvatarPropsSchema","AvatarGroupPropsSchema"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Accessible avatar with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant)\n * - Layer 2: Visual Avatar (configurable size, shape, status ring)\n * - Layer 3: Content (image, initials, skeleton shimmer)\n *\n * @see plan.md for implementation details\n * @see avatar-prd.md for requirements\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useState, useCallback, type ReactElement } from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type {\n AvatarProps,\n AvatarSize,\n AvatarShape,\n AvatarStatus,\n AvatarLoadingState,\n} from './Avatar.types';\n\n/**\n * Extract initials from a name\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\nfunction getInitials(name: string | undefined): string {\n if (!name || !name.trim()) return '';\n\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n\n const firstPart = parts[0];\n if (parts.length === 1) {\n return firstPart ? firstPart.charAt(0).toUpperCase() : '';\n }\n\n // First and last initials (max 2 characters)\n const lastPart = parts[parts.length - 1];\n const first = firstPart ? firstPart.charAt(0).toUpperCase() : '';\n const last = lastPart ? lastPart.charAt(0).toUpperCase() : '';\n return first + last;\n}\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n */\nexport const avatarOuterVariants = cva(\n 'relative inline-flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'min-h-[44px] min-w-[44px]',\n default: 'min-h-[44px] min-w-[44px]',\n lg: 'min-h-[48px] min-w-[48px]',\n },\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-105',\n ],\n false: '',\n },\n },\n defaultVariants: {\n size: 'default',\n interactive: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual avatar appearance\n * Provides the visual appearance with configurable size, shape, and status ring\n */\nexport const avatarVisualVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'overflow-hidden',\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'font-medium',\n 'select-none',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n status: {\n online: 'ring-[3px] ring-[var(--status-online,#22c55e)]',\n offline: 'ring-[3px] ring-[var(--status-offline,#6b7280)]',\n busy: 'ring-[3px] ring-[var(--status-busy,#ef4444)]',\n away: 'ring-[3px] ring-[var(--status-away,#eab308)]',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Displays user avatar with image or initials fallback\n *\n * Layer 1: Touch Target - 44x44px WCAG AAA compliant\n * Layer 2: Visual Avatar - configurable appearance\n * Layer 3: Content - image, initials, or loading skeleton\n */\nconst Avatar = memo(\n forwardRef<HTMLDivElement, AvatarProps>(\n (\n {\n src,\n alt,\n name,\n size = 'default',\n shape = 'circle',\n status,\n onPress,\n className,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n _groupIndex,\n },\n ref\n ): ReactElement => {\n // Loading state machine: loading → loaded | error\n const [loadingState, setLoadingState] = useState<AvatarLoadingState>(\n src ? 'loading' : 'error'\n );\n\n const handleImageLoad = useCallback(() => {\n setLoadingState('loaded');\n }, []);\n\n const handleImageError = useCallback(() => {\n setLoadingState('error');\n }, []);\n\n const initials = getInitials(name);\n const isInteractive = Boolean(onPress);\n\n // Build aria-label for actionable avatars with status\n const computedAriaLabel =\n ariaLabel ||\n (isInteractive && status ? `${alt}, ${status}` : undefined);\n\n // Calculate brightness for group darkening effect\n // First avatar (index 0) = 100%, each subsequent gets 5% darker\n const groupBrightness =\n _groupIndex !== undefined ? 1 - _groupIndex * 0.05 : undefined;\n\n // Common visual layer content\n const visualContent = (\n <div\n data-testid=\"avatar-visual\"\n className={cn(avatarVisualVariants({ size, shape, status }))}\n style={groupBrightness !== undefined ? { filter: `brightness(${groupBrightness})` } : undefined}\n >\n {/* Layer 3: Content */}\n\n {/* Skeleton shimmer while loading */}\n {loadingState === 'loading' && (\n <div\n data-testid=\"avatar-skeleton\"\n className={cn(\n 'absolute inset-0 animate-pulse bg-[var(--accent-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg'\n )}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Image (hidden during loading, removed on error) */}\n {src && loadingState !== 'error' && (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- onLoad/onError are lifecycle events, not interactions\n <img\n src={src}\n alt={isInteractive ? '' : alt}\n onLoad={handleImageLoad}\n onError={handleImageError}\n className={cn(\n 'h-full w-full object-cover',\n loadingState === 'loading' && 'invisible'\n )}\n />\n )}\n\n {/* Initials fallback (shown when no src or on error) */}\n {loadingState === 'error' && initials && (\n <span aria-hidden=\"true\">{initials}</span>\n )}\n </div>\n );\n\n // Render as button if interactive\n if (isInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={computedAriaLabel || alt}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(\n avatarOuterVariants({ size, interactive: true }),\n className\n )}\n >\n {visualContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(\n avatarOuterVariants({ size, interactive: false }),\n className\n )}\n >\n {visualContent}\n </div>\n );\n }\n )\n);\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar };\nexport type { AvatarProps, AvatarSize, AvatarShape, AvatarStatus };\n","'use client';\n\n/**\n * AvatarGroup Component\n * Displays multiple avatars with overlap, overflow indicator, and optional actionability\n *\n * Features:\n * - Overlapping display with configurable overlap\n * - Overflow indicator (+X) when exceeding max\n * - 2px border for visual separation\n * - First avatar on top (highest z-index)\n * - Mutually exclusive actions (group OR individual, not both)\n * - Shape/size inheritance to children\n *\n * @see plan.md Clarifications 1, 2, 5, 6, 7\n * @see avatar-prd.md US-5, US-7\n */\n\nimport {\n forwardRef,\n memo,\n Children,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type { AvatarGroupProps, AvatarSize, AvatarShape } from './Avatar.types';\n\n/**\n * Overlap class by size (proportional)\n * sm: 8px (-ml-2), default: 12px (-ml-3), lg: 16px (-ml-4)\n */\nconst OVERLAP_CLASS_BY_SIZE: Record<AvatarSize, string> = {\n sm: '-ml-2',\n default: '-ml-3',\n lg: '-ml-4',\n};\n\n/**\n * Overflow indicator variants (the +X badge)\n */\nconst avatarOverflowVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'bg-[var(--secondary)]',\n 'text-[var(--secondary-foreground)]',\n 'font-semibold',\n 'select-none',\n 'border-2 border-[var(--page-background)]',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Group outer variants\n */\nconst avatarGroupVariants = cva('inline-flex items-center', {\n variants: {\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n ],\n false: '',\n },\n },\n defaultVariants: {\n interactive: false,\n },\n});\n\n/**\n * AvatarGroup Component\n * Groups multiple Avatar components with overlap and overflow handling\n */\nconst AvatarGroup = memo(\n forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n max = 3,\n shape = 'circle',\n size = 'default',\n onPress,\n className,\n children,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n },\n ref\n ): ReactElement => {\n const childArray = Children.toArray(children);\n const totalCount = childArray.length;\n const visibleCount = Math.min(totalCount, max);\n const overflowCount = totalCount - visibleCount;\n\n const isGroupInteractive = Boolean(onPress);\n const overlapClass = OVERLAP_CLASS_BY_SIZE[size];\n\n // Warn about mutually exclusive actions (Clarification 2)\n // Uses !== 'production' to work in both development and test modes\n if (process.env.NODE_ENV !== 'production' && isGroupInteractive) {\n Children.forEach(children, (child) => {\n if (\n isValidElement(child) &&\n (child.props as { onPress?: unknown }).onPress\n ) {\n console.warn(\n '[AvatarGroup] onPress on individual avatars is ignored when AvatarGroup has onPress. ' +\n 'Actions are mutually exclusive - use group onPress OR individual onPress, not both.'\n );\n }\n });\n }\n\n // Build visible avatars with proper styling\n const visibleAvatars = childArray.slice(0, visibleCount).map((child, index) => {\n if (!isValidElement(child)) return null;\n\n // Clone child with inherited props and group styling\n const childProps = child.props as {\n size?: AvatarSize;\n shape?: AvatarShape;\n onPress?: unknown;\n className?: string;\n };\n\n // First avatar has highest z-index (Clarification 5)\n const zIndex = visibleCount - index;\n\n // Determine if individual avatar is interactive\n // If group has onPress, individual onPress is ignored\n const individualOnPress = isGroupInteractive\n ? undefined\n : childProps.onPress;\n\n return (\n <div\n key={index}\n data-testid=\"avatar-group-item\"\n className={cn(\n 'relative',\n // 2px border for visual separation (Clarification 1 & 7)\n 'border-2 border-[var(--page-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg',\n // Overlap negative margin (except first)\n index > 0 && overlapClass,\n // Hover lift animation when group is NOT actionable\n // Individual avatars get lift effect regardless of their own onPress\n !isGroupInteractive && [\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-[1.15]',\n 'motion-safe:hover:z-10',\n ]\n )}\n style={{ zIndex }}\n >\n {cloneElement(child as ReactElement, {\n size,\n shape,\n // Individual onPress is ignored if group has onPress\n onPress: individualOnPress,\n // Pass group index for progressive darkening\n _groupIndex: index,\n // Remove outer wrapper styling since we handle it\n className: cn(\n childProps.className,\n // Remove min dimensions since group item handles touch target\n '!min-h-0 !min-w-0'\n ),\n } as Partial<typeof childProps>)}\n </div>\n );\n });\n\n // Overflow indicator (+X)\n const overflowIndicator =\n overflowCount > 0 ? (\n <div\n data-testid=\"avatar-overflow\"\n className={cn(\n avatarOverflowVariants({ size, shape }),\n overlapClass\n )}\n style={{ zIndex: 0 }}\n aria-hidden=\"true\"\n >\n +{overflowCount}\n </div>\n ) : null;\n\n // Auto-generate aria-label if not provided\n const computedAriaLabel =\n ariaLabel ||\n `${totalCount} ${totalCount === 1 ? 'member' : 'members'}`;\n\n // Group content\n const groupContent = (\n <>\n {visibleAvatars}\n {overflowIndicator}\n </>\n );\n\n // Render as button if group is interactive\n // Button inherently has role=\"button\", aria-label provides group context\n if (isGroupInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId}\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(avatarGroupVariants({ interactive: true }), className)}\n >\n {groupContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div with role=\"group\"\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId}\n role=\"group\"\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(avatarGroupVariants({ interactive: false }), className)}\n >\n {groupContent}\n </div>\n );\n }\n )\n);\n\nAvatarGroup.displayName = 'AvatarGroup';\n\nexport { AvatarGroup, avatarOverflowVariants };\nexport type { AvatarGroupProps };\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type { ReactNode } from 'react';\nimport type { PressEvent } from 'react-aria-components';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * Avatar size schema\n *\n * Size mappings (visual | touch target | overlap):\n * - sm: 32px | 44x44px | 8px\n * - default: 44px | 44x44px | 12px\n * - lg: 48px | 48x48px | 16px\n *\n * @see plan.md Clarification 6 (Overlap Scale)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 44x44px touch targets)\n */\nexport const AvatarSizeSchema = z.enum(['sm', 'default', 'lg']);\nexport type AvatarSize = z.infer<typeof AvatarSizeSchema>;\n\n/**\n * Avatar shape schema\n * - circle: Fully rounded (rounded-full)\n * - rounded: Rounded corners (rounded-lg)\n */\nexport const AvatarShapeSchema = z.enum(['circle', 'rounded']);\nexport type AvatarShape = z.infer<typeof AvatarShapeSchema>;\n\n/**\n * Avatar status schema for ring indicators\n *\n * Status ring colors:\n * - online: green (#22c55e)\n * - offline: gray (#6b7280)\n * - busy: red (#ef4444)\n * - away: yellow (#eab308)\n */\nexport const AvatarStatusSchema = z.enum(['online', 'offline', 'busy', 'away']);\nexport type AvatarStatus = z.infer<typeof AvatarStatusSchema>;\n\n/**\n * Overlap values by size (in pixels)\n * @see plan.md Clarification 6 (Overlap Scale)\n */\nexport const AVATAR_OVERLAP_BY_SIZE: Record<AvatarSize, number> = {\n sm: 8,\n default: 12,\n lg: 16,\n};\n\n/**\n * Avatar props schema extending BaseComponentProps\n *\n * @see React Aria Button: https://react-aria.adobe.com/Button\n * @see ShadCN Avatar: https://ui.shadcn.com/docs/components/avatar\n * @see plan.md Phase 1 (Type System)\n * @see constitution.md Principle IV (Accessibility First)\n */\nexport const AvatarPropsSchema = BaseComponentPropsSchema.extend({\n /** Image source URL */\n src: z.string().optional(),\n\n /**\n * Alt text for the avatar image (required for accessibility)\n * When actionable, this is used for the aria-label\n */\n alt: z.string().min(1, 'alt is required for accessibility'),\n\n /**\n * Name used to generate initials fallback\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\n name: z.string().optional(),\n\n /** Visual size variant (touch target always 44x44px min for sm/default) */\n size: AvatarSizeSchema.optional().default('default'),\n\n /** Shape variant */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /** Status ring indicator */\n status: AvatarStatusSchema.optional(),\n\n /**\n * Click handler for actionable avatar\n * When provided, renders as Button with 3-layer architecture\n * @see plan.md Clarification 2 (mutually exclusive with group onPress)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarProps {\n className?: string;\n children?: unknown;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'aria-hidden'?: boolean;\n 'data-testid'?: string;\n src?: string;\n alt: string;\n name?: string;\n size?: AvatarSize;\n shape?: AvatarShape;\n status?: AvatarStatus;\n onPress?: (e: PressEvent) => void;\n /** @internal Set by AvatarGroup, do not use directly */\n _groupIndex?: number;\n}\n\n/**\n * Avatar group props schema extending BaseComponentProps\n *\n * @see plan.md Clarification 1 (Group Border)\n * @see plan.md Clarification 5 (Stack Order - first on top)\n * @see plan.md Clarification 7 (Border Scope - groups only)\n */\nexport const AvatarGroupPropsSchema = BaseComponentPropsSchema.extend({\n /**\n * Maximum avatars to display before overflow (+X indicator)\n * Default: 3\n */\n max: z.number().int().positive().optional().default(3),\n\n /**\n * Shape inherited by all child avatars\n * Individual avatar shape props are ignored in groups\n */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /**\n * Size inherited by all child avatars\n * Individual avatar size props are ignored in groups\n * Also determines overlap spacing (sm: 8px, default: 12px, lg: 16px)\n */\n size: AvatarSizeSchema.optional().default('default'),\n\n /**\n * Click handler for the entire group\n * When provided, individual avatar onPress handlers are ignored\n * @see plan.md Clarification 2 (Mutually exclusive)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarGroupProps {\n className?: string;\n children?: ReactNode;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'data-testid'?: string;\n max?: number;\n shape?: AvatarShape;\n size?: AvatarSize;\n onPress?: (e: PressEvent) => void;\n}\n\n/**\n * Internal loading state for avatar images\n * @see plan.md Clarification 4 (Loading State)\n */\nexport type AvatarLoadingState = 'loading' | 'loaded' | 'error';\n"]}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Avatar/Avatar.tsx","../../../src/elements/Avatar/AvatarGroup.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/Avatar/Avatar.types.ts"],"names":["cn","inputs","twMerge","clsx","getInitials","name","parts","firstPart","lastPart","first","last","avatarOuterVariants","cva","avatarVisualVariants","Avatar","memo","forwardRef","src","alt","size","shape","status","onPress","className","id","dataTestId","ariaLabel","ariaLabelledBy","ariaDescribedBy","_groupIndex","ref","loadingState","setLoadingState","useState","handleImageLoad","useCallback","handleImageError","initials","isInteractive","computedAriaLabel","groupBrightness","visualContent","jsxs","jsx","AriaButton","OVERLAP_CLASS_BY_SIZE","avatarOverflowVariants","avatarGroupVariants","AvatarGroup","max","children","childArray","Children","totalCount","visibleCount","overflowCount","isGroupInteractive","overlapClass","child","isValidElement","visibleAvatars","index","childProps","zIndex","individualOnPress","cloneElement","overflowIndicator","groupContent","Fragment","BaseComponentPropsSchema","z","AvatarSizeSchema","AvatarShapeSchema","AvatarStatusSchema","AVATAR_OVERLAP_BY_SIZE","AvatarPropsSchema","AvatarGroupPropsSchema"],"mappings":"uTAcO,SAASA,CAAAA,CAAAA,GAAMC,CAAAA,CAA8B,CAClD,OAAOC,OAAAA,CAAQC,IAAAA,CAAKF,CAAM,CAAC,CAC7B,CCkBA,SAASG,EAAAA,CAAYC,CAAAA,CAAkC,CACrD,GAAI,CAACA,CAAAA,EAAQ,CAACA,CAAAA,CAAK,IAAA,EAAK,CAAG,OAAO,EAAA,CAElC,IAAMC,CAAAA,CAAQD,CAAAA,CAAK,IAAA,EAAK,CAAE,MAAM,KAAK,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CACrD,GAAIC,CAAAA,CAAM,MAAA,GAAW,CAAA,CAAG,OAAO,EAAA,CAE/B,IAAMC,CAAAA,CAAYD,CAAAA,CAAM,CAAC,CAAA,CACzB,GAAIA,CAAAA,CAAM,MAAA,GAAW,CAAA,CACnB,OAAOC,CAAAA,CAAYA,CAAAA,CAAU,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,CAAI,EAAA,CAIzD,IAAMC,CAAAA,CAAWF,CAAAA,CAAMA,CAAAA,CAAM,MAAA,CAAS,CAAC,CAAA,CACjCG,CAAAA,CAAQF,CAAAA,CAAYA,CAAAA,CAAU,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,CAAI,EAAA,CACxDG,CAAAA,CAAOF,CAAAA,CAAWA,CAAAA,CAAS,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,CAAI,EAAA,CAC3D,OAAOC,CAAAA,CAAQC,CACjB,CAOO,IAAMC,CAAAA,CAAsBC,GAAAA,CACjC,kDAAA,CACA,CACE,QAAA,CAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,2BAAA,CACJ,OAAA,CAAS,2BAAA,CACT,EAAA,CAAI,2BACN,CAAA,CACA,WAAA,CAAa,CACX,IAAA,CAAM,CACJ,gBAAA,CACA,4BAAA,CACA,sBAAA,CACA,kCAAA,CACA,6BAAA,CACA,oDAAA,CACA,kCAAA,CACA,0BAAA,CACA,sBAAA,CACA,6BACF,CAAA,CACA,KAAA,CAAO,EACT,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,SAAA,CACN,WAAA,CAAa,KACf,CACF,CACF,CAAA,CAMaC,CAAAA,CAAuBD,GAAAA,CAClC,CACE,kDAAA,CACA,iBAAA,CACA,+BAAA,CACA,iCAAA,CACA,aAAA,CACA,aACF,CAAA,CACA,CACE,QAAA,CAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,iBAAA,CACJ,OAAA,CAAS,mBAAA,CACT,EAAA,CAAI,qBACN,CAAA,CACA,KAAA,CAAO,CACL,MAAA,CAAQ,cAAA,CACR,OAAA,CAAS,YACX,CAAA,CACA,MAAA,CAAQ,CACN,MAAA,CAAQ,gDAAA,CACR,OAAA,CAAS,iDAAA,CACT,IAAA,CAAM,8CAAA,CACN,IAAA,CAAM,8CACR,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,SAAA,CACN,KAAA,CAAO,QACT,CACF,CACF,CAAA,CAUME,CAAAA,CAASC,IAAAA,CACbC,UAAAA,CACE,CACE,CACE,GAAA,CAAAC,CAAAA,CACA,GAAA,CAAAC,CAAAA,CACA,IAAA,CAAAb,EACA,IAAA,CAAAc,CAAAA,CAAO,SAAA,CACP,KAAA,CAAAC,CAAAA,CAAQ,QAAA,CACR,MAAA,CAAAC,CAAAA,CACA,OAAA,CAAAC,CAAAA,CACA,SAAA,CAAAC,CAAAA,CACA,EAAA,CAAAC,CAAAA,CACA,aAAA,CAAeC,CAAAA,CACf,YAAA,CAAcC,CAAAA,CACd,iBAAA,CAAmBC,CAAAA,CACnB,kBAAA,CAAoBC,CAAAA,CACpB,WAAA,CAAAC,CACF,CAAA,CACAC,CAAAA,GACiB,CAEjB,GAAM,CAACC,CAAAA,CAAcC,CAAe,CAAA,CAAIC,SACtChB,CAAAA,CAAM,SAAA,CAAY,OACpB,CAAA,CAEMiB,CAAAA,CAAkBC,WAAAA,CAAY,IAAM,CACxCH,CAAAA,CAAgB,QAAQ,EAC1B,CAAA,CAAG,EAAE,CAAA,CAECI,CAAAA,CAAmBD,WAAAA,CAAY,IAAM,CACzCH,CAAAA,CAAgB,OAAO,EACzB,CAAA,CAAG,EAAE,CAAA,CAECK,CAAAA,CAAWjC,EAAAA,CAAYC,CAAI,CAAA,CAC3BiC,CAAAA,CAAgB,CAAA,CAAQhB,CAAAA,CAGxBiB,CAAAA,CACJb,CAAAA,GACCY,CAAAA,EAAiBjB,CAAAA,CAAS,CAAA,EAAGH,CAAG,CAAA,EAAA,EAAKG,CAAM,CAAA,CAAA,CAAK,MAAA,CAAA,CAI7CmB,CAAAA,CACJX,CAAAA,GAAgB,MAAA,CAAY,CAAA,CAAIA,CAAAA,CAAc,GAAA,CAAO,MAAA,CAGjDY,CAAAA,CACJC,IAAAA,CAAC,KAAA,CAAA,CACC,aAAA,CAAY,eAAA,CACZ,SAAA,CAAW1C,CAAAA,CAAGa,CAAAA,CAAqB,CAAE,IAAA,CAAAM,CAAAA,CAAM,KAAA,CAAAC,CAAAA,CAAO,MAAA,CAAAC,CAAO,CAAC,CAAC,CAAA,CAC3D,KAAA,CAAOmB,CAAAA,GAAoB,MAAA,CAAY,CAAE,MAAA,CAAQ,CAAA,WAAA,EAAcA,CAAe,CAAA,CAAA,CAAI,CAAA,CAAI,MAAA,CAKrF,QAAA,CAAA,CAAAT,CAAAA,GAAiB,SAAA,EAChBY,GAAAA,CAAC,KAAA,CAAA,CACC,aAAA,CAAY,iBAAA,CACZ,SAAA,CAAW3C,CAAAA,CACT,8DAAA,CACAoB,CAAAA,GAAU,QAAA,CAAW,cAAA,CAAiB,YACxC,CAAA,CACA,aAAA,CAAY,MAAA,CACd,CAAA,CAIDH,CAAAA,EAAOc,IAAiB,OAAA,EAEvBY,GAAAA,CAAC,KAAA,CAAA,CACC,GAAA,CAAK1B,CAAAA,CACL,GAAA,CAAKqB,CAAAA,CAAgB,EAAA,CAAKpB,CAAAA,CAC1B,MAAA,CAAQgB,CAAAA,CACR,OAAA,CAASE,CAAAA,CACT,SAAA,CAAWpC,CAAAA,CACT,4BAAA,CACA+B,CAAAA,GAAiB,SAAA,EAAa,WAChC,CAAA,CACF,CAAA,CAIDA,CAAAA,GAAiB,OAAA,EAAWM,CAAAA,EAC3BM,GAAAA,CAAC,MAAA,CAAA,CAAK,aAAA,CAAY,MAAA,CAAQ,QAAA,CAAAN,CAAAA,CAAS,CAAA,CAAA,CAEvC,CAAA,CAIF,OAAIC,CAAAA,CAEAK,GAAAA,CAACC,MAAAA,CAAA,CACC,GAAA,CAAKd,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,EAAc,cAAA,CAC3B,YAAA,CAAYc,CAAAA,EAAqBrB,CAAAA,CACjC,iBAAA,CAAiBS,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,OAAA,CAASN,CAAAA,CACT,SAAA,CAAWtB,CAAAA,CACTW,CAAAA,CAAoB,CAAE,IAAA,CAAAQ,CAAAA,CAAM,WAAA,CAAa,IAAK,CAAC,CAAA,CAC/CI,CACF,CAAA,CAEC,QAAA,CAAAkB,CAAAA,CACH,CAAA,CAMFE,GAAAA,CAAC,KAAA,CAAA,CACC,GAAA,CAAKb,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,EAAc,cAAA,CAC3B,YAAA,CAAYC,CAAAA,CACZ,iBAAA,CAAiBC,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,SAAA,CAAW5B,CAAAA,CACTW,CAAAA,CAAoB,CAAE,IAAA,CAAAQ,CAAAA,CAAM,WAAA,CAAa,KAAM,CAAC,CAAA,CAChDI,CACF,CAAA,CAEC,QAAA,CAAAkB,CAAAA,CACH,CAEJ,CACF,CACF,EAEA3B,CAAAA,CAAO,WAAA,CAAc,QAAA,CCxOrB,IAAM+B,EAAAA,CAAoD,CACxD,EAAA,CAAI,OAAA,CACJ,OAAA,CAAS,OAAA,CACT,EAAA,CAAI,OACN,CAAA,CAKMC,CAAAA,CAAyBlC,GAAAA,CAC7B,CACE,kDAAA,CACA,uBAAA,CACA,oCAAA,CACA,eAAA,CACA,aAAA,CACA,0CACF,CAAA,CACA,CACE,QAAA,CAAU,CACR,IAAA,CAAM,CACJ,EAAA,CAAI,iBAAA,CACJ,OAAA,CAAS,mBAAA,CACT,EAAA,CAAI,qBACN,EACA,KAAA,CAAO,CACL,MAAA,CAAQ,cAAA,CACR,OAAA,CAAS,YACX,CACF,CAAA,CACA,eAAA,CAAiB,CACf,IAAA,CAAM,SAAA,CACN,KAAA,CAAO,QACT,CACF,CACF,CAAA,CAKMmC,CAAAA,CAAsBnC,GAAAA,CAAI,0BAAA,CAA4B,CAC1D,QAAA,CAAU,CACR,WAAA,CAAa,CACX,IAAA,CAAM,CACJ,gBAAA,CACA,4BAAA,CACA,sBAAA,CACA,kCAAA,CACA,6BAAA,CACA,oDACF,CAAA,CACA,KAAA,CAAO,EACT,CACF,CAAA,CACA,eAAA,CAAiB,CACf,WAAA,CAAa,KACf,CACF,CAAC,CAAA,CAMKoC,CAAAA,CAAcjC,IAAAA,CAClBC,UAAAA,CACE,CACE,CACE,GAAA,CAAAiC,CAAAA,CAAM,CAAA,CACN,KAAA,CAAA7B,CAAAA,CAAQ,QAAA,CACR,IAAA,CAAAD,CAAAA,CAAO,SAAA,CACP,OAAA,CAAAG,CAAAA,CACA,SAAA,CAAAC,CAAAA,CACA,QAAA,CAAA2B,EACA,EAAA,CAAA1B,CAAAA,CACA,aAAA,CAAeC,CAAAA,CACf,YAAA,CAAcC,CAAAA,CACd,iBAAA,CAAmBC,CAAAA,CACnB,kBAAA,CAAoBC,CACtB,CAAA,CACAE,CAAAA,GACiB,CACjB,IAAMqB,CAAAA,CAAaC,QAAAA,CAAS,OAAA,CAAQF,CAAQ,CAAA,CACtCG,CAAAA,CAAaF,CAAAA,CAAW,MAAA,CACxBG,CAAAA,CAAe,IAAA,CAAK,GAAA,CAAID,CAAAA,CAAYJ,CAAG,CAAA,CACvCM,CAAAA,CAAgBF,CAAAA,CAAaC,CAAAA,CAE7BE,CAAAA,CAAqB,EAAQlC,CAAAA,CAC7BmC,CAAAA,CAAeZ,EAAAA,CAAsB1B,CAAI,CAAA,CAI3C,OAAA,CAAQ,GAAA,CAAI,QAAA,GAAa,YAAA,EAAgBqC,CAAAA,EAC3CJ,QAAAA,CAAS,OAAA,CAAQF,CAAAA,CAAWQ,CAAAA,EAAU,CAElCC,cAAAA,CAAeD,CAAK,CAAA,EACnBA,CAAAA,CAAM,KAAA,CAAgC,OAAA,EAEvC,OAAA,CAAQ,IAAA,CACN,0KAEF,EAEJ,CAAC,CAAA,CAIH,IAAME,CAAAA,CAAiBT,CAAAA,CAAW,KAAA,CAAM,CAAA,CAAGG,CAAY,CAAA,CAAE,GAAA,CAAI,CAACI,CAAAA,CAAOG,CAAAA,GAAU,CAC7E,GAAI,CAACF,cAAAA,CAAeD,CAAK,CAAA,CAAG,OAAO,IAAA,CAGnC,IAAMI,CAAAA,CAAaJ,CAAAA,CAAM,KAAA,CAQnBK,CAAAA,CAAST,CAAAA,CAAeO,CAAAA,CAIxBG,CAAAA,CAAoBR,CAAAA,CACtB,MAAA,CACAM,CAAAA,CAAW,OAAA,CAEf,OACEnB,GAAAA,CAAC,KAAA,CAAA,CAEC,aAAA,CAAY,mBAAA,CACZ,SAAA,CAAW3C,CAAAA,CACT,UAAA,CAEA,0CAAA,CACAoB,CAAAA,GAAU,QAAA,CAAW,cAAA,CAAiB,YAAA,CAEtCyC,CAAAA,CAAQ,CAAA,EAAKJ,CAAAA,CAGb,CAACD,CAAAA,EAAsB,CACrB,kCAAA,CACA,0BAAA,CACA,sBAAA,CACA,gCAAA,CACA,wBACF,CACF,CAAA,CACA,KAAA,CAAO,CAAE,MAAA,CAAAO,CAAO,CAAA,CAEf,QAAA,CAAAE,YAAAA,CAAaP,CAAAA,CAAuB,CACnC,IAAA,CAAAvC,CAAAA,CACA,KAAA,CAAAC,CAAAA,CAEA,QAAS4C,CAAAA,CAET,WAAA,CAAaH,CAAAA,CAEb,SAAA,CAAW7D,CAAAA,CACT8D,CAAAA,CAAW,SAAA,CAEX,mBACF,CACF,CAA+B,CAAA,CAAA,CAlC1BD,CAmCP,CAEJ,CAAC,CAAA,CAGKK,CAAAA,CACJX,CAAAA,CAAgB,CAAA,CACdb,IAAAA,CAAC,KAAA,CAAA,CACC,aAAA,CAAY,iBAAA,CACZ,SAAA,CAAW1C,CAAAA,CACT8C,CAAAA,CAAuB,CAAE,IAAA,CAAA3B,CAAAA,CAAM,KAAA,CAAAC,CAAM,CAAC,CAAA,CACtCqC,CACF,CAAA,CACA,KAAA,CAAO,CAAE,MAAA,CAAQ,CAAE,CAAA,CACnB,aAAA,CAAY,MAAA,CACb,QAAA,CAAA,CAAA,GAAA,CACGF,CAAAA,CAAAA,CACJ,CAAA,CACE,IAAA,CAGAhB,CAAAA,CACJb,CAAAA,EACA,CAAA,EAAG2B,CAAU,CAAA,CAAA,EAAIA,CAAAA,GAAe,CAAA,CAAI,QAAA,CAAW,SAAS,CAAA,CAAA,CAGpDc,CAAAA,CACJzB,IAAAA,CAAA0B,QAAAA,CAAA,CACG,QAAA,CAAA,CAAAR,CAAAA,CACAM,CAAAA,CAAAA,CACH,CAAA,CAKF,OAAIV,CAAAA,CAEAb,IAACC,MAAAA,CAAA,CACC,GAAA,CAAKd,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,CACb,YAAA,CAAYc,CAAAA,CACZ,iBAAA,CAAiBZ,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,OAAA,CAASN,CAAAA,CACT,SAAA,CAAWtB,CAAAA,CAAG+C,CAAAA,CAAoB,CAAE,WAAA,CAAa,IAAK,CAAC,CAAA,CAAGxB,CAAS,CAAA,CAElE,QAAA,CAAA4C,CAAAA,CACH,CAAA,CAMFxB,GAAAA,CAAC,KAAA,CAAA,CACC,GAAA,CAAKb,CAAAA,CACL,EAAA,CAAIN,CAAAA,CACJ,aAAA,CAAaC,CAAAA,CACb,IAAA,CAAK,OAAA,CACL,YAAA,CAAYc,CAAAA,CACZ,iBAAA,CAAiBZ,CAAAA,CACjB,kBAAA,CAAkBC,CAAAA,CAClB,SAAA,CAAW5B,CAAAA,CAAG+C,CAAAA,CAAoB,CAAE,WAAA,CAAa,KAAM,CAAC,CAAA,CAAGxB,CAAS,CAAA,CAEnE,QAAA,CAAA4C,CAAAA,CACH,CAEJ,CACF,CACF,EAEAnB,CAAAA,CAAY,WAAA,CAAc,aAAA,CCpQnB,IAAMqB,CAAAA,CAA2BC,CAAAA,CAAE,MAAA,CAAO,CAE/C,SAAA,CAAWA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CAG/B,QAAA,CAAUA,CAAAA,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAC3B,EAAA,CAAIA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CAGxB,YAAA,CAAcA,CAAAA,CAAE,QAAO,CAAE,QAAA,EAAS,CAClC,iBAAA,CAAmBA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CACvC,kBAAA,CAAoBA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CACxC,WAAA,CAAaA,CAAAA,CAAE,IAAA,CAAK,CAAC,KAAA,CAAO,QAAA,CAAU,WAAW,CAAC,CAAA,CAAE,QAAA,EAAS,CAC7D,aAAA,CAAeA,CAAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS,CAGpC,cAAeA,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAC5B,CAAC,CAAA,CCVM,IAAMC,CAAAA,CAAmBD,CAAAA,CAAE,IAAA,CAAK,CAAC,IAAA,CAAM,SAAA,CAAW,IAAI,CAAC,CAAA,CAQjDE,CAAAA,CAAoBF,CAAAA,CAAE,IAAA,CAAK,CAAC,QAAA,CAAU,SAAS,CAAC,CAAA,CAYhDG,CAAAA,CAAqBH,CAAAA,CAAE,IAAA,CAAK,CAAC,QAAA,CAAU,SAAA,CAAW,MAAA,CAAQ,MAAM,CAAC,CAAA,CAOjEI,EAAAA,CAAqD,CAChE,EAAA,CAAI,CAAA,CACJ,OAAA,CAAS,EAAA,CACT,EAAA,CAAI,EACN,CAAA,CAUaC,EAAAA,CAAoBN,CAAAA,CAAyB,MAAA,CAAO,CAE/D,GAAA,CAAKC,CAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS,CAMzB,GAAA,CAAKA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAA,CAAG,mCAAmC,CAAA,CAQ1D,IAAA,CAAMA,CAAAA,CAAE,QAAO,CAAE,QAAA,EAAS,CAG1B,IAAA,CAAMC,CAAAA,CAAiB,QAAA,EAAS,CAAE,OAAA,CAAQ,SAAS,CAAA,CAGnD,KAAA,CAAOC,CAAAA,CAAkB,QAAA,EAAS,CAAE,OAAA,CAAQ,QAAQ,CAAA,CAGpD,MAAA,CAAQC,CAAAA,CAAmB,QAAA,EAAS,CAOpC,OAAA,CAASH,CAAAA,CAAE,QAAA,EAAS,CAAE,QAAA,EACxB,CAAC,CAAA,CA6BYM,EAAAA,CAAyBP,CAAAA,CAAyB,MAAA,CAAO,CAKpE,GAAA,CAAKC,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA,CAMrD,KAAA,CAAOE,CAAAA,CAAkB,QAAA,EAAS,CAAE,OAAA,CAAQ,QAAQ,CAAA,CAOpD,IAAA,CAAMD,CAAAA,CAAiB,QAAA,EAAS,CAAE,OAAA,CAAQ,SAAS,CAAA,CAOnD,OAAA,CAASD,CAAAA,CAAE,QAAA,EAAS,CAAE,QAAA,EACxB,CAAC","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Accessible avatar with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant)\n * - Layer 2: Visual Avatar (configurable size, shape, status ring)\n * - Layer 3: Content (image, initials, skeleton shimmer)\n *\n * @see plan.md for implementation details\n * @see avatar-prd.md for requirements\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useState, useCallback, type ReactElement } from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type {\n AvatarProps,\n AvatarSize,\n AvatarShape,\n AvatarStatus,\n AvatarLoadingState,\n} from './Avatar.types';\n\n/**\n * Extract initials from a name\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\nfunction getInitials(name: string | undefined): string {\n if (!name || !name.trim()) return '';\n\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n\n const firstPart = parts[0];\n if (parts.length === 1) {\n return firstPart ? firstPart.charAt(0).toUpperCase() : '';\n }\n\n // First and last initials (max 2 characters)\n const lastPart = parts[parts.length - 1];\n const first = firstPart ? firstPart.charAt(0).toUpperCase() : '';\n const last = lastPart ? lastPart.charAt(0).toUpperCase() : '';\n return first + last;\n}\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n */\nexport const avatarOuterVariants = cva(\n 'relative inline-flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'min-h-[44px] min-w-[44px]',\n default: 'min-h-[44px] min-w-[44px]',\n lg: 'min-h-[48px] min-w-[48px]',\n },\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-105',\n ],\n false: '',\n },\n },\n defaultVariants: {\n size: 'default',\n interactive: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual avatar appearance\n * Provides the visual appearance with configurable size, shape, and status ring\n */\nexport const avatarVisualVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'overflow-hidden',\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'font-medium',\n 'select-none',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n status: {\n online: 'ring-[3px] ring-[var(--status-online,#22c55e)]',\n offline: 'ring-[3px] ring-[var(--status-offline,#6b7280)]',\n busy: 'ring-[3px] ring-[var(--status-busy,#ef4444)]',\n away: 'ring-[3px] ring-[var(--status-away,#eab308)]',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Displays user avatar with image or initials fallback\n *\n * Layer 1: Touch Target - 44x44px WCAG AAA compliant\n * Layer 2: Visual Avatar - configurable appearance\n * Layer 3: Content - image, initials, or loading skeleton\n */\nconst Avatar = memo(\n forwardRef<HTMLDivElement, AvatarProps>(\n (\n {\n src,\n alt,\n name,\n size = 'default',\n shape = 'circle',\n status,\n onPress,\n className,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n _groupIndex,\n },\n ref\n ): ReactElement => {\n // Loading state machine: loading → loaded | error\n const [loadingState, setLoadingState] = useState<AvatarLoadingState>(\n src ? 'loading' : 'error'\n );\n\n const handleImageLoad = useCallback(() => {\n setLoadingState('loaded');\n }, []);\n\n const handleImageError = useCallback(() => {\n setLoadingState('error');\n }, []);\n\n const initials = getInitials(name);\n const isInteractive = Boolean(onPress);\n\n // Build aria-label for actionable avatars with status\n const computedAriaLabel =\n ariaLabel ||\n (isInteractive && status ? `${alt}, ${status}` : undefined);\n\n // Calculate brightness for group darkening effect\n // First avatar (index 0) = 100%, each subsequent gets 5% darker\n const groupBrightness =\n _groupIndex !== undefined ? 1 - _groupIndex * 0.05 : undefined;\n\n // Common visual layer content\n const visualContent = (\n <div\n data-testid=\"avatar-visual\"\n className={cn(avatarVisualVariants({ size, shape, status }))}\n style={groupBrightness !== undefined ? { filter: `brightness(${groupBrightness})` } : undefined}\n >\n {/* Layer 3: Content */}\n\n {/* Skeleton shimmer while loading */}\n {loadingState === 'loading' && (\n <div\n data-testid=\"avatar-skeleton\"\n className={cn(\n 'absolute inset-0 animate-pulse bg-[var(--accent-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg'\n )}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Image (hidden during loading, removed on error) */}\n {src && loadingState !== 'error' && (\n \n <img\n src={src}\n alt={isInteractive ? '' : alt}\n onLoad={handleImageLoad}\n onError={handleImageError}\n className={cn(\n 'h-full w-full object-cover',\n loadingState === 'loading' && 'invisible'\n )}\n />\n )}\n\n {/* Initials fallback (shown when no src or on error) */}\n {loadingState === 'error' && initials && (\n <span aria-hidden=\"true\">{initials}</span>\n )}\n </div>\n );\n\n // Render as button if interactive\n if (isInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={computedAriaLabel || alt}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(\n avatarOuterVariants({ size, interactive: true }),\n className\n )}\n >\n {visualContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(\n avatarOuterVariants({ size, interactive: false }),\n className\n )}\n >\n {visualContent}\n </div>\n );\n }\n )\n);\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar };\nexport type { AvatarProps, AvatarSize, AvatarShape, AvatarStatus };\n","'use client';\n\n/**\n * AvatarGroup Component\n * Displays multiple avatars with overlap, overflow indicator, and optional actionability\n *\n * Features:\n * - Overlapping display with configurable overlap\n * - Overflow indicator (+X) when exceeding max\n * - 2px border for visual separation\n * - First avatar on top (highest z-index)\n * - Mutually exclusive actions (group OR individual, not both)\n * - Shape/size inheritance to children\n *\n * @see plan.md Clarifications 1, 2, 5, 6, 7\n * @see avatar-prd.md US-5, US-7\n */\n\nimport {\n forwardRef,\n memo,\n Children,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type { AvatarGroupProps, AvatarSize, AvatarShape } from './Avatar.types';\n\n/**\n * Overlap class by size (proportional)\n * sm: 8px (-ml-2), default: 12px (-ml-3), lg: 16px (-ml-4)\n */\nconst OVERLAP_CLASS_BY_SIZE: Record<AvatarSize, string> = {\n sm: '-ml-2',\n default: '-ml-3',\n lg: '-ml-4',\n};\n\n/**\n * Overflow indicator variants (the +X badge)\n */\nconst avatarOverflowVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'bg-[var(--secondary)]',\n 'text-[var(--secondary-foreground)]',\n 'font-semibold',\n 'select-none',\n 'border-2 border-[var(--page-background)]',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Group outer variants\n */\nconst avatarGroupVariants = cva('inline-flex items-center', {\n variants: {\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n ],\n false: '',\n },\n },\n defaultVariants: {\n interactive: false,\n },\n});\n\n/**\n * AvatarGroup Component\n * Groups multiple Avatar components with overlap and overflow handling\n */\nconst AvatarGroup = memo(\n forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n max = 3,\n shape = 'circle',\n size = 'default',\n onPress,\n className,\n children,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n },\n ref\n ): ReactElement => {\n const childArray = Children.toArray(children);\n const totalCount = childArray.length;\n const visibleCount = Math.min(totalCount, max);\n const overflowCount = totalCount - visibleCount;\n\n const isGroupInteractive = Boolean(onPress);\n const overlapClass = OVERLAP_CLASS_BY_SIZE[size];\n\n // Warn about mutually exclusive actions (Clarification 2)\n // Uses !== 'production' to work in both development and test modes\n if (process.env.NODE_ENV !== 'production' && isGroupInteractive) {\n Children.forEach(children, (child) => {\n if (\n isValidElement(child) &&\n (child.props as { onPress?: unknown }).onPress\n ) {\n console.warn(\n '[AvatarGroup] onPress on individual avatars is ignored when AvatarGroup has onPress. ' +\n 'Actions are mutually exclusive - use group onPress OR individual onPress, not both.'\n );\n }\n });\n }\n\n // Build visible avatars with proper styling\n const visibleAvatars = childArray.slice(0, visibleCount).map((child, index) => {\n if (!isValidElement(child)) return null;\n\n // Clone child with inherited props and group styling\n const childProps = child.props as {\n size?: AvatarSize;\n shape?: AvatarShape;\n onPress?: unknown;\n className?: string;\n };\n\n // First avatar has highest z-index (Clarification 5)\n const zIndex = visibleCount - index;\n\n // Determine if individual avatar is interactive\n // If group has onPress, individual onPress is ignored\n const individualOnPress = isGroupInteractive\n ? undefined\n : childProps.onPress;\n\n return (\n <div\n key={index}\n data-testid=\"avatar-group-item\"\n className={cn(\n 'relative',\n // 2px border for visual separation (Clarification 1 & 7)\n 'border-2 border-[var(--page-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg',\n // Overlap negative margin (except first)\n index > 0 && overlapClass,\n // Hover lift animation when group is NOT actionable\n // Individual avatars get lift effect regardless of their own onPress\n !isGroupInteractive && [\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-[1.15]',\n 'motion-safe:hover:z-10',\n ]\n )}\n style={{ zIndex }}\n >\n {cloneElement(child as ReactElement, {\n size,\n shape,\n // Individual onPress is ignored if group has onPress\n onPress: individualOnPress,\n // Pass group index for progressive darkening\n _groupIndex: index,\n // Remove outer wrapper styling since we handle it\n className: cn(\n childProps.className,\n // Remove min dimensions since group item handles touch target\n '!min-h-0 !min-w-0'\n ),\n } as Partial<typeof childProps>)}\n </div>\n );\n });\n\n // Overflow indicator (+X)\n const overflowIndicator =\n overflowCount > 0 ? (\n <div\n data-testid=\"avatar-overflow\"\n className={cn(\n avatarOverflowVariants({ size, shape }),\n overlapClass\n )}\n style={{ zIndex: 0 }}\n aria-hidden=\"true\"\n >\n +{overflowCount}\n </div>\n ) : null;\n\n // Auto-generate aria-label if not provided\n const computedAriaLabel =\n ariaLabel ||\n `${totalCount} ${totalCount === 1 ? 'member' : 'members'}`;\n\n // Group content\n const groupContent = (\n <>\n {visibleAvatars}\n {overflowIndicator}\n </>\n );\n\n // Render as button if group is interactive\n // Button inherently has role=\"button\", aria-label provides group context\n if (isGroupInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId}\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(avatarGroupVariants({ interactive: true }), className)}\n >\n {groupContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div with role=\"group\"\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId}\n role=\"group\"\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(avatarGroupVariants({ interactive: false }), className)}\n >\n {groupContent}\n </div>\n );\n }\n )\n);\n\nAvatarGroup.displayName = 'AvatarGroup';\n\nexport { AvatarGroup, avatarOverflowVariants };\nexport type { AvatarGroupProps };\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type { ReactNode } from 'react';\nimport type { PressEvent } from 'react-aria-components';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * Avatar size schema\n *\n * Size mappings (visual | touch target | overlap):\n * - sm: 32px | 44x44px | 8px\n * - default: 44px | 44x44px | 12px\n * - lg: 48px | 48x48px | 16px\n *\n * @see plan.md Clarification 6 (Overlap Scale)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 44x44px touch targets)\n */\nexport const AvatarSizeSchema = z.enum(['sm', 'default', 'lg']);\nexport type AvatarSize = z.infer<typeof AvatarSizeSchema>;\n\n/**\n * Avatar shape schema\n * - circle: Fully rounded (rounded-full)\n * - rounded: Rounded corners (rounded-lg)\n */\nexport const AvatarShapeSchema = z.enum(['circle', 'rounded']);\nexport type AvatarShape = z.infer<typeof AvatarShapeSchema>;\n\n/**\n * Avatar status schema for ring indicators\n *\n * Status ring colors:\n * - online: green (#22c55e)\n * - offline: gray (#6b7280)\n * - busy: red (#ef4444)\n * - away: yellow (#eab308)\n */\nexport const AvatarStatusSchema = z.enum(['online', 'offline', 'busy', 'away']);\nexport type AvatarStatus = z.infer<typeof AvatarStatusSchema>;\n\n/**\n * Overlap values by size (in pixels)\n * @see plan.md Clarification 6 (Overlap Scale)\n */\nexport const AVATAR_OVERLAP_BY_SIZE: Record<AvatarSize, number> = {\n sm: 8,\n default: 12,\n lg: 16,\n};\n\n/**\n * Avatar props schema extending BaseComponentProps\n *\n * @see React Aria Button: https://react-aria.adobe.com/Button\n * @see ShadCN Avatar: https://ui.shadcn.com/docs/components/avatar\n * @see plan.md Phase 1 (Type System)\n * @see constitution.md Principle IV (Accessibility First)\n */\nexport const AvatarPropsSchema = BaseComponentPropsSchema.extend({\n /** Image source URL */\n src: z.string().optional(),\n\n /**\n * Alt text for the avatar image (required for accessibility)\n * When actionable, this is used for the aria-label\n */\n alt: z.string().min(1, 'alt is required for accessibility'),\n\n /**\n * Name used to generate initials fallback\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\n name: z.string().optional(),\n\n /** Visual size variant (touch target always 44x44px min for sm/default) */\n size: AvatarSizeSchema.optional().default('default'),\n\n /** Shape variant */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /** Status ring indicator */\n status: AvatarStatusSchema.optional(),\n\n /**\n * Click handler for actionable avatar\n * When provided, renders as Button with 3-layer architecture\n * @see plan.md Clarification 2 (mutually exclusive with group onPress)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarProps {\n className?: string;\n children?: unknown;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'aria-hidden'?: boolean;\n 'data-testid'?: string;\n src?: string;\n alt: string;\n name?: string;\n size?: AvatarSize;\n shape?: AvatarShape;\n status?: AvatarStatus;\n onPress?: (e: PressEvent) => void;\n /** @internal Set by AvatarGroup, do not use directly */\n _groupIndex?: number;\n}\n\n/**\n * Avatar group props schema extending BaseComponentProps\n *\n * @see plan.md Clarification 1 (Group Border)\n * @see plan.md Clarification 5 (Stack Order - first on top)\n * @see plan.md Clarification 7 (Border Scope - groups only)\n */\nexport const AvatarGroupPropsSchema = BaseComponentPropsSchema.extend({\n /**\n * Maximum avatars to display before overflow (+X indicator)\n * Default: 3\n */\n max: z.number().int().positive().optional().default(3),\n\n /**\n * Shape inherited by all child avatars\n * Individual avatar shape props are ignored in groups\n */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /**\n * Size inherited by all child avatars\n * Individual avatar size props are ignored in groups\n * Also determines overlap spacing (sm: 8px, default: 12px, lg: 16px)\n */\n size: AvatarSizeSchema.optional().default('default'),\n\n /**\n * Click handler for the entire group\n * When provided, individual avatar onPress handlers are ignored\n * @see plan.md Clarification 2 (Mutually exclusive)\n */\n onPress: z.function().optional(),\n});\n\nexport interface AvatarGroupProps {\n className?: string;\n children?: ReactNode;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n 'data-testid'?: string;\n max?: number;\n shape?: AvatarShape;\n size?: AvatarSize;\n onPress?: (e: PressEvent) => void;\n}\n\n/**\n * Internal loading state for avatar images\n * @see plan.md Clarification 4 (Loading State)\n */\nexport type AvatarLoadingState = 'loading' | 'loaded' | 'error';\n"]}