@allurereport/web-commons 3.0.1 → 3.2.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/attachments.d.ts +27 -14
- package/dist/attachments.js +20 -45
- package/dist/charts/generateStatusAgePyramid.d.ts +5 -0
- package/dist/charts/{generateFBSUAgePyramid.js → generateStatusAgePyramid.js} +29 -17
- package/dist/charts/generators.d.ts +2 -1
- package/dist/charts/generators.js +59 -9
- package/dist/charts/types.d.ts +3 -3
- package/dist/charts/utils.js +1 -1
- package/dist/data.d.ts +5 -1
- package/dist/data.js +8 -2
- package/dist/filters/builders.d.ts +4 -0
- package/dist/filters/builders.js +160 -0
- package/dist/filters/index.d.ts +3 -0
- package/dist/filters/index.js +2 -0
- package/dist/filters/model.d.ts +39 -0
- package/dist/filters/model.js +1 -0
- package/dist/i18n.d.ts +10 -1
- package/dist/i18n.js +69 -20
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/prose.d.ts +2 -0
- package/dist/prose.js +390 -0
- package/dist/sanitize.d.ts +1 -0
- package/dist/sanitize.js +4 -0
- package/dist/stores/loadableStore/constants.d.ts +1 -0
- package/dist/stores/loadableStore/constants.js +1 -0
- package/dist/stores/loadableStore/index.d.ts +3 -0
- package/dist/stores/loadableStore/index.js +2 -0
- package/dist/stores/loadableStore/store.d.ts +13 -0
- package/dist/stores/loadableStore/store.js +47 -0
- package/dist/stores/loadableStore/types.d.ts +7 -0
- package/dist/stores/loadableStore/types.js +1 -0
- package/dist/stores/loadableStore/utils.d.ts +2 -0
- package/dist/stores/loadableStore/utils.js +7 -0
- package/dist/stores/persister/index.d.ts +12 -0
- package/dist/stores/persister/index.js +36 -0
- package/dist/stores/router/index.d.ts +18 -0
- package/dist/stores/router/index.js +95 -0
- package/dist/stores/theme/actions.d.ts +3 -0
- package/dist/stores/theme/actions.js +30 -0
- package/dist/stores/theme/constants.d.ts +6 -0
- package/dist/stores/theme/constants.js +5 -0
- package/dist/stores/theme/index.d.ts +2 -0
- package/dist/stores/theme/index.js +2 -0
- package/dist/stores/theme/store.d.ts +8 -0
- package/dist/stores/theme/store.js +41 -0
- package/dist/stores/theme/types.d.ts +2 -0
- package/dist/stores/theme/types.js +1 -0
- package/dist/stores/theme/utils.d.ts +4 -0
- package/dist/stores/theme/utils.js +23 -0
- package/dist/stores/url/helpers.d.ts +12 -0
- package/dist/stores/url/helpers.js +74 -0
- package/dist/stores/url/index.d.ts +2 -0
- package/dist/stores/url/index.js +2 -0
- package/dist/stores/url/store.d.ts +19 -0
- package/dist/stores/url/store.js +45 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +9 -0
- package/package.json +10 -5
- package/dist/charts/generateFBSUAgePyramid.d.ts +0 -5
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LOADABLE_STORE_BRAND } from "./constants.js";
|
|
2
|
+
import type { LoadableStoreValue } from "./types.js";
|
|
3
|
+
export type LoadableStore<T> = ReturnType<typeof loadableStore<T>>;
|
|
4
|
+
export declare const loadableStore: <T>(options: {
|
|
5
|
+
initialState: T;
|
|
6
|
+
}) => {
|
|
7
|
+
readonly value: LoadableStoreValue<T>;
|
|
8
|
+
readonly onInit: () => void;
|
|
9
|
+
readonly onLoad: (silent?: boolean) => void;
|
|
10
|
+
readonly onSuccess: (data: T) => void;
|
|
11
|
+
readonly onError: (error?: Error) => void;
|
|
12
|
+
readonly brand: typeof LOADABLE_STORE_BRAND;
|
|
13
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { batch, computed, signal } from "@preact/signals-core";
|
|
2
|
+
import { LOADABLE_STORE_BRAND } from "./constants.js";
|
|
3
|
+
export const loadableStore = (options) => {
|
|
4
|
+
const loadingSignal = signal(false);
|
|
5
|
+
const errorSignal = signal(undefined);
|
|
6
|
+
const dataSignal = signal(options.initialState);
|
|
7
|
+
return {
|
|
8
|
+
value: {
|
|
9
|
+
data: computed(() => dataSignal.value),
|
|
10
|
+
loading: computed(() => loadingSignal.value),
|
|
11
|
+
error: computed(() => errorSignal.value),
|
|
12
|
+
errorMessage: computed(() => {
|
|
13
|
+
if (errorSignal.value) {
|
|
14
|
+
return errorSignal.value.message;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}),
|
|
18
|
+
},
|
|
19
|
+
onInit: () => {
|
|
20
|
+
batch(() => {
|
|
21
|
+
loadingSignal.value = false;
|
|
22
|
+
errorSignal.value = undefined;
|
|
23
|
+
dataSignal.value = options.initialState;
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
onLoad: (silent = false) => {
|
|
27
|
+
batch(() => {
|
|
28
|
+
loadingSignal.value = !silent;
|
|
29
|
+
errorSignal.value = undefined;
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
onSuccess: (data) => {
|
|
33
|
+
batch(() => {
|
|
34
|
+
loadingSignal.value = false;
|
|
35
|
+
errorSignal.value = undefined;
|
|
36
|
+
dataSignal.value = data;
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
onError: (error = new Error("Unknown error")) => {
|
|
40
|
+
batch(() => {
|
|
41
|
+
loadingSignal.value = false;
|
|
42
|
+
errorSignal.value = error;
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
brand: LOADABLE_STORE_BRAND,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Signal } from "@preact/signals-core";
|
|
2
|
+
export declare const persistSignal: <S extends Signal<unknown>>(options: {
|
|
3
|
+
signal: S;
|
|
4
|
+
key: string;
|
|
5
|
+
shouldPersist?: (v: S extends Signal<infer U> ? U : never) => boolean;
|
|
6
|
+
}) => (() => void) | undefined;
|
|
7
|
+
export declare const restoreSignal: <S extends Signal<unknown>, V = S extends Signal<infer U> ? U : unknown>(options: {
|
|
8
|
+
signal: S;
|
|
9
|
+
key: string;
|
|
10
|
+
defaultValue?: V;
|
|
11
|
+
onRestore?: (value: any) => V | undefined;
|
|
12
|
+
}) => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { effect } from "@preact/signals-core";
|
|
2
|
+
export const persistSignal = (options) => {
|
|
3
|
+
if (typeof window === "undefined") {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const { signal, key, shouldPersist = () => true } = options;
|
|
7
|
+
return effect(() => {
|
|
8
|
+
const value = signal.value;
|
|
9
|
+
if (!shouldPersist(value)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
window.localStorage.setItem(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export const restoreSignal = (options) => {
|
|
19
|
+
if (typeof window === "undefined") {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const { signal, key, defaultValue, onRestore = (v) => v } = options;
|
|
23
|
+
const value = window.localStorage.getItem(key);
|
|
24
|
+
if (value !== null) {
|
|
25
|
+
let parsedValue = value;
|
|
26
|
+
try {
|
|
27
|
+
parsedValue = JSON.parse(value);
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
signal.value = onRestore(parsedValue);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if ("defaultValue" in options) {
|
|
34
|
+
signal.value = defaultValue;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const router: import("@preact/signals-core").ReadonlySignal<{
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly pathParts: string[];
|
|
4
|
+
}>;
|
|
5
|
+
type NavigateTo = {
|
|
6
|
+
path: string;
|
|
7
|
+
keepSearchParams?: boolean;
|
|
8
|
+
searchParams?: Record<string, string | string[] | number | undefined>;
|
|
9
|
+
params?: Record<string, string | undefined>;
|
|
10
|
+
replace?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare const navigateTo: (to: NavigateTo) => void;
|
|
13
|
+
type Route<Params extends Record<string, string | undefined>> = {
|
|
14
|
+
matches: boolean;
|
|
15
|
+
params: Params;
|
|
16
|
+
};
|
|
17
|
+
export declare const createRoute: <Params extends Record<string, string | undefined>>(path: string, validate?: (route: Route<Params>) => boolean) => Route<Params>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { computed } from "@preact/signals-core";
|
|
2
|
+
import { paramsToSearchParams } from "../url/helpers.js";
|
|
3
|
+
import { currentUrl, goTo } from "../url/index.js";
|
|
4
|
+
export const router = computed(() => {
|
|
5
|
+
const hash = currentUrl.value.hash.startsWith("#") ? currentUrl.value.hash.slice(1) : currentUrl.value.hash;
|
|
6
|
+
return {
|
|
7
|
+
path: hash,
|
|
8
|
+
pathParts: hash.split("/").filter(Boolean),
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
const createRouteUrl = (path, params) => {
|
|
12
|
+
return path
|
|
13
|
+
.split("/")
|
|
14
|
+
.map((part) => {
|
|
15
|
+
if (part.startsWith(":")) {
|
|
16
|
+
const isOptional = part.endsWith("?");
|
|
17
|
+
const paramKey = isOptional ? part.slice(1, -1) : part.slice(1);
|
|
18
|
+
const value = params[paramKey];
|
|
19
|
+
if (value) {
|
|
20
|
+
return value.toString();
|
|
21
|
+
}
|
|
22
|
+
if (isOptional) {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
return part;
|
|
26
|
+
}
|
|
27
|
+
return part;
|
|
28
|
+
})
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.join("/");
|
|
31
|
+
};
|
|
32
|
+
export const navigateTo = (to) => {
|
|
33
|
+
const { path, params = {}, replace = false, searchParams = {}, keepSearchParams = false } = to;
|
|
34
|
+
const currentPathname = currentUrl.value.pathname;
|
|
35
|
+
const newUrl = new URL(currentPathname, currentUrl.value.origin);
|
|
36
|
+
const routeUrl = createRouteUrl(path, params);
|
|
37
|
+
newUrl.hash = routeUrl === "" || routeUrl === "/" ? "" : `#${routeUrl}`;
|
|
38
|
+
if (keepSearchParams) {
|
|
39
|
+
paramsToSearchParams(currentUrl.value.params, newUrl.searchParams);
|
|
40
|
+
}
|
|
41
|
+
Object.entries(searchParams).forEach(([key, value]) => {
|
|
42
|
+
if (!value) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
for (const v of value) {
|
|
47
|
+
newUrl.searchParams.set(key, v.toString());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
newUrl.searchParams.set(key, value.toString());
|
|
51
|
+
});
|
|
52
|
+
goTo(newUrl, { replace });
|
|
53
|
+
};
|
|
54
|
+
export const createRoute = (path, validate = () => true) => {
|
|
55
|
+
if (path === "/" && router.value.pathParts.length === 0) {
|
|
56
|
+
return { matches: true, params: {} };
|
|
57
|
+
}
|
|
58
|
+
const routeParts = path.split("/").filter(Boolean);
|
|
59
|
+
const currentParts = router.value.pathParts;
|
|
60
|
+
const params = {};
|
|
61
|
+
let routeIndex = 0;
|
|
62
|
+
let currentIndex = 0;
|
|
63
|
+
while (routeIndex < routeParts.length && currentIndex < currentParts.length) {
|
|
64
|
+
const routePart = routeParts[routeIndex];
|
|
65
|
+
const currentPart = currentParts[currentIndex];
|
|
66
|
+
if (routePart.startsWith(":")) {
|
|
67
|
+
const isOptional = routePart.endsWith("?");
|
|
68
|
+
const paramKey = isOptional ? routePart.slice(1, -1) : routePart.slice(1);
|
|
69
|
+
params[paramKey] = currentPart;
|
|
70
|
+
routeIndex++;
|
|
71
|
+
currentIndex++;
|
|
72
|
+
}
|
|
73
|
+
else if (routePart === currentPart) {
|
|
74
|
+
routeIndex++;
|
|
75
|
+
currentIndex++;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return { matches: false, params: {} };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
while (routeIndex < routeParts.length) {
|
|
82
|
+
const routePart = routeParts[routeIndex];
|
|
83
|
+
if (routePart.startsWith(":") && routePart.endsWith("?")) {
|
|
84
|
+
const paramKey = routePart.slice(1, -1);
|
|
85
|
+
params[paramKey] = undefined;
|
|
86
|
+
routeIndex++;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return { matches: false, params: {} };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const matches = currentIndex === currentParts.length;
|
|
93
|
+
const route = { matches, params };
|
|
94
|
+
return { matches: matches && validate(route), params };
|
|
95
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { effect } from "@preact/signals-core";
|
|
2
|
+
import { SELECTED_THEMES, THEME_DARK, THEME_LIGHT } from "./constants.js";
|
|
3
|
+
import { currentTheme, preferredTheme, userTheme } from "./store.js";
|
|
4
|
+
import { getPrefersColorSchemeMQ } from "./utils.js";
|
|
5
|
+
const initThemeStore = () => {
|
|
6
|
+
if (typeof window === "undefined") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
effect(() => {
|
|
10
|
+
document.documentElement.setAttribute("data-theme", currentTheme.value);
|
|
11
|
+
});
|
|
12
|
+
const preffersMediaQuery = getPrefersColorSchemeMQ();
|
|
13
|
+
preffersMediaQuery.addEventListener("change", (event) => {
|
|
14
|
+
if (event.matches) {
|
|
15
|
+
preferredTheme.value = THEME_DARK;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
preferredTheme.value = THEME_LIGHT;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
export const setUserTheme = (theme) => {
|
|
23
|
+
userTheme.value = theme;
|
|
24
|
+
};
|
|
25
|
+
export const toggleUserTheme = () => {
|
|
26
|
+
const current = userTheme.peek();
|
|
27
|
+
const next = SELECTED_THEMES[(SELECTED_THEMES.indexOf(current) + 1) % SELECTED_THEMES.length];
|
|
28
|
+
setUserTheme(next);
|
|
29
|
+
};
|
|
30
|
+
initThemeStore();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UserTheme } from "./types.js";
|
|
2
|
+
export declare const THEME_AUTO = "auto";
|
|
3
|
+
export declare const THEME_LIGHT = "light";
|
|
4
|
+
export declare const THEME_DARK = "dark";
|
|
5
|
+
export declare const STORAGE_KEY = "theme";
|
|
6
|
+
export declare const SELECTED_THEMES: UserTheme[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { UITheme, UserTheme } from "./types.js";
|
|
2
|
+
export declare const currentTheme: import("@preact/signals-core").ReadonlySignal<UITheme>;
|
|
3
|
+
export declare const userTheme: import("@preact/signals-core").Signal<UserTheme>;
|
|
4
|
+
export declare const preferredTheme: import("@preact/signals-core").Signal<UITheme>;
|
|
5
|
+
export declare const themeStore: import("@preact/signals-core").ReadonlySignal<{
|
|
6
|
+
readonly current: UITheme;
|
|
7
|
+
readonly selected: UserTheme;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { computed, signal } from "@preact/signals-core";
|
|
2
|
+
import { getReportOptions } from "../../data.js";
|
|
3
|
+
import { persistSignal, restoreSignal } from "../persister/index.js";
|
|
4
|
+
import { STORAGE_KEY, THEME_AUTO, THEME_DARK, THEME_LIGHT } from "./constants.js";
|
|
5
|
+
import { getPrefersColorSchemeMQ, isAcceptedThemeValue, isAutoTheme } from "./utils.js";
|
|
6
|
+
const reportConfigTheme = getReportOptions()?.theme;
|
|
7
|
+
const getInitialPreferredTheme = () => {
|
|
8
|
+
if (typeof window === "undefined") {
|
|
9
|
+
return THEME_LIGHT;
|
|
10
|
+
}
|
|
11
|
+
if (getPrefersColorSchemeMQ().matches) {
|
|
12
|
+
return THEME_DARK;
|
|
13
|
+
}
|
|
14
|
+
return THEME_LIGHT;
|
|
15
|
+
};
|
|
16
|
+
export const currentTheme = computed(() => {
|
|
17
|
+
if (isAutoTheme(userTheme.value)) {
|
|
18
|
+
return preferredTheme.value;
|
|
19
|
+
}
|
|
20
|
+
return userTheme.value;
|
|
21
|
+
});
|
|
22
|
+
export const userTheme = signal(THEME_AUTO);
|
|
23
|
+
export const preferredTheme = signal(getInitialPreferredTheme());
|
|
24
|
+
restoreSignal({
|
|
25
|
+
signal: userTheme,
|
|
26
|
+
key: STORAGE_KEY,
|
|
27
|
+
onRestore: (value) => {
|
|
28
|
+
if (isAcceptedThemeValue(value)) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
return reportConfigTheme ?? THEME_AUTO;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
persistSignal({
|
|
35
|
+
signal: userTheme,
|
|
36
|
+
key: STORAGE_KEY,
|
|
37
|
+
});
|
|
38
|
+
export const themeStore = computed(() => ({
|
|
39
|
+
current: currentTheme.value,
|
|
40
|
+
selected: userTheme.value,
|
|
41
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { UserTheme } from "./types.js";
|
|
2
|
+
export declare const getPrefersColorSchemeMQ: () => MediaQueryList;
|
|
3
|
+
export declare const isAcceptedThemeValue: (value: unknown) => value is UserTheme;
|
|
4
|
+
export declare const isAutoTheme: (theme: UserTheme) => theme is "auto";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SELECTED_THEMES, THEME_AUTO } from "./constants.js";
|
|
2
|
+
const nullMediaQueryList = {
|
|
3
|
+
matches: false,
|
|
4
|
+
addEventListener: () => { },
|
|
5
|
+
removeEventListener: () => { },
|
|
6
|
+
media: "",
|
|
7
|
+
onchange: () => { },
|
|
8
|
+
addListener: () => { },
|
|
9
|
+
removeListener: () => { },
|
|
10
|
+
dispatchEvent: () => true,
|
|
11
|
+
};
|
|
12
|
+
export const getPrefersColorSchemeMQ = () => {
|
|
13
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
14
|
+
return nullMediaQueryList;
|
|
15
|
+
}
|
|
16
|
+
return window.matchMedia("(prefers-color-scheme: dark)");
|
|
17
|
+
};
|
|
18
|
+
export const isAcceptedThemeValue = (value) => {
|
|
19
|
+
return SELECTED_THEMES.includes(value);
|
|
20
|
+
};
|
|
21
|
+
export const isAutoTheme = (theme) => {
|
|
22
|
+
return theme === THEME_AUTO;
|
|
23
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const subscribeToUrlChange: (callback: () => void) => (() => void) | undefined;
|
|
2
|
+
type NavigateTo = URL | string | {
|
|
3
|
+
path: string;
|
|
4
|
+
};
|
|
5
|
+
type NavigateToOptions = {
|
|
6
|
+
replace?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare const goTo: (to: NavigateTo, options?: NavigateToOptions) => void;
|
|
9
|
+
export declare const getCurrentUrl: () => string;
|
|
10
|
+
export declare const paramsToSearchParams: (params: Record<string, string | string[]>, searchParams?: URLSearchParams) => URLSearchParams;
|
|
11
|
+
export declare const searchParamsToParams: (searchParams: URLSearchParams) => Record<string, string | string[]>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const subscribeToUrlChange = (callback) => {
|
|
2
|
+
if (typeof window === "undefined") {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
window.addEventListener("popstate", callback);
|
|
6
|
+
window.addEventListener("replaceState", callback);
|
|
7
|
+
window.addEventListener("pushState", callback);
|
|
8
|
+
window.addEventListener("hashchange", callback);
|
|
9
|
+
return () => {
|
|
10
|
+
window.removeEventListener("popstate", callback);
|
|
11
|
+
window.removeEventListener("replaceState", callback);
|
|
12
|
+
window.removeEventListener("pushState", callback);
|
|
13
|
+
window.removeEventListener("hashchange", callback);
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
const getUrl = (to) => {
|
|
17
|
+
if (typeof to === "string") {
|
|
18
|
+
return to;
|
|
19
|
+
}
|
|
20
|
+
if (to instanceof URL) {
|
|
21
|
+
return to;
|
|
22
|
+
}
|
|
23
|
+
return new URL(to.path, getCurrentUrl());
|
|
24
|
+
};
|
|
25
|
+
export const goTo = (to, options) => {
|
|
26
|
+
if (typeof window === "undefined") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const url = getUrl(to);
|
|
30
|
+
if (options?.replace) {
|
|
31
|
+
window.history.replaceState(null, "", url);
|
|
32
|
+
window.dispatchEvent(new Event("replaceState"));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
window.history.pushState(null, "", url);
|
|
36
|
+
window.dispatchEvent(new Event("pushState"));
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export const getCurrentUrl = () => {
|
|
40
|
+
if (typeof window === "undefined") {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
return window.location.href;
|
|
44
|
+
};
|
|
45
|
+
export const paramsToSearchParams = (params, searchParams = new URLSearchParams()) => {
|
|
46
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
for (const v of value) {
|
|
49
|
+
searchParams.append(key, v);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
searchParams.set(key, value);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return searchParams;
|
|
57
|
+
};
|
|
58
|
+
export const searchParamsToParams = (searchParams) => {
|
|
59
|
+
const params = {};
|
|
60
|
+
searchParams.forEach((value, key) => {
|
|
61
|
+
if (key in params) {
|
|
62
|
+
if (Array.isArray(params[key])) {
|
|
63
|
+
params[key].push(value);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
params[key] = [params[key], value];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
params[key] = value;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return params;
|
|
74
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const currentUrlSignal: import("@preact/signals-core").Signal<string>;
|
|
2
|
+
export declare const urlSearchParams: import("@preact/signals-core").ReadonlySignal<URLSearchParams>;
|
|
3
|
+
export type Param = {
|
|
4
|
+
key: string;
|
|
5
|
+
value: string | string[] | undefined;
|
|
6
|
+
};
|
|
7
|
+
export declare const setParams: (...params: Param[]) => void;
|
|
8
|
+
export declare const currentUrl: import("@preact/signals-core").ReadonlySignal<{
|
|
9
|
+
readonly hash: string;
|
|
10
|
+
readonly pathname: string;
|
|
11
|
+
readonly origin: string;
|
|
12
|
+
readonly params: Record<string, string | string[]>;
|
|
13
|
+
}>;
|
|
14
|
+
export declare const getParamValue: (key: string) => string | null;
|
|
15
|
+
export declare const getParamValues: (key: string) => string[];
|
|
16
|
+
export declare const hasParam: (key: string) => boolean;
|
|
17
|
+
export declare const getParamValueComputed: (key: string) => import("@preact/signals-core").ReadonlySignal<string | null>;
|
|
18
|
+
export declare const getParamValuesComputed: (key: string) => import("@preact/signals-core").ReadonlySignal<string[]>;
|
|
19
|
+
export declare const hasParamComputed: (key: string) => import("@preact/signals-core").ReadonlySignal<boolean>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { computed, signal } from "@preact/signals-core";
|
|
2
|
+
import { getCurrentUrl, goTo, searchParamsToParams, subscribeToUrlChange } from "./helpers.js";
|
|
3
|
+
export const currentUrlSignal = signal(getCurrentUrl());
|
|
4
|
+
subscribeToUrlChange(() => {
|
|
5
|
+
if (currentUrlSignal.peek() === getCurrentUrl()) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
currentUrlSignal.value = getCurrentUrl();
|
|
9
|
+
});
|
|
10
|
+
const urlSignal = computed(() => new URL(currentUrlSignal.value));
|
|
11
|
+
export const urlSearchParams = computed(() => urlSignal.value.searchParams);
|
|
12
|
+
export const setParams = (...params) => {
|
|
13
|
+
const newUrl = new URL(getCurrentUrl());
|
|
14
|
+
for (const param of params) {
|
|
15
|
+
newUrl.searchParams.delete(param.key);
|
|
16
|
+
if (Array.isArray(param.value)) {
|
|
17
|
+
for (const value of param.value) {
|
|
18
|
+
newUrl.searchParams.append(param.key, value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else if (typeof param.value === "string") {
|
|
22
|
+
newUrl.searchParams.set(param.key, param.value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (newUrl.href === urlSignal.peek().href) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
goTo(newUrl.href, {
|
|
29
|
+
replace: true,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
export const currentUrl = computed(() => {
|
|
33
|
+
return {
|
|
34
|
+
hash: urlSignal.value.hash,
|
|
35
|
+
pathname: urlSignal.value.pathname,
|
|
36
|
+
origin: urlSignal.value.origin,
|
|
37
|
+
params: searchParamsToParams(urlSearchParams.value),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
export const getParamValue = (key) => urlSearchParams.value.get(key);
|
|
41
|
+
export const getParamValues = (key) => urlSearchParams.value.getAll(key);
|
|
42
|
+
export const hasParam = (key) => urlSearchParams.value.has(key);
|
|
43
|
+
export const getParamValueComputed = (key) => computed(() => getParamValue(key));
|
|
44
|
+
export const getParamValuesComputed = (key) => computed(() => getParamValues(key));
|
|
45
|
+
export const hasParamComputed = (key) => computed(() => hasParam(key));
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { signal } from "@preact/signals-core";
|
|
2
|
+
const signalExample = signal(1);
|
|
3
|
+
export const isSignal = (value) => {
|
|
4
|
+
return (typeof value === "object" &&
|
|
5
|
+
value !== null &&
|
|
6
|
+
"brand" in value &&
|
|
7
|
+
typeof value.brand === "symbol" &&
|
|
8
|
+
value.brand === signalExample.brand);
|
|
9
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/web-commons",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Collection of utilities used across the web Allure reports",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -20,12 +20,16 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "run clean && tsc --project ./tsconfig.json",
|
|
23
|
-
"clean": "rimraf ./dist"
|
|
23
|
+
"clean": "rimraf ./dist",
|
|
24
|
+
"test": "vitest run"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
|
-
"@allurereport/
|
|
27
|
-
"@allurereport/
|
|
28
|
-
"@allurereport/
|
|
27
|
+
"@allurereport/aql": "3.2.0",
|
|
28
|
+
"@allurereport/charts-api": "3.2.0",
|
|
29
|
+
"@allurereport/core-api": "3.2.0",
|
|
30
|
+
"@allurereport/plugin-api": "3.2.0",
|
|
31
|
+
"@preact/signals": "^2.6.1",
|
|
32
|
+
"@preact/signals-core": "^1.12.2",
|
|
29
33
|
"ansi-to-html": "^0.7.2",
|
|
30
34
|
"d3-interpolate": "^3.0.1",
|
|
31
35
|
"d3-scale": "^4.0.2",
|
|
@@ -51,6 +55,7 @@
|
|
|
51
55
|
"eslint-plugin-n": "^17.10.1",
|
|
52
56
|
"eslint-plugin-no-null": "^1.0.2",
|
|
53
57
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
|
58
|
+
"jsdom": "^26.1.0",
|
|
54
59
|
"rimraf": "^6.0.1",
|
|
55
60
|
"tslib": "^2.7.0",
|
|
56
61
|
"typescript": "^5.6.3",
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { AllureChartsStoreData, FBSUAgePyramidChartData, FBSUAgePyramidChartOptions } from "@allurereport/charts-api";
|
|
2
|
-
export declare const generateFBSUAgePyramid: (props: {
|
|
3
|
-
options: FBSUAgePyramidChartOptions;
|
|
4
|
-
storeData: AllureChartsStoreData;
|
|
5
|
-
}) => FBSUAgePyramidChartData;
|