This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-01-30 11:24:07 +05:00
parent f439f9bbdf
commit b1095f2c12
14 changed files with 250 additions and 16 deletions

View File

@@ -2,7 +2,7 @@ import "./back.css";
export default function BackAnimatsiya() { export default function BackAnimatsiya() {
return ( return (
<div className="fixed inset-0 w-full h-full flex items-center justify-center pointer-events-none z-0 opacity-100"> <div className="fixed inset-0 w-full h-full flex items-center justify-center pointer-events-none z-0 opacity-50">
<svg <svg
id="Layer_2" id="Layer_2"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -0,0 +1,129 @@
"use client";
import { useFilter } from "@/lib/filter-zustand";
import { Check } from "lucide-react";
import { useState } from "react";
export default function Filter() {
const filter = useFilter((state) => state.filter);
const toggleFilter = useFilter((state) => state.toggleFilter);
const hasData = useFilter((state) => state.hasFilter);
const [dataExpanded, setDataExpanded] = useState<boolean>(false);
const [numberExpanded, setNumberExpanded] = useState<boolean>(false);
const sectionData = [
"SLT-Aqua",
"Вварное седло",
"Кран шаровый",
"Муфты",
"Муфты комбинированные",
"Муфты переходные",
"Тройник комбинированный",
"Тройники",
"Трубы SDR 6",
"Трубы SDR 7,4",
"Угол 45",
"Угол 90",
"Угольник комбинированный",
"Фланцы+бурты",
];
const sectionNumber = [
"25", '25х1/2"', '25х3/4"', "32", "32x25x25", "32x25x32",
'32х1"', '32х1/2"', "32х25", '32х3/4"', "40", "40x25x40",
"40x32x40", '40х1 1/4"', '40х1 3/4"', '40х1/2"', "40х25",
"40х32", "50", "50x25x50", "50x32x50", "50x40x50",
'50х1 1/2"', '50х1/2"', "50х25", "50х32", "50х40", "63",
"63x25x63", "63x32x63", "63x40x63", "63x50x63", '63х1/2"',
'63х2"', "63х25", "63х32", "63х40", "63х50", "75",
"75x25x75", "75x32x75", "75x40x75", "75x50x75", "75x63x75",
'75х1/2"', "75х32", "75х40", "75х50", "75х63", "90",
"90x40x90", "90x50x90", "90x63x90", "90x75x90", '90х1/2"',
"90х32", "90х40", "90х50", "90х63", "90х75", "110",
"110x50x110", "110x63x110", "110x75x110", "110x90x110",
'110х1/2"', "110х25", "110х32", "110х40", "110х50",
"110х63", "110х75", "110х90", "125", "160",
];
// Bo'lim uchun ko'rsatiladigan itemlar
const visibleSectionData = dataExpanded ? sectionData : sectionData.slice(0, 5);
// O'lcham uchun ko'rsatiladigan itemlar
const visibleSectionNumber = numberExpanded ? sectionNumber : sectionNumber.slice(0, 10);
return (
<div className="space-y-3 max-w-70 w-full text-white">
{/* Bo'lim filtri */}
<div className="bg-gray-500 rounded-lg">
<p className="bg-red-500 text-white p-2 font-semibold font-almarai text-lg rounded-t-lg">
Bo'lim
</p>
<div className="space-y-3 p-2">
{visibleSectionData.map((item) => (
<div
key={item}
onClick={() => toggleFilter(item)}
className="hover:cursor-pointer flex items-center gap-2"
>
<span
className={`flex h-5 w-5 items-center justify-center rounded border-2 transition ${
hasData(item)
? "border-red-600 bg-red-600"
: "border-gray-400 bg-transparent"
}`}
aria-label="Filter checkbox"
>
{hasData(item) && (
<Check className="h-3 w-3 text-white" strokeWidth={3} />
)}
</span>
<p>{item}</p>
</div>
))}
</div>
<button
className="p-2 text-lg underline hover:text-red-300 transition"
onClick={() => setDataExpanded(!dataExpanded)}
>
{dataExpanded ? "Yashirish" : "Ko'proq ko'rish"}
</button>
</div>
{/* O'lcham filtri */}
<div className="bg-gray-500 rounded-lg">
<p className="bg-red-500 text-white p-2 font-semibold font-almarai text-lg rounded-t-lg">
O'lcham
</p>
<div className="grid grid-cols-2 gap-3 p-2">
{visibleSectionNumber.map((item) => (
<div
key={item}
onClick={() => toggleFilter(item)}
className="hover:cursor-pointer flex items-center gap-2"
>
<span
className={`flex h-5 w-5 items-center justify-center rounded border-2 transition ${
hasData(item)
? "border-red-600 bg-red-600"
: "border-gray-400 bg-transparent"
}`}
aria-label="Filter checkbox"
>
{hasData(item) && (
<Check className="h-3 w-3 text-white" strokeWidth={3} />
)}
</span>
<p>{item}</p>
</div>
))}
</div>
<button
onClick={() => setNumberExpanded(!numberExpanded)}
className="p-2 text-lg underline hover:text-red-300 transition"
>
{numberExpanded ? "Yashirish" : "Ko'proq ko'rish"}
</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,35 @@
"use client";
import { useFilter } from "@/lib/filter-zustand";
import { X } from "lucide-react";
export default function FilterInfo() {
const filtered = useFilter((state) => state.filter);
const resetFilter = useFilter((state) => state.resetFilter);
const togleFilter = useFilter((state) => state.toggleFilter);
if (filtered.length === 0) {
return null;
}
return (
<div className="fixed bottom-13 left-5 z-10 bg-gray-500 p-3 rounded-lg space-y-3 max-w-70 w-full">
<p className="text-white ">Found: 20</p>
<div className="flex gap-1 flex-wrap">
{filtered &&
filtered.map((item) => (
<div
key={item}
className="flex items-center gap-1 p-1 rounded-lg bg-gray-700 text-white text-sm "
>
<button onClick={() => togleFilter(item)}>
<X size={16} />
</button>
{item}
</div>
))}
</div>
<button onClick={resetFilter} className="text-white underline ">
Clear all
</button>
</div>
);
}

View File

@@ -1,9 +1,16 @@
import Filter from "./filter";
import FilterInfo from "./filterInfo";
import ProductCard from "./productCard"; import ProductCard from "./productCard";
export function Products() { export function Products() {
return ( return (
<div className="bg-[#1e1d1c] py-20"> <div className="bg-[#1e1d1c] py-20">
<div className="max-w-250 mx-auto w-full sm:-mt-50 -mt-30 z-20 relative"> <div className="max-w-300 mx-auto w-full z-20 relative">
<div className="flex items-start gap-5">
{/* filter part */}
<Filter/>
{/* main products */}
<div className="grid lg:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5"> <div className="grid lg:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5">
{Array(9) {Array(9)
.fill(null) .fill(null)
@@ -18,6 +25,8 @@ export function Products() {
/> />
))} ))}
</div> </div>
<FilterInfo/>
</div>
</div> </div>
</div> </div>
); );

33
lib/filter-zustand.ts Normal file
View File

@@ -0,0 +1,33 @@
import { create } from "zustand";
interface FilterZustandTypes {
filter: string[];
removeFilter: (data: string) => void;
toggleFilter: (data: string) => void;
resetFilter: () => void;
hasFilter: (data: string) => boolean;
}
export const useFilter = create<FilterZustandTypes>((set, get) => ({
filter: [],
removeFilter: (data) =>
set((state) => ({
filter: state.filter.filter((item) => item !== data),
})),
// Toggle: mavjud bo'lsa o'chirish, yo'q bo'lsa qo'shish
toggleFilter: (data) =>
set((state) => {
if (state.filter.includes(data)) {
return { filter: state.filter.filter((item) => item !== data) };
}
return { filter: [...state.filter, data] };
}),
resetFilter: () => set({ filter: [] }),
hasFilter: (data) => {
return get().filter.includes(data);
},
}));

View File

@@ -64,7 +64,8 @@
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2", "vaul": "^1.1.2",
"zod": "3.25.76" "zod": "3.25.76",
"zustand": "^5.0.10"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4.1.9", "@tailwindcss/postcss": "^4.1.9",

27
pnpm-lock.yaml generated
View File

@@ -176,6 +176,9 @@ importers:
zod: zod:
specifier: 3.25.76 specifier: 3.25.76
version: 3.25.76 version: 3.25.76
zustand:
specifier: ^5.0.10
version: 5.0.10(@types/react@19.2.9)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0))
devDependencies: devDependencies:
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.1.9 specifier: ^4.1.9
@@ -2006,6 +2009,24 @@ packages:
zod@3.25.76: zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
zustand@5.0.10:
resolution: {integrity: sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': '>=18.0.0'
immer: '>=9.0.6'
react: '>=18.0.0'
use-sync-external-store: '>=1.2.0'
peerDependenciesMeta:
'@types/react':
optional: true
immer:
optional: true
react:
optional: true
use-sync-external-store:
optional: true
snapshots: snapshots:
'@alloc/quick-lru@5.2.0': {} '@alloc/quick-lru@5.2.0': {}
@@ -3687,3 +3708,9 @@ snapshots:
d3-timer: 3.0.1 d3-timer: 3.0.1
zod@3.25.76: {} zod@3.25.76: {}
zustand@5.0.10(@types/react@19.2.9)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)):
optionalDependencies:
'@types/react': 19.2.9
react: 19.2.0
use-sync-external-store: 1.6.0(react@19.2.0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB