@hitachivantara/app-shell-ui 1.14.0 → 2.0.0-next.2

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 (75) hide show
  1. package/dist/components/AppShellProvider/AppShellProvider.js +149 -0
  2. package/dist/{esm/components → components}/AppShellRoutes/AppShellRoutes.js +48 -32
  3. package/dist/{esm/components → components}/layout/Header/Header.js +8 -8
  4. package/dist/{esm/components → components}/layout/Header/HeaderActions/HeaderActions.js +4 -5
  5. package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/AppSwitcherToggle/AppSwitcherToggle.js +3 -3
  6. package/dist/{esm/components → components}/layout/VerticalNavigation/NavigationHeader.js +3 -3
  7. package/dist/hooks/useConditionsEvaluator.js +87 -0
  8. package/dist/hooks/useFilteredModel.js +30 -0
  9. package/dist/hooks/useModelFromConfig.js +42 -0
  10. package/dist/{esm/hooks → hooks}/useNavigationMenuItems.js +2 -2
  11. package/dist/{esm/hooks → hooks}/useNotificationsEventListener.js +2 -2
  12. package/dist/{esm/hooks → hooks}/useThemeEventListener.js +4 -5
  13. package/dist/index.d.ts +9 -0
  14. package/dist/{esm/pages → pages}/ErrorPage/ErrorPage.js +1 -1
  15. package/dist/{esm/pages → pages}/ErrorPage/Footer.js +2 -2
  16. package/dist/pages/Root/Root.js +26 -0
  17. package/dist/{esm/providers → providers}/BannerProvider.js +1 -1
  18. package/dist/types/index.d.ts +4 -9
  19. package/dist/utils/CombinedProviders.js +20 -0
  20. package/dist/utils/filterModel.js +177 -0
  21. package/dist/utils/lazyImport.js +40 -0
  22. package/dist/utils/processConfig.js +158 -0
  23. package/package.json +13 -13
  24. package/dist/esm/components/AppShellProvider/AppShellProvider.js +0 -115
  25. package/dist/esm/pages/Root/Root.js +0 -21
  26. package/dist/esm/utils/CombinedProviders.js +0 -23
  27. /package/dist/{esm/components → components}/AppShell/AppShell.js +0 -0
  28. /package/dist/{esm/components → components}/AppShellViewProvider/AppShellViewProvider.js +0 -0
  29. /package/dist/{esm/components → components}/CustomHooksInitializer/CustomHooksInitializer.js +0 -0
  30. /package/dist/{esm/components → components}/GlobalStyles/GlobalStyles.js +0 -0
  31. /package/dist/{esm/components → components}/GlobalStyles/index.js +0 -0
  32. /package/dist/{esm/components → components}/IconUiKit/IconUiKit.js +0 -0
  33. /package/dist/{esm/components → components}/IconUiKit/index.js +0 -0
  34. /package/dist/{esm/components → components}/SnackbarProvider/SnackbarProvider.js +0 -0
  35. /package/dist/{esm/components → components}/hoc/withClickAwayListener.js +0 -0
  36. /package/dist/{esm/components → components}/hoc/withGlobalProvider.js +0 -0
  37. /package/dist/{esm/components → components}/layout/BrandLogo/BrandLogo.js +0 -0
  38. /package/dist/{esm/components → components}/layout/BrandLogo/logos.js +0 -0
  39. /package/dist/{esm/components → components}/layout/Header/HeaderActions/DynamicAction.js +0 -0
  40. /package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/AppSwitcherToggle/index.js +0 -0
  41. /package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/AppSwitcherToggle/styles.js +0 -0
  42. /package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/ColorModeSwitcher.js +0 -0
  43. /package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/HelpButton/HelpButton.js +0 -0
  44. /package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/HelpButton/index.js +0 -0
  45. /package/dist/{esm/components → components}/layout/Header/HeaderActions/InternalActions/InternalAction/InternalAction.js +0 -0
  46. /package/dist/{esm/components → components}/layout/Header/HeaderActions/index.js +0 -0
  47. /package/dist/{esm/components → components}/layout/Header/styles.js +0 -0
  48. /package/dist/{esm/components → components}/layout/Loading/Loading.js +0 -0
  49. /package/dist/{esm/components → components}/layout/Loading/styles.js +0 -0
  50. /package/dist/{esm/components → components}/layout/Main/Main.js +0 -0
  51. /package/dist/{esm/components → components}/layout/Main/styles.js +0 -0
  52. /package/dist/{esm/components → components}/layout/VerticalNavigation/NavigationCollapse.js +0 -0
  53. /package/dist/{esm/components → components}/layout/VerticalNavigation/VerticalNavigation.js +0 -0
  54. /package/dist/{esm/components → components}/layout/VerticalNavigation/styles.js +0 -0
  55. /package/dist/{esm/hooks → hooks}/useClearLocationState.js +0 -0
  56. /package/dist/{esm/hooks → hooks}/useCustomEventListener.js +0 -0
  57. /package/dist/{esm/hooks → hooks}/useLocalStorage.js +0 -0
  58. /package/dist/{esm/hooks → hooks}/useResizeObserver.js +0 -0
  59. /package/dist/{esm/i18n → i18n}/index.js +0 -0
  60. /package/dist/{esm/i18n → i18n}/localization/en.json.js +0 -0
  61. /package/dist/{esm/i18n → i18n}/localization/pt.json.js +0 -0
  62. /package/dist/{esm/index.js → index.js} +0 -0
  63. /package/dist/{esm/pages → pages}/ErrorPage/styles.js +0 -0
  64. /package/dist/{esm/pages → pages}/GenericError/500.svg.js +0 -0
  65. /package/dist/{esm/pages → pages}/GenericError/GenericError.js +0 -0
  66. /package/dist/{esm/pages → pages}/LoadingPage/LoadingPage.js +0 -0
  67. /package/dist/{esm/pages → pages}/LoadingPage/index.js +0 -0
  68. /package/dist/{esm/pages → pages}/LoadingPage/styles.js +0 -0
  69. /package/dist/{esm/pages → pages}/NotFound/404.svg.js +0 -0
  70. /package/dist/{esm/pages → pages}/NotFound/NotFound.js +0 -0
  71. /package/dist/{esm/pages → pages}/NotFound/index.js +0 -0
  72. /package/dist/{esm/providers → providers}/LayoutProvider.js +0 -0
  73. /package/dist/{esm/providers → providers}/NavigationProvider.js +0 -0
  74. /package/dist/{esm/utils → utils}/documentUtil.js +0 -0
  75. /package/dist/{esm/utils → utils}/navigationUtil.js +0 -0
