Ui Kit построен на базе Ant Design
Изначально есть компонент <Provider />
Он оборачивает все остальные компоненты и нужен для смены темы
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import { Provider } from '@relabs_test/relabs-test-uikit-radix';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider>
<App />
</Provider>
</React.StrictMode>,
);
import { useToggleTheme } from 'src/lib/theme/utils/ThemeContext';
const App = () => {
const toggleTheme = useToggleTheme();
return (
<>
<button onClick={toggleTheme}> Change Theme</button>
{/* остальной код */}
</>
);
};
// и все остальные компоненты
-
Клонируете репозиторий
-
Устанавливаете зависимости
-
Разрабатываете 😁
-
Есть Storybook
Основная точка входа для пакета - src/components/index.ts
.
Из этого файла импортируется то, что будет в итоговом npm пакете, точнее что можно будет использовать.
Если вы написали компонент и не экспортировали его, он будет недоступен
. Тоже самое
касается хуков и функций.
// `src/components/index.ts`
export * from './provider/Provider';
export * from './slider/Slider';
export * from './table/table';
// и все остальные компоненты
- Обновите версию в package.json (можно увеличить последнюю цифру: было 0.0.1, стало 0.0.2)
{
"name": "@relabs_test/test-ui-kit",
"version": "0.0.1",
"type": "module",
"license": "MIT",
"publishConfig": {
"access": "public"
}
}
-
Вызовите команду
yarn build
илиnpm run build
-
Вызовите команту
npm login
-
В терминале в ide будет предложено ввести Username, Password и Email
терминал
Username: relabs_test
Password: relabs123456789
Email: test.relabs.library@gmail.com
-
На указанную почту придет одноразовый пароль, который вас попросят ввести в терминал
-
Заходим на почту test.relabs.library@gmail.com (password: relabs123456789)
-
Вводим одноразовый пароль
-
Запускаем команду npm publish
-
Хваставемся друзьям! 😁
ReLabs React UI KIT
написан на typescript и расширяет стандартные типы
компонентов antd.
-
checked (boolean | indeterminate) - контролируемое проверенное состояние. Необходимо использовать вместе с onCheckedChange
-
onCheckedChange ((checked: CheckedState): void) - обработчик событий, вызываемый при изменении состояния
-
defaultChecked (boolean | indeterminate) - состояние при первоначальном отображении. Используйте, когда вам не нужно контролировать состояние
const App = () => {
const toggleTheme = useToggleTheme();
return (
<>
<Checkbox defaultChecked={theme === 'dark'} />
</>
);
};
const App = () => {
const [checked, setChecked] = useState(false);
return (
<>
<Checkbox
value='react'
label='react'
checked={checked}
onCheckedChange={() => setChecked((prevState) => !prevState)}
/>
</>
);
};
export const GroupOfCheckbox = () => {
const [value, setValue] = useState<string[]>([]);
return (
<>
{/* Checkbox Group Uncontrolled */}
<Checkbox.Group defaultValue={['react', 'vue']} gap='lg'>
<Checkbox value='react' label='react' />
<Checkbox value='svelte' label='svelte' />
<Checkbox value='vue' label='vue' />
</Checkbox.Group>
{/* Checkbox Group Controlled */}
<Checkbox.Group value={value} onCheckedChange={setValue} vertical gap='md'>
<Checkbox value='react' label='react' />
<Checkbox value='svelte' label='svelte' />
<Checkbox value='vue' label='vue' />
</Checkbox.Group>
</>
);
};
export const IndeterminateCheckbox = () => {
const [values, handlers] = useListState(initialValues);
const allChecked = values.every((value) => value.checked);
const indeterminate = values.some((value) => value.checked) && !allChecked && 'indeterminate';
const handleToggleCheckbox = (index: number) => {
handlers.setItemProp(index, 'checked', !values[index].checked);
};
const items = values.map((value, index) => (
<Checkbox
key={value.id}
label={value.label}
checked={value.checked}
defaultChecked={value.checked}
onCheckedChange={() => handleToggleCheckbox(index)}
classNameRoot={cls.root}
classNameIcon={cls.icon}
/>
));
return (
<div className={cls.container}>
<Checkbox
checked={indeterminate || allChecked}
label='Receive all notifications'
defaultChecked={false}
onCheckedChange={() =>
handlers.setState((current) =>
current.map((value) => ({ ...value, checked: !allChecked })),
)
}
/>
<div className={cls.container_inner}>{items}</div>
</div>
);
};
- value (string) - входное значение инпута .
- onChange((event: React.ChangeEvent<HTMLInputElement>) => void) - колбэк функция при вводе данных пользователем.
Неуправляемые компоненты похожи на обычные HTML-формы. Они запоминают всё, что вы печатали. Затем вы можете получить их значение, используя ref. Например, в обработчик onChange:
import React from 'react';
const App = () => {
const myRef = React.useRef<HTMLInputElement>(null);
return (
<Input
placeholder='uncontrolled input'
ref={myRef}
onChange={() => console.log(ref.current.value)}
/>
);
};
Управляемый компонент принимает свое текущее значение в качестве пропсов, а также коллбэк для изменения этого значения. Вы можете сказать, что это более “реактивный” способ управления компонентом, однако это не означает, что вы всегда должны использовать этот метод. Значение формы ввода должно существовать в неком state. Как правило, компонент, который рэндерит форму ввода (т.е. форма), сохраняет их в своем state:
import React from 'react';
const App = () => {
const [inputValue, setInputValue] = React.useState('');
console.log(inputValue);
return <Input value={inputValue} onChange={(e) => setInputValue(e.curentTarget.value)} />;
};
-
mask (string) - Маска ввода, replacement используется для замены символов.
-
replacement (string | Replacement) - Устанавливает заменяемые символы в маске, где «ключ» — заменяемый символ, «значение» — регулярное выражение, которому должен соответствовать входной символ. Символ замены можно передать в виде строки, тогда replacement="_" по умолчанию будет использоваться значение replacement={ _: /./ }. Клавиши игнорируются при вводе. См. примеры в InputMasked
-
showMask (boolean) - Управляет отображением маски, например, +0 ( 123) ___-__-_-__ вместо +0 (123)
const App = () => {
return (
<Input
showMask
mask='+375 (__) ___-__-__'
replacement={{ _: /\d/ }}
label='mask:+375 (__) ___-__-__, replacement: {{ _: /\d/ }} '
/>
);
};
Customization Input элемент представляет собой конструкцию блоков с вложенностью, и предоставляет возможность кастомизаци каждого элемета путем добавления классов стилизации для предназначенного пропса: classNameInput classNameContainer classNameLabel classNameField classNameBeforeIcon classNameIcon classNameMessage classNameMessageError classNameMessageSuccess
Icons
- icon (JSX.Element) - изображение в конце инпута.
- beforeIcon (JSX.Element) - изображение в начале инпута.
- onClickBeforeIcon (() => void) - коллбэк функция обработки клика по beforeIcon .
- onClickIcon (() => void) - коллбэк функция обработки клика по icon .
-
value (string) - входное значение инпута .
-
onChange((event: React.ChangeEvent<HTMLInputElement>) => void) - колбэк функция при вводе данных пользователем.
Customization
- arrowColor(string) - цвет стрелок изменеия значения инпута .
Input элемент представляет собой конструкцию блоков с вложенностью, и предоставляет возможность кастомизаци каждого элемета путем добавления классов стилизации для предназначенного пропса:
classNameInput
classNameContainer
classNameLabel
classNameField
classNameMessage
classNameMessageError
classNameMessageSuccess
-
value (string) - входное значение поля .
-
onChange((event: React.ChangeEvent<HTMLTexyAreaElement>) => void) - колбэк функция при вводе данных пользователем.
Неуправляемые компоненты похожи на обычные HTML-формы.
Они запоминают всё, что вы печатали. Затем вы можете получить их значение, используя ref.
Например, в обработчик onChange:
import React from 'react';
const App = () => {
const myRef = React.useRef<HTMLTextAreaElement>(null);
return (
<TextArea
placeholder='uncontrolled TextArea'
ref={myRef}
onChange={() => console.log(ref.current.value)}
/>
);
};
Управляемый компонент принимает свое текущее значение в качестве пропсов, а также коллбэк для изменения этого значения. Вы можете сказать, что это более “реактивный” способ управления компонентом, однако это не означает, что вы всегда должны использовать этот метод. Значение формы ввода должно существовать в неком state. Как правило, компонент, который рэндерит форму ввода (т.е. форма), сохраняет их в своем state:
import React from 'react';
const App = () => {
const [TextAreaValue, setTextAreaValue] = React.useState('');
console.log(TextAreaValue);
return <TextArea value={inputValue} onChange={(e) => setTextAreaValue(e.curentTarget.value)} />;
};
Customization
Input элемент представляет собой конструкцию блоков с вложенностью, и предоставляет
возможность кастомизаци поля предоставлена возможностью передачи класса:
className
-
value (Date) - входное значение инпута .
-
onChange((newValue: Date) => void) - колбэк функция при изменении даты пользователем.
-
showCalendar(boolean), - указатель отображения календаря (default : true).
-
leftArrowIcon / rightArrowIcon(JSX.Element) -изображения левой и правой стрелки календаря соответственно.
-
calendarIcon(JSX.Element) - изображение кнопки отображения календаря.
-
classNameArrowBtn(string) - класс стрелок календаря.
-
classNameHeaderDatepicker - класс шапки календаря.
-
classNameDaysOfWeek - класс дней недели календаря.
-
classNameCell - класс ячейки календаря. Все пропсы Input также поддерживаютя. Смотрите Input
- data (TableData) - используется для автоматической генерации таблицы
- size (TableSize => sm / md) - размер ячеек
- striped (Striped => boolean | 'odd' | 'even') - выделение четных/нечетных строк
- withTableBorder (boolean) - border вокруг таблицы
- withRowBorders (boolean) - border у строк
- withColumnBorders (boolean) - border у столбцов
- stickyHeader (boolean) - прилипание хэдера таблицы к верху страницы при прокрутке
- stickyHeaderOffset (number) - значение top для position:sticky. По умолчанию 0
- onRowClick ((row: unknown) => void;) - обработчик клика на строку
Для удобства создадим моковые данные. Они будут использоваться во всех примерах
type Student = {
Application_No: number;
Name: string;
Father_Name: string;
DOB: string;
};
const data: Student[] = [
{
Application_No: 1036,
Name: 'NABILA TOHEED',
Father_Name: 'TOHEED AHMED',
DOB: '15-Dec-1998',
},
{
Application_No: 1072,
Name: 'PIRHA MASOOD',
Father_Name: 'MASOOD AHMED SHAIKH',
DOB: '17-Jan-1997',
},
{
Application_No: 1294,
Name: 'MUHAMMAD ASHAR NAEEM',
Father_Name: 'MUHAMMAD NAEEM ANWAR LATE',
DOB: '20-Feb-1998',
},
];
const tableData: TableData = {
head: ['Application_No', 'Name', 'Father Name', 'DOB'],
body: data.map((item) => Object.values(item)),
};
Для автогенерации таблицы нужно прокинуть в нее объект data (data: TableData):
const App = () => {
return <Table striped='odd' data={tableData} onRowClick={(row) => console.log(row)} />;
};
export interface TableData {
head?: ReactNode[];
body?: ReactNode[][];
foot?: ReactNode[];
}
То есть
const tableData: TableData = {
head: ['Application_No', 'Name', 'Father Name', 'DOB'],
body: [
[1036, 'NABILA TOHEED', 'TOHEED AHMED', '15-Dec-1998'],
[1072, 'PIRHA MASOOD', 'MASOOD AHMED SHAIKH', '17-Jan-1997'],
[1294, 'MUHAMMAD ASHAR NAEEM', 'MUHAMMAD NAEEM ANWAR LATE', '20-Feb-1998'],
],
};
будет выглядеть следующим образом:
onRowClick() - в данном примере кникнув на первую строку мы получим row:
{
Application_No: 1036,
Name: 'NABILA TOHEED',
Father_Name: 'TOHEED AHMED',
DOB: '15-Dec-1998',
}
const App = () => {
return (
<Table size='md' withTableBorder withRowBorders withColumnBorders striped>
<Table.THead>
<Table.Tr>
<Table.Th>Application_No</Table.Th>
<Table.Th>Name</Table.Th>
<Table.Th>Father_Name</Table.Th>
<Table.Th>DOB</Table.Th>
</Table.Tr>
</Table.THead>
<Table.TBody>
{data.map((row, index) => (
<Table.Tr key={index}>
<Table.Td>{row.Application_No}</Table.Td>
<Table.Td>{row.Name}</Table.Td>
<Table.Td>{row.Father_Name}</Table.Td>
<Table.Td>{row.DOB}</Table.Td>
</Table.Tr>
))}
</Table.TBody>
</Table>
);
};
Вручную можно прокидывать в компоненты таблицы другие компоненты
То есть никто не запрещает прокинуть кнопки, картинки, иконки и так далее
const App = () => {
return (
<Table size='md' withTableBorder withRowBorders withColumnBorders striped>
<Table.THead>
<Table.Tr>
<Table.Th>
<button>Click me</button>
</Table.Th>
<Table.Th>
<AddIcon />
</Table.Th>
<Table.Th>
<Checkbox />
</Table.Th>
<Table.Th>DOB</Table.Th>
</Table.Tr>
</Table.THead>
{/* ... */}
</Table>
);
};
- size (string) - по дефолту md.
- variant (string) - по дефолту primary.
- isLoading (boolean) - кнопка загрузки.
- selected (boolean) - выбранная кнопка.
- children (boolean) - обязательное присутствие текста.
- start_icon (JSX.Element) - вставить иконку в начало.
- end_icon (JSX.Element) - вставить икконку в конец.
const App = () => {
return (
<Button
size='sm'
selected={selected}
isLoading={loading}
disabled={disabled}
start_icon={<AddIcon />}
>
Button
</Button>
);
};
- size (string) - по дефолту md.
- variant (string) - по дефолту primary.
- isLoading (boolean) - кнопка загрузки.
- selected (boolean) - выбранная кнопка.
- children (boolean) - обязательное присутствие текста.
- icon (JSX.Element) - вставить иконку.
const App = () => {
return (
<Button
size='sm'
selected={selected}
isLoading={loading}
disabled={disabled}
icon={<AddIcon />}
>
Button
</Button>
);
};
- size (string) - по дефолту md.
-
spinnerColor(string) - Добавление цвета, можно добавить в любом формате #fff, rgb и другие, в том числе и var() - Пример:
spinnerColor: "--spinner-color"
',
const App = () => {
return <Spinner size='lg' />;
};
- label (string) - добавление Lable.
- labelClassName (string) - кастомизация Lable.
- iconClassName (string) - кастомизация chekbox.
- inputClassName (string) - кастомизация input.
- onChangeValue (void) - отслеживание изменений значения Radio.
const App = () => {
return (
<>
<Radio name='example' label='Radio button' checked />
<Radio name='example' />
</>
);
};
- name (strin) - присвоение уникального значение группе кнопок, обязательный параметр.
- gap (string) - отсуп меджу кнопками в группе, по умолчанию 'md'.
- vertical (boolean) - вертикальное расположение кнопок.
- defaultValue (string) - выбор кнопки по дефолту со значением checked.
- onChangeValue (void) - отслеживание изменений значения RadioGroup.
- optionsType ('button' | 'radio') - radio/radio-button.
- buttonStyle ('solid' | 'outline') - вид кнопки для radio-button (по умолчанию outline).
- size ('sm' | 'md') - размер для radio-button (по умолчанию 'md').
const App = () => {
const [value, setValue] = useState('two');
return (
<RadioGroup name='testRadioGroup' value={value} onChangeValue={setValue} vertical>
<Radio value='one' label='First' />
<Radio value='two' label='Second' />
<Radio value='three' label='Threeth' />
<Radio value='four' />
</RadioGroup>
);
};
const App = () => {
return (
<RadioGroup name='testRadio' defaultValue='one'>
<Radio value='one' label='First' />
<Radio value='two' label='Second' />
<Radio value='three' />
</RadioGroup>
);
};
- data (DataGridProps.data: TData[]) контент таблицы
- columns (DataGridColumn) - определения столбцов — самая важная часть построения таблицы.
- rowSelection (rowSelection?: boolean) - чекбоксы первой колонкой
- onRowSelect (onRowSelect?: (row: SelectedRow[]) => void) - callback, который срабатывает при нажатии на чекбокс
- sortingFn ((order: Order, columnTitle: keyof TData) => void) - отключает клиентскую сортировку (нужно, если сортировка происходит на сервере)
- size (TableSize => sm / md) - размер ячеек
- striped (Striped => boolean | 'odd' | 'even') - выделение четных/нечетных строк
- withTableBorder (boolean) - border вокруг таблицы
- withRowBorders (boolean) - border у строк
- withColumnBorders (boolean) - border у столбцов
- stickyHeader (boolean) - прилипание хэдера таблицы к верху страницы при прокрутке
- stickyHeaderOffset (number) - значение top для position:sticky. По умолчанию 0
- onRowClick ((row: unknown) => void;) - обработчик клика на строку
Для удобства создадим моковые данные. Они будут использоваться во всех примерах
type Student = {
Application_No: number;
Name: string;
Father_Name: string;
DOB: string;
};
const data: Student[] = [
{
Application_No: 1036,
Name: 'NABILA TOHEED',
Father_Name: 'TOHEED AHMED',
DOB: '15-Dec-1998',
},
{
Application_No: 1072,
Name: 'PIRHA MASOOD',
Father_Name: 'MASOOD AHMED SHAIKH',
DOB: '17-Jan-1997',
},
{
Application_No: 1294,
Name: 'MUHAMMAD ASHAR NAEEM',
Father_Name: 'MUHAMMAD NAEEM ANWAR LATE',
DOB: '20-Feb-1998',
},
];
У нас есть тип Student и data: Student[]. Нужно определить столбцы:
- id(id?: string) - id колонки
- accessorKey((string & NonNullable) | keyof T) - ключ колонки (в данном примере keyof Student)
- header(string) - навзание колонки (то, что мы видим в th)
- sortable(boolean) - вкл/выкл сортировку колонки
-
sortDescFirst(false) - начальное направление сортровки
desc
. Для чисел по умолчинаю = true - cell - Используется для форматирования ячеек. Вы можете предоставить собственный форматировщик ячеек, передав функцию свойству ячейки и используя функцию props.getValue() для доступа к значению вашей ячейки (ниже в примере).
Теперь мы можем передать data и columns в DataGrid:
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
const columns: DataGridColumn<Post>[] = [
{
accessorKey: 'id',
header: 'ID',
sortable: true,
},
{
accessorKey: 'title',
header: 'Title',
sortable: true,
cell: ({ cell }) => <span>{cell.getValue()}</span>,
},
{
accessorKey: 'userId',
header: () => <div style={{ minWidth: '100px' }}>User Id</div>,
sortable: true,
},
{
accessorKey: 'body',
header: 'Body',
cell: ({ cell }) => <span>{cell.getValue()}</span>,
},
];
const loadData = async (columnTitle = '', order = '') => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=10&_sort=${columnTitle}&_order=${order}`,
{
method: 'Get',
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
},
);
const data = await res.json();
return data as Post[];
};
export const DataGridExample = () => {
const [data, setData] = useState<Post[]>([]);
const loadSortingPosts = async (order: Order, columnTitle: keyof Post) => {
const posts = await loadData(columnTitle, order);
setData(posts);
};
useEffect(() => {
const loadInitData = async () => {
const data = await loadData();
setData(data);
};
loadInitData();
}, []);
return <DataGrid data={data} columns={columns} sortingFn={loadSortingPosts} rowSelection />;
};
- animate (boolean) - добавляет анимацию мерцания. По умолчанию false
- height (CSSProperties['height']) - высота, по умолчанию 8px (если задано число - применяется в px, если передать строку '50%' или '10rem' высота будет 50% или 10rem соответственно)
- width (CSSProperties['width']) - ширина. По умолчанию 100%. Вычисляется как и с height. Если props circle=true, то значение width игнорируется
- circle (boolean) - круг. Ширина круга = высоте. По умолчанию false
- radius (CSSProperties['borderRadius']) - border-radius. По умолчанию 0
- visible (boolean) - показывать/скрывать skeleton (пример ниже)
import { useState } from 'react';
import { Button, Skeleton } from '@/lib';
import cls from './styles.module.css';
export const SkeletonExample = () => {
const [visible, setVisible] = useState(true);
return (
<div className={cls.container}>
<div className={cls.container}>
<Button onClick={() => setVisible((prevState) => !prevState)}>
{visible ? 'hide' : 'show'} skeleton
</Button>
<Skeleton visible={visible} radius='5rem'>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi dolor nihil amet tempore
magnam optio, numquam nostrum inventore tempora assumenda saepe, aut repellat.
Temporibus aspernatur aperiam magnam debitis facere odio? Laborum fuga quam voluptas aut
pariatur delectus repudiandae commodi tempora debitis dolores vero cumque magni cum,
deserunt, ad tempore consectetur libero molestias similique nemo eum! Dolore maxime
voluptate inventore atque.
</p>
</Skeleton>
</div>
<div className={cls.wrapper}>
<Skeleton height={50} circle animate />
<div className={cls.right_side}>
<Skeleton height={15} radius={5} />
<Skeleton width='50%' height={12} />
<Skeleton />
</div>
</div>
</div>
);
};
- size (number) - размер(значение в px)
- src (string) - src изображения
- alt (string) - альтернативный текст
-
letters (string) - Перые буквы слов вместо стандартной картинки (Если ввести
Люблю программировать
, будетЛП
) -
tag (HTMLElement) - стандартный тэг
div
, но можно поменять. Если нужны ссылка, нужно написатьtag={'a'}
- isOnline (boolean) - показать/скрыть онлайн индикатор (по умолчанию false)
- max (number) - максимальное количество аватаров (см пример ниже)
- size (string) - ощий размер для всех аватаров
- count (number) - количество заглушек
- animate (boolean) - вкл/выкл анимацию
- size (string) - ощий размер для всех аватаров
import { Avatar } from '@/lib';
export const AvatarExample = () => {
return (
<div>
<Avatar size={150} isOnline tag='a' href='#' target='_blank' />
<Avatar size={60} isOnline letters='Oleg Pol' />
<Avatar
size={60}
isOnline
src='https://images.unsplash.com/photo-1580489944761-15a19d654956?q=80&w=1961&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'
/>
<Avatar.Group max={4} size={100}>
<Avatar isOnline />
<Avatar isOnline />
</Avatar.Group>
<Avatar.Skeleton size={100} count={3} />
</div>
);
};
Dropdown представляет собой конструктор из компонентов:
-
Dropdown - рутовый компонент, в него вкладываются все остальные
-
Dropdown.Trigger - кнопка, которая переключает выпадающее меню
-
Dropdown.Content - компонент, который появляется, когда открыто раскрывающееся меню
-
Dropdown.Item - компонент, содержащий пункты выпадающего меню
-
Dropdown.Label - используется для отображения заголовка
-
Dropdown.Sub - содержит все части подменю
-
Dropdown.SubTrigger - пункт, открывающий подменю. Должен быть отображен внутри DropdownMenu.Sub
-
Dropdown.SubContent - компонент, который появляется при открытии подменю. Должен быть отображен внутри DropdownMenu.Sub
-
Dropdown.CheckboxItem - элемент, которым можно управлять и который отображается как флажок
-
Dropdown.Separator - используется для визуального разделения элементов в раскрывающемся меню
-
trigger ('click' | 'hover) - тригер по клику/наведению (по умолчанию click)
-
open (boolean) - состояние открыто/зыкрыто
-
modal (boolean) - если установлено значение true, взаимодействие с внешними элементами будет отключено
-
letters (string) - открытое состояние раскрывающегося меню при его первоначальном отображении
-
children (ReactNode)
-
withArrow (boolean) - иконка стрелки справа
-
iconLeft (ReactNode) - иконка слева
-
iconRight (ReactNode) - иконка справа
-
arrowClassName (boolean) - className для стрелки
-
className (string) - className для trigger
-
children (ReactNode)
-
loop (boolean) - eсли установлено значение true, навигация с помощью клавиатуры будет циклически переходить от последнего элемента к первому и наоборот
-
onEscapeKeyDown ((event: KeyboardEvent) => void) - обработчик событий вызывается, когда клавиша Escape нажата. Это можно предотвратить, вызвав event.preventDefault
-
side ('top' | 'right' | 'bottom' | 'left') - предпочтительная сторона триггера для рендеринга при открытии
-
sideOffset (number) - расстояние в пикселях от триггера
-
portalContainer (HTMLElement) - элемент контейнера для переноса содержимого (по умолчанию document.body)
-
portalContainer (HTMLElement) - элемент контейнера для переноса содержимого (по умолчанию document.body)
-
className (string) - className для Dropdown.Content
-
withArrow (boolean) - добавляет стрелочку для контейнера
-
arrowWidth (number) - ширина стрелки
-
arrowHeight (number) - высота стрелки
-
disabled (boolean) - если установлено значение true, пользователь не может взаимодействовать с элементом
-
onSelect ((event: Event) => void) - обработчик событий вызывается, когда пользователь выбирает элемент (с помощью мыши или клавиатуры). Вызов event.preventDefault в этом обработчике предотвратит закрытие раскрывающегося меню при выборе этого элемента.
-
className (string) - className для Dropdown.Item
-
children (ReactNode)
-
className (string) - className для Dropdown.Label
-
children (ReactNode)
-
open (boolean) - состояние открыто/зыкрыто
-
onOpenChange ((open: boolean): void) - обработчик событий, вызываемый при изменении открытого состояния подменю
-
children (ReactNode)
-
withArrow (boolean) - иконка стрелки справа
-
disabled (boolean) - если установлено значение true, пользователь не может взаимодействовать с элементом
-
arrowClassName (boolean) - className для стрелки
-
className (string) - className для trigger
-
children (ReactNode)
-
loop (boolean) - eсли установлено значение true, навигация с помощью клавиатуры будет циклически переходить от последнего элемента к первому и наоборот
-
onEscapeKeyDown ((event: KeyboardEvent) => void) - обработчик событий вызывается, когда клавиша Escape нажата. Это можно предотвратить, вызвав event.preventDefault
-
sideOffset (number) - расстояние в пикселях от триггера
-
className (string) - className для Dropdown.Content
-
withArrow (boolean) - добавляет стрелочку для контейнера
-
arrowWidth (number) - ширина стрелки
-
arrowHeight (number) - высота стрелки
-
checked (boolean) - контролируемое состояние элемента. Необходимо использовать вместе с onCheckedChange
-
onCheckedChange ((checked: boolean) => void) - обработчик событий, вызываемый при изменении проверенного состояния
-
disabled (boolean) - Если установлено значение true, пользователь не может взаимодействовать с элементом
-
className (string) - className для Dropdown.CheckboxItem
-
children: (ReactNode)
- className (string) - className для Dropdown.Separator
import { Dropdown } from '@/lib';
export const DropdownExample = () => {
return (
<Dropdown {...args}>
<Dropdown.Trigger withArrow className={cls.trigger}>
<span>Dropdown</span>
</Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item>Option1</Dropdown.Item>
<Dropdown.Item>Option2</Dropdown.Item>
<Dropdown.Separator />
<Dropdown.Label>Title</Dropdown.Label>
<Dropdown.Item>Option 3</Dropdown.Item>
<Dropdown.Item disabled>Option disabled</Dropdown.Item>
</Dropdown.Content>
</Dropdown>
);
};
import { BarChartIcon, Dropdown, SaveIcon, SettingsIcon, UniverseIcon } from '@/lib';
import cls from './DropdownExample.module.css';
export const DropdownExample = () => {
const [checkboxesState, setCheckboxesState] = useState({
option1: false,
option2: false,
option3: false,
option4: true,
});
const handleChangeCheckboxState = (checked: boolean, title: string) => {
setCheckboxesState((prevState) => ({ ...prevState, [title]: checked }));
};
return (
<Dropdown trigger='click'>
<Dropdown.Trigger withArrow className={cls.trigger}>
<span className={cls.trigger_content}>
{/* <SettingsIcon /> */}
Dropdown
</span>
</Dropdown.Trigger>
<Dropdown.Content>
<Dropdown.Item>Option1</Dropdown.Item>
<Dropdown.Item>Option2</Dropdown.Item>
<Dropdown.Separator />
<Dropdown.Label>Title</Dropdown.Label>
<Dropdown.Item>Option 3</Dropdown.Item>
<Dropdown.Item disabled>Option disabled</Dropdown.Item>
<Dropdown.Sub>
<Dropdown.SubTrigger>
<span>Sub Dropdown</span>
</Dropdown.SubTrigger>
<Dropdown.SubContent>
<Dropdown.CheckboxItem
checked={checkboxesState.option1}
onCheckedChange={(checked) => handleChangeCheckboxState(checked, 'option1')}
>
<div className={cls.sub_item_container}>
<SaveIcon />
Sub Item 1
</div>
</Dropdown.CheckboxItem>
<Dropdown.CheckboxItem
checked={checkboxesState.option2}
onCheckedChange={(checked) => handleChangeCheckboxState(checked, 'option2')}
>
<div className={cls.sub_item_container}>
<UniverseIcon />
Sub Item 2
</div>
</Dropdown.CheckboxItem>
<Dropdown.CheckboxItem
checked={checkboxesState.option3}
onCheckedChange={(checked) => handleChangeCheckboxState(checked, 'option3')}
>
<div className={cls.sub_item_container}>
<SettingsIcon />
Sub Item 3
</div>
</Dropdown.CheckboxItem>
<Dropdown.CheckboxItem
disabled
checked={checkboxesState.option4}
onCheckedChange={(checked) => handleChangeCheckboxState(checked, 'option4')}
>
<div className={cls.sub_item_container}>
<BarChartIcon />
<span className={cls.sub_item_with_icon}>Sub Item</span>
</div>
</Dropdown.CheckboxItem>
</Dropdown.SubContent>
</Dropdown.Sub>
</Dropdown.Content>
</Dropdown>
);
};
- value (number) - передача внутрь основного значения рейтинга ( количество активных звезд). По умолчанию 0.
- starSize (number) - размер самой звезды в пикселях По умолчанию 24.
- isOnlyRead (boolean) - возможность либо невозможность изменять количество активных звезд.
- onFixRating (star:number) - отслеживание изменений значения рэйтинга и передача значений наружу.
export const RateExample = () => {
const [value, setValue] = useState(0);
const onFixRating = (star: number) => {
setValue(star);
};
return (
<div>
<h4>Uncontrolled</h4>
<Toggle />
<h4>Controlled</h4>
<Toggle isOnlyRead={false} value={value} onFixRating={onFixRating} />
<h4>Read Only</h4>
<Toggle value={4.6} isOnlyRead />
</div>
);
};
- disabled (boolean)
- isLoading (boolean) - Отвечает за отрисовку спиннера.
- isEmpty (boolean) - Отвечает за отрисовку статичного элемента внутри тогла, заменяя динамический элемент
- checked (boolean) - Передает внутрь компоненты состояние в котором находится динамический элемент. Проще говоря, вкл или выкл.
- switchToggle (boolean) - Изменяет состояние тогла.
- size ('xs' | 'md') - Изменяет размер тогла.
- classNameThumb (sting) - Дополнительные стили для динамического внутреннего элемента.
- className (sting) - Дополнительные стили для статического внешнего элемента.
export const ToggleExample = () => {
const [checked, setChecked] = useState(false);
const onChangeHandler = () => {
setChecked((prevState) => !prevState);
};
return (
<div>
<h4>Uncontrolled with focus and hover</h4>
<Toggle />
<h4>Disabled</h4>
<Toggle disabled />
<h4>Spinner</h4>
<Toggle isLoading />
<h4>Empty toggle</h4>
<Toggle isEmpty />
<h4>Controlled with focus and hover</h4>
<Toggle checked={checked} switchToggle={onChangeHandler} />
<h4>Uncontrolled with focus and hover small size</h4>
<Toggle size='xs' />
<h4>Empty toggle with small size</h4>
<Toggle isEmpty size='xs' />
</div>
);
};
-
className (string) - Дополнительный класс CSS для корневого узла DOM слайдера.
-
value (number | number[]) - Установите текущее значение ползунка.
-
defaultValue (number) - Значения слайдера по молчанию.
-
min (number) - Минимальное начения слайдера, 0 по умолчанию.
-
max (number) - Максимальное значения слайдера, 100 по умолчнию.
-
marks ({number: ReactNode} | {number: { style, label }}) - Метки на слайдере. Ключ определяет позицию, а значение определяет, что будет отображаться. Если вы хотите задать стиль определенной точки отметки, значением должен быть объект, содержащий свойства стиля и метки.
-
step (number | null) - Значение, которое будет добавляться или вычитаться на каждом шаге, выполняемое ползунком. Должно быть больше нуля, а max - min должно быть равномерно кратно значению шага. Если marks не является пустым объектом, step можно установить равным null, чтобы сделать отметки шагами.
-
disabled (boolean) - Если значени true, то ползунок нельзя двигать.
-
dots (boolean) - Если значение шага больше 1, вы можете установить для точек значение true, если хотите отображать ползунок с точками.
-
showLabels (boolean) - Отображение начального и конечного значений всей шкалы.
-
range (boolean) - Использовать range slider.
-
pushable (boolean | number) - pushable может быть установлено как true (в режиме range), чтобы разрешить нажатие окружающих маркеров при перемещении маркера. Если установлено число, оно будет представлять собой минимальное гарантированное расстояние между маркерами.
-
onChange (Function) - передача внутрь основного значения рейтинга ( количество активных звезд).
-
showTooltip (boolean) - Отображение tooltip, по умолчанию работает на hover и dragging, при установке значения на true, будет виден постоянно, при установке значения на false отключается отображение.
-
tipProps (object) - Объект c пропсами компонента Tooltip, документацию можно найти по ссылке https://react-component.github.io/tooltip'.
-
classNamesLabels (string) - ClassName для label слайдера.
Дополнительные пропсы можно наайти по ссылке https://www.npmjs.com/package/rc-slider
const App = () => {
const [value, setValue] = useState('two');
return (
<div style={{ width: '300px' }}>
<Slider defaultValue={50} showLabels />
</div>
);
};
-
open (boolean) - Состояние открыто/закрыто
-
onClose (() => void) - Колбэк при закрытии модального окна
-
onOk (() => () => void) - Колбэк при нажатии на "Ok"
-
onCancel (star:number) - Колбэк при нажатии на "Cancel"
-
title (string) - Заголовок модальног окна
-
size ('lg' | 'md' | 'sm') - Размеры модального окна
-
centered (boolean) - Выравнивание по вертикали
-
withOverlay (boolean) - Показывать/не показывать затемняющий фон
-
withCloseButton (boolean) - Показывать/не показывать кнопку закрыть. Лкно закрывается при нажатии на Escape, или при клике вне окна
-
renderHeader (ReactNode) - Свой header
-
renderBody (ReactNode) - Свой body
-
renderFooter (ReactNode) - Свой footer
-
okButtonProps (ButtonProps) - Пропсы для кнопки "Ok"
-
cancelButtonProps (ButtonProps) - Пропсы для кнопки "Cancel"
import { useState } from 'react';
import { Button, Input, Modal } from '@/lib';
import cls from './ModalExample.module.css';
export const ModalExample = () => {
const [open, setOpen] = useState(false);
return (
<>
<Button variant='outline' onClick={() => setOpen(true)}>
Trigger modal
</Button>
<Modal title='Authentication' open={open} onClose={() => setOpen(false)} size='sm'>
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has
been the industry's standard dummy text ever since the 1500s, when an unknown printer
took a galley of type and scrambled it to make a type specimen book. It has survived not
only five centuries, but also the leap into electronic type
<Input placeholder='some input' className={cls.input} />
<Input placeholder='some input' className={cls.input} />
<Input placeholder='some input' className={cls.input} />
</Modal>
</>
);
};
import { useState } from 'react';
import { Button, Input, Modal } from '@/lib';
import cls from './ModalExample.module.css';
const CustomHeader = <header>My custom header</header>;
export const ModalExample = () => {
const [open, setOpen] = useState(false);
return (
<>
<Button variant='outline' onClick={() => setOpen(true)}>
Trigger modal
</Button>
<Modal
title='Authentication'
open={open}
onClose={() => setOpen(false)}
renderHeader={() => <CustomHeader />}
okButtonProps={{ children: 'Новый текст для кнопки', disabled: true, isLoading: true }}
/>
</>
);
};
-
backgroundColor (string) - устанавливает цвет фона тэга. По умолчанию 'neutral-3'.
-
textColor (string) - устанавливает цвет текста тэга.
-
link (boolean) - определяет, использовать ли тэг как ссылку. По умолчанию false.
-
src (string) - URL-адрес для использования, если тэг представляет собой ссылку. По умолчанию пустая строка.
-
removable (boolean) - указывает, является ли тэг удалаемым. По умолчанию false.
-
onRemoveTag (() => void) - функция, вызванная при удалении тега, если он является удаляемым.
-
startIcon (React.ReactNode) - иконка в начале тега.
-
children (string) - текстовое содержимое тэга. По умолчанию 'Tag'.
-
className (string) - использование для передачи пользовательского класса в компонент тега.
export const TagExample = () => {
const tags = [
{
backgroundColor: 'neutral-3',
title: 'Tag 1',
textColor: 'red-3',
link: false,
src: 'http://example1.com',
removable: true,
onRemoveTag: () => console.log('Remove tag1'),
className: 'class1',
},
{
backgroundColor: 'neutral-3',
title: 'Tag 2',
textColor: 'red-3',
link: true,
src: 'http://example2.com',
removable: false,
startIcon: <Avatar />,
onRemoveTag: () => console.log('Remove tag2'),
className: 'class2',
},
{
backgroundColor: 'neutral-3',
title: 'Tag 3',
textColor: 'red-3',
link: false,
src: 'http://example3.com',
removable: true,
startIcon: <Avatar />,
onRemoveTag: () => console.log('Remove tag3'),
className: 'class3',
},
{
backgroundColor: 'neutral-3',
title: 'Tag 4',
textColor: 'red-3',
className: 'class4',
},
];
return (
<div className={cls.container}>
<div className={cls.tag}>
<Tag backgroundColor='neutral-3' textColor='red-3' startIcon={<Avatar />} removable>
Tag
</Tag>
</div>
<div>
<span>LINKED TAGS: </span>
<div className={cls.tag}>
<Tag backgroundColor='cyan-3' link src={'https://ant.design/components/tag'}>
Tag
</Tag>
</div>
</div>
<div>
<span>TAG GROUP: </span>
<div className={cls.tagGroup}>
{tags.map((tag, index) => {
return (
<Tag
key={index}
backgroundColor={tag.backgroundColor}
textColor={tag.textColor}
link={tag.link}
src={tag.src}
removable={tag.removable}
startIcon={tag.startIcon}
onRemoveTag={tag.onRemoveTag}
className={tag.className}
>
{tag.title}
</Tag>
);
})}
</div>
</div>
</div>
);
};
Данный компонент служит своего рода оберткой для другого элемента, это может быть как отдельная компонента, так и просто блок, которые принимаются в виде children в компонент badge (см пример ниже)
- dot (boolean) Если он тру, то будет отрисовываться просто точка, при этом, если мы обрисовываем точку, а не число, то все статусы применять нельзя, можно только те, которые входят в type BaseStatus.
- value (number) - Отвечает за отрисовку числового значения .
- status ("Success""Error""Default""Processing""Warning""Primary""Removed""Added") - Отвечает за цвет. Статусы, которые можно применить к числовому значению шире, чем те, что можно применить к точке. Для точки type BaseStatus, для числового значения ExtendedStatus (он расширяет BaseStatus).
- size ("sm""md""lg") - По умолчанию lg. Отвечает за размер бейджа, если это не точка.
- badgePosition ("top-left""top-right""bottom-left""bottom-right") - Изменяет позицию бэйджа относительно чилдрена По умолчанию "top-right".
export const BadgeExample = () => {
return (
<div>
<h4>Just dot</h4>
<div style={{ display: 'flex', gap: '15px' }}>
<div>
<Badge dot status='Success' badgePosition='top-left'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge dot status='Error' badgePosition='top-right'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge dot status='Processing' badgePosition='bottom-left'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<h4>With Numbers Large</h4>
<div style={{ display: 'flex', gap: '25px' }}>
<div>
<Badge value={21} status='Success' badgePosition='top-right'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge value={21} status='Error' badgePosition='bottom-left'>
<div style={{ width: '90px', height: '90px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge value={21} status='Default' badgePosition='bottom-right'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge value={21} status='Processing'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge value={21} status='Primary'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
<div>
<Badge value={21} status='Added'>
<div style={{ width: '20px', height: '20px', background: 'yellow' }} />
</Badge>
</div>
</div>
</div>
</div>
);
};
- container? (HTMLElement | null) HTML Element, куда будет помещаться сожержимое портала. По умолчанию document.body
- containerId? (string) - id элемента, куда будет помещаться содержимое портала
- children? (ReactNode) - содержимое портала
- as (string) - устанавливает HTML-тег контейнера подсказки. По умолчанию 'div'.
- distance (number) - устанавливает расстояние от контейнера подсказки до целевого элемента.
- behavior (TTooltipBehaviorType) - определяет поведение подсказки. Возможные значения: 'Наведение', 'Клик'. По умолчанию 'Наведение'.
- children (ReactNode) - содержимое подсказки.
- classes (TClasses) - устанавливает дополнительные CSS-классы для стилизации.
- classNames (string) - позволяет передать пользовательские классы в компонент подсказки.
- message (string | React.ReactElement) - содержимое или React-элемент для отображения внутри подсказки.
- modifiers (TModifiers) - устанавливает дополнительные модификаторы для подсказки.
- placement (TPlacement) - устанавливает расположение подсказки. По умолчанию 'вверх'.
- timerDelay (number) - устанавливает задержку по времени для отображения подсказки. По умолчанию 100 мс.
- arrow (boolean) - определяет, нужно ли отображать стрелку в подсказке. По умолчанию false.
- width (number) - определяет ширину Tooltip. По умолчанию 500.
<Tooltip
message={
'Use this if you want to insert a text hint. The tooltip appears when hovering over an object. This is the maximum block width that should be used.'
}
placement='top'
distance={10}
arrow={true}
behavior='hover'
>
Hello
</Tooltip>
- breadcrumbItems({href: string, title?: string, icon?: ReactNode}) - Массив объетов {href: string , title?: string , icon?: ReactNode} где передаются значения Breadcrumbs.
- variantLink(string) - Выбор стиля отображения значений Breadcrumbs, может быть "background" | "underline", по умолчанию "background".
- separator(ReactNode) - Рзделитель, по молчанию "/", можно вставить картинку.
- collapsed(boolean | number) - Можно сжать Breadcrumb до последнего значения, принимает как boolean так и number, при передачи числа, отслеживает сколько предыдущих ссылок будет видно перед активной.
- tooltipProps(object) - объект пропсов компонента , основные пропсы компонента смотреть в описании Tooltip.
const items = [
{
href: '/1',
title: 'Item1',
},
{
href: '/2',
title: 'Item2',
},
{
href: '/3',
title: 'Item3',
},
];
<div>
<Breadcrumbs breadcrumbItems={items} collapsed />
</div>;
- progress(number) - Значение прогресса загрузки от 0 до 100,
- size('sm' | 'md' | 'lg') - Размер полоски,
- width(number) - Длинна полоски загрузки, по умолчанию 100%,
- className(string) - Передача стилей основвной обертки,
- isError(boolean) - Передача присутствия ошибки,
- isSuccessful(boolean) - Передача успеха загрузки, по умолчаанию срабатывает при 100%,
- linearColor(string) - Изменение цвета полоски загрузки, передача цета в различных форматах (Keyword, Hexadecimal, RGB, HSL и т.д),
- backgroundLinearColor(string) - Изменение цвета заднего фона полоски загрузки, передача цета в различных форматах (Keyword, Hexadecimal, RGB, HSL и т.д),
const [progress, setProgress] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setProgress((oldProgress) => {
if (oldProgress === 100) {
return 0;
}
const diff = Math.random() * 10;
return Math.floor(Math.min(oldProgress + diff, 100));
});
}, 500);
return () => {
clearInterval(timer);
};
}, []);
return (
<>
<LinearProgress size='lg' width={500} progress={100} />
<LinearProgress width={450} progress={progress} />
<div className={cls.progressWrapper}>
<LinearProgress isError size='sm' progress={60} />
</div>
<LinearProgress size='sm' progress={progress} />
</>
);
- disabledAll(boolean) - Устанавливает кнопки в неактивное состояниее.
все остальные props можно найти перейдя поссылке https://www.npmjs.com/package/react-responsive-pagination
const [currentPage, setCurrentPage] = useState<number>(1);
return (
<>
<Pagination
total={20}
maxWidth={500}
current={currentPage}
onPageChange={(page) => setCurrentPage(page)}
/>
</>
);
-
value?:{string) - Значение в формате
__:__:__
, в противном случае будет установлено00:00:00
. - onChange? ((timeString: string) => void) - Обработчик для изменения значения.
-
size? ('sm' | 'md') - Размер контейнера. По умолчанию
'md'
. -
invalid? (boolean) - Индикатор невалидности значения (автоматически становится true, если
0 > h > 23
,0 > m > 59
,0 > s > 59
). - disabled? (boolean) - Заблокировать timepicker
- className? (string) - ClassName для основного контейнера
- inputClassName? (string) - ClassName для input
- iconClassName? (string) - ClassName для иконки
- closeIconClassName? (string) - ClassName для кнопки "remove"
- popupClassName? (string) - ClassName для контейнера со временем
- nowButtonClassName? (string) - ClassName для кнопки 'Now'
- okButtonClassName? (string) - ClassName для кнопки 'Ok'
- nowButtonTitle? (string) - Текст для кнопки 'Now'
- okButtonTitle? (string) - Текст для кнопки 'Ok'
- disabledTime? (DisabledTime) - Заблокированное время
type DisabledTime = (now: string) => {
disabledHours?: () => number[];
disabledMinutes?: (selectedHour: number) => number[];
disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[];
};
const disabledTime: DisabledTime = (now) => {
return {
disabledHours: () => [1, 2, 3, 5, 10, 14],
disabledMinutes: () => [20, 21, 22, 30, 46, 59],
disabledSeconds: () => [],
};
};
import { useState } from 'react';
import { TimePicker } from '@/lib';
export const TimePickerExample = () => {
const [time, setTime] = useState('10:27:00');
const [invalid, setInvalid] = useState(false);
const handleChange = (time: string) => {
setTime(time);
};
return (
<div style={{ margin: '50px' }}>
<TimePicker size='md' value={time} onChange={handleChange} />
<TimePicker size='sm' invalid={invalid} />
<button onClick={() => setInvalid((prevState) => !prevState)}>Toggle Invalid</button>
</div>
);
};
- основне пропсы от TimePicker
- startValue? (string) - Начальное значение
- endValue? (string) - Конечное значение
- onChange?: (startValue: string, endValue: string) => void - Обработчик для изменения значения
import { useState } from 'react';
import { TimePicker } from '@/lib';
export const TimePickerExample = () => {
const [time, setTime] = useState('10:27:00');
const [invalid, setInvalid] = useState(false);
const handleChange = (time: string) => {
setTime(time);
};
const handleDisabledTime: DisabledTime = (now) => {
return {
disabledHours: () => [1, 2, 3, 5, 10, 14],
disabledMinutes: () => [20, 21, 33, 59],
disabledSeconds: () => [],
};
};
const handleDisabledEndTime: DisabledTime = (now) => {
return {
disabledHours: () => [10, 11, 12, 13, 14, 15, 16],
disabledMinutes: () => [],
disabledSeconds: () => [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
],
};
};
return (
<div>
<TimePicker.RangePicker
disabledStartTime={handleDisabledTime}
disabledEndTime={handleDisabledEndTime}
/>
<button onClick={() => setInvalid((prevState) => !prevState)}>Toggle Invalid</button>
</div>
);
};