@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.
- package/core/createElement.d.ts +22 -15
- package/core/createElement.js +67 -23
- package/core/hostAdapter.d.ts +17 -7
- package/core/hostAdapter.js +4 -1
- package/core/index.d.ts +68 -3
- package/core/index.js +6 -3
- package/core/internal.d.ts +6 -3
- package/core/internal.js +6 -3
- package/core/lifecycleEventBus.d.ts +26 -0
- package/core/lifecycleEventBus.js +2 -0
- package/core/mounting.d.ts +9 -8
- package/core/mounting.js +87 -50
- package/core/patching.d.ts +6 -4
- package/core/patching.js +150 -162
- package/core/portal.d.ts +2 -0
- package/core/portal.js +9 -0
- package/core/ref.d.ts +17 -0
- package/core/ref.js +27 -0
- package/core/rerender.d.ts +11 -0
- package/core/rerender.js +61 -3
- package/core/unmounting.d.ts +2 -4
- package/core/unmounting.js +26 -27
- package/dom/attach-element-to-dom.d.ts +4 -0
- package/dom/attach-element-to-dom.js +9 -0
- package/dom/domAdapter.d.ts +3 -2
- package/dom/domAdapter.js +37 -113
- package/dom/events.d.ts +19 -0
- package/dom/events.js +129 -0
- package/dom/index.d.ts +1733 -1
- package/dom/index.js +2 -0
- package/dom/namespace.d.ts +2 -0
- package/dom/namespace.js +1 -0
- package/dom/props/controlled/index.d.ts +7 -0
- package/dom/props/controlled/index.js +51 -0
- package/dom/props/controlled/input.d.ts +7 -0
- package/dom/props/controlled/input.js +80 -0
- package/dom/props/controlled/select.d.ts +6 -0
- package/dom/props/controlled/select.js +76 -0
- package/dom/props/controlled/textarea.d.ts +6 -0
- package/dom/props/controlled/textarea.js +61 -0
- package/dom/props/dangerInnerHTML.d.ts +7 -0
- package/dom/props/dangerInnerHTML.js +24 -0
- package/dom/props/index.d.ts +1 -0
- package/dom/props/index.js +1 -0
- package/dom/props/props.d.ts +5 -0
- package/dom/props/props.js +197 -0
- package/dom/props/style.d.ts +1 -0
- package/dom/props/style.js +32 -0
- package/dom/render.d.ts +2 -2
- package/dom/render.js +31 -19
- package/hooks/index.d.ts +23 -12
- package/hooks/index.js +123 -29
- package/jsx-runtime/index.d.ts +247 -4
- package/jsx-runtime/index.js +2 -5
- package/package.json +22 -15
- package/shared/index.d.ts +19 -4
- package/shared/index.js +5 -4
- package/shared/lang.d.ts +3 -3
- package/shared/lang.js +3 -3
- package/shared/utils.d.ts +3 -2
- package/shared/utils.js +3 -6
- package/core/global.d.ts +0 -21
- package/core/global.js +0 -5
- package/shared/types.d.ts +0 -8
- package/shared/types.js +0 -1
package/dom/index.js
CHANGED
package/dom/namespace.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const defaultNamespace = 'http://www.w3.org/1999/xhtml';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SimpElement } from '../../../core/internal.js';
|
|
2
|
+
import type { Dict } from '../../../shared';
|
|
3
|
+
export declare function isEventNameIgnored(element: SimpElement, eventName: string): boolean;
|
|
4
|
+
export declare function isFormElementControlled(props: Dict): boolean;
|
|
5
|
+
export declare function addControlledFormElementEventHandlers(element: SimpElement): void;
|
|
6
|
+
export declare function removeControlledFormElementEventHandlers(element: SimpElement): void;
|
|
7
|
+
export declare function syncControlledFormElementPropsWithAttrs(element: SimpElement, props: Dict, mounting?: boolean): void;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { addControlledInputEventHandlers, isEventNameIgnored as isEventNameIgnoredInput, removeControlledInputEventHandlers, syncControlledInputProps, } from './input';
|
|
2
|
+
import { addControlledSelectEventHandlers, isEventNameIgnored as isEventNameIgnoredSelect, removeControlledSelectEventHandlers, syncControlledSelectProps, } from './select';
|
|
3
|
+
import { addControlledTextareaEventHandlers, isEventNameIgnored as isEventNameIgnoredTextarea, removeControlledTextareaEventHandlers, syncControlledTextareaProps, } from './textarea';
|
|
4
|
+
export function isEventNameIgnored(element, eventName) {
|
|
5
|
+
if (element.type === 'input') {
|
|
6
|
+
return isEventNameIgnoredInput(element.props, eventName);
|
|
7
|
+
}
|
|
8
|
+
else if (element.type === 'select') {
|
|
9
|
+
return isEventNameIgnoredSelect(eventName);
|
|
10
|
+
}
|
|
11
|
+
else if (element.type === 'textarea') {
|
|
12
|
+
return isEventNameIgnoredTextarea(eventName);
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
export function isFormElementControlled(props) {
|
|
17
|
+
return props.value != null || props.checked != null;
|
|
18
|
+
}
|
|
19
|
+
export function addControlledFormElementEventHandlers(element) {
|
|
20
|
+
if (element.type === 'input') {
|
|
21
|
+
addControlledInputEventHandlers(element.reference);
|
|
22
|
+
}
|
|
23
|
+
else if (element.type === 'select') {
|
|
24
|
+
addControlledSelectEventHandlers(element.reference);
|
|
25
|
+
}
|
|
26
|
+
else if (element.type === 'textarea') {
|
|
27
|
+
addControlledTextareaEventHandlers(element.reference);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function removeControlledFormElementEventHandlers(element) {
|
|
31
|
+
if (element.type === 'input') {
|
|
32
|
+
removeControlledInputEventHandlers(element.reference);
|
|
33
|
+
}
|
|
34
|
+
else if (element.type === 'select') {
|
|
35
|
+
removeControlledSelectEventHandlers(element.reference);
|
|
36
|
+
}
|
|
37
|
+
else if (element.type === 'textarea') {
|
|
38
|
+
removeControlledTextareaEventHandlers(element.reference);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function syncControlledFormElementPropsWithAttrs(element, props, mounting = false) {
|
|
42
|
+
if (element.type === 'input') {
|
|
43
|
+
syncControlledInputProps(element, props);
|
|
44
|
+
}
|
|
45
|
+
else if (element.type === 'select') {
|
|
46
|
+
syncControlledSelectProps(element, props, mounting);
|
|
47
|
+
}
|
|
48
|
+
else if (element.type === 'textarea') {
|
|
49
|
+
syncControlledTextareaProps(element, props, mounting);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SimpElement } from '../../../core/internal.js';
|
|
2
|
+
import type { Dict } from '../../../shared';
|
|
3
|
+
export declare function isCheckedType(type: string): boolean;
|
|
4
|
+
export declare function isEventNameIgnored(props: Dict, eventName: string): boolean;
|
|
5
|
+
export declare function addControlledInputEventHandlers(dom: HTMLInputElement): void;
|
|
6
|
+
export declare function removeControlledInputEventHandlers(dom: HTMLInputElement): void;
|
|
7
|
+
export declare function syncControlledInputProps(element: SimpElement, props: Dict): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { syncRerenderLocker } from '../../../core/internal.js';
|
|
2
|
+
import { getElementFromDom } from '../../attach-element-to-dom';
|
|
3
|
+
export function isCheckedType(type) {
|
|
4
|
+
return type === 'checkbox' || type === 'radio';
|
|
5
|
+
}
|
|
6
|
+
export function isEventNameIgnored(props, eventName) {
|
|
7
|
+
return isCheckedType(props.type) ? eventName === 'onChange' : eventName === 'onInput';
|
|
8
|
+
}
|
|
9
|
+
function onControlledInputInput(event) {
|
|
10
|
+
let element = getElementFromDom(event.target);
|
|
11
|
+
if (!element || !element.props) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (element.props['onInput']) {
|
|
15
|
+
syncRerenderLocker.lock();
|
|
16
|
+
element.props['onInput'](event);
|
|
17
|
+
syncRerenderLocker.flush();
|
|
18
|
+
element = getElementFromDom(event.target);
|
|
19
|
+
}
|
|
20
|
+
if (element) {
|
|
21
|
+
syncControlledInputProps(element, element.props);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function onControlledInputChange(event) {
|
|
25
|
+
let element = getElementFromDom(event.target);
|
|
26
|
+
if (!element || !element.props) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (element.props['onChange']) {
|
|
30
|
+
syncRerenderLocker.lock();
|
|
31
|
+
element.props['onChange'](event);
|
|
32
|
+
syncRerenderLocker.flush();
|
|
33
|
+
element = getElementFromDom(event.target);
|
|
34
|
+
}
|
|
35
|
+
if (element) {
|
|
36
|
+
syncControlledInputProps(element, element.props);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function addControlledInputEventHandlers(dom) {
|
|
40
|
+
if (isCheckedType(dom.type)) {
|
|
41
|
+
dom.addEventListener('change', onControlledInputChange);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
dom.addEventListener('input', onControlledInputInput);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function removeControlledInputEventHandlers(dom) {
|
|
48
|
+
if (isCheckedType(dom.type)) {
|
|
49
|
+
dom.removeEventListener('change', onControlledInputChange);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
dom.removeEventListener('input', onControlledInputInput);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function syncControlledInputProps(element, props) {
|
|
56
|
+
const { type, value, checked, multiple, defaultValue } = props;
|
|
57
|
+
const dom = element.reference;
|
|
58
|
+
const hasValue = value != null;
|
|
59
|
+
const hasChecked = checked != null;
|
|
60
|
+
if (type != null && type !== dom.type) {
|
|
61
|
+
dom.setAttribute('type', type);
|
|
62
|
+
}
|
|
63
|
+
if (multiple != null && multiple !== dom.multiple) {
|
|
64
|
+
dom.multiple = multiple;
|
|
65
|
+
}
|
|
66
|
+
if (defaultValue != null && !hasValue) {
|
|
67
|
+
dom.defaultValue = defaultValue + '';
|
|
68
|
+
}
|
|
69
|
+
if (isCheckedType(type)) {
|
|
70
|
+
if (hasChecked) {
|
|
71
|
+
dom.checked = checked;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
if (hasValue && dom.value !== value) {
|
|
76
|
+
dom.defaultValue = value;
|
|
77
|
+
dom.value = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SimpElement } from '../../../core/internal.js';
|
|
2
|
+
import type { Dict } from '../../../shared';
|
|
3
|
+
export declare function isEventNameIgnored(eventName: string): boolean;
|
|
4
|
+
export declare function addControlledSelectEventHandlers(dom: HTMLSelectElement): void;
|
|
5
|
+
export declare function removeControlledSelectEventHandlers(dom: HTMLSelectElement): void;
|
|
6
|
+
export declare function syncControlledSelectProps(element: SimpElement, props: Dict, mounting?: boolean): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { syncRerenderLocker } from '../../../core/internal.js';
|
|
2
|
+
import { emptyObject } from '../../../shared';
|
|
3
|
+
import { getElementFromDom } from '../../attach-element-to-dom';
|
|
4
|
+
export function isEventNameIgnored(eventName) {
|
|
5
|
+
return eventName === 'onChange';
|
|
6
|
+
}
|
|
7
|
+
function onControlledInputChange(event) {
|
|
8
|
+
let element = getElementFromDom(event.target);
|
|
9
|
+
if (!element || !element.props) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (element.props['onChange']) {
|
|
13
|
+
syncRerenderLocker.lock();
|
|
14
|
+
element.props['onChange'](event);
|
|
15
|
+
syncRerenderLocker.flush();
|
|
16
|
+
element = getElementFromDom(event.target);
|
|
17
|
+
}
|
|
18
|
+
if (element) {
|
|
19
|
+
syncControlledSelectProps(element, element.props);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function addControlledSelectEventHandlers(dom) {
|
|
23
|
+
dom.addEventListener('change', onControlledInputChange);
|
|
24
|
+
}
|
|
25
|
+
export function removeControlledSelectEventHandlers(dom) {
|
|
26
|
+
dom.removeEventListener('change', onControlledInputChange);
|
|
27
|
+
}
|
|
28
|
+
export function syncControlledSelectProps(element, props, mounting = false) {
|
|
29
|
+
const multiple = Boolean(props.multiple);
|
|
30
|
+
const dom = element.reference;
|
|
31
|
+
if (props.multiple != null && multiple !== dom.multiple) {
|
|
32
|
+
dom.multiple = multiple;
|
|
33
|
+
}
|
|
34
|
+
const index = props.selectedIndex;
|
|
35
|
+
if (index === -1) {
|
|
36
|
+
dom.selectedIndex = -1;
|
|
37
|
+
}
|
|
38
|
+
let value = props.value;
|
|
39
|
+
if (typeof index === 'number' && index > -1 && dom.options[index] != null) {
|
|
40
|
+
value = dom.options[index].value;
|
|
41
|
+
}
|
|
42
|
+
if (mounting && value == null) {
|
|
43
|
+
value = props.defaultValue;
|
|
44
|
+
}
|
|
45
|
+
updateOptions(element, value);
|
|
46
|
+
}
|
|
47
|
+
function updateOptions(element, value) {
|
|
48
|
+
if (element.type === 'option') {
|
|
49
|
+
updateOption(element, value);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const children = element.children;
|
|
53
|
+
if (Array.isArray(children)) {
|
|
54
|
+
for (let i = 0, len = children.length; i < len; ++i) {
|
|
55
|
+
updateOptions(children[i], value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (children) {
|
|
59
|
+
updateOptions(children, value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function updateOption(element, value) {
|
|
63
|
+
const props = element.props || emptyObject;
|
|
64
|
+
const propsValue = props.value;
|
|
65
|
+
const dom = element.reference;
|
|
66
|
+
if (!dom) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
dom.value = propsValue;
|
|
70
|
+
if (propsValue === value || (Array.isArray(value) && value.includes(propsValue))) {
|
|
71
|
+
dom.selected = true;
|
|
72
|
+
}
|
|
73
|
+
else if (value != null || props.selected != null) {
|
|
74
|
+
dom.selected = Boolean(props.selected);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SimpElement } from '../../../core/internal.js';
|
|
2
|
+
import type { Dict } from '../../../shared';
|
|
3
|
+
export declare function isEventNameIgnored(eventName: string): boolean;
|
|
4
|
+
export declare function addControlledTextareaEventHandlers(dom: HTMLTextAreaElement): void;
|
|
5
|
+
export declare function removeControlledTextareaEventHandlers(dom: HTMLTextAreaElement): void;
|
|
6
|
+
export declare function syncControlledTextareaProps(element: SimpElement, props: Dict, mounting?: boolean): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { syncRerenderLocker } from '../../../core/internal.js';
|
|
2
|
+
import { getElementFromDom } from '../../attach-element-to-dom';
|
|
3
|
+
export function isEventNameIgnored(eventName) {
|
|
4
|
+
return eventName === 'onChange' || eventName === 'onInput';
|
|
5
|
+
}
|
|
6
|
+
function onControlledTextareaChange(event) {
|
|
7
|
+
let element = getElementFromDom(event.target);
|
|
8
|
+
if (!element || !element.props) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (element.props['onChange']) {
|
|
12
|
+
syncRerenderLocker.lock();
|
|
13
|
+
element.props['onChange'](event);
|
|
14
|
+
syncRerenderLocker.flush();
|
|
15
|
+
element = getElementFromDom(event.target);
|
|
16
|
+
}
|
|
17
|
+
if (element) {
|
|
18
|
+
syncControlledTextareaProps(element, element.props);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function onControlledTextareaInput(event) {
|
|
22
|
+
let element = getElementFromDom(event.target);
|
|
23
|
+
if (!element || !element.props) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (element.props['onInput']) {
|
|
27
|
+
syncRerenderLocker.lock();
|
|
28
|
+
element.props['onInput'](event);
|
|
29
|
+
syncRerenderLocker.flush();
|
|
30
|
+
element = getElementFromDom(event.target);
|
|
31
|
+
}
|
|
32
|
+
if (element) {
|
|
33
|
+
syncControlledTextareaProps(element, element.props);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function addControlledTextareaEventHandlers(dom) {
|
|
37
|
+
dom.addEventListener('input', onControlledTextareaInput);
|
|
38
|
+
dom.addEventListener('change', onControlledTextareaChange);
|
|
39
|
+
}
|
|
40
|
+
export function removeControlledTextareaEventHandlers(dom) {
|
|
41
|
+
dom.removeEventListener('input', onControlledTextareaInput);
|
|
42
|
+
dom.removeEventListener('change', onControlledTextareaChange);
|
|
43
|
+
}
|
|
44
|
+
export function syncControlledTextareaProps(element, props, mounting = false) {
|
|
45
|
+
const dom = element.reference;
|
|
46
|
+
const value = props.value;
|
|
47
|
+
const domValue = dom.value;
|
|
48
|
+
if (value == null) {
|
|
49
|
+
if (mounting) {
|
|
50
|
+
const defaultValue = props.defaultValue;
|
|
51
|
+
if (defaultValue != null && defaultValue !== domValue) {
|
|
52
|
+
dom.defaultValue = defaultValue;
|
|
53
|
+
dom.value = defaultValue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (domValue !== value) {
|
|
58
|
+
dom.defaultValue = value;
|
|
59
|
+
dom.value = value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Maybe } from '../../shared';
|
|
2
|
+
import type { SimpElement } from '../../core/internal.js';
|
|
3
|
+
export declare function patchDangerInnerHTML(prevValue: Maybe<{
|
|
4
|
+
__html: string;
|
|
5
|
+
}>, nextValue: Maybe<{
|
|
6
|
+
__html: string;
|
|
7
|
+
}>, prevElement: Maybe<SimpElement>, nextElement: SimpElement, dom: Element): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { unmount } from '../../core/internal.js';
|
|
2
|
+
export function patchDangerInnerHTML(prevValue, nextValue, prevElement, nextElement, dom) {
|
|
3
|
+
const prevHTML = prevValue?.__html || '';
|
|
4
|
+
const nextHTML = nextValue?.__html || '';
|
|
5
|
+
if (nextElement.children) {
|
|
6
|
+
console.warn('Avoid setting both children and props.dangerouslySetInnerHTML at the same time — this can cause unpredictable behavior.');
|
|
7
|
+
}
|
|
8
|
+
if (prevHTML !== nextHTML) {
|
|
9
|
+
if (nextHTML != null && !isSameInnerHTML(dom, nextHTML)) {
|
|
10
|
+
if (prevElement != null) {
|
|
11
|
+
if (prevElement.children) {
|
|
12
|
+
unmount(prevElement.children);
|
|
13
|
+
prevElement.children = undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
dom.innerHTML = nextHTML;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function isSameInnerHTML(dom, innerHTML) {
|
|
21
|
+
const temp = document.createElement('i');
|
|
22
|
+
temp.innerHTML = innerHTML;
|
|
23
|
+
return temp.innerHTML === dom.innerHTML;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './props';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './props';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SimpElement } from '../../core/internal.js';
|
|
2
|
+
import type { Namespace } from '../namespace';
|
|
3
|
+
export declare function mountProps(dom: HTMLElement | SVGElement, element: SimpElement, namespace: Namespace): void;
|
|
4
|
+
export declare function unmountProps(dom: HTMLElement | SVGElement, element: SimpElement): void;
|
|
5
|
+
export declare function patchProps(dom: HTMLElement | SVGElement, prevElement: SimpElement, nextElement: SimpElement, namespace: Namespace): void;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { emptyObject } from '../../shared';
|
|
2
|
+
import { defaultNamespace } from '../namespace';
|
|
3
|
+
import { addControlledFormElementEventHandlers, isEventNameIgnored, isFormElementControlled, removeControlledFormElementEventHandlers, syncControlledFormElementPropsWithAttrs, } from './controlled';
|
|
4
|
+
import { isPropNameEventName, patchEvent } from '../events';
|
|
5
|
+
import { patchStyle } from './style';
|
|
6
|
+
import { patchDangerInnerHTML } from './dangerInnerHTML';
|
|
7
|
+
export function mountProps(dom, element, namespace) {
|
|
8
|
+
if (!isFormElement(element)) {
|
|
9
|
+
for (const propName in element.props) {
|
|
10
|
+
patchDefaultElementPropAndAttrs(propName, dom, null, element, null, element.props[propName], namespace);
|
|
11
|
+
}
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const isControlled = isFormElementControlled(element.props);
|
|
15
|
+
for (const propName in element.props) {
|
|
16
|
+
patchFormElementsPropAndAttrs(propName, dom, element, isControlled, null, element.props[propName]);
|
|
17
|
+
}
|
|
18
|
+
if (isControlled) {
|
|
19
|
+
addControlledFormElementEventHandlers(element);
|
|
20
|
+
syncControlledFormElementPropsWithAttrs(element, element.props, true);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function unmountProps(dom, element) {
|
|
24
|
+
if (!element.props) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (const propName in element.props) {
|
|
28
|
+
if (isPropNameEventName(propName)) {
|
|
29
|
+
patchEvent(propName, element.props[propName], null, dom);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function patchProps(dom, prevElement, nextElement, namespace) {
|
|
34
|
+
const prevProps = prevElement.props || emptyObject;
|
|
35
|
+
const nextProps = nextElement.props || emptyObject;
|
|
36
|
+
if (!isFormElement(nextElement)) {
|
|
37
|
+
for (const propName in nextProps) {
|
|
38
|
+
const prevValue = prevProps[propName];
|
|
39
|
+
const nextValue = nextProps[propName];
|
|
40
|
+
if (prevValue !== nextValue) {
|
|
41
|
+
patchDefaultElementPropAndAttrs(propName, dom, prevElement, nextElement, prevValue, nextValue, namespace);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const propName in prevProps) {
|
|
45
|
+
if (nextProps[propName] == null && prevProps[propName] != null) {
|
|
46
|
+
patchDefaultElementPropAndAttrs(propName, dom, prevElement, nextElement, prevProps[propName], null, namespace);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const isPrevElementControlled = isFormElementControlled(prevProps);
|
|
52
|
+
const isNextElementControlled = isFormElementControlled(nextProps);
|
|
53
|
+
for (const propName in nextProps) {
|
|
54
|
+
const prevValue = prevProps[propName];
|
|
55
|
+
const nextValue = nextProps[propName];
|
|
56
|
+
if (prevValue !== nextValue) {
|
|
57
|
+
patchFormElementsPropAndAttrs(propName, dom, nextElement, isNextElementControlled, prevValue, nextValue);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const propName in prevProps) {
|
|
61
|
+
if (nextProps[propName] == null && prevProps[propName] != null) {
|
|
62
|
+
patchFormElementsPropAndAttrs(propName, dom, prevElement, isPrevElementControlled, prevProps[propName], null);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!isPrevElementControlled && isNextElementControlled) {
|
|
66
|
+
addControlledFormElementEventHandlers(nextElement);
|
|
67
|
+
}
|
|
68
|
+
else if (isPrevElementControlled && !isNextElementControlled) {
|
|
69
|
+
removeControlledFormElementEventHandlers(prevElement);
|
|
70
|
+
}
|
|
71
|
+
if (isNextElementControlled) {
|
|
72
|
+
syncControlledFormElementPropsWithAttrs(nextElement, nextProps);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function patchFormElementsPropAndAttrs(propName, dom, element, isControlled, prevValue, nextValue) {
|
|
76
|
+
switch (propName) {
|
|
77
|
+
case 'children':
|
|
78
|
+
case 'className':
|
|
79
|
+
case 'key':
|
|
80
|
+
case 'ref':
|
|
81
|
+
break;
|
|
82
|
+
case 'value':
|
|
83
|
+
if (!isControlled) {
|
|
84
|
+
patchDomProp(nextValue, dom, propName);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case 'defaultValue':
|
|
88
|
+
case 'selectedIndex':
|
|
89
|
+
if (!isControlled) {
|
|
90
|
+
patchDomProp(nextValue, dom, propName);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
case 'multiple':
|
|
94
|
+
case 'defaultChecked':
|
|
95
|
+
case 'checked':
|
|
96
|
+
if (!isControlled) {
|
|
97
|
+
dom[propName] = !!nextValue;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
case 'capture':
|
|
101
|
+
case 'indeterminate':
|
|
102
|
+
case 'readOnly':
|
|
103
|
+
case 'required':
|
|
104
|
+
dom[propName] = !!nextValue;
|
|
105
|
+
break;
|
|
106
|
+
case 'style':
|
|
107
|
+
patchStyle(prevValue, nextValue, dom);
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
if (isPropNameEventName(propName)) {
|
|
111
|
+
if (isControlled && isEventNameIgnored(element, propName)) {
|
|
112
|
+
patchEvent(propName, prevValue, null, dom);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
patchEvent(propName, prevValue, nextValue, dom);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
patchDomAttr(dom, nextValue, propName);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function patchDefaultElementPropAndAttrs(propName, dom, prevElement, nextElement, prevValue, nextValue, namespace) {
|
|
123
|
+
switch (propName) {
|
|
124
|
+
case 'children':
|
|
125
|
+
case 'className':
|
|
126
|
+
case 'key':
|
|
127
|
+
case 'ref':
|
|
128
|
+
break;
|
|
129
|
+
case 'autoFocus':
|
|
130
|
+
dom.autofocus = !!nextValue;
|
|
131
|
+
break;
|
|
132
|
+
case 'allowfullscreen':
|
|
133
|
+
case 'autoplay':
|
|
134
|
+
case 'controls':
|
|
135
|
+
case 'default':
|
|
136
|
+
case 'disabled':
|
|
137
|
+
case 'hidden':
|
|
138
|
+
case 'loop':
|
|
139
|
+
case 'muted':
|
|
140
|
+
case 'novalidate':
|
|
141
|
+
case 'open':
|
|
142
|
+
case 'reversed':
|
|
143
|
+
case 'selected':
|
|
144
|
+
dom[propName] = !!nextValue;
|
|
145
|
+
break;
|
|
146
|
+
case 'volume':
|
|
147
|
+
patchDomProp(nextValue, dom, propName);
|
|
148
|
+
break;
|
|
149
|
+
case 'style':
|
|
150
|
+
patchStyle(prevValue, nextValue, dom);
|
|
151
|
+
break;
|
|
152
|
+
case 'dangerouslySetInnerHTML':
|
|
153
|
+
patchDangerInnerHTML(prevValue, nextValue, prevElement, nextElement, dom);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
if (isPropNameEventName(propName)) {
|
|
157
|
+
patchEvent(propName, prevValue, nextValue, dom);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
patchDomAttr(dom, nextValue, propName, namespace);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function patchDomProp(nextValue, dom, propKey) {
|
|
165
|
+
const value = nextValue == null ? '' : nextValue;
|
|
166
|
+
if (dom[propKey] !== value) {
|
|
167
|
+
dom[propKey] = value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function patchDomAttr(dom, value, propName, namespace = defaultNamespace) {
|
|
171
|
+
if (value == null) {
|
|
172
|
+
dom.removeAttribute(propName);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
if (namespace === 'http://www.w3.org/2000/svg' && svgAttrsNamespaces[propName]) {
|
|
176
|
+
dom.setAttributeNS(svgAttrsNamespaces[propName], propName, value);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
dom.setAttribute(propName, value);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const svgAttrsNamespaces = {
|
|
184
|
+
'xlink:actuate': 'http://www.w3.org/1999/xlink',
|
|
185
|
+
'xlink:arcrole': 'http://www.w3.org/1999/xlink',
|
|
186
|
+
'xlink:href': 'http://www.w3.org/1999/xlink',
|
|
187
|
+
'xlink:role': 'http://www.w3.org/1999/xlink',
|
|
188
|
+
'xlink:show': 'http://www.w3.org/1999/xlink',
|
|
189
|
+
'xlink:title': 'http://www.w3.org/1999/xlink',
|
|
190
|
+
'xlink:type': 'http://www.w3.org/1999/xlink',
|
|
191
|
+
'xml:base': 'http://www.w3.org/XML/1998/namespace',
|
|
192
|
+
'xml:lang': 'http://www.w3.org/XML/1998/namespace',
|
|
193
|
+
'xml:space': 'http://www.w3.org/XML/1998/namespace',
|
|
194
|
+
};
|
|
195
|
+
function isFormElement(element) {
|
|
196
|
+
return element.type === 'input' || element.type === 'textarea' || element.type === 'select';
|
|
197
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function patchStyle(prevAttrValue: any, nextAttrValue: any, dom: HTMLElement | SVGElement): void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function patchStyle(prevAttrValue, nextAttrValue, dom) {
|
|
2
|
+
if (nextAttrValue == null) {
|
|
3
|
+
dom.removeAttribute('style');
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const domStyle = dom.style;
|
|
7
|
+
let style;
|
|
8
|
+
let value;
|
|
9
|
+
if (typeof nextAttrValue === 'string') {
|
|
10
|
+
domStyle.cssText = nextAttrValue;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (prevAttrValue != null && typeof prevAttrValue !== 'string') {
|
|
14
|
+
for (style in nextAttrValue) {
|
|
15
|
+
value = nextAttrValue[style];
|
|
16
|
+
if (value !== prevAttrValue[style]) {
|
|
17
|
+
domStyle.setProperty(style, value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (style in prevAttrValue) {
|
|
21
|
+
if (nextAttrValue[style] == null) {
|
|
22
|
+
domStyle.removeProperty(style);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
for (style in nextAttrValue) {
|
|
28
|
+
value = nextAttrValue[style];
|
|
29
|
+
domStyle.setProperty(style, value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dom/render.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type SimpElement } from '../core/internal';
|
|
1
|
+
import { type SimpElement } from '../core/internal.js';
|
|
2
2
|
import type { Nullable } from '../shared';
|
|
3
|
-
export declare function render(element: SimpElement
|
|
3
|
+
export declare function render(element: Nullable<SimpElement>, container: Nullable<Element | DocumentFragment>): void;
|
|
4
4
|
interface SimpRoot {
|
|
5
5
|
render(element: SimpElement): void;
|
|
6
6
|
unmount(): void;
|
package/dom/render.js
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { asyncRerenderLocker, hostAdapter, mount, patch, provideHostAdapter, remove, } from '../core/internal.js';
|
|
2
2
|
import { domAdapter } from './domAdapter';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { attachElementToDom, getElementFromDom } from './attach-element-to-dom';
|
|
4
|
+
provideHostAdapter(domAdapter);
|
|
5
|
+
export function render(element, container) {
|
|
6
|
+
if (!container) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const currentRootElement = getElementFromDom(container);
|
|
10
|
+
asyncRerenderLocker.lock();
|
|
11
|
+
if (!currentRootElement) {
|
|
12
|
+
if (element) {
|
|
13
|
+
hostAdapter.clearNode(container);
|
|
14
|
+
attachElementToDom((element.parent = { flag: 'HOST', reference: container, children: element, parent: null }), container);
|
|
15
|
+
mount(element, container, null, null, hostAdapter.getHostNamespaces(element, undefined)?.self);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
if (!element) {
|
|
20
|
+
remove(currentRootElement.children, container);
|
|
21
|
+
currentRootElement.children = null;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
const prevChildren = currentRootElement.children;
|
|
25
|
+
currentRootElement.children = element;
|
|
26
|
+
element.parent = currentRootElement;
|
|
27
|
+
patch(prevChildren, element, container, null, null, hostAdapter.getHostNamespaces(element, undefined)?.self);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
asyncRerenderLocker.flush();
|
|
6
31
|
}
|
|
7
32
|
export function createRoot(container) {
|
|
8
|
-
let currentRoot = container.__SIMP_ROOT__;
|
|
9
33
|
return {
|
|
10
34
|
render(element) {
|
|
11
|
-
|
|
12
|
-
patch(currentRoot, element, container, null, null);
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
GLOBAL.hostAdapter.clearNode(container);
|
|
16
|
-
mount(element, container, null, null);
|
|
17
|
-
}
|
|
18
|
-
currentRoot = element;
|
|
19
|
-
container.__SIMP_ROOT__ = currentRoot;
|
|
35
|
+
render(element, container);
|
|
20
36
|
},
|
|
21
37
|
unmount() {
|
|
22
|
-
|
|
23
|
-
// enqueueRender(currentRoot, null);
|
|
24
|
-
// currentRoot = null;
|
|
25
|
-
// }
|
|
26
|
-
currentRoot = null;
|
|
38
|
+
render(null, container);
|
|
27
39
|
},
|
|
28
40
|
};
|
|
29
41
|
}
|