@simpreact/simpreact 0.0.0-alpha.1f6ee65 → 0.0.1

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.
Files changed (65) hide show
  1. package/core/createElement.d.ts +22 -15
  2. package/core/createElement.js +67 -23
  3. package/core/hostAdapter.d.ts +17 -7
  4. package/core/hostAdapter.js +4 -1
  5. package/core/index.d.ts +68 -3
  6. package/core/index.js +6 -3
  7. package/core/internal.d.ts +6 -3
  8. package/core/internal.js +6 -3
  9. package/core/lifecycleEventBus.d.ts +26 -0
  10. package/core/lifecycleEventBus.js +2 -0
  11. package/core/mounting.d.ts +9 -8
  12. package/core/mounting.js +87 -50
  13. package/core/patching.d.ts +6 -4
  14. package/core/patching.js +150 -162
  15. package/core/portal.d.ts +2 -0
  16. package/core/portal.js +9 -0
  17. package/core/ref.d.ts +17 -0
  18. package/core/ref.js +27 -0
  19. package/core/rerender.d.ts +11 -0
  20. package/core/rerender.js +61 -3
  21. package/core/unmounting.d.ts +2 -4
  22. package/core/unmounting.js +26 -27
  23. package/dom/attach-element-to-dom.d.ts +4 -0
  24. package/dom/attach-element-to-dom.js +9 -0
  25. package/dom/domAdapter.d.ts +3 -2
  26. package/dom/domAdapter.js +37 -113
  27. package/dom/events.d.ts +19 -0
  28. package/dom/events.js +129 -0
  29. package/dom/index.d.ts +1733 -1
  30. package/dom/index.js +2 -0
  31. package/dom/namespace.d.ts +2 -0
  32. package/dom/namespace.js +1 -0
  33. package/dom/props/controlled/index.d.ts +7 -0
  34. package/dom/props/controlled/index.js +51 -0
  35. package/dom/props/controlled/input.d.ts +7 -0
  36. package/dom/props/controlled/input.js +80 -0
  37. package/dom/props/controlled/select.d.ts +6 -0
  38. package/dom/props/controlled/select.js +76 -0
  39. package/dom/props/controlled/textarea.d.ts +6 -0
  40. package/dom/props/controlled/textarea.js +61 -0
  41. package/dom/props/dangerInnerHTML.d.ts +7 -0
  42. package/dom/props/dangerInnerHTML.js +24 -0
  43. package/dom/props/index.d.ts +1 -0
  44. package/dom/props/index.js +1 -0
  45. package/dom/props/props.d.ts +5 -0
  46. package/dom/props/props.js +197 -0
  47. package/dom/props/style.d.ts +1 -0
  48. package/dom/props/style.js +32 -0
  49. package/dom/render.d.ts +2 -2
  50. package/dom/render.js +31 -19
  51. package/hooks/index.d.ts +23 -12
  52. package/hooks/index.js +123 -29
  53. package/jsx-runtime/index.d.ts +247 -4
  54. package/jsx-runtime/index.js +2 -5
  55. package/package.json +22 -15
  56. package/shared/index.d.ts +19 -4
  57. package/shared/index.js +5 -4
  58. package/shared/lang.d.ts +3 -3
  59. package/shared/lang.js +3 -3
  60. package/shared/utils.d.ts +3 -2
  61. package/shared/utils.js +3 -6
  62. package/core/global.d.ts +0 -21
  63. package/core/global.js +0 -5
  64. package/shared/types.d.ts +0 -8
  65. package/shared/types.js +0 -1
package/core/patching.js CHANGED
@@ -1,208 +1,171 @@
1
+ import { emptyMap, emptyObject } from '../shared';
1
2
  import { normalizeRoot } from './createElement';
2
- import { EMPTY_MAP, EMPTY_OBJECT, isPrimitive } from '../shared';
3
- import { clearElementHostReference, remove, removeAllChildren, unmount, unmountAllChildren } from './unmounting';
3
+ import { hostAdapter } from './hostAdapter';
4
+ import { clearElementHostReference, remove, unmount } from './unmounting';
4
5
  import { mount, mountArrayChildren } from './mounting';
