@godxjp/ui 18.8.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.
Files changed (83) hide show
  1. package/LICENSE +202 -0
  2. package/dist/components/data-display/badge.d.ts +7 -0
  3. package/dist/components/data-display/badge.js +1 -1
  4. package/dist/components/data-display/card.d.ts +3 -27
  5. package/dist/components/data-display/card.js +1 -4
  6. package/dist/components/data-display/data-table.js +2 -2
  7. package/dist/components/data-display/index.d.ts +2 -0
  8. package/dist/components/data-display/index.js +2 -0
  9. package/dist/components/data-display/permission-matrix.d.ts +26 -0
  10. package/dist/components/data-display/permission-matrix.js +219 -0
  11. package/dist/components/data-display/table.d.ts +12 -0
  12. package/dist/components/data-entry/branch-scope-picker.d.ts +43 -0
  13. package/dist/components/data-entry/branch-scope-picker.js +200 -0
  14. package/dist/components/data-entry/command-palette.js +8 -1
  15. package/dist/components/data-entry/index.d.ts +2 -0
  16. package/dist/components/data-entry/index.js +2 -0
  17. package/dist/components/data-entry/input-otp.d.ts +1 -1
  18. package/dist/components/feedback/alert.d.ts +2 -20
  19. package/dist/components/feedback/alert.js +1 -11
  20. package/dist/components/feedback/banner.d.ts +21 -0
  21. package/dist/components/feedback/banner.js +15 -0
  22. package/dist/components/feedback/index.d.ts +4 -2
  23. package/dist/components/feedback/index.js +2 -2
  24. package/dist/components/general/typography.d.ts +1 -0
  25. package/dist/components/general/typography.js +32 -13
  26. package/dist/components/layout/auth-shell.d.ts +2 -2
  27. package/dist/components/layout/index.d.ts +2 -2
  28. package/dist/components/layout/index.js +2 -2
  29. package/dist/components/layout/page-container.d.ts +1 -1
  30. package/dist/components/layout/page-container.js +45 -19
  31. package/dist/components/layout/service-role-panel.d.ts +25 -0
  32. package/dist/components/layout/service-role-panel.js +206 -0
  33. package/dist/components/navigation/filter-bar.d.ts +2 -2
  34. package/dist/components/navigation/filter-bar.js +113 -34
  35. package/dist/components/navigation/index.d.ts +1 -1
  36. package/dist/components/navigation/steps.d.ts +2 -2
  37. package/dist/components/navigation/steps.js +2 -4
  38. package/dist/components/ui/avatar.d.ts +1 -1
  39. package/dist/components/ui/avatar.js +1 -2
  40. package/dist/components/ui/input-otp.d.ts +3 -15
  41. package/dist/components/ui/input-otp.js +1 -2
  42. package/dist/email/tokens.generated.d.ts +2 -2
  43. package/dist/email/tokens.generated.js +2 -2
  44. package/dist/i18n/messages/en.json +56 -4
  45. package/dist/i18n/messages/ja.json +51 -5
  46. package/dist/i18n/messages/vi.json +50 -4
  47. package/dist/props/components/data-display.prop.d.ts +66 -16
  48. package/dist/props/components/data-entry.prop.d.ts +54 -7
  49. package/dist/props/components/feedback.prop.d.ts +3 -3
  50. package/dist/props/components/general.prop.d.ts +8 -1
  51. package/dist/props/components/layout.prop.d.ts +75 -54
  52. package/dist/props/components/navigation.prop.d.ts +77 -59
  53. package/dist/props/registry.d.ts +273 -72
  54. package/dist/props/registry.js +361 -92
  55. package/dist/props/vocabulary/content.prop.d.ts +5 -0
  56. package/dist/props/vocabulary/index.d.ts +1 -1
  57. package/dist/props/vocabulary/interaction.prop.d.ts +4 -7
  58. package/dist/props/vocabulary/layout.prop.d.ts +2 -2
  59. package/dist/styles/alert-layout.css +15 -32
  60. package/dist/styles/badge-layout.css +5 -0
  61. package/dist/styles/card-layout.css +13 -49
  62. package/dist/styles/control.css +2 -12
  63. package/dist/styles/data-display-layout.css +0 -16
  64. package/dist/styles/layout.css +102 -108
  65. package/dist/styles/navigation-layout.css +3 -8
  66. package/dist/styles/shell-layout.css +17 -17
  67. package/dist/styles/table-layout.css +77 -2
  68. package/dist/styles/text-layout.css +13 -0
  69. package/dist/tokens/base.css +1 -0
  70. package/dist/tokens/components/badge.css +5 -0
  71. package/dist/tokens/components/banner.css +16 -0
  72. package/dist/tokens/components/card.css +0 -12
  73. package/dist/tokens/components/control.css +0 -16
  74. package/dist/tokens/components/data-display.css +0 -12
  75. package/dist/tokens/components/email.css +2 -8
  76. package/dist/tokens/components/feedback.css +0 -20
  77. package/dist/tokens/components/navigation.css +10 -34
  78. package/dist/tokens/components/shell.css +24 -36
  79. package/dist/tokens/components/table.css +41 -1
  80. package/dist/tokens/semantic/layout.css +5 -12
  81. package/package.json +15 -2
  82. package/dist/components/layout/page-header.d.ts +0 -22
  83. package/dist/components/layout/page-header.js +0 -89
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+ import type { BranchScopeValueProp } from "../../props/components/data-entry.prop.js";
3
+ export type { BranchScopeModeProp, BranchScopeOptionProp, BranchScopePickerProp, BranchScopePickerProp as BranchScopePickerProps, BranchScopeValueProp, } from "../../props/components/data-entry.prop.js";
4
+ /**
5
+ * BranchScopePicker — the canonical "all branches vs an explicit subset" scope control
6
+ * (gh#257 / DXS platform#311).
7
+ *
8
+ * A THIN formalized composition (the gh#251 lesson: a consumer cannot import a docs page): the
9
+ * mode switch is the real `RadioGroup`, the subset is the real `CheckboxGroup`, search is the real
10
+ * `SearchInput` — so keyboard behaviour (roving radios, Space on checkboxes) and the field-a11y
11
+ * contract (`aria-labelledby`/`aria-errormessage` via `FormField`) come from the primitives, not
12
+ * from new machinery. Domain data (the branches) is 100% consumer-supplied.
13
+ *
14
+ * - **Value** is a single controlled/uncontrolled object `{ mode, branchIds }`, so a form treats
15
+ * scope as ONE field. Switching back to `all` PRESERVES `branchIds` — an accidental mode flip
16
+ * must not destroy a curated subset.
17
+ * - **Validation** (`error`) renders the message and wires `aria-invalid`/`aria-errormessage` onto
18
+ * the radiogroup widget; collection READ failures are `listError`/`denied`, mirroring the
19
+ * DataTable #216 vocabulary (precedence `loading` → `denied` → `listError` → `empty`).
20
+ * - **`readOnly`** renders the current scope as a static summary (mode + branch badges) with no
21
+ * editable affordance; `disabled` keeps the controls visible but inert.
22
+ * - **Responsive**: a single-column stack; the branch list scrolls inside its own bounded box, so
23
+ * the control keeps one measure at 1440/1024/390.
24
+ */
25
+ export declare const BranchScopePicker: React.ForwardRefExoticComponent<import("../../lib/field-a11y.js").FieldA11yProps & {
26
+ branches: readonly import("./branch-scope-picker.js").BranchScopeOptionProp[];
27
+ value?: BranchScopeValueProp;
28
+ defaultValue?: BranchScopeValueProp;
29
+ onValueChange?: (value: BranchScopeValueProp) => void;
30
+ disabled?: import("../../props/index.js").DisabledProp;
31
+ readOnly?: boolean;
32
+ searchable?: boolean;
33
+ error?: import("../../props/index.js").ErrorProp;
34
+ loading?: boolean;
35
+ empty?: React.ReactNode;
36
+ listError?: React.ReactNode;
37
+ denied?: React.ReactNode;
38
+ allLabel?: React.ReactNode;
39
+ selectedLabel?: React.ReactNode;
40
+ name?: import("../../props/index.js").NameProp;
41
+ id?: import("../../props/index.js").IdProp;
42
+ className?: import("../../props/index.js").ClassNameProp;
43
+ } & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,200 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { AlertCircle, ShieldAlert } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { mergeAriaIds, pickFieldA11y } from "../../lib/field-a11y.js";
7
+ import { cn } from "../../lib/utils.js";
8
+ import { Badge } from "../data-display/badge.js";
9
+ import { EmptyState } from "../data-display/empty-state.js";
10
+ import { SkeletonRows } from "../feedback/skeleton.js";
11
+ import { Text } from "../general/typography.js";
12
+ import { CheckboxGroup } from "./checkbox-group.js";
13
+ import { RadioGroup } from "./radio.js";
14
+ import { SearchInput } from "./search-input.js";
15
+ const DEFAULT_VALUE = { mode: "all" };
16
+ const BranchScopePicker = React.forwardRef(
17
+ function BranchScopePicker2({
18
+ branches,
19
+ value: controlledValue,
20
+ defaultValue = DEFAULT_VALUE,
21
+ onValueChange,
22
+ disabled = false,
23
+ readOnly = false,
24
+ searchable = true,
25
+ error,
26
+ loading = false,
27
+ empty,
28
+ listError,
29
+ denied,
30
+ allLabel,
31
+ selectedLabel,
32
+ name,
33
+ id,
34
+ className,
35
+ ...ariaProps
36
+ }, ref) {
37
+ const { t } = useTranslation();
38
+ const reactId = React.useId();
39
+ const rootId = id ?? `branch-scope-${reactId}`;
40
+ const errorId = `${rootId}-error`;
41
+ const [uncontrolled, setUncontrolled] = React.useState(defaultValue);
42
+ const value = controlledValue ?? uncontrolled;
43
+ const branchIds = React.useMemo(() => value.branchIds ?? [], [value.branchIds]);
44
+ const commit = (next) => {
45
+ if (controlledValue === void 0) setUncontrolled(next);
46
+ onValueChange?.(next);
47
+ };
48
+ const [search, setSearch] = React.useState("");
49
+ const filtered = React.useMemo(() => {
50
+ const term = search.trim().toLocaleLowerCase();
51
+ if (term === "") return branches;
52
+ return branches.filter(
53
+ (branch) => `${branch.name} ${branch.description ?? ""}`.toLocaleLowerCase().includes(term)
54
+ );
55
+ }, [branches, search]);
56
+ const fieldA11y = pickFieldA11y(ariaProps);
57
+ const invalid = error !== void 0 && error !== null && error !== false;
58
+ const isDenied = denied !== void 0 && denied !== null && denied !== false;
59
+ const isListError = listError !== void 0 && listError !== null && listError !== false;
60
+ const resolvedAllLabel = allLabel ?? t("dataEntry.branchScope.all");
61
+ const resolvedSelectedLabel = selectedLabel ?? t("dataEntry.branchScope.selected");
62
+ const surface = (children) => /* @__PURE__ */ jsx(
63
+ "div",
64
+ {
65
+ ref,
66
+ id: rootId,
67
+ "data-slot": "branch-scope-picker",
68
+ "data-readonly": readOnly || void 0,
69
+ className: cn("ui-branch-scope-picker flex flex-col gap-3", className),
70
+ children
71
+ }
72
+ );
73
+ if (loading) {
74
+ return surface(
75
+ /* @__PURE__ */ jsx("div", { "aria-busy": "true", "aria-label": t("dataEntry.branchScope.loading"), role: "status", children: /* @__PURE__ */ jsx(SkeletonRows, { rows: 4, columns: 2 }) })
76
+ );
77
+ }
78
+ if (isDenied) {
79
+ return surface(
80
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", children: denied === true ? /* @__PURE__ */ jsx(
81
+ EmptyState,
82
+ {
83
+ icon: ShieldAlert,
84
+ tone: "warning",
85
+ variant: "compact",
86
+ title: t("dataEntry.branchScope.denied"),
87
+ titleAs: "p"
88
+ }
89
+ ) : denied })
90
+ );
91
+ }
92
+ if (isListError) {
93
+ return surface(
94
+ /* @__PURE__ */ jsx("div", { role: "alert", children: listError === true ? /* @__PURE__ */ jsx(
95
+ EmptyState,
96
+ {
97
+ icon: AlertCircle,
98
+ tone: "destructive",
99
+ variant: "compact",
100
+ title: t("dataEntry.branchScope.error"),
101
+ titleAs: "p"
102
+ }
103
+ ) : listError })
104
+ );
105
+ }
106
+ if (branches.length === 0) {
107
+ return surface(
108
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", children: empty ?? /* @__PURE__ */ jsx(EmptyState, { variant: "compact", title: t("dataEntry.branchScope.empty"), titleAs: "p" }) })
109
+ );
110
+ }
111
+ if (readOnly) {
112
+ const selectedBranches = branches.filter((branch) => branchIds.includes(branch.id));
113
+ return surface(
114
+ /* @__PURE__ */ jsxs(Fragment, { children: [
115
+ /* @__PURE__ */ jsx(Text, { size: "sm", weight: "medium", children: value.mode === "all" ? resolvedAllLabel : resolvedSelectedLabel }),
116
+ value.mode === "selected" && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedBranches.map((branch) => /* @__PURE__ */ jsx(Badge, { variant: "outline", children: branch.name }, branch.id)) })
117
+ ] })
118
+ );
119
+ }
120
+ return surface(
121
+ /* @__PURE__ */ jsxs(Fragment, { children: [
122
+ /* @__PURE__ */ jsx(
123
+ RadioGroup,
124
+ {
125
+ value: value.mode,
126
+ onValueChange: (mode) => {
127
+ commit({ ...value, mode });
128
+ },
129
+ options: [
130
+ {
131
+ value: "all",
132
+ label: resolvedAllLabel,
133
+ description: allLabel === void 0 ? t("dataEntry.branchScope.allDescription") : void 0
134
+ },
135
+ {
136
+ value: "selected",
137
+ label: resolvedSelectedLabel,
138
+ description: selectedLabel === void 0 ? t("dataEntry.branchScope.selectedDescription") : void 0
139
+ }
140
+ ],
141
+ disabled,
142
+ name,
143
+ ...fieldA11y,
144
+ "aria-invalid": invalid ? true : fieldA11y["aria-invalid"],
145
+ "aria-errormessage": invalid ? mergeAriaIds(errorId, fieldA11y["aria-errormessage"]) : void 0
146
+ }
147
+ ),
148
+ value.mode === "selected" && /* @__PURE__ */ jsxs("div", { className: "border-border flex flex-col gap-2 border-s-2 ps-4", children: [
149
+ searchable && branches.length > 0 && /* @__PURE__ */ jsx(
150
+ SearchInput,
151
+ {
152
+ value: search,
153
+ onValueChange: setSearch,
154
+ debounce: 0,
155
+ placeholder: t("dataEntry.branchScope.searchPlaceholder"),
156
+ ariaLabel: t("dataEntry.branchScope.searchPlaceholder")
157
+ }
158
+ ),
159
+ filtered.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", tone: "muted", "aria-live": "polite", children: t("dataEntry.branchScope.noMatches") }) : (
160
+ // A bounded list is a scroll container, so it MUST be reachable and scrollable
161
+ // with the keyboard alone even when every row is unfocusable (all checkboxes
162
+ // `disabled`) — WCAG 2.1.1 / axe `scrollable-region-focusable`; same contract as
163
+ // MasterDetail's bounded master region. It is an ordinary tab stop (no trap), and
164
+ // it carries the group's accessible name so the stop is announced meaningfully —
165
+ // the inner CheckboxGroup deliberately no longer duplicates that label.
166
+ /* @__PURE__ */ jsx(
167
+ "div",
168
+ {
169
+ role: "group",
170
+ "aria-label": t("dataEntry.branchScope.listLabel"),
171
+ tabIndex: 0,
172
+ className: "max-h-64 overflow-y-auto",
173
+ children: /* @__PURE__ */ jsx(
174
+ CheckboxGroup,
175
+ {
176
+ value: [...branchIds],
177
+ onValueChange: (next) => commit({ mode: "selected", branchIds: next }),
178
+ options: filtered.map((branch) => ({
179
+ value: branch.id,
180
+ label: branch.name,
181
+ description: branch.description,
182
+ disabled: branch.disabled
183
+ })),
184
+ disabled
185
+ }
186
+ )
187
+ }
188
+ )
189
+ ),
190
+ /* @__PURE__ */ jsx(Text, { size: "xs", tone: "muted", "aria-live": "polite", children: t("dataEntry.branchScope.selectedCount", { count: branchIds.length }) })
191
+ ] }),
192
+ invalid && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-destructive text-xs", children: error })
193
+ ] })
194
+ );
195
+ }
196
+ );
197
+ BranchScopePicker.displayName = "BranchScopePicker";
198
+ export {
199
+ BranchScopePicker
200
+ };
@@ -80,7 +80,14 @@ function CommandPalette({
80
80
  /* @__PURE__ */ jsx(Dialog.Title, { className: "sr-only", children: labels.title }),
81
81
  /* @__PURE__ */ jsx(Dialog.Description, { id: "ui-command-palette-description", className: "sr-only", children: labels.description }),
82
82
  /* @__PURE__ */ jsxs(Command, { label: labels.title, children: [
83
- /* @__PURE__ */ jsx(CommandInput, { autoFocus: true, placeholder: labels.placeholder, "aria-label": labels.placeholder }),
83
+ /* @__PURE__ */ jsx(
84
+ CommandInput,
85
+ {
86
+ autoFocus: true,
87
+ placeholder: labels.placeholder,
88
+ "aria-label": labels.placeholder
89
+ }
90
+ ),
84
91
  /* @__PURE__ */ jsx(CommandList, { "aria-busy": loading, children: loading ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "alert", children: error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
85
92
  /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }),
86
93
  groups.map((group) => /* @__PURE__ */ jsx(CommandGroup, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
@@ -60,3 +60,5 @@ export { Rating } from "./rating.js";
60
60
  export type { RatingProps } from "./rating.js";
61
61
  export { TagInput } from "./tag-input.js";
62
62
  export type { TagInputProps } from "./tag-input.js";
63
+ export { BranchScopePicker } from "./branch-scope-picker.js";
64
+ export type { BranchScopeModeProp, BranchScopeOptionProp, BranchScopePickerProp, BranchScopePickerProps, BranchScopeValueProp, } from "./branch-scope-picker.js";
@@ -50,7 +50,9 @@ import { PasswordStrength, usePasswordStrength } from "./password-strength.js";
50
50
  import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "./input-otp.js";
51
51
  import { Rating } from "./rating.js";
52
52
  import { TagInput } from "./tag-input.js";
53
+ import { BranchScopePicker } from "./branch-scope-picker.js";
53
54
  export {
55
+ BranchScopePicker,
54
56
  Calendar,
55
57
  Cascader,
56
58
  Checkbox,
@@ -1,2 +1,2 @@
1
1
  export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "../ui/input-otp.js";
2
- export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
2
+ export type { InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import type { ToneProp } from "../../props/vocabulary/index.js";
3
- import type { AlertQueryErrorProp, BannerProp } from "../../props/components/feedback.prop.js";
4
- export type { BannerProp, BannerProp as BannerProps, AlertProp, AlertProp as AlertProps, AlertTitleProp, AlertTitleProp as AlertTitleProps, AlertContentProp, AlertContentProp as AlertContentProps, AlertDescriptionProp, AlertDescriptionProp as AlertDescriptionProps, AlertActionsProp, AlertActionsProp as AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProp as AlertQueryErrorProps, } from "../../props/components/feedback.prop.js";
3
+ import type { AlertQueryErrorProp } from "../../props/components/feedback.prop.js";
4
+ export type { AlertProp, AlertProp as AlertProps, AlertTitleProp, AlertTitleProp as AlertTitleProps, AlertContentProp, AlertContentProp as AlertContentProps, AlertDescriptionProp, AlertDescriptionProp as AlertDescriptionProps, AlertActionsProp, AlertActionsProp as AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProp as AlertQueryErrorProps, } from "../../props/components/feedback.prop.js";
5
5
  declare const AlertBase: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
6
6
  variant?: import("../../props/index.js").AlertVariantProp;
7
7
  tone?: ToneProp;
@@ -63,22 +63,4 @@ export declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes
63
63
  } & React.RefAttributes<HTMLDivElement>>;
64
64
  QueryError: typeof AlertQueryError;
65
65
  };
66
- export declare const Banner: React.ForwardRefExoticComponent<BannerProp & React.RefAttributes<HTMLDivElement>> & {
67
- Title: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & {
68
- className?: import("../../props/index.js").ClassNameProp;
69
- children?: import("../../props/index.js").ChildrenProp;
70
- } & React.RefAttributes<HTMLParagraphElement>>;
71
- Content: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
72
- className?: import("../../props/index.js").ClassNameProp;
73
- children?: import("../../props/index.js").ChildrenProp;
74
- } & React.RefAttributes<HTMLDivElement>>;
75
- Description: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & {
76
- className?: import("../../props/index.js").ClassNameProp;
77
- children?: import("../../props/index.js").ChildrenProp;
78
- } & React.RefAttributes<HTMLParagraphElement>>;
79
- Actions: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
80
- className?: import("../../props/index.js").ClassNameProp;
81
- children?: import("../../props/index.js").ChildrenProp;
82
- } & React.RefAttributes<HTMLDivElement>>;
83
- };
84
66
  export { AlertBase };
@@ -159,15 +159,6 @@ const Alert = Object.assign(AlertBase, {
159
159
  Actions: AlertActions,
160
160
  QueryError: AlertQueryError
161
161
  });
162
- const BannerBase = React.forwardRef(function Banner(props, ref) {
163
- return /* @__PURE__ */ jsx(AlertBase, { ...props, ref, variant: "banner" });
164
- });
165
- const Banner2 = Object.assign(BannerBase, {
166
- Title: AlertTitle,
167
- Content: AlertContent,
168
- Description: AlertDescription,
169
- Actions: AlertActions
170
- });
171
162
  export {
172
163
  Alert,
173
164
  AlertActions,
@@ -175,6 +166,5 @@ export {
175
166
  AlertContent,
176
167
  AlertDescription,
177
168
  AlertQueryError,
178
- AlertTitle,
179
- Banner2 as Banner
169
+ AlertTitle
180
170
  };
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ import type { BannerProp } from "../../props/components/feedback.prop.js";
3
+ export type { BannerProp, BannerProp as BannerProps } from "../../props/components/feedback.prop.js";
4
+ export declare const Banner: React.ForwardRefExoticComponent<BannerProp & React.RefAttributes<HTMLDivElement>> & {
5
+ Title: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & {
6
+ className?: import("../../props/index.js").ClassNameProp;
7
+ children?: import("../../props/index.js").ChildrenProp;
8
+ } & React.RefAttributes<HTMLParagraphElement>>;
9
+ Content: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
10
+ className?: import("../../props/index.js").ClassNameProp;
11
+ children?: import("../../props/index.js").ChildrenProp;
12
+ } & React.RefAttributes<HTMLDivElement>>;
13
+ Description: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & {
14
+ className?: import("../../props/index.js").ClassNameProp;
15
+ children?: import("../../props/index.js").ChildrenProp;
16
+ } & React.RefAttributes<HTMLParagraphElement>>;
17
+ Actions: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
18
+ className?: import("../../props/index.js").ClassNameProp;
19
+ children?: import("../../props/index.js").ChildrenProp;
20
+ } & React.RefAttributes<HTMLDivElement>>;
21
+ };
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { AlertActions, AlertBase, AlertContent, AlertDescription, AlertTitle } from "./alert.js";
5
+ const BannerBase = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(AlertBase, { ref, variant: "banner", ...props }));
6
+ BannerBase.displayName = "Banner";
7
+ const Banner = Object.assign(BannerBase, {
8
+ Title: AlertTitle,
9
+ Content: AlertContent,
10
+ Description: AlertDescription,
11
+ Actions: AlertActions
12
+ });
13
+ export {
14
+ Banner
15
+ };
@@ -8,6 +8,8 @@ export { Toaster } from "./sonner.js";
8
8
  export { toast } from "./use-toast.js";
9
9
  export { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat } from "./skeleton.js";
10
10
  export type { SkeletonProps } from "./skeleton.js";
11
- export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, Banner, } from "./alert.js";
12
- export type { BannerProp, BannerProps, AlertProp, AlertProps, AlertTitleProp, AlertTitleProps, AlertContentProp, AlertContentProps, AlertDescriptionProp, AlertDescriptionProps, AlertActionsProp, AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProps, } from "./alert.js";
11
+ export { Banner } from "./banner.js";
12
+ export type { BannerProp, BannerProps } from "./banner.js";
13
+ export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, } from "./alert.js";
14
+ export type { AlertProp, AlertProps, AlertTitleProp, AlertTitleProps, AlertContentProp, AlertContentProps, AlertDescriptionProp, AlertDescriptionProps, AlertActionsProp, AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProps, } from "./alert.js";
13
15
  export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./tooltip.js";
@@ -43,14 +43,14 @@ import { TwoFactorSetup } from "./two-factor-setup.js";
43
43
  import { Toaster } from "./sonner.js";
44
44
  import { toast } from "./use-toast.js";
45
45
  import { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat } from "./skeleton.js";
46
+ import { Banner } from "./banner.js";
46
47
  import {
47
48
  Alert,
48
49
  AlertTitle,
49
50
  AlertContent,
50
51
  AlertDescription,
51
52
  AlertActions,
52
- AlertQueryError,
53
- Banner
53
+ AlertQueryError
54
54
  } from "./alert.js";
55
55
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./tooltip.js";
56
56
  export {
@@ -13,6 +13,7 @@ export declare const Text: React.ForwardRefExoticComponent<Omit<React.HTMLAttrib
13
13
  weight?: import("../../props/index.js").FontWeightProp;
14
14
  align?: import("../../props/index.js").TextAlignProp;
15
15
  truncate?: boolean;
16
+ clamp?: number;
16
17
  tabular?: boolean;
17
18
  mono?: boolean;
18
19
  htmlFor?: string;
@@ -8,23 +8,42 @@ const Text = React.forwardRef(
8
8
  weight = "regular",
9
9
  align,
10
10
  truncate,
11
+ clamp,
11
12
  tabular,
12
13
  mono,
13
14
  className,
15
+ style,
14
16
  ...props
15
- }, ref) => React.createElement(as, {
16
- ref,
17
- "data-slot": "text",
18
- "data-size": size,
19
- "data-tone": tone,
20
- "data-weight": weight,
21
- "data-align": align,
22
- "data-truncate": truncate ? "" : void 0,
23
- "data-tabular": tabular ? "" : void 0,
24
- "data-mono": mono ? "" : void 0,
25
- className: cn("ui-text", className),
26
- ...props
27
- })
17
+ }, ref) => {
18
+ const clampLines = typeof clamp === "number" && Number.isFinite(clamp) && clamp >= 1 ? Math.floor(clamp) : void 0;
19
+ if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
20
+ if (clamp !== void 0 && clampLines === void 0) {
21
+ console.warn(
22
+ `Text: \`clamp\` must be a finite number \u2265 1 (got ${String(clamp)}); ignored.`
23
+ );
24
+ }
25
+ if (truncate && clampLines !== void 0) {
26
+ console.warn(
27
+ "Text: `truncate` and `clamp` are mutually exclusive \u2014 `clamp` takes precedence; drop `truncate`."
28
+ );
29
+ }
30
+ }
31
+ return React.createElement(as, {
32
+ ref,
33
+ "data-slot": "text",
34
+ "data-size": size,
35
+ "data-tone": tone,
36
+ "data-weight": weight,
37
+ "data-align": align,
38
+ "data-truncate": truncate && clampLines === void 0 ? "" : void 0,
39
+ "data-clamp": clampLines !== void 0 ? "" : void 0,
40
+ style: clampLines !== void 0 ? { ...style, "--text-clamp": clampLines } : style,
41
+ "data-tabular": tabular ? "" : void 0,
42
+ "data-mono": mono ? "" : void 0,
43
+ className: cn("ui-text", className),
44
+ ...props
45
+ });
46
+ }
28
47
  );
