@matthesketh/utopia-router 0.3.0 → 0.4.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/index.cjs +112 -37
- package/dist/index.d.cts +28 -3
- package/dist/index.d.ts +28 -3
- package/dist/index.js +106 -36
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -31,9 +31,14 @@ __export(index_exports, {
|
|
|
31
31
|
destroy: () => destroy,
|
|
32
32
|
filePathToRoute: () => filePathToRoute,
|
|
33
33
|
forward: () => forward,
|
|
34
|
+
getQueryParam: () => getQueryParam,
|
|
35
|
+
getRouteParam: () => getRouteParam,
|
|
34
36
|
isNavigating: () => isNavigating,
|
|
35
37
|
matchRoute: () => matchRoute,
|
|
36
|
-
navigate: () => navigate
|
|
38
|
+
navigate: () => navigate,
|
|
39
|
+
queryParams: () => queryParams,
|
|
40
|
+
setQueryParam: () => setQueryParam,
|
|
41
|
+
setQueryParams: () => setQueryParams
|
|
37
42
|
});
|
|
38
43
|
module.exports = __toCommonJS(index_exports);
|
|
39
44
|
|
|
@@ -401,33 +406,41 @@ function createRouterView() {
|
|
|
401
406
|
container.setAttribute("data-utopia-router-view", "");
|
|
402
407
|
let currentCleanup = null;
|
|
403
408
|
let currentMatch = null;
|
|
409
|
+
let loadId = 0;
|
|
404
410
|
(0, import_utopia_core2.effect)(() => {
|
|
405
411
|
const match = currentRoute();
|
|
406
412
|
if (match === currentMatch) {
|
|
407
413
|
return;
|
|
408
414
|
}
|
|
409
415
|
currentMatch = match;
|
|
410
|
-
|
|
411
|
-
currentCleanup();
|
|
412
|
-
currentCleanup = null;
|
|
413
|
-
}
|
|
414
|
-
while (container.firstChild) {
|
|
415
|
-
container.removeChild(container.firstChild);
|
|
416
|
-
}
|
|
416
|
+
const thisLoadId = ++loadId;
|
|
417
417
|
if (!match) {
|
|
418
|
+
if (currentCleanup) {
|
|
419
|
+
currentCleanup();
|
|
420
|
+
currentCleanup = null;
|
|
421
|
+
}
|
|
422
|
+
clearContainer(container);
|
|
418
423
|
const notFound = document.createElement("div");
|
|
419
424
|
notFound.setAttribute("data-utopia-not-found", "");
|
|
420
425
|
notFound.textContent = "Page not found";
|
|
421
426
|
container.appendChild(notFound);
|
|
422
427
|
return;
|
|
423
428
|
}
|
|
424
|
-
loadRouteComponent(match
|
|
425
|
-
|
|
429
|
+
loadRouteComponent(match).then((result) => {
|
|
430
|
+
if (thisLoadId !== loadId) return;
|
|
431
|
+
if (!result) return;
|
|
432
|
+
if (currentCleanup) {
|
|
433
|
+
currentCleanup();
|
|
434
|
+
currentCleanup = null;
|
|
435
|
+
}
|
|
436
|
+
clearContainer(container);
|
|
437
|
+
container.appendChild(result.node);
|
|
438
|
+
currentCleanup = result.cleanup;
|
|
426
439
|
});
|
|
427
440
|
});
|
|
428
441
|
return container;
|
|
429
442
|
}
|
|
430
|
-
async function loadRouteComponent(match
|
|
443
|
+
async function loadRouteComponent(match) {
|
|
431
444
|
try {
|
|
432
445
|
const promises = [match.route.component()];
|
|
433
446
|
if (match.route.layout) {
|
|
@@ -441,26 +454,26 @@ async function loadRouteComponent(match, container) {
|
|
|
441
454
|
}
|
|
442
455
|
const PageComponent = pageModule.default ?? pageModule;
|
|
443
456
|
const LayoutComponent = layoutModule ? layoutModule.default ?? layoutModule : null;
|
|
444
|
-
while (container.firstChild) {
|
|
445
|
-
container.removeChild(container.firstChild);
|
|
446
|
-
}
|
|
447
457
|
const pageNode = renderComponent(PageComponent, {
|
|
448
458
|
params: match.params,
|
|
449
459
|
url: match.url
|
|
450
460
|
});
|
|
461
|
+
let node;
|
|
451
462
|
if (LayoutComponent) {
|
|
452
|
-
|
|
463
|
+
node = renderComponent(LayoutComponent, {
|
|
453
464
|
params: match.params,
|
|
454
465
|
url: match.url,
|
|
455
466
|
children: pageNode
|
|
456
467
|
});
|
|
457
|
-
container.appendChild(layoutNode);
|
|
458
468
|
} else {
|
|
459
|
-
|
|
469
|
+
node = pageNode;
|
|
460
470
|
}
|
|
461
|
-
return
|
|
462
|
-
|
|
463
|
-
|
|
471
|
+
return {
|
|
472
|
+
node,
|
|
473
|
+
cleanup: () => {
|
|
474
|
+
if (node.parentNode) {
|
|
475
|
+
node.parentNode.removeChild(node);
|
|
476
|
+
}
|
|
464
477
|
}
|
|
465
478
|
};
|
|
466
479
|
} catch (err) {
|
|
@@ -468,26 +481,33 @@ async function loadRouteComponent(match, container) {
|
|
|
468
481
|
try {
|
|
469
482
|
const errorModule = await match.route.error();
|
|
470
483
|
const ErrorComponent = errorModule.default ?? errorModule;
|
|
471
|
-
while (container.firstChild) {
|
|
472
|
-
container.removeChild(container.firstChild);
|
|
473
|
-
}
|
|
474
484
|
const errorNode = renderComponent(ErrorComponent, {
|
|
475
485
|
error: err,
|
|
476
486
|
params: match.params,
|
|
477
487
|
url: match.url
|
|
478
488
|
});
|
|
479
|
-
|
|
489
|
+
return {
|
|
490
|
+
node: errorNode,
|
|
491
|
+
cleanup: () => {
|
|
492
|
+
if (errorNode.parentNode) {
|
|
493
|
+
errorNode.parentNode.removeChild(errorNode);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
480
497
|
} catch {
|
|
481
|
-
|
|
498
|
+
return {
|
|
499
|
+
node: createFallbackErrorNode(err),
|
|
500
|
+
cleanup: () => {
|
|
501
|
+
}
|
|
502
|
+
};
|
|
482
503
|
}
|
|
483
504
|
} else {
|
|
484
|
-
|
|
505
|
+
return {
|
|
506
|
+
node: createFallbackErrorNode(err),
|
|
507
|
+
cleanup: () => {
|
|
508
|
+
}
|
|
509
|
+
};
|
|
485
510
|
}
|
|
486
|
-
return () => {
|
|
487
|
-
while (container.firstChild) {
|
|
488
|
-
container.removeChild(container.firstChild);
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
511
|
}
|
|
492
512
|
}
|
|
493
513
|
function renderComponent(component, props) {
|
|
@@ -510,10 +530,7 @@ function renderComponent(component, props) {
|
|
|
510
530
|
div.textContent = "[Component render error]";
|
|
511
531
|
return div;
|
|
512
532
|
}
|
|
513
|
-
function
|
|
514
|
-
while (container.firstChild) {
|
|
515
|
-
container.removeChild(container.firstChild);
|
|
516
|
-
}
|
|
533
|
+
function createFallbackErrorNode(error) {
|
|
517
534
|
const errorDiv = document.createElement("div");
|
|
518
535
|
errorDiv.setAttribute("data-utopia-error", "");
|
|
519
536
|
errorDiv.style.cssText = "padding:2rem;color:#dc2626;font-family:monospace;";
|
|
@@ -523,7 +540,7 @@ function renderFallbackError(container, error) {
|
|
|
523
540
|
error instanceof Error ? error.message : String(error)
|
|
524
541
|
)}</pre>
|
|
525
542
|
`;
|
|
526
|
-
|
|
543
|
+
return errorDiv;
|
|
527
544
|
}
|
|
528
545
|
function createLink(props) {
|
|
529
546
|
const anchor = document.createElement("a");
|
|
@@ -550,9 +567,62 @@ function createLink(props) {
|
|
|
550
567
|
}
|
|
551
568
|
return anchor;
|
|
552
569
|
}
|
|
570
|
+
function clearContainer(container) {
|
|
571
|
+
while (container.firstChild) {
|
|
572
|
+
container.removeChild(container.firstChild);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
553
575
|
function escapeHtml(str) {
|
|
554
576
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
555
577
|
}
|
|
578
|
+
|
|
579
|
+
// src/query.ts
|
|
580
|
+
var import_utopia_core3 = require("@matthesketh/utopia-core");
|
|
581
|
+
var queryParams = (0, import_utopia_core3.computed)(() => {
|
|
582
|
+
const match = currentRoute();
|
|
583
|
+
if (!match) return {};
|
|
584
|
+
const result = {};
|
|
585
|
+
match.url.searchParams.forEach((value, key) => {
|
|
586
|
+
result[key] = value;
|
|
587
|
+
});
|
|
588
|
+
return result;
|
|
589
|
+
});
|
|
590
|
+
function getQueryParam(name) {
|
|
591
|
+
return (0, import_utopia_core3.computed)(() => {
|
|
592
|
+
const match = currentRoute();
|
|
593
|
+
if (!match) return null;
|
|
594
|
+
return match.url.searchParams.get(name);
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
function setQueryParam(name, value) {
|
|
598
|
+
if (typeof window === "undefined") return;
|
|
599
|
+
const url = new URL(window.location.href);
|
|
600
|
+
if (value === null) {
|
|
601
|
+
url.searchParams.delete(name);
|
|
602
|
+
} else {
|
|
603
|
+
url.searchParams.set(name, value);
|
|
604
|
+
}
|
|
605
|
+
navigate(url.pathname + url.search + url.hash, { replace: true });
|
|
606
|
+
}
|
|
607
|
+
function setQueryParams(params) {
|
|
608
|
+
if (typeof window === "undefined") return;
|
|
609
|
+
const url = new URL(window.location.href);
|
|
610
|
+
for (const [key, value] of Object.entries(params)) {
|
|
611
|
+
if (value === null) {
|
|
612
|
+
url.searchParams.delete(key);
|
|
613
|
+
} else {
|
|
614
|
+
url.searchParams.set(key, value);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
navigate(url.pathname + url.search + url.hash, { replace: true });
|
|
618
|
+
}
|
|
619
|
+
function getRouteParam(name) {
|
|
620
|
+
return (0, import_utopia_core3.computed)(() => {
|
|
621
|
+
const match = currentRoute();
|
|
622
|
+
if (!match) return null;
|
|
623
|
+
return match.params[name] ?? null;
|
|
624
|
+
});
|
|
625
|
+
}
|
|
556
626
|
// Annotate the CommonJS export names for ESM import in node:
|
|
557
627
|
0 && (module.exports = {
|
|
558
628
|
back,
|
|
@@ -566,7 +636,12 @@ function escapeHtml(str) {
|
|
|
566
636
|
destroy,
|
|
567
637
|
filePathToRoute,
|
|
568
638
|
forward,
|
|
639
|
+
getQueryParam,
|
|
640
|
+
getRouteParam,
|
|
569
641
|
isNavigating,
|
|
570
642
|
matchRoute,
|
|
571
|
-
navigate
|
|
643
|
+
navigate,
|
|
644
|
+
queryParams,
|
|
645
|
+
setQueryParam,
|
|
646
|
+
setQueryParams
|
|
572
647
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -171,8 +171,8 @@ declare function destroy(): void;
|
|
|
171
171
|
* Creates a DOM node that renders the current route's component.
|
|
172
172
|
*
|
|
173
173
|
* When the route changes:
|
|
174
|
-
* 1. The
|
|
175
|
-
* 2.
|
|
174
|
+
* 1. The new component is lazily loaded (old content stays visible)
|
|
175
|
+
* 2. Once loaded, the old component is swapped out atomically
|
|
176
176
|
* 3. If the route has a layout, the page is wrapped in the layout
|
|
177
177
|
* 4. If loading fails and an error component exists, it is shown instead
|
|
178
178
|
*
|
|
@@ -214,4 +214,29 @@ declare function createLink(props: {
|
|
|
214
214
|
activeClass?: string;
|
|
215
215
|
}): HTMLAnchorElement;
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Reactive computed signal returning all current query parameters as a plain object.
|
|
219
|
+
*/
|
|
220
|
+
declare const queryParams: _matthesketh_utopia_core.ReadonlySignal<Record<string, string>>;
|
|
221
|
+
/**
|
|
222
|
+
* Returns a computed signal for a specific query parameter.
|
|
223
|
+
* Returns null if the parameter is not present.
|
|
224
|
+
*/
|
|
225
|
+
declare function getQueryParam(name: string): _matthesketh_utopia_core.ReadonlySignal<string | null>;
|
|
226
|
+
/**
|
|
227
|
+
* Update a single query parameter and navigate (replace mode).
|
|
228
|
+
* Pass null to remove the parameter.
|
|
229
|
+
*/
|
|
230
|
+
declare function setQueryParam(name: string, value: string | null): void;
|
|
231
|
+
/**
|
|
232
|
+
* Update multiple query parameters in a single navigation.
|
|
233
|
+
* Pass null for a value to remove that parameter.
|
|
234
|
+
*/
|
|
235
|
+
declare function setQueryParams(params: Record<string, string | null>): void;
|
|
236
|
+
/**
|
|
237
|
+
* Returns a computed signal for a specific route path parameter.
|
|
238
|
+
* Returns null if the parameter is not present.
|
|
239
|
+
*/
|
|
240
|
+
declare function getRouteParam(name: string): _matthesketh_utopia_core.ReadonlySignal<string | null>;
|
|
241
|
+
|
|
242
|
+
export { type BeforeNavigateHook, type Route, type RouteMatch, type RouterState, back, beforeNavigate, buildRouteTable, compilePattern, createLink, createRouter, createRouterView, currentRoute, destroy, filePathToRoute, forward, getQueryParam, getRouteParam, isNavigating, matchRoute, navigate, queryParams, setQueryParam, setQueryParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -171,8 +171,8 @@ declare function destroy(): void;
|
|
|
171
171
|
* Creates a DOM node that renders the current route's component.
|
|
172
172
|
*
|
|
173
173
|
* When the route changes:
|
|
174
|
-
* 1. The
|
|
175
|
-
* 2.
|
|
174
|
+
* 1. The new component is lazily loaded (old content stays visible)
|
|
175
|
+
* 2. Once loaded, the old component is swapped out atomically
|
|
176
176
|
* 3. If the route has a layout, the page is wrapped in the layout
|
|
177
177
|
* 4. If loading fails and an error component exists, it is shown instead
|
|
178
178
|
*
|
|
@@ -214,4 +214,29 @@ declare function createLink(props: {
|
|
|
214
214
|
activeClass?: string;
|
|
215
215
|
}): HTMLAnchorElement;
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Reactive computed signal returning all current query parameters as a plain object.
|
|
219
|
+
*/
|
|
220
|
+
declare const queryParams: _matthesketh_utopia_core.ReadonlySignal<Record<string, string>>;
|
|
221
|
+
/**
|
|
222
|
+
* Returns a computed signal for a specific query parameter.
|
|
223
|
+
* Returns null if the parameter is not present.
|
|
224
|
+
*/
|
|
225
|
+
declare function getQueryParam(name: string): _matthesketh_utopia_core.ReadonlySignal<string | null>;
|
|
226
|
+
/**
|
|
227
|
+
* Update a single query parameter and navigate (replace mode).
|
|
228
|
+
* Pass null to remove the parameter.
|
|
229
|
+
*/
|
|
230
|
+
declare function setQueryParam(name: string, value: string | null): void;
|
|
231
|
+
/**
|
|
232
|
+
* Update multiple query parameters in a single navigation.
|
|
233
|
+
* Pass null for a value to remove that parameter.
|
|
234
|
+
*/
|
|
235
|
+
declare function setQueryParams(params: Record<string, string | null>): void;
|
|
236
|
+
/**
|
|
237
|
+
* Returns a computed signal for a specific route path parameter.
|
|
238
|
+
* Returns null if the parameter is not present.
|
|
239
|
+
*/
|
|
240
|
+
declare function getRouteParam(name: string): _matthesketh_utopia_core.ReadonlySignal<string | null>;
|
|
241
|
+
|
|
242
|
+
export { type BeforeNavigateHook, type Route, type RouteMatch, type RouterState, back, beforeNavigate, buildRouteTable, compilePattern, createLink, createRouter, createRouterView, currentRoute, destroy, filePathToRoute, forward, getQueryParam, getRouteParam, isNavigating, matchRoute, navigate, queryParams, setQueryParam, setQueryParams };
|
package/dist/index.js
CHANGED
|
@@ -362,33 +362,41 @@ function createRouterView() {
|
|
|
362
362
|
container.setAttribute("data-utopia-router-view", "");
|
|
363
363
|
let currentCleanup = null;
|
|
364
364
|
let currentMatch = null;
|
|
365
|
+
let loadId = 0;
|
|
365
366
|
effect(() => {
|
|
366
367
|
const match = currentRoute();
|
|
367
368
|
if (match === currentMatch) {
|
|
368
369
|
return;
|
|
369
370
|
}
|
|
370
371
|
currentMatch = match;
|
|
371
|
-
|
|
372
|
-
currentCleanup();
|
|
373
|
-
currentCleanup = null;
|
|
374
|
-
}
|
|
375
|
-
while (container.firstChild) {
|
|
376
|
-
container.removeChild(container.firstChild);
|
|
377
|
-
}
|
|
372
|
+
const thisLoadId = ++loadId;
|
|
378
373
|
if (!match) {
|
|
374
|
+
if (currentCleanup) {
|
|
375
|
+
currentCleanup();
|
|
376
|
+
currentCleanup = null;
|
|
377
|
+
}
|
|
378
|
+
clearContainer(container);
|
|
379
379
|
const notFound = document.createElement("div");
|
|
380
380
|
notFound.setAttribute("data-utopia-not-found", "");
|
|
381
381
|
notFound.textContent = "Page not found";
|
|
382
382
|
container.appendChild(notFound);
|
|
383
383
|
return;
|
|
384
384
|
}
|
|
385
|
-
loadRouteComponent(match
|
|
386
|
-
|
|
385
|
+
loadRouteComponent(match).then((result) => {
|
|
386
|
+
if (thisLoadId !== loadId) return;
|
|
387
|
+
if (!result) return;
|
|
388
|
+
if (currentCleanup) {
|
|
389
|
+
currentCleanup();
|
|
390
|
+
currentCleanup = null;
|
|
391
|
+
}
|
|
392
|
+
clearContainer(container);
|
|
393
|
+
container.appendChild(result.node);
|
|
394
|
+
currentCleanup = result.cleanup;
|
|
387
395
|
});
|
|
388
396
|
});
|
|
389
397
|
return container;
|
|
390
398
|
}
|
|
391
|
-
async function loadRouteComponent(match
|
|
399
|
+
async function loadRouteComponent(match) {
|
|
392
400
|
try {
|
|
393
401
|
const promises = [match.route.component()];
|
|
394
402
|
if (match.route.layout) {
|
|
@@ -402,26 +410,26 @@ async function loadRouteComponent(match, container) {
|
|
|
402
410
|
}
|
|
403
411
|
const PageComponent = pageModule.default ?? pageModule;
|
|
404
412
|
const LayoutComponent = layoutModule ? layoutModule.default ?? layoutModule : null;
|
|
405
|
-
while (container.firstChild) {
|
|
406
|
-
container.removeChild(container.firstChild);
|
|
407
|
-
}
|
|
408
413
|
const pageNode = renderComponent(PageComponent, {
|
|
409
414
|
params: match.params,
|
|
410
415
|
url: match.url
|
|
411
416
|
});
|
|
417
|
+
let node;
|
|
412
418
|
if (LayoutComponent) {
|
|
413
|
-
|
|
419
|
+
node = renderComponent(LayoutComponent, {
|
|
414
420
|
params: match.params,
|
|
415
421
|
url: match.url,
|
|
416
422
|
children: pageNode
|
|
417
423
|
});
|
|
418
|
-
container.appendChild(layoutNode);
|
|
419
424
|
} else {
|
|
420
|
-
|
|
425
|
+
node = pageNode;
|
|
421
426
|
}
|
|
422
|
-
return
|
|
423
|
-
|
|
424
|
-
|
|
427
|
+
return {
|
|
428
|
+
node,
|
|
429
|
+
cleanup: () => {
|
|
430
|
+
if (node.parentNode) {
|
|
431
|
+
node.parentNode.removeChild(node);
|
|
432
|
+
}
|
|
425
433
|
}
|
|
426
434
|
};
|
|
427
435
|
} catch (err) {
|
|
@@ -429,26 +437,33 @@ async function loadRouteComponent(match, container) {
|
|
|
429
437
|
try {
|
|
430
438
|
const errorModule = await match.route.error();
|
|
431
439
|
const ErrorComponent = errorModule.default ?? errorModule;
|
|
432
|
-
while (container.firstChild) {
|
|
433
|
-
container.removeChild(container.firstChild);
|
|
434
|
-
}
|
|
435
440
|
const errorNode = renderComponent(ErrorComponent, {
|
|
436
441
|
error: err,
|
|
437
442
|
params: match.params,
|
|
438
443
|
url: match.url
|
|
439
444
|
});
|
|
440
|
-
|
|
445
|
+
return {
|
|
446
|
+
node: errorNode,
|
|
447
|
+
cleanup: () => {
|
|
448
|
+
if (errorNode.parentNode) {
|
|
449
|
+
errorNode.parentNode.removeChild(errorNode);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
};
|
|
441
453
|
} catch {
|
|
442
|
-
|
|
454
|
+
return {
|
|
455
|
+
node: createFallbackErrorNode(err),
|
|
456
|
+
cleanup: () => {
|
|
457
|
+
}
|
|
458
|
+
};
|
|
443
459
|
}
|
|
444
460
|
} else {
|
|
445
|
-
|
|
461
|
+
return {
|
|
462
|
+
node: createFallbackErrorNode(err),
|
|
463
|
+
cleanup: () => {
|
|
464
|
+
}
|
|
465
|
+
};
|
|
446
466
|
}
|
|
447
|
-
return () => {
|
|
448
|
-
while (container.firstChild) {
|
|
449
|
-
container.removeChild(container.firstChild);
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
467
|
}
|
|
453
468
|
}
|
|
454
469
|
function renderComponent(component, props) {
|
|
@@ -471,10 +486,7 @@ function renderComponent(component, props) {
|
|
|
471
486
|
div.textContent = "[Component render error]";
|
|
472
487
|
return div;
|
|
473
488
|
}
|
|
474
|
-
function
|
|
475
|
-
while (container.firstChild) {
|
|
476
|
-
container.removeChild(container.firstChild);
|
|
477
|
-
}
|
|
489
|
+
function createFallbackErrorNode(error) {
|
|
478
490
|
const errorDiv = document.createElement("div");
|
|
479
491
|
errorDiv.setAttribute("data-utopia-error", "");
|
|
480
492
|
errorDiv.style.cssText = "padding:2rem;color:#dc2626;font-family:monospace;";
|
|
@@ -484,7 +496,7 @@ function renderFallbackError(container, error) {
|
|
|
484
496
|
error instanceof Error ? error.message : String(error)
|
|
485
497
|
)}</pre>
|
|
486
498
|
`;
|
|
487
|
-
|
|
499
|
+
return errorDiv;
|
|
488
500
|
}
|
|
489
501
|
function createLink(props) {
|
|
490
502
|
const anchor = document.createElement("a");
|
|
@@ -511,9 +523,62 @@ function createLink(props) {
|
|
|
511
523
|
}
|
|
512
524
|
return anchor;
|
|
513
525
|
}
|
|
526
|
+
function clearContainer(container) {
|
|
527
|
+
while (container.firstChild) {
|
|
528
|
+
container.removeChild(container.firstChild);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
514
531
|
function escapeHtml(str) {
|
|
515
532
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
516
533
|
}
|
|
534
|
+
|
|
535
|
+
// src/query.ts
|
|
536
|
+
import { computed } from "@matthesketh/utopia-core";
|
|
537
|
+
var queryParams = computed(() => {
|
|
538
|
+
const match = currentRoute();
|
|
539
|
+
if (!match) return {};
|
|
540
|
+
const result = {};
|
|
541
|
+
match.url.searchParams.forEach((value, key) => {
|
|
542
|
+
result[key] = value;
|
|
543
|
+
});
|
|
544
|
+
return result;
|
|
545
|
+
});
|
|
546
|
+
function getQueryParam(name) {
|
|
547
|
+
return computed(() => {
|
|
548
|
+
const match = currentRoute();
|
|
549
|
+
if (!match) return null;
|
|
550
|
+
return match.url.searchParams.get(name);
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
function setQueryParam(name, value) {
|
|
554
|
+
if (typeof window === "undefined") return;
|
|
555
|
+
const url = new URL(window.location.href);
|
|
556
|
+
if (value === null) {
|
|
557
|
+
url.searchParams.delete(name);
|
|
558
|
+
} else {
|
|
559
|
+
url.searchParams.set(name, value);
|
|
560
|
+
}
|
|
561
|
+
navigate(url.pathname + url.search + url.hash, { replace: true });
|
|
562
|
+
}
|
|
563
|
+
function setQueryParams(params) {
|
|
564
|
+
if (typeof window === "undefined") return;
|
|
565
|
+
const url = new URL(window.location.href);
|
|
566
|
+
for (const [key, value] of Object.entries(params)) {
|
|
567
|
+
if (value === null) {
|
|
568
|
+
url.searchParams.delete(key);
|
|
569
|
+
} else {
|
|
570
|
+
url.searchParams.set(key, value);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
navigate(url.pathname + url.search + url.hash, { replace: true });
|
|
574
|
+
}
|
|
575
|
+
function getRouteParam(name) {
|
|
576
|
+
return computed(() => {
|
|
577
|
+
const match = currentRoute();
|
|
578
|
+
if (!match) return null;
|
|
579
|
+
return match.params[name] ?? null;
|
|
580
|
+
});
|
|
581
|
+
}
|
|
517
582
|
export {
|
|
518
583
|
back,
|
|
519
584
|
beforeNavigate,
|
|
@@ -526,7 +591,12 @@ export {
|
|
|
526
591
|
destroy,
|
|
527
592
|
filePathToRoute,
|
|
528
593
|
forward,
|
|
594
|
+
getQueryParam,
|
|
595
|
+
getRouteParam,
|
|
529
596
|
isNavigating,
|
|
530
597
|
matchRoute,
|
|
531
|
-
navigate
|
|
598
|
+
navigate,
|
|
599
|
+
queryParams,
|
|
600
|
+
setQueryParam,
|
|
601
|
+
setQueryParams
|
|
532
602
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthesketh/utopia-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "File-based routing for UtopiaJS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"dist"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@matthesketh/utopia-core": "0.
|
|
44
|
-
"@matthesketh/utopia-runtime": "0.
|
|
43
|
+
"@matthesketh/utopia-core": "0.4.0",
|
|
44
|
+
"@matthesketh/utopia-runtime": "0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|