5
- import { GLOBAL } from './global';
6
- export function patch(prevElement, nextElement, parentReference, nextReference, contextMap) {
6
+ import { applyRef } from './ref';
7
+ import { lifecycleEventBus } from './lifecycleEventBus';
8
+ export function patch(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
7
9
  if (prevElement.type !== nextElement.type || prevElement.key !== nextElement.key) {
8
- replaceWithNewElement(prevElement, nextElement, parentReference, contextMap);
10
+ replaceWithNewElement(prevElement, nextElement, parentReference, contextMap, hostNamespace);
9
11
  }
10
12
  else if (nextElement.flag === 'HOST') {
11
- patchElement(prevElement, nextElement, contextMap);
13
+ patchHostElement(prevElement, nextElement, contextMap, hostNamespace);
12
14
  }
13
15
  else if (nextElement.flag === 'FC') {
14
- if (prevElement.store != null) {
15
- nextElement.store = prevElement.store;
16
- }
17
- patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap);
16
+ patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace);
18
17
  }
19
18
  else if (nextElement.flag === 'TEXT') {
20
- patchText(prevElement, nextElement);
19
+ patchTextElement(prevElement, nextElement);
21
20
  }
22
21
  else if (nextElement.flag === 'FRAGMENT') {
23
- patchFragment(prevElement, nextElement, parentReference, nextReference, contextMap);
22
+ patchFragment(prevElement, nextElement, parentReference, contextMap, hostNamespace);
24
23
  }
25
24
  else if (nextElement.flag === 'PROVIDER') {
26
- patchProvider(prevElement, nextElement, parentReference, nextReference, contextMap);
25
+ patchProvider(prevElement, nextElement, parentReference, contextMap, hostNamespace);
26
+ }
27
+ else if (nextElement.flag === 'PORTAL') {
28
+ patchPortal(prevElement, nextElement, contextMap);
27
29
  }
28
30
  else {
29
- patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap);
31
+ patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace);
30
32
  }
31
33
  }
32
- function replaceWithNewElement(prevElement, nextElement, parentReference, contextMap) {
34
+ function replaceWithNewElement(prevElement, nextElement, parentReference, contextMap, hostNamespace) {
33
35
  unmount(prevElement);
36
+ nextElement.parent = prevElement.parent;
34
37
  if (nextElement.flag === 'HOST' && prevElement.flag === 'HOST') {
35
- mount(nextElement, null, null, contextMap);
36
- GLOBAL.hostAdapter.replaceChild(parentReference, nextElement.reference, prevElement.reference);
38
+ mount(nextElement, null, null, contextMap, hostNamespace);
39
+ hostAdapter.replaceChild(parentReference, nextElement.reference, prevElement.reference);
37
40
  }
38
41
  else {
39
- mount(nextElement, parentReference, findHostReferenceFromElement(prevElement), contextMap);
42
+ mount(nextElement, parentReference, findHostReferenceFromElement(prevElement), contextMap, hostNamespace);
40
43
  clearElementHostReference(prevElement, parentReference);
41
44
  }
42
45
  }
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);
46
+ function patchHostElement(prevElement, nextElement, contextMap, hostNamespace) {
47
+ if (prevElement.ref) {
48
+ nextElement.ref = prevElement.ref;
52
49
  }
53
- return null;
50
+ const hostNamespaces = hostAdapter.getHostNamespaces(nextElement, hostNamespace);
51
+ hostNamespace = hostNamespaces?.self;
52
+ nextElement.reference = prevElement.reference;
53
+ hostAdapter.attachElementToReference(nextElement, nextElement.reference);
54
+ patchChildren(prevElement.children, nextElement.children, nextElement.reference, null, nextElement, contextMap, hostNamespaces?.children);
55
+ hostAdapter.patchProps(nextElement.reference, prevElement, nextElement, hostNamespace);
56
+ if (prevElement.className !== nextElement.className) {
57
+ hostAdapter.setClassname(nextElement.reference, nextElement.className, hostNamespace);
58
+ }
59
+ applyRef(nextElement);
54
60
  }
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
- }
61
+ function patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
62
+ (nextElement.store = prevElement.store ||= {}).latestElement = nextElement;
63
+ if (hostNamespace) {
64
+ nextElement.store.hostNamespace = hostNamespace;
65
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
- }
66
+ if (contextMap) {
67
+ nextElement.contextMap = contextMap;
70
68
  }
