keycloak_theme/src/lib/usePrepareTemplate.ts

120 lines
3.0 KiB
TypeScript
Raw Normal View History

2023-03-17 20:40:29 +01:00
import { useReducer, useEffect } from "react";
2023-03-19 23:12:45 +01:00
import { headInsert } from "keycloakify/tools/headInsert";
import { clsx } from "keycloakify/tools/clsx";
import { assert } from "tsafe/assert";
2023-03-17 20:40:29 +01:00
export function usePrepareTemplate(params: {
styles: string[];
scripts: {
isModule: boolean;
source:
| {
type: "url";
src: string;
}
| {
type: "inline";
code: string;
};
}[];
htmlClassName: string | undefined;
bodyClassName: string | undefined;
htmlLangProperty: string | undefined;
documentTitle: string | undefined;
2023-03-17 20:40:29 +01:00
}) {
const { styles, scripts, htmlClassName, bodyClassName, htmlLangProperty, documentTitle } = params;
2023-03-17 20:40:29 +01:00
const [isReady, setReady] = useReducer(() => true, styles.length === 0 && scripts.length === 0);
2023-03-17 20:40:29 +01:00
useEffect(() => {
if (htmlLangProperty === undefined) {
return;
}
const html = document.querySelector("html");
assert(html !== null);
html.lang = htmlLangProperty;
}, [htmlLangProperty]);
useEffect(() => {
if (documentTitle === undefined) {
return;
}
document.title = documentTitle;
}, [documentTitle]);
2023-03-17 20:40:29 +01:00
useEffect(() => {
let isUnmounted = false;
const removeArray: (() => void)[] = [];
(async () => {
2023-10-09 00:49:35 +02:00
for (const style of [...styles].reverse()) {
2023-08-20 03:00:45 +02:00
const { prLoaded, remove } = headInsert({
"type": "css",
"position": "prepend",
2023-10-09 00:49:35 +02:00
"href": style
});
2023-08-20 03:00:45 +02:00
removeArray.push(remove);
2023-10-09 00:49:35 +02:00
// TODO: Find a way to do that in parallel (without breaking the order)
await prLoaded;
2023-10-09 00:49:35 +02:00
if (isUnmounted) {
return;
}
2023-03-17 20:40:29 +01:00
}
setReady();
})();
2023-03-17 20:40:29 +01:00
scripts.forEach(script => {
const { remove } = headInsert({
2023-03-17 20:40:29 +01:00
"type": "javascript",
...script
});
removeArray.push(remove);
});
2023-03-17 20:40:29 +01:00
return () => {
isUnmounted = true;
removeArray.forEach(remove => remove());
2023-03-17 20:40:29 +01:00
};
}, []);
useSetClassName({
"target": "html",
"className": htmlClassName
});
useSetClassName({
"target": "body",
"className": bodyClassName
});
return { isReady };
}
function useSetClassName(params: { target: "html" | "body"; className: string | undefined }) {
const { target, className } = params;
2023-03-17 20:40:29 +01:00
useEffect(() => {
if (className === undefined) {
return;
}
2023-03-21 03:01:49 +01:00
const htmlClassList = document.getElementsByTagName(target)[0].classList;
2023-03-17 20:40:29 +01:00
2023-03-21 03:01:49 +01:00
const tokens = clsx(className).split(" ");
2023-03-17 20:40:29 +01:00
htmlClassList.add(...tokens);
return () => {
htmlClassList.remove(...tokens);
};
}, [className]);
2023-03-17 20:40:29 +01:00
}