docs: обновление правил UI Kit и блока поставщиков + новые glass компоненты
- Добавлены правила анализа UI Kit компонентов (interaction-integrity-rules.md) - Обновлены правила блока 1: убран заголовок, добавлена кнопка навигации (rules-complete.md) - Детальная спецификация плавающей кнопки "Назад" (seller-ui-rules.md) - Новые компоненты: GlassDatePicker и GlassSelect для UI Kit - Улучшены hover/focus эффекты в DatePicker 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -139,6 +139,52 @@ ignores: ['diagnostic-script.js', 'legacy-config.js'] // конкретные ф
|
|||||||
- Планирую создать новые файлы вместо редактирования существующих
|
- Планирую создать новые файлы вместо редактирования существующих
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 1.5 🎯 ПРАВИЛА АНАЛИЗА ИСХОДНЫХ ПРИМЕРОВ
|
||||||
|
|
||||||
|
**КРИТИЧЕСКИ ВАЖНО**: При работе с существующими примерами кода или UI Kit компонентами:
|
||||||
|
|
||||||
|
#### 🔍 **ОБЯЗАТЕЛЬНЫЙ ТРЕХУРОВНЕВЫЙ АНАЛИЗ:**
|
||||||
|
|
||||||
|
1. **📋 СОДЕРЖАТЕЛЬНЫЙ АНАЛИЗ** (что делает код):
|
||||||
|
- Функциональность компонента
|
||||||
|
- Логика работы
|
||||||
|
- Данные и состояния
|
||||||
|
|
||||||
|
2. **🏗️ АРХИТЕКТУРНЫЙ АНАЛИЗ** (как организован код):
|
||||||
|
- Структура компонентов и контейнеров
|
||||||
|
- Взаимосвязи между элементами
|
||||||
|
- Позиционирование и layout
|
||||||
|
- Иерархия DOM-элементов
|
||||||
|
|
||||||
|
3. **🎨 СТИЛЕВОЙ АНАЛИЗ** (как выглядит код):
|
||||||
|
- CSS классы и стили
|
||||||
|
- Анимации и переходы
|
||||||
|
- Цвета и размеры
|
||||||
|
|
||||||
|
#### ❌ **ТИПИЧНЫЕ ОШИБКИ АНАЛИЗА:**
|
||||||
|
|
||||||
|
- **Поверхностный анализ**: Копирование только стилей без понимания архитектуры
|
||||||
|
- **Игнорирование контекста**: Непонимание места элемента в общей структуре
|
||||||
|
- **Буквальное копирование**: Применение решения без адаптации к текущей задаче
|
||||||
|
|
||||||
|
#### ✅ **ПРАВИЛЬНЫЙ ПОДХОД:**
|
||||||
|
|
||||||
|
```
|
||||||
|
🔬 АЛГОРИТМ АНАЛИЗА ПРИМЕРА:
|
||||||
|
1. Прочитать ВЕСЬ код компонента-примера
|
||||||
|
2. Понять АРХИТЕКТУРУ: где элемент размещен относительно других
|
||||||
|
3. Понять ЛОГИКУ: почему именно так структурировано
|
||||||
|
4. Адаптировать к ТЕКУЩЕЙ ЗАДАЧЕ: применить принципы, а не просто скопировать
|
||||||
|
5. Проверить СООТВЕТСТВИЕ правилам проекта
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 🚨 **СТОП-ВОПРОСЫ ПЕРЕД РЕАЛИЗАЦИЕЙ:**
|
||||||
|
|
||||||
|
- "Понимаю ли я **архитектуру** этого решения?"
|
||||||
|
- "Где именно должен располагаться элемент в **общей структуре**?"
|
||||||
|
- "Какова **семантическая роль** этого элемента?"
|
||||||
|
- "Как это решение **адаптируется** к моей текущей задаче?"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔄 II. ПРОЦЕДУРНЫЙ УРОВЕНЬ: ПОСЛЕДОВАТЕЛЬНОСТИ ДЕЙСТВИЙ
|
## 🔄 II. ПРОЦЕДУРНЫЙ УРОВЕНЬ: ПОСЛЕДОВАТЕЛЬНОСТИ ДЕЙСТВИЙ
|
||||||
@ -563,9 +609,69 @@ ignores: ['diagnostic-script.js', 'legacy-config.js'] // конкретные ф
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 📚 VIII. ИЗВЛЕЧЕННЫЕ УРОКИ И АНТИ-ПАТТЕРНЫ
|
||||||
|
|
||||||
|
### 8.1 🚨 КРИТИЧЕСКИЕ ОШИБКИ В АНАЛИЗЕ UI КОМПОНЕНТОВ
|
||||||
|
|
||||||
|
#### **CASE STUDY: Ошибка с плавающей кнопкой из UI Kit**
|
||||||
|
|
||||||
|
**❌ ОШИБКА**: При добавлении кнопки "🌟 Вариант 1: Плавающая кнопка слева" из UI Kit:
|
||||||
|
|
||||||
|
1. **Поверхностный анализ**: Скопировал только стили кнопки
|
||||||
|
2. **Игнорирование архитектуры**: Не заметил, что кнопка в **отдельном контейнере**
|
||||||
|
3. **Неправильное размещение**: Добавил как часть блока контента
|
||||||
|
4. **Непонимание термина**: "Плавающая" = независимая от контента, между элементами
|
||||||
|
|
||||||
|
**✅ ПРАВИЛЬНОЕ РЕШЕНИЕ**:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// ❌ НЕПРАВИЛЬНО - кнопка внутри блока контента
|
||||||
|
<div className="content-block relative">
|
||||||
|
<button className="absolute...">Назад</button>
|
||||||
|
<div className="actual-content">...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
// ✅ ПРАВИЛЬНО - кнопка в отдельном контейнере
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Sidebar />
|
||||||
|
<div className="relative"> {/* Отдельный контейнер */}
|
||||||
|
<button className="absolute...">Назад</button>
|
||||||
|
</div>
|
||||||
|
<div className="content-block">...</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **📋 ОБЯЗАТЕЛЬНЫЙ ЧЕКЛИСТ ДЛЯ UI KIT КОМПОНЕНТОВ:**
|
||||||
|
|
||||||
|
```
|
||||||
|
🔍 ПЕРЕД РЕАЛИЗАЦИЕЙ:
|
||||||
|
□ Прочитал ВЕСЬ код компонента-примера
|
||||||
|
□ Понял архитектуру размещения в layout
|
||||||
|
□ Определил семантическую роль элемента
|
||||||
|
□ Понял взаимосвязи с соседними элементами
|
||||||
|
□ Адаптировал принципы к текущей задаче
|
||||||
|
□ Проверил соответствие правилам проекта
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **⚡ АНТИ-ПАТТЕРНЫ:**
|
||||||
|
|
||||||
|
- **"Быстрое копирование"** - копировать стили без понимания архитектуры
|
||||||
|
- **"Частичный анализ"** - читать только нужную часть кода
|
||||||
|
- **"Буквальное применение"** - использовать без адаптации к контексту
|
||||||
|
- **"Игнорирование контейнеров"** - не обращать внимание на DOM-структуру
|
||||||
|
|
||||||
|
#### **✅ ПРАВИЛЬНЫЕ ПАТТЕРНЫ:**
|
||||||
|
|
||||||
|
- **"Архитектурный анализ первым"** - понять структуру, потом стили
|
||||||
|
- **"Контекстная адаптация"** - применять принципы, а не код буквально
|
||||||
|
- **"Семантическое понимание"** - осознавать роль каждого элемента
|
||||||
|
- **"Итеративная проверка"** - сверяться с примером на каждом шаге
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**Дата создания**: Декабрь 2024
|
**Дата создания**: Декабрь 2024
|
||||||
**Последнее обновление**: Август 2025
|
**Последнее обновление**: Август 2025
|
||||||
**Версия**: 3.0 - ПОЛНАЯ РЕСТРУКТУРИЗАЦИЯ
|
**Версия**: 3.1 - ДОПОЛНЕНЫ ПРАВИЛА АНАЛИЗА UI KIT
|
||||||
**Статус**: АКТИВЕН - ОБЯЗАТЕЛЕН К ИСПОЛНЕНИЮ
|
**Статус**: АКТИВЕН - ОБЯЗАТЕЛЕН К ИСПОЛНЕНИЮ
|
||||||
|
|
||||||
**Связанные файлы**:
|
**Связанные файлы**:
|
||||||
|
@ -854,7 +854,8 @@ const handleSuppliesClick = () => {
|
|||||||
|
|
||||||
**БЛОК 1: ПОСТАВЩИКИ** _(адаптивная сетка)_
|
**БЛОК 1: ПОСТАВЩИКИ** _(адаптивная сетка)_
|
||||||
|
|
||||||
- **Заголовок**: Минималистичный "🏢 Поставщики" без лишних элементов
|
- **Навигация**: Плавающая кнопка "Назад" между сайдбаром и контентом (см. детали в seller-ui-rules.md)
|
||||||
|
- **Заголовок**: БЕЗ заголовка - блок начинается сразу с функционального контента
|
||||||
- **Поиск**: Компактное поле справа "Поиск поставщиков..." (w-64)
|
- **Поиск**: Компактное поле справа "Поиск поставщиков..." (w-64)
|
||||||
- **Отображение**: Карточки поставщиков из раздела "Партнеры" в адаптивной сетке
|
- **Отображение**: Карточки поставщиков из раздела "Партнеры" в адаптивной сетке
|
||||||
- **Выбор**: Клик выделяет карточку поставщика
|
- **Выбор**: Клик выделяет карточку поставщика
|
||||||
@ -1780,6 +1781,8 @@ height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins);
|
|||||||
|
|
||||||
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, 180px)_:
|
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, 180px)_:
|
||||||
|
|
||||||
|
- **Навигация**: Плавающая кнопка "Назад" между сайдбаром и контентом (см. seller-ui-rules.md)
|
||||||
|
- **БЕЗ заголовка** - блок начинается сразу с поиска и карточек
|
||||||
- Карточки поставщиков из раздела "Партнеры"
|
- Карточки поставщиков из раздела "Партнеры"
|
||||||
- Горизонтальный скролл при превышении ширины
|
- Горизонтальный скролл при превышении ширины
|
||||||
- Выбор только одного поставщика одновременно
|
- Выбор только одного поставщика одновременно
|
||||||
|
@ -56,11 +56,48 @@ switch (user?.organization?.type) {
|
|||||||
|
|
||||||
### 2.2 Детальные правила горизонтального скролла поставщиков
|
### 2.2 Детальные правила горизонтального скролла поставщиков
|
||||||
|
|
||||||
**СТРУКТУРА И ОТОБРАЖЕНИЕ:**
|
**КНОПКА НАВИГАЦИИ "НАЗАД":**
|
||||||
|
|
||||||
|
**Архитектура размещения:**
|
||||||
|
|
||||||
|
- **Расположение**: Между сайдбаром и основным контентом
|
||||||
|
- **Контейнер**: Отдельный `<div className="relative">`
|
||||||
|
- **НЕ является частью блока 1** - независимый навигационный элемент
|
||||||
|
- **Позиционирование**: `absolute left-0 top-6`
|
||||||
|
- **z-index**: 10 (поверх контента)
|
||||||
|
|
||||||
|
**Визуальный дизайн:**
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
className =
|
||||||
|
'absolute left-0 top-6 z-10 w-8 h-8 bg-white/10 backdrop-blur-xl border border-white/20 hover:border-white/40 rounded-full flex items-center justify-center transition-all duration-300 hover:scale-110 hover:bg-white/20 group'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Параметры кнопки:**
|
||||||
|
|
||||||
|
- **Размер**: 32×32px (`w-8 h-8`)
|
||||||
|
- **Форма**: Круглая (`rounded-full`)
|
||||||
|
- **Фон**: `bg-white/10` с эффектом размытия `backdrop-blur-xl`
|
||||||
|
- **Граница**: `border border-white/20`, при hover `border-white/40`
|
||||||
|
- **Иконка**: `ArrowLeft` 16×16px (`h-4 w-4`), цвет `text-white/70`
|
||||||
|
- **Hover эффекты**:
|
||||||
|
- Масштабирование `scale-110`
|
||||||
|
- Изменение фона `bg-white/20`
|
||||||
|
- Иконка становится белой и увеличивается до `h-5 w-5`
|
||||||
|
- **Анимация**: `transition-all duration-300`
|
||||||
|
|
||||||
|
**Функциональность:**
|
||||||
|
|
||||||
|
- **onClick**: Переход на `/supplies?tab=goods&subTab=suppliers`
|
||||||
|
- **aria-label**: "Вернуться к поставщикам"
|
||||||
|
- **Семантика**: Кнопка возврата к списку поставок
|
||||||
|
|
||||||
|
**СТРУКТУРА И ОТОБРАЖЕНИЕ БЛОКА 1:**
|
||||||
|
|
||||||
|
- **БЕЗ ЗАГОЛОВКА** - блок начинается сразу с поиска и контента
|
||||||
- **Источник данных**: Партнеры типа `WHOLESALE` из раздела "Партнеры"
|
- **Источник данных**: Партнеры типа `WHOLESALE` из раздела "Партнеры"
|
||||||
- **Контейнер**: Фиксированная высота 176px (h-44) с горизонтальным скроллом
|
- **Контейнер**: Фиксированная высота 176px (h-44) с горизонтальным скроллом
|
||||||
- **Блок поставщиков**: Общая высота 180px, включает заголовок + контейнер скролла
|
- **Блок поставщиков**: Общая высота 180px
|
||||||
- **Направление**: Слева направо (LTR)
|
- **Направление**: Слева направо (LTR)
|
||||||
- **Поведение**: Плавный скролл с автоскрытием полосы прокрутки
|
- **Поведение**: Плавный скролл с автоскрытием полосы прокрутки
|
||||||
|
|
||||||
@ -382,6 +419,8 @@ height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins);
|
|||||||
|
|
||||||
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, 180px)_:
|
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, 180px)_:
|
||||||
|
|
||||||
|
- БЕЗ заголовка - сразу функциональный контент
|
||||||
|
- Кнопка навигации "Назад" - между сайдбаром и блоком (см. раздел 2.2)
|
||||||
- Карточки поставщиков из раздела "Партнеры"
|
- Карточки поставщиков из раздела "Партнеры"
|
||||||
- Горизонтальный скролл при превышении ширины
|
- Горизонтальный скролл при превышении ширины
|
||||||
- Выбор только одного поставщика одновременно
|
- Выбор только одного поставщика одновременно
|
||||||
@ -448,6 +487,7 @@ height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins);
|
|||||||
### 3.1 React компоненты селлера
|
### 3.1 React компоненты селлера
|
||||||
|
|
||||||
**Основные компоненты:**
|
**Основные компоненты:**
|
||||||
|
|
||||||
- `SellerHomePage` - главная страница селлера (4 типо-зависимых компонента)
|
- `SellerHomePage` - главная страница селлера (4 типо-зависимых компонента)
|
||||||
- `SellerEconomicsPage` - экономическая аналитика селлера
|
- `SellerEconomicsPage` - экономическая аналитика селлера
|
||||||
|
|
||||||
|
@ -19,7 +19,13 @@ interface DatePickerProps {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DatePicker({ value, onChange, placeholder = 'Выберите дату', className, disabled }: DatePickerProps) {
|
export function DatePicker({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder: _placeholder = 'Выберите дату',
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
}: DatePickerProps) {
|
||||||
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
onChange?.(e.target.value)
|
onChange?.(e.target.value)
|
||||||
}
|
}
|
||||||
@ -32,7 +38,7 @@ export function DatePicker({ value, onChange, placeholder = 'Выберите д
|
|||||||
onChange={handleDateChange}
|
onChange={handleDateChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-9 w-full rounded-lg border border-white/10 bg-white/5 backdrop-blur px-3 py-1 text-sm text-white placeholder:text-white/50 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/20 disabled:cursor-not-allowed disabled:opacity-50',
|
'flex h-9 w-full rounded-lg border border-white/10 bg-white/5 backdrop-blur px-3 py-1 text-sm text-white placeholder:text-white/50 hover:border-white/30 hover:bg-white/10 focus:border-purple-400/50 focus:outline-none focus:ring-2 focus:ring-purple-400/20 transition-all duration-200 disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
'pr-8', // Place for calendar icon
|
'pr-8', // Place for calendar icon
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
46
src/components/ui/glass-date-picker.tsx
Normal file
46
src/components/ui/glass-date-picker.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Calendar } from 'lucide-react'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
interface GlassDatePickerProps {
|
||||||
|
value?: string
|
||||||
|
onChange?: (date: string) => void
|
||||||
|
placeholder?: string
|
||||||
|
className?: string
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GlassDatePicker({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder: _placeholder = 'дд.мм.гггг',
|
||||||
|
className = '',
|
||||||
|
disabled = false,
|
||||||
|
}: GlassDatePickerProps) {
|
||||||
|
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange?.(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative" data-testid="glass-date-picker-wrapper">
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={value}
|
||||||
|
onChange={handleDateChange}
|
||||||
|
disabled={disabled}
|
||||||
|
data-testid="glass-date-picker-input"
|
||||||
|
className={`
|
||||||
|
bg-white/5 border border-white/10 text-white rounded-lg h-9 px-3 text-sm
|
||||||
|
hover:border-white/30 hover:bg-white/10
|
||||||
|
focus:border-purple-400/50 focus:ring-2 focus:ring-purple-400/20
|
||||||
|
transition-all duration-200 outline-none
|
||||||
|
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-white/10 disabled:hover:bg-white/5
|
||||||
|
pr-8
|
||||||
|
${className}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
<Calendar className="absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-white/40 pointer-events-none" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
114
src/components/ui/glass-select.tsx
Normal file
114
src/components/ui/glass-select.tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
interface GlassSelectOption {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GlassSelectProps {
|
||||||
|
value: string
|
||||||
|
onChange: (value: string) => void
|
||||||
|
placeholder?: string
|
||||||
|
options: GlassSelectOption[]
|
||||||
|
className?: string
|
||||||
|
size?: 'sm' | 'md' | 'lg'
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GlassSelect({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder = 'Выберите вариант',
|
||||||
|
options,
|
||||||
|
className = '',
|
||||||
|
size = 'md',
|
||||||
|
disabled = false,
|
||||||
|
}: GlassSelectProps) {
|
||||||
|
const sizeClasses = {
|
||||||
|
sm: 'h-8 text-xs px-3',
|
||||||
|
md: 'h-9 text-sm px-3',
|
||||||
|
lg: 'h-11 text-base px-4',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
|
className={`
|
||||||
|
bg-white/5 border border-white/10 text-white rounded-lg
|
||||||
|
hover:border-white/30 hover:bg-white/10
|
||||||
|
focus:border-purple-400/50 focus:ring-2 focus:ring-purple-400/20
|
||||||
|
transition-all duration-200 outline-none
|
||||||
|
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-white/10 disabled:hover:bg-white/5
|
||||||
|
${sizeClasses[size]}
|
||||||
|
${className}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{placeholder && (
|
||||||
|
<option value="" disabled className="bg-gray-800 text-white/60">
|
||||||
|
{placeholder}
|
||||||
|
</option>
|
||||||
|
)}
|
||||||
|
{options.map((option) => (
|
||||||
|
<option key={option.value} value={option.value} disabled={option.disabled} className="bg-gray-800 text-white">
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Дополнительный компонент для простого использования с children
|
||||||
|
interface GlassSelectSimpleProps {
|
||||||
|
value: string
|
||||||
|
onChange: (value: string) => void
|
||||||
|
placeholder?: string
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
size?: 'sm' | 'md' | 'lg'
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GlassSelectSimple({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder,
|
||||||
|
children,
|
||||||
|
className = '',
|
||||||
|
size = 'md',
|
||||||
|
disabled = false,
|
||||||
|
}: GlassSelectSimpleProps) {
|
||||||
|
const sizeClasses = {
|
||||||
|
sm: 'h-8 text-xs px-3',
|
||||||
|
md: 'h-9 text-sm px-3',
|
||||||
|
lg: 'h-11 text-base px-4',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
disabled={disabled}
|
||||||
|
className={`
|
||||||
|
bg-white/5 border border-white/10 text-white rounded-lg
|
||||||
|
hover:border-white/30 hover:bg-white/10
|
||||||
|
focus:border-purple-400/50 focus:ring-2 focus:ring-purple-400/20
|
||||||
|
transition-all duration-200 outline-none
|
||||||
|
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-white/10 disabled:hover:bg-white/5
|
||||||
|
${sizeClasses[size]}
|
||||||
|
${className}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{placeholder && (
|
||||||
|
<option value="" disabled className="bg-gray-800 text-white/60">
|
||||||
|
{placeholder}
|
||||||
|
</option>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</select>
|
||||||
|
)
|
||||||
|
}
|
Reference in New Issue
Block a user