{"version":3,"file":"avatar.cjs","names":[],"sources":["../src/content/avatar/avatar.ts"],"sourcesContent":["import { define, html, prop } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { ComponentSize, RoundedSize, ThemeColor } from '../../types';\n\nimport '../icon/icon';\nimport { roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, roundedVariantMixin, sizeVariantMixin } from '../../styles';\nimport componentStyles from './avatar.css?inline';\n\nexport type AvatarStatus = 'online' | 'offline' | 'busy' | 'away';\n\nconst STATUS_LABELS: Record<AvatarStatus, string> = {\n  away: 'Away',\n  busy: 'Busy',\n  offline: 'Offline',\n  online: 'Online',\n};\n\n/** Avatar component properties */\nexport type OreAvatarProps = {\n  /** Alt text for the image; also used to derive initials when no `initials` prop is given */\n  alt?: string;\n  /** Theme color (used for initials background) */\n  color?: ThemeColor;\n  /** Explicit initials to display when no image is available (e.g. \"JD\") */\n  initials?: string;\n  /** Border radius */\n  rounded?: RoundedSize | '';\n  /** Component size */\n  size?: ComponentSize;\n  /** Image source URL */\n  src?: string;\n  /** Online presence indicator */\n  status?: AvatarStatus;\n};\n\n/**\n * Displays a user avatar: image → initials → generic fallback icon, in that priority order.\n *\n * @element ore-avatar\n *\n * @attr {string} src - Image source URL\n * @attr {string} alt - Image alt text (also used to derive initials)\n * @attr {string} initials - Explicit initials (up to 2 chars)\n * @attr {string} color - Theme color for initials background: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} status - 'online' | 'offline' | 'busy' | 'away'\n *\n * @slot - Fallback content shown when image and initials are unavailable.\n * @cssprop --avatar-size - Diameter of the avatar\n * @cssprop --avatar-bg - Background color\n * @cssprop --avatar-color - Text/icon color\n * @cssprop --avatar-radius - Border radius\n * @cssprop --avatar-border - Border shorthand\n * @cssprop --avatar-border-color - Border color (also controls status dot border)\n *\n * @part avatar - Avatar root container.\n * @part img - Image element.\n * @part initials - Initials fallback element.\n * @part fallback - Shadow part for the `fallback` element.\n * @part status - Status indicator element.\n * @part overflow - Overflow indicator element.\n * @example\n * ```html\n * <ore-avatar src=\"/jane.jpg\" alt=\"Jane Doe\"></ore-avatar>\n * <ore-avatar initials=\"JD\" color=\"primary\"></ore-avatar>\n * <ore-avatar alt=\"John Smith\" status=\"online\"></ore-avatar>\n * ```\n */\nexport const AVATAR_TAG = 'ore-avatar' as const;\ndefine<OreAvatarProps>(AVATAR_TAG, {\n  props: {\n    ...themableBundle,\n    ...sizableBundle,\n    ...roundableBundle,\n    alt: prop.string(),\n    initials: prop.string(),\n    src: prop.string(),\n    status: prop.string<AvatarStatus>(),\n  },\n  setup(props) {\n    const imgFailed = signal(false);\n\n    // Reset stale error state whenever src changes\n    watch(props.src, () => {\n      imgFailed.value = false;\n    });\n\n    // Attach load/error listeners reactively when the img element mounts\n    const attachImgListeners = (el: HTMLImageElement | null) => {\n      if (!el) return;\n\n      el.addEventListener('error', () => {\n        imgFailed.value = true;\n      });\n      el.addEventListener('load', () => {\n        imgFailed.value = false;\n      });\n    };\n    const derivedInitials = computed(() => {\n      if (props.initials.value) return props.initials.value.slice(0, 2);\n\n      const alt = props.alt.value;\n\n      if (!alt) return '';\n\n      const parts = alt.trim().split(/\\s+/);\n\n      if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n\n      return parts[0].slice(0, 2).toUpperCase();\n    });\n    const showImage = computed(() => !!props.src.value && !imgFailed.value);\n    const showInitials = computed(() => !showImage.value && !!derivedInitials.value);\n    const showFallback = computed(() => !showImage.value && !showInitials.value);\n    const avatarLabel = () => {\n      const name = (props.alt.value as string | undefined) || null;\n      const statusKey = props.status.value as AvatarStatus | undefined;\n      const status = statusKey ? STATUS_LABELS[statusKey] : null;\n\n      if (!name && !status) return null;\n\n      if (!name) return `Status: ${status}`;\n\n      if (!status) return name;\n\n      return `${name}, ${status}`;\n    };\n    const avatarRole = () => (avatarLabel() ? 'img' : null);\n\n    return html`\n      <span class=\"avatar\" part=\"avatar\" aria-label=\"${avatarLabel}\" role=\"${avatarRole}\">\n        ${() =>\n          props.src.value\n            ? html`\n                <img\n                  ref=\"${attachImgListeners}\"\n                  part=\"img\"\n                  src=\"${props.src}\"\n                  alt=\"${props.alt}\"\n                  ?hidden=\"${() => !showImage.value}\"\n                  aria-hidden=\"true\" />\n              `\n            : ''}\n        ${() =>\n          showInitials.value\n            ? html`\n                <span class=\"initials\" part=\"initials\" aria-hidden=\"true\">${derivedInitials}</span>\n              `\n            : ''}\n        ${() =>\n          showFallback.value\n            ? html`\n                <ore-icon class=\"icon-fallback\" part=\"fallback\" name=\"user\" size=\"50%\"></ore-icon>\n              `\n            : ''}\n      </span>\n      ${() =>\n        props.status.value\n          ? html`\n              <span class=\"status\" part=\"status\" data-status=\"${props.status}\" aria-hidden=\"true\"></span>\n            `\n          : ''}\n    `;\n  },\n  styles: [\n    colorThemeMixin,\n    roundedVariantMixin,\n    sizeVariantMixin({\n      lg: { fontSize: 'var(--avatar-font-size, var(--text-base))', size: 'var(--avatar-size, var(--size-14))' },\n      md: { fontSize: 'var(--avatar-font-size, var(--text-sm))', size: 'var(--avatar-size, var(--size-10))' },\n      sm: { fontSize: 'var(--avatar-font-size, var(--text-xs))', size: 'var(--avatar-size, var(--size-7))' },\n    }),\n    componentStyles,\n  ],\n});\n"],"mappings":"gTAYA,IAAM,EAA8C,CAClD,KAAM,OACN,KAAM,OACN,QAAS,UACT,OAAQ,QACV,EAsDa,EAAa,cAC1B,EAAA,EAAA,OAAA,CAAuB,EAAY,CACjC,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,gBACH,IAAK,EAAA,KAAK,OAAO,EACjB,SAAU,EAAA,KAAK,OAAO,EACtB,IAAK,EAAA,KAAK,OAAO,EACjB,OAAQ,EAAA,KAAK,OAAqB,CACpC,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAY,EAAA,OAAA,CAAO,EAAK,GAG9B,EAAA,EAAA,MAAA,CAAM,EAAM,QAAW,CACrB,EAAU,MAAQ,EACpB,CAAC,EAGD,IAAM,EAAsB,GAAgC,CACrD,IAEL,EAAG,iBAAiB,YAAe,CACjC,EAAU,MAAQ,EACpB,CAAC,EACD,EAAG,iBAAiB,WAAc,CAChC,EAAU,MAAQ,EACpB,CAAC,EACH,EACM,GAAA,EAAkB,EAAA,SAAA,KAAe,CACrC,GAAI,EAAM,SAAS,MAAO,OAAO,EAAM,SAAS,MAAM,MAAM,EAAG,CAAC,EAEhE,IAAM,EAAM,EAAM,IAAI,MAEtB,GAAI,CAAC,EAAK,MAAO,GAEjB,IAAM,EAAQ,EAAI,KAAK,CAAC,CAAC,MAAM,KAAK,EAIpC,OAFI,EAAM,QAAU,GAAW,EAAM,EAAE,CAAC,GAAK,EAAM,EAAM,OAAS,EAAE,CAAC,GAAA,CAAI,YAAY,EAE9E,EAAM,EAAE,CAAC,MAAM,EAAG,CAAC,CAAC,CAAC,YAAY,CAC1C,CAAC,EACK,GAAA,EAAY,EAAA,SAAA,KAAe,CAAC,CAAC,EAAM,IAAI,OAAS,CAAC,EAAU,KAAK,EAChE,GAAA,EAAe,EAAA,SAAA,KAAe,CAAC,EAAU,OAAS,CAAC,CAAC,EAAgB,KAAK,EACzE,GAAA,EAAe,EAAA,SAAA,KAAe,CAAC,EAAU,OAAS,CAAC,EAAa,KAAK,EACrE,MAAoB,CACxB,IAAM,EAAQ,EAAM,IAAI,OAAgC,KAClD,EAAY,EAAM,OAAO,MACzB,EAAS,EAAY,EAAc,GAAa,KAQtD,MANI,CAAC,GAAQ,CAAC,EAAe,KAExB,EAEA,EAEE,GAAG,EAAK,IAAI,IAFC,EAFF,WAAW,GAK/B,EAGA,MAAO,GAAA,IAAI;uDACwC,EAAY,cAHrC,EAAY,EAAI,MAAQ,KAGkC;cAE9E,EAAM,IAAI,MACN,EAAA,IAAI;;yBAEO,EAAmB;;yBAEnB,EAAM,IAAI;yBACV,EAAM,IAAI;iCACA,CAAC,EAAU,MAAM;;gBAGtC,GAAG;cAEP,EAAa,MACT,EAAA,IAAI;4EAC0D,EAAgB;gBAE9E,GAAG;cAEP,EAAa,MACT,EAAA,IAAI;;gBAGJ,GAAG;;YAGT,EAAM,OAAO,MACT,EAAA,IAAI;gEACgD,EAAM,OAAO;cAEjE,GAAG;KAEb,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,oBACA,EAAA,iBAAiB,CACf,GAAI,CAAE,SAAU,4CAA6C,KAAM,oCAAqC,EACxG,GAAI,CAAE,SAAU,0CAA2C,KAAM,oCAAqC,EACtG,GAAI,CAAE,SAAU,0CAA2C,KAAM,mCAAoC,CACvG,CAAC,EACD,EAAA,OACF,CACF,CAAC"}