@robr0/design-system 0.9.0 → 0.11.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/README.md +16 -10
- package/components/AgentPlan/AgentPlan.css +14 -3
- package/components/AiButton/AiButton.css +1 -1
- package/components/Alert/Alert.css +9 -0
- package/components/AlertDialog/AlertDialog.css +3 -1
- package/components/AlertDialog/AlertDialog.js +2 -3
- package/components/AppSidebar/AppSidebar.css +22 -3
- package/components/AppSidebar/AppSidebar.d.ts +6 -2
- package/components/AppSidebar/AppSidebar.js +67 -36
- package/components/Card/Card.css +7 -2
- package/components/CardStack/CardStack.css +106 -0
- package/components/CardStack/CardStack.d.ts +44 -0
- package/components/CardStack/CardStack.js +140 -0
- package/components/Carousel/Carousel.css +9 -0
- package/components/Carousel/Carousel.js +37 -4
- package/components/Chart/AreaChart.js +6 -5
- package/components/Chart/BarChart.js +1 -1
- package/components/Chart/LineChart.js +4 -3
- package/components/Chart/PieChart.js +2 -15
- package/components/Chart/RadarChart.js +5 -4
- package/components/Chart/RadialChart.js +2 -15
- package/components/Chart/ScatterChart.js +3 -13
- package/components/Chart/StackedBarChart.js +2 -15
- package/components/Chart/Treemap.js +3 -17
- package/components/Chart/palette.d.ts +1 -0
- package/components/Chart/palette.js +19 -0
- package/components/ChatMarker/ChatMarker.css +9 -0
- package/components/ChatMessage/ChatMessage.css +16 -0
- package/components/ChatThread/ChatThread.js +2 -2
- package/components/Chip/Chip.css +9 -0
- package/components/CodeBlock/CodeBlock.css +10 -1
- package/components/CodeBlock/CodeBlock.js +2 -1
- package/components/ColorPicker/ColorPicker.js +10 -0
- package/components/Combobox/Combobox.css +9 -0
- package/components/CommandPalette/CommandPalette.css +9 -0
- package/components/CommandPalette/CommandPalette.js +31 -5
- package/components/Composer/Composer.css +118 -0
- package/components/Composer/Composer.d.ts +7 -0
- package/components/Composer/Composer.js +7 -1
- package/components/ContactCard/ContactCard.css +9 -0
- package/components/ContextMenu/ContextMenu.js +39 -3
- package/components/DatePicker/DatePicker.js +16 -0
- package/components/Dialog/Dialog.css +3 -1
- package/components/Dialog/Dialog.js +5 -4
- package/components/DocumentChip/DocumentChip.css +9 -0
- package/components/Dropdown/Dropdown.js +22 -3
- package/components/DropdownMenu/DropdownMenu.js +43 -6
- package/components/EmptyState/EmptyState.css +9 -0
- package/components/FileInput/FileInput.css +9 -0
- package/components/Globe/Globe.css +155 -0
- package/components/Globe/Globe.d.ts +96 -0
- package/components/Globe/Globe.js +415 -0
- package/components/InterruptCard/InterruptCard.css +27 -17
- package/components/InterruptCard/InterruptCard.d.ts +1 -1
- package/components/InterruptCard/InterruptCard.js +1 -1
- package/components/MapCallout/MapCallout.css +46 -0
- package/components/MapCallout/MapCallout.d.ts +29 -0
- package/components/MapCallout/MapCallout.js +17 -0
- package/components/MapLegend/MapLegend.css +88 -0
- package/components/MapLegend/MapLegend.d.ts +39 -0
- package/components/MapLegend/MapLegend.js +68 -0
- package/components/MessageActions/MessageActions.css +9 -0
- package/components/MessageCard/MessageCard.css +68 -16
- package/components/MessageCard/MessageCard.d.ts +1 -1
- package/components/MessageCard/MessageCard.js +6 -4
- package/components/ModelPicker/ModelPicker.css +4 -1
- package/components/ModelPicker/ModelPicker.js +43 -1
- package/components/NotificationCenter/NotificationCenter.css +9 -0
- package/components/NotificationCenter/NotificationCenter.js +26 -10
- package/components/NumberInput/NumberInput.css +9 -0
- package/components/Popover/Popover.css +3 -1
- package/components/Popover/Popover.js +2 -1
- package/components/SectionTitle/SectionTitle.css +8 -0
- package/components/SectionTitle/SectionTitle.d.ts +3 -1
- package/components/SectionTitle/SectionTitle.js +6 -1
- package/components/SourceChip/SourceChip.css +9 -0
- package/components/Stat/Stat.css +9 -0
- package/components/Stepper/Stepper.css +9 -0
- package/components/TagInput/TagInput.css +9 -0
- package/components/TimePicker/TimePicker.js +4 -1
- package/components/Toast/Toast.css +32 -11
- package/components/Toast/Toast.js +5 -4
- package/components/ToggleGroup/ToggleGroup.css +9 -0
- package/components/ToolCall/ToolCall.css +18 -6
- package/components/Tooltip/Tooltip.js +20 -16
- package/components/registry.json +37 -0
- package/components/registry.json.d.ts +37 -0
- package/components/registry.json.js +2 -2
- package/index.d.ts +4 -0
- package/index.js +8 -0
- package/package.json +5 -1
- package/tokens/motion.d.ts +23 -0
- package/tokens/motion.js +14 -0
- package/tokens/registry.json +17 -0
- package/tokens/registry.json.d.ts +17 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +28 -13
- package/tokens/tokens-light.css +26 -12
- package/tokens/tokens-typography.css +20 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
-
import React, { useState, useRef, useCallback, useEffect } from "react";
|
|
3
|
+
import React, { useState, useRef, useId, useCallback, useEffect } from "react";
|
|
4
|
+
import { MOTION_HOVER_HIDE_DELAY_MS } from "../../tokens/motion.js";
|
|
4
5
|
import "./DropdownMenu.css";
|
|
5
6
|
import "../../fonts/material-symbols.css";
|
|
6
7
|
function collectItems(entries) {
|
|
@@ -15,7 +16,7 @@ function collectItems(entries) {
|
|
|
15
16
|
}
|
|
16
17
|
return result;
|
|
17
18
|
}
|
|
18
|
-
const SubMenuItem = ({ item, baseClass, focused, size, onActivate }) => {
|
|
19
|
+
const SubMenuItem = ({ item, baseClass, itemId, focused, size, onActivate }) => {
|
|
19
20
|
const [subOpen, setSubOpen] = useState(false);
|
|
20
21
|
const timeoutRef = useRef(null);
|
|
21
22
|
const handleMouseEnter = () => {
|
|
@@ -23,7 +24,7 @@ const SubMenuItem = ({ item, baseClass, focused, size, onActivate }) => {
|
|
|
23
24
|
setSubOpen(true);
|
|
24
25
|
};
|
|
25
26
|
const handleMouseLeave = () => {
|
|
26
|
-
timeoutRef.current = setTimeout(() => setSubOpen(false),
|
|
27
|
+
timeoutRef.current = setTimeout(() => setSubOpen(false), MOTION_HOVER_HIDE_DELAY_MS);
|
|
27
28
|
};
|
|
28
29
|
useEffect(() => {
|
|
29
30
|
return () => {
|
|
@@ -45,6 +46,7 @@ const SubMenuItem = ({ item, baseClass, focused, size, onActivate }) => {
|
|
|
45
46
|
return /* @__PURE__ */ jsxs(
|
|
46
47
|
"li",
|
|
47
48
|
{
|
|
49
|
+
id: itemId,
|
|
48
50
|
className: classes,
|
|
49
51
|
role: "menuitem",
|
|
50
52
|
"aria-haspopup": "menu",
|
|
@@ -67,6 +69,7 @@ const SubMenuItem = ({ item, baseClass, focused, size, onActivate }) => {
|
|
|
67
69
|
{
|
|
68
70
|
entries: item.children,
|
|
69
71
|
baseClass,
|
|
72
|
+
itemIdBase: `${itemId}-sub`,
|
|
70
73
|
size,
|
|
71
74
|
isSubmenu: true,
|
|
72
75
|
onItemActivate: onActivate
|
|
@@ -79,6 +82,7 @@ const SubMenuItem = ({ item, baseClass, focused, size, onActivate }) => {
|
|
|
79
82
|
const MenuPanel = ({
|
|
80
83
|
entries,
|
|
81
84
|
baseClass,
|
|
85
|
+
itemIdBase,
|
|
82
86
|
size,
|
|
83
87
|
isSubmenu = false,
|
|
84
88
|
onItemActivate
|
|
@@ -88,14 +92,15 @@ const MenuPanel = ({
|
|
|
88
92
|
isSubmenu ? `${baseClass}__submenu` : "",
|
|
89
93
|
size === "compact" ? `${baseClass}__panel--compact` : ""
|
|
90
94
|
].filter(Boolean).join(" ");
|
|
91
|
-
const renderEntry = (entry, index) => {
|
|
95
|
+
const renderEntry = (entry, index, idBase) => {
|
|
96
|
+
const entryId = `${idBase}-${index}`;
|
|
92
97
|
if (entry.type === "separator") {
|
|
93
98
|
return /* @__PURE__ */ jsx("li", { className: `${baseClass}__separator`, role: "separator" }, `sep-${index}`);
|
|
94
99
|
}
|
|
95
100
|
if (entry.type === "group") {
|
|
96
101
|
return /* @__PURE__ */ jsxs("li", { className: `${baseClass}__group`, role: "group", "aria-label": entry.label, children: [
|
|
97
102
|
/* @__PURE__ */ jsx("span", { className: `${baseClass}__group-label`, children: entry.label }),
|
|
98
|
-
/* @__PURE__ */ jsx("ul", { className: `${baseClass}__group-list`, role: "group", children: entry.items.map((subEntry, subIndex) => renderEntry(subEntry, subIndex)) })
|
|
103
|
+
/* @__PURE__ */ jsx("ul", { className: `${baseClass}__group-list`, role: "group", children: entry.items.map((subEntry, subIndex) => renderEntry(subEntry, subIndex, entryId)) })
|
|
99
104
|
] }, `group-${index}`);
|
|
100
105
|
}
|
|
101
106
|
if (entry.children && entry.children.length > 0) {
|
|
@@ -104,6 +109,7 @@ const MenuPanel = ({
|
|
|
104
109
|
{
|
|
105
110
|
item: entry,
|
|
106
111
|
baseClass,
|
|
112
|
+
itemId: entryId,
|
|
107
113
|
focused: false,
|
|
108
114
|
size,
|
|
109
115
|
onActivate: onItemActivate
|
|
@@ -119,6 +125,7 @@ const MenuPanel = ({
|
|
|
119
125
|
return /* @__PURE__ */ jsxs(
|
|
120
126
|
"li",
|
|
121
127
|
{
|
|
128
|
+
id: entryId,
|
|
122
129
|
className: itemClasses,
|
|
123
130
|
role: "menuitem",
|
|
124
131
|
"aria-disabled": entry.disabled || void 0,
|
|
@@ -140,7 +147,7 @@ const MenuPanel = ({
|
|
|
140
147
|
`item-${index}`
|
|
141
148
|
);
|
|
142
149
|
};
|
|
143
|
-
return /* @__PURE__ */ jsx("ul", { className: panelClasses, role: "menu", children: entries.map((entry, index) => renderEntry(entry, index)) });
|
|
150
|
+
return /* @__PURE__ */ jsx("ul", { className: panelClasses, role: "menu", children: entries.map((entry, index) => renderEntry(entry, index, itemIdBase)) });
|
|
144
151
|
};
|
|
145
152
|
const DropdownMenu = ({
|
|
146
153
|
trigger,
|
|
@@ -153,9 +160,12 @@ const DropdownMenu = ({
|
|
|
153
160
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
154
161
|
const rootRef = useRef(null);
|
|
155
162
|
const panelRef = useRef(null);
|
|
163
|
+
const generatedId = useId();
|
|
156
164
|
const baseClass = "ds-dropdown-menu";
|
|
157
165
|
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
158
166
|
const flatItems = collectItems(items);
|
|
167
|
+
const menuId = `${generatedId}-menu`;
|
|
168
|
+
const activeDescendantId = isOpen && focusedIndex >= 0 ? `${generatedId}-item-${focusedIndex}` : void 0;
|
|
159
169
|
const handleToggle = () => {
|
|
160
170
|
setIsOpen((prev) => {
|
|
161
171
|
if (!prev) setFocusedIndex(-1);
|
|
@@ -214,6 +224,26 @@ const DropdownMenu = ({
|
|
|
214
224
|
});
|
|
215
225
|
}
|
|
216
226
|
break;
|
|
227
|
+
case "Home":
|
|
228
|
+
if (isOpen) {
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
setFocusedIndex((prev) => {
|
|
231
|
+
let next = 0;
|
|
232
|
+
while (next < flatItems.length && flatItems[next].disabled) next++;
|
|
233
|
+
return next < flatItems.length ? next : prev;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
case "End":
|
|
238
|
+
if (isOpen) {
|
|
239
|
+
e.preventDefault();
|
|
240
|
+
setFocusedIndex((prev) => {
|
|
241
|
+
let next = flatItems.length - 1;
|
|
242
|
+
while (next >= 0 && flatItems[next].disabled) next--;
|
|
243
|
+
return next >= 0 ? next : prev;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
217
247
|
case "Escape":
|
|
218
248
|
case "Tab":
|
|
219
249
|
handleClose();
|
|
@@ -248,6 +278,7 @@ const DropdownMenu = ({
|
|
|
248
278
|
{
|
|
249
279
|
item: entry,
|
|
250
280
|
baseClass,
|
|
281
|
+
itemId: `${generatedId}-item-${myIndex}`,
|
|
251
282
|
focused: isFocused,
|
|
252
283
|
size,
|
|
253
284
|
onActivate: handleClose
|
|
@@ -264,6 +295,7 @@ const DropdownMenu = ({
|
|
|
264
295
|
return /* @__PURE__ */ jsxs(
|
|
265
296
|
"li",
|
|
266
297
|
{
|
|
298
|
+
id: `${generatedId}-item-${myIndex}`,
|
|
267
299
|
className: itemClasses,
|
|
268
300
|
role: "menuitem",
|
|
269
301
|
"aria-disabled": entry.disabled || void 0,
|
|
@@ -295,6 +327,8 @@ const DropdownMenu = ({
|
|
|
295
327
|
/* @__PURE__ */ jsx("div", { className: `${baseClass}__trigger`, children: React.isValidElement(trigger) ? React.cloneElement(trigger, {
|
|
296
328
|
"aria-haspopup": "menu",
|
|
297
329
|
"aria-expanded": isOpen,
|
|
330
|
+
"aria-controls": isOpen ? menuId : void 0,
|
|
331
|
+
"aria-activedescendant": activeDescendantId,
|
|
298
332
|
onClick: handleToggle,
|
|
299
333
|
onKeyDown: handleKeyDown
|
|
300
334
|
}) : /* @__PURE__ */ jsx(
|
|
@@ -303,6 +337,8 @@ const DropdownMenu = ({
|
|
|
303
337
|
type: "button",
|
|
304
338
|
"aria-haspopup": "menu",
|
|
305
339
|
"aria-expanded": isOpen,
|
|
340
|
+
"aria-controls": isOpen ? menuId : void 0,
|
|
341
|
+
"aria-activedescendant": activeDescendantId,
|
|
306
342
|
onClick: handleToggle,
|
|
307
343
|
onKeyDown: handleKeyDown,
|
|
308
344
|
children: trigger
|
|
@@ -311,6 +347,7 @@ const DropdownMenu = ({
|
|
|
311
347
|
isOpen && /* @__PURE__ */ jsx(
|
|
312
348
|
"ul",
|
|
313
349
|
{
|
|
350
|
+
id: menuId,
|
|
314
351
|
className: panelClasses,
|
|
315
352
|
role: "menu",
|
|
316
353
|
ref: (el) => {
|
|
@@ -113,3 +113,12 @@
|
|
|
113
113
|
line-height: var(--font-paragraph-em-line-height);
|
|
114
114
|
letter-spacing: var(--font-paragraph-em-letter-spacing);
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
/* Icons take the colour of the text around them. Explicit, not just
|
|
118
|
+
inherited by default: a consumer stylesheet that sets a colour on
|
|
119
|
+
.material-symbols-rounded itself (a common global default) would
|
|
120
|
+
otherwise override inheritance and detach the icon from the state
|
|
121
|
+
colours this component sets on its parents. */
|
|
122
|
+
.ds-empty-state .material-symbols-rounded {
|
|
123
|
+
color: inherit;
|
|
124
|
+
}
|
|
@@ -270,3 +270,12 @@
|
|
|
270
270
|
.ds-file-input--compact .ds-file-input__file {
|
|
271
271
|
padding: var(--padding-xs) var(--padding-sm); /* 6px 8px */
|
|
272
272
|
}
|
|
273
|
+
|
|
274
|
+
/* Icons take the colour of the text around them. Explicit, not just
|
|
275
|
+
inherited by default: a consumer stylesheet that sets a colour on
|
|
276
|
+
.material-symbols-rounded itself (a common global default) would
|
|
277
|
+
otherwise override inheritance and detach the icon from the state
|
|
278
|
+
colours this component sets on its parents. */
|
|
279
|
+
.ds-file-input .material-symbols-rounded {
|
|
280
|
+
color: inherit;
|
|
281
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
GLOBE COMPONENT
|
|
3
|
+
An orthographic globe: graticule, markers and
|
|
4
|
+
great-circle arcs. Every colour is a token; the
|
|
5
|
+
stroke widths are SVG geometry in viewBox units,
|
|
6
|
+
like Sparkline's, and scale with the drawing.
|
|
7
|
+
============================================ */
|
|
8
|
+
|
|
9
|
+
/* Base */
|
|
10
|
+
|
|
11
|
+
.ds-globe {
|
|
12
|
+
position: relative;
|
|
13
|
+
display: block;
|
|
14
|
+
width: 100%;
|
|
15
|
+
aspect-ratio: 1;
|
|
16
|
+
color: var(--color-text-primary);
|
|
17
|
+
border-radius: var(--radius-md);
|
|
18
|
+
outline: none;
|
|
19
|
+
touch-action: none;
|
|
20
|
+
user-select: none;
|
|
21
|
+
-webkit-user-select: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.ds-globe:focus-visible {
|
|
25
|
+
outline: var(--border-md) solid var(--color-action-primary-bg);
|
|
26
|
+
outline-offset: var(--padding-xxs);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ds-globe--interactive {
|
|
30
|
+
cursor: grab;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ds-globe--dragging {
|
|
34
|
+
cursor: grabbing;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.ds-globe__svg {
|
|
38
|
+
display: block;
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
overflow: visible;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Sphere */
|
|
45
|
+
|
|
46
|
+
.ds-globe__limb {
|
|
47
|
+
fill: none;
|
|
48
|
+
stroke: var(--color-text-tertiary);
|
|
49
|
+
stroke-width: 1;
|
|
50
|
+
opacity: 0.6;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ds-globe__graticule {
|
|
54
|
+
fill: none;
|
|
55
|
+
stroke: var(--color-divider);
|
|
56
|
+
stroke-width: 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ds-globe__graticule-back {
|
|
60
|
+
fill: none;
|
|
61
|
+
stroke: var(--color-divider);
|
|
62
|
+
stroke-width: 1;
|
|
63
|
+
stroke-dasharray: 2 4;
|
|
64
|
+
opacity: 0.5;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Arcs — the default gradient runs along the cobalt-to-violet end of the
|
|
68
|
+
chart series; a per-arc `color` replaces it with a flat stroke. */
|
|
69
|
+
|
|
70
|
+
.ds-globe__arc-stop-start {
|
|
71
|
+
stop-color: var(--color-chart-series-7);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.ds-globe__arc-stop-end {
|
|
75
|
+
stop-color: var(--color-chart-series-5);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.ds-globe__arc {
|
|
79
|
+
fill: none;
|
|
80
|
+
stroke-width: 1.5;
|
|
81
|
+
stroke-linecap: round;
|
|
82
|
+
stroke-linejoin: round;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ds-globe__arc-back {
|
|
86
|
+
fill: none;
|
|
87
|
+
stroke-width: 1;
|
|
88
|
+
stroke-linecap: round;
|
|
89
|
+
opacity: 0.15;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Markers */
|
|
93
|
+
|
|
94
|
+
.ds-globe__point {
|
|
95
|
+
color: var(--color-text-secondary);
|
|
96
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.ds-globe__point--back {
|
|
100
|
+
opacity: 0.3;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ds-globe__point--active,
|
|
104
|
+
.ds-globe__point:hover {
|
|
105
|
+
color: var(--color-text-primary);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.ds-globe__hit {
|
|
109
|
+
fill: transparent;
|
|
110
|
+
stroke: none;
|
|
111
|
+
pointer-events: all;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.ds-globe--interactive .ds-globe__point {
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.ds-globe__glyph {
|
|
119
|
+
fill: none;
|
|
120
|
+
stroke: currentColor;
|
|
121
|
+
stroke-width: 1.5;
|
|
122
|
+
stroke-linecap: round;
|
|
123
|
+
transform-box: fill-box;
|
|
124
|
+
transform-origin: center;
|
|
125
|
+
transition: transform var(--motion-duration-fast) var(--motion-ease-emphasized);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.ds-globe__point--active .ds-globe__glyph {
|
|
129
|
+
transform: scale(1.4);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.ds-globe__label {
|
|
133
|
+
font-family: var(--font-family-code);
|
|
134
|
+
font-size: var(--font-caption-size);
|
|
135
|
+
fill: var(--color-text-tertiary);
|
|
136
|
+
pointer-events: none;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.ds-globe__point--active .ds-globe__label {
|
|
140
|
+
fill: var(--color-text-primary);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Callout overlay — positioned at the marker, opening away from the centre. */
|
|
144
|
+
|
|
145
|
+
.ds-globe__callout {
|
|
146
|
+
position: absolute;
|
|
147
|
+
pointer-events: none;
|
|
148
|
+
padding: 0 var(--padding-md);
|
|
149
|
+
transform: translate(0, -100%);
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.ds-globe__callout[data-side='left'] {
|
|
154
|
+
transform: translate(-100%, -100%);
|
|
155
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** A place on the globe. */
|
|
3
|
+
export interface GlobePoint {
|
|
4
|
+
/** Stable identifier, referenced by arcs and by `activePointId`. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Latitude in degrees, north positive. */
|
|
7
|
+
lat: number;
|
|
8
|
+
/** Longitude in degrees, east positive. */
|
|
9
|
+
lng: number;
|
|
10
|
+
/** Short label drawn beside the marker when `showLabels` is on. */
|
|
11
|
+
label?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Marker glyph. `point` is a cross (something that moves or listens),
|
|
14
|
+
* `anchor` is a square (something fixed). The glyph is the only difference.
|
|
15
|
+
*/
|
|
16
|
+
kind?: 'point' | 'anchor';
|
|
17
|
+
}
|
|
18
|
+
/** A great-circle arc between two points. */
|
|
19
|
+
export interface GlobeArc {
|
|
20
|
+
/** `id` of the point the arc leaves. */
|
|
21
|
+
from: string;
|
|
22
|
+
/** `id` of the point the arc reaches. */
|
|
23
|
+
to: string;
|
|
24
|
+
/**
|
|
25
|
+
* Stroke colour, any CSS colour (`var(--color-chart-series-3)` works).
|
|
26
|
+
* Omit for the default cobalt-to-violet gradient along the arc.
|
|
27
|
+
*/
|
|
28
|
+
color?: string;
|
|
29
|
+
/** How far the arc lifts off the surface at its midpoint, as a fraction of the radius. */
|
|
30
|
+
altitude?: number;
|
|
31
|
+
}
|
|
32
|
+
/** Where the globe is looking: `[longitude, latitude]` of the centre, in degrees. */
|
|
33
|
+
export type GlobeRotation = [number, number];
|
|
34
|
+
/** Props owned by Globe itself — everything else falls through to the root `<div>`. */
|
|
35
|
+
type GlobeOwnProps = {
|
|
36
|
+
/** The places to mark. */
|
|
37
|
+
points?: GlobePoint[];
|
|
38
|
+
/** The arcs to draw between them. An arc whose endpoint is not in `points` is skipped. */
|
|
39
|
+
arcs?: GlobeArc[];
|
|
40
|
+
/**
|
|
41
|
+
* Controlled view: `[longitude, latitude]` of the centre, in degrees.
|
|
42
|
+
* Pair with `onRotationChange`. Omit to let the globe own its rotation.
|
|
43
|
+
*/
|
|
44
|
+
rotation?: GlobeRotation;
|
|
45
|
+
/** Initial view when uncontrolled. */
|
|
46
|
+
defaultRotation?: GlobeRotation;
|
|
47
|
+
/** Fires whenever the view changes — drag, keys, or the auto-rotation. */
|
|
48
|
+
onRotationChange?: (rotation: GlobeRotation) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Spin slowly on its own, in degrees per second. Pauses while the pointer
|
|
51
|
+
* is over the globe or it has focus, and never runs under
|
|
52
|
+
* `prefers-reduced-motion`. `0` switches it off.
|
|
53
|
+
*/
|
|
54
|
+
autoRotate?: number;
|
|
55
|
+
/** Drag to rotate, and rotate with the arrow keys (or W, A, S, D) when focused. */
|
|
56
|
+
interactive?: boolean;
|
|
57
|
+
/** Degrees between graticule lines. `0` removes the graticule. */
|
|
58
|
+
graticuleStep?: number;
|
|
59
|
+
/** Draw each point's `label` beside its marker. */
|
|
60
|
+
showLabels?: boolean;
|
|
61
|
+
/** The point to single out: its marker enlarges and its callout renders. */
|
|
62
|
+
activePointId?: string;
|
|
63
|
+
/** Fires as the pointer enters a marker, and with `null` as it leaves. */
|
|
64
|
+
onPointHover?: (point: GlobePoint | null) => void;
|
|
65
|
+
/** Fires when a marker is clicked. */
|
|
66
|
+
onPointClick?: (point: GlobePoint) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Renders the annotation for the active (or hovered) point. It is placed
|
|
69
|
+
* beside the marker in an HTML overlay, so any markup works; MapCallout is
|
|
70
|
+
* the intended filling. The overlay carries `data-side="left"|"right"` for
|
|
71
|
+
* which side of the marker it sits on.
|
|
72
|
+
*/
|
|
73
|
+
renderCallout?: (point: GlobePoint) => React.ReactNode;
|
|
74
|
+
/**
|
|
75
|
+
* Accessible name for the globe, e.g. "Listening points across the
|
|
76
|
+
* network". The point and arc counts are appended for screen readers.
|
|
77
|
+
*/
|
|
78
|
+
label?: string;
|
|
79
|
+
/** Additional CSS classes */
|
|
80
|
+
className?: string;
|
|
81
|
+
};
|
|
82
|
+
export interface GlobeProps extends GlobeOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof GlobeOwnProps | 'children'> {
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Globe — an orthographic globe drawn in SVG, for showing where things are
|
|
86
|
+
* and what connects them: a graticule, markers at lat/lng, and great-circle
|
|
87
|
+
* arcs between them. No map library, no tile server, no land data; the
|
|
88
|
+
* graticule alone gives the sphere its shape, which is the point — this is
|
|
89
|
+
* for the geometry of a network, not for navigation.
|
|
90
|
+
*
|
|
91
|
+
* Colour comes from the tokens: the graticule in the divider colour, markers
|
|
92
|
+
* in the text colours, arcs along the cobalt-to-violet chart series. Rotate
|
|
93
|
+
* it by dragging, with the arrow keys when focused, or let it turn on its own.
|
|
94
|
+
*/
|
|
95
|
+
export declare const Globe: React.ForwardRefExoticComponent<GlobeProps & React.RefAttributes<HTMLDivElement>>;
|
|
96
|
+
export {};
|