@simpreact/simpreact 0.0.0-alpha.1f6ee65
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/LICENSE.txt +21 -0
- package/README.md +1 -0
- package/core/context.d.ts +18 -0
- package/core/context.js +18 -0
- package/core/createElement.d.ts +26 -0
- package/core/createElement.js +166 -0
- package/core/fragment.d.ts +5 -0
- package/core/fragment.js +1 -0
- package/core/global.d.ts +21 -0
- package/core/global.js +5 -0
- package/core/hostAdapter.d.ts +17 -0
- package/core/hostAdapter.js +1 -0
- package/core/index.d.ts +3 -0
- package/core/index.js +3 -0
- package/core/internal.d.ts +8 -0
- package/core/internal.js +8 -0
- package/core/mounting.d.ts +12 -0
- package/core/mounting.js +95 -0
- package/core/patching.d.ts +8 -0
- package/core/patching.js +279 -0
- package/core/rerender.d.ts +2 -0
- package/core/rerender.js +5 -0
- package/core/unmounting.d.ts +8 -0
- package/core/unmounting.js +65 -0
- package/dom/domAdapter.d.ts +2 -0
- package/dom/domAdapter.js +159 -0
- package/dom/index.d.ts +1 -0
- package/dom/index.js +1 -0
- package/dom/render.d.ts +9 -0
- package/dom/render.js +29 -0
- package/hooks/index.d.ts +12 -0
- package/hooks/index.js +68 -0
- package/jsx-runtime/index.d.ts +7 -0
- package/jsx-runtime/index.js +16 -0
- package/package.json +45 -0
- package/shared/EventBus.d.ts +18 -0
- package/shared/EventBus.js +28 -0
- package/shared/index.d.ts +4 -0
- package/shared/index.js +4 -0
- package/shared/lang.d.ts +3 -0
- package/shared/lang.js +3 -0
- package/shared/types.d.ts +8 -0
- package/shared/types.js +1 -0
- package/shared/utils.d.ts +2 -0
- package/shared/utils.js +7 -0
package/core/patching.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { normalizeRoot } from './createElement';
|
|
2
|
+
import { EMPTY_MAP, EMPTY_OBJECT, isPrimitive } from '../shared';
|
|
3
|
+
import { clearElementHostReference, remove, removeAllChildren, unmount, unmountAllChildren } from './unmounting';
|
|
4
|
+
import { mount, mountArrayChildren } from './mounting';
|
|
5
|
+
import { GLOBAL } from './global';
|
|
6
|
+
export function patch(prevElement, nextElement, parentReference, nextReference, contextMap) {
|
|
7
|
+
if (prevElement.type !== nextElement.type || prevElement.key !== nextElement.key) {
|
|
8
|
+
replaceWithNewElement(prevElement, nextElement, parentReference, contextMap);
|
|
9
|
+
}
|
|
10
|
+
else if (nextElement.flag === 'HOST') {
|
|
11
|
+
patchElement(prevElement, nextElement, contextMap);
|
|
12
|
+
}
|
|
13
|
+
else if (nextElement.flag === 'FC') {
|
|
14
|
+
if (prevElement.store != null) {
|
|
15
|
+
nextElement.store = prevElement.store;
|
|
16
|
+
}
|
|
17
|
+
patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap);
|
|
18
|
+
}
|
|
19
|
+
else if (nextElement.flag === 'TEXT') {
|
|
20
|
+
patchText(prevElement, nextElement);
|
|
21
|
+
}
|
|
22
|
+
else if (nextElement.flag === 'FRAGMENT') {
|
|
23
|
+
patchFragment(prevElement, nextElement, parentReference, nextReference, contextMap);
|
|
24
|
+
}
|
|
25
|
+
else if (nextElement.flag === 'PROVIDER') {
|
|
26
|
+
patchProvider(prevElement, nextElement, parentReference, nextReference, contextMap);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function replaceWithNewElement(prevElement, nextElement, parentReference, contextMap) {
|
|
33
|
+
unmount(prevElement);
|
|
34
|
+
if (nextElement.flag === 'HOST' && prevElement.flag === 'HOST') {
|
|
35
|
+
mount(nextElement, null, null, contextMap);
|
|
36
|
+
GLOBAL.hostAdapter.replaceChild(parentReference, nextElement.reference, prevElement.reference);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
mount(nextElement, parentReference, findHostReferenceFromElement(prevElement), contextMap);
|
|
40
|
+
clearElementHostReference(prevElement, parentReference);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function findHostReferenceFromElement(element) {
|
|
44
|
+
let flag;
|
|
45
|
+
let temp = element;
|
|
46
|
+
while (temp != null) {
|
|
47
|
+
flag = temp.flag;
|
|
48
|
+
if (flag === 'HOST' || flag === 'TEXT') {
|
|
49
|
+
return temp.reference;
|
|
50
|
+
}
|
|
51
|
+
temp = (Array.isArray(temp.children) ? temp.children[0] : temp.children);
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
function patchElement(prevElement, nextElement, contextMap) {
|
|
56
|
+
const hostReference = (nextElement.reference = prevElement.reference);
|
|
57
|
+
const prevProps = prevElement.props;
|
|
58
|
+
const nextProps = nextElement.props;
|
|
59
|
+
for (const propName in nextProps) {
|
|
60
|
+
const prevValue = prevProps[propName];
|
|
61
|
+
const nextValue = nextProps[propName];
|
|
62
|
+
if (prevValue !== nextValue) {
|
|
63
|
+
GLOBAL.hostAdapter.patchProp(hostReference, propName, prevValue, nextValue);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const propName in prevProps) {
|
|
67
|
+
if (nextProps[propName] == null && prevProps[propName] != null) {
|
|
68
|
+
GLOBAL.hostAdapter.patchProp(hostReference, propName, prevProps[propName], null);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (prevElement.className !== nextElement.className) {
|
|
72
|
+
GLOBAL.hostAdapter.setClassname(hostReference, nextElement.className);
|
|
73
|
+
}
|
|
74
|
+
patchChildren(prevElement.children, nextElement.children, hostReference, null, prevElement, contextMap);
|
|
75
|
+
}
|
|
76
|
+
function patchChildren(prevChildren, nextChildren, parentReference, nextReference, parentElement, contextMap) {
|
|
77
|
+
if (Array.isArray(prevChildren)) {
|
|
78
|
+
if (Array.isArray(nextChildren)) {
|
|
79
|
+
const prevChildrenLength = prevChildren.length;
|
|
80
|
+
const nextChildrenLength = nextChildren.length;
|
|
81
|
+
if (prevChildrenLength === 0) {
|
|
82
|
+
if (nextChildrenLength > 0) {
|
|
83
|
+
mountArrayChildren(nextChildren, parentReference, nextReference, contextMap);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (nextChildrenLength === 0) {
|
|
87
|
+
removeAllChildren(parentReference, parentElement, prevChildren);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, contextMap);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (isPrimitive(nextChildren)) {
|
|
94
|
+
unmountAllChildren(prevChildren);
|
|
95
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, (nextChildren || ''));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
removeAllChildren(parentReference, parentElement, prevChildren);
|
|
99
|
+
mount(nextChildren, parentReference, nextReference, contextMap);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (isPrimitive(prevChildren)) {
|
|
103
|
+
if (Array.isArray(nextChildren)) {
|
|
104
|
+
GLOBAL.hostAdapter.clearNode(parentReference);
|
|
105
|
+
mountArrayChildren(nextChildren, parentReference, nextReference, contextMap);
|
|
106
|
+
}
|
|
107
|
+
else if (isPrimitive(nextChildren)) {
|
|
108
|
+
patchSingleTextChild(prevChildren, nextChildren, parentReference);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
GLOBAL.hostAdapter.clearNode(parentReference);
|
|
112
|
+
mount(nextChildren, parentReference, nextReference, contextMap);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
if (Array.isArray(nextChildren)) {
|
|
117
|
+
replaceOneElementWithMultipleElements(prevChildren, nextChildren, parentReference, contextMap);
|
|
118
|
+
}
|
|
119
|
+
else if (isPrimitive(nextChildren)) {
|
|
120
|
+
unmount(prevChildren);
|
|
121
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, nextChildren);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
patch(prevChildren, nextChildren, parentReference, nextReference, contextMap);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function replaceOneElementWithMultipleElements(prevChildren, nextChildren, parentReference, contextMap) {
|
|
129
|
+
unmount(prevChildren);
|
|
130
|
+
mountArrayChildren(nextChildren, parentReference, findHostReferenceFromElement(prevChildren), contextMap);
|
|
131
|
+
clearElementHostReference(prevChildren, parentReference);
|
|
132
|
+
}
|
|
133
|
+
function patchSingleTextChild(prevChildren, nextChildren, parentReference) {
|
|
134
|
+
if (prevChildren !== nextChildren) {
|
|
135
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, nextChildren);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// export function patchNonKeyedChildren(
|
|
139
|
+
// prevChildren: SimpElement[],
|
|
140
|
+
// nextChildren: SimpElement[],
|
|
141
|
+
// parentReference: HostReference,
|
|
142
|
+
// prevChildrenLength: number,
|
|
143
|
+
// nextChildrenLength: number,
|
|
144
|
+
// nextReference: Nullable<HostReference>
|
|
145
|
+
// ): void {
|
|
146
|
+
// const commonLength = prevChildrenLength > nextChildrenLength ? nextChildrenLength : prevChildrenLength;
|
|
147
|
+
// let i = 0;
|
|
148
|
+
// let prevChild;
|
|
149
|
+
// let nextChild;
|
|
150
|
+
//
|
|
151
|
+
// for (; i < commonLength; ++i) {
|
|
152
|
+
// nextChild = nextChildren[i];
|
|
153
|
+
// prevChild = prevChildren[i];
|
|
154
|
+
//
|
|
155
|
+
// patch(prevChild as SimpElement, nextChild as SimpElement, parentReference, nextReference);
|
|
156
|
+
// prevChildren[i] = nextChild!;
|
|
157
|
+
// }
|
|
158
|
+
// if (prevChildrenLength < nextChildrenLength) {
|
|
159
|
+
// for (i = commonLength; i < nextChildrenLength; ++i) {
|
|
160
|
+
// nextChild = nextChildren[i];
|
|
161
|
+
// mount(nextChild as SimpElement, parentReference, nextReference);
|
|
162
|
+
// }
|
|
163
|
+
// } else if (prevChildrenLength > nextChildrenLength) {
|
|
164
|
+
// for (i = commonLength; i < prevChildrenLength; ++i) {
|
|
165
|
+
// remove(prevChildren[i] as SimpElement, parentReference);
|
|
166
|
+
// }
|
|
167
|
+
// }
|
|
168
|
+
// }
|
|
169
|
+
function patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap) {
|
|
170
|
+
nextElement.contextMap = contextMap;
|
|
171
|
+
GLOBAL.eventBus.publish({ type: 'beforeRender', element: nextElement });
|
|
172
|
+
const nextChildren = normalizeRoot(nextElement.type(nextElement.props || EMPTY_OBJECT));
|
|
173
|
+
GLOBAL.eventBus.publish({ type: 'afterRender' });
|
|
174
|
+
if (nextChildren != null) {
|
|
175
|
+
nextElement.children = nextChildren;
|
|
176
|
+
}
|
|
177
|
+
patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, prevElement, contextMap);
|
|
178
|
+
GLOBAL.eventBus.publish({ type: 'mounted', element: nextElement });
|
|
179
|
+
}
|
|
180
|
+
function patchText(prevElement, nextElement) {
|
|
181
|
+
const nextText = nextElement.children;
|
|
182
|
+
const reference = (nextElement.reference = prevElement.reference);
|
|
183
|
+
if (nextText !== prevElement.children) {
|
|
184
|
+
GLOBAL.hostAdapter.setTextContent(reference, nextText);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function patchFragment(prevElement, nextElement, parentReference, nextReference, contextMap) {
|
|
188
|
+
patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, prevElement, contextMap);
|
|
189
|
+
}
|
|
190
|
+
function patchProvider(prevElement, nextElement, parentReference, nextReference, contextMap) {
|
|
191
|
+
contextMap = new Map(contextMap);
|
|
192
|
+
contextMap.set(nextElement.type.context, nextElement.props.value);
|
|
193
|
+
patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, prevElement, contextMap);
|
|
194
|
+
}
|
|
195
|
+
function patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap) {
|
|
196
|
+
const children = normalizeRoot(nextElement.type(nextElement.props || EMPTY_OBJECT, contextMap || EMPTY_MAP));
|
|
197
|
+
if (children != null) {
|
|
198
|
+
nextElement.children = children;
|
|
199
|
+
}
|
|
200
|
+
patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, prevElement, contextMap);
|
|
201
|
+
}
|
|
202
|
+
export function updateFunctionalComponent(element, parentReference, nextReference, contextMap) {
|
|
203
|
+
patch(element, element, parentReference, nextReference, contextMap);
|
|
204
|
+
}
|
|
205
|
+
export function patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, contextMap) {
|
|
206
|
+
let prevStart = 0;
|
|
207
|
+
let nextStart = 0;
|
|
208
|
+
let prevEnd = prevChildren.length - 1;
|
|
209
|
+
let nextEnd = nextChildren.length - 1;
|
|
210
|
+
// Step 1: Sync from start
|
|
211
|
+
while (prevStart <= prevEnd &&
|
|
212
|
+
nextStart <= nextEnd &&
|
|
213
|
+
prevChildren[prevStart].key === nextChildren[nextStart].key) {
|
|
214
|
+
patch(prevChildren[prevStart], nextChildren[nextStart], parentReference, null, contextMap);
|
|
215
|
+
prevStart++;
|
|
216
|
+
nextStart++;
|
|
217
|
+
}
|
|
218
|
+
// Step 2: Sync from end
|
|
219
|
+
while (prevStart <= prevEnd && nextStart <= nextEnd && prevChildren[prevEnd].key === nextChildren[nextEnd].key) {
|
|
220
|
+
patch(prevChildren[prevEnd], nextChildren[nextEnd], parentReference, null, contextMap);
|
|
221
|
+
prevEnd--;
|
|
222
|
+
nextEnd--;
|
|
223
|
+
}
|
|
224
|
+
// Step 3: Mount new nodes if prev list is exhausted
|
|
225
|
+
if (prevStart > prevEnd) {
|
|
226
|
+
const before = nextChildren[nextEnd + 1]?.reference || nextReference;
|
|
227
|
+
for (let i = nextStart; i <= nextEnd; i++) {
|
|
228
|
+
mount(nextChildren[i], parentReference, before, contextMap);
|
|
229
|
+
}
|
|
230
|
+
// Step 4: Remove prev nodes if next list is exhausted
|
|
231
|
+
}
|
|
232
|
+
else if (nextStart > nextEnd) {
|
|
233
|
+
for (let i = prevStart; i <= prevEnd; i++) {
|
|
234
|
+
remove(prevChildren[i], parentReference);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Step 5: Full diff with keyed lookup and movement
|
|
238
|
+
else {
|
|
239
|
+
// Create map of keys to indices for prev children
|
|
240
|
+
const keyToPrevIndexMap = new Map();
|
|
241
|
+
for (let i = prevStart; i <= prevEnd; i++) {
|
|
242
|
+
const key = prevChildren[i].key;
|
|
243
|
+
if (key != null)
|
|
244
|
+
keyToPrevIndexMap.set(key, i);
|
|
245
|
+
}
|
|
246
|
+
// Track reused indices and move plan
|
|
247
|
+
const toMove = new Array(nextEnd - nextStart + 1);
|
|
248
|
+
const usedIndices = new Set();
|
|
249
|
+
// Match and patch/mount
|
|
250
|
+
for (let i = nextStart; i <= nextEnd; i++) {
|
|
251
|
+
const nextChild = nextChildren[i];
|
|
252
|
+
const prevIndex = keyToPrevIndexMap.get(nextChild.key);
|
|
253
|
+
if (prevIndex != null) {
|
|
254
|
+
const prevElement = prevChildren[prevIndex];
|
|
255
|
+
patch(prevElement, nextChild, parentReference, null, contextMap);
|
|
256
|
+
toMove[i - nextStart] = prevIndex;
|
|
257
|
+
usedIndices.add(prevIndex);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
mount(nextChild, parentReference, nextChildren[i + 1]?.reference || nextReference, contextMap);
|
|
261
|
+
toMove[i - nextStart] = -1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Remove nodes not matched
|
|
265
|
+
for (let i = prevStart; i <= prevEnd; i++) {
|
|
266
|
+
if (!usedIndices.has(i)) {
|
|
267
|
+
remove(prevChildren[i], parentReference);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Insert in correct order
|
|
271
|
+
for (let i = nextEnd; i >= nextStart; i--) {
|
|
272
|
+
const currentChild = nextChildren[i];
|
|
273
|
+
const reference = nextChildren[i + 1]?.reference || nextReference;
|
|
274
|
+
if (toMove[i - nextStart] !== -1) {
|
|
275
|
+
GLOBAL.hostAdapter.insertBefore(parentReference, currentChild.reference, reference);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
package/core/rerender.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { findHostReferenceFromElement, updateFunctionalComponent } from './patching';
|
|
2
|
+
import { GLOBAL } from './global';
|
|
3
|
+
export function rerender(element) {
|
|
4
|
+
updateFunctionalComponent(element, GLOBAL.hostAdapter.findParentReference(findHostReferenceFromElement(element)), null, element.contextMap || null);
|
|
5
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SimpElement } from './createElement';
|
|
2
|
+
import type { Maybe } from '../shared';
|
|
3
|
+
import type { HostReference } from './hostAdapter';
|
|
4
|
+
export declare function unmount(element: SimpElement): void;
|
|
5
|
+
export declare function unmountAllChildren(children: SimpElement[]): void;
|
|
6
|
+
export declare function clearElementHostReference(element: Maybe<SimpElement>, parentHostReference: HostReference): void;
|
|
7
|
+
export declare function removeAllChildren(hostReference: HostReference, element: SimpElement, children: SimpElement[]): void;
|
|
8
|
+
export declare function remove(element: SimpElement, parentReference: HostReference): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { GLOBAL } from './global';
|
|
2
|
+
export function unmount(element) {
|
|
3
|
+
if (element.flag === 'FC') {
|
|
4
|
+
// FC element always has only one root element due to normalization.
|
|
5
|
+
unmount(element.children);
|
|
6
|
+
GLOBAL.eventBus.publish({ type: 'unmounted', element });
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (element.flag === 'TEXT') {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// Only FRAGMENT, PROVIDER, CONSUMER, and HOST elements remain,
|
|
13
|
+
// with Maybe<Many<SimpElement>> children due to normalization.
|
|
14
|
+
if (Array.isArray(element.children)) {
|
|
15
|
+
unmountAllChildren(element.children);
|
|
16
|
+
}
|
|
17
|
+
else if (element.children != null) {
|
|
18
|
+
unmount(element.children);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function unmountAllChildren(children) {
|
|
22
|
+
for (const child of children) {
|
|
23
|
+
unmount(child);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function clearElementHostReference(element, parentHostReference) {
|
|
27
|
+
while (element != null) {
|
|
28
|
+
if (element.flag === 'HOST' || element.flag === 'TEXT') {
|
|
29
|
+
GLOBAL.hostAdapter.removeChild(parentHostReference, element.reference);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const children = element.children;
|
|
33
|
+
if (element.flag === 'FC' || element.flag === 'CONSUMER') {
|
|
34
|
+
element = children;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (element.flag === 'FRAGMENT' || element.flag === 'PROVIDER') {
|
|
38
|
+
if (Array.isArray(children)) {
|
|
39
|
+
for (let i = 0, len = children.length; i < len; ++i) {
|
|
40
|
+
clearElementHostReference(children[i], parentHostReference);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
else if (children != null) {
|
|
45
|
+
element = children;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function removeAllChildren(hostReference, element, children) {
|
|
51
|
+
unmountAllChildren(children);
|
|
52
|
+
if (element.flag === 'FRAGMENT' ||
|
|
53
|
+
element.flag === 'FC' ||
|
|
54
|
+
element.flag === 'PROVIDER' ||
|
|
55
|
+
element.flag === 'CONSUMER') {
|
|
56
|
+
clearElementHostReference(element, hostReference);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
GLOBAL.hostAdapter.clearNode(hostReference);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function remove(element, parentReference) {
|
|
63
|
+
unmount(element);
|
|
64
|
+
clearElementHostReference(element, parentReference);
|
|
65
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
export const domAdapter = {
|
|
2
|
+
createReference(type) {
|
|
3
|
+
return document.createElement(type);
|
|
4
|
+
},
|
|
5
|
+
createTextReference(text) {
|
|
6
|
+
return document.createTextNode(text);
|
|
7
|
+
},
|
|
8
|
+
mountProps(reference, props) {
|
|
9
|
+
mountProps(props, reference);
|
|
10
|
+
},
|
|
11
|
+
patchProp(reference, propName, prevValue, nextValue) {
|
|
12
|
+
patchProp(propName, prevValue, nextValue, reference);
|
|
13
|
+
},
|
|
14
|
+
setClassname(reference, className) {
|
|
15
|
+
if (!className) {
|
|
16
|
+
reference.removeAttribute('class');
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
reference.className = className;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
setTextContent(reference, text) {
|
|
23
|
+
reference.textContent = text;
|
|
24
|
+
},
|
|
25
|
+
appendChild(parent, child) {
|
|
26
|
+
parent.appendChild(child);
|
|
27
|
+
},
|
|
28
|
+
insertBefore(parent, child, before) {
|
|
29
|
+
parent.insertBefore(child, before);
|
|
30
|
+
},
|
|
31
|
+
insertOrAppend(parent, child, before) {
|
|
32
|
+
if (before) {
|
|
33
|
+
domAdapter.insertBefore(parent, child, before);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
domAdapter.appendChild(parent, child);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
removeChild(parent, child) {
|
|
40
|
+
parent.removeChild(child);
|
|
41
|
+
},
|
|
42
|
+
replaceChild(parent, replacer, toBeReplaced) {
|
|
43
|
+
parent.replaceChild(replacer, toBeReplaced);
|
|
44
|
+
},
|
|
45
|
+
findParentReference(reference) {
|
|
46
|
+
return reference.parentElement;
|
|
47
|
+
},
|
|
48
|
+
clearNode(reference) {
|
|
49
|
+
reference.textContent = '';
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
function mountProps(props, reference) {
|
|
53
|
+
for (const propsKey in props) {
|
|
54
|
+
patchProp(propsKey, null, props[propsKey], reference);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function patchProp(propName, prevValue, nextValue, dom) {
|
|
58
|
+
switch (propName) {
|
|
59
|
+
case 'children':
|
|
60
|
+
case 'childrenType':
|
|
61
|
+
case 'className':
|
|
62
|
+
case 'defaultValue':
|
|
63
|
+
case 'key':
|
|
64
|
+
case 'multiple':
|
|
65
|
+
case 'ref':
|
|
66
|
+
case 'selectedIndex':
|
|
67
|
+
break;
|
|
68
|
+
case 'autoFocus':
|
|
69
|
+
dom.autofocus = !!nextValue;
|
|
70
|
+
break;
|
|
71
|
+
case 'allowfullscreen':
|
|
72
|
+
case 'autoplay':
|
|
73
|
+
case 'capture':
|
|
74
|
+
case 'checked':
|
|
75
|
+
case 'controls':
|
|
76
|
+
case 'default':
|
|
77
|
+
case 'disabled':
|
|
78
|
+
case 'hidden':
|
|
79
|
+
case 'indeterminate':
|
|
80
|
+
case 'loop':
|
|
81
|
+
case 'muted':
|
|
82
|
+
case 'novalidate':
|
|
83
|
+
case 'open':
|
|
84
|
+
case 'readOnly':
|
|
85
|
+
case 'required':
|
|
86
|
+
case 'reversed':
|
|
87
|
+
case 'scoped':
|
|
88
|
+
case 'seamless':
|
|
89
|
+
case 'selected':
|
|
90
|
+
dom[propName] = !!nextValue;
|
|
91
|
+
break;
|
|
92
|
+
case 'defaultChecked':
|
|
93
|
+
case 'value':
|
|
94
|
+
case 'volume':
|
|
95
|
+
patchDomProp(nextValue, dom, propName);
|
|
96
|
+
break;
|
|
97
|
+
case 'style':
|
|
98
|
+
patchStyle(prevValue, nextValue, dom);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
if (propName.charCodeAt(0) === 111 && propName.charCodeAt(1) === 110) {
|
|
102
|
+
patchEvent(propName, prevValue, nextValue, dom);
|
|
103
|
+
}
|
|
104
|
+
else if (nextValue == null) {
|
|
105
|
+
dom.removeAttribute(propName);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
dom.setAttribute(propName, nextValue);
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function patchDomProp(nextValue, dom, propKey) {
|
|
114
|
+
const value = nextValue == null ? '' : nextValue;
|
|
115
|
+
if (dom[propKey] !== value) {
|
|
116
|
+
dom[propKey] = value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function patchStyle(lastAttrValue, nextAttrValue, dom) {
|
|
120
|
+
if (nextAttrValue == null) {
|
|
121
|
+
dom.removeAttribute('style');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const domStyle = dom.style;
|
|
125
|
+
let style;
|
|
126
|
+
let value;
|
|
127
|
+
if (typeof nextAttrValue === 'string') {
|
|
128
|
+
domStyle.cssText = nextAttrValue;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (lastAttrValue != null && typeof lastAttrValue !== 'string') {
|
|
132
|
+
for (style in nextAttrValue) {
|
|
133
|
+
value = nextAttrValue[style];
|
|
134
|
+
if (value !== lastAttrValue[style]) {
|
|
135
|
+
domStyle.setProperty(style, value);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (style in lastAttrValue) {
|
|
139
|
+
if (nextAttrValue[style] == null) {
|
|
140
|
+
domStyle.removeProperty(style);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
for (style in nextAttrValue) {
|
|
146
|
+
value = nextAttrValue[style];
|
|
147
|
+
domStyle.setProperty(style, value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function patchEvent(name, lastValue, nextValue, dom) {
|
|
152
|
+
name = name.toLowerCase().substring(2);
|
|
153
|
+
if (typeof lastValue === 'function') {
|
|
154
|
+
dom.removeEventListener(name, lastValue);
|
|
155
|
+
}
|
|
156
|
+
if (typeof nextValue === 'function') {
|
|
157
|
+
dom.addEventListener(name, nextValue);
|
|
158
|
+
}
|
|
159
|
+
}
|
package/dom/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createRoot, render } from './render';
|
package/dom/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createRoot, render } from './render';
|
package/dom/render.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SimpElement } from '../core/internal';
|
|
2
|
+
import type { Nullable } from '../shared';
|
|
3
|
+
export declare function render(element: SimpElement, parentReference: Nullable<HTMLElement>): void;
|
|
4
|
+
interface SimpRoot {
|
|
5
|
+
render(element: SimpElement): void;
|
|
6
|
+
unmount(): void;
|
|
7
|
+
}
|
|
8
|
+
export declare function createRoot(container: Element | DocumentFragment): SimpRoot;
|
|
9
|
+
export {};
|
package/dom/render.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { GLOBAL, mount, patch } from '../core/internal';
|
|
2
|
+
import { domAdapter } from './domAdapter';
|
|
3
|
+
GLOBAL.hostAdapter = domAdapter;
|
|
4
|
+
export function render(element, parentReference) {
|
|
5
|
+
mount(element, parentReference, null, null);
|
|
6
|
+
}
|
|
7
|
+
export function createRoot(container) {
|
|
8
|
+
let currentRoot = container.__SIMP_ROOT__;
|
|
9
|
+
return {
|
|
10
|
+
render(element) {
|
|
11
|
+
if (currentRoot) {
|
|
12
|
+
patch(currentRoot, element, container, null, null);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
GLOBAL.hostAdapter.clearNode(container);
|
|
16
|
+
mount(element, container, null, null);
|
|
17
|
+
}
|
|
18
|
+
currentRoot = element;
|
|
19
|
+
container.__SIMP_ROOT__ = currentRoot;
|
|
20
|
+
},
|
|
21
|
+
unmount() {
|
|
22
|
+
// if (currentRoot != null) {
|
|
23
|
+
// enqueueRender(currentRoot, null);
|
|
24
|
+
// currentRoot = null;
|
|
25
|
+
// }
|
|
26
|
+
currentRoot = null;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
package/hooks/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SimpContext } from '../core';
|
|
2
|
+
import type { VoidFunction } from '../shared';
|
|
3
|
+
type Cleanup = VoidFunction;
|
|
4
|
+
type Effect = () => void | Cleanup;
|
|
5
|
+
type DependencyList = readonly unknown[];
|
|
6
|
+
export declare function useRef<T>(initialValue: T): {
|
|
7
|
+
current: T;
|
|
8
|
+
};
|
|
9
|
+
export declare function useRerender(): VoidFunction;
|
|
10
|
+
export declare function useEffect(effect: Effect, deps?: DependencyList): void;
|
|
11
|
+
export declare function useContext<T>(context: SimpContext<T>): T;
|
|
12
|
+
export {};
|
package/hooks/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { GLOBAL, rerender } from '../core/internal';
|
|
2
|
+
let currentIndex = 0;
|
|
3
|
+
let currentElement = null;
|
|
4
|
+
GLOBAL.eventBus.subscribe(event => {
|
|
5
|
+
if (event.type === 'beforeRender') {
|
|
6
|
+
currentElement = event.element;
|
|
7
|
+
currentElement.store ||= { hookStates: [], mountEffects: [] };
|
|
8
|
+
currentElement.store.mountEffects = [];
|
|
9
|
+
}
|
|
10
|
+
if (event.type === 'afterRender') {
|
|
11
|
+
currentElement = null;
|
|
12
|
+
currentIndex = 0;
|
|
13
|
+
}
|
|
14
|
+
if (event.type === 'mounted') {
|
|
15
|
+
for (const state of event.element.store.mountEffects) {
|
|
16
|
+
if (typeof state.cleanup === 'function') {
|
|
17
|
+
state.cleanup();
|
|
18
|
+
}
|
|
19
|
+
state.cleanup = state.effect() || undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (event.type === 'unmounted') {
|
|
23
|
+
for (const state of event.element.store.hookStates) {
|
|
24
|
+
if (state && 'cleanup' in state && typeof state.cleanup === 'function') {
|
|
25
|
+
state.cleanup();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
export function useRef(initialValue) {
|
|
31
|
+
return (currentElement.store.hookStates[currentIndex++] ||= { current: initialValue });
|
|
32
|
+
}
|
|
33
|
+
export function useRerender() {
|
|
34
|
+
const state = (currentElement.store.hookStates[currentIndex++] ||= {
|
|
35
|
+
element: null,
|
|
36
|
+
fn() {
|
|
37
|
+
rerender(state.element);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
state.element = currentElement;
|
|
41
|
+
return state.fn;
|
|
42
|
+
}
|
|
43
|
+
export function useEffect(effect, deps) {
|
|
44
|
+
const state = (currentElement.store.hookStates[currentIndex++] ||= {
|
|
45
|
+
effect,
|
|
46
|
+
deps: undefined,
|
|
47
|
+
cleanup: undefined,
|
|
48
|
+
});
|
|
49
|
+
if (!areDepsEqual(deps, state.deps)) {
|
|
50
|
+
state.effect = effect;
|
|
51
|
+
state.deps = deps;
|
|
52
|
+
currentElement.store.mountEffects.push(state);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function useContext(context) {
|
|
56
|
+
return currentElement.contextMap?.get(context) ?? context.defaultValue;
|
|
57
|
+
}
|
|
58
|
+
function areDepsEqual(nextDeps, prevDeps) {
|
|
59
|
+
if (nextDeps == null || prevDeps == null || nextDeps.length !== prevDeps.length) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
for (let i = 0; i < prevDeps.length; i++) {
|
|
63
|
+
if (!Object.is(nextDeps[i], prevDeps[i])) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FunctionComponent, SimpElement } from '../core';
|
|
2
|
+
import { Fragment } from '../core';
|
|
3
|
+
import type { Maybe } from '../shared';
|
|
4
|
+
import type { Key, Props } from '../core/createElement';
|
|
5
|
+
export declare function jsx(type: string | FunctionComponent<any>, props?: Maybe<Props>, key?: Maybe<Key>): SimpElement;
|
|
6
|
+
export { jsx as jsxs, jsx as jsxDEV };
|
|
7
|
+
export { Fragment };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createElement, Fragment } from '../core';
|
|
2
|
+
export function jsx(type, props, key) {
|
|
3
|
+
let _key;
|
|
4
|
+
if (key != null) {
|
|
5
|
+
_key = key;
|
|
6
|
+
}
|
|
7
|
+
if (props && props.key != null) {
|
|
8
|
+
_key = props.key;
|
|
9
|
+
}
|
|
10
|
+
if (_key != null) {
|
|
11
|
+
(props ||= {}).key = _key;
|
|
12
|
+
}
|
|
13
|
+
return createElement(type, props);
|
|
14
|
+
}
|
|
15
|
+
export { jsx as jsxs, jsx as jsxDEV };
|
|
16
|
+
export { Fragment };
|