@reblu/site-blocks 0.32.0 → 0.34.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.
@@ -1,5 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ShopCheckoutCopy, ShopCheckoutInput, ShopBillingInput } from '@reblu/site-contracts';
2
+ import { ShopCheckoutCopy, ShopCheckoutInput, ShopBillingInput, BlockType, PageBlock, ResolvedEmbed } from '@reblu/site-contracts';
3
+ import { ComponentType } from 'react';
3
4
 
4
5
  /** A heading level a caller can configure — `h1`…`h6`. */
5
6
  type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
@@ -137,4 +138,106 @@ interface FullInvoiceFieldsProps {
137
138
  }
138
139
  declare function FullInvoiceFields({ value, onChange, overCap, issuesInvoices, errors, countries, labels, className, }: FullInvoiceFieldsProps): react_jsx_runtime.JSX.Element | null;
139
140
 
140
- export { type CheckoutTermsCopy as C, DEFAULT_INVOICE_COUNTRIES as D, EMPTY_FULL_INVOICE_VALUE as E, type FullInvoiceFieldsLabels as F, type HeadingLevel as H, type FullInvoiceValue as a, CheckoutTermsAcceptance as b, type CheckoutTermsAcceptanceProps as c, type FullInvoiceErrors as d, FullInvoiceFields as e, type FullInvoiceFieldsProps as f, type FullInvoiceIdType as g, toCheckoutTermsFields as h, isOverSimplifiedInvoiceCap as i, shopIssuesInvoices as s, toCheckoutInvoiceFields as t };
141
+ /**
142
+ * REB-850 — `PageBlocks`: the shared renderer for a page's registry blocks
143
+ * (`PageDetail.blocks`, REB-75), so a SITE stops hand-writing its own.
144
+ *
145
+ * A SERVER COMPONENT by default: no hooks, no `'use client'`, reachable from the
146
+ * RSC-safe root entry (`test/rsc-safety.test.ts` renders it under
147
+ * `--conditions=react-server`).
148
+ *
149
+ * Each block is wrapped in a `<section>` carrying the parent section's
150
+ * mirrored fields (REB-849): `heading`, `backgroundColor` and `cta`. The body
151
+ * comes from a per-type renderer that the `components` map can replace.
152
+ *
153
+ * Fail-closed rules:
154
+ * • an unknown `blockType` renders nothing (the whole section) and never throws;
155
+ * • an embed whose `resolved` is `null`/absent renders nothing — no heading,
156
+ * no dead link;
157
+ * • deprecated alias types (`doctor_embed`, `specialty_embed` — REB-852)
158
+ * render through their generic type's renderer (`profile_embed`, `page_embed`).
159
+ */
160
+ type DeprecatedAlias = 'doctor_embed' | 'specialty_embed';
161
+ /** Block types a renderer is registered under — deprecated aliases resolve to these. */
162
+ type CanonicalBlockType = Exclude<BlockType, DeprecatedAlias>;
163
+ type EmbedType = 'page_embed' | 'profile_embed';
164
+ type WireTypesOf<T extends CanonicalBlockType> = T extends 'profile_embed' ? 'profile_embed' | 'doctor_embed' : T extends 'page_embed' ? 'page_embed' | 'specialty_embed' : T;
165
+ /**
166
+ * The block a renderer for `T` receives — including its deprecated alias, and
167
+ * with `resolved` guaranteed non-null for embeds (unresolved ones never render).
168
+ */
169
+ type RenderableBlock<T extends CanonicalBlockType> = T extends EmbedType ? Extract<PageBlock, {
170
+ blockType: WireTypesOf<T>;
171
+ }> & {
172
+ resolved: ResolvedEmbed;
173
+ } : Extract<PageBlock, {
174
+ blockType: WireTypesOf<T>;
175
+ }>;
176
+ interface BlockRendererProps<T extends CanonicalBlockType> {
177
+ block: RenderableBlock<T>;
178
+ /** Level for headings INSIDE the block — one below the section heading when there is one. */
179
+ headingLevel: HeadingLevel;
180
+ /** Builds an embed's href (the `embedHref` prop, or the default `/${slug}`). */
181
+ embedHref: (resolved: ResolvedEmbed) => string;
182
+ /**
183
+ * REB-1268 — set only for block types that draw the SECTION heading
184
+ * themselves (`image_text`, whose heading sits beside the image): the id the
185
+ * section's `aria-labelledby` points at. `headingLevel` is then the section
186
+ * heading's own level. Undefined for every other block type.
187
+ */
188
+ headingId?: string;
189
+ }
190
+ /** Per-block-type renderer overrides, keyed by canonical type. */
191
+ type PageBlocksComponents = Partial<{
192
+ [T in CanonicalBlockType]: ComponentType<BlockRendererProps<T>>;
193
+ }>;
194
+ interface PageBlocksProps {
195
+ blocks: readonly PageBlock[] | null | undefined;
196
+ /** Replace the body renderer of any block type. */
197
+ components?: PageBlocksComponents;
198
+ /** Level of each section heading. Default `2` (the page title is the `h1`). */
199
+ headingLevel?: HeadingLevel;
200
+ /** Href for an embedded page. Default `/${resolved.slug}`. */
201
+ embedHref?: (resolved: ResolvedEmbed) => string;
202
+ /**
203
+ * Namespace for the section heading ids (`aria-labelledby` targets). Pass a
204
+ * distinct value when one document renders `PageBlocks` more than once with
205
+ * overlapping blocks. Sanitised to a valid IDREF. Default `'page-block'`.
206
+ */
207
+ idPrefix?: string;
208
+ className?: string;
209
+ sectionClassName?: string;
210
+ }
211
+ declare function PageBlocks({ blocks, components, headingLevel, embedHref, idPrefix, className, sectionClassName, }: PageBlocksProps): react_jsx_runtime.JSX.Element | null;
212
+
213
+ /**
214
+ * REB-1100 — pure helpers over a page's `links` (the pages it picked in nexus,
215
+ * e.g. a team profile's areas). RSC-safe, no React.
216
+ *
217
+ * Structural types on purpose: any list item that carries `links` works, and
218
+ * an item from an API that predates `links` (absent) simply has none.
219
+ */
220
+ interface PageLinkRef {
221
+ slug: string;
222
+ title: string;
223
+ category?: {
224
+ name: string;
225
+ slug: string;
226
+ } | null;
227
+ }
228
+ interface LinkFilterOption {
229
+ /** Stable key: `{categorySlug}/{slug}` (a slug is unique per category). */
230
+ key: string;
231
+ label: string;
232
+ }
233
+ declare function linkKey(link: PageLinkRef): string;
234
+ /** Each distinct link across `items`, once, in first-seen order. */
235
+ declare function linkFilterOptions(items: Array<{
236
+ links?: PageLinkRef[] | null;
237
+ }>): LinkFilterOption[];
238
+ /** The items linked to `key`; every item when `key` is null. */
239
+ declare function filterByLink<T extends {
240
+ links?: PageLinkRef[] | null;
241
+ }>(items: T[], key: string | null): T[];
242
+
243
+ export { type BlockRendererProps as B, type CheckoutTermsCopy as C, DEFAULT_INVOICE_COUNTRIES as D, EMPTY_FULL_INVOICE_VALUE as E, type FullInvoiceFieldsLabels as F, type HeadingLevel as H, type LinkFilterOption as L, type PageLinkRef as P, type RenderableBlock as R, type FullInvoiceValue as a, type CanonicalBlockType as b, CheckoutTermsAcceptance as c, type CheckoutTermsAcceptanceProps as d, type FullInvoiceErrors as e, FullInvoiceFields as f, type FullInvoiceFieldsProps as g, type FullInvoiceIdType as h, PageBlocks as i, type PageBlocksComponents as j, type PageBlocksProps as k, filterByLink as l, isOverSimplifiedInvoiceCap as m, linkFilterOptions as n, linkKey as o, toCheckoutTermsFields as p, shopIssuesInvoices as s, toCheckoutInvoiceFields as t };
@@ -1,5 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ShopCheckoutCopy, ShopCheckoutInput, ShopBillingInput } from '@reblu/site-contracts';
2
+ import { ShopCheckoutCopy, ShopCheckoutInput, ShopBillingInput, BlockType, PageBlock, ResolvedEmbed } from '@reblu/site-contracts';
3
+ import { ComponentType } from 'react';
3
4
 
