@hitachivantara/app-shell-ui 2.2.7 → 2.3.1
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/AppShellContainer.js +54 -27
- package/dist/components/AppShellI18nProvider/AppShellI18nProvider.js +29 -0
- package/dist/components/AppShellProvider/AppShellProvider.js +8 -27
- package/dist/components/ConfigIcon.js +18 -0
- package/dist/components/InitErrorFallback/InitErrorFallback.js +24 -0
- package/dist/components/layout/BrandLogo/BrandLogo.js +5 -2
- package/dist/components/layout/Header/Header.js +9 -3
- package/dist/components/layout/HeaderActions/AppSwitcherToggle/AppSwitcherToggle.js +13 -6
- package/dist/components/layout/HeaderActions/ColorModeSwitcher.js +3 -0
- package/dist/components/layout/HeaderActions/HelpButton/HelpButton.js +9 -3
- package/dist/components/layout/VerticalNavigation/NavigationCollapse.js +6 -1
- package/dist/components/layout/VerticalNavigation/VerticalNavigation.js +6 -1
- package/dist/hooks/useNavigationMenuItems.js +7 -8
- package/dist/i18n/constants.js +6 -0
- package/dist/i18n/index.js +16 -25
- package/dist/i18n/useI18nInit.js +71 -0
- package/dist/locales/de/appShell.json +48 -0
- package/dist/locales/en/appShell.json +48 -0
- package/dist/locales/fr/appShell.json +48 -0
- package/dist/locales/ja/appShell.json +48 -0
- package/dist/locales/pt/appShell.json +48 -0
- package/dist/locales/supported-locales.json +1 -0
- package/dist/locales/zh-CN/appShell.json +48 -0
- package/dist/locales/zh-TW/appShell.json +48 -0
- package/dist/pages/ErrorPage/Footer.js +3 -2
- package/dist/pages/GenericError/GenericError.js +13 -3
- package/dist/pages/NotFound/NotFound.js +6 -1
- package/dist/providers/BannerProvider.js +3 -0
- package/dist/utils/navigationUtil.js +2 -2
- package/package.json +12 -10
- package/dist/i18n/localization/en.json.js +0 -17
- package/dist/i18n/localization/pt.json.js +0 -17
|
@@ -1,47 +1,74 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Suspense } from "react";
|
|
3
3
|
import { ErrorBoundary } from "react-error-boundary";
|
|
4
4
|
import { HelmetProvider } from "react-helmet-async";
|
|
5
5
|
import { I18nextProvider } from "react-i18next";
|
|
6
|
-
import { HvProvider } from "@hitachivantara/uikit-react-core";
|
|
7
|
-
import {
|
|
6
|
+
import { HvProvider, HvLoading } from "@hitachivantara/uikit-react-core";
|
|
7
|
+
import { createI18NextInstance } from "../../i18n/index.js";
|
|
8
8
|
import { LayoutProvider } from "../../providers/LayoutProvider.js";
|
|
9
|
+
import { HvAppShellI18nProvider } from "../AppShellI18nProvider/AppShellI18nProvider.js";
|
|
9
10
|
import { HvAppShellProvider } from "../AppShellProvider/AppShellProvider.js";
|
|
10
11
|
import { GlobalStyles } from "../GlobalStyles.js";
|
|
12
|
+
import InitErrorFallback from "../InitErrorFallback/InitErrorFallback.js";
|
|
13
|
+
import useI18nInit from "../../i18n/useI18nInit.js";
|
|
11
14
|
import SnackbarProvider from "../SnackbarProvider/SnackbarProvider.js";
|
|
12
15
|
import GenericError from "../../pages/GenericError/GenericError.js";
|
|
13
|
-
const i18n =
|
|
14
|
-
|
|
16
|
+
const i18n = createI18NextInstance();
|
|
17
|
+
const configRequests = /* @__PURE__ */ new Map();
|
|
18
|
+
function useConfig(configProp, configUrl) {
|
|
19
|
+
if (configProp) return configProp;
|
|
20
|
+
if (!configUrl) return void 0;
|
|
21
|
+
let cached = configRequests.get(configUrl);
|
|
22
|
+
if (!cached) {
|
|
23
|
+
const req = {
|
|
24
|
+
status: "pending",
|
|
25
|
+
promise: fetch(new URL(configUrl, document.baseURI)).then((r) => r.json()).then((data) => {
|
|
26
|
+
req.status = "fulfilled";
|
|
27
|
+
req.result = data;
|
|
28
|
+
}).catch((err) => {
|
|
29
|
+
req.status = "rejected";
|
|
30
|
+
req.error = err;
|
|
31
|
+
})
|
|
32
|
+
};
|
|
33
|
+
configRequests.set(configUrl, req);
|
|
34
|
+
cached = req;
|
|
35
|
+
}
|
|
36
|
+
if (cached.status === "fulfilled") return cached.result;
|
|
37
|
+
if (cached.status === "rejected") throw cached.error;
|
|
38
|
+
throw cached.promise;
|
|
39
|
+
}
|
|
40
|
+
function AppShellContainerInner({
|
|
15
41
|
config: configProp,
|
|
16
42
|
configUrl,
|
|
17
43
|
children
|
|
18
44
|
}) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (configProp || !configUrl) return;
|
|
23
|
-
fetch(new URL(configUrl)).then((result) => result.json()).then((data) => setLoadedConfig(data)).catch((e) => {
|
|
24
|
-
console.error(`Failed to obtain the context from: ${configUrl}`, e);
|
|
25
|
-
setLoadedConfig(void 0);
|
|
26
|
-
setHasError(true);
|
|
27
|
-
});
|
|
28
|
-
}, [configProp, configUrl]);
|
|
29
|
-
const config = useMemo(
|
|
30
|
-
() => configProp ?? loadedConfig,
|
|
31
|
-
[configProp, loadedConfig]
|
|
45
|
+
const config = useConfig(
|
|
46
|
+
configProp,
|
|
47
|
+
configUrl
|
|
32
48
|
);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
useI18nInit(i18n, config, configUrl);
|
|
50
|
+
return /* @__PURE__ */ jsx(I18nextProvider, { i18n, children: /* @__PURE__ */ jsx(HvAppShellI18nProvider, { children: /* @__PURE__ */ jsx(
|
|
51
|
+
ErrorBoundary,
|
|
52
|
+
{
|
|
53
|
+
fallback: /* @__PURE__ */ jsx(GenericError, { includeFooter: false }),
|
|
54
|
+
children: /* @__PURE__ */ jsx(HvAppShellProvider, { config, children: /* @__PURE__ */ jsx(LayoutProvider, { children: /* @__PURE__ */ jsx(SnackbarProvider, { children }) }) })
|
|
55
|
+
},
|
|
56
|
+
"general"
|
|
57
|
+
) }) });
|
|
58
|
+
}
|
|
59
|
+
function HvAppShellContainer({
|
|
60
|
+
config,
|
|
61
|
+
configUrl,
|
|
62
|
+
children
|
|
63
|
+
}) {
|
|
36
64
|
return /* @__PURE__ */ jsx(HelmetProvider, { children: /* @__PURE__ */ jsxs(HvProvider, { children: [
|
|
37
65
|
/* @__PURE__ */ jsx(GlobalStyles, {}),
|
|
38
|
-
/* @__PURE__ */ jsx(
|
|
39
|
-
|
|
66
|
+
/* @__PURE__ */ jsx(ErrorBoundary, { fallback: /* @__PURE__ */ jsx(InitErrorFallback, {}), children: /* @__PURE__ */ jsx(
|
|
67
|
+
Suspense,
|
|
40
68
|
{
|
|
41
|
-
fallback: /* @__PURE__ */ jsx(
|
|
42
|
-
children: /* @__PURE__ */ jsx(
|
|
43
|
-
}
|
|
44
|
-
"general"
|
|
69
|
+
fallback: /* @__PURE__ */ jsx(HvLoading, { style: { height: "100vh", width: "100%" } }),
|
|
70
|
+
children: /* @__PURE__ */ jsx(AppShellContainerInner, { config, configUrl, children })
|
|
71
|
+
}
|
|
45
72
|
) })
|
|
46
73
|
] }) });
|
|
47
74
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, useMemo } from "react";
|
|
3
|
+
import { I18nContext, useTranslation } from "react-i18next";
|
|
4
|
+
import { HvAppShellI18nContext } from "@hitachivantara/app-shell-shared";
|
|
5
|
+
function HvAppShellI18nProvider({
|
|
6
|
+
children
|
|
7
|
+
}) {
|
|
8
|
+
const { i18n } = useContext(I18nContext);
|
|
9
|
+
useTranslation();
|
|
10
|
+
const language = i18n.language;
|
|
11
|
+
const appShellI18n = useMemo(
|
|
12
|
+
() => ({
|
|
13
|
+
language,
|
|
14
|
+
changeLanguage: async (newLng) => {
|
|
15
|
+
if (i18n) {
|
|
16
|
+
await i18n.changeLanguage(newLng);
|
|
17
|
+
} else {
|
|
18
|
+
console.warn("I18N not initialized");
|
|
19
|
+
}
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
[i18n, language]
|
|
24
|
+
);
|
|
25
|
+
return /* @__PURE__ */ jsx(HvAppShellI18nContext.Provider, { value: appShellI18n, children });
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
HvAppShellI18nProvider
|
|
29
|
+
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useContext, useMemo, useState, useEffect } from "react";
|
|
3
3
|
import { I18nContext } from "react-i18next";
|
|
4
|
-
import { HvAppShellContext, HvAppShellModelContext,
|
|
4
|
+
import { HvAppShellRuntimeContext, HvAppShellContext, HvAppShellModelContext, HvAppShellCombinedProvidersContext } from "@hitachivantara/app-shell-shared";
|
|
5
5
|
import { themes, HvProvider } from "@hitachivantara/uikit-react-core";
|
|
6
6
|
import { useFilteredModel } from "../../hooks/useFilteredModel.js";
|
|
7
7
|
import useLocalStorage from "../../hooks/useLocalStorage.js";
|
|
8
8
|
import { useModelFromConfig } from "../../hooks/useModelFromConfig.js";
|
|
9
|
-
import { addResourceBundles } from "../../i18n/index.js";
|
|
10
9
|
import CombinedProviders from "../../utils/CombinedProviders.js";
|
|
11
10
|
const AppShellProviderInner = ({
|
|
12
11
|
config,
|
|
@@ -14,15 +13,7 @@ const AppShellProviderInner = ({
|
|
|
14
13
|
children
|
|
15
14
|
}) => {
|
|
16
15
|
const { value: storedColorModeValue } = useLocalStorage("COLOR_MODE");
|
|
17
|
-
const { i18n } = useContext(I18nContext);
|
|
18
16
|
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
17
|
const [theme, setTheme] = useState();
|
|
27
18
|
useEffect(() => {
|
|
28
19
|
const theme2 = filteredModel?.theming?.theme;
|
|
@@ -57,12 +48,6 @@ const AppShellProviderInner = ({
|
|
|
57
48
|
}
|
|
58
49
|
return providersComponents;
|
|
59
50
|
}, [filteredModel?.providers, model.preloadedBundles]);
|
|
60
|
-
const runtimeContext = useMemo(
|
|
61
|
-
() => ({
|
|
62
|
-
i18n
|
|
63
|
-
}),
|
|
64
|
-
[i18n]
|
|
65
|
-
);
|
|
66
51
|
const providersContext = useMemo(
|
|
67
52
|
() => ({
|
|
68
53
|
providers
|
|
@@ -77,25 +62,21 @@ const AppShellProviderInner = ({
|
|
|
77
62
|
if (isModelPending || !filteredModel || filteredModel.theming?.theme && !theme) {
|
|
78
63
|
return null;
|
|
79
64
|
}
|
|
80
|
-
return /* @__PURE__ */ jsx(HvAppShellContext.Provider, { value: appShellConfigContextValue, children: /* @__PURE__ */ jsx(HvAppShellModelContext.Provider, { value: appShellModelContextValue, children: /* @__PURE__ */ jsx(
|
|
65
|
+
return /* @__PURE__ */ jsx(HvAppShellContext.Provider, { value: appShellConfigContextValue, children: /* @__PURE__ */ jsx(HvAppShellModelContext.Provider, { value: appShellModelContextValue, children: /* @__PURE__ */ jsx(
|
|
81
66
|
HvProvider,
|
|
82
67
|
{
|
|
83
68
|
theme,
|
|
84
69
|
colorMode: storedColorModeValue ?? filteredModel.theming?.colorMode,
|
|
85
|
-
children: /* @__PURE__ */ jsx(
|
|
86
|
-
HvAppShellCombinedProvidersContext.Provider,
|
|
87
|
-
{
|
|
88
|
-
value: providersContext,
|
|
89
|
-
children
|
|
90
|
-
}
|
|
91
|
-
)
|
|
70
|
+
children: /* @__PURE__ */ jsx(HvAppShellCombinedProvidersContext.Provider, { value: providersContext, children })
|
|
92
71
|
}
|
|
93
|
-
) }) })
|
|
72
|
+
) }) });
|
|
94
73
|
};
|
|
95
74
|
function HvAppShellProvider({
|
|
96
75
|
children,
|
|
97
76
|
config: configProp
|
|
98
77
|
}) {
|
|
78
|
+
const { i18n } = useContext(I18nContext);
|
|
79
|
+
const runtimeContext = useMemo(() => ({ i18n }), [i18n]);
|
|
99
80
|
const { model, isPending: areBundlesLoading } = useModelFromConfig(configProp);
|
|
100
81
|
const systemProviders = useMemo(() => {
|
|
101
82
|
if (!model?.systemProviders) return void 0;
|
|
@@ -108,7 +89,7 @@ function HvAppShellProvider({
|
|
|
108
89
|
if (!configProp || !model || areBundlesLoading) {
|
|
109
90
|
return null;
|
|
110
91
|
}
|
|
111
|
-
return /* @__PURE__ */ jsx(CombinedProviders, { providers: systemProviders, children: /* @__PURE__ */ jsx(AppShellProviderInner, { config: configProp, model, children }) });
|
|
92
|
+
return /* @__PURE__ */ jsx(HvAppShellRuntimeContext.Provider, { value: runtimeContext, children: /* @__PURE__ */ jsx(CombinedProviders, { providers: systemProviders, children: /* @__PURE__ */ jsx(AppShellProviderInner, { config: configProp, model, children }) }) });
|
|
112
93
|
}
|
|
113
94
|
export {
|
|
114
95
|
HvAppShellProvider
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { HvIconContainer } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
import IconUiKit from "./IconUiKit/index.js";
|
|
4
|
+
function ConfigIcon({ icon }) {
|
|
5
|
+
if (icon?.iconType === "unocss") {
|
|
6
|
+
return (
|
|
7
|
+
// TODO(minor): remove once "uikit" & ""unocss" icon types aren't used interchangeably
|
|
8
|
+
/* @__PURE__ */ jsx(HvIconContainer, { size: "sm", style: { margin: 4 }, children: /* @__PURE__ */ jsx("div", { className: icon.name }) })
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
if (icon?.iconType === "uikit") {
|
|
12
|
+
return /* @__PURE__ */ jsx(IconUiKit, { name: icon.name });
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
ConfigIcon
|
|
18
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { HvTypography } from "@hitachivantara/uikit-react-core";
|
|
3
|
+
const InitErrorFallback = () => /* @__PURE__ */ jsxs(
|
|
4
|
+
"div",
|
|
5
|
+
{
|
|
6
|
+
style: {
|
|
7
|
+
display: "flex",
|
|
8
|
+
flexDirection: "column",
|
|
9
|
+
alignItems: "center",
|
|
10
|
+
justifyContent: "center",
|
|
11
|
+
height: "100vh",
|
|
12
|
+
width: "100%",
|
|
13
|
+
textAlign: "center",
|
|
14
|
+
padding: 24
|
|
15
|
+
},
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsx(HvTypography, { variant: "title2", children: "Initialization Error" }),
|
|
18
|
+
/* @__PURE__ */ jsx(HvTypography, { style: { marginTop: 8 }, children: "Something went wrong during initialization. Please try again later." })
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
export {
|
|
23
|
+
InitErrorFallback as default
|
|
24
|
+
};
|
|
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import styled from "@emotion/styled";
|
|
5
|
-
import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
5
|
+
import { useHvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
6
6
|
import { Hitachi, Pentaho, Lumada } from "./logos.js";
|
|
7
7
|
const LogoContainer = styled("div")({
|
|
8
8
|
height: 20,
|
|
@@ -11,7 +11,10 @@ const LogoContainer = styled("div")({
|
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
const BrandLogo = ({ logo, ...others }) => {
|
|
14
|
-
const {
|
|
14
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
15
|
+
const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, {
|
|
16
|
+
i18n
|
|
17
|
+
});
|
|
15
18
|
const LogoComponent = useMemo(() => {
|
|
16
19
|
const logoName = logo && logo.name?.toUpperCase();
|
|
17
20
|
switch (logoName) {
|
|
@@ -2,15 +2,21 @@ import { jsxs, jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { Helmet } from "react-helmet-async";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
|
|
5
|
-
import { CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
|
|
5
|
+
import { useHvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
|
|
6
6
|
import { useTheme, HvHeader, HvButton, HvHeaderBrand, HvHeaderNavigation } from "@hitachivantara/uikit-react-core";
|
|
7
7
|
import { useNavigationContext } from "../../../providers/NavigationProvider.js";
|
|
8
8
|
import IconUiKit from "../../IconUiKit/index.js";
|
|
9
9
|
import { BrandLogo } from "../BrandLogo/BrandLogo.js";
|
|
10
10
|
import HeaderActions from "../HeaderActions/HeaderActions.js";
|
|
11
11
|
const Header = () => {
|
|
12
|
-
const {
|
|
13
|
-
const { t
|
|
12
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
13
|
+
const { t } = useTranslation(void 0, {
|
|
14
|
+
i18n,
|
|
15
|
+
keyPrefix: "header.navigation"
|
|
16
|
+
});
|
|
17
|
+
const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, {
|
|
18
|
+
i18n
|
|
19
|
+
});
|
|
14
20
|
const { navigationMode, name, logo } = useHvAppShellModel();
|
|
15
21
|
const { activeTheme } = useTheme();
|
|
16
22
|
const { navigate } = useHvNavigation();
|
|
@@ -3,10 +3,11 @@ import { useState, useId, useMemo } 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, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
|
|
6
|
+
import { useHvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
|
|
7
7
|
import { HvIconButton, theme, HvAppSwitcher, HvTypography } from "@hitachivantara/uikit-react-core";
|
|
8
|
+
import { AppSwitcher } from "@hitachivantara/uikit-react-icons";
|
|
8
9
|
import createAppContainerElement from "../../../../utils/documentUtil.js";
|
|
9
|
-
import
|
|
10
|
+
import { ConfigIcon } from "../../../ConfigIcon.js";
|
|
10
11
|
import { BrandLogo } from "../../BrandLogo/BrandLogo.js";
|
|
11
12
|
import StyledAppShellPanelWrapper from "./styles.js";
|
|
12
13
|
const AppSwitcherToggle = ({
|
|
@@ -14,8 +15,14 @@ const AppSwitcherToggle = ({
|
|
|
14
15
|
apps,
|
|
15
16
|
showLogo = false
|
|
16
17
|
}) => {
|
|
17
|
-
const {
|
|
18
|
-
const { t
|
|
18
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
19
|
+
const { t } = useTranslation(void 0, {
|
|
20
|
+
i18n,
|
|
21
|
+
keyPrefix: "header.appSwitcher"
|
|
22
|
+
});
|
|
23
|
+
const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, {
|
|
24
|
+
i18n
|
|
25
|
+
});
|
|
19
26
|
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
|
20
27
|
const appSwitcherPanelId = useId();
|
|
21
28
|
const { logo } = useHvAppShellModel();
|
|
@@ -26,7 +33,7 @@ const AppSwitcherToggle = ({
|
|
|
26
33
|
description: app.description ? tConfig(app.description).toString() : void 0,
|
|
27
34
|
url: app.url?.includes(":") ? app.url : tConfig(app.url).toString(),
|
|
28
35
|
target: app.target === "NEW" ? "_blank" : "_top",
|
|
29
|
-
iconElement: app.icon && /* @__PURE__ */ jsx(
|
|
36
|
+
iconElement: app.icon && /* @__PURE__ */ jsx(ConfigIcon, { icon: app.icon })
|
|
30
37
|
}));
|
|
31
38
|
}, [apps, tConfig]);
|
|
32
39
|
if (!apps || apps.length === 0) return null;
|
|
@@ -40,7 +47,7 @@ const AppSwitcherToggle = ({
|
|
|
40
47
|
onClick: () => setIsPanelOpen(!isPanelOpen),
|
|
41
48
|
...isPanelOpen && { "aria-controls": appSwitcherPanelId },
|
|
42
49
|
children: [
|
|
43
|
-
/* @__PURE__ */ jsx(
|
|
50
|
+
/* @__PURE__ */ jsx(AppSwitcher, {}),
|
|
44
51
|
showLogo && /* @__PURE__ */ jsx(BrandLogo, { logo, style: { paddingRight: theme.space.xs } })
|
|
45
52
|
]
|
|
46
53
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useTranslation } from "react-i18next";
|
|
3
3
|
import { HvAppShellEventThemeTrigger } from "@hitachivantara/app-shell-events";
|
|
4
|
+
import { useHvAppShellRuntimeContext } from "@hitachivantara/app-shell-shared";
|
|
4
5
|
import { useTheme, HvIconButton } from "@hitachivantara/uikit-react-core";
|
|
5
6
|
import IconUiKit from "../../IconUiKit/index.js";
|
|
6
7
|
const ColorModeSwitcher = () => {
|
|
8
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
7
9
|
const { t } = useTranslation(void 0, {
|
|
10
|
+
i18n,
|
|
8
11
|
keyPrefix: "header.colorModeSwitcher"
|
|
9
12
|
});
|
|
10
13
|
const { colorModes } = useTheme();
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useTranslation } from "react-i18next";
|
|
3
|
-
import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
3
|
+
import { useHvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
4
4
|
import { HvIconButton } from "@hitachivantara/uikit-react-core";
|
|
5
5
|
import IconUiKit from "../../../IconUiKit/index.js";
|
|
6
6
|
const HelpButton = ({ url, description }) => {
|
|
7
|
-
const {
|
|
8
|
-
const { t
|
|
7
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
8
|
+
const { t } = useTranslation(void 0, {
|
|
9
|
+
i18n,
|
|
10
|
+
keyPrefix: "header.helpUrl"
|
|
11
|
+
});
|
|
12
|
+
const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, {
|
|
13
|
+
i18n
|
|
14
|
+
});
|
|
9
15
|
if (!url) {
|
|
10
16
|
return null;
|
|
11
17
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useTranslation } from "react-i18next";
|
|
3
3
|
import { css } from "@emotion/css";
|
|
4
|
+
import { useHvAppShellRuntimeContext } from "@hitachivantara/app-shell-shared";
|
|
4
5
|
import { HvVerticalNavigationAction } from "@hitachivantara/uikit-react-core";
|
|
5
6
|
import { Start, End } from "@hitachivantara/uikit-react-icons";
|
|
6
7
|
const classes = {
|
|
@@ -14,7 +15,11 @@ const classes = {
|
|
|
14
15
|
})
|
|
15
16
|
};
|
|
16
17
|
const NavigationCollapse = ({ onClick, isOpen }) => {
|
|
17
|
-
const {
|
|
18
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
19
|
+
const { t } = useTranslation(void 0, {
|
|
20
|
+
i18n,
|
|
21
|
+
keyPrefix: "verticalNavigation"
|
|
22
|
+
});
|
|
18
23
|
return /* @__PURE__ */ jsxs("div", { className: classes.root, children: [
|
|
19
24
|
isOpen && /* @__PURE__ */ jsx(Start, { className: classes.icon }),
|
|
20
25
|
/* @__PURE__ */ jsx(
|
|
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
|
|
4
4
|
import { cx, css } from "@emotion/css";
|
|
5
5
|
import ClickAwayListener from "@mui/material/ClickAwayListener";
|
|
6
6
|
import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
|
|
7
|
+
import { useHvAppShellRuntimeContext } from "@hitachivantara/app-shell-shared";
|
|
7
8
|
import { useTheme, HvVerticalNavigation, HvVerticalNavigationHeader, HvVerticalNavigationTree, HvVerticalNavigationActions, theme, verticalNavigationTreeClasses } from "@hitachivantara/uikit-react-core";
|
|
8
9
|
import { useResizeObserver } from "../../../hooks/useResizeObserver.js";
|
|
9
10
|
import { useLayoutContext } from "../../../providers/LayoutProvider.js";
|
|
@@ -33,7 +34,11 @@ const classes = {
|
|
|
33
34
|
})
|
|
34
35
|
};
|
|
35
36
|
const VerticalNavigation = () => {
|
|
36
|
-
const {
|
|
37
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
38
|
+
const { t } = useTranslation(void 0, {
|
|
39
|
+
i18n,
|
|
40
|
+
keyPrefix: "verticalNavigation"
|
|
41
|
+
});
|
|
37
42
|
const {
|
|
38
43
|
selectedMenuItemId,
|
|
39
44
|
rootMenuItemId,
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
2
3
|
import { useLocation } from "react-router-dom";
|
|
3
4
|
import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
|
|
4
|
-
import { useHvAppShellModel,
|
|
5
|
+
import { useHvAppShellModel, useHvAppShellRuntimeContext, CONFIG_TRANSLATIONS_NAMESPACE, useHvMenuItems } from "@hitachivantara/app-shell-shared";
|
|
5
6
|
import { createNavigationMenuItems } from "../utils/navigationUtil.js";
|
|
6
7
|
const MAX_TOP_MENU_DEPTH = 2;
|
|
7
8
|
const useNavigationMenuItems = () => {
|
|
8
9
|
const { pathname } = useLocation();
|
|
9
10
|
const { navigationMode } = useHvAppShellModel();
|
|
10
11
|
const { navigate } = useHvNavigation();
|
|
11
|
-
const { i18n } =
|
|
12
|
-
const tConfig =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
[i18n]
|
|
16
|
-
);
|
|
12
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
13
|
+
const { t: tConfig } = useTranslation(CONFIG_TRANSLATIONS_NAMESPACE, {
|
|
14
|
+
i18n
|
|
15
|
+
});
|
|
17
16
|
const { items, selectedMenuItemId, rootMenuItemId } = useHvMenuItems();
|
|
18
17
|
const navigationMenuItemsTmp = createNavigationMenuItems(
|
|
19
18
|
tConfig,
|
package/dist/i18n/index.js
CHANGED
|
@@ -1,33 +1,24 @@
|
|
|
1
|
-
import { initReactI18next } from "react-i18next";
|
|
2
|
-
import { createInstance } from "i18next";
|
|
3
1
|
import LanguageDetector from "i18next-browser-languagedetector";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
namespace ?? APP_SHELL_NAMESPACE,
|
|
13
|
-
value
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
};
|
|
17
|
-
const createI18Next = () => {
|
|
18
|
-
const newInstance = createInstance();
|
|
19
|
-
newInstance.use(LanguageDetector).use(initReactI18next).init({
|
|
20
|
-
defaultNS: APP_SHELL_NAMESPACE,
|
|
2
|
+
import { createI18n } from "@hitachivantara/app-shell-i18next";
|
|
3
|
+
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],
|
|
21
10
|
fallbackLng: "en",
|
|
22
11
|
detection: { order: ["navigator"] },
|
|
23
|
-
|
|
12
|
+
partialBundledLanguages: true,
|
|
13
|
+
resources: {
|
|
14
|
+
en: { [APP_SHELL_NAMESPACE]: en }
|
|
15
|
+
}
|
|
24
16
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return newInstance;
|
|
17
|
+
instance.use(LanguageDetector);
|
|
18
|
+
return instance;
|
|
28
19
|
};
|
|
29
20
|
export {
|
|
30
21
|
APP_SHELL_NAMESPACE,
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
CONFIG_TRANSLATIONS_NAMESPACE2 as CONFIG_TRANSLATIONS_NAMESPACE,
|
|
23
|
+
createI18NextInstance
|
|
33
24
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useRef, useMemo } from "react";
|
|
2
|
+
import { HttpResourcesBackend, useI18n } from "@hitachivantara/app-shell-i18next";
|
|
3
|
+
import { CONFIG_TRANSLATIONS_NAMESPACE } from "@hitachivantara/app-shell-shared";
|
|
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;
|
|
29
|
+
};
|
|
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;
|
|
68
|
+
};
|
|
69
|
+
export {
|
|
70
|
+
useI18nInit as default
|
|
71
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "Fehlende oder unvollständige Konfiguration."
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "Hoppla! Die Seite scheint im Weltraum verloren gegangen zu sein.",
|
|
9
|
+
"image_description": "404 Seite nicht gefunden"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "Hoppla! Wir haben ein Problem! Wir sind bald zurück.",
|
|
14
|
+
"image_description": "500 Allgemeiner Fehler"
|
|
15
|
+
},
|
|
16
|
+
"footer": "Klicken Sie <navigate>hier</navigate>, um zur Seite {{label}} zurückzukehren."
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "Dokumentationslink"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "Anwendungen",
|
|
24
|
+
"ariaLabel": "Anwendungsauswahl-Panel"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "Navigationspanel öffnen",
|
|
28
|
+
"closeNavigationPanel": "Navigationspanel schließen"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "Farbmodus wechseln"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "Vertikale Navigation",
|
|
36
|
+
"ariaLabelCollapse": "Vertikale Navigation einklappen",
|
|
37
|
+
"ariaLabelExpand": "Vertikale Navigation ausklappen",
|
|
38
|
+
"title": "Menü",
|
|
39
|
+
"ariaLabelSliderForwardButton": "Zum Untermenü navigieren",
|
|
40
|
+
"ariaLabelHeaderBackButton": "Zurück",
|
|
41
|
+
"collapseAction": "Menü einklappen"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "Banner schließen"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "Missing or incomplete configuration."
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "Oops! Seems like the page is lost in space.",
|
|
9
|
+
"image_description": "404 Page not found"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "Shoot! We have a problem! Be back soon.",
|
|
14
|
+
"image_description": "500 Generic error"
|
|
15
|
+
},
|
|
16
|
+
"footer": "Click <navigate>here</navigate> to go back to the {{label}} page."
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "Documentation link"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "Apps",
|
|
24
|
+
"ariaLabel": "App Switcher Panel"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "Open navigation panel",
|
|
28
|
+
"closeNavigationPanel": "Close navigation panel"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "Switch color mode"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "Vertical navigation",
|
|
36
|
+
"ariaLabelCollapse": "Collapse vertical navigation",
|
|
37
|
+
"ariaLabelExpand": "Expand vertical navigation",
|
|
38
|
+
"title": "Menu",
|
|
39
|
+
"ariaLabelSliderForwardButton": "Navigate to submenu",
|
|
40
|
+
"ariaLabelHeaderBackButton": "Back",
|
|
41
|
+
"collapseAction": "Collapse Menu"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "Close banner"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "Configuration manquante ou incomplète."
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "Oops ! La page semble perdue dans l'espace.",
|
|
9
|
+
"image_description": "404 Page non trouvée"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "Zut ! Nous avons un problème ! Nous revenons bientôt.",
|
|
14
|
+
"image_description": "500 Erreur générique"
|
|
15
|
+
},
|
|
16
|
+
"footer": "Cliquez <navigate>ici</navigate> pour revenir à la page {{label}}."
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "Lien de documentation"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "Applications",
|
|
24
|
+
"ariaLabel": "Panneau de sélection d'applications"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "Ouvrir le panneau de navigation",
|
|
28
|
+
"closeNavigationPanel": "Fermer le panneau de navigation"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "Changer le mode de couleur"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "Navigation verticale",
|
|
36
|
+
"ariaLabelCollapse": "Réduire la navigation verticale",
|
|
37
|
+
"ariaLabelExpand": "Développer la navigation verticale",
|
|
38
|
+
"title": "Menu",
|
|
39
|
+
"ariaLabelSliderForwardButton": "Naviguer vers le sous-menu",
|
|
40
|
+
"ariaLabelHeaderBackButton": "Retour",
|
|
41
|
+
"collapseAction": "Réduire le menu"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "Fermer la bannière"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "設定が見つからないか、不完全です。"
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "おっと!ページが宇宙で迷子になったようです。",
|
|
9
|
+
"image_description": "404 ページが見つかりません"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "問題が発生しました!まもなく復旧します。",
|
|
14
|
+
"image_description": "500 一般エラー"
|
|
15
|
+
},
|
|
16
|
+
"footer": "<navigate>こちら</navigate>をクリックして{{label}}ページに戻ります。"
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "ドキュメントリンク"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "アプリケーション",
|
|
24
|
+
"ariaLabel": "アプリ切替パネル"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "ナビゲーションパネルを開く",
|
|
28
|
+
"closeNavigationPanel": "ナビゲーションパネルを閉じる"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "カラーモードを切り替える"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "垂直ナビゲーション",
|
|
36
|
+
"ariaLabelCollapse": "垂直ナビゲーションを折りたたむ",
|
|
37
|
+
"ariaLabelExpand": "垂直ナビゲーションを展開する",
|
|
38
|
+
"title": "メニュー",
|
|
39
|
+
"ariaLabelSliderForwardButton": "サブメニューへ移動",
|
|
40
|
+
"ariaLabelHeaderBackButton": "戻る",
|
|
41
|
+
"collapseAction": "メニューを折りたたむ"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "バナーを閉じる"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "Configuração em falta ou incompleta."
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "Oops! Parece que a página está perdida no espaço.",
|
|
9
|
+
"image_description": "404 Página não encontrada"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "Bolas! Temos um problema! Estaremos de volta em breve.",
|
|
14
|
+
"image_description": "500 Erro genérico"
|
|
15
|
+
},
|
|
16
|
+
"footer": "Carregue <navigate>aqui</navigate> para voltar à página {{label}}."
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "Link de documentação"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "Aplicações",
|
|
24
|
+
"ariaLabel": "Painel do App Switcher"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "Abrir painel de navegação",
|
|
28
|
+
"closeNavigationPanel": "Fechar painel de navegação"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "Alternar modo de cor"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "Navegação vertical",
|
|
36
|
+
"ariaLabelCollapse": "Recolher navegação vertical",
|
|
37
|
+
"ariaLabelExpand": "Expandir navegação vertical",
|
|
38
|
+
"title": "Menu",
|
|
39
|
+
"ariaLabelSliderForwardButton": "Navegar para submenu",
|
|
40
|
+
"ariaLabelHeaderBackButton": "Voltar",
|
|
41
|
+
"collapseAction": "Fechar"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "Fechar barra de notificação"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
["en", "pt", "fr", "de", "ja", "zh-CN", "zh-TW"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "配置缺失或不完整。"
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "糟糕!页面似乎迷失在太空中了。",
|
|
9
|
+
"image_description": "404 页面未找到"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "糟糕!我们遇到了问题!很快就会恢复。",
|
|
14
|
+
"image_description": "500 一般错误"
|
|
15
|
+
},
|
|
16
|
+
"footer": "点击<navigate>此处</navigate>返回{{label}}页面。"
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "文档链接"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "应用程序",
|
|
24
|
+
"ariaLabel": "应用切换面板"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "打开导航面板",
|
|
28
|
+
"closeNavigationPanel": "关闭导航面板"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "切换颜色模式"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "垂直导航",
|
|
36
|
+
"ariaLabelCollapse": "折叠垂直导航",
|
|
37
|
+
"ariaLabelExpand": "展开垂直导航",
|
|
38
|
+
"title": "菜单",
|
|
39
|
+
"ariaLabelSliderForwardButton": "导航到子菜单",
|
|
40
|
+
"ariaLabelHeaderBackButton": "返回",
|
|
41
|
+
"collapseAction": "折叠菜单"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "关闭横幅"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"errors": {
|
|
3
|
+
"missing": {
|
|
4
|
+
"configuration": "設定遺失或不完整。"
|
|
5
|
+
},
|
|
6
|
+
"notFound": {
|
|
7
|
+
"code": "404",
|
|
8
|
+
"title": "糟糕!頁面似乎迷失在太空中了。",
|
|
9
|
+
"image_description": "404 找不到頁面"
|
|
10
|
+
},
|
|
11
|
+
"genericError": {
|
|
12
|
+
"code": "500",
|
|
13
|
+
"title": "糟糕!我們遇到了問題!很快就會恢復。",
|
|
14
|
+
"image_description": "500 一般錯誤"
|
|
15
|
+
},
|
|
16
|
+
"footer": "點擊<navigate>此處</navigate>返回{{label}}頁面。"
|
|
17
|
+
},
|
|
18
|
+
"header": {
|
|
19
|
+
"helpUrl": {
|
|
20
|
+
"documentationLink": "文件連結"
|
|
21
|
+
},
|
|
22
|
+
"appSwitcher": {
|
|
23
|
+
"title": "應用程式",
|
|
24
|
+
"ariaLabel": "應用程式切換面板"
|
|
25
|
+
},
|
|
26
|
+
"navigation": {
|
|
27
|
+
"openNavigationPanel": "開啟導覽面板",
|
|
28
|
+
"closeNavigationPanel": "關閉導覽面板"
|
|
29
|
+
},
|
|
30
|
+
"colorModeSwitcher": {
|
|
31
|
+
"ariaLabel": "切換色彩模式"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"verticalNavigation": {
|
|
35
|
+
"ariaLabelNavigationTree": "垂直導覽",
|
|
36
|
+
"ariaLabelCollapse": "摺疊垂直導覽",
|
|
37
|
+
"ariaLabelExpand": "展開垂直導覽",
|
|
38
|
+
"title": "選單",
|
|
39
|
+
"ariaLabelSliderForwardButton": "導覽至子選單",
|
|
40
|
+
"ariaLabelHeaderBackButton": "返回",
|
|
41
|
+
"collapseAction": "摺疊選單"
|
|
42
|
+
},
|
|
43
|
+
"notifications": {
|
|
44
|
+
"banner": {
|
|
45
|
+
"close": "關閉橫幅"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -2,11 +2,12 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useErrorBoundary } from "react-error-boundary";
|
|
3
3
|
import { useTranslation, Trans } from "react-i18next";
|
|
4
4
|
import { useHvNavigation } from "@hitachivantara/app-shell-navigation";
|
|
5
|
-
import { useHvAppShellModel } from "@hitachivantara/app-shell-shared";
|
|
5
|
+
import { useHvAppShellRuntimeContext, useHvAppShellModel } from "@hitachivantara/app-shell-shared";
|
|
6
6
|
import { HvTypography } from "@hitachivantara/uikit-react-core";
|
|
7
7
|
import { useNavigationContext } from "../../providers/NavigationProvider.js";
|
|
8
8
|
const Footer = () => {
|
|
9
|
-
const {
|
|
9
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
10
|
+
const { t } = useTranslation(void 0, { i18n });
|
|
10
11
|
const { navigate } = useHvNavigation();
|
|
11
12
|
const { navigationMode } = useHvAppShellModel();
|
|
12
13
|
const { resetBoundary } = useErrorBoundary();
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { lazy } from "react";
|
|
2
|
+
import { useContext, Suspense, lazy } from "react";
|
|
3
|
+
import { ErrorBoundary } from "react-error-boundary";
|
|
3
4
|
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { HvAppShellRuntimeContext } from "@hitachivantara/app-shell-shared";
|
|
4
6
|
import { ErrorPage } from "../ErrorPage/ErrorPage.js";
|
|
5
7
|
const CatServer = lazy(() => import("./CatServer.js"));
|
|
6
8
|
const GenericError = ({ includeFooter = true }) => {
|
|
7
|
-
const
|
|
9
|
+
const runtimeContext = useContext(HvAppShellRuntimeContext);
|
|
10
|
+
const { t } = useTranslation(void 0, {
|
|
11
|
+
...runtimeContext && { i18n: runtimeContext.i18n },
|
|
12
|
+
keyPrefix: "errors.genericError"
|
|
13
|
+
});
|
|
8
14
|
return /* @__PURE__ */ jsx(
|
|
9
15
|
ErrorPage,
|
|
10
16
|
{
|
|
11
17
|
code: t("code"),
|
|
12
18
|
title: t("title"),
|
|
13
19
|
includeFooter,
|
|
14
|
-
image:
|
|
20
|
+
image: (
|
|
21
|
+
// CatServer is lazy-loaded; if the chunk fails to load (e.g., 401 on session timeout),
|
|
22
|
+
// the local ErrorBoundary prevents the failure from cascading up.
|
|
23
|
+
/* @__PURE__ */ jsx(ErrorBoundary, { fallback: null, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(CatServer, { title: t("image_description") }) }) })
|
|
24
|
+
)
|
|
15
25
|
}
|
|
16
26
|
);
|
|
17
27
|
};
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { lazy } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { useHvAppShellRuntimeContext } from "@hitachivantara/app-shell-shared";
|
|
4
5
|
import { ErrorPage } from "../ErrorPage/ErrorPage.js";
|
|
5
6
|
const DogeSpace = lazy(() => import("./DogeSpace.js"));
|
|
6
7
|
const NotFound = () => {
|
|
7
|
-
const {
|
|
8
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
9
|
+
const { t } = useTranslation(void 0, {
|
|
10
|
+
i18n,
|
|
11
|
+
keyPrefix: "errors.notFound"
|
|
12
|
+
});
|
|
8
13
|
return /* @__PURE__ */ jsx(
|
|
9
14
|
ErrorPage,
|
|
10
15
|
{
|
|
@@ -3,6 +3,7 @@ import { useState, useMemo, useEffect, createContext, useContext } from "react";
|
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { css } from "@emotion/css";
|
|
5
5
|
import { uid } from "uid";
|
|
6
|
+
import { useHvAppShellRuntimeContext } from "@hitachivantara/app-shell-shared";
|
|
6
7
|
import { useTheme, theme, HvBanner } from "@hitachivantara/uikit-react-core";
|
|
7
8
|
import { useLayoutContext } from "./LayoutProvider.js";
|
|
8
9
|
import { useNavigationContext } from "./NavigationProvider.js";
|
|
@@ -13,7 +14,9 @@ const BannerContext = createContext({
|
|
|
13
14
|
}
|
|
14
15
|
});
|
|
15
16
|
const BannerProvider = ({ children }) => {
|
|
17
|
+
const { i18n } = useHvAppShellRuntimeContext();
|
|
16
18
|
const { t } = useTranslation(void 0, {
|
|
19
|
+
i18n,
|
|
17
20
|
keyPrefix: "notifications.banner"
|
|
18
21
|
});
|
|
19
22
|
const { activeTheme } = useTheme();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { ConfigIcon } from "../components/ConfigIcon.js";
|
|
3
3
|
const createNavigationMenuItems = (t, menuItems, maxDepth) => {
|
|
4
4
|
if (maxDepth !== void 0 && maxDepth <= 0) {
|
|
5
5
|
return [];
|
|
@@ -8,7 +8,7 @@ const createNavigationMenuItems = (t, menuItems, maxDepth) => {
|
|
|
8
8
|
const updatedDepth = maxDepth !== void 0 ? maxDepth - 1 : void 0;
|
|
9
9
|
const navItem = {
|
|
10
10
|
...currentValue,
|
|
11
|
-
icon: currentValue.icon
|
|
11
|
+
icon: currentValue.icon && /* @__PURE__ */ jsx(ConfigIcon, { icon: currentValue.icon }),
|
|
12
12
|
data: currentValue.data ? createNavigationMenuItems(t, currentValue.data, updatedDepth) : void 0
|
|
13
13
|
};
|
|
14
14
|
accumulator.push(navItem);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hitachivantara/app-shell-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Hitachi Vantara UI Kit Team",
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
"@emotion/react": "^11.10.5",
|
|
20
20
|
"@emotion/styled": "^11.10.5",
|
|
21
21
|
"@hitachivantara/app-shell-events": "^2.0.4",
|
|
22
|
-
"@hitachivantara/app-shell-
|
|
22
|
+
"@hitachivantara/app-shell-i18next": "^0.2.1",
|
|
23
|
+
"@hitachivantara/app-shell-navigation": "^2.1.11",
|
|
23
24
|
"@hitachivantara/app-shell-services": "^2.0.3",
|
|
24
|
-
"@hitachivantara/app-shell-shared": "^2.
|
|
25
|
-
"@hitachivantara/uikit-react-core": "^6.8.
|
|
25
|
+
"@hitachivantara/app-shell-shared": "^2.3.0",
|
|
26
|
+
"@hitachivantara/uikit-react-core": "^6.8.1",
|
|
26
27
|
"@hitachivantara/uikit-react-icons": "^6.0.4",
|
|
27
28
|
"@mui/material": "^7.0.2",
|
|
28
29
|
"i18next": "^24.2.2",
|
|
@@ -40,18 +41,19 @@
|
|
|
40
41
|
"files": [
|
|
41
42
|
"dist"
|
|
42
43
|
],
|
|
43
|
-
"publishConfig": {
|
|
44
|
-
"access": "public",
|
|
45
|
-
"directory": "package"
|
|
46
|
-
},
|
|
47
|
-
"gitHead": "f79885f320f5cdf5b438ef8b2e866965c9626ce7",
|
|
48
44
|
"exports": {
|
|
49
45
|
".": {
|
|
50
46
|
"types": "./dist/index.d.ts",
|
|
51
47
|
"default": "./dist/index.js"
|
|
52
48
|
},
|
|
53
|
-
"./package.json": "./package.json"
|
|
49
|
+
"./package.json": "./package.json",
|
|
50
|
+
"./locales/*": "./dist/locales/*"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public",
|
|
54
|
+
"directory": "package"
|
|
54
55
|
},
|
|
56
|
+
"gitHead": "96cf5b00b7c82a85b7ebae56e00d48c0b543bebc",
|
|
55
57
|
"types": "./dist/index.d.ts",
|
|
56
58
|
"module": "dist/index.js"
|
|
57
59
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const errors = { "missing": { "configuration": "Missing or incomplete configuration." }, "notFound": { "code": "404", "title": "Oops! Seems like the page is lost in space.", "image_description": "404 Page not found" }, "genericError": { "code": "500", "title": "Shoot! We have a problem! Be back soon.", "image_description": "500 Generic error" }, "footer": "Click <navigate>here</navigate> to go back to the {{label}} page." };
|
|
2
|
-
const header = { "helpUrl": { "documentationLink": "Documentation link" }, "appSwitcher": { "title": "Apps", "ariaLabel": "App Switcher Panel" }, "navigation": { "openNavigationPanel": "Open navigation panel", "closeNavigationPanel": "Close navigation panel" }, "colorModeSwitcher": { "ariaLabel": "Switch color mode" } };
|
|
3
|
-
const verticalNavigation = { "ariaLabelNavigationTree": "Vertical navigation", "ariaLabelCollapse": "Collapse vertical navigation", "ariaLabelExpand": "Expand vertical navigation", "title": "Menu", "ariaLabelSliderForwardButton": "Navigate to submenu", "ariaLabelHeaderBackButton": "Back", "collapseAction": "Collapse Menu" };
|
|
4
|
-
const notifications = { "banner": { "close": "Close banner" } };
|
|
5
|
-
const en = {
|
|
6
|
-
errors,
|
|
7
|
-
header,
|
|
8
|
-
verticalNavigation,
|
|
9
|
-
notifications
|
|
10
|
-
};
|
|
11
|
-
export {
|
|
12
|
-
en as default,
|
|
13
|
-
errors,
|
|
14
|
-
header,
|
|
15
|
-
notifications,
|
|
16
|
-
verticalNavigation
|
|
17
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const errors = { "missing": { "configuration": "Configuração em falta ou incompleta." }, "notFound": { "code": "404", "title": "Oops! Parece que a página está perdida no espaço.", "image_description": "404 Página não encontrada" }, "genericError": { "code": "500", "title": "Bolas! Temos um problema! Estaremos de volta em breve.", "image_description": "500 Erro genérico" }, "footer": "Carregue <navigate>aqui</navigate> para voltar à página {{label}}." };
|
|
2
|
-
const header = { "helpUrl": { "documentationLink": "Link de documentação" }, "appSwitcher": { "title": "Aplicações", "ariaLabel": "Painel do App Switcher" }, "navigation": { "openNavigationPanel": "Abrir painel de navegação", "closeNavigationPanel": "Fechar painel de navegação" }, "colorModeSwitcher": { "ariaLabel": "Alternar modo de cor" } };
|
|
3
|
-
const verticalNavigation = { "ariaLabelNavigationTree": "Navegação vertical", "ariaLabelCollapse": "Recolher navegação vertical", "ariaLabelExpand": "Expandir navegação vertical", "title": "Menu", "ariaLabelSliderForwardButton": "Navegar para submenu", "ariaLabelHeaderBackButton": "Voltar", "collapseAction": "Fechar" };
|
|
4
|
-
const notifications = { "banner": { "close": "Fechar barra de notificação" } };
|
|
5
|
-
const pt = {
|
|
6
|
-
errors,
|
|
7
|
-
header,
|
|
8
|
-
verticalNavigation,
|
|
9
|
-
notifications
|
|
10
|
-
};
|
|
11
|
-
export {
|
|
12
|
-
pt as default,
|
|
13
|
-
errors,
|
|
14
|
-
header,
|
|
15
|
-
notifications,
|
|
16
|
-
verticalNavigation
|
|
17
|
-
};
|