@godxjp/ui 22.0.0 → 23.0.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/README.md +4 -4
  2. package/dist/components/data-display/scroll-area.d.ts +24 -4
  3. package/dist/components/data-display/scroll-area.js +34 -72
  4. package/dist/components/data-entry/cascader.js +8 -6
  5. package/dist/components/data-entry/checkbox.js +2 -0
  6. package/dist/components/data-entry/choice-hit-target.d.ts +24 -0
  7. package/dist/components/data-entry/choice-hit-target.js +12 -0
  8. package/dist/components/data-entry/control-surface.d.ts +8 -0
  9. package/dist/components/data-entry/control-surface.js +10 -1
  10. package/dist/components/data-entry/form-field.js +3 -2
  11. package/dist/components/data-entry/radio.js +3 -2
  12. package/dist/components/data-entry/search-select.js +225 -95
  13. package/dist/components/data-entry/select.d.ts +76 -14
  14. package/dist/components/data-entry/select.js +495 -163
  15. package/dist/components/data-entry/slider.d.ts +3 -11
  16. package/dist/components/data-entry/slider.js +582 -93
  17. package/dist/components/data-entry/switch.js +2 -0
  18. package/dist/components/navigation/dropdown-menu.d.ts +11 -3
  19. package/dist/components/navigation/dropdown-menu.js +190 -15
  20. package/dist/components/navigation/index.d.ts +0 -3
  21. package/dist/components/navigation/index.js +0 -78
  22. package/dist/components/ui/index.d.ts +0 -3
  23. package/dist/components/ui/index.js +0 -3
  24. package/dist/components/ui/input-otp.d.ts +1 -1
  25. package/dist/components/ui/segmented.js +3 -1
  26. package/dist/components/ui/tag-input.js +3 -5
  27. package/dist/lib/select-options.d.ts +21 -0
  28. package/dist/lib/select-options.js +46 -0
  29. package/dist/props/components/data-display.prop.d.ts +9 -6
  30. package/dist/props/components/data-entry.prop.d.ts +323 -30
  31. package/dist/props/components/index.d.ts +1 -1
  32. package/dist/props/components/navigation.prop.d.ts +17 -0
  33. package/dist/props/registry.d.ts +63 -0
  34. package/dist/props/registry.js +69 -0
  35. package/dist/styles/control.css +205 -23
  36. package/dist/styles/data-display-layout.css +18 -10
  37. package/dist/styles/focus-ring.css +2 -2
  38. package/dist/styles/fonts.css +88 -4
  39. package/dist/styles/navigation-layout.css +11 -236
  40. package/dist/styles/shell-layout.css +144 -139
  41. package/dist/styles/table-layout.css +7 -3
  42. package/dist/tokens/components/control.css +4 -0
  43. package/dist/tokens/components/data-display.css +0 -5
  44. package/dist/tokens/components/navigation.css +3 -5
  45. package/dist/tokens/components/scroll-area.css +7 -0
  46. package/docs/CUSTOMER-THEMING.md +7 -1
  47. package/docs/FRAME-COVERAGE-REPORT.md +4 -7
  48. package/docs/data-display/data-table/index.tsx +27 -1
  49. package/docs/data-display/popover.tsx +4 -6
  50. package/docs/data-display/scroll-area.tsx +51 -45
  51. package/docs/data-entry/date-picker.tsx +2 -3
  52. package/docs/data-entry/input-otp.tsx +5 -2
  53. package/docs/data-entry/segmented.tsx +40 -1
  54. package/docs/data-entry/select-matrix.tsx +1 -2
  55. package/docs/data-entry/select.tsx +108 -3
  56. package/docs/data-entry/slider.tsx +125 -47
  57. package/docs/feedback/dialog.tsx +1 -2
  58. package/docs/feedback/sheet.tsx +1 -2
  59. package/docs/layout/app-shell-states.tsx +143 -0
  60. package/docs/navigation/dropdown-menu.tsx +75 -4
  61. package/docs/roadmap/parity-audit-data-entry.md +44 -33
  62. package/docs/roadmap/parity-audit-layout-navigation-general.md +304 -310
  63. package/docs/roadmap/parity-backlog.md +2 -3
  64. package/package.json +20 -44
  65. package/scripts/ui-audit.mjs +1 -1
  66. package/scripts/visual-audit-rules.mjs +0 -7
  67. package/scripts/visual-audit.mjs +6 -27
  68. package/dist/components/navigation/context-menu.d.ts +0 -21
  69. package/dist/components/navigation/context-menu.js +0 -149
  70. package/dist/components/navigation/menubar.d.ts +0 -28
  71. package/dist/components/navigation/menubar.js +0 -136
  72. package/dist/components/navigation/navigation-menu.d.ts +0 -10
  73. package/dist/components/navigation/navigation-menu.js +0 -91
  74. package/dist/components/ui/context-menu.d.ts +0 -1
  75. package/dist/components/ui/context-menu.js +0 -2
  76. package/dist/components/ui/menubar.d.ts +0 -1
  77. package/dist/components/ui/menubar.js +0 -2
  78. package/dist/components/ui/navigation-menu.d.ts +0 -1
  79. package/dist/components/ui/navigation-menu.js +0 -2
  80. package/docs/FRAME-A11Y-CI.md +0 -349
  81. package/docs/navigation/context-menu.tsx +0 -128
  82. package/docs/navigation/menubar.tsx +0 -141
  83. package/docs/navigation/navigation-menu.tsx +0 -158
