Skip to content

Commit

Permalink
feat: move icons to use fontSize
Browse files Browse the repository at this point in the history
  • Loading branch information
inomdzhon committed Aug 10, 2022
1 parent e81e48e commit dd63966
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 26 deletions.
21 changes: 16 additions & 5 deletions packages/icons-scripts/scripts/reactify.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ const Compiler = require('svg-baker');

const compiler = new Compiler();

function pxToEm(pxSize, baseSize) {
return `${Number((pxSize / baseSize).toFixed(4))}em`;
}

const reactify = (symbol, componentName) => {
const width = symbol.viewBox.split(' ')[2];
const height = symbol.viewBox.split(' ')[3];
const defaultWidth = Number(symbol.viewBox.split(' ')[2]);
const defaultHeight = Number(symbol.viewBox.split(' ')[3]);

const baseSize = Math.max(defaultWidth, defaultHeight);
const relativeWidth = pxToEm(defaultWidth, baseSize);
const relativeHeight = pxToEm(defaultHeight, baseSize);

return `import { HTMLAttributes, RefCallback, RefObject } from 'react';
import { makeIcon } from '../SvgIcon';
Expand All @@ -20,9 +28,12 @@ export default makeIcon<${componentName}Props>(
'${componentName}',
'${symbol.id}',
'${symbol.viewBox}',
'${symbol.render()}',
${width},
${height}
${defaultWidth},
${defaultHeight},
'${relativeWidth}',
'${relativeHeight}',
${baseSize},
'${symbol.render()}'
);
`;
};
Expand Down
76 changes: 59 additions & 17 deletions packages/icons-scripts/src/SvgIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,29 @@ import { IconSettingsInterface, IconSettingsContext } from './IconSettings';
import { addSpriteSymbol, useIsomorphicLayoutEffect } from './sprite';

export interface SvgIconProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* Для пропорционального изменения размера иконки относительно размера шрифта.
*
* > 📝 Чтобы размер наследовался от базового размера шрифта, передайте `"inherit"` или `"1em"`.
*
* @default берётся `Math.max(width, height)` иконки (см. `viewBox`)
*/
fontSize?: string | number;
fill?: string;
width?: number;
height?: number;
viewBox?: string;
fill?: string;
getRootRef?: React.RefCallback<HTMLDivElement> | React.RefObject<HTMLDivElement>;
Component?: React.ElementType,
}

interface SvgIconInternalProps extends SvgIconProps {
defaultWidth: number;
defaultHeight: number;
relativeWidth: string;
relativeHeight: string;
}

const svgStyle = { display: 'block' };

