@xiwebdesign/xicms-core 5.6.0 → 5.6.1
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.
|
@@ -65,52 +65,44 @@ function ProductCard({
|
|
|
65
65
|
const simple = (product.options ?? []).length === 0 && !(product.personalization ?? []).length;
|
|
66
66
|
const soldOut = isSoldOut(product);
|
|
67
67
|
const image = hoverImage ?? product.images?.[0];
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
loading: "lazy",
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
return (
|
|
69
|
+
// Flex-kolom over de volle kaarthoogte: titel (vast 2 regels) → prijs → swatches →
|
|
70
|
+
// knop onderaan. Zo lijnen prijs/swatches/knop over álle kaarten in de rij uit, ook
|
|
71
|
+
// als een productnaam één of twee regels beslaat. Alle opmaak in styles.css.
|
|
72
|
+
/* @__PURE__ */ jsxs(Card, { padding: 0, className: "xicms-shop-card", children: [
|
|
73
|
+
/* @__PURE__ */ jsxs("a", { className: "xicms-shop-card__link", href, children: [
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { className: "xicms-shop-card__media", children: [
|
|
75
|
+
image ? /* @__PURE__ */ jsx("img", { src: image, alt: product.name, loading: "lazy", className: "xicms-shop-card__img" }) : /* @__PURE__ */ jsx("div", { className: "xicms-shop-card__placeholder", "aria-hidden": "true" }),
|
|
76
|
+
soldOut && /* @__PURE__ */ jsx("span", { className: "xicms-shop-card__soldout", children: t("soldOut") })
|
|
77
|
+
] }),
|
|
78
|
+
/* @__PURE__ */ jsxs("div", { className: "xicms-shop-card__body", children: [
|
|
79
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-shop-card__name", children: product.name }),
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-shop-card__priceline", children: /* @__PURE__ */ jsx(PriceLine, { product, currency }) }),
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: "xicms-shop-card__swatchrow", children: /* @__PURE__ */ jsx(ColorSwatches, { product, onHover: setHoverImage }) })
|
|
82
|
+
] })
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsx("div", { className: "xicms-shop-card__actions", children: simple ? added ? /* @__PURE__ */ jsx("a", { className: "xicms-shop-card__added", href: cartPath, children: t("addedGoToCart") }) : /* @__PURE__ */ jsx(
|
|
85
|
+
"button",
|
|
80
86
|
{
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
|
|
87
|
+
type: "button",
|
|
88
|
+
className: "xicms-btn xicms-btn--primary",
|
|
89
|
+
disabled: soldOut,
|
|
90
|
+
style: { cursor: soldOut ? "not-allowed" : "pointer" },
|
|
91
|
+
onClick: () => {
|
|
92
|
+
cartAdd(siteId, {
|
|
93
|
+
productId: product.id,
|
|
94
|
+
slug: product.slug,
|
|
95
|
+
name: product.name,
|
|
96
|
+
unitPriceCents: product.priceCents,
|
|
97
|
+
imageUrl: product.images?.[0]
|
|
98
|
+
});
|
|
99
|
+
setAdded(true);
|
|
100
|
+
},
|
|
101
|
+
children: t("addToCart")
|
|
84
102
|
}
|
|
85
|
-
),
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
/* @__PURE__ */ jsx("span", { style: { fontWeight: 700 }, children: product.name }),
|
|
89
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.92em" }, children: /* @__PURE__ */ jsx(PriceLine, { product, currency }) }),
|
|
90
|
-
/* @__PURE__ */ jsx("span", { style: { display: "inline-flex", gap: 5, minHeight: 12 }, children: /* @__PURE__ */ jsx(ColorSwatches, { product, onHover: setHoverImage }) })
|
|
91
|
-
] })
|
|
92
|
-
] }),
|
|
93
|
-
/* @__PURE__ */ jsx("div", { className: "xicms-shop-card__actions", style: { padding: "0.5rem 1rem 1rem" }, children: simple ? added ? /* @__PURE__ */ jsx("a", { className: "xicms-shop-card__added", href: cartPath, style: { fontSize: "0.9rem" }, children: t("addedGoToCart") }) : /* @__PURE__ */ jsx(
|
|
94
|
-
"button",
|
|
95
|
-
{
|
|
96
|
-
type: "button",
|
|
97
|
-
className: "xicms-btn xicms-btn--primary",
|
|
98
|
-
disabled: soldOut,
|
|
99
|
-
style: { cursor: soldOut ? "not-allowed" : "pointer" },
|
|
100
|
-
onClick: () => {
|
|
101
|
-
cartAdd(siteId, {
|
|
102
|
-
productId: product.id,
|
|
103
|
-
slug: product.slug,
|
|
104
|
-
name: product.name,
|
|
105
|
-
unitPriceCents: product.priceCents,
|
|
106
|
-
imageUrl: product.images?.[0]
|
|
107
|
-
});
|
|
108
|
-
setAdded(true);
|
|
109
|
-
},
|
|
110
|
-
children: t("addToCart")
|
|
111
|
-
}
|
|
112
|
-
) : /* @__PURE__ */ jsx("a", { className: "xicms-btn xicms-btn--secondary", href, children: t("view") }) })
|
|
113
|
-
] });
|
|
103
|
+
) : /* @__PURE__ */ jsx("a", { className: "xicms-btn xicms-btn--secondary", href, children: t("view") }) })
|
|
104
|
+
] })
|
|
105
|
+
);
|
|
114
106
|
}
|
|
115
107
|
function ProductGridRender({ block, siteId }) {
|
|
116
108
|
const settings = useShopSettings();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/shop/blocks/ProductGrid.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport type { z } from \"zod\";\nimport { ProductGridBlockSchema, formatCents } from \"../../../schema/shop\";\nimport { defineBlock, type BlockRenderProps } from \"../../../blocks/registry\";\nimport { useBlockData } from \"../../../blocks/context\";\nimport { Card, Heading, Text } from \"../../../blocks/ui\";\nimport { cartAdd } from \"../cartStore\";\nimport { useShopStrings, type ShopT } from \"../strings\";\nimport {\n fetchActiveProducts,\n fetchShopCategories,\n kleurNaarCss,\n kleurOptie,\n readQueryParam,\n useShopSettings,\n type StoredProduct,\n type StoredShopCategory,\n} from \"../lib\";\nimport type { ShopPageData } from \"../types\";\n\ntype Blk = z.infer<typeof ProductGridBlockSchema>;\n\n/** Prijsregel met optionele doorgestreepte \"van\"-prijs en \"vanaf\" bij variant-prijzen. */\nexport function PriceLine({ product, currency }: { product: StoredProduct; currency: string }) {\n const variantPrices = (product.variants ?? [])\n .filter((v) => v.priceCents !== undefined)\n .map((v) => v.priceCents as number);\n const min = Math.min(product.priceCents, ...variantPrices);\n const hasRange = variantPrices.some((p) => p !== product.priceCents);\n return (\n <span className=\"xicms-shop-price\">\n {product.compareAtCents !== undefined && product.compareAtCents > min && (\n <s style={{ opacity: 0.6, marginRight: 6 }}>{formatCents(product.compareAtCents, currency)}</s>\n )}\n {hasRange ? `vanaf ${formatCents(min, currency)}` : formatCents(min, currency)}\n </span>\n );\n}\n\n/** Uitverkocht (alleen betrouwbaar vast te stellen zonder opties: expliciete voorraad 0). */\nfunction isSoldOut(p: StoredProduct): boolean {\n return p.trackStock === true && (p.options ?? []).length === 0 && p.stock !== undefined && p.stock <= 0;\n}\n\n/** Stof-stipjes; hover wisselt de kaartfoto naar de per-kleur-afbeelding (als die er is). */\nfunction ColorSwatches({ product, onHover }: { product: StoredProduct; onHover?: (url: string | null) => void }) {\n const optie = kleurOptie(product);\n if (!optie) return null;\n const dots = optie.values\n .map((v) => ({ v, css: kleurNaarCss(v), img: optie.images?.[v] }))\n .filter((d): d is { v: string; css: string; img: string | undefined } => d.css !== null);\n if (dots.length === 0) return null;\n return (\n <span className=\"xicms-shop-card__swatches\" aria-label={`Kleuren: ${optie.values.join(\", \")}`}>\n {dots.map((d) => (\n <span\n key={d.v}\n className=\"xicms-shop-card__swatch\"\n title={d.v}\n onMouseEnter={d.img && onHover ? () => onHover(d.img ?? null) : undefined}\n onMouseLeave={d.img && onHover ? () => onHover(null) : undefined}\n style={{\n display: \"inline-block\",\n width: 12,\n height: 12,\n borderRadius: 999,\n background: d.css,\n boxShadow: \"inset 0 0 0 1px rgba(0,0,0,0.18)\",\n }}\n />\n ))}\n </span>\n );\n}\n\nexport function ProductCard({\n product,\n siteId,\n currency,\n detailPath,\n cartPath,\n t,\n}: {\n product: StoredProduct;\n siteId: string;\n currency: string;\n detailPath: string;\n cartPath: string;\n t: ShopT;\n}) {\n const [added, setAdded] = useState(false);\n const [hoverImage, setHoverImage] = useState<string | null>(null);\n const href = `${detailPath}?p=${encodeURIComponent(product.slug)}`;\n const simple = (product.options ?? []).length === 0 && !(product.personalization ?? []).length;\n const soldOut = isSoldOut(product);\n const image = hoverImage ?? product.images?.[0];\n\n return (\n <Card padding={0} className=\"xicms-shop-card\">\n <a href={href} style={{ textDecoration: \"none\", color: \"inherit\", display: \"block\", position: \"relative\" }}>\n {image ? (\n <img\n src={image}\n alt={product.name}\n loading=\"lazy\"\n style={{ width: \"100%\", aspectRatio: \"1/1\", objectFit: \"cover\", display: \"block\", borderRadius: \"inherit\" }}\n />\n ) : (\n <div\n className=\"xicms-shop-card__placeholder\"\n aria-hidden=\"true\"\n style={{ width: \"100%\", aspectRatio: \"1/1\", background: \"var(--xicms-muted, #f1f1f1)\", borderRadius: \"inherit\" }}\n />\n )}\n {soldOut && <span className=\"xicms-shop-card__soldout\">{t(\"soldOut\")}</span>}\n <div style={{ padding: \"0.8rem 1rem 0.25rem\", display: \"grid\", gap: 4 }}>\n <span style={{ fontWeight: 700 }}>{product.name}</span>\n <span style={{ fontSize: \"0.92em\" }}>\n <PriceLine product={product} currency={currency} />\n </span>\n <span style={{ display: \"inline-flex\", gap: 5, minHeight: 12 }}>\n <ColorSwatches product={product} onHover={setHoverImage} />\n </span>\n </div>\n </a>\n <div className=\"xicms-shop-card__actions\" style={{ padding: \"0.5rem 1rem 1rem\" }}>\n {simple ? (\n added ? (\n <a className=\"xicms-shop-card__added\" href={cartPath} style={{ fontSize: \"0.9rem\" }}>\n {t(\"addedGoToCart\")}\n </a>\n ) : (\n <button\n type=\"button\"\n className=\"xicms-btn xicms-btn--primary\"\n disabled={soldOut}\n style={{ cursor: soldOut ? \"not-allowed\" : \"pointer\" }}\n onClick={() => {\n cartAdd(siteId, {\n productId: product.id,\n slug: product.slug,\n name: product.name,\n unitPriceCents: product.priceCents,\n imageUrl: product.images?.[0],\n });\n setAdded(true);\n }}\n >\n {t(\"addToCart\")}\n </button>\n )\n ) : (\n <a className=\"xicms-btn xicms-btn--secondary\" href={href}>\n {t(\"view\")}\n </a>\n )}\n </div>\n </Card>\n );\n}\n\nfunction ProductGridRender({ block, siteId }: BlockRenderProps<Blk>) {\n const settings = useShopSettings();\n const t = useShopStrings();\n const serverData = useBlockData<ShopPageData>(\"shop\");\n const [products, setProducts] = useState<StoredProduct[] | null>(serverData?.products ?? null);\n const [cats, setCats] = useState<StoredShopCategory[]>(serverData?.categories ?? []);\n const [queryCat, setQueryCat] = useState<string | null>(null);\n const [filterColor, setFilterColor] = useState<string | null>(null);\n const [filterSize, setFilterSize] = useState<string | null>(null);\n const [sort, setSort] = useState<Blk[\"sort\"]>(block.sort);\n\n useEffect(() => {\n // Server-data aanwezig (SSR/ISR)? Dan geen client-fetch — geen flits, minder reads.\n if (!serverData?.products) {\n void fetchActiveProducts(siteId).then(setProducts).catch((e) => {\n console.warn(\"[xicms] producten laden mislukt:\", e);\n setProducts([]);\n });\n }\n if (!serverData?.categories) {\n void fetchShopCategories(siteId).then(setCats).catch(() => setCats([]));\n }\n if (block.useQueryCategory && !block.categorySlug) setQueryCat(readQueryParam(\"cat\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [siteId, block.useQueryCategory, block.categorySlug]);\n\n const activeSlug = block.categorySlug || queryCat || null;\n const activeCat = activeSlug ? cats.find((c) => c.slug === activeSlug) ?? null : null;\n\n /** Beschikbare filterwaarden uit de geladen set (client-side; geen extra reads). */\n const filterValues = useMemo(() => {\n if (!block.showFilters || !products) return { colors: [] as string[], sizes: [] as string[] };\n const colors = new Set<string>();\n const sizes = new Set<string>();\n for (const p of products) {\n for (const o of p.options ?? []) {\n if (/^kleur|^colou?r/i.test(o.name)) o.values.forEach((v) => colors.add(v));\n if (/^maat|^size/i.test(o.name)) o.values.forEach((v) => sizes.add(v));\n }\n }\n return { colors: [...colors], sizes: [...sizes].sort((a, b) => Number(a) - Number(b) || a.localeCompare(b)) };\n }, [products, block.showFilters]);\n\n const visible = useMemo(() => {\n if (!products) return [];\n let list = products;\n if (activeCat) list = list.filter((p) => (p.categoryIds ?? []).includes(activeCat.id));\n if (block.featuredOnly) list = list.filter((p) => p.featured);\n if (filterColor) {\n list = list.filter((p) =>\n (p.options ?? []).some((o) => /^kleur|^colou?r/i.test(o.name) && o.values.includes(filterColor)),\n );\n }\n if (filterSize) {\n list = list.filter((p) =>\n (p.options ?? []).some((o) => /^maat|^size/i.test(o.name) && o.values.includes(filterSize)),\n );\n }\n const sorted = [...list];\n if (sort === \"name\") sorted.sort((a, b) => a.name.localeCompare(b.name, \"nl\"));\n else if (sort === \"priceAsc\") sorted.sort((a, b) => a.priceCents - b.priceCents);\n else if (sort === \"priceDesc\") sorted.sort((a, b) => b.priceCents - a.priceCents);\n else sorted.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name, \"nl\"));\n return sorted.slice(0, block.limit);\n }, [products, activeCat, block.featuredOnly, block.limit, sort, filterColor, filterSize]);\n\n if (products === null) return null;\n\n const chip = (label: string, active: boolean, onClick: () => void, swatch?: string | null) => (\n <button\n key={label}\n type=\"button\"\n className={`xicms-shop-filterchip${active ? \" xicms-shop-filterchip--active\" : \"\"}`}\n aria-pressed={active}\n onClick={onClick}\n >\n {swatch && (\n <span\n aria-hidden\n style={{ display: \"inline-block\", width: 10, height: 10, borderRadius: 999, background: swatch, boxShadow: \"inset 0 0 0 1px rgba(0,0,0,0.18)\", marginRight: 5 }}\n />\n )}\n {label}\n </button>\n );\n\n return (\n <div className=\"xicms-shop-grid\">\n {(block.heading || activeCat) && (\n <Heading level={2} data-xicms-edit=\"heading\">\n {activeCat?.name ?? block.heading}\n </Heading>\n )}\n {activeCat && (\n <Text size=\"sm\" display=\"block\" style={{ marginTop: 4 }}>\n <a href={settings.shopPath}>← {t(\"allCategories\")}</a>\n </Text>\n )}\n\n {block.showFilters && (filterValues.colors.length > 0 || filterValues.sizes.length > 0) && (\n <div className=\"xicms-shop-filters\">\n {filterValues.colors.length > 0 && (\n <div className=\"xicms-shop-filters__row\" role=\"group\" aria-label=\"Kleur\">\n {chip(t(\"filterAll\"), filterColor === null, () => setFilterColor(null))}\n {filterValues.colors.map((c) =>\n chip(c, filterColor === c, () => setFilterColor(filterColor === c ? null : c), kleurNaarCss(c)),\n )}\n </div>\n )}\n {filterValues.sizes.length > 0 && (\n <div className=\"xicms-shop-filters__row\" role=\"group\" aria-label=\"Maat\">\n {chip(t(\"filterAll\"), filterSize === null, () => setFilterSize(null))}\n {filterValues.sizes.map((s) => chip(s, filterSize === s, () => setFilterSize(filterSize === s ? null : s)))}\n </div>\n )}\n <label className=\"xicms-shop-filters__sort\">\n <span>{t(\"sortLabel\")}</span>\n <select className=\"xicms-input\" value={sort} onChange={(e) => setSort(e.target.value as Blk[\"sort\"])}>\n <option value=\"order\">{t(\"sortDefault\")}</option>\n <option value=\"name\">{t(\"sortName\")}</option>\n <option value=\"priceAsc\">{t(\"sortPriceAsc\")}</option>\n <option value=\"priceDesc\">{t(\"sortPriceDesc\")}</option>\n </select>\n </label>\n </div>\n )}\n\n {visible.length === 0 ? (\n <Card padding={4} variant=\"muted\" style={{ marginTop: \"1rem\" }}>\n <Text>{t(\"noProducts\")}</Text>\n </Card>\n ) : (\n <div\n className=\"xicms-shop-grid__items\"\n data-columns={block.columns}\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(auto-fill, minmax(min(240px, calc(100%/${block.columns} - 1rem)), 1fr))`,\n gap: \"1.25rem\",\n marginTop: \"1.25rem\",\n }}\n >\n {visible.map((p) => (\n <ProductCard\n key={p.id}\n product={p}\n siteId={siteId}\n currency={settings.currency}\n detailPath={settings.detailPath}\n cartPath={settings.cartPath}\n t={t}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport const productGridBlock = defineBlock({\n type: \"productGrid\",\n schema: ProductGridBlockSchema,\n Render: ProductGridRender,\n label: \"Producten-overzicht\",\n icon: \"🛍️\",\n defaults: () => ({ useQueryCategory: true, columns: 3, limit: 60, sort: \"order\" }),\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/shop/blocks/ProductGrid.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport type { z } from \"zod\";\nimport { ProductGridBlockSchema, formatCents } from \"../../../schema/shop\";\nimport { defineBlock, type BlockRenderProps } from \"../../../blocks/registry\";\nimport { useBlockData } from \"../../../blocks/context\";\nimport { Card, Heading, Text } from \"../../../blocks/ui\";\nimport { cartAdd } from \"../cartStore\";\nimport { useShopStrings, type ShopT } from \"../strings\";\nimport {\n fetchActiveProducts,\n fetchShopCategories,\n kleurNaarCss,\n kleurOptie,\n readQueryParam,\n useShopSettings,\n type StoredProduct,\n type StoredShopCategory,\n} from \"../lib\";\nimport type { ShopPageData } from \"../types\";\n\ntype Blk = z.infer<typeof ProductGridBlockSchema>;\n\n/** Prijsregel met optionele doorgestreepte \"van\"-prijs en \"vanaf\" bij variant-prijzen. */\nexport function PriceLine({ product, currency }: { product: StoredProduct; currency: string }) {\n const variantPrices = (product.variants ?? [])\n .filter((v) => v.priceCents !== undefined)\n .map((v) => v.priceCents as number);\n const min = Math.min(product.priceCents, ...variantPrices);\n const hasRange = variantPrices.some((p) => p !== product.priceCents);\n return (\n <span className=\"xicms-shop-price\">\n {product.compareAtCents !== undefined && product.compareAtCents > min && (\n <s style={{ opacity: 0.6, marginRight: 6 }}>{formatCents(product.compareAtCents, currency)}</s>\n )}\n {hasRange ? `vanaf ${formatCents(min, currency)}` : formatCents(min, currency)}\n </span>\n );\n}\n\n/** Uitverkocht (alleen betrouwbaar vast te stellen zonder opties: expliciete voorraad 0). */\nfunction isSoldOut(p: StoredProduct): boolean {\n return p.trackStock === true && (p.options ?? []).length === 0 && p.stock !== undefined && p.stock <= 0;\n}\n\n/** Stof-stipjes; hover wisselt de kaartfoto naar de per-kleur-afbeelding (als die er is). */\nfunction ColorSwatches({ product, onHover }: { product: StoredProduct; onHover?: (url: string | null) => void }) {\n const optie = kleurOptie(product);\n if (!optie) return null;\n const dots = optie.values\n .map((v) => ({ v, css: kleurNaarCss(v), img: optie.images?.[v] }))\n .filter((d): d is { v: string; css: string; img: string | undefined } => d.css !== null);\n if (dots.length === 0) return null;\n return (\n <span className=\"xicms-shop-card__swatches\" aria-label={`Kleuren: ${optie.values.join(\", \")}`}>\n {dots.map((d) => (\n <span\n key={d.v}\n className=\"xicms-shop-card__swatch\"\n title={d.v}\n onMouseEnter={d.img && onHover ? () => onHover(d.img ?? null) : undefined}\n onMouseLeave={d.img && onHover ? () => onHover(null) : undefined}\n style={{\n display: \"inline-block\",\n width: 12,\n height: 12,\n borderRadius: 999,\n background: d.css,\n boxShadow: \"inset 0 0 0 1px rgba(0,0,0,0.18)\",\n }}\n />\n ))}\n </span>\n );\n}\n\nexport function ProductCard({\n product,\n siteId,\n currency,\n detailPath,\n cartPath,\n t,\n}: {\n product: StoredProduct;\n siteId: string;\n currency: string;\n detailPath: string;\n cartPath: string;\n t: ShopT;\n}) {\n const [added, setAdded] = useState(false);\n const [hoverImage, setHoverImage] = useState<string | null>(null);\n const href = `${detailPath}?p=${encodeURIComponent(product.slug)}`;\n const simple = (product.options ?? []).length === 0 && !(product.personalization ?? []).length;\n const soldOut = isSoldOut(product);\n const image = hoverImage ?? product.images?.[0];\n\n return (\n // Flex-kolom over de volle kaarthoogte: titel (vast 2 regels) → prijs → swatches →\n // knop onderaan. Zo lijnen prijs/swatches/knop over álle kaarten in de rij uit, ook\n // als een productnaam één of twee regels beslaat. Alle opmaak in styles.css.\n <Card padding={0} className=\"xicms-shop-card\">\n <a className=\"xicms-shop-card__link\" href={href}>\n <div className=\"xicms-shop-card__media\">\n {image ? (\n <img src={image} alt={product.name} loading=\"lazy\" className=\"xicms-shop-card__img\" />\n ) : (\n <div className=\"xicms-shop-card__placeholder\" aria-hidden=\"true\" />\n )}\n {soldOut && <span className=\"xicms-shop-card__soldout\">{t(\"soldOut\")}</span>}\n </div>\n <div className=\"xicms-shop-card__body\">\n <span className=\"xicms-shop-card__name\">{product.name}</span>\n <span className=\"xicms-shop-card__priceline\">\n <PriceLine product={product} currency={currency} />\n </span>\n <span className=\"xicms-shop-card__swatchrow\">\n <ColorSwatches product={product} onHover={setHoverImage} />\n </span>\n </div>\n </a>\n <div className=\"xicms-shop-card__actions\">\n {simple ? (\n added ? (\n <a className=\"xicms-shop-card__added\" href={cartPath}>\n {t(\"addedGoToCart\")}\n </a>\n ) : (\n <button\n type=\"button\"\n className=\"xicms-btn xicms-btn--primary\"\n disabled={soldOut}\n style={{ cursor: soldOut ? \"not-allowed\" : \"pointer\" }}\n onClick={() => {\n cartAdd(siteId, {\n productId: product.id,\n slug: product.slug,\n name: product.name,\n unitPriceCents: product.priceCents,\n imageUrl: product.images?.[0],\n });\n setAdded(true);\n }}\n >\n {t(\"addToCart\")}\n </button>\n )\n ) : (\n <a className=\"xicms-btn xicms-btn--secondary\" href={href}>\n {t(\"view\")}\n </a>\n )}\n </div>\n </Card>\n );\n}\n\nfunction ProductGridRender({ block, siteId }: BlockRenderProps<Blk>) {\n const settings = useShopSettings();\n const t = useShopStrings();\n const serverData = useBlockData<ShopPageData>(\"shop\");\n const [products, setProducts] = useState<StoredProduct[] | null>(serverData?.products ?? null);\n const [cats, setCats] = useState<StoredShopCategory[]>(serverData?.categories ?? []);\n const [queryCat, setQueryCat] = useState<string | null>(null);\n const [filterColor, setFilterColor] = useState<string | null>(null);\n const [filterSize, setFilterSize] = useState<string | null>(null);\n const [sort, setSort] = useState<Blk[\"sort\"]>(block.sort);\n\n useEffect(() => {\n // Server-data aanwezig (SSR/ISR)? Dan geen client-fetch — geen flits, minder reads.\n if (!serverData?.products) {\n void fetchActiveProducts(siteId).then(setProducts).catch((e) => {\n console.warn(\"[xicms] producten laden mislukt:\", e);\n setProducts([]);\n });\n }\n if (!serverData?.categories) {\n void fetchShopCategories(siteId).then(setCats).catch(() => setCats([]));\n }\n if (block.useQueryCategory && !block.categorySlug) setQueryCat(readQueryParam(\"cat\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [siteId, block.useQueryCategory, block.categorySlug]);\n\n const activeSlug = block.categorySlug || queryCat || null;\n const activeCat = activeSlug ? cats.find((c) => c.slug === activeSlug) ?? null : null;\n\n /** Beschikbare filterwaarden uit de geladen set (client-side; geen extra reads). */\n const filterValues = useMemo(() => {\n if (!block.showFilters || !products) return { colors: [] as string[], sizes: [] as string[] };\n const colors = new Set<string>();\n const sizes = new Set<string>();\n for (const p of products) {\n for (const o of p.options ?? []) {\n if (/^kleur|^colou?r/i.test(o.name)) o.values.forEach((v) => colors.add(v));\n if (/^maat|^size/i.test(o.name)) o.values.forEach((v) => sizes.add(v));\n }\n }\n return { colors: [...colors], sizes: [...sizes].sort((a, b) => Number(a) - Number(b) || a.localeCompare(b)) };\n }, [products, block.showFilters]);\n\n const visible = useMemo(() => {\n if (!products) return [];\n let list = products;\n if (activeCat) list = list.filter((p) => (p.categoryIds ?? []).includes(activeCat.id));\n if (block.featuredOnly) list = list.filter((p) => p.featured);\n if (filterColor) {\n list = list.filter((p) =>\n (p.options ?? []).some((o) => /^kleur|^colou?r/i.test(o.name) && o.values.includes(filterColor)),\n );\n }\n if (filterSize) {\n list = list.filter((p) =>\n (p.options ?? []).some((o) => /^maat|^size/i.test(o.name) && o.values.includes(filterSize)),\n );\n }\n const sorted = [...list];\n if (sort === \"name\") sorted.sort((a, b) => a.name.localeCompare(b.name, \"nl\"));\n else if (sort === \"priceAsc\") sorted.sort((a, b) => a.priceCents - b.priceCents);\n else if (sort === \"priceDesc\") sorted.sort((a, b) => b.priceCents - a.priceCents);\n else sorted.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name, \"nl\"));\n return sorted.slice(0, block.limit);\n }, [products, activeCat, block.featuredOnly, block.limit, sort, filterColor, filterSize]);\n\n if (products === null) return null;\n\n const chip = (label: string, active: boolean, onClick: () => void, swatch?: string | null) => (\n <button\n key={label}\n type=\"button\"\n className={`xicms-shop-filterchip${active ? \" xicms-shop-filterchip--active\" : \"\"}`}\n aria-pressed={active}\n onClick={onClick}\n >\n {swatch && (\n <span\n aria-hidden\n style={{ display: \"inline-block\", width: 10, height: 10, borderRadius: 999, background: swatch, boxShadow: \"inset 0 0 0 1px rgba(0,0,0,0.18)\", marginRight: 5 }}\n />\n )}\n {label}\n </button>\n );\n\n return (\n <div className=\"xicms-shop-grid\">\n {(block.heading || activeCat) && (\n <Heading level={2} data-xicms-edit=\"heading\">\n {activeCat?.name ?? block.heading}\n </Heading>\n )}\n {activeCat && (\n <Text size=\"sm\" display=\"block\" style={{ marginTop: 4 }}>\n <a href={settings.shopPath}>← {t(\"allCategories\")}</a>\n </Text>\n )}\n\n {block.showFilters && (filterValues.colors.length > 0 || filterValues.sizes.length > 0) && (\n <div className=\"xicms-shop-filters\">\n {filterValues.colors.length > 0 && (\n <div className=\"xicms-shop-filters__row\" role=\"group\" aria-label=\"Kleur\">\n {chip(t(\"filterAll\"), filterColor === null, () => setFilterColor(null))}\n {filterValues.colors.map((c) =>\n chip(c, filterColor === c, () => setFilterColor(filterColor === c ? null : c), kleurNaarCss(c)),\n )}\n </div>\n )}\n {filterValues.sizes.length > 0 && (\n <div className=\"xicms-shop-filters__row\" role=\"group\" aria-label=\"Maat\">\n {chip(t(\"filterAll\"), filterSize === null, () => setFilterSize(null))}\n {filterValues.sizes.map((s) => chip(s, filterSize === s, () => setFilterSize(filterSize === s ? null : s)))}\n </div>\n )}\n <label className=\"xicms-shop-filters__sort\">\n <span>{t(\"sortLabel\")}</span>\n <select className=\"xicms-input\" value={sort} onChange={(e) => setSort(e.target.value as Blk[\"sort\"])}>\n <option value=\"order\">{t(\"sortDefault\")}</option>\n <option value=\"name\">{t(\"sortName\")}</option>\n <option value=\"priceAsc\">{t(\"sortPriceAsc\")}</option>\n <option value=\"priceDesc\">{t(\"sortPriceDesc\")}</option>\n </select>\n </label>\n </div>\n )}\n\n {visible.length === 0 ? (\n <Card padding={4} variant=\"muted\" style={{ marginTop: \"1rem\" }}>\n <Text>{t(\"noProducts\")}</Text>\n </Card>\n ) : (\n <div\n className=\"xicms-shop-grid__items\"\n data-columns={block.columns}\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(auto-fill, minmax(min(240px, calc(100%/${block.columns} - 1rem)), 1fr))`,\n gap: \"1.25rem\",\n marginTop: \"1.25rem\",\n }}\n >\n {visible.map((p) => (\n <ProductCard\n key={p.id}\n product={p}\n siteId={siteId}\n currency={settings.currency}\n detailPath={settings.detailPath}\n cartPath={settings.cartPath}\n t={t}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport const productGridBlock = defineBlock({\n type: \"productGrid\",\n schema: ProductGridBlockSchema,\n Render: ProductGridRender,\n label: \"Producten-overzicht\",\n icon: \"🛍️\",\n defaults: () => ({ useQueryCategory: true, columns: 3, limit: 60, sort: \"order\" }),\n});\n"],"mappings":"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","names":[]}
|
package/dist/styles.css
CHANGED
|
@@ -2702,13 +2702,81 @@
|
|
|
2702
2702
|
|
|
2703
2703
|
/* ── Shop (5.6) ─────────────────────────────────────────────────────────── */
|
|
2704
2704
|
|
|
2705
|
+
/* Uitgelijnde productkaart: flex-kolom over de volle hoogte. De grid-cellen zijn even
|
|
2706
|
+
hoog (align-items: stretch is de default), de kaart vult de cel, en de knop-rij wordt
|
|
2707
|
+
met margin-top:auto naar onderen gedrukt. Zo staan prijs, kleurstipjes en knop over
|
|
2708
|
+
álle kaarten op dezelfde lijn — ook als een productnaam één of twee regels beslaat. */
|
|
2709
|
+
.xicms-shop-card {
|
|
2710
|
+
display: flex;
|
|
2711
|
+
flex-direction: column;
|
|
2712
|
+
height: 100%;
|
|
2713
|
+
overflow: hidden;
|
|
2714
|
+
}
|
|
2715
|
+
.xicms-shop-card__link {
|
|
2716
|
+
display: flex;
|
|
2717
|
+
flex-direction: column;
|
|
2718
|
+
flex: 1 1 auto;
|
|
2719
|
+
text-decoration: none;
|
|
2720
|
+
color: inherit;
|
|
2721
|
+
}
|
|
2722
|
+
.xicms-shop-card__media {
|
|
2723
|
+
position: relative;
|
|
2724
|
+
}
|
|
2725
|
+
.xicms-shop-card__img,
|
|
2726
|
+
.xicms-shop-card__placeholder {
|
|
2727
|
+
display: block;
|
|
2728
|
+
width: 100%;
|
|
2729
|
+
aspect-ratio: 1 / 1;
|
|
2730
|
+
object-fit: cover;
|
|
2731
|
+
border-radius: inherit;
|
|
2732
|
+
background: var(--xicms-muted, #f1f1f1);
|
|
2733
|
+
}
|
|
2734
|
+
.xicms-shop-card__body {
|
|
2735
|
+
display: flex;
|
|
2736
|
+
flex-direction: column;
|
|
2737
|
+
gap: 0.25rem;
|
|
2738
|
+
padding: 0.8rem 1rem 0.25rem;
|
|
2739
|
+
flex: 1 1 auto;
|
|
2740
|
+
}
|
|
2741
|
+
/* Productnaam: reserveer altijd twee regels zodat één- en twee-regelige namen op dezelfde
|
|
2742
|
+
basislijn staan; langer dan twee regels wordt met een ellipsis afgekapt. */
|
|
2743
|
+
.xicms-shop-card__name {
|
|
2744
|
+
font-weight: 700;
|
|
2745
|
+
line-height: 1.25;
|
|
2746
|
+
min-height: 2.5em;
|
|
2747
|
+
display: -webkit-box;
|
|
2748
|
+
-webkit-line-clamp: 2;
|
|
2749
|
+
line-clamp: 2;
|
|
2750
|
+
-webkit-box-orient: vertical;
|
|
2751
|
+
overflow: hidden;
|
|
2752
|
+
}
|
|
2753
|
+
.xicms-shop-card__priceline {
|
|
2754
|
+
font-size: 0.92em;
|
|
2755
|
+
}
|
|
2756
|
+
/* Swatch-rij duwt (leeg of gevuld) de rest omlaag en houdt een vaste hoogte. */
|
|
2757
|
+
.xicms-shop-card__swatchrow {
|
|
2758
|
+
display: inline-flex;
|
|
2759
|
+
gap: 5px;
|
|
2760
|
+
min-height: 14px;
|
|
2761
|
+
margin-top: auto;
|
|
2762
|
+
padding-top: 0.3rem;
|
|
2763
|
+
}
|
|
2764
|
+
.xicms-shop-card__actions {
|
|
2765
|
+
padding: 0.5rem 1rem 1rem;
|
|
2766
|
+
}
|
|
2705
2767
|
/* Kaartknoppen: passen binnen de kaart, ook bij smalle kolommen. */
|
|
2706
|
-
.xicms-shop-card__actions .xicms-btn
|
|
2768
|
+
.xicms-shop-card__actions .xicms-btn,
|
|
2769
|
+
.xicms-shop-card__added {
|
|
2770
|
+
display: block;
|
|
2707
2771
|
width: 100%;
|
|
2708
2772
|
padding: 0.45rem 0.6rem;
|
|
2709
2773
|
font-size: 0.85rem;
|
|
2710
2774
|
text-align: center;
|
|
2711
2775
|
}
|
|
2776
|
+
.xicms-shop-card__added {
|
|
2777
|
+
color: var(--xicms-primary, #4f46e5);
|
|
2778
|
+
text-decoration: none;
|
|
2779
|
+
}
|
|
2712
2780
|
|
|
2713
2781
|
/* Mobiel: nooit meer dan 2 kolommen (inline grid-template wint anders van media queries). */
|
|
2714
2782
|
@media (max-width: 520px) {
|
|
@@ -2720,6 +2788,9 @@
|
|
|
2720
2788
|
font-size: 0.8rem;
|
|
2721
2789
|
padding: 0.4rem 0.5rem;
|
|
2722
2790
|
}
|
|
2791
|
+
.xicms-shop-card__name {
|
|
2792
|
+
font-size: 0.92rem;
|
|
2793
|
+
}
|
|
2723
2794
|
}
|
|
2724
2795
|
|
|
2725
2796
|
/* Uitverkocht-badge op de kaartfoto. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiwebdesign/xicms-core",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.1",
|
|
4
4
|
"description": "XiCMS engine: gestructureerde content, pagina's, draft/publish, admin (TipTap+Radix), SEO en beveiliging voor klantsites.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|