@godxjp/ui 30.4.2 → 30.5.2
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.
- package/agent/START-HERE.md +6 -6
- package/agent/components/Carousel.json +4 -1
- package/agent/components/RecordPicker.json +103 -0
- package/agent/components/ResponsiveGrid.json +3 -1
- package/agent/components/Reveal.json +6 -1
- package/agent/components/Table.json +6 -0
- package/agent/components/Text.json +8 -0
- package/agent/components-index.json +5 -0
- package/agent/components.json +130 -3
- package/agent/index.json +7 -7
- package/agent/llms.txt +7 -7
- package/agent/patterns/brand-theme-block.json +33 -0
- package/agent/patterns-index.json +32 -0
- package/agent/patterns.json +33 -0
- package/agent/tokens.json +12 -0
- package/dist/components/data-display/table.d.ts +30 -1
- package/dist/components/data-display/table.js +2 -1
- package/dist/components/data-entry/index.d.ts +2 -0
- package/dist/components/data-entry/index.js +2 -0
- package/dist/components/data-entry/record-picker.d.ts +62 -0
- package/dist/components/data-entry/record-picker.js +296 -0
- package/dist/components/general/typography.d.ts +1 -0
- package/dist/components/general/typography.js +10 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +18 -0
- package/dist/i18n/messages/ja.json +18 -0
- package/dist/i18n/messages/vi.json +18 -0
- package/dist/props/components/data-entry.prop.d.ts +58 -0
- package/dist/props/components/general.prop.d.ts +15 -1
- package/dist/props/registry.d.ts +17 -0
- package/dist/props/registry.js +17 -0
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +13 -0
- package/dist/styles/data-display-layout.css +6 -3
- package/dist/styles/data-entry-layout.css +71 -0
- package/dist/styles/dialog-layout.css +8 -0
- package/dist/styles/focus-ring.css +2 -0
- package/dist/styles/table-layout.css +13 -0
- package/dist/styles/text-layout.css +8 -0
- package/dist/tokens/components/data-display.css +2 -0
- package/dist/tokens/components/data-entry.css +3 -0
- package/docs/FRAME-COVERAGE-REPORT.md +3 -2
- package/docs/assets/tcgm/card-1.svg +1 -0
- package/docs/assets/tcgm/card-2.svg +1 -0
- package/docs/assets/tcgm/card-3.svg +1 -0
- package/docs/assets/tcgm/card-4.svg +1 -0
- package/docs/assets/tcgm/card-5.svg +1 -0
- package/docs/assets/tcgm/card-6.svg +1 -0
- package/docs/assets/tcgm/hero-prism.svg +1 -0
- package/docs/assets/tcgm/scene-1.svg +1 -0
- package/docs/assets/tcgm/scene-2.svg +1 -0
- package/docs/assets/tcgm/scene-3.svg +1 -0
- package/docs/assets/tcgm/scene-4.svg +1 -0
- package/docs/assets/tcgm/scene-5.svg +1 -0
- package/docs/assets/tcgm/tcgm-lockup-color.svg +1 -0
- package/docs/assets/tcgm/tcgm-symbol-color.svg +1 -0
- package/docs/data-entry/record-picker.tsx +208 -0
- package/docs/general/typography.tsx +28 -0
- package/docs/i18n/messages/en.json +185 -1
- package/docs/i18n/messages/ja.json +185 -1
- package/docs/i18n/messages/vi.json +185 -1
- package/docs/showcase/tcgm-website.tsx +1047 -0
- package/package.json +3 -3
- package/scripts/ui-audit.mjs +23 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Shield, User } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
5
|
+
import { RecordPicker } from "@godxjp/ui/data-entry";
|
|
6
|
+
import { Icon, Text } from "@godxjp/ui/general";
|
|
7
|
+
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* RecordPicker — the three shapes a real consumer needed (gh#932).
|
|
11
|
+
*
|
|
12
|
+
* Every case on this page is one that shipped WRONG in an app before the component existed, and
|
|
13
|
+
* the page is arranged so the failure each one fixes is visible: a dropdown that cannot be
|
|
14
|
+
* searched, two kinds of thing in one field, and a free-text key that only failed validation
|
|
15
|
+
* after save.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const SMALL = [
|
|
19
|
+
{ value: "u1", label: "Alex Tran" },
|
|
20
|
+
{ value: "u2", label: "Bea Nakamura" },
|
|
21
|
+
{ value: "u3", label: "Cam Ito" },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/** 800 people is the case the report is actually about; a dropdown cannot be the answer. */
|
|
25
|
+
const PEOPLE = Array.from({ length: 800 }, (_, i) => ({
|
|
26
|
+
value: `u${i}`,
|
|
27
|
+
label: `Member ${String(i).padStart(3, "0")}`,
|
|
28
|
+
sublabel: `member${i}@example.co.jp`,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const ROLES = [
|
|
32
|
+
{ value: "r-admin", label: "Administrator", group: "Roles" },
|
|
33
|
+
{ value: "r-review", label: "Reviewer", group: "Roles" },
|
|
34
|
+
{ value: "r-observer", label: "Observer", group: "Roles" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const ISSUES = Array.from({ length: 300 }, (_, i) => ({
|
|
38
|
+
value: `PKG-${i + 1}`,
|
|
39
|
+
label: `PKG-${i + 1}`,
|
|
40
|
+
sublabel: `Requirement ${i + 1} — imported from the spec sheet`,
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const delay = <T,>(value: T) => new Promise<T>((r) => setTimeout(() => r(value), 120));
|
|
44
|
+
|
|
45
|
+
export default function Demo() {
|
|
46
|
+
const [small, setSmall] = useState<string | null>("u2");
|
|
47
|
+
const [owner, setOwner] = useState<string | null>("");
|
|
48
|
+
const [approvers, setApprovers] = useState<string[]>(["r-review"]);
|
|
49
|
+
const [issue, setIssue] = useState<string | null>("PKG-1");
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<PageContainer
|
|
53
|
+
title="RecordPicker"
|
|
54
|
+
subtitle="One control whose shape follows the size of the set — dropdown under the threshold, searchable dialog over it"
|
|
55
|
+
>
|
|
56
|
+
<Flex direction="col" gap="lg">
|
|
57
|
+
<Card>
|
|
58
|
+
<CardHeader>
|
|
59
|
+
<CardTitle level={2}>Under the threshold, it IS a Select</CardTitle>
|
|
60
|
+
<CardDescription>
|
|
61
|
+
Three people, so nothing is gained by a dialog. The same component renders Select —
|
|
62
|
+
same trigger, same panel, same search. Nothing here is a lookalike.
|
|
63
|
+
</CardDescription>
|
|
64
|
+
</CardHeader>
|
|
65
|
+
<CardContent>
|
|
66
|
+
<Flex direction="col" gap="sm" align="start">
|
|
67
|
+
<RecordPicker options={SMALL} value={small} onValueChange={(v) => setSmall(v as string)} />
|
|
68
|
+
<Text as="p" size="2xs" tone="muted" tabular>
|
|
69
|
+
value: {JSON.stringify(small)}
|
|
70
|
+
</Text>
|
|
71
|
+
</Flex>
|
|
72
|
+
</CardContent>
|
|
73
|
+
</Card>
|
|
74
|
+
|
|
75
|
+
<Card>
|
|
76
|
+
<CardHeader>
|
|
77
|
+
<CardTitle level={2}>Assigning an owner out of 800</CardTitle>
|
|
78
|
+
<CardDescription>
|
|
79
|
+
The case the report is about: this field was a dropdown with no search. `count` comes
|
|
80
|
+
from the server — the page of rows in hand says nothing about the set behind it. The
|
|
81
|
+
pinned “Unassigned” row is a value the record HOLDS, not an empty field.
|
|
82
|
+
</CardDescription>
|
|
83
|
+
</CardHeader>
|
|
84
|
+
<CardContent>
|
|
85
|
+
<Flex direction="col" gap="sm" align="start">
|
|
86
|
+
<RecordPicker
|
|
87
|
+
count={PEOPLE.length}
|
|
88
|
+
loadOptions={async ({ query, filters }) =>
|
|
89
|
+
delay({
|
|
90
|
+
options: PEOPLE.filter(
|
|
91
|
+
(p) =>
|
|
92
|
+
p.label.toLowerCase().includes(query.toLowerCase()) &&
|
|
93
|
+
(!filters.scope || filters.scope === "project"),
|
|
94
|
+
).slice(0, 40),
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
filters={[
|
|
98
|
+
{
|
|
99
|
+
name: "scope",
|
|
100
|
+
label: "Scope",
|
|
101
|
+
options: [
|
|
102
|
+
{ value: "project", label: "On this project" },
|
|
103
|
+
{ value: "all", label: "Everyone" },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
]}
|
|
107
|
+
emptyOption={{ value: "", label: "Unassigned" }}
|
|
108
|
+
selectedOptions={[{ value: "", label: "Unassigned" }]}
|
|
109
|
+
value={owner}
|
|
110
|
+
onValueChange={(v) => setOwner(v as string)}
|
|
111
|
+
dialogTitle="Choose an owner"
|
|
112
|
+
/>
|
|
113
|
+
<Text as="p" size="2xs" tone="muted" tabular>
|
|
114
|
+
value: {JSON.stringify(owner)}
|
|
115
|
+
</Text>
|
|
116
|
+
</Flex>
|
|
117
|
+
</CardContent>
|
|
118
|
+
</Card>
|
|
119
|
+
|
|
120
|
+
<Card>
|
|
121
|
+
<CardHeader>
|
|
122
|
+
<CardTitle level={2}>Two kinds of thing in one field</CardTitle>
|
|
123
|
+
<CardDescription>
|
|
124
|
+
An approval step accepts a PERSON or a ROLE. They are two `group`s in one list rather
|
|
125
|
+
than two controls, and the option `icon` is what keeps the chips apart once chosen.
|
|
126
|
+
`multiple` commits on Confirm, so a mis-click is undone by Cancel.
|
|
127
|
+
</CardDescription>
|
|
128
|
+
</CardHeader>
|
|
129
|
+
<CardContent>
|
|
130
|
+
<Flex direction="col" gap="sm" align="start">
|
|
131
|
+
<RecordPicker
|
|
132
|
+
mode="multiple"
|
|
133
|
+
count={PEOPLE.length + ROLES.length}
|
|
134
|
+
loadOptions={async ({ query }) =>
|
|
135
|
+
delay({
|
|
136
|
+
options: [
|
|
137
|
+
...ROLES.filter((r) => r.label.toLowerCase().includes(query.toLowerCase())).map(
|
|
138
|
+
(r) => ({ ...r, icon: <Icon as={Shield} size="sm" tone="muted" /> }),
|
|
139
|
+
),
|
|
140
|
+
...PEOPLE.filter((p) => p.label.toLowerCase().includes(query.toLowerCase()))
|
|
141
|
+
.slice(0, 30)
|
|
142
|
+
.map((p) => ({
|
|
143
|
+
...p,
|
|
144
|
+
group: "Users",
|
|
145
|
+
icon: <Icon as={User} size="sm" tone="muted" />,
|
|
146
|
+
})),
|
|
147
|
+
],
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
selectedOptions={ROLES}
|
|
151
|
+
value={approvers}
|
|
152
|
+
onValueChange={(v) => setApprovers(v as string[])}
|
|
153
|
+
dialogTitle="Choose approvers"
|
|
154
|
+
/>
|
|
155
|
+
<Text as="p" size="2xs" tone="muted" tabular>
|
|
156
|
+
value: {JSON.stringify(approvers)}
|
|
157
|
+
</Text>
|
|
158
|
+
</Flex>
|
|
159
|
+
</CardContent>
|
|
160
|
+
</Card>
|
|
161
|
+
|
|
162
|
+
<Card>
|
|
163
|
+
<CardHeader>
|
|
164
|
+
<CardTitle level={2}>A record, by key or by title</CardTitle>
|
|
165
|
+
<CardDescription>
|
|
166
|
+
This field used to be free text: you typed the key, and a wrong one failed with a 422
|
|
167
|
+
AFTER save. Searching both the key and the title, with the key shown beside it, means
|
|
168
|
+
the value cannot be wrong by the time it is submitted.
|
|
169
|
+
</CardDescription>
|
|
170
|
+
</CardHeader>
|
|
171
|
+
<CardContent>
|
|
172
|
+
<Flex direction="col" gap="sm" align="start">
|
|
173
|
+
<RecordPicker
|
|
174
|
+
count={ISSUES.length}
|
|
175
|
+
loadOptions={async ({ query, filters }) =>
|
|
176
|
+
delay({
|
|
177
|
+
options: ISSUES.filter(
|
|
178
|
+
(o) =>
|
|
179
|
+
(o.label + o.sublabel).toLowerCase().includes(query.toLowerCase()) &&
|
|
180
|
+
(!filters.state || filters.state === "open"),
|
|
181
|
+
).slice(0, 40),
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
filters={[
|
|
185
|
+
{
|
|
186
|
+
name: "state",
|
|
187
|
+
label: "State",
|
|
188
|
+
options: [
|
|
189
|
+
{ value: "open", label: "Open" },
|
|
190
|
+
{ value: "closed", label: "Closed" },
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
]}
|
|
194
|
+
selectedOptions={[{ value: "PKG-1", label: "PKG-1", sublabel: "Requirement 1" }]}
|
|
195
|
+
value={issue}
|
|
196
|
+
onValueChange={(v) => setIssue(v as string)}
|
|
197
|
+
dialogTitle="Choose a requirement"
|
|
198
|
+
/>
|
|
199
|
+
<Text as="p" size="2xs" tone="muted" tabular>
|
|
200
|
+
value: {JSON.stringify(issue)}
|
|
201
|
+
</Text>
|
|
202
|
+
</Flex>
|
|
203
|
+
</CardContent>
|
|
204
|
+
</Card>
|
|
205
|
+
</Flex>
|
|
206
|
+
</PageContainer>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
@@ -425,6 +425,34 @@ export default function Demo() {
|
|
|
425
425
|
<VisuallyHidden>{t("textExamples.preservedText")}</VisuallyHidden>
|
|
426
426
|
</CardContent>
|
|
427
427
|
</Card>
|
|
428
|
+
<Card>
|
|
429
|
+
<CardHeader>
|
|
430
|
+
<CardTitle level={2}>{t("textExamples.breakTitle")}</CardTitle>
|
|
431
|
+
<CardDescription>{t("textExamples.breakDescription")}</CardDescription>
|
|
432
|
+
</CardHeader>
|
|
433
|
+
<CardContent flush>
|
|
434
|
+
<Table>
|
|
435
|
+
<TableHeader>
|
|
436
|
+
<TableRow>
|
|
437
|
+
<TableHead>{t("textExamples.breakColumnOwner")}</TableHead>
|
|
438
|
+
<TableHead>{t("textExamples.breakColumnEmail")}</TableHead>
|
|
439
|
+
</TableRow>
|
|
440
|
+
</TableHeader>
|
|
441
|
+
<TableBody>
|
|
442
|
+
<TableRow>
|
|
443
|
+
<TableCell>
|
|
444
|
+
<Text size="sm">{t("textExamples.breakSampleName")}</Text>
|
|
445
|
+
</TableCell>
|
|
446
|
+
<TableCell>
|
|
447
|
+
<Text size="sm" break="anywhere">
|
|
448
|
+
hanako.sato.customer-success-team@example-enterprise-holdings.co.jp
|
|
449
|
+
</Text>
|
|
450
|
+
</TableCell>
|
|
451
|
+
</TableRow>
|
|
452
|
+
</TableBody>
|
|
453
|
+
</Table>
|
|
454
|
+
</CardContent>
|
|
455
|
+
</Card>
|
|
428
456
|
</Flex>
|
|
429
457
|
</PageContainer>
|
|
430
458
|
);
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"textExamples": {
|
|
3
3
|
"preservedText": "Plain text",
|
|
4
|
-
"sampleText": "First line\n Indented line\n\nLast line"
|
|
4
|
+
"sampleText": "First line\n Indented line\n\nLast line",
|
|
5
|
+
"breakTitle": "break · wrapping an identifier",
|
|
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
|
+
"breakColumnOwner": "Owner",
|
|
8
|
+
"breakColumnEmail": "Email address",
|
|
9
|
+
"breakSampleName": "Sato"
|
|
5
10
|
},
|
|
6
11
|
"themeShowcase": {
|
|
7
12
|
"page": {
|
|
@@ -1260,5 +1265,184 @@
|
|
|
1260
1265
|
"attach": "Attach a file",
|
|
1261
1266
|
"enterHint": "Enter to send · Shift + Enter for a new line",
|
|
1262
1267
|
"sendComment": "Send the comment"
|
|
1268
|
+
},
|
|
1269
|
+
"tcgm": {
|
|
1270
|
+
"demoNotice": "DESIGN PROTOTYPE · every card, price, shop and release date below is a fictional sample.",
|
|
1271
|
+
"brandAlt": "TCG-M",
|
|
1272
|
+
"nav": {
|
|
1273
|
+
"cards": "Find cards",
|
|
1274
|
+
"news": "News",
|
|
1275
|
+
"calendar": "Release calendar",
|
|
1276
|
+
"watchlist": "Watchlist",
|
|
1277
|
+
"method": "Data & editorial policy",
|
|
1278
|
+
"label": "Main navigation"
|
|
1279
|
+
},
|
|
1280
|
+
"header": {
|
|
1281
|
+
"search": "Card search",
|
|
1282
|
+
"watchlist": "Watchlist",
|
|
1283
|
+
"tryApp": "Try the app",
|
|
1284
|
+
"openMenu": "Open menu",
|
|
1285
|
+
"menu": "Menu"
|
|
1286
|
+
},
|
|
1287
|
+
"hero": {
|
|
1288
|
+
"eyebrow": "Different cards. Same passion.",
|
|
1289
|
+
"titleLine1": "Find more of",
|
|
1290
|
+
"titleLine2": "the cards you love.",
|
|
1291
|
+
"leadLine1": "From the one card you came for to the next thing you discover.",
|
|
1292
|
+
"leadLine2": "Card data, price comparison and news, in one place.",
|
|
1293
|
+
"searchPlaceholder": "Search by card name, number or set",
|
|
1294
|
+
"searchLabel": "Card name, number or set",
|
|
1295
|
+
"artAlt": "Original card illustration spread across a pale sky",
|
|
1296
|
+
"artTag": "More cards. More possibilities.",
|
|
1297
|
+
"hintTitles": "Many titles",
|
|
1298
|
+
"hintCompare": "Compare like for like",
|
|
1299
|
+
"hintDiscover": "New discoveries"
|
|
1300
|
+
},
|
|
1301
|
+
"games": {
|
|
1302
|
+
"label": "Filter by title",
|
|
1303
|
+
"all": "All",
|
|
1304
|
+
"pokemon": "Pokémon Card",
|
|
1305
|
+
"onepiece": "ONE PIECE",
|
|
1306
|
+
"yugioh": "Yu-Gi-Oh!",
|
|
1307
|
+
"duel": "Duel Masters",
|
|
1308
|
+
"mtg": "MTG",
|
|
1309
|
+
"other": "Other"
|
|
1310
|
+
},
|
|
1311
|
+
"card": {
|
|
1312
|
+
"classifiedAs": "Category: {game}",
|
|
1313
|
+
"priceLabel": "Listed shop price",
|
|
1314
|
+
"noPrice": "No price listed",
|
|
1315
|
+
"shippingNote": "Excl. shipping",
|
|
1316
|
+
"sampleNote": "Sample",
|
|
1317
|
+
"sampleBadge": "SAMPLE",
|
|
1318
|
+
"artAlt": "Fictional card: {name}",
|
|
1319
|
+
"watchAdd": "Add {name} to watchlist",
|
|
1320
|
+
"watchRemove": "Remove {name} from watchlist"
|
|
1321
|
+
},
|
|
1322
|
+
"cards": {
|
|
1323
|
+
"demo-01": {
|
|
1324
|
+
"name": "Guide of the Star Crossing",
|
|
1325
|
+
"edition": "Standard"
|
|
1326
|
+
},
|
|
1327
|
+
"demo-02": {
|
|
1328
|
+
"name": "Atelier of Sky Blue",
|
|
1329
|
+
"edition": "Holo"
|
|
1330
|
+
},
|
|
1331
|
+
"demo-03": {
|
|
1332
|
+
"name": "Garden in Moonlight",
|
|
1333
|
+
"edition": "Standard"
|
|
1334
|
+
},
|
|
1335
|
+
"demo-04": {
|
|
1336
|
+
"name": "Voyage of Coral",
|
|
1337
|
+
"edition": "Alternate art"
|
|
1338
|
+
},
|
|
1339
|
+
"demo-05": {
|
|
1340
|
+
"name": "A Letter That Carries Wind",
|
|
1341
|
+
"edition": "Standard"
|
|
1342
|
+
},
|
|
1343
|
+
"demo-06": {
|
|
1344
|
+
"name": "Prism at Daybreak",
|
|
1345
|
+
"edition": "Holo"
|
|
1346
|
+
}
|
|
1347
|
+
},
|
|
1348
|
+
"collection": {
|
|
1349
|
+
"eyebrow": "Explore the collection",
|
|
1350
|
+
"title": "Meet a card you haven't met yet.",
|
|
1351
|
+
"action": "Find cards",
|
|
1352
|
+
"count": "{count} samples",
|
|
1353
|
+
"note": "The cards below are fictional, for design review only. They are not official cards of any real title.",
|
|
1354
|
+
"emptyTitle": "No card matches these conditions",
|
|
1355
|
+
"emptyBody": "Try a different title or card number.",
|
|
1356
|
+
"reset": "Reset filters"
|
|
1357
|
+
},
|
|
1358
|
+
"news": {
|
|
1359
|
+
"eyebrow": "Read & discover",
|
|
1360
|
+
"title": "Know your cards a little better.",
|
|
1361
|
+
"action": "Read articles",
|
|
1362
|
+
"sample": "Sample article",
|
|
1363
|
+
"meta": "2026.09.24 · 4 min read",
|
|
1364
|
+
"manual": "Manual only · no autoplay"
|
|
1365
|
+
},
|
|
1366
|
+
"articles": {
|
|
1367
|
+
"compare": {
|
|
1368
|
+
"title": "Five things to check before you look at the price",
|
|
1369
|
+
"cat": "Starter guide"
|
|
1370
|
+
},
|
|
1371
|
+
"edition": {
|
|
1372
|
+
"title": "Same name, different card? Telling editions apart",
|
|
1373
|
+
"cat": "Card knowledge"
|
|
1374
|
+
},
|
|
1375
|
+
"watch": {
|
|
1376
|
+
"title": "Keep the cards you care about in one list",
|
|
1377
|
+
"cat": "How to"
|
|
1378
|
+
},
|
|
1379
|
+
"sources": {
|
|
1380
|
+
"title": "Why the \"last updated\" time on price data matters",
|
|
1381
|
+
"cat": "Reading the data"
|
|
1382
|
+
}
|
|
1383
|
+
},
|
|
1384
|
+
"releases": {
|
|
1385
|
+
"eyebrow": "Coming next",
|
|
1386
|
+
"title": "Put what you're waiting for on the calendar.",
|
|
1387
|
+
"action": "Open calendar",
|
|
1388
|
+
"view": "View release"
|
|
1389
|
+
},
|
|
1390
|
+
"release": {
|
|
1391
|
+
"prism": {
|
|
1392
|
+
"title": "Prism World — Chapter of Sky",
|
|
1393
|
+
"kind": "Original fictional set"
|
|
1394
|
+
},
|
|
1395
|
+
"notes": {
|
|
1396
|
+
"title": "Collector's Notes Vol.01",
|
|
1397
|
+
"kind": "Guide release example"
|
|
1398
|
+
},
|
|
1399
|
+
"study": {
|
|
1400
|
+
"title": "Edition Study — Light and Shadow",
|
|
1401
|
+
"kind": "Original fictional set"
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
"trust": {
|
|
1405
|
+
"eyebrow": "Clarity comes first",
|
|
1406
|
+
"titleLine1": "The facts you need",
|
|
1407
|
+
"titleLine2": "to compare, made plain.",
|
|
1408
|
+
"bodyLine1": "Not just the price: the edition, the condition, the shipping and the time it was last checked.",
|
|
1409
|
+
"bodyLine2": "The footing you need to choose with confidence.",
|
|
1410
|
+
"link": "Data & editorial policy",
|
|
1411
|
+
"editionTitle": "Same edition, same condition",
|
|
1412
|
+
"editionBody": "Standard and alternate-art printings are compared separately.",
|
|
1413
|
+
"freshnessTitle": "The update time is stated",
|
|
1414
|
+
"freshnessBody": "Conditions at the time of listing may differ from now.",
|
|
1415
|
+
"adsTitle": "Ads kept apart from comparison",
|
|
1416
|
+
"adsBody": "PR labelling and the reason for the ordering, both in plain sight."
|
|
1417
|
+
},
|
|
1418
|
+
"footer": {
|
|
1419
|
+
"taglineLine1": "The cards you love, connecting a wider world.",
|
|
1420
|
+
"taglineLine2": "Card price comparison · card data · news",
|
|
1421
|
+
"accent": "Different cards. Same passion.",
|
|
1422
|
+
"colFind": "Find & compare",
|
|
1423
|
+
"colRead": "Read & learn",
|
|
1424
|
+
"colAbout": "About TCG-M",
|
|
1425
|
+
"linkSearch": "Card search",
|
|
1426
|
+
"linkCompare": "Compare editions",
|
|
1427
|
+
"linkWatchlist": "Watchlist",
|
|
1428
|
+
"linkNews": "News & guides",
|
|
1429
|
+
"linkCalendar": "Release calendar",
|
|
1430
|
+
"linkBasics": "Comparison basics",
|
|
1431
|
+
"linkMethod": "Data & editorial policy",
|
|
1432
|
+
"linkComponents": "UI components",
|
|
1433
|
+
"linkConsent": "Demo storage settings",
|
|
1434
|
+
"legal": "© TCG-M · tcg-m.com · Design prototype",
|
|
1435
|
+
"disclaimer": "Title names are category examples. They do not indicate an official service or partnership."
|
|
1436
|
+
},
|
|
1437
|
+
"toast": {
|
|
1438
|
+
"saved": "Saved to this browser",
|
|
1439
|
+
"removed": "Removed from your watchlist"
|
|
1440
|
+
},
|
|
1441
|
+
"dialog": {
|
|
1442
|
+
"title": "Fictional release",
|
|
1443
|
+
"body": "This is a calendar display example. It is not an actual product release or pre-order.",
|
|
1444
|
+
"sample": "Sample",
|
|
1445
|
+
"date": "September 2026 · demo date"
|
|
1446
|
+
}
|
|
1263
1447
|
}
|
|
1264
1448
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"textExamples": {
|
|
3
3
|
"preservedText": "プレーンテキスト",
|
|
4
|
-
"sampleText": "最初の行\n 字下げした行\n\n最後の行"
|
|
4
|
+
"sampleText": "最初の行\n 字下げした行\n\n最後の行",
|
|
5
|
+
"breakTitle": "break · 識別子を折り返す",
|
|
6
|
+
"breakDescription": "メールアドレス・クーポンコード・ログイン ID のような区切りのない識別子は break=\"anywhere\"。表のセルでも列が画面幅に収まります。whitespace=\"pre-wrap\" は人が入力した改行のためのもので、表のセルでは識別子を折り返せません。",
|
|
7
|
+
"breakColumnOwner": "担当者",
|
|
8
|
+
"breakColumnEmail": "メールアドレス",
|
|
9
|
+
"breakSampleName": "佐藤"
|
|
5
10
|
},
|
|
6
11
|
"themeShowcase": {
|
|
7
12
|
"page": {
|
|
@@ -1254,5 +1259,184 @@
|
|
|
1254
1259
|
"attach": "ファイルを添付",
|
|
1255
1260
|
"enterHint": "Enter で送信 · Shift + Enter で改行",
|
|
1256
1261
|
"sendComment": "コメントを送信"
|
|
1262
|
+
},
|
|
1263
|
+
"tcgm": {
|
|
1264
|
+
"demoNotice": "DESIGN PROTOTYPE · 掲載カード・価格・店舗・発売予定はすべて架空のサンプルです。",
|
|
1265
|
+
"brandAlt": "TCG-M",
|
|
1266
|
+
"nav": {
|
|
1267
|
+
"cards": "カードを探す",
|
|
1268
|
+
"news": "ニュース",
|
|
1269
|
+
"calendar": "発売カレンダー",
|
|
1270
|
+
"watchlist": "ウォッチリスト",
|
|
1271
|
+
"method": "データと編集方針",
|
|
1272
|
+
"label": "メインナビゲーション"
|
|
1273
|
+
},
|
|
1274
|
+
"header": {
|
|
1275
|
+
"search": "カード検索",
|
|
1276
|
+
"watchlist": "ウォッチリスト",
|
|
1277
|
+
"tryApp": "アプリを体験",
|
|
1278
|
+
"openMenu": "メニューを開く",
|
|
1279
|
+
"menu": "メニュー"
|
|
1280
|
+
},
|
|
1281
|
+
"hero": {
|
|
1282
|
+
"eyebrow": "Different cards. Same passion.",
|
|
1283
|
+
"titleLine1": "好きなカードを、",
|
|
1284
|
+
"titleLine2": "もっと見つけよう。",
|
|
1285
|
+
"leadLine1": "気になる一枚から、新しい発見まで。",
|
|
1286
|
+
"leadLine2": "カード情報・価格比較・ニュースを、ひとつの場所で。",
|
|
1287
|
+
"searchPlaceholder": "カード名・番号・セット名で検索",
|
|
1288
|
+
"searchLabel": "カード名・番号・セット名",
|
|
1289
|
+
"artAlt": "淡い空の中に広がるオリジナルのカードイラスト",
|
|
1290
|
+
"artTag": "More cards. More possibilities.",
|
|
1291
|
+
"hintTitles": "さまざまなタイトル",
|
|
1292
|
+
"hintCompare": "条件をそろえて比較",
|
|
1293
|
+
"hintDiscover": "新しい発見"
|
|
1294
|
+
},
|
|
1295
|
+
"games": {
|
|
1296
|
+
"label": "タイトルで絞り込む",
|
|
1297
|
+
"all": "すべて",
|
|
1298
|
+
"pokemon": "ポケモンカード",
|
|
1299
|
+
"onepiece": "ONE PIECE",
|
|
1300
|
+
"yugioh": "遊戯王",
|
|
1301
|
+
"duel": "デュエマ",
|
|
1302
|
+
"mtg": "MTG",
|
|
1303
|
+
"other": "その他"
|
|
1304
|
+
},
|
|
1305
|
+
"card": {
|
|
1306
|
+
"classifiedAs": "分類例:{game}",
|
|
1307
|
+
"priceLabel": "掲載ショップの販売価格",
|
|
1308
|
+
"noPrice": "価格情報なし",
|
|
1309
|
+
"shippingNote": "送料別",
|
|
1310
|
+
"sampleNote": "サンプル",
|
|
1311
|
+
"sampleBadge": "SAMPLE",
|
|
1312
|
+
"artAlt": "架空のカード:{name}",
|
|
1313
|
+
"watchAdd": "{name}をウォッチに追加",
|
|
1314
|
+
"watchRemove": "{name}をウォッチリストから削除"
|
|
1315
|
+
},
|
|
1316
|
+
"cards": {
|
|
1317
|
+
"demo-01": {
|
|
1318
|
+
"name": "星渡りの案内人",
|
|
1319
|
+
"edition": "通常版"
|
|
1320
|
+
},
|
|
1321
|
+
"demo-02": {
|
|
1322
|
+
"name": "空色のアトリエ",
|
|
1323
|
+
"edition": "ホロ仕様"
|
|
1324
|
+
},
|
|
1325
|
+
"demo-03": {
|
|
1326
|
+
"name": "月明かりの庭園",
|
|
1327
|
+
"edition": "通常版"
|
|
1328
|
+
},
|
|
1329
|
+
"demo-04": {
|
|
1330
|
+
"name": "珊瑚の旅路",
|
|
1331
|
+
"edition": "別イラスト版"
|
|
1332
|
+
},
|
|
1333
|
+
"demo-05": {
|
|
1334
|
+
"name": "風をつなぐ手紙",
|
|
1335
|
+
"edition": "通常版"
|
|
1336
|
+
},
|
|
1337
|
+
"demo-06": {
|
|
1338
|
+
"name": "夜明けのプリズム",
|
|
1339
|
+
"edition": "ホロ仕様"
|
|
1340
|
+
}
|
|
1341
|
+
},
|
|
1342
|
+
"collection": {
|
|
1343
|
+
"eyebrow": "Explore the collection",
|
|
1344
|
+
"title": "カードとの、新しい出会い。",
|
|
1345
|
+
"action": "カードを探す",
|
|
1346
|
+
"count": "{count}件のサンプル",
|
|
1347
|
+
"note": "以下はデザイン確認用の架空カードです。実在タイトルの公式カードではありません。",
|
|
1348
|
+
"emptyTitle": "条件に合うカードが見つかりませんでした",
|
|
1349
|
+
"emptyBody": "タイトルやカード番号を変えて検索してください。",
|
|
1350
|
+
"reset": "絞り込みをリセット"
|
|
1351
|
+
},
|
|
1352
|
+
"news": {
|
|
1353
|
+
"eyebrow": "Read & discover",
|
|
1354
|
+
"title": "トレカを、もっと知る。",
|
|
1355
|
+
"action": "記事を読む",
|
|
1356
|
+
"sample": "サンプル記事",
|
|
1357
|
+
"meta": "2026.09.24 · 4分で読む",
|
|
1358
|
+
"manual": "手動で切り替え · 自動再生なし"
|
|
1359
|
+
},
|
|
1360
|
+
"articles": {
|
|
1361
|
+
"compare": {
|
|
1362
|
+
"title": "価格を見る前に。比較で確認したい5つのこと",
|
|
1363
|
+
"cat": "はじめてガイド"
|
|
1364
|
+
},
|
|
1365
|
+
"edition": {
|
|
1366
|
+
"title": "同じカード名でも違う?版・加工の見分け方",
|
|
1367
|
+
"cat": "カードの知識"
|
|
1368
|
+
},
|
|
1369
|
+
"watch": {
|
|
1370
|
+
"title": "気になるカードを、自分のリストにまとめよう",
|
|
1371
|
+
"cat": "使い方"
|
|
1372
|
+
},
|
|
1373
|
+
"sources": {
|
|
1374
|
+
"title": "価格データの「更新日時」は、なぜ大切?",
|
|
1375
|
+
"cat": "データの読み方"
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
"releases": {
|
|
1379
|
+
"eyebrow": "Coming next",
|
|
1380
|
+
"title": "楽しみを、カレンダーに。",
|
|
1381
|
+
"action": "カレンダーを見る",
|
|
1382
|
+
"view": "予定を見る"
|
|
1383
|
+
},
|
|
1384
|
+
"release": {
|
|
1385
|
+
"prism": {
|
|
1386
|
+
"title": "Prism World — 空の章",
|
|
1387
|
+
"kind": "オリジナル架空セット"
|
|
1388
|
+
},
|
|
1389
|
+
"notes": {
|
|
1390
|
+
"title": "Collector's Notes Vol.01",
|
|
1391
|
+
"kind": "ガイド公開例"
|
|
1392
|
+
},
|
|
1393
|
+
"study": {
|
|
1394
|
+
"title": "Edition Study — 光と影",
|
|
1395
|
+
"kind": "オリジナル架空セット"
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1398
|
+
"trust": {
|
|
1399
|
+
"eyebrow": "Clarity comes first",
|
|
1400
|
+
"titleLine1": "比べるための情報を、",
|
|
1401
|
+
"titleLine2": "わかりやすく。",
|
|
1402
|
+
"bodyLine1": "価格だけでなく、版・状態・送料・更新日時まで。",
|
|
1403
|
+
"bodyLine2": "納得して選ぶための手がかりを届けます。",
|
|
1404
|
+
"link": "データと編集方針",
|
|
1405
|
+
"editionTitle": "同じ版・状態を確認",
|
|
1406
|
+
"editionBody": "通常版と別イラスト版を区別して比較。",
|
|
1407
|
+
"freshnessTitle": "更新日時を明記",
|
|
1408
|
+
"freshnessBody": "掲載時点と現在の条件は異なる場合があります。",
|
|
1409
|
+
"adsTitle": "広告と比較を分ける",
|
|
1410
|
+
"adsBody": "PR表示と並び順の理由をわかりやすく。"
|
|
1411
|
+
},
|
|
1412
|
+
"footer": {
|
|
1413
|
+
"taglineLine1": "好きなカードで、つながる世界へ。",
|
|
1414
|
+
"taglineLine2": "トレカの価格比較・カード情報・ニュース",
|
|
1415
|
+
"accent": "Different cards. Same passion.",
|
|
1416
|
+
"colFind": "探す・比べる",
|
|
1417
|
+
"colRead": "読む・知る",
|
|
1418
|
+
"colAbout": "TCG-Mについて",
|
|
1419
|
+
"linkSearch": "カード検索",
|
|
1420
|
+
"linkCompare": "版の比較",
|
|
1421
|
+
"linkWatchlist": "ウォッチリスト",
|
|
1422
|
+
"linkNews": "ニュース・ガイド",
|
|
1423
|
+
"linkCalendar": "発売カレンダー",
|
|
1424
|
+
"linkBasics": "比較の基本",
|
|
1425
|
+
"linkMethod": "データと編集方針",
|
|
1426
|
+
"linkComponents": "UIコンポーネント",
|
|
1427
|
+
"linkConsent": "デモの保存設定",
|
|
1428
|
+
"legal": "© TCG-M · tcg-m.com · Design prototype",
|
|
1429
|
+
"disclaimer": "各タイトル名は分類例です。公式サービス・提携を示すものではありません。"
|
|
1430
|
+
},
|
|
1431
|
+
"toast": {
|
|
1432
|
+
"saved": "このブラウザーに保存しました",
|
|
1433
|
+
"removed": "ウォッチリストから削除しました"
|
|
1434
|
+
},
|
|
1435
|
+
"dialog": {
|
|
1436
|
+
"title": "架空の予定",
|
|
1437
|
+
"body": "これはカレンダー表示例です。実際の商品発売・予約受付ではありません。",
|
|
1438
|
+
"sample": "サンプル",
|
|
1439
|
+
"date": "2026年9月 · 予定日はデモ用"
|
|
1440
|
+
}
|
|
1257
1441
|
}
|
|
1258
1442
|
}
|