71
- if (prevElement.className !== nextElement.className) {
72
- GLOBAL.hostAdapter.setClassname(hostReference, nextElement.className);
69
+ let nextChildren;
70
+ try {
71
+ lifecycleEventBus.publish({ type: 'beforeRender', element: nextElement, phase: 'updating' });
72
+ nextChildren = normalizeRoot(nextElement.type(nextElement.props || emptyObject), false);
73
+ lifecycleEventBus.publish({ type: 'afterRender', phase: 'updating' });
74
+ }
75
+ catch (error) {
76
+ lifecycleEventBus.publish({ type: 'errored', element: nextElement, error, phase: 'updating' });
77
+ return;
73
78
  }
74
- patchChildren(prevElement.children, nextElement.children, hostReference, null, prevElement, contextMap);
79
+ const prevChildren = prevElement.children;
80
+ if (nextChildren) {
81
+ nextElement.children = nextChildren;
82
+ }
83
+ patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, contextMap, hostNamespace);
84
+ lifecycleEventBus.publish({ type: 'updated', element: nextElement });
75
85
  }
76
- function patchChildren(prevChildren, nextChildren, parentReference, nextReference, parentElement, contextMap) {
86
+ function patchTextElement(prevElement, nextElement) {
87
+ nextElement.reference = prevElement.reference;
88
+ if (nextElement.children !== prevElement.children) {
89
+ hostAdapter.setTextContent(nextElement.reference, nextElement.children);
90
+ }
91
+ }
92
+ function patchFragment(prevElement, nextElement, parentReference, contextMap, hostNamespace) {
93
+ let nextReference = null;
94
+ if (Array.isArray(prevElement.children) && !Array.isArray(nextElement.children) && nextElement.children) {
95
+ nextReference = hostAdapter.findNextSiblingReference(findHostReferenceFromElement(prevElement.children[prevElement.children.length - 1]));
96
+ }
97
+ patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, contextMap, hostNamespace);
98
+ }
99
+ function patchProvider(prevElement, nextElement, parentReference, contextMap, hostNamespace) {
100
+ let nextReference = null;
101
+ if (Array.isArray(prevElement.children) && !Array.isArray(nextElement.children) && nextElement.children) {
102
+ nextReference = hostAdapter.findNextSiblingReference(findHostReferenceFromElement(prevElement.children[prevElement.children.length - 1]));
103
+ }
104
+ contextMap = new Map(contextMap);
105
+ contextMap.set(nextElement.type.context, nextElement.props.value);
106
+ patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, contextMap, hostNamespace);
107
+ }
108
+ function patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
109
+ const children = normalizeRoot(nextElement.type(nextElement.props || emptyObject, contextMap || emptyMap), false);
110
+ if (children) {
111
+ nextElement.children = children;
112
+ }
113
+ patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, contextMap, hostNamespace);
114
+ }
115
+ export function patchPortal(prevElement, nextElement, contextMap) {
116
+ const prevContainer = prevElement.ref;
117
+ const nextContainer = nextElement.ref;
118
+ const nextChildren = nextElement.children;
119
+ patchChildren(prevElement.children, nextChildren, prevContainer, null, nextElement, contextMap, hostAdapter.getHostNamespaces(nextChildren, undefined)?.self);
120
+ nextElement.reference = prevElement.reference;
121
+ if (prevContainer !== nextContainer && nextChildren != null) {
122
+ hostAdapter.removeChild(prevContainer, nextChildren.reference);
123
+ hostAdapter.appendChild(nextContainer, nextChildren.reference);
124
+ }
125
+ }
126
+ export function updateFunctionalComponent(element, parentReference, nextReference, contextMap, hostNamespace) {
127
+ patchFunctionalComponent(element, element, parentReference, nextReference, contextMap, hostNamespace);
128
+ }
129
+ function patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, contextMap, hostNamespace) {
77
130
  if (Array.isArray(prevChildren)) {
78
131
  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);
132
+ for (const child of nextChildren) {
133
+ child.parent = nextElement;
91
134
  }
135
+ patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace);
92
136
  }
