lilact 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/lilact.development.js +231 -182
  2. package/dist/lilact.development.js.map +1 -1
  3. package/dist/lilact.development.min.js +1 -1
  4. package/dist/lilact.development.min.js.map +1 -1
  5. package/dist/lilact.production.min.js +1 -1
  6. package/dist/lilact.production.min.js.map +1 -1
  7. package/docs/classes/accessories.ErrorBoundary.html +8 -8
  8. package/docs/classes/accessories.Suspense.html +7 -7
  9. package/docs/classes/components.Component.html +11 -11
  10. package/docs/classes/components.HTMLComponent.html +11 -11
  11. package/docs/classes/components.RootComponent.html +11 -11
  12. package/docs/functions/components.createComponent.html +1 -1
  13. package/docs/functions/components.createRoot.html +1 -1
  14. package/docs/functions/components.render.html +1 -1
  15. package/docs/functions/errors.globalErrorHandler.html +1 -1
  16. package/docs/functions/errors.traceError.html +1 -1
  17. package/docs/functions/hooks.createContext.html +1 -1
  18. package/docs/functions/hooks.useActionState.html +1 -1
  19. package/docs/functions/hooks.useCallback.html +1 -1
  20. package/docs/functions/hooks.useContext.html +1 -1
  21. package/docs/functions/hooks.useDeferredValue.html +1 -1
  22. package/docs/functions/hooks.useEffect.html +1 -1
  23. package/docs/functions/hooks.useHook.html +1 -1
  24. package/docs/functions/hooks.useId.html +1 -1
  25. package/docs/functions/hooks.useImperativeHandle.html +1 -1
  26. package/docs/functions/hooks.useLayoutEffect.html +1 -1
  27. package/docs/functions/hooks.useLocalStorage.html +1 -1
  28. package/docs/functions/hooks.useMemo.html +1 -1
  29. package/docs/functions/hooks.useReducer.html +1 -1
  30. package/docs/functions/hooks.useRef.html +1 -1
  31. package/docs/functions/hooks.useState.html +1 -1
  32. package/docs/functions/hooks.useTransition.html +1 -1
  33. package/docs/functions/router.HashRouter.html +1 -1
  34. package/docs/functions/router.Link.html +1 -1
  35. package/docs/functions/router.NavLink.html +1 -1
  36. package/docs/functions/router.Route.html +1 -1
  37. package/docs/functions/router.Routes.html +1 -1
  38. package/docs/functions/router.useLocation.html +1 -1
  39. package/docs/functions/router.useNavigate.html +1 -1
  40. package/docs/functions/timers.timeoutPromise.html +4 -4
  41. package/docs/functions/transition.CSSTransition.html +1 -1
  42. package/docs/functions/transition.SwitchTransition.html +1 -1
  43. package/docs/functions/transition.Transition.html +1 -1
  44. package/docs/functions/transition.TransitionGroup.html +1 -1
  45. package/docs/static/demos/error-callback.jsx +2 -23
  46. package/docs/static/lilact.development.js +231 -182
  47. package/docs/static/lilact.development.min.js +1 -1
  48. package/docs/static/lilact.production.min.js +1 -1
  49. package/package.json +1 -1
  50. package/root/demos/error-callback.jsx +2 -23
  51. package/root/lilact.development.js +231 -182
  52. package/root/lilact.development.min.js +1 -1
  53. package/root/lilact.production.min.js +1 -1
  54. package/src/components.jsx +83 -22
  55. package/src/errors.jsx +11 -9
  56. package/src/hooks.jsx +8 -7
  57. package/src/misc.jsx +0 -56
  58. package/src/router.jsx +87 -48
  59. package/src/run.jsx +1 -1
  60. package/src/symbols.jsx +1 -0
  61. package/src/transition.jsx +2 -0
@@ -28,8 +28,12 @@
28
28
 
29
29
  */
30
30
 
31
+ import Lilact from './lilact.jsx';
32
+
31
33
  import { CORE, COMPONENT, TEXT, IS_ZOMBIE, IDX, CHILD_CLASS_ADDENDUM, MEMOIZED } from "./symbols.jsx"
32
- import { shallowEqual } from "./misc.jsx";
34
+ import { shallowEqual, toBool, isClass } from "./misc.jsx";
35
+
36
+ import PropTypes from 'prop-types';
33
37
 
