@se-studio/core-ui 1.19.9 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cmsRegistration.d.ts +16 -0
- package/dist/cmsRegistration.d.ts.map +1 -1
- package/dist/cmsRegistration.js +10 -0
- package/dist/cmsRegistration.js.map +1 -1
- package/dist/elements/TrackedLink.d.ts.map +1 -1
- package/dist/elements/TrackedLink.js +7 -15
- package/dist/elements/TrackedLink.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/route-handlers/createCmsTxtHandler.d.ts.map +1 -1
- package/dist/route-handlers/createCmsTxtHandler.js +4 -0
- package/dist/route-handlers/createCmsTxtHandler.js.map +1 -1
- package/dist/rtf/rtf.d.ts.map +1 -1
- package/dist/rtf/rtf.js +5 -2
- package/dist/rtf/rtf.js.map +1 -1
- package/dist/showcase/components/AllViewPanel.d.ts.map +1 -1
- package/dist/showcase/components/AllViewPanel.js +5 -2
- package/dist/showcase/components/AllViewPanel.js.map +1 -1
- package/dist/showcase/components/Controls.d.ts.map +1 -1
- package/dist/showcase/components/Controls.js +2 -5
- package/dist/showcase/components/Controls.js.map +1 -1
- package/dist/showcase/components/ControlsWrapper.d.ts +3 -1
- package/dist/showcase/components/ControlsWrapper.d.ts.map +1 -1
- package/dist/showcase/components/ControlsWrapper.js +2 -2
- package/dist/showcase/components/ControlsWrapper.js.map +1 -1
- package/dist/showcase/components/IframeHeightReporter.d.ts +6 -3
- package/dist/showcase/components/IframeHeightReporter.d.ts.map +1 -1
- package/dist/showcase/components/IframeHeightReporter.js +32 -5
- package/dist/showcase/components/IframeHeightReporter.js.map +1 -1
- package/dist/showcase/components/PreviewPanel.d.ts +6 -1
- package/dist/showcase/components/PreviewPanel.d.ts.map +1 -1
- package/dist/showcase/components/PreviewPanel.js +5 -3
- package/dist/showcase/components/PreviewPanel.js.map +1 -1
- package/dist/showcase/components/ScaledIframe.d.ts +12 -1
- package/dist/showcase/components/ScaledIframe.d.ts.map +1 -1
- package/dist/showcase/components/ScaledIframe.js +18 -11
- package/dist/showcase/components/ScaledIframe.js.map +1 -1
- package/dist/showcase/components/ShowcaseAllRenderPage.d.ts.map +1 -1
- package/dist/showcase/components/ShowcaseAllRenderPage.js +3 -2
- package/dist/showcase/components/ShowcaseAllRenderPage.js.map +1 -1
- package/dist/showcase/components/ShowcasePage.d.ts +6 -1
- package/dist/showcase/components/ShowcasePage.d.ts.map +1 -1
- package/dist/showcase/components/ShowcasePage.js +8 -2
- package/dist/showcase/components/ShowcasePage.js.map +1 -1
- package/dist/showcase/components/ShowcaseRenderPage.d.ts.map +1 -1
- package/dist/showcase/components/ShowcaseRenderPage.js +9 -8
- package/dist/showcase/components/ShowcaseRenderPage.js.map +1 -1
- package/dist/showcase/components/ShowcaseViewportGuard.d.ts +14 -0
- package/dist/showcase/components/ShowcaseViewportGuard.d.ts.map +1 -0
- package/dist/showcase/components/ShowcaseViewportGuard.js +77 -0
- package/dist/showcase/components/ShowcaseViewportGuard.js.map +1 -0
- package/dist/showcase/controlFieldSupport.d.ts +12 -0
- package/dist/showcase/controlFieldSupport.d.ts.map +1 -0
- package/dist/showcase/controlFieldSupport.js +35 -0
- package/dist/showcase/controlFieldSupport.js.map +1 -0
- package/dist/showcase/index.d.ts +7 -1
- package/dist/showcase/index.d.ts.map +1 -1
- package/dist/showcase/index.js +7 -1
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/isCmsShowcase.d.ts +10 -0
- package/dist/showcase/isCmsShowcase.d.ts.map +1 -0
- package/dist/showcase/isCmsShowcase.js +12 -0
- package/dist/showcase/isCmsShowcase.js.map +1 -0
- package/dist/showcase/mockFactory.d.ts +3 -0
- package/dist/showcase/mockFactory.d.ts.map +1 -1
- package/dist/showcase/mockFactory.js +133 -27
- package/dist/showcase/mockFactory.js.map +1 -1
- package/dist/showcase/showcaseLayout.d.ts +17 -0
- package/dist/showcase/showcaseLayout.d.ts.map +1 -0
- package/dist/showcase/showcaseLayout.js +11 -0
- package/dist/showcase/showcaseLayout.js.map +1 -0
- package/dist/showcase/showcaseRelatedContent.d.ts +30 -0
- package/dist/showcase/showcaseRelatedContent.d.ts.map +1 -0
- package/dist/showcase/showcaseRelatedContent.js +106 -0
- package/dist/showcase/showcaseRelatedContent.js.map +1 -0
- package/dist/utils/reactNodeText.d.ts +7 -0
- package/dist/utils/reactNodeText.d.ts.map +1 -0
- package/dist/utils/reactNodeText.js +43 -0
- package/dist/utils/reactNodeText.js.map +1 -0
- package/dist/utils/richTextNodeText.d.ts +13 -0
- package/dist/utils/richTextNodeText.d.ts.map +1 -0
- package/dist/utils/richTextNodeText.js +29 -0
- package/dist/utils/richTextNodeText.js.map +1 -0
- package/package.json +1 -1
- package/src/showcase/README.md +130 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Children, isValidElement } from 'react';
|
|
2
|
+
const DEFAULT_MAX_DEPTH = 8;
|
|
3
|
+
/**
|
|
4
|
+
* Recursively extract visible text from React children (strings, numbers, nested elements).
|
|
5
|
+
* Used by TrackedLink when CMS/RTF does not pass an explicit `linkText`.
|
|
6
|
+
*/
|
|
7
|
+
export function extractTextFromReactNode(node, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
walk(node, parts, 0, maxDepth);
|
|
10
|
+
return parts.join('').replace(/\s+/g, ' ').trim();
|
|
11
|
+
}
|
|
12
|
+
function walk(node, parts, depth, maxDepth) {
|
|
13
|
+
if (node == null || typeof node === 'boolean' || depth > maxDepth) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
17
|
+
parts.push(String(node));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(node)) {
|
|
21
|
+
for (const child of node) {
|
|
22
|
+
walk(child, parts, depth, maxDepth);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (isValidElement(node)) {
|
|
27
|
+
const props = node.props;
|
|
28
|
+
if (props?.children != null) {
|
|
29
|
+
walk(props.children, parts, depth + 1, maxDepth);
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Iterable children (e.g. some Fragment edge cases)
|
|
34
|
+
try {
|
|
35
|
+
Children.forEach(node, (child) => {
|
|
36
|
+
walk(child, parts, depth + 1, maxDepth);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// ignore non-children values
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=reactNodeText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactNodeText.js","sourceRoot":"","sources":["../../src/utils/reactNodeText.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAkB,MAAM,OAAO,CAAC;AAEjE,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAe,EACf,WAAmB,iBAAiB;IAEpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,IAAI,CAAC,IAAe,EAAE,KAAe,EAAE,KAAa,EAAE,QAAgB;IAC7E,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,SAAS,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;QAClE,OAAO;IACT,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzB,OAAO;IACT,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAiC,CAAC;QACrD,IAAI,KAAK,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QACnD,CAAC;QACD,OAAO;IACT,CAAC;IAED,oDAAoD;IACpD,IAAI,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,IAAiB,EAAE,CAAC,KAAK,EAAE,EAAE;YAC5C,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Block, Inline, Text } from '@contentful/rich-text-types';
|
|
2
|
+
type RichTextLikeNode = Block | Inline | Text | {
|
|
3
|
+
nodeType?: string;
|
|
4
|
+
value?: string;
|
|
5
|
+
content?: unknown[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Flatten plain text from a Contentful rich-text node (hyperlink, paragraph, etc.).
|
|
9
|
+
* Concatenates all `text` node values under `content`, ignoring marks and nested structure.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractRichTextNodeText(node: RichTextLikeNode | null | undefined): string;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=richTextNodeText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"richTextNodeText.d.ts","sourceRoot":"","sources":["../../src/utils/richTextNodeText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAEvE,KAAK,gBAAgB,GACjB,KAAK,GACL,MAAM,GACN,IAAI,GACJ;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAUzF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten plain text from a Contentful rich-text node (hyperlink, paragraph, etc.).
|
|
3
|
+
* Concatenates all `text` node values under `content`, ignoring marks and nested structure.
|
|
4
|
+
*/
|
|
5
|
+
export function extractRichTextNodeText(node) {
|
|
6
|
+
if (!node)
|
|
7
|
+
return '';
|
|
8
|
+
if (typeof node.value === 'string' && node.nodeType === 'text') {
|
|
9
|
+
return (node.value ?? '').trim();
|
|
10
|
+
}
|
|
11
|
+
const parts = [];
|
|
12
|
+
walk(node, parts);
|
|
13
|
+
return parts.join('').replace(/\s+/g, ' ').trim();
|
|
14
|
+
}
|
|
15
|
+
function walk(node, parts) {
|
|
16
|
+
if (!node || typeof node !== 'object')
|
|
17
|
+
return;
|
|
18
|
+
if (node.nodeType === 'text' && typeof node.value === 'string') {
|
|
19
|
+
parts.push(node.value);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const content = node.content;
|
|
23
|
+
if (!Array.isArray(content))
|
|
24
|
+
return;
|
|
25
|
+
for (const child of content) {
|
|
26
|
+
walk(child, parts);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=richTextNodeText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"richTextNodeText.js","sourceRoot":"","sources":["../../src/utils/richTextNodeText.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAyC;IAC/E,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAErB,IAAI,OAAQ,IAAa,CAAC,KAAK,KAAK,QAAQ,IAAK,IAAa,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QACnF,OAAO,CAAE,IAAa,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClB,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,IAAI,CAAC,IAAsB,EAAE,KAAe;IACnD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO;IAE9C,IAAK,IAAa,CAAC,QAAQ,KAAK,MAAM,IAAI,OAAQ,IAAa,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACnF,KAAK,CAAC,IAAI,CAAE,IAAa,CAAC,KAAK,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAI,IAAuB,CAAC,OAAO,CAAC;IACjD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO;IAEpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAyB,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
package/src/showcase/README.md
CHANGED
|
@@ -130,6 +130,136 @@ To add a CMS Showcase to a project:
|
|
|
130
130
|
};
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
## Query parameters
|
|
134
|
+
|
|
135
|
+
| Param | Effect |
|
|
136
|
+
|-------|--------|
|
|
137
|
+
| `type` | Component or collection type name |
|
|
138
|
+
| `mode` | `component` (default) or `collection` |
|
|
139
|
+
| `view` | `single` (default) or `all` |
|
|
140
|
+
| `controls` | `true` opens the controls panel on load (closed by default) |
|
|
141
|
+
| `clean` | `true` uses default control state only (no registration/curated mocks) |
|
|
142
|
+
| `debug` | `true` / `1` shows developer chrome (e.g. “fixed frame” labels on viewport layouts) |
|
|
143
|
+
| control keys | e.g. `heading`, `body`, `showVisual`, `backgroundColour` — override mock defaults |
|
|
144
|
+
|
|
145
|
+
Example: `/cms/showcase?type=Feature+Split&controls=true`
|
|
146
|
+
|
|
147
|
+
### Wiring `build*Maps` after this release
|
|
148
|
+
|
|
149
|
+
`buildComponentMaps` / `buildCollectionMaps` now also return:
|
|
150
|
+
|
|
151
|
+
- `componentShowcaseLayoutMap`
|
|
152
|
+
- `collectionShowcaseLayoutMap`
|
|
153
|
+
|
|
154
|
+
Existing destructures that only pick known keys keep working. To honour registration `showcaseLayout`, pass the maps into `ShowcasePage`:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const {
|
|
158
|
+
componentMap,
|
|
159
|
+
componentFieldsMap,
|
|
160
|
+
componentMockMap,
|
|
161
|
+
componentShowcaseLayoutMap,
|
|
162
|
+
excludedComponents,
|
|
163
|
+
// ...
|
|
164
|
+
} = buildComponentMaps(componentRegistrations);
|
|
165
|
+
|
|
166
|
+
const {
|
|
167
|
+
collectionMap,
|
|
168
|
+
collectionFieldsMap,
|
|
169
|
+
collectionMockMap,
|
|
170
|
+
collectionCardMockMap,
|
|
171
|
+
collectionCardFieldsMap,
|
|
172
|
+
collectionShowcaseLayoutMap,
|
|
173
|
+
excludedCollections,
|
|
174
|
+
} = buildCollectionMaps(collectionRegistrations);
|
|
175
|
+
|
|
176
|
+
// Showcase page:
|
|
177
|
+
<ShowcasePage
|
|
178
|
+
componentShowcaseLayoutMap={componentShowcaseLayoutMap}
|
|
179
|
+
collectionShowcaseLayoutMap={collectionShowcaseLayoutMap}
|
|
180
|
+
...
|
|
181
|
+
/>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
If you omit the maps, layout defaults to `document` (auto-height) for every type.
|
|
185
|
+
|
|
186
|
+
## Controls support mapping
|
|
187
|
+
|
|
188
|
+
The sidebar maps control keys to CMS `usedFields` (e.g. `showVisual` → `visual` / `backgroundVisual`, `showLinks` → `links`). Registration mocks may use plain string `body` / `additionalCopy`; the mock factory converts them to rich text. When a type uses `visual` but the mock has no image URL, a placehold.co placeholder is generated.
|
|
189
|
+
|
|
190
|
+
**Video URLs:** curated `visual.url` values that point at Vimeo/YouTube pages are turned into **External video** visuals (not `next/image` Pictures). Prefer image CDN URLs for posters; use video page URLs only for video modules.
|
|
191
|
+
|
|
192
|
+
## Iframe height behaviour
|
|
193
|
+
|
|
194
|
+
Render iframes report content height via `postMessage` from `#showcase-measure-root` so short components shrink below the initial frame size. Single previews cap at 900px. Viewport-unit heroes (`svh` / `70vh` / inline `100svh` styles) are capped inside the iframe by `ShowcaseViewportGuard` so All-view and full-screen collections cannot explode to multi-megapixel heights. Production pages are unaffected.
|
|
195
|
+
|
|
196
|
+
### `showcaseLayout` on registrations
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
defineComponent({
|
|
200
|
+
name: 'Dynamic hero',
|
|
201
|
+
showcaseLayout: 'compact', // document | viewport | compact
|
|
202
|
+
// ...
|
|
203
|
+
});
|
|
204
|
+
defineCollection({
|
|
205
|
+
name: 'Full Screen Slides',
|
|
206
|
+
showcaseLayout: 'viewport',
|
|
207
|
+
// ...
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
| Mode | Parent iframe |
|
|
212
|
+
|------|----------------|
|
|
213
|
+
| `document` (default) | Auto-grow to content height |
|
|
214
|
+
| `viewport` | Fixed frame (~640 mobile / ~720 desktop) + internal scroll |
|
|
215
|
+
| `compact` | Shorter fixed frame (~400 / ~480) for full-bleed heroes |
|
|
216
|
+
|
|
217
|
+
Pass maps from `buildComponentMaps` / `buildCollectionMaps` into `ShowcasePage`:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
const { componentShowcaseLayoutMap, ... } = buildComponentMaps(...);
|
|
221
|
+
const { collectionShowcaseLayoutMap, ... } = buildCollectionMaps(...);
|
|
222
|
+
|
|
223
|
+
<ShowcasePage
|
|
224
|
+
componentShowcaseLayoutMap={componentShowcaseLayoutMap}
|
|
225
|
+
collectionShowcaseLayoutMap={collectionShowcaseLayoutMap}
|
|
226
|
+
...
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Prefer component-level compact UIs** when `isCmsShowcase(contentContext)` is true (e.g. stack Full Screen Slides cards instead of sticky `100svh` scroll). The layout flag only controls the outer frame.
|
|
231
|
+
|
|
232
|
+
### Related articles / people collections
|
|
233
|
+
|
|
234
|
+
Types that call `fetchHelpers.getRelatedArticles` / `getRelatedPeople` will get **empty lists** from showcase mock `contents` (those are mock components, not CMS link entries). Use:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import {
|
|
238
|
+
isCmsShowcase,
|
|
239
|
+
articlesFromShowcaseContents,
|
|
240
|
+
peopleFromShowcaseContents,
|
|
241
|
+
} from '@se-studio/core-ui';
|
|
242
|
+
|
|
243
|
+
const articles =
|
|
244
|
+
result.data.length > 0
|
|
245
|
+
? result.data
|
|
246
|
+
: isCmsShowcase(contentContext)
|
|
247
|
+
? articlesFromShowcaseContents(information)
|
|
248
|
+
: result.data;
|
|
249
|
+
// articles is IArticleLink[] — type: 'Internal link', internalType: 'Article',
|
|
250
|
+
// with title, href, visuals[], lastModified, etc. suitable for CoreNewsCard-style UIs.
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Keep `cardUsedFields` broad enough that mock factory does not strip `heading` / `visual` from card mocks.
|
|
254
|
+
|
|
255
|
+
### Page-context components
|
|
256
|
+
|
|
257
|
+
Components that read `contentContext.pageContext.article` (e.g. poster download blocks) render nothing in showcase unless the site provides a showcase stub when `isCmsShowcase()`. Alternatively set `showcaseExclude: true`.
|
|
258
|
+
|
|
259
|
+
### View transitions on cms-dev
|
|
260
|
+
|
|
261
|
+
If the site uses `next-view-transitions` / `useTransitionRouter` on public routes, wrap **`(cms-dev)`** layout with `<ViewTransitions>` as well, or showcase collections that use those links will crash.
|
|
262
|
+
|
|
133
263
|
## Scroll-gated animations
|
|
134
264
|
|
|
135
265
|
Some projects pause entrance animations until `ClientMonitor` sets `data-seen` on `[data-component]` sections (e.g. `animate-when-seen` + `animate-text-in`). Showcase routes under `/cms/*` do not mount `ClientMonitor`.
|