93
- else if (isPrimitive(nextChildren)) {
94
- unmountAllChildren(prevChildren);
95
- GLOBAL.hostAdapter.setTextContent(parentReference, (nextChildren || ''));
137
+ else if (nextChildren) {
138
+ patchKeyedChildren(prevChildren, [nextChildren], parentReference, nextReference, contextMap, hostNamespace);
96
139
  }
97
140
  else {
98
- removeAllChildren(parentReference, parentElement, prevChildren);
99
- mount(nextChildren, parentReference, nextReference, contextMap);
141
+ unmount(prevChildren);
142
+ hostAdapter.clearNode(parentReference);
100
143
  }
101
144
  }
102
- else if (isPrimitive(prevChildren)) {
145
+ else if (prevChildren) {
103
146
  if (Array.isArray(nextChildren)) {
104
- GLOBAL.hostAdapter.clearNode(parentReference);
105
- mountArrayChildren(nextChildren, parentReference, nextReference, contextMap);
147
+ patchKeyedChildren([prevChildren], nextChildren, parentReference, nextReference, contextMap, hostNamespace);
106
148
  }
107
- else if (isPrimitive(nextChildren)) {
108
- patchSingleTextChild(prevChildren, nextChildren, parentReference);
149
+ else if (nextChildren) {
150
+ nextChildren.parent = nextElement;
151
+ patch(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace);
109
152
  }
110
153
  else {
111
- GLOBAL.hostAdapter.clearNode(parentReference);
112
- mount(nextChildren, parentReference, nextReference, contextMap);
154
+ unmount(prevChildren);
155
+ hostAdapter.clearNode(parentReference);
113
156
  }
114
157
  }
115
158
  else {
116
159
  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);
160
+ mountArrayChildren(nextChildren, parentReference, nextReference, contextMap, nextElement, hostNamespace);
122
161
  }
123
- else {
124
- patch(prevChildren, nextChildren, parentReference, nextReference, contextMap);
162
+ else if (nextChildren) {
163
+ nextChildren.parent = nextElement;
164
+ mount(nextChildren, parentReference, nextReference, contextMap, hostNamespace);
125
165
  }
126
166
  }
127
167
  }
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) {
168
+ export function patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace) {
206
169
  let prevStart = 0;
207
170
  let nextStart = 0;
208
171
  let prevEnd = prevChildren.length - 1;
@@ -211,13 +174,13 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
211
174
  while (prevStart <= prevEnd &&
212
175
  nextStart <= nextEnd &&
213
176
  prevChildren[prevStart].key === nextChildren[nextStart].key) {
214
- patch(prevChildren[prevStart], nextChildren[nextStart], parentReference, null, contextMap);
177
+ patch(prevChildren[prevStart], nextChildren[nextStart], parentReference, null, contextMap, hostNamespace);
215
178
  prevStart++;
216
179
  nextStart++;
217
180
  }
218
181
  // Step 2: Sync from end
219
182
  while (prevStart <= prevEnd && nextStart <= nextEnd && prevChildren[prevEnd].key === nextChildren[nextEnd].key) {
220
- patch(prevChildren[prevEnd], nextChildren[nextEnd], parentReference, null, contextMap);
183
+ patch(prevChildren[prevEnd], nextChildren[nextEnd], parentReference, null, contextMap, hostNamespace);
221
184
  prevEnd--;
222
185
  nextEnd--;
223
186
  }
@@ -225,7 +188,7 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
225
188
  if (prevStart > prevEnd) {
226
189
  const before = nextChildren[nextEnd + 1]?.reference || nextReference;
227
190
  for (let i = nextStart; i <= nextEnd; i++) {
228
- mount(nextChildren[i], parentReference, before, contextMap);
191
+ mount(nextChildren[i], parentReference, before, contextMap, hostNamespace);
229
192
  }
230
193
  // Step 4: Remove prev nodes if next list is exhausted
231
194
  }
@@ -240,8 +203,9 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
240
203
  const keyToPrevIndexMap = new Map();
241
204
  for (let i = prevStart; i <= prevEnd; i++) {
242
205
  const key = prevChildren[i].key;
243
- if (key != null)
206
+ if (key != null) {
244
207
  keyToPrevIndexMap.set(key, i);
208
+ }
245
209
  }
246
210
  // Track reused indices and move plan
247
211
  const toMove = new Array(nextEnd - nextStart + 1);
@@ -252,12 +216,12 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
252
216
  const prevIndex = keyToPrevIndexMap.get(nextChild.key);
253
217
  if (prevIndex != null) {
254
218
  const prevElement = prevChildren[prevIndex];
255
- patch(prevElement, nextChild, parentReference, null, contextMap);
219
+ patch(prevElement, nextChild, parentReference, null, contextMap, hostNamespace);
256
220
  toMove[i - nextStart] = prevIndex;
257
221
  usedIndices.add(prevIndex);
258
222
  }
259
223
  else {
260
- mount(nextChild, parentReference, nextChildren[i + 1]?.reference || nextReference, contextMap);
224
+ mount(nextChild, parentReference, nextChildren[i + 1]?.reference || nextReference, contextMap, hostNamespace);
261
225
  toMove[i - nextStart] = -1;
262
226
  }
263
227
  }