function iconClass(fragments: string[], { classPrefix, globalClasses }: IconSettingsInterface) {
Expand All @@ -28,9 +43,20 @@ function iconClass(fragments: string[], { classPrefix, globalClasses }: IconSett
return res;
}

const SvgIcon: React.FC<SvgIconProps> = ({
width,
height,
const SvgIcon: React.FC<SvgIconInternalProps> = ({
/**
* Внутренние параметры (скрыты от пользователя)
*/
defaultWidth,
defaultHeight,
relativeWidth,
relativeHeight,

/**
* Пользовательские параметры.
*/
width: widthProp,
height: heightProp,
viewBox,
id,
className = '',
Expand All @@ -39,22 +65,28 @@ const SvgIcon: React.FC<SvgIconProps> = ({
getRootRef,
Component = 'div',
role,
fontSize,
"aria-label": ariaLabel,
"aria-hidden": ariaHidden,
...restProps
}) => {
const size = Math.max(width, height);

const iconSettings = React.useContext(IconSettingsContext);
const ownClass = iconClass(['Icon', `Icon--${size}`, `Icon--w-${width}`, `Icon--h-${height}`, `Icon--${id}`], iconSettings);

const classNameWidth = widthProp || defaultWidth;
const classNameHeight = heightProp || defaultHeight;
const classNameSize = Math.max(classNameWidth, classNameHeight);
const ownClass = iconClass(['Icon', `Icon--${classNameSize}`, `Icon--w-${classNameWidth}`, `Icon--h-${classNameHeight}`, `Icon--${id}`], iconSettings);

const width = widthProp || relativeWidth;
const height = heightProp || relativeHeight;

return (
<Component
role="presentation"
{...restProps}
ref={getRootRef}
className={`${ownClass} ${className}`}
style={{ ...style, width, height }}
style={{ ...style, width, height, fontSize }}
>
<svg
viewBox={viewBox}
Expand All @@ -74,29 +106,39 @@ const SvgIcon: React.FC<SvgIconProps> = ({
export function makeIcon<Props extends SvgIconProps = SvgIconProps>(
componentName: string,
id: string,
viewBox: string,
defaultViewBox: string,
defaultWidth: number,
defaultHeight: number,
relativeWidth: string,
relativeHeight: string,
defaultFontSize: number,
content: string,
width: number,
height: number
): React.FC<Props> {
let isMounted = false;
function mountIcon() {
if (!isMounted) {
addSpriteSymbol(new BrowserSymbol({ id, viewBox, content }));
addSpriteSymbol(new BrowserSymbol({ id, viewBox: defaultViewBox, content }));
isMounted = true;
}
}

const Icon: React.FC<Props> = (props) => {
const Icon: React.FC<Props> = ({
viewBox = defaultViewBox,
fontSize = defaultFontSize,
...restProps
}) => {
useIsomorphicLayoutEffect(mountIcon, []);

return (
<SvgIcon
{...props}
viewBox={viewBox}
{...restProps}
id={id}
width={!isNaN(props.width) ? +props.width : width}
height={!isNaN(props.height) ? +props.height : height}
viewBox={viewBox}
defaultWidth={defaultWidth}
defaultHeight={defaultHeight}
relativeWidth={relativeWidth}
relativeHeight={relativeHeight}
fontSize={fontSize}
/>
)
};
Expand Down
39 changes: 35 additions & 4 deletions src/docs/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,16 @@ const example =
<Icon24Cancel />`;

const sizeExample = `<Icon24LogoVk width={20} height={20} />`;
const sizeExampleFontSizeInherit =
`<p style={{ fontSize: 16 }>
<a href="https://google.com" target="_blank">
https://google.com <Icon24ExternalLinkOutline fontSize="inherit" />
</a>
</p>`;

const sizeExampleFontSizeFix = `<Icon24LogoVk fontSize={20} />`;

const sizeExampleFixSize = `<Icon16MoreVertical width={12} height={24} />`;

class Docs extends React.PureComponent {
constructor (props) {
Expand Down Expand Up @@ -87,10 +96,32 @@ class Docs extends React.PureComponent {
<pre>{example}</pre>
<h2>Кастомные размеры</h2>
<p>
Иногда может потребоваться установить для иконки другой размер.
Для этого можно передать свойства <code>width</code> и <code>height</code>, ширина и высота в пикселях. <b>Они должны быть числовыми значениями.</b>
Иногда может потребоваться установить для иконки другой размер. Для этого используйте параметр
{" "}<code>fontSize</code>, который принимает все валидные для него значения.
</p>
<p>
Например, иконка вставлена в блок с текстом и, для лучшего выравнивания, нам нужно, чтобы она имела размер,
равный высоте текста. Мы добьёмся этого передав <code>fontSize="inherit"</code> или
{" "}<code>fontSize="1em"</code>:
</p>
<pre>{sizeExampleFontSizeInherit}</pre>
<p>
А вот пример с фиксированным размером иконки:
</p>
<pre>{sizeExampleFontSizeFix}</pre>
<br />
<p>
Также можно изменить размер через параметры <code>width</code> и <code>height</code>. Нюансы:

<ol>
<li>значения должны быть <b>числовыми</b>;</li>
<li>следует соблюсти <b>соотношение сторон</b> (см. <code>viewBox</code> конкретной иконки);</li>
<li>параметр <code>fontSize</code> больше не будет играть роли</li>
</ol>

Пример:
</p>
<pre>{sizeExample}</pre>
<pre>{sizeExampleFixSize}</pre>
<h2>Стилизация</h2>
<div className="color">
<p>
Expand Down

0 comments on commit dd63966

Please sign in to comment.