@godxjp/ui 31.6.0 → 31.8.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 (61) hide show
  1. package/agent/START-HERE.md +4 -4
  2. package/agent/components/Breadcrumb.json +1 -1
  3. package/agent/components/ColorPicker.json +16 -8
  4. package/agent/components/PageContainer.json +1 -1
  5. package/agent/components/Progress.json +12 -1
  6. package/agent/components/Select.json +22 -0
  7. package/agent/components/Text.json +1 -1
  8. package/agent/components.json +53 -12
  9. package/agent/index.json +4 -4
  10. package/agent/llms.txt +5 -5
  11. package/agent/tokens.json +30 -0
  12. package/dist/components/data-display/progress.d.ts +16 -0
  13. package/dist/components/data-display/progress.js +17 -3
  14. package/dist/components/data-entry/chat-composer.js +11 -4
  15. package/dist/components/data-entry/chat-suggestion.js +2 -0
  16. package/dist/components/data-entry/color-picker.d.ts +2 -2
  17. package/dist/components/data-entry/color-picker.js +150 -37
  18. package/dist/components/data-entry/command-palette.js +2 -1
  19. package/dist/components/data-entry/date-picker.js +2 -0
  20. package/dist/components/data-entry/index.d.ts +1 -1
  21. package/dist/components/data-entry/number-input.js +2 -1
  22. package/dist/components/data-entry/search-select.js +55 -17
  23. package/dist/components/data-entry/select.js +1 -1
  24. package/dist/components/data-entry/time-picker.js +3 -0
  25. package/dist/components/feedback/non-modal-layer.js +2 -1
  26. package/dist/components/feedback/tooltip.d.ts +21 -0
  27. package/dist/components/feedback/tooltip.js +101 -0
  28. package/dist/components/general/typography.js +35 -12
  29. package/dist/components/layout/breadcrumb-ellipsis.d.ts +16 -0
  30. package/dist/components/layout/breadcrumb-ellipsis.js +28 -0
  31. package/dist/components/layout/breadcrumb.js +16 -7
  32. package/dist/components/layout/page-container.js +29 -20
  33. package/dist/components/navigation/pagination.js +2 -1
  34. package/dist/components/ui/tag-input.js +2 -1
  35. package/dist/contracts/measurement.json +1 -1
  36. package/dist/i18n/messages/en.json +6 -3
  37. package/dist/i18n/messages/ja.json +6 -3
  38. package/dist/i18n/messages/vi.json +6 -3
  39. package/dist/lib/ime.d.ts +12 -0
  40. package/dist/lib/ime.js +7 -0
  41. package/dist/props/components/data-entry.prop.d.ts +66 -1
  42. package/dist/props/components/index.d.ts +1 -1
  43. package/dist/props/registry.d.ts +20 -0
  44. package/dist/props/registry.js +22 -0
  45. package/dist/props/vocabulary/index.d.ts +1 -1
  46. package/dist/props/vocabulary/navigation.prop.d.ts +14 -0
  47. package/dist/styles/control.css +79 -0
  48. package/dist/styles/layers.json +3 -2
  49. package/dist/styles/layout.css +22 -0
  50. package/dist/styles/table-layout.css +8 -0
  51. package/dist/tokens/components/control.css +8 -0
  52. package/dist/tokens/components/navigation.css +2 -0
  53. package/docs/data-display/progress.tsx +24 -6
  54. package/docs/data-entry/color-picker.tsx +72 -0
  55. package/docs/data-entry/select.tsx +38 -0
  56. package/docs/general/typography.tsx +84 -1
  57. package/docs/i18n/messages/en.json +47 -1
  58. package/docs/i18n/messages/ja.json +47 -1
  59. package/docs/i18n/messages/vi.json +47 -1
  60. package/docs/navigation/breadcrumb.tsx +24 -0
  61. package/package.json +2 -2