@@ -272,8 +236,32 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
272
236
  const currentChild = nextChildren[i];
273
237
  const reference = nextChildren[i + 1]?.reference || nextReference;
274
238
  if (toMove[i - nextStart] !== -1) {
275
- GLOBAL.hostAdapter.insertBefore(parentReference, currentChild.reference, reference);
239
+ hostAdapter.insertBefore(parentReference, currentChild.reference, reference);
276
240
  }
277
241
  }
278
242
  }
279
243
  }
244
+ export function findParentReferenceFromElement(element) {
245
+ let flag;
246
+ let temp = element;
247
+ while (temp != null) {
248
+ flag = temp.flag;
249
+ if (flag === 'HOST') {
250
+ return temp.reference;
251
+ }
252
+ temp = temp.parent;
253
+ }
254
+ return null;
255
+ }
256
+ export function findHostReferenceFromElement(element) {
257
+ let flag;
258
+ let temp = element;
259
+ while (temp != null) {
260
+ flag = temp.flag;
261
+ if (flag === 'HOST' || flag === 'TEXT' || flag === 'PORTAL') {
262
+ return temp.reference;
263
+ }
264
+ temp = (Array.isArray(temp.children) ? temp.children[0] : temp.children);
265
+ }
266
+ return null;
267
+ }
@@ -0,0 +1,2 @@
1
+ import type { SimpElement, SimpNode } from './createElement';
2
+ export declare function createPortal(children: SimpNode, container: any): SimpElement;
package/core/portal.js ADDED
@@ -0,0 +1,9 @@
1
+ import { normalizeRoot } from './createElement';
2
+ export function createPortal(children, container) {
3
+ const element = { flag: 'PORTAL', parent: null };
4
+ if ((children = normalizeRoot(children, false))) {
5
+ element.children = children;
6
+ }
7
+ element.ref = container;
8
+ return element;
9
+ }
package/core/ref.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { SimpElement } from './createElement';
2
+ interface RefSimpElement extends SimpElement {
3
+ ref?: {
4
+ value: NonNullable<Ref<unknown>>;
5
+ cleanup?: () => void;
6
+ };
7
+ }
8
+ export interface RefObject<T> {
9
+ current: T;
10
+ }
11
+ export type RefCallback<T> = {
12
+ bivarianceHack(instance: T | null): (() => void) | void;
13
+ }['bivarianceHack'];
14
+ export type Ref<T> = RefCallback<T> | RefObject<T> | null;
15
+ export declare function unmountRef(element: RefSimpElement): void;
16
+ export declare function applyRef(element: RefSimpElement): void;
17
+ export {};
package/core/ref.js ADDED
@@ -0,0 +1,27 @@
1
+ export function unmountRef(element) {
2
+ if (element.ref == null) {
3
+ return;
4
+ }
5
+ if (typeof element.ref.cleanup === 'function') {
6
+ element.ref.cleanup();
7
+ }
8
+ if (typeof element.ref.value !== 'function') {
9
+ element.ref.value.current = null;
10
+ }
11
+ }
12
+ export function applyRef(element) {
13
+ if (element.ref == null) {
14
+ return;
15
+ }
16
+ if (typeof element.ref.cleanup === 'function') {
17
+ element.ref.cleanup();
18
+ }
19
+ if (typeof element.ref.value === 'function') {
20
+ let cleanup;
21
+ if ((cleanup = element.ref.value(element.reference))) {
22
+ element.ref.cleanup = cleanup;
23
+ }
24
+ return;
25
+ }
26
+ element.ref.value.current = element.reference;
27
+ }
@@ -1,2 +1,13 @@
1
1
  import type { SimpElement } from './createElement';