@@ -93,11 +93,6 @@
93
93
  --qr-code-size-md: 10rem;
94
94
  --qr-code-size-lg: 12.5rem;
95
95
 
96
- --scroll-area-bar-size: 0.625rem;
97
-
98
- --scroll-area-bar-padding: 1px;
99
- --scroll-area-thumb-radius: var(--radius-pill);
100
-
101
96
  --timeline-grid-hour-height: 2.5rem;
102
97
  --timeline-grid-axis-width: 3.25rem;
103
98
  --timeline-grid-column-min-width: 6rem;
@@ -37,7 +37,7 @@
37
37
  --breadcrumb-font-size: var(--font-size-xs);
38
38
 
39
39
  --breadcrumb-menu-trigger-gap: var(--space-inline-xs);
40
- --menubar-shortcut-font-size: var(--font-size-xs);
40
+ --menu-shortcut-font-size: var(--font-size-xs);
41
41
  --tabs-list-max-inline-size: 100%;
42
42
  --tabs-list-overflow: auto;
43
43
 
@@ -89,8 +89,8 @@
89
89
  --tabs-tab-remove-icon-size: var(--icon-size-xs);
90
90
  --tabs-tab-remove-space-inline-end: var(--space-1);
91
91
 
92
- --menubar-item-hover-background: initial;
93
- --menubar-item-hover-foreground: initial;
92
+ --menu-item-hover-background: initial;
93
+ --menu-item-hover-foreground: initial;
94
94
 
95
95
  --filter-bar-sticky-offset: 0px;
96
96
 
@@ -128,8 +128,6 @@
128
128
  --menu-separator-space-block: var(--space-1);
129
129
  --menu-separator-space-inline: calc(var(--space-1) * -1);
130
130
 
131
- --navigation-menu-trigger-icon-size: var(--icon-size-sm);
132
-
133
131
  --steps-dot-size: 0.625rem;
134
132
  --steps-dot-process-ring-width: var(--stroke-xl);
135
133
  --steps-marker-border-width: var(--stroke-md);
@@ -1,5 +1,12 @@
1
1
 
2
2
  :root {
3
3
 
4
+ --scroll-area-scrollbar-width: thin;
5
+
6
+ --scroll-area-thumb-color: initial;
7
+ --scroll-area-track-color: initial;
8
+
9
+ --scroll-area-scrollbar-space: auto;
10
+
4
11
  --scroll-area-anchor-offset: 3rem;
5
12
  }
@@ -56,7 +56,13 @@ overwrites `--font-sans-base` (and `--font-sans-vi`) with the bundled stack —
56
56
  Noto Sans JP + Montserrat; v18.12.0–18.12.19 bundled M PLUS 2 + Noto Sans JP;