4
5
  /** A heading level a caller can configure — `h1`…`h6`. */
5
6
  type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
@@ -137,4 +138,106 @@ interface FullInvoiceFieldsProps {
137
138
  }
138
139
  declare function FullInvoiceFields({ value, onChange, overCap, issuesInvoices, errors, countries, labels, className, }: FullInvoiceFieldsProps): react_jsx_runtime.JSX.Element | null;
139
140
 
140
- export { type CheckoutTermsCopy as C, DEFAULT_INVOICE_COUNTRIES as D, EMPTY_FULL_INVOICE_VALUE as E, type FullInvoiceFieldsLabels as F, type HeadingLevel as H, type FullInvoiceValue as a, CheckoutTermsAcceptance as b, type CheckoutTermsAcceptanceProps as c, type FullInvoiceErrors as d, FullInvoiceFields as e, type FullInvoiceFieldsProps as f, type FullInvoiceIdType as g, toCheckoutTermsFields as h, isOverSimplifiedInvoiceCap as i, shopIssuesInvoices as s, toCheckoutInvoiceFields as t };
141
+ /**
142
+ * REB-850 — `PageBlocks`: the shared renderer for a page's registry blocks
143
+ * (`PageDetail.blocks`, REB-75), so a SITE stops hand-writing its own.
144
+ *
145
+ * A SERVER COMPONENT by default: no hooks, no `'use client'`, reachable from the
146
+ * RSC-safe root entry (`test/rsc-safety.test.ts` renders it under
147
+ * `--conditions=react-server`).
148
+ *
149
+ * Each block is wrapped in a `<section>` carrying the parent section's
150
+ * mirrored fields (REB-849): `heading`, `backgroundColor` and `cta`. The body
151
+ * comes from a per-type renderer that the `components` map can replace.
152
+ *
153
+ * Fail-closed rules:
154
+ * • an unknown `blockType` renders nothing (the whole section) and never throws;
155
+ * • an embed whose `resolved` is `null`/absent renders nothing — no heading,
156
+ * no dead link;
157
+ * • deprecated alias types (`doctor_embed`, `specialty_embed` — REB-852)
158
+ * render through their generic type's renderer (`profile_embed`, `page_embed`).
159
+ */
160
+ type DeprecatedAlias = 'doctor_embed' | 'specialty_embed';
161
+ /** Block types a renderer is registered under — deprecated aliases resolve to these. */
162
+ type CanonicalBlockType = Exclude<BlockType, DeprecatedAlias>;
163
+ type EmbedType = 'page_embed' | 'profile_embed';
164
+ type WireTypesOf<T extends CanonicalBlockType> = T extends 'profile_embed' ? 'profile_embed' | 'doctor_embed' : T extends 'page_embed' ? 'page_embed' | 'specialty_embed' : T;
165
+ /**
166
+ * The block a renderer for `T` receives — including its deprecated alias, and
167
+ * with `resolved` guaranteed non-null for embeds (unresolved ones never render).
168
+ */
169
+ type RenderableBlock<T extends CanonicalBlockType> = T extends EmbedType ? Extract<PageBlock, {
170
+ blockType: WireTypesOf<T>;
171
+ }> & {
172
+ resolved: ResolvedEmbed;
173
+ } : Extract<PageBlock, {
174
+ blockType: WireTypesOf<T>;
175
+ }>;
176
+ interface BlockRendererProps<T extends CanonicalBlockType> {
177
+ block: RenderableBlock<T>;
178
+ /** Level for headings INSIDE the block — one below the section heading when there is one. */
179
+ headingLevel: HeadingLevel;
180
+ /** Builds an embed's href (the `embedHref` prop, or the default `/${slug}`). */
181
+ embedHref: (resolved: ResolvedEmbed) => string;
182
+ /**
183
+ * REB-1268 — set only for block types that draw the SECTION heading
184
+ * themselves (`image_text`, whose heading sits beside the image): the id the
185
+ * section's `aria-labelledby` points at. `headingLevel` is then the section
186
+ * heading's own level. Undefined for every other block type.
187
+ */
188
+ headingId?: string;
189
+ }
190
+ /** Per-block-type renderer overrides, keyed by canonical type. */
191
+ type PageBlocksComponents = Partial<{
192
+ [T in CanonicalBlockType]: ComponentType<BlockRendererProps<T>>;
193
+ }>;
194
+ interface PageBlocksProps {
195
+ blocks: readonly PageBlock[] | null | undefined;
196
+ /** Replace the body renderer of any block type. */
197
+ components?: PageBlocksComponents;
198
+ /** Level of each section heading. Default `2` (the page title is the `h1`). */
199
+ headingLevel?: HeadingLevel;
200
+ /** Href for an embedded page. Default `/${resolved.slug}`. */
201
+ embedHref?: (resolved: ResolvedEmbed) => string;
202
+ /**
203
+ * Namespace for the section heading ids (`aria-labelledby` targets). Pass a
204
+ * distinct value when one document renders `PageBlocks` more than once with
205
+ * overlapping blocks. Sanitised to a valid IDREF. Default `'page-block'`.
206
+ */
207
+ idPrefix?: string;
208
+ className?: string;
209
+ sectionClassName?: string;
210
+ }
211
+ declare function PageBlocks({ blocks, components, headingLevel, embedHref, idPrefix, className, sectionClassName, }: PageBlocksProps): react_jsx_runtime.JSX.Element | null;
212
+
213
+ /**
214
+ * REB-1100 — pure helpers over a page's `links` (the pages it picked in nexus,
215
+ * e.g. a team profile's areas). RSC-safe, no React.
216
+ *
217
+ * Structural types on purpose: any list item that carries `links` works, and
218
+ * an item from an API that predates `links` (absent) simply has none.
219
+ */
220
+ interface PageLinkRef {
221
+ slug: string;
222
+ title: string;
223
+ category?: {
224
+ name: string;
225
+ slug: string;
226
+ } | null;
227
+ }
228
+ interface LinkFilterOption {
229
+ /** Stable key: `{categorySlug}/{slug}` (a slug is unique per category). */
230
+ key: string;
231
+ label: string;
232
+ }
233
+ declare function linkKey(link: PageLinkRef): string;
234
+ /** Each distinct link across `items`, once, in first-seen order. */
235
+ declare function linkFilterOptions(items: Array<{
236
+ links?: PageLinkRef[] | null;
237
+ }>): LinkFilterOption[];
238
+ /** The items linked to `key`; every item when `key` is null. */
239
+ declare function filterByLink<T extends {
240
+ links?: PageLinkRef[] | null;
241
+ }>(items: T[], key: string | null): T[];
242
+
243
+ export { type BlockRendererProps as B, type CheckoutTermsCopy as C, DEFAULT_INVOICE_COUNTRIES as D, EMPTY_FULL_INVOICE_VALUE as E, type FullInvoiceFieldsLabels as F, type HeadingLevel as H, type LinkFilterOption as L, type PageLinkRef as P, type RenderableBlock as R, type FullInvoiceValue as a, type CanonicalBlockType as b, CheckoutTermsAcceptance as c, type CheckoutTermsAcceptanceProps as d, type FullInvoiceErrors as e, FullInvoiceFields as f, type FullInvoiceFieldsProps as g, type FullInvoiceIdType as h, PageBlocks as i, type PageBlocksComponents as j, type PageBlocksProps as k, filterByLink as l, isOverSimplifiedInvoiceCap as m, linkFilterOptions as n, linkKey as o, toCheckoutTermsFields as p, shopIssuesInvoices as s, toCheckoutInvoiceFields as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reblu/site-blocks",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "Data-driven presentational organisms for Reblu SITEs (Collection, MarkdownRenderer, Treatment, ParentPage, ProfileCard, ProductCard) — clean, token-driven, white-label.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Reblu — Adolfo Unturbe",
@@ -70,8 +70,8 @@
70
70
  "dependencies": {
71
71
  "class-variance-authority": "^0.7.1",
72
72
  "html-react-parser": "5.2.17",
73
- "@reblu/site-ui": "^0.7.2",
74
- "@reblu/site-contracts": "^1.30.0"
73
+ "@reblu/site-contracts": "^1.32.0",
74
+ "@reblu/site-ui": "^0.7.2"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "react": ">=19",
@@ -101,7 +101,7 @@
101
101
  "unist-util-visit": "^5.1.0",
102
102
  "vitest": "^4.0.15",
103
103
  "@reblu/config": "0.0.1",
104
- "@reblu/site-client": "^5.20.0"
104
+ "@reblu/site-client": "^5.21.0"
105
105
  },
106
106
  "scripts": {
107
107
  "build": "tsup",