2
2
  export declare function rerender(element: SimpElement): void;
3
+ interface IRendererLocker {
4
+ _isLocked: boolean;
5
+ _elements: Set<SimpElement>;
6
+ isLocked: boolean;
7
+ lock(): void;
8
+ track(element: SimpElement): void;
9
+ flush(): void;
10
+ }
11
+ export declare const syncRerenderLocker: IRendererLocker;
12
+ export declare const asyncRerenderLocker: IRendererLocker;
13
+ export {};
package/core/rerender.js CHANGED
@@ -1,5 +1,63 @@
1
- import { findHostReferenceFromElement, updateFunctionalComponent } from './patching';
2
- import { GLOBAL } from './global';
1
+ import { findParentReferenceFromElement, updateFunctionalComponent } from './patching';
3
2
  export function rerender(element) {
4
- updateFunctionalComponent(element, GLOBAL.hostAdapter.findParentReference(findHostReferenceFromElement(element)), null, element.contextMap || null);
3
+ if (element.flag !== 'FC') {
4
+ throw new TypeError('Re-rendering is only supported for FC elements.');
5
+ }
6
+ if (element.unmounted) {
7
+ console.warn('The component unmounted.');
8
+ }
9
+ if (syncRerenderLocker.isLocked) {
10
+ syncRerenderLocker.track(element);
11
+ return;
12
+ }
13
+ else if (asyncRerenderLocker.isLocked) {
14
+ asyncRerenderLocker.track(element);
15
+ }
16
+ else {
17
+ asyncRerenderLocker.lock();
18
+ updateFunctionalComponent(element, findParentReferenceFromElement(element), null, element.contextMap || null, element.store.hostNamespace);
19
+ asyncRerenderLocker.flush();
20
+ }
5
21
  }
22
+ export const syncRerenderLocker = {
23
+ _isLocked: false,
24
+ _elements: new Set(),
25
+ get isLocked() {
26
+ return this._isLocked;
27
+ },
28
+ lock() {
29
+ this._isLocked = true;
30
+ },
31
+ track(element) {
32
+ this._elements.add(element);
33
+ },
34
+ flush() {
35
+ this._isLocked = false;
36
+ for (const element of this._elements) {
37
+ this._elements.delete(element);
38
+ rerender(element.store.latestElement);
39
+ }
40
+ },
41
+ };
42
+ export const asyncRerenderLocker = {
43
+ _isLocked: false,
44
+ _elements: new Set(),
45
+ get isLocked() {
46
+ return this._isLocked;
47
+ },
48
+ lock() {
49
+ this._isLocked = true;
50
+ },
51
+ track(element) {
52
+ this._elements.add(element);
53
+ },
54
+ flush() {
55
+ this._isLocked = false;
56
+ queueMicrotask(() => {
57
+ for (const element of this._elements) {
58
+ this._elements.delete(element);
59
+ rerender(element.store.latestElement);
60
+ }
61
+ });
62
+ },
63
+ };
@@ -1,8 +1,6 @@
1
+ import type { Many, Maybe } from '../shared';
1
2
  import type { SimpElement } from './createElement';