@@ -33,6 +33,20 @@ export type BreadcrumbItemProp = {
33
33
  to?: string;
34
34
  /** Ant Design `menu` — a sibling dropdown on this segment. @see BreadcrumbItemMenuProp */
35
35
  menu?: BreadcrumbItemMenuProp;
36
+ /**
37
+ * One line, a long label cut with "…" (gh#1046). antd's Breadcrumb has no per-item ellipsis;
38
+ * the name and shape are antd Typography's `ellipsis`. The full label shows in a tooltip on hover
39
+ * and on keyboard focus of the crumb link — `true` / `{}` use the label, `{ tooltip: node }`
40
+ * replaces it, `{ tooltip: false }` drops it. Applies to link and current crumbs, not to a
41
+ * `menu` segment or an `itemRender` body. The trail then stays on one line; a theme may cap
42
+ * each such crumb with `--breadcrumb-item-max-inline-size`.
43
+ * @see BreadcrumbItemEllipsisProp
44
+ */
45
+ ellipsis?: BreadcrumbItemEllipsisProp;
46
+ };
47
+ /** `BreadcrumbItemProp.ellipsis` — on, or on with an explicit tooltip (gh#1046). */
48
+ export type BreadcrumbItemEllipsisProp = boolean | {
49
+ tooltip?: React.ReactNode;
36
50
  };
37
51
  /** Ordered breadcrumb trail above page title. */
38
52
  export type BreadcrumbProp = BreadcrumbItemProp[];
@@ -738,6 +738,85 @@
738
738
  text-transform: uppercase;
739
739
  }
740
740
 
741
+ .ui-color-picker[data-panel] {
742
+ flex-direction: column;
743
+ align-items: stretch;
744
+ }
745
+
746
+ .ui-color-picker-picker {
747
+ display: flex;
748
+ align-items: center;
749
+ gap: var(--control-gap);
750
+ }
751
+
752
+ .ui-color-picker-presets {
753
+ display: flex;
754
+ flex-direction: column;
755
+ gap: var(--color-picker-presets-gap);
756
+ }
757
+
758
+ .ui-color-picker-presets-trigger {
759
+ display: inline-flex;
760
+ align-items: center;
761
+ gap: var(--space-1);
762
+ min-block-size: var(--touch-target-min);
763
+ color: hsl(var(--muted-foreground));
764
+ font-size: var(--color-picker-presets-font-size);
765
+ text-align: start;
766
+ }
767
+
768
+ .ui-color-picker-presets-chevron {
769
+ width: var(--control-icon-size);
770
+ height: var(--control-icon-size);
771
+ flex-shrink: 0;
772
+ transition: rotate 150ms;
773
+ }
774
+
775
+ .ui-color-picker-presets-trigger[data-state="closed"] .ui-color-picker-presets-chevron {
776
+ rotate: -90deg;
777
+ }
778
+
779
+ .ui-color-picker-presets-trigger[data-state="closed"]:dir(rtl) .ui-color-picker-presets-chevron {
780
+ rotate: 90deg;
781
+ }
782
+
783
+ .ui-color-picker-presets-items {
784
+ display: flex;
785
+ flex-wrap: wrap;
786
+ gap: var(--color-picker-preset-gap);
787
+ padding-block: var(--space-1);
788
+ }
789
+
790
+ .ui-color-picker-presets-color {
791
+ display: inline-block;
792
+ flex-shrink: 0;
793
+ inline-size: var(--color-picker-preset-size);
794
+ block-size: var(--color-picker-preset-size);
795
+ border: var(--stroke-hairline) solid hsl(var(--input) / var(--input-alpha, 100%));
796
+ border-radius: var(--control-radius);
797
+ background-color: var(--color-picker-preset-color, transparent);
798
+ cursor: pointer;
799
+ forced-color-adjust: none;
800
+ }
801
+
802
+ .ui-color-picker-presets-color[data-selected] {
803
+ box-shadow:
804
+ 0 0 0 var(--stroke-md) hsl(var(--background)),
805
+ 0 0 0 calc(var(--stroke-md) * 2) hsl(var(--foreground));
806
+ }
807
+
808
+ .ui-color-picker-presets-color[data-disabled] {
809
+ cursor: not-allowed;
810
+ opacity: var(--disabled-opacity);
811
+ }
812
+
813
+ .ui-color-picker-presets-empty {
814
+ display: block;
815
+ padding-block: var(--space-1);
816
+ color: hsl(var(--muted-foreground));
817
+ font-size: var(--color-picker-presets-font-size);
818
+ }
819
+
741
820
  .ui-query-refetch-icon[data-fetching="true"] {
742
821
  animation: spin 1s linear infinite;
743
822
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.6.0",
2
+ "version": "31.8.0",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -702,7 +702,8 @@
702
702
  },
