@lexriver/dome 2.0.3 → 2.0.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/README.md +13 -18
- package/out/index.cjs +199 -0
- package/out/index.mjs +184 -0
- package/out/src/DomeRouter.d.mts +12 -2
- package/out/src/DomeRouter.d.ts +12 -2
- package/out/src/DomeRouter.mjs +11 -2
- package/out/src/index.d.mts +5 -1
- package/out/src/index.d.ts +5 -1
- package/out/src/index.mjs +5 -1
- package/package.json +3 -2
- package/out/src/DomeRouter.console-test.d.mts +0 -1
- package/out/src/DomeRouter.console-test.d.ts +0 -1
- package/out/src/DomeRouter.console-test.mjs +0 -44
- package/out/src/concurrent-updates.test.d.mts +0 -1
- package/out/src/concurrent-updates.test.d.ts +0 -1
- package/out/src/concurrent-updates.test.mjs +0 -203
- package/out/src/temp-test.d.mts +0 -1
- package/out/src/temp-test.d.ts +0 -1
- package/out/src/temp-test.mjs +0 -20
package/README.md
CHANGED
|
@@ -1507,9 +1507,9 @@ Parameters:
|
|
|
1507
1507
|
Where `RouteAction` type is:
|
|
1508
1508
|
```typescript
|
|
1509
1509
|
export type RouteAction = (
|
|
1510
|
-
params:{[key:string]:string
|
|
1511
|
-
url:string, //
|
|
1512
|
-
scrollToPreviousPositionAsync:()=>Promise<void>, // this
|
|
1510
|
+
params:{[key:string]:string}, // unannotated parameters from the URL path
|
|
1511
|
+
url:string, // URL path without query string
|
|
1512
|
+
scrollToPreviousPositionAsync:()=>Promise<void>, // call this to scroll to the previous page position
|
|
1513
1513
|
query:{[key:string]:string} // query parameters from window.location.search
|
|
1514
1514
|
) => void|Promise<void>
|
|
1515
1515
|
```
|
|
@@ -1529,28 +1529,23 @@ DomeRouter.onRoute('/product/:productId', true, (params, url) => {
|
|
|
1529
1529
|
})
|
|
1530
1530
|
```
|
|
1531
1531
|
|
|
1532
|
-
|
|
1532
|
+
For numeric parameters, use `onTypedRoute`. This opt-in API types route values as `string | number`, because TypeScript 3.9 cannot infer parameter types from route-string annotations.
|
|
1533
|
+
|
|
1533
1534
|
```typescript
|
|
1534
|
-
DomeRouter.
|
|
1535
|
-
//
|
|
1536
|
-
|
|
1537
|
-
|
|
1535
|
+
DomeRouter.onTypedRoute('/product/:productId<number>', true, (params, url) => {
|
|
1536
|
+
// For '/product/456', productId is 456 at runtime.
|
|
1537
|
+
if(typeof params.productId === 'number') {
|
|
1538
|
+
console.log('productId=', params.productId)
|
|
1539
|
+
}
|
|
1538
1540
|
})
|
|
1539
1541
|
```
|
|
1540
1542
|
|
|
1541
|
-
|
|
1543
|
+
The supported numeric annotations are:
|
|
1542
1544
|
* `int` - uses `parseInt(p)` internally
|
|
1543
1545
|
* `float` - uses `parseFloat(p)` internally
|
|
1544
|
-
* `number` - uses `Number(p)`
|
|
1545
|
-
But there is no validation for paramters, so the result of specified function will be returned.
|
|
1546
|
+
* `number` - uses `Number(p)` internally
|
|
1546
1547
|
|
|
1547
|
-
|
|
1548
|
-
DomeRouter.onRoute('/product/:productId<int>', true, (params, url) => {
|
|
1549
|
-
// for example for '/product/456' the output will be
|
|
1550
|
-
// 'productId=', 456, number
|
|
1551
|
-
console.log('productId=', params.productId, typeof params.productId)
|
|
1552
|
-
})
|
|
1553
|
-
```
|
|
1548
|
+
There is no numeric validation, so the result of the corresponding conversion function is returned. Existing unannotated routes should continue using `onRoute`; their parameters remain strings without casts.
|
|
1554
1549
|
|
|
1555
1550
|
<br/>
|
|
1556
1551
|
|
package/out/index.cjs
CHANGED
|
@@ -26,16 +26,33 @@ __export(src_exports, {
|
|
|
26
26
|
AnimatedArray: () => AnimatedArray,
|
|
27
27
|
AnimatedTable: () => AnimatedTable,
|
|
28
28
|
AnimatedText: () => AnimatedText,
|
|
29
|
+
Async: () => import_async2.Async,
|
|
30
|
+
DataTypes: () => import_data_types3.DataTypes,
|
|
29
31
|
DomeComponent: () => DomeComponent,
|
|
30
32
|
DomeManipulator: () => DomeManipulator,
|
|
33
|
+
DomeRouter: () => DomeRouter,
|
|
34
|
+
Lock: () => import_async2.Lock,
|
|
35
|
+
ObservableArray: () => import_observable3.ObservableArray,
|
|
36
|
+
ObservableLocalStorageArray: () => import_observable3.ObservableLocalStorageArray,
|
|
37
|
+
ObservableLocalStorageVariable: () => import_observable3.ObservableLocalStorageVariable,
|
|
38
|
+
ObservableMap: () => import_observable3.ObservableMap,
|
|
39
|
+
ObservableValue: () => import_observable3.ObservableVariable,
|
|
40
|
+
ObservableVariable: () => import_observable3.ObservableVariable,
|
|
31
41
|
React: () => React,
|
|
42
|
+
TypeEvent: () => import_type_event.TypeEvent,
|
|
43
|
+
checkIfObservable: () => import_observable3.checkIfObservable,
|
|
44
|
+
createObservable: () => import_observable3.createObservable,
|
|
32
45
|
h: () => h
|
|
33
46
|
});
|
|
34
47
|
module.exports = __toCommonJS(src_exports);
|
|
35
48
|
__reExport(src_exports, require("@lexriver/async"), module.exports);
|
|
49
|
+
var import_async2 = require("@lexriver/async");
|
|
36
50
|
__reExport(src_exports, require("@lexriver/data-types"), module.exports);
|
|
51
|
+
var import_data_types3 = require("@lexriver/data-types");
|
|
37
52
|
__reExport(src_exports, require("@lexriver/observable"), module.exports);
|
|
53
|
+
var import_observable3 = require("@lexriver/observable");
|
|
38
54
|
__reExport(src_exports, require("@lexriver/type-event"), module.exports);
|
|
55
|
+
var import_type_event = require("@lexriver/type-event");
|
|
39
56
|
|
|
40
57
|
// src/DomeManipulator.mts
|
|
41
58
|
var import_async = require("@lexriver/async");
|
|
@@ -800,3 +817,185 @@ var React = {
|
|
|
800
817
|
Fragment: typeof DocumentFragment === "function" ? DocumentFragment : () => {
|
|
801
818
|
}
|
|
802
819
|
};
|
|
820
|
+
|
|
821
|
+
// src/DomeRouter.mts
|
|
822
|
+
var DomeRouter;
|
|
823
|
+
((DomeRouter2) => {
|
|
824
|
+
const historyUrls = [];
|
|
825
|
+
const scrollPositionByUrl = /* @__PURE__ */ new Map();
|
|
826
|
+
DomeRouter2.maxHistoryUrlsCount = 20;
|
|
827
|
+
const allRoutes = [];
|
|
828
|
+
let onNotFoundAction = void 0;
|
|
829
|
+
window.addEventListener("popstate", async (e) => {
|
|
830
|
+
const url = window.location.pathname;
|
|
831
|
+
await executeAsync(url, getScrollPositionForUrl(url));
|
|
832
|
+
await DomeManipulator.scrollToAsync({
|
|
833
|
+
pxFromTop: getScrollPositionForUrl(window.location.pathname)
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
function getScrollPositionForUrl(url) {
|
|
837
|
+
var _a;
|
|
838
|
+
return (_a = scrollPositionByUrl.get(url)) != null ? _a : 0;
|
|
839
|
+
}
|
|
840
|
+
function saveScrollPositionForUrl(url) {
|
|
841
|
+
scrollPositionByUrl.set(url, DomeManipulator.getCurrentScrollPosition());
|
|
842
|
+
}
|
|
843
|
+
function saveScrollPositionForCurrentUrl() {
|
|
844
|
+
scrollPositionByUrl.set(getCurrentUrl(), DomeManipulator.getCurrentScrollPosition());
|
|
845
|
+
}
|
|
846
|
+
function navigate(url) {
|
|
847
|
+
addUrlToHistory(getCurrentUrl());
|
|
848
|
+
saveScrollPositionForCurrentUrl();
|
|
849
|
+
window.history.pushState(null, "", url);
|
|
850
|
+
executeAsync(url, 0);
|
|
851
|
+
DomeManipulator.scrollToTop();
|
|
852
|
+
}
|
|
853
|
+
DomeRouter2.navigate = navigate;
|
|
854
|
+
function goBack() {
|
|
855
|
+
window.history.go(-1);
|
|
856
|
+
}
|
|
857
|
+
DomeRouter2.goBack = goBack;
|
|
858
|
+
function goForward() {
|
|
859
|
+
window.history.go(1);
|
|
860
|
+
}
|
|
861
|
+
DomeRouter2.goForward = goForward;
|
|
862
|
+
function changeUrl(url) {
|
|
863
|
+
window.history.replaceState(null, "", url);
|
|
864
|
+
}
|
|
865
|
+
DomeRouter2.changeUrl = changeUrl;
|
|
866
|
+
function addUrlToHistory(url) {
|
|
867
|
+
historyUrls.push({ url, scroll: 0 });
|
|
868
|
+
while (historyUrls.length > DomeRouter2.maxHistoryUrlsCount) {
|
|
869
|
+
historyUrls.shift();
|
|
870
|
+
}
|
|
871
|
+
if (historyUrls.length > 1) {
|
|
872
|
+
historyUrls[historyUrls.length - 2].scroll = DomeManipulator.getCurrentScrollPosition();
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function getCurrentUrl() {
|
|
876
|
+
return window.location.pathname;
|
|
877
|
+
}
|
|
878
|
+
DomeRouter2.getCurrentUrl = getCurrentUrl;
|
|
879
|
+
function getPreviousPageUrl(previousPageIndex = 0) {
|
|
880
|
+
let index = historyUrls.length - 2 - previousPageIndex;
|
|
881
|
+
if (index >= 0 && index < historyUrls.length) return historyUrls[index].url;
|
|
882
|
+
return void 0;
|
|
883
|
+
}
|
|
884
|
+
DomeRouter2.getPreviousPageUrl = getPreviousPageUrl;
|
|
885
|
+
function resolveUrl(url = window.location.pathname, addToHistory = true) {
|
|
886
|
+
if (addToHistory) addUrlToHistory(window.location.pathname);
|
|
887
|
+
executeAsync(url, getScrollPositionForUrl(url));
|
|
888
|
+
}
|
|
889
|
+
DomeRouter2.resolveUrl = resolveUrl;
|
|
890
|
+
function addRoute(route, exactMatch, action) {
|
|
891
|
+
if (route[0] !== "/") throw new Error("Please provide correct route. route=" + route);
|
|
892
|
+
const routeSlices = getRouteSlices(route);
|
|
893
|
+
allRoutes.push({ routeSlices, exactMatch, action });
|
|
894
|
+
}
|
|
895
|
+
function onRoute(route, exactMatch, action) {
|
|
896
|
+
addRoute(route, exactMatch, action);
|
|
897
|
+
}
|
|
898
|
+
DomeRouter2.onRoute = onRoute;
|
|
899
|
+
function onTypedRoute(route, exactMatch, action) {
|
|
900
|
+
addRoute(route, exactMatch, action);
|
|
901
|
+
}
|
|
902
|
+
DomeRouter2.onTypedRoute = onTypedRoute;
|
|
903
|
+
function getRouteSlices(route) {
|
|
904
|
+
return route.split("/").map((x) => decodeURIComponent(x));
|
|
905
|
+
}
|
|
906
|
+
function onNotFound(action) {
|
|
907
|
+
onNotFoundAction = action;
|
|
908
|
+
}
|
|
909
|
+
DomeRouter2.onNotFound = onNotFound;
|
|
910
|
+
function checkUrlMatchRouteAndGetParameters(url, route, exactMatch = true) {
|
|
911
|
+
const urlSlices = getRouteSlices(url);
|
|
912
|
+
const routeSlices = getRouteSlices(route);
|
|
913
|
+
if (exactMatch && routeSlices.length !== urlSlices.length) return void 0;
|
|
914
|
+
const extractedParameters = {};
|
|
915
|
+
for (let i = 0; i < routeSlices.length; i++) {
|
|
916
|
+
let cRouteSlice = routeSlices[i];
|
|
917
|
+
let cUrlSlice = urlSlices[i];
|
|
918
|
+
if (cRouteSlice.startsWith(":")) {
|
|
919
|
+
if (cUrlSlice === void 0) {
|
|
920
|
+
return void 0;
|
|
921
|
+
}
|
|
922
|
+
const [name, value] = parseToNameAndValue(cRouteSlice, cUrlSlice);
|
|
923
|
+
extractedParameters[name] = value;
|
|
924
|
+
} else {
|
|
925
|
+
if (cRouteSlice !== cUrlSlice) {
|
|
926
|
+
return void 0;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return extractedParameters;
|
|
931
|
+
}
|
|
932
|
+
DomeRouter2.checkUrlMatchRouteAndGetParameters = checkUrlMatchRouteAndGetParameters;
|
|
933
|
+
function parseToNameAndValue(name, value) {
|
|
934
|
+
if (!name) throw new Error("no name");
|
|
935
|
+
const haveMatch = name.match(/:(.+)<(.+)>/);
|
|
936
|
+
if (haveMatch) {
|
|
937
|
+
const paramName = haveMatch[1];
|
|
938
|
+
const paramType = haveMatch[2].toLowerCase();
|
|
939
|
+
if (paramType == "int") {
|
|
940
|
+
return [paramName, parseInt(value)];
|
|
941
|
+
} else if (paramType == "float") {
|
|
942
|
+
return [paramName, parseFloat(value)];
|
|
943
|
+
} else if (paramType == "number") {
|
|
944
|
+
return [paramName, Number(value)];
|
|
945
|
+
} else {
|
|
946
|
+
return [paramName, value];
|
|
947
|
+
}
|
|
948
|
+
} else {
|
|
949
|
+
return [name.substring(1), value];
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
function parseQueryString(search) {
|
|
953
|
+
const query = {};
|
|
954
|
+
if (!search || search[0] !== "?") return query;
|
|
955
|
+
const params = new URLSearchParams(search.substring(1));
|
|
956
|
+
for (const [key, value] of params.entries()) {
|
|
957
|
+
query[key] = value;
|
|
958
|
+
}
|
|
959
|
+
return query;
|
|
960
|
+
}
|
|
961
|
+
async function executeAsync(url = window.location.pathname, scrollToPosition) {
|
|
962
|
+
const queryString = window.location.search;
|
|
963
|
+
const query = parseQueryString(queryString);
|
|
964
|
+
url = url.split("?")[0];
|
|
965
|
+
const urlSlices = getRouteSlices(url);
|
|
966
|
+
let countOfFoundRoutes = 0;
|
|
967
|
+
for (let route of allRoutes) {
|
|
968
|
+
if (route.exactMatch && route.routeSlices.length != urlSlices.length) continue;
|
|
969
|
+
let matched = true;
|
|
970
|
+
const extractedParameters = {};
|
|
971
|
+
for (let i = 0; i < route.routeSlices.length; i++) {
|
|
972
|
+
let cRouteSlice = route.routeSlices[i];
|
|
973
|
+
let cUrlSlice = urlSlices[i];
|
|
974
|
+
if (cRouteSlice && typeof cRouteSlice === "string" && cRouteSlice.startsWith(":")) {
|
|
975
|
+
if (cUrlSlice === void 0) {
|
|
976
|
+
matched = false;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
const [name, value] = parseToNameAndValue(cRouteSlice, cUrlSlice);
|
|
980
|
+
extractedParameters[name] = value;
|
|
981
|
+
} else {
|
|
982
|
+
if (cRouteSlice !== cUrlSlice) {
|
|
983
|
+
matched = false;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (matched) {
|
|
989
|
+
countOfFoundRoutes++;
|
|
990
|
+
await route.action(extractedParameters, url, async () => {
|
|
991
|
+
await DomeManipulator.scrollToAsync({
|
|
992
|
+
pxFromTop: scrollToPosition
|
|
993
|
+
});
|
|
994
|
+
}, query);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (countOfFoundRoutes == 0 && onNotFoundAction) {
|
|
998
|
+
onNotFoundAction();
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
})(DomeRouter || (DomeRouter = {}));
|
package/out/index.mjs
CHANGED
|
@@ -1965,6 +1965,188 @@ var React = {
|
|
|
1965
1965
|
Fragment: typeof DocumentFragment === "function" ? DocumentFragment : () => {
|
|
1966
1966
|
}
|
|
1967
1967
|
};
|
|
1968
|
+
|
|
1969
|
+
// src/DomeRouter.mts
|
|
1970
|
+
var DomeRouter;
|
|
1971
|
+
((DomeRouter2) => {
|
|
1972
|
+
const historyUrls = [];
|
|
1973
|
+
const scrollPositionByUrl = /* @__PURE__ */ new Map();
|
|
1974
|
+
DomeRouter2.maxHistoryUrlsCount = 20;
|
|
1975
|
+
const allRoutes = [];
|
|
1976
|
+
let onNotFoundAction = void 0;
|
|
1977
|
+
window.addEventListener("popstate", async (e) => {
|
|
1978
|
+
const url = window.location.pathname;
|
|
1979
|
+
await executeAsync(url, getScrollPositionForUrl(url));
|
|
1980
|
+
await DomeManipulator.scrollToAsync({
|
|
1981
|
+
pxFromTop: getScrollPositionForUrl(window.location.pathname)
|
|
1982
|
+
});
|
|
1983
|
+
});
|
|
1984
|
+
function getScrollPositionForUrl(url) {
|
|
1985
|
+
var _a;
|
|
1986
|
+
return (_a = scrollPositionByUrl.get(url)) != null ? _a : 0;
|
|
1987
|
+
}
|
|
1988
|
+
function saveScrollPositionForUrl(url) {
|
|
1989
|
+
scrollPositionByUrl.set(url, DomeManipulator.getCurrentScrollPosition());
|
|
1990
|
+
}
|
|
1991
|
+
function saveScrollPositionForCurrentUrl() {
|
|
1992
|
+
scrollPositionByUrl.set(getCurrentUrl(), DomeManipulator.getCurrentScrollPosition());
|
|
1993
|
+
}
|
|
1994
|
+
function navigate(url) {
|
|
1995
|
+
addUrlToHistory(getCurrentUrl());
|
|
1996
|
+
saveScrollPositionForCurrentUrl();
|
|
1997
|
+
window.history.pushState(null, "", url);
|
|
1998
|
+
executeAsync(url, 0);
|
|
1999
|
+
DomeManipulator.scrollToTop();
|
|
2000
|
+
}
|
|
2001
|
+
DomeRouter2.navigate = navigate;
|
|
2002
|
+
function goBack() {
|
|
2003
|
+
window.history.go(-1);
|
|
2004
|
+
}
|
|
2005
|
+
DomeRouter2.goBack = goBack;
|
|
2006
|
+
function goForward() {
|
|
2007
|
+
window.history.go(1);
|
|
2008
|
+
}
|
|
2009
|
+
DomeRouter2.goForward = goForward;
|
|
2010
|
+
function changeUrl(url) {
|
|
2011
|
+
window.history.replaceState(null, "", url);
|
|
2012
|
+
}
|
|
2013
|
+
DomeRouter2.changeUrl = changeUrl;
|
|
2014
|
+
function addUrlToHistory(url) {
|
|
2015
|
+
historyUrls.push({ url, scroll: 0 });
|
|
2016
|
+
while (historyUrls.length > DomeRouter2.maxHistoryUrlsCount) {
|
|
2017
|
+
historyUrls.shift();
|
|
2018
|
+
}
|
|
2019
|
+
if (historyUrls.length > 1) {
|
|
2020
|
+
historyUrls[historyUrls.length - 2].scroll = DomeManipulator.getCurrentScrollPosition();
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
function getCurrentUrl() {
|
|
2024
|
+
return window.location.pathname;
|
|
2025
|
+
}
|
|
2026
|
+
DomeRouter2.getCurrentUrl = getCurrentUrl;
|
|
2027
|
+
function getPreviousPageUrl(previousPageIndex = 0) {
|
|
2028
|
+
let index = historyUrls.length - 2 - previousPageIndex;
|
|
2029
|
+
if (index >= 0 && index < historyUrls.length) return historyUrls[index].url;
|
|
2030
|
+
return void 0;
|
|
2031
|
+
}
|
|
2032
|
+
DomeRouter2.getPreviousPageUrl = getPreviousPageUrl;
|
|
2033
|
+
function resolveUrl(url = window.location.pathname, addToHistory = true) {
|
|
2034
|
+
if (addToHistory) addUrlToHistory(window.location.pathname);
|
|
2035
|
+
executeAsync(url, getScrollPositionForUrl(url));
|
|
2036
|
+
}
|
|
2037
|
+
DomeRouter2.resolveUrl = resolveUrl;
|
|
2038
|
+
function addRoute(route, exactMatch, action) {
|
|
2039
|
+
if (route[0] !== "/") throw new Error("Please provide correct route. route=" + route);
|
|
2040
|
+
const routeSlices = getRouteSlices(route);
|
|
2041
|
+
allRoutes.push({ routeSlices, exactMatch, action });
|
|
2042
|
+
}
|
|
2043
|
+
function onRoute(route, exactMatch, action) {
|
|
2044
|
+
addRoute(route, exactMatch, action);
|
|
2045
|
+
}
|
|
2046
|
+
DomeRouter2.onRoute = onRoute;
|
|
2047
|
+
function onTypedRoute(route, exactMatch, action) {
|
|
2048
|
+
addRoute(route, exactMatch, action);
|
|
2049
|
+
}
|
|
2050
|
+
DomeRouter2.onTypedRoute = onTypedRoute;
|
|
2051
|
+
function getRouteSlices(route) {
|
|
2052
|
+
return route.split("/").map((x) => decodeURIComponent(x));
|
|
2053
|
+
}
|
|
2054
|
+
function onNotFound(action) {
|
|
2055
|
+
onNotFoundAction = action;
|
|
2056
|
+
}
|
|
2057
|
+
DomeRouter2.onNotFound = onNotFound;
|
|
2058
|
+
function checkUrlMatchRouteAndGetParameters(url, route, exactMatch = true) {
|
|
2059
|
+
const urlSlices = getRouteSlices(url);
|
|
2060
|
+
const routeSlices = getRouteSlices(route);
|
|
2061
|
+
if (exactMatch && routeSlices.length !== urlSlices.length) return void 0;
|
|
2062
|
+
const extractedParameters = {};
|
|
2063
|
+
for (let i = 0; i < routeSlices.length; i++) {
|
|
2064
|
+
let cRouteSlice = routeSlices[i];
|
|
2065
|
+
let cUrlSlice = urlSlices[i];
|
|
2066
|
+
if (cRouteSlice.startsWith(":")) {
|
|
2067
|
+
if (cUrlSlice === void 0) {
|
|
2068
|
+
return void 0;
|
|
2069
|
+
}
|
|
2070
|
+
const [name, value] = parseToNameAndValue(cRouteSlice, cUrlSlice);
|
|
2071
|
+
extractedParameters[name] = value;
|
|
2072
|
+
} else {
|
|
2073
|
+
if (cRouteSlice !== cUrlSlice) {
|
|
2074
|
+
return void 0;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
return extractedParameters;
|
|
2079
|
+
}
|
|
2080
|
+
DomeRouter2.checkUrlMatchRouteAndGetParameters = checkUrlMatchRouteAndGetParameters;
|
|
2081
|
+
function parseToNameAndValue(name, value) {
|
|
2082
|
+
if (!name) throw new Error("no name");
|
|
2083
|
+
const haveMatch = name.match(/:(.+)<(.+)>/);
|
|
2084
|
+
if (haveMatch) {
|
|
2085
|
+
const paramName = haveMatch[1];
|
|
2086
|
+
const paramType = haveMatch[2].toLowerCase();
|
|
2087
|
+
if (paramType == "int") {
|
|
2088
|
+
return [paramName, parseInt(value)];
|
|
2089
|
+
} else if (paramType == "float") {
|
|
2090
|
+
return [paramName, parseFloat(value)];
|
|
2091
|
+
} else if (paramType == "number") {
|
|
2092
|
+
return [paramName, Number(value)];
|
|
2093
|
+
} else {
|
|
2094
|
+
return [paramName, value];
|
|
2095
|
+
}
|
|
2096
|
+
} else {
|
|
2097
|
+
return [name.substring(1), value];
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
function parseQueryString(search) {
|
|
2101
|
+
const query = {};
|
|
2102
|
+
if (!search || search[0] !== "?") return query;
|
|
2103
|
+
const params = new URLSearchParams(search.substring(1));
|
|
2104
|
+
for (const [key, value] of params.entries()) {
|
|
2105
|
+
query[key] = value;
|
|
2106
|
+
}
|
|
2107
|
+
return query;
|
|
2108
|
+
}
|
|
2109
|
+
async function executeAsync(url = window.location.pathname, scrollToPosition) {
|
|
2110
|
+
const queryString = window.location.search;
|
|
2111
|
+
const query = parseQueryString(queryString);
|
|
2112
|
+
url = url.split("?")[0];
|
|
2113
|
+
const urlSlices = getRouteSlices(url);
|
|
2114
|
+
let countOfFoundRoutes = 0;
|
|
2115
|
+
for (let route of allRoutes) {
|
|
2116
|
+
if (route.exactMatch && route.routeSlices.length != urlSlices.length) continue;
|
|
2117
|
+
let matched = true;
|
|
2118
|
+
const extractedParameters = {};
|
|
2119
|
+
for (let i = 0; i < route.routeSlices.length; i++) {
|
|
2120
|
+
let cRouteSlice = route.routeSlices[i];
|
|
2121
|
+
let cUrlSlice = urlSlices[i];
|
|
2122
|
+
if (cRouteSlice && typeof cRouteSlice === "string" && cRouteSlice.startsWith(":")) {
|
|
2123
|
+
if (cUrlSlice === void 0) {
|
|
2124
|
+
matched = false;
|
|
2125
|
+
continue;
|
|
2126
|
+
}
|
|
2127
|
+
const [name, value] = parseToNameAndValue(cRouteSlice, cUrlSlice);
|
|
2128
|
+
extractedParameters[name] = value;
|
|
2129
|
+
} else {
|
|
2130
|
+
if (cRouteSlice !== cUrlSlice) {
|
|
2131
|
+
matched = false;
|
|
2132
|
+
continue;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
if (matched) {
|
|
2137
|
+
countOfFoundRoutes++;
|
|
2138
|
+
await route.action(extractedParameters, url, async () => {
|
|
2139
|
+
await DomeManipulator.scrollToAsync({
|
|
2140
|
+
pxFromTop: scrollToPosition
|
|
2141
|
+
});
|
|
2142
|
+
}, query);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
if (countOfFoundRoutes == 0 && onNotFoundAction) {
|
|
2146
|
+
onNotFoundAction();
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
})(DomeRouter || (DomeRouter = {}));
|
|
1968
2150
|
export {
|
|
1969
2151
|
AnimatedArray,
|
|
1970
2152
|
AnimatedTable,
|
|
@@ -1973,11 +2155,13 @@ export {
|
|
|
1973
2155
|
DataTypes,
|
|
1974
2156
|
DomeComponent,
|
|
1975
2157
|
DomeManipulator,
|
|
2158
|
+
DomeRouter,
|
|
1976
2159
|
Lock,
|
|
1977
2160
|
ObservableArray,
|
|
1978
2161
|
ObservableLocalStorageArray,
|
|
1979
2162
|
ObservableLocalStorageVariable,
|
|
1980
2163
|
ObservableMap,
|
|
2164
|
+
ObservableVariable as ObservableValue,
|
|
1981
2165
|
ObservableVariable,
|
|
1982
2166
|
React,
|
|
1983
2167
|
TypeEvent,
|
package/out/src/DomeRouter.d.mts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type RouteParameters = {
|
|
2
|
+
[key: string]: string;
|
|
3
|
+
};
|
|
4
|
+
export type TypedRouteParameters = {
|
|
2
5
|
[key: string]: string | number;
|
|
3
|
-
}
|
|
6
|
+
};
|
|
7
|
+
type RouteCallback<Parameters> = (params: Parameters, url: string, scrollToPreviousPositionAsync: () => Promise<void>, query: {
|
|
4
8
|
[key: string]: string;
|
|
5
9
|
}) => void | Promise<void>;
|
|
10
|
+
export type RouteAction = RouteCallback<RouteParameters>;
|
|
11
|
+
export type TypedRouteAction = RouteCallback<TypedRouteParameters>;
|
|
6
12
|
export declare namespace DomeRouter {
|
|
7
13
|
let maxHistoryUrlsCount: number;
|
|
8
14
|
function navigate(url: string): void;
|
|
@@ -16,7 +22,10 @@ export declare namespace DomeRouter {
|
|
|
16
22
|
*/
|
|
17
23
|
function getPreviousPageUrl(previousPageIndex?: number): string | undefined;
|
|
18
24
|
function resolveUrl(url?: string, addToHistory?: boolean): void;
|
|
25
|
+
/** Register a route whose unannotated parameters are strings. */
|
|
19
26
|
function onRoute(route: string, exactMatch: boolean, action: RouteAction): void;
|
|
27
|
+
/** Register a route using <int>, <float>, or <number> parameter annotations. */
|
|
28
|
+
function onTypedRoute(route: string, exactMatch: boolean, action: TypedRouteAction): void;
|
|
20
29
|
function onNotFound(action: () => void): void;
|
|
21
30
|
/**
|
|
22
31
|
* Check if url matched route.
|
|
@@ -30,3 +39,4 @@ export declare namespace DomeRouter {
|
|
|
30
39
|
*/
|
|
31
40
|
function checkUrlMatchRouteAndGetParameters(url: string, route: string, exactMatch?: boolean): Object | undefined;
|
|
32
41
|
}
|
|
42
|
+
export {};
|
package/out/src/DomeRouter.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type RouteParameters = {
|
|
2
|
+
[key: string]: string;
|
|
3
|
+
};
|
|
4
|
+
export type TypedRouteParameters = {
|
|
2
5
|
[key: string]: string | number;
|
|
3
|
-
}
|
|
6
|
+
};
|
|
7
|
+
type RouteCallback<Parameters> = (params: Parameters, url: string, scrollToPreviousPositionAsync: () => Promise<void>, query: {
|
|
4
8
|
[key: string]: string;
|
|
5
9
|
}) => void | Promise<void>;
|
|
10
|
+
export type RouteAction = RouteCallback<RouteParameters>;
|
|
11
|
+
export type TypedRouteAction = RouteCallback<TypedRouteParameters>;
|
|
6
12
|
export declare namespace DomeRouter {
|
|
7
13
|
let maxHistoryUrlsCount: number;
|
|
8
14
|
function navigate(url: string): void;
|
|
@@ -16,7 +22,10 @@ export declare namespace DomeRouter {
|
|
|
16
22
|
*/
|
|
17
23
|
function getPreviousPageUrl(previousPageIndex?: number): string | undefined;
|
|
18
24
|
function resolveUrl(url?: string, addToHistory?: boolean): void;
|
|
25
|
+
/** Register a route whose unannotated parameters are strings. */
|
|
19
26
|
function onRoute(route: string, exactMatch: boolean, action: RouteAction): void;
|
|
27
|
+
/** Register a route using <int>, <float>, or <number> parameter annotations. */
|
|
28
|
+
function onTypedRoute(route: string, exactMatch: boolean, action: TypedRouteAction): void;
|
|
20
29
|
function onNotFound(action: () => void): void;
|
|
21
30
|
/**
|
|
22
31
|
* Check if url matched route.
|
|
@@ -30,3 +39,4 @@ export declare namespace DomeRouter {
|
|
|
30
39
|
*/
|
|
31
40
|
function checkUrlMatchRouteAndGetParameters(url: string, route: string, exactMatch?: boolean): Object | undefined;
|
|
32
41
|
}
|
|
42
|
+
export {};
|
package/out/src/DomeRouter.mjs
CHANGED
|
@@ -88,13 +88,22 @@ export var DomeRouter;
|
|
|
88
88
|
executeAsync(url, getScrollPositionForUrl(url));
|
|
89
89
|
}
|
|
90
90
|
DomeRouter.resolveUrl = resolveUrl;
|
|
91
|
-
function
|
|
91
|
+
function addRoute(route, exactMatch, action) {
|
|
92
92
|
if (route[0] !== '/')
|
|
93
93
|
throw new Error('Please provide correct route. route=' + route);
|
|
94
|
-
|
|
94
|
+
const routeSlices = getRouteSlices(route);
|
|
95
95
|
allRoutes.push({ routeSlices, exactMatch, action });
|
|
96
96
|
}
|
|
97
|
+
/** Register a route whose unannotated parameters are strings. */
|
|
98
|
+
function onRoute(route, exactMatch, action) {
|
|
99
|
+
addRoute(route, exactMatch, action);
|
|
100
|
+
}
|
|
97
101
|
DomeRouter.onRoute = onRoute;
|
|
102
|
+
/** Register a route using <int>, <float>, or <number> parameter annotations. */
|
|
103
|
+
function onTypedRoute(route, exactMatch, action) {
|
|
104
|
+
addRoute(route, exactMatch, action);
|
|
105
|
+
}
|
|
106
|
+
DomeRouter.onTypedRoute = onTypedRoute;
|
|
98
107
|
function getRouteSlices(route) {
|
|
99
108
|
//return route.split('/').filter(x => x.length>0).map(x => decodeURIComponent(x))
|
|
100
109
|
return route.split('/').map(x => decodeURIComponent(x));
|
package/out/src/index.d.mts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
export * from '@lexriver/async';
|
|
2
|
+
export { Async, Lock } from '@lexriver/async';
|
|
2
3
|
export * from '@lexriver/data-types';
|
|
4
|
+
export { DataTypes } from '@lexriver/data-types';
|
|
3
5
|
export * from '@lexriver/observable';
|
|
6
|
+
export { ObservableArray, ObservableLocalStorageArray, ObservableLocalStorageVariable, ObservableMap, ObservableVariable, ObservableVariable as ObservableValue, checkIfObservable, createObservable } from '@lexriver/observable';
|
|
4
7
|
export * from '@lexriver/type-event';
|
|
8
|
+
export { TypeEvent } from '@lexriver/type-event';
|
|
5
9
|
export * from './AnimatedArray.mjs';
|
|
6
10
|
export * from './AnimatedTable.mjs';
|
|
7
11
|
export * from './AnimatedText.mjs';
|
|
8
12
|
export * from './Dome.mjs';
|
|
9
13
|
export * from './DomeComponent.mjs';
|
|
10
14
|
export * from './DomeManipulator.mjs';
|
|
11
|
-
export * from './DomeRouter.
|
|
15
|
+
export * from './DomeRouter.mjs';
|
package/out/src/index.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
export * from '@lexriver/async';
|
|
2
|
+
export { Async, Lock } from '@lexriver/async';
|
|
2
3
|
export * from '@lexriver/data-types';
|
|
4
|
+
export { DataTypes } from '@lexriver/data-types';
|
|
3
5
|
export * from '@lexriver/observable';
|
|
6
|
+
export { ObservableArray, ObservableLocalStorageArray, ObservableLocalStorageVariable, ObservableMap, ObservableVariable, ObservableVariable as ObservableValue, checkIfObservable, createObservable } from '@lexriver/observable';
|
|
4
7
|
export * from '@lexriver/type-event';
|
|
8
|
+
export { TypeEvent } from '@lexriver/type-event';
|
|
5
9
|
export * from './AnimatedArray';
|
|
6
10
|
export * from './AnimatedTable';
|
|
7
11
|
export * from './AnimatedText';
|
|
8
12
|
export * from './Dome';
|
|
9
13
|
export * from './DomeComponent';
|
|
10
14
|
export * from './DomeManipulator';
|
|
11
|
-
export * from './DomeRouter
|
|
15
|
+
export * from './DomeRouter';
|
package/out/src/index.mjs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
export * from '@lexriver/async';
|
|
2
|
+
export { Async, Lock } from '@lexriver/async';
|
|
2
3
|
export * from '@lexriver/data-types';
|
|
4
|
+
export { DataTypes } from '@lexriver/data-types';
|
|
3
5
|
export * from '@lexriver/observable';
|
|
6
|
+
export { ObservableArray, ObservableLocalStorageArray, ObservableLocalStorageVariable, ObservableMap, ObservableVariable, ObservableVariable as ObservableValue, checkIfObservable, createObservable } from '@lexriver/observable';
|
|
4
7
|
export * from '@lexriver/type-event';
|
|
8
|
+
export { TypeEvent } from '@lexriver/type-event';
|
|
5
9
|
export * from './AnimatedArray.mjs';
|
|
6
10
|
export * from './AnimatedTable.mjs';
|
|
7
11
|
export * from './AnimatedText.mjs';
|
|
8
12
|
export * from './Dome.mjs';
|
|
9
13
|
export * from './DomeComponent.mjs';
|
|
10
14
|
export * from './DomeManipulator.mjs';
|
|
11
|
-
export * from './DomeRouter.
|
|
15
|
+
export * from './DomeRouter.mjs';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sideEffects": false,
|
|
3
3
|
"name": "@lexriver/dome",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.5",
|
|
5
5
|
"description": "DOM manipulator",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./out/src/index.d.ts",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"test": "vitest",
|
|
24
24
|
"build": "rimraf out && tsc && node scripts/build-compat.mjs",
|
|
25
|
-
"test:compat": "npm run build &&
|
|
25
|
+
"test:api-compat": "npm run build && node scripts/test-api-compat.mjs",
|
|
26
|
+
"test:compat": "npm run build && node scripts/test-api-compat.mjs && npm --prefix compatibility/ts39-webpack4 install && npm --prefix compatibility/ts39-webpack4 run build"
|
|
26
27
|
},
|
|
27
28
|
"keywords": [
|
|
28
29
|
"typescript",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// //import { DomeRouter } from "./DomeRouter"
|
|
2
|
-
// function getRouteSlices(route:string){
|
|
3
|
-
// return route.split('/').filter(x => x.length>0)
|
|
4
|
-
// }
|
|
5
|
-
export {};
|
|
6
|
-
// function checkUrlMatchRouteAndGetParameters(url:string, route:string, exactMatch:boolean = true):Map<string,string>|undefined{
|
|
7
|
-
// // this function is purely for export
|
|
8
|
-
// const urlSlices = getRouteSlices(url)
|
|
9
|
-
// const routeSlices = getRouteSlices(route)
|
|
10
|
-
// if(exactMatch && routeSlices.length !== urlSlices.length) return undefined
|
|
11
|
-
// // all routeSlices must match for !exactMatch
|
|
12
|
-
// const extractedParameters = new Map<string,string>()
|
|
13
|
-
// for(let i=0;i<routeSlices.length;i++){
|
|
14
|
-
// let cRouteSlice = routeSlices[i]
|
|
15
|
-
// let cUrlSlice = urlSlices[i] // could be undefined
|
|
16
|
-
// //console.info('\t\t cRouteSlice=', cRouteSlice, 'cUrlSlice=', cUrlSlice)
|
|
17
|
-
// if(cRouteSlice.startsWith(':')){
|
|
18
|
-
// // we have a param name
|
|
19
|
-
// if(cUrlSlice === undefined){
|
|
20
|
-
// return undefined
|
|
21
|
-
// }
|
|
22
|
-
// // we have some variable, save it
|
|
23
|
-
// extractedParameters.set(cRouteSlice.substring(1), cUrlSlice)
|
|
24
|
-
// } else {
|
|
25
|
-
// // no param name
|
|
26
|
-
// if(cRouteSlice !== cUrlSlice){
|
|
27
|
-
// return undefined
|
|
28
|
-
// }
|
|
29
|
-
// }
|
|
30
|
-
// }
|
|
31
|
-
// return extractedParameters
|
|
32
|
-
// }
|
|
33
|
-
// DomeRouter.onRoute('/', true, ()=>{ console.log('exact /')})
|
|
34
|
-
// DomeRouter.onRoute('/', false, ()=>{ console.log('notexact /')})
|
|
35
|
-
// DomeRouter.onRoute('/test', true, ()=>{ console.log('exact /test')})
|
|
36
|
-
// DomeRouter.onRoute('/test/:id', true, (p)=>{ console.log('exact /test/:id', 'params=', p)})
|
|
37
|
-
// DomeRouter.onRoute('/test/:id', false, (p)=>{ console.log('notexact /test/:id', 'params=', p)})
|
|
38
|
-
// DomeRouter.onRoute('/test', false, ()=>{ console.log('notexact /test')})
|
|
39
|
-
// DomeRouter.onRoute('/test/:id/:id2', true, (p)=>{ console.log('exact /test/:id/:id2', 'params=', p)})
|
|
40
|
-
// let testRoute = '//test/34/st'
|
|
41
|
-
// console.log('** testing route:', testRoute)
|
|
42
|
-
// DomeRouter.execute(testRoute)
|
|
43
|
-
// //expect(!true).toBeTruthy()
|
|
44
|
-
//console.log(checkUrlMatchRouteAndGetParameters('/get-product/123', '/get-product/:id/:id2', false))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { DomeComponent } from './DomeComponent.mjs';
|
|
3
|
-
import { DomeManipulator } from './DomeManipulator.mjs';
|
|
4
|
-
class FakeElement {
|
|
5
|
-
nodeType = 1;
|
|
6
|
-
childNodes = [];
|
|
7
|
-
classList = {
|
|
8
|
-
values: new Set(),
|
|
9
|
-
add: (name) => this.classList.values.add(name),
|
|
10
|
-
remove: (name) => this.classList.values.delete(name)
|
|
11
|
-
};
|
|
12
|
-
parentNode = null;
|
|
13
|
-
throwOnAppend = false;
|
|
14
|
-
get firstChild() {
|
|
15
|
-
return this.childNodes[0] ?? null;
|
|
16
|
-
}
|
|
17
|
-
appendChild(child) {
|
|
18
|
-
if (child.throwOnAppend) {
|
|
19
|
-
throw new Error('append failed');
|
|
20
|
-
}
|
|
21
|
-
child.parentNode = this;
|
|
22
|
-
this.childNodes.push(child);
|
|
23
|
-
return child;
|
|
24
|
-
}
|
|
25
|
-
remove() {
|
|
26
|
-
if (!this.parentNode)
|
|
27
|
-
return;
|
|
28
|
-
const index = this.parentNode.childNodes.indexOf(this);
|
|
29
|
-
if (index >= 0) {
|
|
30
|
-
this.parentNode.childNodes.splice(index, 1);
|
|
31
|
-
}
|
|
32
|
-
this.parentNode = null;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const asElement = (element) => element;
|
|
36
|
-
const animation = (name) => ({ cssClassName: name, timeMs: 5 });
|
|
37
|
-
const waitAsync = (timeMs) => new Promise(resolve => setTimeout(resolve, timeMs));
|
|
38
|
-
async function waitForAsync(condition) {
|
|
39
|
-
const timeoutAt = Date.now() + 500;
|
|
40
|
-
while (!condition()) {
|
|
41
|
-
if (Date.now() >= timeoutAt) {
|
|
42
|
-
throw new Error('Timed out waiting for condition');
|
|
43
|
-
}
|
|
44
|
-
await waitAsync(1);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function deferred() {
|
|
48
|
-
let resolve;
|
|
49
|
-
const promise = new Promise(resolvePromise => {
|
|
50
|
-
resolve = resolvePromise;
|
|
51
|
-
});
|
|
52
|
-
return { promise, resolve };
|
|
53
|
-
}
|
|
54
|
-
const originalNode = Object.getOwnPropertyDescriptor(globalThis, 'Node');
|
|
55
|
-
beforeAll(() => {
|
|
56
|
-
Object.defineProperty(globalThis, 'Node', {
|
|
57
|
-
configurable: true,
|
|
58
|
-
value: { ELEMENT_NODE: 1 }
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
afterAll(() => {
|
|
62
|
-
if (originalNode) {
|
|
63
|
-
Object.defineProperty(globalThis, 'Node', originalNode);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
delete globalThis.Node;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
describe('DomeManipulator.replaceAllChildrenAsync', () => {
|
|
70
|
-
async function expectLastQueuedReplacement(animationForHide, animationForShow) {
|
|
71
|
-
const container = new FakeElement();
|
|
72
|
-
const existing = new FakeElement();
|
|
73
|
-
const first = new FakeElement();
|
|
74
|
-
const second = new FakeElement();
|
|
75
|
-
container.appendChild(existing);
|
|
76
|
-
await Promise.all([
|
|
77
|
-
DomeManipulator.replaceAllChildrenAsync(asElement(container), asElement(first), animationForHide, animationForShow),
|
|
78
|
-
DomeManipulator.replaceAllChildrenAsync(asElement(container), asElement(second), animationForHide, animationForShow)
|
|
79
|
-
]);
|
|
80
|
-
expect(container.childNodes).toEqual([second]);
|
|
81
|
-
}
|
|
82
|
-
it('serializes two concurrent replacements without animations', async () => {
|
|
83
|
-
await expectLastQueuedReplacement();
|
|
84
|
-
});
|
|
85
|
-
it('serializes concurrent replacements with a hide animation', async () => {
|
|
86
|
-
await expectLastQueuedReplacement(animation('hide'));
|
|
87
|
-
});
|
|
88
|
-
it('serializes concurrent replacements with a show animation', async () => {
|
|
89
|
-
await expectLastQueuedReplacement(undefined, animation('show'));
|
|
90
|
-
});
|
|
91
|
-
it('serializes concurrent replacements with hide and show animations', async () => {
|
|
92
|
-
await expectLastQueuedReplacement(animation('hide'), animation('show'));
|
|
93
|
-
});
|
|
94
|
-
it('runs replacements on different containers independently', async () => {
|
|
95
|
-
const slowContainer = new FakeElement();
|
|
96
|
-
const fastContainer = new FakeElement();
|
|
97
|
-
const oldSlowChild = new FakeElement();
|
|
98
|
-
const slowChild = new FakeElement();
|
|
99
|
-
const fastChild = new FakeElement();
|
|
100
|
-
slowContainer.appendChild(oldSlowChild);
|
|
101
|
-
let slowReplacementFinished = false;
|
|
102
|
-
const slowReplacement = DomeManipulator.replaceAllChildrenAsync(asElement(slowContainer), asElement(slowChild), { cssClassName: 'hide', timeMs: 30 }).then(() => {
|
|
103
|
-
slowReplacementFinished = true;
|
|
104
|
-
});
|
|
105
|
-
await waitAsync(1);
|
|
106
|
-
await DomeManipulator.replaceAllChildrenAsync(asElement(fastContainer), asElement(fastChild));
|
|
107
|
-
expect(fastContainer.childNodes).toEqual([fastChild]);
|
|
108
|
-
expect(slowReplacementFinished).toBe(false);
|
|
109
|
-
expect(slowContainer.childNodes).toEqual([oldSlowChild]);
|
|
110
|
-
await slowReplacement;
|
|
111
|
-
});
|
|
112
|
-
it('does not let a failed replacement block a later replacement', async () => {
|
|
113
|
-
const container = new FakeElement();
|
|
114
|
-
const failingChild = new FakeElement();
|
|
115
|
-
const successfulChild = new FakeElement();
|
|
116
|
-
failingChild.throwOnAppend = true;
|
|
117
|
-
const failedReplacement = DomeManipulator.replaceAllChildrenAsync(asElement(container), asElement(failingChild));
|
|
118
|
-
const queuedReplacement = DomeManipulator.replaceAllChildrenAsync(asElement(container), asElement(successfulChild));
|
|
119
|
-
await expect(failedReplacement).rejects.toThrow('append failed');
|
|
120
|
-
await queuedReplacement;
|
|
121
|
-
expect(container.childNodes).toEqual([successfulChild]);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
class ScheduledTestComponent extends DomeComponent {
|
|
125
|
-
updateCount = 0;
|
|
126
|
-
activeUpdates = 0;
|
|
127
|
-
maximumActiveUpdates = 0;
|
|
128
|
-
updateBehavior = async () => undefined;
|
|
129
|
-
render() {
|
|
130
|
-
return {};
|
|
131
|
-
}
|
|
132
|
-
async updateAsync() {
|
|
133
|
-
this.updateCount++;
|
|
134
|
-
this.activeUpdates++;
|
|
135
|
-
this.maximumActiveUpdates = Math.max(this.maximumActiveUpdates, this.activeUpdates);
|
|
136
|
-
try {
|
|
137
|
-
await this.updateBehavior();
|
|
138
|
-
}
|
|
139
|
-
finally {
|
|
140
|
-
this.activeUpdates--;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
describe('DomeComponent.scheduleUpdate', () => {
|
|
145
|
-
it('debounces repeated requests made before an update starts', async () => {
|
|
146
|
-
const component = new ScheduledTestComponent({}, null);
|
|
147
|
-
await Promise.all([
|
|
148
|
-
component.scheduleUpdate(),
|
|
149
|
-
component.scheduleUpdate(),
|
|
150
|
-
component.scheduleUpdate()
|
|
151
|
-
]);
|
|
152
|
-
expect(component.updateCount).toBe(1);
|
|
153
|
-
});
|
|
154
|
-
it('coalesces requests during an update into exactly one follow-up update', async () => {
|
|
155
|
-
const component = new ScheduledTestComponent({}, null);
|
|
156
|
-
const firstUpdate = deferred();
|
|
157
|
-
component.updateBehavior = () => component.updateCount === 1
|
|
158
|
-
? firstUpdate.promise
|
|
159
|
-
: Promise.resolve();
|
|
160
|
-
const initialSchedule = component.scheduleUpdate();
|
|
161
|
-
await waitForAsync(() => component.updateCount === 1);
|
|
162
|
-
component.scheduleUpdate();
|
|
163
|
-
component.scheduleUpdate();
|
|
164
|
-
component.scheduleUpdate();
|
|
165
|
-
await waitAsync(10);
|
|
166
|
-
firstUpdate.resolve();
|
|
167
|
-
await initialSchedule;
|
|
168
|
-
expect(component.updateCount).toBe(2);
|
|
169
|
-
});
|
|
170
|
-
it('never runs scheduled updates concurrently for one component', async () => {
|
|
171
|
-
const component = new ScheduledTestComponent({}, null);
|
|
172
|
-
const firstUpdate = deferred();
|
|
173
|
-
component.updateBehavior = () => component.updateCount === 1
|
|
174
|
-
? firstUpdate.promise
|
|
175
|
-
: Promise.resolve();
|
|
176
|
-
const initialSchedule = component.scheduleUpdate();
|
|
177
|
-
await waitForAsync(() => component.updateCount === 1);
|
|
178
|
-
component.scheduleUpdate();
|
|
179
|
-
await waitAsync(10);
|
|
180
|
-
expect(component.maximumActiveUpdates).toBe(1);
|
|
181
|
-
firstUpdate.resolve();
|
|
182
|
-
await initialSchedule;
|
|
183
|
-
expect(component.maximumActiveUpdates).toBe(1);
|
|
184
|
-
});
|
|
185
|
-
it('allows subsequent scheduled updates after updateAsync rejects', async () => {
|
|
186
|
-
const component = new ScheduledTestComponent({}, null);
|
|
187
|
-
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
188
|
-
component.updateBehavior = async () => {
|
|
189
|
-
if (component.updateCount === 1) {
|
|
190
|
-
throw new Error('update failed');
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
try {
|
|
194
|
-
await component.scheduleUpdate();
|
|
195
|
-
await component.scheduleUpdate();
|
|
196
|
-
}
|
|
197
|
-
finally {
|
|
198
|
-
consoleError.mockRestore();
|
|
199
|
-
}
|
|
200
|
-
expect(component.updateCount).toBe(2);
|
|
201
|
-
expect(component.maximumActiveUpdates).toBe(1);
|
|
202
|
-
});
|
|
203
|
-
});
|
package/out/src/temp-test.d.mts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/out/src/temp-test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/out/src/temp-test.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { LongestCommonSubsequence } from "./LongestCommonSubsequence.mjs";
|
|
2
|
-
let oldArray = "ABCD".split('');
|
|
3
|
-
let newArray = "AXYZBCD345".split('');
|
|
4
|
-
const countOfOperations = LongestCommonSubsequence.getPatchOrdered({
|
|
5
|
-
oldArray: [...oldArray],
|
|
6
|
-
newArray: newArray,
|
|
7
|
-
onRemove: (index, item) => {
|
|
8
|
-
console.log('-', item, index);
|
|
9
|
-
oldArray.splice(index, 1);
|
|
10
|
-
},
|
|
11
|
-
onAdd: (index, item) => {
|
|
12
|
-
console.log('+', item, index);
|
|
13
|
-
oldArray.splice(index, 0, item);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
console.log('expecting equal', oldArray, newArray);
|
|
17
|
-
console.log('countOfOperations=', countOfOperations);
|
|
18
|
-
//expect(oldArray).toEqual(newArray)
|
|
19
|
-
// console.log('result oldArray=', oldArray)
|
|
20
|
-
// console.log('result nweArray=', newArray)
|