34
38
  /*
35
39
  ComponentCache is for internal use. It is the heart of the JSX runtime,
@@ -165,10 +169,10 @@ class ComponentCore
165
169
  if(DEBUG) {
166
170
 
167
171
  if(this.entity?.propTypes) {
168
- Lilact.PropTypes.checkPropTypes(this.entity.propTypes, this.props, 'prop', this.entity.name);
172
+ PropTypes.checkPropTypes(this.entity.propTypes, this.props, 'prop', this.entity.name);
169
173
  }
170
174
  else if(this.component?.propTypes) {
171
- Lilact.PropTypes.checkPropTypes(this.component.propTypes, this.props, 'prop', this.component.name);
175
+ PropTypes.checkPropTypes(this.component.propTypes, this.props, 'prop', this.component.name);
172
176
  }
173
177
 
174
178
  }
@@ -404,7 +408,7 @@ if(DEBUG) {
404
408
 
405
409
  if( !patch.hasOwnProperty(a) ) {
406
410
 
407
- if( Lilact.events_set.has(al) ) {
411
+ if( events_set.has(al) ) {
408
412
  this.event_detachers[al]();
409
413
  }
410
414
  else {
@@ -416,17 +420,17 @@ if(DEBUG) {
416
420
  for(let a in patch) {
417
421
  const al = a.toLowerCase();
418
422
 
419
- if( Lilact.special_attributes.has(al) ) continue;
423
+ if( special_attributes.has(al) ) continue;
420
424
 
421
425
  if( patch===this.props || !Lilact.defaultIsEqual(patch[a], this.props[a]) || force ) {
422
426
 
423
- if( Lilact.events_set.has(al) ) {
427
+ if( events_set.has(al) ) {
424
428
  this.event_detachers ??= {};
425
429
  this.event_detachers[al]?.();
426
430
  this.event_detachers[al] = Lilact.addWrappedEventListener(this.element, al.substring(2), patch[a]);
427
431
  }
428
- else if( Lilact.capture_events_set.hasOwnProperty(al) ) {
429
- const alc = Lilact.capture_events_set[al];
432
+ else if( capture_events_set.hasOwnProperty(al) ) {
433
+ const alc = capture_events_set[al];
430
434
  this.event_detachers ??= {};
431
435
  this.event_detachers[al]?.();
432
436
  this.event_detachers[al] =
@@ -438,7 +442,7 @@ if(DEBUG) {
438
442
  }
439
443
  else {
440
444
  for(const x in patch[a]) {
441
- if( Lilact.length_css_attributes_set.has(x) ) {
445
+ if( length_css_attributes_set.has(x) ) {
442
446
  if(isFinite(patch[a][x])) {
443
447
  patch[a][x]+='px';
444
448
  }
@@ -447,11 +451,11 @@ if(DEBUG) {
447
451
  Object.assign(this.element.style, patch[a]);
448
452
  }
449
453
  }
450
- else if(Lilact.boolean_html_attributes_set.has(a)) { // not lower cased(al), as it is set as a js property
451
- this.element[a] = Lilact.toBool(patch[a]);
454
+ else if(boolean_html_attributes_set.has(a)) { // not lower cased(al), as it is set as a js property
455
+ this.element[a] = toBool(patch[a]);
452
456
  }
453
457
  else if(a==='autoFocus') { // not lower cased(al), as it is set as a js property
454
- this.element['autofocus'] = Lilact.toBool(patch[a]);
458
+ this.element['autofocus'] = toBool(patch[a]);
455
459
  }
456
460
  else if(a==='htmlFor') { // not lower cased(al), as it is set as a js property
457
461
  this.element.setAttribute('for', patch[a]);
@@ -627,7 +631,7 @@ function constructFunc(core, parent) // returns {text} or component, and not com
627
631
  }
628
632
  else {
629
633
 
630
- if( Lilact.isClass(core.entity) ) {
634
+ if( isClass(core.entity) ) {
631
635
  if(core.entity?.defaultProps) {
632
636
  core.props = { ...core.entity.defaultProps, ...core.props };
633
637
  }
@@ -709,19 +713,18 @@ function prepareCore(parent, core)
709
713
  function doUpdates()
710
714
  {
711
715
  requestAnimationFrame(()=>{
712
- let layout_effects = Lilact.layout_effects;
713
- let update_cbs = Lilact.update_cbs;
714
- let update_set = Lilact.update_set;
716
+ let _layout_effects = Lilact.layout_effects;
717
+ let _update_cbs = Lilact.update_cbs;
718
+ let _update_set = Lilact.update_set;
715
719
 
716
720
  Lilact.layout_effects = new Set;
717
721
  Lilact.update_cbs = new Set;
718
722
  Lilact.update_set = new Set;
719
723
 
720
- for(const le of layout_effects) le();
721
-
722
- for(const u of update_set) u.apply();
723
- for(const cb of update_cbs) cb();
724
+ for(const le of _layout_effects) le();
724
725
 
726
+ for(const u of _update_set) u.apply();
727
+ for(const cb of _update_cbs) cb();
725
728
  });
726
729
  }
727
730
 
@@ -881,7 +884,7 @@ export class Component
881
884
  {
882
885
  if(this[CORE].entity?.displayName) return this[CORE].entity?.displayName;
883
886
  if(typeof(this[CORE].entity)==='string') return this[CORE].entity;
884
- if( Lilact.isClass(this[CORE].entity) ) this[CORE].entity.constructor.name;
887
+ if( isClass(this[CORE].entity) ) this[CORE].entity.constructor.name;
885
888
  if( typeof(this[CORE].entity)==='function' ) return this[CORE].entity.name;
886
889
  return "Component";
887
890
  }
@@ -1135,4 +1138,62 @@ export function render(component, element)
1135
1138
  }
1136
1139
 
1137
1140
  /** @ignore */
