@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.
Files changed (62) hide show
  1. package/dist/components/AppShell/AppShell.js +16 -5
  2. package/dist/components/AppShell/AppShellContainer.js +75 -66
  3. package/dist/components/AppShell/AppShellRouter.js +98 -109
  4. package/dist/components/AppShellI18nProvider/AppShellI18nProvider.js +19 -25
  5. package/dist/components/AppShellProvider/AppShellProvider.js +84 -91
  6. package/dist/components/AppShellViewProvider/AppShellViewProvider.js +10 -7
  7. package/dist/components/ConfigIcon.js +13 -15
  8. package/dist/components/CustomHooksInitializer/CustomHooksInitializer.js +7 -7
  9. package/dist/components/GlobalStyles.js +11 -9
  10. package/dist/components/IconUiKit/IconUiKit.js +10 -8
  11. package/dist/components/InitErrorFallback/InitErrorFallback.js +31 -23
  12. package/dist/components/SnackbarProvider/SnackbarProvider.js +17 -20
  13. package/dist/components/layout/AppShellLayout.js +50 -65
  14. package/dist/components/layout/BrandLogo/BrandLogo.js +25 -35
  15. package/dist/components/layout/BrandLogo/logos.js +38 -55
  16. package/dist/components/layout/Header/Header.js +55 -74
  17. package/dist/components/layout/HeaderActions/AppSwitcherToggle/AppSwitcherToggle.js +66 -74
  18. package/dist/components/layout/HeaderActions/AppSwitcherToggle/styles.js +12 -12
  19. package/dist/components/layout/HeaderActions/ColorModeSwitcher.js +23 -26
  20. package/dist/components/layout/HeaderActions/DynamicAction.js +22 -21
  21. package/dist/components/layout/HeaderActions/HeaderActions.js +15 -22
  22. package/dist/components/layout/HeaderActions/HelpButton/HelpButton.js +22 -31
  23. package/dist/components/layout/HeaderActions/InternalAction/InternalAction.js +25 -36
  24. package/dist/components/layout/VerticalNavigation/NavigationCollapse.js +29 -34
  25. package/dist/components/layout/VerticalNavigation/NavigationHeader.js +18 -20
  26. package/dist/components/layout/VerticalNavigation/VerticalNavigation.js +96 -130
  27. package/dist/hooks/useClearLocationState.js +10 -12
  28. package/dist/hooks/useConditionsEvaluator.js +67 -81
  29. package/dist/hooks/useCustomEventListener.js +16 -28
  30. package/dist/hooks/useFilteredModel.js +30 -27
  31. package/dist/hooks/useLocalStorage.js +26 -26
  32. package/dist/hooks/useModelFromConfig.js +43 -39
  33. package/dist/hooks/useNavigationMenuItems.js +27 -30
  34. package/dist/hooks/useNotificationsEventListener.js +35 -42
  35. package/dist/hooks/useResizeObserver.js +13 -13
  36. package/dist/hooks/useThemeEventListener.js +17 -18
  37. package/dist/i18n/constants.js +5 -6
  38. package/dist/i18n/index.js +26 -20
  39. package/dist/i18n/useI18nInit.js +72 -66
  40. package/dist/index.js +4 -3
  41. package/dist/pages/ErrorPage/ErrorPage.js +33 -32
  42. package/dist/pages/ErrorPage/Footer.js +46 -55
  43. package/dist/pages/GenericError/CatServer.js +585 -569
  44. package/dist/pages/GenericError/GenericError.js +25 -26
  45. package/dist/pages/LoadingPage/LoadingPage.js +9 -17
  46. package/dist/pages/LoadingPage/index.js +4 -3
  47. package/dist/pages/NotFound/DogeSpace.js +505 -540
  48. package/dist/pages/NotFound/NotFound.js +17 -20
  49. package/dist/pages/NotFound/index.js +2 -4
  50. package/dist/pages/RootRoute.js +32 -19
  51. package/dist/providers/BannerProvider.js +98 -123
  52. package/dist/providers/LayoutProvider.js +26 -32
  53. package/dist/providers/NavigationProvider.js +96 -107
  54. package/dist/utils/CombinedProviders.js +12 -18
  55. package/dist/utils/documentUtil.js +12 -12
  56. package/dist/utils/filterModel.js +134 -170
  57. package/dist/utils/lazyImport.js +31 -36
  58. package/dist/utils/navigationUtil.js +68 -53
  59. package/dist/utils/processConfig.js +119 -153
  60. package/package.json +8 -8
  61. package/dist/components/IconUiKit/index.js +0 -6
  62. package/dist/pages/LoadingPage/styles.js +0 -30
