diff --git a/website/plasma-giga-docs/.gitignore b/website/plasma-giga-docs/.gitignore new file mode 100644 index 0000000000..b2d6de3062 --- /dev/null +++ b/website/plasma-giga-docs/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/website/plasma-giga-docs/.npmrc b/website/plasma-giga-docs/.npmrc new file mode 100644 index 0000000000..2359dcc5f5 --- /dev/null +++ b/website/plasma-giga-docs/.npmrc @@ -0,0 +1,11 @@ +@salutejs:registry=https://registry.npmjs.org/ + +save-exact=true + +# disabled auto-installing peer dependencies +legacy-peer-deps=true + +# for specify Node, NPM Version to use +engine-strict=true + +//registry.npmjs.org/:_authToken=${NPM_REGISTRY_TOKEN} diff --git a/website/plasma-giga-docs/LICENSE.txt b/website/plasma-giga-docs/LICENSE.txt new file mode 100644 index 0000000000..4ac192b87f --- /dev/null +++ b/website/plasma-giga-docs/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Salute Devices + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/website/plasma-giga-docs/babel.config.js b/website/plasma-giga-docs/babel.config.js new file mode 100644 index 0000000000..0adade1fb9 --- /dev/null +++ b/website/plasma-giga-docs/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/website/plasma-giga-docs/docs/_category_.json b/website/plasma-giga-docs/docs/_category_.json new file mode 100644 index 0000000000..514fe8a7a0 --- /dev/null +++ b/website/plasma-giga-docs/docs/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "PLASMA-GIGA", + "position": 2 +} diff --git a/website/plasma-giga-docs/docs/components/Accordion.mdx b/website/plasma-giga-docs/docs/components/Accordion.mdx new file mode 100644 index 0000000000..c19d9daaef --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Accordion.mdx @@ -0,0 +1,267 @@ +--- +id: accordion +title: Accordion +--- + +import { PropsTable } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Accordion +Компонент выпадающей информации + + +# AccordionItem +Компонент выпадающей информации + + +Компонент представляет собой заголовок и контент, который раскрывается при нажатии. + +## Примеры Accordion + + + + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+
+ ); + } + ``` +
+
+ +### Использование Accordion в Controlled варианте + +```tsx live + import React, { useState } from 'react'; + import { Accordion, AccordionItem, IconButton } from '@salutejs/plasma-giga'; + import { IconPlus } from '@salutejs/plasma-icons'; + + export function App() { + const [activeFirst, setActiveFirst] = useState(false); + const [activeSecond, setActiveSecond] = useState(false); + const [activeThree, setActiveThree] = useState(false); + + const contentRight = (active, setActive) => { + return ( + setActive(!active)}> + + + ) + } + + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` + +### Использование Accordion SingleActive + +```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` + +### Использование Accordion с eventKey и defaultActiveEventKey + +```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` + + +### Использование AccordionItem атрибута Type + + + + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Accordion, AccordionItem } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + После указания деталей заправки нажмите кнопку «К оплате». Откроется окно оплаты, где вы сможете списать бонусы и оплатить ими до 99% стоимости топлива + +
+ ); + } + ``` +
+
+ diff --git a/website/plasma-giga-docs/docs/components/Attach.mdx b/website/plasma-giga-docs/docs/components/Attach.mdx new file mode 100644 index 0000000000..dfbeb1ebe5 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Attach.mdx @@ -0,0 +1,151 @@ +--- +id: attach +title: Attach +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Attach + + + +## Примеры + +### Подсказка к кнопке +Вид `helperText` задается с помощью свойства `helperTextView`. Возможные значения свойства: ++ `"default"` – по умолчанию; ++ `"negative"` – ошибка. + +```tsx live +import React from 'react'; +import { Attach } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + +
+ ); +} +``` + +### Вид кнопки +С помощью свойства `buttonType` можно менять вид кнопки: обычный или с иконкой. + +```tsx live +import React from 'react'; +import { Attach } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + +
+ ); +} +``` + +### Расположение элементов +C помощью свойства `flow` можно регулировать расположение элементов в зависимости от ширины контейнера. + +```tsx live +import React from 'react'; +import { Attach } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + +
+ ); +} +``` + +### Фильтр форматов файлов +Свойство `acceptedFileFormats` устанавливает доступные форматы файлов. + +```tsx live +import React from 'react'; +import { Attach } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ +
+ ); +} +``` + +### Пример использования в форме + +```tsx live +import React, { useState } from 'react'; +import { Attach, Button } from '@salutejs/plasma-giga'; + +function App() { + const ids = ['0', '1', '2']; + const [isLoading, setIsLoading] = useState(false); + const [attachedFiles, setAttachedFiles] = useState([]); + + const handleAttachFile = (e) => { + setAttachedFiles((prevAttachedFiles) => [ + ...prevAttachedFiles, + { + fileData: e.target.files[0], + id: e.target.id, + }, + ]); + }; + + const handleAttachClear = (id) => { + setAttachedFiles(attachedFiles.filter((file) => file.id !== id)); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + setIsLoading(true); + + const formData = new FormData(e.target); + console.log('formData', Object.fromEntries(formData)); + + setTimeout(() => { + setAttachedFiles([]); + setIsLoading(false); + }, 2000); + }; + + return ( + <> + {isLoading ? 'Форма отправляется' : 'Прикрепленные файлы:'} + {!isLoading && attachedFiles.length > 0 && ( +
+ {attachedFiles.map((file) => ( + {file.fileData.name} + ))} +
+ )} + + {!isLoading && ( +
+ {ids.map((id) => ( + handleAttachClear(id)} + /> + ))} + + + + )} + + ); +} +``` \ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Autocomplete.mdx b/website/plasma-giga-docs/docs/components/Autocomplete.mdx new file mode 100644 index 0000000000..1f89e9ccfa --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Autocomplete.mdx @@ -0,0 +1,561 @@ +--- +id: autocomplete +title: Autocomplete +--- + +import { PropsTable, Description } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Autocomplete +Поле ввода с подсказками в выпадающем списке. + + + + +## Использование +Расширенная версия компонента `TextField`. Добавилась возможность использования выпадающего списка с подсказками.\ +По умолчанию подсказки фильтруются вне зависимости от регистра. Но это можно изменить, прокинув свой коллбэк `filter`.\ +Все пропсы, которые поддерживает компонент `TextField` также поддерживаются и здесь. +Формат подсказок `suggestions` следующий: + +```tsx +type SuggestionItem = { + /** + * Метка-подпись к подсказке + */ + label: string; + /** + * Слот для контента слева + */ + contentLeft?: ReactNode; + /** + * Слот для контента справа + */ + contentRight?: ReactNode; +}; +``` + +## Примеры + + + + ```tsx live + import React from 'react'; + import { Autocomplete } from '@salutejs/plasma-giga'; + + export function App() { + const mockData = [ + { label: 'Алексей Смирнов' }, + { label: 'Екатерина Иванова' }, + { label: 'Дмитрий Петров' }, + { label: 'Ольга Васильева' }, + { label: 'Сергей Сидоров' }, + { label: 'Мария Кузнецова' }, + { label: 'Андрей Попов' }, + { label: 'Анна Николаева' }, + { label: 'Иван Федоров' }, + { label: 'Наталья Морозова' }, + { label: 'Михаил Павлов' }, + { label: 'Елена Романова' }, + { label: 'Владимир Киселев' }, + { label: 'Татьяна Захарова' }, + { label: 'Николай Семенов' }, + { label: 'Юлия Белова' }, + { label: 'Александр Гусев' }, + { label: 'Оксана Яковлева' }, + { label: 'Игорь Егорова' }, + { label: 'Вера Тихомирова' }, + { label: 'Артем Григорьев' }, + { label: 'Евгения Козлова' }, + { label: 'Максим Лебедев' }, + { label: 'Виктория Калашникова' }, + { label: 'Константин Абрамов' }, + { label: 'Светлана Новикова' }, + { label: 'Юрий Волков' }, + { label: 'Валентина Воробьева' }, + { label: 'Павел Сергеев' }, + { label: 'Людмила Виноградова' }, + { label: 'Антон Соловьев' }, + { label: 'Маргарита Цветкова' }, + { label: 'Роман Трофимов' }, + { label: 'Лариса Зайцева' }, + { label: 'Евгений Никитин' }, + { label: 'Галина Михайлова' }, + { label: 'Владислав Антонов' }, + { label: 'Дарья Филатова' }, + { label: 'Олег Буров' }, + { label: 'Инна Медведева' }, + { label: 'Вячеслав Крылов' }, + { label: 'Тамара Беляева' }, + { label: 'Кирилл Марков' }, + { label: 'Марина Пономарева' }, + { label: 'Борис Захаров' }, + { label: 'Жанна Савельева' }, + { label: 'Федор Жуков' }, + { label: 'Елизавета Логинова' }, + { label: 'Виктор Рыбаков' }, + { label: 'Лилия Макарова' }, + ]; + + return ( +
+ +
+ ); + } + ``` +
+ + ```tsx live + import React from 'react'; + import { Autocomplete } from '@salutejs/plasma-giga'; + + export function App() { + const mockData = [ + { label: 'Алексей Смирнов' }, + { label: 'Екатерина Иванова' }, + { label: 'Дмитрий Петров' }, + { label: 'Ольга Васильева' }, + { label: 'Сергей Сидоров' }, + { label: 'Мария Кузнецова' }, + { label: 'Андрей Попов' }, + { label: 'Анна Николаева' }, + { label: 'Иван Федоров' }, + { label: 'Наталья Морозова' }, + { label: 'Михаил Павлов' }, + { label: 'Елена Романова' }, + { label: 'Владимир Киселев' }, + { label: 'Татьяна Захарова' }, + { label: 'Николай Семенов' }, + { label: 'Юлия Белова' }, + { label: 'Александр Гусев' }, + { label: 'Оксана Яковлева' }, + { label: 'Игорь Егорова' }, + { label: 'Вера Тихомирова' }, + { label: 'Артем Григорьев' }, + { label: 'Евгения Козлова' }, + { label: 'Максим Лебедев' }, + { label: 'Виктория Калашникова' }, + { label: 'Константин Абрамов' }, + { label: 'Светлана Новикова' }, + { label: 'Юрий Волков' }, + { label: 'Валентина Воробьева' }, + { label: 'Павел Сергеев' }, + { label: 'Людмила Виноградова' }, + { label: 'Антон Соловьев' }, + { label: 'Маргарита Цветкова' }, + { label: 'Роман Трофимов' }, + { label: 'Лариса Зайцева' }, + { label: 'Евгений Никитин' }, + { label: 'Галина Михайлова' }, + { label: 'Владислав Антонов' }, + { label: 'Дарья Филатова' }, + { label: 'Олег Буров' }, + { label: 'Инна Медведева' }, + { label: 'Вячеслав Крылов' }, + { label: 'Тамара Беляева' }, + { label: 'Кирилл Марков' }, + { label: 'Марина Пономарева' }, + { label: 'Борис Захаров' }, + { label: 'Жанна Савельева' }, + { label: 'Федор Жуков' }, + { label: 'Елизавета Логинова' }, + { label: 'Виктор Рыбаков' }, + { label: 'Лилия Макарова' }, + ]; + + const [value, setValue] = useState('') + + return ( +
+ setValue(e.target.value)} onSuggestionSelect={(e) => setValue(e.label)} suggestions={mockData} listMaxHeight="250px" label="Label" placeholder="Placeholder" leftHelper="Введите имя Алексей" /> +
+ ); + } + ``` +
+ + Это пример с бесконечным лоадингом подсказок в списке. Для удобства порог для открытия списка подсказок понижен до 0 (открываться будет сразу при фокусе). + + ```tsx live + import React from 'react'; + import { Autocomplete, Cell, Spinner } from '@salutejs/plasma-giga'; + + export function App() { + const mockData = [ + { label: 'Алексей Смирнов' }, + { label: 'Екатерина Иванова' }, + { label: 'Дмитрий Петров' }, + { label: 'Ольга Васильева' }, + { label: 'Сергей Сидоров' }, + { label: 'Мария Кузнецова' }, + { label: 'Андрей Попов' }, + { label: 'Анна Николаева' }, + { label: 'Иван Федоров' }, + { label: 'Наталья Морозова' }, + { label: 'Михаил Павлов' }, + { label: 'Елена Романова' }, + { label: 'Владимир Киселев' }, + { label: 'Татьяна Захарова' }, + { label: 'Николай Семенов' }, + { label: 'Юлия Белова' }, + { label: 'Александр Гусев' }, + { label: 'Оксана Яковлева' }, + { label: 'Игорь Егорова' }, + { label: 'Вера Тихомирова' }, + { label: 'Артем Григорьев' }, + { label: 'Евгения Козлова' }, + { label: 'Максим Лебедев' }, + { label: 'Виктория Калашникова' }, + { label: 'Константин Абрамов' }, + { label: 'Светлана Новикова' }, + { label: 'Юрий Волков' }, + { label: 'Валентина Воробьева' }, + { label: 'Павел Сергеев' }, + { label: 'Людмила Виноградова' }, + { label: 'Антон Соловьев' }, + { label: 'Маргарита Цветкова' }, + { label: 'Роман Трофимов' }, + { label: 'Лариса Зайцева' }, + { label: 'Евгений Никитин' }, + { label: 'Галина Михайлова' }, + { label: 'Владислав Антонов' }, + { label: 'Дарья Филатова' }, + { label: 'Олег Буров' }, + { label: 'Инна Медведева' }, + { label: 'Вячеслав Крылов' }, + { label: 'Тамара Беляева' }, + { label: 'Кирилл Марков' }, + { label: 'Марина Пономарева' }, + { label: 'Борис Захаров' }, + { label: 'Жанна Савельева' }, + { label: 'Федор Жуков' }, + { label: 'Елизавета Логинова' }, + { label: 'Виктор Рыбаков' }, + { label: 'Лилия Макарова' }, + ]; + + const getData = async (data, page, pageSize = 10) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({page, data: data.slice(page * pageSize - 1, page * pageSize - 1 + pageSize)}) + }, 1500) + }) + } + + const [suggestions, setSuggestions] = useState({ page: 1, data: mockData.slice(0, 10) }) + const [isInfiniteLoading, setIsInfiniteLoading] = useState(false) + + const onScroll = async (e) => { + if (isInfiniteLoading) return + + if (e.target.scrollTop + e.target.offsetHeight + 10 > e.target.scrollHeight) { + setIsInfiniteLoading(true) + + const res = await getData(mockData, suggestions.page + 1, 10) + setSuggestions({page: res.page, data: [...suggestions.data, ...res.data]}) + + setIsInfiniteLoading(false) + } + }; + + return ( +
+ } title="Загрузка" stretching="auto" /> : undefined} + label="Label" + placeholder="Placeholder" + leftHelper="Введите имя Алексей" + /> +
+ ); + } + ``` +
+ + Пример кастомной фильтрации на бекенде. Не забываем отключить дефолтную фильтрацию. + + ```tsx live + import React from 'react'; + import { Autocomplete } from '@salutejs/plasma-giga'; + + export function App() { + const mockData = [ + { label: 'Алексей Смирнов' }, + { label: 'Екатерина Иванова' }, + { label: 'Дмитрий Петров' }, + { label: 'Ольга Васильева' }, + { label: 'Сергей Сидоров' }, + { label: 'Мария Кузнецова' }, + { label: 'Андрей Попов' }, + { label: 'Анна Николаева' }, + { label: 'Иван Федоров' }, + { label: 'Наталья Морозова' }, + { label: 'Михаил Павлов' }, + { label: 'Елена Романова' }, + { label: 'Владимир Киселев' }, + { label: 'Татьяна Захарова' }, + { label: 'Николай Семенов' }, + { label: 'Юлия Белова' }, + { label: 'Александр Гусев' }, + { label: 'Оксана Яковлева' }, + { label: 'Игорь Егорова' }, + { label: 'Вера Тихомирова' }, + { label: 'Артем Григорьев' }, + { label: 'Евгения Козлова' }, + { label: 'Максим Лебедев' }, + { label: 'Виктория Калашникова' }, + { label: 'Константин Абрамов' }, + { label: 'Светлана Новикова' }, + { label: 'Юрий Волков' }, + { label: 'Валентина Воробьева' }, + { label: 'Павел Сергеев' }, + { label: 'Людмила Виноградова' }, + { label: 'Антон Соловьев' }, + { label: 'Маргарита Цветкова' }, + { label: 'Роман Трофимов' }, + { label: 'Лариса Зайцева' }, + { label: 'Евгений Никитин' }, + { label: 'Галина Михайлова' }, + { label: 'Владислав Антонов' }, + { label: 'Дарья Филатова' }, + { label: 'Олег Буров' }, + { label: 'Инна Медведева' }, + { label: 'Вячеслав Крылов' }, + { label: 'Тамара Беляева' }, + { label: 'Кирилл Марков' }, + { label: 'Марина Пономарева' }, + { label: 'Борис Захаров' }, + { label: 'Жанна Савельева' }, + { label: 'Федор Жуков' }, + { label: 'Елизавета Логинова' }, + { label: 'Виктор Рыбаков' }, + { label: 'Лилия Макарова' }, + ]; + + const [value, setValue] = useState('') + const [suggestions, setSuggestions] = useState(mockData) + + + const getData = async (value) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(mockData.filter(({ label }) => label.toLowerCase().includes(value.toString().toLowerCase()))) + }, 200) + }) + } + + useEffect(() => { + getData(value).then(setSuggestions) + }, [value]) + + return ( +
+ setValue(e.target.value)} + onSuggestionSelect={(e) => setValue(e.label)} + suggestions={suggestions} + listMaxHeight="250px" + filter={() => true} // Отключаем дефолтную фильтрацию + label="Label" + placeholder="Placeholder" + leftHelper="Введите имя Алексей" + /> +
+ ); + } + ``` +
+ + Если произошла ошибка, или же к примеру нужно показать некую информацию пользователю, то для этой цели можно использовать компонент `EmptyState`. + + ```tsx live + import React from 'react'; + import { Autocomplete, EmptyState, Spinner } from '@salutejs/plasma-giga'; + import { IconRefresh } from '@salutejs/plasma-icons'; + + export function App() { + const mockData = [ + { label: 'Алексей Смирнов' }, + { label: 'Екатерина Иванова' }, + { label: 'Дмитрий Петров' }, + { label: 'Ольга Васильева' }, + { label: 'Сергей Сидоров' }, + { label: 'Мария Кузнецова' }, + { label: 'Андрей Попов' }, + { label: 'Анна Николаева' }, + { label: 'Иван Федоров' }, + { label: 'Наталья Морозова' }, + { label: 'Михаил Павлов' }, + { label: 'Елена Романова' }, + { label: 'Владимир Киселев' }, + { label: 'Татьяна Захарова' }, + { label: 'Николай Семенов' }, + { label: 'Юлия Белова' }, + { label: 'Александр Гусев' }, + { label: 'Оксана Яковлева' }, + { label: 'Игорь Егорова' }, + { label: 'Вера Тихомирова' }, + { label: 'Артем Григорьев' }, + { label: 'Евгения Козлова' }, + { label: 'Максим Лебедев' }, + { label: 'Виктория Калашникова' }, + { label: 'Константин Абрамов' }, + { label: 'Светлана Новикова' }, + { label: 'Юрий Волков' }, + { label: 'Валентина Воробьева' }, + { label: 'Павел Сергеев' }, + { label: 'Людмила Виноградова' }, + { label: 'Антон Соловьев' }, + { label: 'Маргарита Цветкова' }, + { label: 'Роман Трофимов' }, + { label: 'Лариса Зайцева' }, + { label: 'Евгений Никитин' }, + { label: 'Галина Михайлова' }, + { label: 'Владислав Антонов' }, + { label: 'Дарья Филатова' }, + { label: 'Олег Буров' }, + { label: 'Инна Медведева' }, + { label: 'Вячеслав Крылов' }, + { label: 'Тамара Беляева' }, + { label: 'Кирилл Марков' }, + { label: 'Марина Пономарева' }, + { label: 'Борис Захаров' }, + { label: 'Жанна Савельева' }, + { label: 'Федор Жуков' }, + { label: 'Елизавета Логинова' }, + { label: 'Виктор Рыбаков' }, + { label: 'Лилия Макарова' }, + ]; + + const getData = async () => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(mockData) + }, 1500) + }) + } + + const [isError, setIsError] = useState(true) + const [isLoading, setIsLoading] = useState(false) + const [value, setValue] = useState('') + const [suggestions, setSuggestions] = useState([]) + + const handleGetData = async () => { + setIsLoading(true) + + const res = await getData() + + setIsLoading(false) + setIsError(false) + setSuggestions(res) + } + + return ( +
+ setValue(e.target.value)} + onSuggestionSelect={(e) => setValue(e.label)} + suggestions={suggestions} + listMaxHeight="250px" + renderList={isError ? () => } description="Произошла ошибка. Попробуйте обновить." buttonText="Обновить" buttonAction={handleGetData} /> : undefined} + contentRight={isLoading ? : undefined} + label="Label" + placeholder="Placeholder" + leftHelper="Введите имя Алексей" + /> +
+ ); + } + ``` +
+ + Иногда возникает необходимость вынесения выпадающего списка на уровни выше в DOM. К примеру, когда у родительского блока имеется скролл, и список будет обрезаться, чего хотелось бы избежать.\ + Для такой реализации имеется пропс `portal`, который принимает либо `ref` либо `id` html-тега. + ```tsx live + import React from 'react'; + import { Autocomplete } from '@salutejs/plasma-giga'; + + export function App() { + const mockData = [ + { label: 'Алексей Смирнов' }, + { label: 'Екатерина Иванова' }, + { label: 'Дмитрий Петров' }, + { label: 'Ольга Васильева' }, + { label: 'Сергей Сидоров' }, + { label: 'Мария Кузнецова' }, + { label: 'Андрей Попов' }, + { label: 'Анна Николаева' }, + { label: 'Иван Федоров' }, + { label: 'Наталья Морозова' }, + { label: 'Михаил Павлов' }, + { label: 'Елена Романова' }, + { label: 'Владимир Киселев' }, + { label: 'Татьяна Захарова' }, + { label: 'Николай Семенов' }, + { label: 'Юлия Белова' }, + { label: 'Александр Гусев' }, + { label: 'Оксана Яковлева' }, + { label: 'Игорь Егорова' }, + { label: 'Вера Тихомирова' }, + { label: 'Артем Григорьев' }, + { label: 'Евгения Козлова' }, + { label: 'Максим Лебедев' }, + { label: 'Виктория Калашникова' }, + { label: 'Константин Абрамов' }, + { label: 'Светлана Новикова' }, + { label: 'Юрий Волков' }, + { label: 'Валентина Воробьева' }, + { label: 'Павел Сергеев' }, + { label: 'Людмила Виноградова' }, + { label: 'Антон Соловьев' }, + { label: 'Маргарита Цветкова' }, + { label: 'Роман Трофимов' }, + { label: 'Лариса Зайцева' }, + { label: 'Евгений Никитин' }, + { label: 'Галина Михайлова' }, + { label: 'Владислав Антонов' }, + { label: 'Дарья Филатова' }, + { label: 'Олег Буров' }, + { label: 'Инна Медведева' }, + { label: 'Вячеслав Крылов' }, + { label: 'Тамара Беляева' }, + { label: 'Кирилл Марков' }, + { label: 'Марина Пономарева' }, + { label: 'Борис Захаров' }, + { label: 'Жанна Савельева' }, + { label: 'Федор Жуков' }, + { label: 'Елизавета Логинова' }, + { label: 'Виктор Рыбаков' }, + { label: 'Лилия Макарова' }, + ]; + + const ref = useRef(null); + + return ( +
+ +
+ ); + } + ``` +
+
+ +## Клавиатурная навигация + +Данный компонент соответствует требования W3C: [Combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list/). + +- `Tab, Escape` - закрывает автокомплит. Перемещает фокус на следующий элемент на странице; +- `Enter` - выбираем подсказку из списка; +- `Home` - перемещает фокус на первый элемент; +- `End` - перемещает фокус на последний элемент; +- `ArrowUp` - перемещает фокус на один элемент выше; +- `ArrowDown` - перемещает фокус на один элемент ниже; diff --git a/website/plasma-giga-docs/docs/components/Avatar.mdx b/website/plasma-giga-docs/docs/components/Avatar.mdx new file mode 100644 index 0000000000..c50175c6a0 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Avatar.mdx @@ -0,0 +1,149 @@ +--- +id: avatar +title: Avatar +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Avatar + + + + +## Примеры + +### Размер аватара +Размер задается с помощью свойства `size`. Возможные значения свойства: `"xxl"`, `"l"`, `"m"`, `"s"`, `"fit"`. + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + + + + ); +} +``` + +### Инициалы вместо фотографии +Инициалы можно задать с помощью свойства `name`. +Желательный формат строки - "Имя Фамилия". +Также не должно быть свойства url. + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + ); +} +``` + +### Статус аватара +Статус задается с помощью свойства `status`. Возможные значения: `"active"`, `"inactive"`. + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + + ); +} +``` + +### Увеличение при наведении +Опциональное свойство `"isScalable"`. + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + ); +} +``` + +### Доступность + +#### Avatar c использованием изображения + +В данном случае руководствуемся принципом универсального дизайна, т.е. незрячий должен получить ту же информацию, что и зрячий. + +Поэтому добавляем/используем свойства: `role`, `tabIndex` и `aria-label`. + +Примечание: +- если указано свойство `name` то `aria-label` можно опустить; + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + ); +} +``` + +#### Avatar c текстом + +В этом случае достаточно указать свойство `name`. + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + ); +} +``` + +#### Avatar и статус + +Если указано свойство `status` его значение будет так же озвучено в комбинации со свойством `name` или `aria-label`. + +```tsx live +import React from 'react'; +import { Avatar } from '@salutejs/plasma-giga'; + +export function App() { + return ( + <> + + + ); +} +``` + +Озвучит как `ИФ. Неактивен`. (В данном примере озвучиваются инициалы, производное от ФИО) + +#### Свойство statusLabels + +Опциональное свойство для корректной озвучки значений свойства `status`. + +По-умолчанию стоит значение для русскоговорящих `{ active: 'Активен', inactive: 'Неактивен' }`. diff --git a/website/plasma-giga-docs/docs/components/AvatarGroup.mdx b/website/plasma-giga-docs/docs/components/AvatarGroup.mdx new file mode 100644 index 0000000000..e8160e64e1 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/AvatarGroup.mdx @@ -0,0 +1,29 @@ +--- +id: avatarGroup +title: AvatarGroup +--- + +import { PropsTable, Description } from '@site/src/components'; + +# AvatarGroup + + + +AvatarGroup это обертка для Avatar. Принимает в себя группу из avatar's. + +```tsx live +import React from 'react'; +import { Avatar, AvatarGroup } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + { + Array(5).fill(true).map(() => ( + + )) + } + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Badge.mdx b/website/plasma-giga-docs/docs/components/Badge.mdx new file mode 100644 index 0000000000..00c7606081 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Badge.mdx @@ -0,0 +1,148 @@ +--- +id: badge +title: Badge +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Badge + + + +## Примеры + +### Размер badge +Размер задается с помощью свойства `size`. + +Возможные значения свойства: `l`, `m`, `s`, `xs`. + +```tsx live +import React from 'react'; +import { Badge } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + + +
+ ); +} +``` + +### Скругленность badge +Скругленность задается с помощью свойства `pilled`: + +```tsx live +import React from 'react'; +import { Badge } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + + +
+ ); +} +``` + +### Вид badge +Вид `badge` задается с помощью свойства `view`. +Так же на вид badge влияет свойства `transparent` и `clear`. + +```tsx live +import React from 'react'; +import { Badge } from '@salutejs/plasma-giga'; + +export function App() { + const Badges = ({transparent, clear}) => { + return ( +
+ + + + + + + +
+ ) + } + + return ( +
+ + + +
+ ); +} +``` + +### Иконка слева / справа +В левой или правой части badge можно отобразить иконку. +Если же нужен Badge с иконкой без текста, нужно использовать `contentLeft`. + +```tsx live +import React from 'react'; +import { Badge } from '@salutejs/plasma-giga'; +import { IconEye } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ + } + /> + + } + /> + + } + /> +
+ ); +} +``` + +### Задание цвета текста и фона +Цвет текста и фона можно задать с помощью свойств +`customColor` и `customBackgroundColor` соответственно. + +```tsx live +import React from 'react'; +import { Badge } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ +
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Breadcrumbs.mdx b/website/plasma-giga-docs/docs/components/Breadcrumbs.mdx new file mode 100644 index 0000000000..5e790ccc69 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Breadcrumbs.mdx @@ -0,0 +1,148 @@ +--- +id: breadcrumbs +title: Breadcrumbs +--- + +import { PropsTable } from '@site/src/components'; + +# Breadcrumbs + + +# Типизация элементов + +Параметр `items` является обязательным и поддерживает следующие типы: + +```tsx +type Items = + | { + /** + * Обработчик клика на элемент + */ + onClick?: () => void; + /** + * Элемент заголовка + */ + title: string; + /** + * Элемент выключен + */ + disabled?: boolean; + } + | { + /** + * Ссылка на страницу ( если не указана, то ссылка не кликабельна ) + */ + href?: string; + /** + * Элемент заголовка + */ + title: string; + /** + * Элемент выключен + */ + disabled?: boolean; + } + | { + /** + * Функция рендера элемента + */ + renderItem: () => ReactNode; + }; +``` + +При использовании одного из трёх вариантов, свойства остальных будут не доступны. + +# Пример + +```tsx live +import React from 'react'; +import { Breadcrumbs } from '@salutejs/plasma-giga'; + +export function App() { + const items = [ + { title: 'Home', href: '/' }, + { title: 'Without link' }, + { title: 'Disabled', disabled: true }, + { title: 'Disabled with link', href: '/', disabled: true }, + { + title: 'On Click', + onClick: () => { + alert('On Click'); + }, + }, + { renderItem: () => Custom }, + { title: 'Main' }, + ]; + + return ( +
+ +
+ ); +} +``` +# Использование с shorter +```tsx live +import React from 'react'; +import { Breadcrumbs } from '@salutejs/plasma-giga'; + +export function App() { + const items = [ + { title: 'Home', href: '/' }, + { title: 'About as', href: '/' }, + { renderItem: () => Custom Item }, + { title: 'Contacts' }, + ]; + + return ( +
+
+ +
+
+ +
+
+ ); +} +``` + +# Использование с кастомным элементом +```tsx live +import React from 'react'; +import { Breadcrumbs, Dropdown } from '@salutejs/plasma-giga'; + +export function App() { + const items = [ + { title: 'Home', href: '/' }, + { title: 'About as', href: '/' }, + { + renderItem: () => { + const itemsDropdown = [ + { + value: 'Custom Item 1', + label: 'Custom Item 1', + }, + { + value: 'Custom Item 2', + label: 'Custom Item 2', + }, + ]; + return ( + + ... + + ); + }, + }, + { title: 'Contacts' }, + ]; + + + return ( +
+ +
+ ); +} +``` \ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Button.mdx b/website/plasma-giga-docs/docs/components/Button.mdx new file mode 100644 index 0000000000..8fde928fb4 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Button.mdx @@ -0,0 +1,198 @@ +--- +id: button +title: Button +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Button +Кнопки могут отображаться в нескольких размерах и цветах, могут содержать текст и/или иконку. + +## Button + + + +:::caution Взаимоисключающие свойства +Свойство `value` - это значение кнопки. Оно отображается справа от основного текста.
+`value` и `contentRight` взаимоисключающие: если передано одно, второе передать нельзя. +::: + +## Использование +Компонент `Button` может содержать текст, который указывается в +свойстве `text`, или любой контент напрямую через `children`. + +Свойство text можно использовать вместе со свойствами `contentLeft` и `contentRight`. +С их помощью можно размещать **иконку** слева или справа от текста. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; +import { IconDownload } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+
} /> + + + + ); +} +``` + +## Примеры + +### Размер кнопки +Размер кнопки задается с помощью свойства `size`. Возможные значения свойства: `"l"`, `"m"` или `"s"`: + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+
+ ); +} +``` + +### Ширина кнопки +Ширина кнопки регулируется с помощью свойства `stretching`. +Возможные значения свойства `contentPlacing`: ++ `auto` – ширина подстраивается под контент; ++ `filled` – кнопка растягивается на всю доступную ширину; ++ `fixed` – кнопка фиксированной ширины. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+
+ ); +} +``` + +### Позиционирование контента внутри +Позиционирование контента внутри кнопки регулируется с помощью свойства `contentPlacing`. +Возможные значения свойства `contentPlacing`: ++ `default` – контент центрируется; ++ `relaxed` – контент располагается по краям. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+
+ ); +} +``` + +### Вид кнопки +Вид кнопки задается с помощью свойства `view`: + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+
+ ); +} +``` + +### Границы кнопки +Границы кнопки задаются с помощью свойства `pin`. Возможные значения свойства `pin`: ++ `square` – обычное скругление; ++ `circle` – сильное скругление; ++ `clear` – нет скругления. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + + + + + +
+ ); +} +``` + +### Квадратные и круглые кнопки +Для отображения иконок и/или текста в квадратных или круглых кнопках с **равными сторонами**, +используйте компонент `Button` и свойство `contentLeft`, в которое требуется передать нужное значение. + +По умолчанию границы кнопки **квадратные** (со скругленными углами) — `pin="square-square"`. +**Круглые** границы кнопки можно сделать с помощью свойства `pin` со значением `"circle-circle"`. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; +import { IconDownload } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+
+ ); +} +``` + +### Гиперссылка +Компонент поддерживает вывод в виде тега ``, для этого необходимо указать свойство `as`: + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + )} + onClose={() => setIsOpenA(false)} + > +

