diff --git a/README.md b/README.md index fcd791ae..e4ee437f 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ## Файловая структура - `core` - содержит библиотеки, которые являются обязательными зависимостями для работы дизайн системы. +- `docs` - содержит документацию по всем компонентам библиотеки. - `libraries` - содержит пользовательские библиотеки компонентов. - `packages` - содержит утилитарные пакеты, необходимые для корректной работы дизайн системы. - `themes` - содержит пользовательские темы, разбитые на вертикали, сгенерированные на основе файлов из [репозитория](https://github.com/salute-developers/theme-converter). @@ -14,13 +15,17 @@ Содержит библиотеки, которые являются обязательными зависимостями для работы дизайн системы. +### Директория `docs` + +Содержит документацию по всем компонент библиотеки `core-components`. + ### Директория `libraries` Содержит пользовательские библиотеки компонентов. ### Директория `packages` -Содержит утилитарные пакеты, необходимые для корректной работы дизайн системы, а также набор иконок всех размеров. +Содержит утилитарные пакеты, необходимые для корректной работы дизайн системы, а также набор иконок всех размеров. ### Директория `themes` @@ -48,6 +53,12 @@ При дальнейшей разработке можно использовать команду `npx lerna bootstrap` как обычно. +Также перед выполнением всех дальнейших команд необходимо выполнить в корне проекта. + +```bash +$ npm install +``` + ### Поддержка актуального состояния Если нужно обновить файл `platforms/mobile/package.json` (`platforms/tv/package.json`), то необходимо внести все изменения в `package.json` как при обычной разработке и вызвать команду в директории с библиотекой компонент (например plasma-b2c) @@ -72,13 +83,13 @@ $ npm run platform:tv $ cd ./libraries/plasma-b2c ``` -Для сборки storybook на iPhone необходимо запустить iOS симулятор (через `Xcode`) и выполнить команду запуска +Для сборки storybook на iPhone / AppleTV необходимо запустить iOS симулятор (через `Xcode`) и выполнить команду ```bash $ npm run storybook:ios ``` -Для сборки storybook на Android необходимо запустить Android симулятор (через `Android Studio`) и выполнить команду запуска +Для сборки storybook на Android / AndroidTV необходимо запустить Android симулятор (через `Android Studio`) и выполнить команду ```bash $ npm run storybook:android @@ -138,7 +149,7 @@ sectionList: { #### Пересборка проекта -Есть возникают какие-то не описанные здесь ошибки, можно выполнить команду, которая полностью пересоберёт проекты для android и ios. +Есть возникают какие-то не описанные здесь ошибки, можно выполнить команду в директории библиотеки компонент, которая полностью пересоберёт проекты для android и ios. ```bash $ npm run storybook:tv-prebuild diff --git a/core/core-components/platforms/tv/package-lock.json b/core/core-components/platforms/tv/package-lock.json index 71ead0db..930a5485 100644 --- a/core/core-components/platforms/tv/package-lock.json +++ b/core/core-components/platforms/tv/package-lock.json @@ -13,7 +13,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-native": "npm:react-native-tvos@0.72.5-0", - "react-native-svg": "^15.3.0" + "react-native-svg": "15.3.0" }, "devDependencies": { "ts-node": "^10.2.1", diff --git a/core/core-components/platforms/tv/package.json b/core/core-components/platforms/tv/package.json index 84d60b48..27498581 100644 --- a/core/core-components/platforms/tv/package.json +++ b/core/core-components/platforms/tv/package.json @@ -27,7 +27,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-native": "npm:react-native-tvos@0.72.5-0", - "react-native-svg": "^15.3.0" + "react-native-svg": "15.3.0" }, "devDependencies": { "ts-node": "^10.2.1", diff --git a/core/core-components/src/components/Cell/Cell.styles.ts b/core/core-components/src/components/Cell/Cell.styles.ts index 6b6374b6..c2bee841 100644 --- a/core/core-components/src/components/Cell/Cell.styles.ts +++ b/core/core-components/src/components/Cell/Cell.styles.ts @@ -103,6 +103,7 @@ export const getStyle = ( gap: sizeStyle.gap, ...externalStyle?.container, }, + // TODO: Удалить и проверить, что всё работает content: { height: 'auto', display: 'flex', diff --git a/core/core-components/src/components/Cell/Cell.types.ts b/core/core-components/src/components/Cell/Cell.types.ts index fb9fc0d4..a4e57cb9 100644 --- a/core/core-components/src/components/Cell/Cell.types.ts +++ b/core/core-components/src/components/Cell/Cell.types.ts @@ -13,7 +13,7 @@ export type CellWithContentRight = { */ alignContentRight?: AlignProp; /** - * Слот для контента спарва, например `Icon` + * Слот для контента справа, например `Icon` */ contentRight?: ReactNode; /** @@ -32,7 +32,7 @@ export type CellWithDisclosure = { */ alignContentRight?: never; /** - * Слот для контента спарва, например `Icon` + * Слот для контента справа, например `Icon` */ contentRight?: never; /** diff --git a/core/core-components/src/components/FocusContainer/FocusContainer.types.ts b/core/core-components/src/components/FocusContainer/FocusContainer.types.ts index 42677ae8..a6535d8c 100644 --- a/core/core-components/src/components/FocusContainer/FocusContainer.types.ts +++ b/core/core-components/src/components/FocusContainer/FocusContainer.types.ts @@ -1,15 +1,41 @@ import { PressableProps } from 'react-native'; +interface FocusProps { + /** + * Цвет текста компонента в фокусе + */ + focusedTextColor?: string; + /** + * Бэкграунд компонента в фокусе + */ + focusedBackgroundColor?: string; + /** + * Размер зума компонента в фокусе + */ + focusedScale: number; + /** + * Находится ли компонент в фокусе + */ + focused?: boolean; + /** + * Нажат ли компонент + */ + pressed?: boolean; +} + export interface CustomFocusContainerProps extends Omit { - children: (state: { - focusedTextColor: string; - focusedBackgroundColor: string; - focusedScale: number; - focused?: boolean; - pressed?: boolean; - }) => React.ReactNode; - unfocusedBackgroundColor: string; - unfocusedTextColor: string; + /** + * Функция, которая возвращает компонент, на который нужно сфокусироваться + */ + children: (state: FocusProps) => React.ReactNode; + /** + * Бэкграунд компонента вне фокуса + */ + unfocusedBackgroundColor?: string; + /** + * Цвет текста компонента вне фокуса + */ + unfocusedTextColor?: string; } export interface FocusContainerProps extends CustomFocusContainerProps {} diff --git a/core/core-components/src/components/ThemeProvider/ThemeProvider.types.ts b/core/core-components/src/components/ThemeProvider/ThemeProvider.types.ts index bf6c3f3b..3d6b13ca 100644 --- a/core/core-components/src/components/ThemeProvider/ThemeProvider.types.ts +++ b/core/core-components/src/components/ThemeProvider/ThemeProvider.types.ts @@ -1,10 +1,10 @@ import { ReactElement } from 'react'; -export type Color = { +export interface Color { [k: string]: { [k: string]: string; }; -}; +} export interface LinearGradientType { kind: 'linear'; @@ -39,6 +39,12 @@ export interface Shape { }; } +export interface Spacing { + [k: string]: { + [k: string]: number; + }; +} + export interface Shadow { [k: string]: { [k: string]: { @@ -86,13 +92,23 @@ export interface Data { gradient: Gradient; shadow: Shadow; shape: Shape; + spacing: Spacing; fontFamily: FontFamily; typography: Typography; } export interface Theme { + /* + * Режим темы + */ mode: 'light' | 'dark'; + /* + * Размер экрана, на котором + */ screenSize: 'screenS' | 'screenM' | 'screenL'; + /* + * Объект с токенами темы + */ data: T; } diff --git a/core/core-themes/src/creators/createSpacingTokens.ts b/core/core-themes/src/creators/createSpacingTokens.ts new file mode 100644 index 00000000..0a3fe7f0 --- /dev/null +++ b/core/core-themes/src/creators/createSpacingTokens.ts @@ -0,0 +1,33 @@ +import { TokenType } from '../types'; +import { kebabToCamel, writeTokens } from '../utils'; + +const getSpacing = (spacing: any, tokens: TokenType[], kind: 'spacing') => + tokens + .filter((token) => token.tags[0] === kind) + .map((token) => { + const [, ...rest] = token.name.split('.'); + const value = spacing[token.name]; + const tokenName = kebabToCamel(rest.join('-')).replace(/(.*)x/gi, 'x$1'); + + return `/** ${token.description} */\n ${tokenName}: ${JSON.stringify(value.value)},`; + }) + .join('\n '); + +const getContent = (sizeSpacing: string) => `export const spacing = { + size: { + ${sizeSpacing} + }, +}; +`; + +export const createSpacingTokens = (themeDir: string, spacing: any, tokens?: Array) => { + if (!tokens?.length) { + return; + } + + const sizeSpacing = getSpacing(spacing, tokens, 'spacing'); + + const content = getContent(sizeSpacing); + + writeTokens(content, themeDir, 'spacing.ts'); +}; diff --git a/core/core-themes/src/creators/index.ts b/core/core-themes/src/creators/index.ts index dfb085d2..2107b026 100644 --- a/core/core-themes/src/creators/index.ts +++ b/core/core-themes/src/creators/index.ts @@ -2,5 +2,6 @@ export { createColorTokens } from './createColorTokens'; export { createGradientTokens } from './createGradientTokens'; export { createShadowTokens } from './createShadowTokens'; export { createShapeTokens } from './createShapeTokens'; +export { createSpacingTokens } from './createSpacingTokens'; export { createFontFamilyTokens } from './createFontFamilyTokens'; export { createTypographyTokens } from './createTypographyTokens'; diff --git a/core/core-themes/src/generators/generateTheme.ts b/core/core-themes/src/generators/generateTheme.ts index b1b3aa27..4fcf61a8 100644 --- a/core/core-themes/src/generators/generateTheme.ts +++ b/core/core-themes/src/generators/generateTheme.ts @@ -3,15 +3,16 @@ import fs from 'fs'; import { getMetaGrouped } from '../utils'; import { ThemeSource } from '../types'; - import { createColorTokens, createFontFamilyTokens, createGradientTokens, createShadowTokens, createShapeTokens, + createSpacingTokens, createTypographyTokens, } from '../creators'; + import { generateIndex } from '.'; export const generateTheme = (outDir: string, themeSource: ThemeSource) => { @@ -25,6 +26,7 @@ export const generateTheme = (outDir: string, themeSource: ThemeSource) => { createGradientTokens(themeDir, variations.gradient, metaGrouped.gradient); createShadowTokens(themeDir, variations.shadow, metaGrouped.shadow); createShapeTokens(themeDir, variations.shape, metaGrouped.shape); + createSpacingTokens(themeDir, variations.spacing, metaGrouped.spacing); createFontFamilyTokens(themeDir, variations.fontFamily, metaGrouped.fontFamily); createTypographyTokens(themeDir, variations.typography, metaGrouped.typography); diff --git a/core/core-themes/src/types/index.ts b/core/core-themes/src/types/index.ts index acb35b9d..1c798687 100644 --- a/core/core-themes/src/types/index.ts +++ b/core/core-themes/src/types/index.ts @@ -18,14 +18,16 @@ export interface Variations< T3 extends any = any, T4 extends any = any, T5 extends any = any, - T6 extends any = any + T6 extends any = any, + T7 extends any = any > { color: T1; gradient: T2; - shape: T4; - shadow: T3; - typography: T5; - fontFamily: T6; + shape: T3; + spacing: T4; + shadow: T5; + typography: T6; + fontFamily: T7; } export type Variation = keyof Variations; diff --git a/docs/Button.md b/docs/Button.md new file mode 100644 index 00000000..6c60f53b --- /dev/null +++ b/docs/Button.md @@ -0,0 +1,148 @@ +## Button + +Компонент, с помощью которого можно вызвать какое-либо действие и который может отображать текст с иконками. + +### Свойства и типы + +| Название свойства | Тип | Значение по умолчанию | Описание | +| ----------------- | ---------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| style | Style | - | Объект для стилизации компонента | +| spacing | Spacing | 'packed' | Расположение контента внутри кнопки | +| value | string | - | Значение кнопки | +| children | ReactNode | - | Контент кнопки | +| text | string | - | Текстовая надпись | +| contentLeft | ReactNode | - | Слот для контента слева, например `Icon` | +| contentRight | ReactNode | - | Слот для контента справа, например `Icon` | +| isLoading | boolean | - | Состояние загрузки | +| loader | ReactNode | - | Слот для контента загрузки | +| stretching | Stretching | 'auto' | Поведение ширины кнопки. Может принимать три значения: fixed - кнопка фиксированной ширины; filled - кнопка занимает всю доступную ширину auto - кнопка растягивается в зависимости от контента | +| disabled | boolean | - | Кнопка неактивна | +| pin | Pin | 'square-square' | Скругление border-radius | +| view | string | - | Вид кнопки | +| size | string | - | Размер кнопки | + +#### Расширенные типы + +Дополнительное описание типов + +```ts +type Spacing = 'packed' | 'space-between'; + +type Stretching = 'fixed' | 'filled' | 'auto'; + +type Pin = | + | 'square-square' + | 'square-clear' + | 'clear-square' + | 'clear-clear' + | 'clear-circle' + | 'circle-clear' + | 'circle-circle' +``` + +#### Взаимоисключающие свойства + +Свойство `value` - это значение кнопки. Оно отображается справа от основного текста. + +`value` и `contentRight` взаимоисключающие: если передано одно, второе передать нельзя. + +### Стилизация + +Для того, чтобы изменить внешний вид компонента, необходимо использовать свойство `style`, в который можно передать объекты, содержащие наборы стилей типа `ViewStyle` или `TextStyle`. + +Пример стилизации компонента находится в разделе ниже. + +### Примеры использования + +#### Стандартный пример + +```ts +import { Button } from '@salutejs/plasma-star-ds-tv'; +import { IconPlasma } from '@salutejs/plasma-icons-native'; + +const App = () => { + const onPress = () => { + alert('Произошёл onPress'); + }; + + return ( + + ); +}; + +export default App; +``` + +#### Пример с переопределением стилей + +```ts +import { Button } from '@salutejs/plasma-star-ds-tv'; +import { IconPlasma } from '@salutejs/plasma-icons-native'; + +const App = () => { + const onPress = () => { + alert('Произошёл onPress'); + }; + + return ( +