onejs-core 0.3.5
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/.gitattributes +2 -0
- package/.github/workflows/jsr.yml +19 -0
- package/.prettierrc +5 -0
- package/3rdparty/preact/LICENSE +21 -0
- package/3rdparty/preact/clone-element.ts +45 -0
- package/3rdparty/preact/compat/Children.ts +21 -0
- package/3rdparty/preact/compat/forwardRef.ts +49 -0
- package/3rdparty/preact/compat/index.ts +3 -0
- package/3rdparty/preact/compat/memo.ts +34 -0
- package/3rdparty/preact/compat/util.ts +38 -0
- package/3rdparty/preact/component.ts +235 -0
- package/3rdparty/preact/constants.ts +3 -0
- package/3rdparty/preact/create-context.ts +71 -0
- package/3rdparty/preact/create-element.ts +98 -0
- package/3rdparty/preact/diff/catch-error.ts +40 -0
- package/3rdparty/preact/diff/children.ts +355 -0
- package/3rdparty/preact/diff/index.ts +563 -0
- package/3rdparty/preact/diff/props.ts +174 -0
- package/3rdparty/preact/hooks/index.ts +536 -0
- package/3rdparty/preact/hooks/internal.d.ts +85 -0
- package/3rdparty/preact/hooks.d.ts +145 -0
- package/3rdparty/preact/index.ts +13 -0
- package/3rdparty/preact/internal.d.ts +155 -0
- package/3rdparty/preact/jsx-runtime/index.ts +80 -0
- package/3rdparty/preact/jsx.d.ts +1008 -0
- package/3rdparty/preact/options.ts +16 -0
- package/3rdparty/preact/preact.d.ts +317 -0
- package/3rdparty/preact/render.ts +76 -0
- package/3rdparty/preact/signals/index.ts +443 -0
- package/3rdparty/preact/signals/internal.d.ts +36 -0
- package/3rdparty/preact/signals-core/index.ts +663 -0
- package/3rdparty/preact/style.d.ts +205 -0
- package/3rdparty/preact/util.ts +29 -0
- package/@DO_NOT_CHANGE.txt +3 -0
- package/README.md +33 -0
- package/definitions/app.d.ts +52048 -0
- package/definitions/augments.d.ts +16 -0
- package/definitions/globals.d.ts +34 -0
- package/definitions/index.d.ts +9 -0
- package/definitions/jsx.d.ts +517 -0
- package/definitions/modules.d.ts +29 -0
- package/definitions/onejs.d.ts +164 -0
- package/definitions/preact.jsx.d.ts +7 -0
- package/definitions/proto-overrides.d.ts +13 -0
- package/definitions/puerts.d.ts +31 -0
- package/definitions/unity-engine.d.ts +23 -0
- package/hooks/eventful.ts +56 -0
- package/import-transform.mjs +42 -0
- package/index.ts +44 -0
- package/jsr.json +10 -0
- package/onejs-tw-config.cjs +188 -0
- package/package.json +9 -0
- package/preloads/inject.ts +44 -0
- package/styling/index.tsx +80 -0
- package/styling/utils/generateAlphabeticName.ts +21 -0
- package/styling/utils/generateComponentId.ts +6 -0
- package/styling/utils/hash.ts +46 -0
- package/switch.cjs +185 -0
- package/uss-transform-plugin.cjs +83 -0
- package/utils/color-palettes.ts +3 -0
- package/utils/color-parser.ts +249 -0
- package/utils/float-parser.ts +31 -0
- package/utils/index.ts +12 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { IS_NON_DIMENSIONAL } from '../constants'
|
|
2
|
+
import options from '../options'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Diff the old and new properties of a VNode and apply changes to the DOM node
|
|
6
|
+
* @param {import('../internal').PreactElement} dom The DOM node to apply
|
|
7
|
+
* changes to
|
|
8
|
+
* @param {object} newProps The new props
|
|
9
|
+
* @param {object} oldProps The old props
|
|
10
|
+
* @param {boolean} isSvg Whether or not this node is an SVG node
|
|
11
|
+
* @param {boolean} hydrate Whether or not we are in hydration mode
|
|
12
|
+
*/
|
|
13
|
+
export function diffProps(dom, newProps, oldProps, isSvg, hydrate) {
|
|
14
|
+
let i;
|
|
15
|
+
|
|
16
|
+
for (i in oldProps) {
|
|
17
|
+
if (i !== 'children' && i !== 'key' && !(i in newProps)) {
|
|
18
|
+
setProperty(dom, i, null, oldProps[i], isSvg);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (i in newProps) {
|
|
23
|
+
if (
|
|
24
|
+
(!hydrate || typeof newProps[i] == 'function') &&
|
|
25
|
+
i !== 'children' &&
|
|
26
|
+
i !== 'key' &&
|
|
27
|
+
i !== 'value' &&
|
|
28
|
+
i !== 'checked' &&
|
|
29
|
+
oldProps[i] !== newProps[i]
|
|
30
|
+
) {
|
|
31
|
+
setProperty(dom, i, newProps[i], oldProps[i], isSvg);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function setStyle(style, key, value) {
|
|
37
|
+
if (key[0] === '-') {
|
|
38
|
+
style.setProperty(key, value);
|
|
39
|
+
} else if (value == null) {
|
|
40
|
+
style[key] = null;
|
|
41
|
+
} else if (typeof value != 'number' || IS_NON_DIMENSIONAL.test(key)) {
|
|
42
|
+
style[key] = value;
|
|
43
|
+
} else {
|
|
44
|
+
style[key] = value + 'px';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// styleProcessors[key](style, value)
|
|
48
|
+
// globalThis.__setStyleProperty(style.GetVEStyle(), key, value)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set a property value on a DOM node
|
|
53
|
+
* @param {import('../internal').PreactElement} dom The DOM node to modify
|
|
54
|
+
* @param {string} name The name of the property to set
|
|
55
|
+
* @param {*} value The value to set the property to
|
|
56
|
+
* @param {*} oldValue The old value the property had
|
|
57
|
+
* @param {boolean} isSvg Whether or not this DOM node is an SVG node or not
|
|
58
|
+
*/
|
|
59
|
+
export function setProperty(dom, name, value, oldValue, isSvg) {
|
|
60
|
+
let useCapture;
|
|
61
|
+
|
|
62
|
+
o: if (name === 'style') {
|
|
63
|
+
if (typeof value == 'string') {
|
|
64
|
+
dom.style.cssText = value;
|
|
65
|
+
} else {
|
|
66
|
+
if (typeof oldValue == 'string') {
|
|
67
|
+
dom.style.cssText = oldValue = '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (oldValue) {
|
|
71
|
+
for (name in oldValue) {
|
|
72
|
+
if (!(value && name in value)) {
|
|
73
|
+
setStyle(dom.style, name, '');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (value) {
|
|
79
|
+
for (name in value) {
|
|
80
|
+
if (!oldValue || value[name] !== oldValue[name]) {
|
|
81
|
+
setStyle(dom.style, name, value[name]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6
|
|
88
|
+
else if (name[0] === 'o' && name[1] === 'n') {
|
|
89
|
+
useCapture = name !== (name = name.replace(/Capture$/, ''));
|
|
90
|
+
|
|
91
|
+
// Infer correct casing for DOM built-in events:
|
|
92
|
+
if (name.toLowerCase() in dom) name = name.toLowerCase().slice(2);
|
|
93
|
+
else name = name.slice(2);
|
|
94
|
+
|
|
95
|
+
if (!dom._listeners) dom._listeners = {};
|
|
96
|
+
// dom._listeners[name + useCapture] = value;
|
|
97
|
+
dom._addToListeners(name + useCapture, value)
|
|
98
|
+
|
|
99
|
+
if (value) {
|
|
100
|
+
if (!oldValue) {
|
|
101
|
+
const handler = useCapture ? eventProxyCapture : eventProxy;
|
|
102
|
+
dom.addEventListener(name, handler.bind(dom), useCapture);
|
|
103
|
+
// dom.addEventListener(name, value, useCapture);
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
const handler = useCapture ? eventProxyCapture : eventProxy;
|
|
107
|
+
dom.removeEventListener(name, handler.bind(dom), useCapture);
|
|
108
|
+
// dom.removeEventListener(name, value, useCapture);
|
|
109
|
+
}
|
|
110
|
+
} else if (name !== 'dangerouslySetInnerHTML') {
|
|
111
|
+
if (isSvg) {
|
|
112
|
+
// Normalize incorrect prop usage for SVG:
|
|
113
|
+
// - xlink:href / xlinkHref --> href (xlink:href was removed from SVG and isn't needed)
|
|
114
|
+
// - className --> class
|
|
115
|
+
name = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');
|
|
116
|
+
} else if (
|
|
117
|
+
name !== 'width' &&
|
|
118
|
+
name !== 'height' &&
|
|
119
|
+
name !== 'href' &&
|
|
120
|
+
name !== 'list' &&
|
|
121
|
+
name !== 'form' &&
|
|
122
|
+
// Default value in browsers is `-1` and an empty string is
|
|
123
|
+
// cast to `0` instead
|
|
124
|
+
name !== 'tabIndex' &&
|
|
125
|
+
name !== 'download' &&
|
|
126
|
+
name !== 'rowSpan' &&
|
|
127
|
+
name !== 'colSpan' &&
|
|
128
|
+
name in dom
|
|
129
|
+
) {
|
|
130
|
+
try {
|
|
131
|
+
// dom[name] = value == null ? '' : value;
|
|
132
|
+
dom.setAttribute(name, value == null ? null : value); // MODDED
|
|
133
|
+
// labelled break is 1b smaller here than a return statement (sorry)
|
|
134
|
+
break o;
|
|
135
|
+
} catch (e) { }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ARIA-attributes have a different notion of boolean values.
|
|
139
|
+
// The value `false` is different from the attribute not
|
|
140
|
+
// existing on the DOM, so we can't remove it. For non-boolean
|
|
141
|
+
// ARIA-attributes we could treat false as a removal, but the
|
|
142
|
+
// amount of exceptions would cost us too many bytes. On top of
|
|
143
|
+
// that other VDOM frameworks also always stringify `false`.
|
|
144
|
+
|
|
145
|
+
if (name == "disabled") { // MODDED
|
|
146
|
+
dom.setAttribute(name, value);
|
|
147
|
+
} else if (value != null && (value !== false || name[4] === '-')) {
|
|
148
|
+
dom.setAttribute(name, value);
|
|
149
|
+
} else {
|
|
150
|
+
dom.removeAttribute(name);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Proxy an event to hooked event handlers
|
|
157
|
+
* @param {Event} e The event object from the browser
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
function eventProxy(e) {
|
|
161
|
+
let eventName = e.GetType().Name.replace("Event", "")
|
|
162
|
+
eventName = eventName === "Change`1" ? "ValueChanged" : eventName
|
|
163
|
+
// this.CallListener(eventName + false, options.event ? options.event(e) : e)
|
|
164
|
+
// this._listeners[eventName + false](options.event ? options.event(e) : e);
|
|
165
|
+
this._callListener(eventName + false, options.event ? options.event(e) : e);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function eventProxyCapture(e) {
|
|
169
|
+
let eventName = e.GetType().Name.replace("Event", "")
|
|
170
|
+
eventName = eventName === "Change`1" ? "ValueChanged" : eventName
|
|
171
|
+
// this.CallListener(eventName + true, options.event ? options.event(e) : e)
|
|
172
|
+
// this._listeners[eventName + true](options.event ? options.event(e) : e);
|
|
173
|
+
this._callListener(eventName + true, options.event ? options.event(e) : e);
|
|
174
|
+
}
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import { Fragment, options } from 'preact/';
|
|
2
|
+
|
|
3
|
+
/** @type {number} */
|
|
4
|
+
let currentIndex;
|
|
5
|
+
|
|
6
|
+
/** @type {import('./internal').Component} */
|
|
7
|
+
let currentComponent;
|
|
8
|
+
|
|
9
|
+
/** @type {import('./internal').Component} */
|
|
10
|
+
let previousComponent;
|
|
11
|
+
|
|
12
|
+
/** @type {number} */
|
|
13
|
+
let currentHook = 0;
|
|
14
|
+
|
|
15
|
+
/** @type {Array<import('./internal').Component>} */
|
|
16
|
+
let afterPaintEffects: any[] = [];
|
|
17
|
+
|
|
18
|
+
let EMPTY = [];
|
|
19
|
+
|
|
20
|
+
let oldBeforeDiff = options._diff;
|
|
21
|
+
let oldBeforeRender = options._render;
|
|
22
|
+
let oldAfterDiff = options.diffed;
|
|
23
|
+
let oldCommit = options._commit;
|
|
24
|
+
let oldBeforeUnmount = options.unmount;
|
|
25
|
+
|
|
26
|
+
const RAF_TIMEOUT = 100;
|
|
27
|
+
let prevRaf;
|
|
28
|
+
|
|
29
|
+
options._diff = vnode => {
|
|
30
|
+
// if (
|
|
31
|
+
// typeof vnode.type === 'function' &&
|
|
32
|
+
// !vnode._mask &&
|
|
33
|
+
// vnode.type !== Fragment
|
|
34
|
+
// ) {
|
|
35
|
+
// vnode._mask =
|
|
36
|
+
// (vnode._parent && vnode._parent._mask ? vnode._parent._mask : '') +
|
|
37
|
+
// (vnode._parent && vnode._parent._children
|
|
38
|
+
// ? vnode._parent._children.indexOf(vnode)
|
|
39
|
+
// : 0);
|
|
40
|
+
// } else if (!vnode._mask) {
|
|
41
|
+
// vnode._mask =
|
|
42
|
+
// vnode._parent && vnode._parent._mask ? vnode._parent._mask : '';
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
currentComponent = null;
|
|
46
|
+
if (oldBeforeDiff) oldBeforeDiff(vnode);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
options._render = vnode => {
|
|
50
|
+
if (oldBeforeRender) oldBeforeRender(vnode);
|
|
51
|
+
|
|
52
|
+
currentComponent = vnode._component;
|
|
53
|
+
currentIndex = 0;
|
|
54
|
+
|
|
55
|
+
const hooks = currentComponent.__hooks;
|
|
56
|
+
if (hooks) {
|
|
57
|
+
if (previousComponent === currentComponent) {
|
|
58
|
+
hooks._pendingEffects = [];
|
|
59
|
+
currentComponent._renderCallbacks = [];
|
|
60
|
+
hooks._list.forEach(hookItem => {
|
|
61
|
+
if (hookItem._nextValue) {
|
|
62
|
+
hookItem._value = hookItem._nextValue;
|
|
63
|
+
}
|
|
64
|
+
hookItem._pendingValue = EMPTY;
|
|
65
|
+
hookItem._nextValue = hookItem._pendingArgs = undefined;
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
hooks._pendingEffects.forEach(invokeCleanup);
|
|
69
|
+
hooks._pendingEffects.forEach(invokeEffect);
|
|
70
|
+
hooks._pendingEffects = [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
previousComponent = currentComponent;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
options.diffed = vnode => {
|
|
77
|
+
if (oldAfterDiff) oldAfterDiff(vnode);
|
|
78
|
+
|
|
79
|
+
const c = vnode._component;
|
|
80
|
+
if (c && c.__hooks) {
|
|
81
|
+
if (c.__hooks._pendingEffects.length) afterPaint(afterPaintEffects.push(c));
|
|
82
|
+
c.__hooks._list.forEach(hookItem => {
|
|
83
|
+
if (hookItem._pendingArgs) {
|
|
84
|
+
hookItem._args = hookItem._pendingArgs;
|
|
85
|
+
}
|
|
86
|
+
if (hookItem._pendingValue !== EMPTY) {
|
|
87
|
+
hookItem._value = hookItem._pendingValue;
|
|
88
|
+
}
|
|
89
|
+
hookItem._pendingArgs = undefined;
|
|
90
|
+
hookItem._pendingValue = EMPTY;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
previousComponent = currentComponent = null;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
options._commit = (vnode, commitQueue) => {
|
|
97
|
+
commitQueue.some(component => {
|
|
98
|
+
try {
|
|
99
|
+
component._renderCallbacks.forEach(invokeCleanup);
|
|
100
|
+
component._renderCallbacks = component._renderCallbacks.filter(cb =>
|
|
101
|
+
cb._value ? invokeEffect(cb) : true
|
|
102
|
+
);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
commitQueue.some(c => {
|
|
105
|
+
if (c._renderCallbacks) c._renderCallbacks = [];
|
|
106
|
+
});
|
|
107
|
+
commitQueue = [];
|
|
108
|
+
options._catchError(e, component._vnode);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (oldCommit) oldCommit(vnode, commitQueue);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
options.unmount = vnode => {
|
|
116
|
+
if (oldBeforeUnmount) oldBeforeUnmount(vnode);
|
|
117
|
+
|
|
118
|
+
const c = vnode._component;
|
|
119
|
+
if (c && c.__hooks) {
|
|
120
|
+
let hasErrored;
|
|
121
|
+
c.__hooks._list.forEach(s => {
|
|
122
|
+
try {
|
|
123
|
+
invokeCleanup(s);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
hasErrored = e;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
c.__hooks = undefined;
|
|
129
|
+
if (hasErrored) options._catchError(hasErrored, c._vnode);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get a hook's state from the currentComponent
|
|
135
|
+
* @param {number} index The index of the hook to get
|
|
136
|
+
* @param {number} type The index of the hook to get
|
|
137
|
+
* @returns {any}
|
|
138
|
+
*/
|
|
139
|
+
function getHookState(index, type) {
|
|
140
|
+
if (options._hook) {
|
|
141
|
+
options._hook(currentComponent, index, currentHook || type);
|
|
142
|
+
}
|
|
143
|
+
currentHook = 0;
|
|
144
|
+
|
|
145
|
+
// Largely inspired by:
|
|
146
|
+
// * https://github.com/michael-klein/funcy.js/blob/f6be73468e6ec46b0ff5aa3cc4c9baf72a29025a/src/hooks/core_hooks.mjs
|
|
147
|
+
// * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs
|
|
148
|
+
// Other implementations to look at:
|
|
149
|
+
// * https://codesandbox.io/s/mnox05qp8
|
|
150
|
+
const hooks =
|
|
151
|
+
currentComponent.__hooks ||
|
|
152
|
+
(currentComponent.__hooks = {
|
|
153
|
+
_list: [],
|
|
154
|
+
_pendingEffects: []
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (index >= hooks._list.length) {
|
|
158
|
+
hooks._list.push({ _pendingValue: EMPTY });
|
|
159
|
+
}
|
|
160
|
+
return hooks._list[index];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {import('./index').StateUpdater<any>} [initialState]
|
|
165
|
+
*/
|
|
166
|
+
export function useState(initialState?) {
|
|
167
|
+
currentHook = 1;
|
|
168
|
+
return useReducer(invokeOrReturn, initialState);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {import('./index').Reducer<any, any>} reducer
|
|
173
|
+
* @param {import('./index').StateUpdater<any>} initialState
|
|
174
|
+
* @param {(initialState: any) => void} [init]
|
|
175
|
+
* @returns {[ any, (state: any) => void ]}
|
|
176
|
+
*/
|
|
177
|
+
export function useReducer(reducer, initialState, init?) {
|
|
178
|
+
/** @type {import('./internal').ReducerHookState} */
|
|
179
|
+
const hookState = getHookState(currentIndex++, 2);
|
|
180
|
+
hookState._reducer = reducer;
|
|
181
|
+
if (typeof hookState._component === "undefined" || hookState._component === null) { // MODDED
|
|
182
|
+
hookState._value = [
|
|
183
|
+
!init ? invokeOrReturn(undefined, initialState) : init(initialState),
|
|
184
|
+
|
|
185
|
+
action => {
|
|
186
|
+
const currentValue = hookState._nextValue
|
|
187
|
+
? hookState._nextValue[0]
|
|
188
|
+
: hookState._value[0];
|
|
189
|
+
const nextValue = hookState._reducer(currentValue, action);
|
|
190
|
+
|
|
191
|
+
if (currentValue !== nextValue) {
|
|
192
|
+
hookState._nextValue = [nextValue, hookState._value[1]];
|
|
193
|
+
hookState._component.setState({});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
hookState._component = currentComponent;
|
|
199
|
+
|
|
200
|
+
if (!currentComponent._hasScuFromHooks) {
|
|
201
|
+
currentComponent._hasScuFromHooks = true;
|
|
202
|
+
let prevScu = currentComponent.shouldComponentUpdate;
|
|
203
|
+
const prevCWU = currentComponent.componentWillUpdate;
|
|
204
|
+
|
|
205
|
+
// If we're dealing with a forced update `shouldComponentUpdate` will
|
|
206
|
+
// not be called. But we use that to update the hook values, so we
|
|
207
|
+
// need to call it.
|
|
208
|
+
currentComponent.componentWillUpdate = function (p, s, c) {
|
|
209
|
+
if (this._force) {
|
|
210
|
+
let tmp = prevScu;
|
|
211
|
+
// Clear to avoid other sCU hooks from being called
|
|
212
|
+
prevScu = undefined;
|
|
213
|
+
updateHookState(p, s, c);
|
|
214
|
+
prevScu = tmp;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (prevCWU) prevCWU.call(this, p, s, c);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// This SCU has the purpose of bailing out after repeated updates
|
|
221
|
+
// to stateful hooks.
|
|
222
|
+
// we store the next value in _nextValue[0] and keep doing that for all
|
|
223
|
+
// state setters, if we have next states and
|
|
224
|
+
// all next states within a component end up being equal to their original state
|
|
225
|
+
// we are safe to bail out for this specific component.
|
|
226
|
+
/**
|
|
227
|
+
*
|
|
228
|
+
* @type {import('./internal').Component["shouldComponentUpdate"]}
|
|
229
|
+
*/
|
|
230
|
+
// @ts-ignore - We don't use TS to downtranspile
|
|
231
|
+
// eslint-disable-next-line no-inner-declarations
|
|
232
|
+
function updateHookState(p, s, c) {
|
|
233
|
+
if (typeof hookState._component.__hooks == "undefined" || hookState._component.__hooks === null) return true;
|
|
234
|
+
|
|
235
|
+
const stateHooks = hookState._component.__hooks._list.filter(
|
|
236
|
+
x => x._component
|
|
237
|
+
);
|
|
238
|
+
const allHooksEmpty = stateHooks.every(x => typeof x._nextValue == "undefined" || x._nextValue === null);
|
|
239
|
+
// When we have no updated hooks in the component we invoke the previous SCU or
|
|
240
|
+
// traverse the VDOM tree further.
|
|
241
|
+
if (allHooksEmpty) {
|
|
242
|
+
return prevScu ? prevScu.call(this, p, s, c) : true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// We check whether we have components with a nextValue set that
|
|
246
|
+
// have values that aren't equal to one another this pushes
|
|
247
|
+
// us to update further down the tree
|
|
248
|
+
let shouldUpdate = false;
|
|
249
|
+
stateHooks.forEach(hookItem => {
|
|
250
|
+
if (hookItem._nextValue) {
|
|
251
|
+
const currentValue = hookItem._value[0];
|
|
252
|
+
hookItem._value = hookItem._nextValue;
|
|
253
|
+
hookItem._nextValue = undefined;
|
|
254
|
+
if (currentValue !== hookItem._value[0]) shouldUpdate = true;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return shouldUpdate || hookState._component.props !== p
|
|
259
|
+
? prevScu
|
|
260
|
+
? prevScu.call(this, p, s, c)
|
|
261
|
+
: true
|
|
262
|
+
: false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
currentComponent.shouldComponentUpdate = updateHookState;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return hookState._nextValue || hookState._value;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @param {import('./internal').Effect} callback
|
|
274
|
+
* @param {any[]} args
|
|
275
|
+
*/
|
|
276
|
+
export function useEffect(callback, args) {
|
|
277
|
+
/** @type {import('./internal').EffectHookState} */
|
|
278
|
+
const state = getHookState(currentIndex++, 3);
|
|
279
|
+
if (!options._skipEffects && argsChanged(state._args, args)) {
|
|
280
|
+
state._value = callback;
|
|
281
|
+
state._pendingArgs = args;
|
|
282
|
+
|
|
283
|
+
currentComponent.__hooks._pendingEffects.push(state);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @param {import('./internal').Effect} callback
|
|
289
|
+
* @param {any[]} args
|
|
290
|
+
*/
|
|
291
|
+
export function useLayoutEffect(callback, args) {
|
|
292
|
+
/** @type {import('./internal').EffectHookState} */
|
|
293
|
+
const state = getHookState(currentIndex++, 4);
|
|
294
|
+
if (!options._skipEffects && argsChanged(state._args, args)) {
|
|
295
|
+
state._value = callback;
|
|
296
|
+
state._pendingArgs = args;
|
|
297
|
+
|
|
298
|
+
currentComponent._renderCallbacks.push(state);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function useRef(initialValue) {
|
|
303
|
+
currentHook = 5;
|
|
304
|
+
return useMemo(() => ({ current: initialValue }), []);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @param {object} ref
|
|
309
|
+
* @param {() => object} createHandle
|
|
310
|
+
* @param {any[]} args
|
|
311
|
+
*/
|
|
312
|
+
export function useImperativeHandle(ref, createHandle, args) {
|
|
313
|
+
currentHook = 6;
|
|
314
|
+
useLayoutEffect(
|
|
315
|
+
() => {
|
|
316
|
+
if (typeof ref == 'function') {
|
|
317
|
+
ref(createHandle());
|
|
318
|
+
return () => ref(null);
|
|
319
|
+
} else if (ref) {
|
|
320
|
+
ref.current = createHandle();
|
|
321
|
+
return () => (ref.current = null);
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
args == null ? args : args.concat(ref)
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* @param {() => any} factory
|
|
330
|
+
* @param {any[]} args
|
|
331
|
+
*/
|
|
332
|
+
export function useMemo(factory, args) {
|
|
333
|
+
/** @type {import('./internal').MemoHookState} */
|
|
334
|
+
const state = getHookState(currentIndex++, 7);
|
|
335
|
+
if (argsChanged(state._args, args)) {
|
|
336
|
+
state._pendingValue = factory();
|
|
337
|
+
state._pendingArgs = args;
|
|
338
|
+
state._factory = factory;
|
|
339
|
+
return state._pendingValue;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return state._value;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* @param {() => void} callback
|
|
347
|
+
* @param {any[]} args
|
|
348
|
+
*/
|
|
349
|
+
export function useCallback(callback, args) {
|
|
350
|
+
currentHook = 8;
|
|
351
|
+
return useMemo(() => callback, args);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @param {import('./internal').PreactContext} context
|
|
356
|
+
*/
|
|
357
|
+
export function useContext(context) {
|
|
358
|
+
const provider = currentComponent.context[context._id];
|
|
359
|
+
// We could skip this call here, but than we'd not call
|
|
360
|
+
// `options._hook`. We need to do that in order to make
|
|
361
|
+
// the devtools aware of this hook.
|
|
362
|
+
/** @type {import('./internal').ContextHookState} */
|
|
363
|
+
const state = getHookState(currentIndex++, 9);
|
|
364
|
+
// The devtools needs access to the context object to
|
|
365
|
+
// be able to pull of the default value when no provider
|
|
366
|
+
// is present in the tree.
|
|
367
|
+
state._context = context;
|
|
368
|
+
if (typeof provider == "undefined" || provider === null) return context._defaultValue;
|
|
369
|
+
// This is probably not safe to convert to "!"
|
|
370
|
+
if (state._value == null) {
|
|
371
|
+
state._value = true;
|
|
372
|
+
provider.sub(currentComponent);
|
|
373
|
+
}
|
|
374
|
+
return provider.props.value;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Display a custom label for a custom hook for the devtools panel
|
|
379
|
+
* @type {<T>(value: T, cb?: (value: T) => string | number) => void}
|
|
380
|
+
*/
|
|
381
|
+
export function useDebugValue(value, formatter) {
|
|
382
|
+
if (options.useDebugValue) {
|
|
383
|
+
options.useDebugValue(formatter ? formatter(value) : value);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* @param {(error: any, errorInfo: import('preact').ErrorInfo) => void} cb
|
|
389
|
+
*/
|
|
390
|
+
export function useErrorBoundary(cb) {
|
|
391
|
+
/** @type {import('./internal').ErrorBoundaryHookState} */
|
|
392
|
+
const state = getHookState(currentIndex++, 10);
|
|
393
|
+
const errState = useState();
|
|
394
|
+
state._value = cb;
|
|
395
|
+
if (!currentComponent.componentDidCatch) {
|
|
396
|
+
currentComponent.componentDidCatch = (err, errorInfo) => {
|
|
397
|
+
if (state._value) state._value(err, errorInfo);
|
|
398
|
+
errState[1](err);
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
return [
|
|
402
|
+
errState[0],
|
|
403
|
+
() => {
|
|
404
|
+
errState[1](undefined);
|
|
405
|
+
}
|
|
406
|
+
];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export function useId() {
|
|
410
|
+
const state = getHookState(currentIndex++, 11);
|
|
411
|
+
if (!state._value) {
|
|
412
|
+
// Grab either the root node or the nearest async boundary node.
|
|
413
|
+
/** @type {import('./internal.d').VNode} */
|
|
414
|
+
let root = currentComponent._vnode;
|
|
415
|
+
while (root !== null && !root._mask && root._parent !== null) {
|
|
416
|
+
root = root._parent;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
let mask = root._mask || (root._mask = [0, 0]);
|
|
420
|
+
state._value = 'P' + mask[0] + '-' + mask[1]++;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return state._value;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* After paint effects consumer.
|
|
427
|
+
*/
|
|
428
|
+
function flushAfterPaintEffects() {
|
|
429
|
+
let component;
|
|
430
|
+
while ((component = afterPaintEffects.shift())) {
|
|
431
|
+
if (!component._parentDom || typeof component.__hooks == "undefined" || component.__hooks === null) continue; // MODDED
|
|
432
|
+
try {
|
|
433
|
+
component.__hooks._pendingEffects.forEach(invokeCleanup);
|
|
434
|
+
component.__hooks._pendingEffects.forEach(invokeEffect);
|
|
435
|
+
component.__hooks._pendingEffects = [];
|
|
436
|
+
} catch (e) {
|
|
437
|
+
component.__hooks._pendingEffects = [];
|
|
438
|
+
options._catchError(e, component._vnode);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// globalThis.requestAnimationFrame = function (callback) {
|
|
444
|
+
// return setTimeout(callback)
|
|
445
|
+
// } as any
|
|
446
|
+
|
|
447
|
+
// globalThis.cancelAnimationFrame = function (id) {
|
|
448
|
+
// clearTimeout(id)
|
|
449
|
+
// } as any
|
|
450
|
+
|
|
451
|
+
let HAS_RAF = typeof requestAnimationFrame == 'function';
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Schedule a callback to be invoked after the browser has a chance to paint a new frame.
|
|
455
|
+
* Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after
|
|
456
|
+
* the next browser frame.
|
|
457
|
+
*
|
|
458
|
+
* Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked
|
|
459
|
+
* even if RAF doesn't fire (for example if the browser tab is not visible)
|
|
460
|
+
*
|
|
461
|
+
* @param {() => void} callback
|
|
462
|
+
*/
|
|
463
|
+
function afterNextFrame(callback) {
|
|
464
|
+
const done = () => {
|
|
465
|
+
clearTimeout(timeout);
|
|
466
|
+
if (HAS_RAF) cancelAnimationFrame(raf);
|
|
467
|
+
setTimeout(callback);
|
|
468
|
+
};
|
|
469
|
+
const timeout = setTimeout(done, RAF_TIMEOUT);
|
|
470
|
+
|
|
471
|
+
let raf;
|
|
472
|
+
if (HAS_RAF) {
|
|
473
|
+
raf = requestAnimationFrame(done);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Note: if someone used options.debounceRendering = requestAnimationFrame,
|
|
478
|
+
// then effects will ALWAYS run on the NEXT frame instead of the current one, incurring a ~16ms delay.
|
|
479
|
+
// Perhaps this is not such a big deal.
|
|
480
|
+
/**
|
|
481
|
+
* Schedule afterPaintEffects flush after the browser paints
|
|
482
|
+
* @param {number} newQueueLength
|
|
483
|
+
*/
|
|
484
|
+
function afterPaint(newQueueLength) {
|
|
485
|
+
if (newQueueLength === 1 || prevRaf !== options.requestAnimationFrame) {
|
|
486
|
+
prevRaf = options.requestAnimationFrame;
|
|
487
|
+
(prevRaf || afterNextFrame)(flushAfterPaintEffects);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
options.debounceRendering = requestAnimationFrame // ADDED
|
|
492
|
+
options.requestAnimationFrame = requestAnimationFrame // ADDED
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @param {import('./internal').EffectHookState} hook
|
|
496
|
+
*/
|
|
497
|
+
function invokeCleanup(hook) {
|
|
498
|
+
// A hook cleanup can introduce a call to render which creates a new root, this will call options.vnode
|
|
499
|
+
// and move the currentComponent away.
|
|
500
|
+
const comp = currentComponent;
|
|
501
|
+
let cleanup = hook._cleanup;
|
|
502
|
+
if (typeof cleanup == 'function') {
|
|
503
|
+
hook._cleanup = undefined;
|
|
504
|
+
cleanup();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
currentComponent = comp;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Invoke a Hook's effect
|
|
512
|
+
* @param {import('./internal').EffectHookState} hook
|
|
513
|
+
*/
|
|
514
|
+
function invokeEffect(hook) {
|
|
515
|
+
// A hook call can introduce a call to render which creates a new root, this will call options.vnode
|
|
516
|
+
// and move the currentComponent away.
|
|
517
|
+
const comp = currentComponent;
|
|
518
|
+
hook._cleanup = hook._value();
|
|
519
|
+
currentComponent = comp;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* @param {any[]} oldArgs
|
|
524
|
+
* @param {any[]} newArgs
|
|
525
|
+
*/
|
|
526
|
+
function argsChanged(oldArgs, newArgs) {
|
|
527
|
+
return (
|
|
528
|
+
!oldArgs ||
|
|
529
|
+
oldArgs.length !== newArgs.length ||
|
|
530
|
+
Array.isArray(newArgs) && newArgs.some((arg, index) => arg !== oldArgs[index]) // MODDED
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function invokeOrReturn(arg, f) {
|
|
535
|
+
return typeof f == 'function' ? f(arg) : f;
|
|
536
|
+
}
|