@@ -74,6 +74,7 @@ const priceToValue = computed({
7474})
7575const isInStock = ref (true )
7676const categoriesExpanded = ref (false )
77+ const isPriceRangeDragging = ref (false )
7778
7879watch (priceFromValue , (newValue ) => {
7980 const clampedValue = clampPriceValue (newValue )
@@ -101,14 +102,21 @@ watch(priceToValue, (newValue) => {
101102 }
102103})
103104
104- const visibleCategories = computed (() => {
105- return categoriesExpanded .value ? categories .value : categories .value .slice (0 , 5 )
106- })
105+ const primaryCategories = computed (() => categories .value .slice (0 , 5 ))
106+ const additionalCategories = computed (() => categories .value .slice (5 ))
107107
108108const hasMoreCategories = computed (() => {
109109 return categories .value .length > 5
110110})
111111
112+ const startPriceRangeDrag = () => {
113+ isPriceRangeDragging .value = true
114+ }
115+
116+ const stopPriceRangeDrag = () => {
117+ isPriceRangeDragging .value = false
118+ }
119+
112120const pricePresets = [50000 , 100000 , 200000 , 300000 , 500000 , 800000 , 1000000 , 1500000 ]
113121
114122const getIcon = (category : CategoryOption ) => {
@@ -123,20 +131,20 @@ const classes = {
123131 resetButton: ' filter-panel-reset' ,
124132 section: ' space-y-4' ,
125133 sectionTitle: ' text-sm font-semibold tracking-tight filter-panel-heading' ,
126- sectionList: ' space-y-3 ' ,
134+ sectionList: ' space-y-2 ' ,
127135 categoryList: ' space-y-2' ,
128136 categoryRow: ' flex items-center gap-2 px-2 py-2 cursor-pointer transition filter-panel-category-row w-full' ,
129137 categoryRowActive: ' filter-panel-category-row-active' ,
130138 categoryIcon: ' text-base shrink-0' ,
131139 categoryLabel: ' text-sm filter-panel-heading' ,
132140 categoryCount: ' text-xs filter-panel-muted ml-auto' ,
133141 expandButton: ' w-full mt-2 px-3 py-2 text-sm font-semibold text-left transition filter-panel-expand-btn' ,
134- brandRow: ' flex items-center justify-between gap-3 px-2 py-3 cursor-pointer transition filter-panel-brand-row' ,
142+ brandRow: ' flex items-center justify-between gap-3 px-2.5 py-2.5 cursor-pointer transition filter-panel-brand-row' ,
135143 brandRowActive: ' filter-panel-brand-row-active' ,
136144 brandLabel: ' text-sm font-semibold filter-panel-heading' ,
137145 brandAction: ' hidden text-xs filter-panel-muted sm:inline' ,
138146 priceSection: ' space-y-3' ,
139- priceInputs: ' grid grid-cols-2 gap-3 ' ,
147+ priceInputs: ' grid grid-cols-1 gap-2.5 ' ,
140148 stockRow: ' flex items-center justify-between' ,
141149 stockCheckbox: ' flex items-center gap-3' ,
142150 stockCount: ' text-sm font-semibold filter-panel-heading' ,
@@ -189,7 +197,7 @@ const classes = {
189197 <h3 id =" catalog-category-title" :class =" classes.sectionTitle" >Категории</h3 >
190198 <div :class =" classes.categoryList" >
191199 <button
192- v-for =" category in visibleCategories "
200+ v-for =" category in primaryCategories "
193201 :key =" category.slug"
194202 type =" button"
195203 :class =" [
@@ -203,6 +211,28 @@ const classes = {
203211 <span :class =" classes.categoryLabel" >{{ category.label }}</span >
204212 <span :class =" classes.categoryCount" >{{ category.count ?? 0 }}</span >
205213 </button >
214+ <TransitionGroup
215+ v-if =" hasMoreCategories "
216+ name="filter-category-reveal"
217+ tag="div"
218+ class="filter-panel-category-extra"
219+ >
220+ <button
221+ v-for =" category in categoriesExpanded ? additionalCategories : []"
222+ :key =" category.slug"
223+ type =" button"
224+ :class =" [
225+ classes.categoryRow,
226+ selectedCategorySlugs.includes(category.slug) && classes.categoryRowActive
227+ ]"
228+ :aria-pressed =" selectedCategorySlugs.includes(category.slug)"
229+ @click =" $emit('toggleCategory', category.slug)"
230+ >
231+ <span :class =" classes.categoryIcon" >{{ getIcon(category) }}</span >
232+ <span :class =" classes.categoryLabel" >{{ category.label }}</span >
233+ <span :class =" classes.categoryCount" >{{ category.count ?? 0 }}</span >
234+ </button >
235+ </TransitionGroup >
206236 </div >
207237 <button
208238 v-if =" hasMoreCategories"
@@ -217,7 +247,10 @@ const classes = {
217247 <section aria-labelledby =" catalog-price-title" :class =" classes.section" >
218248 <h3 id =" catalog-price-title" :class =" classes.sectionTitle" >Цена</h3 >
219249 <div :class =" classes.priceSection" >
220- <div class =" range-slider-container" >
250+ <div
251+ class =" range-slider-container"
252+ :class =" { 'is-dragging': isPriceRangeDragging }"
253+ >
221254 <input
222255 v-model.number =" priceFromValue"
223256 type =" range"
@@ -226,6 +259,10 @@ const classes = {
226259 step =" 1000"
227260 class =" range-slider range-slider-from"
228261 aria-label =" Минимальная цена"
262+ @pointerdown =" startPriceRangeDrag"
263+ @pointerup =" stopPriceRangeDrag"
264+ @pointercancel =" stopPriceRangeDrag"
265+ @blur =" stopPriceRangeDrag"
229266 >
230267 <input
231268 v-model.number =" priceToValue"
@@ -235,6 +272,10 @@ const classes = {
235272 step =" 1000"
236273 class =" range-slider range-slider-to"
237274 aria-label =" Максимальная цена"
275+ @pointerdown =" startPriceRangeDrag"
276+ @pointerup =" stopPriceRangeDrag"
277+ @pointercancel =" stopPriceRangeDrag"
278+ @blur =" stopPriceRangeDrag"
238279 >
239280 <div class =" range-track" >
240281 <div
@@ -357,18 +398,49 @@ const classes = {
357398 color : rgba (var (--color-text-rgb ), 0.55 );
358399}
359400
360- .filter-panel-brand-row ,
401+ .filter-panel-brand-row {
402+ border : 1px solid transparent ;
403+ border-radius : 1rem ;
404+ background : transparent ;
405+ min-height : 3.45rem ;
406+ outline : none ;
407+ transition :
408+ background-color 190ms ease-out ,
409+ border-color 190ms ease-out ,
410+ box-shadow 190ms ease-out ,
411+ color 190ms ease-out ;
412+ }
413+
361414.filter-panel-category-row {
362415 background : transparent ;
363416 border : none ;
364417}
365418
366419.filter-panel-brand-row :hover ,
420+ .filter-panel-brand-row :focus-visible {
421+ border-color : rgba (var (--color-border-rgb ), 0.18 );
422+ background : rgba (var (--color-text-rgb ), 0.055 );
423+ box-shadow :
424+ 0 10px 24px rgba (0 , 0 , 0 , 0.08 ),
425+ inset 0 1px 0 rgba (255 , 255 , 255 , 0.08 );
426+ }
427+
428+ .filter-panel-brand-row :focus-visible {
429+ box-shadow :
430+ 0 10px 24px rgba (0 , 0 , 0 , 0.08 ),
431+ 0 0 0 3px rgba (var (--color-primary-rgb ), 0.2 );
432+ }
433+
367434.filter-panel-category-row :hover {
368435 background : transparent ;
369436}
370437
371- .filter-panel-brand-row-active ,
438+ .filter-panel-brand-row-active {
439+ border-color : rgba (var (--color-primary-rgb ), 0.2 );
440+ background : rgba (var (--color-primary-rgb ), 0.075 );
441+ color : var (--color-primary );
442+ }
443+
372444.filter-panel-category-row-active {
373445 background : transparent ;
374446 color : var (--color-primary );
@@ -388,6 +460,36 @@ const classes = {
388460 background : rgba (var (--color-text-rgb ), 0.04 );
389461}
390462
463+ .filter-panel-category-extra {
464+ display : grid ;
465+ gap : 0.5rem ;
466+ overflow : hidden ;
467+ }
468+
469+ .filter-category-reveal-enter-active ,
470+ .filter-category-reveal-leave-active {
471+ transition :
472+ opacity 190ms ease-out ,
473+ transform 190ms ease-out ,
474+ max-height 220ms ease-out ,
475+ margin 220ms ease-out ;
476+ }
477+
478+ .filter-category-reveal-enter-from ,
479+ .filter-category-reveal-leave-to {
480+ max-height : 0 ;
481+ margin-top : -0.35rem ;
482+ opacity : 0 ;
483+ transform : translateY (-0.25rem );
484+ }
485+
486+ .filter-category-reveal-enter-to ,
487+ .filter-category-reveal-leave-from {
488+ max-height : 3rem ;
489+ opacity : 1 ;
490+ transform : translateY (0 );
491+ }
492+
391493/* Dual range slider */
392494.range-slider-container {
393495 position : relative ;
@@ -418,6 +520,10 @@ const classes = {
418520 transition : left 180ms ease-out , right 180ms ease-out ;
419521}
420522
523+ .range-slider-container.is-dragging .range-fill {
524+ transition : none ;
525+ }
526+
421527.range-slider {
422528 position : absolute ;
423529 width : 100% ;
@@ -545,11 +651,12 @@ const classes = {
545651
546652.filter-panel-price-input-left {
547653 padding-left : 1.7rem ;
654+ padding-right : 0.9rem ;
548655}
549656
550657.filter-panel-price-input-right {
551658 padding-left : 1.5rem ;
552- padding-right : 2.65 rem ;
659+ padding-right : 3 rem ;
553660}
554661
555662.filter-panel-price-input :focus {
0 commit comments