@godxjp/ui 31.17.0 → 31.17.1

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.
@@ -3,7 +3,7 @@
3
3
  You are about to write code against a design system you did not author. This file is the whole
4
4
  contract. Read it before you write JSX.
5
5
 
6
- **This catalog describes `@godxjp/ui` 31.17.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 31.17.1.** If the project you are editing has a different
7
7
  version in its `package.json`, read the pinned catalog for THAT version instead
8
8
  (`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
9
9
  not exist yet; older, and it hides props that do. Neither failure announces itself.
@@ -49,7 +49,7 @@
49
49
  "type": "React.ComponentType<{ className?: string }> | null"
50
50
  },
51
51
  {
52
- "description": "Badge label. When omitted with status, Badge renders the translated lifecycle label or raw status. LONG LABEL CONTRACT (gh#1101): the chip is `max-inline-size: 100%` of its container and never wraps; the label ellipsizes and, when it was cut, shows the full text as a native `title` on hover (antd `Typography ellipsis`). A caller's own `title` on the Badge wins. So a Badge in a fixed-width DataTable cell or a narrow Card stays inside it with no extra props; `<Badge><Text ellipsis>` also works. Do not reach for `Text break=\"anywhere\"` to make a chip wrap.",
52
+ "description": "Badge label. When omitted with status, Badge renders the translated lifecycle label or raw status. LONG LABEL CONTRACT (gh#1101): the chip is `max-inline-size: 100%` of its container and never wraps; the label ellipsizes and, when it was cut, opens the full text in the same tooltip as `Text ellipsis={{ tooltip: true }}` — on pointer hover of the label and on keyboard focus of the nearest focusable control around the chip (a row link, a treeitem, a cell button), only while it is actually cut; the chip never becomes a tab stop (gh#1105). A caller's own `title` on the Badge turns the tooltip off. So a Badge in a fixed-width DataTable cell or a narrow Card stays inside it with no extra props; `<Badge><Text ellipsis>` also works. Do not reach for `Text break=\"anywhere\"` to make a chip wrap.",
53
53
  "name": "children",
54
54
  "type": "ReactNode"
55
55
  },
@@ -74,7 +74,7 @@
74
74
  "subParts": [
75
75
  "StatusBadge"
76
76
  ],