@@ -0,0 +1,149 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo, useContext } from "react";
3
+ import { I18nContext } from "react-i18next";
4
+ import { HvAppShellContext, HvAppShellModelContext, HvAppShellRuntimeContext, HvAppShellCombinedProvidersContext, CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
5
+ import { themes, HvProvider } from "@hitachivantara/uikit-react-core";
6
+ import { useFilteredModel } from "../../hooks/useFilteredModel.js";
7
+ import useLocalStorage from "../../hooks/useLocalStorage.js";
8
+ import { useModelFromConfig } from "../../hooks/useModelFromConfig.js";
9
+ import { addResourceBundles } from "../../i18n/index.js";
10
+ import CombinedProviders from "../../utils/CombinedProviders.js";
11
+ const AppShellProviderInner = ({
12
+ config,
13
+ model,
14
+ children
15
+ }) => {
16
+ const { value: storedColorModeValue } = useLocalStorage("COLOR_MODE");
17
+ const { i18n } = useContext(I18nContext);
18
+ const { isPending: isModelPending, model: filteredModel } = useFilteredModel(model);
19
+ if (filteredModel?.translations) {
20
+ addResourceBundles(
21
+ i18n,
22
+ filteredModel.translations,
23
+ CONFIG_TRANSLATIONS_NAMESPACE
24
+ );
25
+ }
26
+ const [theme, setTheme] = useState();
27
+ useEffect(() => {
28
+ const theme2 = filteredModel?.theming?.theme;
29
+ if (!theme2) return;
30
+ if (themes[theme2]) {
31
+ setTheme(themes[theme2]);
32
+ return;
33
+ }
34
+ import(
35
+ /* @vite-ignore */
36
+ theme2
37
+ ).then((module) => {
38
+ setTheme(module.default);
39
+ }).catch((e) => {
40
+ console.error(`Import of theme bundle ${theme2} failed! ${e}`);
41
+ });
42
+ }, [filteredModel?.theming?.theme]);
43
+ const providers = useMemo(() => {
44
+ if (!filteredModel?.providers) {
45
+ return;
46
+ }
47
+ const providersComponents = [];
48
+ for (const { bundle, key, config: config2 } of filteredModel.providers) {
49
+ const component = model.preloadedBundles.get(
50
+ bundle
51
+ );
52
+ providersComponents.push({
53
+ key,
54
+ component,
55
+ config: config2
56
+ });
57
+ }
58
+ return providersComponents;
59
+ }, [filteredModel?.providers, model.preloadedBundles]);
60
+ const runtimeContext = useMemo(
61
+ () => ({
62
+ i18n
63
+ }),
64
+ [i18n]
65
+ );
66
+ const providersContext = useMemo(
67
+ () => ({
68
+ providers
69
+ }),
70
+ [providers]
71
+ );
72
+ const appShellConfigContextValue = useMemo(() => config, [config]);
73
+ const appShellModelContextValue = useMemo(
74
+ () => filteredModel,
75
+ [filteredModel]
76
+ );
77
+ if (isModelPending || !filteredModel || filteredModel.theming?.theme && !theme) {
78
+ return null;
79
+ }
80
+ return /* @__PURE__ */ jsx(HvAppShellContext.Provider, { value: appShellConfigContextValue, children: /* @__PURE__ */ jsx(HvAppShellModelContext.Provider, { value: appShellModelContextValue, children: /* @__PURE__ */ jsx(HvAppShellRuntimeContext.Provider, { value: runtimeContext, children: /* @__PURE__ */ jsx(
81
+ HvProvider,
82
+ {
83
+ theme,
84
+ colorMode: storedColorModeValue ?? filteredModel.theming?.colorMode,
85
+ children: /* @__PURE__ */ jsx(
86
+ HvAppShellCombinedProvidersContext.Provider,
87
+ {
88
+ value: providersContext,
89
+ children
90
+ }
91
+ )
92
+ }
93
+ ) }) }) });
94
+ };
95
+ const AppShellProvider = ({
96
+ children,
97
+ config: localConfig,
98
+ configUrl
99
+ }) => {
100
+ const [loadedConfig, setLoadedConfig] = useState(void 0);
101
+ const [hasError, setHasError] = useState(false);
102
+ useEffect(() => {
103
+ if (!localConfig && configUrl) {
104
+ fetch(new URL(configUrl)).then((result) => result.json()).then((data) => setLoadedConfig(data)).catch((e) => {
105
+ console.error(`Failed to obtain the context from: ${configUrl}`, e);
106
+ setLoadedConfig(void 0);
107
+ setHasError(true);
108
+ });
109
+ }
110
+ }, [localConfig, configUrl]);
111
+ const rawConfig = useMemo(
112
+ () => localConfig ?? loadedConfig,
113
+ [localConfig, loadedConfig]
114
+ );
115
+ const [initialConfig, setInitialConfig] = useState(void 0);
116
+ useEffect(() => {
117
+ if (rawConfig && !initialConfig) {
118
+ setInitialConfig(rawConfig);
119
+ }
120
+ }, [rawConfig, initialConfig]);
121
+ const { model, isPending: areBundlesLoading } = useModelFromConfig(initialConfig);
122
+ const systemProviders = useMemo(() => {
123
+ if (!model?.systemProviders) {
124
+ return void 0;
125
+ }
126
+ const providersComponents = [];
127
+ for (const provider of model.systemProviders) {
128
+ const component = model.preloadedBundles.get(
129
+ provider.bundle
130
+ );
131
+ providersComponents.push({
132
+ key: provider.key,
133
+ component,
134
+ config: provider.config
135
+ });
136
+ }
137
+ return providersComponents;
138
+ }, [model?.systemProviders, model?.preloadedBundles]);
139
+ if (hasError) {
140
+ throw Error("Failed to obtain the configuration");
141
+ }
142
+ if (!rawConfig || !model || areBundlesLoading) {
143
+ return null;
144
+ }
145
+ return /* @__PURE__ */ jsx(CombinedProviders, { providers: systemProviders, children: /* @__PURE__ */ jsx(AppShellProviderInner, { config: rawConfig, model, children }) });
146
+ };
147
+ export {
148
+ AppShellProvider as default
149
+ };
@@ -1,8 +1,8 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { lazy } from "react";
2
+ import { useRef, useState, useMemo, useEffect, lazy } from "react";
3
3
  import { ErrorBoundary } from "react-error-boundary";
4
- import { RouterProvider, createBrowserRouter, Outlet } from "react-router-dom";
5
- import { useHvAppShellConfig, useHvAppShellCombinedProviders } from "@hitachivantara/app-shell-shared";
4
+ import { matchRoutes, RouterProvider, createBrowserRouter, Outlet } from "react-router-dom";
5
+ import { useHvAppShellModel } from "@hitachivantara/app-shell-shared";
6
6
  import { HvContainer } from "@hitachivantara/uikit-react-core";
7
7
  import LoadingPage from "../../pages/LoadingPage/LoadingPage.js";
8
8
  import { getAppIdFromBundle } from "../../utils/navigationUtil.js";
@@ -15,13 +15,13 @@ function renderNestedRoutes(views) {
15
15
  return void 0;
16
16
  }
17
17
  return views.map((view) => {
18
- const { bundle } = view;
18
+ const { bundle, route } = view;
19
19
  const appId = getAppIdFromBundle(bundle);
20
20
  const RouteComponent = lazy(() => import(
21
21
  /* @vite-ignore */
22
22
  bundle
23
23
  ));
24
- const path = view.route.replace(/^\//, "");
24
+ const path = route.replace(/^\//, "");
25
25
  return {
26
26
  path,
27
27
  // "Component" used instead of "element" due to lazy loading
@@ -29,16 +29,16 @@ function renderNestedRoutes(views) {
29
29
  ErrorBoundary,
30
30
  {
31
31
  fallback: /* @__PURE__ */ jsx(GenericError, { fullPage: false }),
32
- children: /* @__PURE__ */ jsx(RouteComponent, { ...view.config, children: view.views != null ? /* @__PURE__ */ jsx(Outlet, {}) : null })
32
+ children: /* @__PURE__ */ jsx(RouteComponent, { ...view.config, children: view.views ? /* @__PURE__ */ jsx(Outlet, {}) : null })
33
33
  },
34
- view.route
34
+ view.key
35
35
  ) }),
36
36
  children: renderNestedRoutes(view.views)
37
37
  };
38
38
  });
39
39
  }
