@hitachivantara/app-shell-ui 2.3.2 → 2.3.3
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/components/AppShell/AppShell.js +16 -5
- package/dist/components/AppShell/AppShellContainer.js +75 -66
- package/dist/components/AppShell/AppShellRouter.js +98 -109
- package/dist/components/AppShellI18nProvider/AppShellI18nProvider.js +19 -25
- package/dist/components/AppShellProvider/AppShellProvider.js +84 -91
- package/dist/components/AppShellViewProvider/AppShellViewProvider.js +10 -7
- package/dist/components/ConfigIcon.js +13 -15
- package/dist/components/CustomHooksInitializer/CustomHooksInitializer.js +7 -7
- package/dist/components/GlobalStyles.js +11 -9
- package/dist/components/IconUiKit/IconUiKit.js +10 -8
- package/dist/components/InitErrorFallback/InitErrorFallback.js +31 -23
- package/dist/components/SnackbarProvider/SnackbarProvider.js +17 -20
- package/dist/components/layout/AppShellLayout.js +50 -65
- package/dist/components/layout/BrandLogo/BrandLogo.js +25 -35
- package/dist/components/layout/BrandLogo/logos.js +38 -55
- package/dist/components/layout/Header/Header.js +55 -74
- package/dist/components/layout/HeaderActions/AppSwitcherToggle/AppSwitcherToggle.js +66 -74
- package/dist/components/layout/HeaderActions/AppSwitcherToggle/styles.js +12 -12
- package/dist/components/layout/HeaderActions/ColorModeSwitcher.js +23 -26
- package/dist/components/layout/HeaderActions/DynamicAction.js +22 -21
- package/dist/components/layout/HeaderActions/HeaderActions.js +15 -22
- package/dist/components/layout/HeaderActions/HelpButton/HelpButton.js +22 -31
- package/dist/components/layout/HeaderActions/InternalAction/InternalAction.js +25 -36
- package/dist/components/layout/VerticalNavigation/NavigationCollapse.js +29 -34
- package/dist/components/layout/VerticalNavigation/NavigationHeader.js +18 -20
- package/dist/components/layout/VerticalNavigation/VerticalNavigation.js +96 -130
- package/dist/hooks/useClearLocationState.js +10 -12
- package/dist/hooks/useConditionsEvaluator.js +67 -81
- package/dist/hooks/useCustomEventListener.js +16 -28
- package/dist/hooks/useFilteredModel.js +30 -27
- package/dist/hooks/useLocalStorage.js +26 -26
- package/dist/hooks/useModelFromConfig.js +43 -39
- package/dist/hooks/useNavigationMenuItems.js +27 -30
- package/dist/hooks/useNotificationsEventListener.js +35 -42
- package/dist/hooks/useResizeObserver.js +13 -13
- package/dist/hooks/useThemeEventListener.js +17 -18
- package/dist/i18n/constants.js +5 -6
- package/dist/i18n/index.js +26 -20
- package/dist/i18n/useI18nInit.js +72 -66
- package/dist/index.js +4 -3
- package/dist/pages/ErrorPage/ErrorPage.js +33 -32
- package/dist/pages/ErrorPage/Footer.js +46 -55
- package/dist/pages/GenericError/CatServer.js +585 -569
- package/dist/pages/GenericError/GenericError.js +25 -26
- package/dist/pages/LoadingPage/LoadingPage.js +9 -17
- package/dist/pages/LoadingPage/index.js +4 -3
- package/dist/pages/NotFound/DogeSpace.js +505 -540
- package/dist/pages/NotFound/NotFound.js +17 -20
- package/dist/pages/NotFound/index.js +2 -4
- package/dist/pages/RootRoute.js +32 -19
- package/dist/providers/BannerProvider.js +98 -123
- package/dist/providers/LayoutProvider.js +26 -32
- package/dist/providers/NavigationProvider.js +96 -107
- package/dist/utils/CombinedProviders.js +12 -18
- package/dist/utils/documentUtil.js +12 -12
- package/dist/utils/filterModel.js +134 -170
- package/dist/utils/lazyImport.js +31 -36
- package/dist/utils/navigationUtil.js +68 -53
- package/dist/utils/processConfig.js +119 -153
- package/package.json +8 -8
- package/dist/components/IconUiKit/index.js +0 -6
- package/dist/pages/LoadingPage/styles.js +0 -30
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
//#region src/hooks/useLocalStorage.ts
|
|
3
|
+
var LOCAL_STORAGE_KEYS = {
|
|
4
|
+
COLOR_MODE: "hv/user-preferences/color-mode",
|
|
5
|
+
NAV_EXPANDED: "hv/app-shell/expanded"
|
|
5
6
|
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
export {
|
|
26
|
-
LOCAL_STORAGE_KEYS,
|
|
27
|
-
useLocalStorage as default
|
|
7
|
+
var useLocalStorage = (storageKey) => {
|
|
8
|
+
const key = LOCAL_STORAGE_KEYS[storageKey] || storageKey;
|
|
9
|
+
const [value, setValue] = useState(() => localStorage.getItem(key));
|
|
10
|
+
const setStoredValue = (newValue) => {
|
|
11
|
+
localStorage.setItem(key, newValue);
|
|
12
|
+
};
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const handleStorageChange = (event) => {
|
|
15
|
+
if (event.key === key) setValue(event.newValue);
|
|
16
|
+
};
|
|
17
|
+
window.addEventListener("storage", handleStorageChange);
|
|
18
|
+
return () => {
|
|
19
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
20
|
+
};
|
|
21
|
+
}, [key]);
|
|
22
|
+
return {
|
|
23
|
+
value,
|
|
24
|
+
setStoredValue
|
|
25
|
+
};
|
|
28
26
|
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { useLocalStorage as default };
|
|
@@ -1,42 +1,46 @@
|
|
|
1
|
-
import { useMemo, useCallback } from "react";
|
|
2
|
-
import { useAsync } from "@hitachivantara/app-shell-shared";
|
|
3
1
|
import { importAllBundles } from "../utils/lazyImport.js";
|
|
4
2
|
import processConfig from "../utils/processConfig.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { useAsync } from "@hitachivantara/app-shell-shared";
|
|
5
|
+
//#region src/hooks/useModelFromConfig.ts
|
|
6
|
+
/**
|
|
7
|
+
* Build an App Shell model from a configuration and preload referenced bundles.
|
|
8
|
+
*
|
|
9
|
+
* @param config - The App Shell configuration object.
|
|
10
|
+
* @returns An object containing the loading state, any error encountered, and
|
|
11
|
+
* the constructed `HvAppShellModel` with preloaded bundles.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* - Synchronously processes the provided `config` (adds internal keys and
|
|
15
|
+
* global indices to condition definitions) to produce a base `HvAppShellModel`.
|
|
16
|
+
* - Extracts bundle paths from all conditions and providers, concatenates them
|
|
17
|
+
* and preloads those bundles using `importAllBundles`.
|
|
18
|
+
* - Returns an AsyncResult-shaped value so callers can read `isPending`,
|
|
19
|
+
* `error` and the `model` property (the data property is named `model`).
|
|
20
|
+
*/
|
|
21
|
+
var useModelFromConfig = (config) => {
|
|
22
|
+
const initialModel = useMemo(() => config ? processConfig(config) : void 0, [config]);
|
|
23
|
+
const systemProvidersBundles = useMemo(() => (initialModel?.systemProviders ?? []).map((p) => p.bundle), [initialModel]);
|
|
24
|
+
const conditionBundles = useMemo(() => (initialModel?.allConditions ?? []).map((c) => c.bundle), [initialModel]);
|
|
25
|
+
const providerBundles = useMemo(() => (initialModel?.providers ?? []).map((p) => p.bundle), [initialModel]);
|
|
26
|
+
const bundles = useMemo(() => [
|
|
27
|
+
...systemProvidersBundles,
|
|
28
|
+
...conditionBundles,
|
|
29
|
+
...providerBundles
|
|
30
|
+
], [
|
|
31
|
+
systemProvidersBundles,
|
|
32
|
+
conditionBundles,
|
|
33
|
+
providerBundles
|
|
34
|
+
]);
|
|
35
|
+
return useAsync(useCallback(async () => {
|
|
36
|
+
if (!initialModel) return;
|
|
37
|
+
if (bundles.length === 0) return { ...initialModel };
|
|
38
|
+
const preloadedBundles = await importAllBundles(bundles);
|
|
39
|
+
return {
|
|
40
|
+
...initialModel,
|
|
41
|
+
preloadedBundles
|
|
42
|
+
};
|
|
43
|
+
}, [initialModel, bundles]), { dataProp: "model" });
|
|
42
44
|
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { useModelFromConfig };
|
|
@@ -1,35 +1,32 @@
|
|
|
1
|
+
import { createNavigationMenuItems } from "../utils/navigationUtil.js";
|
|
1
2
|
import { useEffect } from "react";
|
|
2
3
|
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellModel, useHvAppShellRuntimeContext, useHvMenuItems } from "@hitachivantara/app-shell-shared";
|
|
3
5
|
import { useLocation } from "react-router-dom";
|
|
4
6
|
import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
items: navigationMenuItemsTmp,
|
|
29
|
-
selectedMenuItemId,
|
|
30
|
-
rootMenuItemId
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
export {
|
|
34
|
-
useNavigationMenuItems as default
|
|
7
|
+
//#region src/hooks/useNavigationMenuItems.tsx
|
|
8
|
+
var MAX_TOP_MENU_DEPTH = 2;
|
|
9
|
+
var useNavigationMenuItems = () => {
|
|
10
|
+
const { pathname } = useLocation();
|
|
11
|
+
const { navigationMode } = useHvAppShellModel();
|
|
12
|
+
const { navigate } = useHvNavigation();
|
|
13
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
14
|
+
const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, { i18n });
|
|
15
|
+
const { items, selectedMenuItemId, rootMenuItemId } = useHvMenuItems();
|
|
16
|
+
const navigationMenuItemsTmp = createNavigationMenuItems(tConfig, items, navigationMode === "ONLY_TOP" ? MAX_TOP_MENU_DEPTH : void 0);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!selectedMenuItemId && pathname === "/" && items[0]?.href) navigate(items[0].href);
|
|
19
|
+
}, [
|
|
20
|
+
pathname,
|
|
21
|
+
items,
|
|
22
|
+
navigate,
|
|
23
|
+
selectedMenuItemId
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
items: navigationMenuItemsTmp,
|
|
27
|
+
selectedMenuItemId,
|
|
28
|
+
rootMenuItemId
|
|
29
|
+
};
|
|
35
30
|
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { useNavigationMenuItems as default };
|
|
@@ -1,44 +1,37 @@
|
|
|
1
|
-
import { useHvSnackbar } from "@hitachivantara/uikit-react-core";
|
|
2
1
|
import { useBannerContext } from "../providers/BannerProvider.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
default:
|
|
37
|
-
console.warn("Invalid notification type", notificationEvent.type);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
return { handleNotificationEvent };
|
|
41
|
-
};
|
|
42
|
-
export {
|
|
43
|
-
useNotificationsEventListener as default
|
|
2
|
+
import { useHvSnackbar } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
//#region src/hooks/useNotificationsEventListener.ts
|
|
4
|
+
var useNotificationsEventListener = () => {
|
|
5
|
+
const { enqueueSnackbar } = useHvSnackbar();
|
|
6
|
+
const { show } = useBannerContext();
|
|
7
|
+
const handleCustomEventSnackbar = (notification) => {
|
|
8
|
+
const { message, variant, actions, onAction } = notification;
|
|
9
|
+
let snackbarContentProps;
|
|
10
|
+
if (actions) snackbarContentProps = {
|
|
11
|
+
action: Array.isArray(actions) ? actions[0] : actions,
|
|
12
|
+
onAction
|
|
13
|
+
};
|
|
14
|
+
if (message) enqueueSnackbar(message, {
|
|
15
|
+
variant: variant ?? "default",
|
|
16
|
+
snackbarContentProps
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const handleCustomEventBanner = (notification) => {
|
|
20
|
+
if (notification.message) show(notification);
|
|
21
|
+
};
|
|
22
|
+
const handleNotificationEvent = (event) => {
|
|
23
|
+
const notificationEvent = event.detail;
|
|
24
|
+
switch (notificationEvent.type) {
|
|
25
|
+
case "snackbar":
|
|
26
|
+
handleCustomEventSnackbar(notificationEvent);
|
|
27
|
+
break;
|
|
28
|
+
case "banner":
|
|
29
|
+
handleCustomEventBanner(notificationEvent);
|
|
30
|
+
break;
|
|
31
|
+
default: console.warn("Invalid notification type", notificationEvent.type);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return { handleNotificationEvent };
|
|
44
35
|
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { useNotificationsEventListener as default };
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
+
//#region src/hooks/useResizeObserver.ts
|
|
2
3
|
function useResizeObserver(ref, onResize) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
if (!ref.current) return void 0;
|
|
6
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
7
|
+
onResize(entry.contentRect.width, entry.contentRect.height);
|
|
8
|
+
});
|
|
9
|
+
observer.observe(ref.current);
|
|
10
|
+
return () => {
|
|
11
|
+
observer.disconnect();
|
|
12
|
+
};
|
|
13
|
+
}, [onResize, ref]);
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { useResizeObserver };
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { useTheme } from "@hitachivantara/uikit-react-core";
|
|
2
1
|
import useLocalStorage from "./useLocalStorage.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
export {
|
|
19
|
-
useThemeEventListener as default
|
|
2
|
+
import { useTheme } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
//#region src/hooks/useThemeEventListener.ts
|
|
4
|
+
var useThemeEventListener = () => {
|
|
5
|
+
const { selectedMode, colorModes, changeMode } = useTheme();
|
|
6
|
+
const { setStoredValue } = useLocalStorage("COLOR_MODE");
|
|
7
|
+
const getNextColorMode = () => {
|
|
8
|
+
return colorModes[(colorModes.indexOf(selectedMode) + 1) % colorModes.length];
|
|
9
|
+
};
|
|
10
|
+
const handleThemeEvent = (event) => {
|
|
11
|
+
const { colorMode } = event.detail;
|
|
12
|
+
const newColorMode = !colorMode || colorModes.indexOf(colorMode) === -1 ? getNextColorMode() : colorMode;
|
|
13
|
+
changeMode(newColorMode);
|
|
14
|
+
setStoredValue(newColorMode);
|
|
15
|
+
};
|
|
16
|
+
return { handleThemeEvent };
|
|
20
17
|
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { useThemeEventListener as default };
|
package/dist/i18n/constants.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
1
|
+
import { CONFIG_TRANSLATIONS_NAMESPACE as CONFIG_TRANSLATIONS_NAMESPACE$1 } from "@hitachivantara/app-shell-shared";
|
|
2
|
+
//#region src/i18n/constants.ts
|
|
3
|
+
var APP_SHELL_NAMESPACE = "appShell";
|
|
4
|
+
//#endregion
|
|
5
|
+
export { APP_SHELL_NAMESPACE, CONFIG_TRANSLATIONS_NAMESPACE$1 as CONFIG_TRANSLATIONS_NAMESPACE };
|
package/dist/i18n/index.js
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
|
+
import { APP_SHELL_NAMESPACE, CONFIG_TRANSLATIONS_NAMESPACE } from "./constants.js";
|
|
2
|
+
import "./useI18nInit.js";
|
|
1
3
|
import LanguageDetector from "i18next-browser-languagedetector";
|
|
2
4
|
import { createI18n } from "@hitachivantara/app-shell-i18next";
|
|
3
5
|
import en from "../locales/en/appShell.json";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
6
|
+
//#region src/i18n/index.ts
|
|
7
|
+
/**
|
|
8
|
+
* Creates the i18next instance and registers the language detector, but does
|
|
9
|
+
* NOT register the HTTP backend or call init().
|
|
10
|
+
*
|
|
11
|
+
* The HTTP backend is conditionally registered by `useI18nInit` only when
|
|
12
|
+
* `translationsBaseUrl` is not `false` — this avoids the backend's `init()`
|
|
13
|
+
* being called without a `baseUrl` when HTTP loading is disabled.
|
|
14
|
+
*
|
|
15
|
+
* The instance must be created at module level so the same reference is
|
|
16
|
+
* provided to `I18nextProvider` across renders.
|
|
17
|
+
*/
|
|
18
|
+
var createI18NextInstance = () => {
|
|
19
|
+
const instance = createI18n({
|
|
20
|
+
ns: [APP_SHELL_NAMESPACE, CONFIG_TRANSLATIONS_NAMESPACE],
|
|
21
|
+
fallbackLng: "en",
|
|
22
|
+
detection: { order: ["navigator"] },
|
|
23
|
+
partialBundledLanguages: true,
|
|
24
|
+
resources: { en: { [APP_SHELL_NAMESPACE]: en } }
|
|
25
|
+
});
|
|
26
|
+
instance.use(LanguageDetector);
|
|
27
|
+
return instance;
|
|
24
28
|
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { createI18NextInstance };
|
package/dist/i18n/useI18nInit.js
CHANGED
|
@@ -1,71 +1,77 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { APP_SHELL_NAMESPACE } from "./constants.js";
|
|
2
|
+
import { useMemo, useRef } from "react";
|
|
2
3
|
import { HttpResourcesBackend, useI18n } from "@hitachivantara/app-shell-i18next";
|
|
3
|
-
import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
4
4
|
import en from "../locales/en/appShell.json";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (translations) {
|
|
21
|
-
Object.entries(translations).forEach(([lng, bundle]) => {
|
|
22
|
-
resources[lng] = {
|
|
23
|
-
...resources[lng],
|
|
24
|
-
[CONFIG_TRANSLATIONS_NAMESPACE]: bundle
|
|
25
|
-
};
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return resources;
|
|
5
|
+
import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
6
|
+
//#region src/i18n/useI18nInit.ts
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a possibly-relative URL to an absolute one.
|
|
9
|
+
* Uses `document.baseURI` as the fallback so that paths like `/foo/bar/`
|
|
10
|
+
* are turned into full URLs before being fed to the `URL` constructor as
|
|
11
|
+
* a base.
|
|
12
|
+
*/
|
|
13
|
+
var toAbsoluteUrl = (url) => new URL(url, document.baseURI).href;
|
|
14
|
+
/** Resolves the translations base URL to an absolute URL. */
|
|
15
|
+
var resolveTranslationsBaseUrl = (translationsBaseUrl, configUrl, baseUrl) => {
|
|
16
|
+
const raw = translationsBaseUrl ?? "./";
|
|
17
|
+
const base = configUrl ? new URL(".", toAbsoluteUrl(configUrl)).href : toAbsoluteUrl(baseUrl ?? "./");
|
|
18
|
+
const resolved = new URL(raw, base).href;
|
|
19
|
+
return resolved.endsWith("/") ? resolved : `${resolved}/`;
|
|
29
20
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
48
|
-
}, [
|
|
49
|
-
configUrl,
|
|
50
|
-
config?.baseUrl,
|
|
51
|
-
config?.translations,
|
|
52
|
-
translationsBaseUrl,
|
|
53
|
-
backendDisabled
|
|
54
|
-
]);
|
|
55
|
-
if (!backendDisabled && !i18n.modules.backend) {
|
|
56
|
-
i18n.use(HttpResourcesBackend);
|
|
57
|
-
}
|
|
58
|
-
const result = useI18n(i18n, initOptions);
|
|
59
|
-
if (!reloadTriggered.current && !backendDisabled) {
|
|
60
|
-
reloadTriggered.current = true;
|
|
61
|
-
i18n.reloadResources("en", [
|
|
62
|
-
APP_SHELL_NAMESPACE,
|
|
63
|
-
CONFIG_TRANSLATIONS_NAMESPACE
|
|
64
|
-
]).catch(() => {
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
21
|
+
/**
|
|
22
|
+
* Builds the `resources` object for i18next init.
|
|
23
|
+
* Always includes `en/appShell` (bundled) and `en/app` (empty placeholder).
|
|
24
|
+
* Merges any inline translations from the config per language.
|
|
25
|
+
*/
|
|
26
|
+
var buildResources = (translations) => {
|
|
27
|
+
const resources = { en: {
|
|
28
|
+
[APP_SHELL_NAMESPACE]: en,
|
|
29
|
+
[CONFIG_TRANSLATIONS_NAMESPACE]: {}
|
|
30
|
+
} };
|
|
31
|
+
if (translations) Object.entries(translations).forEach(([lng, bundle]) => {
|
|
32
|
+
resources[lng] = {
|
|
33
|
+
...resources[lng],
|
|
34
|
+
[CONFIG_TRANSLATIONS_NAMESPACE]: bundle
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return resources;
|
|
68
38
|
};
|
|
69
|
-
|
|
70
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Initializes the App Shell's i18next instance with the resolved config.
|
|
41
|
+
*
|
|
42
|
+
* - Pre-loads `appShell` (English) and config `translations` into `resources`
|
|
43
|
+
* so they're available before the backend fetches remote bundles.
|
|
44
|
+
* - Computes `translationsBaseUrl` internally from the config.
|
|
45
|
+
* - Right after the first init call, triggers `reloadResources` for both
|
|
46
|
+
* `appShell` and `app` namespaces (English) so server-side translations
|
|
47
|
+
* override the pre-bundled defaults.
|
|
48
|
+
*
|
|
49
|
+
* Returns `{ isInitialized }` — same shape as `useI18n`.
|
|
50
|
+
*/
|
|
51
|
+
var useI18nInit = (i18n, config, configUrl) => {
|
|
52
|
+
const reloadTriggered = useRef(false);
|
|
53
|
+
const translationsBaseUrl = config?.translationsBaseUrl;
|
|
54
|
+
const backendDisabled = translationsBaseUrl === false;
|
|
55
|
+
const initOptions = useMemo(() => {
|
|
56
|
+
return {
|
|
57
|
+
resources: buildResources(config?.translations),
|
|
58
|
+
partialBundledLanguages: true,
|
|
59
|
+
...backendDisabled ? {} : { backend: { baseUrl: resolveTranslationsBaseUrl(translationsBaseUrl, configUrl, config?.baseUrl) } }
|
|
60
|
+
};
|
|
61
|
+
}, [
|
|
62
|
+
configUrl,
|
|
63
|
+
config?.baseUrl,
|
|
64
|
+
config?.translations,
|
|
65
|
+
translationsBaseUrl,
|
|
66
|
+
backendDisabled
|
|
67
|
+
]);
|
|
68
|
+
if (!backendDisabled && !i18n.modules.backend) i18n.use(HttpResourcesBackend);
|
|
69
|
+
const result = useI18n(i18n, initOptions);
|
|
70
|
+
if (!reloadTriggered.current && !backendDisabled) {
|
|
71
|
+
reloadTriggered.current = true;
|
|
72
|
+
i18n.reloadResources("en", [APP_SHELL_NAMESPACE, CONFIG_TRANSLATIONS_NAMESPACE]).catch(() => {});
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
71
75
|
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { useI18nInit as default };
|
package/dist/index.js
CHANGED