29
48
  Text.displayName = "Text";
30
49
  const Heading = React.forwardRef(
@@ -10,8 +10,8 @@ export type { AuthShellProp, AuthShellProp as AuthShellProps, } from "../../prop
10
10
  *
11
11
  * `preset` names the flow MEASURE (card max-width + desktop/mobile page gutters) so a consumer
12
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
13
+ * for standalone and real requester states; `"registration"` = the 360px sign-up measure,
14
+ * start-aligned like login (a tall sign-up card must scroll, not clip its own top above the scroll
15
15
  * origin) and the only one with its own footer clearance; `"device-authorization"` = 380px card
16
16
  * with a 5px inline gutter at 390px; `"context-selection"` = 25rem card, edge-to-edge on mobile;
17
17
  * `"account-recovery"` = the 432px SCR-008 panel measure shared by password recovery and the
@@ -1,7 +1,5 @@
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";
5
3
  export { Flex } from "./flex.js";
6
4
  export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProps, } from "./flex.js";
7
5
  export { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
@@ -44,3 +42,5 @@ export { Separator } from "./separator.js";
44
42
  export { AspectRatio } from "./aspect-ratio.js";
45
43
  export { LegalDocumentShell } from "./legal-document-shell.js";
46
44
  export type { LegalDocumentSectionProp, LegalDocumentShellProp, LegalDocumentShellProps, } from "./legal-document-shell.js";
45
+ export { ServiceRolePanel } from "./service-role-panel.js";
46
+ export type { ServiceRoleItemProp, ServiceRolePanelProp, ServiceRolePanelProps, } from "./service-role-panel.js";
@@ -1,5 +1,4 @@
1
1
  import { PageContainer } from "./page-container.js";
2
- import { PageHeader } from "./page-header.js";
3
2
  import { Flex } from "./flex.js";
4
3
  import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
5
4
  import { AppShell } from "./app-shell.js";
@@ -21,6 +20,7 @@ import { SplitPane } from "./split-pane.js";
21
20
  import { Separator } from "./separator.js";
22
21
  import { AspectRatio } from "./aspect-ratio.js";
23
22
  import { LegalDocumentShell } from "./legal-document-shell.js";
23
+ import { ServiceRolePanel } from "./service-role-panel.js";
24
24
  export {
25
25
  AppShell,
26
26
  AspectRatio,
@@ -38,12 +38,12 @@ export {
38
38
  MasterDetail,
39
39
  OrgSwitcher,
40
40
  PageContainer,
41
- PageHeader,
42
41
  ResizableHandle,
43
42
  ResizablePanel,
44
43
  ResizablePanelGroup,
45
44
  ResponsiveGrid,
46
45
  Separator,
46
+ ServiceRolePanel,
47
47
  Sidebar,
48
48
  SidebarHeader,
49
49
  SidebarItem,
@@ -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, meta, headerLoading, extra, footer, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, preset, headerLayout, measure, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
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
- meta,
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__ */ 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
- ),
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
  ]