{"version":3,"file":"icon-button-DekZjtOn.cjs","names":["Children"],"sources":["../src/config.ts","../src/icon.tsx","../src/icon-button.tsx"],"sourcesContent":["import { ICON_CDN_VERSION } from './icons.generated';\n\nconst DEFAULT_BASE_URL = 'https://www.volvocars.com/static/shared/';\n\nlet baseUrl = DEFAULT_BASE_URL;\n\n/**\n * Set the custom base URL before rendering, at app startup.\n */\nexport function setBaseUrl(url: string) {\n  baseUrl = url.endsWith('/') ? url : `${url}/`;\n}\n\nexport function getIconSrc(\n  name: string,\n  size: number,\n  filled: boolean,\n): string {\n  const fileSize = size <= 20 ? 16 : 24;\n  return `${baseUrl}icons/${ICON_CDN_VERSION}/${name}-${fileSize}.svg${\n    filled ? '#filled' : ''\n  }`;\n}\n\nexport function getBrandedIconSrc(name: string): string {\n  return `${baseUrl}icons/${ICON_CDN_VERSION}/branded/${name}-24.svg`;\n}\n\nexport function getWordmarkSrc() {\n  return `${baseUrl}images/volvo-spread-wordmark.svg`;\n}\n","import { cssMerge } from '@volvo-cars/css/utils';\nimport type React from 'react';\nimport { forwardRef } from 'react';\nimport { getIconSrc } from './config';\nimport type { IconName } from './icons.generated';\n\nconst COLOR = {\n  inherit: 'text-inherit',\n  inverted: 'text-inverted',\n  primary: 'text-primary',\n  secondary: 'text-secondary',\n  'accent-blue': 'text-accent-blue',\n  'feedback-green': 'text-feedback-green',\n  'feedback-orange': 'text-feedback-orange',\n  'feedback-red': 'text-feedback-red',\n  'always-white': 'text-always-white',\n  'always-black': 'text-always-black',\n} as const;\n\nexport type IconColor = keyof typeof COLOR | 'currentColor';\n\ntype IconMaskStyle = React.CSSProperties & {\n  '--icon-url': string;\n};\n\nexport type IconProps = {\n  /**\n   * The name of the icon in the 1.0 icon system.\n   */\n  icon: IconName;\n\n  /**\n   * The size of the icon in pixels.\n   *\n   * Sizes 12 and 16 will use a smaller, simplified icon asset.\n   */\n  size: 12 | 16 | 24 | 32 | 40 | 48 | 64 | 80;\n\n  /**\n   * Use the filled variant of the icon, typically for active or selected states.\n   */\n  filled?: boolean;\n\n  /**\n   * By default icons use the primary foreground color.\n   *\n   * To change color together with the surrounding text, such as when hovering a button,\n   * use `currentColor` or `inherit`.\n   */\n  color?: IconColor;\n\n  /**\n   * Custom class name, merged with existing classes.\n   */\n  className?: string;\n\n  /**\n   * Alternate text.\n   *\n   * May be omitted if the icon is purely decorational or if the alternate text is already present,\n   * such as in a button that already has a label.\n   */\n  alt?: string;\n\n  /**\n   * The loading strategy for the icon image.\n   *\n   * @default 'lazy'\n   */\n  loading?: 'eager' | 'lazy';\n\n  hidden?: boolean;\n  id?: string;\n  title?: string;\n  dir?: string;\n  lang?: string;\n  slot?: string;\n  style?: React.CSSProperties;\n\n  onAnimationEnd?: React.AnimationEventHandler<HTMLImageElement>;\n  onAnimationStart?: React.AnimationEventHandler<HTMLImageElement>;\n  onTransitionEnd?: React.TransitionEventHandler<HTMLImageElement>;\n};\n\n/**\n * Icon renders an `img` with a src on the volvocars.com CDN.\n *\n * By default icons use the primary foreground color. To change color together with the surrounding text,\n * such as when hovering a button, use `color=\"currentColor\"` to inherit the surrounding `color`.\n *\n * To create a clickable icon, see [IconButton](https://developer.volvocars.com/design-system/web/?path=/docs/components-iconbutton--docs).\n */\nexport const Icon = forwardRef<HTMLImageElement, IconProps>(function Icon(\n  { alt, color, icon, size, loading = 'lazy', filled = false, ...props },\n  ref,\n) {\n  const iconSrc = getIconSrc(icon, size, filled);\n  const sprite = !color || color === 'primary';\n  const className = cssMerge(\n    sprite ? 'icon-sprite' : 'icon-mask',\n    color && color !== 'currentColor' ? COLOR[color] : undefined,\n    props.className,\n  );\n  const maskStyle: IconMaskStyle = {\n    '--icon-url': `url(${iconSrc})`,\n    ...props.style,\n  };\n  return (\n    <img\n      {...props}\n      ref={ref}\n      className={className}\n      width={size}\n      height={size}\n      src={iconSrc}\n      role={alt ? undefined : 'presentation'}\n      aria-hidden={alt ? undefined : 'true'}\n      loading={loading}\n      alt={alt || ''}\n      style={sprite ? props.style : maskStyle}\n    />\n  );\n});\n","import { setRef } from '@volvo-cars/react-utils';\nimport type React from 'react';\nimport { type AriaAttributes, Children, cloneElement, forwardRef } from 'react';\nimport { Icon, type IconProps } from './icon';\nimport type { IconName } from './icons.generated';\n\ntype BaseProps = {\n  /**\n   * The icon to render in the IconButton using the 1.0 icon system.\n   */\n  icon: IconName;\n\n  /**\n   * Use the filled variant of the icon, typically for active or selected states.\n   */\n  iconFilled?: boolean;\n\n  /**\n   * The loading strategy for the icon image.\n   *\n   * @default 'lazy'\n   */\n  iconLoading?: IconProps['loading'];\n\n  /**\n   * The color of the IconButton\n   *\n   * @default 'neutral'\n   */\n  color?: 'neutral' | 'destructive' | 'subtle';\n\n  /**\n   * The size of the IconButton.\n   *\n   * - `'medium'` — 40 px button, 16 px icon\n   * - `'large'` — 48 px button, 24 px icon\n   *\n   * @default 'medium'\n   */\n  size?: 'medium' | 'large';\n\n  /**\n   * Disables the button. Avoid using disabled buttons whenever possible - instead show error messages explaining the next steps the user should take.\n   */\n  disabled?: boolean;\n\n  /**\n   * Custom class name, merged with existing classes.\n   */\n  className?: string;\n\n  hidden?: boolean;\n  id?: string;\n  title?: string;\n  dir?: string;\n  lang?: string;\n  slot?: string;\n  style?: React.CSSProperties;\n  tabIndex?: number;\n};\n\ntype VariantProps = {\n  /**\n   * The visual style of the IconButton.\n   *\n   * @default 'outlined'\n   */\n  variant?: 'outlined' | 'filled' | 'clear';\n\n  /**\n   * Allows the background to bleed out into the surrounding layout,\n   * leaving the button to only take up the space for the icon itself.\n   * Only supported on the `clear` variant.\n   *\n   * @default false\n   */\n  bleed?: boolean;\n};\n\ntype AriaProps = AriaAttributes &\n  (\n    | { 'aria-label': string; 'aria-labelledby'?: never }\n    | { 'aria-label'?: never; 'aria-labelledby': string }\n  );\n\ntype AnchorProps = { href: string; onClick?: never; children?: never };\n\ntype ButtonProps = {\n  href?: never;\n  children?: never;\n  /**\n   * Id of a form element that this button should be associated with. Defaults to the containing\n   * form element.\n   */\n  form?: string;\n  /**\n   * The URL that processes the information submitted by the button, overriding the `action` attribute of the button's form.\n   */\n  formAction?: string;\n  /**\n   * Specifies the HTTP method used to submit the form, overriding the `method` attribute of the button's form.\n   */\n  formMethod?: 'post' | 'get';\n\n  /**\n   * The name of the button, submitted as a pair with the button's value as part of the form data.\n   */\n  name?: string;\n  /**\n   * The value associated with the button's `name` in the form data when the form is submitted using this button.\n   */\n  value?: string;\n\n  /**\n   * @default 'button'\n   */\n  type?: 'submit' | 'button';\n\n  /**\n   * Called when the button is clicked.\n   *\n   * If used within a `<form>`, prefer to set the button to `type=submit` and use the `onSubmit` event on the form\n   * instead.\n   */\n  onClick?: React.MouseEventHandler<HTMLButtonElement>;\n\n  onBlur?: React.FocusEventHandler<HTMLButtonElement>;\n  onFocus?: React.FocusEventHandler<HTMLButtonElement>;\n  onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;\n  onKeyUp?: React.KeyboardEventHandler<HTMLButtonElement>;\n  onPointerDown?: React.PointerEventHandler<HTMLButtonElement>;\n  onPointerEnter?: React.PointerEventHandler<HTMLButtonElement>;\n  onPointerLeave?: React.PointerEventHandler<HTMLButtonElement>;\n  onPointerMove?: React.PointerEventHandler<HTMLButtonElement>;\n  onPointerUp?: React.PointerEventHandler<HTMLButtonElement>;\n  onAnimationEnd?: React.AnimationEventHandler<HTMLButtonElement>;\n  onAnimationStart?: React.AnimationEventHandler<HTMLButtonElement>;\n  onTransitionEnd?: React.TransitionEventHandler<HTMLButtonElement>;\n};\n\ntype AsChildProps = {\n  asChild: boolean;\n  children: React.ReactElement;\n  href?: never;\n  onClick?: never;\n};\n\nexport type IconButtonProps = BaseProps &\n  VariantProps &\n  AriaProps &\n  (AnchorProps | ButtonProps | AsChildProps);\n\n/**\n * Base props for localized icon button components.\n * Excludes conflicting properties that are handled internally by the localized components.\n */\nexport type LocalizedIconButtonProps = Omit<\n  BaseProps & VariantProps & AriaAttributes & ButtonProps,\n  'aria-label' | 'aria-labelledby' | 'icon'\n> & {\n  'aria-label'?: string;\n};\n\nexport const IconButton = forwardRef<\n  HTMLButtonElement | HTMLAnchorElement,\n  IconButtonProps\n>(function IconButton(\n  {\n    iconFilled,\n    iconLoading,\n    icon,\n    size: sizeProp,\n    className = '',\n    color = 'neutral',\n    variant = 'outlined',\n    bleed,\n    disabled,\n    ...props\n  }: IconButtonProps,\n  ref,\n) {\n  const sharedProps = {\n    className: `icon-button-${variant} ${className}`.trim(),\n    'data-color':\n      color === 'neutral' && variant === 'filled'\n        ? 'neutral'\n        : color === 'neutral'\n          ? undefined\n          : color,\n    'data-size': sizeProp === 'large' ? 'large' : undefined,\n    'data-bleed': bleed && variant === 'clear' ? true : undefined,\n    'aria-disabled': disabled || props['aria-disabled'] || undefined,\n  };\n\n  const iconComponent = (\n    <Icon\n      icon={icon}\n      size={sizeProp === 'large' ? 24 : 16}\n      color={\n        variant === 'filled' || color !== 'neutral' ? 'currentColor' : undefined\n      }\n      filled={iconFilled}\n      loading={iconLoading}\n    />\n  );\n\n  if (props.children && props.asChild) {\n    const { children, asChild: _, ...buttonProps } = props;\n    return cloneElement(\n      Children.only(children),\n      {\n        ...buttonProps,\n        ...sharedProps,\n      },\n      iconComponent,\n    );\n  }\n\n  if (props.href) {\n    return (\n      <a ref={(node) => setRef(ref, node)} {...props} {...sharedProps}>\n        {iconComponent}\n      </a>\n    );\n  }\n\n  return (\n    <button\n      ref={(node) => setRef(ref, node)}\n      type=\"button\"\n      {...props}\n      {...sharedProps}\n      onClick={(event) => {\n        if (disabled) {\n          event.preventDefault();\n        } else if (props.onClick) {\n          props.onClick(event);\n        }\n      }}\n    >\n      {iconComponent}\n    </button>\n  );\n});\n"],"mappings":"wKAII,EAAU,2CAKd,SAAgB,EAAW,EAAa,CACtC,EAAU,EAAI,SAAS,GAAG,EAAI,EAAM,GAAG,EAAI,EAC7C,CAEA,SAAgB,EACd,EACA,EACA,EACQ,CAER,MAAO,GAAG,EAAQ,WAA4B,EAAK,GADlC,GAAQ,GAAK,GAAK,GAC4B,MAC7D,EAAS,UAAY,IAEzB,CAEA,SAAgB,EAAkB,EAAsB,CACtD,MAAO,GAAG,EAAQ,mBAAoC,EAAK,QAC7D,CAEA,SAAgB,GAAiB,CAC/B,MAAO,GAAG,EAAQ,iCACpB,CCxBA,MAAM,EAAQ,CACZ,QAAS,eACT,SAAU,gBACV,QAAS,eACT,UAAW,iBACX,cAAe,mBACf,iBAAkB,sBAClB,kBAAmB,uBACnB,eAAgB,oBAChB,eAAgB,oBAChB,eAAgB,mBAClB,EA2Ea,GAAA,EAAA,EAAA,WAAA,CAA+C,SAC1D,CAAE,MAAK,QAAO,OAAM,OAAM,UAAU,OAAQ,SAAS,GAAO,GAAG,GAC/D,EACA,CACA,IAAM,EAAU,EAAW,EAAM,EAAM,CAAM,EACvC,EAAS,CAAC,GAAS,IAAU,UAC7B,GAAA,EAAA,EAAA,SAAA,CACJ,EAAS,cAAgB,YACzB,GAAS,IAAU,eAAiB,EAAM,GAAS,IAAA,GACnD,EAAM,SACR,EACM,EAA2B,CAC/B,aAAc,OAAO,EAAQ,GAC7B,GAAG,EAAM,KACX,EACA,OACE,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,GAAI,EACC,MACM,YACX,MAAO,EACP,OAAQ,EACR,IAAK,EACL,KAAM,EAAM,IAAA,GAAY,eACxB,cAAa,EAAM,IAAA,GAAY,OACtB,UACT,IAAK,GAAO,GACZ,MAAO,EAAS,EAAM,MAAQ,CAC/B,CAAA,CAEL,CAAC,ECyCY,GAAA,EAAA,EAAA,WAAA,CAGX,SACA,CACE,aACA,cACA,OACA,KAAM,EACN,YAAY,GACZ,QAAQ,UACR,UAAU,WACV,QACA,WACA,GAAG,GAEL,EACA,CACA,IAAM,EAAc,CAClB,UAAW,eAAe,EAAQ,GAAG,IAAY,KAAK,EACtD,aACE,IAAU,WAAa,IAAY,SAC/B,UACA,IAAU,UACR,IAAA,GACA,EACR,YAAa,IAAa,QAAU,QAAU,IAAA,GAC9C,aAAc,GAAS,IAAY,QAAU,GAAO,IAAA,GACpD,gBAAiB,GAAY,EAAM,kBAAoB,IAAA,EACzD,EAEM,GACJ,EAAA,EAAA,IAAA,CAAC,EAAD,CACQ,OACN,KAAM,IAAa,QAAU,GAAK,GAClC,MACE,IAAY,UAAY,IAAU,UAAY,eAAiB,IAAA,GAEjE,OAAQ,EACR,QAAS,CACV,CAAA,EAGH,GAAI,EAAM,UAAY,EAAM,QAAS,CACnC,GAAM,CAAE,WAAU,QAAS,EAAG,GAAG,GAAgB,EACjD,OAAA,EAAA,EAAA,aAAA,CACEA,EAAAA,SAAS,KAAK,CAAQ,EACtB,CACE,GAAG,EACH,GAAG,CACL,EACA,CACF,CACF,CAUA,OARI,EAAM,MAEN,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,IAAM,IAAA,EAAA,EAAA,OAAA,CAAgB,EAAK,CAAI,EAAG,GAAI,EAAO,GAAI,EACjD,SAAA,CACA,CAAA,GAKL,EAAA,EAAA,IAAA,CAAC,SAAD,CACE,IAAM,IAAA,EAAA,EAAA,OAAA,CAAgB,EAAK,CAAI,EAC/B,KAAK,SACL,GAAI,EACJ,GAAI,EACJ,QAAU,GAAU,CACd,EACF,EAAM,eAAe,EACZ,EAAM,SACf,EAAM,QAAQ,CAAK,CAEvB,EAEC,SAAA,CACK,CAAA,CAEZ,CAAC"}