forked from bem/bem-react
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdi.tsx
More file actions
157 lines (128 loc) · 3.67 KB
/
Copy pathdi.tsx
File metadata and controls
157 lines (128 loc) · 3.67 KB
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import React, { ReactNode, FC, ComponentType, createContext, useContext } from 'react'
export type RegistryContext = Record<string, Registry>
export const registryContext = createContext<RegistryContext>({})
const RegistryProvider = registryContext.Provider
export const RegistryConsumer = registryContext.Consumer
export function withRegistry(...registries: Registry[]) {
return function WithRegistry<P>(Component: ComponentType<P>) {
const RegistryResolver: FC<P> = (props) => {
return (
<RegistryConsumer>
{(contextRegistries) => {
const providedRegistries = { ...contextRegistries }
registries.forEach((registry) => {
const overrides = contextRegistries[registry.id]
providedRegistries[registry.id] = registry.overridable
? overrides
? registry.merge(overrides)
: registry
: registry && overrides
? overrides.merge(registry)
: registry
})
return (
<RegistryProvider value={providedRegistries}>
<Component {...props} />
</RegistryProvider>
)
}}
</RegistryConsumer>
)
}
RegistryResolver.displayName = `RegistryResolver(${registries.map((r) => r.id).join(', ')})`
return RegistryResolver
}
}
export interface IComponentRegistryConsumerProps {
id: string
children: (registry: any) => ReactNode
}
export const ComponentRegistryConsumer: FC<IComponentRegistryConsumerProps> = (props) => (
<RegistryConsumer>
{(registries) => {
if (__DEV__) {
if (!registries[props.id]) {
throw new Error(`Registry with id '${props.id}' not found.`)
}
}
return props.children(registries[props.id].snapshot())
}}
</RegistryConsumer>
)
export const useRegistries = () => {
return useContext(registryContext)
}
export const useComponentRegistry = <T extends {}>(id: string) => {
const registries = useRegistries()
return registries[id].snapshot<T>()
}
export interface IRegistryOptions {
id: string
overridable?: boolean
}
interface IRegistryComponents {
[key: string]: any
}
export class Registry {
id: string
overridable: boolean
private components: IRegistryComponents = {}
constructor({ id, overridable = true }: IRegistryOptions) {
this.id = id
this.overridable = overridable
}
/**
* Set react component in registry by id.
*
* @param id component id
* @param component valid react component
*/
set<T>(id: string, component: ComponentType<T>) {
this.components[id] = component
return this
}
/**
* Set react components in registry via object literal.
*
* @param componentsSet set of valid react components
*/
fill(componentsSet: IRegistryComponents) {
this.components = {
...this.components,
...componentsSet,
}
return this
}
/**
* Get react component from registry by id.
*
* @param id component id
*/
get<T>(id: string): ComponentType<T> {
if (__DEV__) {
if (!this.components[id]) {
throw new Error(`Component with id '${id}' not found.`)
}
}
return this.components[id]
}
/**
* Returns list of components from registry.
*/
snapshot<RT>(): RT {
return this.components as any
}
/**
* Override components by external registry.
*
* @param registry external registry
*/
merge(registry: Registry) {
const clone = new Registry({ id: this.id, overridable: this.overridable })
clone.components = {
...this.components,
...(registry ? registry.snapshot() : {}),
}
return clone
}
}