{"version":3,"file":"create-icon.cjs","sources":["../../icons/create-icon.tsx"],"sourcesContent":["'use client';\n\nimport {\n  type ComponentType,\n  createContext,\n  type ReactNode,\n  type SVGProps,\n  useContext,\n  useMemo,\n  useRef\n} from 'react';\nimport type { IconName } from './types';\n\n/**\n * The props of an Apsara icon: the SVG attributes, without `children`.\n *\n * An icon draws a fixed shape, so it takes no children. Leaving `children` out\n * is also what keeps a real icon library assignable to `IconComponent`: props\n * are contravariant, so an override has to accept every prop this type permits,\n * and some libraries declare `children?: undefined` to forbid children.\n */\nexport type IconProps = Omit<SVGProps<SVGSVGElement>, 'children'>;\nexport type IconComponent = ComponentType<IconProps>;\nexport type IconOverrides = Partial<Record<IconName, IconComponent>>;\n\n/**\n * The icons and the icon props together, so `<Theme icons>` takes one object.\n *\n * `components` replaces a drawing by key. `props` applies to every icon built\n * by `createIcon`, the consumer's own included.\n */\nexport interface IconOptions {\n  components?: IconOverrides;\n  props?: IconProps;\n}\n\n/** What `createIcon` reads: the override map, and the shared props. */\nexport interface IconContextValue {\n  icons?: IconOverrides;\n  props?: IconProps;\n}\n\n// The context holds overrides only, never the defaults. A merged\n// `{ ...defaultIcons, ...icons }` map here would make this module reference\n// every icon, and no bundler could then drop the unused ones. Each wrapper\n// closes over its own default instead.\nconst IconContext = createContext<IconContextValue>({});\n\nfunction shallowEqual(a?: object, b?: object): boolean {\n  if (a === b) return true;\n  if (!a || !b) return false;\n  const ka = Object.keys(a);\n  const kb = Object.keys(b);\n  if (ka.length !== kb.length) return false;\n  return ka.every(k => (a as never)[k] === (b as never)[k]);\n}\n\n// Hold the last value that passed a shallow compare, so an inline object\n// literal at the call site keeps its identity and the context value stays\n// stable. The result depends only on the input, so StrictMode and concurrent\n// rendering are safe.\nfunction useStable<T extends object | undefined>(value: T): T {\n  const ref = useRef(value);\n  if (!shallowEqual(ref.current, value)) ref.current = value;\n  return ref.current;\n}\n\nexport interface IconProviderProps extends IconOptions {\n  children: ReactNode;\n}\n\nexport function IconProvider({\n  components,\n  props,\n  children\n}: IconProviderProps) {\n  const parent = useContext(IconContext);\n  const stableIcons = useStable(components);\n  const stableProps = useStable(props);\n\n  // Layer on the parent, so a nested provider changes only the keys it names\n  // and inherits the rest — the way `Scoped` layers theme tokens. Only supplied\n  // maps are merged, never the defaults, so an icon nobody overrides stays\n  // absent from the context and removable by a bundler.\n  const value = useMemo(\n    () => ({\n      icons: parent.icons ? { ...parent.icons, ...stableIcons } : stableIcons,\n      props: parent.props ? { ...parent.props, ...stableProps } : stableProps\n    }),\n    [parent.icons, parent.props, stableIcons, stableProps]\n  );\n  return <IconContext value={value}>{children}</IconContext>;\n}\n\nIconProvider.displayName = 'IconProvider';\n\n/**\n * Builds an Apsara icon: a wrapper that applies the base props, stamps\n * `data-icon`, and lets a `<Theme icons>` above it swap the drawing.\n *\n * Use it for an icon Apsara does not ship, and it behaves like the ones it does:\n *\n * ```tsx\n * // src/icons.ts\n * import { createIcon } from '@raystack/apsara/icons';\n * import { Rocket } from 'lucide-react';\n *\n * export const RocketIcon = createIcon('RocketIcon', Rocket);\n * ```\n *\n * `name` is any string. `IconName` covers the keys Apsara ships, so those are\n * the ones `<Theme icons>` can replace with types on your side — but every icon\n * built here reads the same context, so its `props` reach yours too.\n *\n * Resolution: the override from the context, then `Default`.\n * Prop priority: the base values, then the provider `props`, then the props at\n * the call site.\n */\nexport function createIcon(name: string, Default: IconComponent) {\n  const Icon = (callProps: IconProps) => {\n    const { icons, props } = useContext(IconContext);\n    // `name` is a plain string, so the cast only satisfies the index type. A\n    // name with no override in the context misses and falls back to `Default`.\n    const Resolved = icons?.[name as IconName] ?? Default;\n    return (\n      // `strokeWidth` counts units of the icon's own viewBox, and lucide draws\n      // in a 24-unit box, so the rendered stroke is `strokeWidth * width / 24`.\n      // The design draws a 1px stroke in a 16px frame, which is 1.5 here — not\n      // 1, which would render a 0.67px stroke.\n      <Resolved\n        width={16}\n        height={16}\n        strokeWidth={1.5}\n        {...props}\n        {...callProps}\n        data-icon={name}\n      />\n    );\n  };\n  Icon.displayName = name;\n  return Icon;\n}\n"],"names":[],"mappings":";;;;;;AA0CA;AACA;AACA;AACA;AACA;AAEA;;AACe;AACb;AAAc;;;AAGd;AAA6B;AAC7B;AACF;AAEA;AACA;AACA;AACA;AACA;AACE;;AACuC;;AAEzC;AAMM;AAKJ;AACA;AACA;;;;;AAMA;AAEI;AACA;AACD;;AAIL;AAEA;AAEA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACa;AACd;;;;;;;;;;;AAmBA;AACA;AACA;AACF;;;"}