@godxjp/ui 31.28.4 → 31.30.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.
- package/agent/START-HERE.md +5 -5
- package/agent/components/AppLauncher.json +5 -0
- package/agent/components/Avatar.json +1 -0
- package/agent/components/Dialog.json +3 -1
- package/agent/components/Legend.json +1 -1
- package/agent/components/PasswordStrength.json +2 -2
- package/agent/components/RangeTimeline.json +28 -1
- package/agent/components/ResponsiveGrid.json +7 -0
- package/agent/components/Sidebar.json +5 -0
- package/agent/components/SortableList.json +78 -0
- package/agent/components/TopbarItem.json +1 -1
- package/agent/components-index.json +5 -0
- package/agent/components.json +131 -6
- package/agent/index.json +6 -6
- package/agent/llms.txt +6 -6
- package/agent/tokens.json +122 -2
- package/dist/components/data-display/legend.d.ts +3 -2
- package/dist/components/data-display/legend.js +10 -1
- package/dist/components/data-display/range-timeline.d.ts +93 -2
- package/dist/components/data-display/range-timeline.js +220 -58
- package/dist/components/data-entry/index.d.ts +2 -0
- package/dist/components/data-entry/index.js +2 -0
- package/dist/components/data-entry/password-strength.d.ts +15 -3
- package/dist/components/data-entry/password-strength.js +16 -34
- package/dist/components/data-entry/sortable-list.d.ts +10 -0
- package/dist/components/data-entry/sortable-list.js +237 -0
- package/dist/components/feedback/dialog.d.ts +8 -2
- package/dist/components/feedback/dialog.js +8 -1
- package/dist/components/feedback/sheet.js +1 -1
- package/dist/components/layout/app-launcher.d.ts +1 -1
- package/dist/components/layout/app-launcher.js +18 -14
- package/dist/components/layout/responsive-grid.d.ts +17 -2
- package/dist/components/layout/responsive-grid.js +17 -4
- package/dist/components/layout/sidebar.d.ts +1 -1
- package/dist/components/layout/sidebar.js +3 -0
- package/dist/components/layout/topbar-item.js +8 -2
- package/dist/components/ui/avatar.d.ts +3 -1
- package/dist/components/ui/avatar.js +2 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +31 -1
- package/dist/i18n/messages/ja.json +31 -1
- package/dist/i18n/messages/vi.json +31 -1
- package/dist/props/components/data-display.prop.d.ts +18 -0
- package/dist/props/components/data-entry.prop.d.ts +34 -0
- package/dist/props/components/feedback.prop.d.ts +6 -1
- package/dist/props/components/layout.prop.d.ts +22 -0
- package/dist/props/registry.d.ts +33 -2
- package/dist/props/registry.js +44 -1
- package/dist/styles/control.css +50 -0
- package/dist/styles/data-display-layout.css +139 -2
- package/dist/styles/dialog-layout.css +14 -1
- package/dist/styles/layers.json +6 -1
- package/dist/styles/layout.css +29 -7
- package/dist/styles/shell-layout.css +26 -0
- package/dist/tokens/components/control.css +5 -0
- package/dist/tokens/components/data-display.css +12 -0
- package/dist/tokens/components/feedback.css +6 -0
- package/dist/tokens/components/shell.css +6 -0
- package/dist/tokens/semantic/layout.css +1 -4
- package/docs/FRAME-COVERAGE-REPORT.md +4 -3
- package/docs/data-display/avatar.tsx +32 -0
- package/docs/data-display/timeline.tsx +84 -1
- package/docs/data-entry/sortable-list.tsx +76 -0
- package/docs/feedback/dialog.tsx +37 -1
- package/docs/i18n/messages/en.json +62 -0
- package/docs/i18n/messages/ja.json +62 -0
- package/docs/i18n/messages/vi.json +62 -0
- package/docs/layout/app-launcher.tsx +17 -1
- package/docs/layout/responsive-grid.tsx +23 -0
- package/docs/layout/sidebar.tsx +24 -0
- package/docs/layout/topbar-item.tsx +11 -0
- package/package.json +2 -2
|
@@ -1631,5 +1631,67 @@
|
|
|
1631
1631
|
"label": "アイコン",
|
|
1632
1632
|
"chosen": "選択中: {icon}",
|
|
1633
1633
|
"none": "アイコンなし"
|
|
1634
|
+
},
|
|
1635
|
+
"dialogWidthDocs": {
|
|
1636
|
+
"title": "幅の広いダイアログ(width)",
|
|
1637
|
+
"description": "DialogContent の width で一覧グリッド向けに幅を広げます。画面幅から余白を引いた幅が上限なので、スマートフォンでもはみ出しません。",
|
|
1638
|
+
"open": "一覧を開く",
|
|
1639
|
+
"dialogTitle": "すべてのデスクトップ",
|
|
1640
|
+
"dialogDescription": "3 列のグリッドを収めた 60rem のダイアログです。",
|
|
1641
|
+
"desktop": "デスクトップ {n}"
|
|
1642
|
+
},
|
|
1643
|
+
"sortableListDocs": {
|
|
1644
|
+
"subtitle": "つまみをドラッグ、または Space・矢印・Space・Esc — 移動のたびに読み上げます",
|
|
1645
|
+
"widgetsTitle": "ホームのウィジェット",
|
|
1646
|
+
"widgetsDescription": "デスクトップのタイルを並べ替えます。順序は置いたときに一度だけ保存されます。",
|
|
1647
|
+
"calendar": "カレンダー",
|
|
1648
|
+
"mail": "メール",
|
|
1649
|
+
"tasks": "タスク",
|
|
1650
|
+
"notes": "メモ",
|
|
1651
|
+
"slot": "{position} 番目",
|
|
1652
|
+
"saved": "保存された順序: {order}",
|
|
1653
|
+
"tabsTitle": "プロジェクトのタブ",
|
|
1654
|
+
"tabsDescription": "リスト表示です。請求はプランで固定されているため持ち上げられませんが、ほかの項目はその前後に移動できます。",
|
|
1655
|
+
"overview": "概要",
|
|
1656
|
+
"members": "メンバー",
|
|
1657
|
+
"billing": "請求",
|
|
1658
|
+
"audit": "監査ログ"
|
|
1659
|
+
},
|
|
1660
|
+
"topbarItemDocs": {
|
|
1661
|
+
"notifications": "通知",
|
|
1662
|
+
"notificationsUnread": "通知(未読 {count} 件)"
|
|
1663
|
+
},
|
|
1664
|
+
"appLauncherDocs": {
|
|
1665
|
+
"requestAccess": "アクセスをリクエスト",
|
|
1666
|
+
"allApps": "すべてのアプリ"
|
|
1667
|
+
},
|
|
1668
|
+
"responsiveGridDocs": {
|
|
1669
|
+
"boardTitle": "ウィジェットボード(rowSpan + dense)",
|
|
1670
|
+
"boardDescription": "2×1 のセル、縦長 1×2 のセル(ResponsiveGrid.Item rowSpan={2})、1×1 のセル。dense で小さなセルが横長・縦長セルの隣の空きを埋めます。",
|
|
1671
|
+
"revenue": "月次売上",
|
|
1672
|
+
"tasks": "未完了タスク",
|
|
1673
|
+
"members": "メンバー",
|
|
1674
|
+
"invoices": "請求書",
|
|
1675
|
+
"services": "サービス",
|
|
1676
|
+
"invitations": "招待"
|
|
1677
|
+
},
|
|
1678
|
+
"avatarDocs": {
|
|
1679
|
+
"fitTitle": "ロゴ:fit=\"contain\"",
|
|
1680
|
+
"fitDescription": "cover(既定)は枠いっぱいに切り抜きます(顔写真向け)。contain は横長・透過のロゴを切らずに全体表示します。",
|
|
1681
|
+
"logo": "Acme ロゴ"
|
|
1682
|
+
},
|
|
1683
|
+
"sidebarDocs": {
|
|
1684
|
+
"newEntry": "仕訳を追加"
|
|
1685
|
+
},
|
|
1686
|
+
"rangeTimelineDocs": {
|
|
1687
|
+
"resizeTitle": "ラベル列の幅変更・読み込み中・日付なしの行",
|
|
1688
|
+
"resizeDescription": "ヘッダーの区切りをドラッグ(またはフォーカスして矢印キー、Shiftで大きく、Home/End)するとラベル列の幅が変わります。幅は制御プロパティなので、アプリ側でユーザーごとに保存できます。スイッチは新しい期間の読み込み中を示し、日付のない行はバーの代わりに案内を表示します。",
|
|
1689
|
+
"busyToggle": "新しい期間を読み込み中",
|
|
1690
|
+
"longTask": "EXSELI-81 問い合わせフォームと確認メールの改修",
|
|
1691
|
+
"undatedTask": "EXSELI-82 まだ日程未定",
|
|
1692
|
+
"width": "ラベル列: {width}px",
|
|
1693
|
+
"doneTask": "EXSELI-79 リリースノート(完了)",
|
|
1694
|
+
"lateTask": "EXSELI-80 データ移行(期限超過)",
|
|
1695
|
+
"plannedTask": "EXSELI-84 決済API(予定 3–7、実績 3–10)"
|
|
1634
1696
|
}
|
|
1635
1697
|
}
|
|
@@ -1631,5 +1631,67 @@
|
|
|
1631
1631
|
"label": "Biểu tượng",
|
|
1632
1632
|
"chosen": "Đã chọn: {icon}",
|
|
1633
1633
|
"none": "Không có biểu tượng"
|
|
1634
|
+
},
|
|
1635
|
+
"dialogWidthDocs": {
|
|
1636
|
+
"title": "Hộp thoại rộng (width)",
|
|
1637
|
+
"description": "width của DialogContent nới rộng bảng cho một lưới tổng quan; bị giới hạn bằng chiều rộng màn hình trừ lề, nên vẫn vừa điện thoại.",
|
|
1638
|
+
"open": "Mở tổng quan",
|
|
1639
|
+
"dialogTitle": "Tất cả màn hình làm việc",
|
|
1640
|
+
"dialogDescription": "Hộp thoại 60rem chứa một lưới ba cột.",
|
|
1641
|
+
"desktop": "Màn hình {n}"
|
|
1642
|
+
},
|
|
1643
|
+
"sortableListDocs": {
|
|
1644
|
+
"subtitle": "Kéo tay nắm, hoặc Space · mũi tên · Space · Esc — mỗi lần di chuyển đều được đọc",
|
|
1645
|
+
"widgetsTitle": "Widget trang chủ",
|
|
1646
|
+
"widgetsDescription": "Sắp xếp các ô trên màn hình. Thứ tự được lưu một lần khi bạn thả.",
|
|
1647
|
+
"calendar": "Lịch",
|
|
1648
|
+
"mail": "Thư",
|
|
1649
|
+
"tasks": "Công việc",
|
|
1650
|
+
"notes": "Ghi chú",
|
|
1651
|
+
"slot": "Ô {position}",
|
|
1652
|
+
"saved": "Thứ tự đã lưu: {order}",
|
|
1653
|
+
"tabsTitle": "Tab dự án",
|
|
1654
|
+
"tabsDescription": "Dạng danh sách. Thanh toán bị khoá theo gói nên không nhấc được — các mục khác vẫn di chuyển qua nó.",
|
|
1655
|
+
"overview": "Tổng quan",
|
|
1656
|
+
"members": "Thành viên",
|
|
1657
|
+
"billing": "Thanh toán",
|
|
1658
|
+
"audit": "Nhật ký kiểm tra"
|
|
1659
|
+
},
|
|
1660
|
+
"topbarItemDocs": {
|
|
1661
|
+
"notifications": "Thông báo",
|
|
1662
|
+
"notificationsUnread": "Thông báo, {count} chưa đọc"
|
|
1663
|
+
},
|
|
1664
|
+
"appLauncherDocs": {
|
|
1665
|
+
"requestAccess": "Yêu cầu quyền truy cập",
|
|
1666
|
+
"allApps": "Tất cả ứng dụng"
|
|
1667
|
+
},
|
|
1668
|
+
"responsiveGridDocs": {
|
|
1669
|
+
"boardTitle": "Bảng widget (rowSpan + dense)",
|
|
1670
|
+
"boardDescription": "Một ô 2×1, một ô cao 1×2 (ResponsiveGrid.Item rowSpan={2}) và các ô 1×1. dense cho các ô nhỏ lấp chỗ trống cạnh ô rộng và ô cao.",
|
|
1671
|
+
"revenue": "Doanh thu tháng",
|
|
1672
|
+
"tasks": "Công việc đang mở",
|
|
1673
|
+
"members": "Thành viên",
|
|
1674
|
+
"invoices": "Hoá đơn",
|
|
1675
|
+
"services": "Dịch vụ",
|
|
1676
|
+
"invitations": "Lời mời"
|
|
1677
|
+
},
|
|
1678
|
+
"avatarDocs": {
|
|
1679
|
+
"fitTitle": "Logo: fit=\"contain\"",
|
|
1680
|
+
"fitDescription": "cover (mặc định) cắt cho đầy khung — hợp với ảnh khuôn mặt. contain hiện trọn logo rộng hoặc trong suốt.",
|
|
1681
|
+
"logo": "Logo Acme"
|
|
1682
|
+
},
|
|
1683
|
+
"sidebarDocs": {
|
|
1684
|
+
"newEntry": "Thêm bút toán"
|
|
1685
|
+
},
|
|
1686
|
+
"rangeTimelineDocs": {
|
|
1687
|
+
"resizeTitle": "Cột nhãn đổi độ rộng, đang tải, hàng chưa có ngày",
|
|
1688
|
+
"resizeDescription": "Kéo vạch ngăn ở header (hoặc focus rồi dùng phím mũi tên, Shift để bước lớn, Home/End) để mở rộng cột nhãn. Độ rộng là prop controlled nên app có thể lưu theo từng người dùng. Công tắc đánh dấu đang tải khoảng mới; hàng chưa có ngày hiện gợi ý thay cho thanh.",
|
|
1689
|
+
"busyToggle": "Đang tải khoảng mới",
|
|
1690
|
+
"longTask": "EXSELI-81 Làm lại form liên hệ và email xác nhận",
|
|
1691
|
+
"undatedTask": "EXSELI-82 Chưa lên lịch",
|
|
1692
|
+
"width": "Cột nhãn: {width}px",
|
|
1693
|
+
"doneTask": "EXSELI-79 Ghi chú phát hành (xong)",
|
|
1694
|
+
"lateTask": "EXSELI-80 Chuyển dữ liệu (quá hạn)",
|
|
1695
|
+
"plannedTask": "EXSELI-84 API thanh toán (kế hoạch 3–7, thực tế 3–10)"
|
|
1634
1696
|
}
|
|
1635
1697
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ResponsiveGrid,
|
|
12
12
|
Topbar,
|
|
13
13
|
} from "@godxjp/ui/layout";
|
|
14
|
+
import { useTranslation } from "@godxjp/ui/i18n";
|
|
14
15
|
|
|
15
16
|
const apps = [
|
|
16
17
|
{ id: "console", name: "Console", href: "/console", icon: <BarChart3 />, current: true },
|
|
@@ -62,6 +63,7 @@ const labels: AppLauncherLabels = {
|
|
|
62
63
|
* width (the Slack shape). Shipping both puts one scope in two places.
|
|
63
64
|
*/
|
|
64
65
|
export default function Demo() {
|
|
66
|
+
const { t } = useTranslation();
|
|
65
67
|
const [error, setError] = useState<string | undefined>("アプリ一覧を取得できませんでした。");
|
|
66
68
|
|
|
67
69
|
return (
|
|
@@ -82,7 +84,21 @@ export default function Demo() {
|
|
|
82
84
|
<Flex direction="col" gap="md">
|
|
83
85
|
<Topbar
|
|
84
86
|
start={<Text weight="medium">Acme Console</Text>}
|
|
85
|
-
end={
|
|
87
|
+
end={
|
|
88
|
+
<AppLauncher
|
|
89
|
+
apps={apps}
|
|
90
|
+
groups={groups}
|
|
91
|
+
labels={labels}
|
|
92
|
+
footer={
|
|
93
|
+
<>
|
|
94
|
+
<a href="#app-launcher-request-access">
|
|
95
|
+
{t("appLauncherDocs.requestAccess")}
|
|
96
|
+
</a>
|
|
97
|
+
<a href="#app-launcher-all-apps">{t("appLauncherDocs.allApps")}</a>
|
|
98
|
+
</>
|
|
99
|
+
}
|
|
100
|
+
/>
|
|
101
|
+
}
|
|
86
102
|
/>
|
|
87
103
|
<Text size="sm" tone="muted">
|
|
88
104
|
current のアプリだけが `aria-current="page"` を持ちます。
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "@godxjp/ui/data-entry";
|
|
18
18
|
import { Text } from "@godxjp/ui/general";
|
|
19
19
|
import { Flex, PageContainer, ResponsiveGrid, SplitPane } from "@godxjp/ui/layout";
|
|
20
|
+
import { useTranslation } from "@godxjp/ui/i18n";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* ResponsiveGrid · equal-width multi-column tile grid with automatic responsive
|
|
@@ -44,6 +45,7 @@ const KANBAN_TASKS: { id: string; title: string; lane: KanbanLane; priority: str
|
|
|
44
45
|
];
|
|
45
46
|
|
|
46
47
|
export default function Demo() {
|
|
48
|
+
const { t } = useTranslation();
|
|
47
49
|
// Kanban: which lane each card is in, moved by native drag and drop.
|
|
48
50
|
const [tasks, setTasks] = useState(KANBAN_TASKS);
|
|
49
51
|
const moveTask = (id: string, lane: KanbanLane) =>
|
|
@@ -54,6 +56,27 @@ export default function Demo() {
|
|
|
54
56
|
subtitle="等幅タイルグリッド · KPI・カード比較・ダッシュボード行"
|
|
55
57
|
>
|
|
56
58
|
<Flex direction="col" gap="lg">
|
|
59
|
+
<Card>
|
|
60
|
+
<CardHeader>
|
|
61
|
+
<CardTitle level={2}>{t("responsiveGridDocs.boardTitle")}</CardTitle>
|
|
62
|
+
<CardDescription>{t("responsiveGridDocs.boardDescription")}</CardDescription>
|
|
63
|
+
</CardHeader>
|
|
64
|
+
<CardContent>
|
|
65
|
+
{/* gh#1175: a widget board — 2×1, 1×2 (rowSpan) and 1×1 cells, packed dense. */}
|
|
66
|
+
<ResponsiveGrid columns={4} dense>
|
|
67
|
+
<ResponsiveGrid.Item span={2}>
|
|
68
|
+
<StatCard label={t("responsiveGridDocs.revenue")} value="¥12,400,000" />
|
|
69
|
+
</ResponsiveGrid.Item>
|
|
70
|
+
<ResponsiveGrid.Item rowSpan={2}>
|
|
71
|
+
<StatCard label={t("responsiveGridDocs.tasks")} value="18" />
|
|
72
|
+
</ResponsiveGrid.Item>
|
|
73
|
+
<StatCard label={t("responsiveGridDocs.members")} value="14" />
|
|
74
|
+
<StatCard label={t("responsiveGridDocs.invoices")} value="2" />
|
|
75
|
+
<StatCard label={t("responsiveGridDocs.services")} value="3" />
|
|
76
|
+
<StatCard label={t("responsiveGridDocs.invitations")} value="0" />
|
|
77
|
+
</ResponsiveGrid>
|
|
78
|
+
</CardContent>
|
|
79
|
+
</Card>
|
|
57
80
|
<Card>
|
|
58
81
|
<CardHeader>
|
|
59
82
|
<CardTitle level={2}>Compact mobile metrics</CardTitle>
|
package/docs/layout/sidebar.tsx
CHANGED
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
CardTitle,
|
|
24
24
|
} from "@godxjp/ui/data-display";
|
|
25
25
|
import { Button, Text } from "@godxjp/ui/general";
|
|
26
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@godxjp/ui/feedback";
|
|
27
|
+
import { useTranslation } from "@godxjp/ui/i18n";
|
|
26
28
|
import {
|
|
27
29
|
LayoutDashboard,
|
|
28
30
|
FileText,
|
|
@@ -207,6 +209,7 @@ const COMPOSED_ITEMS: SidebarItemData[] = [
|
|
|
207
209
|
];
|
|
208
210
|
|
|
209
211
|
export default function Demo() {
|
|
212
|
+
const { t } = useTranslation();
|
|
210
213
|
const [activeId, setActiveId] = useState("journal");
|
|
211
214
|
const [collapsed, setCollapsed] = useState(false);
|
|
212
215
|
const [brandActiveId, setBrandActiveId] = useState("projects");
|
|
@@ -235,6 +238,27 @@ export default function Demo() {
|
|
|
235
238
|
* を読むと、AppShell が同じ Sidebar をドロワーにも渡す(ドロワーは展開表示に戻す)ため
|
|
236
239
|
* 全幅のドロワーの中でアイコンだけの足元になります。
|
|
237
240
|
*/
|
|
241
|
+
/*
|
|
242
|
+
* action · the rail's ONE primary action (gh#1185), above the scrolling nav. Full width
|
|
243
|
+
* when expanded; icon-only with a tooltip in the collapsed rail.
|
|
244
|
+
*/
|
|
245
|
+
action={(railCollapsed) =>
|
|
246
|
+
railCollapsed ? (
|
|
247
|
+
<Tooltip>
|
|
248
|
+
<TooltipTrigger asChild>
|
|
249
|
+
<Button size="sm" aria-label={t("sidebarDocs.newEntry")}>
|
|
250
|
+
<Plus />
|
|
251
|
+
</Button>
|
|
252
|
+
</TooltipTrigger>
|
|
253
|
+
<TooltipContent side="right">{t("sidebarDocs.newEntry")}</TooltipContent>
|
|
254
|
+
</Tooltip>
|
|
255
|
+
) : (
|
|
256
|
+
<Button size="sm" fullWidth>
|
|
257
|
+
<Plus />
|
|
258
|
+
{t("sidebarDocs.newEntry")}
|
|
259
|
+
</Button>
|
|
260
|
+
)
|
|
261
|
+
}
|
|
238
262
|
footer={(railCollapsed) =>
|
|
239
263
|
railCollapsed ? (
|
|
240
264
|
<Flex justify="center">
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "@godxjp/ui/data-display";
|
|
12
12
|
import { Button, Icon, Text } from "@godxjp/ui/general";
|
|
13
13
|
import { AppShell, Flex, PageContainer, Sidebar, Topbar, TopbarItem } from "@godxjp/ui/layout";
|
|
14
|
+
import { useTranslation } from "@godxjp/ui/i18n";
|
|
14
15
|
import type { SidebarSectionProp } from "@godxjp/ui/layout";
|
|
15
16
|
import {
|
|
16
17
|
DropdownMenu,
|
|
@@ -51,6 +52,7 @@ const SECTIONS: SidebarSectionProp[] = [
|
|
|
51
52
|
];
|
|
52
53
|
|
|
53
54
|
export default function Demo() {
|
|
55
|
+
const { t } = useTranslation();
|
|
54
56
|
const [collapsed, setCollapsed] = useState(false);
|
|
55
57
|
|
|
56
58
|
return (
|
|
@@ -199,6 +201,15 @@ export default function Demo() {
|
|
|
199
201
|
<TopbarItem asChild icon={<Target aria-hidden="true" />}>
|
|
200
202
|
<a href="#topbar-item-goals">目標</a>
|
|
201
203
|
</TopbarItem>
|
|
204
|
+
{/* gh#1171: a bell that is a LINK keeps its count under asChild. */}
|
|
205
|
+
<TopbarItem asChild badge={3} icon={<Bell aria-hidden="true" />}>
|
|
206
|
+
<a
|
|
207
|
+
href="#topbar-item-notifications"
|
|
208
|
+
aria-label={t("topbarItemDocs.notificationsUnread", { count: 3 })}
|
|
209
|
+
>
|
|
210
|
+
{t("topbarItemDocs.notifications")}
|
|
211
|
+
</a>
|
|
212
|
+
</TopbarItem>
|
|
202
213
|
<TopbarItem
|
|
203
214
|
asChild
|
|
204
215
|
icon={<Target aria-hidden="true" />}
|