@profullstack/hqtui-demo 0.3.0 → 0.4.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 +8 -2
- package/dist/main.js +23 -7
- package/dist/main.js.map +1 -1
- package/dist/screens/components.js +3 -3
- package/dist/screens/components.js.map +1 -1
- package/dist/screens/dashboard.js +2 -1
- package/dist/screens/dashboard.js.map +1 -1
- package/dist/screens/graphics.js +3 -3
- package/dist/screens/graphics.js.map +1 -1
- package/dist/screens/index.js +1 -0
- package/dist/screens/index.js.map +1 -1
- package/dist/screens/network.js +2 -2
- package/dist/screens/network.js.map +1 -1
- package/dist/screens/services.js +2 -2
- package/dist/screens/services.js.map +1 -1
- package/dist/screens/sessions.js +2 -2
- package/dist/screens/sessions.js.map +1 -1
- package/dist/screens/themes.js +3 -1
- package/dist/screens/themes.js.map +1 -1
- package/dist/screens/traffic.js +3 -3
- package/dist/screens/traffic.js.map +1 -1
- package/dist/screens/world.js +97 -0
- package/dist/screens/world.js.map +1 -0
- package/dist/state.js +12 -1
- package/dist/state.js.map +1 -1
- package/package.json +2 -2
- package/src/main.ts +17 -7
- package/src/screens/components.ts +3 -3
- package/src/screens/dashboard.ts +2 -1
- package/src/screens/graphics.ts +3 -3
- package/src/screens/index.ts +1 -0
- package/src/screens/network.ts +2 -2
- package/src/screens/services.ts +2 -2
- package/src/screens/sessions.ts +2 -2
- package/src/screens/themes.ts +3 -2
- package/src/screens/traffic.ts +3 -3
- package/src/screens/world.ts +102 -0
- package/src/state.ts +18 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Container, Theme } from "@profullstack/hqtui";
|
|
2
|
+
import { WORLD_COUNTRIES, WORLD_X, WORLD_Y, countryBounds } from "@profullstack/hqtui";
|
|
3
|
+
import { focusPane, pane, panelGap, scrollPane, type DemoState } from "../state.ts";
|
|
4
|
+
|
|
5
|
+
/** How many coordinates a country's outlines are drawn from. */
|
|
6
|
+
function pointCount(rings: number[][]): number {
|
|
7
|
+
let total = 0;
|
|
8
|
+
for (const ring of rings) total += ring.length / 2;
|
|
9
|
+
return total;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function degrees(value: number, positive: string, negative: string): string {
|
|
13
|
+
return `${Math.abs(value).toFixed(1)}°${value >= 0 ? positive : negative}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The clickable world map.
|
|
18
|
+
*
|
|
19
|
+
* The country list and the map are two views of one selection rather than two
|
|
20
|
+
* selections that have to be kept in step: the list's pane index *is* which
|
|
21
|
+
* country is picked, so the arrows move the highlight on the map for free, and
|
|
22
|
+
* a click on the map only has to move the list.
|
|
23
|
+
*/
|
|
24
|
+
export function worldScreen(ui: Container, state: DemoState, theme: Theme): void {
|
|
25
|
+
const gap = panelGap(state);
|
|
26
|
+
const countries = pane(state, "world.countries", WORLD_COUNTRIES.length);
|
|
27
|
+
const selected = WORLD_COUNTRIES[countries.selected];
|
|
28
|
+
const hovered = state.worldHovered ? WORLD_COUNTRIES.find((c) => c.name === state.worldHovered) : undefined;
|
|
29
|
+
|
|
30
|
+
// Zooming is only meaningful with something to zoom to, and a country whose
|
|
31
|
+
// outlines are a single pixel would otherwise zoom to an empty window.
|
|
32
|
+
const window = state.worldZoom && selected ? countryBounds(selected) : { x: WORLD_X, y: WORLD_Y };
|
|
33
|
+
|
|
34
|
+
ui.row({ size: "1fr", gap }, (row) => {
|
|
35
|
+
row.panel({
|
|
36
|
+
title: "World",
|
|
37
|
+
subtitle: hovered ? hovered.name : selected?.name ?? "click a country",
|
|
38
|
+
subtitleColor: hovered ? theme.accent : theme.muted,
|
|
39
|
+
borderColor: theme.primary,
|
|
40
|
+
footer: state.worldZoom ? "click select · z zoom · r whole globe" : "click select · z zoom to selection",
|
|
41
|
+
}, (p) => {
|
|
42
|
+
p.worldMap({
|
|
43
|
+
...window,
|
|
44
|
+
color: theme.border,
|
|
45
|
+
highlight: [selected?.iso || selected?.name || "", hovered?.iso || hovered?.name || ""],
|
|
46
|
+
highlightColor: theme.accent,
|
|
47
|
+
onSelect: (country) => {
|
|
48
|
+
if (!country) return;
|
|
49
|
+
const index = WORLD_COUNTRIES.findIndex((c) => c.name === country.name);
|
|
50
|
+
if (index >= 0) {
|
|
51
|
+
countries.selected = index;
|
|
52
|
+
focusPane(state, "world.countries");
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
onHover: (country) => {
|
|
56
|
+
state.worldHovered = country?.name ?? "";
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
row.column({ width: 34, gap, bordered: true }, (column) => {
|
|
62
|
+
column.panel({ title: "Selection", size: 9, borderColor: theme.accent }, (p) => {
|
|
63
|
+
if (!selected) {
|
|
64
|
+
p.label("Nothing selected.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const b = countryBounds(selected);
|
|
68
|
+
p.keyValues([
|
|
69
|
+
{ label: "Country", value: selected.name, color: theme.accent },
|
|
70
|
+
{ label: "ISO", value: selected.iso || "—" },
|
|
71
|
+
{ label: "Longitude", value: `${degrees(b.x.min, "E", "W")} – ${degrees(b.x.max, "E", "W")}`, color: theme.primary },
|
|
72
|
+
{ label: "Latitude", value: `${degrees(b.y.min, "N", "S")} – ${degrees(b.y.max, "N", "S")}`, color: theme.primary },
|
|
73
|
+
{ label: "Outlines", value: String(selected.rings.length), color: theme.muted },
|
|
74
|
+
{ label: "Points", value: String(pointCount(selected.rings)), color: theme.muted },
|
|
75
|
+
], { spread: false });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
column.panel({
|
|
79
|
+
title: "Countries",
|
|
80
|
+
subtitle: String(WORLD_COUNTRIES.length),
|
|
81
|
+
size: "1fr",
|
|
82
|
+
borderColor: theme.secondary,
|
|
83
|
+
}, (p) => {
|
|
84
|
+
p.table({
|
|
85
|
+
rows: WORLD_COUNTRIES as unknown as Array<{ name: string; iso: string }>,
|
|
86
|
+
selected: countries.selected,
|
|
87
|
+
offset: countries.offset,
|
|
88
|
+
followSelection: true,
|
|
89
|
+
onScroll: (delta) => scrollPane(countries, delta),
|
|
90
|
+
onFocus: () => focusPane(state, "world.countries"),
|
|
91
|
+
onSelectRow: (row) => { countries.selected = countries.offset + row; },
|
|
92
|
+
zebra: true,
|
|
93
|
+
scrollbar: true,
|
|
94
|
+
columns: [
|
|
95
|
+
{ key: "name", title: "Country", min: 16, color: theme.foreground },
|
|
96
|
+
{ key: "iso", title: "ISO", width: 4, color: theme.muted },
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
package/src/state.ts
CHANGED
|
@@ -3,13 +3,23 @@ import type { SystemSample } from "./system/index.ts";
|
|
|
3
3
|
|
|
4
4
|
export type ScreenName =
|
|
5
5
|
| "dashboard" | "traffic" | "sessions" | "network" | "services"
|
|
6
|
-
| "components" | "graphics" | "themes" | "input" | "stress";
|
|
6
|
+
| "components" | "graphics" | "themes" | "input" | "stress" | "world";
|
|
7
7
|
|
|
8
8
|
export const SCREENS: ScreenName[] = [
|
|
9
9
|
"dashboard", "traffic", "sessions", "network", "services",
|
|
10
|
-
"components", "graphics", "themes", "input", "stress",
|
|
10
|
+
"components", "graphics", "themes", "input", "stress", "world",
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* The key that jumps straight to each screen. Digits run out at ten, so the
|
|
15
|
+
* eleventh takes a letter rather than a second "1" that would shadow the first.
|
|
16
|
+
*/
|
|
17
|
+
export const SCREEN_KEYS: Record<ScreenName, string> = {
|
|
18
|
+
dashboard: "1", traffic: "2", sessions: "3", network: "4", services: "5",
|
|
19
|
+
components: "6", graphics: "7", themes: "8", input: "9", stress: "0",
|
|
20
|
+
world: "w",
|
|
21
|
+
};
|
|
22
|
+
|
|
13
23
|
export interface DemoState {
|
|
14
24
|
sample: SystemSample;
|
|
15
25
|
screen: ScreenName;
|
|
@@ -42,6 +52,10 @@ export interface DemoState {
|
|
|
42
52
|
paletteIndex: number;
|
|
43
53
|
themeIndex: number;
|
|
44
54
|
/** Component-showcase interactive state. */
|
|
55
|
+
/** The country under the pointer on the world map, by name; "" for open water. */
|
|
56
|
+
worldHovered: string;
|
|
57
|
+
/** Whether the world map is framed on the selection rather than the globe. */
|
|
58
|
+
worldZoom: boolean;
|
|
45
59
|
toggle: boolean;
|
|
46
60
|
checkbox: boolean;
|
|
47
61
|
selectOpen: boolean;
|
|
@@ -142,6 +156,8 @@ export function createState(
|
|
|
142
156
|
focused: {},
|
|
143
157
|
sort: "cpu",
|
|
144
158
|
collapsed: false,
|
|
159
|
+
worldHovered: "",
|
|
160
|
+
worldZoom: false,
|
|
145
161
|
filter: "",
|
|
146
162
|
filtering: false,
|
|
147
163
|
showHelp: false,
|