generated from secundant/product-template
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathicon.tsx
54 lines (49 loc) · 1.88 KB
/
icon.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import clsx from 'clsx';
import type { SVGProps } from 'react';
import { SPRITES_META, type SpritesMap } from './sprite.gen';
// Our icon will extend an SVG element and accept all its props
export interface IconProps extends SVGProps<SVGSVGElement> {
name: AnyIconName;
}
// Merging all possible icon names as `sprite/icon` string
export type AnyIconName = { [Key in keyof SpritesMap]: IconName<Key> }[keyof SpritesMap];
// Icon name for a specific sprite, e.g. "common/left"
export type IconName<Key extends keyof SpritesMap> = `${Key}/${SpritesMap[Key]}`;
export function Icon({ name, className, ...props }: IconProps) {
const { viewBox, filePath, iconName, axis } = getIconMeta(name);
return (
<svg
// "icon" isn't inlined because of data-axis attribute
className={clsx('icon', className)}
viewBox={viewBox}
/**
* This prop is used by the "icon" class to set the icon's scaled size
* @see https://github.com/secundant/neodx/issues/92
*/
data-axis={axis}
// prevent icon from being focused when using keyboard navigation
focusable="false"
// hide icon from screen readers
aria-hidden
{...props}
>
{/* For example, "/sprites/common.svg#favourite". Change a base path if you don't store sprites under the "/sprites". */}
<use href={`/sprites/${filePath}#${iconName}`} />
</svg>
);
}
/**
* A function to get and process icon metadata.
* It was moved out of the Icon component to prevent type inference issues.
*/
const getIconMeta = <Key extends keyof SpritesMap>(name: IconName<Key>) => {
const [spriteName, iconName] = name.split('/') as [Key, SpritesMap[Key]];
const {
filePath,
items: {
[iconName]: { viewBox, width, height }
}
} = SPRITES_META[spriteName];
const axis = width === height ? 'xy' : width > height ? 'x' : 'y';
return { filePath, iconName, viewBox, axis };
};