40
40
  function renderRoutes(mainPanel) {
41
- if (mainPanel == null || mainPanel.views == null) {
41
+ if (mainPanel?.views == null) {
42
42
  return [];
43
43
  }
44
44
  const { views, maxWidth = "xl", ...mainContainerProps } = mainPanel;
@@ -49,6 +49,8 @@ function renderRoutes(mainPanel) {
49
49
  config,
50
50
  views: nestedViews,
51
51
  maxWidth: viewMaxWidth,
52
+ key,
53
+ conditions,
52
54
  ...viewContainerProps
53
55
  } = view;
54
56
  const appId = getAppIdFromBundle(bundle);
@@ -56,25 +58,22 @@ function renderRoutes(mainPanel) {
56
58
  /* @vite-ignore */
57
59
  bundle
58
60
  ));
61
+ const containerProps = {
62
+ maxWidth: viewMaxWidth ?? maxWidth,
63
+ ...mainContainerProps,
64
+ ...viewContainerProps
65
+ };
59
66
  return {
60
67
  path: route,
61
68
  // "Component" used instead of "element" due to lazy loading
62
- Component: () => /* @__PURE__ */ jsx(
63
- HvContainer,
69
+ Component: () => /* @__PURE__ */ jsx(HvContainer, { ...containerProps, children: /* @__PURE__ */ jsx(AppShellViewProvider, { id: appId, children: /* @__PURE__ */ jsx(
70
+ ErrorBoundary,
64
71
  {
65
- maxWidth: viewMaxWidth ?? maxWidth,
66
- ...mainContainerProps,
67
- ...viewContainerProps,
68
- children: /* @__PURE__ */ jsx(AppShellViewProvider, { id: appId, children: /* @__PURE__ */ jsx(
69
- ErrorBoundary,
70
- {
71
- fallback: /* @__PURE__ */ jsx(GenericError, { fullPage: false }),
72
- children: /* @__PURE__ */ jsx(RouteComponent, { ...config, children: nestedViews != null ? /* @__PURE__ */ jsx(Outlet, {}) : null })
73
- },
74
- route
75
- ) })
76
- }
77
- ),
72
+ fallback: /* @__PURE__ */ jsx(GenericError, { fullPage: false }),
73
+ children: /* @__PURE__ */ jsx(RouteComponent, { ...config, children: nestedViews ? /* @__PURE__ */ jsx(Outlet, {}) : null })
74
+ },
75
+ view.key
76
+ ) }) }),
78
77
  children: renderNestedRoutes(nestedViews)
79
78
  };
