@se-studio/core-ui 0.1.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/README.md +121 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +8 -0
- package/dist/client.js.map +1 -0
- package/dist/components/ClientMonitor.d.ts +3 -0
- package/dist/components/ClientMonitor.js +135 -0
- package/dist/components/ClientMonitor.js.map +1 -0
- package/dist/components/Preview.d.ts +3 -0
- package/dist/components/Preview.js +33 -0
- package/dist/components/Preview.js.map +1 -0
- package/dist/hooks/useDocumentVisible.d.ts +3 -0
- package/dist/hooks/useDocumentVisible.js +20 -0
- package/dist/hooks/useDocumentVisible.js.map +1 -0
- package/dist/index.d.ts +172 -0
- package/dist/index.js +1078 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @se-studio/core-ui
|
|
2
|
+
|
|
3
|
+
Shared React UI component library with Tailwind CSS v4 for SE Studio applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **React 19** - Built with the latest React
|
|
8
|
+
- **TypeScript** - Full type safety
|
|
9
|
+
- **Tailwind CSS v4** - Modern utility-first CSS framework
|
|
10
|
+
- **Tree-shakeable** - Import only what you need
|
|
11
|
+
- **Dual exports** - ESM and CJS support
|
|
12
|
+
- **Fully tested** - Comprehensive test coverage with Vitest
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @se-studio/core-ui
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Importing Components
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { ResponsiveVisual } from '@se-studio/core-ui';
|
|
26
|
+
import type { IResponsiveVisual } from '@se-studio/core-data-types';
|
|
27
|
+
|
|
28
|
+
const visual: IResponsiveVisual = {
|
|
29
|
+
// ... your visual data
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default function MyPage() {
|
|
33
|
+
return <ResponsiveVisual visual={visual} />;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Using Tailwind Configuration
|
|
38
|
+
|
|
39
|
+
Import the Tailwind configuration in your `tailwind.config.ts`:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import baseConfig from '@se-studio/core-ui/tailwind-config';
|
|
43
|
+
import type { Config } from 'tailwindcss';
|
|
44
|
+
|
|
45
|
+
const config: Config = {
|
|
46
|
+
...baseConfig,
|
|
47
|
+
content: [
|
|
48
|
+
'./src/**/*.{ts,tsx}',
|
|
49
|
+
'./node_modules/@se-studio/core-ui/dist/**/*.{js,jsx}',
|
|
50
|
+
],
|
|
51
|
+
// Add your customizations here
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default config;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Importing Styles
|
|
58
|
+
|
|
59
|
+
In your Next.js app layout or root component:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import '@se-studio/core-ui/styles';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Components
|
|
66
|
+
|
|
67
|
+
### ResponsiveVisual
|
|
68
|
+
|
|
69
|
+
Renders responsive visual content (images, videos, animations) from Contentful.
|
|
70
|
+
|
|
71
|
+
**Props:**
|
|
72
|
+
|
|
73
|
+
- `visual` - `IResponsiveVisual` - The visual data from Contentful
|
|
74
|
+
- `className?` - `string` - Additional CSS classes
|
|
75
|
+
- `priority?` - `boolean` - Enable priority loading for above-the-fold images
|
|
76
|
+
|
|
77
|
+
**Example:**
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { ResponsiveVisual } from '@se-studio/core-ui';
|
|
81
|
+
|
|
82
|
+
<ResponsiveVisual
|
|
83
|
+
visual={responsiveVisualData}
|
|
84
|
+
className="w-full h-auto"
|
|
85
|
+
priority
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Install dependencies
|
|
93
|
+
pnpm install
|
|
94
|
+
|
|
95
|
+
# Run tests
|
|
96
|
+
pnpm test
|
|
97
|
+
|
|
98
|
+
# Run tests in watch mode
|
|
99
|
+
pnpm test:watch
|
|
100
|
+
|
|
101
|
+
# Build the library
|
|
102
|
+
pnpm build
|
|
103
|
+
|
|
104
|
+
# Type check
|
|
105
|
+
pnpm type-check
|
|
106
|
+
|
|
107
|
+
# Lint
|
|
108
|
+
pnpm lint
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Custom Breakpoints
|
|
112
|
+
|
|
113
|
+
The library includes custom breakpoints optimized for modern responsive design:
|
|
114
|
+
|
|
115
|
+
- `tablet`: 768px
|
|
116
|
+
- `laptop`: 1024px
|
|
117
|
+
- `desktop`: 1440px
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["'use client';\n\n// Client-only components that require browser APIs\nexport { ClientMonitor } from './components/ClientMonitor.js';\nexport { Preview } from './components/Preview.js';\n"],"mappings":";AAGA,SAAS,qBAAqB;AAC9B,SAAS,eAAe;","names":[]}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { usePathname } from "next/navigation";
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { useDocumentVisible } from "../hooks/useDocumentVisible";
|
|
5
|
+
let _observer;
|
|
6
|
+
function handleVideo(video, isIntersecting) {
|
|
7
|
+
if (!isIntersecting) {
|
|
8
|
+
if (!video.paused) {
|
|
9
|
+
video.pause();
|
|
10
|
+
}
|
|
11
|
+
} else if (video.paused) {
|
|
12
|
+
video.play().then(() => {
|
|
13
|
+
video.controls = false;
|
|
14
|
+
}).catch(() => {
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function handleAnimation(animation, isIntersecting) {
|
|
19
|
+
const currentState = animation.currentState;
|
|
20
|
+
if (!isIntersecting) {
|
|
21
|
+
if (currentState === "playing") {
|
|
22
|
+
animation.pause();
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
if (currentState === "paused") {
|
|
26
|
+
animation.play();
|
|
27
|
+
}
|
|
28
|
+
if (!currentState || currentState === "loading") {
|
|
29
|
+
if (animation.play) {
|
|
30
|
+
animation.play();
|
|
31
|
+
}
|
|
32
|
+
animation.autoplay = true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function handleAnimatedSection(section, isIntersecting) {
|
|
37
|
+
section.dataset.visible = isIntersecting ? "true" : "false";
|
|
38
|
+
if (isIntersecting) {
|
|
39
|
+
if (!section.dataset.seen) {
|
|
40
|
+
section.dataset.seen = "true";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function intersectionHandler(entries) {
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const target = entry.target;
|
|
47
|
+
switch (target.localName) {
|
|
48
|
+
case "video":
|
|
49
|
+
handleVideo(target, entry.isIntersecting);
|
|
50
|
+
break;
|
|
51
|
+
case "lottie-player":
|
|
52
|
+
handleAnimation(target, entry.isIntersecting);
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
{
|
|
56
|
+
const element = target;
|
|
57
|
+
handleAnimatedSection(element, entry.isIntersecting);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function getObserver() {
|
|
64
|
+
if (!_observer) {
|
|
65
|
+
const observer = new IntersectionObserver(intersectionHandler, {
|
|
66
|
+
threshold: 0.2
|
|
67
|
+
});
|
|
68
|
+
_observer = observer;
|
|
69
|
+
}
|
|
70
|
+
return _observer;
|
|
71
|
+
}
|
|
72
|
+
function findAllVideos() {
|
|
73
|
+
const videos = document.getElementsByTagName("video");
|
|
74
|
+
const observer = getObserver();
|
|
75
|
+
for (const video of videos) {
|
|
76
|
+
video.controls = false;
|
|
77
|
+
video.muted = true;
|
|
78
|
+
observer.observe(video);
|
|
79
|
+
}
|
|
80
|
+
return () => {
|
|
81
|
+
for (const video of videos) {
|
|
82
|
+
observer.unobserve(video);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function findAllAnimations() {
|
|
87
|
+
const animations = document.getElementsByTagName("lottie-player");
|
|
88
|
+
const observer = getObserver();
|
|
89
|
+
for (const animation of animations) {
|
|
90
|
+
observer.observe(animation);
|
|
91
|
+
}
|
|
92
|
+
return () => {
|
|
93
|
+
for (const animation of animations) {
|
|
94
|
+
observer.unobserve(animation);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function findAllAnimatedSections() {
|
|
99
|
+
const sections = document.querySelectorAll("[data-component]");
|
|
100
|
+
const observer = getObserver();
|
|
101
|
+
for (const section of sections) {
|
|
102
|
+
observer.observe(section);
|
|
103
|
+
}
|
|
104
|
+
return () => {
|
|
105
|
+
for (const section of sections) {
|
|
106
|
+
observer.unobserve(section);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function findAll() {
|
|
111
|
+
const videoCleanup = findAllVideos();
|
|
112
|
+
const animationCleanup = findAllAnimations();
|
|
113
|
+
const animatedSectionCleanup = findAllAnimatedSections();
|
|
114
|
+
return () => {
|
|
115
|
+
videoCleanup();
|
|
116
|
+
animationCleanup();
|
|
117
|
+
animatedSectionCleanup();
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const ClientMonitor = () => {
|
|
121
|
+
const isDocumentVisible = useDocumentVisible();
|
|
122
|
+
const pathname = usePathname();
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
pathname;
|
|
125
|
+
if (isDocumentVisible) {
|
|
126
|
+
return findAll();
|
|
127
|
+
}
|
|
128
|
+
return void 0;
|
|
129
|
+
}, [pathname, isDocumentVisible]);
|
|
130
|
+
return null;
|
|
131
|
+
};
|
|
132
|
+
export {
|
|
133
|
+
ClientMonitor
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=ClientMonitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/ClientMonitor.tsx"],"sourcesContent":["'use client';\nimport { usePathname } from 'next/navigation';\nimport { useEffect } from 'react';\nimport { useDocumentVisible } from '../hooks/useDocumentVisible';\n\nlet _observer: IntersectionObserver | undefined;\n\nfunction handleVideo(video: HTMLVideoElement, isIntersecting: boolean) {\n if (!isIntersecting) {\n if (!video.paused) {\n // console.log(\"pause video\", video.src);\n video.pause();\n }\n } else if (video.paused) {\n // console.log(\"play video\", video.src);\n video\n .play()\n .then(() => {\n video.controls = false;\n })\n .catch(() => {\n // console.log(\"failed to play\", error);\n // video.controls = true;\n });\n }\n}\n\ninterface ILottiePlayer extends HTMLElement {\n pause: () => void;\n play: () => void;\n autoplay: boolean;\n currentState: 'playing' | 'paused' | 'loading' | undefined;\n}\n\nfunction handleAnimation(animation: ILottiePlayer, isIntersecting: boolean) {\n const currentState = animation.currentState;\n // console.log('current state', currentState)\n if (!isIntersecting) {\n if (currentState === 'playing') {\n animation.pause();\n }\n } else {\n if (currentState === 'paused') {\n // console.log('play animation')\n animation.play();\n }\n if (!currentState || currentState === 'loading') {\n // console.log('set to autoplay')\n if (animation.play) {\n animation.play();\n }\n animation.autoplay = true;\n }\n }\n}\n\nfunction handleAnimatedSection(section: HTMLElement, isIntersecting: boolean) {\n section.dataset.visible = isIntersecting ? 'true' : 'false';\n if (isIntersecting) {\n if (!section.dataset.seen) {\n section.dataset.seen = 'true';\n }\n }\n}\n\nfunction intersectionHandler(entries: IntersectionObserverEntry[]) {\n for (const entry of entries) {\n // console.log(\"target\", entry.target.localName, entry.target);\n const target = entry.target;\n switch (target.localName) {\n case 'video':\n handleVideo(target as HTMLVideoElement, entry.isIntersecting);\n break;\n case 'lottie-player':\n handleAnimation(target as ILottiePlayer, entry.isIntersecting);\n break;\n default:\n {\n const element = target as HTMLElement;\n handleAnimatedSection(element, entry.isIntersecting);\n }\n break;\n }\n }\n}\n\nfunction getObserver() {\n if (!_observer) {\n const observer = new IntersectionObserver(intersectionHandler, {\n threshold: 0.2,\n });\n _observer = observer;\n }\n return _observer;\n}\n\nfunction findAllVideos() {\n const videos = document.getElementsByTagName('video');\n // console.log(\"got\", videos.length, \"videos\");\n const observer = getObserver();\n for (const video of videos) {\n video.controls = false;\n video.muted = true;\n // console.log(\n // `Video ${video.src} ready state ${video.readyState} ${video.paused}`,\n // );\n observer.observe(video);\n }\n return () => {\n // console.log(\"clear down video observer\");\n for (const video of videos) {\n observer.unobserve(video);\n }\n };\n}\n\nfunction findAllAnimations() {\n const animations = document.getElementsByTagName('lottie-player');\n // console.log(\"got\", animations.length, \"animations\");\n const observer = getObserver();\n for (const animation of animations) {\n observer.observe(animation);\n }\n return () => {\n // console.log(\"clear down animation observer\");\n for (const animation of animations) {\n observer.unobserve(animation);\n }\n };\n}\n\nfunction findAllAnimatedSections() {\n const sections = document.querySelectorAll('[data-component]');\n const observer = getObserver();\n for (const section of sections) {\n observer.observe(section);\n }\n return () => {\n // console.log(\"clear down animation observer\");\n for (const section of sections) {\n observer.unobserve(section);\n }\n };\n}\n\nfunction findAll() {\n const videoCleanup = findAllVideos();\n const animationCleanup = findAllAnimations();\n const animatedSectionCleanup = findAllAnimatedSections();\n return () => {\n videoCleanup();\n animationCleanup();\n animatedSectionCleanup();\n };\n}\n\nexport const ClientMonitor: React.FC = () => {\n const isDocumentVisible = useDocumentVisible();\n const pathname = usePathname();\n useEffect(() => {\n pathname;\n if (isDocumentVisible) {\n return findAll();\n }\n return undefined;\n }, [pathname, isDocumentVisible]);\n\n return null;\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ContentfulLivePreview } from "@contentful/live-preview";
|
|
3
|
+
import { useRouter } from "next/navigation";
|
|
4
|
+
import { useEffect, useLayoutEffect } from "react";
|
|
5
|
+
const Preview = () => {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
ContentfulLivePreview.init({
|
|
9
|
+
locale: "en-US",
|
|
10
|
+
debugMode: true,
|
|
11
|
+
enableLiveUpdates: true,
|
|
12
|
+
enableInspectorMode: true
|
|
13
|
+
});
|
|
14
|
+
}, []);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const checkLivePreview = (event) => {
|
|
17
|
+
if (typeof event.data !== "object" || !event.data || !("from" in event.data) || !("action" in event.data))
|
|
18
|
+
return;
|
|
19
|
+
if (event.data.from !== "live-preview") return;
|
|
20
|
+
if (event.data.action !== "ENTRY_SAVED") return;
|
|
21
|
+
router.refresh();
|
|
22
|
+
};
|
|
23
|
+
window.addEventListener("message", checkLivePreview);
|
|
24
|
+
return () => {
|
|
25
|
+
window.removeEventListener("message", checkLivePreview);
|
|
26
|
+
};
|
|
27
|
+
}, [router]);
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
export {
|
|
31
|
+
Preview
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=Preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Preview.tsx"],"sourcesContent":["'use client';\n\nimport { ContentfulLivePreview } from '@contentful/live-preview';\nimport { useRouter } from 'next/navigation';\nimport { useEffect, useLayoutEffect } from 'react';\n\nexport const Preview: React.FC = () => {\n const router = useRouter();\n useLayoutEffect(() => {\n ContentfulLivePreview.init({\n locale: 'en-US',\n debugMode: true,\n enableLiveUpdates: true,\n enableInspectorMode: true,\n });\n }, []);\n\n useEffect(() => {\n const checkLivePreview = (event: MessageEvent<unknown>) => {\n if (\n typeof event.data !== 'object' ||\n !event.data ||\n !('from' in event.data) ||\n !('action' in event.data)\n )\n return;\n if (event.data.from !== 'live-preview') return;\n if (event.data.action !== 'ENTRY_SAVED') return;\n router.refresh();\n };\n window.addEventListener('message', checkLivePreview);\n\n return () => {\n window.removeEventListener('message', checkLivePreview);\n };\n }, [router]);\n\n return null;\n};\n"],"mappings":";AAEA,SAAS,6BAA6B;AACtC,SAAS,iBAAiB;AAC1B,SAAS,WAAW,uBAAuB;AAEpC,MAAM,UAAoB,MAAM;AACrC,QAAM,SAAS,UAAU;AACzB,kBAAgB,MAAM;AACpB,0BAAsB,KAAK;AAAA,MACzB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,mBAAmB;AAAA,MACnB,qBAAqB;AAAA,IACvB,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,UAAM,mBAAmB,CAAC,UAAiC;AACzD,UACE,OAAO,MAAM,SAAS,YACtB,CAAC,MAAM,QACP,EAAE,UAAU,MAAM,SAClB,EAAE,YAAY,MAAM;AAEpB;AACF,UAAI,MAAM,KAAK,SAAS,eAAgB;AACxC,UAAI,MAAM,KAAK,WAAW,cAAe;AACzC,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO,iBAAiB,WAAW,gBAAgB;AAEnD,WAAO,MAAM;AACX,aAAO,oBAAoB,WAAW,gBAAgB;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
function useDocumentVisible(initialState = true) {
|
|
4
|
+
const [isVisible, setIsVisible] = useState(initialState);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const handler = () => {
|
|
7
|
+
setIsVisible(document.visibilityState === "visible");
|
|
8
|
+
};
|
|
9
|
+
document.addEventListener("visibilitychange", handler, true);
|
|
10
|
+
handler();
|
|
11
|
+
return () => {
|
|
12
|
+
document.removeEventListener("visibilitychange", handler, true);
|
|
13
|
+
};
|
|
14
|
+
}, []);
|
|
15
|
+
return isVisible;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
useDocumentVisible
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=useDocumentVisible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useDocumentVisible.ts"],"sourcesContent":["'use client';\nimport { useEffect, useState } from 'react';\n\nexport function useDocumentVisible(initialState = true) {\n const [isVisible, setIsVisible] = useState(initialState);\n useEffect(() => {\n const handler = () => {\n setIsVisible(document.visibilityState === 'visible');\n };\n document.addEventListener('visibilitychange', handler, true);\n handler();\n return () => {\n document.removeEventListener('visibilitychange', handler, true);\n };\n }, []);\n return isVisible;\n}\n"],"mappings":";AACA,SAAS,WAAW,gBAAgB;AAE7B,SAAS,mBAAmB,eAAe,MAAM;AACtD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,YAAY;AACvD,YAAU,MAAM;AACd,UAAM,UAAU,MAAM;AACpB,mBAAa,SAAS,oBAAoB,SAAS;AAAA,IACrD;AACA,aAAS,iBAAiB,oBAAoB,SAAS,IAAI;AAC3D,YAAQ;AACR,WAAO,MAAM;AACX,eAAS,oBAAoB,oBAAoB,SAAS,IAAI;AAAA,IAChE;AAAA,EACF,GAAG,CAAC,CAAC;AACL,SAAO;AACT;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { BaseCmsConfig, IContentContext, IVisual, PageContent, IPageContext, IVisualSizes, IAnalyticsContext, IVisualCommon } from '@se-studio/core-data-types';
|
|
2
|
+
import { React as React$2 } from 'next/dist/server/route-modules/app-page/vendored/rsc/entrypoints';
|
|
3
|
+
import React$1, { PropsWithChildren, CSSProperties, HTMLAttributes } from 'react';
|
|
4
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
|
+
import { Document } from '@contentful/rich-text-types';
|
|
6
|
+
|
|
7
|
+
type CollectionMap<TConfig extends BaseCmsConfig> = Record<TConfig['CollectionType'], React$1.FC<{
|
|
8
|
+
information: TConfig['Collection'];
|
|
9
|
+
contentContext: IContentContext<TConfig>;
|
|
10
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
11
|
+
}>>;
|
|
12
|
+
declare function CmsCollection<TConfig extends BaseCmsConfig>({ information, contentContext, rendererConfig, }: {
|
|
13
|
+
information: TConfig['Collection'];
|
|
14
|
+
contentContext: IContentContext<TConfig>;
|
|
15
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
16
|
+
}): react_jsx_runtime.JSX.Element;
|
|
17
|
+
|
|
18
|
+
type ComponentMap<TConfig extends BaseCmsConfig> = Record<TConfig['ComponentType'], React$1.FC<{
|
|
19
|
+
information: TConfig['Component'];
|
|
20
|
+
contentContext: IContentContext<TConfig>;
|
|
21
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
22
|
+
}>>;
|
|
23
|
+
declare function CmsComponent<TConfig extends BaseCmsConfig>({ information, contentContext, rendererConfig, }: {
|
|
24
|
+
information: TConfig['Component'];
|
|
25
|
+
contentContext: IContentContext<TConfig>;
|
|
26
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
27
|
+
}): react_jsx_runtime.JSX.Element;
|
|
28
|
+
|
|
29
|
+
type ExternalComponentRenderer<TConfig extends BaseCmsConfig> = React$1.FC<{
|
|
30
|
+
information: TConfig['ExternalComponent'];
|
|
31
|
+
contentContext: IContentContext<TConfig>;
|
|
32
|
+
}>;
|
|
33
|
+
type ExternalComponentMap<TConfig extends BaseCmsConfig> = Partial<Record<TConfig['ExternalComponentType'], ExternalComponentRenderer<TConfig>>>;
|
|
34
|
+
type VisualComponentRenderer<TConfig extends BaseCmsConfig> = React$1.FC<{
|
|
35
|
+
information: IVisual;
|
|
36
|
+
contentContext: IContentContext<TConfig>;
|
|
37
|
+
}>;
|
|
38
|
+
type InternalLinkRenderer<TConfig extends BaseCmsConfig> = React$1.FC<{
|
|
39
|
+
information: Extract<PageContent<TConfig>, {
|
|
40
|
+
type: 'Internal link';
|
|
41
|
+
}>;
|
|
42
|
+
contentContext: IContentContext<TConfig>;
|
|
43
|
+
}>;
|
|
44
|
+
declare function CmsContent<TConfig extends BaseCmsConfig>({ pageContext, contents, rendererConfig, }: {
|
|
45
|
+
pageContext: IPageContext<TConfig>;
|
|
46
|
+
contents?: ReadonlyArray<PageContent<TConfig>>;
|
|
47
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
48
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
49
|
+
|
|
50
|
+
interface CmsRendererConfig<TConfig extends BaseCmsConfig> {
|
|
51
|
+
componentMap: ComponentMap<TConfig>;
|
|
52
|
+
collectionMap: CollectionMap<TConfig>;
|
|
53
|
+
externalComponentMap: ExternalComponentMap<TConfig>;
|
|
54
|
+
visualComponentRenderer: VisualComponentRenderer<TConfig>;
|
|
55
|
+
internalLinkRenderer: InternalLinkRenderer<TConfig>;
|
|
56
|
+
showUnknownTypeErrors: boolean;
|
|
57
|
+
showRenderErrors: boolean;
|
|
58
|
+
defaultComponentRenderer: React$2.FC<{
|
|
59
|
+
information: TConfig['Component'];
|
|
60
|
+
contentContext: IContentContext<TConfig>;
|
|
61
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
62
|
+
}>;
|
|
63
|
+
defaultCollectionRenderer: React$2.FC<{
|
|
64
|
+
information: TConfig['Collection'];
|
|
65
|
+
contentContext: IContentContext<TConfig>;
|
|
66
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
67
|
+
}>;
|
|
68
|
+
basicLinkRenderer: React$2.FC<PropsWithChildren<{
|
|
69
|
+
href?: string;
|
|
70
|
+
}>>;
|
|
71
|
+
embeddedContentRenderer: React$2.FC<{
|
|
72
|
+
information: TConfig['PageContent'];
|
|
73
|
+
contentContext: IContentContext<TConfig>;
|
|
74
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
declare function createCmsRendererConfig<TConfig extends BaseCmsConfig>(config: CmsRendererConfig<TConfig>): CmsRendererConfig<TConfig>;
|
|
78
|
+
type PartialCmsRendererConfig<TConfig extends BaseCmsConfig> = {
|
|
79
|
+
[K in keyof CmsRendererConfig<TConfig>]?: CmsRendererConfig<TConfig>[K];
|
|
80
|
+
};
|
|
81
|
+
declare function mergeCmsRendererConfig<TConfig extends BaseCmsConfig>(base: CmsRendererConfig<TConfig>, overrides: PartialCmsRendererConfig<TConfig>): CmsRendererConfig<TConfig>;
|
|
82
|
+
|
|
83
|
+
type IVisualProps = {
|
|
84
|
+
className?: string;
|
|
85
|
+
style?: Omit<CSSProperties, 'objectFit' | 'objectPosition'>;
|
|
86
|
+
autoPlay?: boolean;
|
|
87
|
+
loop?: boolean;
|
|
88
|
+
loopDelay?: number;
|
|
89
|
+
muted?: boolean;
|
|
90
|
+
controls?: boolean;
|
|
91
|
+
loading?: 'eager' | 'lazy' | undefined;
|
|
92
|
+
priority?: boolean;
|
|
93
|
+
fetchPriority?: 'high' | 'low' | undefined;
|
|
94
|
+
visualSizes?: IVisualSizes;
|
|
95
|
+
fullPlayer?: boolean;
|
|
96
|
+
analyticsContext?: IAnalyticsContext;
|
|
97
|
+
componentLabel?: string | null;
|
|
98
|
+
};
|
|
99
|
+
declare function calculateCropDetails(details: Omit<IVisualCommon, 'id' | 'type' | 'name' | 'nameAsCaption'>): Pick<CSSProperties, 'objectFit' | 'objectPosition'>;
|
|
100
|
+
declare function calculateImagePriority(index: number): {
|
|
101
|
+
priority: boolean;
|
|
102
|
+
fetchPriority: 'high' | 'low' | undefined;
|
|
103
|
+
} | undefined;
|
|
104
|
+
declare function calculateImageWidthStyleVariable(visual?: IVisual, baseWidth?: number | null): CSSProperties | undefined;
|
|
105
|
+
|
|
106
|
+
declare const Visual: React$1.FC<{
|
|
107
|
+
visual: IVisual;
|
|
108
|
+
} & IVisualProps>;
|
|
109
|
+
|
|
110
|
+
type ArrayToUnion<T extends ReadonlyArray<unknown>> = T[number];
|
|
111
|
+
interface ComponentConfig<TConfig extends BaseCmsConfig = BaseCmsConfig, TAdditional = Record<string, unknown>> {
|
|
112
|
+
componentProps: ReadonlyArray<keyof TConfig['Component']>;
|
|
113
|
+
contextProps: ReadonlyArray<keyof IPageContext<TConfig>>;
|
|
114
|
+
additionalProps?: TAdditional;
|
|
115
|
+
}
|
|
116
|
+
interface CollectionConfig<TConfig extends BaseCmsConfig = BaseCmsConfig, TAdditional = Record<string, unknown>> {
|
|
117
|
+
collectionProps: ReadonlyArray<keyof TConfig['Collection']>;
|
|
118
|
+
contextProps: ReadonlyArray<keyof IPageContext<TConfig>>;
|
|
119
|
+
additionalProps?: TAdditional;
|
|
120
|
+
}
|
|
121
|
+
type ComponentPropsFromConfig<TConfig extends BaseCmsConfig, T extends ComponentConfig<TConfig>> = T['componentProps'] extends ReadonlyArray<keyof TConfig['Component']> ? Pick<TConfig['Component'], ArrayToUnion<T['componentProps']>> : never;
|
|
122
|
+
type CollectionPropsFromConfig<TConfig extends BaseCmsConfig, T extends CollectionConfig<TConfig>> = T['collectionProps'] extends ReadonlyArray<keyof TConfig['Collection']> ? Pick<TConfig['Collection'], ArrayToUnion<T['collectionProps']>> : never;
|
|
123
|
+
type ContextPropsFromConfig<TConfig extends BaseCmsConfig, T extends ComponentConfig<TConfig> | CollectionConfig<TConfig>> = Pick<IPageContext<TConfig>, ArrayToUnion<T['contextProps']>>;
|
|
124
|
+
type GeneratePropsFromComponentConfig<TConfig extends BaseCmsConfig, T extends ComponentConfig<TConfig>> = ComponentPropsFromConfig<TConfig, T> & ContextPropsFromConfig<TConfig, T> & (T['additionalProps'] extends Record<string, unknown> ? T['additionalProps'] : Record<string, never>);
|
|
125
|
+
type GeneratePropsFromCollectionConfig<TConfig extends BaseCmsConfig, T extends CollectionConfig<TConfig>> = CollectionPropsFromConfig<TConfig, T> & ContextPropsFromConfig<TConfig, T> & (T['additionalProps'] extends Record<string, unknown> ? T['additionalProps'] : Record<string, never>);
|
|
126
|
+
declare function extractPropsFromComponentConfig<TConfig extends BaseCmsConfig, T extends ComponentConfig<TConfig>>(config: T, component: TConfig['Component'], context?: IPageContext<TConfig>): GeneratePropsFromComponentConfig<TConfig, T>;
|
|
127
|
+
declare function extractPropsFromCollectionConfig<TConfig extends BaseCmsConfig, T extends CollectionConfig<TConfig>>(config: T, collection: TConfig['Collection'], context?: IPageContext<TConfig>): GeneratePropsFromCollectionConfig<TConfig, T>;
|
|
128
|
+
|
|
129
|
+
interface IEmbeddableComponentContext<TConfig extends BaseCmsConfig = BaseCmsConfig, T extends TConfig['Component'] = TConfig['Component']> {
|
|
130
|
+
information: T;
|
|
131
|
+
parentIndex: number;
|
|
132
|
+
parentId: string;
|
|
133
|
+
pageContext: IPageContext<TConfig>;
|
|
134
|
+
}
|
|
135
|
+
interface IEmbeddableCollectionContext<TConfig extends BaseCmsConfig = BaseCmsConfig, T extends TConfig['Collection'] = TConfig['Collection']> {
|
|
136
|
+
information: T;
|
|
137
|
+
parentIndex: number;
|
|
138
|
+
parentId: string;
|
|
139
|
+
pageContext: IPageContext<TConfig>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
declare function BuildEmbedFromComponentConfig<TConfig extends BaseCmsConfig, TProps extends Record<string, unknown>, TComponentConfig extends ComponentConfig<TConfig>>(Component: React$1.FC<TProps>, componentConfig: TComponentConfig): React$1.FC<IEmbeddableComponentContext<TConfig>>;
|
|
143
|
+
declare function BuildEmbedFromCollectionConfig<TConfig extends BaseCmsConfig, TProps extends Record<string, unknown>, TCollectionConfig extends CollectionConfig<TConfig>>(Collection: React$1.FC<TProps>, collectionConfig: TCollectionConfig): React$1.FC<IEmbeddableCollectionContext<TConfig>>;
|
|
144
|
+
|
|
145
|
+
type EmbeddableComponentMap<TConfig extends BaseCmsConfig> = Partial<Record<TConfig['ComponentType'], React$1.FC<IEmbeddableComponentContext<TConfig>>>>;
|
|
146
|
+
type EmbeddableCollectionMap<TConfig extends BaseCmsConfig> = Partial<Record<TConfig['CollectionType'], React$1.FC<IEmbeddableCollectionContext<TConfig>>>>;
|
|
147
|
+
declare function isComponentEmbeddable<TConfig extends BaseCmsConfig>(componentType: TConfig['ComponentType'], embeddableMap: EmbeddableComponentMap<TConfig>): boolean;
|
|
148
|
+
declare function isCollectionEmbeddable<TConfig extends BaseCmsConfig>(collectionType: TConfig['CollectionType'], embeddableMap: EmbeddableCollectionMap<TConfig>): boolean;
|
|
149
|
+
|
|
150
|
+
type RTFProps<TConfig extends BaseCmsConfig> = {
|
|
151
|
+
content: Document;
|
|
152
|
+
rendererConfig: CmsRendererConfig<TConfig>;
|
|
153
|
+
contentContext: IContentContext<TConfig>;
|
|
154
|
+
} & Omit<HTMLAttributes<HTMLElement>, 'content'>;
|
|
155
|
+
declare function RTF<TConfig extends BaseCmsConfig>({ content, className, style, rendererConfig, contentContext, ...other }: RTFProps<TConfig>): react_jsx_runtime.JSX.Element;
|
|
156
|
+
|
|
157
|
+
declare const DEFAULT_ALLOWED_UNUSED: Set<string>;
|
|
158
|
+
type UnusedType = Record<string, unknown>;
|
|
159
|
+
declare const UnusedChecker: React.FC<PropsWithChildren<{
|
|
160
|
+
componentName: string;
|
|
161
|
+
unused: UnusedType;
|
|
162
|
+
allowedToBeUnused?: Set<string>;
|
|
163
|
+
showWarnings?: boolean;
|
|
164
|
+
}>>;
|
|
165
|
+
|
|
166
|
+
declare const UnsupportedWarning: React.FC<PropsWithChildren<{
|
|
167
|
+
className?: string;
|
|
168
|
+
style?: CSSProperties;
|
|
169
|
+
showWarnings?: boolean;
|
|
170
|
+
}>>;
|
|
171
|
+
|
|
172
|
+
export { type ArrayToUnion, BuildEmbedFromCollectionConfig, BuildEmbedFromComponentConfig, CmsCollection, CmsComponent, CmsContent, type CmsRendererConfig, type CollectionConfig, type CollectionMap, type CollectionPropsFromConfig, type ComponentConfig, type ComponentMap, type ComponentPropsFromConfig, type ContextPropsFromConfig, DEFAULT_ALLOWED_UNUSED, type EmbeddableCollectionMap, type EmbeddableComponentMap, type ExternalComponentMap, type ExternalComponentRenderer, type GeneratePropsFromCollectionConfig, type GeneratePropsFromComponentConfig, type IEmbeddableCollectionContext, type IEmbeddableComponentContext, type IVisualProps, type InternalLinkRenderer, type PartialCmsRendererConfig, RTF, UnsupportedWarning, UnusedChecker, Visual, type VisualComponentRenderer, calculateCropDetails, calculateImagePriority, calculateImageWidthStyleVariable, createCmsRendererConfig, extractPropsFromCollectionConfig, extractPropsFromComponentConfig, isCollectionEmbeddable, isComponentEmbeddable, mergeCmsRendererConfig };
|