1138
- export const createElement = createComponent;
1141
+ export const createElement = createComponent;
1142
+
1143
+ /** @ignore */
1144
+ export let current_component = [];
1145
+ /** @ignore */
1146
+
1147
+ export let update_set = new Set;
1148
+ /** @ignore */
1149
+ export let update_cbs = new Set;
1150
+ /** @ignore */
1151
+ export let roots = new Set;
1152
+ /** @ignore */
1153
+ export let layout_effects = new Set;
1154
+
1155
+ /** @ignore */
1156
+ export const special_attributes = new Set([
1157
+ "classname", "ref", "action", "lilact_jsx_loc", "children", "key",
1158
+ "defaultvalue", "defaultchecked"
1159
+ ]);
1160
+
1161
+ /** @ignore */
1162
+ export const events_set = new Set([
1163
+ "onafterprint","onbeforeprint","onbeforeunload","onerror","onhashchange","onload","onmessage",
1164
+ "onoffline","ononline","onpagehide","onpageshow","onpopstate","onresize","onstorage","onunload",
1165
+ "onblur","onchange","oncontextmenu","onfocus","oninput","oninvalid","onreset","onsearch","onselect",
1166
+ "onsubmit",
1167
+ "onkeydown","onkeypress","onkeyup",
1168
+ "onclick","ondblclick","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel",
1169
+ "onwheel",
1170
+ "ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","onscroll",
1171
+ "oncopy","oncut","onpaste",
1172
+ "onabort","oncanplay","oncanplaythrough","oncuechange","ondurationchange","onemptied","onended","onerror",
1173
+ "onloadeddata","onloadedmetadata","onloadstart","onpause","onplay","onplaying","onprogress","onratechange",
1174
+ "onseeked","onseeking","onstalled","onsuspend","ontimeupdate","onvolumechange","onwaiting",
1175
+ "ontoggle",
1176
+ "onpointerdown", "onpointerup", "onpointermove", "onpointercancel", "onpointerover", "onpointerout",
1177
+ "onpointerenter", "onpointerleave"
1178
+ ]);
1179
+
1180
+ /** @ignore */
1181
+ export const capture_events_set = {};
1182
+ for (const x of events_set) {
1183
+ capture_events_set[x+"capture"] = x;
1184
+ }
1185
+
1186
+ /** @ignore */
1187
+ export const length_css_attributes_set = new Set([
1188
+ "width","height","minWidth","minHeight","maxWidth","maxHeight","top","right","bottom","left","margin",
1189
+ "marginTop","marginRight","marginBottom","marginLeft","padding","paddingTop","paddingRight","paddingBottom",
1190
+ "paddingLeft","borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth",
1191
+ "outlineWidth","fontSize","lineHeight","letterSpacing","wordSpacing","textIndent","borderRadius",
1192
+ "borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius",
1193
+ "columnGap","rowGap","gap"
1194
+ ]);
1195
+
1196
+ /** @ignore */
1197
+ export const boolean_html_attributes_set = new
1198
+ Set(["disabled", "readOnly", "required", "checked", "multiple",
1199
+ "hidden","open","loop","muted","controls","playsInline","allowFullScreen"]);
package/src/errors.jsx CHANGED
@@ -27,8 +27,11 @@
27
27
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
 