703
703
  "PageContainer": {
704
704
  "layers": [
705
- "layout.css"
705
+ "layout.css",
706
+ "dialog-layout.css"
706
707
  ]
707
708
  },
708
709
  "Pagination": {
@@ -1036,6 +1036,28 @@
1036
1036
  color: hsl(var(--foreground));
1037
1037
  }
1038
1038
 
1039
+ .ui-breadcrumb-list[data-ellipsis] {
1040
+ flex-wrap: nowrap;
1041
+ }
1042
+ .ui-breadcrumb-list[data-ellipsis] > .ui-breadcrumb-item:not([data-ellipsis]) {
1043
+ flex-shrink: 0;
1044
+ white-space: nowrap;
1045
+ }
1046
+ .ui-breadcrumb-item[data-ellipsis] {
1047
+ min-inline-size: 0;
1048
+ max-inline-size: var(--breadcrumb-item-max-inline-size);
1049
+ }
1050
+ .ui-breadcrumb-item[data-ellipsis] > svg,
1051
+ .ui-breadcrumb-item[data-ellipsis] > .ui-breadcrumb-separator {
1052
+ flex-shrink: 0;
1053
+ }
1054
+ .ui-breadcrumb-ellipsis {
1055
+ min-inline-size: 0;
1056
+ overflow: hidden;
1057
+ text-overflow: ellipsis;
1058
+ white-space: nowrap;
1059
+ }
1060
+
1039
1061
  .ui-empty-state {
1040
1062
  display: flex;
1041
1063
  flex-direction: column;
@@ -212,6 +212,9 @@
212
212
  font: inherit;
213
213
  cursor: pointer;
214
214
  outline: none;
215
+
216
+ max-inline-size: 100%;
217
+ min-inline-size: 0;
215
218
  }
216
219
 
217
220
  .ui-data-table-sort-button::after {
@@ -334,6 +337,11 @@
334
337
  display: inline-flex;
335
338
  align-items: center;
336
339
  gap: var(--space-inline-xs);
340
+ min-inline-size: 0;
341
+ max-inline-size: 100%;
342
+ }
343
+ .ui-data-table-sort-label > .ui-data-table-sort-icon {
344
+ flex-shrink: 0;
337
345
  }
338
346
 
339
347
  :is(th, td).text-center > .ui-data-table-sort-button > .ui-data-table-sort-label {
@@ -157,6 +157,10 @@
157
157
 
158
158
  --color-picker-input-width: 6.5rem;
159
159
 
160
+ --color-picker-preset-size: var(--touch-target-min);
161
+ --color-picker-preset-gap: var(--space-2);
162
+ --color-picker-presets-gap: var(--space-1);
163
+
160
164
  --command-list-max-height: min(300px, 50vh);
161
165
  --command-input-padding-x: var(--space-3);
162
166
  --command-group-padding: var(--space-1);
@@ -192,6 +196,10 @@
192
196
  --font-size-xs,
193
197
  calc(var(--font-size-base) / var(--font-size-ratio))
194
198
  );
199
+ --color-picker-presets-font-size: var(
200
+ --font-size-xs,
201
+ calc(var(--font-size-base) / var(--font-size-ratio))
202
+ );
195
203
  --command-group-heading-font-size: var(
196
204
  --font-size-xs,
197
205
  calc(var(--font-size-base) / var(--font-size-ratio))
@@ -46,6 +46,8 @@
46
46
  --breadcrumb-font-size: var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)));
47
47
 
48
48
  --breadcrumb-menu-trigger-gap: var(--space-inline-xs);
