lilact 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lilact.development.js +156 -118
- package/dist/lilact.development.js.map +3 -3
- package/dist/lilact.development.min.js +20 -20
- package/dist/lilact.development.min.js.map +3 -3
- package/dist/lilact.production.min.js +18 -18
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/accessories.ErrorBoundary.html +8 -8
- package/docs/classes/accessories.Suspense.html +7 -7
- package/docs/classes/components.Component.html +11 -11
- package/docs/classes/components.HTMLComponent.html +11 -11
- package/docs/classes/components.RootComponent.html +11 -11
- package/docs/functions/components.createComponent.html +1 -1
- package/docs/functions/components.createPortal.html +11 -0
- package/docs/functions/components.createRoot.html +1 -1
- package/docs/functions/components.memo.html +7 -0
- package/docs/functions/components.render.html +1 -1
- package/docs/functions/hooks.startTransition.html +1 -1
- package/docs/functions/hooks.useActionState.html +1 -1
- package/docs/functions/hooks.useDebugValue.html +3 -3
- package/docs/functions/hooks.useDeferredValue.html +1 -1
- package/docs/functions/hooks.useEffect.html +4 -3
- package/docs/functions/hooks.useImperativeHandle.html +1 -1
- package/docs/functions/hooks.useInsertionEffect.html +4 -3
- package/docs/functions/hooks.useLayoutEffect.html +4 -3
- package/docs/functions/hooks.useMemo.html +4 -3
- package/docs/functions/hooks.useReducer.html +1 -1
- package/docs/functions/misc.Fragment.html +1 -1
- package/docs/functions/misc.Portal.html +4 -0
- package/docs/functions/misc.deepEqual.html +1 -1
- package/docs/functions/misc.forwardRef.html +1 -1
- package/docs/functions/misc.getComponentByPointer.html +1 -1
- package/docs/functions/misc.isAsync.html +1 -1
- package/docs/functions/misc.isClass.html +1 -1
- package/docs/functions/misc.isEmpty.html +1 -1
- package/docs/functions/misc.isError.html +1 -1
- package/docs/functions/misc.isThenable.html +1 -1
- package/docs/functions/misc.shallowEqual.html +3 -2
- package/docs/functions/misc.toBool.html +1 -1
- package/docs/functions/timers.timeoutPromise.html +5 -6
- package/docs/hierarchy.html +1 -1
- package/docs/modules/components.html +1 -1
- package/docs/modules/misc.html +1 -1
- package/docs/static/demos/memo.jsx +26 -0
- package/docs/static/demos/portal.jsx +56 -0
- package/docs/static/index.html +8 -3
- package/docs/static/lilact.development.js +156 -118
- package/docs/static/lilact.development.js.map +3 -3
- package/docs/static/lilact.development.min.js +20 -20
- package/docs/static/lilact.development.min.js.map +3 -3
- package/docs/static/lilact.production.min.js +18 -18
- package/docs/variables/misc.Children.html +2 -2
- package/examples/demos/memo.jsx +26 -0
- package/examples/demos/portal.jsx +56 -0
- package/examples/index.html +8 -3
- package/examples/lilact.development.js +156 -118
- package/examples/lilact.development.js.map +3 -3
- package/examples/lilact.development.min.js +20 -20
- package/examples/lilact.development.min.js.map +3 -3
- package/examples/lilact.production.min.js +18 -18
- package/package.json +1 -1
- package/src/components.jsx +101 -33
- package/src/hooks.jsx +10 -6
- package/src/lilact.jsx +1 -1
- package/src/misc.jsx +22 -4
package/src/components.jsx
CHANGED
|
@@ -97,7 +97,7 @@ class ComponentCache
|
|
|
97
97
|
if(ex.cleanup) {
|
|
98
98
|
ex.cleanup();
|
|
99
99
|
}
|
|
100
|
-
else if(ex.element) {
|
|
100
|
+
else if(ex.element && !ex.portal) {
|
|
101
101
|
ex.element.parentElement.removeChild(ex.element);
|
|
102
102
|
}
|
|
103
103
|
});
|
|
@@ -162,19 +162,23 @@ class ComponentCore
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/*
|
|
165
|
-
//let do_rerender = true;
|
|
166
|
-
|
|
167
|
-
// if(this.outlet && this.entity[MEMOIZED]) {
|
|
168
|
-
// if(shallowEqual(this.props, next_props)) do_rerender=false;
|
|
169
|
-
// delete this.entity[MEMOIZED];
|
|
170
|
-
// }
|
|
171
|
-
|
|
172
|
-
if(do_rerender) {
|
|
173
165
|
*/
|
|
174
166
|
// TODO: should componentDidUpdate be called after arranging/appending the outlet or before?
|
|
175
167
|
apply(next_props = this.props, next_state = this.next_state || this.state)
|
|
176
168
|
{
|
|
177
169
|
|
|
170
|
+
let do_rerender = true;
|
|
171
|
+
|
|
172
|
+
if(this.outlet && this?.[MEMOIZED]) {
|
|
173
|
+
|
|
174
|
+
if( shallowEqual(this.props, next_props, "children") &&
|
|
175
|
+
shallowEqual(this.props?.children, next_props?.children) ) {
|
|
176
|
+
do_rerender=false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if(do_rerender) {
|
|
181
|
+
|
|
178
182
|
if(DEBUG) {
|
|
179
183
|
|
|
180
184
|
if(this.entity?.propTypes) {
|
|
@@ -205,7 +209,6 @@ if(DEBUG) {
|
|
|
205
209
|
this.element[COMPONENT] = this.component;
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
|
|
209
212
|
if(next_props.ref) {
|
|
210
213
|
if(typeof(next_props.ref)==='function') {
|
|
211
214
|
next_props.ref(this.element || this.component);
|
|
@@ -262,6 +265,12 @@ if(DEBUG) {
|
|
|
262
265
|
}
|
|
263
266
|
}
|
|
264
267
|
|
|
268
|
+
|
|
269
|
+
if( this?.portal ) {
|
|
270
|
+
this.element = this.portal;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
265
274
|
if(this.outlet?.constructor?.name!=='Array') {
|
|
266
275
|
this.outlet = [this.outlet];
|
|
267
276
|
}
|
|
@@ -308,13 +317,14 @@ if(DEBUG) {
|
|
|
308
317
|
if(this.cache) this.cache.commit();
|
|
309
318
|
|
|
310
319
|
|
|
311
|
-
|
|
320
|
+
if(this.element) this.arrangeOutlet();
|
|
312
321
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
322
|
+
if(this.component.componentDidUpdate) {
|
|
323
|
+
this.component.componentDidUpdate(prev_props, prev_state, this.last_snapshot);
|
|
324
|
+
}
|
|
316
325
|
|
|
317
|
-
|
|
326
|
+
if(this.last_snapshot) delete this.last_snapshot;
|
|
327
|
+
}
|
|
318
328
|
|
|
319
329
|
}
|
|
320
330
|
|
|
@@ -336,7 +346,7 @@ if(DEBUG) {
|
|
|
336
346
|
this.component.componentWillUnmount();
|
|
337
347
|
}
|
|
338
348
|
|
|
339
|
-
if(this?.element?.parentElement) {
|
|
349
|
+
if(this?.element?.parentElement && !this.portal) {
|
|
340
350
|
this.element.parentElement.removeChild( this.element );
|
|
341
351
|
}
|
|
342
352
|
|
|
@@ -533,6 +543,8 @@ if(DEBUG) {
|
|
|
533
543
|
|
|
534
544
|
appendElement(core)
|
|
535
545
|
{
|
|
546
|
+
if(core.portal) return;
|
|
547
|
+
|
|
536
548
|
this.scanZombies(core.container, core.element);
|
|
537
549
|
|
|
538
550
|
if(core?.element.parentNode===null) {
|
|
@@ -635,17 +647,30 @@ function constructFunc(core, parent) // returns {text} or component, and not com
|
|
|
635
647
|
// do nothing...
|
|
636
648
|
}
|
|
637
649
|
else {
|
|
638
|
-
|
|
639
|
-
|
|
650
|
+
let entity = core.entity;
|
|
651
|
+
let memoized = false;
|
|
652
|
+
|
|
653
|
+
if(typeof(entity)==='object') {
|
|
654
|
+
if( entity[MEMOIZED] ) {
|
|
655
|
+
memoized = entity[MEMOIZED];
|
|
656
|
+
entity = entity.component;
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
throw new Error("Invalid component.");
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if(typeof(entity)==='string') {
|
|
664
|
+
comp = new HTMLComponent(entity, core.props);
|
|
640
665
|
}
|
|
641
666
|
else {
|
|
642
667
|
|
|
643
|
-
if( isClass(
|
|
644
|
-
if(
|
|
645
|
-
core.props = { ...
|
|
668
|
+
if( isClass(entity) ) {
|
|
669
|
+
if(entity?.defaultProps) {
|
|
670
|
+
core.props = { ...entity.defaultProps, ...core.props };
|
|
646
671
|
}
|
|
647
672
|
|
|
648
|
-
comp = new
|
|
673
|
+
comp = new entity(core.props);
|
|
649
674
|
|
|
650
675
|
const desc = Object.getOwnPropertyDescriptor(comp, "state");
|
|
651
676
|
if(desc) {
|
|
@@ -667,35 +692,35 @@ function constructFunc(core, parent) // returns {text} or component, and not com
|
|
|
667
692
|
}
|
|
668
693
|
}
|
|
669
694
|
}
|
|
670
|
-
else if(typeof(
|
|
695
|
+
else if(typeof(entity)==='function') {
|
|
671
696
|
|
|
672
|
-
if(
|
|
673
|
-
core.props = { ...
|
|
697
|
+
if(entity?.defaultProps) {
|
|
698
|
+
core.props = { ...entity.defaultProps, ...core.props };
|
|
674
699
|
}
|
|
675
700
|
|
|
676
701
|
comp = new Component(core.props);
|
|
677
702
|
|
|
678
703
|
// the binding is not necessary and is not according to the specs,
|
|
679
704
|
// probably not even recommended! but helpful.
|
|
680
|
-
comp.render =
|
|
705
|
+
comp.render = entity.bind(comp);
|
|
681
706
|
comp[CORE].hooks = [];
|
|
682
707
|
comp[CORE].hook_index = 0;
|
|
683
708
|
}
|
|
684
709
|
else {
|
|
685
|
-
throw new Error("
|
|
710
|
+
throw new Error("Error in constructing component.");
|
|
686
711
|
}
|
|
687
712
|
|
|
688
|
-
comp[CORE].entity =
|
|
713
|
+
comp[CORE].entity = entity;
|
|
689
714
|
|
|
690
715
|
if(core.container) {
|
|
691
716
|
comp[CORE].container = core.container;
|
|
692
717
|
}
|
|
693
718
|
}
|
|
694
719
|
|
|
720
|
+
if(memoized) comp[CORE][MEMOIZED] = true;
|
|
695
721
|
}
|
|
696
722
|
|
|
697
723
|
if(parent instanceof ComponentCore) comp[CORE].parent = parent;
|
|
698
|
-
|
|
699
724
|
return comp;
|
|
700
725
|
}
|
|
701
726
|
|
|
@@ -1089,8 +1114,12 @@ export class RootComponent extends HTMLComponent
|
|
|
1089
1114
|
|
|
1090
1115
|
export function createComponent(entity, props={}, ...children)
|
|
1091
1116
|
{
|
|
1092
|
-
|
|
1093
|
-
|
|
1117
|
+
|
|
1118
|
+
if (typeof(entity)!=='string' && typeof(entity)!=='function' )
|
|
1119
|
+
{
|
|
1120
|
+
if(typeof(entity)!=='object' || !entity[MEMOIZED]) {
|
|
1121
|
+
throw new Error("Invalid entity for createComponent.");
|
|
1122
|
+
}
|
|
1094
1123
|
}
|
|
1095
1124
|
|
|
1096
1125
|
for(let i=0; i<children.length; i++) {
|
|
@@ -1114,8 +1143,6 @@ export function createComponent(entity, props={}, ...children)
|
|
|
1114
1143
|
}
|
|
1115
1144
|
}
|
|
1116
1145
|
|
|
1117
|
-
//if(entity===null) return children; // <> style fragment
|
|
1118
|
-
|
|
1119
1146
|
props.key = generateComponentKey(entity, props);
|
|
1120
1147
|
props.children = children;
|
|
1121
1148
|
|
|
@@ -1162,6 +1189,27 @@ export function createRoot(element)
|
|
|
1162
1189
|
}
|
|
1163
1190
|
}
|
|
1164
1191
|
|
|
1192
|
+
/**
|
|
1193
|
+
* Creates a portal — a way to render children into a DOM node
|
|
1194
|
+
* that exists outside the current component hierarchy.
|
|
1195
|
+
*
|
|
1196
|
+
* Note: In React, events from portals propagate according to the React tree rather
|
|
1197
|
+
* than the DOM tree. For example, if you click inside a portal, and the portal is
|
|
1198
|
+
* wrapped in `<div onClick>`, that onClick handler will fire. Lilact doesn't act
|
|
1199
|
+
* this way. In Lilact, event propagation follows DOM tree, while the ownership
|
|
1200
|
+
* follow the JSX structure.
|
|
1201
|
+
*
|
|
1202
|
+
* @param {Component} children - The component(s) to render into the portal.
|
|
1203
|
+
* @param {Element} element - The DOM node that will receive the portal content.
|
|
1204
|
+
* @returns {Component} A portal object that can be rendered.
|
|
1205
|
+
*/
|
|
1206
|
+
|
|
1207
|
+
export function createPortal(children, element)
|
|
1208
|
+
{
|
|
1209
|
+
return <Lilact.Portal view={element}>{children}</Lilact.Portal>;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
|
|
1165
1213
|
/**
|
|
1166
1214
|
* Renders a component into a target DOM element.
|
|
1167
1215
|
* If the component maintains internal state, this typically mounts it (or updates the existing tree) under `element`.
|
|
@@ -1181,6 +1229,26 @@ export function render(component, element)
|
|
|
1181
1229
|
return createRoot(element).render(component);
|
|
1182
1230
|
}
|
|
1183
1231
|
|
|
1232
|
+
/**
|
|
1233
|
+
* Memoizes the given component. The memoized component will only rerender if its props or children are modified.
|
|
1234
|
+
* Unlike React, Lilact memo also works on class components.
|
|
1235
|
+
*
|
|
1236
|
+
* @param {Object} component - Component instance to render.
|
|
1237
|
+
*
|
|
1238
|
+
* @returns {component} - Memoized component.
|
|
1239
|
+
*/
|
|
1240
|
+
|
|
1241
|
+
export function memo(component)
|
|
1242
|
+
{
|
|
1243
|
+
if(typeof(component)==='object') {
|
|
1244
|
+
component[MEMOIZED] = true;
|
|
1245
|
+
}
|
|
1246
|
+
else {
|
|
1247
|
+
component = { component, [MEMOIZED]: true };
|
|
1248
|
+
}
|
|
1249
|
+
return component;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1184
1252
|
|
|
1185
1253
|
/** @ignore */
|
|
1186
1254
|
export const createElement = createComponent;
|
package/src/hooks.jsx
CHANGED
|
@@ -269,7 +269,8 @@ export function useRef(initialValue = null)
|
|
|
269
269
|
* Runs an effect synchronously after all DOM mutations but before the browser paints.
|
|
270
270
|
*
|
|
271
271
|
* @param {Function} effect - Effect callback.
|
|
272
|
-
* @param deps
|
|
272
|
+
* @param {Array<any>} [deps] Optional dependency list (or an object for shallow comparison)
|
|
273
|
+
* used to determine when to re-run the effect.
|
|
273
274
|
* @returns {void}
|
|
274
275
|
*/
|
|
275
276
|
export function useLayoutEffect(effect, deps=undefined)
|
|
@@ -299,7 +300,8 @@ export function useLayoutEffect(effect, deps=undefined)
|
|
|
299
300
|
* Runs a side effect after render commits.
|
|
300
301
|
*
|
|
301
302
|
* @param effect - Callback invoked.
|
|
302
|
-
* @param deps
|
|
303
|
+
* @param {Array<any>} [deps] Optional dependency list (or an object for shallow comparison)
|
|
304
|
+
* used to determine when to re-run the effect.
|
|
303
305
|
* @returns {void}
|
|
304
306
|
*/
|
|
305
307
|
export function useEffect(effect, deps=undefined)
|
|
@@ -330,7 +332,8 @@ export function useEffect(effect, deps=undefined)
|
|
|
330
332
|
* Executes a side effect after the DOM updates have been committed.
|
|
331
333
|
*
|
|
332
334
|
* @param effect - Callback invoked after render commit.
|
|
333
|
-
* @param deps
|
|
335
|
+
* @param {Array<any>} [deps] Optional dependency list (or an object for shallow comparison)
|
|
336
|
+
* used to determine when to re-run the effect.
|
|
334
337
|
* @returns void
|
|
335
338
|
*/
|
|
336
339
|
export function useInsertionEffect(effect, deps=undefined)
|
|
@@ -361,7 +364,8 @@ export function useInsertionEffect(effect, deps=undefined)
|
|
|
361
364
|
* Memoizes a computed value until dependencies change.
|
|
362
365
|
*
|
|
363
366
|
* @param {Function} factory - Function that creates the value.
|
|
364
|
-
* @param deps
|
|
367
|
+
* @param {Array<any>} [deps] Optional dependency list (or an object for shallow comparison)
|
|
368
|
+
* used to determine when to recompute the value.
|
|
365
369
|
* @returns {any} Memoized value.
|
|
366
370
|
*/
|
|
367
371
|
export function useMemo(factory,deps=undefined)
|
|
@@ -525,8 +529,8 @@ export function useImperativeHandle(ref, factory, deps=undefined)
|
|
|
525
529
|
|
|
526
530
|
|
|
527
531
|
/**
|
|
528
|
-
* A hook for debug purposes.
|
|
529
|
-
* for
|
|
532
|
+
* A hook for debug purposes. By default it only logs the output to console, but it can be overrided
|
|
533
|
+
* for the development environment.
|
|
530
534
|
*
|
|
531
535
|
* @param {any} val
|
|
532
536
|
* The debug value.
|
package/src/lilact.jsx
CHANGED
package/src/misc.jsx
CHANGED
|
@@ -95,9 +95,26 @@ export const findDOMNode = (component)=>{
|
|
|
95
95
|
*
|
|
96
96
|
* @param children - The nodes to group without adding an extra DOM element.
|
|
97
97
|
*/
|
|
98
|
-
export
|
|
98
|
+
export function Fragment({children})
|
|
99
|
+
{
|
|
100
|
+
return children;
|
|
101
|
+
};
|
|
99
102
|
Fragment.displayName = "Fragment";
|
|
100
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Portal helper/utility.
|
|
106
|
+
*
|
|
107
|
+
* @param view - The DOM node that will receive the portal content.
|
|
108
|
+
* @param children - The nodes to put in the portal element.
|
|
109
|
+
*/
|
|
110
|
+
export function Portal({children, view})
|
|
111
|
+
{
|
|
112
|
+
this[CORE].portal = view;
|
|
113
|
+
|
|
114
|
+
return children;
|
|
115
|
+
};
|
|
116
|
+
Portal.displayName = "Portal";
|
|
117
|
+
|
|
101
118
|
/**
|
|
102
119
|
* Children namespace for utilities that operate on `props.children`. `Children` is deprecated
|
|
103
120
|
* and not recommended by React documentation itself. But `only` and `toArray` are used
|
|
@@ -220,9 +237,10 @@ export function isEmpty(value) {
|
|
|
220
237
|
*
|
|
221
238
|
* @param source - First object to compare.
|
|
222
239
|
* @param target - Second object to compare.
|
|
240
|
+
* @param ignore - specific property/index to be ignored in comparison.
|
|
223
241
|
* @returns True if shallowly equal; otherwise false.
|
|
224
242
|
*/
|
|
225
|
-
export const shallowEqual = (source, target) => {
|
|
243
|
+
export const shallowEqual = (source, target, ignore) => {
|
|
226
244
|
if (typeOf(source) !== typeOf(target)) {
|
|
227
245
|
return false;
|
|
228
246
|
}
|
|
@@ -231,9 +249,9 @@ export const shallowEqual = (source, target) => {
|
|
|
231
249
|
if (source.length !== target.length) {
|
|
232
250
|
return false;
|
|
233
251
|
}
|
|
234
|
-
return source.every((el, index) => el === target[index]);
|
|
252
|
+
return source.every((el, index) => el === target[index] || index===ignore );
|
|
235
253
|
} else if (typeOf(source) === "object") {
|
|
236
|
-
return Object.keys(source).every((key) => source[key] === target[key]);
|
|
254
|
+
return Object.keys(source).every((key) => source[key] === target[key] || key===ignore );
|
|
237
255
|
} else if (typeOf(source) === "date") {
|
|
238
256
|
return source.getTime() === target.getTime();
|
|
239
257
|
}
|