@reblu/site-blocks 0.3.1 → 0.4.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.
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all3) => {
9
7
  for (var name in all3)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
@@ -13053,9 +13043,13 @@ function markdownToSafeHtml(markdown) {
13053
13043
  }
13054
13044
 
13055
13045
  // src/components/markdown-renderer.tsx
13056
- var import_site_ui = require("@reblu/site-ui");
13057
- var import_html_react_parser = __toESM(require("html-react-parser"), 1);
13046
+ var import_utils = require("@reblu/site-ui/utils");
13047
+ var import_html_react_parser = require("html-react-parser");
13058
13048
  var import_jsx_runtime = require("react/jsx-runtime");
13049
+ var HTML_TO_DOM_OPTIONS = { lowerCaseAttributeNames: false };
13050
+ function parse3(html5, options) {
13051
+ return (0, import_html_react_parser.domToReact)((0, import_html_react_parser.htmlToDOM)(html5, HTML_TO_DOM_OPTIONS), options);
13052
+ }
13059
13053
  var HEADING_CLASSES = {
13060
13054
  1: "text-3xl font-bold mt-8 mb-4 first:mt-0",
13061
13055
  2: "text-2xl font-bold mt-7 mb-3 first:mt-0",
@@ -13067,6 +13061,41 @@ var HEADING_CLASSES = {
13067
13061
  function hasClass(el, name) {
13068
13062
  return typeof el.attribs.class === "string" && el.attribs.class.split(/\s+/).includes(name);
13069
13063
  }
13064
+ var PRESERVED_GLOBAL_ATTRIBUTES = [
13065
+ // Anchor targets. In-document links ("see clause 4" → `#clause-4`) resolve to
13066
+ // nothing without them.
13067
+ "id",
13068
+ // Language and direction of a passage — a bilingual legal page is the normal case
13069
+ // here, and screen readers switch voice on it.
13070
+ "lang",
13071
+ "dir",
13072
+ // Advisory text the author wrote; it is content, not styling.
13073
+ "title",
13074
+ // Explicit "do not publish this". Without it a block authored as `<p hidden>`
13075
+ // becomes VISIBLE — publishing what the author marked as not-for-publication.
13076
+ "hidden",
13077
+ // Accessible semantics. `../lib/markdown` restores these through the sanitiser
13078
+ // precisely so they are not lost (REB-286's axe gate); dropping them one layer
13079
+ // later would undo that.
13080
+ "role"
13081
+ ];
13082
+ var PRESERVED_ATTRIBUTE_PREFIXES = ["aria-", "data-"];
13083
+ var PRESERVED_TAG_ATTRIBUTES = {
13084
+ ol: ["start", "type"],
13085
+ li: ["value", "type"]
13086
+ };
13087
+ var BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set(["hidden"]);
13088
+ function authorAttributes(el) {
13089
+ const tagSpecific = PRESERVED_TAG_ATTRIBUTES[el.name] ?? [];
13090
+ const preserved = {};
13091
+ for (const [rawName, value] of Object.entries(el.attribs)) {
13092
+ const name = rawName.toLowerCase();
13093
+ const keep = PRESERVED_GLOBAL_ATTRIBUTES.includes(name) || tagSpecific.includes(name) || PRESERVED_ATTRIBUTE_PREFIXES.some((prefix) => name.startsWith(prefix));
13094
+ if (!keep) continue;
13095
+ preserved[name] = BOOLEAN_ATTRIBUTES.has(name) ? true : value;
13096
+ }
13097
+ return preserved;
13098
+ }
13070
13099
  function isExternalHref(href) {
13071
13100
  return !!href && /^(https?:)?\/\//.test(href);
13072
13101
  }
@@ -13076,29 +13105,46 @@ function buildOptions(headingOffset) {
13076
13105
  if (!(node instanceof import_html_react_parser.Element)) return void 0;
13077
13106
  const children2 = () => (0, import_html_react_parser.domToReact)(node.children, options);
13078
13107
  const { name, attribs } = node;
13108
+ const author = authorAttributes(node);
13079
13109
  const headingMatch = /^h([1-6])$/.exec(name);
13080
13110
  if (headingMatch) {
13081
13111
  const rendered = Math.min(6, Number(headingMatch[1]) + headingOffset);
13082
13112
  const Tag = `h${rendered}`;
13083
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { className: HEADING_CLASSES[rendered], children: children2() });
13113
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { ...author, className: HEADING_CLASSES[rendered], children: children2() });
13084
13114
  }
13085
13115
  switch (name) {
13086
13116
  case "p":
13087
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "my-4 leading-relaxed text-foreground/90", children: children2() });
13117
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { ...author, className: "my-4 leading-relaxed text-foreground/90", children: children2() });
13088
13118
  case "ul":
