beads-map 0.2.5 → 0.3.1
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/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 +4 -4
- 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-a0493d6741516b53.js +1 -0
- package/.next/static/css/4fded26534cb91e3.css +3 -0
- package/README.md +12 -4
- package/app/globals.css +12 -0
- package/app/page.tsx +115 -7
- package/components/BeadTooltip.tsx +222 -0
- package/components/BeadsGraph.tsx +421 -79
- package/components/ContextMenu.tsx +49 -3
- package/lib/parse-beads.ts +2 -0
- package/lib/types.ts +68 -0
- package/package.json +1 -1
- package/public/image.png +0 -0
- 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/{hVVu0gp79UGF1SPHv0aXk → _OvcD8YYgVPHv6Tomg-pB}/_buildManifest.js +0 -0
- /package/.next/static/{hVVu0gp79UGF1SPHv0aXk → _OvcD8YYgVPHv6Tomg-pB}/_ssgManifest.js +0 -0
package/app/page.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState, useCallback, useMemo, useRef } from "react";
|
|
4
|
-
import type { BeadsApiResponse, GraphNode, GraphLink } from "@/lib/types";
|
|
4
|
+
import type { BeadsApiResponse, GraphNode, GraphLink, ColorMode } from "@/lib/types";
|
|
5
|
+
import { getCatppuccinPrefixColor } 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,8 @@ 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());
|
|
192
|
+
const [colorMode, setColorMode] = useState<ColorMode>("status");
|
|
189
193
|
const [projectName, setProjectName] = useState("Beads");
|
|
190
194
|
const [repoCount, setRepoCount] = useState(0);
|
|
191
195
|
const [repoUrls, setRepoUrls] = useState<Record<string, string>>({});
|
|
@@ -283,6 +287,13 @@ export default function Home() {
|
|
|
283
287
|
y: number;
|
|
284
288
|
} | null>(null);
|
|
285
289
|
|
|
290
|
+
// Node hover tooltip state
|
|
291
|
+
const [nodeTooltip, setNodeTooltip] = useState<{
|
|
292
|
+
node: GraphNode;
|
|
293
|
+
x: number;
|
|
294
|
+
y: number;
|
|
295
|
+
} | null>(null);
|
|
296
|
+
|
|
286
297
|
// Search state
|
|
287
298
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
288
299
|
const [searchQuery, setSearchQuery] = useState("");
|
|
@@ -516,8 +527,49 @@ export default function Home() {
|
|
|
516
527
|
setActivityPanelOpen(false);
|
|
517
528
|
}, []);
|
|
518
529
|
|
|
519
|
-
const handleNodeHover = useCallback((node: GraphNode | null) => {
|
|
530
|
+
const handleNodeHover = useCallback((node: GraphNode | null, x: number, y: number) => {
|
|
520
531
|
setHoveredNode(node);
|
|
532
|
+
setNodeTooltip(node ? { node, x, y } : null);
|
|
533
|
+
}, []);
|
|
534
|
+
|
|
535
|
+
const handleToggleEpicCollapse = useCallback((epicId: string) => {
|
|
536
|
+
setCollapsedEpicIds((prev) => {
|
|
537
|
+
const next = new Set(prev);
|
|
538
|
+
if (next.has(epicId)) next.delete(epicId);
|
|
539
|
+
else next.add(epicId);
|
|
540
|
+
return next;
|
|
541
|
+
});
|
|
542
|
+
}, []);
|
|
543
|
+
|
|
544
|
+
// Compute all epic IDs that have children (for collapse-all)
|
|
545
|
+
const allParentEpicIds = useMemo(() => {
|
|
546
|
+
if (!data) return new Set<string>();
|
|
547
|
+
const { nodes, links } = data.graphData;
|
|
548
|
+
const parentIds = new Set<string>();
|
|
549
|
+
// From parent-child links
|
|
550
|
+
for (const link of links) {
|
|
551
|
+
if (link.type === "parent-child") {
|
|
552
|
+
const src = typeof link.source === "object" ? (link.source as any).id : link.source;
|
|
553
|
+
parentIds.add(src);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// From hierarchical IDs
|
|
557
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
558
|
+
for (const node of nodes) {
|
|
559
|
+
if (node.id.includes(".")) {
|
|
560
|
+
const parentId = node.id.split(".")[0];
|
|
561
|
+
if (nodeIds.has(parentId)) parentIds.add(parentId);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return parentIds;
|
|
565
|
+
}, [data]);
|
|
566
|
+
|
|
567
|
+
const handleCollapseAll = useCallback(() => {
|
|
568
|
+
setCollapsedEpicIds(new Set(allParentEpicIds));
|
|
569
|
+
}, [allParentEpicIds]);
|
|
570
|
+
|
|
571
|
+
const handleExpandAll = useCallback(() => {
|
|
572
|
+
setCollapsedEpicIds(new Set());
|
|
521
573
|
}, []);
|
|
522
574
|
|
|
523
575
|
const handleBackgroundClick = useCallback(() => {
|
|
@@ -528,9 +580,10 @@ export default function Home() {
|
|
|
528
580
|
|
|
529
581
|
const handleNodeRightClick = useCallback(
|
|
530
582
|
(node: GraphNode, event: MouseEvent) => {
|
|
531
|
-
// Dismiss any open comment tooltip
|
|
583
|
+
// Dismiss any open comment tooltip and hover tooltip
|
|
532
584
|
setCommentTooltipState(null);
|
|
533
|
-
|
|
585
|
+
setNodeTooltip(null);
|
|
586
|
+
if (!node.description && !isAuthenticated && node.issueType !== "epic") {
|
|
534
587
|
// No description and not logged in → only action is comment → skip menu
|
|
535
588
|
setCommentTooltipState({
|
|
536
589
|
node,
|
|
@@ -742,17 +795,38 @@ export default function Home() {
|
|
|
742
795
|
[data]
|
|
743
796
|
);
|
|
744
797
|
|
|
745
|
-
//
|
|
798
|
+
// Build a map of nodeId -> commenter handles string for search
|
|
799
|
+
const commenterHandlesByNode = useMemo(() => {
|
|
800
|
+
const map = new Map<string, string>();
|
|
801
|
+
if (!allComments) return map;
|
|
802
|
+
const handlesMap = new Map<string, Set<string>>();
|
|
803
|
+
for (const comment of allComments) {
|
|
804
|
+
if (!handlesMap.has(comment.nodeId)) {
|
|
805
|
+
handlesMap.set(comment.nodeId, new Set());
|
|
806
|
+
}
|
|
807
|
+
handlesMap.get(comment.nodeId)!.add(comment.handle);
|
|
808
|
+
if (comment.displayName) {
|
|
809
|
+
handlesMap.get(comment.nodeId)!.add(comment.displayName);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
for (const [nodeId, handles] of handlesMap) {
|
|
813
|
+
map.set(nodeId, Array.from(handles).join(" "));
|
|
814
|
+
}
|
|
815
|
+
return map;
|
|
816
|
+
}, [allComments]);
|
|
817
|
+
|
|
818
|
+
// Search results - fuzzy match on id, title, people, and commenter handles
|
|
746
819
|
const searchResults = useMemo(() => {
|
|
747
820
|
if (!data || !searchQuery.trim()) return [];
|
|
748
821
|
const term = searchQuery.toLowerCase();
|
|
749
822
|
return data.graphData.nodes
|
|
750
823
|
.filter((n) => {
|
|
751
|
-
const
|
|
824
|
+
const commenters = commenterHandlesByNode.get(n.id) || "";
|
|
825
|
+
const searchable = `${n.id} ${n.title} ${n.prefix} ${n.owner || ""} ${n.assignee || ""} ${n.createdBy || ""} ${commenters}`.toLowerCase();
|
|
752
826
|
return searchable.includes(term);
|
|
753
827
|
})
|
|
754
828
|
.slice(0, 8);
|
|
755
|
-
}, [searchQuery, data]);
|
|
829
|
+
}, [searchQuery, data, commenterHandlesByNode]);
|
|
756
830
|
|
|
757
831
|
// Reset highlight index when query changes
|
|
758
832
|
useEffect(() => {
|
|
@@ -1172,6 +1246,11 @@ export default function Home() {
|
|
|
1172
1246
|
timelineActive={timelineActive}
|
|
1173
1247
|
stats={data.stats}
|
|
1174
1248
|
sidebarOpen={!!selectedNode || allCommentsPanelOpen || activityPanelOpen}
|
|
1249
|
+
collapsedEpicIds={collapsedEpicIds}
|
|
1250
|
+
onCollapseAll={handleCollapseAll}
|
|
1251
|
+
onExpandAll={handleExpandAll}
|
|
1252
|
+
colorMode={colorMode}
|
|
1253
|
+
onColorModeChange={setColorMode}
|
|
1175
1254
|
/>
|
|
1176
1255
|
|
|
1177
1256
|
{/* Timeline bar — replaces legend hint when active */}
|
|
@@ -1252,6 +1331,24 @@ export default function Home() {
|
|
|
1252
1331
|
setContextMenu(null);
|
|
1253
1332
|
};
|
|
1254
1333
|
})()}
|
|
1334
|
+
onCollapseEpic={
|
|
1335
|
+
contextMenu.node.issueType === "epic" &&
|
|
1336
|
+
!collapsedEpicIds.has(contextMenu.node.id)
|
|
1337
|
+
? () => {
|
|
1338
|
+
handleToggleEpicCollapse(contextMenu.node.id);
|
|
1339
|
+
setContextMenu(null);
|
|
1340
|
+
}
|
|
1341
|
+
: undefined
|
|
1342
|
+
}
|
|
1343
|
+
onUncollapseEpic={
|
|
1344
|
+
contextMenu.node.issueType === "epic" &&
|
|
1345
|
+
collapsedEpicIds.has(contextMenu.node.id)
|
|
1346
|
+
? () => {
|
|
1347
|
+
handleToggleEpicCollapse(contextMenu.node.id);
|
|
1348
|
+
setContextMenu(null);
|
|
1349
|
+
}
|
|
1350
|
+
: undefined
|
|
1351
|
+
}
|
|
1255
1352
|
onClose={() => setContextMenu(null)}
|
|
1256
1353
|
/>
|
|
1257
1354
|
)}
|
|
@@ -1282,6 +1379,17 @@ export default function Home() {
|
|
|
1282
1379
|
/>
|
|
1283
1380
|
)}
|
|
1284
1381
|
|
|
1382
|
+
{/* Node hover tooltip */}
|
|
1383
|
+
{nodeTooltip && !avatarTooltip && (
|
|
1384
|
+
<BeadTooltip
|
|
1385
|
+
node={nodeTooltip.node}
|
|
1386
|
+
x={nodeTooltip.x}
|
|
1387
|
+
y={nodeTooltip.y}
|
|
1388
|
+
prefixColor={getCatppuccinPrefixColor(nodeTooltip.node.prefix)}
|
|
1389
|
+
allNodes={timelineActive && timelineData ? timelineData.graphData.nodes : data.graphData.nodes}
|
|
1390
|
+
/>
|
|
1391
|
+
)}
|
|
1392
|
+
|
|
1285
1393
|
{/* Avatar hover tooltip */}
|
|
1286
1394
|
{avatarTooltip && (
|
|
1287
1395
|
<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
|
+
};
|