feat: UI style refresh — Lucide icons, theme, slider, filters, buttons, VK
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useMemo } from 'react'
|
||||
import Box from '@mui/material/Box'
|
||||
import Button from '@mui/material/Button'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Collapse from '@mui/material/Collapse'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import FormControl from '@mui/material/FormControl'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import InputLabel from '@mui/material/InputLabel'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import Paper from '@mui/material/Paper'
|
||||
@@ -13,6 +15,7 @@ import TextField from '@mui/material/TextField'
|
||||
import ToggleButton from '@mui/material/ToggleButton'
|
||||
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import { Search, SlidersHorizontal } from 'lucide-react'
|
||||
import type { Category } from '@/entities/product/model/types'
|
||||
import type { UseProductFiltersResult } from '../lib/use-product-filters'
|
||||
|
||||
@@ -60,53 +63,49 @@ export function ProductFilters({
|
||||
spacing={2}
|
||||
sx={{ alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }}
|
||||
>
|
||||
<FormControl sx={{ minWidth: 220 }} size="small">
|
||||
<InputLabel id="category-filter-label">Категория</InputLabel>
|
||||
<Select<string>
|
||||
labelId="category-filter-label"
|
||||
label="Категория"
|
||||
value={categorySlug}
|
||||
onChange={handleCategoryChange}
|
||||
disabled={categoriesLoading}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>Все</em>
|
||||
</MenuItem>
|
||||
{categoriesForFilter.map((c) => (
|
||||
<MenuItem key={c.id} value={c.slug}>
|
||||
{c.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label="Поиск"
|
||||
placeholder="Поиск"
|
||||
value={qInput}
|
||||
onChange={(e) => setQInput(e.target.value)}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Search size={18} />
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
sx={{ flexGrow: 1, minWidth: { xs: '100%', md: 360 } }}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Paper
|
||||
variant="outlined"
|
||||
sx={{
|
||||
p: 1.5,
|
||||
borderRadius: 2,
|
||||
bgcolor: 'background.paper',
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', sm: 'row' },
|
||||
gap: 1.5,
|
||||
alignItems: { sm: 'center' },
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
sx={{ p: 1.5, borderRadius: 3, bgcolor: 'background.paper', display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 1.5, alignItems: { sm: 'center' }, justifyContent: 'space-between' }}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant="subtitle2">Наличие</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Быстрый фильтр по наличию
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, alignItems: { sm: 'center' }, gap: 1.5, flexGrow: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75, alignItems: 'center' }}>
|
||||
<Chip
|
||||
label="Все"
|
||||
size="small"
|
||||
variant={categorySlug === '' ? 'filled' : 'outlined'}
|
||||
color={categorySlug === '' ? 'primary' : 'default'}
|
||||
onClick={() => handleCategoryChange('')}
|
||||
/>
|
||||
{categoriesForFilter.map((c) => (
|
||||
<Chip
|
||||
key={c.id}
|
||||
label={c.name}
|
||||
size="small"
|
||||
variant={categorySlug === c.slug ? 'filled' : 'outlined'}
|
||||
color={categorySlug === c.slug ? 'primary' : 'default'}
|
||||
onClick={() => handleCategoryChange(c.slug)}
|
||||
disabled={categoriesLoading}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<ToggleButtonGroup
|
||||
@@ -116,7 +115,7 @@ export function ProductFilters({
|
||||
onChange={(_, v) => handleAvailabilityChange(v)}
|
||||
sx={{
|
||||
alignSelf: { xs: 'flex-start', sm: 'auto' },
|
||||
'& .MuiToggleButton-root': { px: 2, fontWeight: 700, letterSpacing: 0.2, textTransform: 'none' },
|
||||
'& .MuiToggleButton-root': { px: 1.5, fontWeight: 600, textTransform: 'none' },
|
||||
'& .MuiToggleButton-root.Mui-selected': {
|
||||
bgcolor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
@@ -135,114 +134,81 @@ export function ProductFilters({
|
||||
spacing={1.5}
|
||||
sx={{ alignItems: { sm: 'center' }, justifyContent: 'space-between', flexWrap: 'wrap' }}
|
||||
>
|
||||
<Button variant="text" onClick={() => setMoreOpen((v) => !v)} sx={{ alignSelf: { xs: 'flex-start' } }}>
|
||||
{moreOpen ? 'Скрыть фильтры' : 'Фильтры и сортировка'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={resetFilters}
|
||||
variant="text"
|
||||
onClick={() => setMoreOpen((v) => !v)}
|
||||
startIcon={<SlidersHorizontal size={18} />}
|
||||
sx={{ alignSelf: { xs: 'flex-start' } }}
|
||||
>
|
||||
{moreOpen ? 'Скрыть' : 'Фильтры'}
|
||||
</Button>
|
||||
<Button variant="outlined" onClick={resetFilters} sx={{ alignSelf: { xs: 'flex-start' } }}>
|
||||
Сбросить
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
<Collapse in={moreOpen} unmountOnExit>
|
||||
<Stack
|
||||
direction={{ xs: 'column', md: 'row' }}
|
||||
spacing={2}
|
||||
sx={{ mt: 2, alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }}
|
||||
>
|
||||
<FormControl sx={{ minWidth: 220 }} size="small">
|
||||
<InputLabel id="sort-label">Сортировка</InputLabel>
|
||||
<Select<string> labelId="sort-label" label="Сортировка" value={sort} onChange={handleSortChange}>
|
||||
<MenuItem value="">
|
||||
<em>Сначала новые</em>
|
||||
</MenuItem>
|
||||
<MenuItem value="price_asc">Цена: по возрастанию</MenuItem>
|
||||
<MenuItem value="price_desc">Цена: по убыванию</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Paper variant="outlined" sx={{ p: 2, borderRadius: 3, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Stack direction={{ xs: 'column', md: 'row' }} spacing={2} sx={{ alignItems: { md: 'center' }, flexWrap: { md: 'wrap' } }}>
|
||||
<FormControl sx={{ minWidth: 200 }} size="small">
|
||||
<InputLabel id="sort-label">Сортировка</InputLabel>
|
||||
<Select<string> labelId="sort-label" label="Сортировка" value={sort} onChange={handleSortChange}>
|
||||
<MenuItem value=""><em>Сначала новые</em></MenuItem>
|
||||
<MenuItem value="price_asc">Цена: по возрастанию</MenuItem>
|
||||
<MenuItem value="price_desc">Цена: по убыванию</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
size="small"
|
||||
label="Цена от, ₽"
|
||||
value={priceMinRub}
|
||||
onChange={(e) => handlePriceMinChange(e.target.value)}
|
||||
sx={{ width: { xs: '100%', md: 180 } }}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Цена до, ₽"
|
||||
value={priceMaxRub}
|
||||
onChange={(e) => handlePriceMaxChange(e.target.value)}
|
||||
sx={{ width: { xs: '100%', md: 180 } }}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Цена от, ₽"
|
||||
value={priceMinRub}
|
||||
onChange={(e) => handlePriceMinChange(e.target.value)}
|
||||
sx={{ width: { xs: '100%', md: 160 } }}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Цена до, ₽"
|
||||
value={priceMaxRub}
|
||||
onChange={(e) => handlePriceMaxChange(e.target.value)}
|
||||
sx={{ width: { xs: '100%', md: 160 } }}
|
||||
/>
|
||||
|
||||
<FormControl sx={{ minWidth: 220 }} size="small">
|
||||
<InputLabel id="page-size-label">На странице</InputLabel>
|
||||
<Select<string>
|
||||
labelId="page-size-label"
|
||||
label="На странице"
|
||||
value={String(pageSize)}
|
||||
onChange={handlePageSizeChange}
|
||||
>
|
||||
{[6, 12, 18, 24].map((n) => (
|
||||
<MenuItem key={n} value={String(n)}>
|
||||
{n}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
<FormControl sx={{ minWidth: 160 }} size="small">
|
||||
<InputLabel id="page-size-label">На странице</InputLabel>
|
||||
<Select<string> labelId="page-size-label" label="На странице" value={String(pageSize)} onChange={handlePageSizeChange}>
|
||||
{[6, 12, 18, 24].map((n) => (
|
||||
<MenuItem key={n} value={String(n)}>{n}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
|
||||
<Divider sx={{ my: 2 }} />
|
||||
<Divider />
|
||||
|
||||
<Paper
|
||||
variant="outlined"
|
||||
sx={{
|
||||
p: 1.5,
|
||||
borderRadius: 2,
|
||||
bgcolor: 'background.paper',
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', sm: 'row' },
|
||||
gap: 1.5,
|
||||
alignItems: { sm: 'center' },
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 1.5, alignItems: { sm: 'center' }, justifyContent: 'space-between' }}>
|
||||
<Typography variant="subtitle2">Масштаб карточек</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Выберите размер карточек в каталоге
|
||||
</Typography>
|
||||
<ToggleButtonGroup
|
||||
exclusive
|
||||
size="small"
|
||||
value={cardScale}
|
||||
onChange={(_, v) => handleCardScaleChange(v)}
|
||||
sx={{
|
||||
alignSelf: { xs: 'flex-start', sm: 'auto' },
|
||||
'& .MuiToggleButton-root': { px: 1.5, fontWeight: 600, textTransform: 'none' },
|
||||
'& .MuiToggleButton-root.Mui-selected': {
|
||||
bgcolor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
'&:hover': { bgcolor: 'primary.dark' },
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ToggleButton value={70}>S</ToggleButton>
|
||||
<ToggleButton value={90}>M</ToggleButton>
|
||||
<ToggleButton value={110}>L</ToggleButton>
|
||||
<ToggleButton value={130}>XL</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Box>
|
||||
|
||||
<ToggleButtonGroup
|
||||
exclusive
|
||||
size="small"
|
||||
value={cardScale}
|
||||
onChange={(_, v) => handleCardScaleChange(v)}
|
||||
sx={{
|
||||
alignSelf: { xs: 'flex-start', sm: 'auto' },
|
||||
'& .MuiToggleButton-root': {
|
||||
px: 2,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 0.2,
|
||||
textTransform: 'none',
|
||||
},
|
||||
'& .MuiToggleButton-root.Mui-selected': {
|
||||
bgcolor: 'primary.main',
|
||||
color: 'primary.contrastText',
|
||||
'&:hover': { bgcolor: 'primary.dark' },
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ToggleButton value={70}>S</ToggleButton>
|
||||
<ToggleButton value={90}>M</ToggleButton>
|
||||
<ToggleButton value={110}>L</ToggleButton>
|
||||
<ToggleButton value={130}>XL</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Paper>
|
||||
</Collapse>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user