@contentful/experiences-react 0.5.1 → 0.5.3
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 +24 -0
- package/README.md +61 -35
- package/dist/client-renderer.d.ts +1 -1
- package/dist/client-renderer.js.map +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/missing-component.js +1 -0
- package/dist/missing-component.js.map +1 -1
- package/dist/nodes-renderer.d.ts +1 -1
- package/dist/nodes-renderer.js +1 -1
- package/dist/nodes-renderer.js.map +1 -1
- package/dist/server-renderer.d.ts +1 -1
- package/dist/server-renderer.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js.map +1 -1
- package/dist/use-active-viewport.d.ts +1 -1
- package/dist/use-active-viewport.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## 0.5.3 (2026-07-21)
|
|
2
|
+
|
|
3
|
+
### 🩹 Fixes
|
|
4
|
+
|
|
5
|
+
- render missing-component fallback as element in RSC [AIS-316] ([#79](https://github.com/contentful/experiences/pull/79))
|
|
6
|
+
|
|
7
|
+
### 🧱 Updated Dependencies
|
|
8
|
+
|
|
9
|
+
- Updated client to 0.1.3
|
|
10
|
+
- Updated design to 0.5.3
|
|
11
|
+
- Updated core to 0.5.3
|
|
12
|
+
|
|
13
|
+
## 0.5.2 (2026-07-20)
|
|
14
|
+
|
|
15
|
+
### 🩹 Fixes
|
|
16
|
+
|
|
17
|
+
- rename experiences-core to experiences-sdk-core [AIS-305] ([#76](https://github.com/contentful/experiences/pull/76))
|
|
18
|
+
|
|
19
|
+
### 🧱 Updated Dependencies
|
|
20
|
+
|
|
21
|
+
- Updated client to 0.1.2
|
|
22
|
+
- Updated design to 0.5.2
|
|
23
|
+
- Updated core to 0.5.2
|
|
24
|
+
|
|
1
25
|
## 0.5.1 (2026-07-17)
|
|
2
26
|
|
|
3
27
|
### 🚀 Features
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @contentful/experiences-react
|
|
2
2
|
|
|
3
|
-
> ⚠️ **
|
|
3
|
+
> ⚠️ **Alpha.** Published to npm. APIs are unstable and will change.
|
|
4
4
|
|
|
5
|
-
The React adapter for the Contentful Experiences SDK
|
|
5
|
+
The React adapter for the Contentful Experiences SDK. You bring your own React components; it renders Experience payloads from the Experience Delivery API (XDA) with them.
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
npm install @contentful/experiences-react
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
This is the **only SDK package
|
|
11
|
+
This is the **only SDK package you install**. It re-exports everything you need from `@contentful/experiences-sdk-core`, `@contentful/experiences-design`, and `@contentful/experiences-client`. The other packages are workspace-internal.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -24,7 +24,7 @@ defineTemplate<Props>(config); // Same shape, for page-level template wrappers
|
|
|
24
24
|
### Fetching
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
-
fetchExperience(experienceOptions, clientOptions, resolveOptions) // Async
|
|
27
|
+
fetchExperience(experienceOptions, clientOptions, resolveOptions) // Async; fetches from XDA and resolves in one call
|
|
28
28
|
createClient(options) // Functional constructor matching the SDK's option shape
|
|
29
29
|
ContentfulViewDeliveryClient // Re-exported delivery client for advanced use cases
|
|
30
30
|
NotFoundError // Thrown when the Experience ID doesn't exist
|
|
@@ -35,7 +35,7 @@ type ExperienceOptions, ClientOptions, ResolveOptions, CreateClientOptions
|
|
|
35
35
|
### Resolver
|
|
36
36
|
|
|
37
37
|
```ts
|
|
38
|
-
resolveExperience(payload, config, opts?) // Async
|
|
38
|
+
resolveExperience(payload, config, opts?) // Async; walks payload, runs resolveData, returns a PortableRenderPlan
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
### Renderers
|
|
@@ -44,25 +44,39 @@ resolveExperience(payload, config, opts?) // Async — walks payload, runs res
|
|
|
44
44
|
ServerExperienceRenderer; // RSC-friendly, active viewport seeded from initialViewportId
|
|
45
45
|
ClientExperienceRenderer; // 'use client', subscribes to window.matchMedia, alias: ExperienceRenderer
|
|
46
46
|
MissingComponent; // Default fallback for unregistered component types
|
|
47
|
-
useActiveViewport; // Hook used inside ClientExperienceRenderer (rarely
|
|
47
|
+
useActiveViewport; // Hook used inside ClientExperienceRenderer (you'll rarely need it directly)
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
### Styling + runtime context (hooks)
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
useDesignValues<T>(); // Resolved design values for the current node (viewport-cascaded + token-resolved)
|
|
54
|
+
toCss(design, options?); // Turns a design record into CSSProperties, keeping only real CSS keys
|
|
55
|
+
useExperience(); // RenderContext: isPreview, metadata, viewports, activeViewport
|
|
56
|
+
useContentfulComponent(); // Raw payload for the enclosing node (or null)
|
|
57
|
+
useContentfulTemplate(); // Same, for the page-level template
|
|
58
|
+
type ToCssOptions;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Design is **not** injected as props; components read it via `useDesignValues()`. Token resolution is configured with `resolveToken` on your `Config` (`type ResolveToken`).
|
|
62
|
+
|
|
50
63
|
### Re-exported types and utilities
|
|
51
64
|
|
|
52
65
|
```ts
|
|
53
66
|
// From core
|
|
54
|
-
type Config, Components, Templates,
|
|
67
|
+
type Config, Components, Templates, Registration, TemplateRegistration,
|
|
55
68
|
type ComponentConfig, TemplateConfig,
|
|
56
|
-
type
|
|
57
|
-
type RenderContext,
|
|
69
|
+
type ContentfulComponent, ContentfulTemplate,
|
|
70
|
+
type RenderContext, ResolveToken,
|
|
58
71
|
type ExperiencePayload, ExperienceNode, ComponentTypeNode, TemplateNode,
|
|
59
72
|
type PortableRenderPlan, PortableRenderNode, PortableTemplate,
|
|
60
73
|
type DesignPropValue, ManualDesignValue, DesignToken, ValuesByViewport,
|
|
61
74
|
type ViewportDef, ExperienceContext, ResolveContext,
|
|
62
75
|
type ResolverConfig, ResolveExperienceOptions
|
|
63
76
|
|
|
64
|
-
// From design (
|
|
65
|
-
getValueForViewport, getViewportIndex, resolveDesignProperties, toCssMediaQuery
|
|
77
|
+
// From design (if you want to do your own viewport-aware resolution)
|
|
78
|
+
getValueForViewport, getViewportIndex, resolveDesignProperties, toCssMediaQuery,
|
|
79
|
+
isCssProperty, toCssKey, CSS_PROPERTIES
|
|
66
80
|
```
|
|
67
81
|
|
|
68
82
|
---
|
|
@@ -70,42 +84,54 @@ getValueForViewport, getViewportIndex, resolveDesignProperties, toCssMediaQuery
|
|
|
70
84
|
## Quick reference
|
|
71
85
|
|
|
72
86
|
```tsx
|
|
73
|
-
|
|
87
|
+
// components/Button.tsx: reads content props; reads design via the hook
|
|
88
|
+
'use client';
|
|
89
|
+
import { toCss, useDesignValues } from '@contentful/experiences-react';
|
|
90
|
+
|
|
91
|
+
export function Button({ label, url }: { label?: string; url?: string }) {
|
|
92
|
+
const design = useDesignValues();
|
|
93
|
+
return (
|
|
94
|
+
<a href={url} style={toCss(design)}>
|
|
95
|
+
{label}
|
|
96
|
+
</a>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// lib/experience-config.tsx
|
|
74
103
|
import {
|
|
75
104
|
defineComponent,
|
|
76
|
-
defineTemplate,
|
|
77
|
-
fetchExperience,
|
|
78
|
-
NotFoundError,
|
|
79
|
-
ServerExperienceRenderer,
|
|
80
|
-
type Config,
|
|
81
105
|
type Components,
|
|
82
|
-
type
|
|
106
|
+
type Config,
|
|
107
|
+
type ResolveToken,
|
|
83
108
|
} from '@contentful/experiences-react';
|
|
84
109
|
|
|
85
|
-
import { Button
|
|
110
|
+
import { Button } from './components/Button';
|
|
86
111
|
|
|
87
112
|
const components: Components = {
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
// Bare component, or defineComponent({...}) when you need defaults/resolveData.
|
|
114
|
+
Button: defineComponent({
|
|
90
115
|
resolveData: ({ content }) => ({ url: ensureScheme(content.url) }),
|
|
91
|
-
|
|
116
|
+
component: Button,
|
|
92
117
|
}),
|
|
93
118
|
};
|
|
94
119
|
|
|
95
|
-
const
|
|
120
|
+
const resolveToken: ResolveToken = (token) => designTokens[token.value];
|
|
96
121
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
122
|
+
export const experienceConfig: Config = { components, resolveToken };
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
// app/[slug]/page.tsx: in a server component
|
|
127
|
+
import { fetchExperience, ServerExperienceRenderer } from '@contentful/experiences-react';
|
|
128
|
+
|
|
129
|
+
const experience = await fetchExperience(
|
|
130
|
+
{ spaceId: process.env.SPACE_ID!, environmentId: 'master', experienceId: slug },
|
|
131
|
+
{ accessToken: process.env.CDA_TOKEN! },
|
|
132
|
+
{ config: experienceConfig }
|
|
133
|
+
);
|
|
134
|
+
return <ServerExperienceRenderer experience={experience} config={experienceConfig} />;
|
|
109
135
|
```
|
|
110
136
|
|
|
111
137
|
For the full getting-started walkthrough, the merge-precedence rules, viewport handling, and design rationale, see the [root README](../../README.md) and [`AGENTS.md`](../../AGENTS.md).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { PortableRenderPlan, ExperienceContext } from '@contentful/experiences-core';
|
|
2
|
+
import { PortableRenderPlan, ExperienceContext } from '@contentful/experiences-sdk-core';
|
|
3
3
|
import { RenderUnknown } from './nodes-renderer.js';
|
|
4
4
|
import { Config } from './types.js';
|
|
5
5
|
import './missing-component.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client-renderer.tsx"],"sourcesContent":["/*\n * Client Experience renderer. First paint matches the server renderer; after\n * hydration `useActiveViewport` takes over via `matchMedia`. Server-safe: the\n * hook returns the seeded index and registers no listeners when there's no\n * window, so SSR output matches `<ServerExperienceRenderer>`.\n */\n\n'use client';\n\nimport type { ReactNode } from 'react';\n\nimport type {\n ExperienceContext,\n PortableRenderPlan,\n ViewportDef,\n} from '@contentful/experiences-core';\n\nimport { ExperienceProvider } from './context';\nimport { MissingComponent } from './missing-component';\nimport { NodesRenderer, WrapWithTemplate, type RenderUnknown } from './nodes-renderer';\nimport type { Config, RenderContext } from './types';\nimport { useActiveViewport } from './use-active-viewport';\n\nconst DEFAULT_CONTEXT: ExperienceContext = {\n isPreview: false,\n metadata: {},\n viewports: [],\n};\n\nconst FALLBACK_VIEWPORT: ViewportDef = {\n id: '_',\n query: '*',\n displayName: 'Default',\n previewSize: '100%',\n};\n\nexport interface ClientExperienceRendererProps {\n experience: PortableRenderPlan | null | undefined;\n config: Config;\n /** Initial viewport seed; should match what the server-rendered output used. */\n initialViewportId?: string;\n context?: Partial<ExperienceContext>;\n renderUnknown?: RenderUnknown;\n}\n\nexport function ClientExperienceRenderer({\n experience,\n config,\n initialViewportId,\n context,\n renderUnknown = MissingComponent,\n}: ClientExperienceRendererProps): ReactNode {\n if (!experience) return null;\n const { activeViewportIndex } = useActiveViewport(experience.viewports, initialViewportId);\n // Copy so the context shares no object identity with the plan arrays — see\n // the note in `server-renderer.tsx`.\n const contextViewports = experience.viewports.map((v) => ({ ...v }));\n const activeViewport = { ...(experience.viewports[activeViewportIndex] ?? FALLBACK_VIEWPORT) };\n\n const renderContext: RenderContext = {\n ...DEFAULT_CONTEXT,\n ...context,\n metadata: { ...DEFAULT_CONTEXT.metadata, ...(context?.metadata ?? {}) },\n viewports: contextViewports,\n activeViewport,\n activeViewportIndex,\n };\n\n return (\n <ExperienceProvider value={renderContext}>\n <WrapWithTemplate\n template={experience.template}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n >\n <NodesRenderer\n nodes={experience.nodes}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n </WrapWithTemplate>\n </ExperienceProvider>\n );\n}\n"],"mappings":";AA4EQ;AA3DR,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,eAAe,wBAA4C;AAEpE,SAAS,yBAAyB;AAElC,MAAM,kBAAqC;AAAA,EACzC,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,WAAW,CAAC;AACd;AAEA,MAAM,oBAAiC;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,aAAa;AACf;AAWO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAA6C;AAC3C,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,EAAE,oBAAoB,IAAI,kBAAkB,WAAW,WAAW,iBAAiB;AAGzF,QAAM,mBAAmB,WAAW,UAAU,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AACnE,QAAM,iBAAiB,EAAE,GAAI,WAAW,UAAU,mBAAmB,KAAK,kBAAmB;AAE7F,QAAM,gBAA+B;AAAA,IACnC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU,EAAE,GAAG,gBAAgB,UAAU,GAAI,SAAS,YAAY,CAAC,EAAG;AAAA,IACtE,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,sBAAmB,OAAO,eACzB;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,WAAW;AAAA,MACrB;AAAA,MACA,WAAW,WAAW;AAAA,MACtB;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,WAAW;AAAA,UAClB;AAAA,UACA,WAAW,WAAW;AAAA,UACtB;AAAA,UACA;AAAA;AAAA,MACF;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client-renderer.tsx"],"sourcesContent":["/*\n * Client Experience renderer. First paint matches the server renderer; after\n * hydration `useActiveViewport` takes over via `matchMedia`. Server-safe: the\n * hook returns the seeded index and registers no listeners when there's no\n * window, so SSR output matches `<ServerExperienceRenderer>`.\n */\n\n'use client';\n\nimport type { ReactNode } from 'react';\n\nimport type {\n ExperienceContext,\n PortableRenderPlan,\n ViewportDef,\n} from '@contentful/experiences-sdk-core';\n\nimport { ExperienceProvider } from './context';\nimport { MissingComponent } from './missing-component';\nimport { NodesRenderer, WrapWithTemplate, type RenderUnknown } from './nodes-renderer';\nimport type { Config, RenderContext } from './types';\nimport { useActiveViewport } from './use-active-viewport';\n\nconst DEFAULT_CONTEXT: ExperienceContext = {\n isPreview: false,\n metadata: {},\n viewports: [],\n};\n\nconst FALLBACK_VIEWPORT: ViewportDef = {\n id: '_',\n query: '*',\n displayName: 'Default',\n previewSize: '100%',\n};\n\nexport interface ClientExperienceRendererProps {\n experience: PortableRenderPlan | null | undefined;\n config: Config;\n /** Initial viewport seed; should match what the server-rendered output used. */\n initialViewportId?: string;\n context?: Partial<ExperienceContext>;\n renderUnknown?: RenderUnknown;\n}\n\nexport function ClientExperienceRenderer({\n experience,\n config,\n initialViewportId,\n context,\n renderUnknown = MissingComponent,\n}: ClientExperienceRendererProps): ReactNode {\n if (!experience) return null;\n const { activeViewportIndex } = useActiveViewport(experience.viewports, initialViewportId);\n // Copy so the context shares no object identity with the plan arrays — see\n // the note in `server-renderer.tsx`.\n const contextViewports = experience.viewports.map((v) => ({ ...v }));\n const activeViewport = { ...(experience.viewports[activeViewportIndex] ?? FALLBACK_VIEWPORT) };\n\n const renderContext: RenderContext = {\n ...DEFAULT_CONTEXT,\n ...context,\n metadata: { ...DEFAULT_CONTEXT.metadata, ...(context?.metadata ?? {}) },\n viewports: contextViewports,\n activeViewport,\n activeViewportIndex,\n };\n\n return (\n <ExperienceProvider value={renderContext}>\n <WrapWithTemplate\n template={experience.template}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n >\n <NodesRenderer\n nodes={experience.nodes}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n </WrapWithTemplate>\n </ExperienceProvider>\n );\n}\n"],"mappings":";AA4EQ;AA3DR,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,eAAe,wBAA4C;AAEpE,SAAS,yBAAyB;AAElC,MAAM,kBAAqC;AAAA,EACzC,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,WAAW,CAAC;AACd;AAEA,MAAM,oBAAiC;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,aAAa;AACf;AAWO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAA6C;AAC3C,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,EAAE,oBAAoB,IAAI,kBAAkB,WAAW,WAAW,iBAAiB;AAGzF,QAAM,mBAAmB,WAAW,UAAU,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AACnE,QAAM,iBAAiB,EAAE,GAAI,WAAW,UAAU,mBAAmB,KAAK,kBAAmB;AAE7F,QAAM,gBAA+B;AAAA,IACnC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU,EAAE,GAAG,gBAAgB,UAAU,GAAI,SAAS,YAAY,CAAC,EAAG;AAAA,IACtE,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,sBAAmB,OAAO,eACzB;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,WAAW;AAAA,MACrB;AAAA,MACA,WAAW,WAAW;AAAA,MACtB;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,WAAW;AAAA,UAClB;AAAA,UACA,WAAW,WAAW;AAAA,UACtB;AAAA,UACA;AAAA;AAAA,MACF;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { ContentfulComponent, ContentfulTemplate, RenderContext } from './types.js';
|
|
3
|
-
import '@contentful/experiences-core';
|
|
3
|
+
import '@contentful/experiences-sdk-core';
|
|
4
4
|
|
|
5
5
|
declare function ExperienceProvider({ value, children, }: {
|
|
6
6
|
value: RenderContext;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { useContentfulComponent, useContentfulTemplate, useExperience } from './
|
|
|
7
7
|
export { useDesignValues } from './use-design-values.js';
|
|
8
8
|
export { ToCssOptions, toCss } from './design-utils.js';
|
|
9
9
|
export { ComponentConfig, Components, Config, ContentfulComponent, ContentfulTemplate, Registration, RenderContext, TemplateConfig, TemplateRegistration, Templates, defineComponent, defineTemplate, normalizeComponentRegistration, normalizeTemplateRegistration } from './types.js';
|
|
10
|
-
export { ComponentTypeNode, ComponentTypeRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSys, ManualDesignValue, PortableRegistration, PortableRenderNode, PortableRenderPlan, PortableTemplate, ResolveContext, ResolveExperienceOptions, ResolveToken, ResolverConfig, TemplateNode, TemplateRef, ValuesByViewport, ViewportDef, resolveExperience } from '@contentful/experiences-core';
|
|
10
|
+
export { ComponentTypeNode, ComponentTypeRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSys, ManualDesignValue, PortableRegistration, PortableRenderNode, PortableRenderPlan, PortableTemplate, ResolveContext, ResolveExperienceOptions, ResolveToken, ResolverConfig, TemplateNode, TemplateRef, ValuesByViewport, ViewportDef, resolveExperience } from '@contentful/experiences-sdk-core';
|
|
11
11
|
export { CSS_PROPERTIES, getValueForViewport, getViewportIndex, isCssProperty, resolveDesignProperties, toCssKey, toCssMediaQuery } from '@contentful/experiences-design';
|
|
12
12
|
export { ClientOptions, ContentfulViewDelivery, ContentfulViewDeliveryClient, CreateClientOptions, ExperienceOptions, NotFoundError, ResolveOptions, createClient, fetchExperience } from '@contentful/experiences-client';
|
|
13
13
|
import 'react';
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
normalizeComponentRegistration,
|
|
15
15
|
normalizeTemplateRegistration
|
|
16
16
|
} from "./types";
|
|
17
|
-
import { resolveExperience } from "@contentful/experiences-core";
|
|
17
|
+
import { resolveExperience } from "@contentful/experiences-sdk-core";
|
|
18
18
|
import {
|
|
19
19
|
CSS_PROPERTIES,
|
|
20
20
|
getValueForViewport,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/*\n * Public API surface for `@contentful/experiences-react`.\n *\n * Customers add ONLY this package to their app's dependencies. Everything\n * needed to render an Experience — types, the resolver, viewport utilities,\n * the renderer components, the authoring helpers — is re-exported from here.\n * The internal `@contentful/experiences-core` and\n * `@contentful/experiences-design` packages are workspace-only implementation\n * details; they are not part of the public API.\n *\n * `ExperienceRenderer` is an alias for `ClientExperienceRenderer`; SSR\n * consumers explicitly import `ServerExperienceRenderer`.\n */\n\n// ─── Renderers ─────────────────────────────────────────────────────────────\nexport {\n ClientExperienceRenderer as ExperienceRenderer,\n ClientExperienceRenderer,\n} from './client-renderer';\nexport type { ClientExperienceRendererProps as ExperienceRendererProps } from './client-renderer';\nexport type { ClientExperienceRendererProps } from './client-renderer';\n\nexport { ServerExperienceRenderer } from './server-renderer';\nexport type { ServerExperienceRendererProps } from './server-renderer';\n\nexport { MissingComponent } from './missing-component';\nexport type { MissingComponentProps } from './missing-component';\n\nexport { useActiveViewport } from './use-active-viewport';\nexport type { UseActiveViewportResult } from './use-active-viewport';\n\nexport type { RenderUnknown } from './nodes-renderer';\n\n// ─── Runtime context hooks ───────────────────────────────────────────────\nexport { useExperience, useContentfulComponent, useContentfulTemplate } from './context';\nexport { useDesignValues } from './use-design-values';\nexport { toCss } from './design-utils';\nexport type { ToCssOptions } from './design-utils';\n\n// ─── Authoring helpers + Config types ─────────────────────────────────────\nexport {\n defineComponent,\n defineTemplate,\n normalizeComponentRegistration,\n normalizeTemplateRegistration,\n} from './types';\nexport type {\n ComponentConfig,\n Components,\n Config,\n ContentfulComponent,\n ContentfulTemplate,\n Registration,\n RenderContext,\n ResolveToken,\n TemplateConfig,\n TemplateRegistration,\n Templates,\n} from './types';\n\n// ─── Resolver (re-exported from render-core) ──────────────────────────────\nexport { resolveExperience } from '@contentful/experiences-core';\nexport type { ResolverConfig, ResolveExperienceOptions } from '@contentful/experiences-core';\n\n// ─── Core IR + payload types (re-exported from render-core) ───────────────\nexport type {\n ComponentTypeNode,\n ComponentTypeRef,\n DesignPropValue,\n DesignToken,\n ExperienceContext,\n ExperienceNode,\n ExperiencePayload,\n ExperienceSys,\n ManualDesignValue,\n PortableRegistration,\n PortableRenderNode,\n PortableRenderPlan,\n PortableTemplate,\n ResolveContext,\n TemplateNode,\n TemplateRef,\n ValuesByViewport,\n ViewportDef,\n} from '@contentful/experiences-core';\n\n// ─── Design utilities (re-exported from design) ───────────────────────────\nexport {\n CSS_PROPERTIES,\n getValueForViewport,\n getViewportIndex,\n isCssProperty,\n resolveDesignProperties,\n toCssKey,\n toCssMediaQuery,\n} from '@contentful/experiences-design';\n\n// ─── Delivery client + fetchExperience ────────────────────────────────────\nexport {\n ContentfulViewDelivery,\n ContentfulViewDeliveryClient,\n NotFoundError,\n createClient,\n fetchExperience,\n} from '@contentful/experiences-client';\nexport type {\n ClientOptions,\n CreateClientOptions,\n ExperienceOptions,\n ResolveOptions,\n} from '@contentful/experiences-client';\n"],"mappings":"AAeA;AAAA,EAC8B;AAAA,EAC5B,4BAAAA;AAAA,OACK;AAIP,SAAS,gCAAgC;AAGzC,SAAS,wBAAwB;AAGjC,SAAS,yBAAyB;AAMlC,SAAS,eAAe,wBAAwB,6BAA6B;AAC7E,SAAS,uBAAuB;AAChC,SAAS,aAAa;AAItB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgBP,SAAS,yBAAyB;AA0BlC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":["ClientExperienceRenderer"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/*\n * Public API surface for `@contentful/experiences-react`.\n *\n * Customers add ONLY this package to their app's dependencies. Everything\n * needed to render an Experience — types, the resolver, viewport utilities,\n * the renderer components, the authoring helpers — is re-exported from here.\n * The internal `@contentful/experiences-sdk-core` and\n * `@contentful/experiences-design` packages are workspace-only implementation\n * details; they are not part of the public API.\n *\n * `ExperienceRenderer` is an alias for `ClientExperienceRenderer`; SSR\n * consumers explicitly import `ServerExperienceRenderer`.\n */\n\n// ─── Renderers ─────────────────────────────────────────────────────────────\nexport {\n ClientExperienceRenderer as ExperienceRenderer,\n ClientExperienceRenderer,\n} from './client-renderer';\nexport type { ClientExperienceRendererProps as ExperienceRendererProps } from './client-renderer';\nexport type { ClientExperienceRendererProps } from './client-renderer';\n\nexport { ServerExperienceRenderer } from './server-renderer';\nexport type { ServerExperienceRendererProps } from './server-renderer';\n\nexport { MissingComponent } from './missing-component';\nexport type { MissingComponentProps } from './missing-component';\n\nexport { useActiveViewport } from './use-active-viewport';\nexport type { UseActiveViewportResult } from './use-active-viewport';\n\nexport type { RenderUnknown } from './nodes-renderer';\n\n// ─── Runtime context hooks ───────────────────────────────────────────────\nexport { useExperience, useContentfulComponent, useContentfulTemplate } from './context';\nexport { useDesignValues } from './use-design-values';\nexport { toCss } from './design-utils';\nexport type { ToCssOptions } from './design-utils';\n\n// ─── Authoring helpers + Config types ─────────────────────────────────────\nexport {\n defineComponent,\n defineTemplate,\n normalizeComponentRegistration,\n normalizeTemplateRegistration,\n} from './types';\nexport type {\n ComponentConfig,\n Components,\n Config,\n ContentfulComponent,\n ContentfulTemplate,\n Registration,\n RenderContext,\n ResolveToken,\n TemplateConfig,\n TemplateRegistration,\n Templates,\n} from './types';\n\n// ─── Resolver (re-exported from render-core) ──────────────────────────────\nexport { resolveExperience } from '@contentful/experiences-sdk-core';\nexport type { ResolverConfig, ResolveExperienceOptions } from '@contentful/experiences-sdk-core';\n\n// ─── Core IR + payload types (re-exported from render-core) ───────────────\nexport type {\n ComponentTypeNode,\n ComponentTypeRef,\n DesignPropValue,\n DesignToken,\n ExperienceContext,\n ExperienceNode,\n ExperiencePayload,\n ExperienceSys,\n ManualDesignValue,\n PortableRegistration,\n PortableRenderNode,\n PortableRenderPlan,\n PortableTemplate,\n ResolveContext,\n TemplateNode,\n TemplateRef,\n ValuesByViewport,\n ViewportDef,\n} from '@contentful/experiences-sdk-core';\n\n// ─── Design utilities (re-exported from design) ───────────────────────────\nexport {\n CSS_PROPERTIES,\n getValueForViewport,\n getViewportIndex,\n isCssProperty,\n resolveDesignProperties,\n toCssKey,\n toCssMediaQuery,\n} from '@contentful/experiences-design';\n\n// ─── Delivery client + fetchExperience ────────────────────────────────────\nexport {\n ContentfulViewDelivery,\n ContentfulViewDeliveryClient,\n NotFoundError,\n createClient,\n fetchExperience,\n} from '@contentful/experiences-client';\nexport type {\n ClientOptions,\n CreateClientOptions,\n ExperienceOptions,\n ResolveOptions,\n} from '@contentful/experiences-client';\n"],"mappings":"AAeA;AAAA,EAC8B;AAAA,EAC5B,4BAAAA;AAAA,OACK;AAIP,SAAS,gCAAgC;AAGzC,SAAS,wBAAwB;AAGjC,SAAS,yBAAyB;AAMlC,SAAS,eAAe,wBAAwB,6BAA6B;AAC7E,SAAS,uBAAuB;AAChC,SAAS,aAAa;AAItB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgBP,SAAS,yBAAyB;AA0BlC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":["ClientExperienceRenderer"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/missing-component.tsx"],"sourcesContent":["/*\n * Default fallback rendered when an instance references a component type\n * not present in the Config.\n *\n * Behavior: a visible red error box in preview, silent null in production.\n * Customers can override per-render via <ExperienceRenderer renderUnknown=...>.\n */\n\nimport type { ReactNode } from 'react';\n\nimport { useExperience } from './context';\n\nexport interface MissingComponentProps {\n componentTypeId: string;\n /** Optional — only present when the payload supplied an id for this node. */\n nodeId?: string;\n}\n\nexport function MissingComponent({ componentTypeId, nodeId }: MissingComponentProps): ReactNode {\n const { isPreview } = useExperience();\n if (typeof console !== 'undefined') {\n const idLabel = nodeId ? ` (nodeId: ${nodeId})` : '';\n console.warn(\n `[@contentful/experiences] No component registered for type \"${componentTypeId}\"${idLabel}.`\n );\n }\n if (!isPreview) return null;\n return (\n <div\n style={{\n border: '2px solid red',\n padding: '1rem',\n color: 'red',\n background: '#fff',\n }}\n data-experiences-missing={componentTypeId}\n >\n <strong>Missing component ‘{componentTypeId}’</strong>\n <p>\n This component is referenced by the Experience payload but is not registered in the Config.\n </p>\n </div>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/missing-component.tsx"],"sourcesContent":["/*\n * Default fallback rendered when an instance references a component type\n * not present in the Config.\n *\n * Behavior: a visible red error box in preview, silent null in production.\n * Customers can override per-render via <ExperienceRenderer renderUnknown=...>.\n */\n'use client';\n\nimport type { ReactNode } from 'react';\n\nimport { useExperience } from './context';\n\nexport interface MissingComponentProps {\n componentTypeId: string;\n /** Optional — only present when the payload supplied an id for this node. */\n nodeId?: string;\n}\n\nexport function MissingComponent({ componentTypeId, nodeId }: MissingComponentProps): ReactNode {\n const { isPreview } = useExperience();\n if (typeof console !== 'undefined') {\n const idLabel = nodeId ? ` (nodeId: ${nodeId})` : '';\n console.warn(\n `[@contentful/experiences] No component registered for type \"${componentTypeId}\"${idLabel}.`\n );\n }\n if (!isPreview) return null;\n return (\n <div\n style={{\n border: '2px solid red',\n padding: '1rem',\n color: 'red',\n background: '#fff',\n }}\n data-experiences-missing={componentTypeId}\n >\n <strong>Missing component ‘{componentTypeId}’</strong>\n <p>\n This component is referenced by the Experience payload but is not registered in the Config.\n </p>\n </div>\n );\n}\n"],"mappings":";AAsCM,SACA,KADA;AA3BN,SAAS,qBAAqB;AAQvB,SAAS,iBAAiB,EAAE,iBAAiB,OAAO,GAAqC;AAC9F,QAAM,EAAE,UAAU,IAAI,cAAc;AACpC,MAAI,OAAO,YAAY,aAAa;AAClC,UAAM,UAAU,SAAS,aAAa,MAAM,MAAM;AAClD,YAAQ;AAAA,MACN,+DAA+D,eAAe,IAAI,OAAO;AAAA,IAC3F;AAAA,EACF;AACA,MAAI,CAAC,UAAW,QAAO;AACvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,QACP,YAAY;AAAA,MACd;AAAA,MACA,4BAA0B;AAAA,MAE1B;AAAA,6BAAC,YAAO;AAAA;AAAA,UAA0B;AAAA,UAAgB;AAAA,WAAO;AAAA,QACzD,oBAAC,OAAE,yGAEH;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
package/dist/nodes-renderer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { PortableRenderNode, ViewportDef, PortableTemplate } from '@contentful/experiences-core';
|
|
2
|
+
import { PortableRenderNode, ViewportDef, PortableTemplate } from '@contentful/experiences-sdk-core';
|
|
3
3
|
import { MissingComponentProps } from './missing-component.js';
|
|
4
4
|
import { Config } from './types.js';
|
|
5
5
|
|
package/dist/nodes-renderer.js
CHANGED
|
@@ -40,7 +40,7 @@ function NodeRenderer({
|
|
|
40
40
|
const { componentTypeId } = node.registration;
|
|
41
41
|
const entry = config.components[componentTypeId];
|
|
42
42
|
if (!entry) {
|
|
43
|
-
return renderUnknown
|
|
43
|
+
return createElement(renderUnknown, { componentTypeId, nodeId: node.nodeId });
|
|
44
44
|
}
|
|
45
45
|
const componentConfig = normalizeComponentRegistration(entry);
|
|
46
46
|
const slotProps = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nodes-renderer.tsx"],"sourcesContent":["/*\n * Recursive renderer over PortableRenderNodes. Customer components receive the\n * merged prop bag (defaults + content + resolveData + slots); design values\n * are read via `useDesignValues()`, not injected as props.\n */\n\nimport { Fragment, createElement, type ReactNode } from 'react';\n\nimport type {\n PortableRenderNode,\n PortableTemplate,\n ViewportDef,\n} from '@contentful/experiences-core';\nimport { applyTokenResolver, resolveDesignProperties } from '@contentful/experiences-design';\n\nimport {\n ContentfulComponentProvider,\n ContentfulTemplateProvider,\n ResolvedDesignProvider,\n} from './context';\nimport type { MissingComponentProps } from './missing-component';\nimport {\n normalizeComponentRegistration,\n normalizeTemplateRegistration,\n type ContentfulComponent,\n type ContentfulTemplate,\n type Config,\n} from './types';\n\nexport type RenderUnknown = (props: MissingComponentProps) => ReactNode;\n\n// Internal renderers take `viewports` + `activeViewportIndex`, not the whole\n// RenderContext object — the context is published once via ExperienceProvider,\n// and re-threading it as an element prop makes React's RSC serializer back-patch\n// a shared reference into frozen props (\"Cannot assign to read only property\").\nexport interface NodesRendererProps {\n nodes: PortableRenderNode[];\n config: Config;\n viewports: ViewportDef[];\n activeViewportIndex: number;\n renderUnknown: RenderUnknown;\n}\n\nexport function NodesRenderer({\n nodes,\n config,\n viewports,\n activeViewportIndex,\n renderUnknown,\n}: NodesRendererProps): ReactNode {\n if (!nodes.length) return null;\n return (\n <Fragment>\n {nodes.map((node, index) => (\n <NodeRenderer\n key={node.nodeId ?? index}\n node={node}\n config={config}\n viewports={viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n ))}\n </Fragment>\n );\n}\n\ninterface NodeRendererProps {\n node: PortableRenderNode;\n config: Config;\n viewports: ViewportDef[];\n activeViewportIndex: number;\n renderUnknown: RenderUnknown;\n}\n\nfunction NodeRenderer({\n node,\n config,\n viewports,\n activeViewportIndex,\n renderUnknown,\n}: NodeRendererProps): ReactNode {\n const { componentTypeId } = node.registration;\n const entry = config.components[componentTypeId];\n if (!entry) {\n return renderUnknown
|
|
1
|
+
{"version":3,"sources":["../src/nodes-renderer.tsx"],"sourcesContent":["/*\n * Recursive renderer over PortableRenderNodes. Customer components receive the\n * merged prop bag (defaults + content + resolveData + slots); design values\n * are read via `useDesignValues()`, not injected as props.\n */\n\nimport { Fragment, createElement, type ReactNode } from 'react';\n\nimport type {\n PortableRenderNode,\n PortableTemplate,\n ViewportDef,\n} from '@contentful/experiences-sdk-core';\nimport { applyTokenResolver, resolveDesignProperties } from '@contentful/experiences-design';\n\nimport {\n ContentfulComponentProvider,\n ContentfulTemplateProvider,\n ResolvedDesignProvider,\n} from './context';\nimport type { MissingComponentProps } from './missing-component';\nimport {\n normalizeComponentRegistration,\n normalizeTemplateRegistration,\n type ContentfulComponent,\n type ContentfulTemplate,\n type Config,\n} from './types';\n\nexport type RenderUnknown = (props: MissingComponentProps) => ReactNode;\n\n// Internal renderers take `viewports` + `activeViewportIndex`, not the whole\n// RenderContext object — the context is published once via ExperienceProvider,\n// and re-threading it as an element prop makes React's RSC serializer back-patch\n// a shared reference into frozen props (\"Cannot assign to read only property\").\nexport interface NodesRendererProps {\n nodes: PortableRenderNode[];\n config: Config;\n viewports: ViewportDef[];\n activeViewportIndex: number;\n renderUnknown: RenderUnknown;\n}\n\nexport function NodesRenderer({\n nodes,\n config,\n viewports,\n activeViewportIndex,\n renderUnknown,\n}: NodesRendererProps): ReactNode {\n if (!nodes.length) return null;\n return (\n <Fragment>\n {nodes.map((node, index) => (\n <NodeRenderer\n key={node.nodeId ?? index}\n node={node}\n config={config}\n viewports={viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n ))}\n </Fragment>\n );\n}\n\ninterface NodeRendererProps {\n node: PortableRenderNode;\n config: Config;\n viewports: ViewportDef[];\n activeViewportIndex: number;\n renderUnknown: RenderUnknown;\n}\n\nfunction NodeRenderer({\n node,\n config,\n viewports,\n activeViewportIndex,\n renderUnknown,\n}: NodeRendererProps): ReactNode {\n const { componentTypeId } = node.registration;\n const entry = config.components[componentTypeId];\n if (!entry) {\n return createElement(renderUnknown, { componentTypeId, nodeId: node.nodeId });\n }\n const componentConfig = normalizeComponentRegistration(entry);\n\n // Pre-render slot subtrees so components receive ReactNodes by slot name.\n const slotProps: Record<string, ReactNode> = {};\n for (const [slotName, children] of Object.entries(node.slots)) {\n slotProps[slotName] = (\n <NodesRenderer\n nodes={children}\n config={config}\n viewports={viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n );\n }\n\n // Cascade design to the active viewport, then resolve DesignToken envelopes.\n // Published on context for useDesignValues() — never spread onto props.\n const resolvedDesign = resolveDesignProperties(node.props.design, viewports, activeViewportIndex);\n const { props: tokenResolvedDesign, unresolved } = applyTokenResolver(\n resolvedDesign,\n config.resolveToken\n );\n if (unresolved.length && typeof console !== 'undefined') {\n console.warn(\n `[@contentful/experiences-react] resolveToken returned undefined for token id(s) on \"${componentTypeId}\": ${unresolved.join(', ')}. useDesignValues() will omit those keys.`\n );\n }\n\n const contentful: ContentfulComponent = {\n componentTypeId,\n nodeId: node.nodeId,\n content: node.props.content,\n design: node.props.design,\n resolved: node.props.resolved,\n };\n\n // Merge precedence (last wins): defaults < content < resolveData < slots.\n const composed = {\n ...componentConfig.defaults,\n ...node.props.content,\n ...node.props.resolved,\n ...slotProps,\n };\n\n return (\n <ContentfulComponentProvider value={contentful}>\n <ResolvedDesignProvider value={tokenResolvedDesign}>\n {createElement(componentConfig.component, composed)}\n </ResolvedDesignProvider>\n </ContentfulComponentProvider>\n );\n}\n\nexport interface WrapWithTemplateProps {\n template: PortableTemplate | undefined;\n config: Config;\n viewports: ViewportDef[];\n activeViewportIndex: number;\n children: ReactNode;\n}\n\n/**\n * Wraps the rendered nodes with the page-level template. If the template is\n * referenced but unregistered, warns once and renders children unwrapped.\n */\nexport function WrapWithTemplate({\n template,\n config,\n viewports,\n activeViewportIndex,\n children,\n}: WrapWithTemplateProps): ReactNode {\n if (!template) return <Fragment>{children}</Fragment>;\n const entry = config.templates?.[template.templateId];\n if (!entry) {\n if (typeof console !== 'undefined') {\n console.warn(\n `[@contentful/experiences-react] No template registered for id \"${template.templateId}\". Rendering nodes without the template wrapper.`\n );\n }\n return <Fragment>{children}</Fragment>;\n }\n const templateConfig = normalizeTemplateRegistration(entry);\n\n const resolvedDesign = resolveDesignProperties(\n template.props.design,\n viewports,\n activeViewportIndex\n );\n\n const { props: tokenResolvedDesign, unresolved } = applyTokenResolver(\n resolvedDesign,\n config.resolveToken\n );\n if (unresolved.length && typeof console !== 'undefined') {\n console.warn(\n `[@contentful/experiences-react] resolveToken returned undefined for token id(s) on template \"${template.templateId}\": ${unresolved.join(', ')}. useDesignValues() will omit those keys.`\n );\n }\n\n const contentful: ContentfulTemplate = {\n templateId: template.templateId,\n content: template.props.content,\n design: template.props.design,\n resolved: template.props.resolved,\n };\n\n const composed = {\n ...templateConfig.defaults,\n ...template.props.content,\n ...template.props.resolved,\n children,\n };\n\n return (\n <ContentfulTemplateProvider value={contentful}>\n <ResolvedDesignProvider value={tokenResolvedDesign}>\n {createElement(templateConfig.component, composed)}\n </ResolvedDesignProvider>\n </ContentfulTemplateProvider>\n );\n}\n"],"mappings":"AAsDQ;AAhDR,SAAS,UAAU,qBAAqC;AAOxD,SAAS,oBAAoB,+BAA+B;AAE5D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAgBA,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAkC;AAChC,MAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,SACE,oBAAC,YACE,gBAAM,IAAI,CAAC,MAAM,UAChB;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IALK,KAAK,UAAU;AAAA,EAMtB,CACD,GACH;AAEJ;AAUA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,EAAE,gBAAgB,IAAI,KAAK;AACjC,QAAM,QAAQ,OAAO,WAAW,eAAe;AAC/C,MAAI,CAAC,OAAO;AACV,WAAO,cAAc,eAAe,EAAE,iBAAiB,QAAQ,KAAK,OAAO,CAAC;AAAA,EAC9E;AACA,QAAM,kBAAkB,+BAA+B,KAAK;AAG5D,QAAM,YAAuC,CAAC;AAC9C,aAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,KAAK,KAAK,GAAG;AAC7D,cAAU,QAAQ,IAChB;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,EAEJ;AAIA,QAAM,iBAAiB,wBAAwB,KAAK,MAAM,QAAQ,WAAW,mBAAmB;AAChG,QAAM,EAAE,OAAO,qBAAqB,WAAW,IAAI;AAAA,IACjD;AAAA,IACA,OAAO;AAAA,EACT;AACA,MAAI,WAAW,UAAU,OAAO,YAAY,aAAa;AACvD,YAAQ;AAAA,MACN,uFAAuF,eAAe,MAAM,WAAW,KAAK,IAAI,CAAC;AAAA,IACnI;AAAA,EACF;AAEA,QAAM,aAAkC;AAAA,IACtC;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,SAAS,KAAK,MAAM;AAAA,IACpB,QAAQ,KAAK,MAAM;AAAA,IACnB,UAAU,KAAK,MAAM;AAAA,EACvB;AAGA,QAAM,WAAW;AAAA,IACf,GAAG,gBAAgB;AAAA,IACnB,GAAG,KAAK,MAAM;AAAA,IACd,GAAG,KAAK,MAAM;AAAA,IACd,GAAG;AAAA,EACL;AAEA,SACE,oBAAC,+BAA4B,OAAO,YAClC,8BAAC,0BAAuB,OAAO,qBAC5B,wBAAc,gBAAgB,WAAW,QAAQ,GACpD,GACF;AAEJ;AAcO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqC;AACnC,MAAI,CAAC,SAAU,QAAO,oBAAC,YAAU,UAAS;AAC1C,QAAM,QAAQ,OAAO,YAAY,SAAS,UAAU;AACpD,MAAI,CAAC,OAAO;AACV,QAAI,OAAO,YAAY,aAAa;AAClC,cAAQ;AAAA,QACN,kEAAkE,SAAS,UAAU;AAAA,MACvF;AAAA,IACF;AACA,WAAO,oBAAC,YAAU,UAAS;AAAA,EAC7B;AACA,QAAM,iBAAiB,8BAA8B,KAAK;AAE1D,QAAM,iBAAiB;AAAA,IACrB,SAAS,MAAM;AAAA,IACf;AAAA,IACA;AAAA,EACF;AAEA,QAAM,EAAE,OAAO,qBAAqB,WAAW,IAAI;AAAA,IACjD;AAAA,IACA,OAAO;AAAA,EACT;AACA,MAAI,WAAW,UAAU,OAAO,YAAY,aAAa;AACvD,YAAQ;AAAA,MACN,gGAAgG,SAAS,UAAU,MAAM,WAAW,KAAK,IAAI,CAAC;AAAA,IAChJ;AAAA,EACF;AAEA,QAAM,aAAiC;AAAA,IACrC,YAAY,SAAS;AAAA,IACrB,SAAS,SAAS,MAAM;AAAA,IACxB,QAAQ,SAAS,MAAM;AAAA,IACvB,UAAU,SAAS,MAAM;AAAA,EAC3B;AAEA,QAAM,WAAW;AAAA,IACf,GAAG,eAAe;AAAA,IAClB,GAAG,SAAS,MAAM;AAAA,IAClB,GAAG,SAAS,MAAM;AAAA,IAClB;AAAA,EACF;AAEA,SACE,oBAAC,8BAA2B,OAAO,YACjC,8BAAC,0BAAuB,OAAO,qBAC5B,wBAAc,eAAe,WAAW,QAAQ,GACnD,GACF;AAEJ;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { PortableRenderPlan, ExperienceContext } from '@contentful/experiences-core';
|
|
2
|
+
import { PortableRenderPlan, ExperienceContext } from '@contentful/experiences-sdk-core';
|
|
3
3
|
import { RenderUnknown } from './nodes-renderer.js';
|
|
4
4
|
import { Config } from './types.js';
|
|
5
5
|
import './missing-component.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server-renderer.tsx"],"sourcesContent":["/*\n * Server-safe Experience renderer. Resolves the active viewport once from\n * `initialViewportId` and renders without any reactive subscription.\n */\n\nimport type { ReactNode } from 'react';\n\nimport type {\n ExperienceContext,\n PortableRenderPlan,\n ViewportDef,\n} from '@contentful/experiences-core';\nimport { getViewportIndex } from '@contentful/experiences-design';\n\nimport { ExperienceProvider } from './context';\nimport { MissingComponent } from './missing-component';\nimport { NodesRenderer, WrapWithTemplate, type RenderUnknown } from './nodes-renderer';\nimport type { Config, RenderContext } from './types';\n\nconst DEFAULT_CONTEXT: ExperienceContext = {\n isPreview: false,\n metadata: {},\n viewports: [],\n};\n\n// Keeps `activeViewport` non-null when a payload declares no viewports.\nconst FALLBACK_VIEWPORT: ViewportDef = {\n id: '_',\n query: '*',\n displayName: 'Default',\n previewSize: '100%',\n};\n\nexport interface ServerExperienceRendererProps {\n experience: PortableRenderPlan | null | undefined;\n config: Config;\n /** Initial viewport seed (e.g. derived from User-Agent). Defaults to viewport[0]. */\n initialViewportId?: string;\n /** Per-render context shallow-merged onto defaults. */\n context?: Partial<ExperienceContext>;\n /** Override the fallback rendered for unregistered component types. */\n renderUnknown?: RenderUnknown;\n}\n\nexport function ServerExperienceRenderer({\n experience,\n config,\n initialViewportId,\n context,\n renderUnknown = MissingComponent,\n}: ServerExperienceRendererProps): ReactNode {\n if (!experience) return null;\n\n const activeViewportIndex = getViewportIndex(experience.viewports, initialViewportId);\n\n // Copy viewports/activeViewport so the context (serialized + frozen by RSC)\n // shares no object identity with the plan arrays the renderers read below —\n // a shared reference makes Flight back-patch into frozen props and throw.\n const contextViewports = experience.viewports.map((v) => ({ ...v }));\n const activeViewport = { ...(experience.viewports[activeViewportIndex] ?? FALLBACK_VIEWPORT) };\n\n const renderContext: RenderContext = {\n ...DEFAULT_CONTEXT,\n ...context,\n metadata: { ...DEFAULT_CONTEXT.metadata, ...(context?.metadata ?? {}) },\n viewports: contextViewports,\n activeViewport,\n activeViewportIndex,\n };\n\n return (\n <ExperienceProvider value={renderContext}>\n <WrapWithTemplate\n template={experience.template}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n >\n <NodesRenderer\n nodes={experience.nodes}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n </WrapWithTemplate>\n </ExperienceProvider>\n );\n}\n"],"mappings":"AA8EQ;AAlER,SAAS,wBAAwB;AAEjC,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,eAAe,wBAA4C;AAGpE,MAAM,kBAAqC;AAAA,EACzC,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,WAAW,CAAC;AACd;AAGA,MAAM,oBAAiC;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,aAAa;AACf;AAaO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAA6C;AAC3C,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,sBAAsB,iBAAiB,WAAW,WAAW,iBAAiB;AAKpF,QAAM,mBAAmB,WAAW,UAAU,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AACnE,QAAM,iBAAiB,EAAE,GAAI,WAAW,UAAU,mBAAmB,KAAK,kBAAmB;AAE7F,QAAM,gBAA+B;AAAA,IACnC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU,EAAE,GAAG,gBAAgB,UAAU,GAAI,SAAS,YAAY,CAAC,EAAG;AAAA,IACtE,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,sBAAmB,OAAO,eACzB;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,WAAW;AAAA,MACrB;AAAA,MACA,WAAW,WAAW;AAAA,MACtB;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,WAAW;AAAA,UAClB;AAAA,UACA,WAAW,WAAW;AAAA,UACtB;AAAA,UACA;AAAA;AAAA,MACF;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server-renderer.tsx"],"sourcesContent":["/*\n * Server-safe Experience renderer. Resolves the active viewport once from\n * `initialViewportId` and renders without any reactive subscription.\n */\n\nimport type { ReactNode } from 'react';\n\nimport type {\n ExperienceContext,\n PortableRenderPlan,\n ViewportDef,\n} from '@contentful/experiences-sdk-core';\nimport { getViewportIndex } from '@contentful/experiences-design';\n\nimport { ExperienceProvider } from './context';\nimport { MissingComponent } from './missing-component';\nimport { NodesRenderer, WrapWithTemplate, type RenderUnknown } from './nodes-renderer';\nimport type { Config, RenderContext } from './types';\n\nconst DEFAULT_CONTEXT: ExperienceContext = {\n isPreview: false,\n metadata: {},\n viewports: [],\n};\n\n// Keeps `activeViewport` non-null when a payload declares no viewports.\nconst FALLBACK_VIEWPORT: ViewportDef = {\n id: '_',\n query: '*',\n displayName: 'Default',\n previewSize: '100%',\n};\n\nexport interface ServerExperienceRendererProps {\n experience: PortableRenderPlan | null | undefined;\n config: Config;\n /** Initial viewport seed (e.g. derived from User-Agent). Defaults to viewport[0]. */\n initialViewportId?: string;\n /** Per-render context shallow-merged onto defaults. */\n context?: Partial<ExperienceContext>;\n /** Override the fallback rendered for unregistered component types. */\n renderUnknown?: RenderUnknown;\n}\n\nexport function ServerExperienceRenderer({\n experience,\n config,\n initialViewportId,\n context,\n renderUnknown = MissingComponent,\n}: ServerExperienceRendererProps): ReactNode {\n if (!experience) return null;\n\n const activeViewportIndex = getViewportIndex(experience.viewports, initialViewportId);\n\n // Copy viewports/activeViewport so the context (serialized + frozen by RSC)\n // shares no object identity with the plan arrays the renderers read below —\n // a shared reference makes Flight back-patch into frozen props and throw.\n const contextViewports = experience.viewports.map((v) => ({ ...v }));\n const activeViewport = { ...(experience.viewports[activeViewportIndex] ?? FALLBACK_VIEWPORT) };\n\n const renderContext: RenderContext = {\n ...DEFAULT_CONTEXT,\n ...context,\n metadata: { ...DEFAULT_CONTEXT.metadata, ...(context?.metadata ?? {}) },\n viewports: contextViewports,\n activeViewport,\n activeViewportIndex,\n };\n\n return (\n <ExperienceProvider value={renderContext}>\n <WrapWithTemplate\n template={experience.template}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n >\n <NodesRenderer\n nodes={experience.nodes}\n config={config}\n viewports={experience.viewports}\n activeViewportIndex={activeViewportIndex}\n renderUnknown={renderUnknown}\n />\n </WrapWithTemplate>\n </ExperienceProvider>\n );\n}\n"],"mappings":"AA8EQ;AAlER,SAAS,wBAAwB;AAEjC,SAAS,0BAA0B;AACnC,SAAS,wBAAwB;AACjC,SAAS,eAAe,wBAA4C;AAGpE,MAAM,kBAAqC;AAAA,EACzC,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,WAAW,CAAC;AACd;AAGA,MAAM,oBAAiC;AAAA,EACrC,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,aAAa;AACf;AAaO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAA6C;AAC3C,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,sBAAsB,iBAAiB,WAAW,WAAW,iBAAiB;AAKpF,QAAM,mBAAmB,WAAW,UAAU,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;AACnE,QAAM,iBAAiB,EAAE,GAAI,WAAW,UAAU,mBAAmB,KAAK,kBAAmB;AAE7F,QAAM,gBAA+B;AAAA,IACnC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU,EAAE,GAAG,gBAAgB,UAAU,GAAI,SAAS,YAAY,CAAC,EAAG;AAAA,IACtE,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,sBAAmB,OAAO,eACzB;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,WAAW;AAAA,MACrB;AAAA,MACA,WAAW,WAAW;AAAA,MACtB;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,WAAW;AAAA,UAClB;AAAA,UACA,WAAW,WAAW;AAAA,UACtB;AAAA,UACA;AAAA;AAAA,MACF;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ComponentType, ReactNode } from 'react';
|
|
2
|
-
import { ResolveContext, ResolveToken, DesignPropValue, ExperienceContext, ViewportDef } from '@contentful/experiences-core';
|
|
3
|
-
export { ResolveContext, ResolveToken } from '@contentful/experiences-core';
|
|
2
|
+
import { ResolveContext, ResolveToken, DesignPropValue, ExperienceContext, ViewportDef } from '@contentful/experiences-sdk-core';
|
|
3
|
+
export { ResolveContext, ResolveToken } from '@contentful/experiences-sdk-core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The full Contentful-side payload for a single component instance, exposed
|
|
@@ -116,7 +116,7 @@ interface Config {
|
|
|
116
116
|
/**
|
|
117
117
|
* Resolves `DesignToken` envelopes to runtime values before they reach a
|
|
118
118
|
* component. If omitted, envelopes pass through unchanged. See `ResolveToken`
|
|
119
|
-
* in `@contentful/experiences-core` for the full contract.
|
|
119
|
+
* in `@contentful/experiences-sdk-core` for the full contract.
|
|
120
120
|
*/
|
|
121
121
|
resolveToken?: ResolveToken;
|
|
122
122
|
}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\n\nimport type {\n DesignPropValue,\n ExperienceContext,\n ResolveContext,\n ResolveToken,\n ViewportDef,\n} from '@contentful/experiences-core';\n\n/**\n * Re-exported for ergonomics: customer code authoring `resolveData` doesn't\n * have to dig into the core package for the context type.\n */\nexport type { ResolveContext, ResolveToken };\n\n/**\n * The full Contentful-side payload for a single component instance, exposed\n * via `useContentfulComponent()` to any descendant of a rendered Experience\n * node. Useful for custom design-property resolution outside the SDK's\n * cascade, branching by `componentTypeId` in a generic wrapper, keying\n * analytics on `nodeId`, or rendering a raw-payload panel in preview.\n *\n * Design properties stay in their **raw envelope form** here (the same shape\n * `ctx.design` carries inside `resolveData`). The viewport-cascaded, token-\n * resolved values are what `useDesignValues()` returns.\n */\nexport interface ContentfulComponent {\n componentTypeId: string;\n nodeId?: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n}\n\n/**\n * Same shape as `ContentfulComponent`, but for the page-level template.\n * Exposed via `useContentfulTemplate()` inside a template's component tree.\n */\nexport interface ContentfulTemplate {\n templateId: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n}\n\n/**\n * Render-time experience context. Extends the core `ExperienceContext` with\n * the active viewport — a render-time value that resolvers cannot see (they\n * run once before viewport resolution and are not re-triggered by viewport\n * changes). Exposed via `useExperience()` to any descendant of the renderer.\n */\nexport interface RenderContext extends ExperienceContext {\n activeViewport: ViewportDef;\n activeViewportIndex: number;\n}\n\n/**\n * Customer-supplied configuration for a single component type. The `component`\n * receives the merged prop bag (content + resolveData + slots). Design values\n * are not injected — a component reads them via `useDesignValues()`. Runtime\n * context and raw payload come from `useExperience()` / `useContentfulComponent()`.\n */\nexport interface ComponentConfig<Props extends object = Record<string, unknown>> {\n /**\n * Lowest-precedence prop bag. Merged in before content / resolveData /\n * slots. Useful for variant fallbacks the editorial layer doesn't always supply.\n */\n defaults?: Partial<Props>;\n /**\n * Optional sync-or-async hook that derives final props from the raw\n * Experience inputs. Runs once during `resolveExperience`, before render —\n * does NOT re-fire on viewport changes.\n */\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n /**\n * The React component to render. Receives the merged prop bag as its props.\n */\n component: ComponentType<Props>;\n}\n\n/**\n * Registry value. Register a bare React component for the common case, or\n * the full `ComponentConfig` shape when you need defaults or a `resolveData`\n * hook.\n *\n * button: Button, // bare\n * header: { component: Header, defaults: {...} }, // with defaults\n * card: defineComponent<CardProps>({ component: Card, resolveData: ... }),\n */\nexport type Registration<Props extends object = Record<string, unknown>> =\n | ComponentType<Props>\n | ComponentConfig<Props>;\n\n/**\n * Customer-supplied configuration for a page-level template. The template's\n * component receives the rendered Experience nodes as `children`.\n */\nexport interface TemplateConfig<Props extends object = Record<string, unknown>> {\n defaults?: Partial<Props>;\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n component: ComponentType<Props & { children?: ReactNode }>;\n}\n\n/**\n * Registry value for templates. Same dual-shape as component registrations.\n */\nexport type TemplateRegistration<Props extends object = Record<string, unknown>> =\n | ComponentType<Props & { children?: ReactNode }>\n | TemplateConfig<Props>;\n\n/**\n * Identity helper — returns the config as-is, but narrows the `resolveData`\n * and `component` parameter types to your declared `Props`.\n */\nexport function defineComponent<Props extends object = Record<string, unknown>>(\n config: ComponentConfig<Props>\n): ComponentConfig<Props> {\n return config;\n}\n\n/**\n * Identity helper — returns the template config as-is, with `Props` narrowing.\n */\nexport function defineTemplate<Props extends object = Record<string, unknown>>(\n config: TemplateConfig<Props>\n): TemplateConfig<Props> {\n return config;\n}\n\n/**\n * Component registry — keyed by `componentTypeId` (last slash-segment of\n * `componentType.sys.urn`). Per-entry prop narrowing happens at\n * `defineComponent<Props>` or at the bare-component's own type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type Components = Record<string, Registration<any>>;\n\n/**\n * Template registry — keyed by `templateId` (last slash-segment of\n * `payload.sys.template.sys.urn`).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type Templates = Record<string, TemplateRegistration<any>>;\n\nexport interface Config {\n components: Components;\n templates?: Templates;\n /**\n * Resolves `DesignToken` envelopes to runtime values before they reach a\n * component. If omitted, envelopes pass through unchanged. See `ResolveToken`\n * in `@contentful/experiences-core` for the full contract.\n */\n resolveToken?: ResolveToken;\n}\n\n/**\n * Normalize a registry entry — bare component OR config object — into the\n * common `ComponentConfig` shape used by the renderer.\n *\n * React function components are plain functions; `React.memo` /\n * `React.forwardRef` yield objects carrying a `$$typeof` symbol. Both count\n * as bare components; a config object is discriminated by having a\n * `component` field and no `$$typeof`.\n */\nexport function normalizeComponentRegistration<P extends object>(\n reg: Registration<P>\n): ComponentConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as ComponentConfig<P>;\n }\n return { component: reg as ComponentType<P> };\n}\n\nexport function normalizeTemplateRegistration<P extends object>(\n reg: TemplateRegistration<P>\n): TemplateConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as TemplateConfig<P>;\n }\n return { component: reg as ComponentType<P & { children?: ReactNode }> };\n}\n"],"mappings":"AAmHO,SAAS,gBACd,QACwB;AACxB,SAAO;AACT;AAKO,SAAS,eACd,QACuB;AACvB,SAAO;AACT;AAqCO,SAAS,+BACd,KACoB;AACpB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAwB;AAC9C;AAEO,SAAS,8BACd,KACmB;AACnB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAmD;AACzE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ComponentType, ReactNode } from 'react';\n\nimport type {\n DesignPropValue,\n ExperienceContext,\n ResolveContext,\n ResolveToken,\n ViewportDef,\n} from '@contentful/experiences-sdk-core';\n\n/**\n * Re-exported for ergonomics: customer code authoring `resolveData` doesn't\n * have to dig into the core package for the context type.\n */\nexport type { ResolveContext, ResolveToken };\n\n/**\n * The full Contentful-side payload for a single component instance, exposed\n * via `useContentfulComponent()` to any descendant of a rendered Experience\n * node. Useful for custom design-property resolution outside the SDK's\n * cascade, branching by `componentTypeId` in a generic wrapper, keying\n * analytics on `nodeId`, or rendering a raw-payload panel in preview.\n *\n * Design properties stay in their **raw envelope form** here (the same shape\n * `ctx.design` carries inside `resolveData`). The viewport-cascaded, token-\n * resolved values are what `useDesignValues()` returns.\n */\nexport interface ContentfulComponent {\n componentTypeId: string;\n nodeId?: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n}\n\n/**\n * Same shape as `ContentfulComponent`, but for the page-level template.\n * Exposed via `useContentfulTemplate()` inside a template's component tree.\n */\nexport interface ContentfulTemplate {\n templateId: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n}\n\n/**\n * Render-time experience context. Extends the core `ExperienceContext` with\n * the active viewport — a render-time value that resolvers cannot see (they\n * run once before viewport resolution and are not re-triggered by viewport\n * changes). Exposed via `useExperience()` to any descendant of the renderer.\n */\nexport interface RenderContext extends ExperienceContext {\n activeViewport: ViewportDef;\n activeViewportIndex: number;\n}\n\n/**\n * Customer-supplied configuration for a single component type. The `component`\n * receives the merged prop bag (content + resolveData + slots). Design values\n * are not injected — a component reads them via `useDesignValues()`. Runtime\n * context and raw payload come from `useExperience()` / `useContentfulComponent()`.\n */\nexport interface ComponentConfig<Props extends object = Record<string, unknown>> {\n /**\n * Lowest-precedence prop bag. Merged in before content / resolveData /\n * slots. Useful for variant fallbacks the editorial layer doesn't always supply.\n */\n defaults?: Partial<Props>;\n /**\n * Optional sync-or-async hook that derives final props from the raw\n * Experience inputs. Runs once during `resolveExperience`, before render —\n * does NOT re-fire on viewport changes.\n */\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n /**\n * The React component to render. Receives the merged prop bag as its props.\n */\n component: ComponentType<Props>;\n}\n\n/**\n * Registry value. Register a bare React component for the common case, or\n * the full `ComponentConfig` shape when you need defaults or a `resolveData`\n * hook.\n *\n * button: Button, // bare\n * header: { component: Header, defaults: {...} }, // with defaults\n * card: defineComponent<CardProps>({ component: Card, resolveData: ... }),\n */\nexport type Registration<Props extends object = Record<string, unknown>> =\n | ComponentType<Props>\n | ComponentConfig<Props>;\n\n/**\n * Customer-supplied configuration for a page-level template. The template's\n * component receives the rendered Experience nodes as `children`.\n */\nexport interface TemplateConfig<Props extends object = Record<string, unknown>> {\n defaults?: Partial<Props>;\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n component: ComponentType<Props & { children?: ReactNode }>;\n}\n\n/**\n * Registry value for templates. Same dual-shape as component registrations.\n */\nexport type TemplateRegistration<Props extends object = Record<string, unknown>> =\n | ComponentType<Props & { children?: ReactNode }>\n | TemplateConfig<Props>;\n\n/**\n * Identity helper — returns the config as-is, but narrows the `resolveData`\n * and `component` parameter types to your declared `Props`.\n */\nexport function defineComponent<Props extends object = Record<string, unknown>>(\n config: ComponentConfig<Props>\n): ComponentConfig<Props> {\n return config;\n}\n\n/**\n * Identity helper — returns the template config as-is, with `Props` narrowing.\n */\nexport function defineTemplate<Props extends object = Record<string, unknown>>(\n config: TemplateConfig<Props>\n): TemplateConfig<Props> {\n return config;\n}\n\n/**\n * Component registry — keyed by `componentTypeId` (last slash-segment of\n * `componentType.sys.urn`). Per-entry prop narrowing happens at\n * `defineComponent<Props>` or at the bare-component's own type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type Components = Record<string, Registration<any>>;\n\n/**\n * Template registry — keyed by `templateId` (last slash-segment of\n * `payload.sys.template.sys.urn`).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type Templates = Record<string, TemplateRegistration<any>>;\n\nexport interface Config {\n components: Components;\n templates?: Templates;\n /**\n * Resolves `DesignToken` envelopes to runtime values before they reach a\n * component. If omitted, envelopes pass through unchanged. See `ResolveToken`\n * in `@contentful/experiences-sdk-core` for the full contract.\n */\n resolveToken?: ResolveToken;\n}\n\n/**\n * Normalize a registry entry — bare component OR config object — into the\n * common `ComponentConfig` shape used by the renderer.\n *\n * React function components are plain functions; `React.memo` /\n * `React.forwardRef` yield objects carrying a `$$typeof` symbol. Both count\n * as bare components; a config object is discriminated by having a\n * `component` field and no `$$typeof`.\n */\nexport function normalizeComponentRegistration<P extends object>(\n reg: Registration<P>\n): ComponentConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as ComponentConfig<P>;\n }\n return { component: reg as ComponentType<P> };\n}\n\nexport function normalizeTemplateRegistration<P extends object>(\n reg: TemplateRegistration<P>\n): TemplateConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as TemplateConfig<P>;\n }\n return { component: reg as ComponentType<P & { children?: ReactNode }> };\n}\n"],"mappings":"AAmHO,SAAS,gBACd,QACwB;AACxB,SAAO;AACT;AAKO,SAAS,eACd,QACuB;AACvB,SAAO;AACT;AAqCO,SAAS,+BACd,KACoB;AACpB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAwB;AAC9C;AAEO,SAAS,8BACd,KACmB;AACnB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAmD;AACzE;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/use-active-viewport.ts"],"sourcesContent":["/*\n * Reactive viewport-matcher hook.\n *\n * \"Active viewport\" = the last-matching media query in the ordered viewports\n * list. With desktop-first ordering (`*`, `<992px`, `<576px`), the narrowest\n * matching query wins. With mobile-first ordering, the widest wins. Either\n * way: the last `findLastIndex` of `mediaQueryMatches[i] === true`.\n *\n * Pass `initialViewportId` (typically derived from User-Agent on the server)\n * to seed the first render so SSR + client agree on viewport before\n * window.matchMedia takes over.\n */\n\n'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport type { ViewportDef } from '@contentful/experiences-core';\nimport { getViewportIndex, toCssMediaQuery } from '@contentful/experiences-design';\n\ninterface MediaQueryMatcher {\n index: number;\n signal: MediaQueryList;\n}\n\nfunction createMediaQueryMatchers(viewports: ViewportDef[]): [MediaQueryMatcher[], boolean[]] {\n const mediaQueryMatches: boolean[] = new Array(viewports.length).fill(false);\n // The first viewport is the wildcard \"*\" and always matches.\n mediaQueryMatches[0] = true;\n\n const mediaQueryMatchers: MediaQueryMatcher[] = [];\n if (typeof window === 'undefined') {\n return [mediaQueryMatchers, mediaQueryMatches];\n }\n for (let index = 0; index < viewports.length; index++) {\n const viewport = viewports[index];\n if (!viewport) continue;\n const cssQuery = toCssMediaQuery(viewport);\n if (!cssQuery) continue;\n const matcher = window.matchMedia(cssQuery);\n mediaQueryMatches[index] = matcher.matches;\n mediaQueryMatchers.push({ index, signal: matcher });\n }\n return [mediaQueryMatchers, mediaQueryMatches];\n}\n\nfunction useMediaQueryMatchers(viewports: ViewportDef[]): boolean[] {\n const [, initialMatches] = createMediaQueryMatchers(viewports);\n const [matches, setMatches] = useState<boolean[]>(initialMatches);\n\n useEffect(() => {\n const [matchers] = createMediaQueryMatchers(viewports);\n const listeners = matchers.map(({ index, signal }) => {\n const onChange = (): void =>\n setMatches((prev) => {\n const next = [...prev];\n next[index] = signal.matches;\n return next;\n });\n signal.addEventListener('change', onChange);\n return onChange;\n });\n return () => {\n listeners.forEach((listener, i) => {\n const matcher = matchers[i];\n if (matcher) matcher.signal.removeEventListener('change', listener);\n });\n };\n }, [viewports]);\n\n return matches;\n}\n\nexport interface UseActiveViewportResult {\n activeViewportIndex: number;\n}\n\nexport function useActiveViewport(\n viewports: ViewportDef[],\n initialViewportId?: string\n): UseActiveViewportResult {\n const matches = useMediaQueryMatchers(viewports);\n const [activeViewportIndex, setActiveViewportIndex] = useState<number>(() =>\n getViewportIndex(viewports, initialViewportId)\n );\n\n useEffect(() => {\n const currentIndex = matches.findLastIndex((isMatch) => isMatch);\n if (currentIndex !== -1 && currentIndex !== activeViewportIndex) {\n setActiveViewportIndex(currentIndex);\n }\n }, [activeViewportIndex, matches]);\n\n return { activeViewportIndex };\n}\n"],"mappings":";AAeA,SAAS,WAAW,gBAAgB;AAGpC,SAAS,kBAAkB,uBAAuB;AAOlD,SAAS,yBAAyB,WAA4D;AAC5F,QAAM,oBAA+B,IAAI,MAAM,UAAU,MAAM,EAAE,KAAK,KAAK;AAE3E,oBAAkB,CAAC,IAAI;AAEvB,QAAM,qBAA0C,CAAC;AACjD,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,CAAC,oBAAoB,iBAAiB;AAAA,EAC/C;AACA,WAAS,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;AACrD,UAAM,WAAW,UAAU,KAAK;AAChC,QAAI,CAAC,SAAU;AACf,UAAM,WAAW,gBAAgB,QAAQ;AACzC,QAAI,CAAC,SAAU;AACf,UAAM,UAAU,OAAO,WAAW,QAAQ;AAC1C,sBAAkB,KAAK,IAAI,QAAQ;AACnC,uBAAmB,KAAK,EAAE,OAAO,QAAQ,QAAQ,CAAC;AAAA,EACpD;AACA,SAAO,CAAC,oBAAoB,iBAAiB;AAC/C;AAEA,SAAS,sBAAsB,WAAqC;AAClE,QAAM,CAAC,EAAE,cAAc,IAAI,yBAAyB,SAAS;AAC7D,QAAM,CAAC,SAAS,UAAU,IAAI,SAAoB,cAAc;AAEhE,YAAU,MAAM;AACd,UAAM,CAAC,QAAQ,IAAI,yBAAyB,SAAS;AACrD,UAAM,YAAY,SAAS,IAAI,CAAC,EAAE,OAAO,OAAO,MAAM;AACpD,YAAM,WAAW,MACf,WAAW,CAAC,SAAS;AACnB,cAAM,OAAO,CAAC,GAAG,IAAI;AACrB,aAAK,KAAK,IAAI,OAAO;AACrB,eAAO;AAAA,MACT,CAAC;AACH,aAAO,iBAAiB,UAAU,QAAQ;AAC1C,aAAO;AAAA,IACT,CAAC;AACD,WAAO,MAAM;AACX,gBAAU,QAAQ,CAAC,UAAU,MAAM;AACjC,cAAM,UAAU,SAAS,CAAC;AAC1B,YAAI,QAAS,SAAQ,OAAO,oBAAoB,UAAU,QAAQ;AAAA,MACpE,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO;AACT;AAMO,SAAS,kBACd,WACA,mBACyB;AACzB,QAAM,UAAU,sBAAsB,SAAS;AAC/C,QAAM,CAAC,qBAAqB,sBAAsB,IAAI;AAAA,IAAiB,MACrE,iBAAiB,WAAW,iBAAiB;AAAA,EAC/C;AAEA,YAAU,MAAM;AACd,UAAM,eAAe,QAAQ,cAAc,CAAC,YAAY,OAAO;AAC/D,QAAI,iBAAiB,MAAM,iBAAiB,qBAAqB;AAC/D,6BAAuB,YAAY;AAAA,IACrC;AAAA,EACF,GAAG,CAAC,qBAAqB,OAAO,CAAC;AAEjC,SAAO,EAAE,oBAAoB;AAC/B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/use-active-viewport.ts"],"sourcesContent":["/*\n * Reactive viewport-matcher hook.\n *\n * \"Active viewport\" = the last-matching media query in the ordered viewports\n * list. With desktop-first ordering (`*`, `<992px`, `<576px`), the narrowest\n * matching query wins. With mobile-first ordering, the widest wins. Either\n * way: the last `findLastIndex` of `mediaQueryMatches[i] === true`.\n *\n * Pass `initialViewportId` (typically derived from User-Agent on the server)\n * to seed the first render so SSR + client agree on viewport before\n * window.matchMedia takes over.\n */\n\n'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport type { ViewportDef } from '@contentful/experiences-sdk-core';\nimport { getViewportIndex, toCssMediaQuery } from '@contentful/experiences-design';\n\ninterface MediaQueryMatcher {\n index: number;\n signal: MediaQueryList;\n}\n\nfunction createMediaQueryMatchers(viewports: ViewportDef[]): [MediaQueryMatcher[], boolean[]] {\n const mediaQueryMatches: boolean[] = new Array(viewports.length).fill(false);\n // The first viewport is the wildcard \"*\" and always matches.\n mediaQueryMatches[0] = true;\n\n const mediaQueryMatchers: MediaQueryMatcher[] = [];\n if (typeof window === 'undefined') {\n return [mediaQueryMatchers, mediaQueryMatches];\n }\n for (let index = 0; index < viewports.length; index++) {\n const viewport = viewports[index];\n if (!viewport) continue;\n const cssQuery = toCssMediaQuery(viewport);\n if (!cssQuery) continue;\n const matcher = window.matchMedia(cssQuery);\n mediaQueryMatches[index] = matcher.matches;\n mediaQueryMatchers.push({ index, signal: matcher });\n }\n return [mediaQueryMatchers, mediaQueryMatches];\n}\n\nfunction useMediaQueryMatchers(viewports: ViewportDef[]): boolean[] {\n const [, initialMatches] = createMediaQueryMatchers(viewports);\n const [matches, setMatches] = useState<boolean[]>(initialMatches);\n\n useEffect(() => {\n const [matchers] = createMediaQueryMatchers(viewports);\n const listeners = matchers.map(({ index, signal }) => {\n const onChange = (): void =>\n setMatches((prev) => {\n const next = [...prev];\n next[index] = signal.matches;\n return next;\n });\n signal.addEventListener('change', onChange);\n return onChange;\n });\n return () => {\n listeners.forEach((listener, i) => {\n const matcher = matchers[i];\n if (matcher) matcher.signal.removeEventListener('change', listener);\n });\n };\n }, [viewports]);\n\n return matches;\n}\n\nexport interface UseActiveViewportResult {\n activeViewportIndex: number;\n}\n\nexport function useActiveViewport(\n viewports: ViewportDef[],\n initialViewportId?: string\n): UseActiveViewportResult {\n const matches = useMediaQueryMatchers(viewports);\n const [activeViewportIndex, setActiveViewportIndex] = useState<number>(() =>\n getViewportIndex(viewports, initialViewportId)\n );\n\n useEffect(() => {\n const currentIndex = matches.findLastIndex((isMatch) => isMatch);\n if (currentIndex !== -1 && currentIndex !== activeViewportIndex) {\n setActiveViewportIndex(currentIndex);\n }\n }, [activeViewportIndex, matches]);\n\n return { activeViewportIndex };\n}\n"],"mappings":";AAeA,SAAS,WAAW,gBAAgB;AAGpC,SAAS,kBAAkB,uBAAuB;AAOlD,SAAS,yBAAyB,WAA4D;AAC5F,QAAM,oBAA+B,IAAI,MAAM,UAAU,MAAM,EAAE,KAAK,KAAK;AAE3E,oBAAkB,CAAC,IAAI;AAEvB,QAAM,qBAA0C,CAAC;AACjD,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,CAAC,oBAAoB,iBAAiB;AAAA,EAC/C;AACA,WAAS,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;AACrD,UAAM,WAAW,UAAU,KAAK;AAChC,QAAI,CAAC,SAAU;AACf,UAAM,WAAW,gBAAgB,QAAQ;AACzC,QAAI,CAAC,SAAU;AACf,UAAM,UAAU,OAAO,WAAW,QAAQ;AAC1C,sBAAkB,KAAK,IAAI,QAAQ;AACnC,uBAAmB,KAAK,EAAE,OAAO,QAAQ,QAAQ,CAAC;AAAA,EACpD;AACA,SAAO,CAAC,oBAAoB,iBAAiB;AAC/C;AAEA,SAAS,sBAAsB,WAAqC;AAClE,QAAM,CAAC,EAAE,cAAc,IAAI,yBAAyB,SAAS;AAC7D,QAAM,CAAC,SAAS,UAAU,IAAI,SAAoB,cAAc;AAEhE,YAAU,MAAM;AACd,UAAM,CAAC,QAAQ,IAAI,yBAAyB,SAAS;AACrD,UAAM,YAAY,SAAS,IAAI,CAAC,EAAE,OAAO,OAAO,MAAM;AACpD,YAAM,WAAW,MACf,WAAW,CAAC,SAAS;AACnB,cAAM,OAAO,CAAC,GAAG,IAAI;AACrB,aAAK,KAAK,IAAI,OAAO;AACrB,eAAO;AAAA,MACT,CAAC;AACH,aAAO,iBAAiB,UAAU,QAAQ;AAC1C,aAAO;AAAA,IACT,CAAC;AACD,WAAO,MAAM;AACX,gBAAU,QAAQ,CAAC,UAAU,MAAM;AACjC,cAAM,UAAU,SAAS,CAAC;AAC1B,YAAI,QAAS,SAAQ,OAAO,oBAAoB,UAAU,QAAQ;AAAA,MACpE,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO;AACT;AAMO,SAAS,kBACd,WACA,mBACyB;AACzB,QAAM,UAAU,sBAAsB,SAAS;AAC/C,QAAM,CAAC,qBAAqB,sBAAsB,IAAI;AAAA,IAAiB,MACrE,iBAAiB,WAAW,iBAAiB;AAAA,EAC/C;AAEA,YAAU,MAAM;AACd,UAAM,eAAe,QAAQ,cAAc,CAAC,YAAY,OAAO;AAC/D,QAAI,iBAAiB,MAAM,iBAAiB,qBAAqB;AAC/D,6BAAuB,YAAY;AAAA,IACrC;AAAA,EACF,GAAG,CAAC,qBAAqB,OAAO,CAAC;AAEjC,SAAO,EAAE,oBAAoB;AAC/B;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experiences-react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "React adapter for Contentful Experiences — Server and Client renderers over a PortableRenderPlan",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"directory": "packages/adapter-react"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@contentful/experiences-client": "0.1.
|
|
34
|
-
"@contentful/experiences-core": "0.5.
|
|
35
|
-
"@contentful/experiences-design": "0.5.
|
|
33
|
+
"@contentful/experiences-client": "0.1.3",
|
|
34
|
+
"@contentful/experiences-sdk-core": "0.5.3",
|
|
35
|
+
"@contentful/experiences-design": "0.5.3"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"react": "^18.0.0 || ^19.0.0",
|