@pradip1995/segment-animation-shop-grid 0.1.2 → 0.1.3
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 +5 -5
- package/src/animation-shop-grid.css +2 -2
- package/src/animation-shop-grid.tsx +168 -73
- package/src/manifest.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pradip1995/segment-animation-shop-grid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -15,10 +15,6 @@
|
|
|
15
15
|
".": "./src/index.ts",
|
|
16
16
|
"./manifest": "./src/manifest.ts"
|
|
17
17
|
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"typecheck": "tsc --noEmit",
|
|
20
|
-
"lint": "tsc --noEmit"
|
|
21
|
-
},
|
|
22
18
|
"peerDependencies": {
|
|
23
19
|
"@pradip1995/commerce-core": "^4.0.0",
|
|
24
20
|
"@pradip1995/medusa-connector": "^0.2.0",
|
|
@@ -38,5 +34,9 @@
|
|
|
38
34
|
"@types/react": "^19",
|
|
39
35
|
"react": "19.0.3",
|
|
40
36
|
"typescript": "^5.7.2"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"lint": "tsc --noEmit"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.animation-shop-grid {
|
|
2
2
|
width: 100%;
|
|
3
|
-
padding:
|
|
3
|
+
padding: 0 1.5rem 4rem 1.5rem;
|
|
4
4
|
background-color: var(--page-bg, #ffffff);
|
|
5
5
|
color: var(--text-color, #111111);
|
|
6
6
|
font-family: inherit;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
@media (min-width: 1024px) {
|
|
11
11
|
.animation-shop-grid {
|
|
12
|
-
padding:
|
|
12
|
+
padding: 0 3rem 6rem 3rem;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -119,6 +119,20 @@ function matchLabel(
|
|
|
119
119
|
return hit?.name || hit?.title || undefined
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
function formatSlugToTitle(slug?: string): string | undefined {
|
|
123
|
+
if (!slug) return undefined
|
|
124
|
+
if (slug.includes(",")) {
|
|
125
|
+
return slug
|
|
126
|
+
.split(",")
|
|
127
|
+
.map((s) => formatSlugToTitle(s))
|
|
128
|
+
.filter(Boolean)
|
|
129
|
+
.join(" & ")
|
|
130
|
+
}
|
|
131
|
+
return slug
|
|
132
|
+
.replace(/[-_]+/g, " ")
|
|
133
|
+
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
134
|
+
}
|
|
135
|
+
|
|
122
136
|
const containerVariants = {
|
|
123
137
|
hidden: { opacity: 0 },
|
|
124
138
|
visible: {
|
|
@@ -135,7 +149,7 @@ const cardVariants = {
|
|
|
135
149
|
visible: {
|
|
136
150
|
opacity: 1,
|
|
137
151
|
y: 0,
|
|
138
|
-
transition: { duration: 0.4, ease: [0.16, 1, 0.3, 1] },
|
|
152
|
+
transition: { duration: 0.4, ease: [0.16, 1, 0.3, 1] as const },
|
|
139
153
|
},
|
|
140
154
|
}
|
|
141
155
|
|
|
@@ -297,12 +311,23 @@ export default function AnimationShopGrid(props: {
|
|
|
297
311
|
|
|
298
312
|
const activeCategory = paramValue(props.category)
|
|
299
313
|
const activeCollection = paramValue(props.collection)
|
|
314
|
+
const activeGender = paramValue(props.gender)
|
|
315
|
+
const activeMaterial = paramValue(props.material)
|
|
316
|
+
const activeColor = paramValue(props.color)
|
|
317
|
+
const activeType = paramValue(props.type)
|
|
318
|
+
|
|
300
319
|
const categoryLabel =
|
|
301
320
|
matchLabel(activeCategory, props.categories || []) ||
|
|
302
|
-
(activeCategory
|
|
321
|
+
formatSlugToTitle(activeCategory)
|
|
322
|
+
|
|
303
323
|
const collectionLabel =
|
|
304
324
|
matchLabel(activeCollection, props.collections || []) ||
|
|
305
|
-
(activeCollection
|
|
325
|
+
formatSlugToTitle(activeCollection)
|
|
326
|
+
|
|
327
|
+
const genderLabel = formatSlugToTitle(activeGender)
|
|
328
|
+
const materialLabel = formatSlugToTitle(activeMaterial)
|
|
329
|
+
const colorLabel = formatSlugToTitle(activeColor)
|
|
330
|
+
const typeLabel = formatSlugToTitle(activeType)
|
|
306
331
|
|
|
307
332
|
const appliedFilters: { key: string; label: string; href: string }[] = []
|
|
308
333
|
if (props.q) {
|
|
@@ -327,90 +352,120 @@ export default function AnimationShopGrid(props: {
|
|
|
327
352
|
})
|
|
328
353
|
}
|
|
329
354
|
|
|
355
|
+
const displayTitle = useMemo(() => {
|
|
356
|
+
if (categoryLabel) return categoryLabel
|
|
357
|
+
if (collectionLabel) return collectionLabel
|
|
358
|
+
if (props.q) return `Search: ${props.q}`
|
|
359
|
+
if (genderLabel) return `${genderLabel}'s Collection`
|
|
360
|
+
if (materialLabel) return materialLabel
|
|
361
|
+
if (colorLabel) return colorLabel
|
|
362
|
+
if (typeLabel) return typeLabel
|
|
363
|
+
if (sortBy === "bestsellers") return "Bestsellers"
|
|
364
|
+
if (!props.title || props.title.toLowerCase() === "all beauty" || props.title.toLowerCase() === "all products") {
|
|
365
|
+
return "ALL PRODUCTS"
|
|
366
|
+
}
|
|
367
|
+
return props.title
|
|
368
|
+
}, [categoryLabel, collectionLabel, props.q, genderLabel, materialLabel, colorLabel, typeLabel, sortBy, props.title])
|
|
369
|
+
|
|
370
|
+
const displaySubtitle = useMemo(() => {
|
|
371
|
+
if (categoryLabel) return `Explore our curated selection of ${categoryLabel.toLowerCase()}.`
|
|
372
|
+
if (collectionLabel) return `Discover the latest items in our ${collectionLabel.toLowerCase()} collection.`
|
|
373
|
+
if (props.q) return `Product results matching "${props.q}".`
|
|
374
|
+
if (sortBy === "bestsellers") return "Our most popular and highly-rated pieces."
|
|
375
|
+
if (genderLabel || materialLabel || colorLabel) return "Handpicked items filtered to your taste."
|
|
376
|
+
return "The latest additions to the lineup."
|
|
377
|
+
}, [categoryLabel, collectionLabel, props.q, sortBy, genderLabel, materialLabel, colorLabel])
|
|
378
|
+
|
|
379
|
+
const [showFilters, setShowFilters] = useState(true)
|
|
380
|
+
|
|
381
|
+
useEffect(() => {
|
|
382
|
+
const sidebar = document.querySelector(".store-page__sidebar") as HTMLElement | null
|
|
383
|
+
const mainCol = document.querySelector(".store-page__main") as HTMLElement | null
|
|
384
|
+
if (sidebar) {
|
|
385
|
+
sidebar.style.display = showFilters ? "" : "none"
|
|
386
|
+
}
|
|
387
|
+
if (mainCol) {
|
|
388
|
+
if (!showFilters) {
|
|
389
|
+
mainCol.classList.add("w-full", "col-span-full")
|
|
390
|
+
} else {
|
|
391
|
+
mainCol.classList.remove("w-full", "col-span-full")
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}, [showFilters])
|
|
395
|
+
|
|
330
396
|
return (
|
|
331
|
-
<section className="animation-shop-grid flex-1 min-w-0" aria-label="Product results">
|
|
397
|
+
<section className="animation-shop-grid flex-1 min-w-0 pt-0" aria-label="Product results">
|
|
398
|
+
{/* Simplified, Clean Hero Header */}
|
|
332
399
|
<motion.header
|
|
333
|
-
className="
|
|
334
|
-
initial={{ opacity: 0, y: -
|
|
400
|
+
className="w-full bg-[#FAF9F6] py-6 px-4 mb-4 flex flex-col justify-center items-center text-center border-b border-gray-100 rounded-sm"
|
|
401
|
+
initial={{ opacity: 0, y: -6 }}
|
|
335
402
|
animate={{ opacity: 1, y: 0 }}
|
|
336
|
-
transition={{ duration: 0.
|
|
403
|
+
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
|
|
337
404
|
>
|
|
338
|
-
<
|
|
339
|
-
<
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
) : null}
|
|
346
|
-
{totalCount > 0 ? (
|
|
347
|
-
<p className="animation-shop-grid__count">
|
|
348
|
-
Showing {showingCount} of {totalCount} {totalCount === 1 ? "product" : "products"}
|
|
349
|
-
</p>
|
|
350
|
-
) : null}
|
|
351
|
-
</div>
|
|
405
|
+
<nav aria-label="Breadcrumb" className="text-[11px] text-gray-400 font-sans tracking-wide mb-1.5 uppercase">
|
|
406
|
+
<LocalizedLink href="/" className="hover:text-black transition-colors" countryCode={countryCode}>
|
|
407
|
+
Home
|
|
408
|
+
</LocalizedLink>
|
|
409
|
+
<span className="mx-2 text-gray-300">/</span>
|
|
410
|
+
<span className="text-gray-900 font-semibold tracking-wider">{displayTitle}</span>
|
|
411
|
+
</nav>
|
|
352
412
|
|
|
353
|
-
<
|
|
354
|
-
{
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
className={`animation-shop-grid__sort-pill${active ? " animation-shop-grid__sort-pill--active" : ""}`}
|
|
361
|
-
countryCode={countryCode}
|
|
362
|
-
>
|
|
363
|
-
{opt.label}
|
|
364
|
-
</LocalizedLink>
|
|
365
|
-
)
|
|
366
|
-
})}
|
|
367
|
-
</div>
|
|
413
|
+
<h1 className="text-xl sm:text-2xl font-normal tracking-widest text-[#111111] uppercase my-1">
|
|
414
|
+
{displayTitle}
|
|
415
|
+
</h1>
|
|
416
|
+
|
|
417
|
+
<p className="text-xs text-gray-500 font-normal tracking-wide max-w-sm m-0">
|
|
418
|
+
{displaySubtitle}
|
|
419
|
+
</p>
|
|
368
420
|
</motion.header>
|
|
369
421
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
animate={{ opacity: 1, height: "auto" }}
|
|
378
|
-
exit={{ opacity: 0, height: 0 }}
|
|
422
|
+
{/* Filter Toolbar with Hide Filters Option */}
|
|
423
|
+
<div className="w-full flex flex-wrap items-center justify-between gap-3 mb-5 py-2.5 px-4 bg-white border border-gray-200/80 rounded-md shadow-2xs">
|
|
424
|
+
<div className="flex items-center gap-3">
|
|
425
|
+
<button
|
|
426
|
+
type="button"
|
|
427
|
+
onClick={() => setShowFilters(!showFilters)}
|
|
428
|
+
className="inline-flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-gray-800 hover:text-black transition-colors py-1 px-2.5 bg-gray-50 hover:bg-gray-100 border border-gray-200 rounded cursor-pointer"
|
|
379
429
|
>
|
|
380
|
-
<
|
|
381
|
-
|
|
430
|
+
<svg className="w-3.5 h-3.5 fill-none stroke-current stroke-[1.8]" viewBox="0 0 24 24">
|
|
431
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9m-9 12h9m-15-6h15M3 6h3m-3 12h3m3-6h3" />
|
|
432
|
+
</svg>
|
|
433
|
+
<span>{showFilters ? "Hide Filters" : "Show Filters"}</span>
|
|
434
|
+
</button>
|
|
435
|
+
|
|
436
|
+
{appliedFilters.length > 0 ? (
|
|
437
|
+
<div className="hidden sm:flex items-center gap-2">
|
|
438
|
+
<span className="text-[11px] font-bold text-gray-400 uppercase tracking-wider">Active:</span>
|
|
382
439
|
{appliedFilters.map((filter) => (
|
|
383
|
-
<
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
×
|
|
393
|
-
</span>
|
|
394
|
-
</LocalizedLink>
|
|
395
|
-
</li>
|
|
440
|
+
<LocalizedLink
|
|
441
|
+
key={filter.key}
|
|
442
|
+
href={filter.href}
|
|
443
|
+
className="inline-flex items-center gap-1 text-xs font-medium bg-gray-100 hover:bg-gray-200 text-gray-900 px-2 py-0.5 rounded transition-colors"
|
|
444
|
+
countryCode={countryCode}
|
|
445
|
+
>
|
|
446
|
+
<span>{filter.label}</span>
|
|
447
|
+
<span className="text-gray-500 hover:text-black">×</span>
|
|
448
|
+
</LocalizedLink>
|
|
396
449
|
))}
|
|
397
|
-
</
|
|
450
|
+
</div>
|
|
451
|
+
) : null}
|
|
452
|
+
</div>
|
|
453
|
+
|
|
454
|
+
<div className="flex items-center gap-3 ml-auto">
|
|
455
|
+
<span className="text-xs text-gray-500 font-medium tracking-wide">
|
|
456
|
+
{showingCount} of {totalCount} {totalCount === 1 ? "Product" : "Products"}
|
|
457
|
+
</span>
|
|
458
|
+
{appliedFilters.length > 0 ? (
|
|
398
459
|
<LocalizedLink
|
|
399
460
|
href={buildClearAllFiltersHref(storeSearchParams)}
|
|
400
|
-
className="
|
|
461
|
+
className="text-xs font-bold text-red-600 hover:text-red-700 uppercase tracking-wider underline ml-2"
|
|
401
462
|
countryCode={countryCode}
|
|
402
463
|
>
|
|
403
|
-
Clear
|
|
464
|
+
Clear All
|
|
404
465
|
</LocalizedLink>
|
|
405
|
-
|
|
406
|
-
) : null}
|
|
407
|
-
</AnimatePresence>
|
|
408
|
-
|
|
409
|
-
{totalCount > 0 && sortBy !== "bestsellers" ? (
|
|
410
|
-
<div className="animation-shop-grid__progress" aria-hidden>
|
|
411
|
-
<span className="animation-shop-grid__progress-fill" style={{ width: `${progressPct}%` }} />
|
|
466
|
+
) : null}
|
|
412
467
|
</div>
|
|
413
|
-
|
|
468
|
+
</div>
|
|
414
469
|
|
|
415
470
|
{items.length === 0 ? (
|
|
416
471
|
<motion.div
|
|
@@ -437,7 +492,7 @@ export default function AnimationShopGrid(props: {
|
|
|
437
492
|
<ProductCard
|
|
438
493
|
product={product}
|
|
439
494
|
countryCode={countryCode}
|
|
440
|
-
variant={props.productCardVariant ?? "
|
|
495
|
+
variant={props.productCardVariant ?? "shop"}
|
|
441
496
|
/>
|
|
442
497
|
</motion.div>
|
|
443
498
|
))}
|
|
@@ -451,6 +506,46 @@ export default function AnimationShopGrid(props: {
|
|
|
451
506
|
</div>
|
|
452
507
|
) : null}
|
|
453
508
|
|
|
509
|
+
{/* Managed Pagination: Only show page buttons if totalPages > 1 */}
|
|
510
|
+
{Math.ceil(totalCount / PAGE_SIZE) > 1 ? (
|
|
511
|
+
<div className="animation-shop-grid__pagination flex items-center justify-center gap-2 mt-8 mb-2">
|
|
512
|
+
{Array.from({ length: Math.ceil(totalCount / PAGE_SIZE) }, (_, i) => i + 1).map((pageNum) => (
|
|
513
|
+
<button
|
|
514
|
+
key={pageNum}
|
|
515
|
+
type="button"
|
|
516
|
+
onClick={() => {
|
|
517
|
+
if (pageNum > currentPage && hasMore) {
|
|
518
|
+
loadNextPage()
|
|
519
|
+
}
|
|
520
|
+
}}
|
|
521
|
+
className={`w-8 h-8 rounded-full text-xs font-semibold flex items-center justify-center transition-all ${
|
|
522
|
+
currentPage === pageNum
|
|
523
|
+
? "bg-black text-white shadow-sm font-bold scale-105"
|
|
524
|
+
: "bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-black"
|
|
525
|
+
}`}
|
|
526
|
+
>
|
|
527
|
+
{pageNum}
|
|
528
|
+
</button>
|
|
529
|
+
))}
|
|
530
|
+
{hasMore && (
|
|
531
|
+
<button
|
|
532
|
+
type="button"
|
|
533
|
+
onClick={loadNextPage}
|
|
534
|
+
className="w-8 h-8 rounded-full bg-black hover:bg-gray-800 text-white flex items-center justify-center transition-all hover:scale-105 shadow-sm ml-1"
|
|
535
|
+
aria-label="Next page"
|
|
536
|
+
>
|
|
537
|
+
<svg className="w-3.5 h-3.5 fill-current" viewBox="0 0 20 20">
|
|
538
|
+
<path
|
|
539
|
+
fillRule="evenodd"
|
|
540
|
+
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
|
541
|
+
clipRule="evenodd"
|
|
542
|
+
/>
|
|
543
|
+
</svg>
|
|
544
|
+
</button>
|
|
545
|
+
)}
|
|
546
|
+
</div>
|
|
547
|
+
) : null}
|
|
548
|
+
|
|
454
549
|
{loadError ? (
|
|
455
550
|
<div className="animation-shop-grid__error">
|
|
456
551
|
<p>{loadError}</p>
|
|
@@ -461,7 +556,7 @@ export default function AnimationShopGrid(props: {
|
|
|
461
556
|
) : null}
|
|
462
557
|
|
|
463
558
|
{!hasMore && items.length > 0 ? (
|
|
464
|
-
<p className="animation-shop-grid__end">
|
|
559
|
+
<p className="animation-shop-grid__end text-xs tracking-wider text-gray-500 uppercase font-medium mt-4">
|
|
465
560
|
You've viewed all {totalCount} {totalCount === 1 ? "product" : "products"}
|
|
466
561
|
</p>
|
|
467
562
|
) : null}
|
package/src/manifest.ts
CHANGED