@@ -1,28 +1,28 @@
1
- import { useState, useEffect } from "react";
2
- const LOCAL_STORAGE_KEYS = {
3
- COLOR_MODE: "hv/user-preferences/color-mode",
4
- NAV_EXPANDED: "hv/app-shell/expanded"
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
- const useLocalStorage = (storageKey) => {
7
- const key = LOCAL_STORAGE_KEYS[storageKey] || storageKey;
8
- const [value, setValue] = useState(() => localStorage.getItem(key));
9
- const setStoredValue = (newValue) => {
10
- localStorage.setItem(key, newValue);
11
- };
12
- useEffect(() => {
13
- const handleStorageChange = (event) => {
14
- if (event.key === key) {
15
- setValue(event.newValue);
16
- }
17
- };
18
- window.addEventListener("storage", handleStorageChange);
19
- return () => {
20
- window.removeEventListener("storage", handleStorageChange);
21
- };
22
- }, [key]);
23
- return { value, setStoredValue };
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
- const useModelFromConfig = (config) => {
6
- const initialModel = useMemo(
7
- () => config ? processConfig(config) : void 0,
8
- [config]
9
- );
10
- const systemProvidersBundles = useMemo(
11
- () => (initialModel?.systemProviders ?? []).map((p) => p.bundle),
12
- [initialModel]
13
- );
14
- const conditionBundles = useMemo(
15
- () => (initialModel?.allConditions ?? []).map((c) => c.bundle),
16
- [initialModel]
17
- );
18
- const providerBundles = useMemo(
19
- () => (initialModel?.providers ?? []).map((p) => p.bundle),
20
- [initialModel]
21
- );
22
- const bundles = useMemo(
23
- () => [...systemProvidersBundles, ...conditionBundles, ...providerBundles],
24
- [systemProvidersBundles, conditionBundles, providerBundles]
25
- );
26
- const promiseFactory = useCallback(async () => {
27
- if (!initialModel) {
28
- return;
29
- }
30
- if (bundles.length === 0) {
31
- return {
32
- ...initialModel
33
- };
34
- }
35
- const preloadedBundles = await importAllBundles(bundles);
36
- return { ...initialModel, preloadedBundles };
37
- }, [initialModel, bundles]);
38
- return useAsync(promiseFactory, { dataProp: "model" });
39
- };
40
- export {
41
- useModelFromConfig
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
- import { useHvAppShellModel, useHvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE, useHvMenuItems } from "@hitachivantara/app-shell-shared";
6
- import { createNavigationMenuItems } from "../utils/navigationUtil.js";
7
- const MAX_TOP_MENU_DEPTH = 2;
8
- const useNavigationMenuItems = () => {
9
- const { pathname } = useLocation();
10
- const { navigationMode } = useHvAppShellModel();
11
- const { navigate } = useHvNavigation();
12
- const { i18n } = useHvAppShellRuntimeContext();
13
- const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, {
14
- i18n
15
- });
16
- const { items, selectedMenuItemId, rootMenuItemId } = useHvMenuItems();
17
- const navigationMenuItemsTmp = createNavigationMenuItems(
18
- tConfig,
19
- items,
20
- navigationMode === "ONLY_TOP" ? MAX_TOP_MENU_DEPTH : void 0
21
- );
22
- useEffect(() => {
23
- if (!selectedMenuItemId && pathname === "/" && items[0]?.href) {
24
- navigate(items[0].href);
25
- }
26
- }, [pathname, items, navigate, selectedMenuItemId]);
27
- return {
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
- const useNotificationsEventListener = () => {
4
- const { enqueueSnackbar } = useHvSnackbar();
5
- const { show } = useBannerContext();
6
- const handleCustomEventSnackbar = (notification) => {
7
- const { message, variant, actions, onAction } = notification;
8
- let snackbarContentProps;
9
- if (actions) {
10
- snackbarContentProps = {
11
- action: Array.isArray(actions) ? actions[0] : actions,
12
- onAction
13
- };
14
- }
15
- if (message) {
16
- enqueueSnackbar(message, {
17
- variant: variant ?? "default",
18
- snackbarContentProps
19
- });
20
- }
21
- };
22
- const handleCustomEventBanner = (notification) => {
23
- if (notification.message) {
24
- show(notification);
25
- }
26
- };
27
- const handleNotificationEvent = (event) => {
28
- const notificationEvent = event.detail;
29
- switch (notificationEvent.type) {
30
- case "snackbar":
31
- handleCustomEventSnackbar(notificationEvent);
32
- break;
33
- case "banner":
34
- handleCustomEventBanner(notificationEvent);
35
- break;
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
- useEffect(() => {
4
- if (!ref.current) return void 0;
5
- const observer = new ResizeObserver(([entry]) => {
6
- onResize(entry.contentRect.width, entry.contentRect.height);
7
- });
8
- observer.observe(ref.current);
9
- return () => {
10
- observer.disconnect();
11
- };
12
- }, [onResize, ref]);
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
- export {
15
- useResizeObserver
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
- const useThemeEventListener = () => {
4
- const { selectedMode, colorModes, changeMode } = useTheme();
5
- const { setStoredValue } = useLocalStorage("COLOR_MODE");
6
- const getNextColorMode = () => {
7
- const nextIndex = colorModes.indexOf(selectedMode) + 1;
8
- return colorModes[nextIndex % 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 };
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 };
@@ -1,6 +1,5 @@
1
- import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
2
- const APP_SHELL_NAMESPACE = "appShell";
3
- export {
4
- APP_SHELL_NAMESPACE,
5
- CONFIG_TRANSLATIONS_NAMESPACE
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 };
@@ -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
- import { APP_SHELL_NAMESPACE } from "./constants.js";
5
- import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
6
- import { CONFIG_TRANSLATIONS_NAMESPACE as CONFIG_TRANSLATIONS_NAMESPACE2 } from "@hitachivantara/app-shell-shared";
7
- const createI18NextInstance = () => {
8
- const instance = createI18n({
9
- ns: [APP_SHELL_NAMESPACE, CONFIG_TRANSLATIONS_NAMESPACE],
10
- fallbackLng: "en",
11
- detection: { order: ["navigator"] },
12
- partialBundledLanguages: true,
13
- resources: {
14
- en: { [APP_SHELL_NAMESPACE]: en }
15
- }
16
- });
17
- instance.use(LanguageDetector);
18
- return instance;
19
- };
20
- export {
21
- APP_SHELL_NAMESPACE,
22
- CONFIG_TRANSLATIONS_NAMESPACE2 as CONFIG_TRANSLATIONS_NAMESPACE,
23
- createI18NextInstance
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 };
@@ -1,71 +1,77 @@
1
- import { useRef, useMemo } from "react";
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 { APP_SHELL_NAMESPACE } from "./constants.js";
6
- const toAbsoluteUrl = (url) => new URL(url, document.baseURI).href;
7
- const resolveTranslationsBaseUrl = (translationsBaseUrl, configUrl, baseUrl) => {
8
- const raw = translationsBaseUrl ?? "./";
9
- const base = configUrl ? new URL(".", toAbsoluteUrl(configUrl)).href : toAbsoluteUrl(baseUrl ?? "./");
10
- const resolved = new URL(raw, base).href;
11
- return resolved.endsWith("/") ? resolved : `${resolved}/`;
12
- };
13
- const buildResources = (translations) => {
14
- const resources = {
15
- en: {
16
- [APP_SHELL_NAMESPACE]: en,
17
- [CONFIG_TRANSLATIONS_NAMESPACE]: {}
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
- const useI18nInit = (i18n, config, configUrl) => {
31
- const reloadTriggered = useRef(false);
32
- const translationsBaseUrl = config?.translationsBaseUrl;
33
- const backendDisabled = translationsBaseUrl === false;
34
- const initOptions = useMemo(() => {
35
- return {
36
- resources: buildResources(config?.translations),
37
- partialBundledLanguages: true,
38
- ...backendDisabled ? {} : {
39
- backend: {
40
- baseUrl: resolveTranslationsBaseUrl(
41
- translationsBaseUrl,
42
- configUrl,
43
- config?.baseUrl
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
- export {
70
- useI18nInit as default
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
@@ -1,4 +1,5 @@
1
1
  import HvAppShell from "./components/AppShell/AppShell.js";
2
- export {
3
- HvAppShell as default
4
- };
2
+ //#region src/index.ts
3
+ var src_default = HvAppShell;
4
+ //#endregion
5
+ export { src_default as default };