Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m54s
555 lines
15 KiB
Markdown
555 lines
15 KiB
Markdown
# 🎨 UX/UI Улучшения - Документация
|
||
|
||
Этот документ описывает все внедренные UX/UI улучшения в проект S3 Lists Manager.
|
||
|
||
## 📋 Содержание
|
||
|
||
1. [CSS Улучшения](#css-улучшения)
|
||
2. [Новые Компоненты](#новые-компоненты)
|
||
3. [Улучшенные Компоненты](#улучшенные-компоненты)
|
||
4. [Примеры Использования](#примеры-использования)
|
||
|
||
---
|
||
|
||
## CSS Улучшения
|
||
|
||
### Semantic Colors
|
||
|
||
Добавлена система семантических цветов для статусов и акцентов:
|
||
|
||
```css
|
||
--status-online: #2fb344;
|
||
--status-offline: #d63939;
|
||
--status-warning: #f59f00;
|
||
--status-unknown: #868e96;
|
||
|
||
--accent-primary: #206bc4;
|
||
--accent-danger: #d63939;
|
||
--accent-success: #2fb344;
|
||
```
|
||
|
||
### Улучшенная Типографика
|
||
|
||
- Увеличенный размер заголовков с лучшей читаемостью
|
||
- Оптимизированная межстрочная высота
|
||
- Letter-spacing для заголовков
|
||
|
||
### Enhanced Spacing
|
||
|
||
- Единая система отступов через CSS переменные
|
||
- Увеличенные padding в таблицах для лучшей читаемости
|
||
- Consistent spacing между карточками
|
||
|
||
### Интерактивные Элементы
|
||
|
||
- **Hover states**: кнопки поднимаются на 1px с тенью
|
||
- **Active states**: визуальный feedback при нажатии
|
||
- **Focus states**: улучшенные для accessibility
|
||
- **Touch targets**: минимум 44x44px на мобильных
|
||
|
||
---
|
||
|
||
## Новые Компоненты
|
||
|
||
### 1. Tooltip
|
||
|
||
Компонент для отображения подсказок с поддержкой keyboard shortcuts.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import Tooltip from './components/Tooltip.jsx'
|
||
|
||
<Tooltip content="Сохранить изменения" shortcut="Ctrl+S" position="top">
|
||
<button className="btn btn-primary">
|
||
<IconDeviceFloppy /> Сохранить
|
||
</button>
|
||
</Tooltip>
|
||
```
|
||
|
||
**Props:**
|
||
- `content` (string) - текст подсказки
|
||
- `shortcut` (string) - keyboard shortcut для отображения
|
||
- `position` (string) - позиция: 'top', 'bottom', 'left', 'right'
|
||
- `delay` (number) - задержка перед показом (ms)
|
||
|
||
---
|
||
|
||
### 2. TrendIndicator
|
||
|
||
Компонент для отображения изменения метрик со стрелками вверх/вниз.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import TrendIndicator from './components/TrendIndicator.jsx'
|
||
|
||
<TrendIndicator
|
||
value={1250}
|
||
previousValue={1180}
|
||
format="number" // или "percent"
|
||
inverse={false} // true если рост = плохо
|
||
/>
|
||
```
|
||
|
||
**Props:**
|
||
- `value` (number) - текущее значение
|
||
- `previousValue` (number) - предыдущее значение
|
||
- `format` ('number' | 'percent') - формат отображения
|
||
- `inverse` (boolean) - инвертировать цвета (рост = красный)
|
||
|
||
---
|
||
|
||
### 3. LastSaved
|
||
|
||
Компонент для отображения времени последнего сохранения с автообновлением.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import LastSaved from './components/LastSaved.jsx'
|
||
|
||
<LastSaved
|
||
timestamp="2025-01-15T10:30:00Z"
|
||
variant="compact" // 'default', 'badge', 'compact'
|
||
/>
|
||
```
|
||
|
||
**Варианты:**
|
||
- `default` - полный вид с иконкой
|
||
- `badge` - зеленый badge "Сохранено X мин назад"
|
||
- `compact` - компактный вид для toolbar
|
||
|
||
---
|
||
|
||
### 4. ContextMenu
|
||
|
||
Компонент контекстного меню для действий с поддержкой правого клика.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import ContextMenu from './components/ContextMenu.jsx'
|
||
import { IconEdit, IconTrash, IconCopy } from '@tabler/icons-react'
|
||
|
||
<ContextMenu
|
||
items={[
|
||
{ label: 'Редактировать', icon: IconEdit, onClick: handleEdit },
|
||
{ label: 'Копировать', icon: IconCopy, onClick: handleCopy },
|
||
{ divider: true },
|
||
{ label: 'Удалить', icon: IconTrash, onClick: handleDelete, variant: 'danger' }
|
||
]}
|
||
align="right"
|
||
>
|
||
{/* Trigger element или оставить пустым для кнопки с тремя точками */}
|
||
</ContextMenu>
|
||
```
|
||
|
||
---
|
||
|
||
### 5. ValidatedInput
|
||
|
||
Input с real-time валидацией и визуальным feedback.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import ValidatedInput from './components/ValidatedInput.jsx'
|
||
|
||
const validateDomain = (value) => {
|
||
const valid = /^([a-z0-9-]+\.)+[a-z]{2,}$/i.test(value)
|
||
return {
|
||
valid,
|
||
message: valid ? 'Домен валиден' : 'Введите корректный домен (example.com)'
|
||
}
|
||
}
|
||
|
||
<ValidatedInput
|
||
value={domain}
|
||
onChange={(e) => setDomain(e.target.value)}
|
||
validate={validateDomain}
|
||
label="Домен"
|
||
placeholder="example.com"
|
||
hint="Введите доменное имя без http://"
|
||
required
|
||
debounce={300}
|
||
/>
|
||
```
|
||
|
||
**Props:**
|
||
- `validate` (function) - функция валидации
|
||
- `debounce` (number) - задержка перед валидацией
|
||
- `showValidIcon` (boolean) - показывать иконку валидации
|
||
- `validateOnChange` (boolean) - валидировать при вводе или только при blur
|
||
|
||
---
|
||
|
||
### 6. ProgressBar & MultiStepProgress
|
||
|
||
Компоненты для отображения прогресса операций.
|
||
|
||
**ProgressBar:**
|
||
|
||
```jsx
|
||
import ProgressBar from './components/ProgressBar.jsx'
|
||
|
||
<ProgressBar
|
||
progress={65}
|
||
status="Загрузка данных..."
|
||
estimatedTime={45} // секунды
|
||
variant="primary"
|
||
striped
|
||
animated
|
||
/>
|
||
```
|
||
|
||
**MultiStepProgress:**
|
||
|
||
```jsx
|
||
import { MultiStepProgress } from './components/ProgressBar.jsx'
|
||
|
||
<MultiStepProgress
|
||
steps={['Валидация', 'Обработка', 'Сохранение', 'Завершение']}
|
||
currentStep={1} // текущий шаг (0-indexed)
|
||
variant="success"
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### 7. MobileCardView
|
||
|
||
Компонент для отображения таблиц в виде карточек на мобильных с swipe gestures.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import MobileCardView from './components/MobileCardView.jsx'
|
||
|
||
<MobileCardView
|
||
items={domains}
|
||
onItemClick={(item) => console.log('Clicked:', item)}
|
||
renderContent={(item) => (
|
||
<div>
|
||
<div className="fw-bold">{item.domain}</div>
|
||
<div className="text-muted small">{item.community}</div>
|
||
</div>
|
||
)}
|
||
actions={[
|
||
{ label: 'Редактировать', icon: IconEdit, onClick: handleEdit },
|
||
{ label: 'Удалить', icon: IconTrash, onClick: handleDelete, variant: 'danger' }
|
||
]}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### 8. KeyboardShortcutHint
|
||
|
||
Компонент для отображения горячих клавиш.
|
||
|
||
**Использование:**
|
||
|
||
```jsx
|
||
import KeyboardShortcutHint, { ShortcutsList } from './components/KeyboardShortcutHint.jsx'
|
||
|
||
// Внутри кнопки:
|
||
<button className="btn btn-primary">
|
||
Сохранить
|
||
<KeyboardShortcutHint shortcut="Ctrl + S" />
|
||
</button>
|
||
|
||
// Список горячих клавиш:
|
||
<ShortcutsList
|
||
shortcuts={[
|
||
{ description: 'Сохранить изменения', keys: 'Ctrl + S' },
|
||
{ description: 'Отменить', keys: 'Ctrl + Z' },
|
||
{ description: 'Поиск', keys: 'Ctrl + F' }
|
||
]}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## Улучшенные Компоненты
|
||
|
||
### ErrorAlert
|
||
|
||
Добавлены новые возможности:
|
||
- Кнопка "Копировать ошибку" для bug reports
|
||
- Кнопка "Повторить" для retry операций
|
||
- Раскрывающиеся детали ошибки
|
||
- Отображение кода ошибки
|
||
|
||
**Новое использование:**
|
||
|
||
```jsx
|
||
<ErrorAlert
|
||
message="Не удалось загрузить данные"
|
||
error={{ code: 'E_NETWORK', details: errorDetails }}
|
||
onRetry={fetchData}
|
||
onClose={() => setError(null)}
|
||
showDetails={true}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### EmptyState
|
||
|
||
Добавлены:
|
||
- Поддержка иллюстраций
|
||
- Различные размеры (small, default, large)
|
||
- Варианты цветов (success, info, warning)
|
||
- Анимация иллюстраций
|
||
|
||
**Новое использование:**
|
||
|
||
```jsx
|
||
<EmptyState
|
||
icon={IconDatabase}
|
||
title="Нет доменов"
|
||
description="Начните с добавления первого домена"
|
||
size="large"
|
||
variant="info"
|
||
illustration="/illustrations/empty-state.svg"
|
||
action={<button className="btn btn-primary">Добавить домен</button>}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
### Pagination
|
||
|
||
Улучшения:
|
||
- Jump to Page для быстрого перехода (показывается при > 10 страниц)
|
||
- Иконки вместо текста для навигации
|
||
- Улучшенная accessibility
|
||
- Адаптивность для мобильных
|
||
|
||
**Автоматически используется везде, где был старый Pagination**
|
||
|
||
---
|
||
|
||
### Dashboard
|
||
|
||
Улучшения:
|
||
- StatCard теперь поддерживает trends
|
||
- Добавлен LastSaved в header
|
||
- Hover эффекты на карточках (card-hover)
|
||
- Tooltips на кнопках
|
||
|
||
**Изменения в коде:**
|
||
|
||
```jsx
|
||
<StatCard
|
||
icon={IconWorld}
|
||
value={stats.domainsCount}
|
||
title="Доменов"
|
||
to="/domains"
|
||
trend={true}
|
||
previousValue={previousStats?.domainsCount}
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## Примеры Использования
|
||
|
||
### Пример 1: Форма с валидацией
|
||
|
||
```jsx
|
||
function AddDomainForm() {
|
||
const [domain, setDomain] = useState('')
|
||
const [community, setCommunity] = useState('')
|
||
|
||
const validateDomain = (value) => ({
|
||
valid: /^([a-z0-9-]+\.)+[a-z]{2,}$/i.test(value),
|
||
message: 'Введите корректный домен'
|
||
})
|
||
|
||
const validateCommunity = (value) => ({
|
||
valid: /^\d+$/.test(value),
|
||
message: 'Введите числовой community'
|
||
})
|
||
|
||
return (
|
||
<form>
|
||
<ValidatedInput
|
||
value={domain}
|
||
onChange={(e) => setDomain(e.target.value)}
|
||
validate={validateDomain}
|
||
label="Домен"
|
||
placeholder="example.com"
|
||
required
|
||
/>
|
||
|
||
<ValidatedInput
|
||
value={community}
|
||
onChange={(e) => setCommunity(e.target.value)}
|
||
validate={validateCommunity}
|
||
label="Community"
|
||
placeholder="65000"
|
||
required
|
||
/>
|
||
|
||
<button type="submit" className="btn btn-primary">
|
||
Добавить
|
||
<KeyboardShortcutHint shortcut="Ctrl + Enter" />
|
||
</button>
|
||
</form>
|
||
)
|
||
}
|
||
```
|
||
|
||
### Пример 2: Таблица с ContextMenu и MobileCardView
|
||
|
||
```jsx
|
||
function DomainsTable({ domains, onEdit, onDelete }) {
|
||
return (
|
||
<>
|
||
{/* Desktop view */}
|
||
<div className="table-responsive d-none d-md-block">
|
||
<table className="table">
|
||
<tbody>
|
||
{domains.map(domain => (
|
||
<tr key={domain.id}>
|
||
<td>{domain.name}</td>
|
||
<td>{domain.community}</td>
|
||
<td>
|
||
<ContextMenu
|
||
items={[
|
||
{ label: 'Редактировать', icon: IconEdit, onClick: () => onEdit(domain) },
|
||
{ label: 'Удалить', icon: IconTrash, onClick: () => onDelete(domain), variant: 'danger' }
|
||
]}
|
||
/>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
{/* Mobile view */}
|
||
<MobileCardView
|
||
items={domains}
|
||
renderContent={(domain) => (
|
||
<div>
|
||
<div className="fw-bold">{domain.name}</div>
|
||
<div className="text-muted small">{domain.community}</div>
|
||
</div>
|
||
)}
|
||
actions={[
|
||
{ label: 'Редактировать', icon: IconEdit, onClick: onEdit },
|
||
{ label: 'Удалить', icon: IconTrash, onClick: onDelete, variant: 'danger' }
|
||
]}
|
||
/>
|
||
</>
|
||
)
|
||
}
|
||
```
|
||
|
||
### Пример 3: Dashboard с трендами
|
||
|
||
```jsx
|
||
function Dashboard() {
|
||
const [stats, setStats] = useState({})
|
||
const [previousStats, setPreviousStats] = useState(null)
|
||
const [lastFetchTime, setLastFetchTime] = useState(null)
|
||
|
||
return (
|
||
<div>
|
||
<PageHeader
|
||
title="Панель"
|
||
actions={
|
||
<div className="d-flex gap-3">
|
||
<LastSaved timestamp={lastFetchTime} variant="compact" />
|
||
<Tooltip content="Обновить данные" shortcut="F5">
|
||
<button className="btn btn-outline-primary">
|
||
<IconRefresh /> Обновить
|
||
</button>
|
||
</Tooltip>
|
||
</div>
|
||
}
|
||
/>
|
||
|
||
<div className="row g-3">
|
||
<div className="col-md-3">
|
||
<StatCard
|
||
icon={IconWorld}
|
||
value={stats.domains}
|
||
title="Доменов"
|
||
trend={true}
|
||
previousValue={previousStats?.domains}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Best Practices
|
||
|
||
### 1. Accessibility
|
||
|
||
- Всегда добавляйте `aria-label` для иконочных кнопок
|
||
- Используйте semantic HTML
|
||
- Обеспечьте минимум 44x44px для touch targets на мобильных
|
||
- Добавляйте keyboard shortcuts для частых действий
|
||
|
||
### 2. Performance
|
||
|
||
- Используйте `useMemo` для тяжелых вычислений
|
||
- Debounce для валидации форм
|
||
- Lazy loading для больших списков
|
||
- Virtualization для таблиц > 1000 записей
|
||
|
||
### 3. User Experience
|
||
|
||
- Показывайте loading states для всех async операций
|
||
- Предоставляйте clear error messages с действиями
|
||
- Используйте optimistic updates где возможно
|
||
- Сохраняйте состояние форм при навигации
|
||
|
||
### 4. Mobile First
|
||
|
||
- Используйте MobileCardView для таблиц
|
||
- Убедитесь что все элементы кликабельны на touch screens
|
||
- Тестируйте swipe gestures
|
||
- Адаптируйте модальные окна для мобильных
|
||
|
||
---
|
||
|
||
## Roadmap
|
||
|
||
Планируемые улучшения:
|
||
|
||
- [ ] Виртуальный скроллинг для больших таблиц
|
||
- [ ] Drag & Drop для изменения порядка
|
||
- [ ] Сохранение пользовательских настроек
|
||
- [ ] Dark mode improvements
|
||
- [ ] Анимированные transitions между страницами
|
||
- [ ] PWA support
|
||
- [ ] Offline mode
|
||
|
||
---
|
||
|
||
## Вклад
|
||
|
||
При добавлении новых компонентов:
|
||
|
||
1. Следуйте существующим паттернам
|
||
2. Добавляйте JSDoc комментарии
|
||
3. Обеспечьте accessibility
|
||
4. Тестируйте на мобильных
|
||
5. Обновляйте эту документацию
|
||
|
||
---
|
||
|
||
**Версия:** 1.0.0
|
||
**Последнее обновление:** 2025-01-15
|
||
|