80
79
  });
@@ -89,8 +88,28 @@ function renderErrorRoutes(mainPanel) {
89
88
  ];
90
89
  }
91
90
  const AppShellRoutes = () => {
92
- const { baseUrl, mainPanel, services } = useHvAppShellConfig();
93
- const { providers } = useHvAppShellCombinedProviders();
91
+ const { baseUrl, mainPanel } = useHvAppShellModel();
92
+ const prevRoutesRef = useRef([]);
93
+ const [routerKey, setRouterKey] = useState("router-initial");
94
+ const childRoutes = useMemo(
95
+ () => [...renderRoutes(mainPanel), ...renderErrorRoutes(mainPanel)],
96
+ [mainPanel]
97
+ );
98
+ useEffect(() => {
99
+ if (prevRoutesRef.current.length === 0) {
100
+ prevRoutesRef.current = childRoutes;
101
+ return;
102
+ }
103
+ const currentPath = globalThis.location.pathname;
104
+ const prevMatch = matchRoutes(prevRoutesRef.current, currentPath);
105
+ const newMatch = matchRoutes(childRoutes, currentPath);
106
+ const prevWas404 = !prevMatch || prevMatch.at(-1)?.route.path === "*";
107
+ const newIs404 = !newMatch || newMatch.at(-1)?.route.path === "*";
108
+ if (prevWas404 && !newIs404 || !prevWas404 && newIs404) {
109
+ setRouterKey(`router-${newIs404 ? "404" : "valid"}`);
110
+ }
111
+ prevRoutesRef.current = childRoutes;
112
+ }, [baseUrl, childRoutes]);
94
113
  return /* @__PURE__ */ jsx(
95
114
  RouterProvider,
96
115
  {
@@ -98,18 +117,15 @@ const AppShellRoutes = () => {
98
117
  router: createBrowserRouter(
99
118
  [
100
119
  {
101
- element: /* @__PURE__ */ jsx(Root, { providers, services }),
102
- // All routes live inside `RootRoute`
120
+ element: /* @__PURE__ */ jsx(Root, {}),
103
121
  errorElement: /* @__PURE__ */ jsx(GenericError, { fullPage: true }),
104
- children: [
105
- ...renderRoutes(mainPanel),
106
- ...renderErrorRoutes(mainPanel)
107
- ]
122
+ children: childRoutes
108
123
  }
109
124
  ],
110
125
  { basename: baseUrl ?? "/" }
111
126
  )
112
- }
127
+ },
128
+ routerKey
113
129
  );
114
130
  };
