@headroom-cms/api 0.1.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/react.js ADDED
@@ -0,0 +1,275 @@
1
+ // src/react/InlineContent.tsx
2
+ import React from "react";
3
+ import { Fragment, jsx } from "react/jsx-runtime";
4
+ function styleClasses(styles) {
5
+ if (!styles) return void 0;
6
+ const classes = [];
7
+ if (styles.bold) classes.push("hr-bold");
8
+ if (styles.italic) classes.push("hr-italic");
9
+ if (styles.underline) classes.push("hr-underline");
10
+ if (styles.strikethrough) classes.push("hr-strikethrough");
11
+ if (styles.code) classes.push("hr-code");
12
+ return classes.length > 0 ? classes.join(" ") : void 0;
13
+ }
14
+ function resolveHref(href, refs, resolveContentLink) {
15
+ if (!href.startsWith("headroom://content/")) return href;
16
+ const match = href.match(/^headroom:\/\/content\/([^/]+)\/([A-Z0-9]+)$/);
17
+ if (!match) return "#";
18
+ const [, , contentId] = match;
19
+ const ref = refs?.[contentId];
20
+ if (!ref) return "#";
21
+ return resolveContentLink ? resolveContentLink(ref) : `/${ref.collection}/${ref.slug}`;
22
+ }
23
+ function InlineContent({ content, refs, resolveContentLink }) {
24
+ if (!content || content.length === 0) return null;
25
+ return /* @__PURE__ */ jsx(Fragment, { children: content.map((item, i) => {
26
+ if (item.type === "link") {
27
+ const resolved = resolveHref(item.href, refs, resolveContentLink);
28
+ return /* @__PURE__ */ jsx("a", { href: resolved, className: "hr-link", children: /* @__PURE__ */ jsx(
29
+ InlineContent,
30
+ {
31
+ content: item.content,
32
+ refs,
33
+ resolveContentLink
34
+ }
35
+ ) }, i);
36
+ }
37
+ const cls = styleClasses(item.styles);
38
+ return cls ? /* @__PURE__ */ jsx("span", { className: cls, children: item.text }, i) : /* @__PURE__ */ jsx(React.Fragment, { children: item.text }, i);
39
+ }) });
40
+ }
41
+
42
+ // src/react/blocks/Paragraph.tsx
43
+ import { jsx as jsx2 } from "react/jsx-runtime";
44
+ function Paragraph({ block, refs, resolveContentLink }) {
45
+ const content = Array.isArray(block.content) ? block.content : [];
46
+ return /* @__PURE__ */ jsx2("p", { className: "hr-paragraph", children: /* @__PURE__ */ jsx2(InlineContent, { content, refs, resolveContentLink }) });
47
+ }
48
+
49
+ // src/react/blocks/Heading.tsx
50
+ import { jsx as jsx3 } from "react/jsx-runtime";
51
+ function Heading({ block, refs, resolveContentLink }) {
52
+ const level = Math.min(block.props?.level || 1, 6);
53
+ const content = Array.isArray(block.content) ? block.content : [];
54
+ const className = `hr-heading hr-h${level}`;
55
+ const inner = /* @__PURE__ */ jsx3(InlineContent, { content, refs, resolveContentLink });
56
+ switch (level) {
57
+ case 1:
58
+ return /* @__PURE__ */ jsx3("h1", { className, children: inner });
59
+ case 2:
60
+ return /* @__PURE__ */ jsx3("h2", { className, children: inner });
61
+ case 3:
62
+ return /* @__PURE__ */ jsx3("h3", { className, children: inner });
63
+ case 4:
64
+ return /* @__PURE__ */ jsx3("h4", { className, children: inner });
65
+ case 5:
66
+ return /* @__PURE__ */ jsx3("h5", { className, children: inner });
67
+ case 6:
68
+ return /* @__PURE__ */ jsx3("h6", { className, children: inner });
69
+ default:
70
+ return /* @__PURE__ */ jsx3("h1", { className, children: inner });
71
+ }
72
+ }
73
+
74
+ // src/react/blocks/Image.tsx
75
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
76
+ function ImageBlock({ block, cdnUrl }) {
77
+ const storedPath = block.props?.url;
78
+ const src = storedPath ? cdnUrl ? `${cdnUrl}${storedPath}` : storedPath : "";
79
+ const alt = block.props?.alt || "";
80
+ const caption = block.props?.caption || "";
81
+ if (!src) return null;
82
+ return /* @__PURE__ */ jsxs("figure", { className: "hr-image", children: [
83
+ /* @__PURE__ */ jsx4("img", { src, alt, loading: "lazy" }),
84
+ caption && /* @__PURE__ */ jsx4("figcaption", { children: caption })
85
+ ] });
86
+ }
87
+
88
+ // src/react/blocks/CodeBlock.tsx
89
+ import { jsx as jsx5 } from "react/jsx-runtime";
90
+ function CodeBlock({ block }) {
91
+ const language = block.props?.language || "";
92
+ const content = Array.isArray(block.content) ? block.content : [];
93
+ const code = content.map((c) => "text" in c ? c.text : "").join("");
94
+ return /* @__PURE__ */ jsx5("pre", { className: "hr-code-block", children: /* @__PURE__ */ jsx5("code", { className: language ? `language-${language}` : "", children: code }) });
95
+ }
96
+
97
+ // src/react/blocks/BulletList.tsx
98
+ import { jsx as jsx6 } from "react/jsx-runtime";
99
+ function BulletList({ items, refs, resolveContentLink }) {
100
+ return /* @__PURE__ */ jsx6("ul", { className: "hr-bullet-list", children: (items || []).map((item) => {
101
+ const content = Array.isArray(item.content) ? item.content : [];
102
+ return /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsx6(InlineContent, { content, refs, resolveContentLink }) }, item.id);
103
+ }) });
104
+ }
105
+
106
+ // src/react/blocks/NumberedList.tsx
107
+ import { jsx as jsx7 } from "react/jsx-runtime";
108
+ function NumberedList({ items, refs, resolveContentLink }) {
109
+ return /* @__PURE__ */ jsx7("ol", { className: "hr-numbered-list", children: (items || []).map((item) => {
110
+ const content = Array.isArray(item.content) ? item.content : [];
111
+ return /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(InlineContent, { content, refs, resolveContentLink }) }, item.id);
112
+ }) });
113
+ }
114
+
115
+ // src/react/blocks/CheckList.tsx
116
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
117
+ function CheckList({ items, refs, resolveContentLink }) {
118
+ return /* @__PURE__ */ jsx8("ul", { className: "hr-check-list", children: (items || []).map((item) => {
119
+ const checked = item.props?.checked;
120
+ const content = Array.isArray(item.content) ? item.content : [];
121
+ return /* @__PURE__ */ jsxs2("li", { className: "hr-check-item", "data-checked": checked || void 0, children: [
122
+ /* @__PURE__ */ jsx8("span", { className: "hr-checkbox", "aria-checked": checked, children: checked && /* @__PURE__ */ jsx8("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx8(
123
+ "path",
124
+ {
125
+ d: "M4 8l3 3 5-6",
126
+ stroke: "currentColor",
127
+ strokeWidth: "2",
128
+ strokeLinecap: "round",
129
+ strokeLinejoin: "round"
130
+ }
131
+ ) }) }),
132
+ /* @__PURE__ */ jsx8("span", { children: /* @__PURE__ */ jsx8(InlineContent, { content, refs, resolveContentLink }) })
133
+ ] }, item.id);
134
+ }) });
135
+ }
136
+
137
+ // src/react/blocks/Table.tsx
138
+ import { jsx as jsx9 } from "react/jsx-runtime";
139
+ function Table({ block, refs, resolveContentLink }) {
140
+ const tableContent = block.content;
141
+ const rows = tableContent?.rows || [];
142
+ if (rows.length === 0) return null;
143
+ return /* @__PURE__ */ jsx9("table", { className: "hr-table", children: /* @__PURE__ */ jsx9("tbody", { children: rows.map((row, rowIndex) => /* @__PURE__ */ jsx9("tr", { children: row.cells.map((cell, cellIndex) => {
144
+ const Tag = rowIndex === 0 ? "th" : "td";
145
+ return /* @__PURE__ */ jsx9(Tag, { children: /* @__PURE__ */ jsx9(InlineContent, { content: cell, refs, resolveContentLink }) }, cellIndex);
146
+ }) }, rowIndex)) }) });
147
+ }
148
+
149
+ // src/react/blocks/Fallback.tsx
150
+ import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
151
+ function Fallback({ block, refs, resolveContentLink }) {
152
+ const content = Array.isArray(block.content) ? block.content : [];
153
+ return /* @__PURE__ */ jsxs3("div", { className: "hr-fallback", "data-block-type": block.type, children: [
154
+ /* @__PURE__ */ jsx10("span", { className: "hr-fallback-label", children: block.type }),
155
+ content.length > 0 && /* @__PURE__ */ jsx10("p", { children: /* @__PURE__ */ jsx10(InlineContent, { content, refs, resolveContentLink }) })
156
+ ] });
157
+ }
158
+
159
+ // src/react/blocks/index.ts
160
+ var defaultBlockComponents = {
161
+ paragraph: Paragraph,
162
+ heading: Heading,
163
+ image: ImageBlock,
164
+ codeBlock: CodeBlock,
165
+ bulletList: BulletList,
166
+ numberedList: NumberedList,
167
+ checkList: CheckList,
168
+ table: Table
169
+ };
170
+
171
+ // src/react/BlockRenderer.tsx
172
+ import { jsx as jsx11 } from "react/jsx-runtime";
173
+ function defaultResolveContentLink(ref) {
174
+ if (!ref.published) return "#";
175
+ return `/${ref.collection}/${ref.slug}`;
176
+ }
177
+ function BlockRenderer({
178
+ blocks,
179
+ components,
180
+ cdnUrl,
181
+ refs,
182
+ resolveContentLink,
183
+ fallback,
184
+ className
185
+ }) {
186
+ const merged = { ...defaultBlockComponents, ...components };
187
+ const renderItems = groupListItems(blocks);
188
+ const FallbackComponent = fallback === null ? null : fallback ?? Fallback;
189
+ const resolve = resolveContentLink ?? defaultResolveContentLink;
190
+ return /* @__PURE__ */ jsx11("div", { className, children: renderItems.map((item, i) => {
191
+ if (item.listType) {
192
+ const ListComponent = merged[item.listType];
193
+ if (ListComponent) {
194
+ return /* @__PURE__ */ jsx11(
195
+ ListComponent,
196
+ {
197
+ block: item.items[0],
198
+ items: item.items,
199
+ cdnUrl,
200
+ refs,
201
+ resolveContentLink: resolve,
202
+ components: merged
203
+ },
204
+ item.items[0].id
205
+ );
206
+ }
207
+ return null;
208
+ }
209
+ const block = item.block;
210
+ const Component = merged[block.type];
211
+ if (!Component) {
212
+ if (FallbackComponent) {
213
+ return /* @__PURE__ */ jsx11(
214
+ FallbackComponent,
215
+ {
216
+ block,
217
+ cdnUrl,
218
+ refs,
219
+ resolveContentLink: resolve,
220
+ components: merged
221
+ },
222
+ block.id
223
+ );
224
+ }
225
+ return null;
226
+ }
227
+ return /* @__PURE__ */ jsx11(
228
+ Component,
229
+ {
230
+ block,
231
+ cdnUrl,
232
+ refs,
233
+ resolveContentLink: resolve,
234
+ components: merged
235
+ },
236
+ block.id
237
+ );
238
+ }) });
239
+ }
240
+ var LIST_TYPE_MAP = {
241
+ bulletListItem: "bulletList",
242
+ numberedListItem: "numberedList",
243
+ checkListItem: "checkList"
244
+ };
245
+ function groupListItems(blocks) {
246
+ const result = [];
247
+ for (const block of blocks) {
248
+ const listType = LIST_TYPE_MAP[block.type];
249
+ if (listType) {
250
+ const last = result[result.length - 1];
251
+ if (last?.listType === listType) {
252
+ last.items.push(block);
253
+ } else {
254
+ result.push({ listType, items: [block] });
255
+ }
256
+ } else {
257
+ result.push({ block });
258
+ }
259
+ }
260
+ return result;
261
+ }
262
+ export {
263
+ BlockRenderer,
264
+ BulletList,
265
+ CheckList,
266
+ CodeBlock,
267
+ Fallback,
268
+ Heading,
269
+ ImageBlock,
270
+ InlineContent,
271
+ NumberedList,
272
+ Paragraph,
273
+ Table,
274
+ defaultBlockComponents
275
+ };
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@headroom-cms/api",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js",
9
+ "require": "./dist/index.cjs"
10
+ },
11
+ "./react": {
12
+ "types": "./dist/react.d.ts",
13
+ "import": "./dist/react.js",
14
+ "require": "./dist/react.cjs"
15
+ },
16
+ "./react/headroom-blocks.css": "./dist/headroom-blocks.css",
17
+ "./blocks/*": "./blocks/*",
18
+ "./astro": {
19
+ "types": "./dist/astro.d.ts",
20
+ "import": "./dist/astro.js"
21
+ },
22
+ "./codegen": {
23
+ "types": "./dist/codegen.d.ts",
24
+ "import": "./dist/codegen.js",
25
+ "require": "./dist/codegen.cjs"
26
+ }
27
+ },
28
+ "license": "PolyForm-Noncommercial-1.0.0",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "blocks"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "dev": "tsup --watch",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "typecheck": "tsc --noEmit"
42
+ },
43
+ "peerDependencies": {
44
+ "react": ">=18",
45
+ "react-dom": ">=18",
46
+ "astro": ">=5"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "react": {
50
+ "optional": true
51
+ },
52
+ "react-dom": {
53
+ "optional": true
54
+ },
55
+ "astro": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "devDependencies": {
60
+ "@testing-library/jest-dom": "~6.6.3",
61
+ "@testing-library/react": "~16.3.0",
62
+ "@types/react": "~19.1.0",
63
+ "jsdom": "~26.1.0",
64
+ "react": "~19.1.0",
65
+ "react-dom": "~19.1.0",
66
+ "tsup": "~8.5.0",
67
+ "typescript": "~5.9.3",
68
+ "astro": "^5.0.0",
69
+ "@types/node": "~22.0.0",
70
+ "vitest": "~4.0.18"
71
+ }
72
+ }