29
29
  */
30
+ import Lilact from './lilact.jsx';
30
31
 
31
-
32
+ import { required_scripts } from './run.jsx'
33
+ import { pauseTimers } from './timers.jsx'
34
+ import { css } from "@emotion/css";
32
35
 
33
36
  // Lilact API
34
37
 
@@ -160,7 +163,7 @@ export function traceError(error)
160
163
  let mps;
161
164
 
162
165
  if(loc.url) {
163
- const rm = Lilact.required_scripts[obj.fileName];
166
+ const rm = required_scripts[obj.fileName];
164
167
  mps = rm.mappings;
165
168
 
166
169
  const mloc = mapLocation(mps, obj.lineNumber-1, obj.columnNumber-1);
@@ -186,7 +189,7 @@ export function traceError(error)
186
189
  obj.fileName = blk.path;
187
190
  obj.label = blk.label;
188
191
 
189
- mps = Lilact.required_scripts[blk.path].mappings;
192
+ mps = required_scripts[blk.path].mappings;
190
193
 
191
194
  loc = mapLocation(mps, loc.line-1, loc.col-1);
192
195
 
@@ -227,12 +230,11 @@ export function traceError(error)
227
230
  */
228
231
  export function globalErrorHandler(error)
229
232
  {
230
- Lilact.pauseTimers();
231
233
  if(error.error) error = error.error;
232
234
 
233
- error = Lilact.traceError(error);
235
+ error = traceError(error);
234
236
 
235
- const cls = Lilact.emotion.css(`
237
+ const cls = css(`
236
238
  background: linear-gradient(135deg, #fff2f2d4, #ffffffd4);
237
239
  backdrop-filter: blur(10px);
238
240
  border: 1px solid rgba(255,255,255,.25);
@@ -261,7 +263,7 @@ export function globalErrorHandler(error)
261
263
  <b>${error.fileName?'At '+error.fileName:''}
262
264
  ${Number.isFinite(error.lineNumber)?": Line "+(error.lineNumber+1):""}</b><br><br>
263
265
  <b>${error.name}</b>:&nbsp;<span>${error.message}</span><br><br>
264
- ${Lilact.required_scripts[error.fileName]?'<code><pre></pre><pre><red></red></pre><pre></pre></code>':''}
266
+ ${required_scripts[error.fileName]?'<code><pre></pre><pre><red></red></pre><pre></pre></code>':''}
265
267
  ${error._error.componentStackLog?'<br>Component Stack:<br><code><pre>'+error._error.componentStackLog+'</pre></code>':''}
266
268
  `;
267
269
 
@@ -270,8 +272,8 @@ export function globalErrorHandler(error)
270
272
 
271
273
  const pres = el.querySelectorAll('pre');
272
274
 
273
- if(Lilact.required_scripts[error.fileName]) {
274
- const lines = Lilact.required_scripts[error.fileName].code.split("\n");
275
+ if(required_scripts[error.fileName]) {
276
+ const lines = required_scripts[error.fileName].code.split("\n");
275
277
 
276
278
  if(lines?.[error.lineNumber-1])
277
279
  pres[0].innerText = lines[error.lineNumber-1];
package/src/hooks.jsx CHANGED
@@ -28,7 +28,8 @@
28
28
 
29
29
  */
30
30
 
31
- import { isEmpty } from "./misc.jsx"
31
+ import Lilact from './lilact.jsx';
32
+ import { isEmpty, shallowEqual } from "./misc.jsx"
32
33
 
33
34
  import { CORE, COMPONENT } from "./symbols.jsx"
34
35
 
@@ -93,7 +94,7 @@ export function useCallback(callback, deps=undefined)
93
94
  const hk = useHook();
94
95
 
95
96
  if( !isEmpty(hk) ) {
96
- if(deps!==undefined && hk?.deps!==undefined && Lilact.shallowEqual(deps, hk.deps)) {
97
+ if(deps!==undefined && hk?.deps!==undefined && shallowEqual(deps, hk.deps)) {
97
98
  return hk.callback;
98
99
  }
99
100
  }
@@ -280,7 +281,7 @@ export function useLayoutEffect(effect, deps=undefined)
280
281
  const hk = useHook();
281
282
 
282
283
  if( !isEmpty(hk) ) {
283
- if(deps!==undefined && hk?.deps!==undefined && Lilact.shallowEqual(deps, hk.deps)) return;
284
+ if(deps!==undefined && hk?.deps!==undefined && shallowEqual(deps, hk.deps)) return;
284
285
  }
285
286
 
286
287
  if(hk?.cleanup) {
@@ -308,7 +309,7 @@ export function useEffect(effect, deps=undefined)
308
309
  const hk = useHook();
309
310
 
310
311
  if( !isEmpty(hk) ) {
311
- if(deps!==undefined && hk?.deps!==undefined && Lilact.shallowEqual(deps, hk.deps)) return;
312
+ if(deps!==undefined && hk?.deps!==undefined && shallowEqual(deps, hk.deps)) return;
312
313
  }
313
314
 
314
315
  if(hk?.cleanup) {
@@ -336,7 +337,7 @@ export function useMemo(factory,deps=undefined)
336
337
  const hk = useHook();
337
338
 
338
339
  if( !isEmpty(hk) ) {
339
- if(deps!==undefined && hk?.deps!==undefined && Lilact.shallowEqual(deps, hk.deps)) {
340
+ if(deps!==undefined && hk?.deps!==undefined && shallowEqual(deps, hk.deps)) {
340
341
  return hk.value;
341
342
  }
342
343
  }
@@ -357,7 +358,7 @@ export function useMemo(factory,deps=undefined)
357
358
  export function useActionState(action, initialState)
358
359
  {
359
360
  const hk = useHook();
360
- const [is_pending, tran_start_func] = Lilact.useTransition();
361
+ const [is_pending, tran_start_func] = useTransition();
361
362
 
362
363
  if( isEmpty(hk) ) {
363
364
 
@@ -475,7 +476,7 @@ export function useDeferredValue(value, initialValue)
475
476
  */
476
477
  export function useImperativeHandle(ref, factory, deps=undefined)
477
478
  {
478
- if(deps!==undefined && ref?.deps!==undefined && Lilact.shallowEqual(deps, ref.deps)) return;
479
+ if(deps!==undefined && ref?.deps!==undefined && shallowEqual(deps, ref.deps)) return;
479
480
 
480
481
  ref.deps = deps;
481
482
 
package/src/misc.jsx CHANGED
@@ -349,59 +349,3 @@ export let eval_num = 0;//Math.floor(Math.random()*10000);
349
349
 
350
350
 
351
351
  // todo =improve these stacks
352
- /** @ignore */
353
- export let current_component = [];
354
- /** @ignore */
355
- export let update_set = new Set;
356
- /** @ignore */
357
- export let update_cbs = new Set;
358
- /** @ignore */
359
- export let roots = new Set;
360
- /** @ignore */
361
- export let layout_effects = new Set;
362
-
363
- /** @ignore */
364
- export const special_attributes = new Set([
365
- "classname", "ref", "action", "lilact_jsx_loc", "children", "key",
366
- "defaultvalue", "defaultchecked"
367
- ]);
368
-
369
- /** @ignore */
370
- export const events_set = new Set([
371
- "onafterprint","onbeforeprint","onbeforeunload","onerror","onhashchange","onload","onmessage",
372
- "onoffline","ononline","onpagehide","onpageshow","onpopstate","onresize","onstorage","onunload",
373
- "onblur","onchange","oncontextmenu","onfocus","oninput","oninvalid","onreset","onsearch","onselect",
374
- "onsubmit",
375
- "onkeydown","onkeypress","onkeyup",
376
- "onclick","ondblclick","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel",
377
- "onwheel",
378
- "ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","onscroll",
379
- "oncopy","oncut","onpaste",
380
- "onabort","oncanplay","oncanplaythrough","oncuechange","ondurationchange","onemptied","onended","onerror",
381
- "onloadeddata","onloadedmetadata","onloadstart","onpause","onplay","onplaying","onprogress","onratechange",
382
- "onseeked","onseeking","onstalled","onsuspend","ontimeupdate","onvolumechange","onwaiting",
383
- "ontoggle",
384
- "onpointerdown", "onpointerup", "onpointermove", "onpointercancel", "onpointerover", "onpointerout",
385
- "onpointerenter", "onpointerleave"
386
- ]);
387
-
388
- /** @ignore */
389
- export const capture_events_set = {};
390
- for (const x of events_set) {
391
- capture_events_set[x+"capture"] = x;
392
- }
393
-
394
- /** @ignore */
395
- export const length_css_attributes_set = new Set([
396
- "width","height","minWidth","minHeight","maxWidth","maxHeight","top","right","bottom","left","margin",
397
- "marginTop","marginRight","marginBottom","marginLeft","padding","paddingTop","paddingRight","paddingBottom",
398
- "paddingLeft","borderWidth","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth",
399
- "outlineWidth","fontSize","lineHeight","letterSpacing","wordSpacing","textIndent","borderRadius",
400
- "borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius",
401
- "columnGap","rowGap","gap"
402
- ]);
403
-
404
- /** @ignore */
405
- export const boolean_html_attributes_set = new
406
- Set(["disabled", "readOnly", "required", "checked", "multiple",
407
- "hidden","open","loop","muted","controls","playsInline","allowFullScreen"]);
package/src/router.jsx CHANGED
@@ -36,8 +36,11 @@ import {Children} from "./misc.jsx"
36
36
  const RouterContext = createContext(null);
37
37
  const RouteContext = createContext({ params: {} });
38
38
 
39
- // --- HashRouter (as before) ---
40
- const createURL = (to) => (typeof to === "string" ? to : (to.pathname || "") + (to.search || "") + (to.hash || ""));
39
+ const createURL = (to) =>
40
+ typeof to === "string" ? to : (to.pathname || "") + (to.search || "") + (to.hash || "");
41
+
42
+ const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
43
+
41
44
 
42
45
  /**
43
46
  * Hash-based router component that syncs navigation state with the URL hash (#...).
@@ -47,54 +50,90 @@ const createURL = (to) => (typeof to === "string" ? to : (to.pathname || "") + (
47
50
  * @param [props.basename=""] - Optional base path prefix applied to all route paths.
48
51
  */
49
52
  export function HashRouter({ children, basename = "" }) {
50
- const readLocation = () => {
51
- const raw = window.location.hash || "#/";
52
- const full = raw.slice(1);
53
- const withoutBase = full.replace(new RegExp(`^${basename}`), "") || "/";
54
- const [pathAndSearch, hashPart] = withoutBase.split("#");
55
- const [path, search = ""] = pathAndSearch.split("?");
56
- return {
57
- pathname: path || "/",
58
- search: search ? "?" + search : "",
59
- hash: hashPart ? "#" + hashPart : "",
60
- state: history.state?.__state,
61
- };
62
- };
63
-
64
- const [location, setLocation] = useState(readLocation);
65
- useEffect(() => {
66
- const onChange = () => setLocation(readLocation());
67
-
68
- window.addEventListener("hashchange", onChange);
69
- window.addEventListener("popstate", onChange);
70
-
71
- // initialize once in case hash/state are already set
72
- onChange();
73
-
74
- return () => {
75
- window.removeEventListener("hashchange", onChange);
76
- window.removeEventListener("popstate", onChange);
77
- };
78
- }, [basename]);
79
-
80
- const navigate = useCallback((to, { replace = false, state } = {}) => {
81
- if (typeof to === "number") {
82
- history.go(to);
83
- return;
84
- }
85
-
86
- const url = createURL(to);
87
- const href = "#" + (basename + url);
88
-
89
- if (replace) history.replaceState({ __state: state }, "", href);
90
- else history.pushState({ __state: state }, "", href);
91
-
92
- setLocation(readLocation());
93
- }, [basename]);
94
-
95
- return <RouterContext.Provider value={{ location, navigate, basename }}>{children}</RouterContext.Provider>;
53
+ const readLocation = () => {
54
+ const raw = window.location.hash || "#/";
55
+ const full = raw.slice(1);
56
+
57
+ const baseRe = new RegExp("^" + escapeRegExp(basename));
58
+ const withoutBase = full.replace(baseRe, "") || "/";
59
+
60
+ const [pathAndSearch, hashPart] = withoutBase.split("#");
61
+ const [path, search = ""] = pathAndSearch.split("?");
62
+
63
+ return {
64
+ pathname: path || "/",
65
+ search: search ? "?" + search : "",
66
+ hash: hashPart ? "#" + hashPart : "",
67
+ state: history.state?.__state,
68
+ };
69
+ };
70
+
71
+ const [location, setLocation] = useState(readLocation);
72
+
73
+ useEffect(() => {
74
+ const onChange = () => setLocation(readLocation());
75
+
76
+ window.addEventListener("hashchange", onChange);
77
+ window.addEventListener("popstate", onChange);
78
+
79
+ // initialize once
80
+ onChange();
81
+
82
+ return () => {
83
+ window.removeEventListener("hashchange", onChange);
84
+ window.removeEventListener("popstate", onChange);
85
+ };
86
+ }, [basename]);
87
+
88
+ const navigate = useCallback((to, { replace = false, state } = {}) => {
89
+ if (typeof to === "number") {
90
+ // Wait for the real popstate (don’t rely on synchronous timing)
91
+ return new Promise((resolve) => {
92
+ let done = false;
93
+
94
+ const cleanup = (fn) => {
95
+ if (done) return;
96
+ done = true;
97
+ window.removeEventListener("popstate", onPop);
98
+ window.removeEventListener("hashchange", onHash);
99
+ fn?.();
100
+ resolve();
101
+ };
102
+
103
+ const onPop = () => cleanup(() => setLocation(readLocation()));
104
+ const onHash = () => cleanup(() => setLocation(readLocation()));
105
+
106
+ window.addEventListener("popstate", onPop, { once: true });
107
+ window.addEventListener("hashchange", onHash, { once: true });
108
+
109
+ // Fallback: in case popstate/hashchange timing is weird in a given browser
110
+ setTimeout(() => cleanup(() => setLocation(readLocation())), 0);
111
+
112
+ history.go(to);
113
+ });
114
+ }
115
+
116
+ const url = createURL(to);
117
+ const href = "#" + (basename + url);
118
+
119
+ if (replace) {
120
+ history.replaceState({ __state: state }, "", href);
121
+ } else {
122
+ history.pushState({ __state: state }, "", href);
123
+ }
124
+
125
+ // Update immediately for the initiating navigation.
126
+ setLocation(readLocation());
127
+ }, [basename]);
128
+
129
+ return (
130
+ <RouterContext.Provider value={{ location, navigate, basename }}>
131
+ {children}
132
+ </RouterContext.Provider>
133
+ );
96
134
  }
97
135
 
136
+
98
137
  /**
99
138
  * Hook that returns the current location object (path, search, hash, and navigation state).
100
139
  *
package/src/run.jsx CHANGED
@@ -70,7 +70,7 @@ export function run(jsx, path=`InlineJSX-${++Lilact.eval_num}`, is_inline=true)
70
70
  appendSourcemap: false,
71
71
  blocks_info: Lilact.blocks_info,
72
72
 
73
- injectTraceLabels: true,
73
+ injectTraceLabels: false,
74
74
  } );
75
75
  }
76
76
  catch(e) {
package/src/symbols.jsx CHANGED
@@ -54,6 +54,7 @@ export const [
54
54
  Symbol.for('LILACT:IDX'),
55
55
  Symbol.for('LILACT:CHILD_CLASS_ADDENDUM'),
56
56
  Symbol.for('LILACT:MEMOIZED'),
57
+ Symbol.for('LILACT:LAZY'),
57
58
 
58
59
  Symbol.for('LILACT:TIMERS:DUE'),
59
60
  Symbol.for('LILACT:TIMERS:REPEAT'),
@@ -27,6 +27,8 @@
27
27
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
 
29
29
  */
30
+ import Lilact from './lilact.jsx';
31
+
30
32
  import { setTimeout, clearTimeout } from "./timers.jsx"
31
33
  import { Children } from "./misc.jsx"
32
34
  import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "./hooks.jsx"