@modernrelay/orbit-react 0.2.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/GraphProvider-B8ITDXS0.d.ts +11 -0
  3. package/dist/chunk-7XB47YST.js +69 -0
  4. package/dist/chunk-7XB47YST.js.map +1 -0
  5. package/dist/chunk-DGFLU2SN.js +108 -0
  6. package/dist/chunk-DGFLU2SN.js.map +1 -0
  7. package/dist/chunk-JN7QCMFW.js +93 -0
  8. package/dist/chunk-JN7QCMFW.js.map +1 -0
  9. package/dist/chunk-QJ7JXQVK.js +119 -0
  10. package/dist/chunk-QJ7JXQVK.js.map +1 -0
  11. package/dist/components/ContextMenu.d.ts +73 -0
  12. package/dist/components/ContextMenu.js +367 -0
  13. package/dist/components/ContextMenu.js.map +1 -0
  14. package/dist/components/Histogram.d.ts +33 -0
  15. package/dist/components/Histogram.js +216 -0
  16. package/dist/components/Histogram.js.map +1 -0
  17. package/dist/components/Inspector.d.ts +63 -0
  18. package/dist/components/Inspector.js +246 -0
  19. package/dist/components/Inspector.js.map +1 -0
  20. package/dist/components/Legend.d.ts +45 -0
  21. package/dist/components/Legend.js +185 -0
  22. package/dist/components/Legend.js.map +1 -0
  23. package/dist/components/Minimap.d.ts +63 -0
  24. package/dist/components/Minimap.js +222 -0
  25. package/dist/components/Minimap.js.map +1 -0
  26. package/dist/components/Navigator.d.ts +90 -0
  27. package/dist/components/Navigator.js +381 -0
  28. package/dist/components/Navigator.js.map +1 -0
  29. package/dist/components/Search.d.ts +70 -0
  30. package/dist/components/Search.js +203 -0
  31. package/dist/components/Search.js.map +1 -0
  32. package/dist/components/SelectionActions.d.ts +36 -0
  33. package/dist/components/SelectionActions.js +126 -0
  34. package/dist/components/SelectionActions.js.map +1 -0
  35. package/dist/components/SimControls.d.ts +124 -0
  36. package/dist/components/SimControls.js +221 -0
  37. package/dist/components/SimControls.js.map +1 -0
  38. package/dist/components/Table.d.ts +92 -0
  39. package/dist/components/Table.js +505 -0
  40. package/dist/components/Table.js.map +1 -0
  41. package/dist/components/Timeline.d.ts +34 -0
  42. package/dist/components/Timeline.js +182 -0
  43. package/dist/components/Timeline.js.map +1 -0
  44. package/dist/components/Toolbar.d.ts +60 -0
  45. package/dist/components/Toolbar.js +220 -0
  46. package/dist/components/Toolbar.js.map +1 -0
  47. package/dist/components/Tooltip.d.ts +65 -0
  48. package/dist/components/Tooltip.js +178 -0
  49. package/dist/components/Tooltip.js.map +1 -0
  50. package/dist/index.d.ts +488 -0
  51. package/dist/index.js +827 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/shared-Bj3Gk219.d.ts +43 -0
  54. package/package.json +99 -0
@@ -0,0 +1,178 @@
1
+ import { useResolvedInstance, overlaySurface } from '../chunk-7XB47YST.js';
2
+ import { useState, useRef, useCallback, useEffect, useLayoutEffect, useSyncExternalStore } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ var DELAY_MS_DEFAULT = 150;
6
+ var TOOLTIP_ATTR_ROWS = 6;
7
+ var POINTER_OFFSET_PX = 12;
8
+ var LAYER_STYLE = {
9
+ position: "absolute",
10
+ inset: 0,
11
+ overflow: "hidden",
12
+ pointerEvents: "none"
13
+ };
14
+ var CARD_STYLE = {
15
+ position: "absolute",
16
+ top: 0,
17
+ left: 0,
18
+ maxWidth: 280,
19
+ padding: "6px 10px",
20
+ borderRadius: 8,
21
+ background: "rgba(23, 25, 32, 0.96)",
22
+ border: "1px solid rgba(255, 255, 255, 0.14)",
23
+ color: "#e8eaf0",
24
+ font: "12px/1.5 system-ui, sans-serif",
25
+ pointerEvents: "none",
26
+ whiteSpace: "nowrap",
27
+ willChange: "transform"
28
+ };
29
+ var TITLE_STYLE = {
30
+ fontWeight: 600,
31
+ overflow: "hidden",
32
+ textOverflow: "ellipsis"
33
+ };
34
+ var ROW_STYLE = {
35
+ display: "flex",
36
+ justifyContent: "space-between",
37
+ gap: 12,
38
+ overflow: "hidden"
39
+ };
40
+ var ROW_KEY_STYLE = { opacity: 0.65 };
41
+ var ROW_VALUE_STYLE = { overflow: "hidden", textOverflow: "ellipsis" };
42
+ function nodeLabelText(node) {
43
+ const attrs = node.attrs;
44
+ const label = attrs?.["label"];
45
+ return label === void 0 || label === null ? String(node.id) : String(label);
46
+ }
47
+ function attrValueText(value) {
48
+ if (typeof value === "string") return value;
49
+ if (typeof value === "object" && value !== null) {
50
+ try {
51
+ return JSON.stringify(value);
52
+ } catch {
53
+ return String(value);
54
+ }
55
+ }
56
+ return String(value);
57
+ }
58
+ function encodeHover(kind, id) {
59
+ return `${kind}\0${id}`;
60
+ }
61
+ function decodeHover(key) {
62
+ const at = key.indexOf("\0");
63
+ return { kind: key.slice(0, at), id: key.slice(at + 1) };
64
+ }
65
+ function useHoveredTarget(instance, edges) {
66
+ const subscribe = useCallback(
67
+ (onChange) => instance.store.subscribe(onChange),
68
+ [instance]
69
+ );
70
+ const getSnapshot = useCallback(() => {
71
+ const hover = instance.store.getState().hover;
72
+ if (hover.nodeId !== null) return encodeHover("node", hover.nodeId);
73
+ if (edges && hover.edgeId !== null) return encodeHover("edge", hover.edgeId);
74
+ return null;
75
+ }, [instance, edges]);
76
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
77
+ }
78
+ function edgeLabelText(edge) {
79
+ const attrs = edge.attrs;
80
+ const label = attrs?.["label"];
81
+ return label === void 0 || label === null ? String(edge.id) : String(label);
82
+ }
83
+ function GraphTooltip(props) {
84
+ const instance = useResolvedInstance(props.instance, "<GraphTooltip>");
85
+ const labels = overlaySurface(instance).labels;
86
+ const delayMs = props.delayMs ?? DELAY_MS_DEFAULT;
87
+ const edgesEnabled = props.edges ?? true;
88
+ const hoverKey = useHoveredTarget(instance, edgesEnabled);
89
+ const [visibleKey, setVisibleKey] = useState(null);
90
+ const layerRef = useRef(null);
91
+ const cardRef = useRef(null);
92
+ const trackedRef = useRef(/* @__PURE__ */ new Map());
93
+ const pointerRef = useRef(null);
94
+ const position = useCallback(
95
+ (id) => {
96
+ const el = cardRef.current;
97
+ if (el === null) return;
98
+ const at = trackedRef.current.get(id) ?? pointerRef.current;
99
+ if (at === null || at === void 0) return;
100
+ el.style.transform = `translate3d(${at[0]}px, ${at[1]}px, 0)`;
101
+ },
102
+ []
103
+ );
104
+ useEffect(() => {
105
+ if (hoverKey === null) {
106
+ setVisibleKey(null);
107
+ return void 0;
108
+ }
109
+ const timer = setTimeout(() => {
110
+ setVisibleKey(hoverKey);
111
+ }, delayMs);
112
+ return () => {
113
+ clearTimeout(timer);
114
+ };
115
+ }, [hoverKey, delayMs]);
116
+ useLayoutEffect(() => {
117
+ if (labels === void 0) return void 0;
118
+ return labels.subscribePositions((list) => {
119
+ const tracked = trackedRef.current;
120
+ tracked.clear();
121
+ for (const p of list) tracked.set(p.id, [p.x, p.y]);
122
+ const id = instance.store.getState().hover.nodeId;
123
+ if (id !== null && tracked.has(id)) position(id);
124
+ });
125
+ }, [labels, instance, position]);
126
+ useEffect(() => {
127
+ const onMove = (e) => {
128
+ const host = layerRef.current?.offsetParent;
129
+ const origin = host instanceof Element ? host.getBoundingClientRect() : null;
130
+ pointerRef.current = [
131
+ e.clientX - (origin?.left ?? 0) + POINTER_OFFSET_PX,
132
+ e.clientY - (origin?.top ?? 0) + POINTER_OFFSET_PX
133
+ ];
134
+ const hover = instance.store.getState().hover;
135
+ if (hover.nodeId !== null) {
136
+ if (!trackedRef.current.has(hover.nodeId)) position(hover.nodeId);
137
+ } else if (hover.edgeId !== null) {
138
+ position(hover.edgeId);
139
+ }
140
+ };
141
+ window.addEventListener("pointermove", onMove);
142
+ return () => {
143
+ window.removeEventListener("pointermove", onMove);
144
+ };
145
+ }, [instance, position]);
146
+ useLayoutEffect(() => {
147
+ if (visibleKey !== null) position(decodeHover(visibleKey).id);
148
+ }, [visibleKey, position]);
149
+ const target = visibleKey === null ? null : decodeHover(visibleKey);
150
+ const node = target?.kind === "node" ? instance.getNode(target.id) : void 0;
151
+ const edge = target?.kind === "edge" ? instance.getEdge(target.id) : void 0;
152
+ const item = node ?? edge;
153
+ const isEdge = edge !== void 0;
154
+ const title = item === void 0 ? "" : isEdge ? (props.getEdgeText ?? edgeLabelText)(edge) : nodeLabelText(node);
155
+ const custom = item === void 0 ? void 0 : isEdge ? props.renderEdge?.(edge) : props.render?.(node);
156
+ return /* @__PURE__ */ jsx("div", { ref: layerRef, "data-orbit-tooltip-layer": "", style: LAYER_STYLE, children: item !== void 0 ? /* @__PURE__ */ jsx(
157
+ "div",
158
+ {
159
+ ref: cardRef,
160
+ "data-orbit-tooltip": "",
161
+ ...isEdge ? { "data-orbit-tooltip-edge": "" } : {},
162
+ role: "tooltip",
163
+ className: props.className,
164
+ style: { ...CARD_STYLE, ...props.style },
165
+ children: custom !== void 0 ? custom : /* @__PURE__ */ jsxs(Fragment, { children: [
166
+ /* @__PURE__ */ jsx("div", { "data-orbit-tooltip-label": "", style: TITLE_STYLE, children: title }),
167
+ Object.entries(item.attrs ?? {}).filter(([key]) => key !== "label").slice(0, TOOLTIP_ATTR_ROWS).map(([key, value]) => /* @__PURE__ */ jsxs("div", { "data-orbit-tooltip-attr": key, style: ROW_STYLE, children: [
168
+ /* @__PURE__ */ jsx("span", { style: ROW_KEY_STYLE, children: key }),
169
+ /* @__PURE__ */ jsx("span", { style: ROW_VALUE_STYLE, children: attrValueText(value) })
170
+ ] }, key))
171
+ ] })
172
+ }
173
+ ) : null });
174
+ }
175
+
176
+ export { GraphTooltip, TOOLTIP_ATTR_ROWS };
177
+ //# sourceMappingURL=Tooltip.js.map
178
+ //# sourceMappingURL=Tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Tooltip/index.tsx"],"names":[],"mappings":"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","file":"Tooltip.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Tooltip` — `<GraphTooltip>`, the §16.9\n * hover card (S11-T12).\n *\n * Visibility rides the store hover slice (the §7.4 typed hover lane): a\n * non-null hover arms a `delayMs` timer (default 150); the card mounts when\n * it fires and unmounts IMMEDIATELY when hover clears — no flicker-out delay.\n *\n * BOTH hover namespaces are served: `hover.nodeId` and `hover.edgeId`. A node\n * WINS when both are set — it is the more specific target and sits on top of\n * its own links. Edges have no entry in the overlay position lane (that lane\n * tracks labeled NODE slots), so an edge card always rides the cursor\n * fallback, which is exactly right: the hovered edge is under the pointer.\n *\n * Positioning is imperative (transform writes on a ref — never a React\n * re-render per movement), fed by two lanes:\n * 1. the S7 overlay position lane (`instance.labels.subscribePositions` —\n * the same public seam <LabelLayer> renders from): when the hovered id is\n * in the tracked candidate set, each scheduler tick writes its fresh\n * container-relative screen x/y, so the card follows the node through\n * simulation drift and camera moves;\n * 2. the pointer fallback: when the hovered id is NOT label-tracked (the\n * common case — labels cover k ranked nodes), a window `pointermove`\n * listener (attached only while hovering) pins the card to the cursor\n * + a fixed offset. The engine's `spaceToScreen` is not reachable through\n * the public instance surface in v0.9, so cursor-anchoring is the\n * documented fallback — the hovered node is by definition under the\n * cursor.\n *\n * Default content: the target's label (`attrs.label` string, else id) plus up\n * to {@link TOOLTIP_ATTR_ROWS} attr rows (label excluded — it is the title),\n * every key/value rendered as TEXT NODES only (§14 untrusted-content rule;\n * hostile attr strings appear literally). `render` / `renderEdge` replace the\n * card content entirely, and `getEdgeText` retitles an edge card without\n * taking over its body — useful where the meaningful name lives in an\n * adapter-injected attr rather than `label` (an Omnigraph edge's relationship\n * type is at `attrs['orbit:type']`, for instance). The card is pointer-inert\n * (it must never steal canvas hover).\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { AcceptedEdge, GraphNode, NodeId } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { overlaySurface, useResolvedInstance } from '../shared';\n\nconst DELAY_MS_DEFAULT = 150;\n/** Max attr rows in the default card content. */\nexport const TOOLTIP_ATTR_ROWS = 6;\n/** Cursor-anchored offset (px) so the card never sits under the pointer. */\nconst POINTER_OFFSET_PX = 12;\n\nexport interface GraphTooltipProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Replaces the default card content for a NODE (label + attr rows). */\n render?: (node: GraphNode<any>) => ReactNode;\n /** Replaces the default card content for an EDGE. */\n renderEdge?: (edge: AcceptedEdge<any>) => ReactNode;\n /** Title text for an edge card. Default `attrs.label ?? id`. */\n getEdgeText?: (edge: AcceptedEdge<any>) => string;\n /** Set false to ignore edge hovers entirely (nodes only). Default true. */\n edges?: boolean;\n /** Hover → show delay in ms. Default 150. */\n delayMs?: number;\n className?: string;\n style?: CSSProperties;\n}\n\nconst LAYER_STYLE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n};\n\nconst CARD_STYLE: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n maxWidth: 280,\n padding: '6px 10px',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '12px/1.5 system-ui, sans-serif',\n pointerEvents: 'none',\n whiteSpace: 'nowrap',\n willChange: 'transform',\n};\n\nconst TITLE_STYLE: CSSProperties = {\n fontWeight: 600,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nconst ROW_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 12,\n overflow: 'hidden',\n};\n\nconst ROW_KEY_STYLE: CSSProperties = { opacity: 0.65 };\nconst ROW_VALUE_STYLE: CSSProperties = { overflow: 'hidden', textOverflow: 'ellipsis' };\n\n/** Same derivation as the core §14 label default: `attrs.label ?? id`. */\nfunction nodeLabelText(node: GraphNode<any>): string {\n const attrs = node.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(node.id) : String(label);\n}\n\n/** Attr value → display TEXT (never markup): primitives verbatim, structured\n * values JSON (guarded — cycles degrade to the String form). */\nfunction attrValueText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'object' && value !== null) {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n/** The hovered target, node-first. Encoded as a STRING (`'node\\u0000id'`)\n * rather than an object so `useSyncExternalStore` compares by value — a fresh\n * object every publication would loop. */\ntype HoverKey = string;\n\nfunction encodeHover(kind: 'node' | 'edge', id: string): HoverKey {\n return `${kind}\\u0000${id}`;\n}\n\nfunction decodeHover(key: HoverKey): { kind: 'node' | 'edge'; id: string } {\n const at = key.indexOf('\\u0000');\n return { kind: key.slice(0, at) as 'node' | 'edge', id: key.slice(at + 1) };\n}\n\nfunction useHoveredTarget(instance: AnyGraphInstance, edges: boolean): HoverKey | null {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback((): HoverKey | null => {\n const hover = instance.store.getState().hover;\n // Node wins: it is the more specific target and sits above its links.\n if (hover.nodeId !== null) return encodeHover('node', hover.nodeId);\n if (edges && hover.edgeId !== null) return encodeHover('edge', hover.edgeId);\n return null;\n }, [instance, edges]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** Same derivation as the node default: `attrs.label ?? id`. */\nfunction edgeLabelText(edge: AcceptedEdge<any>): string {\n const attrs = edge.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(edge.id) : String(label);\n}\n\nexport function GraphTooltip(props: GraphTooltipProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphTooltip>');\n const labels = overlaySurface(instance).labels;\n const delayMs = props.delayMs ?? DELAY_MS_DEFAULT;\n\n const edgesEnabled = props.edges ?? true;\n const hoverKey = useHoveredTarget(instance, edgesEnabled);\n /** The target the mounted card shows — hoverKey promoted after delayMs. */\n const [visibleKey, setVisibleKey] = useState<HoverKey | null>(null);\n\n const layerRef = useRef<HTMLDivElement | null>(null);\n const cardRef = useRef<HTMLDivElement | null>(null);\n /** Freshest label-lane screen position per tracked id (container-relative). */\n const trackedRef = useRef(new Map<NodeId, readonly [number, number]>());\n /** Freshest cursor position (container-relative, offset applied). */\n const pointerRef = useRef<readonly [number, number] | null>(null);\n\n /** One place decides the transform: label lane wins when it tracks the id. */\n const position = useCallback(\n (id: NodeId): void => {\n const el = cardRef.current;\n if (el === null) return;\n const at = trackedRef.current.get(id) ?? pointerRef.current;\n if (at === null || at === undefined) return;\n el.style.transform = `translate3d(${at[0]}px, ${at[1]}px, 0)`;\n },\n [],\n );\n\n // Delay lane: arm on hover, cancel/hide immediately on unhover.\n useEffect(() => {\n if (hoverKey === null) {\n setVisibleKey(null);\n return undefined;\n }\n const timer = setTimeout(() => {\n setVisibleKey(hoverKey);\n }, delayMs);\n return () => {\n clearTimeout(timer);\n };\n }, [hoverKey, delayMs]);\n\n // Position lane 1: the S7 overlay position lane (same seam as LabelLayer).\n useLayoutEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribePositions((list) => {\n const tracked = trackedRef.current;\n tracked.clear();\n for (const p of list) tracked.set(p.id, [p.x, p.y]);\n const id = instance.store.getState().hover.nodeId;\n if (id !== null && tracked.has(id)) position(id);\n });\n }, [labels, instance, position]);\n\n // Position lane 2: cursor fallback. The listener lives for the component's\n // lifetime (storing two numbers per move) so a hover that lands WITHOUT a\n // subsequent move still knows where the cursor already is; it only writes\n // the card transform while a node is hovered and the id is untracked.\n useEffect(() => {\n const onMove = (e: PointerEvent): void => {\n // Container-relative like the label lane: subtract the positioned\n // ancestor's origin (zero-rect hosts — jsdom — degrade to client px).\n const host = layerRef.current?.offsetParent;\n const origin = host instanceof Element ? host.getBoundingClientRect() : null;\n pointerRef.current = [\n e.clientX - (origin?.left ?? 0) + POINTER_OFFSET_PX,\n e.clientY - (origin?.top ?? 0) + POINTER_OFFSET_PX,\n ];\n const hover = instance.store.getState().hover;\n if (hover.nodeId !== null) {\n if (!trackedRef.current.has(hover.nodeId)) position(hover.nodeId);\n } else if (hover.edgeId !== null) {\n // Edges never appear in the label lane — always cursor-anchored.\n position(hover.edgeId);\n }\n };\n window.addEventListener('pointermove', onMove);\n return () => {\n window.removeEventListener('pointermove', onMove);\n };\n }, [instance, position]);\n\n // Place the card the moment it mounts (before paint — no corner flash).\n useLayoutEffect(() => {\n if (visibleKey !== null) position(decodeHover(visibleKey).id);\n }, [visibleKey, position]);\n\n const target = visibleKey === null ? null : decodeHover(visibleKey);\n const node =\n target?.kind === 'node'\n ? (instance.getNode(target.id) as GraphNode<any> | undefined)\n : undefined;\n const edge =\n target?.kind === 'edge'\n ? (instance.getEdge(target.id) as AcceptedEdge<any> | undefined)\n : undefined;\n const item = node ?? edge;\n const isEdge = edge !== undefined;\n\n /** Title + custom body, shared by both kinds — only the text derivation and\n * the `data-orbit-tooltip-edge` marker differ. Computed ONLY when a target\n * actually resolved: `visibleKey` can name an id the model no longer holds\n * (a hover surviving a dataset swap), and `target` is null whenever nothing\n * is hovered at all. */\n const title =\n item === undefined\n ? ''\n : isEdge\n ? (props.getEdgeText ?? edgeLabelText)(edge)\n : nodeLabelText(node as GraphNode<any>);\n const custom =\n item === undefined\n ? undefined\n : isEdge\n ? props.renderEdge?.(edge)\n : props.render?.(node as GraphNode<any>);\n\n return (\n <div ref={layerRef} data-orbit-tooltip-layer=\"\" style={LAYER_STYLE}>\n {item !== undefined ? (\n <div\n ref={cardRef}\n data-orbit-tooltip=\"\"\n {...(isEdge ? { 'data-orbit-tooltip-edge': '' } : {})}\n role=\"tooltip\"\n className={props.className}\n style={{ ...CARD_STYLE, ...props.style }}\n >\n {custom !== undefined ? (\n custom\n ) : (\n <>\n {/* Untrusted text renders as TEXT NODES only (§14). */}\n <div data-orbit-tooltip-label=\"\" style={TITLE_STYLE}>\n {title}\n </div>\n {Object.entries((item.attrs ?? {}) as Record<string, unknown>)\n .filter(([key]) => key !== 'label')\n .slice(0, TOOLTIP_ATTR_ROWS)\n .map(([key, value]) => (\n <div key={key} data-orbit-tooltip-attr={key} style={ROW_STYLE}>\n <span style={ROW_KEY_STYLE}>{key}</span>\n <span style={ROW_VALUE_STYLE}>{attrValueText(value)}</span>\n </div>\n ))}\n </>\n )}\n </div>\n ) : null}\n </div>\n );\n}\n"]}