@godxjp/ui 19.5.0 → 19.6.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.
@@ -5,7 +5,9 @@ export type { TextProp, TextProp as TextProps, HeadingProp, HeadingProp as Headi
5
5
  * font-medium text-muted-foreground">`.
6
6
  */
7
7
  export declare const Text: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLElement>, "color"> & {
8
- as?: "span" | "p" | "div" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr";
8
+ as?: "span" | "p" | "div" | "a" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr";
9
+ asChild?: import("../../props/index.js").AsChildProp;
10
+ link?: boolean;
9
11
  size?: import("../../props/index.js").TextSizeProp;
10
12
  tone?: import("../../props/index.js").TextToneProp;
11
13
  weight?: import("../../props/index.js").FontWeightProp;
@@ -15,6 +17,10 @@ export declare const Text: React.ForwardRefExoticComponent<Omit<React.HTMLAttrib
15
17
  tabular?: boolean;
16
18
  mono?: boolean;
17
19
  htmlFor?: string;
20
+ href?: string;
21
+ target?: React.HTMLAttributeAnchorTarget;
22
+ rel?: string;
23
+ download?: React.AnchorHTMLAttributes<HTMLAnchorElement>["download"];
18
24
  } & React.RefAttributes<HTMLElement>>;
19
25
  /**
20
26
  * Heading — h1..h4 sized from the `--heading-h*` tokens. `level` sets both the size token and the
@@ -3,16 +3,21 @@ import { cn } from "../../lib/utils.js";
3
3
  const Text = React.forwardRef(
4
4
  ({
5
5
  as = "span",
6
+ asChild = false,
6
7
  size = "sm",
7
- tone = "default",
8
+ // `link` is an affordance, not a colour: it only moves the DEFAULT tone, so
9
+ // `link tone="destructive"` is a destructive link rather than an argument between two rules.
10
+ tone,
8
11
  weight = "regular",
9
12
  align,
10
13
  truncate,
11
14
  clamp,
12
15
  tabular,
13
16
  mono,
17
+ link,
14
18
  className,
15
19
  style,
20
+ children,
16
21
  ...props
17
22
  }, ref) => {
18
23
  const clampLines = typeof clamp === "number" && Number.isFinite(clamp) && clamp >= 1 ? Math.floor(clamp) : void 0;
@@ -28,11 +33,11 @@ const Text = React.forwardRef(
28
33
  );
29
34
  }
30
35
  }
31
- return React.createElement(as, {
32
- ref,
36
+ const typography = {
33
37
  "data-slot": "text",
34
38
  "data-size": size,
35
- "data-tone": tone,
39
+ "data-tone": tone ?? (link ? "primary" : "default"),
40
+ "data-link": link ? "" : void 0,
36
41
  "data-weight": weight,
37
42
  "data-align": align,
38
43
  "data-truncate": truncate && clampLines === void 0 ? "" : void 0,
@@ -42,7 +47,17 @@ const Text = React.forwardRef(
42
47
  "data-mono": mono ? "" : void 0,
43
48
  className: cn("ui-text", className),
44
49
  ...props
45
- });
50
+ };
51
+ if (asChild) {
52
+ const child = React.Children.only(children);
53
+ return React.cloneElement(child, {
54
+ ...typography,
55
+ ...child.props,
56
+ ref,
57
+ className: cn(typography.className, child.props.className)
58
+ });
59
+ }
60
+ return React.createElement(as, { ref, ...typography }, children);
46
61
  }
47
62
  );
48
63
  Text.displayName = "Text";
@@ -4,7 +4,25 @@ import type { ActivityAnnounceProp, ActivityVariantProp, AsChildProp, ButtonSize
4
4
  /** @see Text — typographic primitive; replaces hand-rolled `<span className="text-[13px] …">`. */
5
5
  export type TextProp = Omit<React.HTMLAttributes<HTMLElement>, "color"> & {
6
6
  /** Render element. Default `span`. */
7
- as?: "span" | "p" | "div" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr";
7
+ as?: "span" | "p" | "div" | "a" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr";
8
+ /**
9
+ * Render the typography onto the child element instead of emitting one — for a router link
10
+ * (`<Text asChild link><Link href=…>…</Link></Text>`). The child owns the element and its
11
+ * navigation; Text owns the type step, tone, weight and truncation.
12
+ */
13
+ asChild?: AsChildProp;
14
+ /**
15
+ * This text IS a link: underline on hover and on keyboard focus, at the token underline offset,
16
+ * and the focus mark every other interactive element draws.
17
+ *
18
+ * It is an AFFORDANCE, not a colour — `tone` still owns the colour and simply defaults to
19
+ * `primary` here, so a destructive link (`link tone="destructive"`) reads destructive and still
20
+ * underlines. Use this INSTEAD of `className="text-primary hover:underline"`, and instead of
21
+ * `Button variant="link"` whenever the link sits in running content: `.ui-button` is a control
22
+ * box (`white-space: nowrap`, `flex-shrink: 0`, a `--control-height` tier and inline padding),
23
+ * so in a table cell it cannot wrap and cannot share the cell's line height.
24
+ */
25
+ link?: boolean;
8
26
  /** Size from the type scale — never an arbitrary px. Default `sm` (base). */
9
27
  size?: TextSizeProp;
10
28
  /** Semantic colour intent. Default `default` (foreground). */
@@ -24,6 +42,18 @@ export type TextProp = Omit<React.HTMLAttributes<HTMLElement>, "color"> & {
24
42
  /** Monospace family (codes, ids). */
25
43
  mono?: boolean;
26
44
  htmlFor?: string;
45
+ /**
46
+ * Anchor attributes, for `as="a"` (and for the `<a>` a router link supplies under `asChild`).
47
+ *
48
+ * Declared explicitly rather than by widening the base to `AnchorHTMLAttributes`, and for the
49
+ * same reason `htmlFor` is declared explicitly for `as="label"`: the element union is the
50
+ * contract, so each polymorphic branch names the attributes it actually accepts instead of every
51
+ * span silently offering an `href` it will never render.
52
+ */
53
+ href?: string;
54
+ target?: React.HTMLAttributeAnchorTarget;
55
+ rel?: string;
56
+ download?: React.AnchorHTMLAttributes<HTMLAnchorElement>["download"];
27
57
  };
28
58
  /** @see Heading — h1..h4 sized from the `--heading-h*` tokens. */
29
59
  export type HeadingProp = Omit<React.HTMLAttributes<HTMLHeadingElement>, "color"> & {
@@ -23,6 +23,7 @@
23
23
  .ui-carousel-dot,
24
24
  .ui-rating-star,
25
25
  a.ui-list-row,
26
+ .ui-text[data-link],
26
27
  .sb-nav-item,
27
28
  .sb-user,
28
29
  .tb-icon-btn
@@ -65,6 +66,7 @@
65
66
  .ui-carousel-dot,
66
67
  .ui-rating-star,
67
68
  a.ui-list-row,
69
+ .ui-text[data-link],
68
70
  .sb-nav-item,
69
71
  .sb-user,
70
72
  .tb-icon-btn
@@ -81,7 +83,13 @@
81
83
  }
82
84
 
83
85
  :root[data-focus-outline="on"]
84
- :is(.ui-input, .ui-control, .ui-control-multiline, .ui-control-trigger, .ui-tag-input):focus-visible,
86
+ :is(
87
+ .ui-input,
88
+ .ui-control,
89
+ .ui-control-multiline,
90
+ .ui-control-trigger,
91
+ .ui-tag-input
92
+ ):focus-visible,
85
93
  :root[data-focus-outline="on"] .ui-control-trigger[data-state="open"],
86
94
  :root[data-focus-outline="on"] .ui-tag-input:focus-within,
87
95
  :root[data-focus-outline="on"] .ui-control-composite-field:focus-within,
@@ -107,6 +107,17 @@
107
107
  overflow: hidden;
108
108
  min-width: 0;
109
109
  }
110
+
111
+ [data-slot="text"][data-link] {
112
+ text-decoration-line: none;
113
+ text-underline-offset: var(--text-link-underline-offset);
114
+ text-decoration-thickness: var(--text-link-underline-width);
115
+ }
116
+ [data-slot="text"][data-link]:hover,
117
+ [data-slot="text"][data-link]:focus-visible {
118
+ text-decoration-line: underline;
119
+ }
120
+
110
121
  [data-slot="text"][data-tabular] {
111
122
  font-variant-numeric: tabular-nums;
112
123
  }
@@ -31,3 +31,4 @@
31
31
  @import "./components/legal-document.css";
32
32
  @import "./components/error-surface.css";
33
33
  @import "./components/activity.css";
34
+ @import "./components/text.css";
@@ -0,0 +1,7 @@
1
+
2
+ :root {
3
+
4
+ --text-link-underline-offset: 0.2em;
5
+
6
+ --text-link-underline-width: auto;
7
+ }
@@ -1,5 +1,17 @@
1
1
  import { Button, Heading, Text } from "@godxjp/ui/general";
2
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ } from "@godxjp/ui/data-display";
3
15
  import { Flex, PageContainer } from "@godxjp/ui/layout";
