Skip to content

Commit

Permalink
Centralize S2 icon wrapper (#7113)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Sep 27, 2024
1 parent 71072b8 commit e60fb42
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 55 deletions.
46 changes: 45 additions & 1 deletion packages/@react-spectrum/s2/src/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -31,3 +33,45 @@ export interface IllustrationContextValue extends IconContextValue {

export const IconContext = createContext<ContextValue<IconContextValue, SVGElement>>({});
export const IllustrationContext = createContext<ContextValue<IllustrationContextValue, SVGElement>>({});

export function createIcon(Component: ComponentType<SVGProps<SVGSVGElement>>, context: Context<ContextValue<IconContextValue, SVGElement>> = IconContext): FunctionComponent<IconProps> {
return (props: IconProps) => {
let ref = useRef<SVGElement>(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 = (
<SkeletonWrapper>
<Component
{...otherProps}
focusable={false}
aria-label={ariaLabel}
aria-hidden={ariaLabel ? (ariaHidden || undefined) : true}
role="img"
data-slot={slot}
className={(UNSAFE_className ?? '') + ' ' + useSkeletonIcon(styles)}
style={UNSAFE_style} />
</SkeletonWrapper>
);

if (render) {
return render(svg);
}

return svg;
};
}
2 changes: 1 addition & 1 deletion packages/@react-spectrum/s2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
57 changes: 4 additions & 53 deletions packages/dev/parcel-transformer-s2-icon/IconTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<SVGElement>(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 = (
<SkeletonWrapper>
<ForwardRef
{...otherProps}
focusable={false}
aria-label={ariaLabel}
aria-hidden={ariaLabel ? (ariaHidden || undefined) : true}
role="img"
data-slot={slot}
className={(UNSAFE_className ?? '') + ' ' + useSkeletonIcon(styles)}
style={UNSAFE_style} />
</SkeletonWrapper>
);
if (render) {
return render(svg);
}
return svg;
}
export default /*#__PURE__*/ createIcon(ForwardRef, ${context});
`
);
}

1 comment on commit e60fb42

@rspbot
Copy link

@rspbot rspbot commented on e60fb42 Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.