Header

+ + + Content + + +

Footer

+
+ +
+ <>Frame +
+ + setIsOpenB(false)} + placement="right" + asModal={false} + frame={ref} + width="250px" + height="100%" + > + + + + )} + onClose={() => setIsOpenB(false)} + > +

Header

+
+ + Content + + +

Footer

+
+
+ + + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Dropdown.mdx b/website/plasma-giga-docs/docs/components/Dropdown.mdx new file mode 100644 index 0000000000..ce3a1ec5e4 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Dropdown.mdx @@ -0,0 +1,348 @@ +--- +id: dropdown +title: Dropdown +--- + +import { PropsTable, Description } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Dropdown +Выпадающий многоуровневый список. + + + + +## Использование +Обязательным параметром является только массив `items`. +Внутри items может быть такой же вложенный массив items. + +Формат следующий: + +```tsx +type Items = Array<{ + /** + * Значение у item + */ + value: string | number; + /** + * Метка-подпись к item + */ + label: string; + /** + * Список дочерних items. + */ + items?: Items; + /** + * Item не активен + */ + disabled?: boolean; + /** + * Слот для контента слева + */ + contentLeft?: ReactNode; + /** + * Слот для контента справа + */ + contentRight?: ReactNode; + /** + * Отобразить ли разделитель до элемента + */ + dividerBefore?: boolean; + /** + * Отобразить ли разделитель после элемента + */ + dividerAfter?: boolean; + /** + * Выбранный item. + * @deprecated использовать ContentLeft || ContentRight + */ + isActive?: boolean; + /** + * Кастомный цвет текста + * @deprecated + */ + color?: string; + /** + * Айтем не активен + * @deprecated использовать disabled + */ + isDisabled?: boolean; + }>; +``` + +## Примеры + + + + ```tsx live + import React from 'react'; + import { Button, Dropdown } from '@salutejs/plasma-giga'; + + export function App() { + const items = [ + { + value: 'north_america', + label: 'Северная Америка', + }, + { + value: 'south_america', + label: 'Южная Америка', + items: [ + { + value: 'brazil', + label: 'Бразилия', + items: [ + { + value: 'rio_de_janeiro', + label: 'Рио-де-Жанейро', + }, + { + value: 'sao_paulo', + label: 'Сан-Паулу', + }, + ], + }, + { + value: 'argentina', + label: 'Аргентина', + }, + ], + }, + ]; + + + return ( +
+ +
+ ); + } + ``` +
+ + + Размер `Dropdown` задается с помощью свойства `size`.
+ Возможные значения свойства: `"l"`, `"m"`, `"s"` или `"xs"`. + + ```tsx live + import React from 'react'; + import { Button, Dropdown } from '@salutejs/plasma-giga'; + + export function App() { + const items = [ +{ + value: 'north_america', + label: 'Северная Америка', +}, +{ + value: 'south_america', + label: 'Южная Америка', + items: [ +{ + value: 'brazil', + label: 'Бразилия', + items: [ +{ + value: 'rio_de_janeiro', + label: 'Рио-де-Жанейро', +}, +{ + value: 'sao_paulo', + label: 'Сан-Паулу', +}, + ], +}, +{ + value: 'argentina', + label: 'Аргентина', +}, + ], +}, + ]; + + return ( +
+ +
+); +} +``` +
+ + + Параметр `placement` принимает либо строку: `"top"`, `"right"`, `"bottom"`, `"left"`, `"auto"`, либо массив этих же значений, например: `["top", "bottom"]`.
+ Default value для placement - `"bottom"`. + + ```tsx live + import React from 'react'; + import { Button, Dropdown } from '@salutejs/plasma-giga'; + + export function App() { + const items = [ +{ + value: 'north_america', + label: 'Северная Америка', +}, +{ + value: 'south_america', + label: 'Южная Америка', + items: [ +{ + value: 'brazil', + label: 'Бразилия', + items: [ +{ + value: 'rio_de_janeiro', + label: 'Рио-де-Жанейро', +}, +{ + value: 'sao_paulo', + label: 'Сан-Паулу', +}, + ], +}, +{ + value: 'argentina', + label: 'Аргентина', +}, + ], +}, + ]; + + return ( +
+ +
+); +} +``` +
+ + + Параметр `trigger` принимает строку: `"click"` или `"hover"`.
+ Default value - `"click"`.
+ Для примера показано значение `"hover"`. + + ```tsx live + import React from 'react'; + import { Button, Dropdown } from '@salutejs/plasma-giga'; + + export function App() { + const items = [ +{ + value: 'north_america', + label: 'Северная Америка', +}, +{ + value: 'south_america', + label: 'Южная Америка', + items: [ +{ + value: 'brazil', + label: 'Бразилия', + items: [ +{ + value: 'rio_de_janeiro', + label: 'Рио-де-Жанейро', +}, +{ + value: 'sao_paulo', + label: 'Сан-Паулу', +}, + ], +}, +{ + value: 'argentina', + label: 'Аргентина', +}, + ], +}, + ]; + + + return ( +
+ +
+); +} +``` +
+
+ +## Клавиатурная навигация + +Данный компонент соответствует требования W3C: [Combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) и частично [TreeView](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/). + +- `Tab` - закрывает dropdown. Перемещает фокус на следующий элемент на странице; +- `Enter` - открывает/закрывает dropdown. Если на элементе - выбирает его; +- `Space` - открывает/закрывает dropdown. Если на элементе - выбирает его; +- `Home` - открывает dropdown и перемещает фокус на первый элемент; +- `End` - открывает dropdown и перемещает фокус на последний элемент; +- `PageUp` - перемещает фокус на 10 элементов выше либо в начало списка; +- `PageDown` - перемещает фокус на 10 элементов ниже либо в конце списка; +- `ArrowUp` - открывает dropdown и перемещает фокус на первый элемент. Перемещает фокус на один элемент выше; +- `ArrowDown` - открывает dropdown и перемещает фокус на первый элемент. Перемещает фокус на один элемент ниже; +- `ArrowRight` - если фокус на элементе вложенного списка - открывает его и перемещает фокус на первый элемент; +- `ArrowLeft` - закрывает текущий список и перемещает фокус на предыдущий; diff --git a/website/plasma-giga-docs/docs/components/Grid.mdx b/website/plasma-giga-docs/docs/components/Grid.mdx new file mode 100644 index 0000000000..102d316689 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Grid.mdx @@ -0,0 +1,142 @@ +--- +id: grid +title: Grid +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Grid +Набор компонентов для создания сетки. + +## Breakpoints +Каждый breakpoints разрешения содержит собственное максимальное количество колонок: + +| Обозначение | Ширина от, px | Ширина до, px | Кол-во колонок | +|-------------|---------------|---------------|----------------| +| largeM | 1200 | - | 30 | +| largeS | 960 | 1199 | 24 | +| mediumM | 786 | 959 | 18 | +| mediumS | 560 | 785 | 12 | +| smallS | 0 | 559 | 6 | + +## Grid + + + +## Row +Блок с отрицательными отступами для размещения колонок (`Col`) по горизонтали. +Блок нельзя вкладывать сам в себя, но можно чередовать далее по дереву с использованием `Col`. +Стилизованный компонент, обладающий всеми свойствами `div`. + +## Col + + + +## Примеры + +### Базовое применение +Размеры колонок указываются свойством `size`, отступ — свойством `offset`. + +```tsx live +import React from 'react'; +import { Grid, Row, Col } from '@salutejs/plasma-giga'; + +export function App() { + const Filler = ({children}) => { + return ( +
+ {children} +
+ ) + }; + + return ( + + + + 3 + + + 2 + + + 3 + + + 4 + + + 6 + + + + + 4 offset 1 + + + 6 offset 2 + + + + ); +} +``` + +### Адаптивные размеры и отступы колонок +Свойства `size` и `offset` могут быть адаптивными. +Для этого добавьте соответствующие свойства с нужными [breakpoints](#Breakpoints). +При этом, `size` и `offset` могут выступать как fallback-значения для остальных разрешений. + +```tsx live +import React from 'react'; +import { Grid, Row, Col } from '@salutejs/plasma-giga'; + +export function App() { + const Filler = ({children}) => { + return ( +
+ {children} +
+ ) + }; + + return ( + + + + 1 + + + 2 + + + + + 4 offset 1 + + + 6 offset 2 + + + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/IconButton.mdx b/website/plasma-giga-docs/docs/components/IconButton.mdx new file mode 100644 index 0000000000..a581c89dc1 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/IconButton.mdx @@ -0,0 +1,156 @@ +--- +id: IconButton +title: IconButton +--- + +import { PropsTable, Description } from '@site/src/components'; + +# IconButton +Кнопки могут отображаться в нескольких размерах и цветах, могут содержать иконку. + +## IconButton + + + +## Использование +Компонент `IconButton` может содержать иконку, которая указывается напрямую через `children`. + +```tsx live +import React from 'react'; +import { IconButton } from '@salutejs/plasma-giga'; +import { IconClose } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ + + +
+ ); +} +``` + +## Примеры + +### Размер кнопки +Размер кнопки задается с помощью свойства `size`. + +```tsx live +import React from 'react'; +import { IconButton } from '@salutejs/plasma-giga'; +import { IconClose } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ + + + + + + + + + + + +
+ ); +} +``` + +### Вид кнопки +Вид кнопки задается с помощью свойства `view`. + +Возможные значения свойства `view`: ++ `"default"` – по умолчанию; ++ `"secondary"` – вторичная; ++ `"success"` – успешное завершение; ++ `"warning"` – предупреждение; ++ `"critical"` – ошибка; ++ `"clear"` – без цветового сопровождения; ++ `"dark"` – темная; ++ `"black"` – черная; ++ `"white"` – белая. + +```tsx live +import React from 'react'; +import { IconButton } from '@salutejs/plasma-giga'; +import { IconClose } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +} +``` + +### Границы кнопки +Границы кнопки задаются с помощью свойства `pin`. Возможные значения свойства `pin`: ++ `square` – обычное скругление; ++ `circle` – сильное скругление; ++ `clear` – нет скругления. + +```tsx live +import React from 'react'; +import { IconButton } from '@salutejs/plasma-giga'; +import { IconClose } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ + + + + + + + + + + + + + + + + + + + + +
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Image.mdx b/website/plasma-giga-docs/docs/components/Image.mdx new file mode 100644 index 0000000000..d78f78cd8b --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Image.mdx @@ -0,0 +1,26 @@ +--- +id: image +title: Image +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Image + + + +```tsx live +import React from 'react'; +import { Image } from '@salutejs/plasma-giga'; + +export function App() { + return ( + Картинка для примера фоном + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Indicator.mdx b/website/plasma-giga-docs/docs/components/Indicator.mdx new file mode 100644 index 0000000000..35fc2c7355 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Indicator.mdx @@ -0,0 +1,76 @@ +--- +id: indicator +title: Indicator +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Indicator +Индикаторы могут отображаться в нескольких размерах и цветах. + +## Indicator + + + +## Примеры + +### Базовое использование + +```tsx live +import React from 'react'; +import { Indicator } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Link.mdx b/website/plasma-giga-docs/docs/components/Link.mdx new file mode 100644 index 0000000000..dd4f3aea7f --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Link.mdx @@ -0,0 +1,23 @@ +--- +id: link +title: Link +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Link + + + +```tsx live +import React from 'react'; +import { TextS, Link } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + Скачайте приложение. + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Mask.mdx b/website/plasma-giga-docs/docs/components/Mask.mdx new file mode 100644 index 0000000000..d542a76038 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Mask.mdx @@ -0,0 +1,125 @@ +--- +id: mask +title: Mask +--- + +import { PropsTable, Description } from '@site/src/components'; + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Mask + + + +## Использование +Компонент `Mask` представляет собой поле ввода с возможностью маскирования значения. +Он реализован на компоненте [TextField](https://plasma.sberdevices.ru/b2c/components/textfield/) +и наследует практически все его свойства (`size`, `view`, `disabled`, `label` и тд.) + +```tsx live +import React from 'react'; +import { Mask } from '@salutejs/plasma-giga'; +import { IconDone } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ +
+ ); +} +``` + +### Маска +Маска задается с помощью свойства `mask`. +Поддерживает следующие символы: + +| Обозначение | Описание | +|-------------|---------------------------------------| +| 0 | любая цифра от 0 до 9 | +| a (англ.) | A-Z, a-z | +| я | А-Я, а-я, ё, Ё | +| q | буква "q" | +| 2 | цифра "2" | +| * | любой символ | +| \ | символ экранирования "\a" - буква "a" | + + + + + Пример маски для ввода телефона + + ```tsx live + import React from 'react'; + import { Mask } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ +
+ ); + } + ``` +
+ + Пример маски для ввода даты + + ```tsx live + import React from 'react'; + import { Mask } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ +
+ ); + } + ``` +
+ + Пример маски для ввода даты + + ```tsx live + import React from 'react'; + import { Mask } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ +
+ ); + } + ``` +
+
\ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Modal.mdx b/website/plasma-giga-docs/docs/components/Modal.mdx new file mode 100644 index 0000000000..e77929755e --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Modal.mdx @@ -0,0 +1,155 @@ +--- +id: modal +title: Modal +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Modal + + + +## Использование + +Перед использованием убедитесь, что `PopupProvider` подключен. +Компонент Modal во многом схож с Popup, добавляет при этом подложку(`overlay`), а также блокирует scroll и focus. + +```tsx live +import React, { useState } from 'react'; +import { surfaceSolidSecondary } from '@salutejs/plasma-themes/tokens'; +import { SSRProvider, Button, Modal, PopupProvider } from '@salutejs/plasma-giga'; + +export function App() { + const [isOpenA, setIsOpenA] = useState(false); + const [isOpenB, setIsOpenB] = useState(false); + + return ( + + +
+
+
+ setIsOpenA(false)} + opened={isOpenA} + placement="center" + offset={[0, 0]} + > +
+ +
+ setIsOpenB(false)} + opened={isOpenB} + placement="right" + offset={[0, 0]} + > +
+ + Content +
+
+
+
+
+
+ ); +} +``` + +## Использование стилизованной обертки + +Для использования стилизованного модального окна с отступами и крестиком для закрытия, добавьте свойство `hasBody`. +```tsx live +import React, { useState } from 'react'; +import { SSRProvider, Button, Modal, PopupProvider } from '@salutejs/plasma-giga'; + +export function App() { + const [isOpenA, setIsOpenA] = useState(false); + const [isOpenB, setIsOpenB] = useState(false); + + return ( + + +
+
+
+ setIsOpenA(false)} + opened={isOpenA} + placement="center" + offset={[0, 0]} + hasBody + > + + + Content + + +
+
+
+ ); +} +``` + +## Подключение анимации +Подключение анимации аналогично тому, как это происходит в `Popup` - через свойство `withAnimation`(управление через `popupClasses`, `modalClasses`). +Для добавления анимации в оверлей необходимо использовать класс `.modal-overlay` через переменную `modalClasses.overlay` из пакета. + +Пример: + +```tsx +import styled from 'styled-components'; +import { Modal } from '@salutejs/plasma-giga'; + +const StyledModal = styled(Modal)` + && > .${popupClasses.root}, .${modalClasses.overlay} { + animation: fadeIn 1s forwards; + } + + &&.${popupClasses.endAnimation} .${popupClasses.root} { + animation: fadeOut 1s forwards; + } + + &&.${popupClasses.endAnimation} .${modalClasses.overlay} { + animation: fadeOut 1s forwards; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + @keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + } + } +`; +``` diff --git a/website/plasma-giga-docs/docs/components/Notification.mdx b/website/plasma-giga-docs/docs/components/Notification.mdx new file mode 100644 index 0000000000..3ebd726d56 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Notification.mdx @@ -0,0 +1,68 @@ +--- +id: notification +title: Notification +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Notification + + + +## Использование +Компонент `Notification` может использоваться для создания собственных систем оповещения. +Вид компонента контролируется свойствами (props). +Текстовая часть оповещения состоит из `title` и `children`. +Слева или сверху от нее, также можно пробросить иконку через свойство `icon`. +Также есть часть `actions`, в которой предполагается отображение кнопок для взаимодействия. + +### Провайдер контекста +Поместите `NotificationsProvider` в [корень приложения](../../#корень-приложения) или там, где будете применять модальные окна. +В качестве свойств можно указать контейнер для оповещений через `frame` и расположение в контейнере через свойство `placement`, как `bottom-right` _(по умолчанию)_ или `bottom-left`. + +```tsx title="index.ts" +import ReactDOM from 'react-dom'; +import { NotificationsProvider } from '@salutejs/plasma-giga'; + +import { App } from './App'; + +ReactDOM.render( + + + , + document.getElementById('root') +); +``` + +### Вызов уведомления +После подключения `NotificationsProvider` станет возможен вызов функции `addNotification`, который приведет к отображению оповещения. +Функция принимает значения свойств компонента `Notification`, включая необязательное поле `id`. И возвращает сгенерированный или переданный `id`, по которому можно закрыть оповещение через вызов `closeNotification`. +Оповещение закроется автоматически по истечению указанного `timeout` в миллисекундах или будет висеть вечно, если передан `0` или `null`. + +```tsx live +import React from 'react'; +import { Button, ButtonGroup, addNotification, closeNotification, NotificationsProvider } from '@salutejs/plasma-giga'; + +export function App() { + const handleShow = React.useCallback(() => { + addNotification({ + id: 'incoming-call', + title: 'Входящий вызов', + children: 'Принять?', + }, 1000); + }, []); + + const handleHide = React.useCallback(() => { + closeNotification('incoming-call'); + }, []); + + return ( + + + } + offset={[0, 6]} + hasArrow + placement='bottom' + trigger='click' + closeOnOverlayClick + closeOnEsc + isFocusTrapped + > + + <>Content + + + + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Popup.mdx b/website/plasma-giga-docs/docs/components/Popup.mdx new file mode 100644 index 0000000000..3abfd2d914 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Popup.mdx @@ -0,0 +1,205 @@ +--- +id: popup +title: Popup +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Popup + + + +### Провайдер контекста + +Поместите `PopupProvider` в [корень приложения](../../#корень-приложения) или там, где будете применять Popup: + +```tsx title="index.ts" +import ReactDOM from 'react-dom'; +import { PopupProvider } from '@salutejs/plasma-giga'; + +import { App } from './App'; + +ReactDOM.render( + + + , + document.getElementById('root') +); +``` + +### Использование + +Popup можно использовать как и на всем окне, так и в отдельном фрейме - свойство `frame`. + +Также это свойство поддерживает передачу id элемента, в котором будет использоваться компонент. + +Само позиционирование можно указать с помощью свойства `placement`(center - по умолчанию; left, right, top, bottom и их комбинации), +а также определить отступы от точки с помощью `offset`. + +```tsx live +import React, { useRef } from 'react'; +import styled from 'styled-components'; +import { surfaceSolidTertiary, surfaceSolidSecondary } from '@salutejs/plasma-themes/tokens'; + +import { SSRProvider, Button, Popup, PopupProvider } from '@salutejs/plasma-giga'; + +export function App() { + const StyledButton = styled(Button)` + margin-top: 1rem; + width: 15rem; + `; + + const StyledWrapper = styled.div` + height: 500px; + `; + + const OtherContent = styled.div` + margin-top: 1rem; + width: 400px; + height: 500px; + background: ${surfaceSolidTertiary}; + position: absolute; + + display: flex; + align-items: flex-start; + justify-content: center; + padding: 1rem; + + bottom: 0; + right: 0; + `; + + const Content = styled.div` + background: ${surfaceSolidSecondary}; + padding: 1rem; + `; + + const [isOpenA, setIsOpenA] = React.useState(false); + const [isOpenB, setIsOpenB] = React.useState(false); + + const ref = useRef(); + + return ( + + + +
+ setIsOpenB(true)} /> + setIsOpenA(true)} /> +
+ + + + <>Content + + + + <>Frame + + + + + <>Content + + +
+
+
+ ); +} +``` + +## Подключение анимации + +Для подключения анимации нужно добавить параметр `withAnimation`. +Само управление происходит с помощью классов через переменные `endAnimation`, `endTransition` из объекта `PopupClasses` для `Popup`. +Для добавления анимации необходимо использовать класс `.popup-root` через переменную `PopupClasses.root` из пакета. + +Пример: + +```tsx +const StyledPopupTransition = styled(Popup)` + && > .${PopupClasses.root} { + opacity: 1; + transition: opacity 0.5s 0.1s; + } + + &&.${PopupClasses.endTransition} > .${PopupClasses.root} { + opacity: 0; + transition: opacity 0.5s 0.1s; + } +`; +``` + +или + +```tsx +const StyledPopupAnimation = styled(Popup)` + && > .${PopupClasses.root} { + animation: fadeIn 1s forwards; + } + + &&.${PopupClasses.endAnimation} > .${PopupClasses.root} { + animation: fadeOut 1s forwards; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + @keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + } + } +`; +``` + +И в самом компоненте: + +```tsx + const [isOpen, setIsOpen] = React.useState(false); + + return ( + <> +
+ setIsOpen(true)} /> +
+ +
+ +
+
+ + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Portal.mdx b/website/plasma-giga-docs/docs/components/Portal.mdx new file mode 100644 index 0000000000..e39226d646 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Portal.mdx @@ -0,0 +1,66 @@ +--- +id: portal +title: Portal +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Portal + + + +## Использование + +```tsx live +import React, { useState, useRef } from 'react'; +import { Portal, Button, BodyM } from '@salutejs/plasma-giga'; + +export function App() { + const [show, setShow] = useState(false); + const containerRef = useRef(null); + + return ( + <> + +
+ Содержимое портала появится в нижнем блоке. + {show && containerRef.current && ( + + Содержимое портала + + )} +
+
+ + ); +} +``` + +### Disabled +Отключить портал можно с помощью свойства `disabled`. +В этом случае содержимое портала добавится внутрь родительского элемента. + +```tsx live +import React, { useState, useRef } from 'react'; +import { Portal, Button, BodyM } from '@salutejs/plasma-giga'; + +export function App() { + const [show, setShow] = useState(false); + const containerRef = useRef(null); + + return ( + <> + +
+ Содержимое портала появится в данном блоке. + {show && containerRef.current && ( + + Содержимое портала + + )} +
+
+ + ); +} +``` \ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Price.mdx b/website/plasma-giga-docs/docs/components/Price.mdx new file mode 100644 index 0000000000..94f390d2df --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Price.mdx @@ -0,0 +1,24 @@ +--- +id: price +title: Price +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Price + + + +```tsx live +import React from 'react'; +import { Price } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ 12345.67 + 12345.67 +
+ ); +} +``` \ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Progress.mdx b/website/plasma-giga-docs/docs/components/Progress.mdx new file mode 100644 index 0000000000..b56fdbb763 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Progress.mdx @@ -0,0 +1,33 @@ +--- +id: progress +title: Progress +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Progress + + + +##Примеры + +### Вид прогресса +Вид прогресса задается с помощью свойства `view`. Возможные значения свойства `view`: ++ `"default"` – по умолчанию; ++ `"primary"` – основная; ++ `"secondary"` – вторичная; ++ `"accent"` – акцентная; ++ `"success"` – успешное завершение; ++ `"warning"` – предупреждение; ++ `"error"` – ошибка; + +```tsx live +import React from 'react'; +import { Progress } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Radiobox.mdx b/website/plasma-giga-docs/docs/components/Radiobox.mdx new file mode 100644 index 0000000000..59687a73ba --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Radiobox.mdx @@ -0,0 +1,41 @@ +--- +id: radiobox +title: Radiobox +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Radiobox + + + +## Использование +Компонент `Radiobox` может содержать лейбл и описание. + +По умолчанию, контент внутри лейбла и описания многострочный. + +Для того чтобы стал однострочным, необходимо использовать свойство `singleLine`(по умолчанию `false`). + +```tsx live +import React from 'react'; +import { Radiobox } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` + +## RadioGroup +Компоненты `Radiobox` следует объединять в `RadioGroup` + +```tsx live +
+ +

