@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
@@ -110,6 +110,24 @@
110
110
  "previousYear": "Previous year",
111
111
  "nextYear": "Next year",
112
112
  "placeholder": "yyyy/mm"
113
+ },
114
+ "branchScope": {
115
+ "label": "Branch scope",
116
+ "all": "All branches",
117
+ "allDescription": "Applies to every current and future branch",
118
+ "selected": "Selected branches only",
119
+ "selectedDescription": "Applies only to the branches checked below",
120
+ "listLabel": "Branches",
121
+ "searchPlaceholder": "Search branches",
122
+ "selectedCount": {
123
+ "one": "{count} branch selected",
124
+ "other": "{count} branches selected"
125
+ },
126
+ "empty": "No branches to choose from",
127
+ "error": "Could not load branches",
128
+ "denied": "You do not have permission to view branches",
129
+ "loading": "Loading branches…",
130
+ "noMatches": "No branches match your search"
113
131
  }
114
132
  },
115
133
  "feedback": {
@@ -135,9 +153,6 @@
135
153
  "openNav": "Open navigation menu",
136
154
  "navLabel": "Menu"
137
155
  },
138
- "pageHeader": {
139
- "loading": "Loading page…"
140
- },
141
156
  "authShell": {
142
157
  "brandLabel": "Brand",
143
158
  "mainLabel": "Main content",
@@ -167,6 +182,28 @@
167
182
  "searchPlaceholder": "Search...",
168
183
  "notifications": "Notifications",
169
184
  "tweaks": "Open settings"
185
+ },
186
+ "serviceRolePanel": {
187
+ "rolesLabel": "Roles",
188
+ "detailLabel": "Role detail",
189
+ "locked": "Locked",
190
+ "memberCount": {
191
+ "one": "{count} member",
192
+ "other": "{count} members"
193
+ },
194
+ "selectRole": "Select {role}",
195
+ "deleteRole": "Delete {role}",
196
+ "deleteTitle": "Delete this role?",
197
+ "deleteDescription": "Deleting {role} removes its permissions from every assigned member. This cannot be undone.",
198
+ "deleteConfirm": "Delete role",
199
+ "empty": "No roles yet",
200
+ "emptyDescription": "Create a role to start assigning permissions.",
201
+ "error": "Could not load roles",
202
+ "errorDescription": "The data could not be fetched. Try again shortly.",
203
+ "denied": "You do not have permission to view roles",
204
+ "deniedDescription": "Ask an administrator to grant you access.",
205
+ "loading": "Loading roles…",
206
+ "noDetail": "Select a role to see its detail"
170
207
  }
171
208
  },
172
209
  "dataDisplay": {
@@ -182,6 +219,21 @@
182
219
  "listRow": {
183
220
  "unread": "Unread",
184
221
  "read": "Read"
222
+ },
223
+ "permissionMatrix": {
224
+ "caption": "Role and permission matrix",
225
+ "permissionColumn": "Permission",
226
+ "granted": "Granted",
227
+ "notGranted": "Not granted",
228
+ "difference": "Difference",
229
+ "toggle": "Grant {permission} to {role}",
230
+ "locked": "Locked",
231
+ "empty": "No permissions to show",
232
+ "error": "Could not load the permission matrix",
233
+ "errorDescription": "The data could not be fetched. Try again shortly.",
234
+ "denied": "You do not have permission to view this matrix",
235
+ "deniedDescription": "Ask an administrator to grant you access.",
236
+ "loading": "Loading permissions…"
185
237
  }
186
238
  },
187
239
  "ui": {
@@ -232,7 +284,7 @@
232
284
  },
233
285
  "filterBar": {
234
286
  "appliedFilters": "Applied filters",
235
- "removeFilter": "Remove filter {label}",
287
+ "removeFilter": "Remove filter: {label}",
236
288
  "resultCount": {
237
289
  "one": "{count} result",
238
290
  "other": "{count} results"
@@ -110,6 +110,21 @@
110
110
  "previousYear": "前年へ",
111
111
  "nextYear": "翌年へ",
112
112
  "placeholder": "yyyy/mm"
113
+ },
114
+ "branchScope": {
115
+ "label": "ブランチ範囲",
116
+ "all": "すべてのブランチ",
117
+ "allDescription": "現在および今後のすべてのブランチに適用",
118
+ "selected": "選択したブランチのみ",
119
+ "selectedDescription": "下で選択したブランチにのみ適用",
120
+ "listLabel": "ブランチ",
121
+ "searchPlaceholder": "ブランチを検索",
122
+ "selectedCount": "{count} 件選択中",
123
+ "empty": "選択できるブランチがありません",
124
+ "error": "ブランチを取得できませんでした",
125
+ "denied": "ブランチを表示する権限がありません",
126
+ "loading": "ブランチを読み込み中…",
127
+ "noMatches": "検索に一致するブランチがありません"
113
128
  }
114
129
  },
