@etsoo/toolpad 1.0.0 → 1.0.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/README.md +1 -1
- package/build/src/Account/Account.d.ts +0 -5
- package/build/src/Account/Account.js +2 -12
- package/build/src/Account/AccountPreview.js +21 -2
- package/build/src/Account/SignInButton.js +1 -1
- package/build/src/AppProvider/AppProvider.d.ts +6 -0
- package/build/src/AppProvider/AppProviderComponent.js +3 -2
- package/build/src/DashboardLayout/DashboardLayout.d.ts +11 -0
- package/build/src/DashboardLayout/DashboardLayout.js +12 -23
- package/build/src/DashboardLayout/DashboardLayout.test.js +1 -1
- package/build/src/DashboardLayout/ThemeSwitcher.js +10 -4
- package/build/src/DashboardLayout/TitleBar.d.ts +1 -0
- package/build/src/DashboardLayout/TitleBar.js +27 -0
- package/build/src/PageContainer/PageContainer.d.ts +0 -4
- package/build/src/PageContainer/PageContainer.js +2 -13
- package/build/src/PageContainer/PageContainer.test.js +0 -14
- package/build/src/shared/locales/LocaleContext.d.ts +46 -9
- package/build/src/shared/locales/LocaleContext.js +19 -4
- package/package.json +15 -15
- package/src/Account/Account.tsx +6 -24
- package/src/Account/AccountPreview.tsx +26 -3
- package/src/Account/SignInButton.tsx +1 -1
- package/src/AppProvider/AppProvider.tsx +6 -0
- package/src/AppProvider/AppProviderComponent.tsx +9 -5
- package/src/DashboardLayout/DashboardLayout.test.tsx +1 -1
- package/src/DashboardLayout/DashboardLayout.tsx +29 -44
- package/src/DashboardLayout/ThemeSwitcher.tsx +10 -4
- package/src/DashboardLayout/TitleBar.tsx +43 -0
- package/src/PageContainer/PageContainer.test.tsx +0 -23
- package/src/PageContainer/PageContainer.tsx +2 -25
- package/src/shared/locales/LocaleContext.tsx +18 -9
- package/tsconfig.json +1 -1
- package/build/src/shared/locales/en.d.ts +0 -6
- package/build/src/shared/locales/en.js +0 -8
- package/src/shared/locales/en.tsx +0 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Toolpad
|
|
2
2
|
|
|
3
|
-
It's a project originally cloned from Toolpad Core (https://github.com/mui/toolpad/tree/master/packages/toolpad-core).Toolpad Core is a set of full-stack components for building dashboards, internal tools, and B2B web applications with React. Additional simplicity and new features have been implemented for ETSOO SmartERP SaaS platform development.
|
|
3
|
+
It's a project originally cloned from Toolpad Core (https://github.com/mui/toolpad/tree/master/packages/toolpad-core). Toolpad Core is a set of full-stack components for building dashboards, internal tools, and B2B web applications with React. Additional simplicity and new features have been implemented for ETSOO SmartERP SaaS platform development.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -4,7 +4,6 @@ import Popover from "@mui/material/Popover";
|
|
|
4
4
|
import Stack from "@mui/material/Stack";
|
|
5
5
|
import { SignInButton } from "./SignInButton";
|
|
6
6
|
import { AccountPreviewProps } from "./AccountPreview";
|
|
7
|
-
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
8
7
|
export interface AccountSlots {
|
|
9
8
|
/**
|
|
10
9
|
* The component used for the account preview
|
|
@@ -47,10 +46,6 @@ export interface AccountProps {
|
|
|
47
46
|
signInButton?: React.ComponentProps<typeof SignInButton>;
|
|
48
47
|
signOutButton?: React.ComponentProps<typeof Button>;
|
|
49
48
|
};
|
|
50
|
-
/**
|
|
51
|
-
* The labels for the account component.
|
|
52
|
-
*/
|
|
53
|
-
localeText?: Partial<ReturnType<typeof useLocaleText>>;
|
|
54
49
|
}
|
|
55
50
|
/**
|
|
56
51
|
*
|
|
@@ -10,7 +10,6 @@ import { AccountPreview } from "./AccountPreview";
|
|
|
10
10
|
import { AccountPopoverHeader } from "./AccountPopoverHeader";
|
|
11
11
|
import { AccountPopoverFooter } from "./AccountPopoverFooter";
|
|
12
12
|
import { SessionContext, AuthenticationContext } from "../AppProvider/AppProvider";
|
|
13
|
-
import { LocaleProvider } from "../shared/locales/LocaleContext";
|
|
14
13
|
/**
|
|
15
14
|
*
|
|
16
15
|
* Demos:
|
|
@@ -24,7 +23,6 @@ import { LocaleProvider } from "../shared/locales/LocaleContext";
|
|
|
24
23
|
* - [Account API](https://mui.com/toolpad/core/api/account)
|
|
25
24
|
*/
|
|
26
25
|
function Account(props) {
|
|
27
|
-
const { localeText } = props;
|
|
28
26
|
const { slots, slotProps } = props;
|
|
29
27
|
const [anchorEl, setAnchorEl] = React.useState(null);
|
|
30
28
|
const session = React.useContext(SessionContext);
|
|
@@ -40,9 +38,9 @@ function Account(props) {
|
|
|
40
38
|
return null;
|
|
41
39
|
}
|
|
42
40
|
if (!session?.user) {
|
|
43
|
-
return
|
|
41
|
+
return slots?.signInButton ? (_jsx(slots.signInButton, { onClick: authentication.signIn })) : (_jsx(SignInButton, { ...slotProps?.signInButton }));
|
|
44
42
|
}
|
|
45
|
-
return (_jsxs(
|
|
43
|
+
return (_jsxs(React.Fragment, { children: [slots?.preview ? (_jsx(slots.preview, { handleClick: handleClick, open: open })) : (_jsx(AccountPreview, { variant: "condensed", handleClick: handleClick, open: open, ...slotProps?.preview })), slots?.popover ? (_jsx(slots.popover, { ...slotProps?.popover })) : (_jsx(Popover, { anchorEl: anchorEl, id: "account-menu", open: open, onClose: handleClose, onClick: handleClose, transformOrigin: { horizontal: "right", vertical: "top" }, anchorOrigin: { horizontal: "right", vertical: "bottom" }, ...slotProps?.popover, slotProps: {
|
|
46
44
|
paper: {
|
|
47
45
|
elevation: 0,
|
|
48
46
|
sx: {
|
|
@@ -71,14 +69,6 @@ Account.propTypes /* remove-proptypes */ = {
|
|
|
71
69
|
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
72
70
|
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
73
71
|
// └─────────────────────────────────────────────────────────────────────┘
|
|
74
|
-
/**
|
|
75
|
-
* The labels for the account component.
|
|
76
|
-
*/
|
|
77
|
-
localeText: PropTypes.shape({
|
|
78
|
-
iconButtonAriaLabel: PropTypes.string,
|
|
79
|
-
signInLabel: PropTypes.string,
|
|
80
|
-
signOutLabel: PropTypes.string
|
|
81
|
-
}),
|
|
82
72
|
/**
|
|
83
73
|
* The props used for each slot inside.
|
|
84
74
|
*/
|
|
@@ -9,6 +9,25 @@ import IconButton from "@mui/material/IconButton";
|
|
|
9
9
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
|
10
10
|
import { SessionContext } from "../AppProvider";
|
|
11
11
|
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
12
|
+
function formatNameInitials(user) {
|
|
13
|
+
if (user == null)
|
|
14
|
+
return null;
|
|
15
|
+
const name = user.latinName || user.name;
|
|
16
|
+
if (name == null)
|
|
17
|
+
return null;
|
|
18
|
+
const chars = name
|
|
19
|
+
.trim()
|
|
20
|
+
.split(/\s+/g)
|
|
21
|
+
.map((part) => {
|
|
22
|
+
const firstChar = part.charAt(0).toUpperCase();
|
|
23
|
+
return firstChar >= "A" && firstChar <= "Z" ? firstChar : "";
|
|
24
|
+
})
|
|
25
|
+
.filter((c) => c)
|
|
26
|
+
.slice(-2);
|
|
27
|
+
if (chars.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
return chars.join("");
|
|
30
|
+
}
|
|
12
31
|
/**
|
|
13
32
|
* The AccountPreview component displays user account information.
|
|
14
33
|
*
|
|
@@ -30,12 +49,12 @@ function AccountPreview(props) {
|
|
|
30
49
|
const avatarContent = slots?.avatar ? (_jsx(slots.avatar, {})) : (_jsx(Avatar, { src: session.user?.image || "", alt: session.user?.name || session.user?.email || "", sx: {
|
|
31
50
|
height: variant === "expanded" ? 48 : 32,
|
|
32
51
|
width: variant === "expanded" ? 48 : 32
|
|
33
|
-
}, ...slotProps?.avatar }));
|
|
52
|
+
}, ...slotProps?.avatar, children: session.user?.image ? null : formatNameInitials(session.user) }));
|
|
34
53
|
if (variant === "expanded") {
|
|
35
54
|
return (_jsxs(Stack, { direction: "row", justifyContent: "flex-start", spacing: 2, padding: 2, children: [avatarContent, _jsxs(Stack, { direction: "column", justifyContent: "space-evenly", children: [_jsx(Typography, { variant: "body2", fontWeight: "bolder", noWrap: true, children: session.user?.name }), _jsx(Typography, { variant: "caption", noWrap: true, children: session.user?.email })] }), handleClick &&
|
|
36
55
|
(slots?.moreIconButton ? (_jsx(slots.moreIconButton, {})) : (_jsx(IconButton, { size: "small", onClick: handleClick, ...slotProps?.moreIconButton, sx: { alignSelf: "flex-start", ...slotProps?.moreIconButton?.sx }, children: _jsx(MoreVertIcon, { fontSize: "small" }) })))] }));
|
|
37
56
|
}
|
|
38
|
-
return (_jsx(Tooltip, { title: session.user.name ?? "Account", children: slots?.avatarIconButton ? (_jsx(slots.avatarIconButton, {})) : (_jsx(IconButton, { onClick: handleClick, "aria-label": localeText.
|
|
57
|
+
return (_jsx(Tooltip, { title: session.user.name ?? "Account", children: slots?.avatarIconButton ? (_jsx(slots.avatarIconButton, {})) : (_jsx(IconButton, { onClick: handleClick, "aria-label": localeText.accountIconButtonAriaLabel, size: "small", "aria-controls": open ? "account-menu" : undefined, "aria-haspopup": "true", "aria-expanded": open ? "true" : undefined, ...slotProps?.avatarIconButton, children: avatarContent })) }));
|
|
39
58
|
}
|
|
40
59
|
AccountPreview.propTypes /* remove-proptypes */ = {
|
|
41
60
|
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Theme } from "@mui/material/styles";
|
|
3
|
+
import { LocaleContextType } from "../shared/locales/LocaleContext";
|
|
3
4
|
export interface NavigateOptions {
|
|
4
5
|
history?: "auto" | "push" | "replace";
|
|
5
6
|
}
|
|
@@ -40,6 +41,7 @@ export interface Session {
|
|
|
40
41
|
user?: {
|
|
41
42
|
id?: string | null;
|
|
42
43
|
name?: string | null;
|
|
44
|
+
latinName?: string | null;
|
|
43
45
|
image?: string | null;
|
|
44
46
|
email?: string | null;
|
|
45
47
|
};
|
|
@@ -69,6 +71,10 @@ export interface AppProviderProps {
|
|
|
69
71
|
* @default null
|
|
70
72
|
*/
|
|
71
73
|
branding?: Branding | null;
|
|
74
|
+
/**
|
|
75
|
+
* The labels for the component.
|
|
76
|
+
*/
|
|
77
|
+
localeText?: Partial<LocaleContextType>;
|
|
72
78
|
/**
|
|
73
79
|
* Navigation definition for the app.
|
|
74
80
|
* @default []
|
|
@@ -5,6 +5,7 @@ import { BrandingContext, NavigationContext, RouterContext, WindowContext } from
|
|
|
5
5
|
import { AppThemeProvider } from "./AppThemeProvider";
|
|
6
6
|
import { createTheme as createMuiTheme } from "@mui/material/styles";
|
|
7
7
|
import { AuthenticationContext, SessionContext } from "./AppProvider";
|
|
8
|
+
import { LocaleProvider } from "../shared/locales/LocaleContext";
|
|
8
9
|
function createTheme() {
|
|
9
10
|
return createMuiTheme({
|
|
10
11
|
cssVariables: {
|
|
@@ -25,8 +26,8 @@ function createTheme() {
|
|
|
25
26
|
* - [AppProvider API](https://mui.com/toolpad/core/api/app-provider)
|
|
26
27
|
*/
|
|
27
28
|
function AppProvider(props) {
|
|
28
|
-
const { children, theme = createTheme(), branding = null, navigation = [], router = null, authentication = null, session = null, window: appWindow } = props;
|
|
29
|
-
return (_jsx(WindowContext.Provider, { value: appWindow, children: _jsx(AuthenticationContext.Provider, { value: authentication, children: _jsx(SessionContext.Provider, { value: session, children: _jsx(RouterContext.Provider, { value: router, children: _jsx(AppThemeProvider, { theme: theme, window: appWindow, children: _jsx(BrandingContext.Provider, { value: branding, children: _jsx(NavigationContext.Provider, { value: navigation, children: children }) }) }) }) }) }) }));
|
|
29
|
+
const { children, theme = createTheme(), branding = null, localeText, navigation = [], router = null, authentication = null, session = null, window: appWindow } = props;
|
|
30
|
+
return (_jsx(WindowContext.Provider, { value: appWindow, children: _jsx(AuthenticationContext.Provider, { value: authentication, children: _jsx(SessionContext.Provider, { value: session, children: _jsx(RouterContext.Provider, { value: router, children: _jsx(AppThemeProvider, { theme: theme, window: appWindow, children: _jsx(LocaleProvider, { localeText: localeText, children: _jsx(BrandingContext.Provider, { value: branding, children: _jsx(NavigationContext.Provider, { value: navigation, children: children }) }) }) }) }) }) }) }));
|
|
30
31
|
}
|
|
31
32
|
AppProvider.propTypes /* remove-proptypes */ = {
|
|
32
33
|
// ┌────────────────────────────── Warning ──────────────────────────────┐
|
|
@@ -5,11 +5,17 @@ export interface SidebarFooterProps {
|
|
|
5
5
|
mini: boolean;
|
|
6
6
|
}
|
|
7
7
|
export interface DashboardLayoutSlotProps {
|
|
8
|
+
titlebar?: {};
|
|
8
9
|
toolbarActions?: {};
|
|
9
10
|
toolbarAccount?: AccountProps;
|
|
10
11
|
sidebarFooter?: SidebarFooterProps;
|
|
11
12
|
}
|
|
12
13
|
export interface DashboardLayoutSlots {
|
|
14
|
+
/**
|
|
15
|
+
* The title bar component used in the layout header.
|
|
16
|
+
* @default TitleBar
|
|
17
|
+
*/
|
|
18
|
+
titlebar?: React.JSXElementConstructor<{}>;
|
|
13
19
|
/**
|
|
14
20
|
* The toolbar actions component used in the layout header.
|
|
15
21
|
* @default ToolbarActions
|
|
@@ -46,6 +52,11 @@ export interface DashboardLayoutProps {
|
|
|
46
52
|
* @default false
|
|
47
53
|
*/
|
|
48
54
|
hideNavigation?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the theme switcher should be shown in the toolbar.
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
showThemeSwitcher?: boolean;
|
|
49
60
|
/**
|
|
50
61
|
* Width of the sidebar when expanded.
|
|
51
62
|
* @default 320
|
|
@@ -10,18 +10,17 @@ import IconButton from "@mui/material/IconButton";
|
|
|
10
10
|
import Stack from "@mui/material/Stack";
|
|
11
11
|
import Toolbar from "@mui/material/Toolbar";
|
|
12
12
|
import Tooltip from "@mui/material/Tooltip";
|
|
13
|
-
import Typography from "@mui/material/Typography";
|
|
14
13
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
15
14
|
import MenuIcon from "@mui/icons-material/Menu";
|
|
16
15
|
import MenuOpenIcon from "@mui/icons-material/MenuOpen";
|
|
17
|
-
import {
|
|
18
|
-
import { BrandingContext, NavigationContext, WindowContext } from "../shared/context";
|
|
16
|
+
import { NavigationContext, WindowContext } from "../shared/context";
|
|
19
17
|
import { Account } from "../Account";
|
|
20
|
-
import { useApplicationTitle } from "../shared/branding";
|
|
21
18
|
import { DashboardSidebarSubNavigation } from "./DashboardSidebarSubNavigation";
|
|
22
19
|
import { ToolbarActions } from "./ToolbarActions";
|
|
23
20
|
import { ThemeSwitcher } from "./ThemeSwitcher";
|
|
24
21
|
import { getDrawerSxTransitionMixin, getDrawerWidthTransitionMixin } from "./utils";
|
|
22
|
+
import { TitleBar } from "./TitleBar";
|
|
23
|
+
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
25
24
|
const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
|
26
25
|
borderWidth: 0,
|
|
27
26
|
borderBottomWidth: 1,
|
|
@@ -30,13 +29,6 @@ const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
|
|
30
29
|
boxShadow: "none",
|
|
31
30
|
zIndex: theme.zIndex.drawer + 1
|
|
32
31
|
}));
|
|
33
|
-
const LogoContainer = styled("div")({
|
|
34
|
-
position: "relative",
|
|
35
|
-
height: 40,
|
|
36
|
-
"& img": {
|
|
37
|
-
maxHeight: 40
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
32
|
/**
|
|
41
33
|
*
|
|
42
34
|
* Demos:
|
|
@@ -48,12 +40,11 @@ const LogoContainer = styled("div")({
|
|
|
48
40
|
* - [DashboardLayout API](https://mui.com/toolpad/core/api/dashboard-layout)
|
|
49
41
|
*/
|
|
50
42
|
function DashboardLayout(props) {
|
|
51
|
-
const { children, disableCollapsibleSidebar = false, defaultSidebarCollapsed = false, hideNavigation = false, sidebarExpandedWidth = 320, slots, slotProps, sx } = props;
|
|
43
|
+
const { children, disableCollapsibleSidebar = false, defaultSidebarCollapsed = false, hideNavigation = false, showThemeSwitcher = false, sidebarExpandedWidth = 320, slots, slotProps, sx } = props;
|
|
52
44
|
const theme = useTheme();
|
|
53
|
-
const branding = React.useContext(BrandingContext);
|
|
54
45
|
const navigation = React.useContext(NavigationContext);
|
|
55
46
|
const appWindow = React.useContext(WindowContext);
|
|
56
|
-
const
|
|
47
|
+
const localeText = useLocaleText();
|
|
57
48
|
const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] = React.useState(!defaultSidebarCollapsed);
|
|
58
49
|
const [isMobileNavigationExpanded, setIsMobileNavigationExpanded] = React.useState(false);
|
|
59
50
|
const isUnderMdViewport = useMediaQuery(theme.breakpoints.down("md"), appWindow && {
|
|
@@ -104,11 +95,14 @@ function DashboardLayout(props) {
|
|
|
104
95
|
const isDesktopMini = !disableCollapsibleSidebar && !isDesktopNavigationExpanded;
|
|
105
96
|
const isMobileMini = !disableCollapsibleSidebar && !isMobileNavigationExpanded;
|
|
106
97
|
const getMenuIcon = React.useCallback((isExpanded) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
return (_jsx(Tooltip, { title: isExpanded
|
|
99
|
+
? localeText.collapseMenuTitle
|
|
100
|
+
: localeText.expandMenuTitle, enterDelay: 1000, children: _jsx("div", { children: _jsx(IconButton, { "aria-label": isExpanded
|
|
101
|
+
? localeText.collapseNavMenuAriaLabel
|
|
102
|
+
: localeText.expandNavMenuAriaLabel, onClick: toggleNavigationExpanded, children: isExpanded ? _jsx(MenuOpenIcon, {}) : _jsx(MenuIcon, {}) }) }) }));
|
|
110
103
|
}, [toggleNavigationExpanded]);
|
|
111
104
|
const hasDrawerTransitions = isOverSmViewport && (disableCollapsibleSidebar || !isUnderMdViewport);
|
|
105
|
+
const TitlebarSlot = slots?.titlebar ?? TitleBar;
|
|
112
106
|
const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions;
|
|
113
107
|
const ToolbarAccountSlot = slots?.toolbarAccount ?? Account;
|
|
114
108
|
const SidebarFooterSlot = slots?.sidebarFooter ?? null;
|
|
@@ -167,12 +161,7 @@ function DashboardLayout(props) {
|
|
|
167
161
|
position: { xs: "absolute", md: "static" },
|
|
168
162
|
left: { xs: "50%", md: "auto" },
|
|
169
163
|
transform: { xs: "translateX(-50%)", md: "none" }
|
|
170
|
-
}, children: _jsx(
|
|
171
|
-
color: (theme.vars ?? theme).palette.primary.main,
|
|
172
|
-
fontWeight: "700",
|
|
173
|
-
ml: 0.5,
|
|
174
|
-
whiteSpace: "nowrap"
|
|
175
|
-
}, children: applicationTitle })] }) }) }), _jsx(Box, { sx: { flexGrow: 1 } }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(ToolbarActionsSlot, { ...slotProps?.toolbarActions }), _jsx(ThemeSwitcher, {}), _jsx(ToolbarAccountSlot, { ...slotProps?.toolbarAccount })] })] }) }), !hideNavigation ? (_jsxs(React.Fragment, { children: [_jsx(Drawer, { container: layoutRef.current, variant: "temporary", open: isMobileNavigationExpanded, onClose: handleSetNavigationExpanded(false), ModalProps: {
|
|
164
|
+
}, children: _jsx(TitlebarSlot, { ...slotProps?.titlebar }) }), _jsx(Box, { sx: { flexGrow: 1 } }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(ToolbarActionsSlot, { ...slotProps?.toolbarActions }), showThemeSwitcher && _jsx(ThemeSwitcher, {}), _jsx(ToolbarAccountSlot, { ...slotProps?.toolbarAccount })] })] }) }), !hideNavigation ? (_jsxs(React.Fragment, { children: [_jsx(Drawer, { container: layoutRef.current, variant: "temporary", open: isMobileNavigationExpanded, onClose: handleSetNavigationExpanded(false), ModalProps: {
|
|
176
165
|
keepMounted: true // Better open performance on mobile.
|
|
177
166
|
}, sx: {
|
|
178
167
|
display: {
|
|
@@ -27,7 +27,7 @@ describe("DashboardLayout", () => {
|
|
|
27
27
|
});
|
|
28
28
|
test("can switch theme", async () => {
|
|
29
29
|
const user = userEvent.setup();
|
|
30
|
-
render(_jsx(AppProvider, { children: _jsx(DashboardLayout, { children: "Hello world" }) }));
|
|
30
|
+
render(_jsx(AppProvider, { children: _jsx(DashboardLayout, { showThemeSwitcher: true, children: "Hello world" }) }));
|
|
31
31
|
const getBackgroundColorCSSVariable = () => getComputedStyle(document.documentElement).getPropertyValue("--mui-palette-common-background");
|
|
32
32
|
const header = screen.getByRole("banner");
|
|
33
33
|
const themeSwitcherButton = within(header).getByLabelText("Switch to dark mode");
|
|
@@ -8,6 +8,7 @@ import DarkModeIcon from "@mui/icons-material/DarkMode";
|
|
|
8
8
|
import LightModeIcon from "@mui/icons-material/LightMode";
|
|
9
9
|
import { PaletteModeContext } from "../shared/context";
|
|
10
10
|
import useSsr from "../utils/hooks/useSsr";
|
|
11
|
+
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
11
12
|
// TODO: When we use this component as the default for a slot, make it non-internal
|
|
12
13
|
/**
|
|
13
14
|
* @ignore - internal component.
|
|
@@ -15,15 +16,20 @@ import useSsr from "../utils/hooks/useSsr";
|
|
|
15
16
|
function ThemeSwitcher() {
|
|
16
17
|
const isSsr = useSsr();
|
|
17
18
|
const theme = useTheme();
|
|
19
|
+
const localeText = useLocaleText();
|
|
18
20
|
const { paletteMode, setPaletteMode, isDualTheme } = React.useContext(PaletteModeContext);
|
|
19
21
|
const toggleMode = React.useCallback(() => {
|
|
20
22
|
setPaletteMode(paletteMode === "dark" ? "light" : "dark");
|
|
21
23
|
}, [paletteMode, setPaletteMode]);
|
|
22
24
|
return isDualTheme ? (_jsx(Tooltip, { title: isSsr
|
|
23
|
-
?
|
|
24
|
-
:
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
? localeText.switchModeTitle
|
|
26
|
+
: paletteMode === "dark"
|
|
27
|
+
? localeText.lightModeTitle
|
|
28
|
+
: localeText.darkModeTitle, enterDelay: 1000, children: _jsx("div", { children: _jsx(IconButton, { "aria-label": isSsr
|
|
29
|
+
? localeText.switchThemeModeAriaLabel
|
|
30
|
+
: paletteMode === "dark"
|
|
31
|
+
? localeText.switchToLightModeAriaLabel
|
|
32
|
+
: localeText.switchToDarkModeAriaLabel, onClick: toggleMode, sx: {
|
|
27
33
|
color: (theme.vars ?? theme).palette.primary.dark
|
|
28
34
|
}, children: theme.getColorSchemeSelector ? (_jsxs(React.Fragment, { children: [_jsx(DarkModeIcon, { sx: {
|
|
29
35
|
display: "inline",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function TitleBar(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Stack, styled, Typography, useTheme } from "@mui/material";
|
|
3
|
+
import { Link } from "../shared/Link";
|
|
4
|
+
import { useApplicationTitle } from "../shared/branding";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { BrandingContext } from "../shared/context";
|
|
7
|
+
const LogoContainer = styled("div")({
|
|
8
|
+
position: "relative",
|
|
9
|
+
height: 40,
|
|
10
|
+
"& img": {
|
|
11
|
+
maxHeight: 40
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
export function TitleBar() {
|
|
15
|
+
// Theme
|
|
16
|
+
const theme = useTheme();
|
|
17
|
+
// Application title
|
|
18
|
+
const applicationTitle = useApplicationTitle();
|
|
19
|
+
// Branding
|
|
20
|
+
const branding = React.useContext(BrandingContext);
|
|
21
|
+
return (_jsx(Link, { href: "/", style: { color: "inherit", textDecoration: "none" }, children: _jsxs(Stack, { direction: "row", alignItems: "center", children: [branding?.logo && _jsx(LogoContainer, { children: branding.logo }), _jsx(Typography, { variant: "h6", sx: {
|
|
22
|
+
color: (theme.vars ?? theme).palette.primary.main,
|
|
23
|
+
fontWeight: "700",
|
|
24
|
+
ml: 0.5,
|
|
25
|
+
whiteSpace: "nowrap"
|
|
26
|
+
}, children: applicationTitle })] }) }));
|
|
27
|
+
}
|
|
@@ -35,10 +35,6 @@ export interface PageContainerProps extends ContainerProps {
|
|
|
35
35
|
* The breadcrumbs of the page. Leave blank to use the active page breadcrumbs.
|
|
36
36
|
*/
|
|
37
37
|
breadcrumbs?: Breadcrumb[];
|
|
38
|
-
/**
|
|
39
|
-
* @deprecated Use `breadcrumbs` instead.
|
|
40
|
-
*/
|
|
41
|
-
breadCrumbs?: Breadcrumb[];
|
|
42
38
|
/**
|
|
43
39
|
* The components used for each slot inside.
|
|
44
40
|
*/
|
|
@@ -12,7 +12,6 @@ import { Link as ToolpadLink } from "../shared/Link";
|
|
|
12
12
|
import { PageContainerToolbar } from "./PageContainerToolbar";
|
|
13
13
|
import { getItemTitle } from "../shared/navigation";
|
|
14
14
|
import { useActivePage } from "../useActivePage";
|
|
15
|
-
import warnOnce from "../utils/warnOnce";
|
|
16
15
|
const PageContentHeader = styled("div")(({ theme }) => ({
|
|
17
16
|
display: "flex",
|
|
18
17
|
flexDirection: "row",
|
|
@@ -31,13 +30,10 @@ const PageContentHeader = styled("div")(({ theme }) => ({
|
|
|
31
30
|
* - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
|
|
32
31
|
*/
|
|
33
32
|
function PageContainer(props) {
|
|
34
|
-
const { children, slots, slotProps, breadcrumbs,
|
|
35
|
-
if (process.env.NODE_ENV !== "production" && breadCrumbs) {
|
|
36
|
-
warnOnce("The PageContainer `breadCrumbs` prop is deprecated. Use `breadcrumbs` instead.");
|
|
37
|
-
}
|
|
33
|
+
const { children, slots, slotProps, breadcrumbs, ...rest } = props;
|
|
38
34
|
const activePage = useActivePage();
|
|
39
35
|
// TODO: Remove `props.breadCrumbs` in the next major version
|
|
40
|
-
const resolvedBreadcrumbs = breadcrumbs ??
|
|
36
|
+
const resolvedBreadcrumbs = breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
41
37
|
const title = props.title ?? activePage?.title ?? "";
|
|
42
38
|
const ToolbarComponent = props?.slots?.toolbar ?? PageContainerToolbar;
|
|
43
39
|
const toolbarSlotProps = useSlotProps({
|
|
@@ -64,13 +60,6 @@ PageContainer.propTypes /* remove-proptypes */ = {
|
|
|
64
60
|
path: PropTypes.string.isRequired,
|
|
65
61
|
title: PropTypes.string.isRequired
|
|
66
62
|
})),
|
|
67
|
-
/**
|
|
68
|
-
* @deprecated Use `breadcrumbs` instead.
|
|
69
|
-
*/
|
|
70
|
-
breadCrumbs: PropTypes.arrayOf(PropTypes.shape({
|
|
71
|
-
path: PropTypes.string.isRequired,
|
|
72
|
-
title: PropTypes.string.isRequired
|
|
73
|
-
})),
|
|
74
63
|
/**
|
|
75
64
|
* @ignore
|
|
76
65
|
*/
|
|
@@ -110,18 +110,4 @@ describe("PageContainer", () => {
|
|
|
110
110
|
expect(helloLink.getAttribute("href")).toBe("/hello");
|
|
111
111
|
expect(within(breadcrumbs).getByText("World")).toBeTruthy();
|
|
112
112
|
});
|
|
113
|
-
// TODO: Remove in the next major version
|
|
114
|
-
test("renders legacy breadcrumbs prop", async () => {
|
|
115
|
-
vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
116
|
-
render(_jsx(PageContainer, { breadCrumbs: [
|
|
117
|
-
{ title: "Hello", path: "/hello" },
|
|
118
|
-
{ title: "World", path: "/world" }
|
|
119
|
-
] }));
|
|
120
|
-
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
121
|
-
const helloLink = within(breadcrumbs).getByRole("link", { name: "Hello" });
|
|
122
|
-
expect(helloLink.getAttribute("href")).toBe("/hello");
|
|
123
|
-
expect(within(breadcrumbs).getByText("World")).toBeTruthy();
|
|
124
|
-
expect(console.warn).toHaveBeenCalledOnce();
|
|
125
|
-
expect(console.warn).toHaveBeenCalledWith("The PageContainer `breadCrumbs` prop is deprecated. Use `breadcrumbs` instead.");
|
|
126
|
-
});
|
|
127
113
|
});
|
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
signInLabel
|
|
4
|
-
signOutLabel
|
|
5
|
-
|
|
2
|
+
declare const DEFAULT_LOCALE_TEXT: {
|
|
3
|
+
signInLabel: string;
|
|
4
|
+
signOutLabel: string;
|
|
5
|
+
accountIconButtonAriaLabel: string;
|
|
6
|
+
expandMenuTitle: string;
|
|
7
|
+
expandNavMenuAriaLabel: string;
|
|
8
|
+
collapseMenuTitle: string;
|
|
9
|
+
collapseNavMenuAriaLabel: string;
|
|
10
|
+
switchModeTitle: string;
|
|
11
|
+
darkModeTitle: string;
|
|
12
|
+
lightModeTitle: string;
|
|
13
|
+
switchThemeModeAriaLabel: string;
|
|
14
|
+
switchToDarkModeAriaLabel: string;
|
|
15
|
+
switchToLightModeAriaLabel: string;
|
|
6
16
|
};
|
|
7
|
-
export
|
|
17
|
+
export type LocaleContextType = typeof DEFAULT_LOCALE_TEXT;
|
|
18
|
+
export declare const LocaleContext: React.Context<{
|
|
19
|
+
signInLabel: string;
|
|
20
|
+
signOutLabel: string;
|
|
21
|
+
accountIconButtonAriaLabel: string;
|
|
22
|
+
expandMenuTitle: string;
|
|
23
|
+
expandNavMenuAriaLabel: string;
|
|
24
|
+
collapseMenuTitle: string;
|
|
25
|
+
collapseNavMenuAriaLabel: string;
|
|
26
|
+
switchModeTitle: string;
|
|
27
|
+
darkModeTitle: string;
|
|
28
|
+
lightModeTitle: string;
|
|
29
|
+
switchThemeModeAriaLabel: string;
|
|
30
|
+
switchToDarkModeAriaLabel: string;
|
|
31
|
+
switchToLightModeAriaLabel: string;
|
|
32
|
+
}>;
|
|
8
33
|
export interface LocaleProviderProps {
|
|
9
34
|
localeText?: Partial<LocaleContextType>;
|
|
10
35
|
children: React.ReactNode;
|
|
@@ -13,7 +38,19 @@ export interface LocaleProviderProps {
|
|
|
13
38
|
* @ignore - internal component.
|
|
14
39
|
*/
|
|
15
40
|
export declare function LocaleProvider({ localeText, children }: LocaleProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
41
|
+
export declare function useLocaleText(): {
|
|
42
|
+
signInLabel: string;
|
|
43
|
+
signOutLabel: string;
|
|
44
|
+
accountIconButtonAriaLabel: string;
|
|
45
|
+
expandMenuTitle: string;
|
|
46
|
+
expandNavMenuAriaLabel: string;
|
|
47
|
+
collapseMenuTitle: string;
|
|
48
|
+
collapseNavMenuAriaLabel: string;
|
|
49
|
+
switchModeTitle: string;
|
|
50
|
+
darkModeTitle: string;
|
|
51
|
+
lightModeTitle: string;
|
|
52
|
+
switchThemeModeAriaLabel: string;
|
|
53
|
+
switchToDarkModeAriaLabel: string;
|
|
54
|
+
switchToLightModeAriaLabel: string;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
|
|
4
|
+
const DEFAULT_LOCALE_TEXT = {
|
|
5
|
+
// Account
|
|
6
|
+
signInLabel: "Sign In",
|
|
7
|
+
signOutLabel: "Sign Out",
|
|
8
|
+
// Account Preview
|
|
9
|
+
accountIconButtonAriaLabel: "Current User",
|
|
10
|
+
// Menu
|
|
11
|
+
expandMenuTitle: "Expand menu",
|
|
12
|
+
expandNavMenuAriaLabel: "Expand navigation menu",
|
|
13
|
+
collapseMenuTitle: "Collapse menu",
|
|
14
|
+
collapseNavMenuAriaLabel: "Collapse navigation menu",
|
|
15
|
+
// Theme
|
|
16
|
+
switchModeTitle: "Switch mode",
|
|
17
|
+
darkModeTitle: "Dark mode",
|
|
18
|
+
lightModeTitle: "Light mode",
|
|
19
|
+
switchThemeModeAriaLabel: "Switch theme mode",
|
|
20
|
+
switchToDarkModeAriaLabel: "Switch to dark mode",
|
|
21
|
+
switchToLightModeAriaLabel: "Switch to light mode"
|
|
22
|
+
};
|
|
5
23
|
export const LocaleContext = React.createContext(DEFAULT_LOCALE_TEXT);
|
|
6
24
|
/**
|
|
7
25
|
* @ignore - internal component.
|
|
@@ -10,9 +28,6 @@ export function LocaleProvider({ localeText, children }) {
|
|
|
10
28
|
const mergedLocaleText = React.useMemo(() => ({ ...DEFAULT_LOCALE_TEXT, ...localeText }), [localeText]);
|
|
11
29
|
return (_jsx(LocaleContext.Provider, { value: mergedLocaleText, children: children }));
|
|
12
30
|
}
|
|
13
|
-
/**
|
|
14
|
-
* @ignore - internal hook.
|
|
15
|
-
*/
|
|
16
31
|
export function useLocaleText() {
|
|
17
32
|
return React.useContext(LocaleContext);
|
|
18
33
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/toolpad",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"author": "ETSOO",
|
|
5
5
|
"description": "Dashboard framework extention based on Toolpad Core",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -16,25 +16,25 @@
|
|
|
16
16
|
],
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/ETSOO/toolpad.git"
|
|
19
|
+
"url": "git+https://github.com/ETSOO/etsoo-toolpad.git"
|
|
20
20
|
},
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"bugs": {
|
|
23
|
-
"url": "https://github.com/ETSOO/toolpad/issues"
|
|
23
|
+
"url": "https://github.com/ETSOO/etsoo-toolpad/issues"
|
|
24
24
|
},
|
|
25
|
-
"homepage": "https://github.com/ETSOO/toolpad#readme",
|
|
25
|
+
"homepage": "https://github.com/ETSOO/etsoo-toolpad#readme",
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc",
|
|
28
28
|
"test": "vitest",
|
|
29
29
|
"testb": "vitest --browser.enabled"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@babel/runtime": "^7.
|
|
32
|
+
"@babel/runtime": "^7.26.0",
|
|
33
33
|
"@emotion/css": "^11.13.4",
|
|
34
34
|
"@emotion/react": "^11.13.3",
|
|
35
35
|
"@emotion/styled": "^11.13.0",
|
|
36
|
-
"@mui/lab": "6.0.0-beta.
|
|
37
|
-
"@mui/utils": "6.1.
|
|
36
|
+
"@mui/lab": "6.0.0-beta.13",
|
|
37
|
+
"@mui/utils": "6.1.5",
|
|
38
38
|
"@vitejs/plugin-react": "4.3.3",
|
|
39
39
|
"invariant": "2.2.4",
|
|
40
40
|
"path-to-regexp": "6.3.0",
|
|
@@ -42,19 +42,19 @@
|
|
|
42
42
|
"react": "^18.3.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@mui/icons-material": "6.1.
|
|
46
|
-
"@mui/material": "6.1.
|
|
45
|
+
"@mui/icons-material": "6.1.5",
|
|
46
|
+
"@mui/material": "6.1.5",
|
|
47
47
|
"@testing-library/jest-dom": "^6.6.2",
|
|
48
48
|
"@testing-library/react": "^16.0.1",
|
|
49
49
|
"@types/invariant": "2.2.37",
|
|
50
50
|
"@types/prop-types": "15.7.13",
|
|
51
|
-
"@types/react": "18.3.
|
|
51
|
+
"@types/react": "18.3.12",
|
|
52
52
|
"@types/react-dom": "18.3.1",
|
|
53
|
-
"@vitest/browser": "2.1.
|
|
54
|
-
"next": "^
|
|
53
|
+
"@vitest/browser": "2.1.4",
|
|
54
|
+
"next": "^15.0.1",
|
|
55
55
|
"next-router-mock": "^0.9.13",
|
|
56
|
-
"playwright": "^1.
|
|
57
|
-
"react-router-dom": "6.
|
|
58
|
-
"vitest": "2.1.
|
|
56
|
+
"playwright": "^1.48.2",
|
|
57
|
+
"react-router-dom": "6.27.0",
|
|
58
|
+
"vitest": "2.1.4"
|
|
59
59
|
}
|
|
60
60
|
}
|
package/src/Account/Account.tsx
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
SessionContext,
|
|
14
14
|
AuthenticationContext
|
|
15
15
|
} from "../AppProvider/AppProvider";
|
|
16
|
-
import { LocaleProvider, useLocaleText } from "../shared/locales/LocaleContext";
|
|
17
16
|
|
|
18
17
|
export interface AccountSlots {
|
|
19
18
|
/**
|
|
@@ -58,10 +57,6 @@ export interface AccountProps {
|
|
|
58
57
|
signInButton?: React.ComponentProps<typeof SignInButton>;
|
|
59
58
|
signOutButton?: React.ComponentProps<typeof Button>;
|
|
60
59
|
};
|
|
61
|
-
/**
|
|
62
|
-
* The labels for the account component.
|
|
63
|
-
*/
|
|
64
|
-
localeText?: Partial<ReturnType<typeof useLocaleText>>;
|
|
65
60
|
}
|
|
66
61
|
/**
|
|
67
62
|
*
|
|
@@ -76,7 +71,6 @@ export interface AccountProps {
|
|
|
76
71
|
* - [Account API](https://mui.com/toolpad/core/api/account)
|
|
77
72
|
*/
|
|
78
73
|
function Account(props: AccountProps) {
|
|
79
|
-
const { localeText } = props;
|
|
80
74
|
const { slots, slotProps } = props;
|
|
81
75
|
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);
|
|
82
76
|
const session = React.useContext(SessionContext);
|
|
@@ -96,19 +90,15 @@ function Account(props: AccountProps) {
|
|
|
96
90
|
}
|
|
97
91
|
|
|
98
92
|
if (!session?.user) {
|
|
99
|
-
return (
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
) : (
|
|
104
|
-
<SignInButton {...slotProps?.signInButton} />
|
|
105
|
-
)}
|
|
106
|
-
</LocaleProvider>
|
|
93
|
+
return slots?.signInButton ? (
|
|
94
|
+
<slots.signInButton onClick={authentication.signIn} />
|
|
95
|
+
) : (
|
|
96
|
+
<SignInButton {...slotProps?.signInButton} />
|
|
107
97
|
);
|
|
108
98
|
}
|
|
109
99
|
|
|
110
100
|
return (
|
|
111
|
-
<
|
|
101
|
+
<React.Fragment>
|
|
112
102
|
{slots?.preview ? (
|
|
113
103
|
<slots.preview handleClick={handleClick} open={open} />
|
|
114
104
|
) : (
|
|
@@ -170,7 +160,7 @@ function Account(props: AccountProps) {
|
|
|
170
160
|
)}
|
|
171
161
|
</Popover>
|
|
172
162
|
)}
|
|
173
|
-
</
|
|
163
|
+
</React.Fragment>
|
|
174
164
|
);
|
|
175
165
|
}
|
|
176
166
|
|
|
@@ -179,14 +169,6 @@ Account.propTypes /* remove-proptypes */ = {
|
|
|
179
169
|
// │ These PropTypes are generated from the TypeScript type definitions. │
|
|
180
170
|
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
|
|
181
171
|
// └─────────────────────────────────────────────────────────────────────┘
|
|
182
|
-
/**
|
|
183
|
-
* The labels for the account component.
|
|
184
|
-
*/
|
|
185
|
-
localeText: PropTypes.shape({
|
|
186
|
-
iconButtonAriaLabel: PropTypes.string,
|
|
187
|
-
signInLabel: PropTypes.string,
|
|
188
|
-
signOutLabel: PropTypes.string
|
|
189
|
-
}),
|
|
190
172
|
/**
|
|
191
173
|
* The props used for each slot inside.
|
|
192
174
|
*/
|
|
@@ -6,7 +6,7 @@ import Tooltip from "@mui/material/Tooltip";
|
|
|
6
6
|
import Stack from "@mui/material/Stack";
|
|
7
7
|
import IconButton, { IconButtonProps } from "@mui/material/IconButton";
|
|
8
8
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
|
9
|
-
import { SessionContext } from "../AppProvider";
|
|
9
|
+
import { Session, SessionContext } from "../AppProvider";
|
|
10
10
|
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
11
11
|
|
|
12
12
|
export type AccountPreviewVariant = "condensed" | "expanded";
|
|
@@ -60,6 +60,27 @@ export interface AccountPreviewProps {
|
|
|
60
60
|
open?: boolean;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
function formatNameInitials(user?: Session["user"]) {
|
|
64
|
+
if (user == null) return null;
|
|
65
|
+
|
|
66
|
+
const name = user.latinName || user.name;
|
|
67
|
+
if (name == null) return null;
|
|
68
|
+
|
|
69
|
+
const chars = name
|
|
70
|
+
.trim()
|
|
71
|
+
.split(/\s+/g)
|
|
72
|
+
.map((part) => {
|
|
73
|
+
const firstChar = part.charAt(0).toUpperCase();
|
|
74
|
+
return firstChar >= "A" && firstChar <= "Z" ? firstChar : "";
|
|
75
|
+
})
|
|
76
|
+
.filter((c) => c)
|
|
77
|
+
.slice(-2);
|
|
78
|
+
|
|
79
|
+
if (chars.length === 0) return null;
|
|
80
|
+
|
|
81
|
+
return chars.join("");
|
|
82
|
+
}
|
|
83
|
+
|
|
63
84
|
/**
|
|
64
85
|
* The AccountPreview component displays user account information.
|
|
65
86
|
*
|
|
@@ -91,7 +112,9 @@ function AccountPreview(props: AccountPreviewProps) {
|
|
|
91
112
|
width: variant === "expanded" ? 48 : 32
|
|
92
113
|
}}
|
|
93
114
|
{...slotProps?.avatar}
|
|
94
|
-
|
|
115
|
+
>
|
|
116
|
+
{session.user?.image ? null : formatNameInitials(session.user)}
|
|
117
|
+
</Avatar>
|
|
95
118
|
);
|
|
96
119
|
|
|
97
120
|
if (variant === "expanded") {
|
|
@@ -135,7 +158,7 @@ function AccountPreview(props: AccountPreviewProps) {
|
|
|
135
158
|
) : (
|
|
136
159
|
<IconButton
|
|
137
160
|
onClick={handleClick}
|
|
138
|
-
aria-label={localeText.
|
|
161
|
+
aria-label={localeText.accountIconButtonAriaLabel}
|
|
139
162
|
size="small"
|
|
140
163
|
aria-controls={open ? "account-menu" : undefined}
|
|
141
164
|
aria-haspopup="true"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { Theme } from "@mui/material/styles";
|
|
4
|
+
import { LocaleContextType } from "../shared/locales/LocaleContext";
|
|
4
5
|
|
|
5
6
|
export interface NavigateOptions {
|
|
6
7
|
history?: "auto" | "push" | "replace";
|
|
@@ -54,6 +55,7 @@ export interface Session {
|
|
|
54
55
|
user?: {
|
|
55
56
|
id?: string | null;
|
|
56
57
|
name?: string | null;
|
|
58
|
+
latinName?: string | null;
|
|
57
59
|
image?: string | null;
|
|
58
60
|
email?: string | null;
|
|
59
61
|
};
|
|
@@ -87,6 +89,10 @@ export interface AppProviderProps {
|
|
|
87
89
|
* @default null
|
|
88
90
|
*/
|
|
89
91
|
branding?: Branding | null;
|
|
92
|
+
/**
|
|
93
|
+
* The labels for the component.
|
|
94
|
+
*/
|
|
95
|
+
localeText?: Partial<LocaleContextType>;
|
|
90
96
|
/**
|
|
91
97
|
* Navigation definition for the app.
|
|
92
98
|
* @default []
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
AuthenticationContext,
|
|
14
14
|
SessionContext
|
|
15
15
|
} from "./AppProvider";
|
|
16
|
+
import { LocaleProvider } from "../shared/locales/LocaleContext";
|
|
16
17
|
|
|
17
18
|
function createTheme(): Theme {
|
|
18
19
|
return createMuiTheme({
|
|
@@ -39,6 +40,7 @@ function AppProvider(props: AppProviderProps) {
|
|
|
39
40
|
children,
|
|
40
41
|
theme = createTheme(),
|
|
41
42
|
branding = null,
|
|
43
|
+
localeText,
|
|
42
44
|
navigation = [],
|
|
43
45
|
router = null,
|
|
44
46
|
authentication = null,
|
|
@@ -52,11 +54,13 @@ function AppProvider(props: AppProviderProps) {
|
|
|
52
54
|
<SessionContext.Provider value={session}>
|
|
53
55
|
<RouterContext.Provider value={router}>
|
|
54
56
|
<AppThemeProvider theme={theme} window={appWindow}>
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
<LocaleProvider localeText={localeText}>
|
|
58
|
+
<BrandingContext.Provider value={branding}>
|
|
59
|
+
<NavigationContext.Provider value={navigation}>
|
|
60
|
+
{children}
|
|
61
|
+
</NavigationContext.Provider>
|
|
62
|
+
</BrandingContext.Provider>
|
|
63
|
+
</LocaleProvider>
|
|
60
64
|
</AppThemeProvider>
|
|
61
65
|
</RouterContext.Provider>
|
|
62
66
|
</SessionContext.Provider>
|
|
@@ -15,13 +15,8 @@ import type {} from "@mui/material/themeCssVarsAugmentation";
|
|
|
15
15
|
import MenuIcon from "@mui/icons-material/Menu";
|
|
16
16
|
import MenuOpenIcon from "@mui/icons-material/MenuOpen";
|
|
17
17
|
import { Link } from "../shared/Link";
|
|
18
|
-
import {
|
|
19
|
-
BrandingContext,
|
|
20
|
-
NavigationContext,
|
|
21
|
-
WindowContext
|
|
22
|
-
} from "../shared/context";
|
|
18
|
+
import { NavigationContext, WindowContext } from "../shared/context";
|
|
23
19
|
import { Account, type AccountProps } from "../Account";
|
|
24
|
-
import { useApplicationTitle } from "../shared/branding";
|
|
25
20
|
import { DashboardSidebarSubNavigation } from "./DashboardSidebarSubNavigation";
|
|
26
21
|
import { ToolbarActions } from "./ToolbarActions";
|
|
27
22
|
import { ThemeSwitcher } from "./ThemeSwitcher";
|
|
@@ -29,6 +24,8 @@ import {
|
|
|
29
24
|
getDrawerSxTransitionMixin,
|
|
30
25
|
getDrawerWidthTransitionMixin
|
|
31
26
|
} from "./utils";
|
|
27
|
+
import { TitleBar } from "./TitleBar";
|
|
28
|
+
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
32
29
|
|
|
33
30
|
const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
|
34
31
|
borderWidth: 0,
|
|
@@ -39,25 +36,23 @@ const AppBar = styled(MuiAppBar)(({ theme }) => ({
|
|
|
39
36
|
zIndex: theme.zIndex.drawer + 1
|
|
40
37
|
}));
|
|
41
38
|
|
|
42
|
-
const LogoContainer = styled("div")({
|
|
43
|
-
position: "relative",
|
|
44
|
-
height: 40,
|
|
45
|
-
"& img": {
|
|
46
|
-
maxHeight: 40
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
39
|
export interface SidebarFooterProps {
|
|
51
40
|
mini: boolean;
|
|
52
41
|
}
|
|
53
42
|
|
|
54
43
|
export interface DashboardLayoutSlotProps {
|
|
44
|
+
titlebar?: {};
|
|
55
45
|
toolbarActions?: {};
|
|
56
46
|
toolbarAccount?: AccountProps;
|
|
57
47
|
sidebarFooter?: SidebarFooterProps;
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
export interface DashboardLayoutSlots {
|
|
51
|
+
/**
|
|
52
|
+
* The title bar component used in the layout header.
|
|
53
|
+
* @default TitleBar
|
|
54
|
+
*/
|
|
55
|
+
titlebar?: React.JSXElementConstructor<{}>;
|
|
61
56
|
/**
|
|
62
57
|
* The toolbar actions component used in the layout header.
|
|
63
58
|
* @default ToolbarActions
|
|
@@ -95,6 +90,11 @@ export interface DashboardLayoutProps {
|
|
|
95
90
|
* @default false
|
|
96
91
|
*/
|
|
97
92
|
hideNavigation?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether the theme switcher should be shown in the toolbar.
|
|
95
|
+
* @default false
|
|
96
|
+
*/
|
|
97
|
+
showThemeSwitcher?: boolean;
|
|
98
98
|
/**
|
|
99
99
|
* Width of the sidebar when expanded.
|
|
100
100
|
* @default 320
|
|
@@ -132,6 +132,7 @@ function DashboardLayout(props: DashboardLayoutProps) {
|
|
|
132
132
|
disableCollapsibleSidebar = false,
|
|
133
133
|
defaultSidebarCollapsed = false,
|
|
134
134
|
hideNavigation = false,
|
|
135
|
+
showThemeSwitcher = false,
|
|
135
136
|
sidebarExpandedWidth = 320,
|
|
136
137
|
slots,
|
|
137
138
|
slotProps,
|
|
@@ -140,10 +141,9 @@ function DashboardLayout(props: DashboardLayoutProps) {
|
|
|
140
141
|
|
|
141
142
|
const theme = useTheme();
|
|
142
143
|
|
|
143
|
-
const branding = React.useContext(BrandingContext);
|
|
144
144
|
const navigation = React.useContext(NavigationContext);
|
|
145
145
|
const appWindow = React.useContext(WindowContext);
|
|
146
|
-
const
|
|
146
|
+
const localeText = useLocaleText();
|
|
147
147
|
|
|
148
148
|
const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] =
|
|
149
149
|
React.useState(!defaultSidebarCollapsed);
|
|
@@ -227,21 +227,22 @@ function DashboardLayout(props: DashboardLayoutProps) {
|
|
|
227
227
|
|
|
228
228
|
const getMenuIcon = React.useCallback(
|
|
229
229
|
(isExpanded: boolean) => {
|
|
230
|
-
const expandMenuActionText = "Expand";
|
|
231
|
-
const collapseMenuActionText = "Collapse";
|
|
232
|
-
|
|
233
230
|
return (
|
|
234
231
|
<Tooltip
|
|
235
|
-
title={
|
|
236
|
-
isExpanded
|
|
237
|
-
|
|
232
|
+
title={
|
|
233
|
+
isExpanded
|
|
234
|
+
? localeText.collapseMenuTitle
|
|
235
|
+
: localeText.expandMenuTitle
|
|
236
|
+
}
|
|
238
237
|
enterDelay={1000}
|
|
239
238
|
>
|
|
240
239
|
<div>
|
|
241
240
|
<IconButton
|
|
242
|
-
aria-label={
|
|
243
|
-
isExpanded
|
|
244
|
-
|
|
241
|
+
aria-label={
|
|
242
|
+
isExpanded
|
|
243
|
+
? localeText.collapseNavMenuAriaLabel
|
|
244
|
+
: localeText.expandNavMenuAriaLabel
|
|
245
|
+
}
|
|
245
246
|
onClick={toggleNavigationExpanded}
|
|
246
247
|
>
|
|
247
248
|
{isExpanded ? <MenuOpenIcon /> : <MenuIcon />}
|
|
@@ -256,6 +257,7 @@ function DashboardLayout(props: DashboardLayoutProps) {
|
|
|
256
257
|
const hasDrawerTransitions =
|
|
257
258
|
isOverSmViewport && (disableCollapsibleSidebar || !isUnderMdViewport);
|
|
258
259
|
|
|
260
|
+
const TitlebarSlot = slots?.titlebar ?? TitleBar;
|
|
259
261
|
const ToolbarActionsSlot = slots?.toolbarActions ?? ToolbarActions;
|
|
260
262
|
const ToolbarAccountSlot = slots?.toolbarAccount ?? Account;
|
|
261
263
|
const SidebarFooterSlot = slots?.sidebarFooter ?? null;
|
|
@@ -373,29 +375,12 @@ function DashboardLayout(props: DashboardLayoutProps) {
|
|
|
373
375
|
transform: { xs: "translateX(-50%)", md: "none" }
|
|
374
376
|
}}
|
|
375
377
|
>
|
|
376
|
-
<
|
|
377
|
-
<Stack direction="row" alignItems="center">
|
|
378
|
-
{branding?.logo && (
|
|
379
|
-
<LogoContainer>{branding.logo}</LogoContainer>
|
|
380
|
-
)}
|
|
381
|
-
<Typography
|
|
382
|
-
variant="h6"
|
|
383
|
-
sx={{
|
|
384
|
-
color: (theme.vars ?? theme).palette.primary.main,
|
|
385
|
-
fontWeight: "700",
|
|
386
|
-
ml: 0.5,
|
|
387
|
-
whiteSpace: "nowrap"
|
|
388
|
-
}}
|
|
389
|
-
>
|
|
390
|
-
{applicationTitle}
|
|
391
|
-
</Typography>
|
|
392
|
-
</Stack>
|
|
393
|
-
</Link>
|
|
378
|
+
<TitlebarSlot {...slotProps?.titlebar} />
|
|
394
379
|
</Box>
|
|
395
380
|
<Box sx={{ flexGrow: 1 }} />
|
|
396
381
|
<Stack direction="row" spacing={1}>
|
|
397
382
|
<ToolbarActionsSlot {...slotProps?.toolbarActions} />
|
|
398
|
-
<ThemeSwitcher />
|
|
383
|
+
{showThemeSwitcher && <ThemeSwitcher />}
|
|
399
384
|
<ToolbarAccountSlot {...slotProps?.toolbarAccount} />
|
|
400
385
|
</Stack>
|
|
401
386
|
</Toolbar>
|
|
@@ -7,6 +7,7 @@ import DarkModeIcon from "@mui/icons-material/DarkMode";
|
|
|
7
7
|
import LightModeIcon from "@mui/icons-material/LightMode";
|
|
8
8
|
import { PaletteModeContext } from "../shared/context";
|
|
9
9
|
import useSsr from "../utils/hooks/useSsr";
|
|
10
|
+
import { useLocaleText } from "../shared/locales/LocaleContext";
|
|
10
11
|
|
|
11
12
|
// TODO: When we use this component as the default for a slot, make it non-internal
|
|
12
13
|
/**
|
|
@@ -15,6 +16,7 @@ import useSsr from "../utils/hooks/useSsr";
|
|
|
15
16
|
function ThemeSwitcher() {
|
|
16
17
|
const isSsr = useSsr();
|
|
17
18
|
const theme = useTheme();
|
|
19
|
+
const localeText = useLocaleText();
|
|
18
20
|
|
|
19
21
|
const { paletteMode, setPaletteMode, isDualTheme } =
|
|
20
22
|
React.useContext(PaletteModeContext);
|
|
@@ -27,8 +29,10 @@ function ThemeSwitcher() {
|
|
|
27
29
|
<Tooltip
|
|
28
30
|
title={
|
|
29
31
|
isSsr
|
|
30
|
-
?
|
|
31
|
-
:
|
|
32
|
+
? localeText.switchModeTitle
|
|
33
|
+
: paletteMode === "dark"
|
|
34
|
+
? localeText.lightModeTitle
|
|
35
|
+
: localeText.darkModeTitle
|
|
32
36
|
}
|
|
33
37
|
enterDelay={1000}
|
|
34
38
|
>
|
|
@@ -36,8 +40,10 @@ function ThemeSwitcher() {
|
|
|
36
40
|
<IconButton
|
|
37
41
|
aria-label={
|
|
38
42
|
isSsr
|
|
39
|
-
?
|
|
40
|
-
:
|
|
43
|
+
? localeText.switchThemeModeAriaLabel
|
|
44
|
+
: paletteMode === "dark"
|
|
45
|
+
? localeText.switchToLightModeAriaLabel
|
|
46
|
+
: localeText.switchToDarkModeAriaLabel
|
|
41
47
|
}
|
|
42
48
|
onClick={toggleMode}
|
|
43
49
|
sx={{
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Stack, styled, Typography, useTheme } from "@mui/material";
|
|
2
|
+
import { Link } from "../shared/Link";
|
|
3
|
+
import { useApplicationTitle } from "../shared/branding";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { BrandingContext } from "../shared/context";
|
|
6
|
+
|
|
7
|
+
const LogoContainer = styled("div")({
|
|
8
|
+
position: "relative",
|
|
9
|
+
height: 40,
|
|
10
|
+
"& img": {
|
|
11
|
+
maxHeight: 40
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export function TitleBar() {
|
|
16
|
+
// Theme
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
|
|
19
|
+
// Application title
|
|
20
|
+
const applicationTitle = useApplicationTitle();
|
|
21
|
+
|
|
22
|
+
// Branding
|
|
23
|
+
const branding = React.useContext(BrandingContext);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Link href="/" style={{ color: "inherit", textDecoration: "none" }}>
|
|
27
|
+
<Stack direction="row" alignItems="center">
|
|
28
|
+
{branding?.logo && <LogoContainer>{branding.logo}</LogoContainer>}
|
|
29
|
+
<Typography
|
|
30
|
+
variant="h6"
|
|
31
|
+
sx={{
|
|
32
|
+
color: (theme.vars ?? theme).palette.primary.main,
|
|
33
|
+
fontWeight: "700",
|
|
34
|
+
ml: 0.5,
|
|
35
|
+
whiteSpace: "nowrap"
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{applicationTitle}
|
|
39
|
+
</Typography>
|
|
40
|
+
</Stack>
|
|
41
|
+
</Link>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -168,27 +168,4 @@ describe("PageContainer", () => {
|
|
|
168
168
|
expect(helloLink.getAttribute("href")).toBe("/hello");
|
|
169
169
|
expect(within(breadcrumbs).getByText("World")).toBeTruthy();
|
|
170
170
|
});
|
|
171
|
-
|
|
172
|
-
// TODO: Remove in the next major version
|
|
173
|
-
test("renders legacy breadcrumbs prop", async () => {
|
|
174
|
-
vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
175
|
-
render(
|
|
176
|
-
<PageContainer
|
|
177
|
-
breadCrumbs={[
|
|
178
|
-
{ title: "Hello", path: "/hello" },
|
|
179
|
-
{ title: "World", path: "/world" }
|
|
180
|
-
]}
|
|
181
|
-
/>
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
const breadcrumbs = screen.getByRole("navigation", { name: "breadcrumb" });
|
|
185
|
-
|
|
186
|
-
const helloLink = within(breadcrumbs).getByRole("link", { name: "Hello" });
|
|
187
|
-
expect(helloLink.getAttribute("href")).toBe("/hello");
|
|
188
|
-
expect(within(breadcrumbs).getByText("World")).toBeTruthy();
|
|
189
|
-
expect(console.warn).toHaveBeenCalledOnce();
|
|
190
|
-
expect(console.warn).toHaveBeenCalledWith(
|
|
191
|
-
"The PageContainer `breadCrumbs` prop is deprecated. Use `breadcrumbs` instead."
|
|
192
|
-
);
|
|
193
|
-
});
|
|
194
171
|
});
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
} from "./PageContainerToolbar";
|
|
16
16
|
import { getItemTitle } from "../shared/navigation";
|
|
17
17
|
import { useActivePage } from "../useActivePage";
|
|
18
|
-
import warnOnce from "../utils/warnOnce";
|
|
19
18
|
|
|
20
19
|
const PageContentHeader = styled("div")(({ theme }) => ({
|
|
21
20
|
display: "flex",
|
|
@@ -63,11 +62,6 @@ export interface PageContainerProps extends ContainerProps {
|
|
|
63
62
|
* The breadcrumbs of the page. Leave blank to use the active page breadcrumbs.
|
|
64
63
|
*/
|
|
65
64
|
breadcrumbs?: Breadcrumb[];
|
|
66
|
-
// TODO: Remove in the next major version
|
|
67
|
-
/**
|
|
68
|
-
* @deprecated Use `breadcrumbs` instead.
|
|
69
|
-
*/
|
|
70
|
-
breadCrumbs?: Breadcrumb[];
|
|
71
65
|
/**
|
|
72
66
|
* The components used for each slot inside.
|
|
73
67
|
*/
|
|
@@ -90,20 +84,12 @@ export interface PageContainerProps extends ContainerProps {
|
|
|
90
84
|
* - [PageContainer API](https://mui.com/toolpad/core/api/page-container)
|
|
91
85
|
*/
|
|
92
86
|
function PageContainer(props: PageContainerProps) {
|
|
93
|
-
const { children, slots, slotProps, breadcrumbs,
|
|
94
|
-
props;
|
|
95
|
-
|
|
96
|
-
if (process.env.NODE_ENV !== "production" && breadCrumbs) {
|
|
97
|
-
warnOnce(
|
|
98
|
-
"The PageContainer `breadCrumbs` prop is deprecated. Use `breadcrumbs` instead."
|
|
99
|
-
);
|
|
100
|
-
}
|
|
87
|
+
const { children, slots, slotProps, breadcrumbs, ...rest } = props;
|
|
101
88
|
|
|
102
89
|
const activePage = useActivePage();
|
|
103
90
|
|
|
104
91
|
// TODO: Remove `props.breadCrumbs` in the next major version
|
|
105
|
-
const resolvedBreadcrumbs =
|
|
106
|
-
breadcrumbs ?? breadCrumbs ?? activePage?.breadcrumbs ?? [];
|
|
92
|
+
const resolvedBreadcrumbs = breadcrumbs ?? activePage?.breadcrumbs ?? [];
|
|
107
93
|
const title = props.title ?? activePage?.title ?? "";
|
|
108
94
|
|
|
109
95
|
const ToolbarComponent = props?.slots?.toolbar ?? PageContainerToolbar;
|
|
@@ -165,15 +151,6 @@ PageContainer.propTypes /* remove-proptypes */ = {
|
|
|
165
151
|
title: PropTypes.string.isRequired
|
|
166
152
|
})
|
|
167
153
|
),
|
|
168
|
-
/**
|
|
169
|
-
* @deprecated Use `breadcrumbs` instead.
|
|
170
|
-
*/
|
|
171
|
-
breadCrumbs: PropTypes.arrayOf(
|
|
172
|
-
PropTypes.shape({
|
|
173
|
-
path: PropTypes.string.isRequired,
|
|
174
|
-
title: PropTypes.string.isRequired
|
|
175
|
-
})
|
|
176
|
-
),
|
|
177
154
|
/**
|
|
178
155
|
* @ignore
|
|
179
156
|
*/
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import DEFAULT_LOCALE_TEXT from "./en";
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const DEFAULT_LOCALE_TEXT = {
|
|
7
6
|
// Account
|
|
8
|
-
signInLabel
|
|
9
|
-
signOutLabel
|
|
7
|
+
signInLabel: "Sign In",
|
|
8
|
+
signOutLabel: "Sign Out",
|
|
10
9
|
// Account Preview
|
|
11
|
-
|
|
10
|
+
accountIconButtonAriaLabel: "Current User",
|
|
11
|
+
// Menu
|
|
12
|
+
expandMenuTitle: "Expand menu",
|
|
13
|
+
expandNavMenuAriaLabel: "Expand navigation menu",
|
|
14
|
+
collapseMenuTitle: "Collapse menu",
|
|
15
|
+
collapseNavMenuAriaLabel: "Collapse navigation menu",
|
|
16
|
+
// Theme
|
|
17
|
+
switchModeTitle: "Switch mode",
|
|
18
|
+
darkModeTitle: "Dark mode",
|
|
19
|
+
lightModeTitle: "Light mode",
|
|
20
|
+
switchThemeModeAriaLabel: "Switch theme mode",
|
|
21
|
+
switchToDarkModeAriaLabel: "Switch to dark mode",
|
|
22
|
+
switchToLightModeAriaLabel: "Switch to light mode"
|
|
12
23
|
};
|
|
13
24
|
|
|
25
|
+
export type LocaleContextType = typeof DEFAULT_LOCALE_TEXT;
|
|
26
|
+
|
|
14
27
|
export const LocaleContext =
|
|
15
28
|
React.createContext<LocaleContextType>(DEFAULT_LOCALE_TEXT);
|
|
16
29
|
|
|
@@ -35,10 +48,6 @@ export function LocaleProvider({ localeText, children }: LocaleProviderProps) {
|
|
|
35
48
|
);
|
|
36
49
|
}
|
|
37
50
|
|
|
38
|
-
/**
|
|
39
|
-
* @ignore - internal hook.
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
51
|
export function useLocaleText() {
|
|
43
52
|
return React.useContext(LocaleContext);
|
|
44
53
|
}
|
package/tsconfig.json
CHANGED