13089
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: hasClass(node, "contains-task-list") ? "list-none pl-0 my-4 space-y-1" : "list-disc ml-6 my-4", children: children2() });
13119
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { ...author, className: hasClass(node, "contains-task-list") ? "list-none pl-0 my-4 space-y-1" : "list-disc ml-6 my-4", children: children2() });
13090
13120
  case "ol":
13091
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "list-decimal ml-6 my-4", children: children2() });
13121
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { ...author, className: "list-decimal ml-6 my-4", children: children2() });
13092
13122
  case "li":
13093
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: hasClass(node, "task-list-item") ? "flex items-start gap-2 my-1" : "my-1", children: children2() });
13123
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { ...author, className: hasClass(node, "task-list-item") ? "flex items-start gap-2 my-1" : "my-1", children: children2() });
13094
13124
  case "blockquote":
13095
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", { className: "border-l-4 border-primary/50 pl-6 italic my-6 text-muted-foreground", children: children2() });
13125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13126
+ "blockquote",
13127
+ {
13128
+ ...author,
13129
+ className: "border-l-4 border-primary/50 pl-6 italic my-6 text-muted-foreground",
13130
+ children: children2()
13131
+ }
13132
+ );
13096
13133
  case "pre":
13097
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "rounded-lg border border-input bg-muted p-4 my-6 overflow-x-auto font-mono text-sm", children: children2() });
13134
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13135
+ "pre",
13136
+ {
13137
+ ...author,
13138
+ className: "rounded-lg border border-input bg-muted p-4 my-6 overflow-x-auto font-mono text-sm",
13139
+ children: children2()
13140
+ }
13141
+ );
13098
13142
  case "code": {
13099
13143
  const isBlock = node.parent instanceof import_html_react_parser.Element && node.parent.name === "pre";
13100
- if (isBlock) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "block", children: children2() });
13101
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-sm", children: children2() });
13144
+ if (isBlock) {
13145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { ...author, className: "block", children: children2() });
13146
+ }
13147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { ...author, className: "rounded bg-muted px-1.5 py-0.5 font-mono text-sm", children: children2() });
13102
13148
  }
13103
13149
  case "a": {
13104
13150
  const href = attribs.href;
@@ -13106,6 +13152,7 @@ function buildOptions(headingOffset) {
13106
13152
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13107
13153
  "a",
13108
13154
  {
13155
+ ...author,
13109
13156
  href,
13110
13157
  className: "text-primary underline underline-offset-4 hover:text-primary/80",
13111
13158
  ...external ? { target: "_blank", rel: "noopener noreferrer" } : {},
@@ -13122,6 +13169,7 @@ function buildOptions(headingOffset) {
13122
13169
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13123
13170
  "img",
13124
13171
  {
13172
+ ...author,
13125
13173
  src: attribs.src,
13126
13174
  alt: attribs.alt ?? "",
13127
13175
  loading: "lazy",
@@ -13133,6 +13181,7 @@ function buildOptions(headingOffset) {
13133
13181
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative my-6 aspect-video w-full overflow-hidden rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
13134
13182
  "iframe",
13135
13183
  {
13184
+ ...author,
13136
13185
  src: attribs.src,
13137
13186
  title: attribs.title ?? "Embedded video",
13138
13187
  allow: attribs.allow ?? "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
@@ -13141,13 +13190,13 @@ function buildOptions(headingOffset) {
13141
13190
  }
13142
13191
  ) });
13143
13192
  case "hr":
13144
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "my-8 border-muted-foreground/30" });
13193
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { ...author, className: "my-8 border-muted-foreground/30" });
13145
13194
  case "strong":
13146
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "font-bold text-foreground", children: children2() });
13195
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { ...author, className: "font-bold text-foreground", children: children2() });
13147
13196
  case "em":
