@godxjp/ui 18.5.0 → 18.7.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/dist/components/charts/compact-bar-trend.d.ts +2 -1
- package/dist/components/charts/index.d.ts +4 -1
- package/dist/components/data-display/data-table.d.ts +25 -4
- package/dist/components/data-display/data-table.js +13 -2
- package/dist/components/data-display/empty-state.js +1 -1
- package/dist/components/data-display/index.d.ts +1 -1
- package/dist/components/data-display/list-row.d.ts +19 -0
- package/dist/components/data-display/list-row.js +2 -0
- package/dist/components/data-display/qr-code.d.ts +1 -1
- package/dist/components/data-display/table.d.ts +47 -2
- package/dist/components/data-display/table.js +29 -4
- package/dist/components/data-entry/command.d.ts +10 -10
- package/dist/components/data-entry/index.d.ts +1 -0
- package/dist/components/data-entry/input-otp.d.ts +1 -0
- package/dist/components/feedback/alert.d.ts +20 -2
- package/dist/components/feedback/alert.js +11 -1
- package/dist/components/feedback/dialog.d.ts +14 -1
- package/dist/components/feedback/dialog.js +4 -0
- package/dist/components/feedback/index.d.ts +3 -3
- package/dist/components/feedback/index.js +5 -1
- package/dist/components/general/logo.d.ts +7 -1
- package/dist/components/general/logo.js +1 -1
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +55 -46
- package/dist/components/layout/auth-account-summary.d.ts +5 -0
- package/dist/components/layout/auth-account-summary.js +31 -0
- package/dist/components/layout/auth-shell.d.ts +5 -2
- package/dist/components/layout/centered-shell.d.ts +1 -1
- package/dist/components/layout/centered-shell.js +28 -19
- package/dist/components/layout/error-surface.d.ts +35 -0
- package/dist/components/layout/error-surface.js +155 -0
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +4 -0
- package/dist/components/layout/index.d.ts +7 -0
- package/dist/components/layout/index.js +6 -0
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +23 -41
- package/dist/components/layout/page-header.d.ts +22 -0
- package/dist/components/layout/page-header.js +89 -0
- package/dist/components/navigation/filter-bar.d.ts +2 -2
- package/dist/components/navigation/filter-bar.js +44 -1
- package/dist/components/navigation/index.d.ts +1 -1
- package/dist/components/navigation/steps.js +38 -19
- package/dist/components/navigation/tabs.js +12 -3
- package/dist/components/ui/avatar.d.ts +2 -1
- package/dist/components/ui/avatar.js +2 -1
- package/dist/components/ui/input-otp.d.ts +4 -1
- package/dist/components/ui/input-otp.js +2 -1
- package/dist/components/ui/toggle-group.d.ts +7 -4
- package/dist/components/ui/toggle-group.js +32 -21
- package/dist/email/brand-mark.d.ts +21 -5
- package/dist/email/brand-mark.js +5 -0
- package/dist/email/color.d.ts +2 -1
- package/dist/email/geometry.d.ts +9 -1
- package/dist/email/geometry.js +1 -0
- package/dist/email/index.d.ts +6 -1
- package/dist/email/index.js +6 -0
- package/dist/email/inline-style.d.ts +7 -5
- package/dist/email/inline-style.js +5 -0
- package/dist/email/tokens.generated.d.ts +40 -20
- package/dist/email/tokens.generated.js +40 -20
- package/dist/email/urgency.d.ts +22 -0
- package/dist/email/urgency.js +14 -0
- package/dist/i18n/messages/en.json +26 -1
- package/dist/i18n/messages/ja.json +23 -1
- package/dist/i18n/messages/vi.json +23 -1
- package/dist/props/components/data-display.prop.d.ts +63 -1
- package/dist/props/components/data-entry.prop.d.ts +6 -0
- package/dist/props/components/feedback.prop.d.ts +6 -0
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +244 -4
- package/dist/props/components/navigation.prop.d.ts +57 -1
- package/dist/props/registry.d.ts +205 -5
- package/dist/props/registry.js +270 -6
- package/dist/props/vocabulary/data.prop.d.ts +27 -0
- package/dist/props/vocabulary/index.d.ts +3 -3
- package/dist/props/vocabulary/interaction.prop.d.ts +19 -2
- package/dist/props/vocabulary/layout.prop.d.ts +41 -7
- package/dist/styles/alert-layout.css +36 -0
- package/dist/styles/control.css +28 -0
- package/dist/styles/data-display-layout.css +26 -0
- package/dist/styles/layout.css +212 -0
- package/dist/styles/logo-layout.css +42 -9
- package/dist/styles/navigation-layout.css +109 -0
- package/dist/styles/shell-layout.css +336 -0
- package/dist/styles/table-layout.css +164 -1
- package/dist/theme/dxs.canonical.css +5 -4
- package/dist/tokens/axes.css +4 -3
- package/dist/tokens/base.css +1 -0
- package/dist/tokens/components/data-display.css +13 -0
- package/dist/tokens/components/email.css +43 -16
- package/dist/tokens/components/error-surface.css +36 -0
- package/dist/tokens/components/feedback.css +20 -0
- package/dist/tokens/components/list-row.css +20 -0
- package/dist/tokens/components/logo.css +52 -10
- package/dist/tokens/components/navigation.css +41 -0
- package/dist/tokens/components/shell.css +109 -0
- package/dist/tokens/components/table.css +45 -0
- package/dist/tokens/foundation.css +26 -0
- package/dist/tokens/semantic/layout.css +38 -0
- package/package.json +8 -2
|
@@ -15,6 +15,7 @@ function AppShell({
|
|
|
15
15
|
footer,
|
|
16
16
|
children,
|
|
17
17
|
sidebarCollapsed = false,
|
|
18
|
+
responsiveNavigation = "drawer",
|
|
18
19
|
mobileNav,
|
|
19
20
|
mobileNavLabel,
|
|
20
21
|
mobileNavOpen,
|
|
@@ -22,7 +23,7 @@ function AppShell({
|
|
|
22
23
|
}) {
|
|
23
24
|
const { t } = useTranslation();
|
|
24
25
|
const drawerNav = mobileNav !== void 0 ? mobileNav : sidebar;
|
|
25
|
-
const hasDrawer = drawerNav != null;
|
|
26
|
+
const hasDrawer = responsiveNavigation === "drawer" && drawerNav != null;
|
|
26
27
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
27
28
|
const drawerOpen = mobileNavOpen ?? uncontrolledOpen;
|
|
28
29
|
const setDrawerOpen = onMobileNavOpenChange ?? setUncontrolledOpen;
|
|
@@ -39,51 +40,59 @@ function AppShell({
|
|
|
39
40
|
/* @__PURE__ */ jsx("div", { className: "app-topbar-spacer" }),
|
|
40
41
|
topbarRight !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-right", children: topbarRight })
|
|
41
42
|
] });
|
|
42
|
-
return /* @__PURE__ */ jsxs(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
43
|
+
return /* @__PURE__ */ jsxs(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
className: "app-root",
|
|
47
|
+
"data-collapsed": sidebarCollapsed ? "true" : void 0,
|
|
48
|
+
"data-responsive-navigation": responsiveNavigation,
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsx("aside", { className: "app-sidebar", "aria-label": t("layout.appShell.sidebarLabel"), children: sidebar }),
|
|
51
|
+
/* @__PURE__ */ jsxs("header", { className: "app-topbar ui-scale-fixed", "aria-label": t("layout.appShell.headerLabel"), children: [
|
|
52
|
+
hasDrawer && /* @__PURE__ */ jsxs(Sheet, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
|
|
53
|
+
/* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
54
|
+
Button,
|
|
55
|
+
{
|
|
56
|
+
type: "button",
|
|
57
|
+
variant: "ghost",
|
|
58
|
+
size: "sm",
|
|
59
|
+
className: "app-mobile-nav-trigger hidden max-[900px]:inline-flex",
|
|
60
|
+
"aria-label": t("layout.appShell.openNav"),
|
|
61
|
+
"aria-haspopup": "dialog",
|
|
62
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "size-5", "aria-hidden": "true" })
|
|
63
|
+
}
|
|
64
|
+
) }),
|
|
65
|
+
/* @__PURE__ */ jsxs(
|
|
66
|
+
SheetContent,
|
|
67
|
+
{
|
|
68
|
+
side: "left",
|
|
69
|
+
width: "var(--app-shell-mobile-nav-width)",
|
|
70
|
+
className: "app-mobile-nav-drawer",
|
|
71
|
+
overlayClassName: "app-mobile-nav-overlay",
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx(SheetHeader, { title: mobileNavLabel ?? t("layout.appShell.navLabel") }),
|
|
74
|
+
/* @__PURE__ */ jsx(
|
|
75
|
+
SheetBody,
|
|
76
|
+
{
|
|
77
|
+
className: "app-mobile-nav-body px-[var(--app-shell-mobile-nav-inset)]",
|
|
78
|
+
onClick: handleDrawerClick,
|
|
79
|
+
children: drawerNav
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
] }),
|
|
86
|
+
resolvedTopbar
|
|
87
|
+
] }),
|
|
88
|
+
/* @__PURE__ */ jsxs("main", { className: "app-main", "aria-label": t("layout.appShell.mainLabel"), tabIndex: 0, children: [
|
|
89
|
+
breadcrumb !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-breadcrumb", children: breadcrumb }),
|
|
90
|
+
children
|
|
91
|
+
] }),
|
|
92
|
+
footer !== void 0 && /* @__PURE__ */ jsx("footer", { className: "app-footer", "aria-label": t("layout.appShell.footerLabel"), children: footer })
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
87
96
|
}
|
|
88
97
|
export {
|
|
89
98
|
AppShell
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { AuthAccountSummaryProp } from "../../props/components/layout.prop.js";
|
|
3
|
+
export type { AuthAccountSummaryProp, AuthAccountSummaryProp as AuthAccountSummaryProps, } from "../../props/components/layout.prop.js";
|
|
4
|
+
/** Compact, token-owned signed-in identity row for hosted auth and device authorization. */
|
|
5
|
+
export declare const AuthAccountSummary: React.ForwardRefExoticComponent<AuthAccountSummaryProp & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { UserRound } from "lucide-react";
|
|
5
|
+
import { cn } from "../../lib/utils.js";
|
|
6
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../data-display/avatar.js";
|
|
7
|
+
import { Button } from "../general/button.js";
|
|
8
|
+
const AuthAccountSummary = React.forwardRef(
|
|
9
|
+
({ email, avatarSrc, avatarFallback, actionLabel, onAction, disabled, className }, ref) => /* @__PURE__ */ jsxs(
|
|
10
|
+
"div",
|
|
11
|
+
{
|
|
12
|
+
ref,
|
|
13
|
+
"data-slot": "auth-account-summary",
|
|
14
|
+
className: cn("ui-auth-account-summary", className),
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ jsxs("div", { "data-slot": "auth-account-identity", className: "ui-auth-account-identity", children: [
|
|
17
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "ui-auth-account-avatar", "aria-hidden": "true", children: [
|
|
18
|
+
avatarSrc ? /* @__PURE__ */ jsx(AvatarImage, { src: avatarSrc, alt: "" }) : null,
|
|
19
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: avatarFallback ?? /* @__PURE__ */ jsx(UserRound, { className: "ui-auth-account-fallback-icon", "aria-hidden": "true" }) })
|
|
20
|
+
] }),
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: "ui-auth-account-email", dir: "auto", title: email, children: email })
|
|
22
|
+
] }),
|
|
23
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled, onClick: onAction, children: actionLabel })
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
);
|
|
28
|
+
AuthAccountSummary.displayName = "AuthAccountSummary";
|
|
29
|
+
export {
|
|
30
|
+
AuthAccountSummary
|
|
31
|
+
};
|
|
@@ -9,8 +9,11 @@ export type { AuthShellProp, AuthShellProp as AuthShellProps, } from "../../prop
|
|
|
9
9
|
* card) so `prefers-reduced-motion` is honoured in one place.
|
|
10
10
|
*
|
|
11
11
|
* `preset` names the flow MEASURE (card max-width + desktop/mobile page gutters) so a consumer
|
|
12
|
-
* never overrides
|
|
13
|
-
*
|
|
12
|
+
* never overrides page geometry by hand: `"login"` = SCR-001's stable identity/card/footer anchor
|
|
13
|
+
* for standalone and real requester states; `"registration"` = the 360px sign-up measure, the only
|
|
14
|
+
* START-aligned preset (a tall sign-up card must scroll, not clip its own top above the scroll
|
|
15
|
+
* origin) and the only one with its own footer clearance; `"device-authorization"` = 380px card
|
|
16
|
+
* with a 5px inline gutter at 390px; `"context-selection"` = 25rem card, edge-to-edge on mobile;
|
|
14
17
|
* `"account-recovery"` = the 432px SCR-008 panel measure shared by password recovery and the
|
|
15
18
|
* sign-in MFA challenge, with a 15px inline gutter at 390px. It is orthogonal to `variant` —
|
|
16
19
|
* combine `variant="canonical"` with any preset.
|
|
@@ -22,4 +22,4 @@ export type { CenteredShellProp, CenteredShellProp as CenteredShellProps, } from
|
|
|
22
22
|
* footgun). Layout-only: delegate motion to `Reveal` (wrap a section) so `prefers-reduced-motion`
|
|
23
23
|
* is honoured in one place.
|
|
24
24
|
*/
|
|
25
|
-
export declare function CenteredShell({ topbar, footer, children, width, align, className, }: CenteredShellProp): import("react").JSX.Element;
|
|
25
|
+
export declare function CenteredShell({ topbar, footer, children, width, align, preset, className, }: CenteredShellProp): import("react").JSX.Element;
|
|
@@ -8,28 +8,37 @@ function CenteredShell({
|
|
|
8
8
|
children,
|
|
9
9
|
width = "md",
|
|
10
10
|
align = "start",
|
|
11
|
+
preset = "default",
|
|
11
12
|
className
|
|
12
13
|
}) {
|
|
13
14
|
const { t } = useTranslation();
|
|
14
|
-
return /* @__PURE__ */ jsxs(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
children:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
return /* @__PURE__ */ jsxs(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
"data-slot": "centered-shell",
|
|
19
|
+
"data-preset": preset === "default" ? void 0 : preset,
|
|
20
|
+
className: cn("ui-centered-shell", className),
|
|
21
|
+
children: [
|
|
22
|
+
topbar !== void 0 && /* @__PURE__ */ jsx(
|
|
23
|
+
"header",
|
|
24
|
+
{
|
|
25
|
+
className: "ui-centered-shell-bar ui-scale-fixed",
|
|
26
|
+
"aria-label": t("layout.centeredShell.headerLabel"),
|
|
27
|
+
children: topbar
|
|
28
|
+
}
|
|
29
|
+
),
|
|
30
|
+
/* @__PURE__ */ jsx("main", { className: "ui-centered-shell-main", "aria-label": t("layout.centeredShell.mainLabel"), children: /* @__PURE__ */ jsx("div", { className: "ui-centered-shell-column", "data-width": width, "data-align": align, children }) }),
|
|
31
|
+
footer !== void 0 && /* @__PURE__ */ jsx(
|
|
32
|
+
"footer",
|
|
33
|
+
{
|
|
34
|
+
className: "ui-centered-shell-footer",
|
|
35
|
+
"aria-label": t("layout.centeredShell.footerLabel"),
|
|
36
|
+
children: footer
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
);
|
|
33
42
|
}
|
|
34
43
|
export {
|
|
35
44
|
CenteredShell
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ErrorSurfaceProp } from "../../props/components/layout.prop.js";
|
|
3
|
+
export type { ErrorSurfaceMaintenanceProp, ErrorSurfaceProp, ErrorSurfaceProp as ErrorSurfaceProps, } from "../../props/components/layout.prop.js";
|
|
4
|
+
export type { ErrorSurfaceModeProp, ErrorSurfaceStatusProp } from "../../props/vocabulary/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* ErrorSurface — the package-owned semantic exception surface for **403 · 404 · 500 · 503**
|
|
7
|
+
* (gh#221, gh#251).
|
|
8
|
+
*
|
|
9
|
+
* It exists because the alternative — every app composing `EmptyState` + `Flex` + `Text` inside a
|
|
10
|
+
* hand-picked shell — is unimportable: a consumer cannot `import` a docs page, so the four
|
|
11
|
+
* exception screens drifted per application (a generic `Card` in an `AuthShell`, a local
|
|
12
|
+
* `.canonical-auth-card`, a bespoke `min-h-dvh` class). What the surface owns is the CONTRACT the
|
|
13
|
+
* composition kept losing:
|
|
14
|
+
*
|
|
15
|
+
* - **`mode` is the shell contract.** `application` (403/404) renders the surface as the BODY you
|
|
16
|
+
* place inside the `AppShell` the route already provides, so the sidebar, topbar and breadcrumb
|
|
17
|
+
* are PRESERVED — it never reconstructs navigation chrome, because nav data and the user menu are
|
|
18
|
+
* consumer-owned and a component cannot manufacture them. `system` (500/503) owns the page and
|
|
19
|
+
* renders `CenteredShell align="center"`, so the viewport-centred geometry at 1440 / 1024 / 390
|
|
20
|
+
* is package-owned and the consumer writes no `min-h-dvh`, no flex-centring class, no media query.
|
|
21
|
+
* - **Exactly one recovery action**, structurally (see `singleAction`).
|
|
22
|
+
* - **Semantic metadata slots** instead of free-form paragraphs: `requestId` (mono/tabular so a
|
|
23
|
+
* support id is read out accurately), `permission` and `organization` (which of the two a 403 is
|
|
24
|
+
* actually about), and `maintenance` (ISO-8601 + IANA formatted through `Intl.DateTimeFormat`,
|
|
25
|
+
* with an optional labelled `Progress` meter). Each is a `<dt>`/`<dd>` pair, so the label↔value
|
|
26
|
+
* relationship survives for a screen reader instead of being a colon in a sentence.
|
|
27
|
+
* - **Valid heading order by default** — `h2` under a `PageContainer` `h1` in `application` mode,
|
|
28
|
+
* `h1` on a `system` page — and the status code announced as "HTTP status 403", not "403".
|
|
29
|
+
*
|
|
30
|
+
* PRODUCT COPY stays consumer-owned: `title` / `description` / `action` come from the app's own
|
|
31
|
+
* `t()`. The surface localizes only its OWN metadata labels. It holds no state, runs no effect and
|
|
32
|
+
* opens no portal, so it renders fully server-side — an exception page must be readable before
|
|
33
|
+
* hydration.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ErrorSurface: React.ForwardRefExoticComponent<ErrorSurfaceProp & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { SearchX, ServerCrash, ShieldAlert, Wrench } from "lucide-react";
|
|
5
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
+
import { cn } from "../../lib/utils.js";
|
|
7
|
+
import { EmptyState } from "../data-display/empty-state.js";
|
|
8
|
+
import { Progress } from "../data-display/progress.js";
|
|
9
|
+
import { Text } from "../general/typography.js";
|
|
10
|
+
import { CenteredShell } from "./centered-shell.js";
|
|
11
|
+
const STATUS_META = {
|
|
12
|
+
403: { icon: ShieldAlert, tone: "warning" },
|
|
13
|
+
404: { icon: SearchX, tone: "muted" },
|
|
14
|
+
500: { icon: ServerCrash, tone: "destructive" },
|
|
15
|
+
503: { icon: Wrench, tone: "warning" }
|
|
16
|
+
};
|
|
17
|
+
function singleAction(action) {
|
|
18
|
+
const flattened = React.Children.toArray(action).flatMap(
|
|
19
|
+
(child) => React.isValidElement(child) && child.type === React.Fragment ? React.Children.toArray(child.props.children) : [child]
|
|
20
|
+
);
|
|
21
|
+
if (flattened.length > 1 && process.env.NODE_ENV !== "production") {
|
|
22
|
+
console.error(
|
|
23
|
+
"[@godxjp/ui] ErrorSurface: `action` takes EXACTLY ONE recovery action. The extra elements were dropped. Put support contact or secondary guidance in `description`."
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
return flattened[0] ?? null;
|
|
27
|
+
}
|
|
28
|
+
function formatMaintenanceWindow(maintenance, locale) {
|
|
29
|
+
const options = {
|
|
30
|
+
dateStyle: "medium",
|
|
31
|
+
timeStyle: "short",
|
|
32
|
+
...maintenance.timeZone ? { timeZone: maintenance.timeZone } : {}
|
|
33
|
+
};
|
|
34
|
+
const start = new Date(maintenance.start);
|
|
35
|
+
if (Number.isNaN(start.getTime())) return maintenance.start;
|
|
36
|
+
const formatter = new Intl.DateTimeFormat(locale, options);
|
|
37
|
+
if (maintenance.end === void 0) return formatter.format(start);
|
|
38
|
+
const end = new Date(maintenance.end);
|
|
39
|
+
if (Number.isNaN(end.getTime())) return formatter.format(start);
|
|
40
|
+
return formatter.formatRange(start, end);
|
|
41
|
+
}
|
|
42
|
+
const ErrorSurface = React.forwardRef(
|
|
43
|
+
function ErrorSurface2({
|
|
44
|
+
mode,
|
|
45
|
+
status,
|
|
46
|
+
title,
|
|
47
|
+
description,
|
|
48
|
+
action,
|
|
49
|
+
icon,
|
|
50
|
+
tone,
|
|
51
|
+
titleLevel,
|
|
52
|
+
requestId,
|
|
53
|
+
permission,
|
|
54
|
+
organization,
|
|
55
|
+
maintenance,
|
|
56
|
+
brand,
|
|
57
|
+
footer,
|
|
58
|
+
width = "sm",
|
|
59
|
+
id,
|
|
60
|
+
className
|
|
61
|
+
}, ref) {
|
|
62
|
+
const { t, locale } = useTranslation();
|
|
63
|
+
const meta = STATUS_META[status];
|
|
64
|
+
const resolvedIcon = icon ?? meta.icon;
|
|
65
|
+
const resolvedTone = tone ?? meta.tone;
|
|
66
|
+
const resolvedTitleLevel = titleLevel ?? (mode === "system" ? 1 : 2);
|
|
67
|
+
const maintenanceWindow = maintenance === void 0 ? void 0 : formatMaintenanceWindow(maintenance, locale);
|
|
68
|
+
const maintenanceProgress = maintenance?.progress;
|
|
69
|
+
const hasProgress = typeof maintenanceProgress === "number" && Number.isFinite(maintenanceProgress);
|
|
70
|
+
const progressLabel = hasProgress ? t("layout.errorSurface.maintenanceProgress", {
|
|
71
|
+
percent: new Intl.NumberFormat(locale, {
|
|
72
|
+
style: "percent",
|
|
73
|
+
maximumFractionDigits: 0
|
|
74
|
+
}).format(Math.max(0, Math.min(100, maintenanceProgress)) / 100)
|
|
75
|
+
}) : void 0;
|
|
76
|
+
const hasMetadata = requestId !== void 0 || permission !== void 0 || organization !== void 0 || maintenanceWindow !== void 0;
|
|
77
|
+
const surface = /* @__PURE__ */ jsxs(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
ref,
|
|
81
|
+
id,
|
|
82
|
+
"data-slot": "error-surface",
|
|
83
|
+
"data-mode": mode,
|
|
84
|
+
"data-status": status,
|
|
85
|
+
className: cn("ui-error-surface", className),
|
|
86
|
+
children: [
|
|
87
|
+
brand !== void 0 && mode === "system" && /* @__PURE__ */ jsx("div", { className: "ui-error-surface-brand", children: brand }),
|
|
88
|
+
/* @__PURE__ */ jsxs(
|
|
89
|
+
Text,
|
|
90
|
+
{
|
|
91
|
+
as: "p",
|
|
92
|
+
size: "sm",
|
|
93
|
+
tone: "muted",
|
|
94
|
+
weight: "medium",
|
|
95
|
+
mono: true,
|
|
96
|
+
tabular: true,
|
|
97
|
+
className: "ui-error-surface-status",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: status }),
|
|
100
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: t("layout.errorSurface.statusLabel", { status: String(status) }) })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
),
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
EmptyState,
|
|
106
|
+
{
|
|
107
|
+
icon: resolvedIcon,
|
|
108
|
+
tone: resolvedTone,
|
|
109
|
+
titleLevel: resolvedTitleLevel,
|
|
110
|
+
title,
|
|
111
|
+
description,
|
|
112
|
+
action: singleAction(action),
|
|
113
|
+
className: "ui-error-surface-body"
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
hasMetadata && /* A description list, not a sentence: each metadata row keeps a machine-readable
|
|
117
|
+
* label↔value pair, so "Request ID / 01J9Z…" is navigable instead of being one run-on
|
|
118
|
+
* paragraph. `<div>` wrappers around dt/dd are valid HTML5 and give each row its box. */
|
|
119
|
+
/* @__PURE__ */ jsxs("dl", { className: "ui-error-surface-meta", children: [
|
|
120
|
+
requestId !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
|
|
121
|
+
/* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.requestId") }),
|
|
122
|
+
/* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", mono: true, tabular: true, children: requestId })
|
|
123
|
+
] }),
|
|
124
|
+
permission !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.permission") }),
|
|
126
|
+
/* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", mono: true, children: permission })
|
|
127
|
+
] }),
|
|
128
|
+
organization !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
|
|
129
|
+
/* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.organization") }),
|
|
130
|
+
/* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", children: organization })
|
|
131
|
+
] }),
|
|
132
|
+
maintenanceWindow !== void 0 && maintenance !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
|
|
133
|
+
/* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.maintenanceWindow") }),
|
|
134
|
+
/* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", children: /* @__PURE__ */ jsx("time", { dateTime: maintenance.start, children: maintenanceWindow }) })
|
|
135
|
+
] })
|
|
136
|
+
] }),
|
|
137
|
+
hasProgress && progressLabel !== void 0 && /* @__PURE__ */ jsx("div", { className: "ui-error-surface-progress", children: /* @__PURE__ */ jsx(
|
|
138
|
+
Progress,
|
|
139
|
+
{
|
|
140
|
+
value: Math.max(0, Math.min(100, maintenanceProgress)),
|
|
141
|
+
label: progressLabel,
|
|
142
|
+
tone: "warning"
|
|
143
|
+
}
|
|
144
|
+
) })
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
if (mode === "application") return surface;
|
|
149
|
+
return /* @__PURE__ */ jsx(CenteredShell, { align: "center", width, ...footer !== void 0 ? { footer } : {}, children: surface });
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
ErrorSurface.displayName = "ErrorSurface";
|
|
153
|
+
export {
|
|
154
|
+
ErrorSurface
|
|
155
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FlexProp } from "../../props/components/layout.prop.js";
|
|
2
2
|
export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProp as FlexProps, } from "../../props/components/layout.prop.js";
|
|
3
|
-
export declare function Flex({ direction, gap, align, justify, wrap, className, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
3
|
+
export declare function Flex({ direction, gap, align, justify, wrap, hideBelow, hideFrom, className, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
@@ -7,6 +7,8 @@ function Flex({
|
|
|
7
7
|
align,
|
|
8
8
|
justify,
|
|
9
9
|
wrap = false,
|
|
10
|
+
hideBelow,
|
|
11
|
+
hideFrom,
|
|
10
12
|
className,
|
|
11
13
|
children,
|
|
12
14
|
...props
|
|
@@ -18,6 +20,8 @@ function Flex({
|
|
|
18
20
|
"data-align": align,
|
|
19
21
|
"data-justify": justify,
|
|
20
22
|
"data-wrap": wrap ? "true" : void 0,
|
|
23
|
+
"data-hide-below": hideBelow,
|
|
24
|
+
"data-hide-from": hideFrom,
|
|
21
25
|
className: cn("ui-flex", flexGapClass[gap], className),
|
|
22
26
|
...props,
|
|
23
27
|
children
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { PageContainer } from "./page-container.js";
|
|
2
2
|
export type { PageContainerProp, PageContainerProps, BreadcrumbItem, BreadcrumbItemProp, } from "./page-container.js";
|
|
3
|
+
export { PageHeader } from "./page-header.js";
|
|
4
|
+
export type { PageHeaderProp, PageHeaderProps } from "./page-header.js";
|
|
3
5
|
export { Flex } from "./flex.js";
|
|
4
6
|
export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProps, } from "./flex.js";
|
|
5
7
|
export { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
|
|
@@ -14,6 +16,8 @@ export { AuthDivider } from "./auth-divider.js";
|
|
|
14
16
|
export type { AuthDividerProp, AuthDividerProps } from "./auth-divider.js";
|
|
15
17
|
export { AuthIdentity } from "./auth-identity.js";
|
|
16
18
|
export type { AuthIdentityProp, AuthIdentityProps } from "./auth-identity.js";
|
|
19
|
+
export { AuthAccountSummary } from "./auth-account-summary.js";
|
|
20
|
+
export type { AuthAccountSummaryProp, AuthAccountSummaryProps } from "./auth-account-summary.js";
|
|
17
21
|
export { AuthFooter } from "./auth-footer.js";
|
|
18
22
|
export type { AuthFooterProp, AuthFooterProps } from "./auth-footer.js";
|
|
19
23
|
export { AuthStack } from "./auth-stack.js";
|
|
@@ -21,6 +25,9 @@ export type { AuthStackProps } from "./auth-stack.js";
|
|
|
21
25
|
export { CenteredShell } from "./centered-shell.js";
|
|
22
26
|
export type { CenteredShellProp, CenteredShellProps } from "./centered-shell.js";
|
|
23
27
|
export type { CenteredShellWidthProp, CenteredShellAlignProp } from "../../props/vocabulary/index.js";
|
|
28
|
+
export { ErrorSurface } from "./error-surface.js";
|
|
29
|
+
export type { ErrorSurfaceMaintenanceProp, ErrorSurfaceProp, ErrorSurfaceProps, } from "./error-surface.js";
|
|
30
|
+
export type { ErrorSurfaceModeProp, ErrorSurfaceStatusProp } from "../../props/vocabulary/index.js";
|
|
24
31
|
export { Breadcrumb } from "./breadcrumb.js";
|
|
25
32
|
export type { BreadcrumbProps } from "./breadcrumb.js";
|
|
26
33
|
export { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PageContainer } from "./page-container.js";
|
|
2
|
+
import { PageHeader } from "./page-header.js";
|
|
2
3
|
import { Flex } from "./flex.js";
|
|
3
4
|
import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
|
|
4
5
|
import { AppShell } from "./app-shell.js";
|
|
@@ -6,9 +7,11 @@ import { OrgSwitcher } from "./org-switcher.js";
|
|
|
6
7
|
import { AuthShell } from "./auth-shell.js";
|
|
7
8
|
import { AuthDivider } from "./auth-divider.js";
|
|
8
9
|
import { AuthIdentity } from "./auth-identity.js";
|
|
10
|
+
import { AuthAccountSummary } from "./auth-account-summary.js";
|
|
9
11
|
import { AuthFooter } from "./auth-footer.js";
|
|
10
12
|
import { AuthStack } from "./auth-stack.js";
|
|
11
13
|
import { CenteredShell } from "./centered-shell.js";
|
|
14
|
+
import { ErrorSurface } from "./error-surface.js";
|
|
12
15
|
import { Breadcrumb } from "./breadcrumb.js";
|
|
13
16
|
import { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
|
|
14
17
|
import { Topbar } from "./topbar.js";
|
|
@@ -21,6 +24,7 @@ import { LegalDocumentShell } from "./legal-document-shell.js";
|
|
|
21
24
|
export {
|
|
22
25
|
AppShell,
|
|
23
26
|
AspectRatio,
|
|
27
|
+
AuthAccountSummary,
|
|
24
28
|
AuthDivider,
|
|
25
29
|
AuthFooter,
|
|
26
30
|
AuthIdentity,
|
|
@@ -28,11 +32,13 @@ export {
|
|
|
28
32
|
AuthStack,
|
|
29
33
|
Breadcrumb,
|
|
30
34
|
CenteredShell,
|
|
35
|
+
ErrorSurface,
|
|
31
36
|
Flex,
|
|
32
37
|
LegalDocumentShell,
|
|
33
38
|
MasterDetail,
|
|
34
39
|
OrgSwitcher,
|
|
35
40
|
PageContainer,
|
|
41
|
+
PageHeader,
|
|
36
42
|
ResizableHandle,
|
|
37
43
|
ResizablePanel,
|
|
38
44
|
ResizablePanelGroup,
|
|
@@ -2,7 +2,7 @@ import type { PageContainerProp, PageInsetProp } from "../../props/components/la
|
|
|
2
2
|
export type { PageContainerProp, PageContainerProp as PageContainerProps, } from "../../props/components/layout.prop.js";
|
|
3
3
|
export type { BreadcrumbItemProp, BreadcrumbItemProp as BreadcrumbItem, } from "../../props/vocabulary/navigation.prop.js";
|
|
4
4
|
export declare function PageContainerInset({ className, children, ...props }: PageInsetProp): import("react").JSX.Element;
|
|
5
|
-
declare function PageContainerRoot({ title, subtitle, extra, footer, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, headerLayout, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
|
|
5
|
+
declare function PageContainerRoot({ title, subtitle, meta, headerLoading, extra, footer, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, preset, headerLayout, measure, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
|
|
6
6
|
export declare const PageContainer: typeof PageContainerRoot & {
|
|
7
7
|
Inset: typeof PageContainerInset;
|
|
8
8
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import { ChevronRight } from "lucide-react";
|
|
5
|
-
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
4
|
import { cn } from "../../lib/utils.js";
|
|
7
5
|
import { densityClass, pageContainerVariantClass } from "../../lib/variants.js";
|
|
6
|
+
import { PageHeader } from "./page-header.js";
|
|
8
7
|
function scrollParent(el) {
|
|
9
8
|
let node = el?.parentElement ?? null;
|
|
10
9
|
while (node) {
|
|
@@ -36,6 +35,8 @@ function PageContainerInset({ className, children, ...props }) {
|
|
|
36
35
|
function PageContainerRoot({
|
|
37
36
|
title,
|
|
38
37
|
subtitle,
|
|
38
|
+
meta,
|
|
39
|
+
headerLoading,
|
|
39
40
|
extra,
|
|
40
41
|
footer,
|
|
41
42
|
breadcrumb,
|
|
@@ -44,7 +45,9 @@ function PageContainerRoot({
|
|
|
44
45
|
linkComponent: LinkComponent = "a",
|
|
45
46
|
density,
|
|
46
47
|
variant = "default",
|
|
48
|
+
preset = "default",
|
|
47
49
|
headerLayout = "stack",
|
|
50
|
+
measure = "default",
|
|
48
51
|
stickyFooter = false,
|
|
49
52
|
footerReveal = "always",
|
|
50
53
|
fill = false,
|
|
@@ -53,10 +56,11 @@ function PageContainerRoot({
|
|
|
53
56
|
}) {
|
|
54
57
|
const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
|
|
55
58
|
const { headerRef, revealed } = useFooterReveal(reveal);
|
|
56
|
-
const { t } = useTranslation();
|
|
57
59
|
return /* @__PURE__ */ jsxs(
|
|
58
60
|
"div",
|
|
59
61
|
{
|
|
62
|
+
"data-preset": preset,
|
|
63
|
+
"data-measure": measure,
|
|
60
64
|
"data-revealed": revealed ? "true" : void 0,
|
|
61
65
|
className: cn(
|
|
62
66
|
"ui-page-container",
|
|
@@ -70,44 +74,22 @@ function PageContainerRoot({
|
|
|
70
74
|
className
|
|
71
75
|
),
|
|
72
76
|
children: [
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
) : /* @__PURE__ */ jsx(
|
|
91
|
-
"span",
|
|
92
|
-
{
|
|
93
|
-
className: isLast ? "text-foreground" : "",
|
|
94
|
-
"aria-current": isLast ? "page" : void 0,
|
|
95
|
-
children: item.label
|
|
96
|
-
}
|
|
97
|
-
),
|
|
98
|
-
!isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
|
|
99
|
-
] }, i);
|
|
100
|
-
}) })
|
|
101
|
-
}
|
|
102
|
-
),
|
|
103
|
-
/* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
|
|
104
|
-
/* @__PURE__ */ jsxs("div", { className: "ui-page-header-heading min-w-0", children: [
|
|
105
|
-
/* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
|
|
106
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
|
|
107
|
-
] }),
|
|
108
|
-
extra && /* @__PURE__ */ jsx("div", { className: "ui-page-header-extra", children: extra })
|
|
109
|
-
] })
|
|
110
|
-
] }),
|
|
77
|
+
/* @__PURE__ */ jsx(
|
|
78
|
+
PageHeader,
|
|
79
|
+
{
|
|
80
|
+
ref: headerRef,
|
|
81
|
+
title,
|
|
82
|
+
subtitle,
|
|
83
|
+
meta,
|
|
84
|
+
extra,
|
|
85
|
+
breadcrumb,
|
|
86
|
+
breadcrumbLabel,
|
|
87
|
+
breadcrumbAriaLabel,
|
|
88
|
+
linkComponent: LinkComponent,
|
|
89
|
+
layout: headerLayout,
|
|
90
|
+
loading: headerLoading
|
|
91
|
+
}
|
|
92
|
+
),
|
|
111
93
|
children != null && /* @__PURE__ */ jsx("div", { className: "ui-page-body", children }),
|
|
112
94
|
footer && /* @__PURE__ */ jsx("footer", { className: "ui-page-footer", children: footer })
|
|
113
95
|
]
|