diff --git a/packages/@react-spectrum/s2/src/Icon.tsx b/packages/@react-spectrum/s2/src/Icon.tsx index 65f4225f545..c13d2819fcf 100644 --- a/packages/@react-spectrum/s2/src/Icon.tsx +++ b/packages/@react-spectrum/s2/src/Icon.tsx @@ -11,10 +11,12 @@ */ import {AriaLabelingProps, DOMProps} from '@react-types/shared'; +import {ComponentType, Context, createContext, FunctionComponent, ReactNode, SVGProps, useRef} from 'react'; import {ContextValue, SlotProps} from 'react-aria-components'; -import {createContext, ReactNode} from 'react'; +import {SkeletonWrapper, useSkeletonIcon} from './Skeleton'; import {StyleString} from '../style/types'; import {UnsafeStyles} from './style-utils' with {type: 'macro'}; +import {useSpectrumContextProps} from './useSpectrumContextProps'; export interface IconProps extends UnsafeStyles, SlotProps, AriaLabelingProps, DOMProps { 'aria-hidden'?: boolean | 'false' | 'true' @@ -31,3 +33,45 @@ export interface IllustrationContextValue extends IconContextValue { export const IconContext = createContext>({}); export const IllustrationContext = createContext>({}); + +export function createIcon(Component: ComponentType>, context: Context> = IconContext): FunctionComponent { + return (props: IconProps) => { + let ref = useRef(null); + let ctx; + // TODO: remove this default once we release RAC and use DEFAULT_SLOT. + [ctx, ref] = useSpectrumContextProps({slot: props.slot || 'icon'} as IconContextValue, ref, context); + let {render, styles} = ctx; + let { + UNSAFE_className, + UNSAFE_style, + slot, + 'aria-label': ariaLabel, + 'aria-hidden': ariaHidden, + ...otherProps + } = props; + + if (!ariaHidden) { + ariaHidden = undefined; + } + + let svg = ( + + + + ); + + if (render) { + return render(svg); + } + + return svg; + }; +} diff --git a/packages/@react-spectrum/s2/src/index.ts b/packages/@react-spectrum/s2/src/index.ts index a3ce3b154ac..5bc906d5d35 100644 --- a/packages/@react-spectrum/s2/src/index.ts +++ b/packages/@react-spectrum/s2/src/index.ts @@ -40,7 +40,7 @@ export {DialogContainer, useDialogContainer} from './DialogContainer'; export {Divider, DividerContext} from './Divider'; export {DropZone, DropZoneContext} from './DropZone'; export {Form} from './Form'; -export {IconContext, IllustrationContext} from './Icon'; +export {createIcon, IconContext, IllustrationContext} from './Icon'; export {IllustratedMessage, IllustratedMessageContext} from './IllustratedMessage'; export {Image, ImageContext} from './Image'; export {ImageCoordinator} from './ImageCoordinator'; diff --git a/packages/dev/parcel-transformer-s2-icon/IconTransformer.js b/packages/dev/parcel-transformer-s2-icon/IconTransformer.js index 36dbe66d0c3..ae90cddd63a 100644 --- a/packages/dev/parcel-transformer-s2-icon/IconTransformer.js +++ b/packages/dev/parcel-transformer-s2-icon/IconTransformer.js @@ -61,7 +61,7 @@ module.exports = new Transformer({ plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'] }) ).replace('export default ForwardRef;', ''); - let newFile = template(asset, iconName, optimized); + let newFile = template(asset, optimized); return [{ type: 'tsx', content: newFile, @@ -72,65 +72,16 @@ module.exports = new Transformer({ } }); -function template(asset, iconName, svg) { - let importName = iconName - .replace(/^S2_Icon_(.*?)_\d+(?:x\d+)?_N$/, '$1') - .replace(/^S2_(fill|lin)_(.+)_(.+_)?(\d+)$/, (m, name) => name[0].toUpperCase() + name.slice(1)); - let iconRename = importName; - if (/^[0-9]/.test(importName)) { - iconRename = '_' + importName; - } +function template(asset, svg) { let normalizedPath = asset.filePath.replaceAll('\\', '/'); let context = asset.pipeline === 'illustration' || normalizedPath.includes('@react-spectrum/s2/spectrum-illustrations') ? 'IllustrationContext' : 'IconContext'; return ( ` -import {IconProps, ${context}, IconContextValue} from '${normalizedPath.includes('@react-spectrum/s2') ? '~/src/Icon' : '@react-spectrum/s2'}'; -import {SVGProps, useRef} from 'react'; -import {useContextProps} from 'react-aria-components'; -import {SkeletonWrapper, useSkeletonIcon} from '~/src/Skeleton'; +import {createIcon, ${context}} from '${normalizedPath.includes('@react-spectrum/s2') ? '~/src/Icon' : '@react-spectrum/s2'}'; ${svg.replace('import { SVGProps } from "react";', '')} -export default function ${iconRename}(props: IconProps) { - let ref = useRef(null); - let ctx; - // TODO: remove this default once we release RAC and use DEFAULT_SLOT. - [ctx, ref] = useContextProps({slot: props.slot || 'icon'} as IconContextValue, ref, ${context}); - let {render, styles} = ctx; - let { - UNSAFE_className, - UNSAFE_style, - slot, - 'aria-label': ariaLabel, - 'aria-hidden': ariaHidden, - ...otherProps - } = props; - - if (!ariaHidden) { - ariaHidden = undefined; - } - - let svg = ( - - - - ); - - if (render) { - return render(svg); - } - - return svg; -} - +export default /*#__PURE__*/ createIcon(ForwardRef, ${context}); ` ); }