Skip to content

Commit

Permalink
feat(useThemeVars): added hook useThemeVars (#1042)
Browse files Browse the repository at this point in the history
issue - #1046
  • Loading branch information
gizeasy committed Apr 20, 2021
1 parent cc5c1fe commit 6c6f133
Show file tree
Hide file tree
Showing 5 changed files with 377 additions and 0 deletions.
79 changes: 79 additions & 0 deletions src/hooks/useThemeVars/__stories__/useThemeVars.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# useThemeVars

хук позволяет получить css переменные темы в JS - обьекте

- [Переменные](#переменные)
- [Дополнительные зависимости](#дополнительные-зависимости)

## Переменные

возможно указать какие переменные мы хотим получить из темы,
по умолчанию вернуться все переменные темы входящей в пакет consta-uikit.

Это полезно когда у вам либо не нужен весь список переменных,
либо в ващей теме есть отличные от темы по умолчанию переменные

```tsx
const varsMap = {
color: {
primary: {
...
}
accent: {
...
}
invert: {
...
}
};
control: {
...
};
font: {
...
};
size: {
...
};
space: {
...
};
} as const;

const vars = useThemeVars({vars: varsMap});
```

## Дополнительные зависимости

Пересчет переменных всегда зависит от [модификаторов темы](/?path=/docs/components-theme--playground#пресеты)
если они не изменны от пересчет не происходит. Вы можете добавить свои зависимости,
чтобы пересчет проиходил когда вам это требуется.

Это полезно на пример когда у вас в теме используются медиа запросы, и размеры шрифта зависят от ширины экрана,
тогда вам нужно прописать в `useThemeVars` текущий брейкпонит как зависимость

```tsx
const breakPoint = useBreakPoint();
const vars = useThemeVars({ deps: [breakPoint] });
```

## Свойства

```tsx
export type Vars = {
readonly color: {
readonly primary: readonly string[];
readonly accent: readonly string[];
readonly invert: readonly string[];
};
readonly control: readonly string[];
readonly font: readonly string[];
readonly size: readonly string[];
readonly space: readonly string[];
};
```

| Свойство | Тип | По умолчанию | Описание |
| ---------------------- | ------ | ------------------------------------- | -------------------------- |
| [`vars?`](#переменные) | `Vars` | набор переменных из темы по умолчанию | Переменные |
| `deps?` | `[]` | - | Дополнительные зависимости |
56 changes: 56 additions & 0 deletions src/hooks/useThemeVars/__stories__/useThemeVars.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import './useThemeVarsStories.css';

import React from 'react';

import { Text } from '../../../components/Text/Text';
import { cn } from '../../../utils/bem';
import { createMetadata } from '../../../utils/storybook';
import { useThemeVars } from '../useThemeVars';

import mdx from './useThemeVars.mdx';

const cnUseThemeVars = cn('useThemeVars');

const Vars = (props: { vars: { [key: string]: string }; title: string }) => {
const { vars, title } = props;
return (
<div className={cnUseThemeVars('Section')}>
<Text size="3xl" className={cnUseThemeVars('Title')}>
{title}
</Text>
{Object.keys(vars).map((index) => (
<Text>
{index}: {vars[index]}
</Text>
))}
</div>
);
};

const Default = () => {
const vars = useThemeVars();

return (
<>
<Vars vars={vars.color.primary} title="Color" />
<Vars vars={vars.control} title="Control" />
<Vars vars={vars.font} title="Font" />
<Vars vars={vars.size} title="Size" />
<Vars vars={vars.space} title="Space" />
</>
);
};

export function Playground() {
return <Default />;
}

export default createMetadata({
title: 'Hooks|/useThemeVars',
id: 'Hooks|/useThemeVars',
parameters: {
docs: {
page: mdx,
},
},
});
9 changes: 9 additions & 0 deletions src/hooks/useThemeVars/__stories__/useThemeVarsStories.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.useThemeVars {
&-Title {
margin-bottom: var(--space-m);
}

&-Section {
margin-bottom: var(--space-l);
}
}
131 changes: 131 additions & 0 deletions src/hooks/useThemeVars/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
const cssColorVars = [
'--color-bg-default',
'--color-bg-secondary',
'--color-bg-brand',
'--color-bg-link',
'--color-bg-border',
'--color-bg-stripe',
'--color-bg-ghost',
'--color-bg-tone',
'--color-bg-soft',
'--color-bg-system',
'--color-bg-normal',
'--color-bg-success',
'--color-bg-caution',
'--color-bg-warning',
'--color-bg-alert',
'--color-bg-critical',
'--color-typo-primary',
'--color-typo-secondary',
'--color-typo-ghost',
'--color-typo-brand',
'--color-typo-system',
'--color-typo-normal',
'--color-typo-success',
'--color-typo-caution',
'--color-typo-warning',
'--color-typo-alert',
'--color-typo-critical',
'--color-typo-link',
'--color-typo-link-minor',
'--color-typo-link-hover',
'--color-nums-shadow',
'--color-scroll-bg',
'--color-scroll-thumb',
'--color-scroll-thumb-hover',
'--color-control-bg-default',
'--color-control-typo-default',
'--color-control-typo-placeholder',
'--color-control-bg-border-default',
'--color-control-bg-border-default-hover',
'--color-control-bg-border-focus',
'--color-control-bg-focus',
'--color-control-bg-active',
'--color-control-bg-primary',
'--color-control-bg-primary-hover',
'--color-control-typo-primary',
'--color-control-typo-primary-hover',
'--color-control-bg-secondary',
'--color-control-bg-border-secondary',
'--color-control-bg-border-secondary-hover:',
'--color-control-typo-secondary',
'--color-control-typo-secondary-hover',
'--color-control-bg-ghost',
'--color-control-bg-ghost-hover',
'--color-control-typo-ghost',
'--color-control-typo-ghost-hover',
'--color-control-bg-clear',
'--color-control-bg-clear-hover',
'--color-control-typo-clear',
'--color-control-typo-clear-hover',
'--color-control-bg-disable',
'--color-control-bg-border-disable',
'--color-control-typo-disable',
] as const;

const cssControlVars = [
'--control-radius',
'--control-border-width',
'--control-height-l',
'--control-height-m',
'--control-height-s',
'--control-height-xs',
'--control-box-size-m',
'--control-box-size-l',
'--control-space-l',
'--control-space-m',
'--control-space-s',
'--control-space-xs',
'--control-text-size-l',
'--control-text-size-m',
'--control-text-size-s',
'--control-text-size-xs',
] as const;

const cssFontVars = ['--font-primary', '--font-mono'] as const;

const cssSizeVars = [
'--size-text-2xs',
'--size-text-xs',
'--size-text-s',
'--size-text-m',
'--size-text-l',
'--size-text-xl',
'--size-text-2xl',
'--size-text-3xl',
'--size-text-4xl',
'--size-text-5xl',
'--size-text-6xl',
'--line-height-text-2xs',
'--line-height-text-xs',
'--line-height-text-s',
'--line-height-text-m',
'--line-height-text-l',
] as const;

const cssSpaceVars = [
'--space-3xs',
'--space-2xs',
'--space-xs',
'--space-s',
'--space-m',
'--space-l',
'--space-xl',
'--space-2xl',
'--space-3xl',
'--space-4xl',
'--space-5xl',
'--space-6xl',
] as const;

export const defaultVars = {
color: {
primary: cssColorVars,
accent: cssColorVars,
invert: cssColorVars,
},
control: cssControlVars,
font: cssFontVars,
size: cssSizeVars,
space: cssSpaceVars,
} as const;
102 changes: 102 additions & 0 deletions src/hooks/useThemeVars/useThemeVars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { useMemo } from 'react';

import { cnTheme, useTheme } from '../../components/Theme/Theme';

import { defaultVars } from './helpers';

export { defaultVars } from './helpers';

export type Vars = {
readonly color: {
readonly primary: readonly string[];
readonly accent: readonly string[];
readonly invert: readonly string[];
};
readonly control: readonly string[];
readonly font: readonly string[];
readonly size: readonly string[];
readonly space: readonly string[];
};

const getVars = <T extends string>(
cssVars: readonly T[],
element: HTMLDivElement,
): { [key in T]: string } => {
const vars: { [key in T]: string } = {} as { [key in T]: string };

const style = getComputedStyle(element);

for (let i = 0; i < cssVars.length; i++) {
vars[cssVars[i]] = style.getPropertyValue(cssVars[i]).trim();
}

return vars;
};

const addElement = (mods: {}): HTMLDivElement => {
const element = document.createElement('div');
element.setAttribute('class', cnTheme(mods));
document.body.append(element);
return element;
};

type UseThemeVarsOptions<T> = {
vars?: T;
deps?: [];
};

export const useThemeVars = <T extends Vars = typeof defaultVars>(
options?: UseThemeVarsOptions<T>,
) => {
const variables = options?.vars || defaultVars;

const { theme } = useTheme();

type ThemeVars = {
color: {
primary: { [key in T['color']['primary'][number]]: string };
accent: { [key in T['color']['accent'][number]]: string };
invert: { [key in T['color']['invert'][number]]: string };
};
control: { [key in T['control'][number]]: string };
font: { [key in T['font'][number]]: string };
size: { [key in T['size'][number]]: string };
space: { [key in T['space'][number]]: string };
};

return useMemo(() => {
const elementPrimary = addElement({
...theme,
color: theme.color.primary,
});
const elementAccent = addElement({ color: theme.color.accent });
const elementInvert = addElement({ color: theme.color.invert });

const themeVars: ThemeVars = {
color: {
primary: getVars<T['color']['primary'][number]>(variables.color.primary, elementPrimary),
accent: getVars<T['color']['accent'][number]>(variables.color.accent, elementAccent),
invert: getVars<T['color']['invert'][number]>(variables.color.invert, elementInvert),
},
control: getVars<T['control'][number]>(variables.control, elementPrimary),
font: getVars<T['font'][number]>(variables.font, elementPrimary),
size: getVars<T['size'][number]>(variables.size, elementPrimary),
space: getVars<T['space'][number]>(variables.space, elementPrimary),
};

document.body.removeChild(elementPrimary);
document.body.removeChild(elementAccent);
document.body.removeChild(elementInvert);

return themeVars;
}, [
theme.color.primary,
theme.color.accent,
theme.color.invert,
theme.control,
theme.font,
theme.size,
theme.space,
...(options?.deps ? options.deps : []),
]);
};

0 comments on commit 6c6f133

Please sign in to comment.