115
130
  "feedback": {
@@ -135,9 +150,6 @@
135
150
  "openNav": "ナビゲーションメニューを開く",
136
151
  "navLabel": "メニュー"
137
152
  },
138
- "pageHeader": {
139
- "loading": "ページを読み込んでいます…"
140
- },
141
153
  "authShell": {
142
154
  "brandLabel": "ブランド",
143
155
  "mainLabel": "メインコンテンツ",
@@ -167,6 +179,25 @@
167
179
  "searchPlaceholder": "検索...",
168
180
  "notifications": "通知",
169
181
  "tweaks": "設定を開く"
182
+ },
183
+ "serviceRolePanel": {
184
+ "rolesLabel": "ロール一覧",
185
+ "detailLabel": "ロールの詳細",
186
+ "locked": "変更不可",
187
+ "memberCount": "{count} 名",
188
+ "selectRole": "{role} を選択",
189
+ "deleteRole": "{role} を削除",
190
+ "deleteTitle": "ロールを削除しますか?",
191
+ "deleteDescription": "{role} を削除すると、割り当てられたメンバーはこのロールの権限を失います。この操作は取り消せません。",
192
+ "deleteConfirm": "ロールを削除",
193
+ "empty": "ロールがありません",
194
+ "emptyDescription": "ロールを作成して権限の割り当てを始めましょう。",
195
+ "error": "ロールを取得できませんでした",
196
+ "errorDescription": "データを取得できませんでした。時間をおいて再試行してください。",
197
+ "denied": "ロールを表示する権限がありません",
198
+ "deniedDescription": "閲覧するには管理者に権限の付与を依頼してください。",
199
+ "loading": "ロールを読み込み中…",
200
+ "noDetail": "ロールを選択すると詳細が表示されます"
170
201
  }
171
202
  },
172
203
  "dataDisplay": {
@@ -182,6 +213,21 @@
182
213
  "listRow": {
183
214
  "unread": "未読",
184
215
  "read": "既読"
216
+ },
217
+ "permissionMatrix": {
218
+ "caption": "ロールと権限のマトリクス",
219
+ "permissionColumn": "権限",
220
+ "granted": "許可",
221
+ "notGranted": "不許可",
222
+ "difference": "差分",
223
+ "toggle": "{role} に {permission} を付与",
224
+ "locked": "変更不可",
225
+ "empty": "表示できる権限がありません",
226
+ "error": "権限マトリクスを取得できませんでした",
227
+ "errorDescription": "データを取得できませんでした。時間をおいて再試行してください。",
228
+ "denied": "このマトリクスを表示する権限がありません",
229
+ "deniedDescription": "閲覧するには管理者に権限の付与を依頼してください。",
230
+ "loading": "権限を読み込み中…"
185
231
  }
186
232
  },
187
233
  "ui": {
@@ -232,8 +278,8 @@
232
278
  },
233
279
  "filterBar": {
234
280
  "appliedFilters": "適用中のフィルター",
235
- "removeFilter": "フィルター「{label}」を解除",
236
- "resultCount": "{count} "
281
+ "removeFilter": "フィルターを解除: {label}",
282
+ "resultCount": "{count} 件の結果"
237
283
  },
