@reblu/site-blocks 0.17.0 → 0.18.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/CHANGELOG.md +12 -0
- package/README.md +44 -0
- package/dist/index.cjs +67 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +67 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -227,6 +227,14 @@ interface MarkdownRendererProps {
|
|
|
227
227
|
* the page keeps a single h1.
|
|
228
228
|
*/
|
|
229
229
|
headingOffset?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Land the body's SHALLOWEST heading at `1 + headingOffset` instead of shifting every
|
|
232
|
+
* heading by a fixed amount, so standard Markdown (`##` sections) renders `<h2>` under
|
|
233
|
+
* the page `<h1>` just like the convention (`#` sections) does. Relative depth is kept
|
|
234
|
+
* and levels clamp at h6. Use it where the body sits directly under the page `<h1>`;
|
|
235
|
+
* leave it off where the body sits under its own section heading. Default `false`.
|
|
236
|
+
*/
|
|
237
|
+
normalizeHeadings?: boolean;
|
|
230
238
|
/**
|
|
231
239
|
* Restrict and render images (REB-1075). Absent: every image the sanitiser keeps is
|
|
232
240
|
* rendered as a lazy `<img>` inside its paragraph (the historical behaviour).
|
|
@@ -247,7 +255,7 @@ interface MarkdownImageOptions {
|
|
|
247
255
|
alt: string;
|
|
248
256
|
}) => React$1.ReactNode;
|
|
249
257
|
}
|
|
250
|
-
declare function MarkdownRenderer({ content, className, headingOffset, images, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
258
|
+
declare function MarkdownRenderer({ content, className, headingOffset, normalizeHeadings, images, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
251
259
|
/**
|
|
252
260
|
* Render an **already-sanitised** HTML string through the same token-driven
|
|
253
261
|
* component map as {@link MarkdownRenderer}. Consumers pass HTML that has been run
|
|
@@ -258,8 +266,10 @@ interface SafeHtmlProps {
|
|
|
258
266
|
html: string | null | undefined;
|
|
259
267
|
className?: string;
|
|
260
268
|
headingOffset?: number;
|
|
269
|
+
/** See {@link MarkdownRendererProps.normalizeHeadings}. Default `false`. */
|
|
270
|
+
normalizeHeadings?: boolean;
|
|
261
271
|
}
|
|
262
|
-
declare function SafeHtml({ html, className, headingOffset }: SafeHtmlProps): react_jsx_runtime.JSX.Element | null;
|
|
272
|
+
declare function SafeHtml({ html, className, headingOffset, normalizeHeadings, }: SafeHtmlProps): react_jsx_runtime.JSX.Element | null;
|
|
263
273
|
|
|
264
274
|
/** A heading level a caller can configure — `h1`…`h6`. */
|
|
265
275
|
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
package/dist/index.d.ts
CHANGED
|
@@ -227,6 +227,14 @@ interface MarkdownRendererProps {
|
|
|
227
227
|
* the page keeps a single h1.
|
|
228
228
|
*/
|
|
229
229
|
headingOffset?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Land the body's SHALLOWEST heading at `1 + headingOffset` instead of shifting every
|
|
232
|
+
* heading by a fixed amount, so standard Markdown (`##` sections) renders `<h2>` under
|
|
233
|
+
* the page `<h1>` just like the convention (`#` sections) does. Relative depth is kept
|
|
234
|
+
* and levels clamp at h6. Use it where the body sits directly under the page `<h1>`;
|
|
235
|
+
* leave it off where the body sits under its own section heading. Default `false`.
|
|
236
|
+
*/
|
|
237
|
+
normalizeHeadings?: boolean;
|
|
230
238
|
/**
|
|
231
239
|
* Restrict and render images (REB-1075). Absent: every image the sanitiser keeps is
|
|
232
240
|
* rendered as a lazy `<img>` inside its paragraph (the historical behaviour).
|
|
@@ -247,7 +255,7 @@ interface MarkdownImageOptions {
|
|
|
247
255
|
alt: string;
|
|
248
256
|
}) => React$1.ReactNode;
|
|
249
257
|
}
|
|
250
|
-
declare function MarkdownRenderer({ content, className, headingOffset, images, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
258
|
+
declare function MarkdownRenderer({ content, className, headingOffset, normalizeHeadings, images, }: MarkdownRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
251
259
|
/**
|
|
252
260
|
* Render an **already-sanitised** HTML string through the same token-driven
|
|
253
261
|
* component map as {@link MarkdownRenderer}. Consumers pass HTML that has been run
|
|
@@ -258,8 +266,10 @@ interface SafeHtmlProps {
|
|
|
258
266
|
html: string | null | undefined;
|
|
259
267
|
className?: string;
|
|
260
268
|
headingOffset?: number;
|
|
269
|
+
/** See {@link MarkdownRendererProps.normalizeHeadings}. Default `false`. */
|
|
270
|
+
normalizeHeadings?: boolean;
|
|
261
271
|
}
|
|
262
|
-
declare function SafeHtml({ html, className, headingOffset }: SafeHtmlProps): react_jsx_runtime.JSX.Element | null;
|
|
272
|
+
declare function SafeHtml({ html, className, headingOffset, normalizeHeadings, }: SafeHtmlProps): react_jsx_runtime.JSX.Element | null;
|
|
263
273
|
|
|
264
274
|
/** A heading level a caller can configure — `h1`…`h6`. */
|
|
265
275
|
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
package/dist/index.js
CHANGED
|
@@ -13131,8 +13131,25 @@ function slotClassName(structural, defaults, consumer) {
|
|
|
13131
13131
|
// src/components/markdown-renderer.tsx
|
|
13132
13132
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13133
13133
|
var HTML_TO_DOM_OPTIONS = { lowerCaseAttributeNames: false };
|
|
13134
|
-
function
|
|
13135
|
-
|
|
13134
|
+
function shallowestHeadingLevel(nodes) {
|
|
13135
|
+
let shallowest = null;
|
|
13136
|
+
for (const node of nodes) {
|
|
13137
|
+
if (!(node instanceof Element)) continue;
|
|
13138
|
+
const match = /^h([1-6])$/.exec(node.name);
|
|
13139
|
+
const level = match ? Number(match[1]) : shallowestHeadingLevel(node.children);
|
|
13140
|
+
if (level !== null && (shallowest === null || level < shallowest)) shallowest = level;
|
|
13141
|
+
}
|
|
13142
|
+
return shallowest;
|
|
13143
|
+
}
|
|
13144
|
+
function headingShift(nodes, headingOffset, normalize2) {
|
|
13145
|
+
if (!normalize2) return headingOffset;
|
|
13146
|
+
const shallowest = shallowestHeadingLevel(nodes);
|
|
13147
|
+
return shallowest === null ? headingOffset : 1 + headingOffset - shallowest;
|
|
13148
|
+
}
|
|
13149
|
+
function parse3(html5, headingOffset, normalizeHeadings, images) {
|
|
13150
|
+
const nodes = htmlToDOM(html5, HTML_TO_DOM_OPTIONS);
|
|
13151
|
+
const shift = headingShift(nodes, headingOffset, normalizeHeadings);
|
|
13152
|
+
return domToReact(nodes, buildOptions(shift, images));
|
|
13136
13153
|
}
|
|
13137
13154
|
function isAllowedImage(src, prefixes) {
|
|
13138
13155
|
if (!src) return false;
|
|
@@ -13190,6 +13207,14 @@ function authorAttributes(el) {
|
|
|
13190
13207
|
}
|
|
13191
13208
|
return preserved;
|
|
13192
13209
|
}
|
|
13210
|
+
var CELL_ALIGN_CLASSES = {
|
|
13211
|
+
left: "text-left",
|
|
13212
|
+
center: "text-center",
|
|
13213
|
+
right: "text-right"
|
|
13214
|
+
};
|
|
13215
|
+
function cellAlignClass(align) {
|
|
13216
|
+
return align ? CELL_ALIGN_CLASSES[align.toLowerCase()] : void 0;
|
|
13217
|
+
}
|
|
13193
13218
|
function isExternalHref(href) {
|
|
13194
13219
|
return !!href && /^(https?:)?\/\//.test(href);
|
|
13195
13220
|
}
|
|
@@ -13241,7 +13266,7 @@ function buildOptions(headingOffset, images) {
|
|
|
13241
13266
|
const author = authorAttributes(node);
|
|
13242
13267
|
const headingMatch = /^h([1-6])$/.exec(name);
|
|
13243
13268
|
if (headingMatch) {
|
|
13244
|
-
const rendered = Math.min(6, Number(headingMatch[1]) + headingOffset);
|
|
13269
|
+
const rendered = Math.max(1, Math.min(6, Number(headingMatch[1]) + headingOffset));
|
|
13245
13270
|
const Tag = `h${rendered}`;
|
|
13246
13271
|
return /* @__PURE__ */ jsx(Tag, { ...author, className: HEADING_CLASSES[rendered], children: children2() });
|
|
13247
13272
|
}
|
|
@@ -13323,6 +13348,36 @@ function buildOptions(headingOffset, images) {
|
|
|
13323
13348
|
className: "absolute inset-0 h-full w-full border-0"
|
|
13324
13349
|
}
|
|
13325
13350
|
) });
|
|
13351
|
+
// GFM tables (a cookie policy's cookie list, a price table). The table scrolls
|
|
13352
|
+
// inside its own wrapper so a wide one never pushes the page sideways on a phone.
|
|
13353
|
+
case "table":
|
|
13354
|
+
return /* @__PURE__ */ jsx("div", { className: "my-6 w-full overflow-x-auto", children: /* @__PURE__ */ jsx("table", { ...author, className: "w-full border-collapse text-sm", children: children2() }) });
|
|
13355
|
+
case "thead":
|
|
13356
|
+
return /* @__PURE__ */ jsx("thead", { ...author, className: "bg-muted", children: children2() });
|
|
13357
|
+
case "tbody":
|
|
13358
|
+
return /* @__PURE__ */ jsx("tbody", { ...author, children: children2() });
|
|
13359
|
+
case "tr":
|
|
13360
|
+
return /* @__PURE__ */ jsx("tr", { ...author, children: children2() });
|
|
13361
|
+
case "th":
|
|
13362
|
+
return /* @__PURE__ */ jsx(
|
|
13363
|
+
"th",
|
|
13364
|
+
{
|
|
13365
|
+
...author,
|
|
13366
|
+
className: `border border-border px-3 py-2 font-semibold text-foreground ${cellAlignClass(attribs.align) ?? "text-left"}`,
|
|
13367
|
+
children: children2()
|
|
13368
|
+
}
|
|
13369
|
+
);
|
|
13370
|
+
case "td": {
|
|
13371
|
+
const align = cellAlignClass(attribs.align);
|
|
13372
|
+
return /* @__PURE__ */ jsx(
|
|
13373
|
+
"td",
|
|
13374
|
+
{
|
|
13375
|
+
...author,
|
|
13376
|
+
className: `border border-border px-3 py-2 align-top text-foreground/90${align ? ` ${align}` : ""}`,
|
|
13377
|
+
children: children2()
|
|
13378
|
+
}
|
|
13379
|
+
);
|
|
13380
|
+
}
|
|
13326
13381
|
case "hr":
|
|
13327
13382
|
return /* @__PURE__ */ jsx("hr", { ...author, className: "my-8 border-muted-foreground/30" });
|
|
13328
13383
|
case "strong":
|
|
@@ -13342,15 +13397,21 @@ function MarkdownRenderer({
|
|
|
13342
13397
|
content,
|
|
13343
13398
|
className,
|
|
13344
13399
|
headingOffset = 0,
|
|
13400
|
+
normalizeHeadings = false,
|
|
13345
13401
|
images
|
|
13346
13402
|
}) {
|
|
13347
13403
|
const html5 = markdownToSafeHtml(content);
|
|
13348
13404
|
if (!html5) return null;
|
|
13349
|
-
return /* @__PURE__ */ jsx("div", { className: slotClassName("max-w-none", "", className), "data-slot": "markdown", children: parse3(html5,
|
|
13405
|
+
return /* @__PURE__ */ jsx("div", { className: slotClassName("max-w-none", "", className), "data-slot": "markdown", children: parse3(html5, headingOffset, normalizeHeadings, images) });
|
|
13350
13406
|
}
|
|
13351
|
-
function SafeHtml({
|
|
13407
|
+
function SafeHtml({
|
|
13408
|
+
html: html5,
|
|
13409
|
+
className,
|
|
13410
|
+
headingOffset = 0,
|
|
13411
|
+
normalizeHeadings = false
|
|
13412
|
+
}) {
|
|
13352
13413
|
if (!html5) return null;
|
|
13353
|
-
return /* @__PURE__ */ jsx("div", { className: slotClassName("max-w-none", "", className), "data-slot": "safe-html", children: parse3(html5,
|
|
13414
|
+
return /* @__PURE__ */ jsx("div", { className: slotClassName("max-w-none", "", className), "data-slot": "safe-html", children: parse3(html5, headingOffset, normalizeHeadings) });
|
|
13354
13415
|
}
|
|
13355
13416
|
|
|
13356
13417
|
// src/components/course-description.tsx
|