@sybilion/uilib 1.2.24 → 1.2.26
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/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.js +1 -1
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.styl.js +1 -1
- package/dist/esm/components/ui/DropdownMenu/DropdownMenu.js +4 -4
- package/dist/esm/components/widgets/DriverMap/DriverIcon/DriverIcon.constants.json.js +6 -0
- package/dist/esm/components/widgets/DriverMap/DriverIcon/DriverIcon.js +21 -0
- package/dist/esm/components/widgets/DriverMap/DriverIcon/DriverIcon.styl.js +7 -0
- package/dist/esm/components/widgets/DriverMap/DriverMap.helpers.js +107 -0
- package/dist/esm/components/widgets/DriverMap/DriverMap.js +129 -0
- package/dist/esm/components/widgets/DriverMap/DriverMap.styl.js +7 -0
- package/dist/esm/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.js +8 -0
- package/dist/esm/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl.js +7 -0
- package/dist/esm/components/widgets/DriverMap/MapBackground/MapBackground.js +10 -0
- package/dist/esm/components/widgets/DriverMap/MapBackground/MapBackground.styl.js +7 -0
- package/dist/esm/components/widgets/DriverMap/MapBackground/map.svg.js +3 -0
- package/dist/esm/components/widgets/DriverMap/driverCategoryIcon.js +194 -0
- package/dist/esm/components/widgets/DriverMap/driverMapGeography.js +345 -0
- package/dist/esm/components/widgets/DriverMap/driverMapSelection.js +17 -0
- package/dist/esm/hooks/index.js +1 -0
- package/dist/esm/hooks/useEvent.js +0 -2
- package/dist/esm/index.js +6 -0
- package/dist/esm/types/src/components/ui/DropdownMenu/DropdownMenu.d.ts +2 -2
- package/dist/esm/types/src/components/widgets/DriverMap/DriverIcon/DriverIcon.d.ts +17 -0
- package/dist/esm/types/src/components/widgets/DriverMap/DriverMap.d.ts +8 -0
- package/dist/esm/types/src/components/widgets/DriverMap/DriverMap.helpers.d.ts +21 -0
- package/dist/esm/types/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.d.ts +1 -0
- package/dist/esm/types/src/components/widgets/DriverMap/MapBackground/MapBackground.d.ts +1 -0
- package/dist/esm/types/src/components/widgets/DriverMap/driverCategoryIcon.d.ts +1 -0
- package/dist/esm/types/src/components/widgets/DriverMap/driverMapGeography.d.ts +80 -0
- package/dist/esm/types/src/components/widgets/DriverMap/driverMapSelection.d.ts +3 -0
- package/dist/esm/types/src/components/widgets/DriverMap/index.d.ts +8 -0
- package/dist/esm/types/src/docs/pages/DriverMapPage.d.ts +1 -0
- package/dist/esm/types/src/docs/registry.d.ts +1 -1
- package/dist/esm/types/src/hooks/index.d.ts +1 -0
- package/dist/esm/types/src/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/ui/Chat/ChatSheet/ChatSelector.styl +3 -1
- package/src/components/ui/Chat/ChatSheet/ChatSelector.tsx +1 -1
- package/src/components/ui/DropdownMenu/DropdownMenu.tsx +4 -0
- package/src/components/widgets/DriverMap/DriverIcon/DriverIcon.constants.json +3 -0
- package/src/components/widgets/DriverMap/DriverIcon/DriverIcon.styl +125 -0
- package/src/components/widgets/DriverMap/DriverIcon/DriverIcon.styl.d.ts +22 -0
- package/src/components/widgets/DriverMap/DriverIcon/DriverIcon.tsx +81 -0
- package/src/components/widgets/DriverMap/DriverMap.helpers.ts +164 -0
- package/src/components/widgets/DriverMap/DriverMap.styl +45 -0
- package/src/components/widgets/DriverMap/DriverMap.styl.d.ts +11 -0
- package/src/components/widgets/DriverMap/DriverMap.tsx +214 -0
- package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl +24 -0
- package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.styl.d.ts +9 -0
- package/src/components/widgets/DriverMap/LoadingSpinner/LoadingSpinner.tsx +11 -0
- package/src/components/widgets/DriverMap/MapBackground/MapBackground.styl +13 -0
- package/src/components/widgets/DriverMap/MapBackground/MapBackground.styl.d.ts +7 -0
- package/src/components/widgets/DriverMap/MapBackground/MapBackground.tsx +18 -0
- package/src/components/widgets/DriverMap/MapBackground/map.svg +4337 -0
- package/src/components/widgets/DriverMap/MapBackground/mapAspect.mixin.styl +3 -0
- package/src/components/widgets/DriverMap/MapBackground/mapAspect.mixin.styl.d.ts +2 -0
- package/src/components/widgets/DriverMap/driverCategoryIcon.tsx +279 -0
- package/src/components/widgets/DriverMap/driverMapGeography.ts +478 -0
- package/src/components/widgets/DriverMap/driverMapSelection.ts +23 -0
- package/src/components/widgets/DriverMap/index.ts +18 -0
- package/src/docs/config/webpack.config.js +25 -9
- package/src/docs/pages/DriverMapPage.tsx +114 -0
- package/src/docs/pages/TooltipPage.tsx +14 -10
- package/src/docs/registry.ts +6 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useEvent.ts +0 -2
- package/src/index.ts +1 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import cn from 'classnames';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
import useEvent from '#uilib/hooks/useEvent';
|
|
7
|
+
import { Shimmer } from '@homecode/ui';
|
|
8
|
+
|
|
9
|
+
import { BadgeSize, DriverIcon } from './DriverIcon/DriverIcon';
|
|
10
|
+
import constants from './DriverIcon/DriverIcon.constants.json';
|
|
11
|
+
import {
|
|
12
|
+
calculateBadgeSizes,
|
|
13
|
+
findMostSpecificRegion,
|
|
14
|
+
getDriverCoordinates,
|
|
15
|
+
hasValidCoords,
|
|
16
|
+
} from './DriverMap.helpers';
|
|
17
|
+
import S from './DriverMap.styl';
|
|
18
|
+
import { MapBackground } from './MapBackground/MapBackground';
|
|
19
|
+
import type { DriverData } from './driverMapGeography';
|
|
20
|
+
import { getHighestImportanceDriver } from './driverMapSelection';
|
|
21
|
+
|
|
22
|
+
export interface DriverMapProps {
|
|
23
|
+
drivers: DriverData[];
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
setSelectedDriver: (driver: DriverData) => void;
|
|
26
|
+
selectedDriver: DriverData | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const delay = (ms: number) => new Promise<void>(r => setTimeout(r, ms));
|
|
30
|
+
|
|
31
|
+
const transitionDelay = () => delay(constants.FADE_DURATION);
|
|
32
|
+
|
|
33
|
+
export function DriverMap({
|
|
34
|
+
drivers,
|
|
35
|
+
isLoading,
|
|
36
|
+
setSelectedDriver,
|
|
37
|
+
selectedDriver,
|
|
38
|
+
}: DriverMapProps) {
|
|
39
|
+
const visibleDriversRef = useRef<DriverData[]>([]);
|
|
40
|
+
|
|
41
|
+
const [showingDrivers, setShowingDrivers] = useState<DriverData[]>(drivers);
|
|
42
|
+
const [badgeSizes, setBadgeSizes] = useState<Record<string, BadgeSize>>({});
|
|
43
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
44
|
+
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
45
|
+
|
|
46
|
+
const makeTransition = async (nextDrivers: DriverData[]) => {
|
|
47
|
+
const hadDrivers = showingDrivers.length > 0;
|
|
48
|
+
|
|
49
|
+
setIsTransitioning(true);
|
|
50
|
+
|
|
51
|
+
if (hadDrivers) {
|
|
52
|
+
setIsVisible(false);
|
|
53
|
+
await transitionDelay();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (nextDrivers.length > 0) {
|
|
57
|
+
setShowingDrivers(nextDrivers);
|
|
58
|
+
setIsVisible(false);
|
|
59
|
+
|
|
60
|
+
const sizes = calculateBadgeSizes(nextDrivers);
|
|
61
|
+
setBadgeSizes(sizes);
|
|
62
|
+
|
|
63
|
+
await delay(30);
|
|
64
|
+
setIsVisible(true);
|
|
65
|
+
await transitionDelay();
|
|
66
|
+
} else {
|
|
67
|
+
setShowingDrivers([]);
|
|
68
|
+
setBadgeSizes({});
|
|
69
|
+
setIsVisible(false);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setIsTransitioning(false);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const [worldDrivers, otherDrivers] = useMemo(() => {
|
|
76
|
+
const visibleDrivers: DriverData[] = [];
|
|
77
|
+
const worldDriversList: DriverData[] = [];
|
|
78
|
+
|
|
79
|
+
drivers.forEach(driver => {
|
|
80
|
+
let importance = driver.importance || 0;
|
|
81
|
+
if (importance === 0) {
|
|
82
|
+
const mean = driver?.rawImportance?.overall?.mean;
|
|
83
|
+
if (typeof mean === 'number') {
|
|
84
|
+
importance = Math.abs(mean) * 100;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const specificRegion = findMostSpecificRegion(driver);
|
|
89
|
+
const isEmptyRegion = driver.region?.length === 0;
|
|
90
|
+
const isWorldRegion =
|
|
91
|
+
specificRegion.name === 'World' ||
|
|
92
|
+
driver.region?.slice(-1)[0] === 'World';
|
|
93
|
+
|
|
94
|
+
const coordinates = getDriverCoordinates(
|
|
95
|
+
driver,
|
|
96
|
+
specificRegion,
|
|
97
|
+
visibleDrivers,
|
|
98
|
+
);
|
|
99
|
+
const driverToAdd = {
|
|
100
|
+
...driver,
|
|
101
|
+
coordinates,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const shouldGoToWorldDrivers =
|
|
105
|
+
isWorldRegion ||
|
|
106
|
+
!hasValidCoords(driverToAdd.coordinates) ||
|
|
107
|
+
(isEmptyRegion && !specificRegion.name);
|
|
108
|
+
|
|
109
|
+
if (shouldGoToWorldDrivers && importance >= 0) {
|
|
110
|
+
worldDriversList.push(driver);
|
|
111
|
+
} else if (hasValidCoords(driverToAdd.coordinates) && importance >= 0) {
|
|
112
|
+
visibleDrivers.push(driverToAdd);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return [
|
|
117
|
+
worldDriversList.sort((a, b) => b.importance - a.importance),
|
|
118
|
+
visibleDrivers,
|
|
119
|
+
];
|
|
120
|
+
}, [drivers]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
void makeTransition(drivers);
|
|
124
|
+
|
|
125
|
+
if (drivers.length > 0) {
|
|
126
|
+
const idx = getHighestImportanceDriver(drivers);
|
|
127
|
+
if (idx !== null && idx >= 0) {
|
|
128
|
+
setSelectedDriver(drivers[idx]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}, [drivers]);
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
visibleDriversRef.current = otherDrivers;
|
|
135
|
+
}, [otherDrivers]);
|
|
136
|
+
|
|
137
|
+
const handleKeyDown = useCallback(
|
|
138
|
+
(e: KeyboardEvent) => {
|
|
139
|
+
const items = visibleDriversRef.current;
|
|
140
|
+
|
|
141
|
+
if (!selectedDriver || items.length === 0 || e.metaKey || e.ctrlKey)
|
|
142
|
+
return;
|
|
143
|
+
|
|
144
|
+
const stop = () => {
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
e.stopPropagation();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const currIndex = items.findIndex(d => d.id === selectedDriver.id);
|
|
150
|
+
|
|
151
|
+
if (e.key === 'ArrowLeft') {
|
|
152
|
+
const prevIndex = currIndex > 0 ? currIndex - 1 : items.length - 1;
|
|
153
|
+
|
|
154
|
+
setSelectedDriver(items[prevIndex]);
|
|
155
|
+
stop();
|
|
156
|
+
} else if (e.key === 'ArrowRight') {
|
|
157
|
+
const nextIndex = currIndex < items.length - 1 ? currIndex + 1 : 0;
|
|
158
|
+
|
|
159
|
+
setSelectedDriver(items[nextIndex]);
|
|
160
|
+
stop();
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
[selectedDriver, setSelectedDriver],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
useEvent({
|
|
167
|
+
event: 'keydown',
|
|
168
|
+
callback: handleKeyDown,
|
|
169
|
+
isCapture: true,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div className={cn(S.root, isTransitioning && S.inTransition)}>
|
|
174
|
+
<div className={S.mapInner}>
|
|
175
|
+
<MapBackground />
|
|
176
|
+
{isLoading && <Shimmer className={S.shimmerOverlay} size="l" />}
|
|
177
|
+
{otherDrivers.map(driver => (
|
|
178
|
+
<DriverIcon
|
|
179
|
+
key={driver.id}
|
|
180
|
+
isLoading={isLoading}
|
|
181
|
+
isVisible={isVisible}
|
|
182
|
+
driver={driver}
|
|
183
|
+
size={badgeSizes[driver.id] || 's'}
|
|
184
|
+
isSelected={selectedDriver?.id === driver.id}
|
|
185
|
+
onClick={() => setSelectedDriver(driver)}
|
|
186
|
+
/>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
<div className={S.worldDrivers}>
|
|
190
|
+
{worldDrivers.map(driver => {
|
|
191
|
+
const driverWithCoords = {
|
|
192
|
+
...driver,
|
|
193
|
+
coordinates: driver.coordinates || {
|
|
194
|
+
x: 0,
|
|
195
|
+
y: 0,
|
|
196
|
+
continent: 'Global',
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
return (
|
|
200
|
+
<DriverIcon
|
|
201
|
+
key={driver.id}
|
|
202
|
+
isLoading={isLoading}
|
|
203
|
+
isVisible={isVisible}
|
|
204
|
+
driver={driverWithCoords}
|
|
205
|
+
size={badgeSizes[driver.id] || 's'}
|
|
206
|
+
isSelected={selectedDriver?.id === driver.id}
|
|
207
|
+
onClick={() => setSelectedDriver(driver)}
|
|
208
|
+
/>
|
|
209
|
+
);
|
|
210
|
+
})}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Loading spinner component styles
|
|
2
|
+
.loadingSpinnerContainer
|
|
3
|
+
position absolute
|
|
4
|
+
inset 0
|
|
5
|
+
display flex
|
|
6
|
+
align-items center
|
|
7
|
+
justify-content center
|
|
8
|
+
z-index 20
|
|
9
|
+
|
|
10
|
+
.loadingSpinner
|
|
11
|
+
width 1.5rem
|
|
12
|
+
height 1.5rem
|
|
13
|
+
border 2px solid var(--muted-foreground)
|
|
14
|
+
border-opacity 0.2
|
|
15
|
+
border-top-color var(--muted-foreground)
|
|
16
|
+
border-top-opacity 0.6
|
|
17
|
+
border-radius 50%
|
|
18
|
+
animation spin 1s linear infinite
|
|
19
|
+
|
|
20
|
+
@keyframes spin
|
|
21
|
+
from
|
|
22
|
+
transform rotate(0deg)
|
|
23
|
+
to
|
|
24
|
+
transform rotate(360deg)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
@import './mapAspect.mixin.styl'
|
|
2
|
+
|
|
3
|
+
// Map is <img src> from bundled URL (data URL or file). Avoids CSS url() resolving to broken relative paths in consumers.
|
|
4
|
+
.mapBackground
|
|
5
|
+
position absolute
|
|
6
|
+
display block
|
|
7
|
+
width 100%
|
|
8
|
+
height 100%
|
|
9
|
+
mapAspect()
|
|
10
|
+
object-fit contain
|
|
11
|
+
object-position center
|
|
12
|
+
pointer-events none
|
|
13
|
+
user-select none
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import S from './MapBackground.styl';
|
|
4
|
+
import mapBgUrl from './map.svg';
|
|
5
|
+
|
|
6
|
+
export function MapBackground() {
|
|
7
|
+
const src = mapBgUrl as unknown as string;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<img
|
|
11
|
+
alt=""
|
|
12
|
+
className={S.mapBackground}
|
|
13
|
+
decoding="async"
|
|
14
|
+
draggable={false}
|
|
15
|
+
src={src}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|