57
57
  v18.12.20+ bundles Noto Sans JP + M PLUS 2 (product override, direct
58
58
  instruction). If your design spec named an earlier bundle's faces, set the
59
- tokens yourself rather than relying on the bundle.) Headings read
59
+ tokens yourself rather than relying on the bundle.) Second in that stack is
60
+ `"Noto Sans JP Fallback"`: local faces (Arial / Liberation Sans for Latin, the
61
+ platform's gothic face for Japanese) with `size-adjust` and ascent/descent
62
+ overrides tuned to Noto Sans JP. The bundled faces are `font-display: swap`, so
63
+ a cold visit paints in this fallback first; because it occupies the same line
64
+ boxes and nearly the same advances, the swap does not reflow the page (#475).
65
+ If you restate the stack yourself, keep it directly after "Noto Sans JP". Headings read
60
66
  `--font-family-display` and body reads `--font-family-body`, both defaulting to
61
67
  `--font-family-sans` — override them for a dual-font (display + body) brand.
62
68
 
@@ -3,8 +3,8 @@
3
3
  > Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
4
4
  > Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
5
5
 
6
- - Public components: **138**
7
- - With ≥1 frame: **133** · zero-frame: **5**
6
+ - Public components: **135**
7
+ - With ≥1 frame: **130** · zero-frame: **5**
8
8
  - Fully covered (every contract axis declared): **0**
9
9
 
10
10
  ## UNTESTED — no `/frame/**` route
@@ -74,7 +74,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
74
74
  | Input | ✓ | ✓ | · | · | · | · | · | · | · | · |
75
75
  | NumberInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
76
76
  | SearchInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
77
- | Select | ✓ | N/A | · | · | · | · | · | · | · | · |
77
+ | Select | ✓ | · | · | · | · | · | · | · | · | · |
78
78
  | Switch | ✓ | ✓ | · | · | · | · | · | · | · | · |
79
79
  | Textarea | ✓ | ✓ | · | · | · | · | · | · | · | · |
80
80
  | Label | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
@@ -91,7 +91,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
91
91
  | Toaster | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
92
92
  | Tabs | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
93
93
  | Pagination | ✓ | · | · | · | · | · | · | · | · | · |
94
- | DropdownMenu | ✓ | N/A | N/A | · | · | · | · | · | · | · |
94
+ | DropdownMenu | ✓ | N/A | · | · | · | · | · | · | · | · |
95
95
  | Steps | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
96
96
  | Toolbar | ✓ | N/A | · | N/A | · | · | · | · | · | · |
97
97
  | AppProvider | ✓ | · | · | · | · | · | · | · | · | · |
@@ -128,9 +128,6 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
128
128
  | Rating | ✓ | N/A | · | · | · | · | · | · | · | · |
129
129
  | Segmented | ✓ | ✓ | · | · | · | · | · | · | · | · |
130
130
  | TagInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
131
- | ContextMenu | ✓ | N/A | N/A | · | · | · | · | · | · | · |
132
- | Menubar | ✓ | N/A | N/A | · | · | · | · | · | · | · |
133
- | NavigationMenu | ✓ | N/A | N/A | · | · | · | · | · | · | · |
134
131
  | ResizablePanel | ✓ | N/A | · | N/A | · | · | · | · | · | · |
135
132
  | Carousel | ✓ | N/A | N/A | · | · | · | · | · | · | · |
136
133
  | AppSettingPicker | ✓ | N/A | · | · | · | · | · | · | · | · |
@@ -1,7 +1,7 @@
1
1
  import { useMemo, useState } from "react";
2
2
 
3
3
  import { AppProvider } from "@godxjp/ui/app";
4
- import { Badge, DataTable, type ColumnDef } from "@godxjp/ui/data-display";
4
+ import { Badge, Card, CardContent, DataTable, type ColumnDef } from "@godxjp/ui/data-display";
5
5
  import { Button, Text } from "@godxjp/ui/general";
6
6
  import { Flex, PageContainer } from "@godxjp/ui/layout";
7
7
  import {
@@ -93,6 +93,16 @@ const columns: ColumnDef<Invoice>[] = [
93
93
  },
94
94
  ];
95
95
 
96
+ /* Five 240px columns: wider than the frame at every viewport the gates sweep, and wide for a
97
+ * reason no font can change — the width is declared, so the overflow is deterministic in CI. */
98
+ const wideColumns: ColumnDef<Invoice>[] = [
99
+ { key: "id", header: "請求書番号", width: "240px" },
100
+ { key: "partner", header: "取引先", width: "240px" },
101
+ { key: "amount", header: "金額", align: "right", width: "240px" },
102
+ { key: "status", header: "状態", width: "240px" },
103
+ { key: "date", header: "期限日", width: "240px" },
104
+ ];
105
+
96
106
  export default function Demo() {
97
107
  const [selected, setSelected] = useState<Set<string>>(new Set(["INV-0311"]));
98
108
  const [sort, setSort] = useState<SortStateProp | undefined>({
@@ -117,6 +127,22 @@ export default function Demo() {
117
127
  subtitle="sortable · selectable · bulk actions · row actions · loading · clickable rows · empty state"
118
128
  >
119
129
  <Flex direction="col" gap="lg">
130
+ {/* A table WIDER than its frame, inside a flush Card — the shape a list page uses. It must
131
+ scroll inside its own box with the fade at the inline end, never cut its last columns
132
+ (WCAG 1.4.10). `check:data-table-overflow` measures this section. */}
133
+ <Flex direction="col" gap="sm" id="wide-overflow" className="max-w-3xl">
134
+ <Text weight="medium">枠より広い表(横スクロール · 列を切らない)</Text>
135
+ <Card>
136
+ <CardContent flush>
137
+ <DataTable
138
+ data={invoices}
139
+ columns={wideColumns}
140
+ getRowId={(row) => row.id}
141
+ selectable
142
+ />
143
+ </CardContent>
144
+ </Card>
145
+ </Flex>
120
146
  <Flex direction="col" gap="sm" id="text-action-width">
121
147
  <Text weight="medium">日本語の行アクション</Text>
122
148
  <DataTable
@@ -135,10 +135,7 @@ export default function Demo() {
135
135
  <Flex direction="row" wrap gap="sm">
136
136
  <Popover>
137
137
  <PopoverTrigger asChild>
138
- {/* data-axe-open: opened by check-frame-axe before its overlay scan. */}
139
- <Button data-axe-open variant="outline">
140
- side=top
141
- </Button>
138
+ <Button variant="outline">side=top</Button>
142
139
  </PopoverTrigger>
143
140
  <PopoverContent side="top" aria-label="上側の配置例">
144
141
  <PopoverDescription>上に開きます。</PopoverDescription>
@@ -215,8 +212,9 @@ export default function Demo() {
215
212
  <CardDescription>
216
213
  既定の panel は --popover-width(18rem)。auto は中身に測らせます — 自前の幅を持つもの
217
214
  (Calendar、チャート、固定幅のプレビュー)はこれ以外に正解がありません。trigger
218
- はアンカーに合わせます。className に w-* を書くのは呼び出し側の定数で、どのテーマからも
219
- retune できません。flush が padding 側で避けているのと同じ話です。
215
+ はアンカーに合わせます。className に w-*
216
+ を書くのは呼び出し側の定数で、どのテーマからも retune できません。flush が padding
217
+ 側で避けているのと同じ話です。
220
218
  </CardDescription>
221
219
  </CardHeader>
222
220
  <CardContent>
@@ -14,10 +14,10 @@ import { Button, Text } from "@godxjp/ui/general";
14
14
  import { Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
15
15
 
16
16
  /**
17
- * ScrollArea — custom scrollbar container. ALWAYS set an explicit height/max-height
18
- * (vertical) or width (horizontal) on the wrapper or the scrollbar never appears.
19
- * `type` controls when the bar is visible; `ScrollArea orientation="horizontal"`
20
- * adds a horizontal bar. Composed only from real @godxjp/ui components.
17
+ * ScrollArea — a native scrolling box. ALWAYS give it an explicit height/max-height
18
+ * (vertical) or width (horizontal), or nothing ever overflows and no scrollbar appears.
19
+ * `orientation` decides which axes may scroll; the browser draws the bar, styled from
20
+ * the --scroll-area-* tokens. Composed only from real @godxjp/ui components.
21
21
  */
22
22
  const entries = Array.from(
23
23
  { length: 18 },
@@ -97,7 +97,7 @@ export default function Demo() {
97
97
  return (
98
98
  <PageContainer
99
99
  title="ScrollArea"
100
- subtitle="Custom scrollbar container · needs an explicit height"
100
+ subtitle="Native scrolling, token-styled scrollbar · needs an explicit height"
101
101
  >
102
102
  <Flex direction="col" gap="lg">
103
103
  <Card>
@@ -115,7 +115,6 @@ export default function Demo() {
115
115
  anchor="bottom"
116
116
  viewportRef={streamViewport}
117
117
  onAnchoredChange={setAnchored}
118
- type="always"
119
118
  >
120
119
  <CardContent>
121
120
  <Flex direction="col">
@@ -173,7 +172,7 @@ export default function Demo() {
173
172
  <CardContent>
174
173
  <Card variant="outline" className="h-40 w-full">
175
174
  <CardContent flush>
176
- <ScrollArea anchor="bottom" anchorOffset={0} type="always">
175
+ <ScrollArea anchor="bottom" anchorOffset={0}>
177
176
  <CardContent>
178
177
  <Flex direction="col" gap="xs">
179
178
  {entries.map((e) => (
@@ -215,40 +214,13 @@ export default function Demo() {
215
214
  </CardContent>
216
215
  </Card>
217
216
 
218
- <Card>
219
- <CardHeader>
220
- <CardTitle level={2}>type=&quot;always&quot;(バーを常時表示)</CardTitle>
221
- <CardDescription>
222
- 既定の hover はホバー時のみバーを表示します。always
223
- は内容が溢れる限りバーを常に表示します。
224
- </CardDescription>
225
- </CardHeader>
226
- <CardContent>
227
- <Card variant="outline" className="h-56 w-full">
228
- <CardContent flush>
229
- <ScrollArea type="always">
230
- <CardContent>
231
- <Flex direction="col" gap="xs">
232
- {entries.map((e) => (
233
- <div key={e} className="text-sm tabular-nums">
234
- {e}
235
- </div>
236
- ))}
237
- </Flex>
238
- </CardContent>
239
- </ScrollArea>
240
- </CardContent>
241
- </Card>
242
- </CardContent>
243
- </Card>
244
-
245
217
  <Card>
246
218
  <CardHeader>
247
219
  <CardTitle level={2}>ScrollArea</CardTitle>
248
220
  <CardDescription>orientation="horizontal"</CardDescription>
249
221
  </CardHeader>
250
222
  <CardContent>
251
- <ScrollArea orientation="horizontal" type="always">
223
+ <ScrollArea orientation="horizontal">
252
224
  <ResponsiveGrid flow="columns">
253
225
  {columns.map((column) => (
254
226
  <Card key={column}>
@@ -264,17 +236,51 @@ export default function Demo() {
264
236
 
265
237
  <Card>
266
238
  <CardHeader>
267
- <CardTitle level={2}>個別の ScrollBar を追加する</CardTitle>
239
+ <CardTitle level={2}>ScrollBar は何も描画しない(非推奨)</CardTitle>
240
+ <CardDescription>
241
+ v22 まではこの要素を置くことが軸を開く方法だったが、スクロールがブラウザ本来のものに
242
+ なったので ScrollBar は何も描画しない。下のカードには ScrollBar
243
+ が置いてあるが、横に動くのは orientation=&quot;both&quot; のおかげで、ScrollBar
244
+ のおかげではない。残っている &lt;ScrollBar&gt; は消して、軸は orientation で宣言する。
245
+ </CardDescription>
246
+ </CardHeader>
247
+ <CardContent>
248
+ <Card variant="outline" className="h-24 w-full">
249
+ <CardContent flush>
250
+ <ScrollArea orientation="both">
251
+ <ResponsiveGrid flow="columns">
252
+ {columns.map((column) => (
253
+ <Text key={column}>{column}</Text>
254
+ ))}
255
+ </ResponsiveGrid>
256
+ <ScrollBar orientation="horizontal" />
257
+ </ScrollArea>
258
+ </CardContent>
259
+ </Card>
260
+ </CardContent>
261
+ </Card>
262
+
263
+ <Card>
264
+ <CardHeader>
265
+ <CardTitle level={2}>両方向スクロール(orientation=&quot;both&quot;)</CardTitle>
266
+ <CardDescription>
267
+ 縦にも横にも溢れうる面は orientation=&quot;both&quot;。以前は縦の ScrollArea に
268
+ ScrollBar を足して横軸を開けていたが、スクロールはブラウザ本来のものになったので、
269
+ 軸を決めるのは orientation だけになった。
270
+ </CardDescription>
268
271
  </CardHeader>
269
272
  <CardContent>
270
- <ScrollArea type="always">
271
- <ResponsiveGrid flow="columns">
272
- {columns.map((column) => (
273
- <Text key={column}>{column}</Text>
274
- ))}
275
- </ResponsiveGrid>
276
- <ScrollBar orientation="horizontal" />
277
- </ScrollArea>
273
+ <Card variant="outline" className="h-32 w-full">
274
+ <CardContent flush>
275
+ <ScrollArea orientation="both">
276
+ <ResponsiveGrid flow="columns">
277
+ {columns.map((column) => (
278
+ <Text key={column}>{column}</Text>
279
+ ))}
280
+ </ResponsiveGrid>
281
+ </ScrollArea>
282
+ </CardContent>
283
+ </Card>
278
284
  </CardContent>
279
285
  </Card>
280
286
 
@@ -288,7 +294,7 @@ export default function Demo() {
288
294
  <CardContent>
289
295
  <Card variant="outline" className="h-56 w-full">
290
296
  <CardContent flush>
291
- <ScrollArea type="always">
297
+ <ScrollArea>
292
298
  <CardContent>
293
299
  <Flex direction="col" gap="xs">
294
300
  {shortEntries.map((e) => (
@@ -41,8 +41,7 @@ export default function Demo() {
41
41
  形式に整形され、 解釈できない入力は元の値へ復帰する。
42
42
  </CardDescription>
43
43
  </CardHeader>
44
- {/* data-axe-open: check-frame-axe opens the calendar before scanning. */}
45
- <CardContent data-axe-open>
44
+ <CardContent>
46
45
  <FormField id="issue-date" label="発行日" required>
47
46
  <DatePicker
48
47
  id="issue-date"
@@ -288,7 +287,7 @@ export default function Demo() {
288
287
  ISO 送信される。 開始 &gt; 終了で入力しても order (既定 true) が昇順に正規化する。
289
288
  </CardDescription>
290
289
  </CardHeader>
291
- <CardContent data-axe-open>
290
+ <CardContent>
292
291
  <FormField id="period" label="会計期間" required>
293
292
  <DatePicker
294
293
  range
@@ -38,8 +38,11 @@ export default function Demo() {
38
38
  <CardDescription>6スロット 1グループ · ペースト・矢印キー操作に対応。</CardDescription>
39
39
  </CardHeader>
40
40
  <CardContent>
41
- <FormField id="email-otp" label="認証コード" required>
42
- <InputOTP maxLength={6} value={emailCode} onChange={setEmailCode}>
41
+ {/* この行の id は FIELD ではなく CONTROL 側にある。FormField はその id をそのまま
42
+ 使い、ラベルのクリックもその id を見る(gh#477:以前は FormField 自身の id を
43
+ 見ていたので、両者が食い違った瞬間にラベルがどこも指さなくなった)。 */}
44
+ <FormField label="認証コード" required>
45
+ <InputOTP id="email-otp" maxLength={6} value={emailCode} onChange={setEmailCode}>
43
46
  <InputOTPGroup>
44
47
  <InputOTPSlot index={0} />
45
48
  <InputOTPSlot index={1} />
@@ -1,6 +1,13 @@
1
1
  import { useState } from "react";
2
2
 
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
3
+ import {
4
+ Badge,
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle,
10
+ } from "@godxjp/ui/data-display";
4
11
  import { FormField, Segmented } from "@godxjp/ui/data-entry";
5
12
  import { Text, VisuallyHidden } from "@godxjp/ui/general";
6
13
  import { Flex, PageContainer } from "@godxjp/ui/layout";
@@ -84,6 +91,38 @@ export default function Demo() {
84
91
  </CardContent>
85
92
  </Card>
86
93
 
94
+ <Card id="four-with-counts">
95
+ <CardHeader>
96
+ <CardTitle level={2}>4 択 + 件数(スマホ幅では折り返す)</CardTitle>
97
+ <CardDescription>
98
+ 一覧の上の状態フィルター。4 つが 1 行に収まらない幅では、トラックが 2 行目に折り返し
99
+ ます — ラベルも件数も切り詰めません。`check:segmented-wrap` がこのカードを測ります。
100
+ </CardDescription>
101
+ </CardHeader>
102
+ <CardContent>
103
+ <Segmented
104
+ aria-label="状態"
105
+ defaultValue="all"
106
+ options={[
107
+ { value: "all", label: "すべて", count: 128 },
108
+ { value: "active", label: "実習中", count: 96 },
109
+ { value: "pending", label: "申請中", count: 12 },
110
+ { value: "gone", label: "失踪・帰国", count: 0 },
111
+ ].map(({ value, label, count }) => ({
112
+ value,
113
+ label: (
114
+ <>
115
+ {label}
116
+ <Badge as="span" variant="secondary">
117
+ {count}
118
+ </Badge>
119
+ </>
120
+ ),
121
+ }))}
122
+ />
123
+ </CardContent>
124
+ </Card>
125
+
87
126
  <Card>
88
127
  <CardHeader>
89
128
  <CardTitle level={2}>size · vertical · block</CardTitle>
@@ -142,8 +142,7 @@ export default function Demo() {
142
142
  references. The internal search textbox has its own visible-purpose accessible name.
143
143
  </CardDescription>
144
144
  </CardHeader>
145
- {/* data-axe-open: check-frame-axe opens the searchable panel before scanning. */}
146
- <CardContent data-axe-open>
145
+ <CardContent>
147
146
  <FormField
148
147
  id="searchable-invalid"
149
148
  label="担当拠点"
@@ -21,7 +21,9 @@ import {
21
21
  SelectTrigger,
22
22
  SelectValue,
23
23
  } from "@godxjp/ui/data-entry";
24
+ import { Button } from "@godxjp/ui/general";
24
25
  import { Flex, PageContainer } from "@godxjp/ui/layout";
26
+ import { X } from "lucide-react";
25
27
 
26
28
  /**
27
29
  * Select — the single-select control. Prefer the data-driven `options` API
@@ -38,6 +40,12 @@ export default function Demo() {
38
40
  const [reviewerAsync, setReviewerAsync] = useState("tanaka");
39
41
  const [reviewerCustom, setReviewerCustom] = useState("tanaka");
40
42
  const [reviewerLabel, setReviewerLabel] = useState("tanaka");
43
+ const [prefixed, setPrefixed] = useState("JPY");
44
+ const [tags, setTags] = useState<string[]>(["至急", "社内便"]);
45
+ // antd `labelInValue`: the record arrives carrying its own label, before any option list loads.
46
+ const [assignedTo, setAssignedTo] = useState<
47
+ { value: string; label: React.ReactNode } | undefined
48
+ >({ value: "52", label: "東京本社" });
41
49
 
42
50
  const people = [
43
51
  { value: "tanaka", label: "田中 太郎", sublabel: "tanaka@example.com" },
@@ -82,9 +90,7 @@ export default function Demo() {
82
90
  Pass an options array; name= submits the value with the form.
83
91
  </CardDescription>
84
92
  </CardHeader>
85
- {/* data-axe-open: check-frame-axe opens this Select before scanning, so the
86
- overlay-open a11y state is measured. See docs/FRAME-A11Y-CI.md. */}
87
- <CardContent data-axe-open>
93
+ <CardContent>
88
94
  <FormField id="status" label="状態">
89
95
  <Select
90
96
  id="status"
@@ -392,6 +398,105 @@ export default function Demo() {
392
398
  </FormField>
393
399
  </CardContent>
394
400
  </Card>
401
+ <Card>
402
+ <CardHeader>
403
+ <CardTitle level={2}>
404
+ Ant Design 6 · prefix・suffixIcon・placement・listHeight
405
+ </CardTitle>
406
+ <CardDescription>
407
+ グループは options のネスト({`{ label, options }`})でも、行の group
408
+ でも同じ一覧になります。
409
+ </CardDescription>
410
+ </CardHeader>
411
+ <CardContent>
412
+ <FormField id="prefixed" label="通貨">
413
+ <Select
414
+ id="prefixed"
415
+ name="currency_prefixed"
416
+ value={prefixed}
417
+ onValueChange={setPrefixed}
418
+ prefix={<Badge tone="muted">¥</Badge>}
419
+ placement="bottomEnd"
420
+ listHeight={220}
421
+ options={[
422
+ {
423
+ label: "アジア",
424
+ options: [
425
+ { value: "JPY", label: "日本円" },
426
+ { value: "VND", label: "ベトナムドン" },
427
+ ],
428
+ },
429
+ {
430
+ label: "ヨーロッパ",
431
+ options: [{ value: "EUR", label: "ユーロ" }],
432
+ },
433
+ ]}
434
+ />
435
+ </FormField>
436
+ </CardContent>
437
+ </Card>
438
+
439
+ <Card>
440
+ <CardHeader>
441
+ <CardTitle level={2}>mode=&quot;tags&quot; · tokenSeparators・tagRender</CardTitle>
442
+ <CardDescription>
443
+ 一覧にない値も入力できます。カンマ区切りの貼り付けは一度にまとめて確定します。
444
+ </CardDescription>
445
+ </CardHeader>
446
+ <CardContent>
447
+ <FormField id="tags" label="ラベル">
448
+ <Select
449
+ id="tags"
450
+ name="labels[]"
451
+ mode="tags"
452
+ value={tags}
453
+ onValueChange={setTags}
454
+ tokenSeparators={[",", "\n"]}
455
+ maxTagTextLength={6}
456
+ options={[
457
+ { value: "至急", label: "至急" },
458
+ { value: "要確認", label: "要確認" },
459
+ ]}
460
+ tagRender={({ label, onClose }) => (
461
+ <Badge tone="info">
462
+ {label}
463
+ <Button
464
+ variant="ghost"
465
+ size="icon-xs"
466
+ aria-label={`${String(label)} を外す`}
467
+ onClick={onClose}
468
+ >
469
+ <X aria-hidden="true" />
470
+ </Button>
471
+ </Badge>
472
+ )}
473
+ />
474
+ </FormField>
475
+ </CardContent>
476
+ </Card>
477
+
478
+ <Card>
479
+ <CardHeader>
480
+ <CardTitle level={2}>labelInValue · 読み込み前でも名前が出る</CardTitle>
481
+ <CardDescription>
482
+ 値が {`{ value, label }`} を持つので、選択肢がまだ届いていなくても ID
483
+ ではなく名前を表示できます。
484
+ </CardDescription>
485
+ </CardHeader>
486
+ <CardContent>
487
+ <FormField id="assigned" label="担当拠点">
488
+ <Select
489
+ id="assigned"
490
+ name="office_id"
491
+ labelInValue
492
+ value={assignedTo}
493
+ onValueChange={setAssignedTo}
494
+ notFoundContent="拠点を読み込み中"
495
+ options={[]}
496
+ />
497
+ </FormField>
498
+ </CardContent>
499
+ </Card>
395
500
  </Flex>
396
501
  </PageContainer>
397
502
  );