13148
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { className: "italic", children: children2() });
13197
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { ...author, className: "italic", children: children2() });
13149
13198
  case "del":
13150
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("del", { className: "line-through", children: children2() });
13199
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("del", { ...author, className: "line-through", children: children2() });
13151
13200
  default:
13152
13201
  return void 0;
13153
13202
  }
@@ -13158,18 +13207,19 @@ function buildOptions(headingOffset) {
13158
13207
  function MarkdownRenderer({ content, className, headingOffset = 0 }) {
13159
13208
  const html5 = markdownToSafeHtml(content);
13160
13209
  if (!html5) return null;
13161
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_site_ui.cn)("max-w-none", className), "data-slot": "markdown", children: (0, import_html_react_parser.default)(html5, buildOptions(headingOffset)) });
13210
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_utils.cn)("max-w-none", className), "data-slot": "markdown", children: parse3(html5, buildOptions(headingOffset)) });
13162
13211
  }
13163
13212
  function SafeHtml({ html: html5, className, headingOffset = 0 }) {
13164
13213
  if (!html5) return null;
13165
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_site_ui.cn)("max-w-none", className), "data-slot": "safe-html", children: (0, import_html_react_parser.default)(html5, buildOptions(headingOffset)) });
13214
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_utils.cn)("max-w-none", className), "data-slot": "safe-html", children: parse3(html5, buildOptions(headingOffset)) });
13166
13215
  }
13167
13216
 
13168
13217
  // src/components/treatment.tsx
13169
- var import_site_ui3 = require("@reblu/site-ui");
13218
+ var import_utils3 = require("@reblu/site-ui/utils");
13170
13219
 
13171
13220
  // src/components/_page-parts.tsx
13172
- var import_site_ui2 = require("@reblu/site-ui");
13221
+ var import_utils2 = require("@reblu/site-ui/utils");
13222
+ var import_button_variants = require("@reblu/site-ui/button-variants");
13173
13223
  var import_jsx_runtime2 = require("react/jsx-runtime");
13174
13224
  var KNOWN_BUTTON_VARIANTS = /* @__PURE__ */ new Set([
13175
13225
  "default",
@@ -13200,18 +13250,23 @@ function PageHero({ page }) {
13200
13250
  }
13201
13251
  function SectionCta({ section }) {
13202
13252
  if (!section.ctaLabel || !section.ctaUrl) return null;
13203
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pt-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_site_ui2.Button, { asChild: true, variant: toButtonVariant(section.ctaVariant), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
13253
+ const variant = toButtonVariant(section.ctaVariant);
13254
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pt-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
13204
13255
  "a",
13205
13256
  {
13206
13257
  href: section.ctaUrl,
13258
+ "data-slot": "button",
13259
+ "data-variant": variant,
13260
+ "data-size": "default",
13261
+ className: (0, import_utils2.cn)((0, import_button_variants.buttonVariants)({ variant, size: "default" })),
13207
13262
  ...section.ctaIsExternal ? { target: "_blank", rel: "noopener noreferrer" } : {},
13208
13263
  children: section.ctaLabel
13209
13264
  }
13210
- ) }) });
13265
+ ) });
13211
13266
  }
