import { ComponentPropsWithoutRef } from 'react';
import {
Icon as PDSIconComponent,
IconProps as PDSIconProps,
} from '@pantheon-systems/pds-toolkit-react';
/**
* Icon names added by this proposal. Everything else is a published PDS icon
* and is rendered by the real `Icon`.
*/
export type NewIconName = 'microphoneLines' | 'sparklesSolid';
/**
* Hand-authored SVG data for the new icons, in the shape of v2's
* `custom-icons.tsx` (512 height, width follows the aspect ratio, coordinates
* start at 0,0, no fill/stroke/transform).
*/
const agenticIcons: Record<
NewIconName,
{ height: number; svgPathData: string; width: number }
> = {
// Font Awesome Pro regular `microphone-lines`, cropped to the glyph (Figma
// export x32 so it is 512 high like other FA icons). The port can instead
// add it as an FA registry entry (`faMicrophoneLines`, style 'regular').
microphoneLines: {
height: 512,
svgPathData:
'M240 96C240 69.5 218.5 48 192 48C165.5 48 144 69.5 144 96V224C144 250.5 165.5 272 192 272C218.5 272 240 250.5 240 224H216C202.7 224 192 213.3 192 200C192 186.7 202.7 176 216 176H240V144H216C202.7 144 192 133.3 192 120C192 106.7 202.7 96 216 96H240ZM192 0C245 0 288 43 288 96V224C288 277 245 320 192 320C139 320 96 277 96 224V96C96 43 139 0 192 0ZM24 160C37.3 160 48 170.7 48 184V224C48 303.5 112.5 368 192 368C271.5 368 336 303.5 336 224V184C336 170.7 346.7 160 360 160C373.3 160 384 170.7 384 184V224C384 321.9 310.7 402.7 216 414.5V464H264C277.3 464 288 474.7 288 488C288 501.3 277.3 512 264 512H120C106.7 512 96 501.3 96 488C96 474.7 106.7 464 120 464H168V414.5C73.3 402.7 0 321.9 0 224V184C0 170.7 10.7 160 24 160Z',
width: 384,
},
sparklesSolid: {
height: 512,
svgPathData:
'M401.3 62.27L428.97 0L456.65 62.27L518.92 89.95L456.65 117.62L428.97 179.89L401.3 117.62L339.03 89.95L401.3 62.27zM124.54 200.65L179.89 76.11L235.24 200.65L359.78 256L235.24 311.35L179.89 435.89L124.54 311.35L0 256L124.54 200.65zM401.3 332.11L428.97 394.38L491.24 422.05L428.97 449.73L401.3 512L373.62 449.73L311.35 422.05L373.62 394.38L401.3 332.11z',
width: 519,
},
};
export interface IconProps
extends
Omit<PDSIconProps, 'iconName'>,
Omit<ComponentPropsWithoutRef<'svg'>, keyof PDSIconProps> {
/**
* Which icon to render. Includes the agentic icons: `zappy` for PDS agents
* and `sparklesSolid` for other (third-party) agents.
*/
iconName: PDSIconProps['iconName'] | NewIconName;
}
// Mirrors v2's `Icon` markup so the new icons size and color the same way.
// In the port the data moves into `custom-icons.tsx` / the registry and this
// wrapper is deleted.
export const Icon = ({
className,
display = 'inline-block',
iconName,
size = 'xl',
style,
verticalAlign = 'middle',
...props
}: IconProps) => {
if (!(iconName in agenticIcons)) {
return (
<PDSIconComponent
className={className}
display={display}
iconName={iconName as PDSIconProps['iconName']}
size={size}
style={style}
verticalAlign={verticalAlign}
{...props}
/>
);
}
const baseClass = 'pds-icon';
const data = agenticIcons[iconName as NewIconName];
return (
<svg
aria-hidden='true'
className={[
baseClass,
`${baseClass}--${size}`,
`${baseClass}--${iconName}`,
className,
]
.filter(Boolean)
.join(' ')}
fill='none'
focusable='false'
preserveAspectRatio='xMidYMid meet'
role='img'
style={{ display, verticalAlign, ...style }}
viewBox={`0 0 ${data.width} ${data.height}`}
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<path d={data.svgPathData} fill='currentColor' />
</svg>
);
};
Icon: agentic icons
Add the new sharp, solid sparkles glyph to the Icon set alongside zappy.
Extends a PDS componentOpen in Storybook