mui-atelier 0.1.0
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/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/NOTICE +32 -0
- package/README.md +282 -0
- package/dist/components/AddressFields.js +83 -0
- package/dist/components/AuthCard.js +94 -0
- package/dist/components/AuthSplash.js +88 -0
- package/dist/components/BrandHeader.js +81 -0
- package/dist/components/BrandLink.js +22 -0
- package/dist/components/BrandMark.js +69 -0
- package/dist/components/BrandProvider.js +14 -0
- package/dist/components/Breadcrumbs.js +62 -0
- package/dist/components/CardHead.js +46 -0
- package/dist/components/ConfirmProvider.js +77 -0
- package/dist/components/DashboardLayout.js +645 -0
- package/dist/components/DataTable.js +546 -0
- package/dist/components/DetailRows.js +42 -0
- package/dist/components/EmptyNote.js +28 -0
- package/dist/components/Eyebrow.js +21 -0
- package/dist/components/HeadingHelp.js +38 -0
- package/dist/components/HeadingLevel.js +18 -0
- package/dist/components/IconTile.js +42 -0
- package/dist/components/ImagePreviewDialog.js +86 -0
- package/dist/components/LabeledField.js +156 -0
- package/dist/components/LanguageSwitcher.js +43 -0
- package/dist/components/ListRow.js +59 -0
- package/dist/components/ModeToggle.js +35 -0
- package/dist/components/PageHeader.js +65 -0
- package/dist/components/PasswordField.js +43 -0
- package/dist/components/PublicLayout.js +27 -0
- package/dist/components/PublicNav.js +17 -0
- package/dist/components/RouteMessage.js +31 -0
- package/dist/components/RowList.js +10 -0
- package/dist/components/SafeHtml.js +57 -0
- package/dist/components/SaveState.js +13 -0
- package/dist/components/SectionPanel.js +48 -0
- package/dist/components/SectionTabs.js +139 -0
- package/dist/components/SiteNav.js +130 -0
- package/dist/components/Skeletons.js +63 -0
- package/dist/components/Sortable.js +81 -0
- package/dist/components/StatTile.js +66 -0
- package/dist/components/StatusChip.js +17 -0
- package/dist/components/StatusDot.js +42 -0
- package/dist/components/SubCard.js +30 -0
- package/dist/components/TabbedCard.js +20 -0
- package/dist/components/TableFilters.js +247 -0
- package/dist/components/ToastProvider.js +65 -0
- package/dist/eslint/chipRules.mjs +65 -0
- package/dist/hooks/useDocumentTitle.js +19 -0
- package/dist/hooks/useOverflowTabs.js +53 -0
- package/dist/hooks/useUnsavedChanges.js +46 -0
- package/dist/i18n/loadMessages.js +15 -0
- package/dist/i18n/mergeMessages.js +13 -0
- package/dist/i18n/messages/en.json +103 -0
- package/dist/i18n/messages/es.json +103 -0
- package/dist/i18n/messages/zh.json +103 -0
- package/dist/lib/authCard.js +4 -0
- package/dist/lib/chipTone.js +46 -0
- package/dist/lib/countryRegionData.js +29 -0
- package/dist/lib/countryRegionData.json +1 -0
- package/dist/lib/navLock.js +22 -0
- package/dist/lib/navTabSx.js +78 -0
- package/dist/lib/orgTime.js +243 -0
- package/dist/lib/pageHtml.js +139 -0
- package/dist/lib/safeNext.js +13 -0
- package/dist/lib/sanitizeHtml.js +36 -0
- package/dist/lib/slugify.js +7 -0
- package/dist/lib/tableFilter.js +147 -0
- package/dist/lib/tableStyle.js +87 -0
- package/dist/test/jsdomShims.js +53 -0
- package/dist/test/themeContract.js +178 -0
- package/dist/theme/OrgThemeRegistry.js +71 -0
- package/dist/theme/ThemeRegistry.js +17 -0
- package/dist/theme/berryNavRow.js +21 -0
- package/dist/theme/createSiteTheme.js +974 -0
- package/dist/theme/themes.js +438 -0
- package/package.json +118 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
const HeadingLevelContext = createContext(2);
|
|
5
|
+
function useHeadingLevel() {
|
|
6
|
+
return `h${useContext(HeadingLevelContext)}`;
|
|
7
|
+
}
|
|
8
|
+
function useNestedHeadingLevel() {
|
|
9
|
+
return Math.min(useContext(HeadingLevelContext) + 1, 6);
|
|
10
|
+
}
|
|
11
|
+
function HeadingLevel({ level, children }) {
|
|
12
|
+
return /* @__PURE__ */ jsx(HeadingLevelContext.Provider, { value: Math.min(Math.max(level, 2), 6), children });
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
HeadingLevel as default,
|
|
16
|
+
useHeadingLevel,
|
|
17
|
+
useNestedHeadingLevel
|
|
18
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import Box from "@mui/material/Box";
|
|
3
|
+
import { useTheme } from "@mui/material/styles";
|
|
4
|
+
const TINTS = {
|
|
5
|
+
accent: { channel: "primary", fg: "primary.main" },
|
|
6
|
+
success: { channel: "success", fg: "success.main" },
|
|
7
|
+
warning: { channel: "warning", fg: "warning.main" },
|
|
8
|
+
neutral: { channel: "secondary", fg: "text.secondary" }
|
|
9
|
+
};
|
|
10
|
+
function IconTile({ children, tint = "accent", size = 40, sx }) {
|
|
11
|
+
const { rt } = useTheme();
|
|
12
|
+
const ui = rt?.ui ?? {};
|
|
13
|
+
const t = TINTS[tint] ?? TINTS.accent;
|
|
14
|
+
const flat = ui.surface === "flat";
|
|
15
|
+
const bare = flat && !ui.dense;
|
|
16
|
+
const radius = ui.surface === "berry" ? 8 : flat ? ui.dense ? 6 : 3 : Math.round(size * 0.32);
|
|
17
|
+
return /* @__PURE__ */ jsx(
|
|
18
|
+
Box,
|
|
19
|
+
{
|
|
20
|
+
"aria-hidden": true,
|
|
21
|
+
sx: [
|
|
22
|
+
{
|
|
23
|
+
width: size,
|
|
24
|
+
height: size,
|
|
25
|
+
flexShrink: 0,
|
|
26
|
+
borderRadius: `${radius}px`,
|
|
27
|
+
display: "grid",
|
|
28
|
+
placeItems: "center",
|
|
29
|
+
fontSize: size * (bare ? 0.56 : 0.5),
|
|
30
|
+
color: t.fg,
|
|
31
|
+
backgroundColor: bare ? "transparent" : (theme) => `rgba(${theme.vars.palette[t.channel].mainChannel} / ${ui.dense ? 0.11 : 0.14})`,
|
|
32
|
+
"& > svg": { fontSize: "inherit" }
|
|
33
|
+
},
|
|
34
|
+
...Array.isArray(sx) ? sx : [sx]
|
|
35
|
+
],
|
|
36
|
+
children
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
IconTile as default
|
|
42
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import Box from "@mui/material/Box";
|
|
5
|
+
import CircularProgress from "@mui/material/CircularProgress";
|
|
6
|
+
import Dialog from "@mui/material/Dialog";
|
|
7
|
+
import IconButton from "@mui/material/IconButton";
|
|
8
|
+
import Stack from "@mui/material/Stack";
|
|
9
|
+
import Typography from "@mui/material/Typography";
|
|
10
|
+
import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
|
|
11
|
+
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
|
|
12
|
+
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
|
13
|
+
function ImagePreviewDialog({ open, onClose, url, title, onPrev, onNext, counter }) {
|
|
14
|
+
const t = useTranslations("uikit");
|
|
15
|
+
const nav = Boolean(onPrev || onNext);
|
|
16
|
+
return /* @__PURE__ */ jsxs(
|
|
17
|
+
Dialog,
|
|
18
|
+
{
|
|
19
|
+
open,
|
|
20
|
+
onClose,
|
|
21
|
+
maxWidth: false,
|
|
22
|
+
"aria-label": title || "Image preview",
|
|
23
|
+
slotProps: { paper: { sx: { position: "relative", m: 2, maxWidth: "92vw", bgcolor: "background.paper", overflow: "visible" } } },
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsx(
|
|
26
|
+
IconButton,
|
|
27
|
+
{
|
|
28
|
+
onClick: onClose,
|
|
29
|
+
"aria-label": t("image.close"),
|
|
30
|
+
size: "small",
|
|
31
|
+
sx: {
|
|
32
|
+
position: "absolute",
|
|
33
|
+
top: -14,
|
|
34
|
+
right: -14,
|
|
35
|
+
zIndex: 1,
|
|
36
|
+
bgcolor: "background.paper",
|
|
37
|
+
boxShadow: 2,
|
|
38
|
+
"&:hover": { bgcolor: "action.hover" }
|
|
39
|
+
},
|
|
40
|
+
children: /* @__PURE__ */ jsx(CloseRoundedIcon, { fontSize: "small" })
|
|
41
|
+
}
|
|
42
|
+
),
|
|
43
|
+
/* @__PURE__ */ jsxs(Box, { sx: { p: 1.5, display: "flex", flexDirection: "column", alignItems: "center", gap: 1 }, children: [
|
|
44
|
+
/* @__PURE__ */ jsxs(Box, { sx: { position: "relative", display: "flex", alignItems: "center", justifyContent: "center" }, children: [
|
|
45
|
+
url ? /* @__PURE__ */ jsx(
|
|
46
|
+
Box,
|
|
47
|
+
{
|
|
48
|
+
component: "img",
|
|
49
|
+
src: url,
|
|
50
|
+
alt: title || "",
|
|
51
|
+
sx: { display: "block", maxWidth: "88vw", maxHeight: "82vh", width: "auto", height: "auto", borderRadius: (t2) => `${t2.rt?.radii?.button ?? 4}px` }
|
|
52
|
+
}
|
|
53
|
+
) : /* @__PURE__ */ jsx(Box, { sx: { width: "min(60vw, 360px)", height: 240, display: "grid", placeItems: "center" }, children: /* @__PURE__ */ jsx(CircularProgress, { size: 28 }) }),
|
|
54
|
+
nav ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
55
|
+
/* @__PURE__ */ jsx(
|
|
56
|
+
IconButton,
|
|
57
|
+
{
|
|
58
|
+
"aria-label": t("image.previous"),
|
|
59
|
+
onClick: onPrev,
|
|
60
|
+
sx: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", bgcolor: "background.paper", boxShadow: 2, "&:hover": { bgcolor: "action.hover" } },
|
|
61
|
+
children: /* @__PURE__ */ jsx(ChevronLeftIcon, {})
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
/* @__PURE__ */ jsx(
|
|
65
|
+
IconButton,
|
|
66
|
+
{
|
|
67
|
+
"aria-label": t("image.next"),
|
|
68
|
+
onClick: onNext,
|
|
69
|
+
sx: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", bgcolor: "background.paper", boxShadow: 2, "&:hover": { bgcolor: "action.hover" } },
|
|
70
|
+
children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
] }) : null
|
|
74
|
+
] }),
|
|
75
|
+
title || counter ? /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { maxWidth: "88vw", alignItems: "center", justifyContent: "center" }, children: [
|
|
76
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.secondary", textAlign: "center", wordBreak: "break-word" }, children: title }) : null,
|
|
77
|
+
counter ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary", flexShrink: 0 }, children: counter }) : null
|
|
78
|
+
] }) : null
|
|
79
|
+
] })
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
ImagePreviewDialog as default
|
|
86
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useId } from "react";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
5
|
+
import Box from "@mui/material/Box";
|
|
6
|
+
import { useTheme } from "@mui/material/styles";
|
|
7
|
+
import FormControl from "@mui/material/FormControl";
|
|
8
|
+
import Select from "@mui/material/Select";
|
|
9
|
+
import Typography from "@mui/material/Typography";
|
|
10
|
+
const AuthInsetContext = createContext(false);
|
|
11
|
+
function OptionalMark() {
|
|
12
|
+
const t = useTranslations("uikit");
|
|
13
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14
|
+
" ",
|
|
15
|
+
/* @__PURE__ */ jsx(Typography, { component: "span", variant: "caption", sx: { ml: 0.25, color: "text.secondary", fontWeight: 400 }, children: t("optional") })
|
|
16
|
+
] });
|
|
17
|
+
}
|
|
18
|
+
function LabeledField({
|
|
19
|
+
label,
|
|
20
|
+
help,
|
|
21
|
+
helpColor,
|
|
22
|
+
error = false,
|
|
23
|
+
required = false,
|
|
24
|
+
optional = false,
|
|
25
|
+
group = false,
|
|
26
|
+
sx,
|
|
27
|
+
children
|
|
28
|
+
}) {
|
|
29
|
+
const id = useId();
|
|
30
|
+
const labelId = useId();
|
|
31
|
+
const helpId = useId();
|
|
32
|
+
const inAuthCard = useContext(AuthInsetContext);
|
|
33
|
+
const theme = useTheme();
|
|
34
|
+
const inset = inAuthCard && Boolean(theme.rt?.ui?.authInset);
|
|
35
|
+
const describedBy = help ? helpId : void 0;
|
|
36
|
+
const tone = helpColor ?? (error ? "error.main" : "text.secondary");
|
|
37
|
+
const marker = required ? /* @__PURE__ */ jsx(Box, { component: "span", "aria-hidden": true, sx: { color: "error.main", ml: 0.25 }, children: "*" }) : optional ? /* @__PURE__ */ jsx(OptionalMark, {}) : null;
|
|
38
|
+
const helpLine = help ? /* @__PURE__ */ jsx(Typography, { id: helpId, variant: "caption", sx: { display: "block", mt: 0.5, color: tone }, children: help }) : null;
|
|
39
|
+
if (inset) {
|
|
40
|
+
return /* @__PURE__ */ jsxs(Box, { sx, children: [
|
|
41
|
+
/* @__PURE__ */ jsxs(
|
|
42
|
+
Box,
|
|
43
|
+
{
|
|
44
|
+
sx: {
|
|
45
|
+
position: "relative",
|
|
46
|
+
"& label": {
|
|
47
|
+
position: "absolute",
|
|
48
|
+
top: 9,
|
|
49
|
+
left: 14,
|
|
50
|
+
zIndex: 1,
|
|
51
|
+
pointerEvents: "none",
|
|
52
|
+
fontSize: "0.75rem",
|
|
53
|
+
lineHeight: 1,
|
|
54
|
+
color: "text.secondary"
|
|
55
|
+
},
|
|
56
|
+
"& .MuiOutlinedInput-input": { paddingTop: "30.5px !important", paddingBottom: "11.5px !important" },
|
|
57
|
+
"& .MuiOutlinedInput-notchedOutline legend": { display: "none" }
|
|
58
|
+
},
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsxs(Typography, { component: "label", id: labelId, htmlFor: id, variant: "caption", children: [
|
|
61
|
+
label,
|
|
62
|
+
marker
|
|
63
|
+
] }),
|
|
64
|
+
children({ id, labelId, describedBy })
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
helpLine
|
|
69
|
+
] });
|
|
70
|
+
}
|
|
71
|
+
if (group) {
|
|
72
|
+
return /* @__PURE__ */ jsxs(
|
|
73
|
+
Box,
|
|
74
|
+
{
|
|
75
|
+
component: "fieldset",
|
|
76
|
+
"aria-describedby": describedBy,
|
|
77
|
+
sx: { border: 0, m: 0, p: 0, minWidth: 0, ...sx },
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsxs(
|
|
80
|
+
Typography,
|
|
81
|
+
{
|
|
82
|
+
component: "legend",
|
|
83
|
+
id: labelId,
|
|
84
|
+
variant: "body2",
|
|
85
|
+
sx: { fontWeight: 500, display: "block", mb: 0.75, p: 0 },
|
|
86
|
+
children: [
|
|
87
|
+
label,
|
|
88
|
+
marker
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
children({ id, labelId, describedBy }),
|
|
93
|
+
helpLine
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return /* @__PURE__ */ jsxs(Box, { sx, children: [
|
|
99
|
+
/* @__PURE__ */ jsxs(
|
|
100
|
+
Typography,
|
|
101
|
+
{
|
|
102
|
+
component: "label",
|
|
103
|
+
id: labelId,
|
|
104
|
+
htmlFor: id,
|
|
105
|
+
variant: "body2",
|
|
106
|
+
sx: { fontWeight: 500, display: "block", mb: 0.75 },
|
|
107
|
+
children: [
|
|
108
|
+
label,
|
|
109
|
+
marker
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
children({ id, labelId, describedBy }),
|
|
114
|
+
helpLine
|
|
115
|
+
] });
|
|
116
|
+
}
|
|
117
|
+
function LabeledSelect({
|
|
118
|
+
labelId,
|
|
119
|
+
describedBy,
|
|
120
|
+
ariaLabel,
|
|
121
|
+
value,
|
|
122
|
+
onChange,
|
|
123
|
+
disabled = false,
|
|
124
|
+
error = false,
|
|
125
|
+
multiple = false,
|
|
126
|
+
renderValue,
|
|
127
|
+
displayEmpty = true,
|
|
128
|
+
fullWidth = true,
|
|
129
|
+
size,
|
|
130
|
+
sx,
|
|
131
|
+
children
|
|
132
|
+
}) {
|
|
133
|
+
return /* @__PURE__ */ jsx(FormControl, { fullWidth, size, disabled, error, children: /* @__PURE__ */ jsx(
|
|
134
|
+
Select,
|
|
135
|
+
{
|
|
136
|
+
value,
|
|
137
|
+
onChange,
|
|
138
|
+
multiple,
|
|
139
|
+
renderValue,
|
|
140
|
+
displayEmpty,
|
|
141
|
+
SelectDisplayProps: {
|
|
142
|
+
"aria-labelledby": labelId,
|
|
143
|
+
"aria-describedby": describedBy,
|
|
144
|
+
"aria-label": labelId ? void 0 : ariaLabel
|
|
145
|
+
},
|
|
146
|
+
sx,
|
|
147
|
+
children
|
|
148
|
+
}
|
|
149
|
+
) });
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
AuthInsetContext,
|
|
153
|
+
LabeledSelect,
|
|
154
|
+
OptionalMark,
|
|
155
|
+
LabeledField as default
|
|
156
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useLocale, useTranslations } from "next-intl";
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import IconButton from "@mui/material/IconButton";
|
|
7
|
+
import Tooltip from "@mui/material/Tooltip";
|
|
8
|
+
import Menu from "@mui/material/Menu";
|
|
9
|
+
import MenuItem from "@mui/material/MenuItem";
|
|
10
|
+
import ListItemText from "@mui/material/ListItemText";
|
|
11
|
+
import CheckRoundedIcon from "@mui/icons-material/CheckRounded";
|
|
12
|
+
import TranslateOutlinedIcon from "@mui/icons-material/TranslateOutlined";
|
|
13
|
+
const DEFAULT_LOCALES = [
|
|
14
|
+
{ code: "en", label: "English" },
|
|
15
|
+
{ code: "es", label: "Espa\xF1ol" },
|
|
16
|
+
{ code: "zh", label: "\u4E2D\u6587" }
|
|
17
|
+
];
|
|
18
|
+
const ONE_YEAR = 60 * 60 * 24 * 365;
|
|
19
|
+
function writeLocaleCookie(code) {
|
|
20
|
+
document.cookie = `NEXT_LOCALE=${code}; path=/; max-age=${ONE_YEAR}; samesite=lax`;
|
|
21
|
+
}
|
|
22
|
+
function LanguageSwitcher({ locales = DEFAULT_LOCALES }) {
|
|
23
|
+
const locale = useLocale();
|
|
24
|
+
const t = useTranslations("uikit");
|
|
25
|
+
const router = useRouter();
|
|
26
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
27
|
+
function pick(code) {
|
|
28
|
+
writeLocaleCookie(code);
|
|
29
|
+
setAnchorEl(null);
|
|
30
|
+
if (code !== locale) router.refresh();
|
|
31
|
+
}
|
|
32
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
|
+
/* @__PURE__ */ jsx(Tooltip, { title: t("language.label"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: (e) => setAnchorEl(e.currentTarget), "aria-label": t("language.label"), children: /* @__PURE__ */ jsx(TranslateOutlinedIcon, { fontSize: "small" }) }) }),
|
|
34
|
+
/* @__PURE__ */ jsx(Menu, { anchorEl, open: Boolean(anchorEl), onClose: () => setAnchorEl(null), children: locales.map(({ code, label }) => /* @__PURE__ */ jsxs(MenuItem, { selected: code === locale, onClick: () => pick(code), children: [
|
|
35
|
+
/* @__PURE__ */ jsx(ListItemText, { children: label }),
|
|
36
|
+
code === locale ? /* @__PURE__ */ jsx(CheckRoundedIcon, { fontSize: "small", sx: { ml: 2, opacity: 0.7 } }) : null
|
|
37
|
+
] }, code)) })
|
|
38
|
+
] });
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
DEFAULT_LOCALES,
|
|
42
|
+
LanguageSwitcher as default
|
|
43
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Box from "@mui/material/Box";
|
|
3
|
+
import Stack from "@mui/material/Stack";
|
|
4
|
+
import Typography from "@mui/material/Typography";
|
|
5
|
+
import { useTheme } from "@mui/material/styles";
|
|
6
|
+
import IconTile from "./IconTile.js";
|
|
7
|
+
const TINT_CHANNEL = { accent: "primary", success: "success", warning: "warning", neutral: "secondary" };
|
|
8
|
+
function ListRow({ icon, iconTint = "accent", primary, secondary, trailing, tinted, sx }) {
|
|
9
|
+
const { rt } = useTheme();
|
|
10
|
+
const ui = rt?.ui ?? {};
|
|
11
|
+
const channel = TINT_CHANNEL[iconTint] ?? "primary";
|
|
12
|
+
const flat = ui.surface === "flat";
|
|
13
|
+
const glass = ui.surface === "glass";
|
|
14
|
+
const berry = ui.surface === "berry";
|
|
15
|
+
const dense = !!ui.dense;
|
|
16
|
+
const doTint = tinted ?? true;
|
|
17
|
+
const iconSize = dense ? 30 : flat ? 34 : glass || berry ? 40 : 42;
|
|
18
|
+
const rowSx = berry ? {
|
|
19
|
+
alignItems: "center",
|
|
20
|
+
px: 0,
|
|
21
|
+
py: 1.5,
|
|
22
|
+
borderBottom: "1px solid",
|
|
23
|
+
borderColor: "divider",
|
|
24
|
+
"&:last-of-type": { borderBottom: "none" }
|
|
25
|
+
} : {
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
p: dense ? 1 : 1.25,
|
|
28
|
+
borderRadius: `${Math.min(rt?.radii?.card ?? 12, 16)}px`,
|
|
29
|
+
backgroundColor: doTint ? (theme) => `rgba(${theme.vars.palette[channel].mainChannel} / ${flat ? 0.05 : 0.06})` : "transparent",
|
|
30
|
+
// Flat themes (editorial/pro) read best with a crisp edge instead of a wash alone.
|
|
31
|
+
...flat ? { border: "1px solid", borderColor: "divider" } : null
|
|
32
|
+
};
|
|
33
|
+
return /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: dense ? 1.25 : 1.75, sx: [rowSx, ...Array.isArray(sx) ? sx : [sx]], children: [
|
|
34
|
+
icon ? /* @__PURE__ */ jsx(IconTile, { tint: iconTint, size: iconSize, children: icon }) : null,
|
|
35
|
+
/* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
Typography,
|
|
38
|
+
{
|
|
39
|
+
component: "div",
|
|
40
|
+
variant: dense ? "body2" : "subtitle2",
|
|
41
|
+
sx: { fontWeight: berry ? 500 : 600, fontSize: dense ? "0.9rem" : flat ? "1.05rem" : berry ? "1rem" : void 0, fontFamily: rt?.headingFont ?? void 0, ...berry ? { color: "var(--mui-palette-text-heading, inherit)" } : null },
|
|
42
|
+
children: primary
|
|
43
|
+
}
|
|
44
|
+
),
|
|
45
|
+
secondary ? /* @__PURE__ */ jsx(
|
|
46
|
+
Typography,
|
|
47
|
+
{
|
|
48
|
+
variant: "caption",
|
|
49
|
+
sx: { color: "text.secondary", display: "block", overflowWrap: "anywhere", fontFamily: glass ? rt.mono : void 0, fontSize: glass ? "0.72rem" : void 0, letterSpacing: glass ? "0.02em" : void 0 },
|
|
50
|
+
children: secondary
|
|
51
|
+
}
|
|
52
|
+
) : null
|
|
53
|
+
] }),
|
|
54
|
+
trailing
|
|
55
|
+
] });
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
ListRow as default
|
|
59
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
import { useColorScheme } from "@mui/material/styles";
|
|
5
|
+
import { useTranslations } from "next-intl";
|
|
6
|
+
import IconButton from "@mui/material/IconButton";
|
|
7
|
+
import Tooltip from "@mui/material/Tooltip";
|
|
8
|
+
import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined";
|
|
9
|
+
import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined";
|
|
10
|
+
import SettingsBrightnessOutlinedIcon from "@mui/icons-material/SettingsBrightnessOutlined";
|
|
11
|
+
const NEXT = { light: "dark", dark: "system", system: "light" };
|
|
12
|
+
const emptySubscribe = () => () => {
|
|
13
|
+
};
|
|
14
|
+
const useMounted = () => useSyncExternalStore(emptySubscribe, () => true, () => false);
|
|
15
|
+
function ModeToggle() {
|
|
16
|
+
const t = useTranslations("uikit");
|
|
17
|
+
const { mode, setMode } = useColorScheme();
|
|
18
|
+
const mounted = useMounted();
|
|
19
|
+
if (!mounted || !mode) {
|
|
20
|
+
return /* @__PURE__ */ jsx(IconButton, { size: "small", disabled: true, "aria-label": t("mode.label"), children: /* @__PURE__ */ jsx(SettingsBrightnessOutlinedIcon, { fontSize: "small" }) });
|
|
21
|
+
}
|
|
22
|
+
const icon = mode === "light" ? /* @__PURE__ */ jsx(LightModeOutlinedIcon, { fontSize: "small" }) : mode === "dark" ? /* @__PURE__ */ jsx(DarkModeOutlinedIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(SettingsBrightnessOutlinedIcon, { fontSize: "small" });
|
|
23
|
+
return (
|
|
24
|
+
/*
|
|
25
|
+
* Named through the catalogue like everything else in this bar. It was
|
|
26
|
+
* `Colour mode: dark` in a template literal — untranslated in a trilingual
|
|
27
|
+
* product, and spelled the American way in a codebase that writes
|
|
28
|
+
* "organisation" everywhere else.
|
|
29
|
+
*/
|
|
30
|
+
/* @__PURE__ */ jsx(Tooltip, { title: t(`mode.${mode}`), children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => setMode(NEXT[mode]), "aria-label": t(`mode.${mode}`), children: icon }) })
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
ModeToggle as default
|
|
35
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import Box from "@mui/material/Box";
|
|
4
|
+
import { useTheme } from "@mui/material/styles";
|
|
5
|
+
function PageHeader({ children, sx, ...rest }) {
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
const pill = !!theme.rt?.ui?.pillHeading;
|
|
8
|
+
const berry = theme.rt?.ui?.chrome === "berry";
|
|
9
|
+
const berrySx = berry ? {
|
|
10
|
+
backgroundColor: "background.paper",
|
|
11
|
+
// The same shadow the theme's cards carry (createSiteTheme's berry
|
|
12
|
+
// surface, one definition) — this strip is a card, and sat flat
|
|
13
|
+
// beside shadowed ones.
|
|
14
|
+
boxShadow: theme.rt?.cardShadow ?? "none",
|
|
15
|
+
borderRadius: `${theme.rt?.radii?.card ?? 8}px`,
|
|
16
|
+
px: 2,
|
|
17
|
+
py: 1.25,
|
|
18
|
+
/*
|
|
19
|
+
* A two-column grid: everything the page wrote stacks in the first
|
|
20
|
+
* column, the trail alone takes the second, on the first row, pinned
|
|
21
|
+
* right — so a one-line header reads "title … trail" on one row, and
|
|
22
|
+
* a tall one (a record's title and its meta) keeps the trail at
|
|
23
|
+
* its top-right corner rather than under the meta.
|
|
24
|
+
*/
|
|
25
|
+
display: "grid",
|
|
26
|
+
gridTemplateColumns: "minmax(0, 1fr) auto",
|
|
27
|
+
columnGap: 2,
|
|
28
|
+
rowGap: 1,
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
// The header is a size container so the trail can move by the WIDTH
|
|
31
|
+
// IT HAS, not the viewport's: a page that caps its header (the
|
|
32
|
+
// project editor's 680px) is narrow on a wide screen.
|
|
33
|
+
containerType: "inline-size",
|
|
34
|
+
"& > *": { minWidth: 0, gridColumn: 1 },
|
|
35
|
+
// A bare back-link button in the header keeps its size; stretched to
|
|
36
|
+
// the column it would centre its label across the card.
|
|
37
|
+
"& > .MuiButton-root, & > a": { justifySelf: "start" },
|
|
38
|
+
"& > .MuiBreadcrumbs-root": {
|
|
39
|
+
gridColumn: 2,
|
|
40
|
+
gridRow: 1,
|
|
41
|
+
justifySelf: "end",
|
|
42
|
+
alignSelf: "start",
|
|
43
|
+
mb: 0,
|
|
44
|
+
fontSize: "0.75rem",
|
|
45
|
+
fontWeight: 500,
|
|
46
|
+
// Too narrow for a title and a trail side by side: the trail takes
|
|
47
|
+
// the whole first row, right-aligned, and the title drops under it.
|
|
48
|
+
"@container (max-width: 860px)": { gridColumn: "1 / -1", "& .MuiBreadcrumbs-ol": { justifyContent: "flex-end" } }
|
|
49
|
+
},
|
|
50
|
+
"& h1": { fontSize: "1rem", fontWeight: 500 },
|
|
51
|
+
// A standing subtitle under the title: Berry's strip has no room for a
|
|
52
|
+
// sentence, so it becomes the caption size.
|
|
53
|
+
"& h1 + .MuiTypography-root": { fontSize: "0.75rem" }
|
|
54
|
+
} : {};
|
|
55
|
+
const pillSx = pill ? {
|
|
56
|
+
backgroundColor: "background.paper",
|
|
57
|
+
borderRadius: `${theme.rt?.radii?.card ?? 16}px`,
|
|
58
|
+
p: { xs: 2, sm: 2.5 },
|
|
59
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04), 0 8px 24px -18px rgba(0,0,0,0.18)"
|
|
60
|
+
} : {};
|
|
61
|
+
return /* @__PURE__ */ jsx(Box, { "data-pill": pill ? "on" : "off", "data-chrome": berry ? "berry" : void 0, sx: { ...pillSx, ...berrySx, ...sx }, ...rest, children });
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
PageHeader as default
|
|
65
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import IconButton from "@mui/material/IconButton";
|
|
5
|
+
import InputAdornment from "@mui/material/InputAdornment";
|
|
6
|
+
import TextField from "@mui/material/TextField";
|
|
7
|
+
import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined";
|
|
8
|
+
import VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined";
|
|
9
|
+
import { useTranslations } from "next-intl";
|
|
10
|
+
function PasswordField({ id, describedBy, ...rest }) {
|
|
11
|
+
const t = useTranslations("uikit");
|
|
12
|
+
const [shown, setShown] = useState(false);
|
|
13
|
+
return /* @__PURE__ */ jsx(
|
|
14
|
+
TextField,
|
|
15
|
+
{
|
|
16
|
+
id,
|
|
17
|
+
fullWidth: true,
|
|
18
|
+
...rest,
|
|
19
|
+
type: shown ? "text" : "password",
|
|
20
|
+
slotProps: {
|
|
21
|
+
...rest.slotProps,
|
|
22
|
+
htmlInput: { "aria-describedby": describedBy, ...rest.slotProps?.htmlInput },
|
|
23
|
+
input: {
|
|
24
|
+
...rest.slotProps?.input,
|
|
25
|
+
endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx(
|
|
26
|
+
IconButton,
|
|
27
|
+
{
|
|
28
|
+
onClick: () => setShown((v) => !v),
|
|
29
|
+
edge: "end",
|
|
30
|
+
size: "small",
|
|
31
|
+
"aria-label": t(shown ? "password.hide" : "password.show"),
|
|
32
|
+
tabIndex: -1,
|
|
33
|
+
children: shown ? /* @__PURE__ */ jsx(VisibilityOffOutlinedIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(VisibilityOutlinedIcon, { fontSize: "small" })
|
|
34
|
+
}
|
|
35
|
+
) })
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
PasswordField as default
|
|
43
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Box from "@mui/material/Box";
|
|
4
|
+
import Container from "@mui/material/Container";
|
|
5
|
+
import Typography from "@mui/material/Typography";
|
|
6
|
+
import Stack from "@mui/material/Stack";
|
|
7
|
+
import BrandHeader from "./BrandHeader.js";
|
|
8
|
+
import PublicNav from "./PublicNav.js";
|
|
9
|
+
function PublicLayout({ siteName, siteHref = "/", logoUrl = null, footerName = siteName, navItems = [], actions = null, footer = null, children }) {
|
|
10
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { minHeight: "100dvh", display: "flex", flexDirection: "column" }, children: [
|
|
11
|
+
/* @__PURE__ */ jsx(BrandHeader, { siteName, href: siteHref, logoUrl, navItems, actions }),
|
|
12
|
+
navItems.length > 0 ? /* @__PURE__ */ jsx(PublicNav, { items: navItems }) : null,
|
|
13
|
+
/* @__PURE__ */ jsx(Box, { component: "main", sx: { flexGrow: 1 }, children }),
|
|
14
|
+
/* @__PURE__ */ jsx(Box, { component: "footer", sx: { borderTop: 1, borderColor: "divider", py: 3, mt: 8 }, children: /* @__PURE__ */ jsx(Container, { maxWidth: "lg", children: /* @__PURE__ */ jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 2, sx: { alignItems: "center", justifyContent: "space-between" }, children: [
|
|
15
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", sx: { color: "text.secondary" }, children: [
|
|
16
|
+
"\xA9 ",
|
|
17
|
+
(/* @__PURE__ */ new Date()).getFullYear(),
|
|
18
|
+
" ",
|
|
19
|
+
footerName
|
|
20
|
+
] }),
|
|
21
|
+
footer
|
|
22
|
+
] }) }) })
|
|
23
|
+
] });
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
PublicLayout as default
|
|
27
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
import SiteNav from "./SiteNav.js";
|
|
5
|
+
function PublicNav({ items = [] }) {
|
|
6
|
+
const pathname = usePathname() || "";
|
|
7
|
+
if (items.length === 0) return null;
|
|
8
|
+
return /* @__PURE__ */ jsx(
|
|
9
|
+
SiteNav,
|
|
10
|
+
{
|
|
11
|
+
items: items.map((item) => ({ key: item.href, label: item.label, href: item.href, active: pathname === item.href }))
|
|
12
|
+
}
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
PublicNav as default
|
|
17
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Box from "@mui/material/Box";
|
|
4
|
+
import Button from "@mui/material/Button";
|
|
5
|
+
import Container from "@mui/material/Container";
|
|
6
|
+
import Stack from "@mui/material/Stack";
|
|
7
|
+
import Typography from "@mui/material/Typography";
|
|
8
|
+
import NextLink from "next/link";
|
|
9
|
+
import { useTranslations } from "next-intl";
|
|
10
|
+
function RouteMessage({
|
|
11
|
+
title,
|
|
12
|
+
body,
|
|
13
|
+
cta,
|
|
14
|
+
href,
|
|
15
|
+
onRetry,
|
|
16
|
+
retryLabel
|
|
17
|
+
}) {
|
|
18
|
+
const t = useTranslations("uikit");
|
|
19
|
+
return /* @__PURE__ */ jsxs(Container, { maxWidth: "sm", sx: { py: { xs: 8, sm: 12 }, textAlign: "center" }, children: [
|
|
20
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h4", component: "h1", gutterBottom: true, children: title }),
|
|
21
|
+
body ? /* @__PURE__ */ jsx(Typography, { variant: "body1", sx: { color: "text.secondary", mb: 4 }, children: body }) : null,
|
|
22
|
+
/* @__PURE__ */ jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 1.5, sx: { justifyContent: "center" }, children: [
|
|
23
|
+
onRetry ? /* @__PURE__ */ jsx(Button, { variant: "contained", onClick: onRetry, children: retryLabel ?? t("route.retry") }) : null,
|
|
24
|
+
href ? /* @__PURE__ */ jsx(Button, { component: NextLink, href, variant: onRetry ? "outlined" : "contained", children: cta ?? t("route.home") }) : null,
|
|
25
|
+
!href && !onRetry ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Button, { variant: "outlined", onClick: () => window.history.back(), children: t("route.back") }) }) : null
|
|
26
|
+
] })
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
RouteMessage as default
|
|
31
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import Stack from "@mui/material/Stack";
|
|
3
|
+
import { useTheme } from "@mui/material/styles";
|
|
4
|
+
function RowList({ children, sx }) {
|
|
5
|
+
const berry = useTheme().rt?.ui?.surface === "berry";
|
|
6
|
+
return /* @__PURE__ */ jsx(Stack, { spacing: berry ? 0 : 1, sx, children });
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
RowList as default
|
|
10
|
+
};
|