2
- import type { Maybe } from '../shared';
3
3
  import type { HostReference } from './hostAdapter';
4
- export declare function unmount(element: SimpElement): void;
5
- export declare function unmountAllChildren(children: SimpElement[]): void;
4
+ export declare function unmount(element: Many<SimpElement>): void;
6
5
  export declare function clearElementHostReference(element: Maybe<SimpElement>, parentHostReference: HostReference): void;
7
- export declare function removeAllChildren(hostReference: HostReference, element: SimpElement, children: SimpElement[]): void;
8
6
  export declare function remove(element: SimpElement, parentReference: HostReference): void;
@@ -1,32 +1,43 @@
1
- import { GLOBAL } from './global';
1
+ import { hostAdapter } from './hostAdapter';
2
+ import { unmountRef } from './ref';
3
+ import { lifecycleEventBus } from './lifecycleEventBus';
2
4
  export function unmount(element) {
5
+ if (Array.isArray(element)) {
6
+ for (const child of element) {
7
+ unmount(child);
8
+ }
9
+ return;
10
+ }
3
11
  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 });
12
+ // FC element always has Maybe<SimpElement> due to normalization.
13
+ if (element.children) {
14
+ unmount(element.children);
15
+ }
16
+ element.unmounted = true;
17
+ lifecycleEventBus.publish({ type: 'unmounted', element });
7
18
  return;
8
19
  }
9
20
  if (element.flag === 'TEXT') {
10
21
  return;
11
22
  }
23
+ if (element.flag === 'PORTAL') {
24
+ remove(element.children, element.ref);
25
+ return;
26
+ }
12
27
  // Only FRAGMENT, PROVIDER, CONSUMER, and HOST elements remain,
13
28
  // 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) {
29
+ if (element.children) {
18
30
  unmount(element.children);
19
31
  }
20
- }
21
- export function unmountAllChildren(children) {
22
- for (const child of children) {
23
- unmount(child);
32
+ if (element.flag === 'HOST') {
33
+ unmountRef(element);
34
+ hostAdapter.unmountProps(element.reference, element);
24
35
  }
25
36
  }
26
37
  export function clearElementHostReference(element, parentHostReference) {
27
38
  while (element != null) {
28
- if (element.flag === 'HOST' || element.flag === 'TEXT') {
29
- GLOBAL.hostAdapter.removeChild(parentHostReference, element.reference);
39
+ if (element.flag === 'HOST' || element.flag === 'TEXT' || element.flag === 'PORTAL') {
40
+ hostAdapter.removeChild(parentHostReference, element.reference);
30
41
  return;
31
42
  }
32
43
  const children = element.children;
@@ -41,24 +52,12 @@ export function clearElementHostReference(element, parentHostReference) {
41
52
  }
42
53
  return;
43
54
  }
44
- else if (children != null) {
55
+ else if (children) {
45
56
  element = children;
46
57
  }
47
58
  }
48
59
  }
49
60
  }
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
61
  export function remove(element, parentReference) {
63
62
  unmount(element);
64
63
  clearElementHostReference(element, parentReference);
@@ -0,0 +1,4 @@
1
+ import type { Nullable } from '../shared';
2
+ import type { SimpElement } from '../core/internal.js';
3
+ export declare function attachElementToDom(element: SimpElement, dom: Node): void;
4
+ export declare function getElementFromDom(target: Nullable<EventTarget>): Nullable<SimpElement>;
@@ -0,0 +1,9 @@
1
+ const elementPropertyName = '__SIMP_ELEMENT__';
2
+ export function attachElementToDom(element, dom) {
3
+ if (dom.nodeType !== Node.TEXT_NODE) {
4
+ Object.defineProperty(dom, elementPropertyName, { value: element, writable: true });
5
+ }
6
+ }
7
+ export function getElementFromDom(target) {
8
+ return target?.[elementPropertyName] ?? null;
9
+ }