4
16
 
5
17
  /**
@@ -146,6 +158,77 @@ export default function Demo() {
146
158
  </Flex>
147
159
  </CardContent>
148
160
  </Card>
161
+
162
+ <Card>
163
+ <CardHeader>
164
+ <CardTitle level={2}>Links in running content</CardTitle>
165
+ <CardDescription>
166
+ link は色ではなく「これはリンクである」という手がかりです。ホバーだけでなく
167
+ キーボードフォーカスでも下線が出るため、hover: だけのユーティリティでは
168
+ 届かない利用者にも伝わります。色は tone のままなので、tone=&quot;destructive&quot;
169
+ のリンクは destructive のまま下線を引きます。
170
+ </CardDescription>
171
+ </CardHeader>
172
+ <CardContent>
173
+ <Flex direction="col" gap="md">
174
+ <Flex direction="row" gap="md" wrap align="center">
175
+ <Text as="a" href="#typography" link>
176
+ 既定 · tone は primary
177
+ </Text>
178
+ <Text as="a" href="#typography" link tone="destructive">
179
+ 取り消す
180
+ </Text>
181
+ <Text as="a" href="#typography" link size="xs" tone="muted">
182
+ すべての通知を見る
183
+ </Text>
184
+ </Flex>
185
+
186
+ {/* The case Button variant="link" cannot serve. `.ui-button` is a control box —
187
+ * white-space: nowrap, flex-shrink: 0, a --control-height tier and inline padding —
188
+ * so in a cell it neither wraps to a second line nor shares the row's line box.
189
+ * `<Text link>` is text, so it does both. */}
190
+ <Table>
191
+ <TableHeader>
192
+ <TableRow>
193
+ <TableHead>キー</TableHead>
194
+ <TableHead>件名</TableHead>
195
+ </TableRow>
196
+ </TableHeader>
197
+ <TableBody>
198
+ <TableRow>
199
+ <TableCell>
200
+ <Text size="xs" mono tone="muted">
201
+ PKG-128
202
+ </Text>
203
+ </TableCell>
204
+ <TableCell>
205
+ <Text as="a" href="#typography" link>
206
+ ログイン画面の余白が狭く、パスワード再設定リンクが本文と重なって見える
207
+ </Text>
208
+ </TableCell>
209
+ </TableRow>
210
+ <TableRow>
211
+ <TableCell>
212
+ <Text size="xs" mono tone="muted">
213
+ PKG-129
214
+ </Text>
215
+ </TableCell>
216
+ <TableCell>
217
+ <Text as="a" href="#typography" link>
218
+ 請求書PDFの明細行
219
+ </Text>
220
+ </TableCell>
221
+ </TableRow>
222
+ </TableBody>
223
+ </Table>
224
+
225
+ <Text size="xs" tone="muted">
226
+ アクション(送信・展開)にはリンク風の Button variant=&quot;link&quot;
227
+ を使います。本文の中のリンクは Text link です。
228
+ </Text>
229
+ </Flex>
230
+ </CardContent>
231
+ </Card>
149
232
  </Flex>
150
233
  </PageContainer>
151
234
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "19.5.0",
4
- "godxUiMcp": "19.5.0",
3
+ "version": "19.6.0",
4
+ "godxUiMcp": "19.6.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",