238
284
  "localePicker": {
239
285
  "ariaLabel": "言語"
@@ -110,6 +110,21 @@
110
110
  "previousYear": "Năm trước",
111
111
  "nextYear": "Năm sau",
112
112
  "placeholder": "yyyy/mm"
113
+ },
114
+ "branchScope": {
115
+ "label": "Phạm vi chi nhánh",
116
+ "all": "Tất cả chi nhánh",
117
+ "allDescription": "Áp dụng cho mọi chi nhánh hiện tại và sau này",
118
+ "selected": "Chỉ chi nhánh đã chọn",
119
+ "selectedDescription": "Chỉ áp dụng cho các chi nhánh được chọn bên dưới",
120
+ "listLabel": "Chi nhánh",
121
+ "searchPlaceholder": "Tìm chi nhánh",
122
+ "selectedCount": "Đã chọn {count} chi nhánh",
123
+ "empty": "Chưa có chi nhánh để chọn",
124
+ "error": "Không tải được danh sách chi nhánh",
125
+ "denied": "Bạn không có quyền xem chi nhánh",
126
+ "loading": "Đang tải chi nhánh…",
127
+ "noMatches": "Không có chi nhánh khớp với tìm kiếm"
113
128
  }
114
129
  },
115
130
  "feedback": {
@@ -135,9 +150,6 @@
135
150
  "openNav": "Mở menu điều hướng",
136
151
  "navLabel": "Menu"
137
152
  },
138
- "pageHeader": {
139
- "loading": "Đang tải trang…"
140
- },
141
153
  "authShell": {
142
154
  "brandLabel": "Thương hiệu",
143
155
  "mainLabel": "Nội dung chính",
@@ -167,6 +179,25 @@
167
179
  "searchPlaceholder": "Tìm kiếm...",
168
180
  "notifications": "Thông báo",
169
181
  "tweaks": "Mở tùy chỉnh"
182
+ },
183
+ "serviceRolePanel": {
184
+ "rolesLabel": "Danh sách vai trò",
185
+ "detailLabel": "Chi tiết vai trò",
186
+ "locked": "Đã khóa",
187
+ "memberCount": "{count} thành viên",
188
+ "selectRole": "Chọn {role}",
189
+ "deleteRole": "Xóa {role}",
190
+ "deleteTitle": "Xóa vai trò này?",
191
+ "deleteDescription": "Xóa {role} sẽ gỡ quyền của vai trò này khỏi mọi thành viên được gán. Không thể hoàn tác.",
192
+ "deleteConfirm": "Xóa vai trò",
193
+ "empty": "Chưa có vai trò",
194
+ "emptyDescription": "Tạo một vai trò để bắt đầu gán quyền.",
195
+ "error": "Không tải được vai trò",
196
+ "errorDescription": "Không lấy được dữ liệu. Vui lòng thử lại sau giây lát.",
197
+ "denied": "Bạn không có quyền xem vai trò",
198
+ "deniedDescription": "Hãy đề nghị quản trị viên cấp quyền truy cập.",
199
+ "loading": "Đang tải vai trò…",
200
+ "noDetail": "Chọn một vai trò để xem chi tiết"
170
201
  }
171
202
  },
172
203
  "dataDisplay": {
@@ -182,6 +213,21 @@
182
213
  "listRow": {
183
214
  "unread": "Chưa đọc",
184
215
  "read": "Đã đọc"
216
+ },
217
+ "permissionMatrix": {
218
+ "caption": "Ma trận vai trò và quyền",
219
+ "permissionColumn": "Quyền",
220
+ "granted": "Được phép",
221
+ "notGranted": "Không được phép",
222
+ "difference": "Khác biệt",
223
+ "toggle": "Cấp {permission} cho {role}",
224
+ "locked": "Đã khóa",
225
+ "empty": "Chưa có quyền nào để hiển thị",
226
+ "error": "Không tải được ma trận quyền",
227
+ "errorDescription": "Không lấy được dữ liệu. Vui lòng thử lại sau giây lát.",
228
+ "denied": "Bạn không có quyền xem ma trận này",
229
+ "deniedDescription": "Hãy đề nghị quản trị viên cấp quyền truy cập.",
230
+ "loading": "Đang tải quyền…"
185
231
  }
186
232
  },
187
233
  "ui": {
@@ -232,7 +278,7 @@
232
278
  },
233
279
  "filterBar": {
234
280
  "appliedFilters": "Bộ lọc đang áp dụng",
235
- "removeFilter": "Bỏ bộ lọc {label}",
281
+ "removeFilter": "Bỏ bộ lọc: {label}",
236
282
  "resultCount": "{count} kết quả"
237
283
  },
