@reblu/site-next 0.1.0 → 1.0.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.
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/cta-buttons.tsx
32
+ var cta_buttons_exports = {};
33
+ __export(cta_buttons_exports, {
34
+ CtaButtons: () => CtaButtons
35
+ });
36
+ module.exports = __toCommonJS(cta_buttons_exports);
37
+ var import_site_ui = require("@reblu/site-ui");
38
+ var import_link = __toESM(require("next/link"), 1);
39
+ var import_jsx_runtime = require("react/jsx-runtime");
40
+ function isExternal(href) {
41
+ return /^https?:\/\//i.test(href);
42
+ }
43
+ function CtaButtons({ ctas, className, buttonClassName, size = "lg" }) {
44
+ if (ctas.length === 0) return null;
45
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, "data-slot": "cta-buttons", children: ctas.map((cta) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
46
+ import_site_ui.Button,
47
+ {
48
+ asChild: true,
49
+ size,
50
+ className: buttonClassName,
51
+ "data-cta-id": cta.analyticsId ?? void 0,
52
+ children: isExternal(cta.href) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: cta.href, target: "_blank", rel: "noopener noreferrer", children: cta.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: cta.href, children: cta.label })
53
+ },
54
+ cta.id
55
+ )) });
56
+ }
57
+ // Annotate the CommonJS export names for ESM import in node:
58
+ 0 && (module.exports = {
59
+ CtaButtons
60
+ });
61
+ //# sourceMappingURL=cta-buttons.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cta-buttons.tsx"],"sourcesContent":["'use client'\n\nimport type { Cta } from '@reblu/site-contracts'\nimport { Button } from '@reblu/site-ui'\nimport Link from 'next/link'\n\ntype ButtonSize = 'sm' | 'default' | 'lg'\n\nexport interface CtaButtonsProps {\n ctas: Cta[]\n /** Wrapper class (layout of the button group). */\n className?: string\n /** Class applied to each button. */\n buttonClassName?: string\n size?: ButtonSize\n}\n\n/** External = absolute http(s) URL (e.g. a Klinikare booking link). */\nfunction isExternal(href: string): boolean {\n return /^https?:\\/\\//i.test(href)\n}\n\n/**\n * Presentational CTA renderer (REB-394, moved to `@reblu/site-next` in REB-418).\n * Pure — takes already-resolved CTAs and renders one styled button-link each.\n * Internal hrefs use `next/link` (client routing); external URLs open in a new,\n * safely-rel tab. `analyticsId` is surfaced as `data-cta-id` on the clickable\n * element for GA `cta_click` tracking. Renders nothing when there are no CTAs.\n *\n * Imports `Button` from `@reblu/site-ui` (public) — never `@reblu/ui` (private).\n */\nexport function CtaButtons({ ctas, className, buttonClassName, size = 'lg' }: CtaButtonsProps) {\n if (ctas.length === 0) return null\n\n return (\n <div className={className} data-slot='cta-buttons'>\n {ctas.map((cta) => (\n <Button\n key={cta.id}\n asChild\n size={size}\n className={buttonClassName}\n data-cta-id={cta.analyticsId ?? undefined}\n >\n {isExternal(cta.href) ? (\n <a href={cta.href} target='_blank' rel='noopener noreferrer'>\n {cta.label}\n </a>\n ) : (\n <Link href={cta.href}>{cta.label}</Link>\n )}\n </Button>\n ))}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAAuB;AACvB,kBAAiB;AAyCL;AA3BZ,SAAS,WAAW,MAAuB;AACzC,SAAO,gBAAgB,KAAK,IAAI;AAClC;AAWO,SAAS,WAAW,EAAE,MAAM,WAAW,iBAAiB,OAAO,KAAK,GAAoB;AAC7F,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,SACE,4CAAC,SAAI,WAAsB,aAAU,eAClC,eAAK,IAAI,CAAC,QACT;AAAA,IAAC;AAAA;AAAA,MAEC,SAAO;AAAA,MACP;AAAA,MACA,WAAW;AAAA,MACX,eAAa,IAAI,eAAe;AAAA,MAE/B,qBAAW,IAAI,IAAI,IAClB,4CAAC,OAAE,MAAM,IAAI,MAAM,QAAO,UAAS,KAAI,uBACpC,cAAI,OACP,IAEA,4CAAC,YAAAA,SAAA,EAAK,MAAM,IAAI,MAAO,cAAI,OAAM;AAAA;AAAA,IAX9B,IAAI;AAAA,EAaX,CACD,GACH;AAEJ;","names":["Link"]}
@@ -0,0 +1,24 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Cta } from '@reblu/site-contracts';
3
+
4
+ type ButtonSize = 'sm' | 'default' | 'lg';
5
+ interface CtaButtonsProps {
6
+ ctas: Cta[];
7
+ /** Wrapper class (layout of the button group). */
8
+ className?: string;
9
+ /** Class applied to each button. */
10
+ buttonClassName?: string;
11
+ size?: ButtonSize;
12
+ }
13
+ /**
14
+ * Presentational CTA renderer (REB-394, moved to `@reblu/site-next` in REB-418).
15
+ * Pure — takes already-resolved CTAs and renders one styled button-link each.
16
+ * Internal hrefs use `next/link` (client routing); external URLs open in a new,
17
+ * safely-rel tab. `analyticsId` is surfaced as `data-cta-id` on the clickable
18
+ * element for GA `cta_click` tracking. Renders nothing when there are no CTAs.
19
+ *
20
+ * Imports `Button` from `@reblu/site-ui` (public) — never `@reblu/ui` (private).
21
+ */
22
+ declare function CtaButtons({ ctas, className, buttonClassName, size }: CtaButtonsProps): react_jsx_runtime.JSX.Element | null;
23
+
24
+ export { CtaButtons, type CtaButtonsProps };
@@ -0,0 +1,24 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { Cta } from '@reblu/site-contracts';
3
+
4
+ type ButtonSize = 'sm' | 'default' | 'lg';
5
+ interface CtaButtonsProps {
6
+ ctas: Cta[];
7
+ /** Wrapper class (layout of the button group). */
8
+ className?: string;
9
+ /** Class applied to each button. */
10
+ buttonClassName?: string;
11
+ size?: ButtonSize;
12
+ }
13
+ /**
14
+ * Presentational CTA renderer (REB-394, moved to `@reblu/site-next` in REB-418).
15
+ * Pure — takes already-resolved CTAs and renders one styled button-link each.
16
+ * Internal hrefs use `next/link` (client routing); external URLs open in a new,
17
+ * safely-rel tab. `analyticsId` is surfaced as `data-cta-id` on the clickable
18
+ * element for GA `cta_click` tracking. Renders nothing when there are no CTAs.
19
+ *
20
+ * Imports `Button` from `@reblu/site-ui` (public) — never `@reblu/ui` (private).
21
+ */
22
+ declare function CtaButtons({ ctas, className, buttonClassName, size }: CtaButtonsProps): react_jsx_runtime.JSX.Element | null;
23
+
24
+ export { CtaButtons, type CtaButtonsProps };
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ // src/cta-buttons.tsx
4
+ import { Button } from "@reblu/site-ui";
5
+ import Link from "next/link";
6
+ import { jsx } from "react/jsx-runtime";
7
+ function isExternal(href) {
8
+ return /^https?:\/\//i.test(href);
9
+ }
10
+ function CtaButtons({ ctas, className, buttonClassName, size = "lg" }) {
11
+ if (ctas.length === 0) return null;
12
+ return /* @__PURE__ */ jsx("div", { className, "data-slot": "cta-buttons", children: ctas.map((cta) => /* @__PURE__ */ jsx(
13
+ Button,
14
+ {
15
+ asChild: true,
16
+ size,
17
+ className: buttonClassName,
18
+ "data-cta-id": cta.analyticsId ?? void 0,
19
+ children: isExternal(cta.href) ? /* @__PURE__ */ jsx("a", { href: cta.href, target: "_blank", rel: "noopener noreferrer", children: cta.label }) : /* @__PURE__ */ jsx(Link, { href: cta.href, children: cta.label })
20
+ },
21
+ cta.id
22
+ )) });
23
+ }
24
+ export {
25
+ CtaButtons
26
+ };
27
+ //# sourceMappingURL=cta-buttons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cta-buttons.tsx"],"sourcesContent":["'use client'\n\nimport type { Cta } from '@reblu/site-contracts'\nimport { Button } from '@reblu/site-ui'\nimport Link from 'next/link'\n\ntype ButtonSize = 'sm' | 'default' | 'lg'\n\nexport interface CtaButtonsProps {\n ctas: Cta[]\n /** Wrapper class (layout of the button group). */\n className?: string\n /** Class applied to each button. */\n buttonClassName?: string\n size?: ButtonSize\n}\n\n/** External = absolute http(s) URL (e.g. a Klinikare booking link). */\nfunction isExternal(href: string): boolean {\n return /^https?:\\/\\//i.test(href)\n}\n\n/**\n * Presentational CTA renderer (REB-394, moved to `@reblu/site-next` in REB-418).\n * Pure — takes already-resolved CTAs and renders one styled button-link each.\n * Internal hrefs use `next/link` (client routing); external URLs open in a new,\n * safely-rel tab. `analyticsId` is surfaced as `data-cta-id` on the clickable\n * element for GA `cta_click` tracking. Renders nothing when there are no CTAs.\n *\n * Imports `Button` from `@reblu/site-ui` (public) — never `@reblu/ui` (private).\n */\nexport function CtaButtons({ ctas, className, buttonClassName, size = 'lg' }: CtaButtonsProps) {\n if (ctas.length === 0) return null\n\n return (\n <div className={className} data-slot='cta-buttons'>\n {ctas.map((cta) => (\n <Button\n key={cta.id}\n asChild\n size={size}\n className={buttonClassName}\n data-cta-id={cta.analyticsId ?? undefined}\n >\n {isExternal(cta.href) ? (\n <a href={cta.href} target='_blank' rel='noopener noreferrer'>\n {cta.label}\n </a>\n ) : (\n <Link href={cta.href}>{cta.label}</Link>\n )}\n </Button>\n ))}\n </div>\n )\n}\n"],"mappings":";;;AAGA,SAAS,cAAc;AACvB,OAAO,UAAU;AAyCL;AA3BZ,SAAS,WAAW,MAAuB;AACzC,SAAO,gBAAgB,KAAK,IAAI;AAClC;AAWO,SAAS,WAAW,EAAE,MAAM,WAAW,iBAAiB,OAAO,KAAK,GAAoB;AAC7F,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,SACE,oBAAC,SAAI,WAAsB,aAAU,eAClC,eAAK,IAAI,CAAC,QACT;AAAA,IAAC;AAAA;AAAA,MAEC,SAAO;AAAA,MACP;AAAA,MACA,WAAW;AAAA,MACX,eAAa,IAAI,eAAe;AAAA,MAE/B,qBAAW,IAAI,IAAI,IAClB,oBAAC,OAAE,MAAM,IAAI,MAAM,QAAO,UAAS,KAAI,uBACpC,cAAI,OACP,IAEA,oBAAC,QAAK,MAAM,IAAI,MAAO,cAAI,OAAM;AAAA;AAAA,IAX9B,IAAI;AAAA,EAaX,CACD,GACH;AAEJ;","names":[]}
package/dist/index.cjs ADDED
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
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
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ CtaButtons: () => CtaButtons,
34
+ EditableCta: () => EditableCta,
35
+ NextCacheAdapter: () => NextCacheAdapter,
36
+ StickyMobileCta: () => StickyMobileCta
37
+ });
38
+ module.exports = __toCommonJS(src_exports);
39
+
40
+ // src/next-cache-adapter.ts
41
+ var NextCacheAdapter = class {
42
+ /**
43
+ * @param defaultRevalidate ISR window (seconds) applied when a request omits
44
+ * its own `revalidate`. Defaults to `60` for parity with demo-site's current
45
+ * ISR default (`client.ts:45`). Pass `false`/`0` to default to no-store.
46
+ */
47
+ constructor(defaultRevalidate = 60) {
48
+ this.defaultRevalidate = defaultRevalidate;
49
+ }
50
+ defaultRevalidate;
51
+ resolveRequestInit(policy) {
52
+ const revalidate = policy.revalidate ?? this.defaultRevalidate;
53
+ if (revalidate === false || revalidate === 0) {
54
+ return { cache: "no-store" };
55
+ }
56
+ return { next: { revalidate } };
57
+ }
58
+ };
59
+
60
+ // src/cta-buttons.tsx
61
+ var import_site_ui = require("@reblu/site-ui");
62
+ var import_link = __toESM(require("next/link"), 1);
63
+ var import_jsx_runtime = require("react/jsx-runtime");
64
+ function isExternal(href) {
65
+ return /^https?:\/\//i.test(href);
66
+ }
67
+ function CtaButtons({ ctas, className, buttonClassName, size = "lg" }) {
68
+ if (ctas.length === 0) return null;
69
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, "data-slot": "cta-buttons", children: ctas.map((cta) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
70
+ import_site_ui.Button,
71
+ {
72
+ asChild: true,
73
+ size,
74
+ className: buttonClassName,
75
+ "data-cta-id": cta.analyticsId ?? void 0,
76
+ children: isExternal(cta.href) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: cta.href, target: "_blank", rel: "noopener noreferrer", children: cta.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: cta.href, children: cta.label })
77
+ },
78
+ cta.id
79
+ )) });
80
+ }
81
+
82
+ // src/sticky-mobile-cta.tsx
83
+ var import_jsx_runtime2 = require("react/jsx-runtime");
84
+ function StickyMobileCta({ ctas }) {
85
+ if (ctas.length === 0) return null;
86
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "fixed inset-x-0 bottom-0 z-50 md:hidden border-t border-border bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
87
+ CtaButtons,
88
+ {
89
+ ctas,
90
+ className: "flex justify-center gap-3",
91
+ buttonClassName: "w-full rounded-lg font-semibold",
92
+ size: "lg"
93
+ }
94
+ ) });
95
+ }
96
+
97
+ // src/editable-cta.tsx
98
+ var import_jsx_runtime3 = require("react/jsx-runtime");
99
+ async function EditableCta({
100
+ slot,
101
+ client,
102
+ className,
103
+ buttonClassName,
104
+ size,
105
+ fallback = null
106
+ }) {
107
+ const { data: ctas } = await client.ctas.list(slot).catch(() => ({ data: [] }));
108
+ if (ctas.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
109
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CtaButtons, { ctas, className, buttonClassName, size });
110
+ }
111
+ // Annotate the CommonJS export names for ESM import in node:
112
+ 0 && (module.exports = {
113
+ CtaButtons,
114
+ EditableCta,
115
+ NextCacheAdapter,
116
+ StickyMobileCta
117
+ });
118
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/next-cache-adapter.ts","../src/cta-buttons.tsx","../src/sticky-mobile-cta.tsx","../src/editable-cta.tsx"],"sourcesContent":["/**\n * `@reblu/site-next` — Next.js RSC glue for Reblu SITEs (REB-418 / design doc §5).\n *\n * Two things live here, both framework-coupled to Next so the lower SDK layers\n * (`@reblu/site-client`, `@reblu/site-contracts`, `@reblu/site-ui`) stay\n * framework-agnostic:\n *\n * 1. {@link NextCacheAdapter} — implements `@reblu/site-client`'s\n * `RebluCacheAdapter`, translating cache policy into Next ISR `fetch` options.\n * 2. The editable-CTA glue (`EditableCta` server component + its `CtaButtons`\n * client boundary + `StickyMobileCta`), consuming the injected SDK client.\n *\n * `EditableCta` re-exports the `'use client'` `CtaButtons` from its own emitted\n * chunk, so the RSC boundary survives the build (see the R4 smoke test).\n */\nexport { NextCacheAdapter } from './next-cache-adapter'\nexport { CtaButtons, type CtaButtonsProps } from './cta-buttons'\nexport { StickyMobileCta, type StickyMobileCtaProps } from './sticky-mobile-cta'\nexport { EditableCta, type EditableCtaProps, type CtaClient, type CtaSlot } from './editable-cta'\n","import type { RebluCacheAdapter, RebluCachePolicy } from '@reblu/site-client'\n\n/**\n * Next.js ISR cache adapter (REB-418 / design doc §5.2).\n *\n * Implements the framework-agnostic {@link RebluCacheAdapter} interface exported\n * by `@reblu/site-client`, translating a {@link RebluCachePolicy} into the\n * `fetch` options Next understands. This is the piece that restores the exact\n * ISR behavior demo-site hard-codes today — now injected instead of baked in:\n *\n * ```ts\n * createRebluClient({ baseUrl, apiKey, cache: new NextCacheAdapter(60) })\n * ```\n *\n * - `revalidate` > 0 → `{ next: { revalidate } }` (time-based ISR)\n * - `revalidate` `false` / `0` → `{ cache: 'no-store' }` (dynamic / preview)\n * - omitted `revalidate` → the constructor `defaultRevalidate`\n */\nexport class NextCacheAdapter implements RebluCacheAdapter {\n /**\n * @param defaultRevalidate ISR window (seconds) applied when a request omits\n * its own `revalidate`. Defaults to `60` for parity with demo-site's current\n * ISR default (`client.ts:45`). Pass `false`/`0` to default to no-store.\n */\n constructor(private readonly defaultRevalidate: number | false = 60) {}\n\n resolveRequestInit(policy: RebluCachePolicy): RequestInit {\n const revalidate = policy.revalidate ?? this.defaultRevalidate\n if (revalidate === false || revalidate === 0) {\n return { cache: 'no-store' }\n }\n return { next: { revalidate } } as RequestInit\n }\n}\n","'use client'\n\nimport type { Cta } from '@reblu/site-contracts'\nimport { Button } from '@reblu/site-ui'\nimport Link from 'next/link'\n\ntype ButtonSize = 'sm' | 'default' | 'lg'\n\nexport interface CtaButtonsProps {\n ctas: Cta[]\n /** Wrapper class (layout of the button group). */\n className?: string\n /** Class applied to each button. */\n buttonClassName?: string\n size?: ButtonSize\n}\n\n/** External = absolute http(s) URL (e.g. a Klinikare booking link). */\nfunction isExternal(href: string): boolean {\n return /^https?:\\/\\//i.test(href)\n}\n\n/**\n * Presentational CTA renderer (REB-394, moved to `@reblu/site-next` in REB-418).\n * Pure — takes already-resolved CTAs and renders one styled button-link each.\n * Internal hrefs use `next/link` (client routing); external URLs open in a new,\n * safely-rel tab. `analyticsId` is surfaced as `data-cta-id` on the clickable\n * element for GA `cta_click` tracking. Renders nothing when there are no CTAs.\n *\n * Imports `Button` from `@reblu/site-ui` (public) — never `@reblu/ui` (private).\n */\nexport function CtaButtons({ ctas, className, buttonClassName, size = 'lg' }: CtaButtonsProps) {\n if (ctas.length === 0) return null\n\n return (\n <div className={className} data-slot='cta-buttons'>\n {ctas.map((cta) => (\n <Button\n key={cta.id}\n asChild\n size={size}\n className={buttonClassName}\n data-cta-id={cta.analyticsId ?? undefined}\n >\n {isExternal(cta.href) ? (\n <a href={cta.href} target='_blank' rel='noopener noreferrer'>\n {cta.label}\n </a>\n ) : (\n <Link href={cta.href}>{cta.label}</Link>\n )}\n </Button>\n ))}\n </div>\n )\n}\n","import type { Cta } from '@reblu/site-contracts'\n\nimport { CtaButtons } from './cta-buttons'\n\nexport interface StickyMobileCtaProps {\n ctas: Cta[]\n}\n\n/**\n * StickyMobileCta (REB-394, moved to `@reblu/site-next` in REB-418) — a\n * mobile-only sticky bottom bar surfacing the `sticky-mobile` slot CTAs. Hidden\n * on `md+` (desktop has its own CTAs). Renders nothing when no CTA is configured,\n * so it never adds empty chrome.\n */\nexport function StickyMobileCta({ ctas }: StickyMobileCtaProps) {\n if (ctas.length === 0) return null\n\n return (\n <div className='fixed inset-x-0 bottom-0 z-50 md:hidden border-t border-border bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80'>\n <CtaButtons\n ctas={ctas}\n className='flex justify-center gap-3'\n buttonClassName='w-full rounded-lg font-semibold'\n size='lg'\n />\n </div>\n )\n}\n","import type { RebluClient } from '@reblu/site-client'\nimport type { Cta } from '@reblu/site-contracts'\nimport type { ReactNode } from 'react'\n\nimport { CtaButtons } from './cta-buttons'\n\ntype ButtonSize = 'sm' | 'default' | 'lg'\n\n/** The `Cta.slot` union — the valid placement keys, sourced from the contract. */\nexport type CtaSlot = Cta['slot']\n\n/**\n * The slice of the SDK client `EditableCta` needs: just the `ctas` namespace.\n * Typing it structurally lets a SITE pass either the full `RebluClient` or a\n * narrower singleton, without coupling this component to the whole surface.\n */\nexport type CtaClient = Pick<RebluClient, 'ctas'>\n\nexport interface EditableCtaProps {\n /** CtaSlot value: hero | blog-end | services | contact | sticky-mobile | footer. */\n slot: CtaSlot\n /** The Reblu SDK client instance the SITE owns (env-configured upstream). */\n client: CtaClient\n className?: string\n buttonClassName?: string\n size?: ButtonSize\n /**\n * Rendered when the slot has no configured CTA (or the upstream fetch fails).\n * Lets each placement keep its existing static CTA as a graceful fallback.\n */\n fallback?: ReactNode\n}\n\n/**\n * EditableCta (REB-394, moved to `@reblu/site-next` in REB-418) — drop-anywhere\n * async Server Component that renders the CTAs configured for a slot from nexus,\n * with no redeploy. Fetches active CTAs via the injected SDK client and degrades\n * gracefully: on empty/failure it renders `fallback` (a static CTA) instead of\n * breaking the page.\n *\n * The client is injected (prop or per-SITE singleton) so this package never\n * reads `process.env` and stays framework-glue only — the SITE owns the env and\n * wires the ISR-aware {@link NextCacheAdapter}.\n */\nexport async function EditableCta({\n slot,\n client,\n className,\n buttonClassName,\n size,\n fallback = null,\n}: EditableCtaProps) {\n const { data: ctas } = await client.ctas\n .list(slot)\n .catch(() => ({ data: [] as Cta[] }))\n\n if (ctas.length === 0) return <>{fallback}</>\n\n return (\n <CtaButtons ctas={ctas} className={className} buttonClassName={buttonClassName} size={size} />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACkBO,IAAM,mBAAN,MAAoD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,YAA6B,oBAAoC,IAAI;AAAxC;AAAA,EAAyC;AAAA,EAAzC;AAAA,EAE7B,mBAAmB,QAAuC;AACxD,UAAM,aAAa,OAAO,cAAc,KAAK;AAC7C,QAAI,eAAe,SAAS,eAAe,GAAG;AAC5C,aAAO,EAAE,OAAO,WAAW;AAAA,IAC7B;AACA,WAAO,EAAE,MAAM,EAAE,WAAW,EAAE;AAAA,EAChC;AACF;;;AC9BA,qBAAuB;AACvB,kBAAiB;AAyCL;AA3BZ,SAAS,WAAW,MAAuB;AACzC,SAAO,gBAAgB,KAAK,IAAI;AAClC;AAWO,SAAS,WAAW,EAAE,MAAM,WAAW,iBAAiB,OAAO,KAAK,GAAoB;AAC7F,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,SACE,4CAAC,SAAI,WAAsB,aAAU,eAClC,eAAK,IAAI,CAAC,QACT;AAAA,IAAC;AAAA;AAAA,MAEC,SAAO;AAAA,MACP;AAAA,MACA,WAAW;AAAA,MACX,eAAa,IAAI,eAAe;AAAA,MAE/B,qBAAW,IAAI,IAAI,IAClB,4CAAC,OAAE,MAAM,IAAI,MAAM,QAAO,UAAS,KAAI,uBACpC,cAAI,OACP,IAEA,4CAAC,YAAAA,SAAA,EAAK,MAAM,IAAI,MAAO,cAAI,OAAM;AAAA;AAAA,IAX9B,IAAI;AAAA,EAaX,CACD,GACH;AAEJ;;;ACpCM,IAAAC,sBAAA;AALC,SAAS,gBAAgB,EAAE,KAAK,GAAyB;AAC9D,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,SACE,6CAAC,SAAI,WAAU,uJACb;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV,iBAAgB;AAAA,MAChB,MAAK;AAAA;AAAA,EACP,GACF;AAEJ;;;AC6BgC,IAAAC,sBAAA;AAZhC,eAAsB,YAAY;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAqB;AACnB,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,OAAO,KACjC,KAAK,IAAI,EACT,MAAM,OAAO,EAAE,MAAM,CAAC,EAAW,EAAE;AAEtC,MAAI,KAAK,WAAW,EAAG,QAAO,6EAAG,oBAAS;AAE1C,SACE,6CAAC,cAAW,MAAY,WAAsB,iBAAkC,MAAY;AAEhG;","names":["Link","import_jsx_runtime","import_jsx_runtime"]}
@@ -0,0 +1,81 @@
1
+ import { RebluCacheAdapter, RebluCachePolicy, RebluClient } from '@reblu/site-client';
2
+ export { CtaButtons, CtaButtonsProps } from './cta-buttons.cjs';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { Cta } from '@reblu/site-contracts';
5
+ import { ReactNode } from 'react';
6
+
7
+ /**
8
+ * Next.js ISR cache adapter (REB-418 / design doc §5.2).
9
+ *
10
+ * Implements the framework-agnostic {@link RebluCacheAdapter} interface exported
11
+ * by `@reblu/site-client`, translating a {@link RebluCachePolicy} into the
12
+ * `fetch` options Next understands. This is the piece that restores the exact
13
+ * ISR behavior demo-site hard-codes today — now injected instead of baked in:
14
+ *
15
+ * ```ts
16
+ * createRebluClient({ baseUrl, apiKey, cache: new NextCacheAdapter(60) })
17
+ * ```
18
+ *
19
+ * - `revalidate` > 0 → `{ next: { revalidate } }` (time-based ISR)
20
+ * - `revalidate` `false` / `0` → `{ cache: 'no-store' }` (dynamic / preview)
21
+ * - omitted `revalidate` → the constructor `defaultRevalidate`
22
+ */
23
+ declare class NextCacheAdapter implements RebluCacheAdapter {
24
+ private readonly defaultRevalidate;
25
+ /**
26
+ * @param defaultRevalidate ISR window (seconds) applied when a request omits
27
+ * its own `revalidate`. Defaults to `60` for parity with demo-site's current
28
+ * ISR default (`client.ts:45`). Pass `false`/`0` to default to no-store.
29
+ */
30
+ constructor(defaultRevalidate?: number | false);
31
+ resolveRequestInit(policy: RebluCachePolicy): RequestInit;
32
+ }
33
+
34
+ interface StickyMobileCtaProps {
35
+ ctas: Cta[];
36
+ }
37
+ /**
38
+ * StickyMobileCta (REB-394, moved to `@reblu/site-next` in REB-418) — a
39
+ * mobile-only sticky bottom bar surfacing the `sticky-mobile` slot CTAs. Hidden
40
+ * on `md+` (desktop has its own CTAs). Renders nothing when no CTA is configured,
41
+ * so it never adds empty chrome.
42
+ */
43
+ declare function StickyMobileCta({ ctas }: StickyMobileCtaProps): react_jsx_runtime.JSX.Element | null;
44
+
45
+ type ButtonSize = 'sm' | 'default' | 'lg';
46
+ /** The `Cta.slot` union — the valid placement keys, sourced from the contract. */
47
+ type CtaSlot = Cta['slot'];
48
+ /**
49
+ * The slice of the SDK client `EditableCta` needs: just the `ctas` namespace.
50
+ * Typing it structurally lets a SITE pass either the full `RebluClient` or a
51
+ * narrower singleton, without coupling this component to the whole surface.
52
+ */
53
+ type CtaClient = Pick<RebluClient, 'ctas'>;
54
+ interface EditableCtaProps {
55
+ /** CtaSlot value: hero | blog-end | services | contact | sticky-mobile | footer. */
56
+ slot: CtaSlot;
57
+ /** The Reblu SDK client instance the SITE owns (env-configured upstream). */
58
+ client: CtaClient;
59
+ className?: string;
60
+ buttonClassName?: string;
61
+ size?: ButtonSize;
62
+ /**
63
+ * Rendered when the slot has no configured CTA (or the upstream fetch fails).
64
+ * Lets each placement keep its existing static CTA as a graceful fallback.
65
+ */
66
+ fallback?: ReactNode;
67
+ }
68
+ /**
69
+ * EditableCta (REB-394, moved to `@reblu/site-next` in REB-418) — drop-anywhere
70
+ * async Server Component that renders the CTAs configured for a slot from nexus,
71
+ * with no redeploy. Fetches active CTAs via the injected SDK client and degrades
72
+ * gracefully: on empty/failure it renders `fallback` (a static CTA) instead of
73
+ * breaking the page.
74
+ *
75
+ * The client is injected (prop or per-SITE singleton) so this package never
76
+ * reads `process.env` and stays framework-glue only — the SITE owns the env and
77
+ * wires the ISR-aware {@link NextCacheAdapter}.
78
+ */
79
+ declare function EditableCta({ slot, client, className, buttonClassName, size, fallback, }: EditableCtaProps): Promise<react_jsx_runtime.JSX.Element>;
80
+
81
+ export { type CtaClient, type CtaSlot, EditableCta, type EditableCtaProps, NextCacheAdapter, StickyMobileCta, type StickyMobileCtaProps };
@@ -0,0 +1,81 @@
1
+ import { RebluCacheAdapter, RebluCachePolicy, RebluClient } from '@reblu/site-client';
2
+ export { CtaButtons, CtaButtonsProps } from './cta-buttons.js';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { Cta } from '@reblu/site-contracts';
5
+ import { ReactNode } from 'react';
6
+
7
+ /**
8
+ * Next.js ISR cache adapter (REB-418 / design doc §5.2).
9
+ *
10
+ * Implements the framework-agnostic {@link RebluCacheAdapter} interface exported
11
+ * by `@reblu/site-client`, translating a {@link RebluCachePolicy} into the
12
+ * `fetch` options Next understands. This is the piece that restores the exact
13
+ * ISR behavior demo-site hard-codes today — now injected instead of baked in:
14
+ *
15
+ * ```ts
16
+ * createRebluClient({ baseUrl, apiKey, cache: new NextCacheAdapter(60) })
17
+ * ```
18
+ *
19
+ * - `revalidate` > 0 → `{ next: { revalidate } }` (time-based ISR)
20
+ * - `revalidate` `false` / `0` → `{ cache: 'no-store' }` (dynamic / preview)
21
+ * - omitted `revalidate` → the constructor `defaultRevalidate`
22
+ */
23
+ declare class NextCacheAdapter implements RebluCacheAdapter {
24
+ private readonly defaultRevalidate;
25
+ /**
26
+ * @param defaultRevalidate ISR window (seconds) applied when a request omits
27
+ * its own `revalidate`. Defaults to `60` for parity with demo-site's current
28
+ * ISR default (`client.ts:45`). Pass `false`/`0` to default to no-store.
29
+ */
30
+ constructor(defaultRevalidate?: number | false);
31
+ resolveRequestInit(policy: RebluCachePolicy): RequestInit;
32
+ }
33
+
34
+ interface StickyMobileCtaProps {
35
+ ctas: Cta[];
36
+ }
37
+ /**
38
+ * StickyMobileCta (REB-394, moved to `@reblu/site-next` in REB-418) — a
39
+ * mobile-only sticky bottom bar surfacing the `sticky-mobile` slot CTAs. Hidden
40
+ * on `md+` (desktop has its own CTAs). Renders nothing when no CTA is configured,
41
+ * so it never adds empty chrome.
42
+ */
43
+ declare function StickyMobileCta({ ctas }: StickyMobileCtaProps): react_jsx_runtime.JSX.Element | null;
44
+
45
+ type ButtonSize = 'sm' | 'default' | 'lg';
46
+ /** The `Cta.slot` union — the valid placement keys, sourced from the contract. */
47
+ type CtaSlot = Cta['slot'];
48
+ /**
49
+ * The slice of the SDK client `EditableCta` needs: just the `ctas` namespace.
50
+ * Typing it structurally lets a SITE pass either the full `RebluClient` or a
51
+ * narrower singleton, without coupling this component to the whole surface.
52
+ */
53
+ type CtaClient = Pick<RebluClient, 'ctas'>;
54
+ interface EditableCtaProps {
55
+ /** CtaSlot value: hero | blog-end | services | contact | sticky-mobile | footer. */
56
+ slot: CtaSlot;
57
+ /** The Reblu SDK client instance the SITE owns (env-configured upstream). */
58
+ client: CtaClient;
59
+ className?: string;
60
+ buttonClassName?: string;
61
+ size?: ButtonSize;
62
+ /**
63
+ * Rendered when the slot has no configured CTA (or the upstream fetch fails).
64
+ * Lets each placement keep its existing static CTA as a graceful fallback.
65
+ */
66
+ fallback?: ReactNode;
67
+ }
68
+ /**
69
+ * EditableCta (REB-394, moved to `@reblu/site-next` in REB-418) — drop-anywhere
70
+ * async Server Component that renders the CTAs configured for a slot from nexus,
71
+ * with no redeploy. Fetches active CTAs via the injected SDK client and degrades
72
+ * gracefully: on empty/failure it renders `fallback` (a static CTA) instead of
73
+ * breaking the page.
74
+ *
75
+ * The client is injected (prop or per-SITE singleton) so this package never
76
+ * reads `process.env` and stays framework-glue only — the SITE owns the env and
77
+ * wires the ISR-aware {@link NextCacheAdapter}.
78
+ */
79
+ declare function EditableCta({ slot, client, className, buttonClassName, size, fallback, }: EditableCtaProps): Promise<react_jsx_runtime.JSX.Element>;
80
+
81
+ export { type CtaClient, type CtaSlot, EditableCta, type EditableCtaProps, NextCacheAdapter, StickyMobileCta, type StickyMobileCtaProps };
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ // src/next-cache-adapter.ts
2
+ var NextCacheAdapter = class {
3
+ /**
4
+ * @param defaultRevalidate ISR window (seconds) applied when a request omits
5
+ * its own `revalidate`. Defaults to `60` for parity with demo-site's current
6
+ * ISR default (`client.ts:45`). Pass `false`/`0` to default to no-store.
7
+ */
8
+ constructor(defaultRevalidate = 60) {
9
+ this.defaultRevalidate = defaultRevalidate;
10
+ }
11
+ defaultRevalidate;
12
+ resolveRequestInit(policy) {
13
+ const revalidate = policy.revalidate ?? this.defaultRevalidate;
14
+ if (revalidate === false || revalidate === 0) {
15
+ return { cache: "no-store" };
16
+ }
17
+ return { next: { revalidate } };
18
+ }
19
+ };
20
+
21
+ // src/cta-buttons.tsx
22
+ import { Button } from "@reblu/site-ui";
23
+ import Link from "next/link";
24
+ import { jsx } from "react/jsx-runtime";
25
+ function isExternal(href) {
26
+ return /^https?:\/\//i.test(href);
27
+ }
28
+ function CtaButtons({ ctas, className, buttonClassName, size = "lg" }) {
29
+ if (ctas.length === 0) return null;
30
+ return /* @__PURE__ */ jsx("div", { className, "data-slot": "cta-buttons", children: ctas.map((cta) => /* @__PURE__ */ jsx(
31
+ Button,
32
+ {
33
+ asChild: true,
34
+ size,
35
+ className: buttonClassName,
36
+ "data-cta-id": cta.analyticsId ?? void 0,
37
+ children: isExternal(cta.href) ? /* @__PURE__ */ jsx("a", { href: cta.href, target: "_blank", rel: "noopener noreferrer", children: cta.label }) : /* @__PURE__ */ jsx(Link, { href: cta.href, children: cta.label })
38
+ },
39
+ cta.id
40
+ )) });
41
+ }
42
+
43
+ // src/sticky-mobile-cta.tsx
44
+ import { jsx as jsx2 } from "react/jsx-runtime";
45
+ function StickyMobileCta({ ctas }) {
46
+ if (ctas.length === 0) return null;
47
+ return /* @__PURE__ */ jsx2("div", { className: "fixed inset-x-0 bottom-0 z-50 md:hidden border-t border-border bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80", children: /* @__PURE__ */ jsx2(
48
+ CtaButtons,
49
+ {
50
+ ctas,
51
+ className: "flex justify-center gap-3",
52
+ buttonClassName: "w-full rounded-lg font-semibold",
53
+ size: "lg"
54
+ }
55
+ ) });
56
+ }
57
+
58
+ // src/editable-cta.tsx
59
+ import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
60
+ async function EditableCta({
61
+ slot,
62
+ client,
63
+ className,
64
+ buttonClassName,
65
+ size,
66
+ fallback = null
67
+ }) {
68
+ const { data: ctas } = await client.ctas.list(slot).catch(() => ({ data: [] }));
69
+ if (ctas.length === 0) return /* @__PURE__ */ jsx3(Fragment, { children: fallback });
70
+ return /* @__PURE__ */ jsx3(CtaButtons, { ctas, className, buttonClassName, size });
71
+ }
72
+ export {
73
+ CtaButtons,
74
+ EditableCta,
75
+ NextCacheAdapter,
76
+ StickyMobileCta
77
+ };
78
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/next-cache-adapter.ts","../src/cta-buttons.tsx","../src/sticky-mobile-cta.tsx","../src/editable-cta.tsx"],"sourcesContent":["import type { RebluCacheAdapter, RebluCachePolicy } from '@reblu/site-client'\n\n/**\n * Next.js ISR cache adapter (REB-418 / design doc §5.2).\n *\n * Implements the framework-agnostic {@link RebluCacheAdapter} interface exported\n * by `@reblu/site-client`, translating a {@link RebluCachePolicy} into the\n * `fetch` options Next understands. This is the piece that restores the exact\n * ISR behavior demo-site hard-codes today — now injected instead of baked in:\n *\n * ```ts\n * createRebluClient({ baseUrl, apiKey, cache: new NextCacheAdapter(60) })\n * ```\n *\n * - `revalidate` > 0 → `{ next: { revalidate } }` (time-based ISR)\n * - `revalidate` `false` / `0` → `{ cache: 'no-store' }` (dynamic / preview)\n * - omitted `revalidate` → the constructor `defaultRevalidate`\n */\nexport class NextCacheAdapter implements RebluCacheAdapter {\n /**\n * @param defaultRevalidate ISR window (seconds) applied when a request omits\n * its own `revalidate`. Defaults to `60` for parity with demo-site's current\n * ISR default (`client.ts:45`). Pass `false`/`0` to default to no-store.\n */\n constructor(private readonly defaultRevalidate: number | false = 60) {}\n\n resolveRequestInit(policy: RebluCachePolicy): RequestInit {\n const revalidate = policy.revalidate ?? this.defaultRevalidate\n if (revalidate === false || revalidate === 0) {\n return { cache: 'no-store' }\n }\n return { next: { revalidate } } as RequestInit\n }\n}\n","'use client'\n\nimport type { Cta } from '@reblu/site-contracts'\nimport { Button } from '@reblu/site-ui'\nimport Link from 'next/link'\n\ntype ButtonSize = 'sm' | 'default' | 'lg'\n\nexport interface CtaButtonsProps {\n ctas: Cta[]\n /** Wrapper class (layout of the button group). */\n className?: string\n /** Class applied to each button. */\n buttonClassName?: string\n size?: ButtonSize\n}\n\n/** External = absolute http(s) URL (e.g. a Klinikare booking link). */\nfunction isExternal(href: string): boolean {\n return /^https?:\\/\\//i.test(href)\n}\n\n/**\n * Presentational CTA renderer (REB-394, moved to `@reblu/site-next` in REB-418).\n * Pure — takes already-resolved CTAs and renders one styled button-link each.\n * Internal hrefs use `next/link` (client routing); external URLs open in a new,\n * safely-rel tab. `analyticsId` is surfaced as `data-cta-id` on the clickable\n * element for GA `cta_click` tracking. Renders nothing when there are no CTAs.\n *\n * Imports `Button` from `@reblu/site-ui` (public) — never `@reblu/ui` (private).\n */\nexport function CtaButtons({ ctas, className, buttonClassName, size = 'lg' }: CtaButtonsProps) {\n if (ctas.length === 0) return null\n\n return (\n <div className={className} data-slot='cta-buttons'>\n {ctas.map((cta) => (\n <Button\n key={cta.id}\n asChild\n size={size}\n className={buttonClassName}\n data-cta-id={cta.analyticsId ?? undefined}\n >\n {isExternal(cta.href) ? (\n <a href={cta.href} target='_blank' rel='noopener noreferrer'>\n {cta.label}\n </a>\n ) : (\n <Link href={cta.href}>{cta.label}</Link>\n )}\n </Button>\n ))}\n </div>\n )\n}\n","import type { Cta } from '@reblu/site-contracts'\n\nimport { CtaButtons } from './cta-buttons'\n\nexport interface StickyMobileCtaProps {\n ctas: Cta[]\n}\n\n/**\n * StickyMobileCta (REB-394, moved to `@reblu/site-next` in REB-418) — a\n * mobile-only sticky bottom bar surfacing the `sticky-mobile` slot CTAs. Hidden\n * on `md+` (desktop has its own CTAs). Renders nothing when no CTA is configured,\n * so it never adds empty chrome.\n */\nexport function StickyMobileCta({ ctas }: StickyMobileCtaProps) {\n if (ctas.length === 0) return null\n\n return (\n <div className='fixed inset-x-0 bottom-0 z-50 md:hidden border-t border-border bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80'>\n <CtaButtons\n ctas={ctas}\n className='flex justify-center gap-3'\n buttonClassName='w-full rounded-lg font-semibold'\n size='lg'\n />\n </div>\n )\n}\n","import type { RebluClient } from '@reblu/site-client'\nimport type { Cta } from '@reblu/site-contracts'\nimport type { ReactNode } from 'react'\n\nimport { CtaButtons } from './cta-buttons'\n\ntype ButtonSize = 'sm' | 'default' | 'lg'\n\n/** The `Cta.slot` union — the valid placement keys, sourced from the contract. */\nexport type CtaSlot = Cta['slot']\n\n/**\n * The slice of the SDK client `EditableCta` needs: just the `ctas` namespace.\n * Typing it structurally lets a SITE pass either the full `RebluClient` or a\n * narrower singleton, without coupling this component to the whole surface.\n */\nexport type CtaClient = Pick<RebluClient, 'ctas'>\n\nexport interface EditableCtaProps {\n /** CtaSlot value: hero | blog-end | services | contact | sticky-mobile | footer. */\n slot: CtaSlot\n /** The Reblu SDK client instance the SITE owns (env-configured upstream). */\n client: CtaClient\n className?: string\n buttonClassName?: string\n size?: ButtonSize\n /**\n * Rendered when the slot has no configured CTA (or the upstream fetch fails).\n * Lets each placement keep its existing static CTA as a graceful fallback.\n */\n fallback?: ReactNode\n}\n\n/**\n * EditableCta (REB-394, moved to `@reblu/site-next` in REB-418) — drop-anywhere\n * async Server Component that renders the CTAs configured for a slot from nexus,\n * with no redeploy. Fetches active CTAs via the injected SDK client and degrades\n * gracefully: on empty/failure it renders `fallback` (a static CTA) instead of\n * breaking the page.\n *\n * The client is injected (prop or per-SITE singleton) so this package never\n * reads `process.env` and stays framework-glue only — the SITE owns the env and\n * wires the ISR-aware {@link NextCacheAdapter}.\n */\nexport async function EditableCta({\n slot,\n client,\n className,\n buttonClassName,\n size,\n fallback = null,\n}: EditableCtaProps) {\n const { data: ctas } = await client.ctas\n .list(slot)\n .catch(() => ({ data: [] as Cta[] }))\n\n if (ctas.length === 0) return <>{fallback}</>\n\n return (\n <CtaButtons ctas={ctas} className={className} buttonClassName={buttonClassName} size={size} />\n )\n}\n"],"mappings":";AAkBO,IAAM,mBAAN,MAAoD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,YAA6B,oBAAoC,IAAI;AAAxC;AAAA,EAAyC;AAAA,EAAzC;AAAA,EAE7B,mBAAmB,QAAuC;AACxD,UAAM,aAAa,OAAO,cAAc,KAAK;AAC7C,QAAI,eAAe,SAAS,eAAe,GAAG;AAC5C,aAAO,EAAE,OAAO,WAAW;AAAA,IAC7B;AACA,WAAO,EAAE,MAAM,EAAE,WAAW,EAAE;AAAA,EAChC;AACF;;;AC9BA,SAAS,cAAc;AACvB,OAAO,UAAU;AAyCL;AA3BZ,SAAS,WAAW,MAAuB;AACzC,SAAO,gBAAgB,KAAK,IAAI;AAClC;AAWO,SAAS,WAAW,EAAE,MAAM,WAAW,iBAAiB,OAAO,KAAK,GAAoB;AAC7F,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,SACE,oBAAC,SAAI,WAAsB,aAAU,eAClC,eAAK,IAAI,CAAC,QACT;AAAA,IAAC;AAAA;AAAA,MAEC,SAAO;AAAA,MACP;AAAA,MACA,WAAW;AAAA,MACX,eAAa,IAAI,eAAe;AAAA,MAE/B,qBAAW,IAAI,IAAI,IAClB,oBAAC,OAAE,MAAM,IAAI,MAAM,QAAO,UAAS,KAAI,uBACpC,cAAI,OACP,IAEA,oBAAC,QAAK,MAAM,IAAI,MAAO,cAAI,OAAM;AAAA;AAAA,IAX9B,IAAI;AAAA,EAaX,CACD,GACH;AAEJ;;;ACpCM,gBAAAA,YAAA;AALC,SAAS,gBAAgB,EAAE,KAAK,GAAyB;AAC9D,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,SACE,gBAAAA,KAAC,SAAI,WAAU,uJACb,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV,iBAAgB;AAAA,MAChB,MAAK;AAAA;AAAA,EACP,GACF;AAEJ;;;AC6BgC,0BAAAC,YAAA;AAZhC,eAAsB,YAAY;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAqB;AACnB,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,OAAO,KACjC,KAAK,IAAI,EACT,MAAM,OAAO,EAAE,MAAM,CAAC,EAAW,EAAE;AAEtC,MAAI,KAAK,WAAW,EAAG,QAAO,gBAAAA,KAAA,YAAG,oBAAS;AAE1C,SACE,gBAAAA,KAAC,cAAW,MAAY,WAAsB,iBAAkC,MAAY;AAEhG;","names":["jsx","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reblu/site-next",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Next.js RSC glue for Reblu SITEs — ISR cache adapter + editable CTA server/client components.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Reblu — Adolfo Unturbe",
@@ -58,7 +58,7 @@
58
58
  "peerDependencies": {
59
59
  "next": ">=16",
60
60
  "react": ">=19",
61
- "@reblu/site-ui": "0.1.0"
61
+ "@reblu/site-ui": "0.2.0"
62
62
  },
63
63
  "dependencies": {
64
64
  "@reblu/site-client": "0.1.0",
@@ -80,7 +80,7 @@
80
80
  "typescript": "^5.8.3",
81
81
  "vitest": "^4.0.15",
82
82
  "@reblu/config": "0.0.1",
83
- "@reblu/site-ui": "0.1.0"
83
+ "@reblu/site-ui": "0.2.0"
84
84
  },
85
85
  "scripts": {
86
86
  "build": "tsup",