13212
13267
  function SectionImages({ section }) {
13213
13268
  if (section.images.length === 0) return null;
13214
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: (0, import_site_ui2.cn)("grid gap-4", section.images.length > 1 ? "sm:grid-cols-2" : "grid-cols-1"), children: section.images.map((img) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("figure", { className: "space-y-1", children: [
13269
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: (0, import_utils2.cn)("grid gap-4", section.images.length > 1 ? "sm:grid-cols-2" : "grid-cols-1"), children: section.images.map((img) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("figure", { className: "space-y-1", children: [
13215
13270
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "relative aspect-video w-full overflow-hidden rounded-lg bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { src: img.url, alt: img.alt, loading: "lazy", className: "h-full w-full object-cover" }) }),
13216
13271
  img.caption && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("figcaption", { className: "text-sm text-muted-foreground", children: img.caption })
13217
13272
  ] }, `${img.url}-${img.order}`)) });
@@ -13248,14 +13303,14 @@ function PageSectionList({ sections }) {
13248
13303
  // src/components/treatment.tsx
13249
13304
  var import_jsx_runtime3 = require("react/jsx-runtime");
13250
13305
  function Treatment({ page, className }) {
13251
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: (0, import_site_ui3.cn)("space-y-12", className), "data-slot": "treatment", children: [
13306
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: (0, import_utils3.cn)("space-y-12", className), "data-slot": "treatment", children: [
13252
13307
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageHero, { page }),
13253
13308
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageSectionList, { sections: page.sections })
13254
13309
  ] });
13255
13310
  }
13256
13311
 
13257
13312
  // src/components/specialty.tsx
13258
- var import_site_ui4 = require("@reblu/site-ui");
13313
+ var import_utils4 = require("@reblu/site-ui/utils");
13259
13314
  var import_jsx_runtime4 = require("react/jsx-runtime");
13260
13315
  function Specialty({
13261
13316
  page,
@@ -13264,7 +13319,7 @@ function Specialty({
13264
13319
  treatmentsLabel = "Treatments",
13265
13320
  className
13266
13321
  }) {
13267
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: (0, import_site_ui4.cn)("space-y-12", className), "data-slot": "specialty", children: [
13322
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: (0, import_utils4.cn)("space-y-12", className), "data-slot": "specialty", children: [
13268
13323
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageHero, { page }),
13269
13324
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageSectionList, { sections: page.sections }),
13270
13325
  treatments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "space-y-6", children: [
@@ -13296,7 +13351,7 @@ function Specialty({
13296
13351
  }
13297
13352
 
13298
13353
  // src/components/profile-card.tsx
13299
- var import_site_ui5 = require("@reblu/site-ui");
13354
+ var import_utils5 = require("@reblu/site-ui/utils");
13300
13355
  var import_jsx_runtime5 = require("react/jsx-runtime");
13301
13356
  function initials(name) {
13302
13357
  return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase() ?? "").join("");
@@ -13306,7 +13361,7 @@ function ProfileCard({ name, role, imageUrl, specialties = [], bio, href, classN
13306
13361
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
13307
13362
  "article",
13308
13363
  {
13309
- className: (0, import_site_ui5.cn)(
13364
+ className: (0, import_utils5.cn)(
13310
13365
  "flex flex-col items-center gap-3 rounded-lg border bg-card p-6 text-center",
13311
13366
  className
13312
13367
  ),
@@ -13338,7 +13393,7 @@ function ProfileCard({ name, role, imageUrl, specialties = [], bio, href, classN
13338
13393
  }
13339
13394
 
13340
13395
  // src/components/product-card.tsx
13341
- var import_site_ui6 = require("@reblu/site-ui");
13396
+ var import_utils6 = require("@reblu/site-ui/utils");
13342
13397
  var import_jsx_runtime6 = require("react/jsx-runtime");
13343
13398
  function ProductCard({ product, href, currencyCode = "EUR", locale, className }) {
13344
13399
  const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
@@ -13346,7 +13401,7 @@ function ProductCard({ product, href, currencyCode = "EUR", locale, className })
13346
13401
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
13347
13402
  "article",
13348
13403
  {
13349
- className: (0, import_site_ui6.cn)(
13404
+ className: (0, import_utils6.cn)(
13350
13405
  "group flex flex-col overflow-hidden rounded-lg border bg-card transition-shadow hover:shadow-lg",
13351
13406
  className
13352
13407
  ),