238
284
  "localePicker": {
@@ -62,25 +62,9 @@ export type DescriptionsItemProp = {
62
62
  */
63
63
  export type AvatarProp = React.ComponentPropsWithoutRef<"span"> & {
64
64
  shape?: AvatarShapeProp;
65
- appearance?: AvatarAppearanceProp;
66
65
  className?: ClassNameProp;
67
66
  children?: ChildrenProp;
68
67
  };
69
- /**
70
- * Avatar fill treatment.
71
- *
72
- * - `"default"` — the identity fill: `--muted` for a person, the solid brand mark for
73
- * `shape="square"`.
74
- * - `"tinted"` — the **capability medallion**: a soft role wash behind a role-coloured glyph.
75
- * This is the plate a capability/feature icon sits on (`shape="square" appearance="tinted"` for
76
- * the canonical rounded square). It exists because the medallion is a composition — `Avatar` +
77
- * a Lucide glyph, per docs/COMPOSITION-VS-COMPONENT.md — but its *tint* was not reachable from
78
- * a token, so consumers were re-deriving `hsl(var(--primary) / 0.1)` in page CSS or giving up
79
- * and rendering a bare glyph. Retune with `--avatar-tinted-*`.
80
- *
81
- * @see Avatar
82
- */
83
- export type AvatarAppearanceProp = "default" | "tinted";
84
68
  /** @see Badge */
85
69
  export type BadgeProp = {
86
70
  variant?: "default" | "secondary" | "outline";
@@ -229,3 +213,69 @@ export type ListRowProp = {
229
213
  unread?: boolean;
230
214
  className?: ClassNameProp;
231
215
  };
216
+ /** @see PermissionMatrix — one role COLUMN. Domain data is consumer-supplied; nothing is encoded. */
217
+ export type PermissionMatrixRoleProp = {
218
+ /** Stable role id — the `roleId` half of a grant key. */
219
+ id: string;
220
+ /** Human role name (also used in accessible cell labels, so a plain string). */
221
+ name: string;
222
+ /** Short hint under the role name (e.g. member count or scope). */
223
+ description?: string;
224
+ /** A locked role renders read-only cells even when the matrix is editable. */
225
+ locked?: boolean;
226
+ };
227
+ /** @see PermissionMatrix — one permission ROW. */
228
+ export type PermissionMatrixPermissionProp = {
229
+ /** Stable permission id — the `permissionId` half of a grant key. */
230
+ id: string;
231
+ /** Human permission name (also used in accessible cell labels, so a plain string). */
232
+ name: string;
233
+ /** Secondary line under the permission name. */
234
+ description?: string;
235
+ /** Optional category caption rendered with the description (e.g. 請求 / レポート). */
236
+ group?: string;
237
+ };
238
+ /**
239
+ * @see PermissionMatrix — the grant relation. Either the `grantKey(roleId, permissionId)` `Set`
240
+ * from `@godxjp/ui/lib/permission-grid` (O(1), the canonical form) or a plain pair array, which the
241
+ * matrix normalizes through the same `grantKey` encoding.
242
+ */
243
+ export type PermissionMatrixGrantsProp = ReadonlySet<string> | readonly {
244
+ roleId: string;
245
+ permissionId: string;
246
+ }[];
247
+ /** @see PermissionMatrix */
248
+ export type PermissionMatrixProp = {
249
+ /** Role columns, in render order. */
250
+ roles: readonly PermissionMatrixRoleProp[];
251
+ /** Permission rows, in render order. */
252
+ permissions: readonly PermissionMatrixPermissionProp[];
253
+ /** The grant relation (see {@link PermissionMatrixGrantsProp}). */
254
+ grants: PermissionMatrixGrantsProp;
255
+ /**
256
+ * Grant toggle handler. Its PRESENCE makes the matrix editable (checkbox cells); omitted, the
257
+ * matrix is the canonical read-only ✓/— grid. Locked roles and `readOnly` stay read-only
258
+ * regardless.
259
+ */
260
+ onGrantChange?: (roleId: string, permissionId: string, granted: boolean) => void;
261
+ /** Force the read-only grid even when `onGrantChange` is present (e.g. viewer permission). */
262
+ readOnly?: boolean;
263
+ /** Two role ids to compare side by side; highlights their columns and the differing rows. */
264
+ compare?: readonly [string, string] | null;
265
+ /** With `compare`, keep only the rows on which the two roles differ (差分のみ). */
266
+ diffOnly?: boolean;
267
+ /** Accessible name for the grid. Defaults to the localized caption. */
268
+ label?: LabelProp;
269
+ /** Show the loading skeleton instead of the grid. Precedence: loading → denied → error → empty. */
270
+ loading?: boolean;
271
+ /** Custom empty content when `permissions` is empty; defaults to a localized EmptyState. */
272
+ empty?: React.ReactNode;
273
+ /** Failure state (mirrors DataTable #216): `true` = built-in localized message, node = replace. */
274
+ error?: React.ReactNode;
275
+ /** Permission-denied state — refused, not failed. Takes precedence over `error`. */
276
+ denied?: React.ReactNode;
277
+ /** Retry handler for the built-in `error` state; omit to hide the retry action. */
278
+ onRetry?: HandlerProp;
279
+ className?: ClassNameProp;
280
+ id?: IdProp;
281
+ };
@@ -16,13 +16,6 @@ export type InputOTPGroupAppearanceProp = "slots" | "grouped";
16
16
  export type InputOTPGroupProp = React.HTMLAttributes<HTMLDivElement> & {
17
17
  appearance?: InputOTPGroupAppearanceProp;
18
18
  };
19
- /**
20
- * Main-axis alignment of the whole code row (groups + separators) inside its container.
21
- * `start` is the historical default. A centred challenge is the common auth case and used to
22
- * force every consumer to wrap `.ui-otp-container` in their own flex-centring div.
23
- * @see InputOTP
24
- */
25
- export type InputOTPAlignProp = "start" | "center" | "end";
26
19
  /** @see Input */
27
20
  export type InputProp = React.InputHTMLAttributes<HTMLInputElement> & {
28
21
  /** Show an inline ✕ that clears the field while it holds text (default false). */
@@ -517,3 +510,57 @@ export type TransferProp = FieldA11yProps & {
517
510
  selectedKeys?: [string[], string[]];
518
511
  onSelectChange?: (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void;
519
512
  };
513
+ /** @see BranchScopePicker — scope mode: every branch, or an explicit subset. */
514
+ export type BranchScopeModeProp = "all" | "selected";
515
+ /**
516
+ * @see BranchScopePicker — the picker value. `mode: "all"` ignores `branchIds`; `mode: "selected"`
517
+ * carries the checked branch ids.
518
+ */
519
+ export type BranchScopeValueProp = {
520
+ mode: BranchScopeModeProp;
521
+ branchIds?: readonly string[];
522
+ };
523
+ /** @see BranchScopePicker — one selectable branch. Domain data is consumer-supplied. */
524
+ export type BranchScopeOptionProp = {
525
+ /** Stable branch id. */
526
+ id: string;
527
+ /** Human branch name (also the search haystack, so a plain string). */
528
+ name: string;
529
+ /** Secondary line under the branch name (e.g. an address or code). */
530
+ description?: string;
531
+ /** Keep the branch visible but unselectable. */
532
+ disabled?: boolean;
533
+ };
534
+ /** @see BranchScopePicker */
535
+ export type BranchScopePickerProp = FieldA11yProps & {
536
+ /** The selectable branches. */
537
+ branches: readonly BranchScopeOptionProp[];
538
+ /** Controlled value. */
539
+ value?: BranchScopeValueProp;
540
+ /** Uncontrolled initial value. Default `{ mode: "all" }`. */
541
+ defaultValue?: BranchScopeValueProp;
542
+ /** Value change handler (fires for mode switches AND branch checks). */
543
+ onValueChange?: (value: BranchScopeValueProp) => void;
544
+ /** Disable the whole control. */
545
+ disabled?: DisabledProp;
546
+ /** Render the current value without any editable affordance (locked view). */
547
+ readOnly?: boolean;
548
+ /** Show a branch search input above the list once there is anything to search. Default `true`. */
549
+ searchable?: boolean;
550
+ /** Validation message under the control (wired via `aria-errormessage`/`aria-invalid`). */
551
+ error?: ErrorProp;
552
+ /** Show the loading skeleton instead of the list. Precedence: loading → denied → listError → empty. */
553
+ loading?: boolean;
554
+ /** Custom empty content when `branches` is empty; defaults to a localized message. */
555
+ empty?: React.ReactNode;
556
+ /** Branch-collection READ failure (distinct from `error`, which is field validation). */
557
+ listError?: React.ReactNode;
558
+ /** Permission-denied state — the branch read was refused. Takes precedence over `listError`. */
559
+ denied?: React.ReactNode;
560
+ /** Radio labels override (localized defaults otherwise). */
561
+ allLabel?: React.ReactNode;
562
+ selectedLabel?: React.ReactNode;
563
+ name?: NameProp;
564
+ id?: IdProp;
565
+ className?: ClassNameProp;
566
+ };
@@ -43,9 +43,9 @@ export type AlertProp = React.HTMLAttributes<HTMLDivElement> & {
43
43
  children?: ChildrenProp;
44
44
  };
45
45
  /**
46
- * @see Banner — page-level Alert. `variant` is fixed to `"banner"` by the alias, so it is the one
47
- * prop a Banner does not take; everything else (tone, icon, onDismiss, children) is Alert's
48
- * contract verbatim. gh#255.
46
+ * @see Banner — the full-bleed attention strip (`<Alert variant="banner">` with the variant fixed).
47
+ * Same contract as {@link AlertProp} minus `variant`: `tone` owns colour + live-region politeness,
48
+ * `icon`/`icon={false}` owns the leading glyph, `onDismiss` renders the built-in dismiss button.
49
49
  */
50
50
  export type BannerProp = Omit<AlertProp, "variant">;
51
51
  /** @see AlertTitle */
@@ -12,8 +12,15 @@ export type TextProp = Omit<React.HTMLAttributes<HTMLElement>, "color"> & {
12
12
  /** Weight (system 2-weight: 400/500). Default `regular`. */
13
13
  weight?: FontWeightProp;
14
14
  align?: TextAlignProp;
15
- /** Single-line ellipsis. */
15
+ /** Single-line ellipsis. Mutually exclusive with `clamp` — when both are set, `clamp` wins. */
16
16
  truncate?: boolean;
17
+ /**
18
+ * Multi-line clamp — max rendered lines (integer ≥ 1); overflow ends in an ellipsis.
19
+ * Token-owned line-clamp styling (never write the `line-clamp-N` utility page-side).
20
+ * Visual-only: the full text stays in the DOM / accessible name. Mutually exclusive with
21
+ * `truncate` — when both are set, `clamp` wins (dev builds warn).
22
+ */
23
+ clamp?: number;
17
24
  /** Tabular figures for aligned numbers. */
18
25
  tabular?: boolean;
19
26
  /** Monospace family (codes, ids). */
@@ -1,7 +1,7 @@
1
1
  /** Layout component prop types — @see docs/COMPONENTS.md#layout */
2
2
  import type * as React from "react";
3
3
  import type { ComponentType, ReactNode, SVGProps } from "react";
4
- import type { BreadcrumbProp, TitleProp, SubtitleProp, ExtraProp, FooterProp, PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, BreakpointProp, GapProp, ClassNameProp, ChildrenProp, IdProp, DisabledProp, DescriptionProp, ActionProp, IconProp, HeadingLevelProp } from "../vocabulary/index.js";
4
+ import type { BreadcrumbProp, TitleProp, SubtitleProp, StatusProp, ExtraProp, FooterProp, PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, BreakpointProp, GapProp, ClassNameProp, ChildrenProp, IdProp, DisabledProp, DescriptionProp, ActionProp, IconProp, HeadingLevelProp } from "../vocabulary/index.js";
5
5
  import type { EmptyStateToneProp } from "./data-display.prop.js";
6
6
  /**
7
7
  * Arrangement of the page header's title band and its `extra` slot below the 640px step.
@@ -24,55 +24,20 @@ export type PageContainerPresetProp = "default" | "admin-collection";
24
24
  * fluid at the compact gutter.
25
25
  */
26
26
  export type PageContainerMeasureProp = "default" | "narrow" | "medium";
27
- /**
28
- * @see PageHeader — the canonical page title band, extracted from PageContainer so a consumer can
29
- * mount the SAME token-owned geometry outside a full page shell (a Sheet detail, a MasterDetail
30
- * pane, a tab body) instead of re-authoring `.ui-page-header` CSS locally (gh#255).
31
- */
32
- export type PageHeaderProp = {
33
- title: TitleProp;
34
- subtitle?: SubtitleProp;
35
- /**
36
- * Status / meta band rendered INLINE with the title (a `Badge` tone, an ID, a timestamp).
37
- * Omitted → the title band emits its historical single-`<h1>` DOM, so no existing page moves.
38
- */
39
- meta?: ReactNode;
40
- /** Trailing action region — buttons, a search field, an overflow menu. */
41
- extra?: ExtraProp;
42
- breadcrumb?: BreadcrumbProp;
43
- /**
44
- * Override the breadcrumb `<nav>` landmark's accessible name. Defaults to a localized
45
- * "Breadcrumb". Needed when more than one header (each with its own `breadcrumb`) renders on the
46
- * same page/view — two `<nav>` landmarks sharing one name/role fail axe's `landmark-unique`
47
- * (WCAG 2.4.1 / 1.3.1).
48
- */
49
- breadcrumbLabel?: string;
50
- /** Kebab/DOM-style alias of `breadcrumbLabel` (same landmark-unique override). */
51
- breadcrumbAriaLabel?: string;
52
- linkComponent?: React.ElementType;
53
- /**
54
- * How the title band and `extra` share the header row below the 640px step. Defaults to
55
- * `stack`. @see PageContainerHeaderLayoutProp
56
- */
57
- layout?: PageContainerHeaderLayoutProp;
58
- /**
59
- * Pending state for the title band while the page's own data resolves. Renders the title/subtitle
60
- * /meta as `Skeleton` placeholders and marks the header `aria-busy`, keeping the `<h1>` in the
61
- * document with an accessible name (an empty heading is an axe violation) so the page's heading
62
- * outline never disappears mid-load. Breadcrumbs and `extra` are NOT skeletonised — they come
63
- * from the route, not the record.
64
- */
65
- loading?: boolean;
66
- className?: ClassNameProp;
67
- };
68
27
  /** @see PageContainer */
69
28
  export type PageContainerProp = {
70
29
  title: TitleProp;
71
30
  subtitle?: SubtitleProp;
72
- /** Status / meta band rendered inline with the title. @see PageHeaderProp.meta */
73
- meta?: ReactNode;
74
- /** Skeletonise the title band while the page record loads. @see PageHeaderProp.loading */
75
- headerLoading?: boolean;
31
+ /**
32
+ * Status/meta band rendered beside the title inside the heading — StatusBadge, environment
33
+ * tag, "updated …" meta text. Geometry is token-owned (`--page-header-status-gap`): the band
34
+ * sits on the title line and WRAPS under it on compact viewports, so a consumer never
35
+ * hand-lays a badge next to an `<h1>`. Part of the canonical page-header contract
36
+ * (godxjp-ui#255): PageContainer's embedded header IS the DXS `PageHeader` — breadcrumbs
37
+ * (`breadcrumb`), title, subtitle, status/meta (this), actions (`extra`) and responsive
38
+ * overflow (`headerLayout` / `measure`) all live on this one renderer.
39
+ */
40
+ status?: StatusProp;
76
41
  extra?: ExtraProp;
77
42
  footer?: FooterProp;
78
43
  breadcrumb?: BreadcrumbProp;
@@ -276,8 +241,8 @@ export type AuthShellProp = {
276
241
  /**
277
242
  * Named flow MEASURE — the page geometry contract for one canonical hosted-identity flow: the
278
243
  * auth card's max-width plus the desktop and mobile page gutters, all owned by component tokens
279
- * (`--auth-shell-{login,device,context,recovery}-*`). Selecting a preset replaces every
280
- * consumer-side geometry override.
244
+ * (`--auth-shell-{login,registration,device,context,recovery}-*`). Selecting a preset replaces
245
+ * every consumer-side geometry override.
281
246
  *
282
247
  * - `"default"` (default) — the shell's own measure; nothing changes.
283
248
  * - `"login"` — SCR-001's 360px card at x=540/332/15 and y=363/363/353 for the canonical
@@ -286,13 +251,14 @@ export type AuthShellProp = {
286
251
  * position without truncating or inventing requester data. Pass AuthIdentity, Card and
287
252
  * AuthFooter as direct children (an anchor may wrap AuthIdentity).
288
253
  * - `"registration"` — the 360px sign-up measure with a 15px inline gutter at 390px (the same
289
- * page rhythm as `"login"`, so sign-in → sign-up never jumps on a phone). The ONLY preset whose
290
- * column is START-aligned: a registration card is the tallest surface in the hosted-identity
291
- * set (name · email · password · confirm · strength · consent · submit · providers), and a
254
+ * page rhythm as `"login"`, so sign-in → sign-up never jumps on a phone). START-aligned like
255
+ * login, because a registration card is the tallest surface in the hosted-identity set
256
+ * (name · email · password · confirm · strength · consent · submit · providers) and a
292
257
  * vertically centred tall card overflows ABOVE the scroll origin on a short viewport, putting
293
- * its first field out of reach. It is also the only preset with a footer-clearance knob of its
294
- * own, so the legal/consent footer never sits flush against the submit button. Carries the full
295
- * password form and the pending-email confirmation state with no consumer geometry CSS.
258
+ * its first field out of reach start-aligned, a long form simply scrolls. It is also the
259
+ * only preset with a footer-clearance knob of its own, so the legal/consent footer never sits
260
+ * flush against the submit button. Carries the full password form and the pending-email
261
+ * confirmation state with no consumer geometry CSS.
296
262
  * - `"device-authorization"` — 380px card measure with a 5px inline page gutter at a 390px
297
263
  * viewport (canonical device-grant artboard).
298
264
  * - `"context-selection"` — 25rem card measure on desktop/tablet, edge-to-edge on mobile, and a
@@ -814,3 +780,58 @@ export type LegalDocumentShellProp = {
814
780
  id?: IdProp;
815
781
  className?: ClassNameProp;
816
782
  };
783
+ /** @see ServiceRolePanel — one role in the master rail. Domain data is consumer-supplied. */
784
+ export type ServiceRoleItemProp = {
785
+ /** Stable role id (the selection value). */
786
+ id: string;
787
+ /** Human role name (also used in accessible labels, so a plain string). */
788
+ name: string;
789
+ /** Secondary line under the role name. */
790
+ description?: string;
791
+ /** Member count caption, pluralized via CLDR. */
792
+ memberCount?: number;
793
+ /** A locked (system) role shows a lock badge and never offers deletion. */
794
+ locked?: boolean;
795
+ };
796
+ /** @see ServiceRolePanel */
797
+ export type ServiceRolePanelProp = {
798
+ /** The roles in the master collection, in render order. */
799
+ roles: readonly ServiceRoleItemProp[];
800
+ /** Controlled selected role id. */
801
+ value?: string;
802
+ /** Uncontrolled initial selected role id. Defaults to the first role. */
803
+ defaultValue?: string;
804
+ /** Selection change handler. */
805
+ onValueChange?: (roleId: string) => void;
806
+ /**
807
+ * Detail surface for the current selection. A render function receives the selected role
808
+ * (or `undefined` when nothing is selected); a plain node is rendered as-is.
809
+ */
810
+ children?: ReactNode | ((role: ServiceRoleItemProp | undefined) => ReactNode);
811
+ /**
812
+ * Confirmed-deletion handler. Its PRESENCE adds a delete affordance per non-locked role behind
813
+ * the built-in destructive `AlertDialog`; the handler fires only AFTER the user confirms.
814
+ */
815
+ onDeleteRole?: (roleId: string) => void;
816
+ /** Hide every mutating affordance (locked view). */
817
+ readOnly?: boolean;
818
+ /** Show the loading skeleton instead of the panel. Precedence: loading → denied → error → empty. */
819
+ loading?: boolean;
820
+ /** Custom empty content when `roles` is empty; defaults to a localized EmptyState. */
821
+ empty?: ReactNode;
822
+ /** Failure state: `true` = built-in localized message, any other node replaces it. */
823
+ error?: ReactNode;
824
+ /** Permission-denied state — refused, not failed. Takes precedence over `error`. */
825
+ denied?: ReactNode;
826
+ /** Retry handler for the built-in `error` state; omit to hide the retry action. */
827
+ onRetry?: () => void;
828
+ /** Accessible names for the two regions (localized defaults otherwise). */
829
+ masterLabel?: string;
830
+ detailLabel?: string;
831
+ /** Forwarded MasterDetail geometry. */
832
+ railWidth?: MasterDetailRailWidthProp;
833
+ masterViewport?: MasterDetailMasterViewportProp;
834
+ collapseBelow?: BreakpointProp | false;
835
+ id?: IdProp;
836
+ className?: ClassNameProp;
837
+ };