/
ComponentRender.tsx
102 lines (93 loc) 路 2.9 KB
/
ComponentRender.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { createContext, Suspense, useContext, useMemo, useState } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { useIsomorphicLayoutEffect } from 'framer-motion'
import type { FC } from 'react'
import { BlockLoading } from '~/components/modules/shared/BlockLoading'
import { loadScript } from '~/lib/load-script'
import { get } from '~/lib/lodash'
const StyleContext = createContext<React.CSSProperties>({})
export interface ReactComponentRenderProps {
dls: string
}
/**
* define the render dls of the component
* ```component
* import=http://127.0.0.1:2333/snippets/js/components.js
* name=Components.Card
* height=20 // This is optional
* ```
*
* name will be used to find the component in the import, if in the nested object, use dot to separate
*
*/
export const ReactComponentRender: FC<ReactComponentRenderProps> = (props) => {
const { dls } = props
const dlsProps = parseDlsContent(dls)
const style: React.CSSProperties = useMemo(() => {
if (!dlsProps.height) return {}
const isNumberString = /^\d+$/.test(dlsProps.height)
return {
height: isNumberString ? `${dlsProps.height}px` : dlsProps.height,
}
}, [dlsProps.height])
return (
<ErrorBoundary fallback={<ComponentBlockError style={style} />}>
<StyleContext.Provider value={style}>
<ReactComponentRenderImpl {...dlsProps} />
</StyleContext.Provider>
</ErrorBoundary>
)
}
const ReactComponentRenderImpl: FC<DlsProps> = (dlsProps) => {
const [Component, setComponent] = useState({
component: ComponentBlockLoading,
})
const style = useContext(StyleContext)
useIsomorphicLayoutEffect(() => {
loadScript(
'https://unpkg.com/styled-components/dist/styled-components.min.js',
)
.then(() => loadScript(dlsProps.import))
.then(() => {
const Component = get(window, dlsProps.name)
console.log('Component', Component)
setComponent({ component: Component })
})
}, [dlsProps])
return (
<ErrorBoundary fallback={<ComponentBlockError style={style} />}>
<Suspense fallback={<ComponentBlockLoading style={style} />}>
<div style={style} className="overflow-hidden">
<Component.component />
</div>
</Suspense>
</ErrorBoundary>
)
}
const ComponentBlockError: FC<{
style?: React.CSSProperties
}> = ({ style }) => {
return (
<BlockLoading style={style} className="bg-red-300 dark:bg-red-700">
Component Error
</BlockLoading>
)
}
const ComponentBlockLoading: FC<{
style?: React.CSSProperties
}> = ({ style }) => {
return <BlockLoading style={style}>Component Loading...</BlockLoading>
}
type DlsProps = {
name: string
import: string
height?: string
}
function parseDlsContent(dls: string) {
const parsedProps = {} as DlsProps
dls.split('\n').forEach((line) => {
const [key, value] = line.split('=')
;(parsedProps as any)[key] = value
})
return parsedProps
}