77
- "tagline": "Plain or lifecycle badge. Use `variant` for static chips, or `status` to auto-map lifecycle keys to semantic tone + icon. Labels never wrap: a label wider than its container ellipsizes, with the full text as a hover `title` (gh#1101).",
77
+ "tagline": "Plain or lifecycle badge. Use `variant` for static chips, or `status` to auto-map lifecycle keys to semantic tone + icon. Labels never wrap: a label wider than its container ellipsizes, and its full text opens in a tooltip on hover or on keyboard focus of the control around the chip (gh#1101, gh#1105).",
78
78
  "usage": [
79
79
  "DO pick the correct variant semantically: `success` (approved/paid), `warning` (pending/overdue), `destructive` (rejected/error), `secondary` (neutral category), `outline` (subtle label), `default` (primary accent). Never force a colour just for aesthetics — agents and screen readers read the variant as intent.",
80
80
  "DO use `status` for entity lifecycle statuses (active, draft, pending, cancelled, failed, scheduled, etc.) so the component resolves the correct tone, icon, and i18n label.",
@@ -239,7 +239,7 @@
239
239
  {
240
240
  "group": "data-display",
241
241
  "name": "Badge",
242
- "tagline": "Plain or lifecycle badge. Use `variant` for static chips, or `status` to auto-map lifecycle keys to semantic tone + icon. Labels never wrap: a label wider than its container ellipsizes, with the full text as a hover `title` (gh#1101)."
242
+ "tagline": "Plain or lifecycle badge. Use `variant` for static chips, or `status` to auto-map lifecycle keys to semantic tone + icon. Labels never wrap: a label wider than its container ellipsizes, and its full text opens in a tooltip on hover or on keyboard focus of the control around the chip (gh#1101, gh#1105)."
243
243
  },
244
244
  {
245
245
  "group": "data-display",
@@ -4081,7 +4081,7 @@
4081
4081
  "type": "React.ComponentType<{ className?: string }> | null"
4082
4082
  },
4083
4083
  {
4084
- "description": "Badge label. When omitted with status, Badge renders the translated lifecycle label or raw status. LONG LABEL CONTRACT (gh#1101): the chip is `max-inline-size: 100%` of its container and never wraps; the label ellipsizes and, when it was cut, shows the full text as a native `title` on hover (antd `Typography ellipsis`). A caller's own `title` on the Badge wins. So a Badge in a fixed-width DataTable cell or a narrow Card stays inside it with no extra props; `<Badge><Text ellipsis>` also works. Do not reach for `Text break=\"anywhere\"` to make a chip wrap.",
4084
+ "description": "Badge label. When omitted with status, Badge renders the translated lifecycle label or raw status. LONG LABEL CONTRACT (gh#1101): the chip is `max-inline-size: 100%` of its container and never wraps; the label ellipsizes and, when it was cut, opens the full text in the same tooltip as `Text ellipsis={{ tooltip: true }}` — on pointer hover of the label and on keyboard focus of the nearest focusable control around the chip (a row link, a treeitem, a cell button), only while it is actually cut; the chip never becomes a tab stop (gh#1105). A caller's own `title` on the Badge turns the tooltip off. So a Badge in a fixed-width DataTable cell or a narrow Card stays inside it with no extra props; `<Badge><Text ellipsis>` also works. Do not reach for `Text break=\"anywhere\"` to make a chip wrap.",
4085
4085
  "name": "children",
4086
4086
  "type": "ReactNode"
4087
4087
  },
@@ -4106,7 +4106,7 @@
4106
4106
  "subParts": [
4107
4107
  "StatusBadge"
4108
4108
  ],
4109
- "tagline": "Plain or lifecycle badge. Use `variant` for static chips, or `status` to auto-map lifecycle keys to semantic tone + icon. Labels never wrap: a label wider than its container ellipsizes, with the full text as a hover `title` (gh#1101).",
4109
+ "tagline": "Plain or lifecycle badge. Use `variant` for static chips, or `status` to auto-map lifecycle keys to semantic tone + icon. Labels never wrap: a label wider than its container ellipsizes, and its full text opens in a tooltip on hover or on keyboard focus of the control around the chip (gh#1101, gh#1105).",
4110
4110
  "usage": [
4111
4111
  "DO pick the correct variant semantically: `success` (approved/paid), `warning` (pending/overdue), `destructive` (rejected/error), `secondary` (neutral category), `outline` (subtle label), `default` (primary accent). Never force a colour just for aesthetics — agents and screen readers read the variant as intent.",
4112
4112
  "DO use `status` for entity lifecycle statuses (active, draft, pending, cancelled, failed, scheduled, etc.) so the component resolves the correct tone, icon, and i18n label.",
package/agent/index.json CHANGED
@@ -48,7 +48,7 @@
48
48
  "note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
49
49
  "read": {
50
50
  "live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
51
- "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.17.0/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.17.1/agent/index.json"
52
52
  },
53
53
  "source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
54
54
  "start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
@@ -62,5 +62,5 @@
62
62
  "foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
63
63
  "semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
64
64
  },
65
- "version": "31.17.0"
65
+ "version": "31.17.1"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
3
  > A Japanese-enterprise React design system: 179 components, 2129 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.17.0.
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.17.1.
5
5
 
6
6
  If your client can run a process, do not read these files — run the MCP server instead
7
- (`npx @godxjp/ui-mcp@31.17.0`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.17.1`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -26,7 +26,7 @@ that can only fetch URLs.
26
26
  ## Pinning
27
27
 
28
28
  Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
29
- the tag: `.../godx-jp/godxjp-ui/v31.17.0/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.17.1/agent/...`. A catalog that does not match the installed
30
30
  package describes props that are absent, or hides props that are present, and says nothing either way.
31
31
 
32
32
  Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cva } from "class-variance-authority";
5
5
  import {
@@ -14,6 +14,7 @@ import {
14
14
  XCircle
15
15
  } from "lucide-react";
16
16
  import { useTranslation } from "../../i18n/use-translation.js";
17
+ import { EllipsisTooltip } from "../feedback/tooltip.js";
17
18
  import { cn } from "../../lib/utils.js";
18
19
  import {
19
20
  toneDestructiveClass,
@@ -105,14 +106,6 @@ const badgeToneClass = {
105
106
  muted: cn("border-transparent", toneMutedClass),
106
107
  neutral: cn("border-transparent", toneNeutralClass)
107
108
  };
108
- function titleTruncatedLabel(event) {
109
- const label = event.currentTarget;
110
- if (label.scrollWidth > label.clientWidth) {
111
- label.title = label.textContent?.replace(/\s+/g, " ").trim() ?? "";
112
- } else {
113
- label.removeAttribute("title");
114
- }
115
- }
116
109
  function Badge({
117
110
  as: Element = "div",
118
111
  className,
@@ -148,58 +141,54 @@ function Badge({
148
141
  const removeLabel = stringLabel ?? (deriveFromDom ? renderedLabel : void 0);
149
142
  const named = props["aria-label"] != null || props["aria-labelledby"] != null;
150
143
  const nameRole = named && props.role === void 0 && !onRemove ? "img" : void 0;
151
- return /* @__PURE__ */ jsxs(
152
- Element,
153
- {
154
- "data-slot": "badge",
155
- role: nameRole,
156
- "data-tone": tinted ? void 0 : resolvedTone,
157
- "data-tinted": tinted ? "" : void 0,
158
- "data-shape": shape ?? "default",
159
- "data-tabular": tabular ? "" : void 0,
160
- "data-removable": onRemove ? "" : void 0,
161
- className: cn(
162
- badgeVariants({
163
- // A status tone paints the chip itself (`badgeToneClass` below), so the variant must not
164
- // also paint one — see the `toned` variant. An EXPLICIT `variant` still wins, which is
165
- // how `<Badge variant="outline" tone="success">` keeps its border treatment.
166
- variant: tinted ? "tinted" : variant ?? (resolvedTone === "default" ? "default" : "toned"),
167
- shape: shape ?? "default"
168
- }),
169
- tinted ? void 0 : badgeToneClass[resolvedTone],
170
- className
171
- ),
172
- style: tinted ? { ...style, ["--badge-color"]: color } : style,
173
- ...props,
174
- children: [
175
- ResolvedIcon ? /* @__PURE__ */ jsx(ResolvedIcon, { "data-slot": "badge-icon", "aria-hidden": "true" }) : null,
176
- resolvedChildren != null ? /* @__PURE__ */ jsx(
177
- "span",
178
- {
179
- ref: labelRef,
180
- "data-slot": "badge-label",
181
- onPointerEnter: props.title == null ? titleTruncatedLabel : void 0,
182
- children: resolvedChildren
183
- }
184
- ) : null,
185
- onRemove ? /* @__PURE__ */ jsx(
186
- "button",
187
- {
188
- type: "button",
189
- "data-slot": "badge-remove",
190
- className: "ui-control-inline-affix-action ui-badge-remove",
191
- "aria-label": removeLabelProp ?? (removeLabel != null ? t("navigation.filterBar.removeFilter", { label: removeLabel }) : t("common.delete")),
192
- onClick: (event) => {
193
- event.stopPropagation();
194
- onRemove();
195
- },
196
- disabled: removeDisabled,
197
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
198
- }
199
- ) : null
200
- ]
201
- }
202
- );
144
+ const labelTooltip = resolvedChildren != null && props.title == null;
145
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
146
+ /* @__PURE__ */ jsxs(
147
+ Element,
148
+ {
149
+ "data-slot": "badge",
150
+ role: nameRole,
151
+ "data-tone": tinted ? void 0 : resolvedTone,
152
+ "data-tinted": tinted ? "" : void 0,
153
+ "data-shape": shape ?? "default",
154
+ "data-tabular": tabular ? "" : void 0,
155
+ "data-removable": onRemove ? "" : void 0,
156
+ className: cn(
157
+ badgeVariants({
158
+ // A status tone paints the chip itself (`badgeToneClass` below), so the variant must not
159
+ // also paint one — see the `toned` variant. An EXPLICIT `variant` still wins, which is
160
+ // how `<Badge variant="outline" tone="success">` keeps its border treatment.
161
+ variant: tinted ? "tinted" : variant ?? (resolvedTone === "default" ? "default" : "toned"),
162
+ shape: shape ?? "default"
163
+ }),
164
+ tinted ? void 0 : badgeToneClass[resolvedTone],
165
+ className
166
+ ),
167
+ style: tinted ? { ...style, ["--badge-color"]: color } : style,
168
+ ...props,
169
+ children: [
170
+ ResolvedIcon ? /* @__PURE__ */ jsx(ResolvedIcon, { "data-slot": "badge-icon", "aria-hidden": "true" }) : null,
171
+ resolvedChildren != null ? /* @__PURE__ */ jsx("span", { ref: labelRef, "data-slot": "badge-label", children: resolvedChildren }) : null,
172
+ onRemove ? /* @__PURE__ */ jsx(
173
+ "button",
174
+ {
175
+ type: "button",
176
+ "data-slot": "badge-remove",
177
+ className: "ui-control-inline-affix-action ui-badge-remove",
178
+ "aria-label": removeLabelProp ?? (removeLabel != null ? t("navigation.filterBar.removeFilter", { label: removeLabel }) : t("common.delete")),
179
+ onClick: (event) => {
180
+ event.stopPropagation();
181
+ onRemove();
182
+ },
183
+ disabled: removeDisabled,
184
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
185
+ }
186
+ ) : null
187
+ ]
188
+ }
189
+ ),
190
+ labelTooltip ? /* @__PURE__ */ jsx(EllipsisTooltip, { anchorRef: labelRef, title: resolvedChildren }) : null
191
+ ] });
203
192
  }
204
193
  const StatusBadge = Badge;
205
194
  export {
@@ -228,6 +228,7 @@ const TableCell = React.forwardRef(
228
228
  ...props
229
229
  }, ref) => {
230
230
  const preset = React.useContext(TablePresetContext);
231
+ const body = priority === "actions" && preset === "action-collection" ? /* @__PURE__ */ jsx("span", { className: "ui-table-actions-content", children }) : children;
231
232
  return /* @__PURE__ */ jsxs(
232
233
  "td",
233
234
  {
@@ -248,7 +249,7 @@ const TableCell = React.forwardRef(
248
249
  ...props,
249
250
  children: [
250
251
  label !== void 0 && children != null && children !== false && children !== "" ? /* @__PURE__ */ jsx("span", { className: "ui-table-stacked-collection-label", "aria-hidden": "true", children: label }) : null,
251
- priority === "actions" && preset === "action-collection" ? /* @__PURE__ */ jsx("span", { className: "ui-table-actions-content", children }) : children
252
+ label !== void 0 ? /* @__PURE__ */ jsx("span", { className: "ui-table-stacked-collection-value", children: body }) : body
252
253
  ]
253
254
  }
254
255
  );
@@ -125,7 +125,8 @@ function TooltipContent({
125
125
  const FOCUS_HOST = 'a[href], button, input, select, textarea, summary, [tabindex]:is([role="treeitem"], [role="gridcell"], [role="row"], [role="option"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="tab"], [role="link"], [role="button"], [role="checkbox"], [role="radio"], [role="switch"])';
126
126
  function isClipping(node) {
127
127
  const box = node.querySelector(':scope > [data-slot="typography-content"]') ?? node;
128
- return box.scrollWidth > box.clientWidth || box.scrollHeight > box.clientHeight;
128
+ if (box.scrollWidth > box.clientWidth) return true;
129
+ return box.scrollHeight > box.clientHeight && getComputedStyle(box).overflowY !== "visible";
129
130
  }
130
131
  function isFocusVisible(node) {
131
132
  try {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "31.17.0",
3
+ "version": "31.17.1",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.17.0",
2
+ "version": "31.17.1",
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",
@@ -196,6 +196,7 @@
196
196
  },
197
197
  "Badge": {
198
198
  "layers": [
199
+ "dialog-layout.css",
199
200
  "badge-layout.css"
200
201
  ]
201
202
  },
@@ -558,6 +558,10 @@
558
558
  white-space: normal;
559
559
  }
560
560
 
561
+ .ui-table-stacked-collection-value {
562
+ display: contents;
563
+ }
564
+
561
565
  .ui-data-table-summary {
562
566
  background-color: var(--table-summary-background, hsl(var(--muted)));
563
567
  font-weight: var(--font-weight-medium);
@@ -932,7 +936,9 @@
932
936
  overflow-wrap: anywhere;
933
937
  }
934
938
 
935
- [data-collapse-below="sm"] [data-slot="table-cell"]:empty {
939
+ [data-collapse-below="sm"] [data-slot="table-cell"]:empty,
940
+ [data-collapse-below="sm"]
941
+ [data-slot="table-cell"]:has(> .ui-table-stacked-collection-value:empty) {
936
942
  display: none;
937
943
  }
938
944
 
@@ -981,7 +987,9 @@
981
987
  overflow-wrap: anywhere;
982
988
  }
983
989
 
984
- [data-collapse-below="md"] [data-slot="table-cell"]:empty {
990
+ [data-collapse-below="md"] [data-slot="table-cell"]:empty,
991
+ [data-collapse-below="md"]
992
+ [data-slot="table-cell"]:has(> .ui-table-stacked-collection-value:empty) {
985
993
  display: none;
986
994
  }
987
995
 
@@ -1030,7 +1038,9 @@
1030
1038
  overflow-wrap: anywhere;
1031
1039
  }
1032
1040
 
1033
- [data-collapse-below="lg"] [data-slot="table-cell"]:empty {
1041
+ [data-collapse-below="lg"] [data-slot="table-cell"]:empty,
1042
+ [data-collapse-below="lg"]
1043
+ [data-slot="table-cell"]:has(> .ui-table-stacked-collection-value:empty) {
1034
1044
  display: none;
1035
1045
  }
1036
1046
 
@@ -1079,7 +1089,9 @@
1079
1089
  overflow-wrap: anywhere;
1080
1090
  }
1081
1091
 
1082
- [data-collapse-below="xl"] [data-slot="table-cell"]:empty {
1092
+ [data-collapse-below="xl"] [data-slot="table-cell"]:empty,
1093
+ [data-collapse-below="xl"]
1094
+ [data-slot="table-cell"]:has(> .ui-table-stacked-collection-value:empty) {
1083
1095
  display: none;
1084
1096
  }
1085
1097
 
@@ -1570,7 +1570,7 @@
1570
1570
  "badgeDocs": {
1571
1571
  "longLabels": {
1572
1572
  "title": "Long labels",
1573
- "description": "A chip never wraps: a label wider than its container ends in an ellipsis (gh#1101), and the full text shows as a title on hover when it was cut. No extra props in a table cell or a Card.",
1573
+ "description": "A chip never wraps: a label wider than its container ends in an ellipsis (gh#1101), and when it was cut the full text opens in a tooltip on hover and on keyboard focus of the link or row around the chip (gh#1105). No extra props in a table cell or a Card.",
1574
1574
  "sample": "Via team · Platform infrastructure operations (East Japan region, night-shift rotation group 2)"
1575
1575
  }
1576
1576
  }
@@ -1563,7 +1563,7 @@
1563
1563
  "badgeDocs": {
1564
1564
  "longLabels": {
1565
1565
  "title": "長いラベル",
1566
- "description": "チップは折り返さず、コンテナより広いラベルは省略記号で切れます(gh#1101)。切れたときはホバーで全文が title に出ます。表のセルでも Card でも追加の指定は要りません。",
1566
+ "description": "チップは折り返さず、コンテナより広いラベルは省略記号で切れます(gh#1101)。切れたときは、ホバーでも、チップを囲むリンクや行へのキーボードフォーカスでも、全文がツールチップに出ます(gh#1105)。表のセルでも Card でも追加の指定は要りません。",
1567
1567
  "sample": "チーム経由 · プラットフォーム基盤運用チーム(東日本リージョン・夜間当番ローテーション第二班)"
1568
1568
  }
1569
1569
  }
@@ -1563,7 +1563,7 @@
1563
1563
  "badgeDocs": {
1564
1564
  "longLabels": {
1565
1565
  "title": "Nhãn dài",
1566
- "description": "Chip không bao giờ xuống dòng: nhãn rộng hơn khung chứa sẽ kết thúc bằng dấu ba chấm (gh#1101), và khi bị cắt thì toàn văn hiện thành title khi rê chuột. Trong ô bảng hay Card đều không cần thêm prop.",
1566
+ "description": "Chip không bao giờ xuống dòng: nhãn rộng hơn khung chứa sẽ kết thúc bằng dấu ba chấm (gh#1101), và khi bị cắt thì toàn văn hiện trong tooltip khi rê chuột hoặc khi focus bàn phím vào liên kết/hàng chứa chip (gh#1105). Trong ô bảng hay Card đều không cần thêm prop.",
1567
1567
  "sample": "Qua nhóm · Đội vận hành hạ tầng nền tảng (vùng Đông Nhật Bản, ca trực đêm nhóm 2)"
1568
1568
  }
1569
1569
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "31.17.0",
4
- "godxUiMcp": "31.17.0",
3
+ "version": "31.17.1",
4
+ "godxUiMcp": "31.17.1",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",