lithent 0.1.0
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 +21 -0
- package/README.md +202 -0
- package/dist/diff.d.ts +14 -0
- package/dist/hook/mountCallback.d.ts +3 -0
- package/dist/hook/ref.d.ts +3 -0
- package/dist/hook/unmount.d.ts +3 -0
- package/dist/hook/updateCallback.d.ts +2 -0
- package/dist/hook/useUpdate.d.ts +3 -0
- package/dist/index.d.ts +29 -0
- package/dist/lithent.mjs +867 -0
- package/dist/lithent.mjs.map +1 -0
- package/dist/lithent.umd.js +2 -0
- package/dist/lithent.umd.js.map +1 -0
- package/dist/render.d.ts +15 -0
- package/dist/types/index.d.ts +65 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/predicator.d.ts +36 -0
- package/dist/utils/universalRef.d.ts +59 -0
- package/dist/wDom.d.ts +17 -0
- package/examples/dist/assets/index.e76a0ef8.js +2 -0
- package/examples/dist/assets/index.e76a0ef8.js.map +1 -0
- package/examples/dist/index.html +13 -0
- package/examples/package.json +33 -0
- package/examples/src/index.tsx +109 -0
- package/helper/dist/hook/computed.d.ts +4 -0
- package/helper/dist/hook/effect.d.ts +1 -0
- package/helper/dist/hook/state.d.ts +4 -0
- package/helper/dist/hook/store.d.ts +8 -0
- package/helper/dist/index.d.ts +4 -0
- package/helper/dist/lithentHelper.mjs +56 -0
- package/helper/dist/lithentHelper.mjs.map +1 -0
- package/helper/dist/lithentHelper.umd.js +2 -0
- package/helper/dist/lithentHelper.umd.js.map +1 -0
- package/helper/package.json +32 -0
- package/helper/src/hook/computed.ts +23 -0
- package/helper/src/hook/effect.ts +20 -0
- package/helper/src/hook/state.ts +26 -0
- package/helper/src/hook/store.ts +25 -0
- package/helper/src/index.ts +4 -0
- package/package.json +90 -0
- package/router/dist/Router.d.ts +4 -0
- package/router/dist/Router.mjs +56 -0
- package/router/dist/Router.mjs.map +1 -0
- package/router/dist/Router.umd.js +2 -0
- package/router/dist/Router.umd.js.map +1 -0
- package/router/dist/hook/params.d.ts +2 -0
- package/router/package.json +37 -0
- package/router/src/Router.tsx +63 -0
- package/router/src/components/Main.tsx +14 -0
- package/router/src/components/Sub.tsx +7 -0
- package/router/src/components/Sub2.tsx +17 -0
- package/router/src/example.tsx +19 -0
- package/router/src/hook/params.ts +9 -0
- package/src/diff.ts +235 -0
- package/src/examples/callback.tsx +81 -0
- package/src/examples/insert.tsx +36 -0
- package/src/examples/loop.tsx +33 -0
- package/src/examples/nested.tsx +44 -0
- package/src/examples/renew.tsx +29 -0
- package/src/examples/selectbox.tsx +42 -0
- package/src/examples/textarea.tsx +24 -0
- package/src/hook/mountCallback.ts +41 -0
- package/src/hook/ref.ts +1 -0
- package/src/hook/unmount.ts +26 -0
- package/src/hook/updateCallback.ts +29 -0
- package/src/hook/useUpdate.ts +51 -0
- package/src/index.ts +50 -0
- package/src/render.ts +409 -0
- package/src/types/index.ts +107 -0
- package/src/utils/index.ts +52 -0
- package/src/utils/predicator.ts +119 -0
- package/src/utils/universalRef.ts +137 -0
- package/src/wDom.ts +284 -0
package/src/render.ts
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rerender시 실제 dom에 반영해줘야 하는 상태
|
|
3
|
+
*
|
|
4
|
+
* 1. (ADD). 가상돔 비교시 오리지날이 없으면 새로 추가되는 dom으로 판단한다.
|
|
5
|
+
* 2. (REPLACE). 오리지날이 있고 같은 엘리먼트타입이 아닌 경우 해당 위치의 태그 자체를 교체해준다.
|
|
6
|
+
* 3. (UPDATE). 오리지날이 있고 같은 타입이면 dom의 속성만 변경해 주며, 텍스트 노드일 경우는 텍스트를 교체해준다.
|
|
7
|
+
* 4. (DELETE). 오리지날이 있고 새로운 엘리먼트 타입은 null타입일 경우 dom을 삭제한다.
|
|
8
|
+
* 5. (SORTED-REPLACE). loop의 경우 REPLACE라도 키값에 의해 순서가 변경될 수 있으므로 새로운 dom을 단순히 기존 위치에 교체하지 않고 새로 삼입하여 정렬한다.
|
|
9
|
+
* 6. (SORTED-UPDATE). loop의 경우 UPDATE라도 키값에 의해 순서가 변경될수 있으므로 재 삽입하여 정렬한다.
|
|
10
|
+
* 7. (NONE). text타입의 경우 text가 같다면 변경이 필요 없으므로 방치한다. (Todo. 다른 타입의 경우 처리필요)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { WDom, Props } from '@/types';
|
|
14
|
+
import {
|
|
15
|
+
checkStyleData,
|
|
16
|
+
checkRefData,
|
|
17
|
+
checkNormalAttribute,
|
|
18
|
+
checkExisty,
|
|
19
|
+
checkOptionElement,
|
|
20
|
+
} from '@/utils/predicator';
|
|
21
|
+
import { runMountedQueueFromWDom } from '@/hook/mountCallback';
|
|
22
|
+
import { runUpdatedQueueFromWDom } from '@/hook/useUpdate';
|
|
23
|
+
import { getParent } from '@/utils';
|
|
24
|
+
|
|
25
|
+
export const render = (
|
|
26
|
+
wDom: WDom,
|
|
27
|
+
wrapElement: HTMLElement | null,
|
|
28
|
+
afterElement?: HTMLElement | null
|
|
29
|
+
) => {
|
|
30
|
+
if (!wrapElement) {
|
|
31
|
+
throw Error('WrapELement is null');
|
|
32
|
+
}
|
|
33
|
+
wDom.isRoot = true;
|
|
34
|
+
wDom.wrapElement = wrapElement;
|
|
35
|
+
|
|
36
|
+
const Dom = wDomToDom(wDom, true);
|
|
37
|
+
|
|
38
|
+
if (afterElement) {
|
|
39
|
+
wrapElement.insertBefore(Dom, afterElement);
|
|
40
|
+
} else {
|
|
41
|
+
wrapElement.appendChild(Dom);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const wDomUpdate = (newWDomTree: WDom) => {
|
|
46
|
+
const { needRerender } = newWDomTree;
|
|
47
|
+
|
|
48
|
+
if (needRerender && needRerender !== 'N') {
|
|
49
|
+
const exec = {
|
|
50
|
+
A: typeAdd,
|
|
51
|
+
D: typeDelete,
|
|
52
|
+
R: typeReplace,
|
|
53
|
+
U: typeUpdate,
|
|
54
|
+
SR: typeSortedReplace,
|
|
55
|
+
SU: typeSortedUpdate,
|
|
56
|
+
}[needRerender];
|
|
57
|
+
exec(newWDomTree);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const recursiveRemoveEvent = (originalWDom: WDom) => {
|
|
62
|
+
if (originalWDom.props && originalWDom.el) {
|
|
63
|
+
removeEvent(originalWDom.props, originalWDom.el);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
(originalWDom.children || []).forEach((childItem: WDom) => {
|
|
67
|
+
recursiveRemoveEvent(childItem);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const typeDelete = (newWDom: WDom) => {
|
|
72
|
+
const parent = findRealParentElement(newWDom);
|
|
73
|
+
|
|
74
|
+
if (newWDom.oldProps && newWDom.el) {
|
|
75
|
+
removeEvent(newWDom.oldProps, newWDom.el);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (parent && newWDom.el) {
|
|
79
|
+
if (newWDom.el?.nodeType === 1) {
|
|
80
|
+
parent.removeChild(newWDom.el);
|
|
81
|
+
} else if (newWDom.el?.nodeType === 11) {
|
|
82
|
+
findChildWithRemoveElement(newWDom, parent as HTMLElement);
|
|
83
|
+
}
|
|
84
|
+
delete newWDom.el;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const findChildWithRemoveElement = (newWDom: WDom, parent: HTMLElement) => {
|
|
89
|
+
(newWDom?.children || []).forEach(item => {
|
|
90
|
+
if (item.el?.nodeType === 1) {
|
|
91
|
+
parent.removeChild(item?.el);
|
|
92
|
+
} else if (item.el?.nodeType === 11) {
|
|
93
|
+
findChildWithRemoveElement(item, parent);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const typeSortedReplace = (newWDom: WDom) => {
|
|
99
|
+
typeDelete(newWDom);
|
|
100
|
+
typeAdd(newWDom);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const typeSortedUpdate = (newWDom: WDom) => {
|
|
104
|
+
typeDelete(newWDom);
|
|
105
|
+
typeAdd(newWDom, newWDom.el);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const typeAdd = (
|
|
109
|
+
newWDom: WDom,
|
|
110
|
+
newElement?: HTMLElement | DocumentFragment | Text
|
|
111
|
+
) => {
|
|
112
|
+
if (!newElement) {
|
|
113
|
+
newElement = wDomToDom(newWDom, true);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const parentWDom = getParent(newWDom);
|
|
117
|
+
if (parentWDom.type) {
|
|
118
|
+
const parentEl = findRealParentElement(parentWDom);
|
|
119
|
+
const nextEl = startFindNextBrotherElement(newWDom, parentWDom);
|
|
120
|
+
|
|
121
|
+
if (newElement && parentEl) {
|
|
122
|
+
if (nextEl) {
|
|
123
|
+
parentEl.insertBefore(newElement, nextEl);
|
|
124
|
+
} else {
|
|
125
|
+
parentEl.appendChild(newElement);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
runMountedQueueFromWDom(newWDom);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const startFindNextBrotherElement = (
|
|
134
|
+
wDom: WDom,
|
|
135
|
+
parentWDom: WDom
|
|
136
|
+
): HTMLElement | DocumentFragment | Text | undefined => {
|
|
137
|
+
const brothers = parentWDom.children || [];
|
|
138
|
+
const index = brothers.indexOf(wDom);
|
|
139
|
+
const nextIndex = index + 1;
|
|
140
|
+
const candidiateBrothers = brothers.slice(nextIndex);
|
|
141
|
+
|
|
142
|
+
const finedNextEl = findChildFragmentNextElement(candidiateBrothers);
|
|
143
|
+
const parentType = parentWDom.type || '';
|
|
144
|
+
|
|
145
|
+
if (finedNextEl) {
|
|
146
|
+
return finedNextEl;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!parentWDom.isRoot && ['fragment', 'loop'].includes(parentType)) {
|
|
150
|
+
return startFindNextBrotherElement(parentWDom, getParent(parentWDom));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return undefined;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const findChildFragmentNextElement = (
|
|
157
|
+
candidiateBrothers: WDom[]
|
|
158
|
+
): HTMLElement | DocumentFragment | Text | undefined =>
|
|
159
|
+
candidiateBrothers.reduce(
|
|
160
|
+
(
|
|
161
|
+
targetEl: HTMLElement | DocumentFragment | Text | undefined,
|
|
162
|
+
bItem: WDom
|
|
163
|
+
) => {
|
|
164
|
+
const type = bItem.type;
|
|
165
|
+
const el = bItem.el;
|
|
166
|
+
const isFragment = type && ['fragment', 'loop'].includes(type);
|
|
167
|
+
|
|
168
|
+
if (targetEl) {
|
|
169
|
+
return targetEl;
|
|
170
|
+
} else if (isFragment) {
|
|
171
|
+
return findChildFragmentNextElement(bItem.children || []);
|
|
172
|
+
} else if (el && el.nodeType !== 11) {
|
|
173
|
+
return el;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return targetEl;
|
|
177
|
+
},
|
|
178
|
+
undefined
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const typeReplace = (newWDom: WDom) => {
|
|
182
|
+
const parentWDom = getParent(newWDom);
|
|
183
|
+
const orignalElement = newWDom.el;
|
|
184
|
+
|
|
185
|
+
if (parentWDom.type && orignalElement) {
|
|
186
|
+
const parentElement = parentWDom.el;
|
|
187
|
+
const newElement = wDomToDom(newWDom, true);
|
|
188
|
+
|
|
189
|
+
if (newWDom.oldProps) {
|
|
190
|
+
removeEvent(newWDom.oldProps, orignalElement);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (parentElement) {
|
|
194
|
+
parentElement.replaceChild(newElement, orignalElement);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
runMountedQueueFromWDom(newWDom);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const removeEvent = (
|
|
202
|
+
oldProps: Props,
|
|
203
|
+
element?: HTMLElement | DocumentFragment | Text
|
|
204
|
+
) => {
|
|
205
|
+
if (element) {
|
|
206
|
+
Object.entries(oldProps || {}).forEach(
|
|
207
|
+
([dataKey, dataValue]: [string, unknown]) => {
|
|
208
|
+
if (dataKey.match(/^on/)) {
|
|
209
|
+
element.removeEventListener(dataKey, dataValue as (e: Event) => void);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const typeUpdate = (newWDom: WDom) => {
|
|
217
|
+
const element = newWDom.el;
|
|
218
|
+
|
|
219
|
+
if (newWDom.type === 'text') {
|
|
220
|
+
updateText(newWDom);
|
|
221
|
+
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (element) {
|
|
226
|
+
const { oldProps, props } = newWDom;
|
|
227
|
+
|
|
228
|
+
updateProps({ oldProps, props, element });
|
|
229
|
+
|
|
230
|
+
delete newWDom.oldProps;
|
|
231
|
+
|
|
232
|
+
if (newWDom.tag === 'input') {
|
|
233
|
+
(element as HTMLInputElement).value = String(newWDom?.props?.value || '');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
(newWDom.children || []).forEach((childItem: WDom) => wDomUpdate(childItem));
|
|
238
|
+
|
|
239
|
+
runUpdatedQueueFromWDom(newWDom);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const updateText = (newWDom: WDom) => {
|
|
243
|
+
const element = newWDom.el;
|
|
244
|
+
|
|
245
|
+
if (element) {
|
|
246
|
+
element.nodeValue = String(newWDom.text);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const updateProps = ({
|
|
251
|
+
oldProps,
|
|
252
|
+
props,
|
|
253
|
+
element,
|
|
254
|
+
}: {
|
|
255
|
+
oldProps?: Props;
|
|
256
|
+
props?: Props;
|
|
257
|
+
element?: HTMLElement | DocumentFragment | Text;
|
|
258
|
+
}) => {
|
|
259
|
+
const originalProps = { ...oldProps };
|
|
260
|
+
|
|
261
|
+
Object.entries(props || {}).forEach(
|
|
262
|
+
([dataKey, dataValue]: [string, unknown]) => {
|
|
263
|
+
if (dataKey === 'key') {
|
|
264
|
+
// Do nothing
|
|
265
|
+
} else if (checkStyleData(dataKey, dataValue)) {
|
|
266
|
+
const style = dataValue;
|
|
267
|
+
const oldStyle = checkStyleData(dataKey, originalProps.style) || {};
|
|
268
|
+
|
|
269
|
+
updateStyle({ style, oldStyle, element });
|
|
270
|
+
} else if (checkRefData(dataKey, dataValue)) {
|
|
271
|
+
dataValue.value = element;
|
|
272
|
+
} else if (dataKey.match(/^on/)) {
|
|
273
|
+
updateEvent({
|
|
274
|
+
element: element as HTMLElement,
|
|
275
|
+
eventKey: dataKey,
|
|
276
|
+
newEventHandler: dataValue as (e: Event) => void,
|
|
277
|
+
oldEventHandler: originalProps[dataKey] as (e: Event) => void,
|
|
278
|
+
});
|
|
279
|
+
} else if (checkNormalAttribute(dataValue)) {
|
|
280
|
+
(element as HTMLElement).setAttribute(dataKey, String(dataValue));
|
|
281
|
+
} else if (checkOptionElement(element) && dataKey === 'selected') {
|
|
282
|
+
(element as HTMLOptionElement).selected = !!dataValue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
delete originalProps[dataKey];
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
Object.keys(originalProps).forEach(dataKey =>
|
|
290
|
+
(element as HTMLElement).removeAttribute(dataKey)
|
|
291
|
+
);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const wDomToDom = (wDom: WDom, init: boolean) => {
|
|
295
|
+
let element;
|
|
296
|
+
const { type, tag, text, props, children = [] } = wDom;
|
|
297
|
+
const isVirtualType = type === 'fragment' || type === 'loop';
|
|
298
|
+
|
|
299
|
+
if (isVirtualType) {
|
|
300
|
+
element = new DocumentFragment();
|
|
301
|
+
} else if (type === 'element' && tag) {
|
|
302
|
+
element = document.createElement(tag);
|
|
303
|
+
} else if (type === 'text' && checkExisty(text)) {
|
|
304
|
+
element = document.createTextNode(String(text));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
wDomChildrenToDom(children, element, init);
|
|
308
|
+
updateProps({ props, element });
|
|
309
|
+
|
|
310
|
+
wDom.el = element;
|
|
311
|
+
|
|
312
|
+
runMountedQueueFromWDom(wDom);
|
|
313
|
+
|
|
314
|
+
return element || document.createElement('div');
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const wDomChildrenToDom = (
|
|
318
|
+
children: WDom[],
|
|
319
|
+
parentElement?: HTMLElement | DocumentFragment | Text,
|
|
320
|
+
init?: boolean
|
|
321
|
+
) => {
|
|
322
|
+
if (init) {
|
|
323
|
+
const elementChildren = children.reduce(
|
|
324
|
+
(acc: DocumentFragment, childItem: WDom) => {
|
|
325
|
+
if (childItem.type) {
|
|
326
|
+
acc.appendChild(wDomToDom(childItem, init));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return acc;
|
|
330
|
+
},
|
|
331
|
+
new DocumentFragment()
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
if (parentElement && elementChildren.hasChildNodes()) {
|
|
335
|
+
parentElement.appendChild(elementChildren);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const updateEvent = ({
|
|
341
|
+
element,
|
|
342
|
+
eventKey,
|
|
343
|
+
newEventHandler,
|
|
344
|
+
oldEventHandler,
|
|
345
|
+
}: {
|
|
346
|
+
element: HTMLElement;
|
|
347
|
+
eventKey: string;
|
|
348
|
+
newEventHandler: (e: Event) => void;
|
|
349
|
+
oldEventHandler: (e: Event) => void;
|
|
350
|
+
}) => {
|
|
351
|
+
const eventName = eventKey.replace(/^on(.*)/, (_match, p1) =>
|
|
352
|
+
p1.toLowerCase()
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
if (oldEventHandler !== newEventHandler) {
|
|
356
|
+
if (oldEventHandler) {
|
|
357
|
+
element.removeEventListener(eventName, oldEventHandler);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (newEventHandler) {
|
|
361
|
+
element.addEventListener(eventName, newEventHandler);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const updateStyle = ({
|
|
367
|
+
style,
|
|
368
|
+
oldStyle,
|
|
369
|
+
element,
|
|
370
|
+
}: {
|
|
371
|
+
style: Record<string, string>;
|
|
372
|
+
oldStyle: Record<string, string>;
|
|
373
|
+
element?: HTMLElement | DocumentFragment | Text;
|
|
374
|
+
}) => {
|
|
375
|
+
const originalStyle = { ...oldStyle };
|
|
376
|
+
const elementStyle = (element as HTMLElement)?.style;
|
|
377
|
+
|
|
378
|
+
if (elementStyle) {
|
|
379
|
+
Object.entries(style).forEach(([styleKey, dataValue]) => {
|
|
380
|
+
elementStyle.setProperty(styleKey, dataValue);
|
|
381
|
+
delete originalStyle[styleKey];
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
Object.entries(originalStyle).forEach(([styleKey]) =>
|
|
385
|
+
elementStyle.removeProperty(styleKey)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const findRealParentElement = (
|
|
391
|
+
vDom: WDom
|
|
392
|
+
): HTMLElement | DocumentFragment | Text | undefined => {
|
|
393
|
+
const isVirtualType =
|
|
394
|
+
vDom.type === 'fragment' ||
|
|
395
|
+
vDom.type === 'loop' ||
|
|
396
|
+
(!vDom.type && vDom?.el?.nodeType === 11);
|
|
397
|
+
|
|
398
|
+
if (vDom.isRoot && isVirtualType) {
|
|
399
|
+
return vDom.wrapElement;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!isVirtualType) {
|
|
403
|
+
return vDom.el?.parentNode as HTMLElement;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const parentVDom = getParent(vDom);
|
|
407
|
+
|
|
408
|
+
return findRealParentElement(parentVDom);
|
|
409
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export type UseDataStoreValue = { [key: string | symbol]: unknown };
|
|
2
|
+
|
|
3
|
+
export type Props = { [key: string]: unknown };
|
|
4
|
+
|
|
5
|
+
export type TagFunction = (
|
|
6
|
+
prop: Props,
|
|
7
|
+
children: WDom[]
|
|
8
|
+
) => (renew: Renew, prop: Props, children: WDom[]) => () => WDom;
|
|
9
|
+
|
|
10
|
+
export type Renew = () => boolean;
|
|
11
|
+
export type Component<T> = (
|
|
12
|
+
renew: Renew,
|
|
13
|
+
props: T,
|
|
14
|
+
childen: WDom[]
|
|
15
|
+
) => () => WDom;
|
|
16
|
+
|
|
17
|
+
export type TagFunctionResolver = {
|
|
18
|
+
tagName: string;
|
|
19
|
+
constructor: Function;
|
|
20
|
+
props: Props;
|
|
21
|
+
children: WDom[];
|
|
22
|
+
resolve: (componentKey?: Props) => WDom;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type FragmentFunction = (props: Props, children: WDom[]) => WDom;
|
|
26
|
+
|
|
27
|
+
export type NodePointer = { value: WDom | undefined };
|
|
28
|
+
|
|
29
|
+
export type NodeChildKey = { value: Props[] };
|
|
30
|
+
|
|
31
|
+
export type MiddleStateWDom =
|
|
32
|
+
| WDom
|
|
33
|
+
| number
|
|
34
|
+
| string
|
|
35
|
+
| false
|
|
36
|
+
| null
|
|
37
|
+
| MiddleStateWDomChildren;
|
|
38
|
+
|
|
39
|
+
export type MiddleStateWDomChildren = MiddleStateWDom[];
|
|
40
|
+
|
|
41
|
+
// component, fragment, element, loop, text, none
|
|
42
|
+
export type WDomType = 'c' | 'f' | 'e' | 'l' | 't' | 'et';
|
|
43
|
+
|
|
44
|
+
export interface WDom {
|
|
45
|
+
type: string | null;
|
|
46
|
+
isRoot?: boolean;
|
|
47
|
+
tag?: string;
|
|
48
|
+
props?: Props;
|
|
49
|
+
oldProps?: Props;
|
|
50
|
+
tagName?: string;
|
|
51
|
+
constructor?: Function;
|
|
52
|
+
children?: WDom[];
|
|
53
|
+
getParent?: () => WDom | undefined;
|
|
54
|
+
nodeChildKey?: NodeChildKey;
|
|
55
|
+
text?: string | number;
|
|
56
|
+
componentKey?: Props;
|
|
57
|
+
reRender?: () => WDom;
|
|
58
|
+
componentProps?: Props;
|
|
59
|
+
componentChildren?: WDom[];
|
|
60
|
+
wrapElement?: HTMLElement;
|
|
61
|
+
el?: HTMLElement | DocumentFragment | Text;
|
|
62
|
+
needRerender?: RenderType;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type RenderType =
|
|
66
|
+
| 'A' // ADD
|
|
67
|
+
| 'D' // DELETE
|
|
68
|
+
| 'R' // REPLACE
|
|
69
|
+
| 'U' // UPDATE
|
|
70
|
+
| 'SR' // S_REPLACE
|
|
71
|
+
| 'SU' // S_UPDATE
|
|
72
|
+
| 'N'; // NONE
|
|
73
|
+
|
|
74
|
+
export type ComponentSubKey =
|
|
75
|
+
| 'up'
|
|
76
|
+
| 'upR'
|
|
77
|
+
| 'upS'
|
|
78
|
+
| 'upD'
|
|
79
|
+
| 'upCB'
|
|
80
|
+
| 'mts'
|
|
81
|
+
| 'umts';
|
|
82
|
+
|
|
83
|
+
export type ComponentRef = WeakMap<
|
|
84
|
+
Props,
|
|
85
|
+
{
|
|
86
|
+
up: () => void;
|
|
87
|
+
upR: (() => void)[];
|
|
88
|
+
upS: { value: number };
|
|
89
|
+
upD: unknown[][];
|
|
90
|
+
upCB: (() => void)[];
|
|
91
|
+
mts: (() => void)[];
|
|
92
|
+
umts: (() => void)[];
|
|
93
|
+
}
|
|
94
|
+
>;
|
|
95
|
+
|
|
96
|
+
export type Param<Updater, Member, Props> = {
|
|
97
|
+
updater: Updater;
|
|
98
|
+
props: Props;
|
|
99
|
+
member: Member;
|
|
100
|
+
children: WDom[];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
declare namespace JSX {
|
|
104
|
+
interface IntrinsicElements {
|
|
105
|
+
[name: string]: any;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { WDom, TagFunctionResolver, Props } from '@/types';
|
|
2
|
+
|
|
3
|
+
export const getParent = (vDom: WDom) => {
|
|
4
|
+
const parentVDom = vDom.getParent && vDom.getParent();
|
|
5
|
+
|
|
6
|
+
if (!parentVDom) {
|
|
7
|
+
throw Error('Not found parent vDom');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return parentVDom;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const reRender = (vDom: WDom, infoVdom: TagFunctionResolver) => {
|
|
14
|
+
const { componentProps: props, componentChildren: children } = vDom;
|
|
15
|
+
const { props: infoProps, children: infoChidren } = infoVdom;
|
|
16
|
+
|
|
17
|
+
if (props) {
|
|
18
|
+
updateProps(props, infoProps);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (children) {
|
|
22
|
+
updateChildren(children, infoChidren);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const newVDom = vDom.reRender && vDom.reRender();
|
|
26
|
+
|
|
27
|
+
if (!newVDom) {
|
|
28
|
+
throw Error('Unable reRender');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return newVDom;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const updateProps = (props: Props, infoProps: Props) => {
|
|
35
|
+
if (props && infoProps !== props) {
|
|
36
|
+
Object.keys(props).forEach(key => delete props[key]);
|
|
37
|
+
|
|
38
|
+
Object.entries(infoProps || {}).forEach(
|
|
39
|
+
([key, value]) => (props[key] = value)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const updateChildren = (children: WDom[], infoChidren: WDom[]) => {
|
|
45
|
+
if (children && infoChidren !== children) {
|
|
46
|
+
children.splice(0, children.length);
|
|
47
|
+
|
|
48
|
+
if (infoChidren) {
|
|
49
|
+
infoChidren.forEach(childrenItem => children.push(childrenItem));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Fragment } from '@/wDom';
|
|
2
|
+
import {
|
|
3
|
+
WDom,
|
|
4
|
+
TagFunction,
|
|
5
|
+
TagFunctionResolver,
|
|
6
|
+
FragmentFunction,
|
|
7
|
+
WDomType,
|
|
8
|
+
} from '@/types';
|
|
9
|
+
|
|
10
|
+
type DiffParam = {
|
|
11
|
+
originalWDom?: WDom;
|
|
12
|
+
newWDom: WDom | TagFunction | TagFunctionResolver;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type WDomParam =
|
|
16
|
+
| string
|
|
17
|
+
| WDom
|
|
18
|
+
| TagFunction
|
|
19
|
+
| TagFunctionResolver
|
|
20
|
+
| FragmentFunction;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Predicator
|
|
24
|
+
*/
|
|
25
|
+
export const checkCustemComponentFunction = (
|
|
26
|
+
target: WDomParam
|
|
27
|
+
): target is TagFunction | TagFunctionResolver =>
|
|
28
|
+
(typeof target === 'function' && !checkFragmentFunction(target)) ||
|
|
29
|
+
(typeof target === 'object' && 'resolve' in target);
|
|
30
|
+
|
|
31
|
+
export const checkFragmentFunction = (
|
|
32
|
+
target: unknown
|
|
33
|
+
): target is FragmentFunction =>
|
|
34
|
+
typeof target === 'function' && target === Fragment;
|
|
35
|
+
|
|
36
|
+
export const checkPlainWDomType = (wDom: WDomParam): wDom is WDom =>
|
|
37
|
+
typeof wDom === 'object' && !('resolve' in wDom);
|
|
38
|
+
|
|
39
|
+
export const checkPlainType = (wDom: WDomParam, typeName: string) =>
|
|
40
|
+
checkPlainWDomType(wDom) && wDom.type === typeName;
|
|
41
|
+
|
|
42
|
+
export const checkEmptyElement = (wDom: WDomParam) =>
|
|
43
|
+
checkPlainWDomType(wDom) && !wDom.type;
|
|
44
|
+
|
|
45
|
+
export const checkSameCustomComponent = ({
|
|
46
|
+
originalWDom,
|
|
47
|
+
newWDom,
|
|
48
|
+
}: DiffParam) => newWDom.constructor === originalWDom?.constructor;
|
|
49
|
+
|
|
50
|
+
export const checkSameFragment = ({ originalWDom, newWDom }: DiffParam) =>
|
|
51
|
+
checkPlainWDomType(newWDom) &&
|
|
52
|
+
originalWDom?.type === 'fragment' &&
|
|
53
|
+
originalWDom?.children?.length === newWDom?.children?.length;
|
|
54
|
+
|
|
55
|
+
export const checkSameTagElement = ({ originalWDom, newWDom }: DiffParam) =>
|
|
56
|
+
checkPlainWDomType(newWDom) &&
|
|
57
|
+
originalWDom?.type === 'element' &&
|
|
58
|
+
originalWDom?.tag === newWDom.tag;
|
|
59
|
+
|
|
60
|
+
export const checkNormalTypeElement = ({ originalWDom, newWDom }: DiffParam) =>
|
|
61
|
+
checkPlainWDomType(newWDom) && originalWDom?.type === newWDom.type;
|
|
62
|
+
|
|
63
|
+
export const checkExisty = (value: unknown) =>
|
|
64
|
+
value !== null && value !== undefined;
|
|
65
|
+
|
|
66
|
+
export const checkFunction = (target: unknown): target is Function =>
|
|
67
|
+
typeof target === 'function';
|
|
68
|
+
|
|
69
|
+
export const checkStyleData = (
|
|
70
|
+
dataKey: string,
|
|
71
|
+
dataValue: unknown
|
|
72
|
+
): dataValue is Record<string, string> =>
|
|
73
|
+
dataKey === 'style' && typeof dataValue === 'object';
|
|
74
|
+
|
|
75
|
+
export const checkRefData = (
|
|
76
|
+
dataKey: string,
|
|
77
|
+
dataValue: unknown
|
|
78
|
+
): dataValue is { value: HTMLElement | DocumentFragment | Text | undefined } =>
|
|
79
|
+
dataKey === 'ref' && typeof dataValue === 'object';
|
|
80
|
+
|
|
81
|
+
export const checkOptionElement = (element: any): element is HTMLElement => {
|
|
82
|
+
return element.nodeType === 1 && element.tagName === 'OPTION';
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const checkNormalAttribute = (
|
|
86
|
+
dataValue: unknown
|
|
87
|
+
): dataValue is number | string =>
|
|
88
|
+
typeof dataValue === 'number' || typeof dataValue === 'string';
|
|
89
|
+
|
|
90
|
+
export const getWDomType = (
|
|
91
|
+
wDom: WDom | TagFunction | TagFunctionResolver
|
|
92
|
+
): WDomType | undefined => {
|
|
93
|
+
let result: WDomType | undefined;
|
|
94
|
+
|
|
95
|
+
if (checkCustemComponentFunction(wDom)) {
|
|
96
|
+
result = 'c';
|
|
97
|
+
} else if (checkPlainType(wDom, 'fragment')) {
|
|
98
|
+
result = 'f';
|
|
99
|
+
} else if (checkPlainType(wDom, 'element')) {
|
|
100
|
+
result = 'e';
|
|
101
|
+
} else if (checkPlainType(wDom, 'loop')) {
|
|
102
|
+
result = 'l';
|
|
103
|
+
} else if (checkPlainType(wDom, 'text')) {
|
|
104
|
+
result = 't';
|
|
105
|
+
} else if (checkEmptyElement(wDom)) {
|
|
106
|
+
result = 'et';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const checkSameWDomWithOriginal = {
|
|
113
|
+
c: checkSameCustomComponent,
|
|
114
|
+
l: checkNormalTypeElement,
|
|
115
|
+
t: checkNormalTypeElement,
|
|
116
|
+
e: checkSameTagElement,
|
|
117
|
+
f: checkSameFragment,
|
|
118
|
+
et: checkNormalTypeElement,
|
|
119
|
+
};
|