@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,47 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClassAttributes,
|
|
3
|
+
ComponentClass,
|
|
4
|
+
PropsWithoutRef,
|
|
5
|
+
RefAttributes,
|
|
6
|
+
VoidFunctionComponent,
|
|
7
|
+
} from 'react'
|
|
8
|
+
|
|
9
|
+
import { Action, ActionTypes } from '../actions'
|
|
10
|
+
|
|
11
|
+
export type ComponentType<P = Record<string, any>, T = any> =
|
|
12
|
+
| ComponentClass<PropsWithoutRef<P> & ClassAttributes<T>>
|
|
13
|
+
| VoidFunctionComponent<PropsWithoutRef<P> & RefAttributes<T>>
|
|
14
|
+
|
|
15
|
+
export type State = Map<string, ComponentType>
|
|
16
|
+
|
|
17
|
+
export function getInitialState({
|
|
18
|
+
reactComponents = new Map(),
|
|
19
|
+
}: { reactComponents?: Map<string, ComponentType> } = {}): State {
|
|
20
|
+
return reactComponents
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getReactComponents(state: State): Map<string, ComponentType> {
|
|
24
|
+
return state
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getReactComponent(state: State, type: string): ComponentType | null {
|
|
28
|
+
return getReactComponents(state).get(type) ?? null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function reducer(state: State = getInitialState(), action: Action) {
|
|
32
|
+
switch (action.type) {
|
|
33
|
+
case ActionTypes.REGISTER_REACT_COMPONENT:
|
|
34
|
+
return new Map(state).set(action.payload.type, action.payload.component)
|
|
35
|
+
|
|
36
|
+
case ActionTypes.UNREGISTER_REACT_COMPONENT: {
|
|
37
|
+
const nextState = new Map(state)
|
|
38
|
+
|
|
39
|
+
const deleted = nextState.delete(action.payload.type)
|
|
40
|
+
|
|
41
|
+
return deleted ? nextState : state
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
default:
|
|
45
|
+
return state
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Action } from '../actions'
|
|
2
|
+
|
|
3
|
+
export type Data = undefined | null | boolean | number | string | Data[] | { [key: string]: Data }
|
|
4
|
+
|
|
5
|
+
export type ElementData = { type: string; key: string; props: Record<string, Data> }
|
|
6
|
+
|
|
7
|
+
export type ElementReference = { type: 'reference'; key: string; value: string }
|
|
8
|
+
|
|
9
|
+
export type Element = ElementData | ElementReference
|
|
10
|
+
|
|
11
|
+
export function isElementReference(element: Element): element is ElementReference {
|
|
12
|
+
return !('props' in element)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type Document = {
|
|
16
|
+
rootElement: Element
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createDocument(rootElement: Element): Document {
|
|
20
|
+
return { rootElement }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type State = Map<string, Document>
|
|
24
|
+
|
|
25
|
+
export function getInitialState({
|
|
26
|
+
rootElements = new Map(),
|
|
27
|
+
}: { rootElements?: Map<string, Element> } = {}): State {
|
|
28
|
+
const initialState = new Map()
|
|
29
|
+
|
|
30
|
+
rootElements.forEach((rootElement, elementKey) => {
|
|
31
|
+
initialState.set(elementKey, createDocument(rootElement))
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return initialState
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getDocuments(state: State): Map<string, Document> {
|
|
38
|
+
return state
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getDocument(state: State, documentKey: string): Document | null {
|
|
42
|
+
return getDocuments(state).get(documentKey) ?? null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getDocumentRootElement(state: State, documentKey: string): Element | null {
|
|
46
|
+
return getDocument(state, documentKey)?.rootElement ?? null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function reducer(state: State = getInitialState(), _action: Action): State {
|
|
50
|
+
return state
|
|
51
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Operation } from 'ot-json0'
|
|
2
|
+
import { removeIn, setIn } from 'immutable'
|
|
3
|
+
|
|
4
|
+
import * as ReadOnlyDocuments from './read-only-documents'
|
|
5
|
+
import { Action, ActionTypes } from '../actions'
|
|
6
|
+
|
|
7
|
+
export type { Document, Element, ElementData, ElementReference } from './read-only-documents'
|
|
8
|
+
export { isElementReference } from './read-only-documents'
|
|
9
|
+
export type { Operation }
|
|
10
|
+
|
|
11
|
+
function apply(data: ReadOnlyDocuments.Element, operation: Operation): ReadOnlyDocuments.Element {
|
|
12
|
+
let applied = data
|
|
13
|
+
|
|
14
|
+
operation.forEach(component => {
|
|
15
|
+
// @ts-expect-error: `ld` isn't in all possible values of `component`
|
|
16
|
+
if (component.ld != null) applied = removeIn(applied, component.p)
|
|
17
|
+
|
|
18
|
+
// @ts-expect-error: `od` isn't in all possible values of `component`
|
|
19
|
+
if (component.od != null) applied = removeIn(applied, component.p)
|
|
20
|
+
|
|
21
|
+
// @ts-expect-error: `li` isn't in all possible values of `component`
|
|
22
|
+
if (component.li != null) applied = setIn(applied, component.p, component.li)
|
|
23
|
+
|
|
24
|
+
// @ts-expect-error: `oi` isn't in all possible values of `component`
|
|
25
|
+
if (component.oi != null) applied = setIn(applied, component.p, component.oi)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return applied
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type State = ReadOnlyDocuments.State
|
|
32
|
+
|
|
33
|
+
export function getInitialState({
|
|
34
|
+
rootElements,
|
|
35
|
+
}: {
|
|
36
|
+
rootElements?: Map<string, ReadOnlyDocuments.Element>
|
|
37
|
+
} = {}): State {
|
|
38
|
+
return ReadOnlyDocuments.getInitialState({ rootElements })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getReadOnlyDocumentsStateSlice(state: State): ReadOnlyDocuments.State {
|
|
42
|
+
return state
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getDocumentRootElement(
|
|
46
|
+
state: State,
|
|
47
|
+
documentKey: string,
|
|
48
|
+
): ReadOnlyDocuments.Element | null {
|
|
49
|
+
return ReadOnlyDocuments.getDocumentRootElement(
|
|
50
|
+
getReadOnlyDocumentsStateSlice(state),
|
|
51
|
+
documentKey,
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function reducer(state: State = getInitialState(), action: Action): State {
|
|
56
|
+
switch (action.type) {
|
|
57
|
+
case ActionTypes.CHANGE_DOCUMENT: {
|
|
58
|
+
const currentRootElement = getDocumentRootElement(state, action.payload.documentKey)
|
|
59
|
+
|
|
60
|
+
if (currentRootElement == null) return state
|
|
61
|
+
|
|
62
|
+
const nextRootElement = apply(currentRootElement, action.payload.operation)
|
|
63
|
+
|
|
64
|
+
return currentRootElement === nextRootElement
|
|
65
|
+
? state
|
|
66
|
+
: new Map(state).set(
|
|
67
|
+
action.payload.documentKey,
|
|
68
|
+
ReadOnlyDocuments.createDocument(nextRootElement),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
default:
|
|
73
|
+
return state
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyMiddleware,
|
|
3
|
+
combineReducers,
|
|
4
|
+
createStore,
|
|
5
|
+
Dispatch as ReduxDispatch,
|
|
6
|
+
Middleware,
|
|
7
|
+
MiddlewareAPI,
|
|
8
|
+
PreloadedState,
|
|
9
|
+
Store as ReduxStore,
|
|
10
|
+
} from 'redux'
|
|
11
|
+
import thunk, { ThunkAction, ThunkDispatch } from 'redux-thunk'
|
|
12
|
+
import deepEqual from '../utils/deepEqual'
|
|
13
|
+
|
|
14
|
+
import * as Documents from './modules/read-write-documents'
|
|
15
|
+
import * as ReactComponents from './modules/react-components'
|
|
16
|
+
import * as BoxModels from './modules/box-models'
|
|
17
|
+
import * as ComponentsMeta from './modules/components-meta'
|
|
18
|
+
import * as PropControllers from './modules/prop-controllers'
|
|
19
|
+
import {
|
|
20
|
+
Action,
|
|
21
|
+
changeDocumentElementSize,
|
|
22
|
+
registerComponent,
|
|
23
|
+
registerMeasurable,
|
|
24
|
+
unregisterMeasurable,
|
|
25
|
+
} from './actions'
|
|
26
|
+
import { ActionTypes } from './actions'
|
|
27
|
+
|
|
28
|
+
export type { Operation } from './modules/read-write-documents'
|
|
29
|
+
export type { BoxModelHandle } from './modules/box-models'
|
|
30
|
+
export { createBox, getBox, parse } from './modules/box-models'
|
|
31
|
+
|
|
32
|
+
const reducer = combineReducers({
|
|
33
|
+
documents: Documents.reducer,
|
|
34
|
+
reactComponents: ReactComponents.reducer,
|
|
35
|
+
boxModels: BoxModels.reducer,
|
|
36
|
+
componentsMeta: ComponentsMeta.reducer,
|
|
37
|
+
propControllers: PropControllers.reducer,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export type State = ReturnType<typeof reducer>
|
|
41
|
+
|
|
42
|
+
function getBoxModelsStateSlice(state: State): BoxModels.State {
|
|
43
|
+
return state.boxModels
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getMeasurables(state: State): Map<string, BoxModels.Measurable> {
|
|
47
|
+
return BoxModels.getMeasurables(getBoxModelsStateSlice(state))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getBoxModels(state: State): Map<string, BoxModels.BoxModel> {
|
|
51
|
+
return BoxModels.getBoxModels(getBoxModelsStateSlice(state))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getBoxModel(state: State, elementKey: string): BoxModels.BoxModel | null {
|
|
55
|
+
return BoxModels.getBoxModel(getBoxModelsStateSlice(state), elementKey)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getComponentsMetaStateSlice(state: State): ComponentsMeta.State {
|
|
59
|
+
return state.componentsMeta
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getComponentsMeta(state: State): Map<string, ComponentsMeta.ComponentMeta> {
|
|
63
|
+
return ComponentsMeta.getComponentsMeta(getComponentsMetaStateSlice(state))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getPropControllersStateSlice(state: State): PropControllers.State {
|
|
67
|
+
return state.propControllers
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getComponentPropControllerDescriptors(
|
|
71
|
+
state: State,
|
|
72
|
+
componentType: string,
|
|
73
|
+
): Record<string, PropControllers.PropControllerDescriptor> | null {
|
|
74
|
+
return PropControllers.getComponentPropControllerDescriptors(
|
|
75
|
+
getPropControllersStateSlice(state),
|
|
76
|
+
componentType,
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function measureElements(): ThunkAction<void, State, unknown, Action> {
|
|
81
|
+
return (dispatch, getState) => {
|
|
82
|
+
const measurables = getMeasurables(getState())
|
|
83
|
+
const currentBoxModels = getBoxModels(getState())
|
|
84
|
+
const measuredBoxModels = new Map(
|
|
85
|
+
Array.from(measurables.entries())
|
|
86
|
+
.map(([elementKey, measurable]) => {
|
|
87
|
+
const boxModel = BoxModels.measure(measurable)
|
|
88
|
+
|
|
89
|
+
return boxModel ? ([elementKey, boxModel] as const) : null
|
|
90
|
+
})
|
|
91
|
+
.filter((entry): entry is NonNullable<typeof entry> => entry != null),
|
|
92
|
+
)
|
|
93
|
+
const changedBoxModels = new Map<string, BoxModels.BoxModel | null>()
|
|
94
|
+
|
|
95
|
+
currentBoxModels.forEach((_boxModel, elementKey) => {
|
|
96
|
+
if (!measuredBoxModels.has(elementKey)) changedBoxModels.set(elementKey, null)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
measuredBoxModels.forEach((measuredBoxModel, elementKey) => {
|
|
100
|
+
const currentBoxModel = getBoxModel(getState(), elementKey)
|
|
101
|
+
|
|
102
|
+
if (currentBoxModel == null || !deepEqual(currentBoxModel, measuredBoxModel)) {
|
|
103
|
+
changedBoxModels.set(elementKey, measuredBoxModel)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
if (changedBoxModels.size > 0) {
|
|
108
|
+
dispatch({
|
|
109
|
+
type: ActionTypes.CHANGE_ELEMENT_BOX_MODELS,
|
|
110
|
+
payload: { changedElementBoxModels: changedBoxModels },
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function startMeasuringElements(): ThunkAction<() => void, State, unknown, Action> {
|
|
117
|
+
return dispatch => {
|
|
118
|
+
let animationFrameHandle = requestAnimationFrame(handleAnimationFrameRequest)
|
|
119
|
+
|
|
120
|
+
return () => {
|
|
121
|
+
cancelAnimationFrame(animationFrameHandle)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function handleAnimationFrameRequest() {
|
|
125
|
+
dispatch(measureElements())
|
|
126
|
+
|
|
127
|
+
animationFrameHandle = requestAnimationFrame(handleAnimationFrameRequest)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type Size = {
|
|
133
|
+
offsetWidth: number
|
|
134
|
+
offsetHeight: number
|
|
135
|
+
clientWidth: number
|
|
136
|
+
clientHeight: number
|
|
137
|
+
scrollWidth: number
|
|
138
|
+
scrollHeight: number
|
|
139
|
+
scrollTop: number
|
|
140
|
+
scrollLeft: number
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getElementSize(element: HTMLElement): Size {
|
|
144
|
+
return {
|
|
145
|
+
offsetWidth: element.offsetWidth,
|
|
146
|
+
offsetHeight: element.offsetHeight,
|
|
147
|
+
clientWidth: element.clientWidth,
|
|
148
|
+
clientHeight: element.clientHeight,
|
|
149
|
+
scrollWidth: element.scrollWidth,
|
|
150
|
+
scrollHeight: element.scrollHeight,
|
|
151
|
+
scrollTop: element.scrollTop,
|
|
152
|
+
scrollLeft: element.scrollLeft,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function startMeasuringDocumentElement(): ThunkAction<() => void, unknown, unknown, Action> {
|
|
157
|
+
return dispatch => {
|
|
158
|
+
let animationFrameHandle = requestAnimationFrame(handleAnimationFrameRequest)
|
|
159
|
+
let lastSize: Size
|
|
160
|
+
|
|
161
|
+
return () => {
|
|
162
|
+
cancelAnimationFrame(animationFrameHandle)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function handleAnimationFrameRequest() {
|
|
166
|
+
const nextSize = getElementSize(window.document.documentElement)
|
|
167
|
+
|
|
168
|
+
if (!deepEqual(lastSize, nextSize)) {
|
|
169
|
+
lastSize = nextSize
|
|
170
|
+
|
|
171
|
+
dispatch(changeDocumentElementSize(nextSize))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
animationFrameHandle = requestAnimationFrame(handleAnimationFrameRequest)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function initialize(): ThunkAction<() => void, State, unknown, Action> {
|
|
180
|
+
return dispatch => {
|
|
181
|
+
const stopMeasuringElements = dispatch(startMeasuringElements())
|
|
182
|
+
const stopMeasuringDocumentElement = dispatch(startMeasuringDocumentElement())
|
|
183
|
+
|
|
184
|
+
return () => {
|
|
185
|
+
stopMeasuringElements()
|
|
186
|
+
stopMeasuringDocumentElement()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export type Dispatch = ThunkDispatch<State, unknown, Action>
|
|
192
|
+
|
|
193
|
+
function measureBoxModelsMiddleware(): Middleware<Dispatch, State, Dispatch> {
|
|
194
|
+
return ({ dispatch }: MiddlewareAPI<Dispatch>) => (next: ReduxDispatch<Action>) => {
|
|
195
|
+
return (action: Action): Action => {
|
|
196
|
+
switch (action.type) {
|
|
197
|
+
case ActionTypes.CHANGE_COMPONENT_HANDLE: {
|
|
198
|
+
if (BoxModels.isMeasurable(action.payload.componentHandle)) {
|
|
199
|
+
dispatch(registerMeasurable(action.payload.elementKey, action.payload.componentHandle))
|
|
200
|
+
} else {
|
|
201
|
+
dispatch(unregisterMeasurable(action.payload.elementKey))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
break
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case ActionTypes.UNMOUNT_COMPONENT:
|
|
208
|
+
dispatch(unregisterMeasurable(action.payload.elementKey))
|
|
209
|
+
break
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return next(action)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function messageChannelMiddleware(): Middleware<Dispatch, State, Dispatch> {
|
|
218
|
+
return ({ dispatch, getState }: MiddlewareAPI<Dispatch, State>) => (
|
|
219
|
+
next: ReduxDispatch<Action>,
|
|
220
|
+
) => {
|
|
221
|
+
const messageChannel = new MessageChannel()
|
|
222
|
+
|
|
223
|
+
window.parent.postMessage(messageChannel.port2, '*', [messageChannel.port2])
|
|
224
|
+
|
|
225
|
+
messageChannel.port1.onmessage = (event: MessageEvent<Action>) => dispatch(event.data)
|
|
226
|
+
|
|
227
|
+
const state = getState()
|
|
228
|
+
const registeredComponentsMeta = getComponentsMeta(state)
|
|
229
|
+
|
|
230
|
+
registeredComponentsMeta.forEach((componentMeta, componentType) => {
|
|
231
|
+
const propControllerDescriptors = getComponentPropControllerDescriptors(state, componentType)
|
|
232
|
+
|
|
233
|
+
if (propControllerDescriptors != null) {
|
|
234
|
+
messageChannel.port1.postMessage(
|
|
235
|
+
registerComponent(componentType, componentMeta, propControllerDescriptors),
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
return (action: Action): Action => {
|
|
241
|
+
switch (action.type) {
|
|
242
|
+
case ActionTypes.CHANGE_ELEMENT_BOX_MODELS:
|
|
243
|
+
case ActionTypes.MOUNT_COMPONENT:
|
|
244
|
+
case ActionTypes.UNMOUNT_COMPONENT:
|
|
245
|
+
case ActionTypes.REGISTER_COMPONENT:
|
|
246
|
+
case ActionTypes.UNREGISTER_COMPONENT:
|
|
247
|
+
case ActionTypes.CHANGE_DOCUMENT_ELEMENT_SIZE:
|
|
248
|
+
messageChannel.port1.postMessage(action)
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
case ActionTypes.CHANGE_DOCUMENT_ELEMENT_SCROLL_TOP:
|
|
252
|
+
window.document.documentElement.scrollTop = action.payload.scrollTop
|
|
253
|
+
break
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return next(action)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export type Store = ReduxStore<State, Action> & { dispatch: Dispatch }
|
|
262
|
+
|
|
263
|
+
export function configureStore({
|
|
264
|
+
preloadedState,
|
|
265
|
+
}: { preloadedState?: PreloadedState<State> } = {}): Store {
|
|
266
|
+
return createStore(
|
|
267
|
+
reducer,
|
|
268
|
+
preloadedState,
|
|
269
|
+
applyMiddleware(thunk, measureBoxModelsMiddleware(), messageChannelMiddleware()),
|
|
270
|
+
)
|
|
271
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyMiddleware,
|
|
3
|
+
combineReducers,
|
|
4
|
+
createStore,
|
|
5
|
+
PreloadedState,
|
|
6
|
+
Store as ReduxStore,
|
|
7
|
+
} from 'redux'
|
|
8
|
+
import thunk, { ThunkDispatch } from 'redux-thunk'
|
|
9
|
+
|
|
10
|
+
import * as Documents from './modules/read-only-documents'
|
|
11
|
+
import * as ReactComponents from './modules/react-components'
|
|
12
|
+
import * as ComponentsMeta from './modules/components-meta'
|
|
13
|
+
import * as PropControllers from './modules/prop-controllers'
|
|
14
|
+
import { Action } from './actions'
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
Data,
|
|
18
|
+
Document,
|
|
19
|
+
Element,
|
|
20
|
+
ElementData,
|
|
21
|
+
ElementReference,
|
|
22
|
+
} from './modules/read-only-documents'
|
|
23
|
+
export { isElementReference } from './modules/read-only-documents'
|
|
24
|
+
export type { ComponentType } from './modules/react-components'
|
|
25
|
+
|
|
26
|
+
const reducer = combineReducers({
|
|
27
|
+
documents: Documents.reducer,
|
|
28
|
+
reactComponents: ReactComponents.reducer,
|
|
29
|
+
componentsMeta: ComponentsMeta.reducer,
|
|
30
|
+
propControllers: PropControllers.reducer,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export type State = ReturnType<typeof reducer>
|
|
34
|
+
|
|
35
|
+
function getDocumentsStateSlice(state: State): Documents.State {
|
|
36
|
+
return state.documents
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getDocumentRootElement(
|
|
40
|
+
state: State,
|
|
41
|
+
documentKey: string,
|
|
42
|
+
): Documents.Element | null {
|
|
43
|
+
return Documents.getDocumentRootElement(getDocumentsStateSlice(state), documentKey)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getReactComponentsStateSlice(state: State): ReactComponents.State {
|
|
47
|
+
return state.reactComponents
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getReactComponent(
|
|
51
|
+
state: State,
|
|
52
|
+
type: string,
|
|
53
|
+
): ReactComponents.ComponentType | null {
|
|
54
|
+
return ReactComponents.getReactComponent(getReactComponentsStateSlice(state), type)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type Dispatch = ThunkDispatch<State, unknown, Action>
|
|
58
|
+
|
|
59
|
+
export type Store = ReduxStore<State, Action> & { dispatch: Dispatch }
|
|
60
|
+
|
|
61
|
+
export function configureStore({
|
|
62
|
+
rootElements,
|
|
63
|
+
preloadedState,
|
|
64
|
+
}: {
|
|
65
|
+
rootElements?: Map<string, Documents.Element>
|
|
66
|
+
preloadedState?: PreloadedState<State>
|
|
67
|
+
} = {}): Store {
|
|
68
|
+
return createStore(
|
|
69
|
+
reducer,
|
|
70
|
+
{ ...preloadedState, documents: Documents.getInitialState({ rootElements }) },
|
|
71
|
+
applyMiddleware(thunk),
|
|
72
|
+
)
|
|
73
|
+
}
|
package/src/style.css
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import shallowEqual from "./shallowEqual";
|
|
2
|
+
|
|
3
|
+
const { hasOwnProperty } = Object.prototype;
|
|
4
|
+
|
|
5
|
+
const deepEqual = (a: unknown, b: unknown): boolean => {
|
|
6
|
+
if (shallowEqual(a, b)) return true;
|
|
7
|
+
|
|
8
|
+
if (
|
|
9
|
+
typeof a !== "object" ||
|
|
10
|
+
a === null ||
|
|
11
|
+
typeof b !== "object" ||
|
|
12
|
+
b === null
|
|
13
|
+
)
|
|
14
|
+
return false;
|
|
15
|
+
|
|
16
|
+
const keysA = Object.keys(a);
|
|
17
|
+
const keysB = Object.keys(b);
|
|
18
|
+
|
|
19
|
+
if (keysA.length !== keysB.length) return false;
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < keysA.length; i += 1) {
|
|
22
|
+
if (
|
|
23
|
+
!hasOwnProperty.call(b, keysA[i]) ||
|
|
24
|
+
// @ts-expect-error: {}[string] is OK.
|
|
25
|
+
!deepEqual(a[keysA[i]], b[keysA[i]])
|
|
26
|
+
)
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default deepEqual;
|
package/src/utils/is.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import is from "./is";
|
|
2
|
+
|
|
3
|
+
const { hasOwnProperty } = Object.prototype;
|
|
4
|
+
|
|
5
|
+
const shallowEqual = (a: unknown, b: unknown): boolean => {
|
|
6
|
+
if (is(a, b)) return true;
|
|
7
|
+
|
|
8
|
+
if (
|
|
9
|
+
typeof a !== "object" ||
|
|
10
|
+
a === null ||
|
|
11
|
+
typeof b !== "object" ||
|
|
12
|
+
b === null
|
|
13
|
+
)
|
|
14
|
+
return false;
|
|
15
|
+
|
|
16
|
+
const keysA = Object.keys(a);
|
|
17
|
+
const keysB = Object.keys(b);
|
|
18
|
+
|
|
19
|
+
if (keysA.length !== keysB.length) return false;
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < keysA.length; i += 1) {
|
|
22
|
+
// @ts-expect-error: {}[string] is OK.
|
|
23
|
+
if (!hasOwnProperty.call(b, keysA[i]) || !is(a[keysA[i]], b[keysA[i]]))
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default shallowEqual;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"lib": ["ESNext", "DOM"],
|
|
7
|
+
"moduleResolution": "Node",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"noUnusedLocals": true,
|
|
13
|
+
"noUnusedParameters": true,
|
|
14
|
+
"noImplicitReturns": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"declaration": true,
|
|
17
|
+
"declarationMap": true,
|
|
18
|
+
"declarationDir": "dist/types",
|
|
19
|
+
"emitDeclarationOnly": true,
|
|
20
|
+
"jsx": "react-jsx"
|
|
21
|
+
},
|
|
22
|
+
"include": ["./src", "./types"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
declare module 'ot-json0' {
|
|
2
|
+
type JSONData = null | boolean | number | string | Array<JSONData> | { [ey: string]: JSONData }
|
|
3
|
+
|
|
4
|
+
type ReadOnlyJSONData =
|
|
5
|
+
| null
|
|
6
|
+
| boolean
|
|
7
|
+
| number
|
|
8
|
+
| string
|
|
9
|
+
| ReadonlyArray<ReadOnlyJSONData>
|
|
10
|
+
| { readonly [key: string]: ReadOnlyJSONData }
|
|
11
|
+
|
|
12
|
+
export type Snapshot = JSONData
|
|
13
|
+
|
|
14
|
+
export type ReadOnlySnapshot = ReadOnlyJSONData
|
|
15
|
+
|
|
16
|
+
type Path = Array<string | number>
|
|
17
|
+
|
|
18
|
+
type OperationComponent =
|
|
19
|
+
| { p: Path; li: ReadOnlySnapshot }
|
|
20
|
+
| { p: Path; ld: ReadOnlySnapshot }
|
|
21
|
+
| { p: Path; ld: ReadOnlySnapshot; li: ReadOnlySnapshot }
|
|
22
|
+
| { p: Path; oi: ReadOnlySnapshot }
|
|
23
|
+
| { p: Path; od: ReadOnlySnapshot }
|
|
24
|
+
| { p: Path; od: ReadOnlySnapshot; oi: ReadOnlySnapshot }
|
|
25
|
+
|
|
26
|
+
export type Operation = ReadonlyArray<OperationComponent>
|
|
27
|
+
|
|
28
|
+
type Type = {
|
|
29
|
+
apply(snapshot: Snapshot, op: Operation): Snapshot
|
|
30
|
+
invert(op: Operation): Operation
|
|
31
|
+
compose(op1: Operation, op2: Operation): Operation
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const type: Type
|
|
35
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
build: {
|
|
8
|
+
emptyOutDir: false,
|
|
9
|
+
sourcemap: true,
|
|
10
|
+
minify: false,
|
|
11
|
+
// @ts-expect-error: we provide `build.rollupOptions.input` so `build.lib.entry` isn't required.
|
|
12
|
+
lib: { formats: ['es', 'cjs'] },
|
|
13
|
+
rollupOptions: {
|
|
14
|
+
input: {
|
|
15
|
+
main: path.resolve(__dirname, 'src'),
|
|
16
|
+
['prop-controllers']: path.resolve(__dirname, 'src', 'prop-controllers'),
|
|
17
|
+
react: path.resolve(__dirname, 'src', 'react'),
|
|
18
|
+
['box-model']: path.resolve(__dirname, 'src', 'box-model'),
|
|
19
|
+
components: path.resolve(__dirname, 'src', 'components'),
|
|
20
|
+
},
|
|
21
|
+
output: {
|
|
22
|
+
entryFileNames: '[name].[format].js',
|
|
23
|
+
chunkFileNames: '[name].[format].js',
|
|
24
|
+
},
|
|
25
|
+
external: ['react', 'styled-components'],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
})
|