{"version":3,"names":["CSS","icon","flipRtl","iconCache","requestCache","scaleToPx","s","m","l","generateIconId","scale","size","name","normalizeIconName","filled","charAt","length","iconName","substring","async","fetchIcon","props","cachedIconKey","cachedIconData","getCachedIconDataByKey","fetch","getAssetPath","then","resp","json","catch","console","error","path","getCachedIconData","id","numberLeadingName","isNaN","Number","parts","split","kebabCased","firstNonDigitInPartPattern","map","part","partIndex","replace","replacer","match","offset","isFirstCharInName","toUpperCase","join","iconCss","CalciteIconStyle0","Icon","connectedCallback","this","visible","waitUntilVisible","loadIconPathData","disconnectedCallback","intersectionObserver","disconnect","render","el","pathData","textLabel","dir","getElementDir","semantic","paths","concat","h","Host","key","toAriaBoolean","role","class","svg","fill","height","viewBox","width","xmlns","d","opacity","isBrowser","fetchIconProps","callback","createObserver","entries","forEach","entry","isIntersecting","rootMargin","observe"],"sources":["src/components/icon/resources.ts","src/components/icon/utils.ts","src/components/icon/icon.scss?tag=calcite-icon&encapsulation=shadow","src/components/icon/icon.tsx"],"sourcesContent":["export const CSS = {\n icon: \"icon\",\n flipRtl: \"flip-rtl\",\n};\n","import { CalciteIconPath } from \"@esri/calcite-ui-icons\";\nimport { getAssetPath } from \"@stencil/core\";\nimport { Scale } from \"../interfaces\";\nimport { IconNameOrString } from \"./interfaces\";\n\nexport interface FetchIconProps {\n icon: IconNameOrString;\n scale: Scale;\n}\n\n/**\n * Icon data cache.\n * Exported for testing purposes.\n *\n * @private\n */\nexport const iconCache: Record = {};\n\n/**\n * Icon request cache.\n * Exported for testing purposes.\n *\n * @private\n */\nexport const requestCache: Record> = {};\n\nexport const scaleToPx: Record = {\n s: 16,\n m: 24,\n l: 32,\n};\n\nfunction generateIconId({ icon, scale }: FetchIconProps): string {\n const size = scaleToPx[scale];\n const name = normalizeIconName(icon);\n const filled = name.charAt(name.length - 1) === \"F\";\n const iconName = filled ? name.substring(0, name.length - 1) : name;\n\n return `${iconName}${size}${filled ? \"F\" : \"\"}`;\n}\n\nexport async function fetchIcon(props: FetchIconProps): Promise {\n const cachedIconKey = generateIconId(props);\n const cachedIconData = getCachedIconDataByKey(cachedIconKey);\n\n if (cachedIconData) {\n return cachedIconData;\n }\n\n if (!requestCache[cachedIconKey]) {\n requestCache[cachedIconKey] = fetch(getAssetPath(`./assets/icon/${cachedIconKey}.json`))\n .then((resp) => resp.json())\n .catch(() => {\n console.error(`\"${cachedIconKey}\" is not a valid calcite-ui-icon name`);\n return \"\";\n });\n }\n\n const path = await requestCache[cachedIconKey];\n iconCache[cachedIconKey] = path;\n\n return path;\n}\n\n/**\n * Util to retrieve cached icon data based on icon name and scale.\n *\n * @param props – icon properties\n */\nexport function getCachedIconData(props: FetchIconProps): CalciteIconPath {\n return getCachedIconDataByKey(generateIconId(props));\n}\n\nfunction getCachedIconDataByKey(id: string): CalciteIconPath {\n return iconCache[id];\n}\n\n/**\n * Normalize the icon name to match the path data module exports.\n * Exported for testing purposes.\n *\n * @param name – an icon name that can be either kebab or camel-cased\n * @private\n */\nexport function normalizeIconName(name: string): string {\n const numberLeadingName = !isNaN(Number(name.charAt(0)));\n const parts = name.split(\"-\");\n const kebabCased = parts.length > 0;\n\n if (kebabCased) {\n const firstNonDigitInPartPattern = /[a-z]/i;\n\n name = parts\n .map((part, partIndex) => {\n return part.replace(firstNonDigitInPartPattern, function replacer(match, offset) {\n const isFirstCharInName = partIndex === 0 && offset === 0;\n\n if (isFirstCharInName) {\n return match;\n }\n\n return match.toUpperCase();\n });\n })\n .join(\"\");\n }\n\n return numberLeadingName ? `i${name}` : name;\n}\n","/**\n * CSS Custom Properties\n *\n * These properties can be overridden using the component's tag as selector.\n *\n * @prop --calcite-ui-icon-color: [Deprecated] Use `--calcite-icon-color`. Specifies the component's color. Defaults to current color.\n * @prop --calcite-icon-color: Specifies the component's color. Defaults to current color.\n */\n\n:host {\n @apply text-color-icon inline-flex;\n}\n\n$icon-size-s: 16px;\n$icon-size-m: 24px;\n$icon-size-l: 32px;\n\n:host([scale=\"s\"]) {\n inline-size: $icon-size-s;\n block-size: $icon-size-s;\n min-inline-size: $icon-size-s;\n min-block-size: $icon-size-s;\n}\n\n:host([scale=\"m\"]) {\n inline-size: $icon-size-m;\n block-size: $icon-size-m;\n min-inline-size: $icon-size-m;\n min-block-size: $icon-size-m;\n}\n\n:host([scale=\"l\"]) {\n inline-size: $icon-size-l;\n block-size: $icon-size-l;\n min-inline-size: $icon-size-l;\n min-block-size: $icon-size-l;\n}\n\n.flip-rtl {\n transform: scaleX(-1);\n}\n\n.svg {\n @apply block;\n}\n\n@include base-component();\n","import { CalciteIconPath, CalciteMultiPathEntry } from \"@esri/calcite-ui-icons\";\nimport { Component, Element, h, Host, Prop, State, VNode, Watch } from \"@stencil/core\";\nimport { getElementDir, toAriaBoolean } from \"../../utils/dom\";\nimport { createObserver } from \"../../utils/observers\";\nimport { Scale } from \"../interfaces\";\nimport { isBrowser } from \"../../utils/browser\";\nimport { CSS } from \"./resources\";\nimport { fetchIcon, getCachedIconData, scaleToPx } from \"./utils\";\nimport { IconNameOrString } from \"./interfaces\";\n\n@Component({\n tag: \"calcite-icon\",\n styleUrl: \"icon.scss\",\n shadow: true,\n assetsDirs: [\"assets\"],\n})\nexport class Icon {\n //--------------------------------------------------------------------------\n //\n // Properties\n //\n //--------------------------------------------------------------------------\n\n /**\n * Displays a specific icon.\n *\n * @see [Icons](https://esri.github.io/calcite-ui-icons)\n */\n @Prop({\n reflect: true,\n })\n icon: IconNameOrString = null;\n\n /**\n * When `true`, the icon will be flipped when the element direction is right-to-left (`\"rtl\"`).\n */\n @Prop({\n reflect: true,\n })\n flipRtl = false;\n\n /**\n * Specifies the size of the component.\n */\n @Prop({\n reflect: true,\n })\n scale: Scale = \"m\";\n\n /**\n * Accessible name for the component.\n *\n * It is recommended to set this value if your icon is semantic.\n */\n @Prop()\n textLabel: string;\n\n //--------------------------------------------------------------------------\n //\n // Lifecycle\n //\n //--------------------------------------------------------------------------\n\n connectedCallback(): void {\n if (!this.visible) {\n this.waitUntilVisible(() => {\n this.visible = true;\n this.loadIconPathData();\n });\n }\n }\n\n disconnectedCallback(): void {\n this.intersectionObserver?.disconnect();\n this.intersectionObserver = null;\n }\n\n render(): VNode {\n const { el, flipRtl, pathData, scale, textLabel } = this;\n const dir = getElementDir(el);\n const size = scaleToPx[scale];\n const semantic = !!textLabel;\n const paths = [].concat(pathData || \"\");\n return (\n \n \n {paths.map((path: string | CalciteMultiPathEntry) =>\n typeof path === \"string\" ? (\n \n ) : (\n \n ),\n )}\n \n \n );\n }\n\n //--------------------------------------------------------------------------\n //\n // Private State/Props\n //\n //--------------------------------------------------------------------------\n\n @Element() el: HTMLCalciteIconElement;\n\n private intersectionObserver: IntersectionObserver;\n\n @State()\n private pathData: CalciteIconPath;\n\n @State()\n private visible = false;\n\n //--------------------------------------------------------------------------\n //\n // Private Methods\n //\n //--------------------------------------------------------------------------\n\n @Watch(\"icon\")\n @Watch(\"scale\")\n private async loadIconPathData(): Promise {\n const { icon, scale, visible } = this;\n\n if (!isBrowser() || !icon || !visible) {\n return;\n }\n\n const fetchIconProps = { icon, scale };\n const pathData = getCachedIconData(fetchIconProps) || (await fetchIcon(fetchIconProps));\n\n // While the fetchIcon method is awaiting response, the icon requested can change. This check is to verify the response received belongs to the current icon.\n if (icon !== this.icon) {\n return;\n }\n\n this.pathData = pathData;\n }\n\n private waitUntilVisible(callback: () => void): void {\n this.intersectionObserver = createObserver(\n \"intersection\",\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n callback();\n }\n });\n },\n { rootMargin: \"50px\" },\n );\n\n if (!this.intersectionObserver) {\n callback();\n return;\n }\n\n this.intersectionObserver.observe(this.el);\n }\n}\n"],"mappings":"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","ignoreList":[]}