@godxjp/ui 27.7.0 → 27.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.
@@ -13,6 +13,8 @@ export type { TopbarItemProp, TopbarItemProp as TopbarItemProps, } from "../../p
13
13
  export declare const TopbarItem: React.ForwardRefExoticComponent<Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
14
14
  hideBelow?: import("../../props/index.js").BreakpointProp;
15
15
  icon?: React.ReactNode;
16
+ iconHideFrom?: import("../../props/index.js").BreakpointProp;
17
+ labelHideBelow?: import("../../props/index.js").BreakpointProp;
16
18
  badge?: React.ReactNode;
17
19
  badgeTone?: import("../../props/components/layout.prop.js").SidebarBadgeToneProp;
18
20
  asChild?: boolean;
@@ -2,10 +2,40 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Slot } from "../../lib/slot.js";
4
4
  import { cn } from "../../lib/utils.js";
5
- const TopbarItem = React.forwardRef(function TopbarItem2({ asChild = false, className, type, hideBelow, icon, badge, badgeTone, children, ...props }, ref) {
5
+ const TopbarItem = React.forwardRef(function TopbarItem2({
6
+ asChild = false,
7
+ className,
8
+ type,
9
+ hideBelow,
10
+ icon,
11
+ iconHideFrom,
12
+ labelHideBelow,
13
+ badge,
14
+ badgeTone,
15
+ children,
16
+ ...props
17
+ }, ref) {
6
18
  const Comp = asChild ? Slot : "button";
7
19
  const showBadge = !asChild && badge !== void 0 && badge !== "";
8
- const iconNode = !asChild && icon !== void 0 && icon !== null && icon !== false ? /* @__PURE__ */ jsx("span", { "data-slot": "topbar-item-icon", className: "ui-topbar-item-icon", children: icon }) : null;
20
+ const iconNode = icon !== void 0 && icon !== null && icon !== false ? /* @__PURE__ */ jsx(
21
+ "span",
22
+ {
23
+ "data-slot": "topbar-item-icon",
24
+ className: "ui-topbar-item-icon",
25
+ "data-hide-from": iconHideFrom,
26
+ children: icon
27
+ }
28
+ ) : null;
29
+ const labelOf = (label) => labelHideBelow === void 0 || label === void 0 || label === null || label === false ? label : /* @__PURE__ */ jsx(
30
+ "span",
31
+ {
32
+ "data-slot": "topbar-item-label",
33
+ className: "ui-topbar-item-label",
34
+ "data-hide-below": labelHideBelow,
35
+ children: label
36
+ }
37
+ );
38
+ const slotted = asChild && (iconNode !== null || labelHideBelow !== void 0) && React.isValidElement(children) ? React.cloneElement(children, void 0, iconNode, labelOf(children.props.children)) : children;
9
39
  const badgeNode = showBadge ? /* @__PURE__ */ jsx(
10
40
  "span",
11
41
  {
@@ -24,9 +54,9 @@ const TopbarItem = React.forwardRef(function TopbarItem2({ asChild = false, clas
24
54
  className: cn("ui-topbar-item ui-focus-ring", className),
25
55
  type: asChild ? void 0 : type ?? "button",
26
56
  ...props,
27
- children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
57
+ children: asChild ? slotted : /* @__PURE__ */ jsxs(Fragment, { children: [
28
58
  iconNode,
29
- children,
59
+ labelOf(children),
30
60
  badgeNode
31
61
  ] })
32
62
  }
@@ -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": "27.7.0",
3
+ "version": "27.8.0",
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,
@@ -1354,8 +1354,24 @@ export type TopbarItemProp = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
1354
1354
  * visible label, not from the glyph. For a glyph that must carry a name, or one that needs a
1355
1355
  * step other than the bar's, pass `<Icon as={…} size=… label=… />` — as this prop's value or as
1356
1356
  * a child — and it keeps its own metric and its own semantics.
1357
+ *
1358
+ * Rendered under `asChild` too: the slot is inserted ahead of the child element's own children,
1359
+ * and the child element stays the cell (gh#726).
1357
1360
  */
1358
1361
  icon?: ReactNode;
1362
+ /**
1363
+ * Drop the `icon` slot FROM a breakpoint step upwards — `Flex hideFrom`'s contract, scoped to
1364
+ * the cell's glyph, so a cell reads label-only on a wide bar without the consumer wrapping the
1365
+ * glyph (gh#726). Purely decorative: the accessible name is unaffected.
1366
+ */
1367
+ iconHideFrom?: BreakpointProp;
1368
+ /**
1369
+ * Visually hide the cell's label BELOW a breakpoint step — `Flex hideBelow`'s contract, scoped to
1370
+ * the label — so the cell collapses to icon-only on a narrow bar (gh#726). The label stays in the
1371
+ * accessibility tree (visually hidden, not `display: none`), so the collapsed cell keeps its
1372
+ * accessible name. Under `asChild` the child element's own children are the label.
1373
+ */
1374
+ labelHideBelow?: BreakpointProp;
1359
1375
  /**
1360
1376
  * Unread count OVERLAID on the cell's glyph — the notification-bell affordance the cell's own use
1361
1377
  * cases name (gh#398). Pass the CONTENT ONLY, exactly like `SidebarItemProp.badge`: a number, a
@@ -1187,6 +1187,14 @@ export declare const COMPONENT_PROP_REGISTRY: {
1187
1187
  readonly field: "asChild";
1188
1188
  readonly local: true;
1189
1189
  readonly reason: "Radix Slot passthrough — merges onto the child";
1190
+ }, "BreakpointProp", {
1191
+ readonly field: "iconHideFrom";
1192
+ readonly local: true;
1193
+ readonly reason: "Flex `hideFrom` scoped to the cell's icon slot — label-only from a breakpoint up without hand-wrapping the glyph (gh#726).";
1194
+ }, {
1195
+ readonly field: "labelHideBelow";
1196
+ readonly local: true;
1197
+ readonly reason: "Flex `hideBelow` scoped to the cell's label — icon-only below a breakpoint; visually hidden so the accessible name survives (gh#726).";
1190
1198
  }];
1191
1199
  };
1192
1200
  readonly NavListProp: {
@@ -1284,7 +1284,18 @@ const COMPONENT_PROP_REGISTRY = {
1284
1284
  vocabulary: [
1285
1285
  "ChildrenProp",
1286
1286
  "ClassNameProp",
1287
- { field: "asChild", local: true, reason: "Radix Slot passthrough \u2014 merges onto the child" }
1287
+ { field: "asChild", local: true, reason: "Radix Slot passthrough \u2014 merges onto the child" },
1288
+ "BreakpointProp",
1289
+ {
1290
+ field: "iconHideFrom",
1291
+ local: true,
1292
+ reason: "Flex `hideFrom` scoped to the cell's icon slot \u2014 label-only from a breakpoint up without hand-wrapping the glyph (gh#726)."
1293
+ },
1294
+ {
1295
+ field: "labelHideBelow",
1296
+ local: true,
1297
+ reason: "Flex `hideBelow` scoped to the cell's label \u2014 icon-only below a breakpoint; visually hidden so the accessible name survives (gh#726)."
1298
+ }
1288
1299
  ]
1289
1300
  },
1290
1301
  NavListProp: {
@@ -1485,28 +1485,84 @@
1485
1485
  }
1486
1486
 
1487
1487
  @media (width >= 40rem) {
1488
- .ui-flex[data-hide-from="sm"] {
1488
+ .ui-flex[data-hide-from="sm"],
1489
+ .ui-topbar-item-icon[data-hide-from="sm"] {
1489
1490
  display: none;
1490
1491
  }
1491
1492
  }
1492
1493
 
1493
1494
  @media (width >= 48rem) {
1494
- .ui-flex[data-hide-from="md"] {
1495
+ .ui-flex[data-hide-from="md"],
1496
+ .ui-topbar-item-icon[data-hide-from="md"] {
1495
1497
  display: none;
1496
1498
  }
1497
1499
  }
1498
1500
 
1499
1501
  @media (width >= 64rem) {
1500
- .ui-flex[data-hide-from="lg"] {
1502
+ .ui-flex[data-hide-from="lg"],
1503
+ .ui-topbar-item-icon[data-hide-from="lg"] {
1501
1504
  display: none;
1502
1505
  }
1503
1506
  }
1504
1507
 
1505
1508
  @media (width >= 80rem) {
1506
- .ui-flex[data-hide-from="xl"] {
1509
+ .ui-flex[data-hide-from="xl"],
1510
+ .ui-topbar-item-icon[data-hide-from="xl"] {
1507
1511
  display: none;
1508
1512
  }
1509
1513
  }
1514
+
1515
+ @media (width < 40rem) {
1516
+ .ui-topbar-item-label[data-hide-below="sm"] {
1517
+ position: absolute;
1518
+ inset-block-start: 0;
1519
+ inset-inline-start: 0;
1520
+ inline-size: var(--stroke-hairline);
1521
+ block-size: var(--stroke-hairline);
1522
+ overflow: hidden;
1523
+ clip-path: inset(50%);
1524
+ white-space: nowrap;
1525
+ }
1526
+ }
1527
+
1528
+ @media (width < 48rem) {
1529
+ .ui-topbar-item-label[data-hide-below="md"] {
1530
+ position: absolute;
1531
+ inset-block-start: 0;
1532
+ inset-inline-start: 0;
1533
+ inline-size: var(--stroke-hairline);
1534
+ block-size: var(--stroke-hairline);
1535
+ overflow: hidden;
1536
+ clip-path: inset(50%);
1537
+ white-space: nowrap;
1538
+ }
1539
+ }
1540
+
1541
+ @media (width < 64rem) {
1542
+ .ui-topbar-item-label[data-hide-below="lg"] {
1543
+ position: absolute;
1544
+ inset-block-start: 0;
1545
+ inset-inline-start: 0;
1546
+ inline-size: var(--stroke-hairline);
1547
+ block-size: var(--stroke-hairline);
1548
+ overflow: hidden;
1549
+ clip-path: inset(50%);
1550
+ white-space: nowrap;
1551
+ }
1552
+ }
1553
+
1554
+ @media (width < 80rem) {
1555
+ .ui-topbar-item-label[data-hide-below="xl"] {
1556
+ position: absolute;
1557
+ inset-block-start: 0;
1558
+ inset-inline-start: 0;
1559
+ inline-size: var(--stroke-hairline);
1560
+ block-size: var(--stroke-hairline);
1561
+ overflow: hidden;
1562
+ clip-path: inset(50%);
1563
+ white-space: nowrap;
1564
+ }
1565
+ }
1510
1566
  }
1511
1567
 
1512
1568
  @layer components {
@@ -29,6 +29,7 @@ import {
29
29
  PanelLeftClose,
30
30
  PanelLeftOpen,
31
31
  Settings,
32
+ Target,
32
33
  Users,
33
34
  } from "lucide-react";
34
35
 
@@ -178,6 +179,42 @@ export default function Demo() {
178
179
  />
179
180
  </CardContent>
180
181
  </Card>
182
+
183
+ <Card>
184
+ <CardHeader>
185
+ <CardTitle level={2}>リンクのセルと、幅で畳むセル</CardTitle>
186
+ <CardDescription>
187
+ `asChild` でもセルは `icon`
188
+ スロットを子要素の中に差し込み、リンクそのものがセルです。 `labelHideBelow`
189
+ はその幅未満でラベルを視覚的にだけ隠すので、アイコンだけになっても
190
+ ラベルがアクセシブルネームとして残ります。`iconHideFrom`
191
+ はその幅以上でアイコンを外します。 ビューポートを 640px
192
+ の前後に動かして確認してください。
193
+ </CardDescription>
194
+ </CardHeader>
195
+ <CardContent>
196
+ <Topbar
197
+ start={
198
+ <>
199
+ <TopbarItem asChild icon={<Target aria-hidden="true" />}>
200
+ <a href="#topbar-item-goals">目標</a>
201
+ </TopbarItem>
202
+ <TopbarItem
203
+ asChild
204
+ icon={<Target aria-hidden="true" />}
205
+ labelHideBelow="sm"
206
+ iconHideFrom="sm"
207
+ >
208
+ <a href="#topbar-item-goals-responsive">今期の目標</a>
209
+ </TopbarItem>
210
+ <TopbarItem icon={<Building2 aria-hidden="true" />} labelHideBelow="md">
211
+ 東京本社
212
+ </TopbarItem>
213
+ </>
214
+ }
215
+ />
216
+ </CardContent>
217
+ </Card>
181
218
  </Flex>
182
219
  </PageContainer>
183
220
  </AppShell>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "27.7.0",
4
- "godxUiMcp": "27.7.0",
3
+ "version": "27.8.0",
4
+ "godxUiMcp": "27.8.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",