@godxjp/ui 18.9.0 → 18.10.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/LICENSE +202 -0
- package/dist/components/data-display/badge.d.ts +7 -0
- package/dist/components/data-display/badge.js +1 -1
- package/dist/components/data-display/card.d.ts +3 -27
- package/dist/components/data-display/card.js +1 -4
- package/dist/components/data-display/data-table.js +5 -4
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/permission-matrix.d.ts +26 -0
- package/dist/components/data-display/permission-matrix.js +219 -0
- package/dist/components/data-display/table.d.ts +12 -0
- package/dist/components/data-entry/branch-scope-picker.d.ts +43 -0
- package/dist/components/data-entry/branch-scope-picker.js +200 -0
- package/dist/components/data-entry/command-palette.d.ts +1 -42
- package/dist/components/data-entry/command-palette.js +2 -31
- package/dist/components/data-entry/index.d.ts +2 -0
- package/dist/components/data-entry/index.js +2 -0
- package/dist/components/data-entry/input-otp.d.ts +1 -1
- package/dist/components/feedback/alert.d.ts +2 -20
- package/dist/components/feedback/alert.js +1 -11
- package/dist/components/feedback/banner.d.ts +21 -0
- package/dist/components/feedback/banner.js +15 -0
- package/dist/components/feedback/index.d.ts +4 -2
- package/dist/components/feedback/index.js +2 -2
- package/dist/components/general/typography.d.ts +1 -0
- package/dist/components/general/typography.js +32 -13
- package/dist/components/layout/auth-shell.d.ts +2 -2
- package/dist/components/layout/index.d.ts +2 -2
- package/dist/components/layout/index.js +2 -2
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +45 -19
- package/dist/components/layout/service-role-panel.d.ts +25 -0
- package/dist/components/layout/service-role-panel.js +206 -0
- package/dist/components/navigation/filter-bar.d.ts +2 -2
- package/dist/components/navigation/filter-bar.js +113 -34
- package/dist/components/navigation/index.d.ts +1 -1
- package/dist/components/navigation/steps.d.ts +2 -2
- package/dist/components/navigation/steps.js +2 -4
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.js +1 -2
- package/dist/components/ui/input-otp.d.ts +3 -15
- package/dist/components/ui/input-otp.js +1 -2
- package/dist/email/tokens.generated.d.ts +2 -2
- package/dist/email/tokens.generated.js +2 -2
- package/dist/i18n/messages/en.json +56 -4
- package/dist/i18n/messages/ja.json +51 -5
- package/dist/i18n/messages/vi.json +50 -4
- package/dist/props/components/data-display.prop.d.ts +66 -16
- package/dist/props/components/data-entry.prop.d.ts +54 -7
- package/dist/props/components/feedback.prop.d.ts +3 -3
- package/dist/props/components/general.prop.d.ts +8 -1
- package/dist/props/components/layout.prop.d.ts +75 -54
- package/dist/props/components/navigation.prop.d.ts +77 -59
- package/dist/props/registry.d.ts +274 -73
- package/dist/props/registry.js +362 -93
- package/dist/props/vocabulary/content.prop.d.ts +5 -0
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +4 -7
- package/dist/props/vocabulary/layout.prop.d.ts +2 -2
- package/dist/styles/alert-layout.css +15 -32
- package/dist/styles/badge-layout.css +5 -0
- package/dist/styles/base.css +0 -28
- package/dist/styles/card-layout.css +13 -49
- package/dist/styles/control.css +2 -12
- package/dist/styles/data-display-layout.css +0 -16
- package/dist/styles/layout.css +102 -108
- package/dist/styles/navigation-layout.css +3 -8
- package/dist/styles/shell-layout.css +17 -17
- package/dist/styles/table-layout.css +77 -29
- package/dist/styles/text-layout.css +13 -0
- package/dist/tokens/base.css +1 -0
- package/dist/tokens/components/badge.css +5 -0
- package/dist/tokens/components/banner.css +16 -0
- package/dist/tokens/components/card.css +0 -12
- package/dist/tokens/components/control.css +0 -16
- package/dist/tokens/components/data-display.css +0 -12
- package/dist/tokens/components/email.css +2 -8
- package/dist/tokens/components/feedback.css +0 -20
- package/dist/tokens/components/navigation.css +10 -34
- package/dist/tokens/components/shell.css +24 -36
- package/dist/tokens/components/table.css +41 -22
- package/dist/tokens/semantic/layout.css +5 -12
- package/package.json +15 -2
- package/dist/components/layout/page-header.d.ts +0 -22
- package/dist/components/layout/page-header.js +0 -89
|
@@ -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,
|
|
5
|
+
declare function PageContainerRoot({ title, subtitle, status, 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,9 +1,10 @@
|
|
|
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";
|
|
4
6
|
import { cn } from "../../lib/utils.js";
|
|
5
7
|
import { densityClass, pageContainerVariantClass } from "../../lib/variants.js";
|
|
6
|
-
import { PageHeader } from "./page-header.js";
|
|
7
8
|
function scrollParent(el) {
|
|
8
9
|
let node = el?.parentElement ?? null;
|
|
9
10
|
while (node) {
|
|
@@ -35,8 +36,7 @@ function PageContainerInset({ className, children, ...props }) {
|
|
|
35
36
|
function PageContainerRoot({
|
|
36
37
|
title,
|
|
37
38
|
subtitle,
|
|
38
|
-
|
|
39
|
-
headerLoading,
|
|
39
|
+
status,
|
|
40
40
|
extra,
|
|
41
41
|
footer,
|
|
42
42
|
breadcrumb,
|
|
@@ -56,6 +56,7 @@ function PageContainerRoot({
|
|
|
56
56
|
}) {
|
|
57
57
|
const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
|
|
58
58
|
const { headerRef, revealed } = useFooterReveal(reveal);
|
|
59
|
+
const { t } = useTranslation();
|
|
59
60
|
return /* @__PURE__ */ jsxs(
|
|
60
61
|
"div",
|
|
61
62
|
{
|
|
@@ -74,22 +75,47 @@ function PageContainerRoot({
|
|
|
74
75
|
className
|
|
75
76
|
),
|
|
76
77
|
children: [
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
78
|
+
/* @__PURE__ */ jsxs("header", { ref: headerRef, className: "ui-page-header", "data-layout": headerLayout, children: [
|
|
79
|
+
breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx(
|
|
80
|
+
"nav",
|
|
81
|
+
{
|
|
82
|
+
"aria-label": breadcrumbLabel ?? breadcrumbAriaLabel ?? t("navigation.breadcrumb.ariaLabel"),
|
|
83
|
+
className: "ui-breadcrumb",
|
|
84
|
+
children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: breadcrumb.map((item, i) => {
|
|
85
|
+
const isLast = i === breadcrumb.length - 1;
|
|
86
|
+
return /* @__PURE__ */ jsxs("li", { className: "ui-inline-xs", children: [
|
|
87
|
+
item.to && !isLast ? /* @__PURE__ */ jsx(
|
|
88
|
+
LinkComponent,
|
|
89
|
+
{
|
|
90
|
+
href: item.to,
|
|
91
|
+
to: item.to,
|
|
92
|
+
className: "hover:text-foreground hover:underline",
|
|
93
|
+
children: item.label
|
|
94
|
+
}
|
|
95
|
+
) : /* @__PURE__ */ jsx(
|
|
96
|
+
"span",
|
|
97
|
+
{
|
|
98
|
+
className: isLast ? "text-foreground" : "",
|
|
99
|
+
"aria-current": isLast ? "page" : void 0,
|
|
100
|
+
children: item.label
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
!isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
|
|
104
|
+
] }, i);
|
|
105
|
+
}) })
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
|
|
109
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-page-header-heading min-w-0", children: [
|
|
110
|
+
status != null ? /* @__PURE__ */ jsxs("div", { className: "ui-page-header-title-row", children: [
|
|
111
|
+
/* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
|
|
112
|
+
/* @__PURE__ */ jsx("div", { className: "ui-page-header-status", children: status })
|
|
113
|
+
] }) : /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
|
|
114
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
|
|
115
|
+
] }),
|
|
116
|
+
extra && /* @__PURE__ */ jsx("div", { className: "ui-page-header-extra", children: extra })
|
|
117
|
+
] })
|
|
118
|
+
] }),
|
|
93
119
|
children != null && /* @__PURE__ */ jsx("div", { className: "ui-page-body", children }),
|
|
94
120
|
footer && /* @__PURE__ */ jsx("footer", { className: "ui-page-footer", children: footer })
|
|
95
121
|
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ServiceRolePanelProp } from "../../props/components/layout.prop.js";
|
|
3
|
+
export type { ServiceRoleItemProp, ServiceRolePanelProp, ServiceRolePanelProp as ServiceRolePanelProps, } from "../../props/components/layout.prop.js";
|
|
4
|
+
/**
|
|
5
|
+
* ServiceRolePanel — the canonical role-collection ⇄ role-detail surface (gh#257 / DXS
|
|
6
|
+
* platform#311).
|
|
7
|
+
*
|
|
8
|
+
* A THIN formalized composition over `MasterDetail` (which owns ALL geometry: rail tracks, gap,
|
|
9
|
+
* stacking below the threshold — 1440/1024 two-track, 390 stacked list-then-detail) plus real
|
|
10
|
+
* primitives, exported because a consumer cannot import a docs page (the gh#251 lesson). What the
|
|
11
|
+
* panel adds is exactly the contract every service-role screen kept re-deriving:
|
|
12
|
+
*
|
|
13
|
+
* - **Selection** as the controlled triad (`value`/`defaultValue`/`onValueChange`, defaulting to
|
|
14
|
+
* the first role). Each role row is a real `Button` carrying `aria-current`, and selecting moves
|
|
15
|
+
* focus-target wiring through `MasterDetail`'s `detailId`/`tabIndex={-1}` detail region.
|
|
16
|
+
* - **Destructive confirmation built in**: `onDeleteRole`'s presence arms a per-role delete
|
|
17
|
+
* affordance behind the package `AlertDialog` (variant `destructive`); the handler fires only
|
|
18
|
+
* after the user confirms. Roles marked `locked` (system roles) never offer deletion and show a
|
|
19
|
+
* localized lock badge; `readOnly` hides every mutating affordance.
|
|
20
|
+
* - **Lifecycle states** mirror the DataTable #216 vocabulary and precedence:
|
|
21
|
+
* `loading` → `denied` → `error` → `empty` → content.
|
|
22
|
+
* - **Domain-neutral**: the roles and the detail surface (`children`, node or render-function) are
|
|
23
|
+
* consumer-supplied; no platform role or permission is encoded.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ServiceRolePanel: React.ForwardRefExoticComponent<ServiceRolePanelProp & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { AlertCircle, Lock, RefreshCw, ShieldAlert, Trash2 } from "lucide-react";
|
|
5
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
+
import { cn } from "../../lib/utils.js";
|
|
7
|
+
import { Badge } from "../data-display/badge.js";
|
|
8
|
+
import { EmptyState } from "../data-display/empty-state.js";
|
|
9
|
+
import { AlertDialog } from "../feedback/dialog.js";
|
|
10
|
+
import { SkeletonRows } from "../feedback/skeleton.js";
|
|
11
|
+
import { Button } from "../general/button.js";
|
|
12
|
+
import { Text } from "../general/typography.js";
|
|
13
|
+
import { MasterDetail } from "./master-detail.js";
|
|
14
|
+
const ServiceRolePanel = React.forwardRef(
|
|
15
|
+
function ServiceRolePanel2({
|
|
16
|
+
roles,
|
|
17
|
+
value: controlledValue,
|
|
18
|
+
defaultValue,
|
|
19
|
+
onValueChange,
|
|
20
|
+
children,
|
|
21
|
+
onDeleteRole,
|
|
22
|
+
readOnly = false,
|
|
23
|
+
loading = false,
|
|
24
|
+
empty,
|
|
25
|
+
error,
|
|
26
|
+
denied,
|
|
27
|
+
onRetry,
|
|
28
|
+
masterLabel,
|
|
29
|
+
detailLabel,
|
|
30
|
+
railWidth,
|
|
31
|
+
masterViewport,
|
|
32
|
+
collapseBelow,
|
|
33
|
+
id,
|
|
34
|
+
className
|
|
35
|
+
}, ref) {
|
|
36
|
+
const { t } = useTranslation();
|
|
37
|
+
const reactId = React.useId();
|
|
38
|
+
const rootId = id ?? `service-role-panel-${reactId}`;
|
|
39
|
+
const detailId = `${rootId}-detail`;
|
|
40
|
+
const [uncontrolled, setUncontrolled] = React.useState(
|
|
41
|
+
defaultValue ?? roles[0]?.id
|
|
42
|
+
);
|
|
43
|
+
const value = controlledValue ?? uncontrolled;
|
|
44
|
+
const selectedRole = roles.find((role) => role.id === value);
|
|
45
|
+
const select = (roleId) => {
|
|
46
|
+
if (controlledValue === void 0) setUncontrolled(roleId);
|
|
47
|
+
onValueChange?.(roleId);
|
|
48
|
+
};
|
|
49
|
+
const [pendingDelete, setPendingDelete] = React.useState(null);
|
|
50
|
+
const isDenied = denied !== void 0 && denied !== null && denied !== false;
|
|
51
|
+
const isError = error !== void 0 && error !== null && error !== false;
|
|
52
|
+
const deletable = typeof onDeleteRole === "function" && !readOnly;
|
|
53
|
+
const surface = (state) => /* @__PURE__ */ jsx(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
ref,
|
|
57
|
+
id: rootId,
|
|
58
|
+
"data-slot": "service-role-panel",
|
|
59
|
+
className: cn("ui-service-role-panel", className),
|
|
60
|
+
children: state
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
if (loading) {
|
|
64
|
+
return surface(
|
|
65
|
+
/* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("layout.serviceRolePanel.loading"), role: "status", children: /* @__PURE__ */ jsx(SkeletonRows, { rows: 5, columns: 3 }) })
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (isDenied) {
|
|
69
|
+
return surface(
|
|
70
|
+
/* @__PURE__ */ jsx("div", { "aria-live": "polite", children: denied === true ? /* @__PURE__ */ jsx(
|
|
71
|
+
EmptyState,
|
|
72
|
+
{
|
|
73
|
+
icon: ShieldAlert,
|
|
74
|
+
tone: "warning",
|
|
75
|
+
variant: "section",
|
|
76
|
+
title: t("layout.serviceRolePanel.denied"),
|
|
77
|
+
description: t("layout.serviceRolePanel.deniedDescription"),
|
|
78
|
+
titleAs: "p"
|
|
79
|
+
}
|
|
80
|
+
) : denied })
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (isError) {
|
|
84
|
+
return surface(
|
|
85
|
+
/* @__PURE__ */ jsx("div", { role: "alert", children: error === true ? /* @__PURE__ */ jsx(
|
|
86
|
+
EmptyState,
|
|
87
|
+
{
|
|
88
|
+
icon: AlertCircle,
|
|
89
|
+
tone: "destructive",
|
|
90
|
+
variant: "section",
|
|
91
|
+
title: t("layout.serviceRolePanel.error"),
|
|
92
|
+
description: t("layout.serviceRolePanel.errorDescription"),
|
|
93
|
+
titleAs: "p",
|
|
94
|
+
action: onRetry ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
|
|
95
|
+
/* @__PURE__ */ jsx(RefreshCw, { "aria-hidden": "true" }),
|
|
96
|
+
t("common.retry")
|
|
97
|
+
] }) : void 0
|
|
98
|
+
}
|
|
99
|
+
) : error })
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (roles.length === 0) {
|
|
103
|
+
return surface(
|
|
104
|
+
/* @__PURE__ */ jsx("div", { "aria-live": "polite", children: empty ?? /* @__PURE__ */ jsx(
|
|
105
|
+
EmptyState,
|
|
106
|
+
{
|
|
107
|
+
variant: "section",
|
|
108
|
+
title: t("layout.serviceRolePanel.empty"),
|
|
109
|
+
description: t("layout.serviceRolePanel.emptyDescription"),
|
|
110
|
+
titleAs: "p"
|
|
111
|
+
}
|
|
112
|
+
) })
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const detail = typeof children === "function" ? children(selectedRole) : children ?? /* @__PURE__ */ jsx(
|
|
116
|
+
EmptyState,
|
|
117
|
+
{
|
|
118
|
+
variant: "compact",
|
|
119
|
+
title: t("layout.serviceRolePanel.noDetail"),
|
|
120
|
+
titleAs: "p"
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
return surface(
|
|
124
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
125
|
+
/* @__PURE__ */ jsx(
|
|
126
|
+
MasterDetail,
|
|
127
|
+
{
|
|
128
|
+
rail: "master",
|
|
129
|
+
railWidth,
|
|
130
|
+
masterViewport,
|
|
131
|
+
collapseBelow,
|
|
132
|
+
masterLabel: masterLabel ?? t("layout.serviceRolePanel.rolesLabel"),
|
|
133
|
+
detailLabel: detailLabel ?? t("layout.serviceRolePanel.detailLabel"),
|
|
134
|
+
detailId,
|
|
135
|
+
master: /* @__PURE__ */ jsx("ul", { className: "flex list-none flex-col gap-1", "data-slot": "service-role-panel-list", children: roles.map((role) => {
|
|
136
|
+
const selected = role.id === value;
|
|
137
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1", children: [
|
|
138
|
+
/* @__PURE__ */ jsx(
|
|
139
|
+
Button,
|
|
140
|
+
{
|
|
141
|
+
variant: selected ? "secondary" : "ghost",
|
|
142
|
+
className: "h-auto flex-1 justify-start py-2 text-start",
|
|
143
|
+
"aria-current": selected ? "true" : void 0,
|
|
144
|
+
"aria-controls": detailId,
|
|
145
|
+
onClick: () => select(role.id),
|
|
146
|
+
children: /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col items-start leading-tight", children: [
|
|
147
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex max-w-full items-center gap-1.5", children: [
|
|
148
|
+
/* @__PURE__ */ jsx(Text, { weight: "medium", className: "truncate", children: role.name }),
|
|
149
|
+
role.locked && /* @__PURE__ */ jsxs(Badge, { tone: "default", variant: "outline", children: [
|
|
150
|
+
/* @__PURE__ */ jsx(Lock, { "aria-hidden": "true" }),
|
|
151
|
+
t("layout.serviceRolePanel.locked")
|
|
152
|
+
] })
|
|
153
|
+
] }),
|
|
154
|
+
(role.description !== void 0 || role.memberCount !== void 0) && /* @__PURE__ */ jsx(Text, { size: "2xs", tone: "muted", className: "max-w-full truncate", children: [
|
|
155
|
+
role.description,
|
|
156
|
+
role.memberCount !== void 0 ? (
|
|
157
|
+
// `{count}` is Intl.NumberFormat-grouped by `interpolate`,
|
|
158
|
+
// pluralized by CLDR category — no hand-built formatting.
|
|
159
|
+
t("layout.serviceRolePanel.memberCount", {
|
|
160
|
+
count: role.memberCount
|
|
161
|
+
})
|
|
162
|
+
) : void 0
|
|
163
|
+
].filter((part) => part !== void 0).join(" \xB7 ") })
|
|
164
|
+
] })
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
deletable && !role.locked && /* @__PURE__ */ jsx(
|
|
168
|
+
Button,
|
|
169
|
+
{
|
|
170
|
+
variant: "ghost",
|
|
171
|
+
size: "icon-sm",
|
|
172
|
+
"aria-label": t("layout.serviceRolePanel.deleteRole", { role: role.name }),
|
|
173
|
+
onClick: () => setPendingDelete(role),
|
|
174
|
+
children: /* @__PURE__ */ jsx(Trash2, { "aria-hidden": "true" })
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
] }, role.id);
|
|
178
|
+
}) }),
|
|
179
|
+
children: detail
|
|
180
|
+
}
|
|
181
|
+
),
|
|
182
|
+
deletable && /* @__PURE__ */ jsx(
|
|
183
|
+
AlertDialog,
|
|
184
|
+
{
|
|
185
|
+
open: pendingDelete !== null,
|
|
186
|
+
onOpenChange: (open) => {
|
|
187
|
+
if (!open) setPendingDelete(null);
|
|
188
|
+
},
|
|
189
|
+
title: t("layout.serviceRolePanel.deleteTitle"),
|
|
190
|
+
description: pendingDelete ? t("layout.serviceRolePanel.deleteDescription", { role: pendingDelete.name }) : void 0,
|
|
191
|
+
confirmLabel: t("layout.serviceRolePanel.deleteConfirm"),
|
|
192
|
+
variant: "destructive",
|
|
193
|
+
onConfirm: () => {
|
|
194
|
+
if (pendingDelete) onDeleteRole?.(pendingDelete.id);
|
|
195
|
+
setPendingDelete(null);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
] })
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
ServiceRolePanel.displayName = "ServiceRolePanel";
|
|
204
|
+
export {
|
|
205
|
+
ServiceRolePanel
|
|
206
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ToolbarGroupProp, ToolbarProp } from "../../props/components/navigation.prop.js";
|
|
2
|
-
export type { FilterBarChipProp, FilterBarChipProp as FilterBarChipProps, FilterBarOverflowProp, ToolbarGroupProp, ToolbarGroupProp as ToolbarGroupProps, ToolbarGroupProp as FilterBarGroupProp, ToolbarGroupProp as FilterBarGroupProps, ToolbarProp, ToolbarProp as ToolbarProps, ToolbarProp as FilterBarProp, ToolbarProp as FilterBarProps, } from "../../props/components/navigation.prop.js";
|
|
3
|
-
export declare function Toolbar({ onClear, hasActiveFilters, sticky, overflow, search, chips, onChipRemove, resultCount,
|
|
2
|
+
export type { FilterBarChipProp, FilterBarChipProp as FilterBarChipProps, FilterBarFilterProp, FilterBarFilterProp as FilterBarFilterProps, FilterBarOverflowProp, FilterBarSearchProp, FilterBarSearchProp as FilterBarSearchProps, ToolbarGroupProp, ToolbarGroupProp as ToolbarGroupProps, ToolbarGroupProp as FilterBarGroupProp, ToolbarGroupProp as FilterBarGroupProps, ToolbarProp, ToolbarProp as ToolbarProps, ToolbarProp as FilterBarProp, ToolbarProp as FilterBarProps, } from "../../props/components/navigation.prop.js";
|
|
3
|
+
export declare function Toolbar({ onClear, hasActiveFilters, sticky, overflow, search, filters, chips, onChipRemove, actions, resultCount, loading, disabled, error, className, children, }: ToolbarProp): import("react").JSX.Element;
|
|
4
4
|
export declare function ToolbarGroup({ label, controlId, className, children }: ToolbarGroupProp): import("react").JSX.Element;
|
|
5
5
|
/**
|
|
6
6
|
* Canonical list-page filter strip.
|
|
@@ -4,6 +4,9 @@ import { useId } from "react";
|
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { Button } from "../general/button.js";
|
|
7
|
+
import { Badge } from "../data-display/badge.js";
|
|
8
|
+
import { SearchInput } from "../data-entry/search-input.js";
|
|
9
|
+
import { Select } from "../data-entry/select.js";
|
|
7
10
|
import { cn } from "../../lib/utils.js";
|
|
8
11
|
function Toolbar({
|
|
9
12
|
onClear,
|
|
@@ -11,65 +14,141 @@ function Toolbar({
|
|
|
11
14
|
sticky = false,
|
|
12
15
|
overflow = "wrap",
|
|
13
16
|
search,
|
|
17
|
+
filters,
|
|
14
18
|
chips,
|
|
15
19
|
onChipRemove,
|
|
16
|
-
resultCount,
|
|
17
20
|
actions,
|
|
21
|
+
resultCount,
|
|
22
|
+
loading,
|
|
23
|
+
disabled,
|
|
24
|
+
error,
|
|
18
25
|
className,
|
|
19
26
|
children
|
|
20
27
|
}) {
|
|
21
28
|
const { t } = useTranslation();
|
|
22
|
-
const
|
|
29
|
+
const reactId = useId();
|
|
30
|
+
const hasModel = search !== void 0 || filters !== void 0 || chips !== void 0 || onChipRemove !== void 0 || actions !== void 0 || resultCount !== void 0 || loading !== void 0 || disabled !== void 0 || error !== void 0;
|
|
31
|
+
if (!hasModel) {
|
|
32
|
+
return /* @__PURE__ */ jsxs(
|
|
33
|
+
"div",
|
|
34
|
+
{
|
|
35
|
+
role: "toolbar",
|
|
36
|
+
"aria-label": t("navigation.toolbar.ariaLabel"),
|
|
37
|
+
"data-overflow": overflow,
|
|
38
|
+
className: cn("ui-toolbar", sticky && "ui-toolbar-sticky", className),
|
|
39
|
+
children: [
|
|
40
|
+
children,
|
|
41
|
+
onClear && hasActiveFilters && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: onClear, className: "ui-toolbar-clear", children: [
|
|
42
|
+
/* @__PURE__ */ jsx(X, { "aria-hidden": "true" }),
|
|
43
|
+
t("common.clearFilters")
|
|
44
|
+
] })
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const showClear = Boolean(onClear && hasActiveFilters);
|
|
50
|
+
const showChips = Boolean(chips && chips.length > 0);
|
|
51
|
+
const showMeta = error != null || resultCount !== void 0;
|
|
23
52
|
return /* @__PURE__ */ jsxs(
|
|
24
53
|
"div",
|
|
25
54
|
{
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"data-overflow": overflow,
|
|
29
|
-
className: cn("ui-toolbar", sticky && "ui-toolbar-sticky", className),
|
|
55
|
+
className: cn("ui-filter-bar", sticky && "ui-toolbar-sticky", className),
|
|
56
|
+
"data-loading": loading || void 0,
|
|
30
57
|
children: [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
58
|
+
/* @__PURE__ */ jsxs(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
role: "toolbar",
|
|
62
|
+
"aria-label": t("navigation.toolbar.ariaLabel"),
|
|
63
|
+
"aria-busy": loading || void 0,
|
|
64
|
+
"data-overflow": overflow,
|
|
65
|
+
className: "ui-toolbar",
|
|
66
|
+
children: [
|
|
67
|
+
search && /* @__PURE__ */ jsx(
|
|
68
|
+
SearchInput,
|
|
69
|
+
{
|
|
70
|
+
id: search.id,
|
|
71
|
+
className: "ui-filter-bar-search",
|
|
72
|
+
label: search.label,
|
|
73
|
+
ariaLabel: search.ariaLabel,
|
|
74
|
+
placeholder: search.placeholder,
|
|
75
|
+
value: search.value,
|
|
76
|
+
defaultValue: search.defaultValue,
|
|
77
|
+
onValueChange: search.onValueChange,
|
|
78
|
+
onSearch: search.onSearch,
|
|
79
|
+
disabled: disabled || search.disabled
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
filters?.map((filter) => {
|
|
83
|
+
const controlId = `${reactId}-filter-${filter.value}`;
|
|
84
|
+
return /* @__PURE__ */ jsx(
|
|
85
|
+
ToolbarGroup,
|
|
86
|
+
{
|
|
87
|
+
label: filter.label,
|
|
88
|
+
controlId,
|
|
89
|
+
className: "ui-filter-bar-filter",
|
|
90
|
+
children: /* @__PURE__ */ jsx(
|
|
91
|
+
Select,
|
|
92
|
+
{
|
|
93
|
+
id: controlId,
|
|
94
|
+
options: filter.options,
|
|
95
|
+
value: filter.selected,
|
|
96
|
+
defaultValue: filter.defaultSelected,
|
|
97
|
+
onValueChange: (selected) => filter.onSelectedChange?.(selected),
|
|
98
|
+
placeholder: filter.placeholder,
|
|
99
|
+
disabled: disabled || filter.disabled
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
},
|
|
103
|
+
filter.value
|
|
104
|
+
);
|
|
105
|
+
}),
|
|
106
|
+
children,
|
|
107
|
+
(showClear || actions != null) && /* @__PURE__ */ jsxs("div", { className: "ui-filter-bar-end", children: [
|
|
108
|
+
showClear && /* @__PURE__ */ jsxs(
|
|
109
|
+
Button,
|
|
110
|
+
{
|
|
111
|
+
variant: "ghost",
|
|
112
|
+
size: "sm",
|
|
113
|
+
onClick: onClear,
|
|
114
|
+
disabled,
|
|
115
|
+
className: "ui-toolbar-clear",
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ jsx(X, { "aria-hidden": "true" }),
|
|
118
|
+
t("common.clearFilters")
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
actions != null && /* @__PURE__ */ jsx("div", { className: "ui-filter-bar-actions", children: actions })
|
|
123
|
+
] })
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
),
|
|
127
|
+
showChips && /* @__PURE__ */ jsx(
|
|
39
128
|
"div",
|
|
40
129
|
{
|
|
41
130
|
role: "group",
|
|
42
131
|
"aria-label": t("navigation.filterBar.appliedFilters"),
|
|
43
132
|
className: "ui-filter-bar-chips",
|
|
44
133
|
children: chips.map((chip) => /* @__PURE__ */ jsxs("span", { className: "ui-filter-bar-chip", children: [
|
|
45
|
-
/* @__PURE__ */ jsx(
|
|
46
|
-
|
|
134
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", children: chip.label }),
|
|
135
|
+
onChipRemove && /* @__PURE__ */ jsx(
|
|
47
136
|
Button,
|
|
48
137
|
{
|
|
49
138
|
variant: "ghost",
|
|
50
|
-
size: "
|
|
51
|
-
|
|
52
|
-
"aria-label": t("navigation.filterBar.removeFilter", {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
139
|
+
size: "icon-sm",
|
|
140
|
+
disabled: disabled || chip.disabled,
|
|
141
|
+
"aria-label": t("navigation.filterBar.removeFilter", {
|
|
142
|
+
label: typeof chip.label === "string" ? chip.label : chip.value
|
|
143
|
+
}),
|
|
144
|
+
onClick: () => onChipRemove(chip.value),
|
|
57
145
|
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
58
146
|
}
|
|
59
147
|
)
|
|
60
|
-
] }, chip.
|
|
148
|
+
] }, chip.value))
|
|
61
149
|
}
|
|
62
150
|
),
|
|
63
|
-
|
|
64
|
-
// change, and a polite live region is what tells everyone else that filtering happened.
|
|
65
|
-
// The number is grouped by `Intl.NumberFormat` and the noun selected by CLDR plural rules
|
|
66
|
-
// for the active locale — never a hand-built "N items".
|
|
67
|
-
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "ui-filter-bar-count", children: t("navigation.filterBar.resultCount", { count: resultCount }) }),
|
|
68
|
-
onClear && hasActiveFilters && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: onClear, className: "ui-toolbar-clear", children: [
|
|
69
|
-
/* @__PURE__ */ jsx(X, { "aria-hidden": "true" }),
|
|
70
|
-
t("common.clearFilters")
|
|
71
|
-
] }),
|
|
72
|
-
actions != null && /* @__PURE__ */ jsx("div", { className: "ui-filter-bar-actions", children: actions })
|
|
151
|
+
showMeta && /* @__PURE__ */ jsx("div", { className: "ui-filter-bar-meta", children: error != null ? /* @__PURE__ */ jsx("p", { role: "alert", className: "ui-filter-bar-error", children: error }) : /* @__PURE__ */ jsx("p", { role: "status", className: "ui-filter-bar-count", children: t("navigation.filterBar.resultCount", { count: resultCount }) }) })
|
|
73
152
|
]
|
|
74
153
|
}
|
|
75
154
|
);
|
|
@@ -5,7 +5,7 @@ export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarItem, MenubarLabel
|
|
|
5
5
|
export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, } from "./navigation-menu.js";
|
|
6
6
|
export { Steps } from "./steps.js";
|
|
7
7
|
export { FilterBar, FilterBarGroup, Toolbar, ToolbarGroup } from "./filter-bar.js";
|
|
8
|
-
export type { FilterBarChipProp, FilterBarChipProps, FilterBarGroupProp, FilterBarGroupProps, FilterBarProp, FilterBarProps, ToolbarGroupProps, ToolbarProps, } from "./filter-bar.js";
|
|
8
|
+
export type { FilterBarChipProp, FilterBarChipProps, FilterBarFilterProp, FilterBarFilterProps, FilterBarGroupProp, FilterBarGroupProps, FilterBarOverflowProp, FilterBarProp, FilterBarProps, FilterBarSearchProp, FilterBarSearchProps, ToolbarGroupProps, ToolbarProps, } from "./filter-bar.js";
|
|
9
9
|
export { AppSettingPicker } from "./app-setting-picker.js";
|
|
10
10
|
export type { AppSettingPickerProp, AppSettingPickerProps, AppSettingKind, } from "./app-setting-picker.js";
|
|
11
11
|
export type { BreadcrumbItemProp as BreadcrumbItem } from "../../props/vocabulary/navigation.prop.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { StepsProp } from "../../props/components/navigation.prop.js";
|
|
3
|
-
export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp,
|
|
4
|
-
export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement,
|
|
3
|
+
export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp, } from "../../props/components/navigation.prop.js";
|
|
4
|
+
export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, onValueChange, className, }: StepsProp): React.JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { Check, ChevronRight, Circle, Loader2, X } from "lucide-react";
|
|
4
4
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
6
|
import { controlIconClass } from "../../lib/control-styles.js";
|
|
@@ -61,7 +61,6 @@ function Steps({
|
|
|
61
61
|
type = "default",
|
|
62
62
|
size = "md",
|
|
63
63
|
titlePlacement = "horizontal",
|
|
64
|
-
separator = "chevron",
|
|
65
64
|
onValueChange,
|
|
66
65
|
className
|
|
67
66
|
}) {
|
|
@@ -70,7 +69,6 @@ function Steps({
|
|
|
70
69
|
const isVertical = orientation === "vertical";
|
|
71
70
|
const compact = size === "sm";
|
|
72
71
|
const inline = type === "inline";
|
|
73
|
-
const SeparatorIcon = separator === "arrow" ? ArrowRight : ChevronRight;
|
|
74
72
|
return /* @__PURE__ */ jsx(
|
|
75
73
|
"ol",
|
|
76
74
|
{
|
|
@@ -153,7 +151,7 @@ function Steps({
|
|
|
153
151
|
}
|
|
154
152
|
) : /* @__PURE__ */ jsx("span", { className: stepClassName, children: stepInner }),
|
|
155
153
|
inline && index < items.length - 1 ? /* @__PURE__ */ jsx(
|
|
156
|
-
|
|
154
|
+
ChevronRight,
|
|
157
155
|
{
|
|
158
156
|
className: "ui-steps-inline-separator rtl:rotate-180",
|
|
159
157
|
"aria-hidden": "true"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
3
3
|
import type { AvatarProp } from "../../props/components/data-display.prop.js";
|
|
4
|
-
export declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & Pick<AvatarProp, "shape"
|
|
4
|
+
export declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & Pick<AvatarProp, "shape"> & React.RefAttributes<HTMLSpanElement>>;
|
|
5
5
|
export declare const AvatarImage: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarImageProps & React.RefAttributes<HTMLImageElement>, "ref"> & React.RefAttributes<HTMLImageElement>>;
|
|
6
6
|
export declare const AvatarFallback: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarFallbackProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -3,13 +3,12 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
-
const Avatar = React.forwardRef(({ className, shape = "circle",
|
|
6
|
+
const Avatar = React.forwardRef(({ className, shape = "circle", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
7
7
|
AvatarPrimitive.Root,
|
|
8
8
|
{
|
|
9
9
|
ref,
|
|
10
10
|
"data-slot": "avatar",
|
|
11
11
|
"data-shape": shape === "square" ? "square" : void 0,
|
|
12
|
-
"data-appearance": appearance === "tinted" ? "tinted" : void 0,
|
|
13
12
|
className: cn("ui-avatar", className),
|
|
14
13
|
...props
|
|
15
14
|
}
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* InputOTP — the code-challenge field.
|
|
5
|
-
*
|
|
6
|
-
* `align` positions the whole row (groups + separators) on the main axis: `start` (default,
|
|
7
|
-
* unchanged), `center` — the canonical auth challenge — or `end`. The attribute lands on the
|
|
8
|
-
* hidden input because `input-otp` owns the container element; `.ui-otp-container` reads it back
|
|
9
|
-
* through `:has()`, exactly as it already does for the invalid and disabled states. A service that
|
|
10
|
-
* wants every code field centred sets `--otp-container-align` once instead.
|
|
11
|
-
*/
|
|
12
|
-
export declare const InputOTP: React.ForwardRefExoticComponent<((Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
|
|
2
|
+
export declare const InputOTP: React.ForwardRefExoticComponent<(Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
|
|
13
3
|
value?: string;
|
|
14
4
|
onChange?: (newValue: string) => unknown;
|
|
15
5
|
maxLength: number;
|
|
@@ -35,10 +25,8 @@ export declare const InputOTP: React.ForwardRefExoticComponent<((Omit<Omit<React
|
|
|
35
25
|
} & {
|
|
36
26
|
render?: never;
|
|
37
27
|
children: React.ReactNode;
|
|
38
|
-
} & React.RefAttributes<HTMLInputElement>, "ref">) &
|
|
39
|
-
|
|
40
|
-
}) & React.RefAttributes<HTMLInputElement>>;
|
|
41
|
-
export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, } from "../../props/components/data-entry.prop.js";
|
|
28
|
+
} & React.RefAttributes<HTMLInputElement>, "ref">) & React.RefAttributes<HTMLInputElement>>;
|
|
29
|
+
export type { InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, } from "../../props/components/data-entry.prop.js";
|
|
42
30
|
export declare const InputOTPGroup: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
43
31
|
appearance?: import("./input-otp.js").InputOTPGroupAppearanceProp;
|
|
44
32
|
} & React.RefAttributes<HTMLDivElement>>;
|