49
+
50
+ --breadcrumb-item-max-inline-size: none;
49
51
  --menu-shortcut-font-size: var(
50
52
  --font-size-xs,
51
53
  calc(var(--font-size-base) / var(--font-size-ratio))
@@ -6,6 +6,7 @@ import {
6
6
  CardTitle,
7
7
  Progress,
8
8
  } from "@godxjp/ui/data-display";
9
+ import { useTranslation } from "@godxjp/ui/i18n";
9
10
  import { Flex, PageContainer } from "@godxjp/ui/layout";
10
11
 
11
12
  /**
@@ -13,6 +14,7 @@ import { Flex, PageContainer } from "@godxjp/ui/layout";
13
14
  * Composed only from real @godxjp/ui components.
14
15
  */
15
16
  export default function Demo() {
17
+ const { t, locale } = useTranslation();
16
18
  return (
17
19
  <PageContainer title="Progress" subtitle="0–100 bar · optional label + semantic tone">
18
20
  <Flex direction="col" gap="lg">
@@ -37,11 +39,10 @@ export default function Demo() {
37
39
  <CardHeader>
38
40
  <CardTitle level={2}>shape=&quot;ring&quot;</CardTitle>
39
41
  <CardDescription>
40
- 同じメーターを弧で描きます。バーと同じ value / tone / size / ARIA
41
- のまま、label がリングの内側に入ります。狭いアプリバーで「18 / 42
42
- 件」を見出しの横に出したいとき、バー+キャプションの 2
43
- 段ではなく 1 マスで済むのが理由です。数値が重要なときではなく、
44
- 場所が正方形のときに選びます。
42
+ 同じメーターを弧で描きます。バーと同じ value / tone / size / ARIA のまま、label
43
+ がリングの内側に入ります。狭いアプリバーで「18 / 42
44
+ 件」を見出しの横に出したいとき、バー+キャプションの 2 段ではなく 1
45
+ マスで済むのが理由です。数値が重要なときではなく、 場所が正方形のときに選びます。
45
46
  </CardDescription>
46
47
  </CardHeader>
47
48
  <CardContent>
@@ -68,7 +69,10 @@ export default function Demo() {
68
69
  1 つの合計を状態ごとに分割します。割合ではなく実数を渡すと、各スライスの比率は
69
70
  コンポーネントが計算します。メーターより背の高いトラックになるのは、0.5rem の pill
70
71
  上に 3 色を並べると比率が読めなくなるためです。role=&quot;img&quot;
71
- として、全スライスを 1 つの名前で読み上げます。
72
+ として、全スライスを 1 つの名前で読み上げます。total を渡すと全体は合計ではなく total
73
+ になり、残りはトラックのまま「残り」として読み上げます(antd の percent +
74
+ success.percent と同じく、未塗りのトラックが残り。antd の全体は常に 100
75
+ ですが、ここは実数の単位で total を指定します)。
72
76
  </CardDescription>
73
77
  </CardHeader>
74
78
  <CardContent>
@@ -89,6 +93,20 @@ export default function Demo() {
89
93
  ]}
90
94
  label="みどり農産株式会社(0 件のスライスは幅 0)"
91
95
  />
96
+ {/* total を渡すと、スライスは合計ではなく total に対して描かれ、残りはトラックのまま。
97
+ 読み上げには残り(remainderLabel)が Intl 形式の数値で入ります。 */}
98
+ <Progress
99
+ segments={[
100
+ { value: 3, tone: "success", label: t("progressDocs.testRun.passed") },
101
+ { value: 1, tone: "destructive", label: t("progressDocs.testRun.failed") },
102
+ { value: 2, tone: "warning", label: t("progressDocs.testRun.blocked") },
103
+ ]}
104
+ total={400}
105
+ remainderLabel={t("progressDocs.testRun.notRun")}
106
+ label={t("progressDocs.testRun.caption", {
107
+ total: new Intl.NumberFormat(locale).format(400),
108
+ })}
109
+ />
92
110
  </Flex>
93
111
  </CardContent>
94
112
  </Card>
@@ -3,19 +3,33 @@ import { useState } from "react";
3
3
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
4
4
  import { ColorPicker, FormField } from "@godxjp/ui/data-entry";
5
5
  import { Text } from "@godxjp/ui/general";
6
+ import { useTranslation } from "@godxjp/ui/i18n";
6
7
  import { Flex, PageContainer } from "@godxjp/ui/layout";
7
8
 
9
+ /** A fixed tag palette (gh#1055): tags pick from it, never from free hex. */
10
+ const TAG_PALETTE = ["#dc2626", "#ea580c", "#ca8a04", "#16a34a", "#0891b2", "#2563eb", "#9333ea"];
11
+
8
12
  /**
9
13
  * ColorPicker — hex color swatch + optional editable hex input.
10
14
  * Always initialize state to a valid 3- or 6-digit hex string.
11
15
  * Use controlled mode (value + onValueChange); no uncontrolled path.
12
16
  * Composed only from real @godxjp/ui components.
17
+ *
18
+ * `presets` / `panelRender` port antd ColorPicker (gh#1055). Deviations from antd, on purpose:
19
+ * - `colors` are hex strings only — the value is hex; antd also takes colour objects/gradients.
20
+ * - There is no popover: the picker is inline, so `panelRender` replaces the inline body. The
21
+ * presets-only recipe is antd's own: `panelRender={(_, { components: { Presets } }) => <Presets />}`.
22
+ * - Each swatch is a real radio (APG radio group, named by its hex, arrow keys select); antd's
23
+ * preset blocks are click-only divs.
13
24
  */
14
25
  export default function Demo() {
15
26
  const [brandColor, setBrandColor] = useState("#2563eb");
16
27
  const [accentColor, setAccentColor] = useState("#16a34a");
17
28
  const [categoryColor, setCategoryColor] = useState("#dc2626");
18
29
  const [validatedColor, setValidatedColor] = useState("#9333ea");
30
+ const [labelColor, setLabelColor] = useState("#16a34a");
31
+ const [tagColor, setTagColor] = useState("#2563eb");
32
+ const { t } = useTranslation();
19
33
 
20
34
  return (
21
35
  <PageContainer title="ColorPicker" subtitle="カラーピッカー · スウォッチ+Hex入力">
@@ -75,6 +89,64 @@ export default function Demo() {
75
89
  </CardContent>
76
90
  </Card>
77
91
 
92
+ <Card>
93
+ <CardHeader>
94
+ <CardTitle level={2}>{t("showcase.colorPicker.presetsTitle")}</CardTitle>
95
+ <CardDescription>{t("showcase.colorPicker.presetsDescription")}</CardDescription>
96
+ </CardHeader>
97
+ <CardContent>
98
+ <FormField id="label-color" label={t("showcase.colorPicker.paletteLabel")}>
99
+ <ColorPicker
100
+ id="label-color"
101
+ value={labelColor}
102
+ onValueChange={setLabelColor}
103
+ presets={[
104
+ {
105
+ key: "recommended",
106
+ label: t("showcase.colorPicker.recommendedGroup"),
107
+ colors: TAG_PALETTE,
108
+ },
109
+ {
110
+ key: "recent",
111
+ label: t("showcase.colorPicker.recentGroup"),
112
+ colors: ["#2563eb", "#16a34a"],
113
+ defaultOpen: false,
114
+ },
115
+ ]}
116
+ />
117
+ </FormField>
118
+ </CardContent>
119
+ </Card>
120
+
121
+ <Card>
122
+ <CardHeader>
123
+ <CardTitle level={2}>{t("showcase.colorPicker.presetsOnlyTitle")}</CardTitle>
124
+ <CardDescription>{t("showcase.colorPicker.presetsOnlyDescription")}</CardDescription>
125
+ </CardHeader>
126
+ <CardContent>
127
+ <FormField
128
+ id="tag-color"
129
+ label={t("showcase.colorPicker.tagColorLabel")}
130
+ helper={t("showcase.colorPicker.tagColorHelper")}
131
+ >
132
+ <ColorPicker
133
+ id="tag-color"
134
+ name="tag_color"
135
+ value={tagColor}
136
+ onValueChange={setTagColor}
137
+ presets={[
138
+ {
139
+ key: "tags",
140
+ label: t("showcase.colorPicker.recommendedGroup"),
141
+ colors: TAG_PALETTE,
142
+ },
143
+ ]}
144
+ panelRender={(_, { components: { Presets } }) => <Presets />}
145
+ />
146
+ </FormField>
147
+ </CardContent>
148
+ </Card>
149
+
78
150
  <Card>
79
151
  <CardHeader>
80
152
  <CardTitle level={2}>Hex バリデーションと確定動作</CardTitle>
@@ -23,6 +23,7 @@ import {
23
23
  SelectValue,
24
24
  } from "@godxjp/ui/data-entry";
25
25
  import { Button } from "@godxjp/ui/general";
26
+ import { useTranslation } from "@godxjp/ui/i18n";
26
27
  import { Flex, PageContainer } from "@godxjp/ui/layout";
27
28
  import { X } from "lucide-react";
28
29
 
@@ -32,6 +33,7 @@ import { X } from "lucide-react";
32
33
  * trigger. Never a raw <select>. Composed only from real @godxjp/ui components.
33
34
  */
34
35
  export default function Demo() {
36
+ const { t, locale } = useTranslation();
35
37
  const [status, setStatus] = useState("paid");
36
38
  const [currency, setCurrency] = useState("JPY");
37
39
  const [priority, setPriority] = useState("medium");
@@ -43,6 +45,8 @@ export default function Demo() {
43
45
  const [reviewerLabel, setReviewerLabel] = useState("tanaka");
44
46
  const [prefixed, setPrefixed] = useState("JPY");
45
47
  const [tags, setTags] = useState<string[]>(["至急", "社内便"]);
48
+ const [labels, setLabels] = useState<string[]>([]);
49
+ const [created, setCreated] = useState<string[]>([]);
46
50
  // antd `labelInValue`: the record arrives carrying its own label, before any option list loads.
47
51
  const [assignedTo, setAssignedTo] = useState<
48
52
  { value: string; label: React.ReactNode } | undefined
@@ -481,6 +485,40 @@ export default function Demo() {
481
485
  </CardContent>
482
486
  </Card>
483
487
 
488
+ <Card>
489
+ <CardHeader>
490
+ <CardTitle level={2}>{t("showcase.select.createTitle")}</CardTitle>
491
+ <CardDescription>{t("showcase.select.createDescription")}</CardDescription>
492
+ </CardHeader>
493
+ <CardContent>
494
+ <FormField
495
+ id="issue-labels"
496
+ label={t("showcase.select.createField")}
497
+ helper={
498
+ created.length
499
+ ? t("showcase.select.createdHelper", {
500
+ list: new Intl.ListFormat(locale).format(created),
501
+ })
502
+ : t("showcase.select.createdNone")
503
+ }
504
+ >
505
+ <Select
506
+ id="issue-labels"
507
+ mode="tags"
508
+ value={labels}
509
+ onValueChange={setLabels}
510
+ onCreate={(text) => setCreated((previous) => [...previous, text])}
511
+ allowCreate={(text) => text.length <= 12}
512
+ options={[
513
+ { value: "bug", label: "Bug" },
514
+ { value: "feature", label: "Feature" },
515
+ { value: "docs", label: "Docs" },
516
+ ]}
517
+ />
518
+ </FormField>
519
+ </CardContent>
520
+ </Card>
521
+
484
522
  <Card>
485
523
  <CardHeader>
486
524
  <CardTitle level={2}>labelInValue · 読み込み前でも名前が出る</CardTitle>
@@ -21,8 +21,10 @@ import {
21
21
  TableHead,
22
22
  TableHeader,
23
23
  TableRow,
24
+ DataTable,
25
+ Tree,
24
26
  } from "@godxjp/ui/data-display";
25
- import { Flex, PageContainer } from "@godxjp/ui/layout";
27
+ import { Breadcrumb, Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
26
28
 
27
29
  /**
28
30
  * Text + Heading — the typographic primitives. Use these INSTEAD of a hand-rolled
@@ -46,8 +48,15 @@ const headingTones = [
46
48
  { tone: "info", title: "システムメンテナンスのお知らせ" },
47
49
  ] as const;
48
50
 
51
+ type SuiteRow = { id: string; title: string };
52
+
49
53
  export default function Demo() {
50
54
  const { t } = useTranslation();
55
+ const longTitle = t("textExamples.ellipsisLongTitle");
56
+ const suiteRows: SuiteRow[] = [
57
+ { id: "TS-0412", title: longTitle },
58
+ { id: "TS-0413", title: t("textExamples.ellipsisShortTitle") },
59
+ ];
51
60
  return (
52
61
  <PageContainer
53
62
  title="Typography"
@@ -388,6 +397,80 @@ export default function Demo() {
388
397
  </Flex>
389
398
  </CardContent>
390
399
  </Card>
400
+ <Card>
401
+ <CardHeader>
402
+ <CardTitle level={2}>{t("textExamples.ellipsisTitle")}</CardTitle>
403
+ <CardDescription>{t("textExamples.ellipsisDescription")}</CardDescription>
404
+ </CardHeader>
405
+ <CardContent>
406
+ <Flex direction="col" gap="md">
407
+ <ResponsiveGrid columns={{ sm: 2, lg: 3 }}>
408
+ <Flex direction="col" gap="xs">
409
+ <Text size="xs" tone="muted">
410
+ {t("textExamples.ellipsisPlain")}
411
+ </Text>
412
+ <Text ellipsis={{ tooltip: true }}>{longTitle}</Text>
413
+ </Flex>
414
+ <Flex direction="col" gap="xs">
415
+ <Text size="xs" tone="muted">
416
+ {t("textExamples.ellipsisLink")}
417
+ </Text>
418
+ <Text asChild link ellipsis={{ tooltip: true }}>
419
+ <a href="#typography">{longTitle}</a>
420
+ </Text>
421
+ </Flex>
422
+ <Flex direction="col" gap="xs">
423
+ <Text size="xs" tone="muted">
424
+ {t("textExamples.ellipsisTree")}
425
+ </Text>
426
+ <Tree
427
+ aria-label={t("textExamples.ellipsisTree")}
428
+ treeData={[
429
+ {
430
+ value: "suite",
431
+ label: longTitle,
432
+ children: [
433
+ { value: "case-1", label: t("textExamples.ellipsisShortTitle") },
434
+ ],
435
+ },
436
+ ]}
437
+ defaultExpandedValues={["suite"]}
438
+ titleRender={(node) => <Text ellipsis={{ tooltip: true }}>{node.label}</Text>}
439
+ />
440
+ </Flex>
441
+ </ResponsiveGrid>
442
+ <DataTable<SuiteRow>
443
+ label={t("textExamples.ellipsisTable")}
444
+ data={suiteRows}
445
+ getRowId={(row) => row.id}
446
+ columns={[
447
+ { key: "id", header: "ID", width: "7rem", render: (row) => row.id },
448
+ {
449
+ key: "title",
450
+ header: (
451
+ <Text ellipsis={{ tooltip: true }}>
452
+ {t("textExamples.ellipsisLongHeader")}
453
+ </Text>
454
+ ),
455
+ width: "12rem",
456
+ ellipsis: true,
457
+ sortable: true,
458
+ sorter: (a, b) => a.title.localeCompare(b.title),
459
+ render: (row) => <Text ellipsis={{ tooltip: true }}>{row.title}</Text>,
460
+ },
461
+ ]}
462
+ />
463
+ <Breadcrumb
464
+ ariaLabel={t("textExamples.ellipsisBreadcrumb")}
465
+ items={[
466
+ { label: t("textExamples.ellipsisCrumbHome"), to: "#typography" },
467
+ { label: longTitle, to: "#typography", ellipsis: true },
468
+ { label: longTitle, ellipsis: true },
469
+ ]}
470
+ />
471
+ </Flex>
472
+ </CardContent>
473
+ </Card>
391
474
  <Card>
392
475
  <CardHeader>
393
476
  <CardTitle level={2}>copyable · editable · 装飾</CardTitle>
@@ -6,7 +6,18 @@
6
6
  "breakDescription": "An identifier with nowhere to break — an email address, a coupon code, a login ID — takes break=\"anywhere\", and the column stays inside the viewport even in a table cell. whitespace=\"pre-wrap\" is for newlines a person typed; it cannot wrap an identifier in a table cell.",
7
7
  "breakColumnOwner": "Owner",
8
8
  "breakColumnEmail": "Email address",
9
- "breakSampleName": "Sato"
9
+ "breakSampleName": "Sato",
10
+ "ellipsisTitle": "ellipsis · the full text in a tooltip while it is cut",
11
+ "ellipsisDescription": "ellipsis={{ tooltip: true }} keeps one line and shows the whole text on hover and on keyboard focus of the control it sits in (the link, the tree item, the sort button). A title that fits shows no tooltip, and the Text never becomes a tab stop of its own.",
12
+ "ellipsisPlain": "Plain Text",
13
+ "ellipsisLink": "Text asChild · link",
14
+ "ellipsisTree": "Tree titleRender",
15
+ "ellipsisTable": "Test suites",
16
+ "ellipsisBreadcrumb": "Truncated breadcrumb",
17
+ "ellipsisCrumbHome": "Home",
18
+ "ellipsisLongTitle": "Q3 integration test · payment module · invoice re-issue scenarios and acceptance criteria review",
19
+ "ellipsisShortTitle": "Login smoke test",
20
+ "ellipsisLongHeader": "Latest run name (release candidate 2026-09)"
10
21
  },
11
22
  "themeShowcase": {
12
23
  "page": {
@@ -532,6 +543,24 @@
532
543
  "longIdTitle": "Long identifiers and +N",
533
544
  "ledgerLabel": "Ledger ID",
534
545
  "ledgerHelper": "A 71-character identifier still stays inside the control"
546
+ },
547
+ "select": {
548
+ "createTitle": "Tags: create row, onCreate and case-insensitive matching",
549
+ "createDescription": "Typed text that matches no option is offered as an explicit “Create” row. onCreate reports only invented text, never an option id. Typing “bug” finds “Bug” instead of creating a duplicate (caseSensitive restores exact matching). Unlike antd, the create row is labelled and text longer than 12 characters is refused through allowCreate.",
550
+ "createField": "Labels",
551
+ "createdHelper": "Created in this session: {list}",
552
+ "createdNone": "Nothing created yet"
553
+ },
554
+ "colorPicker": {
555
+ "presetsTitle": "Tag colour from a palette",
556
+ "presetsDescription": "presets — fixed swatch groups under the picker; each group collapses, and the free picker stays available.",
557
+ "paletteLabel": "Label colour",
558
+ "recommendedGroup": "Recommended",
559
+ "recentGroup": "Recently used",
560
+ "presetsOnlyTitle": "Palette only",
561
+ "presetsOnlyDescription": "panelRender returns only Presets — people pick from the palette and cannot type an arbitrary hex.",
562
+ "tagColorLabel": "Tag colour",
563
+ "tagColorHelper": "Arrow keys move between swatches; the selected one is announced."
535
564
  }
536
565
  },
537
566
  "serviceLauncherShowcase": {
@@ -1469,5 +1498,22 @@
1469
1498
  "other": "Load {count} more"
1470
1499
  }
1471
1500
  }
1501
+ },
1502
+ "progressDocs": {
1503
+ "testRun": {
1504
+ "passed": "Passed",
1505
+ "failed": "Failed",
1506
+ "blocked": "Blocked",
1507
+ "notRun": "Not run",
1508
+ "caption": "Regression suite — {total} cases"
1509
+ }
1510
+ },
1511
+ "breadcrumbExamples": {
1512
+ "ellipsisTitle": "ellipsis · a long segment on one line",
1513
+ "ellipsisDescription": "ellipsis on a segment keeps the trail on one line and cuts that label with \"…\"; the full label shows in a tooltip on hover and when the crumb link takes keyboard focus. antd Breadcrumb has no per-item ellipsis — the shape is Typography’s. PageContainer breadcrumb takes the same item field.",
1514
+ "ellipsisAria": "Truncated breadcrumb",
1515
+ "home": "Home",
1516
+ "longSuite": "Q3 integration test · payment module · invoice re-issue scenarios",
1517
+ "longSection": "Acceptance criteria review for the release candidate of September 2026"
1472
1518
  }
1473
1519
  }