@pradip1995/segment-shop-by-nav 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@pradip1995/segment-shop-by-nav",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "sideEffects": [
9
+ "src/segment.css"
10
+ ],
11
+ "files": [
12
+ "src"
13
+ ],
14
+ "exports": {
15
+ ".": "./src/index.ts",
16
+ "./manifest": "./src/manifest.ts"
17
+ },
18
+ "peerDependencies": {
19
+ "@pradip1995/plugin-sdk": "^0.2.0",
20
+ "react": ">=19",
21
+ "react-dom": ">=19",
22
+ "next": ">=15"
23
+ },
24
+ "dependencies": {
25
+ "@pradip1995/segment-primitives": "0.3.0",
26
+ "@pradip1995/segment-tokens": "0.3.2"
27
+ },
28
+ "devDependencies": {
29
+ "@pradip1995/plugin-sdk": "^0.2.0",
30
+ "@types/react": "^19",
31
+ "react": "19.0.3",
32
+ "typescript": "^5.7.2"
33
+ },
34
+ "scripts": {
35
+ "typecheck": "tsc --noEmit",
36
+ "lint": "tsc --noEmit"
37
+ }
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default } from "./segment"
2
+ export { default as manifest } from "./manifest"
@@ -0,0 +1,11 @@
1
+ import type { SegmentManifest } from "@pradip1995/plugin-sdk"
2
+
3
+ const manifest: SegmentManifest = {
4
+ id: "shop-by-nav",
5
+ type: "segment",
6
+ version: "0.1.0",
7
+ compatibleFramework: ["^1.0.0"],
8
+ dataKey: "store",
9
+ }
10
+
11
+ export default manifest
@@ -0,0 +1,44 @@
1
+ .shop-by-nav {
2
+ width: 100%;
3
+ padding: 1.5rem 0 2rem;
4
+ }
5
+
6
+ .shop-by-nav__inner {
7
+ max-width: var(--container-max);
8
+ margin: 0 auto;
9
+ padding: 0 1rem;
10
+ }
11
+
12
+ .shop-by-nav__title {
13
+ text-align: center;
14
+ font-size: 1.5rem;
15
+ font-weight: 600;
16
+ margin-bottom: 1.5rem;
17
+ color: var(--color-text-heading);
18
+ }
19
+
20
+ .shop-by-nav__list {
21
+ display: flex;
22
+ gap: 0.75rem;
23
+ overflow-x: auto;
24
+ padding-bottom: 0.5rem;
25
+ }
26
+
27
+ .shop-by-nav__pill {
28
+ display: inline-flex;
29
+ padding: 0.5rem 1.25rem;
30
+ border-radius: 9999px;
31
+ border: 1px solid var(--color-border);
32
+ background: var(--color-surface);
33
+ color: var(--color-text-heading);
34
+ white-space: nowrap;
35
+ text-decoration: none;
36
+ font-size: 0.875rem;
37
+ }
38
+
39
+ .shop-by-nav__pill--active,
40
+ .shop-by-nav__pill:hover {
41
+ background: var(--color-brand-accent);
42
+ border-color: var(--color-brand-accent);
43
+ color: var(--color-text-inverse);
44
+ }
@@ -0,0 +1,86 @@
1
+ "use client"
2
+
3
+ import "./segment.css"
4
+ import { useEffect, useMemo, useState } from "react"
5
+ import { useSearchParams } from "next/navigation"
6
+ import LocalizedLink from "@pradip1995/segment-primitives/localized-link"
7
+
8
+ type NavCategory = {
9
+ name: string
10
+ href: string
11
+ }
12
+
13
+ function resolveActiveCategory(categories: NavCategory[], categoryParam: string | null): string {
14
+ if (!categoryParam || categories.length === 0) return ""
15
+
16
+ const categoryLower = categoryParam.toLowerCase()
17
+ const categoryUpper = categoryParam.toUpperCase()
18
+
19
+ const specialMappings: Record<string, string> = {
20
+ new: "NEW IN",
21
+ "new-in": "NEW IN",
22
+ bestsellers: "BESTSELLERS",
23
+ "best-sellers": "BESTSELLERS",
24
+ }
25
+
26
+ const special = specialMappings[categoryLower]
27
+ if (special) {
28
+ const found = categories.find((cat) => cat.name === special)
29
+ if (found) return found.name
30
+ }
31
+
32
+ const found = categories.find((cat) => {
33
+ const catName = cat.name.toUpperCase().replace(/\s+/g, "").replace(/-/g, "")
34
+ const paramName = categoryUpper.replace(/\s+/g, "").replace(/-/g, "")
35
+ const handle = (cat.href.split("category=")[1] || "").split("#")[0].toUpperCase().replace(/-/g, "")
36
+ return cat.name === categoryUpper || catName === paramName || handle === paramName
37
+ })
38
+
39
+ return found?.name || ""
40
+ }
41
+
42
+ export default function ShopByNav({
43
+ categories = [],
44
+ title = "Shop by",
45
+ anchorId = "shop-by-nav",
46
+ }: {
47
+ categories?: NavCategory[]
48
+ title?: string
49
+ anchorId?: string
50
+ }) {
51
+ const searchParams = useSearchParams()
52
+ const categoryParam = searchParams.get("category")
53
+ const items = useMemo(() => categories.filter((c) => c.name && c.href), [categories])
54
+ const [activeCategory, setActiveCategory] = useState("")
55
+
56
+ useEffect(() => {
57
+ if (items.length > 0) {
58
+ setActiveCategory(resolveActiveCategory(items, categoryParam))
59
+ }
60
+ }, [categoryParam, items])
61
+
62
+ if (items.length === 0) return null
63
+
64
+ return (
65
+ <nav id={anchorId} className="shop-by-nav scroll-mt-24" aria-label={title}>
66
+ <div className="shop-by-nav__inner">
67
+ <h2 className="shop-by-nav__title">{title}</h2>
68
+ <div className="shop-by-nav__list">
69
+ {items.map((category) => {
70
+ const isActive = activeCategory === category.name
71
+ return (
72
+ <LocalizedLink
73
+ key={category.name}
74
+ href={category.href}
75
+ onClick={() => setActiveCategory(category.name)}
76
+ className={`shop-by-nav__pill${isActive ? " shop-by-nav__pill--active" : ""}`}
77
+ >
78
+ {category.name}
79
+ </LocalizedLink>
80
+ )
81
+ })}
82
+ </div>
83
+ </div>
84
+ </nav>
85
+ )
86
+ }