From dd63966289c2a7641dc8eff633d2233bd1b787fa Mon Sep 17 00:00:00 2001 From: "i.mirdzhamolov" Date: Wed, 10 Aug 2022 14:46:43 +0300 Subject: [PATCH] feat: move icons to use `fontSize` --- packages/icons-scripts/scripts/reactify.js | 21 ++++-- packages/icons-scripts/src/SvgIcon.tsx | 76 +++++++++++++++++----- src/docs/docs.js | 39 +++++++++-- 3 files changed, 110 insertions(+), 26 deletions(-) diff --git a/packages/icons-scripts/scripts/reactify.js b/packages/icons-scripts/scripts/reactify.js index 66693bfa1..8def69f32 100644 --- a/packages/icons-scripts/scripts/reactify.js +++ b/packages/icons-scripts/scripts/reactify.js @@ -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'; @@ -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()}' ); `; }; diff --git a/packages/icons-scripts/src/SvgIcon.tsx b/packages/icons-scripts/src/SvgIcon.tsx index 3882210ea..645105c62 100644 --- a/packages/icons-scripts/src/SvgIcon.tsx +++ b/packages/icons-scripts/src/SvgIcon.tsx @@ -5,14 +5,29 @@ import { IconSettingsInterface, IconSettingsContext } from './IconSettings'; import { addSpriteSymbol, useIsomorphicLayoutEffect } from './sprite'; export interface SvgIconProps extends React.HTMLAttributes { + /** + * Для пропорционального изменения размера иконки относительно размера шрифта. + * + * > 📝 Чтобы размер наследовался от базового размера шрифта, передайте `"inherit"` или `"1em"`. + * + * @default берётся `Math.max(width, height)` иконки (см. `viewBox`) + */ + fontSize?: string | number; + fill?: string; width?: number; height?: number; viewBox?: string; - fill?: string; getRootRef?: React.RefCallback | React.RefObject; 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) { @@ -28,9 +43,20 @@ function iconClass(fragments: string[], { classPrefix, globalClasses }: IconSett return res; } -const SvgIcon: React.FC = ({ - width, - height, +const SvgIcon: React.FC = ({ + /** + * Внутренние параметры (скрыты от пользователя) + */ + defaultWidth, + defaultHeight, + relativeWidth, + relativeHeight, + + /** + * Пользовательские параметры. + */ + width: widthProp, + height: heightProp, viewBox, id, className = '', @@ -39,14 +65,20 @@ const SvgIcon: React.FC = ({ 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 ( = ({ {...restProps} ref={getRootRef} className={`${ownClass} ${className}`} - style={{ ...style, width, height }} + style={{ ...style, width, height, fontSize }} > = ({ export function makeIcon( 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 { 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) => { + const Icon: React.FC = ({ + viewBox = defaultViewBox, + fontSize = defaultFontSize, + ...restProps + }) => { useIsomorphicLayoutEffect(mountIcon, []); return ( ) }; diff --git a/src/docs/docs.js b/src/docs/docs.js index c3006c135..9fb86eef3 100644 --- a/src/docs/docs.js +++ b/src/docs/docs.js @@ -21,7 +21,16 @@ const example = `; -const sizeExample = ``; +const sizeExampleFontSizeInherit = +`

+ + https://google.com + +

`; + +const sizeExampleFontSizeFix = ``; + +const sizeExampleFixSize = ``; class Docs extends React.PureComponent { constructor (props) { @@ -87,10 +96,32 @@ class Docs extends React.PureComponent {
{example}

Кастомные размеры

- Иногда может потребоваться установить для иконки другой размер. - Для этого можно передать свойства width и height, ширина и высота в пикселях. Они должны быть числовыми значениями. + Иногда может потребоваться установить для иконки другой размер. Для этого используйте параметр + {" "}fontSize, который принимает все валидные для него значения. +

+

+ Например, иконка вставлена в блок с текстом и, для лучшего выравнивания, нам нужно, чтобы она имела размер, + равный высоте текста. Мы добьёмся этого передав fontSize="inherit" или + {" "}fontSize="1em": +

+
{sizeExampleFontSizeInherit}
+

+ А вот пример с фиксированным размером иконки: +

+
{sizeExampleFontSizeFix}
+
+

+ Также можно изменить размер через параметры width и height. Нюансы: + +

    +
  1. значения должны быть числовыми;
  2. +
  3. следует соблюсти соотношение сторон (см. viewBox конкретной иконки);
  4. +
  5. параметр fontSize больше не будет играть роли
  6. +
+ + Пример:

-
{sizeExample}
+
{sizeExampleFixSize}

Стилизация