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,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import AppBar from "@mui/material/AppBar";
|
|
5
|
+
import Toolbar from "@mui/material/Toolbar";
|
|
6
|
+
import Box from "@mui/material/Box";
|
|
7
|
+
import Divider from "@mui/material/Divider";
|
|
8
|
+
import Drawer from "@mui/material/Drawer";
|
|
9
|
+
import IconButton from "@mui/material/IconButton";
|
|
10
|
+
import List from "@mui/material/List";
|
|
11
|
+
import ListItem from "@mui/material/ListItem";
|
|
12
|
+
import ListItemButton from "@mui/material/ListItemButton";
|
|
13
|
+
import ListItemText from "@mui/material/ListItemText";
|
|
14
|
+
import Typography from "@mui/material/Typography";
|
|
15
|
+
import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
|
|
16
|
+
import MenuIcon from "@mui/icons-material/Menu";
|
|
17
|
+
import Link from "next/link";
|
|
18
|
+
import { useTheme } from "@mui/material/styles";
|
|
19
|
+
import { useTranslations } from "next-intl";
|
|
20
|
+
import ModeToggle from "./ModeToggle.js";
|
|
21
|
+
import LanguageSwitcher from "./LanguageSwitcher.js";
|
|
22
|
+
import { useBrand } from "./BrandProvider.js";
|
|
23
|
+
import BrandLink from "./BrandLink.js";
|
|
24
|
+
function BrandHeader({ siteName: siteNameProp, href = "/", logoUrl = null, navItems = [], actions = null }) {
|
|
25
|
+
const brand = useBrand();
|
|
26
|
+
const siteName = siteNameProp === void 0 ? brand.siteName : siteNameProp;
|
|
27
|
+
const { rt } = useTheme();
|
|
28
|
+
const t = useTranslations("uikit");
|
|
29
|
+
const barH = rt?.barHeight ?? null;
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
const hasNav = navItems.length > 0;
|
|
32
|
+
return /* @__PURE__ */ jsxs(AppBar, { position: "sticky", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs(Toolbar, { sx: barH && barH !== 64 ? { gap: 2, minHeight: `${barH}px !important`, px: 2 } : { gap: 2 }, children: [
|
|
34
|
+
hasNav ? /* @__PURE__ */ jsx(
|
|
35
|
+
IconButton,
|
|
36
|
+
{
|
|
37
|
+
edge: "start",
|
|
38
|
+
onClick: () => setOpen(true),
|
|
39
|
+
sx: { display: { md: "none" }, mr: -1 },
|
|
40
|
+
"aria-label": t("nav.open"),
|
|
41
|
+
children: /* @__PURE__ */ jsx(MenuIcon, {})
|
|
42
|
+
}
|
|
43
|
+
) : null,
|
|
44
|
+
/* @__PURE__ */ jsx(BrandLink, { siteName, href, logoUrl }),
|
|
45
|
+
/* @__PURE__ */ jsx(Box, { sx: { flexGrow: 1 } }),
|
|
46
|
+
actions ? /* @__PURE__ */ jsx(Box, { sx: { flexShrink: 0, display: "flex", alignItems: "center" }, children: actions }) : null,
|
|
47
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: hasNav ? { xs: "none", md: "contents" } : "contents" }, children: [
|
|
48
|
+
/* @__PURE__ */ jsx(LanguageSwitcher, {}),
|
|
49
|
+
/* @__PURE__ */ jsx(ModeToggle, {})
|
|
50
|
+
] })
|
|
51
|
+
] }),
|
|
52
|
+
hasNav ? /* @__PURE__ */ jsxs(
|
|
53
|
+
Drawer,
|
|
54
|
+
{
|
|
55
|
+
anchor: "right",
|
|
56
|
+
open,
|
|
57
|
+
onClose: () => setOpen(false),
|
|
58
|
+
sx: { display: { md: "none" } },
|
|
59
|
+
slotProps: { paper: { sx: { width: "min(78vw, 320px)" } } },
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, p: 1.5 }, children: [
|
|
62
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", sx: { color: "text.secondary" }, noWrap: true, children: siteName }),
|
|
63
|
+
/* @__PURE__ */ jsx(IconButton, { onClick: () => setOpen(false), size: "small", "aria-label": t("nav.close"), children: /* @__PURE__ */ jsx(CloseRoundedIcon, { fontSize: "small" }) })
|
|
64
|
+
] }),
|
|
65
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
66
|
+
/* @__PURE__ */ jsx(List, { onClick: (e) => {
|
|
67
|
+
if (e.target.closest("a")) setOpen(false);
|
|
68
|
+
}, children: navItems.map((item) => /* @__PURE__ */ jsx(ListItem, { disablePadding: true, children: /* @__PURE__ */ jsx(ListItemButton, { component: Link, href: item.href, children: /* @__PURE__ */ jsx(ListItemText, { primary: item.label }) }) }, item.href)) }),
|
|
69
|
+
/* @__PURE__ */ jsx(Divider, { sx: { mt: "auto" } }),
|
|
70
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, p: 1.5 }, children: [
|
|
71
|
+
/* @__PURE__ */ jsx(LanguageSwitcher, {}),
|
|
72
|
+
/* @__PURE__ */ jsx(ModeToggle, {})
|
|
73
|
+
] })
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
) : null
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
BrandHeader as default
|
|
81
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Stack from "@mui/material/Stack";
|
|
4
|
+
import Typography from "@mui/material/Typography";
|
|
5
|
+
import Skeleton from "@mui/material/Skeleton";
|
|
6
|
+
import Link from "next/link";
|
|
7
|
+
import BrandMark from "./BrandMark.js";
|
|
8
|
+
import { useBrand } from "./BrandProvider.js";
|
|
9
|
+
function BrandLink({ siteName: siteNameProp, href = "/", logoUrl = null }) {
|
|
10
|
+
const brand = useBrand();
|
|
11
|
+
const siteName = siteNameProp === void 0 ? brand.siteName : siteNameProp;
|
|
12
|
+
if (!siteName && !logoUrl) return null;
|
|
13
|
+
const external = /^https?:\/\//.test(href);
|
|
14
|
+
const linkProps = external ? { component: "a", href, target: "_blank", rel: "noopener noreferrer" } : { component: Link, href };
|
|
15
|
+
return /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1.2, ...linkProps, sx: { alignItems: "center", textDecoration: "none", color: "inherit", minWidth: 0 }, children: [
|
|
16
|
+
/* @__PURE__ */ jsx(BrandMark, { size: 28, logoUrl, alt: "" }),
|
|
17
|
+
siteName ? /* @__PURE__ */ jsx(Typography, { variant: "h6", component: "span", className: "rt-wordmark", noWrap: true, sx: { minWidth: 0 }, children: siteName }) : /* @__PURE__ */ jsx(Skeleton, { variant: "text", width: 104, sx: { fontSize: "1.25rem" } })
|
|
18
|
+
] });
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
BrandLink as default
|
|
22
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Box from "@mui/material/Box";
|
|
3
|
+
function BrandMark({ size = 32, logoUrl = null, alt = "", sx }) {
|
|
4
|
+
const radius = Math.round(size * 0.3);
|
|
5
|
+
const frame = {
|
|
6
|
+
width: size,
|
|
7
|
+
height: size,
|
|
8
|
+
flexShrink: 0,
|
|
9
|
+
borderRadius: `var(--rt-mark-radius, ${radius}px)`,
|
|
10
|
+
display: "grid",
|
|
11
|
+
placeItems: "center"
|
|
12
|
+
};
|
|
13
|
+
if (logoUrl) {
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
Box,
|
|
16
|
+
{
|
|
17
|
+
component: "img",
|
|
18
|
+
src: logoUrl,
|
|
19
|
+
alt,
|
|
20
|
+
sx: [
|
|
21
|
+
{
|
|
22
|
+
...frame,
|
|
23
|
+
/*
|
|
24
|
+
* A logo is sized by HEIGHT, not forced into the glyph's square.
|
|
25
|
+
* Most org logos are wordmarks — wide, not square — and a 3:1
|
|
26
|
+
* wordmark contained in a 28x28 box renders as an unreadable 28x9
|
|
27
|
+
* sliver. Fixing the height and letting the width follow gives a
|
|
28
|
+
* square logo exactly the glyph's footprint and a wordmark room to
|
|
29
|
+
* be legible, up to a cap so a very wide one can't push the nav off
|
|
30
|
+
* the header.
|
|
31
|
+
*/
|
|
32
|
+
width: "auto",
|
|
33
|
+
maxWidth: size * 4,
|
|
34
|
+
// contain, never cover — a logo cropped to fill its box is a
|
|
35
|
+
// mangled logo.
|
|
36
|
+
objectFit: "contain",
|
|
37
|
+
objectPosition: "left center",
|
|
38
|
+
// No gradient and no glow behind a real logo: an org's own mark
|
|
39
|
+
// shouldn't be made to sit on our brand colour.
|
|
40
|
+
backgroundColor: "transparent"
|
|
41
|
+
},
|
|
42
|
+
...Array.isArray(sx) ? sx : [sx]
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return /* @__PURE__ */ jsx(
|
|
48
|
+
Box,
|
|
49
|
+
{
|
|
50
|
+
"aria-hidden": true,
|
|
51
|
+
sx: [
|
|
52
|
+
{
|
|
53
|
+
...frame,
|
|
54
|
+
backgroundImage: "linear-gradient(135deg, var(--rt-grad-from), var(--rt-grad-to))",
|
|
55
|
+
boxShadow: (theme) => `var(--rt-mark-shadow, 0 3px 9px -2px rgba(${theme.vars.palette.primary.mainChannel} / 0.34))`
|
|
56
|
+
},
|
|
57
|
+
...Array.isArray(sx) ? sx : [sx]
|
|
58
|
+
],
|
|
59
|
+
children: /* @__PURE__ */ jsxs(Box, { component: "svg", viewBox: "0 0 24 24", sx: { width: size * 0.58, height: size * 0.58 }, "aria-hidden": true, children: [
|
|
60
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "6", width: "14", height: "2.8", rx: "1.4", fill: "#fff", opacity: "0.95" }),
|
|
61
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "10.6", width: "14", height: "2.8", rx: "1.4", fill: "#fff", opacity: "0.7" }),
|
|
62
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "15.2", width: "9", height: "2.8", rx: "1.4", fill: "#fff", opacity: "0.5" })
|
|
63
|
+
] })
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
BrandMark as default
|
|
69
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
const BrandContext = createContext({ siteName: null, copyright: null });
|
|
5
|
+
function BrandProvider({ siteName = null, copyright = null, children }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(BrandContext.Provider, { value: { siteName, copyright }, children });
|
|
7
|
+
}
|
|
8
|
+
function useBrand() {
|
|
9
|
+
return useContext(BrandContext);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
BrandProvider as default,
|
|
13
|
+
useBrand
|
|
14
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import MuiBreadcrumbs from "@mui/material/Breadcrumbs";
|
|
4
|
+
import Link from "@mui/material/Link";
|
|
5
|
+
import Typography from "@mui/material/Typography";
|
|
6
|
+
import NextLink from "next/link";
|
|
7
|
+
import { useTheme } from "@mui/material/styles";
|
|
8
|
+
import { useTranslations } from "next-intl";
|
|
9
|
+
import HomeOutlinedIcon from "@mui/icons-material/HomeOutlined";
|
|
10
|
+
import ChevronRightRoundedIcon from "@mui/icons-material/ChevronRightRounded";
|
|
11
|
+
function Breadcrumbs({ items, sx }) {
|
|
12
|
+
const t = useTranslations("uikit");
|
|
13
|
+
const { rt } = useTheme();
|
|
14
|
+
const shown = (items ?? []).filter((it) => it && it.label);
|
|
15
|
+
const glass = rt?.ui?.surface === "glass";
|
|
16
|
+
const fontFamily = glass ? rt?.mono : rt?.headingFont ?? void 0;
|
|
17
|
+
const separator = glass ? "/" : "\u203A";
|
|
18
|
+
const berry = rt?.ui?.chrome === "berry";
|
|
19
|
+
const firstHref = shown.find((it) => it.href)?.href ?? "";
|
|
20
|
+
const orgSlug = firstHref.split("/")[1];
|
|
21
|
+
const orgHome = orgSlug ? `/${orgSlug}` : null;
|
|
22
|
+
if (berry) {
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
MuiBreadcrumbs,
|
|
25
|
+
{
|
|
26
|
+
"aria-label": t("breadcrumb.aria"),
|
|
27
|
+
separator: /* @__PURE__ */ jsx(ChevronRightRoundedIcon, { sx: { fontSize: 16 } }),
|
|
28
|
+
sx: [
|
|
29
|
+
{ mb: 2, fontSize: "0.75rem", "& .MuiBreadcrumbs-separator": { color: "text.secondary", mx: 0.5 }, "& .MuiBreadcrumbs-li": { minWidth: 0, display: "flex" } },
|
|
30
|
+
...Array.isArray(sx) ? sx : [sx]
|
|
31
|
+
],
|
|
32
|
+
children: [
|
|
33
|
+
berry && orgHome ? /* @__PURE__ */ jsx(Link, { component: NextLink, href: orgHome, sx: { display: "flex", color: "primary.main" }, "aria-label": t("route.dashboard"), children: /* @__PURE__ */ jsx(HomeOutlinedIcon, { sx: { fontSize: 16 } }) }) : null,
|
|
34
|
+
shown.map((it, i) => it.href && i !== shown.length - 1 ? /* @__PURE__ */ jsx(Link, { component: NextLink, href: it.href, underline: "hover", sx: { color: "text.secondary", fontSize: "0.75rem", fontWeight: 500 }, children: it.label }, i) : /* @__PURE__ */ jsx(Typography, { sx: { color: "text.primary", fontSize: "0.75rem", fontWeight: 500, minWidth: 0, overflowWrap: "anywhere" }, "aria-current": i === shown.length - 1 ? "page" : void 0, children: it.label }, i))
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return /* @__PURE__ */ jsx(
|
|
40
|
+
MuiBreadcrumbs,
|
|
41
|
+
{
|
|
42
|
+
"aria-label": t("breadcrumb.aria"),
|
|
43
|
+
separator,
|
|
44
|
+
sx: [
|
|
45
|
+
{ mb: 2, fontFamily, fontSize: "1rem", "& .MuiBreadcrumbs-separator": { color: "text.secondary", fontWeight: 600 }, "& .MuiBreadcrumbs-li": { minWidth: 0 } },
|
|
46
|
+
...Array.isArray(sx) ? sx : [sx]
|
|
47
|
+
],
|
|
48
|
+
children: shown.map((it, i) => it.href && i !== shown.length - 1 ? (
|
|
49
|
+
// Ancestors carry the org ACCENT so they read as live navigation, not
|
|
50
|
+
// muted chrome — the accent is the theme's, so this stays on-brand
|
|
51
|
+
// under every palette (navigation, not status: green stays reserved).
|
|
52
|
+
/* @__PURE__ */ jsx(Link, { component: NextLink, href: it.href, underline: "hover", color: "primary", variant: "body1", sx: { fontFamily, fontWeight: 600 }, children: it.label }, i)
|
|
53
|
+
) : (
|
|
54
|
+
// The current page is the loudest thing in the trail: full weight, ink.
|
|
55
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", sx: { color: "text.primary", fontFamily, fontWeight: 700, minWidth: 0, overflowWrap: "anywhere" }, "aria-current": i === shown.length - 1 ? "page" : void 0, children: it.label }, i)
|
|
56
|
+
))
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
Breadcrumbs as default
|
|
62
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Box from "@mui/material/Box";
|
|
3
|
+
import Typography from "@mui/material/Typography";
|
|
4
|
+
import { useHeadingLevel } from "./HeadingLevel.js";
|
|
5
|
+
function CardHead({ title, action = null, component }) {
|
|
6
|
+
return /* @__PURE__ */ jsxs(
|
|
7
|
+
Box,
|
|
8
|
+
{
|
|
9
|
+
sx: {
|
|
10
|
+
display: "flex",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
justifyContent: "space-between",
|
|
13
|
+
gap: 2,
|
|
14
|
+
flexWrap: "wrap",
|
|
15
|
+
rowGap: 1.5,
|
|
16
|
+
px: { xs: 2, sm: 3 },
|
|
17
|
+
py: 2,
|
|
18
|
+
borderBottom: 1,
|
|
19
|
+
borderColor: "divider"
|
|
20
|
+
},
|
|
21
|
+
children: [
|
|
22
|
+
title ? /* @__PURE__ */ jsx(CardHeadTitle, { component, children: title }) : /* @__PURE__ */ jsx("span", {}),
|
|
23
|
+
action
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function CardHeadTitle({ children, component }) {
|
|
29
|
+
const level = useHeadingLevel();
|
|
30
|
+
return (
|
|
31
|
+
// Matches SectionPanel's heading: 1.125rem/500 under a berry chrome.
|
|
32
|
+
/* @__PURE__ */ jsx(
|
|
33
|
+
Typography,
|
|
34
|
+
{
|
|
35
|
+
variant: "h6",
|
|
36
|
+
component: component ?? level,
|
|
37
|
+
sx: (t) => ({ minWidth: 0, ...t.rt?.ui?.surface === "berry" ? { fontSize: "1.125rem", fontWeight: 500 } : null }),
|
|
38
|
+
children
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
CardHeadTitle,
|
|
45
|
+
CardHead as default
|
|
46
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useId, useRef, useState } from "react";
|
|
4
|
+
import { useTranslations } from "next-intl";
|
|
5
|
+
import Button from "@mui/material/Button";
|
|
6
|
+
import Dialog from "@mui/material/Dialog";
|
|
7
|
+
import DialogActions from "@mui/material/DialogActions";
|
|
8
|
+
import DialogContent from "@mui/material/DialogContent";
|
|
9
|
+
import DialogContentText from "@mui/material/DialogContentText";
|
|
10
|
+
import DialogTitle from "@mui/material/DialogTitle";
|
|
11
|
+
import { useOrgThemeScope } from "../theme/OrgThemeRegistry.js";
|
|
12
|
+
const TONES = {
|
|
13
|
+
default: { color: "primary", focus: "confirm" },
|
|
14
|
+
caution: { color: "warning", focus: "cancel" },
|
|
15
|
+
destructive: { color: "error", focus: "cancel" }
|
|
16
|
+
};
|
|
17
|
+
const ConfirmContext = createContext(() => Promise.resolve(false));
|
|
18
|
+
function useConfirm() {
|
|
19
|
+
return useContext(ConfirmContext);
|
|
20
|
+
}
|
|
21
|
+
function ConfirmProvider({ children }) {
|
|
22
|
+
const t = useTranslations("uikit");
|
|
23
|
+
const [opts, setOpts] = useState(null);
|
|
24
|
+
const resolver = useRef(null);
|
|
25
|
+
const themeScope = useOrgThemeScope();
|
|
26
|
+
const titleId = useId();
|
|
27
|
+
const bodyId = useId();
|
|
28
|
+
const confirm = useCallback((options) => new Promise((resolve) => {
|
|
29
|
+
resolver.current = resolve;
|
|
30
|
+
setOpts(options ?? {});
|
|
31
|
+
}), []);
|
|
32
|
+
const settle = (result) => {
|
|
33
|
+
resolver.current?.(result);
|
|
34
|
+
resolver.current = null;
|
|
35
|
+
setOpts(null);
|
|
36
|
+
};
|
|
37
|
+
const tone = TONES[opts?.tone] ?? TONES.default;
|
|
38
|
+
const message = opts?.message ?? opts?.body ?? null;
|
|
39
|
+
const confirmLabel = opts?.confirmLabel ?? (opts?.tone === "destructive" ? t("confirm.delete") : t("confirm.confirm"));
|
|
40
|
+
return /* @__PURE__ */ jsxs(ConfirmContext.Provider, { value: confirm, children: [
|
|
41
|
+
children,
|
|
42
|
+
/* @__PURE__ */ jsxs(
|
|
43
|
+
Dialog,
|
|
44
|
+
{
|
|
45
|
+
open: opts !== null,
|
|
46
|
+
onClose: () => settle(false),
|
|
47
|
+
maxWidth: "xs",
|
|
48
|
+
fullWidth: true,
|
|
49
|
+
className: themeScope,
|
|
50
|
+
"aria-labelledby": titleId,
|
|
51
|
+
"aria-describedby": message ? bodyId : void 0,
|
|
52
|
+
slotProps: { paper: { "data-tone": opts?.tone ?? "default" } },
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsx(DialogTitle, { id: titleId, children: opts?.title ?? t("confirm.title") }),
|
|
55
|
+
message ? /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(DialogContentText, { id: bodyId, children: message }) }) : null,
|
|
56
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => settle(false), color: "inherit", autoFocus: tone.focus === "cancel", children: opts?.cancelLabel ?? t("confirm.cancel") }),
|
|
58
|
+
/* @__PURE__ */ jsx(
|
|
59
|
+
Button,
|
|
60
|
+
{
|
|
61
|
+
onClick: () => settle(true),
|
|
62
|
+
color: tone.color,
|
|
63
|
+
variant: "contained",
|
|
64
|
+
autoFocus: tone.focus === "confirm",
|
|
65
|
+
children: confirmLabel
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
] })
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
] });
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
ConfirmProvider as default,
|
|
76
|
+
useConfirm
|
|
77
|
+
};
|