@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/README.md +49 -0
- package/dist/client.cjs +7 -7
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +94 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +91 -22
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -51,9 +51,13 @@ declare function buildShareUrls(url: string, title: string): ShareUrls;
|
|
|
51
51
|
* `dangerouslySetInnerHTML` consumers listed above. `MarkdownRenderer` re-parses that
|
|
52
52
|
* string through `html-react-parser` into a token-styled component map
|
|
53
53
|
* (`markdown-renderer.tsx` `buildOptions`), and that map rebuilds most elements from
|
|
54
|
-
* scratch
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* scratch. It forwards only the subset of author attributes that carries MEANING
|
|
55
|
+
* (`id`, `lang`, `dir`, `title`, `hidden`, `role`, `aria-*`, `data-*`, and the list
|
|
56
|
+
* numbering attributes) — see `PRESERVED_GLOBAL_ATTRIBUTES` there for the line and
|
|
57
|
+
* why it sits where it does. `class` and `style` in particular are guaranteed by THIS
|
|
58
|
+
* module and still dropped by that one, because the map owns presentation (REB-666).
|
|
59
|
+
* Nothing the renderer forwards widens this schema: it can only pass through what
|
|
60
|
+
* `sanitizeHtml` already allowed.
|
|
57
61
|
*
|
|
58
62
|
* REB-606: this used to run on `isomorphic-dompurify`, which pulls `jsdom` into the
|
|
59
63
|
* import graph. `jsdom`'s transitive `html-encoding-sniffer` → `@exodus/bytes` chain
|
package/dist/index.d.ts
CHANGED
|
@@ -51,9 +51,13 @@ declare function buildShareUrls(url: string, title: string): ShareUrls;
|
|
|
51
51
|
* `dangerouslySetInnerHTML` consumers listed above. `MarkdownRenderer` re-parses that
|
|
52
52
|
* string through `html-react-parser` into a token-styled component map
|
|
53
53
|
* (`markdown-renderer.tsx` `buildOptions`), and that map rebuilds most elements from
|
|
54
|
-
* scratch
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* scratch. It forwards only the subset of author attributes that carries MEANING
|
|
55
|
+
* (`id`, `lang`, `dir`, `title`, `hidden`, `role`, `aria-*`, `data-*`, and the list
|
|
56
|
+
* numbering attributes) — see `PRESERVED_GLOBAL_ATTRIBUTES` there for the line and
|
|
57
|
+
* why it sits where it does. `class` and `style` in particular are guaranteed by THIS
|
|
58
|
+
* module and still dropped by that one, because the map owns presentation (REB-666).
|
|
59
|
+
* Nothing the renderer forwards widens this schema: it can only pass through what
|
|
60
|
+
* `sanitizeHtml` already allowed.
|
|
57
61
|
*
|
|
58
62
|
* REB-606: this used to run on `isomorphic-dompurify`, which pulls `jsdom` into the
|
|
59
63
|
* import graph. `jsdom`'s transitive `html-encoding-sniffer` → `@exodus/bytes` chain
|
package/dist/index.js
CHANGED
|
@@ -13011,9 +13011,17 @@ function markdownToSafeHtml(markdown) {
|
|
|
13011
13011
|
}
|
|
13012
13012
|
|
|
13013
13013
|
// src/components/markdown-renderer.tsx
|
|
13014
|
-
import { cn } from "@reblu/site-ui";
|
|
13015
|
-
import
|
|
13014
|
+
import { cn } from "@reblu/site-ui/utils";
|
|
13015
|
+
import {
|
|
13016
|
+
domToReact,
|
|
13017
|
+
Element,
|
|
13018
|
+
htmlToDOM
|
|
13019
|
+
} from "html-react-parser";
|
|
13016
13020
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
13021
|
+
var HTML_TO_DOM_OPTIONS = { lowerCaseAttributeNames: false };
|
|
13022
|
+
function parse3(html5, options) {
|
|
13023
|
+
return domToReact(htmlToDOM(html5, HTML_TO_DOM_OPTIONS), options);
|
|
13024
|
+
}
|
|
13017
13025
|
var HEADING_CLASSES = {
|
|
13018
13026
|
1: "text-3xl font-bold mt-8 mb-4 first:mt-0",
|
|
13019
13027
|
2: "text-2xl font-bold mt-7 mb-3 first:mt-0",
|
|
@@ -13025,6 +13033,41 @@ var HEADING_CLASSES = {
|
|
|
13025
13033
|
function hasClass(el, name) {
|
|
13026
13034
|
return typeof el.attribs.class === "string" && el.attribs.class.split(/\s+/).includes(name);
|
|
13027
13035
|
}
|
|
13036
|
+
var PRESERVED_GLOBAL_ATTRIBUTES = [
|
|
13037
|
+
// Anchor targets. In-document links ("see clause 4" → `#clause-4`) resolve to
|
|
13038
|
+
// nothing without them.
|
|
13039
|
+
"id",
|
|
13040
|
+
// Language and direction of a passage — a bilingual legal page is the normal case
|
|
13041
|
+
// here, and screen readers switch voice on it.
|
|
13042
|
+
"lang",
|
|
13043
|
+
"dir",
|
|
13044
|
+
// Advisory text the author wrote; it is content, not styling.
|
|
13045
|
+
"title",
|
|
13046
|
+
// Explicit "do not publish this". Without it a block authored as `<p hidden>`
|
|
13047
|
+
// becomes VISIBLE — publishing what the author marked as not-for-publication.
|
|
13048
|
+
"hidden",
|
|
13049
|
+
// Accessible semantics. `../lib/markdown` restores these through the sanitiser
|
|
13050
|
+
// precisely so they are not lost (REB-286's axe gate); dropping them one layer
|
|
13051
|
+
// later would undo that.
|
|
13052
|
+
"role"
|
|
13053
|
+
];
|
|
13054
|
+
var PRESERVED_ATTRIBUTE_PREFIXES = ["aria-", "data-"];
|
|
13055
|
+
var PRESERVED_TAG_ATTRIBUTES = {
|
|
13056
|
+
ol: ["start", "type"],
|
|
13057
|
+
li: ["value", "type"]
|
|
13058
|
+
};
|
|
13059
|
+
var BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set(["hidden"]);
|
|
13060
|
+
function authorAttributes(el) {
|
|
13061
|
+
const tagSpecific = PRESERVED_TAG_ATTRIBUTES[el.name] ?? [];
|
|
13062
|
+
const preserved = {};
|
|
13063
|
+
for (const [rawName, value] of Object.entries(el.attribs)) {
|
|
13064
|
+
const name = rawName.toLowerCase();
|
|
13065
|
+
const keep = PRESERVED_GLOBAL_ATTRIBUTES.includes(name) || tagSpecific.includes(name) || PRESERVED_ATTRIBUTE_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
13066
|
+
if (!keep) continue;
|
|
13067
|
+
preserved[name] = BOOLEAN_ATTRIBUTES.has(name) ? true : value;
|
|
13068
|
+
}
|
|
13069
|
+
return preserved;
|
|
13070
|
+
}
|
|
13028
13071
|
function isExternalHref(href) {
|
|
13029
13072
|
return !!href && /^(https?:)?\/\//.test(href);
|
|
13030
13073
|
}
|
|
@@ -13034,29 +13077,46 @@ function buildOptions(headingOffset) {
|
|
|
13034
13077
|
if (!(node instanceof Element)) return void 0;
|
|
13035
13078
|
const children2 = () => domToReact(node.children, options);
|
|
13036
13079
|
const { name, attribs } = node;
|
|
13080
|
+
const author = authorAttributes(node);
|
|
13037
13081
|
const headingMatch = /^h([1-6])$/.exec(name);
|
|
13038
13082
|
if (headingMatch) {
|
|
13039
13083
|
const rendered = Math.min(6, Number(headingMatch[1]) + headingOffset);
|
|
13040
13084
|
const Tag = `h${rendered}`;
|
|
13041
|
-
return /* @__PURE__ */ jsx(Tag, { className: HEADING_CLASSES[rendered], children: children2() });
|
|
13085
|
+
return /* @__PURE__ */ jsx(Tag, { ...author, className: HEADING_CLASSES[rendered], children: children2() });
|
|
13042
13086
|
}
|
|
13043
13087
|
switch (name) {
|
|
13044
13088
|
case "p":
|
|
13045
|
-
return /* @__PURE__ */ jsx("p", { className: "my-4 leading-relaxed text-foreground/90", children: children2() });
|
|
13089
|
+
return /* @__PURE__ */ jsx("p", { ...author, className: "my-4 leading-relaxed text-foreground/90", children: children2() });
|
|
13046
13090
|
case "ul":
|
|
13047
|
-
return /* @__PURE__ */ 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() });
|
|
13091
|
+
return /* @__PURE__ */ 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() });
|
|
13048
13092
|
case "ol":
|
|
13049
|
-
return /* @__PURE__ */ jsx("ol", { className: "list-decimal ml-6 my-4", children: children2() });
|
|
13093
|
+
return /* @__PURE__ */ jsx("ol", { ...author, className: "list-decimal ml-6 my-4", children: children2() });
|
|
13050
13094
|
case "li":
|
|
13051
|
-
return /* @__PURE__ */ jsx("li", { className: hasClass(node, "task-list-item") ? "flex items-start gap-2 my-1" : "my-1", children: children2() });
|
|
13095
|
+
return /* @__PURE__ */ jsx("li", { ...author, className: hasClass(node, "task-list-item") ? "flex items-start gap-2 my-1" : "my-1", children: children2() });
|
|
13052
13096
|
case "blockquote":
|
|
13053
|
-
return /* @__PURE__ */ jsx(
|
|
13097
|
+
return /* @__PURE__ */ jsx(
|
|
13098
|
+
"blockquote",
|
|
13099
|
+
{
|
|
13100
|
+
...author,
|
|
13101
|
+
className: "border-l-4 border-primary/50 pl-6 italic my-6 text-muted-foreground",
|
|
13102
|
+
children: children2()
|
|
13103
|
+
}
|
|
13104
|
+
);
|
|
13054
13105
|
case "pre":
|
|
13055
|
-
return /* @__PURE__ */ jsx(
|
|
13106
|
+
return /* @__PURE__ */ jsx(
|
|
13107
|
+
"pre",
|
|
13108
|
+
{
|
|
13109
|
+
...author,
|
|
13110
|
+
className: "rounded-lg border border-input bg-muted p-4 my-6 overflow-x-auto font-mono text-sm",
|
|
13111
|
+
children: children2()
|
|
13112
|
+
}
|
|
13113
|
+
);
|
|
13056
13114
|
case "code": {
|
|
13057
13115
|
const isBlock = node.parent instanceof Element && node.parent.name === "pre";
|
|
13058
|
-
if (isBlock)
|
|
13059
|
-
|
|
13116
|
+
if (isBlock) {
|
|
13117
|
+
return /* @__PURE__ */ jsx("code", { ...author, className: "block", children: children2() });
|
|
13118
|
+
}
|
|
13119
|
+
return /* @__PURE__ */ jsx("code", { ...author, className: "rounded bg-muted px-1.5 py-0.5 font-mono text-sm", children: children2() });
|
|
13060
13120
|
}
|
|
13061
13121
|
case "a": {
|
|
13062
13122
|
const href = attribs.href;
|
|
@@ -13064,6 +13124,7 @@ function buildOptions(headingOffset) {
|
|
|
13064
13124
|
return /* @__PURE__ */ jsx(
|
|
13065
13125
|
"a",
|
|
13066
13126
|
{
|
|
13127
|
+
...author,
|
|
13067
13128
|
href,
|
|
13068
13129
|
className: "text-primary underline underline-offset-4 hover:text-primary/80",
|
|
13069
13130
|
...external ? { target: "_blank", rel: "noopener noreferrer" } : {},
|
|
@@ -13080,6 +13141,7 @@ function buildOptions(headingOffset) {
|
|
|
13080
13141
|
/* @__PURE__ */ jsx(
|
|
13081
13142
|
"img",
|
|
13082
13143
|
{
|
|
13144
|
+
...author,
|
|
13083
13145
|
src: attribs.src,
|
|
13084
13146
|
alt: attribs.alt ?? "",
|
|
13085
13147
|
loading: "lazy",
|
|
@@ -13091,6 +13153,7 @@ function buildOptions(headingOffset) {
|
|
|
13091
13153
|
return /* @__PURE__ */ jsx("div", { className: "relative my-6 aspect-video w-full overflow-hidden rounded-lg", children: /* @__PURE__ */ jsx(
|
|
13092
13154
|
"iframe",
|
|
13093
13155
|
{
|
|
13156
|
+
...author,
|
|
13094
13157
|
src: attribs.src,
|
|
13095
13158
|
title: attribs.title ?? "Embedded video",
|
|
13096
13159
|
allow: attribs.allow ?? "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
@@ -13099,13 +13162,13 @@ function buildOptions(headingOffset) {
|
|
|
13099
13162
|
}
|
|
13100
13163
|
) });
|
|
13101
13164
|
case "hr":
|
|
13102
|
-
return /* @__PURE__ */ jsx("hr", { className: "my-8 border-muted-foreground/30" });
|
|
13165
|
+
return /* @__PURE__ */ jsx("hr", { ...author, className: "my-8 border-muted-foreground/30" });
|
|
13103
13166
|
case "strong":
|
|
13104
|
-
return /* @__PURE__ */ jsx("strong", { className: "font-bold text-foreground", children: children2() });
|
|
13167
|
+
return /* @__PURE__ */ jsx("strong", { ...author, className: "font-bold text-foreground", children: children2() });
|
|
13105
13168
|
case "em":
|
|
13106
|
-
return /* @__PURE__ */ jsx("em", { className: "italic", children: children2() });
|
|
13169
|
+
return /* @__PURE__ */ jsx("em", { ...author, className: "italic", children: children2() });
|
|
13107
13170
|
case "del":
|
|
13108
|
-
return /* @__PURE__ */ jsx("del", { className: "line-through", children: children2() });
|
|
13171
|
+
return /* @__PURE__ */ jsx("del", { ...author, className: "line-through", children: children2() });
|
|
13109
13172
|
default:
|
|
13110
13173
|
return void 0;
|
|
13111
13174
|
}
|
|
@@ -13124,10 +13187,11 @@ function SafeHtml({ html: html5, className, headingOffset = 0 }) {
|
|
|
13124
13187
|
}
|
|
13125
13188
|
|
|
13126
13189
|
// src/components/treatment.tsx
|
|
13127
|
-
import { cn as cn3 } from "@reblu/site-ui";
|
|
13190
|
+
import { cn as cn3 } from "@reblu/site-ui/utils";
|
|
13128
13191
|
|
|
13129
13192
|
// src/components/_page-parts.tsx
|
|
13130
|
-
import {
|
|
13193
|
+
import { cn as cn2 } from "@reblu/site-ui/utils";
|
|
13194
|
+
import { buttonVariants } from "@reblu/site-ui/button-variants";
|
|
13131
13195
|
import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
13132
13196
|
var KNOWN_BUTTON_VARIANTS = /* @__PURE__ */ new Set([
|
|
13133
13197
|
"default",
|
|
@@ -13158,14 +13222,19 @@ function PageHero({ page }) {
|
|
|
13158
13222
|
}
|
|
13159
13223
|
function SectionCta({ section }) {
|
|
13160
13224
|
if (!section.ctaLabel || !section.ctaUrl) return null;
|
|
13161
|
-
|
|
13225
|
+
const variant = toButtonVariant(section.ctaVariant);
|
|
13226
|
+
return /* @__PURE__ */ jsx2("div", { className: "pt-2", children: /* @__PURE__ */ jsx2(
|
|
13162
13227
|
"a",
|
|
13163
13228
|
{
|
|
13164
13229
|
href: section.ctaUrl,
|
|
13230
|
+
"data-slot": "button",
|
|
13231
|
+
"data-variant": variant,
|
|
13232
|
+
"data-size": "default",
|
|
13233
|
+
className: cn2(buttonVariants({ variant, size: "default" })),
|
|
13165
13234
|
...section.ctaIsExternal ? { target: "_blank", rel: "noopener noreferrer" } : {},
|
|
13166
13235
|
children: section.ctaLabel
|
|
13167
13236
|
}
|
|
13168
|
-
) })
|
|
13237
|
+
) });
|
|
13169
13238
|
}
|
|
13170
13239
|
function SectionImages({ section }) {
|
|
13171
13240
|
if (section.images.length === 0) return null;
|
|
@@ -13213,7 +13282,7 @@ function Treatment({ page, className }) {
|
|
|
13213
13282
|
}
|
|
13214
13283
|
|
|
13215
13284
|
// src/components/specialty.tsx
|
|
13216
|
-
import { cn as cn4 } from "@reblu/site-ui";
|
|
13285
|
+
import { cn as cn4 } from "@reblu/site-ui/utils";
|
|
13217
13286
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
13218
13287
|
function Specialty({
|
|
13219
13288
|
page,
|
|
@@ -13254,7 +13323,7 @@ function Specialty({
|
|
|
13254
13323
|
}
|
|
13255
13324
|
|
|
13256
13325
|
// src/components/profile-card.tsx
|
|
13257
|
-
import { cn as cn5 } from "@reblu/site-ui";
|
|
13326
|
+
import { cn as cn5 } from "@reblu/site-ui/utils";
|
|
13258
13327
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
13259
13328
|
function initials(name) {
|
|
13260
13329
|
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase() ?? "").join("");
|
|
@@ -13296,7 +13365,7 @@ function ProfileCard({ name, role, imageUrl, specialties = [], bio, href, classN
|
|
|
13296
13365
|
}
|
|
13297
13366
|
|
|
13298
13367
|
// src/components/product-card.tsx
|
|
13299
|
-
import { cn as cn6 } from "@reblu/site-ui";
|
|
13368
|
+
import { cn as cn6 } from "@reblu/site-ui/utils";
|
|
13300
13369
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
13301
13370
|
function ProductCard({ product, href, currencyCode = "EUR", locale, className }) {
|
|
13302
13371
|
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|