Современная React UI библиотека компонентов с TypeScript и styled-components
DobruniaUI - это комплексная библиотека React компонентов, разработанная с упором на современный дизайн, производительность и удобство использования. Все компоненты написаны на TypeScript и стилизованы с помощью styled-components.
Попробуйте все компоненты в интерактивном плейграунде →
Вы можете поддержать развитие библиотеки:
npm install dobruniaui react react-domimport React, { useEffect } from 'react';
import { Button, initThemeSystem } from 'dobruniaui';
export default function App() {
useEffect(() => {
// Инициализируем систему тем при монтировании приложения
initThemeSystem();
}, []);
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minHeight: '100vh',
padding: '2rem',
textAlign: 'center',
background: 'var(--c-bg-default)',
color: 'var(--c-text-primary)',
}}
>
<h1
style={{
fontSize: '3rem',
margin: '0 0 1rem 0',
color: 'var(--c-text-primary)',
}}
>
DobruniaUI
</h1>
<p
style={{
fontSize: '1.2rem',
margin: '0 0 2rem 0',
color: 'var(--c-text-secondary)',
}}
>
Современная React UI библиотека компонентов
</p>
<Button
variant='secondary'
onClick={() => window.open('https://github.com/Dobrunia/dobruniaui#readme', '_blank')}
>
Документация
</Button>
</div>
);
}Для лучшей интеграции с системой тем добавьте в ваш главный CSS файл:
/* Базовые стили */
*,
*::before,
*::after {
box-sizing: border-box;
font-family: 'Rubik', sans-serif; /* или ваш предпочитаемый шрифт */
}
html {
font-size: 16px;
}
@media (max-width: 450px) {
html {
font-size: 14px;
}
}
body {
margin: 0;
padding: 0;
background-color: var(--c-bg-default);
color: var(--c-text-primary);
}
/* Опциональные utility классы для размеров шрифтов */
.font-small {
font-size: 0.7rem;
}
.font-small-plus {
font-size: 0.8rem;
}
.font-medium {
font-size: 1rem;
}
.font-large {
font-size: 1.2rem;
}💡 Совет: Использование CSS переменных тем (
var(--c-bg-default),var(--c-text-primary)) обеспечит автоматическое переключение цветов при смене темы.
DobruniaUI включает гибкую систему управления темами с возможностью легкого добавления кастомных тем.
Светлые темы:
- 🌞 Светлая - классическая светлая тема (по умолчанию)
- 🌊 Океан - нежная морская тема в мягких тонах
- 🌸 Розовая - мягкая пастельная тема
- 🌅 Закат - тёплая персиковая тема в оттенках заката
- 💜 Lavender Light - светлая сиреневая тема для дневного использования
Тёмные темы:
- 🌙 Тёмная - современная тёмная тема с улучшенной контрастностью
- ⚫ Угольная - элегантная монохромная тема
- 🌫️ Сланцевая - мягкая тёмная с пастельными акцентами
- 🌌 Полуночная - глубокая тёмная с тёплыми акцентами
- 🌙 Лунная - холодная серебристая тема
- 🌕 Полная луна - золотистая луна над тёмным океаном
- ⚙️ Металлическая - полированное железо с белыми искрами
- ⬛ Графитовая - настоящая графитовая тема
- 🩸 Кровавая луна - мистическая тёмная тема с ярко-красными акцентами
- 🔮 Obsidian Purple - тёмная тема в стиле Obsidian с фиолетовыми акцентами
- 🎩 Old Money - элегантная тема в стиле старых денег с бежево-коричневой палитрой
Базовые функции:
import {
setTheme,
getTheme,
toggleTheme,
removeTheme,
getSystemTheme,
initThemeSystem,
} from 'dobruniaui';
// Установить тему
setTheme('light'); // светлая
setTheme('dark'); // тёмная
setTheme('ocean'); // океан
setTheme('pink'); // розовая
setTheme('sunset'); // закат
setTheme('charcoal'); // угольная
setTheme('slate'); // сланцевая
setTheme('midnight'); // полуночная
setTheme('lunar'); // лунная
setTheme('fullmoon'); // полная луна
setTheme('metallic'); // металлическая
setTheme('graphite'); // графитовая
setTheme('bloodmoon'); // кровавая луна
setTheme('obsidian'); // obsidian purple
setTheme('lavender'); // lavender light
setTheme('oldmoney'); // old money
// Получить текущую тему
const currentTheme = getTheme(); // Theme | null
// Переключить между светлой и тёмной
toggleTheme();
// Удалить сохранённую тему (очистить localStorage и CSS переменные)
removeTheme();
// Получить системную тему пользователя
const systemTheme = getSystemTheme(); // 'light' | 'dark'
// ⚠️ ОБЯЗАТЕЛЬНО: Инициализируем систему тем при монтировании приложения!
initThemeSystem();Компонент выбора темы:
import { ThemeSelect } from 'dobruniaui';
function App() {
useEffect(() => {
// Инициализируем систему тем при монтировании приложения
initThemeSystem();
}, []);
return (
<div>
<ThemeSelect />
</div>
);
}import { registerTheme } from 'dobruniaui';
// Регистрируем новую тему
registerTheme({
name: 'violet',
label: 'Фиолетовая',
icon: '🟣',
description: 'Элегантная фиолетовая тема',
variables: {
'--c-bg-default': '#faf5ff',
'--c-bg-subtle': '#ffffff',
'--c-bg-elevated': '#f3e8ff',
'--c-text-primary': '#581c87',
'--c-text-secondary': '#7c3aed',
'--c-text-inverse': '#ffffff',
'--c-border': '#e9d5ff',
'--c-border-focus': '#8b5cf6',
'--c-accent': '#8b5cf6',
'--c-accent-hover': '#7c3aed',
'--c-accent-active': '#6d28d9',
'--c-success': '#059669',
'--c-error': '#dc2626',
'--c-warning': '#d97706',
'--c-info': '#3b82f6',
},
});
// Тема автоматически появится в ThemeSelectПолучение информации о темах:
import { getAllThemes, getThemeConfig } from 'dobruniaui';
// Получить все зарегистрированные темы
const themes = getAllThemes();
// Получить конфигурацию конкретной темы
const darkTheme = getThemeConfig('dark');Основные функции:
setTheme(themeName: string)- устанавливает тему и сохраняет в localStoragegetTheme()- возвращает текущую сохранённую тему или nulltoggleTheme()- переключает между светлой и тёмной темойremoveTheme()- удаляет сохранённую тему и очищает CSS переменныеgetSystemTheme()- определяет системную тему пользователя ('light' | 'dark')initThemeSystem()- инициализирует систему тем (ОБЯЗАТЕЛЬНО Инициализируем систему тем при монтировании приложения!)
Управление темами:
registerTheme(theme: ThemeConfig)- регистрирует новую тему в системеgetAllThemes()- возвращает массив всех зарегистрированных темgetThemeConfig(name: string)- возвращает конфигурацию темы по имени
Типы:
Theme- строковый тип для имени темыThemeConfig- интерфейс конфигурации темы с полями name, label, icon, description, variables
Пропсы:
variant?: 'primary' | 'secondary' | 'ghost' | 'warning' | 'send' | 'close'- стиль кнопкиsize?: 'small' | 'medium' | 'large'- размер кнопкиshape?: 'default' | 'circle' | 'square'- форма кнопкиfullWidth?: boolean- растянуть на всю ширинуisLoading?: boolean- состояние загрузкиleftIcon?: React.ReactNode- иконка слеваrightIcon?: React.ReactNode- иконка справаoutlined?: boolean- контурная кнопкаdisabled?: boolean- заблокированное состояниеonClick?: () => void- обработчик кликаchildren?: React.ReactNode- содержимое кнопкиclassName?: string- дополнительные CSS классы
Варианты кнопок:
primary- основная кнопка с акцентным цветомsecondary- вторичная кнопка с мягким стилемghost- прозрачная кнопка без фонаwarning- кнопка предупрежденияsend- специальная кнопка для отправки (компактная)close- кнопка закрытия с красным акцентом
Формы кнопок:
default- стандартная прямоугольная формаcircle- круглая кнопка (идеально для иконок)square- квадратная кнопка
<Button variant='primary' size='large' isLoading onClick={() => console.log('Clicked')}>
Отправить
</Button>
<Button variant='secondary' shape='circle' size='small'>
↓
</Button>
<Button variant='close' shape='circle' size='small'>
×
</Button>Пропсы:
tooltipText?: string- текст подсказки при наведенииsize?: 'small' | 'medium' | 'large'- размер кнопкиdisabled?: boolean- заблокированное состояниеonClick?: (event: React.MouseEvent<HTMLButtonElement>) => void- обработчик кликаclassName?: string- дополнительные CSS классы
<ErrorButton tooltipText='Удалить элемент' size='medium' onClick={() => handleDelete()} />Пропсы:
icon: 'clock' | 'exclamation' | 'question' | 'dots' | 'exit' | 'settings' | 'add' | 'search'- тип иконки (обязательный)size?: 'small' | 'medium' | 'large'- размер кнопкиvariant?: 'primary' | 'secondary' | 'ghost' | 'warning'- стиль кнопкиtitle?: string- текст для tooltip при наведенииiconColor?: string- цвет иконки (по умолчанию наследует цвет текста кнопки)disabled?: boolean- заблокированное состояниеonClick?: (event: React.MouseEvent<HTMLButtonElement>) => void- обработчик кликаclassName?: string- дополнительные CSS классы
Доступные иконки:
clock- часы/времяexclamation- восклицательный знакquestion- вопросительный знакdots- три точки (меню)exit- выход/логинsettings- настройки/шестеренкаadd- плюс (добавление)search- лупа (поиск)
<IconBtn icon='add' variant='primary' title='Добавить элемент' onClick={() => addItem()} />
<IconBtn icon='search' variant='secondary' title='Поиск' onClick={() => openSearch()} />
<IconBtn icon='clock' variant='ghost' title='Показать время' onClick={() => showTimeMenu()} />
<IconBtn icon='exclamation' variant='warning' size='large' title='Предупреждение' />
<IconBtn icon='dots' variant='ghost' title='Меню' onClick={() => openContextMenu()} />
<IconBtn icon='settings' variant='secondary' title='Настройки' onClick={() => openSettings()} />Основные пропсы:
variant?: 'primary' | 'secondary' | 'ghost' | 'warning'- стиль кнопки (по умолчанию 'primary')size?: 'small' | 'medium' | 'large'- размер кнопки (по умолчанию 'medium')outlined?: boolean- outlined вариант кнопки (по умолчанию false)centerSlot: SlotProps- центральный слот (ОБЯЗАТЕЛЬНЫЙ)leftSlot?: SlotProps- левый слот (опциональный)rightSlot?: SlotProps- правый слот (опциональный)className?: string- дополнительные CSS классы
SlotProps (для каждого слота):
children?: React.ReactNode- содержимое слота (текст, иконка)onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void- обработчик клика по слотуonMouseEnter?: (event: React.MouseEvent<HTMLButtonElement>) => void- обработчик наведенияonMouseLeave?: (event: React.MouseEvent<HTMLButtonElement>) => void- обработчик ухода курсораdisabled?: boolean- заблокированное состояние конкретного слота
<SlottedButton
variant="secondary"
leftSlot={{
children: "+",
onClick: () => handleAdd(),
}}
centerSlot={{
children: "Добавить в планы",
onClick: () => handleMain(),
}}
rightSlot={{
children: "▼",
onClick: () => handleDropdown(),
}}
/>
<SlottedButton
variant="primary"
outlined
leftSlot={{
children: "🔖",
onClick: () => handleBookmark(),
}}
centerSlot={{
children: "В просмотренное",
}}
rightSlot={{
children: "📋",
onClick: () => handleCopy(),
}}
/>
<SlottedButton
variant="ghost"
leftSlot={{
children: "📱",
onClick: () => alert('Icon clicked'),
}}
centerSlot={{
children: "Download App",
onClick: () => alert('Text clicked'),
}}
/>
<SlottedButton
variant="warning"
size="large"
leftSlot={{
children: "⚠️",
onClick: () => alert('Warning clicked'),
disabled: false,
}}
centerSlot={{
children: "Delete Project",
onClick: () => alert('Delete clicked'),
}}
rightSlot={{
children: "🗑️",
onClick: () => alert('Trash clicked'),
}}
/>Пропсы:
label?: string- метка поляtype?: 'text' | 'password' | 'email' | 'phone' | 'number'- тип поляvalue?: string- значение поляautoComplete?: string | boolean- автозаполнение браузера (по умолчанию true)width?: string- ширина компонентаerror?: boolean- состояние ошибкиerrorText?: string- текст ошибкиhelperText?: string- вспомогательный текстdisabled?: boolean- заблокированное состояниеonChange?: (value: string) => void- обработчик измененияclassName?: string- дополнительные CSS классы
// Базовое использование с автозаполнением
<TextField
label='Email'
type='email'
value={email}
onChange={setEmail}
error={!isValidEmail}
errorText='Введите корректный email'
/>Пропсы:
value: string- значение поиска (обязательный)onChange: (value: string) => void- обработчик изменения (обязательный)size?: 'small' | 'medium' | 'large'- размер поля (по умолчанию 'medium')placeholder?: string- placeholder текст (по умолчанию 'Поиск')...rest- все остальные HTML атрибуты input (ref, disabled, autoFocus, etc.)
Размеры:
small— min-height: 32px, font-size: 0.7remmedium— min-height: 40px, font-size: 1rem (по умолчанию)large— min-height: 48px, font-size: 1.2rem
<SearchInput
value={searchQuery}
onChange={setSearchQuery}
placeholder='Введите запрос для поиска...'
size='large' // small | medium | large
/>Пропсы:
onFilesChange: (files: File[]) => void- обработчик изменения файлов (обязательный)disabled?: boolean- отключить компонентmultiple?: boolean- разрешить множественный выбор (по умолчанию true)accept?: string- принимаемые типы файловclassName?: string- дополнительные CSS классы
<FileInput onFilesChange={setSelectedFiles} multiple accept='image/*' />Пропсы:
onEmojiSelect: (emoji: string) => void- обработчик выбора эмодзи (обязательный)align?: 'left' | 'right'- выравнивание picker'а (по умолчанию 'left')className?: string- дополнительные CSS классы
<EmojiInput onEmojiSelect={(emoji) => setMessage((prev) => prev + emoji)} align='right' />Пропсы:
onAudioRecord: (audio: Blob) => void- обработчик записи аудио (обязательный)className?: string- дополнительные CSS классы
Особенности:
- Визуальная обратная связь - анимация во время записи
- Автоматическое определение длительности - показывает время записи
- Простое управление - один клик для начала/остановки записи
- Качественная запись - использует Web Audio API
<AudioInput onAudioRecord={handleAudioRecord} />Пропсы:
value: string- текст сообщения (обязательный)onChange: (value: string) => void- обработчик изменения текста (обязательный)files: File[]- массив прикрепленных файлов (обязательный)onFilesChange: (files: File[]) => void- обработчик изменения файлов (обязательный)placeholder?: string- placeholder для текстового поля (по умолчанию 'Введите сообщение...')onSend?: () => void- обработчик отправки сообщенияonEmojiSelect?: (emoji: string) => void- дополнительный обработчик выбора эмодзи (автоматически добавляется в текст)onAudioRecord?: (audio: Blob) => void- обработчик записи аудиоdisabled?: boolean- отключить компонентclassName?: string- дополнительные CSS классы
Особенности:
- Авто-высота textarea - автоматически изменяет высоту при вводе текста
- Превью файлов - показывает миниатюры изображений и иконки для других типов файлов
- Lightbox для изображений - полноэкранный просмотр прикрепленных изображений
- Отправка по Ctrl+Enter - комбинация клавиш для быстрой отправки
- Автофокус - автоматически фокусируется на поле ввода после отправки
- Оптимизированная производительность - использует React.memo для предотвращения лишних ререндеров
<MessageInput
value={messageText}
onChange={setMessageText}
files={attachedFiles}
onFilesChange={setAttachedFiles}
placeholder='Введите сообщение...'
onSend={handleSendMessage}
onEmojiSelect={handleEmojiSelect}
onAudioRecord={handleAudioRecord}
/>Пропсы:
label?: string- метка поляvalue?: string- значение поляrows?: number- количество строкautoHeight?: boolean- автоматическая высотаdisabled?: boolean- заблокированное состояниеerror?: boolean- состояние ошибкиerrorText?: string- текст ошибкиonChange?: (value: string) => void- обработчик измененияclassName?: string- дополнительные CSS классы
<Textarea label='Комментарий' rows={4} autoHeight value={comment} onChange={setComment} />Пропсы:
checked?: boolean- состояние чекбоксаdisabled?: boolean- заблокированное состояниеonChange?: (checked: boolean) => void- обработчик измененияchildren?: React.ReactNode- текст рядом с чекбоксомclassName?: string- дополнительные CSS классы
<Checkbox checked={agreed} onChange={setAgreed}>
Согласен с условиями
</Checkbox>Пропсы:
name?: string- имя группы радио кнопокvalue?: string- значение радио кнопкиchecked?: boolean- состояние выбораdisabled?: boolean- заблокированное состояниеonChange?: (value: string) => void- обработчик измененияchildren?: React.ReactNode- текст рядом с радио кнопкойclassName?: string- дополнительные CSS классы
<Radio name='option' value='1' checked={selectedOption === '1'} onChange={setSelectedOption}>
Вариант 1
</Radio>Базовый Switch:
checked?: boolean- состояние переключателяdisabled?: boolean- заблокированное состояниеonChange?: (checked: boolean) => void- обработчик измененияclassName?: string- дополнительные CSS классы
Специальные варианты:
RollingSwitch- с анимацией каченияYinYangSwitch- в стиле инь-яньFlipSwitch- с флип анимациейPowerSwitch- кнопка питания
<Switch checked={isEnabled} onChange={setIsEnabled} />
<YinYangSwitch checked={isDarkMode} onChange={setIsDarkMode} />Пропсы:
options: SelectOption[]- массив опцийvalue?: string | number- выбранное значениеplaceholder?: string- placeholder текстdisabled?: boolean- заблокированное состояниеclearable?: boolean- возможность очисткиtrigger?: 'click' | 'hover'- способ открытияwidth?: number | string- ширина компонентаonChange?: (value: string | number | null) => void- обработчик измененияclassName?: string- дополнительные CSS классы
<Select
options={[
{
value: 'fruits',
label: 'Фрукты',
icon: '🍎',
submenu: [
{ value: 'apple', label: 'Яблоко', icon: '🍎' },
{ value: 'banana', label: 'Банан', icon: '🍌' },
],
},
]}
value={selected}
onChange={setSelected}
placeholder='Выберите категорию'
clearable
/>Пропсы:
options: DropdownOption[]- массив опцийvalue?: string- выбранное значениеlabel?: string- метка поляdisabled?: boolean- заблокированное состояниеclearable?: boolean- возможность очисткиerror?: boolean- состояние ошибкиerrorText?: string- текст ошибкиonChange?: (value: string | null) => void- обработчик измененияclassName?: string- дополнительные CSS классы
<Dropdown
options={[
{ value: '1', label: 'Опция 1' },
{ value: '2', label: 'Опция 2' },
]}
value={selected}
onChange={setSelected}
label='Выберите опцию'
clearable
/>Пропсы:
checked?: boolean- состояние переключателяdisabled?: boolean- заблокированное состояниеsize?: 'small' | 'medium' | 'large'- размер кнопкиshowIcon?: boolean- показывать иконку огонькаname?: string- имя для группировки (radio режим)value?: string- значение для radio режимаonChange?: (checked: boolean, value?: string) => void- обработчик измененияchildren?: React.ReactNode- текст кнопкиclassName?: string- дополнительные CSS классы
<ToggleButton checked={isActive} onChange={setIsActive} showIcon>
Уведомления
</ToggleButton>Пропсы:
sections: SidebarSection[]- массив секцийselected?: string- выбранный элементonSelect?: (key: string) => void- обработчик выбораclassName?: string- дополнительные CSS классы
<SidebarList
sections={[
{
title: 'Навигация',
items: [
{ key: 'home', label: 'Главная' },
{ key: 'about', label: 'О нас' },
],
},
]}
selected='home'
onSelect={handleSelect}
/>Пропсы:
src?: string- URL изображенияalt?: string- альтернативный текст для изображенияname?: string- имя пользователя (для инициалов)size?: 'xxs' | 'sm' | 'md' | 'lg'- размер аватара (20px/32px/40px/56px)status?: 'online' | 'offline' | 'dnd' | 'invisible'- статус пользователяshowStatus?: boolean- показывать статусonStatusChange?: (status: Presence) => void- обработчик изменения статусаonClick?: () => void- обработчик клика по аватаруlanguage?: 'ru' | 'en'- язык интерфейса для статусовclassName?: string- дополнительные CSS классы
Особенности:
- Кликабельность - cursor pointer только когда есть
onClickилиonStatusChange - Приоритет обработчиков - если есть
onClick, он выполняется; если нетonClickно естьonStatusChange, открывается меню статусов - Статусы - поддержка 4 статусов с цветовыми индикаторами
- Инициалы - автоматическая генерация инициалов из имени при отсутствии изображения
<Avatar src='/avatar.jpg' name='John Doe' size='lg' status='online' showStatus />
<Avatar name='Иван Иванов' size='md' status='dnd' onStatusChange={setStatus} language='ru' />
<Avatar name='Кликабельный' onClick={() => alert('Аватар кликнут!')} />
<Avatar name='Не кликабельный' /> {/* cursor: default */}Пропсы:
value?: number | string- значение бейджаmax?: number- максимальное значение для числового бейджаvariant?: 'default' | 'message-date'- вариант отображенияdate?: Date | string | number- дата (для message-date)locale?: string- локаль для форматирования датыchildren?: React.ReactNode- элемент для прикрепления бейджаclassName?: string- дополнительные CSS классы
<Badge value={5} max={99}>
<Button>Уведомления</Button>
</Badge>Пропсы:
type: 'incoming' | 'outgoing'- тип сообщенияtext?: string- текст сообщенияtime?: string- время отправкиisRead?: boolean- прочитано ли сообщениеsender?: MessageSender- отправитель сообщения (id, name, avatar, status = 'offline', showStatus = false)reactions?: MessageReaction[]- массив реакцийreactionEmojis?: string[]- доступные эмодзи для реакцийactions?: ActionItem[]- действия в контекстном менюattachments?: MessageAttachment[]- вложенияreplyTo?: ReplyMessage- ответ на сообщениеforwardedFrom?: ForwardedUser- пересланное от пользователяcurrentUserId?: string- ID текущего пользователяshowActionsOnClick?: boolean- показывать меню при кликеonReaction?: (emoji: string) => void- обработчик реакцииonReplyClick?: (messageId: string) => void- обработчик клика по ответуonForwardedClick?: (userId: string) => void- обработчик клика по пересланномуclassName?: string- дополнительные CSS классы
<Message
type='incoming'
text='Привет! Как дела?'
time='12:30'
sender={{
id: '1',
name: 'John Doe',
avatar: '/avatar.jpg',
status: 'online',
showStatus: true,
}}
reactions={[
{
emoji: '👍',
users: [{ id: '2', name: 'Jane' }],
},
]}
reactionEmojis={['❤️', '😂', '👍', '🔥']}
onReaction={handleReaction}
/>Пропсы:
items?: ChatListItem[]- массив чатовloading?: boolean- состояние загрузкиskeletonCount?: number- количество skeleton элементовselectedId?: string- ID выбранного чатаonSelect?: (id: string) => void- обработчик выбора чатаclassName?: string- дополнительные CSS классы
Типы:
type MessageStatus = 'unread' | 'read' | 'error';
type Presence = 'online' | 'offline' | 'dnd' | 'invisible';
interface ChatListItem {
id: string;
avatar?: string;
name: string;
lastMessage: string;
time: string;
messageStatus?: MessageStatus;
isOutgoing?: boolean; // true - исходящее сообщение, false - входящее
status?: Presence;
unreadCount?: number; // количество непрочитанных сообщений
isTyping?: boolean; // индикатор печати пользователя
}Логика отображения статусов:
-
Исходящие сообщения (
isOutgoing: true):unread→ ✔✔ серые (собеседник не прочитал)read→ ✔✔ синие (собеседник прочитал)error→ ! красный (ошибка отправки)
-
Входящие сообщения (
isOutgoing: false):unread→ текст синий + жирный (я не прочитал)read→ текст обычный серый (я прочитал)error→ ! красный (ошибка получения)
Индикатор печати:
- Показывает анимированные точки когда пользователь печатает (
isTyping: true) - Заменяет последнее сообщение во время печати
- Скрывает галочки статуса и бейдж непрочитанных во время печати
- Анимация с задержкой для каждой точки создает эффект волны
<ChatList
items={[
{
id: '1',
name: 'Алиса',
lastMessage: 'Привет!',
time: '12:30',
messageStatus: 'read',
isOutgoing: true, // моё сообщение, прочитанное
status: 'online',
unreadCount: 3, // количество непрочитанных сообщений
},
{
id: '2',
name: 'Максим',
lastMessage: 'Как дела?',
time: '12:25',
messageStatus: 'unread',
isOutgoing: false, // входящее непрочитанное
status: 'offline',
},
{
id: '3',
name: 'Иван',
lastMessage: '',
time: 'сейчас',
messageStatus: 'read',
isOutgoing: false,
status: 'online',
isTyping: true, // показывает индикатор печати
},
]}
selectedId={selectedChatId}
onSelect={setSelectedChatId}
loading={isLoading}
/>Пропсы:
items: ActionGroup[] | ActionItem[]- элементы менюonClose?: () => void- обработчик закрытияclassName?: string- дополнительные CSS классы
<ActionsMenu
items={[
{
label: 'Редактировать',
icon: <EditIcon />,
onClick: handleEdit,
shortcut: '⌘E',
type: 'primary',
},
{
label: 'Удалить',
icon: <DeleteIcon />,
onClick: handleDelete,
type: 'destructive',
},
]}
onClose={() => setMenuOpen(false)}
/>Пропсы:
emoji: string- эмодзи реакцииusers: ReactionUser[]- пользователи, поставившие реакциюcurrentUserId?: string- ID текущего пользователяonClick?: (event: React.MouseEvent) => void- обработчик кликаclassName?: string- дополнительные CSS классы
<Reaction
emoji='👍'
users={[
{ id: '1', name: 'John', avatar: '/avatar1.jpg' },
{ id: '2', name: 'Jane', avatar: '/avatar2.jpg' },
]}
currentUserId='1'
onClick={handleReactionClick}
/>Пропсы:
type?: 'success' | 'info' | 'warning' | 'error'- тип уведомленияoutlined?: boolean- контурный стильchildren: React.ReactNode- содержимое уведомленияclassName?: string- дополнительные CSS классы
Особенности:
- Цветной фон - фон в цвет уведомления (или контур при
outlined) - Иконка - соответствующая иконка для каждого типа
- Компактный дизайн - идеально для коротких сообщений
<Alert type='success' outlined>
<strong>Успешно!</strong> Данные сохранены.
</Alert>Пропсы:
type: 'success' | 'info' | 'warning' | 'error'- тип уведомления (обязательный)title?: string- заголовок уведомленияdescription?: string- описание уведомленияchildren?: React.ReactNode- дополнительный контентclassName?: string- дополнительные CSS классы
Особенности:
- Левая граница - цветная граница слева в соответствии с типом
- Светлый фон - нежный фон с оттенком цвета уведомления
- Структурированный контент - заголовок, описание и дополнительный контент
- Гибкость - можно использовать только заголовок, только описание или только children
<AlertWithBorder
type="success"
title="Успешное выполнение"
description="Операция была выполнена успешно. Все данные сохранены в системе."
>
Дополнительная информация может быть размещена здесь.
</AlertWithBorder>
<AlertWithBorder
type="error"
title="Ошибка"
description="Произошла ошибка при обработке запроса."
/>
<AlertWithBorder type="info" title="Только заголовок" />
<AlertWithBorder type="warning" description="Только описание" />
<AlertWithBorder type="success">Только контент</AlertWithBorder>Пропсы:
isOpen: boolean- состояние открытияonClose: () => void- обработчик закрытияtitle?: string- заголовок модального окнаsize?: 'small' | 'medium' | 'large'- размер окнаshowCloseButton?: boolean- показывать кнопку закрытияcloseable?: boolean- возможность закрытияcloseOnBackdropClick?: boolean- закрытие при клике на фонcloseOnEscape?: boolean- закрытие по Escapechildren: React.ReactNode- содержимое модального окнаclassName?: string- дополнительные CSS классы
<Modal isOpen={isOpen} onClose={handleClose} title='Настройки' size='medium'>
<p>Содержимое модального окна</p>
</Modal>Пропсы:
isOpen: boolean- состояние открытияonClose: () => void- обработчик закрытияonSubmit: () => void | Promise<void>- обработчик отправкиtitle: string- заголовок модального окнаsubmitText?: string- текст кнопки подтвержденияcancelText?: string- текст кнопки отменыsubmitVariant?: 'primary' | 'warning'- вариант кнопки подтвержденияisLoading?: boolean- состояние загрузкиdisabled?: boolean- заблокированное состояниеsize?: 'small' | 'medium' | 'large'- размер окнаpreventCloseOnSubmit?: boolean- не закрывать после отправкиchildren: React.ReactNode- содержимое формыclassName?: string- дополнительные CSS классы
<ModalSubmit
isOpen={isOpen}
onSubmit={handleSubmit}
onClose={handleClose}
title='Создать элемент'
isLoading={isSubmitting}
>
<TextField label='Название' />
</ModalSubmit>Пропсы:
message: string- текст уведомленияtype?: 'success' | 'info' | 'warning' | 'error'- тип уведомленияisVisible: boolean- видимость уведомленияduration?: number- длительность показа (мс)onClose?: () => void- обработчик закрытияclassName?: string- дополнительные CSS классы
<Snackbar
message='Операция выполнена успешно'
type='success'
isVisible={showSnackbar}
onClose={hideSnackbar}
/>Пропсы:
message: string- текст уведомленияisVisible: boolean- видимость уведомленияundoText?: string- текст кнопки отменыduration?: number- длительность показа (мс)onUndo?: () => void- обработчик отменыonClose?: () => void- обработчик закрытияclassName?: string- дополнительные CSS классы
<UndoSnackbar
message='Элемент удален'
onUndo={handleUndo}
isVisible={showUndo}
onClose={hideUndo}
/>Пропсы:
variant?: 'spinner' | 'dots' | 'pulse' | 'bars' | 'wave'- тип анимацииsize?: 'small' | 'medium' | 'large'- размер индикатораcolor?: string- цвет индикатораclassName?: string- дополнительные CSS классы
<LoadingSpinner variant='dots' size='large' />Пропсы:
value: number- значение прогресса (0-100)variant?: 'linear' | 'circular'- тип прогрессаsize?: 'small' | 'medium' | 'large'- размер (для circular)showLabel?: boolean- показывать процентclassName?: string- дополнительные CSS классы
<Progress value={75} variant="linear" />
<Progress value={60} variant="circular" showLabel />Пропсы:
variant?: 'text' | 'rectangular' | 'circular' | 'card'- тип скелетонаwidth?: number | string- ширинаheight?: number | string- высотаlines?: number- количество строк (для text)className?: string- дополнительные CSS классы
<Skeleton variant="text" lines={3} />
<Skeleton variant="circular" width={40} height={40} />Пропсы:
items: BreadcrumbItem[]- массив элементов навигацииseparator?: string- разделитель между элементамиclassName?: string- дополнительные CSS классы
<Breadcrumbs
items={[
{ label: 'Главная', href: '/' },
{ label: 'Каталог', href: '/catalog' },
{ label: 'Товар' },
]}
/>Пропсы:
current: number- текущая страницаtotal: number- общее количество страницpageSize?: number- размер страницыshowSizeChanger?: boolean- показывать селектор размераonChange?: (page: number, pageSize?: number) => void- обработчик измененияclassName?: string- дополнительные CSS классы
<Pagination current={currentPage} total={totalPages} onChange={handlePageChange} showSizeChanger />Пропсы:
tabs: TabData[]- массив вкладокselectedId: string | number- ID выбранной вкладкиonTabPress: (id: string | number) => void- обработчик выбора вкладкиclassName?: string- дополнительные CSS классы
<Tabbar
tabs={[
{ id: 'tab1', label: 'Вкладка 1', notification: 5 },
{ id: 'tab2', label: 'Вкладка 2' },
]}
selectedId='tab1'
onTabPress={handleTabPress}
/>Пропсы:
tab: TabData- данные вкладкиselected: boolean- состояние выбораonClick: (id: string | number) => void- обработчик кликаclassName?: string- дополнительные CSS классы
<Tab tab={{ id: 1, label: 'Home', notification: 3 }} selected={false} onClick={handleTabClick} />Пропсы:
left?: React.ReactNode- левый слотcenter?: React.ReactNode- центральный слотright?: React.ReactNode- правый слотcenterJustify?: 'left' | 'center' | 'right'- горизонтальное выравнивание содержимого центра (по умолчанию 'left')padding?: string- внутренние отступыminHeight?: string- минимальная высотаonClick?: () => void- обработчик кликаclassName?: string- дополнительные CSS классы
<Row
left={<Avatar name='John Doe' />}
center={<span>Имя пользователя</span>}
right={<Button variant='ghost'>Действие</Button>}
centerJustify='center'
onClick={() => navigate('/profile')}
/>Пропсы:
title?: string- заголовок карточкиsubtitle?: string- подзаголовок карточкиfooter?: React.ReactNode- футер карточкиvariant?: 'default' | 'outlined' | 'elevated' | 'flat'- вариант отображенияclickable?: boolean- кликабельная карточкаwidth?: string- ширина карточкиmaxWidth?: string- максимальная ширинаonClick?: () => void- обработчик кликаchildren: React.ReactNode- содержимое карточкиclassName?: string- дополнительные CSS классы
<Card
title='Продукт'
subtitle='Описание продукта'
variant='elevated'
clickable
onClick={() => navigate('/product')}
footer={<Button variant='primary'>Купить</Button>}
>
Детальная информация о продукте
</Card>Пропсы:
stretched?: boolean- растянуть на всю ширинуleft?: React.ReactNode- левая боковая панельright?: React.ReactNode- правая боковая панельchildren: React.ReactNode- основной контентclassName?: string- дополнительные CSS классы
<PageBlock left={<SidebarContent />} right={<AdditionalInfo />} stretched>
<MainContent />
</PageBlock>Пропсы:
children: React.ReactNode- контент для порталаcontainer?: HTMLElement | string- контейнер для порталаdisabled?: boolean- отключить порталclassName?: string- дополнительные CSS классы
<Portal container='#modal-root'>
<ModalContent />
</Portal>DobruniaUI использует гибридную систему дизайн токенов для оптимальной производительности:
Цветовые токены применяются динамически через JavaScript для поддержки системы тем:
/* Нейтральные поверхности */
--c-bg-default: ; /* основной фон */
--c-bg-subtle: ; /* слегка приподнятые блоки */
--c-bg-elevated: ; /* модальные окна, выпадающие списки */
/* Текст */
--c-text-primary: ; /* основной текст */
--c-text-secondary: ; /* вторичный текст */
--c-text-inverse: ; /* текст на тёмных/акцентных кнопках */
/* Границы */
--c-border: ; /* обычные границы */
--c-border-focus: ; /* границы в фокусе */
/* Акцент */
--c-accent: ; /* основной акцентный цвет */
--c-accent-hover: ; /* hover состояние */
--c-accent-active: ; /* active состояние */
/* Семантические цвета */
--c-success: ; /* успех */
--c-error: ; /* ошибка */
--c-warning: ; /* предупреждение */
--c-info: ; /* информация */Статические значения определены в JavaScript для оптимальной производительности:
import { DESIGN_TOKENS, BREAKPOINTS, RESPONSIVE_TOKENS } from 'dobruniaui';
// Отступы
DESIGN_TOKENS.spacing.tiny; // 0.2rem
DESIGN_TOKENS.spacing.small; // 0.5rem
DESIGN_TOKENS.spacing.medium; // 1rem
DESIGN_TOKENS.spacing.large; // 2rem
// Радиусы скругления
DESIGN_TOKENS.radius.small; // 4px
DESIGN_TOKENS.radius.medium; // 6px
DESIGN_TOKENS.radius.large; // 16px
// Размеры шрифтов
DESIGN_TOKENS.fontSize.small; // 0.7rem
DESIGN_TOKENS.fontSize.smallPlus; // 0.8rem
DESIGN_TOKENS.fontSize.medium; // 1rem
DESIGN_TOKENS.fontSize.large; // 1.2rem
// Высоты компонентов
DESIGN_TOKENS.baseHeight.tiny; // 20px
DESIGN_TOKENS.baseHeight.small; // 32px
DESIGN_TOKENS.baseHeight.medium; // 40px
DESIGN_TOKENS.baseHeight.large; // 48px
DESIGN_TOKENS.baseHeight.extraLarge; // 56px
// Переходы
DESIGN_TOKENS.transition.fast; // 0.15s
DESIGN_TOKENS.transition.slow; // 0.3s
// Макеты
DESIGN_TOKENS.layout.content.desktop; // 1200px
DESIGN_TOKENS.layout.content.tablet; // 1000px
DESIGN_TOKENS.layout.content.mobile; // 100vw
DESIGN_TOKENS.layout.sidebar.desktop; // 300px
DESIGN_TOKENS.layout.sidebar.tablet; // 220px
DESIGN_TOKENS.layout.sidebar.mobile; // 160px
// Брейкпоинты
BREAKPOINTS.mobile; // 900px
BREAKPOINTS.tablet; // 1200px
// Адаптивные токены
RESPONSIVE_TOKENS.tablet.layout.contentWidth; // 1000px
RESPONSIVE_TOKENS.tablet.layout.sidebarWidth; // 220px
RESPONSIVE_TOKENS.mobile.layout.contentWidth; // 100vw
RESPONSIVE_TOKENS.mobile.layout.sidebarWidth; // 160px# Клонировать репозиторий
git clone https://github.com/Dobrunia/DobruniaUI.git
# Установить зависимости
npm install
# Запустить dev server
npm run dev
# Собрать библиотеку
npm run build
# Собрать демо
npm run build:demo- Fork репозитория
- Создайте feature branch (
git checkout -b feature/amazing-component) - Commit изменения (
git commit -m 'Add amazing component') - Push в branch (
git push origin feature/amazing-component) - Создайте Pull Request
MIT © Dobrunia