@xiwebdesign/xicms-core 7.7.0 → 7.8.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/dist/admin/AdminApp.js +3 -1
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/lib/pagesClient.d.ts +13 -0
- package/dist/admin/lib/pagesClient.js +13 -0
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/shop/ProductPaneel.d.ts +15 -0
- package/dist/admin/shop/ProductPaneel.js +55 -0
- package/dist/admin/shop/ProductPaneel.js.map +1 -0
- package/dist/admin/shop/ProductsTab.d.ts +27 -0
- package/dist/admin/shop/ProductsTab.js +3 -2
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/productClient.d.ts +32 -0
- package/dist/admin/shop/productClient.js +31 -0
- package/dist/admin/shop/productClient.js.map +1 -0
- package/dist/admin/visual/VisualEditor.js +296 -43
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin/visual/wijzigingen.d.ts +46 -0
- package/dist/admin/visual/wijzigingen.js +78 -0
- package/dist/admin/visual/wijzigingen.js.map +1 -0
- package/dist/components/EditBar.d.ts +30 -0
- package/dist/components/EditBar.js +32 -6
- package/dist/components/EditBar.js.map +1 -1
- package/dist/editor/EditorProvider.d.ts +5 -0
- package/dist/editor/EditorProvider.js +28 -11
- package/dist/editor/EditorProvider.js.map +1 -1
- package/dist/modules/shop/blocks/ProductDetail.js +3 -3
- package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
- package/dist/modules/shop/blocks/ProductGrid.js +5 -2
- package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
- package/dist/styles.css +163 -46
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -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 filterColor,\n}: {\n product: StoredProduct;\n siteId: string;\n currency: string;\n detailPath: string;\n cartPath: string;\n t: ShopT;\n /** Actief kleurfilter uit het overzicht — toont bij voorkeur de foto van die kleur. */\n filterColor?: string | null;\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 filterImage = filterColor ? kleurOptie(product)?.images?.[filterColor] : undefined;\n const image = hoverImage ?? filterImage ?? 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 filterColor={filterColor}\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 filterColor,\n}: {\n product: StoredProduct;\n siteId: string;\n currency: string;\n detailPath: string;\n cartPath: string;\n t: ShopT;\n /** Actief kleurfilter uit het overzicht — toont bij voorkeur de foto van die kleur. */\n filterColor?: string | null;\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 filterImage = filterColor ? kleurOptie(product)?.images?.[filterColor] : undefined;\n const image = hoverImage ?? filterImage ?? 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 // `data-xicms-product` maakt de kaart in de bewerkmodus aanwijsbaar als PRODUCT (potlood voor\n // de naam, tandwiel voor het hele formulier). Op de cataloguspagina was dat er niet, en dus was\n // een productnaam daar het enige stuk tekst op de site dat je nergens kon aanklikken.\n <Card padding={0} className=\"xicms-shop-card\" data-xicms-product={product.id}>\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\" data-xicms-product-edit=\"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 filterColor={filterColor}\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
|
@@ -250,23 +250,6 @@
|
|
|
250
250
|
.xicms-ve__shell {
|
|
251
251
|
display: contents;
|
|
252
252
|
}
|
|
253
|
-
.xicms-ve__bar {
|
|
254
|
-
position: fixed;
|
|
255
|
-
top: 0;
|
|
256
|
-
left: 0;
|
|
257
|
-
right: 0;
|
|
258
|
-
z-index: 10001;
|
|
259
|
-
display: flex;
|
|
260
|
-
align-items: center;
|
|
261
|
-
gap: 0.75rem;
|
|
262
|
-
padding: 0.6rem 1rem;
|
|
263
|
-
background: #0f172a;
|
|
264
|
-
color: #fff;
|
|
265
|
-
}
|
|
266
|
-
/* De balk dekt de bovenkant van de site af; duw de pagina omlaag zolang er bewerkt wordt. */
|
|
267
|
-
:root:has(.xicms-ve__bar) body {
|
|
268
|
-
padding-top: 3rem;
|
|
269
|
-
}
|
|
270
253
|
.xicms-ve__title {
|
|
271
254
|
font-size: 0.95rem;
|
|
272
255
|
}
|
|
@@ -751,33 +734,57 @@
|
|
|
751
734
|
}
|
|
752
735
|
|
|
753
736
|
/* Zwevende werkbalk voor editors */
|
|
754
|
-
|
|
737
|
+
/*
|
|
738
|
+
* De beheerbalk staat BOVENAAN, en in beide toestanden op precies dezelfde plek.
|
|
739
|
+
*
|
|
740
|
+
* Hij zweefde als pilletje onderaan, terwijl de editor zijn eigen balk bovenaan meebracht. Het
|
|
741
|
+
* overschakelen tussen "live site" en "bewerken" verplaatste de knoppen daarmee van onder naar
|
|
742
|
+
* boven — je moest je houvast opnieuw zoeken op het moment dat je van modus wisselde, en dat is
|
|
743
|
+
* precies het moment waarop je het niet kunt missen. Nu is het één balk die blijft staan en
|
|
744
|
+
* waarin alleen een andere knop oplicht.
|
|
745
|
+
*
|
|
746
|
+
* Onderaan was ook niet meer vrij: daar staat sinds 7.8 de publiceerknop, en die hoort bij je werk
|
|
747
|
+
* op de pagina — niet naast de navigatie.
|
|
748
|
+
*/
|
|
749
|
+
.xicms-editbar,
|
|
750
|
+
.xicms-ve__bar {
|
|
755
751
|
position: fixed;
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
left: 50%;
|
|
761
|
-
transform: translateX(-50%);
|
|
762
|
-
z-index: 9999;
|
|
752
|
+
top: 0;
|
|
753
|
+
left: 0;
|
|
754
|
+
right: 0;
|
|
755
|
+
z-index: 10001;
|
|
763
756
|
display: flex;
|
|
764
757
|
align-items: center;
|
|
765
758
|
gap: 0.3rem;
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
padding: 0.3rem 0.35rem 0.3rem 0.85rem;
|
|
769
|
-
/* Eigen kleuren, los van de site eronder: een donkere glasstaaf leest op een witte én op een
|
|
759
|
+
padding: 0.4rem 0.9rem;
|
|
760
|
+
/* Eigen kleuren, los van de site eronder: een donkere staaf leest op een witte én op een
|
|
770
761
|
donkere pagina, en blijft op élke klantsite hetzelfde stukje gereedschap. */
|
|
771
|
-
background: rgba(15, 23, 42, 0.
|
|
762
|
+
background: rgba(15, 23, 42, 0.95);
|
|
772
763
|
-webkit-backdrop-filter: saturate(1.4) blur(10px);
|
|
773
764
|
backdrop-filter: saturate(1.4) blur(10px);
|
|
774
765
|
color: #fff;
|
|
775
|
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
776
|
-
border-radius: 999px;
|
|
777
|
-
box-shadow: 0 10px 30px rgba(2, 6, 23, 0.35);
|
|
766
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
|
778
767
|
font-size: 0.8rem;
|
|
779
768
|
line-height: 1;
|
|
780
769
|
}
|
|
770
|
+
/*
|
|
771
|
+
* De balk dekt de bovenkant van de site af; duw de pagina omlaag zolang hij er staat.
|
|
772
|
+
*
|
|
773
|
+
* Op `:root` en niet op `body`: de padding hoort bij de scroll-container, en `body` is bij
|
|
774
|
+
* klantsites veel vaker het element waar eigen CSS aan zit (achtergronden, grid, `min-height`).
|
|
775
|
+
* Hoe minder we aan `body` doen, hoe kleiner de kans dat deze balk iets in de weg legt op een
|
|
776
|
+
* site die we niet kennen. Layout-technisch is het resultaat hetzelfde.
|
|
777
|
+
*/
|
|
778
|
+
:root:has(.xicms-editbar),
|
|
779
|
+
:root:has(.xicms-ve__bar) {
|
|
780
|
+
padding-top: 2.6rem;
|
|
781
|
+
}
|
|
782
|
+
.xicms-ve__sep {
|
|
783
|
+
width: 1px;
|
|
784
|
+
align-self: stretch;
|
|
785
|
+
margin: 0.15rem 0.4rem;
|
|
786
|
+
background: rgba(255, 255, 255, 0.16);
|
|
787
|
+
}
|
|
781
788
|
.xicms-editbar__brand {
|
|
782
789
|
white-space: nowrap;
|
|
783
790
|
font-weight: 600;
|
|
@@ -822,16 +829,22 @@
|
|
|
822
829
|
outline: 2px solid #a5b4fc;
|
|
823
830
|
outline-offset: 2px;
|
|
824
831
|
}
|
|
825
|
-
/*
|
|
826
|
-
.
|
|
832
|
+
/*
|
|
833
|
+
* De knop die aan staat, staat aan. Niet de knop die je zou moeten indrukken.
|
|
834
|
+
*
|
|
835
|
+
* Er was één opvallende hoofdactie ("Pagina bewerken"), en die bleef er hetzelfde uitzien of je nu
|
|
836
|
+
* keek of bewerkte. De balk kon dus niet vertellen in welke toestand je zat — het verschil tussen
|
|
837
|
+
* "ik zie de live site" en "ik bewerk maar heb nog niets veranderd" is op het scherm nihil, en
|
|
838
|
+
* juist dáár moest de balk uitsluitsel over geven.
|
|
839
|
+
*/
|
|
840
|
+
.xicms-editbar__btn.is-actief {
|
|
827
841
|
background: #fff;
|
|
828
842
|
color: #0f172a;
|
|
829
843
|
font-weight: 600;
|
|
830
844
|
}
|
|
831
|
-
.xicms-editbar__btn
|
|
845
|
+
.xicms-editbar__btn.is-actief:hover {
|
|
832
846
|
background: #fff;
|
|
833
847
|
color: #0f172a;
|
|
834
|
-
filter: brightness(0.92);
|
|
835
848
|
}
|
|
836
849
|
|
|
837
850
|
/* Demo-bewerkmodus: bovenbalk met pseudo-login + duidelijke "niet opgeslagen"-melding */
|
|
@@ -3050,26 +3063,130 @@
|
|
|
3050
3063
|
/* Tandwiel naast een blok op de pagina. Zelfde familie als het potlood — even ingetogen, want ze
|
|
3051
3064
|
liggen over de site van de klant heen — maar duidelijk een andere handeling: het potlood zit bij
|
|
3052
3065
|
een tekstveld, het tandwiel rechtsboven bij het blok als geheel. */
|
|
3053
|
-
|
|
3066
|
+
/*
|
|
3067
|
+
* Potlood + tandwiel per blok, als één klein paar.
|
|
3068
|
+
*
|
|
3069
|
+
* Ze staan naast elkaar in één schijfje zodat ze bij ELKAAR horen en bij het blok waar ze op
|
|
3070
|
+
* liggen — twee losse knoppen op verschillende hoogtes lezen als twee losse dingen. Halfdoorzichtig
|
|
3071
|
+
* in rust: gereedschap hoort de site niet te overschreeuwen, maar wel te vinden zijn.
|
|
3072
|
+
*/
|
|
3073
|
+
.xicms-ve-blokknoppen {
|
|
3054
3074
|
position: fixed;
|
|
3055
|
-
z-index:
|
|
3075
|
+
z-index: 10005;
|
|
3076
|
+
display: flex;
|
|
3077
|
+
gap: 2px;
|
|
3078
|
+
padding: 2px;
|
|
3079
|
+
border-radius: 999px;
|
|
3080
|
+
background: rgba(15, 23, 42, 0.42);
|
|
3081
|
+
-webkit-backdrop-filter: blur(6px);
|
|
3082
|
+
backdrop-filter: blur(6px);
|
|
3083
|
+
opacity: 0.5;
|
|
3084
|
+
transition: opacity 0.12s ease;
|
|
3085
|
+
}
|
|
3086
|
+
.xicms-ve-blokknoppen:hover,
|
|
3087
|
+
.xicms-ve-blokknoppen:focus-within {
|
|
3088
|
+
opacity: 1;
|
|
3089
|
+
background: rgba(15, 23, 42, 0.9);
|
|
3090
|
+
}
|
|
3091
|
+
.xicms-ve-blokknop {
|
|
3092
|
+
appearance: none;
|
|
3056
3093
|
width: 26px;
|
|
3057
3094
|
height: 26px;
|
|
3058
3095
|
display: grid;
|
|
3059
3096
|
place-items: center;
|
|
3060
3097
|
border: 0;
|
|
3061
3098
|
border-radius: 999px;
|
|
3062
|
-
background:
|
|
3099
|
+
background: transparent;
|
|
3063
3100
|
color: #fff;
|
|
3064
3101
|
cursor: pointer;
|
|
3065
|
-
|
|
3066
|
-
transition: opacity 0.12s ease, background 0.12s ease;
|
|
3102
|
+
transition: background 0.12s ease;
|
|
3067
3103
|
}
|
|
3068
|
-
.xicms-ve-
|
|
3069
|
-
.xicms-ve-
|
|
3070
|
-
.
|
|
3104
|
+
.xicms-ve-blokknop:hover,
|
|
3105
|
+
.xicms-ve-blokknop.is-on {
|
|
3106
|
+
background: rgba(255, 255, 255, 0.22);
|
|
3107
|
+
}
|
|
3108
|
+
.xicms-ve-blokknop:focus-visible {
|
|
3109
|
+
outline: 2px solid #a5b4fc;
|
|
3110
|
+
outline-offset: 1px;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
/* Producten dragen een iets andere tint zodat je ziet dat je iets anders aanwijst dan een blok:
|
|
3114
|
+
wijzigingen aan een product gaan meteen live en horen niet met paginatekst verward te worden. */
|
|
3115
|
+
.xicms-ve-blokknoppen.is-product {
|
|
3116
|
+
background: rgba(120, 53, 15, 0.55);
|
|
3071
3117
|
opacity: 1;
|
|
3072
|
-
|
|
3118
|
+
}
|
|
3119
|
+
.xicms-ve-blokknoppen.is-product:hover,
|
|
3120
|
+
.xicms-ve-blokknoppen.is-product:focus-within {
|
|
3121
|
+
background: rgba(120, 53, 15, 0.92);
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
/*
|
|
3125
|
+
* Rood en onderstreept = dit staat nog NIET op de live site.
|
|
3126
|
+
*
|
|
3127
|
+
* Bewust een onderstreping en niet alleen kleur: kleur alleen is geen boodschap voor wie
|
|
3128
|
+
* kleurenblind is, en dit is te belangrijk om aan kleur alleen op te hangen. De streep loopt onder
|
|
3129
|
+
* de tekst door zonder de regelhoogte te veranderen, zodat de pagina niet verspringt zodra je iets
|
|
3130
|
+
* wijzigt — anders zou de markering zelf de layout laten dansen tijdens het typen.
|
|
3131
|
+
*/
|
|
3132
|
+
.xicms-ve-gewijzigd {
|
|
3133
|
+
color: #b91c1c !important;
|
|
3134
|
+
text-decoration: underline wavy #b91c1c 1px;
|
|
3135
|
+
text-underline-offset: 3px;
|
|
3136
|
+
}
|
|
3137
|
+
/* Een heel nieuw of verplaatst blok: een streep in de kantlijn i.p.v. alle tekst rood. Alles rood
|
|
3138
|
+
kleuren van een blok dat in zijn geheel nieuw is, is geen informatie maar een rode lap. */
|
|
3139
|
+
.xicms-ve-nieuw {
|
|
3140
|
+
box-shadow: inset 3px 0 0 0 #b91c1c;
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
/*
|
|
3144
|
+
* De publiceerknop: groot, rechtsonder, bij je werk.
|
|
3145
|
+
*
|
|
3146
|
+
* Hij stond bovenin tussen de navigatie en riep daar altijd even hard, ook als er niets te
|
|
3147
|
+
* publiceren viel — een knop die altijd beschikbaar is, zegt niets meer. Nu is hij grijs en
|
|
3148
|
+
* uitgeschakeld zolang de pagina gelijk is aan wat er live staat, en kleurt hij pas op als er echt
|
|
3149
|
+
* iets klaarstaat. Zelfde bron als de rode markering, dus knop en pagina spreken elkaar nooit tegen.
|
|
3150
|
+
*/
|
|
3151
|
+
.xicms-ve-publiceer {
|
|
3152
|
+
position: fixed;
|
|
3153
|
+
right: 20px;
|
|
3154
|
+
bottom: calc(20px + var(--xicms-bottombar, 0px));
|
|
3155
|
+
z-index: 10006;
|
|
3156
|
+
display: grid;
|
|
3157
|
+
gap: 2px;
|
|
3158
|
+
justify-items: center;
|
|
3159
|
+
min-width: 8.5rem;
|
|
3160
|
+
padding: 0.7rem 1.3rem;
|
|
3161
|
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
|
3162
|
+
border-radius: 999px;
|
|
3163
|
+
background: rgba(71, 85, 105, 0.85);
|
|
3164
|
+
color: rgba(255, 255, 255, 0.75);
|
|
3165
|
+
font: inherit;
|
|
3166
|
+
font-size: 0.9rem;
|
|
3167
|
+
cursor: not-allowed;
|
|
3168
|
+
box-shadow: 0 10px 30px rgba(2, 6, 23, 0.35);
|
|
3169
|
+
transition: background 0.15s ease, transform 0.15s ease;
|
|
3170
|
+
}
|
|
3171
|
+
.xicms-ve-publiceer.is-klaar {
|
|
3172
|
+
background: #2563eb;
|
|
3173
|
+
color: #fff;
|
|
3174
|
+
cursor: pointer;
|
|
3175
|
+
}
|
|
3176
|
+
.xicms-ve-publiceer.is-klaar:hover {
|
|
3177
|
+
background: #1d4ed8;
|
|
3178
|
+
transform: translateY(-1px);
|
|
3179
|
+
}
|
|
3180
|
+
.xicms-ve-publiceer:focus-visible {
|
|
3181
|
+
outline: 2px solid #a5b4fc;
|
|
3182
|
+
outline-offset: 3px;
|
|
3183
|
+
}
|
|
3184
|
+
.xicms-ve-publiceer__label {
|
|
3185
|
+
font-weight: 600;
|
|
3186
|
+
}
|
|
3187
|
+
.xicms-ve-publiceer__sub {
|
|
3188
|
+
font-size: 0.72rem;
|
|
3189
|
+
opacity: 0.8;
|
|
3073
3190
|
}
|
|
3074
3191
|
|
|
3075
3192
|
/* Melding in de editorbalk (nog geen werkversie). Hoort bij het gereedschap, niet bij de pagina. */
|
package/dist/version.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const CORE_VERSION = true ? "7.
|
|
1
|
+
const CORE_VERSION = true ? "7.8.0" : "0.0.0-dev";
|
|
2
2
|
const CORE_PACKAGE = "@xiwebdesign/xicms-core";
|
|
3
3
|
function isNieuwer(beschikbaar, huidig) {
|
|
4
4
|
const delen = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiwebdesign/xicms-core",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
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": {
|