beads-map 0.2.4 → 0.3.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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +3 -3
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/api/beads/route.js +2 -2
- package/.next/server/app/api/beads/stream/route.js +3 -3
- package/.next/server/app/api/beads.body +1 -1
- package/.next/server/app/api/config/route.js +2 -2
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/page.js +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +5 -5
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/971-bb44d52bcd9ee2a9.js +1 -0
- package/.next/static/chunks/app/page-68492e6aaf15a6dd.js +1 -0
- package/.next/static/css/c854bc2280bc4b27.css +3 -0
- package/README.md +12 -4
- package/app/api/config/route.ts +2 -0
- package/app/globals.css +12 -0
- package/app/page.tsx +111 -6
- package/components/BeadTooltip.tsx +222 -0
- package/components/BeadsGraph.tsx +264 -56
- package/components/ContextMenu.tsx +49 -3
- package/lib/parse-beads.ts +2 -0
- package/lib/types.ts +2 -0
- package/package.json +1 -1
- package/public/image.png +0 -0
- package/.next/server/app/api/config.body +0 -1
- package/.next/server/app/api/config.meta +0 -1
- package/.next/static/chunks/666-fb778298a77f3754.js +0 -1
- package/.next/static/chunks/app/page-13ee27a84e4a0c70.js +0 -1
- package/.next/static/css/dbf588b653aa4019.css +0 -3
- /package/.next/static/{5zW6ptqKxGc0tcnRau9j2 → ac0cLw5kGBDWoceTBnu21}/_buildManifest.js +0 -0
- /package/.next/static/{5zW6ptqKxGc0tcnRau9j2 → ac0cLw5kGBDWoceTBnu21}/_ssgManifest.js +0 -0
package/app/page.tsx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState, useCallback, useMemo, useRef } from "react";
|
|
4
4
|
import type { BeadsApiResponse, GraphNode, GraphLink } from "@/lib/types";
|
|
5
|
+
import { getPrefixColor } from "@/lib/types";
|
|
5
6
|
import { diffBeadsData, linkKey } from "@/lib/diff-beads";
|
|
6
7
|
import type { BeadsDiff } from "@/lib/diff-beads";
|
|
7
8
|
import BeadsGraph from "@/components/BeadsGraph";
|
|
@@ -13,6 +14,7 @@ import { BeadsLogo } from "@/components/BeadsLogo";
|
|
|
13
14
|
import { CommentTooltip } from "@/components/CommentTooltip";
|
|
14
15
|
import { ContextMenu } from "@/components/ContextMenu";
|
|
15
16
|
import { DescriptionModal } from "@/components/DescriptionModal";
|
|
17
|
+
import { BeadTooltip } from "@/components/BeadTooltip";
|
|
16
18
|
import AllCommentsPanel from "@/components/AllCommentsPanel";
|
|
17
19
|
import { ActivityOverlay } from "@/components/ActivityOverlay";
|
|
18
20
|
import { ActivityPanel } from "@/components/ActivityPanel";
|
|
@@ -186,6 +188,7 @@ export default function Home() {
|
|
|
186
188
|
const [error, setError] = useState<string | null>(null);
|
|
187
189
|
const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);
|
|
188
190
|
const [hoveredNode, setHoveredNode] = useState<GraphNode | null>(null);
|
|
191
|
+
const [collapsedEpicIds, setCollapsedEpicIds] = useState<Set<string>>(new Set());
|
|
189
192
|
const [projectName, setProjectName] = useState("Beads");
|
|
190
193
|
const [repoCount, setRepoCount] = useState(0);
|
|
191
194
|
const [repoUrls, setRepoUrls] = useState<Record<string, string>>({});
|
|
@@ -283,6 +286,13 @@ export default function Home() {
|
|
|
283
286
|
y: number;
|
|
284
287
|
} | null>(null);
|
|
285
288
|
|
|
289
|
+
// Node hover tooltip state
|
|
290
|
+
const [nodeTooltip, setNodeTooltip] = useState<{
|
|
291
|
+
node: GraphNode;
|
|
292
|
+
x: number;
|
|
293
|
+
y: number;
|
|
294
|
+
} | null>(null);
|
|
295
|
+
|
|
286
296
|
// Search state
|
|
287
297
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
288
298
|
const [searchQuery, setSearchQuery] = useState("");
|
|
@@ -516,8 +526,49 @@ export default function Home() {
|
|
|
516
526
|
setActivityPanelOpen(false);
|
|
517
527
|
}, []);
|
|
518
528
|
|
|
519
|
-
const handleNodeHover = useCallback((node: GraphNode | null) => {
|
|
529
|
+
const handleNodeHover = useCallback((node: GraphNode | null, x: number, y: number) => {
|
|
520
530
|
setHoveredNode(node);
|
|
531
|
+
setNodeTooltip(node ? { node, x, y } : null);
|
|
532
|
+
}, []);
|
|
533
|
+
|
|
534
|
+
const handleToggleEpicCollapse = useCallback((epicId: string) => {
|
|
535
|
+
setCollapsedEpicIds((prev) => {
|
|
536
|
+
const next = new Set(prev);
|
|
537
|
+
if (next.has(epicId)) next.delete(epicId);
|
|
538
|
+
else next.add(epicId);
|
|
539
|
+
return next;
|
|
540
|
+
});
|
|
541
|
+
}, []);
|
|
542
|
+
|
|
543
|
+
// Compute all epic IDs that have children (for collapse-all)
|
|
544
|
+
const allParentEpicIds = useMemo(() => {
|
|
545
|
+
if (!data) return new Set<string>();
|
|
546
|
+
const { nodes, links } = data.graphData;
|
|
547
|
+
const parentIds = new Set<string>();
|
|
548
|
+
// From parent-child links
|
|
549
|
+
for (const link of links) {
|
|
550
|
+
if (link.type === "parent-child") {
|
|
551
|
+
const src = typeof link.source === "object" ? (link.source as any).id : link.source;
|
|
552
|
+
parentIds.add(src);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// From hierarchical IDs
|
|
556
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
557
|
+
for (const node of nodes) {
|
|
558
|
+
if (node.id.includes(".")) {
|
|
559
|
+
const parentId = node.id.split(".")[0];
|
|
560
|
+
if (nodeIds.has(parentId)) parentIds.add(parentId);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return parentIds;
|
|
564
|
+
}, [data]);
|
|
565
|
+
|
|
566
|
+
const handleCollapseAll = useCallback(() => {
|
|
567
|
+
setCollapsedEpicIds(new Set(allParentEpicIds));
|
|
568
|
+
}, [allParentEpicIds]);
|
|
569
|
+
|
|
570
|
+
const handleExpandAll = useCallback(() => {
|
|
571
|
+
setCollapsedEpicIds(new Set());
|
|
521
572
|
}, []);
|
|
522
573
|
|
|
523
574
|
const handleBackgroundClick = useCallback(() => {
|
|
@@ -528,9 +579,10 @@ export default function Home() {
|
|
|
528
579
|
|
|
529
580
|
const handleNodeRightClick = useCallback(
|
|
530
581
|
(node: GraphNode, event: MouseEvent) => {
|
|
531
|
-
// Dismiss any open comment tooltip
|
|
582
|
+
// Dismiss any open comment tooltip and hover tooltip
|
|
532
583
|
setCommentTooltipState(null);
|
|
533
|
-
|
|
584
|
+
setNodeTooltip(null);
|
|
585
|
+
if (!node.description && !isAuthenticated && node.issueType !== "epic") {
|
|
534
586
|
// No description and not logged in → only action is comment → skip menu
|
|
535
587
|
setCommentTooltipState({
|
|
536
588
|
node,
|
|
@@ -742,17 +794,38 @@ export default function Home() {
|
|
|
742
794
|
[data]
|
|
743
795
|
);
|
|
744
796
|
|
|
745
|
-
//
|
|
797
|
+
// Build a map of nodeId -> commenter handles string for search
|
|
798
|
+
const commenterHandlesByNode = useMemo(() => {
|
|
799
|
+
const map = new Map<string, string>();
|
|
800
|
+
if (!allComments) return map;
|
|
801
|
+
const handlesMap = new Map<string, Set<string>>();
|
|
802
|
+
for (const comment of allComments) {
|
|
803
|
+
if (!handlesMap.has(comment.nodeId)) {
|
|
804
|
+
handlesMap.set(comment.nodeId, new Set());
|
|
805
|
+
}
|
|
806
|
+
handlesMap.get(comment.nodeId)!.add(comment.handle);
|
|
807
|
+
if (comment.displayName) {
|
|
808
|
+
handlesMap.get(comment.nodeId)!.add(comment.displayName);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
for (const [nodeId, handles] of handlesMap) {
|
|
812
|
+
map.set(nodeId, Array.from(handles).join(" "));
|
|
813
|
+
}
|
|
814
|
+
return map;
|
|
815
|
+
}, [allComments]);
|
|
816
|
+
|
|
817
|
+
// Search results - fuzzy match on id, title, people, and commenter handles
|
|
746
818
|
const searchResults = useMemo(() => {
|
|
747
819
|
if (!data || !searchQuery.trim()) return [];
|
|
748
820
|
const term = searchQuery.toLowerCase();
|
|
749
821
|
return data.graphData.nodes
|
|
750
822
|
.filter((n) => {
|
|
751
|
-
const
|
|
823
|
+
const commenters = commenterHandlesByNode.get(n.id) || "";
|
|
824
|
+
const searchable = `${n.id} ${n.title} ${n.prefix} ${n.owner || ""} ${n.assignee || ""} ${n.createdBy || ""} ${commenters}`.toLowerCase();
|
|
752
825
|
return searchable.includes(term);
|
|
753
826
|
})
|
|
754
827
|
.slice(0, 8);
|
|
755
|
-
}, [searchQuery, data]);
|
|
828
|
+
}, [searchQuery, data, commenterHandlesByNode]);
|
|
756
829
|
|
|
757
830
|
// Reset highlight index when query changes
|
|
758
831
|
useEffect(() => {
|
|
@@ -1172,6 +1245,9 @@ export default function Home() {
|
|
|
1172
1245
|
timelineActive={timelineActive}
|
|
1173
1246
|
stats={data.stats}
|
|
1174
1247
|
sidebarOpen={!!selectedNode || allCommentsPanelOpen || activityPanelOpen}
|
|
1248
|
+
collapsedEpicIds={collapsedEpicIds}
|
|
1249
|
+
onCollapseAll={handleCollapseAll}
|
|
1250
|
+
onExpandAll={handleExpandAll}
|
|
1175
1251
|
/>
|
|
1176
1252
|
|
|
1177
1253
|
{/* Timeline bar — replaces legend hint when active */}
|
|
@@ -1252,6 +1328,24 @@ export default function Home() {
|
|
|
1252
1328
|
setContextMenu(null);
|
|
1253
1329
|
};
|
|
1254
1330
|
})()}
|
|
1331
|
+
onCollapseEpic={
|
|
1332
|
+
contextMenu.node.issueType === "epic" &&
|
|
1333
|
+
!collapsedEpicIds.has(contextMenu.node.id)
|
|
1334
|
+
? () => {
|
|
1335
|
+
handleToggleEpicCollapse(contextMenu.node.id);
|
|
1336
|
+
setContextMenu(null);
|
|
1337
|
+
}
|
|
1338
|
+
: undefined
|
|
1339
|
+
}
|
|
1340
|
+
onUncollapseEpic={
|
|
1341
|
+
contextMenu.node.issueType === "epic" &&
|
|
1342
|
+
collapsedEpicIds.has(contextMenu.node.id)
|
|
1343
|
+
? () => {
|
|
1344
|
+
handleToggleEpicCollapse(contextMenu.node.id);
|
|
1345
|
+
setContextMenu(null);
|
|
1346
|
+
}
|
|
1347
|
+
: undefined
|
|
1348
|
+
}
|
|
1255
1349
|
onClose={() => setContextMenu(null)}
|
|
1256
1350
|
/>
|
|
1257
1351
|
)}
|
|
@@ -1282,6 +1376,17 @@ export default function Home() {
|
|
|
1282
1376
|
/>
|
|
1283
1377
|
)}
|
|
1284
1378
|
|
|
1379
|
+
{/* Node hover tooltip */}
|
|
1380
|
+
{nodeTooltip && !avatarTooltip && (
|
|
1381
|
+
<BeadTooltip
|
|
1382
|
+
node={nodeTooltip.node}
|
|
1383
|
+
x={nodeTooltip.x}
|
|
1384
|
+
y={nodeTooltip.y}
|
|
1385
|
+
prefixColor={getPrefixColor(nodeTooltip.node.prefix)}
|
|
1386
|
+
allNodes={timelineActive && timelineData ? timelineData.graphData.nodes : data.graphData.nodes}
|
|
1387
|
+
/>
|
|
1388
|
+
)}
|
|
1389
|
+
|
|
1285
1390
|
{/* Avatar hover tooltip */}
|
|
1286
1391
|
{avatarTooltip && (
|
|
1287
1392
|
<div
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useRef, useState, useEffect } from "react";
|
|
4
|
+
import { GraphNode, PRIORITY_LABELS, PRIORITY_COLORS } from "@/lib/types";
|
|
5
|
+
import { formatRelativeTime } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
interface BeadTooltipProps {
|
|
8
|
+
node: GraphNode;
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
prefixColor: string;
|
|
12
|
+
allNodes: GraphNode[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const COLORS = {
|
|
16
|
+
bg: "#FFFFFF",
|
|
17
|
+
border: "#E5E7EB",
|
|
18
|
+
borderLight: "#F3F4F6",
|
|
19
|
+
shadow: "rgba(0,0,0,0.08)",
|
|
20
|
+
text: "#131316",
|
|
21
|
+
textMuted: "#737680",
|
|
22
|
+
textDim: "#7F818B",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function BeadTooltip({ node, x, y, prefixColor, allNodes }: BeadTooltipProps) {
|
|
26
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
27
|
+
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!ref.current) return;
|
|
31
|
+
const tt = ref.current.getBoundingClientRect();
|
|
32
|
+
const pad = 14;
|
|
33
|
+
|
|
34
|
+
// Prefer placing above and to the right of cursor
|
|
35
|
+
let nx = x + pad;
|
|
36
|
+
let ny = y - tt.height - pad;
|
|
37
|
+
|
|
38
|
+
// Clamp to viewport edges
|
|
39
|
+
if (nx + tt.width > window.innerWidth - 16) {
|
|
40
|
+
nx = x - tt.width - pad;
|
|
41
|
+
}
|
|
42
|
+
if (nx < 16) nx = 16;
|
|
43
|
+
if (ny < 16) {
|
|
44
|
+
ny = y + pad + 8;
|
|
45
|
+
}
|
|
46
|
+
if (ny + tt.height > window.innerHeight - 16) {
|
|
47
|
+
ny = window.innerHeight - tt.height - 16;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setPos({ x: nx, y: ny });
|
|
51
|
+
}, [x, y]);
|
|
52
|
+
|
|
53
|
+
// Resolve blocker IDs to short titles
|
|
54
|
+
const blockers = node.dependentIds
|
|
55
|
+
.map((id) => {
|
|
56
|
+
const blockerNode = allNodes.find((n) => n.id === id);
|
|
57
|
+
return blockerNode ? { id, title: blockerNode.title } : { id, title: id };
|
|
58
|
+
})
|
|
59
|
+
.filter(Boolean);
|
|
60
|
+
|
|
61
|
+
const priorityLabel = PRIORITY_LABELS[node.priority] || `P${node.priority}`;
|
|
62
|
+
const priorityColor = PRIORITY_COLORS[node.priority] || "#a1a1aa";
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
ref={ref}
|
|
67
|
+
className="bead-tooltip"
|
|
68
|
+
style={{
|
|
69
|
+
position: "fixed",
|
|
70
|
+
left: pos.x,
|
|
71
|
+
top: pos.y,
|
|
72
|
+
width: 280,
|
|
73
|
+
zIndex: 100,
|
|
74
|
+
pointerEvents: "none",
|
|
75
|
+
background: COLORS.bg,
|
|
76
|
+
border: `1px solid ${COLORS.border}`,
|
|
77
|
+
boxShadow: `0 8px 32px ${COLORS.shadow}, 0 2px 8px ${COLORS.shadow}`,
|
|
78
|
+
borderRadius: 8,
|
|
79
|
+
padding: "16px 18px",
|
|
80
|
+
animation: "beadTooltipFade 0.2s ease",
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{/* Accent bar */}
|
|
84
|
+
<div
|
|
85
|
+
style={{
|
|
86
|
+
width: 24,
|
|
87
|
+
height: 2,
|
|
88
|
+
background: prefixColor,
|
|
89
|
+
opacity: 0.5,
|
|
90
|
+
marginBottom: 10,
|
|
91
|
+
borderRadius: 1,
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
|
|
95
|
+
{/* Title */}
|
|
96
|
+
<div
|
|
97
|
+
style={{
|
|
98
|
+
fontSize: 14,
|
|
99
|
+
fontWeight: 600,
|
|
100
|
+
color: COLORS.text,
|
|
101
|
+
lineHeight: 1.25,
|
|
102
|
+
marginBottom: 12,
|
|
103
|
+
overflow: "hidden",
|
|
104
|
+
display: "-webkit-box",
|
|
105
|
+
WebkitLineClamp: 2,
|
|
106
|
+
WebkitBoxOrient: "vertical",
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{node.title}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
{/* Metadata rows */}
|
|
113
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
114
|
+
{/* Created */}
|
|
115
|
+
<div>
|
|
116
|
+
<div style={labelStyle}>Created</div>
|
|
117
|
+
<div style={valueStyle}>{formatRelativeTime(node.createdAt)}</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{/* Owner */}
|
|
121
|
+
{node.owner && (
|
|
122
|
+
<div>
|
|
123
|
+
<div style={labelStyle}>Owner</div>
|
|
124
|
+
<div style={valueStyle}>{node.owner}</div>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{/* Assignee */}
|
|
129
|
+
{(node.assignee || node.createdBy) && (
|
|
130
|
+
<div>
|
|
131
|
+
{node.assignee && (
|
|
132
|
+
<>
|
|
133
|
+
<div style={labelStyle}>Assignee</div>
|
|
134
|
+
<div style={valueStyle}>{node.assignee}</div>
|
|
135
|
+
</>
|
|
136
|
+
)}
|
|
137
|
+
{node.createdBy && !node.assignee && (
|
|
138
|
+
<>
|
|
139
|
+
<div style={labelStyle}>Created by</div>
|
|
140
|
+
<div style={valueStyle}>{node.createdBy}</div>
|
|
141
|
+
</>
|
|
142
|
+
)}
|
|
143
|
+
{node.createdBy && node.assignee && node.createdBy !== node.assignee && (
|
|
144
|
+
<>
|
|
145
|
+
<div style={{ ...labelStyle, marginTop: 4 }}>Created by</div>
|
|
146
|
+
<div style={valueStyle}>{node.createdBy}</div>
|
|
147
|
+
</>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{/* Blocked by */}
|
|
153
|
+
<div>
|
|
154
|
+
<div style={labelStyle}>Blocked by</div>
|
|
155
|
+
{blockers.length === 0 ? (
|
|
156
|
+
<div style={{ ...valueStyle, color: "#10b981" }}>None</div>
|
|
157
|
+
) : (
|
|
158
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
|
|
159
|
+
{blockers.map((b) => (
|
|
160
|
+
<div key={b.id} style={{ display: "flex", alignItems: "baseline", gap: 6 }}>
|
|
161
|
+
<span
|
|
162
|
+
style={{
|
|
163
|
+
fontSize: 10,
|
|
164
|
+
fontFamily: "monospace",
|
|
165
|
+
color: COLORS.textDim,
|
|
166
|
+
flexShrink: 0,
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
{b.id}
|
|
170
|
+
</span>
|
|
171
|
+
<span
|
|
172
|
+
style={{
|
|
173
|
+
fontSize: 12,
|
|
174
|
+
color: COLORS.textMuted,
|
|
175
|
+
overflow: "hidden",
|
|
176
|
+
textOverflow: "ellipsis",
|
|
177
|
+
whiteSpace: "nowrap",
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
{b.title}
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
))}
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{/* Priority */}
|
|
189
|
+
<div style={{ borderTop: `1px solid ${COLORS.borderLight}`, paddingTop: 8 }}>
|
|
190
|
+
<div style={labelStyle}>Priority</div>
|
|
191
|
+
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
|
192
|
+
<div
|
|
193
|
+
style={{
|
|
194
|
+
width: 8,
|
|
195
|
+
height: 8,
|
|
196
|
+
borderRadius: "50%",
|
|
197
|
+
background: priorityColor,
|
|
198
|
+
flexShrink: 0,
|
|
199
|
+
}}
|
|
200
|
+
/>
|
|
201
|
+
<span style={valueStyle}>{priorityLabel}</span>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const labelStyle: React.CSSProperties = {
|
|
210
|
+
fontSize: 10,
|
|
211
|
+
fontWeight: 600,
|
|
212
|
+
letterSpacing: 1.5,
|
|
213
|
+
textTransform: "uppercase",
|
|
214
|
+
color: "#7F818B",
|
|
215
|
+
marginBottom: 3,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const valueStyle: React.CSSProperties = {
|
|
219
|
+
fontSize: 12,
|
|
220
|
+
color: "#737680",
|
|
221
|
+
lineHeight: 1.45,
|
|
222
|
+
};
|