Заголовок

+ + +
+
+``` diff --git a/website/plasma-giga-docs/docs/components/Range.mdx b/website/plasma-giga-docs/docs/components/Range.mdx new file mode 100644 index 0000000000..df3f7b182f --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Range.mdx @@ -0,0 +1,359 @@ +--- +id: range +title: Range +--- + +import { PropsTable, Description } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Range + + + +## Использование +Компонент `Range` представляет собой два поля ввода какого-либо диапазона. К примеру диапазон цен или дат. + +```tsx live +import React, {useState} from 'react'; +import { Range, Button } from '@salutejs/plasma-giga'; +import { IconSearch } from '@salutejs/plasma-icons'; + +export function App() { + const [firstValue, setFirstValue] = useState(''); + const [secondValue, setSecondValue] = useState(''); + + const ActionButton = () => { + return ( + + ); + }; + + return ( +
+ } + dividerVariant="dash" + onChangeFirstValue={(e) => { + setFirstValue(e.target.value); + }} + onChangeSecondValue={(e) => { + setSecondValue(e.target.value); + }} + /> +
+ ); +} +``` + +### Размер Range +Размер Range задаётся с помощью свойства `size`: + +```tsx live +import React from 'react'; +import { Range } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + + + + + +
+ ); +} +``` + +### Вид разделителя +Вид разделителя задается с помощью свойства `dividerVariant`: + + + + ```tsx live + import React from 'react'; + import { Range } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ +
+ ); + } + ``` +
+ + + При таком варианте свойства `firstTextfieldTextBefore` и `secondTextfieldTextBefore` становятся обязательными: + + ```tsx live + import React from 'react'; + import { Range } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ +
+ ); + } + ``` +
+ + + При таком варианте свойства `dividerIcon` становится обязательным: + + ```tsx live + import React from 'react'; + import { Range } from '@salutejs/plasma-giga'; + import { IconSber } from '@salutejs/plasma-icons'; + + export function App() { + return ( +
+ } + /> +
+ ); + } + ``` +
+
+ +### Валидация и индикация успешного ввода +За индикацию ошибки или успешного ввода отвечают `firstValueError`, `secondValueError`, `firstValueSuccess`, `secondValueSuccess`. +В данном примере валидация происходит при нажатии клавиши `Enter` после ввода значений: + +```tsx live +import React, {useState, ChangeEvent, Dispatch, SetStateAction} from 'react'; +import { Range, Button } from '@salutejs/plasma-giga'; +import { IconSearch } from '@salutejs/plasma-icons'; + +export function App() { + const [firstValue, setFirstValue] = useState(''); + const [secondValue, setSecondValue] = useState(''); + + const [firstValueError, setFirstValueError] = useState(false); + const [secondValueError, setSecondValueError] = useState(false); + + const [firstValueSuccess, setFirstValueSuccess] = useState(false); + const [secondValueSuccess, setSecondValueSuccess] = useState(false); + + const checkValue = (value: string) => /^([1-9][0-9]*|)$/.test(value); + + const handleChangeValue = ( + e: ChangeEvent, + setter: Dispatch>, + error: boolean, + errorSetter: Dispatch>, + success: boolean, + successSetter: Dispatch>, + ) => { + const { value } = e.target; + if (!checkValue(value)) { + return; + } + + if (error) { + errorSetter(false); + } + + if (success) { + successSetter(false); + } + + setter(value); + }; + + const firstValueConstraint = (value: number) => + value <= 0 || value >= 9 || (secondValue && value >= Number(secondValue) && !secondValueError); + const secondValueConstraint = (value: number) => + value <= 0 || value > 10 || (firstValue && value <= Number(firstValue) && !firstValueError); + + const handleCommitValue = ( + value: string, + constraint: (currentValue: number) => boolean, + errorSetter: Dispatch>, + successSetter: Dispatch>, + ) => { + if (!value) { + return; + } + + if (!checkValue(value)) { + return errorSetter(true); + } + + if (constraint(Number(value))) { + return errorSetter(true); + } + + successSetter(true); + }; + + const ActionButton = () => { + return ( + + ); + }; + + return ( +
+ } + dividerVariant="dash" + firstValueError={firstValueError} + secondValueError={secondValueError} + firstValueSuccess={firstValueSuccess} + secondValueSuccess={secondValueSuccess} + onChangeFirstValue={(e) => { + handleChangeValue( + e, + setFirstValue, + firstValueError, + setFirstValueError, + firstValueSuccess, + setFirstValueSuccess, + ); + }} + onChangeSecondValue={(e) => { + handleChangeValue( + e, + setSecondValue, + secondValueError, + setSecondValueError, + secondValueSuccess, + setSecondValueSuccess, + ); + }} + onSearchFirstValue={(e) => { + const { value } = e.target as HTMLInputElement; + handleCommitValue(value, firstValueConstraint, setFirstValueError, setFirstValueSuccess); + }} + onSearchSecondValue={(e) => { + const { value } = e.target as HTMLInputElement; + handleCommitValue(value, secondValueConstraint, setSecondValueError, setSecondValueSuccess); + }} + /> +
+ ); +} +``` + +### Обязательность поля +Обязательность поля задаётся с помощью свойства `required`. +Если поле является обязательным, то у компонента появляется специальный индикатор. +Изменить расположение индикатора можно с помощью свойства `requiredPlacement`, которое принимает значения `left` и `right`. + +```tsx live +import React from 'react'; +import { Range, IconButton } from '@salutejs/plasma-giga'; +import { IconDone } from '@salutejs/plasma-icons'; + +export function App() { + const ActionButton = () => { + return ( + + + + ); + }; + + return ( +
+ } + required + requiredPlacement="right" + /> +
+ ); +} +``` \ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Rating.mdx b/website/plasma-giga-docs/docs/components/Rating.mdx new file mode 100644 index 0000000000..aef320ce41 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Rating.mdx @@ -0,0 +1,289 @@ +--- +id: rating +title: Rating +--- + +import { PropsTable, Description } from '@site/src/components'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Rating + + + +## Использование +Компонент `Rating` представляет собой неинтерактивный компонент для отображения оценки. + +```tsx live +import React, {useState} from 'react'; +import { Rating, Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ +
+ ); +} +``` + +### Вид Rating +Вид Rating задаётся с помощью свойства `view`: + +```tsx live +import React from 'react'; +import { Rating } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + +
+ ); +} +``` + +### Размер Rating +Размер Rating задается с помощью свойства `size`: + + + + ```tsx live + import React from 'react'; + import { Rating } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + + + + +
+ ); + } + ``` +
+ + + ```tsx live + import React from 'react'; + import { Rating } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + + + + +
+ ); + } + ``` +
+ + + ```tsx live + import React from 'react'; + import { Rating } from '@salutejs/plasma-giga'; + + export function App() { + return ( +
+ + + +
+ ); + } + ``` +
+
+ +### Количество иконок в Rating +Количество иконок в Rating задается с помощью свойства `iconQuantity`: + +```tsx live +import React from 'react'; +import { Rating } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + +
+ ); +} +``` + +### Положение значения в Rating +Положение значения в Rating задается с помощью свойства `valuePlacement`. +Отображение значения можно выключить с помощью свойства `hasValue`: + +```tsx live +import React from 'react'; +import { Rating } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + +
+ ); +} +``` + +### Пользовтельские иконки в Rating +Пользовтельские иконки в Rating задаются с помощью свойств: ++ `iconSlot` – закрашенная иконка; ++ `iconSlotOutline` – outline иконка; ++ `iconSlotHalf` - иконка для отображения дробного значения оценки. + +Для корректного применения размеров к иконкам, необходимо передавать класс `ratingClasses.customIconSizing` ("custom-icon-sizing") + + +```tsx live +import React from 'react'; +import { IconKeyFill, IconKeyOutline, IconLockFill } from '@salutejs/plasma-icons'; +import { ratingClasses, Rating } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ } + iconSlotOutline={} + iconSlotHalf={} + /> +
+ ); +} +``` \ No newline at end of file diff --git a/website/plasma-giga-docs/docs/components/Segment.mdx b/website/plasma-giga-docs/docs/components/Segment.mdx new file mode 100644 index 0000000000..5a6356b00d --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Segment.mdx @@ -0,0 +1,111 @@ +--- +id: segment +title: Segment +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Segment +Набор компонентов для создания группы сегментов. +Структура для сегментов похожа на структуру маркированных списков. + +## SegmentGroup + + + + +## SegmentItem + + + + +### Провайдер контекста + +Оберните в `SegmentProvider` компонент, в котором будут использованы SegmentGroup с SegmentItem. +Внутри данного провайдера есть доступ к хуку `useSegment`. + +### Выбранные по умолчанию SegmentItem + +Можно задать выбранные по умолчанию сегменты с помощью свойства `defaultSelected`. +Оно передается в `SegmentProvider`, в виде массива `value` элементов. + +```tsx live +import React from 'react'; +import { SegmentGroup, SegmentItem, SegmentProvider, useSegment } from '@salutejs/{{ package }}'; + +export function App() { + const items = Array(8).fill(0); + + const SegmentTemplate = () => { + const {selectedSegmentItems} = useSegment(); + + return ( + <> +
Выбранный элемент: {selectedSegmentItems.join(', ')}
+
+ + {items.map((_, i) => ( + + ))} + + + ) + } + + return ( +
+ + + +
+ ); +} +``` + +### Вертикальное отображение + +Для смены ориентации передаем `orientation: 'vertical'` + +```tsx live +import React from 'react'; +import { SegmentGroup, SegmentItem, SegmentProvider, useSegment } from '@salutejs/{{ package }}'; + +export function App() { + const items = Array(8).fill(0); + + const SegmentTemplate = () => { + const {selectedSegmentItems} = useSegment(); + + return ( + <> +
Выбранный элемент: {selectedSegmentItems.join(', ')}
+
+ + {items.map((_, i) => ( + + ))} + + + ) + } + + return ( +
+ + + +
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Sheet.mdx b/website/plasma-giga-docs/docs/components/Sheet.mdx new file mode 100644 index 0000000000..49ac08cddc --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Sheet.mdx @@ -0,0 +1,143 @@ +--- +id: sheet +title: Sheet +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Sheet + + + +## Использование +Компонент `Sheet` может содержать любой контент напрямую через `children`. + +Также есть возможность добавить любой контент в заголовок и футер с помощью свойств `contentHeader` и `contentFooter`. + +```tsx live +import React, { useState } from 'react'; +import { Sheet } from '@salutejs/plasma-giga'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + const [opened, setOpened] = useState(false); + + return ( + <> + + + setOpened(false)} + contentHeader={ +
+

header

+
+ } + contentFooter={ +
+

footer

+
+ } + > +
body
+
+ + ); +} +``` + +## Примеры + +### Закрепление заголовка и футера + +С помощью свойств `isHeaderFixed` и `isFooterFixed` можно закрепить заголовок и футер. +В этом случае при появлении прокрутки контент будет скроллиться под них. + +```tsx live +import React, { useState } from 'react'; +import { Sheet } from '@salutejs/plasma-giga'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + const [opened, setOpened] = useState(false); + + return ( + <> + + setOpened(false)} + contentHeader={ +
+

header

+
+ } + contentFooter={ +
+

footer

+
+ } + isFooterFixed + isHeaderFixed + > +
body
+
+ + ); +} +``` + +### Подложка + +Наличие или отсутствие подложки задается с помощью свойства `withOverlay`. + +В случае, когда подложка отсутствует, у пользователя появляется возможность взаимодействовать с контентом вне шторки. + +```tsx live +import React, { useState } from 'react'; +import { Sheet } from '@salutejs/plasma-giga'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + const [opened, setOpened] = useState(false); + + return ( + <> + + setOpened(false)} + withOverlay={false} + > +
body
+
+ + ); +} +``` + +К подложке можно добавить эффект размытия при помощи свойства `withBlur`. + +```tsx live +import React, { useState } from 'react'; +import { Sheet } from '@salutejs/plasma-giga'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + const [opened, setOpened] = useState(false); + + return ( + <> + + setOpened(false)} + withBlur + > +
body
+
+ + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Skeleton.mdx b/website/plasma-giga-docs/docs/components/Skeleton.mdx new file mode 100644 index 0000000000..be9686d142 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Skeleton.mdx @@ -0,0 +1,103 @@ +--- +id: skeleton +title: Skeleton +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Skeleton +Набор компонентов для создания скелетов загрузки (placeholders). + +## LineSkeleton + + + +```tsx live +import React from 'react'; +import { LineSkeleton } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` + +## RectSkeleton + + + +```tsx live +import React from 'react'; +import { RectSkeleton } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` + +## TextSkeleton + + + +```tsx live +import React from 'react'; +import { TextSkeleton } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` +## На примере карточки + +```tsx live +import React from 'react'; +import { Button, withSkeleton } from '@salutejs/plasma-giga'; + +export function App() { + const ButtonSkeleton = withSkeleton(Button); + + return ( + + ); +} +``` + +## Доступность +При использовании hook `withSkeleton` необходимо воспользоваться атрибутами WAI ARIA. Пример с кнопкой: + +```tsx +import React, { useState } from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +const ButtonSkeleton = withSkeleton(Button); + +export default function App () { + const [skeleton, setSkeleton] = useState(false); + + return ( + setSkeleton((prevValue) => !prevValue)} + /> + ); +} +``` + +Таким образом, при `skeleton=true` и `aria-busy=true` screen-reader будет оповещать о соответствующем статусе компонента. diff --git a/website/plasma-giga-docs/docs/components/Slider.mdx b/website/plasma-giga-docs/docs/components/Slider.mdx new file mode 100644 index 0000000000..527db44039 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Slider.mdx @@ -0,0 +1,104 @@ +--- +id: slider +title: Slider +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Slider + + + + +## Использование + +```tsx live +import React from 'react'; +import { Slider } from '@salutejs/{{ package }}'; + +export function App() { + return ( +
+ {}} min={0} max={100} value={30} /> +
+ ); +} +``` + +Можно использовать диапазон значений. + +```tsx live +import React, { useState } from 'react'; +import { Slider } from '@salutejs/{{ package }}'; + +export function App() { + const [value, setValue] = useState([10, 80]); + const sortValues = (values) => { + return values + .map((val) => { + if (val < 0) { + return 0; + } + if (val > 100) { + return 100; + } + return val; + }) + .sort((a, b) => a - b); + }; + + const onChangeHandle = (values) => { + setValue(sortValues(values)); + }; + + const onChangeCommitedHandle = (values) => { + setValue(sortValues(values)); + }; + + const onBlurTextField = (values) => { + setValue(sortValues(values)); + }; + + const onKeyDownTextField = (values, event) => { + if (event.key === 'Enter') { + setValue(sortValues(values)); + } + }; + + return ( +
+ +
+ ); +} +``` + +# Вертикальное отображение + +```tsx live +import React from 'react'; +import { Slider } from '@salutejs/{{ package }}'; +import { IconMic } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+ {}} + min={0} + max={100} + value={30} + label="Громкость" + labelContent={} + orientation="vertical" + sliderAlign="center" + size="m" + /> +
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Spinner.mdx b/website/plasma-giga-docs/docs/components/Spinner.mdx new file mode 100644 index 0000000000..7169ae7347 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Spinner.mdx @@ -0,0 +1,21 @@ +--- +id: spinner +title: Spinner +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Spinner + + + +```tsx live +import React from 'react'; +import { Spinner } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Steps.mdx b/website/plasma-giga-docs/docs/components/Steps.mdx new file mode 100644 index 0000000000..9cbc65ff73 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Steps.mdx @@ -0,0 +1,84 @@ +--- +id: steps +title: Steps +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Steps + + + + +## Вариант использования с управлением текущим элементом из вне + +```tsx live +import React, { useState } from 'react'; +import { Steps, Button } from '@salutejs/plasma-giga'; + +export function App() { + const items = [{ + indicator: 1, + }, { + indicator: 2, + }, { + indicator: 3, + }]; + + const [current, setCurrent] = useState(0); + const onClick = () => setCurrent(() => current + 1); + + return ( +
+ +
+ +
+ ); +} +``` + +## Вариант более гибкого использования через onChange и status в items + +```tsx live +import React, { useState } from 'react'; +import { Steps, StepItemProps } from '@salutejs/plasma-giga'; + +export function App() { + const initialItems = [{ + indicator: 1, + title: 'Title', + content: 'Content', + }, { + indicator: 2, + title: 'Title', + content: 'Content', + }, { + indicator: 3, + title: 'Title', + content: 'Content', + }]; + + const [items, setItems] = useState(initialItems); + + const onChange = (item: StepItemProps, index: number, prevIndex: number) => { + if (prevIndex !== undefined) { + items[prevIndex].status = 'completed'; + } + + items[index].status = 'active'; + + setItems([...items]); + }; + + return ( +
+ +
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Switch.mdx b/website/plasma-giga-docs/docs/components/Switch.mdx new file mode 100644 index 0000000000..16dd194bca --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Switch.mdx @@ -0,0 +1,60 @@ +--- +id: switch +title: Switch +--- + +import { PropsTable, Description } from '@site/src/components'; + +# Switch + + + +## Использование +Компонент `Switch` может содержать лейбл. + +```tsx live +import React from 'react'; +import { Switch } from '@salutejs/plasma-giga'; + +export function App() { + return ( + + ); +} +``` + +## Примеры + +### Размер Switch +Размер компонента задается с помощью свойства `size`. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + + +
+ ); +} +``` + +Размер переключателя задается с помощью свойства `toggleSize`. + +```tsx live +import React from 'react'; +import { Button } from '@salutejs/plasma-giga'; + +export function App() { + return ( +
+ + +
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/Tabs.mdx b/website/plasma-giga-docs/docs/components/Tabs.mdx new file mode 100644 index 0000000000..dad973a755 --- /dev/null +++ b/website/plasma-giga-docs/docs/components/Tabs.mdx @@ -0,0 +1,245 @@ +--- +id: tabs +title: Tabs +--- + +import { PropsTable, Description } from '@site/src/components'; + +## Tabs + + + + +## TabItem + + + + +:::caution Взаимоисключающие свойства +Свойство `value` - это значение таба. Оно отображается справа от основного текста.
+`value` и `contentRight` взаимоисключающие: если передано одно, второе передать нельзя. +::: + +## TabsController (deprecated) +Вместо этого используйте Tabs, TabItem, указав параметры index, itemIndex, onIndexChange. + + + + +## Использование + +### Стандартное подключение, без клавиатурной навигации + +```tsx live +import React, { useState } from 'react'; +import { Tabs, TabItem } from '@salutejs/plasma-giga'; +import { IconClock } from '@salutejs/plasma-icons'; + +export function App() { + const items = Array(4).fill(0); + const [index, setIndex] = useState(0); + + return ( +
+ + {items.map((_, i) => ( + } + onClick={() => setIndex(i)} + > + {`Label${i + 1}`} + + ))} + +
+ ); +} +``` + +### Расположение табов +Табы могут быть горизонтальными (по умолчанию) и вертикальными. За это отвечает свойство `orientation`. + +```tsx live +import React, { useState } from 'react'; +import { Tabs, TabItem } from '@salutejs/plasma-giga'; +import { IconClock } from '@salutejs/plasma-icons'; + +export function App() { + const items = Array(8).fill(0); + const [index, setIndex] = useState(0); + + return ( +
+ + {items.map((_, i) => ( + } + onClick={() => setIndex(i)} + > + {`Label${i + 1}`} + + ))} + +
+ ); +} +``` + +### Пример с прокруткой + +```tsx live +import React, { useState } from 'react'; +import { Tabs, TabItem } from '@salutejs/plasma-giga'; +import { IconClock } from '@salutejs/plasma-icons'; + +export function App() { + const items = Array(8).fill(0); + const [index, setIndex] = useState(0); + + return ( +
+ + {items.map((_, i) => ( + } + onClick={() => setIndex(i)} + > + {`Label${i + 1}`} + + ))} + +
+ ); +} +``` + +### Пример с Dropdown + +```tsx live +import React, { useState } from 'react'; +import { Tabs, TabItem, Dropdown } from '@salutejs/plasma-giga'; + +export function App() { + const items = Array(8).fill(0); + const [index, setIndex] = useState(0); + + const maxItemQuantity = 3; + const visibleItems = items.slice(0, maxItemQuantity); + const otherItems = items.slice(maxItemQuantity); + + const dropdownItems = otherItems.map((_, i) => { + const itemIndex = maxItemQuantity + i; + + return { + label: `Label${itemIndex + 1}`, + value: itemIndex, + }; + }); + + return ( +
+ + {visibleItems.map((_, i) => ( + setIndex(i)} + tabIndex={0} + size="xs" + > + {`Label${i + 1}`} + + ))} + {dropdownItems.length > 0 && ( +
+ setIndex(item.value)}> + + ShowAll + + +
+ )} +
+
+ ); +} +``` + +### Подключение клавиатурной навигации +Для этого необходимо дополнительно прокинуть свойства `index, itemIndex, onIndexChange`.
+Для горизонтальных табов: клавиши ArrowLeft, ArrowRight, Home, End для навигации по вкладкам.
+Для вертикальных табов: клавиши ArrowUp, ArrowDown, Home, End для навигации по вкладкам. + +```tsx live +import React, { useState } from 'react'; +import { Tabs, TabItem } from '@salutejs/plasma-giga'; +import { IconClock } from '@salutejs/plasma-icons'; + +export function App() { + const items = Array(4).fill(0); + const [horizontalIndex, setHorizontalIndex] = useState(0); + const [verticalIndex, setVerticalIndex] = useState(0); + + return ( +
+
+ + {items.map((_, i) => ( + setHorizontalIndex(i)} + selected={i === horizontalIndex} + tabIndex={0} + contentLeft={} + onClick={() => setHorizontalIndex(i)} + > + {`Label${i + 1}`} + + ))} + +
+ +
+ + {items.map((_, i) => ( + setVerticalIndex(i)} + selected={i === verticalIndex} + tabIndex={0} + contentLeft={} + onClick={() => setVerticalIndex(i)} + > + {`Label${i + 1}`} + + ))} + +
+
+ ); +} +``` diff --git a/website/plasma-giga-docs/docs/components/TextArea.mdx b/website/plasma-giga-docs/docs/components/TextArea.mdx new file mode 100644 index 0000000000..ebf33da23b --- /dev/null +++ b/website/plasma-giga-docs/docs/components/TextArea.mdx @@ -0,0 +1,186 @@ +--- +id: textarea +title: TextArea +--- + +import { PropsTable, Description } from '@site/src/components'; + +# TextArea + + + +## Использование +Компонент `TextArea` может содержать иконку (или кнопку) справа. +Для этого используйте свойство `contentRight`: + +```tsx live +import React from 'react'; +import { TextArea } from '@salutejs/plasma-giga'; +import { IconDownload } from '@salutejs/plasma-icons'; + +export function App() { + return ( +
+