@simpreact/simpreact 0.0.0-alpha.dd6f145
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/createElement.d.ts +24 -0
- package/core/createElement.js +144 -0
- package/core/fragment.d.ts +5 -0
- package/core/fragment.js +2 -0
- package/core/global.d.ts +21 -0
- package/core/global.js +5 -0
- package/core/hostAdapter.d.ts +16 -0
- package/core/hostAdapter.js +1 -0
- package/core/index.d.ts +2 -0
- package/core/index.js +2 -0
- package/core/internal.d.ts +7 -0
- package/core/internal.js +7 -0
- package/core/jsx-runtime.d.ts +2 -0
- package/core/jsx-runtime.js +2 -0
- package/core/mounting.d.ts +9 -0
- package/core/mounting.js +66 -0
- package/core/patching.d.ts +7 -0
- package/core/patching.js +271 -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 +70 -0
- package/dom/domAdapter.d.ts +2 -0
- package/dom/domAdapter.js +156 -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 +27 -0
- package/hooks/index.d.ts +10 -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 +30 -0
- package/shared/index.d.ts +4 -0
- package/shared/index.js +4 -0
- package/shared/lang.d.ts +4 -0
- package/shared/lang.js +4 -0
- package/shared/types.d.ts +8 -0
- package/shared/types.js +1 -0
- package/shared/utils.d.ts +3 -0
- package/shared/utils.js +8 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dmitrii Paskhin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# simpreact
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Many, Maybe, Primitive } from '../shared';
|
|
2
|
+
import type { HostReference } from './hostAdapter';
|
|
3
|
+
export type SimpNode = SimpElement | string | number | bigint | Array<SimpNode> | boolean | null | undefined;
|
|
4
|
+
export type Props = any;
|
|
5
|
+
export type Key = string | number | bigint;
|
|
6
|
+
export interface FunctionComponent<P = Props> {
|
|
7
|
+
(props: P): SimpNode;
|
|
8
|
+
}
|
|
9
|
+
export type FC<P = Props> = FunctionComponent<P>;
|
|
10
|
+
export type SimpElementFlag = 'FC' | 'HOST' | 'TEXT' | 'FRAGMENT';
|
|
11
|
+
export interface SimpElement<T = Props> {
|
|
12
|
+
flag: SimpElementFlag;
|
|
13
|
+
key?: Maybe<Key>;
|
|
14
|
+
type?: Maybe<string | FunctionComponent<T>>;
|
|
15
|
+
props?: Maybe<T>;
|
|
16
|
+
children?: Maybe<SimpNode>;
|
|
17
|
+
className?: Maybe<string>;
|
|
18
|
+
reference?: Maybe<HostReference>;
|
|
19
|
+
store?: unknown;
|
|
20
|
+
}
|
|
21
|
+
export declare function createElement<P = Props>(type: string | FunctionComponent<Readonly<P>>, props?: Maybe<P>, ...children: SimpNode[]): SimpElement<P>;
|
|
22
|
+
export declare function createTextElement(text: Primitive): SimpElement;
|
|
23
|
+
export declare function normalizeChildren(children: SimpNode): Maybe<Many<SimpElement>>;
|
|
24
|
+
export declare function normalizeRoot(node: SimpNode): SimpElement;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { IS_DEVELOPMENT, isArray, isPrimitive } from '../shared';
|
|
2
|
+
import { Fragment } from './fragment';
|
|
3
|
+
export function createElement(type, props, ...children) {
|
|
4
|
+
let newProps;
|
|
5
|
+
let className;
|
|
6
|
+
let key;
|
|
7
|
+
let definedChildren;
|
|
8
|
+
const childLength = children.length;
|
|
9
|
+
if (childLength === 1) {
|
|
10
|
+
definedChildren = children[0];
|
|
11
|
+
}
|
|
12
|
+
else if (childLength > 1) {
|
|
13
|
+
definedChildren = [];
|
|
14
|
+
for (let i = 0; i < childLength; i++) {
|
|
15
|
+
definedChildren.push(children[i]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (typeof type === 'string') {
|
|
19
|
+
if (props != null) {
|
|
20
|
+
for (const propName in props) {
|
|
21
|
+
if (propName === 'className') {
|
|
22
|
+
className = props[propName];
|
|
23
|
+
}
|
|
24
|
+
else if (propName === 'key') {
|
|
25
|
+
key = props[propName];
|
|
26
|
+
}
|
|
27
|
+
else if (propName === 'children') {
|
|
28
|
+
if (definedChildren === undefined) {
|
|
29
|
+
definedChildren = props[propName];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
(newProps || (newProps = {}))[propName] = props[propName];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const element = {
|
|
38
|
+
flag: 'HOST',
|
|
39
|
+
type,
|
|
40
|
+
};
|
|
41
|
+
if (className) {
|
|
42
|
+
element.className = className;
|
|
43
|
+
}
|
|
44
|
+
if (key) {
|
|
45
|
+
element.key = key;
|
|
46
|
+
}
|
|
47
|
+
if ((definedChildren = normalizeChildren(definedChildren))) {
|
|
48
|
+
element.children = definedChildren;
|
|
49
|
+
}
|
|
50
|
+
if (newProps) {
|
|
51
|
+
element.props = newProps;
|
|
52
|
+
}
|
|
53
|
+
return element;
|
|
54
|
+
}
|
|
55
|
+
else if (type === Fragment) {
|
|
56
|
+
const element = {
|
|
57
|
+
flag: 'FRAGMENT',
|
|
58
|
+
};
|
|
59
|
+
element.children = normalizeChildren(definedChildren || (props != null ? props.children : null));
|
|
60
|
+
if (props != null && props.key) {
|
|
61
|
+
element.key = props === null || props === void 0 ? void 0 : props.key;
|
|
62
|
+
}
|
|
63
|
+
return element;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
if (props != null) {
|
|
67
|
+
for (const propName in props) {
|
|
68
|
+
if (propName === 'key') {
|
|
69
|
+
key = props[propName];
|
|
70
|
+
}
|
|
71
|
+
else if (propName === 'children') {
|
|
72
|
+
if (definedChildren === undefined) {
|
|
73
|
+
definedChildren = props[propName];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
(newProps || (newProps = {}))[propName] = props[propName];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (definedChildren !== undefined) {
|
|
82
|
+
(newProps || (newProps = {}))['children'] = definedChildren;
|
|
83
|
+
}
|
|
84
|
+
const element = {
|
|
85
|
+
flag: 'FC',
|
|
86
|
+
type,
|
|
87
|
+
};
|
|
88
|
+
if (key) {
|
|
89
|
+
element.key = key;
|
|
90
|
+
}
|
|
91
|
+
if (newProps != null) {
|
|
92
|
+
element.props = newProps;
|
|
93
|
+
}
|
|
94
|
+
return element;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function createTextElement(text) {
|
|
98
|
+
return {
|
|
99
|
+
flag: 'TEXT',
|
|
100
|
+
children: text == null || text === true || text === false ? '' : text,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function normalizeChildren(children) {
|
|
104
|
+
if (children == null || typeof children === 'boolean') {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const result = [];
|
|
108
|
+
normalizeNode(children, result);
|
|
109
|
+
if (result.length === 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
return result.length === 1 ? result[0] : result;
|
|
113
|
+
}
|
|
114
|
+
function normalizeNode(child, result) {
|
|
115
|
+
if (child == null || typeof child === 'boolean') {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (typeof child === 'string' || typeof child === 'number' || typeof child === 'bigint') {
|
|
119
|
+
result.push(createTextElement(child));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (isArray(child)) {
|
|
123
|
+
for (const nestedChild of child) {
|
|
124
|
+
normalizeNode(nestedChild, result);
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (typeof child === 'object') {
|
|
129
|
+
// noinspection SuspiciousTypeOfGuard
|
|
130
|
+
if (IS_DEVELOPMENT && typeof child.flag !== 'string') {
|
|
131
|
+
throw new TypeError(`Objects are not valid as a child: ${JSON.stringify(child)}.`);
|
|
132
|
+
}
|
|
133
|
+
result.push(child);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export function normalizeRoot(node) {
|
|
137
|
+
if (isPrimitive(node)) {
|
|
138
|
+
return createTextElement(node);
|
|
139
|
+
}
|
|
140
|
+
if (isArray(node)) {
|
|
141
|
+
return createElement(Fragment, { children: node });
|
|
142
|
+
}
|
|
143
|
+
return node;
|
|
144
|
+
}
|
package/core/fragment.js
ADDED
package/core/global.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HostAdapter } from './hostAdapter';
|
|
2
|
+
import type { SimpElement } from './createElement';
|
|
3
|
+
import { EventBus } from '../shared';
|
|
4
|
+
export type LifecycleEvent = {
|
|
5
|
+
type: 'beforeRender';
|
|
6
|
+
element: SimpElement;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'afterRender';
|
|
9
|
+
} | {
|
|
10
|
+
type: 'mounted';
|
|
11
|
+
element: SimpElement;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'unmounted';
|
|
14
|
+
element: SimpElement;
|
|
15
|
+
};
|
|
16
|
+
interface Global {
|
|
17
|
+
hostAdapter: HostAdapter;
|
|
18
|
+
eventBus: EventBus<LifecycleEvent>;
|
|
19
|
+
}
|
|
20
|
+
export declare const GLOBAL: Global;
|
|
21
|
+
export {};
|
package/core/global.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Dict, Maybe, Nullable } from '../shared';
|
|
2
|
+
export type HostReference = never;
|
|
3
|
+
export interface HostAdapter<HostRef = never, HostTextRef = never> {
|
|
4
|
+
createReference(type: string): HostRef;
|
|
5
|
+
createTextReference(text: string): HostTextRef;
|
|
6
|
+
mountProps(reference: HostRef, props: Dict): void;
|
|
7
|
+
patchProp(reference: HostRef, propName: string, prevValue: unknown, nextValue: unknown): void;
|
|
8
|
+
setClassname(reference: HostRef, className: Maybe<string>): void;
|
|
9
|
+
setTextContent(reference: HostRef, text: string): void;
|
|
10
|
+
appendChild(parent: HostRef, child: HostRef | HostTextRef): void;
|
|
11
|
+
removeChild(parent: HostRef, child: HostRef | HostTextRef): void;
|
|
12
|
+
replaceChild(parent: HostRef, replacer: HostRef | HostTextRef, child: HostRef | HostTextRef): void;
|
|
13
|
+
insertBefore(parent: HostRef, child: HostRef | HostTextRef, before: Nullable<HostRef | HostTextRef>): void;
|
|
14
|
+
insertOrAppend(parent: HostRef, child: HostRef | HostTextRef, before: Nullable<HostRef | HostTextRef>): void;
|
|
15
|
+
findParentReference(reference: HostRef | HostTextRef): Nullable<HostRef>;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/core/index.d.ts
ADDED
package/core/index.js
ADDED
package/core/internal.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Nullable } from '../shared';
|
|
2
|
+
import type { HostReference } from './hostAdapter';
|
|
3
|
+
import type { SimpElement } from './createElement';
|
|
4
|
+
export declare function mount<HostRef = HostReference>(element: SimpElement, parentReference: Nullable<HostRef>, nextReference: Nullable<HostRef>): void;
|
|
5
|
+
export declare function mountTextElement<HostRef = HostReference>(element: SimpElement, parentReference: Nullable<HostRef>, nextReference: Nullable<HostRef>): void;
|
|
6
|
+
export declare function mountHostElement<HostRef = HostReference>(element: SimpElement, parentReference: Nullable<HostRef>, nextReference: Nullable<HostRef>): void;
|
|
7
|
+
export declare function mountFunctionalElement<HostRef = HostReference>(element: SimpElement, parentReference: Nullable<HostRef>, nextReference: Nullable<HostRef>): void;
|
|
8
|
+
export declare function mountFragment<HostRef = HostReference>(element: SimpElement, parentReference: Nullable<HostRef>, nextReference: Nullable<HostRef>): void;
|
|
9
|
+
export declare function mountArrayChildren<HostRef = HostReference>(children: SimpElement[], reference: Nullable<HostRef>, nextReference: Nullable<HostRef>): void;
|
package/core/mounting.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { EMPTY_OBJECT } from '../shared';
|
|
2
|
+
import { GLOBAL } from './global';
|
|
3
|
+
import { normalizeRoot } from './createElement';
|
|
4
|
+
export function mount(element, parentReference, nextReference) {
|
|
5
|
+
if (element.flag === 'TEXT') {
|
|
6
|
+
mountTextElement(element, parentReference, nextReference);
|
|
7
|
+
}
|
|
8
|
+
else if (element.flag === 'HOST') {
|
|
9
|
+
mountHostElement(element, parentReference, nextReference);
|
|
10
|
+
}
|
|
11
|
+
else if (element.flag === 'FC') {
|
|
12
|
+
mountFunctionalElement(element, parentReference, nextReference);
|
|
13
|
+
}
|
|
14
|
+
else if (element.flag === 'FRAGMENT') {
|
|
15
|
+
mountFragment(element, parentReference, nextReference);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function mountTextElement(element, parentReference, nextReference) {
|
|
19
|
+
const reference = (element.reference || (element.reference = GLOBAL.hostAdapter.createTextReference(element.children)));
|
|
20
|
+
if (parentReference != null) {
|
|
21
|
+
GLOBAL.hostAdapter.insertOrAppend(parentReference, reference, nextReference);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function mountHostElement(element, parentReference, nextReference) {
|
|
25
|
+
const props = element.props;
|
|
26
|
+
const className = element.className;
|
|
27
|
+
const hostReference = (element.reference = GLOBAL.hostAdapter.createReference(element.type));
|
|
28
|
+
// HOST element always has Maybe<Many<SimpElement>> children due to normalization process.
|
|
29
|
+
const children = element.children;
|
|
30
|
+
if (className != null && className !== '') {
|
|
31
|
+
GLOBAL.hostAdapter.setClassname(hostReference, className);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(children)) {
|
|
34
|
+
mountArrayChildren(children, hostReference, null);
|
|
35
|
+
}
|
|
36
|
+
else if (children != null) {
|
|
37
|
+
mount(children, hostReference, null);
|
|
38
|
+
}
|
|
39
|
+
if (parentReference != null) {
|
|
40
|
+
GLOBAL.hostAdapter.insertOrAppend(parentReference, hostReference, nextReference);
|
|
41
|
+
}
|
|
42
|
+
GLOBAL.hostAdapter.mountProps(hostReference, props);
|
|
43
|
+
}
|
|
44
|
+
export function mountFunctionalElement(element, parentReference, nextReference) {
|
|
45
|
+
const type = element.type;
|
|
46
|
+
GLOBAL.eventBus.publish({ type: 'beforeRender', element });
|
|
47
|
+
element.children = normalizeRoot(type(element.props || EMPTY_OBJECT));
|
|
48
|
+
GLOBAL.eventBus.publish({ type: 'afterRender' });
|
|
49
|
+
mount(element.children, parentReference, nextReference);
|
|
50
|
+
GLOBAL.eventBus.publish({ type: 'mounted', element });
|
|
51
|
+
}
|
|
52
|
+
export function mountFragment(element, parentReference, nextReference) {
|
|
53
|
+
// FRAGMENT element always has Maybe<Many<SimpElement>> children due to normalization process.
|
|
54
|
+
const children = element.children;
|
|
55
|
+
if (Array.isArray(children)) {
|
|
56
|
+
mountArrayChildren(children, parentReference, nextReference);
|
|
57
|
+
}
|
|
58
|
+
else if (children != null) {
|
|
59
|
+
mount(children, parentReference, nextReference);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function mountArrayChildren(children, reference, nextReference) {
|
|
63
|
+
for (const child of children) {
|
|
64
|
+
mount(child, reference, nextReference);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SimpElement } from './createElement';
|
|
2
|
+
import type { Nullable } from '../shared';
|
|
3
|
+
import type { HostReference } from './hostAdapter';
|
|
4
|
+
export declare function patch<HostRef = HostReference>(prevElement: SimpElement, nextElement: SimpElement, parentReference: HostRef, nextReference: Nullable<HostRef>): void;
|
|
5
|
+
export declare function findHostReferenceFromElement(element: SimpElement, startEdge: boolean): Nullable<HostReference>;
|
|
6
|
+
export declare function updateFunctionalComponent(element: SimpElement, parentReference: HostReference, nextReference: Nullable<HostReference>): void;
|
|
7
|
+
export declare function patchKeyedChildren<HostRef = HostReference>(prevChildren: SimpElement[], nextChildren: SimpElement[], parentReference: HostRef, nextReference: Nullable<HostRef>): void;
|
package/core/patching.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { normalizeRoot } from './createElement';
|
|
2
|
+
import { EMPTY_OBJECT, isArray, 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) {
|
|
7
|
+
if (prevElement.type !== nextElement.type || prevElement.key !== nextElement.key) {
|
|
8
|
+
replaceWithNewElement(prevElement, nextElement, parentReference);
|
|
9
|
+
}
|
|
10
|
+
else if (nextElement.flag === 'HOST') {
|
|
11
|
+
patchElement(prevElement, nextElement);
|
|
12
|
+
}
|
|
13
|
+
else if (nextElement.flag === 'FC') {
|
|
14
|
+
if (prevElement.store != null) {
|
|
15
|
+
nextElement.store = prevElement.store;
|
|
16
|
+
}
|
|
17
|
+
patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference);
|
|
18
|
+
}
|
|
19
|
+
else if (nextElement.flag === 'TEXT') {
|
|
20
|
+
patchText(prevElement, nextElement);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
patchFragment(prevElement, nextElement, parentReference);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function replaceWithNewElement(prevElement, nextElement, parentReference) {
|
|
27
|
+
unmount(prevElement);
|
|
28
|
+
if (nextElement.flag === 'HOST' && prevElement.flag === 'HOST') {
|
|
29
|
+
mount(nextElement, null, null);
|
|
30
|
+
GLOBAL.hostAdapter.replaceChild(parentReference, nextElement.reference, prevElement.reference);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
mount(nextElement, parentReference, findHostReferenceFromElement(prevElement, true));
|
|
34
|
+
clearElementHostReference(prevElement, parentReference);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function findHostReferenceFromElement(element, startEdge) {
|
|
38
|
+
let flag;
|
|
39
|
+
let temp = element;
|
|
40
|
+
while (temp != null) {
|
|
41
|
+
flag = temp.flag;
|
|
42
|
+
if (flag === 'HOST' || flag === 'TEXT') {
|
|
43
|
+
return temp.reference;
|
|
44
|
+
}
|
|
45
|
+
temp = findChildElement(temp, startEdge);
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function findChildElement(element, startEdge) {
|
|
50
|
+
const children = element.children;
|
|
51
|
+
if (isArray(children)) {
|
|
52
|
+
return children[startEdge ? 0 : children.length - 1];
|
|
53
|
+
}
|
|
54
|
+
return children;
|
|
55
|
+
}
|
|
56
|
+
function patchElement(prevElement, nextElement) {
|
|
57
|
+
const hostReference = (nextElement.reference = prevElement.reference);
|
|
58
|
+
const prevProps = prevElement.props;
|
|
59
|
+
const nextProps = nextElement.props;
|
|
60
|
+
for (const propName in nextProps) {
|
|
61
|
+
const prevValue = prevProps[propName];
|
|
62
|
+
const nextValue = nextProps[propName];
|
|
63
|
+
if (prevValue !== nextValue) {
|
|
64
|
+
GLOBAL.hostAdapter.patchProp(hostReference, propName, prevValue, nextValue);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const propName in prevProps) {
|
|
68
|
+
if (nextProps[propName] == null && prevProps[propName] != null) {
|
|
69
|
+
GLOBAL.hostAdapter.patchProp(hostReference, propName, prevProps[propName], null);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const prevChildren = prevElement.children;
|
|
73
|
+
const nextChildren = nextElement.children;
|
|
74
|
+
const nextClassName = nextElement.className;
|
|
75
|
+
if (prevElement.className !== nextClassName) {
|
|
76
|
+
GLOBAL.hostAdapter.setClassname(hostReference, nextClassName);
|
|
77
|
+
}
|
|
78
|
+
patchChildren(prevChildren, nextChildren, hostReference, null, prevElement);
|
|
79
|
+
}
|
|
80
|
+
function patchChildren(prevChildren, nextChildren, parentReference, nextReference, parentElement) {
|
|
81
|
+
if (isArray(prevChildren)) {
|
|
82
|
+
if (isArray(nextChildren)) {
|
|
83
|
+
const prevChildrenLength = prevChildren.length;
|
|
84
|
+
const nextChildrenLength = nextChildren.length;
|
|
85
|
+
if (prevChildrenLength === 0) {
|
|
86
|
+
if (nextChildrenLength > 0) {
|
|
87
|
+
mountArrayChildren(nextChildren, parentReference, nextReference);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (nextChildrenLength === 0) {
|
|
91
|
+
removeAllChildren(parentReference, parentElement, prevChildren);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (isPrimitive(nextChildren)) {
|
|
98
|
+
unmountAllChildren(prevChildren);
|
|
99
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, (nextChildren || ''));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
removeAllChildren(parentReference, parentElement, prevChildren);
|
|
103
|
+
mount(nextChildren, parentReference, nextReference);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (isPrimitive(prevChildren)) {
|
|
107
|
+
if (isArray(nextChildren)) {
|
|
108
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, '');
|
|
109
|
+
mountArrayChildren(nextChildren, parentReference, nextReference);
|
|
110
|
+
}
|
|
111
|
+
else if (isPrimitive(nextChildren)) {
|
|
112
|
+
patchSingleTextChild(prevChildren, nextChildren, parentReference);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, '');
|
|
116
|
+
mount(nextChildren, parentReference, nextReference);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
if (isArray(nextChildren)) {
|
|
121
|
+
replaceOneElementWithMultipleElements(prevChildren, nextChildren, parentReference);
|
|
122
|
+
}
|
|
123
|
+
else if (isPrimitive(nextChildren)) {
|
|
124
|
+
unmount(prevChildren);
|
|
125
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, nextChildren);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
patch(prevChildren, nextChildren, parentReference, nextReference);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function replaceOneElementWithMultipleElements(prevChildren, nextChildren, parentReference) {
|
|
133
|
+
unmount(prevChildren);
|
|
134
|
+
mountArrayChildren(nextChildren, parentReference, findHostReferenceFromElement(prevChildren, true));
|
|
135
|
+
clearElementHostReference(prevChildren, parentReference);
|
|
136
|
+
}
|
|
137
|
+
function patchSingleTextChild(prevChildren, nextChildren, parentReference) {
|
|
138
|
+
if (prevChildren !== nextChildren) {
|
|
139
|
+
GLOBAL.hostAdapter.setTextContent(parentReference, nextChildren);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// export function patchNonKeyedChildren(
|
|
143
|
+
// prevChildren: SimpElement[],
|
|
144
|
+
// nextChildren: SimpElement[],
|
|
145
|
+
// parentReference: HostReference,
|
|
146
|
+
// prevChildrenLength: number,
|
|
147
|
+
// nextChildrenLength: number,
|
|
148
|
+
// nextReference: Nullable<HostReference>
|
|
149
|
+
// ): void {
|
|
150
|
+
// const commonLength = prevChildrenLength > nextChildrenLength ? nextChildrenLength : prevChildrenLength;
|
|
151
|
+
// let i = 0;
|
|
152
|
+
// let prevChild;
|
|
153
|
+
// let nextChild;
|
|
154
|
+
//
|
|
155
|
+
// for (; i < commonLength; ++i) {
|
|
156
|
+
// nextChild = nextChildren[i];
|
|
157
|
+
// prevChild = prevChildren[i];
|
|
158
|
+
//
|
|
159
|
+
// patch(prevChild as SimpElement, nextChild as SimpElement, parentReference, nextReference);
|
|
160
|
+
// prevChildren[i] = nextChild!;
|
|
161
|
+
// }
|
|
162
|
+
// if (prevChildrenLength < nextChildrenLength) {
|
|
163
|
+
// for (i = commonLength; i < nextChildrenLength; ++i) {
|
|
164
|
+
// nextChild = nextChildren[i];
|
|
165
|
+
// mount(nextChild as SimpElement, parentReference, nextReference);
|
|
166
|
+
// }
|
|
167
|
+
// } else if (prevChildrenLength > nextChildrenLength) {
|
|
168
|
+
// for (i = commonLength; i < prevChildrenLength; ++i) {
|
|
169
|
+
// remove(prevChildren[i] as SimpElement, parentReference);
|
|
170
|
+
// }
|
|
171
|
+
// }
|
|
172
|
+
// }
|
|
173
|
+
function patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference) {
|
|
174
|
+
const prevChildren = prevElement.children;
|
|
175
|
+
const type = nextElement.type;
|
|
176
|
+
GLOBAL.eventBus.publish({ type: 'beforeRender', element: nextElement });
|
|
177
|
+
const nextChildren = normalizeRoot(type(nextElement.props || EMPTY_OBJECT));
|
|
178
|
+
GLOBAL.eventBus.publish({ type: 'afterRender' });
|
|
179
|
+
patch(prevChildren, nextChildren, parentReference, nextReference);
|
|
180
|
+
GLOBAL.eventBus.publish({ type: 'mounted', element: nextElement });
|
|
181
|
+
nextElement.children = nextChildren;
|
|
182
|
+
}
|
|
183
|
+
function patchText(prevElement, nextElement) {
|
|
184
|
+
const nextText = nextElement.children;
|
|
185
|
+
const reference = (nextElement.reference = prevElement.reference);
|
|
186
|
+
if (nextText !== prevElement.children) {
|
|
187
|
+
GLOBAL.hostAdapter.setTextContent(reference, nextText);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function patchFragment(prevElement, nextElement, parentReference) {
|
|
191
|
+
patchChildren(prevElement.children, nextElement.children, parentReference, null, prevElement);
|
|
192
|
+
}
|
|
193
|
+
export function updateFunctionalComponent(element, parentReference, nextReference) {
|
|
194
|
+
patch(element, element, parentReference, nextReference);
|
|
195
|
+
}
|
|
196
|
+
export function patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference) {
|
|
197
|
+
var _a, _b, _c;
|
|
198
|
+
let prevStart = 0;
|
|
199
|
+
let nextStart = 0;
|
|
200
|
+
let prevEnd = prevChildren.length - 1;
|
|
201
|
+
let nextEnd = nextChildren.length - 1;
|
|
202
|
+
// Step 1: Sync from start
|
|
203
|
+
while (prevStart <= prevEnd &&
|
|
204
|
+
nextStart <= nextEnd &&
|
|
205
|
+
prevChildren[prevStart].key === nextChildren[nextStart].key) {
|
|
206
|
+
patch(prevChildren[prevStart], nextChildren[nextStart], parentReference, null);
|
|
207
|
+
prevStart++;
|
|
208
|
+
nextStart++;
|
|
209
|
+
}
|
|
210
|
+
// Step 2: Sync from end
|
|
211
|
+
while (prevStart <= prevEnd && nextStart <= nextEnd && prevChildren[prevEnd].key === nextChildren[nextEnd].key) {
|
|
212
|
+
patch(prevChildren[prevEnd], nextChildren[nextEnd], parentReference, null);
|
|
213
|
+
prevEnd--;
|
|
214
|
+
nextEnd--;
|
|
215
|
+
}
|
|
216
|
+
// Step 3: Mount new nodes if prev list is exhausted
|
|
217
|
+
if (prevStart > prevEnd) {
|
|
218
|
+
const before = ((_a = nextChildren[nextEnd + 1]) === null || _a === void 0 ? void 0 : _a.reference) || nextReference;
|
|
219
|
+
for (let i = nextStart; i <= nextEnd; i++) {
|
|
220
|
+
mount(nextChildren[i], parentReference, before);
|
|
221
|
+
}
|
|
222
|
+
// Step 4: Remove prev nodes if next list is exhausted
|
|
223
|
+
}
|
|
224
|
+
else if (nextStart > nextEnd) {
|
|
225
|
+
for (let i = prevStart; i <= prevEnd; i++) {
|
|
226
|
+
remove(prevChildren[i], parentReference);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Step 5: Full diff with keyed lookup and movement
|
|
230
|
+
else {
|
|
231
|
+
// Create map of keys to indices for prev children
|
|
232
|
+
const keyToPrevIndexMap = new Map();
|
|
233
|
+
for (let i = prevStart; i <= prevEnd; i++) {
|
|
234
|
+
const key = prevChildren[i].key;
|
|
235
|
+
if (key != null)
|
|
236
|
+
keyToPrevIndexMap.set(key, i);
|
|
237
|
+
}
|
|
238
|
+
// Track reused indices and move plan
|
|
239
|
+
const toMove = new Array(nextEnd - nextStart + 1);
|
|
240
|
+
const usedIndices = new Set();
|
|
241
|
+
// Match and patch/mount
|
|
242
|
+
for (let i = nextStart; i <= nextEnd; i++) {
|
|
243
|
+
const nextChild = nextChildren[i];
|
|
244
|
+
const prevIndex = keyToPrevIndexMap.get(nextChild.key);
|
|
245
|
+
if (prevIndex != null) {
|
|
246
|
+
const prevElement = prevChildren[prevIndex];
|
|
247
|
+
patch(prevElement, nextChild, parentReference, null);
|
|
248
|
+
toMove[i - nextStart] = prevIndex;
|
|
249
|
+
usedIndices.add(prevIndex);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
mount(nextChild, parentReference, ((_b = nextChildren[i + 1]) === null || _b === void 0 ? void 0 : _b.reference) || nextReference);
|
|
253
|
+
toMove[i - nextStart] = -1;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Remove nodes not matched
|
|
257
|
+
for (let i = prevStart; i <= prevEnd; i++) {
|
|
258
|
+
if (!usedIndices.has(i)) {
|
|
259
|
+
remove(prevChildren[i], parentReference);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Insert in correct order
|
|
263
|
+
for (let i = nextEnd; i >= nextStart; i--) {
|
|
264
|
+
const currentChild = nextChildren[i];
|
|
265
|
+
const reference = ((_c = nextChildren[i + 1]) === null || _c === void 0 ? void 0 : _c.reference) || nextReference;
|
|
266
|
+
if (toMove[i - nextStart] !== -1) {
|
|
267
|
+
GLOBAL.hostAdapter.insertBefore(parentReference, currentChild.reference, reference);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
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, true)), 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<HostRef = HostReference>(element: Maybe<SimpElement>, parentHostReference: HostRef): void;
|
|
7
|
+
export declare function removeAllChildren<HostRef = HostReference>(hostReference: HostRef, element: SimpElement, children: SimpElement[]): void;
|
|
8
|
+
export declare function remove<HostRef = HostReference>(element: SimpElement, parentReference: HostRef): void;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isArray } from '../shared';
|
|
2
|
+
import { GLOBAL } from './global';
|
|
3
|
+
export function unmount(element) {
|
|
4
|
+
if (element.flag === 'HOST') {
|
|
5
|
+
if (isArray(element.children)) {
|
|
6
|
+
for (const child of element.children) {
|
|
7
|
+
unmount(child);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
else if (element.children != null) {
|
|
11
|
+
unmount(element.children);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else if (element.flag === 'FC') {
|
|
15
|
+
unmount(element.children);
|
|
16
|
+
GLOBAL.eventBus.publish({ type: 'unmounted', element });
|
|
17
|
+
}
|
|
18
|
+
else if (element.flag === 'FRAGMENT') {
|
|
19
|
+
if (isArray(element.children)) {
|
|
20
|
+
for (const child of element.children) {
|
|
21
|
+
unmount(child);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (element.children != null) {
|
|
25
|
+
unmount(element.children);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function unmountAllChildren(children) {
|
|
30
|
+
for (const child of children) {
|
|
31
|
+
unmount(child);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function clearElementHostReference(element, parentHostReference) {
|
|
35
|
+
while (element != null) {
|
|
36
|
+
if (element.flag === 'HOST' || element.flag === 'TEXT') {
|
|
37
|
+
GLOBAL.hostAdapter.removeChild(parentHostReference, element.reference);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const children = element.children;
|
|
41
|
+
if (element.flag === 'FC') {
|
|
42
|
+
element = children;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (element.flag === 'FRAGMENT') {
|
|
46
|
+
if (isArray(children)) {
|
|
47
|
+
for (let i = 0, len = children.length; i < len; ++i) {
|
|
48
|
+
clearElementHostReference(children[i], parentHostReference);
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
else if (children != null) {
|
|
53
|
+
element = children;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function removeAllChildren(hostReference, element, children) {
|
|
59
|
+
unmountAllChildren(children);
|
|
60
|
+
if (element.flag === 'FRAGMENT') {
|
|
61
|
+
clearElementHostReference(element, hostReference);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
GLOBAL.hostAdapter.setTextContent(hostReference, '');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function remove(element, parentReference) {
|
|
68
|
+
unmount(element);
|
|
69
|
+
clearElementHostReference(element, parentReference);
|
|
70
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
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, child) {
|
|
43
|
+
parent.replaceChild(child, replacer);
|
|
44
|
+
},
|
|
45
|
+
findParentReference(reference) {
|
|
46
|
+
return reference.parentElement;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
function mountProps(props, reference) {
|
|
50
|
+
for (const propsKey in props) {
|
|
51
|
+
patchProp(propsKey, null, props[propsKey], reference);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function patchProp(propName, prevValue, nextValue, dom) {
|
|
55
|
+
switch (propName) {
|
|
56
|
+
case 'children':
|
|
57
|
+
case 'childrenType':
|
|
58
|
+
case 'className':
|
|
59
|
+
case 'defaultValue':
|
|
60
|
+
case 'key':
|
|
61
|
+
case 'multiple':
|
|
62
|
+
case 'ref':
|
|
63
|
+
case 'selectedIndex':
|
|
64
|
+
break;
|
|
65
|
+
case 'autoFocus':
|
|
66
|
+
dom.autofocus = !!nextValue;
|
|
67
|
+
break;
|
|
68
|
+
case 'allowfullscreen':
|
|
69
|
+
case 'autoplay':
|
|
70
|
+
case 'capture':
|
|
71
|
+
case 'checked':
|
|
72
|
+
case 'controls':
|
|
73
|
+
case 'default':
|
|
74
|
+
case 'disabled':
|
|
75
|
+
case 'hidden':
|
|
76
|
+
case 'indeterminate':
|
|
77
|
+
case 'loop':
|
|
78
|
+
case 'muted':
|
|
79
|
+
case 'novalidate':
|
|
80
|
+
case 'open':
|
|
81
|
+
case 'readOnly':
|
|
82
|
+
case 'required':
|
|
83
|
+
case 'reversed':
|
|
84
|
+
case 'scoped':
|
|
85
|
+
case 'seamless':
|
|
86
|
+
case 'selected':
|
|
87
|
+
dom[propName] = !!nextValue;
|
|
88
|
+
break;
|
|
89
|
+
case 'defaultChecked':
|
|
90
|
+
case 'value':
|
|
91
|
+
case 'volume':
|
|
92
|
+
patchDomProp(nextValue, dom, propName);
|
|
93
|
+
break;
|
|
94
|
+
case 'style':
|
|
95
|
+
patchStyle(prevValue, nextValue, dom);
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
if (propName.charCodeAt(0) === 111 && propName.charCodeAt(1) === 110) {
|
|
99
|
+
patchEvent(propName, prevValue, nextValue, dom);
|
|
100
|
+
}
|
|
101
|
+
else if (nextValue == null) {
|
|
102
|
+
dom.removeAttribute(propName);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
dom.setAttribute(propName, nextValue);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function patchDomProp(nextValue, dom, propKey) {
|
|
111
|
+
const value = nextValue == null ? '' : nextValue;
|
|
112
|
+
if (dom[propKey] !== value) {
|
|
113
|
+
dom[propKey] = value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function patchStyle(lastAttrValue, nextAttrValue, dom) {
|
|
117
|
+
if (nextAttrValue == null) {
|
|
118
|
+
dom.removeAttribute('style');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const domStyle = dom.style;
|
|
122
|
+
let style;
|
|
123
|
+
let value;
|
|
124
|
+
if (typeof nextAttrValue === 'string') {
|
|
125
|
+
domStyle.cssText = nextAttrValue;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (lastAttrValue != null && typeof lastAttrValue !== 'string') {
|
|
129
|
+
for (style in nextAttrValue) {
|
|
130
|
+
value = nextAttrValue[style];
|
|
131
|
+
if (value !== lastAttrValue[style]) {
|
|
132
|
+
domStyle.setProperty(style, value);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (style in lastAttrValue) {
|
|
136
|
+
if (nextAttrValue[style] == null) {
|
|
137
|
+
domStyle.removeProperty(style);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
for (style in nextAttrValue) {
|
|
143
|
+
value = nextAttrValue[style];
|
|
144
|
+
domStyle.setProperty(style, value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function patchEvent(name, lastValue, nextValue, dom) {
|
|
149
|
+
name = name.toLowerCase().substring(2);
|
|
150
|
+
if (typeof lastValue === 'function') {
|
|
151
|
+
dom.removeEventListener(name, lastValue);
|
|
152
|
+
}
|
|
153
|
+
if (typeof nextValue === 'function') {
|
|
154
|
+
dom.addEventListener(name, nextValue);
|
|
155
|
+
}
|
|
156
|
+
}
|
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: HTMLElement): SimpRoot;
|
|
9
|
+
export {};
|
package/dom/render.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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);
|
|
6
|
+
}
|
|
7
|
+
export function createRoot(container) {
|
|
8
|
+
let currentRoot = null;
|
|
9
|
+
return {
|
|
10
|
+
render(element) {
|
|
11
|
+
if (currentRoot) {
|
|
12
|
+
patch(currentRoot, element, container, null);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
mount(element, container, null);
|
|
16
|
+
}
|
|
17
|
+
currentRoot = element;
|
|
18
|
+
},
|
|
19
|
+
unmount() {
|
|
20
|
+
// if (currentRoot != null) {
|
|
21
|
+
// enqueueRender(currentRoot, null);
|
|
22
|
+
// currentRoot = null;
|
|
23
|
+
// }
|
|
24
|
+
currentRoot = null;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
package/hooks/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { VoidFunction } from '../shared';
|
|
2
|
+
type Cleanup = VoidFunction;
|
|
3
|
+
type Effect = () => void | Cleanup;
|
|
4
|
+
type DependencyList = readonly unknown[];
|
|
5
|
+
export declare function useRef<T>(initialValue: T): {
|
|
6
|
+
current: T;
|
|
7
|
+
};
|
|
8
|
+
export declare function useRerender(): VoidFunction;
|
|
9
|
+
export declare function useEffect(effect: Effect, deps?: DependencyList): void;
|
|
10
|
+
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 || (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
|
+
var _a, _b;
|
|
32
|
+
return ((_a = currentElement.store.hookStates)[_b = currentIndex++] || (_a[_b] = { current: initialValue }));
|
|
33
|
+
}
|
|
34
|
+
export function useRerender() {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
const state = ((_a = currentElement.store.hookStates)[_b = currentIndex++] || (_a[_b] = {
|
|
37
|
+
element: null,
|
|
38
|
+
fn() {
|
|
39
|
+
rerender(state.element);
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
state.element = currentElement;
|
|
43
|
+
return state.fn;
|
|
44
|
+
}
|
|
45
|
+
export function useEffect(effect, deps) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
const state = ((_a = currentElement.store.hookStates)[_b = currentIndex++] || (_a[_b] = {
|
|
48
|
+
effect,
|
|
49
|
+
deps: undefined,
|
|
50
|
+
cleanup: undefined,
|
|
51
|
+
}));
|
|
52
|
+
if (!areDepsEqual(deps, state.deps)) {
|
|
53
|
+
state.effect = effect;
|
|
54
|
+
state.deps = deps;
|
|
55
|
+
currentElement.store.mountEffects.push(state);
|
|
56
|
+
}
|
|
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 || (props = {})).key = _key;
|
|
12
|
+
}
|
|
13
|
+
return createElement(type, props);
|
|
14
|
+
}
|
|
15
|
+
export { jsx as jsxs, jsx as jsxDEV };
|
|
16
|
+
export { Fragment };
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@simpreact/simpreact",
|
|
3
|
+
"version": "0.0.0-alpha.dd6f145",
|
|
4
|
+
"description": "",
|
|
5
|
+
"homepage": "https://github.com/dPaskhin/simpreact#readme",
|
|
6
|
+
"main": "./core/index.js",
|
|
7
|
+
"module": "./core/index.js",
|
|
8
|
+
"types": "./core/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./core/index.d.ts",
|
|
12
|
+
"import": "./core/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./jsx-runtime": {
|
|
15
|
+
"types": "./jsx-runtime/index.d.ts",
|
|
16
|
+
"import": "./jsx-runtime/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./jsx-dev-runtime": {
|
|
19
|
+
"types": "./jsx-runtime/index.d.ts",
|
|
20
|
+
"import": "./jsx-runtime/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./hooks": {
|
|
23
|
+
"types": "./hooks/index.d.ts",
|
|
24
|
+
"import": "./hooks/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./dom": {
|
|
27
|
+
"types": "./dom/index.d.ts",
|
|
28
|
+
"import": "./dom/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./shared": {
|
|
31
|
+
"types": "./shared/index.d.ts",
|
|
32
|
+
"import": "./shared/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/dPaskhin/simpreact/issues"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/dPaskhin/simpreact.git"
|
|
41
|
+
},
|
|
42
|
+
"type": "module",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"author": "Dmitrii Paskhin <d.pasxin@gmail.com>"
|
|
45
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type Subscriber<Event> = (event: Event) => boolean | void;
|
|
2
|
+
export declare class EventBus<Event = void> {
|
|
3
|
+
private _subscribers;
|
|
4
|
+
/**
|
|
5
|
+
* Synchronously invokes subscribers and passes the event.
|
|
6
|
+
*
|
|
7
|
+
* @param event The event to publish.
|
|
8
|
+
*/
|
|
9
|
+
publish(event: Event): void;
|
|
10
|
+
/**
|
|
11
|
+
* Adds a subscriber to the event bus. Subscriber would receive all events published via {@link EventBus.publish}.
|
|
12
|
+
*
|
|
13
|
+
* @param subscriber The subscriber callback.
|
|
14
|
+
* @returns The callback that unsubscribes the subscriber from the event bus.
|
|
15
|
+
*/
|
|
16
|
+
subscribe(subscriber: Subscriber<Event>): () => void;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class EventBus {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._subscribers = [];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Synchronously invokes subscribers and passes the event.
|
|
7
|
+
*
|
|
8
|
+
* @param event The event to publish.
|
|
9
|
+
*/
|
|
10
|
+
publish(event) {
|
|
11
|
+
for (const subscriber of this._subscribers) {
|
|
12
|
+
subscriber(event);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Adds a subscriber to the event bus. Subscriber would receive all events published via {@link EventBus.publish}.
|
|
17
|
+
*
|
|
18
|
+
* @param subscriber The subscriber callback.
|
|
19
|
+
* @returns The callback that unsubscribes the subscriber from the event bus.
|
|
20
|
+
*/
|
|
21
|
+
subscribe(subscriber) {
|
|
22
|
+
const subscribers = this._subscribers;
|
|
23
|
+
if (subscribers.indexOf(subscriber) === -1) {
|
|
24
|
+
subscribers.push(subscriber);
|
|
25
|
+
}
|
|
26
|
+
return () => {
|
|
27
|
+
subscribers.splice(subscribers.indexOf(subscriber), 1);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
package/shared/index.js
ADDED
package/shared/lang.d.ts
ADDED
package/shared/lang.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Nullable<T> = T | null;
|
|
2
|
+
export type Maybe<T> = Nullable<T> | undefined;
|
|
3
|
+
export type Many<T> = T[] | T;
|
|
4
|
+
export type Dict<T = any> = Record<string, T>;
|
|
5
|
+
export type Primitive = string | number | boolean | bigint | undefined | null;
|
|
6
|
+
export interface VoidFunction {
|
|
7
|
+
(): void;
|
|
8
|
+
}
|
package/shared/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/shared/utils.js
ADDED