@makeswift/runtime 0.0.0-a500663
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/box-model.js +1 -0
- package/components.js +1 -0
- package/favicon.svg +15 -0
- package/index.html +13 -0
- package/package.json +100 -0
- package/prop-controllers.js +1 -0
- package/react.js +1 -0
- package/src/box-model.ts +2 -0
- package/src/components/Root/Root.tsx +72 -0
- package/src/components/Root/components/Placeholder/index.tsx +37 -0
- package/src/components/Root/index.tsx +1 -0
- package/src/components/index.ts +1 -0
- package/src/components/utils/columns.ts +15 -0
- package/src/components/utils/cssMediaRules.ts +91 -0
- package/src/components/utils/devices.ts +86 -0
- package/src/index.ts +8 -0
- package/src/prop-controllers/descriptors.ts +821 -0
- package/src/prop-controllers/index.ts +10 -0
- package/src/react.ts +3 -0
- package/src/runtimes/react.tsx +223 -0
- package/src/state/actions.ts +239 -0
- package/src/state/modules/box-models.ts +112 -0
- package/src/state/modules/components-meta.ts +44 -0
- package/src/state/modules/prop-controllers.ts +45 -0
- package/src/state/modules/react-components.tsx +47 -0
- package/src/state/modules/read-only-documents.ts +51 -0
- package/src/state/modules/read-write-documents.ts +75 -0
- package/src/state/react-builder-preview.ts +271 -0
- package/src/state/react-page.ts +73 -0
- package/src/style.css +8 -0
- package/src/utils/deepEqual.ts +33 -0
- package/src/utils/is.ts +5 -0
- package/src/utils/shallowEqual.ts +30 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +23 -0
- package/types/ot-json0.d.ts +35 -0
- package/vite.config.ts +28 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
Descriptor as PropControllerDescriptor,
|
|
3
|
+
DescriptorValueType as PropControllerDescriptorValueType,
|
|
4
|
+
Device,
|
|
5
|
+
DeviceOverride,
|
|
6
|
+
ResponsiveValue,
|
|
7
|
+
ResponsiveValueType,
|
|
8
|
+
Length,
|
|
9
|
+
} from './descriptors'
|
|
10
|
+
export * as Props from './descriptors'
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
forwardRef,
|
|
4
|
+
memo,
|
|
5
|
+
ReactNode,
|
|
6
|
+
Ref,
|
|
7
|
+
useCallback,
|
|
8
|
+
useContext,
|
|
9
|
+
useEffect,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react'
|
|
12
|
+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
|
|
13
|
+
|
|
14
|
+
import * as ReactPage from '../state/react-page'
|
|
15
|
+
import type * as ReactBuilderPreview from '../state/react-builder-preview'
|
|
16
|
+
import {
|
|
17
|
+
changeComponentHandle,
|
|
18
|
+
mountComponentEffect,
|
|
19
|
+
registerComponentEffect,
|
|
20
|
+
registerReactComponentEffect,
|
|
21
|
+
} from '../state/actions'
|
|
22
|
+
import type {
|
|
23
|
+
PropControllerDescriptor,
|
|
24
|
+
PropControllerDescriptorValueType,
|
|
25
|
+
} from '../prop-controllers'
|
|
26
|
+
import { ComponentIcon } from '../state/modules/components-meta'
|
|
27
|
+
|
|
28
|
+
const contextDefaultValue = ReactPage.configureStore()
|
|
29
|
+
|
|
30
|
+
export const ReactRuntime = {
|
|
31
|
+
registerComponent<
|
|
32
|
+
P extends Record<string, PropControllerDescriptor>,
|
|
33
|
+
C extends ReactPage.ComponentType<{ [K in keyof P]?: PropControllerDescriptorValueType<P[K]> }>
|
|
34
|
+
>(
|
|
35
|
+
component: C,
|
|
36
|
+
{
|
|
37
|
+
type,
|
|
38
|
+
label,
|
|
39
|
+
icon = 'Cube40',
|
|
40
|
+
hidden = false,
|
|
41
|
+
props,
|
|
42
|
+
}: { type: string; label: string; icon?: ComponentIcon; hidden?: boolean; props?: P },
|
|
43
|
+
): () => void {
|
|
44
|
+
const unregisterComponent = contextDefaultValue.dispatch(
|
|
45
|
+
registerComponentEffect(type, { label, icon, hidden }, props ?? {}),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const unregisterReactComponent = contextDefaultValue.dispatch(
|
|
49
|
+
registerReactComponentEffect(type, (component as unknown) as ReactPage.ComponentType),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
unregisterComponent()
|
|
54
|
+
unregisterReactComponent()
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const Context = createContext(contextDefaultValue)
|
|
60
|
+
|
|
61
|
+
type RuntimeProviderProps = {
|
|
62
|
+
defaultRootElements?: Map<string, ReactPage.Element>
|
|
63
|
+
children?: ReactNode
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function RuntimeProvider({
|
|
67
|
+
children,
|
|
68
|
+
defaultRootElements,
|
|
69
|
+
}: RuntimeProviderProps): JSX.Element {
|
|
70
|
+
const [store, setStore] = useState(() =>
|
|
71
|
+
ReactPage.configureStore({
|
|
72
|
+
preloadedState: contextDefaultValue.getState(),
|
|
73
|
+
rootElements: defaultRootElements,
|
|
74
|
+
}),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
// TODO(miguel): perform a more robust validation.
|
|
79
|
+
const isInBuilder = window.parent !== window
|
|
80
|
+
|
|
81
|
+
if (!isInBuilder) return
|
|
82
|
+
|
|
83
|
+
const initializePromise = initializeReactBuilderPreview()
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
initializePromise.then(cleanUp => {
|
|
87
|
+
cleanUp()
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function initializeReactBuilderPreview(): Promise<() => void> {
|
|
92
|
+
const ReactBuilderPreview = await import('../state/react-builder-preview')
|
|
93
|
+
|
|
94
|
+
const store = await new Promise<ReactBuilderPreview.Store>(resolve => {
|
|
95
|
+
setStore(store => {
|
|
96
|
+
const nextStore = ReactBuilderPreview.configureStore({ preloadedState: store.getState() })
|
|
97
|
+
|
|
98
|
+
resolve(nextStore)
|
|
99
|
+
|
|
100
|
+
return nextStore
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return store.dispatch(ReactBuilderPreview.initialize())
|
|
105
|
+
}
|
|
106
|
+
}, [])
|
|
107
|
+
|
|
108
|
+
return <Context.Provider value={store}>{children}</Context.Provider>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type State = ReactPage.State | ReactBuilderPreview.State
|
|
112
|
+
|
|
113
|
+
function useSelector<R>(selector: (state: State) => R): R {
|
|
114
|
+
const store = useContext(Context)
|
|
115
|
+
|
|
116
|
+
return useSyncExternalStoreWithSelector(store.subscribe, store.getState, store.getState, selector)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function useComponent(type: string): ReactPage.ComponentType | null {
|
|
120
|
+
return useSelector(state => ReactPage.getReactComponent(state, type))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function useDocumentRootElement(documentKey: string): ReactPage.Element | null {
|
|
124
|
+
return useSelector(state => ReactPage.getDocumentRootElement(state, documentKey))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
type Dispatch = ReactPage.Dispatch & ReactBuilderPreview.Dispatch
|
|
128
|
+
|
|
129
|
+
function useDispatch(): Dispatch {
|
|
130
|
+
const store = useContext(Context)
|
|
131
|
+
|
|
132
|
+
return store.dispatch
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
type ElementDataProps = {
|
|
136
|
+
elementData: ReactPage.ElementData
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const ElementData = memo(
|
|
140
|
+
forwardRef(function ElementData(
|
|
141
|
+
{ elementData }: ElementDataProps,
|
|
142
|
+
ref: Ref<unknown>,
|
|
143
|
+
): JSX.Element {
|
|
144
|
+
const Component = useComponent(elementData.type)
|
|
145
|
+
|
|
146
|
+
if (Component == null) {
|
|
147
|
+
return (
|
|
148
|
+
<div ref={ref as Ref<HTMLDivElement>}>
|
|
149
|
+
<p>Component Not Found</p>
|
|
150
|
+
<pre>
|
|
151
|
+
<code>{JSON.stringify(elementData, null, 2)}</code>
|
|
152
|
+
</pre>
|
|
153
|
+
</div>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return <Component {...elementData.props} key={elementData.key} ref={ref} />
|
|
158
|
+
}),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
type ElementRefereceProps = {
|
|
162
|
+
elementReference: ReactPage.ElementReference
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const ElementReference = memo(
|
|
166
|
+
forwardRef(function ElementReference(
|
|
167
|
+
{ elementReference }: ElementRefereceProps,
|
|
168
|
+
ref: Ref<unknown>,
|
|
169
|
+
): JSX.Element {
|
|
170
|
+
return (
|
|
171
|
+
<div ref={ref as Ref<HTMLDivElement>}>
|
|
172
|
+
<p>Not Implemented</p>
|
|
173
|
+
<pre>
|
|
174
|
+
<code>{JSON.stringify(elementReference, null, 2)}</code>
|
|
175
|
+
</pre>
|
|
176
|
+
</div>
|
|
177
|
+
)
|
|
178
|
+
}),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
type ElementProps = {
|
|
182
|
+
element: ReactPage.Element
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export const Element = memo(function Element({ element }: ElementProps): JSX.Element {
|
|
186
|
+
const elementKey = element.key
|
|
187
|
+
const dispatch = useDispatch()
|
|
188
|
+
const ref = useCallback(
|
|
189
|
+
(handle: unknown): void => {
|
|
190
|
+
dispatch(changeComponentHandle(elementKey, handle))
|
|
191
|
+
},
|
|
192
|
+
[dispatch, elementKey],
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
useEffect(() => dispatch(mountComponentEffect(elementKey)), [dispatch, elementKey])
|
|
196
|
+
|
|
197
|
+
return ReactPage.isElementReference(element) ? (
|
|
198
|
+
<ElementReference key={elementKey} ref={ref} elementReference={element} />
|
|
199
|
+
) : (
|
|
200
|
+
<ElementData key={elementKey} ref={ref} elementData={element} />
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
type DocumentProps = {
|
|
205
|
+
documentKey: string
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export const Document = memo(function Document({ documentKey }: DocumentProps): JSX.Element {
|
|
209
|
+
const documentRootElement = useDocumentRootElement(documentKey)
|
|
210
|
+
|
|
211
|
+
if (documentRootElement == null) {
|
|
212
|
+
return (
|
|
213
|
+
<div>
|
|
214
|
+
<p>Document Not Found</p>
|
|
215
|
+
<pre>
|
|
216
|
+
<code>{JSON.stringify({ documentKey }, null, 2)}</code>
|
|
217
|
+
</pre>
|
|
218
|
+
</div>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return <Element element={documentRootElement} />
|
|
223
|
+
})
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import type { Operation } from 'ot-json0'
|
|
2
|
+
|
|
3
|
+
import type { ComponentType } from './modules/react-components'
|
|
4
|
+
import type { Measurable, BoxModel } from './modules/box-models'
|
|
5
|
+
import type { ThunkAction } from 'redux-thunk'
|
|
6
|
+
import { ComponentMeta } from './modules/components-meta'
|
|
7
|
+
import { PropControllerDescriptor } from '../prop-controllers'
|
|
8
|
+
import type { Size } from './react-builder-preview'
|
|
9
|
+
|
|
10
|
+
export const ActionTypes = {
|
|
11
|
+
CHANGE_DOCUMENT: 'CHANGE_DOCUMENT',
|
|
12
|
+
|
|
13
|
+
REGISTER_COMPONENT: 'REGISTER_COMPONENT',
|
|
14
|
+
UNREGISTER_COMPONENT: 'UNREGISTER_COMPONENT',
|
|
15
|
+
|
|
16
|
+
REGISTER_REACT_COMPONENT: 'REGISTER_REACT_COMPONENT',
|
|
17
|
+
UNREGISTER_REACT_COMPONENT: 'UNREGISTER_REACT_COMPONENT',
|
|
18
|
+
|
|
19
|
+
MOUNT_COMPONENT: 'MOUNT_COMPONENT',
|
|
20
|
+
UNMOUNT_COMPONENT: 'UNMOUNT_COMPONENT',
|
|
21
|
+
|
|
22
|
+
CHANGE_COMPONENT_HANDLE: 'CHANGE_COMPONENT_HANDLE',
|
|
23
|
+
|
|
24
|
+
REGISTER_MEASURABLE: 'REGISTER_MEASURABLE',
|
|
25
|
+
UNREGISTER_MEASURABLE: 'UNREGISTER_MEASURABLE',
|
|
26
|
+
|
|
27
|
+
CHANGE_ELEMENT_BOX_MODELS: 'CHANGE_ELEMENT_BOX_MODELS',
|
|
28
|
+
|
|
29
|
+
CHANGE_DOCUMENT_ELEMENT_SIZE: 'CHANGE_DOCUMENT_ELEMENT_SIZE',
|
|
30
|
+
CHANGE_DOCUMENT_ELEMENT_SCROLL_TOP: 'CHANGE_DOCUMENT_ELEMENT_SCROLL_TOP',
|
|
31
|
+
} as const
|
|
32
|
+
|
|
33
|
+
type ChangeDocumentAction = {
|
|
34
|
+
type: typeof ActionTypes.CHANGE_DOCUMENT
|
|
35
|
+
payload: { documentKey: string; operation: Operation }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type RegisterComponentAction = {
|
|
39
|
+
type: typeof ActionTypes.REGISTER_COMPONENT
|
|
40
|
+
payload: {
|
|
41
|
+
type: string
|
|
42
|
+
meta: ComponentMeta
|
|
43
|
+
propControllerDescriptors: Record<string, PropControllerDescriptor>
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type UnregisterComponentAction = {
|
|
48
|
+
type: typeof ActionTypes.UNREGISTER_COMPONENT
|
|
49
|
+
payload: { type: string }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type RegisterReactComponentAction = {
|
|
53
|
+
type: typeof ActionTypes.REGISTER_REACT_COMPONENT
|
|
54
|
+
payload: { type: string; component: ComponentType }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type UnregisterReactComponentAction = {
|
|
58
|
+
type: typeof ActionTypes.UNREGISTER_REACT_COMPONENT
|
|
59
|
+
payload: { type: string }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type MountComponentAction = {
|
|
63
|
+
type: typeof ActionTypes.MOUNT_COMPONENT
|
|
64
|
+
payload: { elementKey: string }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type UnmountComponentAction = {
|
|
68
|
+
type: typeof ActionTypes.UNMOUNT_COMPONENT
|
|
69
|
+
payload: { elementKey: string }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type ChangeComponentHandleAction = {
|
|
73
|
+
type: typeof ActionTypes.CHANGE_COMPONENT_HANDLE
|
|
74
|
+
payload: { elementKey: string; componentHandle: unknown }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type RegisterMeasurableAction = {
|
|
78
|
+
type: typeof ActionTypes.REGISTER_MEASURABLE
|
|
79
|
+
payload: { elementKey: string; measurable: Measurable }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type UnregisterMeasurableAction = {
|
|
83
|
+
type: typeof ActionTypes.UNREGISTER_MEASURABLE
|
|
84
|
+
payload: { elementKey: string }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type ChangeElementBoxModelsAction = {
|
|
88
|
+
type: typeof ActionTypes.CHANGE_ELEMENT_BOX_MODELS
|
|
89
|
+
payload: { changedElementBoxModels: Map<string, BoxModel | null> }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type ChangeDocumentElementSizeAction = {
|
|
93
|
+
type: typeof ActionTypes.CHANGE_DOCUMENT_ELEMENT_SIZE
|
|
94
|
+
payload: { size: Size }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type ChangeDocumentElementScrollTopAction = {
|
|
98
|
+
type: typeof ActionTypes.CHANGE_DOCUMENT_ELEMENT_SCROLL_TOP
|
|
99
|
+
payload: { scrollTop: number }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type Action =
|
|
103
|
+
| ChangeDocumentAction
|
|
104
|
+
| RegisterComponentAction
|
|
105
|
+
| UnregisterComponentAction
|
|
106
|
+
| RegisterReactComponentAction
|
|
107
|
+
| UnregisterReactComponentAction
|
|
108
|
+
| MountComponentAction
|
|
109
|
+
| UnmountComponentAction
|
|
110
|
+
| ChangeComponentHandleAction
|
|
111
|
+
| RegisterMeasurableAction
|
|
112
|
+
| UnregisterMeasurableAction
|
|
113
|
+
| ChangeElementBoxModelsAction
|
|
114
|
+
| ChangeDocumentElementSizeAction
|
|
115
|
+
| ChangeDocumentElementScrollTopAction
|
|
116
|
+
|
|
117
|
+
export function changeDocument(documentKey: string, operation: Operation): ChangeDocumentAction {
|
|
118
|
+
return { type: ActionTypes.CHANGE_DOCUMENT, payload: { documentKey, operation } }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function registerComponent(
|
|
122
|
+
type: string,
|
|
123
|
+
meta: ComponentMeta,
|
|
124
|
+
propControllerDescriptors: Record<string, PropControllerDescriptor>,
|
|
125
|
+
): RegisterComponentAction {
|
|
126
|
+
return {
|
|
127
|
+
type: ActionTypes.REGISTER_COMPONENT,
|
|
128
|
+
payload: { type, meta, propControllerDescriptors },
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function unregisterComponent(type: string): UnregisterComponentAction {
|
|
133
|
+
return { type: ActionTypes.UNREGISTER_COMPONENT, payload: { type } }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function registerComponentEffect(
|
|
137
|
+
type: string,
|
|
138
|
+
meta: ComponentMeta,
|
|
139
|
+
propControllerDescriptors: Record<string, PropControllerDescriptor>,
|
|
140
|
+
): ThunkAction<() => void, unknown, unknown, Action> {
|
|
141
|
+
return dispatch => {
|
|
142
|
+
dispatch(registerComponent(type, meta, propControllerDescriptors))
|
|
143
|
+
|
|
144
|
+
return () => {
|
|
145
|
+
dispatch(unregisterComponent(type))
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function registerReactComponent(
|
|
151
|
+
type: string,
|
|
152
|
+
component: ComponentType,
|
|
153
|
+
): RegisterReactComponentAction {
|
|
154
|
+
return { type: ActionTypes.REGISTER_REACT_COMPONENT, payload: { type, component } }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function unregisterReactComponent(type: string): UnregisterReactComponentAction {
|
|
158
|
+
return { type: ActionTypes.UNREGISTER_REACT_COMPONENT, payload: { type } }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function registerReactComponentEffect(
|
|
162
|
+
type: string,
|
|
163
|
+
component: ComponentType,
|
|
164
|
+
): ThunkAction<() => void, unknown, unknown, Action> {
|
|
165
|
+
return dispatch => {
|
|
166
|
+
dispatch(registerReactComponent(type, component))
|
|
167
|
+
|
|
168
|
+
return () => {
|
|
169
|
+
dispatch(unregisterReactComponent(type))
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function mountComponent(elementKey: string): MountComponentAction {
|
|
175
|
+
return { type: ActionTypes.MOUNT_COMPONENT, payload: { elementKey } }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function unmountComponent(elementKey: string): UnmountComponentAction {
|
|
179
|
+
return { type: ActionTypes.UNMOUNT_COMPONENT, payload: { elementKey } }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function mountComponentEffect(
|
|
183
|
+
elementKey: string,
|
|
184
|
+
): ThunkAction<() => void, unknown, unknown, Action> {
|
|
185
|
+
return dispatch => {
|
|
186
|
+
dispatch(mountComponent(elementKey))
|
|
187
|
+
|
|
188
|
+
return () => {
|
|
189
|
+
dispatch(unmountComponent(elementKey))
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function changeComponentHandle(
|
|
195
|
+
elementKey: string,
|
|
196
|
+
componentHandle: unknown,
|
|
197
|
+
): ChangeComponentHandleAction {
|
|
198
|
+
return { type: ActionTypes.CHANGE_COMPONENT_HANDLE, payload: { elementKey, componentHandle } }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function registerMeasurable(
|
|
202
|
+
elementKey: string,
|
|
203
|
+
measurable: Measurable,
|
|
204
|
+
): RegisterMeasurableAction {
|
|
205
|
+
return { type: ActionTypes.REGISTER_MEASURABLE, payload: { elementKey, measurable } }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function unregisterMeasurable(elementKey: string): UnregisterMeasurableAction {
|
|
209
|
+
return { type: ActionTypes.UNREGISTER_MEASURABLE, payload: { elementKey } }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function registerMeasurableEffect(
|
|
213
|
+
elementKey: string,
|
|
214
|
+
measurable: Measurable,
|
|
215
|
+
): ThunkAction<() => void, unknown, unknown, Action> {
|
|
216
|
+
return dispatch => {
|
|
217
|
+
dispatch(registerMeasurable(elementKey, measurable))
|
|
218
|
+
|
|
219
|
+
return () => {
|
|
220
|
+
dispatch(unregisterMeasurable(elementKey))
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function changeElementBoxModels(
|
|
226
|
+
changedElementBoxModels: Map<string, BoxModel | null>,
|
|
227
|
+
): ChangeElementBoxModelsAction {
|
|
228
|
+
return { type: ActionTypes.CHANGE_ELEMENT_BOX_MODELS, payload: { changedElementBoxModels } }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function changeDocumentElementSize(size: Size): ChangeDocumentElementSizeAction {
|
|
232
|
+
return { type: ActionTypes.CHANGE_DOCUMENT_ELEMENT_SIZE, payload: { size } }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function changeDocumentElementScrollTop(
|
|
236
|
+
scrollTop: number,
|
|
237
|
+
): ChangeDocumentElementScrollTopAction {
|
|
238
|
+
return { type: ActionTypes.CHANGE_DOCUMENT_ELEMENT_SCROLL_TOP, payload: { scrollTop } }
|
|
239
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BoxModel,
|
|
3
|
+
createBox as createBoxWithoutScroll,
|
|
4
|
+
CreateBoxArgs,
|
|
5
|
+
getBox as getBoxWithoutScroll,
|
|
6
|
+
withScroll,
|
|
7
|
+
} from 'css-box-model'
|
|
8
|
+
|
|
9
|
+
import { Action, ActionTypes } from '../actions'
|
|
10
|
+
|
|
11
|
+
export type { BoxModel }
|
|
12
|
+
|
|
13
|
+
export function parse(rawString: string): number {
|
|
14
|
+
const value = Number(rawString.replace(/px$/, ''))
|
|
15
|
+
|
|
16
|
+
return Number.isFinite(value) ? value : 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createBox(boxArgs: CreateBoxArgs): BoxModel {
|
|
20
|
+
return withScroll(createBoxWithoutScroll(boxArgs))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getBox(element: Element): BoxModel {
|
|
24
|
+
return withScroll(getBoxWithoutScroll(element))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BoxModelHandle {
|
|
28
|
+
getBoxModel(): BoxModel | null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type Measurable = Element | BoxModelHandle
|
|
32
|
+
|
|
33
|
+
export function isMeasurable(value: unknown): value is Measurable {
|
|
34
|
+
if (value instanceof Element) return true
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
typeof value === 'object' &&
|
|
38
|
+
value !== null &&
|
|
39
|
+
'getBoxModel' in value &&
|
|
40
|
+
typeof (value as { getBoxModel: unknown }).getBoxModel === 'function'
|
|
41
|
+
) {
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function measure(measurable: Measurable): BoxModel | null {
|
|
49
|
+
if (measurable instanceof Element) return getBox(measurable)
|
|
50
|
+
|
|
51
|
+
return measurable.getBoxModel()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type State = {
|
|
55
|
+
measurables: Map<string, Measurable>
|
|
56
|
+
boxModels: Map<string, BoxModel>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getInitialState(): State {
|
|
60
|
+
return { measurables: new Map(), boxModels: new Map() }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getMeasurables(state: State): Map<string, Measurable> {
|
|
64
|
+
return state.measurables
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getBoxModels(state: State): Map<string, BoxModel> {
|
|
68
|
+
return state.boxModels
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getBoxModel(state: State, elementKey: string): BoxModel | null {
|
|
72
|
+
return getBoxModels(state).get(elementKey) ?? null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function reducer(state: State = getInitialState(), action: Action) {
|
|
76
|
+
switch (action.type) {
|
|
77
|
+
case ActionTypes.REGISTER_MEASURABLE:
|
|
78
|
+
return {
|
|
79
|
+
...state,
|
|
80
|
+
measurables: new Map(getMeasurables(state)).set(
|
|
81
|
+
action.payload.elementKey,
|
|
82
|
+
action.payload.measurable,
|
|
83
|
+
),
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case ActionTypes.UNREGISTER_MEASURABLE: {
|
|
87
|
+
const nextMeasurables = new Map(getMeasurables(state))
|
|
88
|
+
|
|
89
|
+
const deleted = nextMeasurables.delete(action.payload.elementKey)
|
|
90
|
+
|
|
91
|
+
return deleted ? { ...state, measurables: nextMeasurables } : state
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
case ActionTypes.CHANGE_ELEMENT_BOX_MODELS: {
|
|
95
|
+
const { changedElementBoxModels } = action.payload
|
|
96
|
+
|
|
97
|
+
if (changedElementBoxModels.size === 0) return state
|
|
98
|
+
|
|
99
|
+
const nextBoxModels = new Map(getBoxModels(state))
|
|
100
|
+
|
|
101
|
+
changedElementBoxModels.forEach((boxModel, elementKey) => {
|
|
102
|
+
if (boxModel == null) nextBoxModels.delete(elementKey)
|
|
103
|
+
else nextBoxModels.set(elementKey, boxModel)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return { ...state, boxModels: nextBoxModels }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
default:
|
|
110
|
+
return state
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Action, ActionTypes } from '../actions'
|
|
2
|
+
|
|
3
|
+
export type ComponentIcon =
|
|
4
|
+
| 'Carousel40'
|
|
5
|
+
| 'Code40'
|
|
6
|
+
| 'Countdown40'
|
|
7
|
+
| 'Cube40'
|
|
8
|
+
| 'Divider40'
|
|
9
|
+
| 'Form40'
|
|
10
|
+
| 'Navigation40'
|
|
11
|
+
| 'SocialLinks40'
|
|
12
|
+
| 'Video40'
|
|
13
|
+
|
|
14
|
+
export type ComponentMeta = { label: string; icon: ComponentIcon; hidden: boolean }
|
|
15
|
+
|
|
16
|
+
export type State = Map<string, ComponentMeta>
|
|
17
|
+
|
|
18
|
+
export function getInitialState({
|
|
19
|
+
componentsMeta = new Map(),
|
|
20
|
+
}: { componentsMeta?: Map<string, ComponentMeta> } = {}): State {
|
|
21
|
+
return componentsMeta
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getComponentsMeta(state: State): Map<string, ComponentMeta> {
|
|
25
|
+
return state
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function reducer(state: State = getInitialState(), action: Action): State {
|
|
29
|
+
switch (action.type) {
|
|
30
|
+
case ActionTypes.REGISTER_COMPONENT:
|
|
31
|
+
return new Map(state).set(action.payload.type, action.payload.meta)
|
|
32
|
+
|
|
33
|
+
case ActionTypes.UNREGISTER_COMPONENT: {
|
|
34
|
+
const nextState = new Map(state)
|
|
35
|
+
|
|
36
|
+
const deleted = nextState.delete(action.payload.type)
|
|
37
|
+
|
|
38
|
+
return deleted ? nextState : state
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
default:
|
|
42
|
+
return state
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Action, ActionTypes } from '../actions'
|
|
2
|
+
import { PropControllerDescriptor } from '../../prop-controllers'
|
|
3
|
+
|
|
4
|
+
export type { PropControllerDescriptor }
|
|
5
|
+
|
|
6
|
+
export type State = Map<string, Record<string, PropControllerDescriptor>>
|
|
7
|
+
|
|
8
|
+
export function getInitialState({
|
|
9
|
+
propControllerDescriptors = new Map(),
|
|
10
|
+
}: {
|
|
11
|
+
propControllerDescriptors?: Map<string, Record<string, PropControllerDescriptor>>
|
|
12
|
+
} = {}): State {
|
|
13
|
+
return propControllerDescriptors
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getPropControllerDescriptors(
|
|
17
|
+
state: State,
|
|
18
|
+
): Map<string, Record<string, PropControllerDescriptor>> {
|
|
19
|
+
return state
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getComponentPropControllerDescriptors(
|
|
23
|
+
state: State,
|
|
24
|
+
componentType: string,
|
|
25
|
+
): Record<string, PropControllerDescriptor> | null {
|
|
26
|
+
return getPropControllerDescriptors(state).get(componentType) ?? null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function reducer(state: State = getInitialState(), action: Action): State {
|
|
30
|
+
switch (action.type) {
|
|
31
|
+
case ActionTypes.REGISTER_COMPONENT:
|
|
32
|
+
return new Map(state).set(action.payload.type, action.payload.propControllerDescriptors)
|
|
33
|
+
|
|
34
|
+
case ActionTypes.UNREGISTER_COMPONENT: {
|
|
35
|
+
const nextState = new Map(state)
|
|
36
|
+
|
|
37
|
+
const deleted = nextState.delete(action.payload.type)
|
|
38
|
+
|
|
39
|
+
return deleted ? nextState : state
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
default:
|
|
43
|
+
return state
|
|
44
|
+
}
|
|
45
|
+
}
|