115
131
  export {
@@ -3,7 +3,7 @@ import { Helmet } from "react-helmet-async";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { css } from "@emotion/css";
5
5
  import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
6
- import { CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellConfig } from "@hitachivantara/app-shell-shared";
6
+ import { CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
7
7
  import { useTheme, HvHeader, HvButton, HvHeaderBrand, HvHeaderNavigation } from "@hitachivantara/uikit-react-core";
8
8
  import { useLayoutContext } from "../../../providers/LayoutProvider.js";
9
9
  import { useNavigationContext } from "../../../providers/NavigationProvider.js";
@@ -14,7 +14,7 @@ import BrandLogo from "../BrandLogo/BrandLogo.js";
14
14
  const Header = () => {
15
15
  const { t } = useTranslation(void 0, { keyPrefix: "header.navigation" });
16
16
  const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE);
17
- const appShellConfig = useHvAppShellConfig();
17
+ const { navigationMode, name, logo } = useHvAppShellModel();
18
18
  const { verticalNavigationWidth } = useLayoutContext();
19
19
  const { activeTheme } = useTheme();
20
20
  const { navigate } = useHvNavigation();
@@ -27,8 +27,8 @@ const Header = () => {
27
27
  verticalNavigationMode,
28
28
  verticalNavigationItems
29
29
  } = useNavigationContext();
30
- const isOnlyTopMode = appShellConfig.navigationMode === "ONLY_TOP";
31
- const showNavigation = !isCompactMode && appShellConfig.navigationMode !== "ONLY_LEFT" && items.length > 0;
30
+ const isOnlyTopMode = navigationMode === "ONLY_TOP";
31
+ const showNavigation = !isCompactMode && navigationMode !== "ONLY_LEFT" && items.length > 0;
32
32
  const isVerticalNavigationClosed = verticalNavigationMode === "CLOSED";
33
33
  const showVerticalNavigationButton = isCompactMode && verticalNavigationItems.length > 0;
34
34
  const handleNavigationChange = (event, selectedItem) => {
@@ -40,7 +40,7 @@ const Header = () => {
40
40
  };
41
41
  const isPentahoTheme = activeTheme?.name === "pentahoPlus";
42
42
  const shouldShrinkHeader = isPentahoTheme && verticalNavigationWidth > 0;
43
- const name = appShellConfig.name ? tConfig(appShellConfig.name) : "";
43
+ const appName = name ? tConfig(name) : "";
44
44
  return /* @__PURE__ */ jsxs(
45
45
  HvHeader,
46
46
  {
@@ -49,7 +49,7 @@ const Header = () => {
49
49
  width: shouldShrinkHeader ? `calc(100% - ${verticalNavigationWidth}px)` : void 0
50
50
  }),
51
51
  children: [
52
- /* @__PURE__ */ jsx(Helmet, { children: /* @__PURE__ */ jsx("title", { children: name }) }),
52
+ /* @__PURE__ */ jsx(Helmet, { children: /* @__PURE__ */ jsx("title", { children: appName }) }),
53
53
  showVerticalNavigationButton && /* @__PURE__ */ jsx(
54
54
  HvButton,
55
55
  {
@@ -64,9 +64,9 @@ const Header = () => {
64
64
  HvHeaderBrand,
65
65
  {
66
66
  ...!isPentahoTheme && {
67
- logo: /* @__PURE__ */ jsx(StyledIconWrapper, { children: /* @__PURE__ */ jsx(BrandLogo, { logo: appShellConfig.logo }) })
67
+ logo: /* @__PURE__ */ jsx(StyledIconWrapper, { children: /* @__PURE__ */ jsx(BrandLogo, { logo }) })
68
68
  },
69
- name
69
+ name: appName
70
70
  }
71
71
  ),
72
72
  showNavigation && /* @__PURE__ */ jsx(
@@ -1,12 +1,11 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { useHvAppShellConfig } from "@hitachivantara/app-shell-shared";
2
+ import { useHvAppShellModel } from "@hitachivantara/app-shell-shared";
3
3
  import { HvHeaderActions } from "@hitachivantara/uikit-react-core";
4
4
  import DynamicAction from "./DynamicAction.js";
5
5
  import InternalAction, { internalActions } from "./InternalActions/InternalAction/InternalAction.js";
6
6
  const HeaderActions = () => {
7
- const { header } = useHvAppShellConfig();
8
- return /* @__PURE__ */ jsx(HvHeaderActions, { children: header?.actions.map((action, index) => {
9
- const headerActionKey = `${action.bundle}${index}`;
7
+ const { header } = useHvAppShellModel();
8
+ return /* @__PURE__ */ jsx(HvHeaderActions, { children: header?.actions.map((action) => {
10
9
  const Component = internalActions.some(
11
10
  (a) => a.bundle === action.bundle
12
11
  ) ? InternalAction : DynamicAction;
@@ -16,7 +15,7 @@ const HeaderActions = () => {
16
15
  bundle: action.bundle,
17
16
  ...action.config
18
17
  },
19
- headerActionKey
18
+ action.key
20
19
  );
21
20
  }) });
22
21
  };
@@ -3,7 +3,7 @@ import { useState, useId } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { useTranslation } from "react-i18next";
5
5
  import ClickAwayListener from "@mui/material/ClickAwayListener";
6
- import { CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellConfig } from "@hitachivantara/app-shell-shared";
6
+ import { CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
7
7
  import { HvIconButton, theme, HvAppSwitcher, HvTypography } from "@hitachivantara/uikit-react-core";
8
8
  import createAppContainerElement from "../../../../../../utils/documentUtil.js";
9
9
  import IconUiKit from "../../../../../IconUiKit/index.js";
@@ -19,7 +19,7 @@ const AppSwitcherToggle = ({
19
19
  const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE);
20
20
  const [isPanelOpen, setIsPanelOpen] = useState(false);
21
21
  const appSwitcherPanelId = useId();
22
- const appShellConfig = useHvAppShellConfig();
22
+ const { logo } = useHvAppShellModel();
23
23
  const createAppsList = () => {
24
24
  return apps ? apps.map((app) => ({
25
25
  name: tConfig(app.label),
@@ -53,7 +53,7 @@ const AppSwitcherToggle = ({
53
53
  ...isPanelOpen && { "aria-controls": appSwitcherPanelId },
54
54
  children: [
55
55
  /* @__PURE__ */ jsx(IconUiKit, { name: "AppSwitcher" }),
56
- showLogo && /* @__PURE__ */ jsx(StyledIconWrapper, { style: { paddingRight: theme.space.xs }, children: /* @__PURE__ */ jsx(BrandLogo, { logo: appShellConfig.logo }) })
56
+ showLogo && /* @__PURE__ */ jsx(StyledIconWrapper, { style: { paddingRight: theme.space.xs }, children: /* @__PURE__ */ jsx(BrandLogo, { logo }) })
57
57
  ]
58
58
  }
59
59
  ),
@@ -1,13 +1,13 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useHvAppShellConfig } from "@hitachivantara/app-shell-shared";
2
+ import { useHvAppShellModel } from "@hitachivantara/app-shell-shared";
3
3
  import { AppSwitcher } from "@hitachivantara/uikit-react-icons";
4
4
  import { classes } from "./styles.js";
5
5
  import BrandLogo from "../BrandLogo/BrandLogo.js";
6
6
  const NavigationHeader = ({ isOpen }) => {
7
- const appShellConfig = useHvAppShellConfig();
7
+ const { logo } = useHvAppShellModel();
8
8
  return /* @__PURE__ */ jsxs("div", { className: classes.navigationHeader, children: [
9
9
  /* @__PURE__ */ jsx(AppSwitcher, {}),
10
- isOpen && /* @__PURE__ */ jsx(BrandLogo, { logo: appShellConfig.logo })
10
+ isOpen && /* @__PURE__ */ jsx(BrandLogo, { logo })
11
11
  ] });
12
12
  };
13
13
  export {
@@ -0,0 +1,87 @@
1
+ import { useRef } from "react";
2
+ function assert(condition, message) {
3
+ if (!condition) {
4
+ throw new Error(message || "Assertion failed");
5
+ }
6
+ }
7
+ function areArraysEqual(a, b) {
8
+ if (a.length !== b.length) {
9
+ return false;
10
+ }
11
+ for (let i = 0; i < a.length; i++) {
12
+ if (a[i] !== b[i]) {
13
+ return false;
14
+ }
15
+ }
16
+ return true;
17
+ }
18
+ const useConditionsEvaluator = (allConditions, preloadedBundles) => {
19
+ const previousResultsRef = useRef([]);
20
+ if (preloadedBundles.size === 0) {
21
+ return {
22
+ isPending: false,
23
+ error: null,
24
+ result: previousResultsRef.current
25
+ };
26
+ }
27
+ const hookResults = [];
28
+ for (const { bundle, config } of allConditions) {
29
+ const module = preloadedBundles.get(bundle);
30
+ if (!module) {
31
+ hookResults.push({
32
+ isPending: false,
33
+ error: new Error(`Bundle '${bundle}' not loaded`),
34
+ result: void 0
35
+ });
36
+ continue;
37
+ }
38
+ try {
39
+ const result2 = module(config);
40
+ hookResults.push(result2);
41
+ } catch (error) {
42
+ hookResults.push({
43
+ isPending: false,
44
+ error,
45
+ result: void 0
46
+ });
47
+ }
48
+ }
49
+ const isAnyPending = hookResults.some((result2) => result2.isPending);
50
+ const currentResults = [];
51
+ for (let i = 0; i < allConditions.length; i++) {
52
+ const condition = allConditions[i];
53
+ const asyncResult = hookResults[i];
54
+ if (asyncResult.error) {
55
+ console.error(
56
+ `Failed to execute ${condition.bundle} with: ${asyncResult.error}`
57
+ );
58
+ }
59
+ const boolResult = asyncResult.isPending || asyncResult.error ? false : asyncResult.result;
60
+ assert(
61
+ i === condition.globalIndex,
62
+ `Index mismatch: expected ${i} to equal globalIndex ${condition.globalIndex}`
63
+ );
64
+ currentResults[condition.globalIndex] = boolResult;
65
+ }
66
+ const previousResults = previousResultsRef.current;
67
+ const hasChanged = !areArraysEqual(previousResults, currentResults);
68
+ const result = hasChanged ? currentResults : previousResults;
69
+ if (hasChanged) {
70
+ previousResultsRef.current = currentResults;
71
+ }
72
+ if (isAnyPending) {
73
+ return {
74
+ isPending: true,
75
+ error: null,
76
+ result: void 0
77
+ };
78
+ }
79
+ return {
80
+ error: null,
81
+ result,
82
+ isPending: false
83
+ };
84
+ };
85
+ export {
86
+ useConditionsEvaluator
87
+ };
@@ -0,0 +1,30 @@
1
+ import { useMemo } from "react";
2
+ import filterModel from "../utils/filterModel.js";
3
+ import { useConditionsEvaluator } from "./useConditionsEvaluator.js";
4
+ const useFilteredModel = (model) => {
5
+ const { isPending: isModelPending, result } = useConditionsEvaluator(
6
+ model.allConditions,
7
+ model.preloadedBundles
8
+ );
9
+ const resolvedModel = useMemo(() => {
10
+ if (isModelPending) {
11
+ return void 0;
12
+ }
13
+ return filterModel(model, result);
14
+ }, [isModelPending, model, result]);
15
+ if (isModelPending) {
16
+ return {
17
+ error: null,
18
+ isPending: true,
19
+ model: void 0
20
+ };
21
+ }
22
+ return {
23
+ error: null,
24
+ isPending: false,
25
+ model: resolvedModel
26
+ };
27
+ };
28
+ export {
29
+ useFilteredModel
30
+ };
@@ -0,0 +1,42 @@
1
+ import { useMemo, useCallback } from "react";
2
+ import { useAsync } from "@hitachivantara/app-shell-shared";
3
+ import { importAllBundles } from "../utils/lazyImport.js";
4
+ 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
42
+ };
@@ -1,12 +1,12 @@
1
1
  import { useContext, useMemo, useEffect } from "react";
2
2
  import { useLocation } from "react-router-dom";
3
3
  import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
4
- import { useHvAppShellConfig, HvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE, useHvMenuItems } from "@hitachivantara/app-shell-shared";
4
+ import { useHvAppShellModel, HvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE, useHvMenuItems } from "@hitachivantara/app-shell-shared";
5
5
  import { createNavigationMenuItems } from "../utils/navigationUtil.js";
6
6
  const MAX_TOP_MENU_DEPTH = 2;
7
7
  const useNavigationMenuItems = () => {
8
8
  const { pathname } = useLocation();
9
- const { navigationMode } = useHvAppShellConfig();
9
+ const { navigationMode } = useHvAppShellModel();
10
10
  const { navigate } = useHvNavigation();
11
11
  const { i18n } = useContext(HvAppShellRuntimeContext) ?? {};
12
12
  const tConfig = useMemo(
@@ -4,12 +4,12 @@ const useNotificationsEventListener = () => {
4
4
  const { enqueueSnackbar } = useHvSnackbar();
5
5
  const { show } = useBannerContext();
6
6
  const handleCustomEventSnackbar = (notification) => {
7
- const { message, variant, actions, actionsCallback } = notification;
7
+ const { message, variant, actions, onAction } = notification;
8
8
  let snackbarContentProps;
9
9
  if (actions) {
10
10
  snackbarContentProps = {
11
11
  action: Array.isArray(actions) ? actions[0] : actions,
12
- actionCallback: actionsCallback
12
+ onAction
13
13
  };
14
14
  }
15
15
  if (message) {
@@ -1,17 +1,16 @@
1
1
  import { useTheme } from "@hitachivantara/uikit-react-core";
2
2
  import useLocalStorage from "./useLocalStorage.js";
3
3
  const useThemeEventListener = () => {
4
- const { selectedTheme, changeTheme, selectedMode, colorModes } = useTheme();
4
+ const { selectedMode, colorModes, changeMode } = useTheme();
5
5
  const { setStoredValue } = useLocalStorage("COLOR_MODE");
6
6
  const getNextColorMode = () => {
7
- const index = colorModes.indexOf(selectedMode);
8
- const nextIndex = (index + 1) % colorModes.length;
9
- return colorModes[nextIndex];
7
+ const nextIndex = colorModes.indexOf(selectedMode) + 1;
8
+ return colorModes[nextIndex % colorModes.length];
10
9
  };
11
10
  const handleThemeEvent = (event) => {
12
11
  const { colorMode } = event.detail;
13
12
  const newColorMode = !colorMode || colorModes.indexOf(colorMode) === -1 ? getNextColorMode() : colorMode;
14
- changeTheme(selectedTheme, newColorMode);
13
+ changeMode(newColorMode);
15
14
  setStoredValue(newColorMode);
16
15
  };
17
16
  return { handleThemeEvent };
@@ -0,0 +1,9 @@
1
+ import { JSX as JSX_2 } from 'react/jsx-runtime';
2
+
3
+ declare const _default: {
4
+ ({ ...wrappedProps }: any): JSX_2.Element;
5
+ displayName: string;
6
+ };
7
+ export default _default;
8
+
9
+ export { }
@@ -21,7 +21,7 @@ const ErrorPage = ({
21
21
  children: [
22
22
  /* @__PURE__ */ jsxs(StyledTitleWrapper, { children: [
23
23
  code && /* @__PURE__ */ jsx(HvTypography, { variant: "title1", children: code }),
24
- /* @__PURE__ */ jsx(HvTypography, { variant: isCompactMode ? "xsTitle" : "display", children: title })
24
+ /* @__PURE__ */ jsx(HvTypography, { variant: isCompactMode ? "title3" : "display", children: title })
25
25
  ] }),
26
26
  /* @__PURE__ */ jsx(
27
27
  StyledImageWrapper,