@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.
Files changed (88) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cmsRegistration.d.ts +16 -0
  3. package/dist/cmsRegistration.d.ts.map +1 -1
  4. package/dist/cmsRegistration.js +10 -0
  5. package/dist/cmsRegistration.js.map +1 -1
  6. package/dist/elements/TrackedLink.d.ts.map +1 -1
  7. package/dist/elements/TrackedLink.js +7 -15
  8. package/dist/elements/TrackedLink.js.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/route-handlers/createCmsTxtHandler.d.ts.map +1 -1
  14. package/dist/route-handlers/createCmsTxtHandler.js +4 -0
  15. package/dist/route-handlers/createCmsTxtHandler.js.map +1 -1
  16. package/dist/rtf/rtf.d.ts.map +1 -1
  17. package/dist/rtf/rtf.js +5 -2
  18. package/dist/rtf/rtf.js.map +1 -1
  19. package/dist/showcase/components/AllViewPanel.d.ts.map +1 -1
  20. package/dist/showcase/components/AllViewPanel.js +5 -2
  21. package/dist/showcase/components/AllViewPanel.js.map +1 -1
  22. package/dist/showcase/components/Controls.d.ts.map +1 -1
  23. package/dist/showcase/components/Controls.js +2 -5
  24. package/dist/showcase/components/Controls.js.map +1 -1
  25. package/dist/showcase/components/ControlsWrapper.d.ts +3 -1
  26. package/dist/showcase/components/ControlsWrapper.d.ts.map +1 -1
  27. package/dist/showcase/components/ControlsWrapper.js +2 -2
  28. package/dist/showcase/components/ControlsWrapper.js.map +1 -1
  29. package/dist/showcase/components/IframeHeightReporter.d.ts +6 -3
  30. package/dist/showcase/components/IframeHeightReporter.d.ts.map +1 -1
  31. package/dist/showcase/components/IframeHeightReporter.js +32 -5
  32. package/dist/showcase/components/IframeHeightReporter.js.map +1 -1
  33. package/dist/showcase/components/PreviewPanel.d.ts +6 -1
  34. package/dist/showcase/components/PreviewPanel.d.ts.map +1 -1
  35. package/dist/showcase/components/PreviewPanel.js +5 -3
  36. package/dist/showcase/components/PreviewPanel.js.map +1 -1
  37. package/dist/showcase/components/ScaledIframe.d.ts +12 -1
  38. package/dist/showcase/components/ScaledIframe.d.ts.map +1 -1
  39. package/dist/showcase/components/ScaledIframe.js +18 -11
  40. package/dist/showcase/components/ScaledIframe.js.map +1 -1
  41. package/dist/showcase/components/ShowcaseAllRenderPage.d.ts.map +1 -1
  42. package/dist/showcase/components/ShowcaseAllRenderPage.js +3 -2
  43. package/dist/showcase/components/ShowcaseAllRenderPage.js.map +1 -1
  44. package/dist/showcase/components/ShowcasePage.d.ts +6 -1
  45. package/dist/showcase/components/ShowcasePage.d.ts.map +1 -1
  46. package/dist/showcase/components/ShowcasePage.js +8 -2
  47. package/dist/showcase/components/ShowcasePage.js.map +1 -1
  48. package/dist/showcase/components/ShowcaseRenderPage.d.ts.map +1 -1
  49. package/dist/showcase/components/ShowcaseRenderPage.js +9 -8
  50. package/dist/showcase/components/ShowcaseRenderPage.js.map +1 -1
  51. package/dist/showcase/components/ShowcaseViewportGuard.d.ts +14 -0
  52. package/dist/showcase/components/ShowcaseViewportGuard.d.ts.map +1 -0
  53. package/dist/showcase/components/ShowcaseViewportGuard.js +77 -0
  54. package/dist/showcase/components/ShowcaseViewportGuard.js.map +1 -0
  55. package/dist/showcase/controlFieldSupport.d.ts +12 -0
  56. package/dist/showcase/controlFieldSupport.d.ts.map +1 -0
  57. package/dist/showcase/controlFieldSupport.js +35 -0
  58. package/dist/showcase/controlFieldSupport.js.map +1 -0
  59. package/dist/showcase/index.d.ts +7 -1
  60. package/dist/showcase/index.d.ts.map +1 -1
  61. package/dist/showcase/index.js +7 -1
  62. package/dist/showcase/index.js.map +1 -1
  63. package/dist/showcase/isCmsShowcase.d.ts +10 -0
  64. package/dist/showcase/isCmsShowcase.d.ts.map +1 -0
  65. package/dist/showcase/isCmsShowcase.js +12 -0
  66. package/dist/showcase/isCmsShowcase.js.map +1 -0
  67. package/dist/showcase/mockFactory.d.ts +3 -0
  68. package/dist/showcase/mockFactory.d.ts.map +1 -1
  69. package/dist/showcase/mockFactory.js +133 -27
  70. package/dist/showcase/mockFactory.js.map +1 -1
  71. package/dist/showcase/showcaseLayout.d.ts +17 -0
  72. package/dist/showcase/showcaseLayout.d.ts.map +1 -0
  73. package/dist/showcase/showcaseLayout.js +11 -0
  74. package/dist/showcase/showcaseLayout.js.map +1 -0
  75. package/dist/showcase/showcaseRelatedContent.d.ts +30 -0
  76. package/dist/showcase/showcaseRelatedContent.d.ts.map +1 -0
  77. package/dist/showcase/showcaseRelatedContent.js +106 -0
  78. package/dist/showcase/showcaseRelatedContent.js.map +1 -0
  79. package/dist/utils/reactNodeText.d.ts +7 -0
  80. package/dist/utils/reactNodeText.d.ts.map +1 -0
  81. package/dist/utils/reactNodeText.js +43 -0
  82. package/dist/utils/reactNodeText.js.map +1 -0
  83. package/dist/utils/richTextNodeText.d.ts +13 -0
  84. package/dist/utils/richTextNodeText.d.ts.map +1 -0
  85. package/dist/utils/richTextNodeText.js +29 -0
  86. package/dist/utils/richTextNodeText.js.map +1 -0
  87. package/package.json +1 -1
  88. 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/core-ui",
3
- "version": "1.19.9",
3
+ "version": "1.20.0",
4
4
  "description": "Shared React UI component library with Tailwind CSS v4 for SE Studio applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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`.