poe-svelte-ui-lib 1.11.2 → 1.11.3
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/Map/Map.svelte
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
interface MapDevice extends IDeviceGNSS {
|
|
12
12
|
isFresh: boolean
|
|
13
|
-
|
|
13
|
+
staleTimeoutId: number | null
|
|
14
|
+
removeTimeoutId: number | null
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
let devices: MapDevice[] = $state([])
|
|
@@ -18,21 +19,29 @@
|
|
|
18
19
|
let isDarkMode = $state(false)
|
|
19
20
|
let markerTimeout = $state(30_000)
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
const clearDeviceTimers = (device: MapDevice) => {
|
|
23
|
+
if (device.staleTimeoutId !== null) clearTimeout(device.staleTimeoutId)
|
|
24
|
+
if (device.removeTimeoutId !== null) clearTimeout(device.removeTimeoutId)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Перезапускает цикл "устройство свежее" -> (без обновлений markerTimeout) "устарело" -> (без обновлений ещё markerTimeout) "удалено"
|
|
28
|
+
const restartFreshTimer = (devSN: string) => {
|
|
29
|
+
const idx = devices.findIndex((d) => d.DevSN === devSN)
|
|
30
|
+
if (idx === -1) return
|
|
31
|
+
|
|
32
|
+
clearDeviceTimers(devices[idx])
|
|
33
|
+
devices[idx].isFresh = true
|
|
34
|
+
|
|
35
|
+
devices[idx].staleTimeoutId = setTimeout(() => {
|
|
36
|
+
const staleIdx = devices.findIndex((d) => d.DevSN === devSN)
|
|
37
|
+
if (staleIdx === -1) return
|
|
38
|
+
|
|
39
|
+
devices[staleIdx].isFresh = false
|
|
40
|
+
devices[staleIdx].staleTimeoutId = null
|
|
41
|
+
devices[staleIdx].removeTimeoutId = setTimeout(() => {
|
|
42
|
+
devices = devices.filter((d) => d.DevSN !== devSN)
|
|
43
|
+
}, markerTimeout) as unknown as number
|
|
33
44
|
}, markerTimeout) as unknown as number
|
|
34
|
-
devices[index].timeoutId = id
|
|
35
|
-
devices[index].isFresh = true
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
// Обработка входящих данных
|
|
@@ -42,17 +51,17 @@
|
|
|
42
51
|
if (idx !== -1) {
|
|
43
52
|
/* Обновление существующего */
|
|
44
53
|
devices[idx] = { ...devices[idx], ...data }
|
|
45
|
-
restartFreshTimer(idx)
|
|
46
54
|
} else {
|
|
47
55
|
/* Новое устройство */
|
|
48
56
|
const newDevice: MapDevice = {
|
|
49
57
|
...data,
|
|
50
58
|
isFresh: true,
|
|
51
|
-
|
|
59
|
+
staleTimeoutId: null,
|
|
60
|
+
removeTimeoutId: null,
|
|
52
61
|
}
|
|
53
62
|
devices.push(newDevice)
|
|
54
|
-
restartFreshTimer(devices.length - 1)
|
|
55
63
|
}
|
|
64
|
+
restartFreshTimer(data.DevSN)
|
|
56
65
|
data = null
|
|
57
66
|
}
|
|
58
67
|
})
|
|
@@ -69,12 +78,10 @@
|
|
|
69
78
|
})
|
|
70
79
|
|
|
71
80
|
onDestroy(() => {
|
|
72
|
-
if (typeof window !== "undefined") window.
|
|
81
|
+
if (typeof window !== "undefined") window.removeEventListener("ThemeChange", handleThemeChange as EventListener)
|
|
73
82
|
|
|
74
83
|
for (const device of devices) {
|
|
75
|
-
|
|
76
|
-
clearTimeout(device.timeoutId)
|
|
77
|
-
}
|
|
84
|
+
clearDeviceTimers(device)
|
|
78
85
|
}
|
|
79
86
|
})
|
|
80
87
|
|
|
@@ -90,7 +97,7 @@
|
|
|
90
97
|
|
|
91
98
|
const changeTimeout = (val: number) => {
|
|
92
99
|
markerTimeout = val
|
|
93
|
-
devices.forEach((
|
|
100
|
+
devices.forEach((device) => restartFreshTimer(device.DevSN))
|
|
94
101
|
}
|
|
95
102
|
</script>
|
|
96
103
|
|
|
@@ -159,56 +166,88 @@
|
|
|
159
166
|
<p class="font-bold">{device.DevName}</p>
|
|
160
167
|
</div>
|
|
161
168
|
{/snippet}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
</
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
169
|
+
|
|
170
|
+
<Popup closeButton={false} class="map-popup rounded-2xl text-left">
|
|
171
|
+
<div class="flex flex-col gap-1.5">
|
|
172
|
+
<div class="flex items-center gap-1.5">
|
|
173
|
+
<span class="size-2 shrink-0 rounded-full {device.isFresh ? 'bg-green-500' : 'bg-red-500'}"></span>
|
|
174
|
+
<h6 class="text-sm leading-none font-bold">{device.DevName}</h6>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div class="grid grid-cols-2 gap-x-3 gap-y-1 rounded-lg bg-(--container-color) p-2 text-xs">
|
|
178
|
+
<div>
|
|
179
|
+
<p class="text-[0.6rem] font-semibold tracking-wide uppercase opacity-60">Lat / Lon</p>
|
|
180
|
+
<p class="font-medium">{device.NavLat.toFixed(5)}, {device.NavLon.toFixed(5)}</p>
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
<p class="text-[0.6rem] font-semibold tracking-wide uppercase opacity-60">Alt</p>
|
|
184
|
+
<p class="font-medium">{device.NavAlt.toFixed(1)} m</p>
|
|
185
|
+
</div>
|
|
186
|
+
<div>
|
|
187
|
+
<p class="text-[0.6rem] font-semibold tracking-wide uppercase opacity-60">Heading</p>
|
|
188
|
+
<p class="font-medium">{device.NavHeading.toFixed(1)}°</p>
|
|
189
|
+
</div>
|
|
190
|
+
<div>
|
|
191
|
+
<p class="text-[0.6rem] font-semibold tracking-wide uppercase opacity-60">Satellites</p>
|
|
192
|
+
<p class="font-medium">{device.NavSatUse}</p>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<p class="text-[0.6rem] font-mono text-center tracking-wide uppercase">{device.DevSN}</p>
|
|
197
|
+
|
|
198
|
+
<div class="flex items-center justify-between border-t border-(--border-color) pt-1">
|
|
199
|
+
<div class="relative flex items-center">
|
|
200
|
+
<button
|
|
201
|
+
class="flex cursor-pointer items-center gap-1 rounded-lg border-none bg-transparent px-1 py-0.5 opacity-70 transition hover:opacity-100"
|
|
202
|
+
onclick={(e) => {
|
|
203
|
+
e.preventDefault()
|
|
204
|
+
navigator.clipboard.writeText(
|
|
205
|
+
`DevName: ${device.DevName}\nDevSN: ${device.DevSN}\nLat: ${device.NavLat.toFixed(3)} | Lon: ${device.NavLon.toFixed(3)} | Alt: ${device.NavAlt}\nHeading: ${device.NavHeading} | Satellites: ${device.NavSatUse}`,
|
|
206
|
+
)
|
|
207
|
+
isCopied = true
|
|
208
|
+
setTimeout(() => (isCopied = false), 1000)
|
|
209
|
+
}}
|
|
210
|
+
aria-label="Copy"
|
|
211
|
+
title="Copy"
|
|
212
|
+
>
|
|
213
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1.25rem" height="1.25rem" viewBox="0 0 24 24">
|
|
214
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
215
|
+
<path
|
|
216
|
+
d="M6 11c0-2.828 0-4.243.879-5.121C7.757 5 9.172 5 12 5h3c2.828 0 4.243 0 5.121.879C21 6.757 21 8.172 21 11v5c0 2.828 0 4.243-.879 5.121C19.243 22 17.828 22 15 22h-3c-2.828 0-4.243 0-5.121-.879C6 20.243 6 18.828 6 16z"
|
|
217
|
+
/>
|
|
218
|
+
<path d="M6 19a3 3 0 0 1-3-3v-6c0-3.771 0-5.657 1.172-6.828S7.229 2 11 2h4a3 3 0 0 1 3 3" />
|
|
219
|
+
</g>
|
|
220
|
+
</svg>
|
|
221
|
+
</button>
|
|
222
|
+
{#if isCopied}
|
|
223
|
+
<div
|
|
224
|
+
class="absolute -top-1 right-0 -translate-y-full rounded-md bg-(--green-color) px-1.5 py-1 text-sm shadow-lg"
|
|
225
|
+
transition:fade={{ duration: 200 }}
|
|
226
|
+
>
|
|
227
|
+
✓
|
|
228
|
+
</div>
|
|
229
|
+
{/if}
|
|
196
230
|
</div>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
stroke="currentColor"
|
|
204
|
-
stroke-linecap="round"
|
|
205
|
-
stroke-linejoin="round"
|
|
206
|
-
stroke-width="1.5"
|
|
207
|
-
d="m19.5 5.5l-.62 10.025c-.158 2.561-.237 3.842-.88 4.763a4 4 0 0 1-1.2 1.128c-.957.584-2.24.584-4.806.584c-2.57 0-3.855 0-4.814-.585a4 4 0 0 1-1.2-1.13c-.642-.922-.72-2.205-.874-4.77L4.5 5.5M3 5.5h18m-4.944 0l-.683-1.408c-.453-.936-.68-1.403-1.071-1.695a2 2 0 0 0-.275-.172C13.594 2 13.074 2 12.035 2c-1.066 0-1.599 0-2.04.234a2 2 0 0 0-.278.18c-.395.303-.616.788-1.058 1.757L8.053 5.5m1.447 11v-6m5 6v-6"
|
|
208
|
-
color="currentColor"
|
|
209
|
-
/></svg
|
|
231
|
+
|
|
232
|
+
<button
|
|
233
|
+
class="cursor-pointer rounded-lg border-none bg-transparent p-0.5 text-red-500 transition hover:text-red-600"
|
|
234
|
+
aria-label="Delete"
|
|
235
|
+
title="Delete"
|
|
236
|
+
onclick={() => (devices = devices.filter((dev) => dev.DevSN !== device.DevSN))}
|
|
210
237
|
>
|
|
211
|
-
|
|
238
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1.25rem" height="1.25rem" viewBox="0 0 24 24"
|
|
239
|
+
><path
|
|
240
|
+
fill="none"
|
|
241
|
+
stroke="currentColor"
|
|
242
|
+
stroke-linecap="round"
|
|
243
|
+
stroke-linejoin="round"
|
|
244
|
+
stroke-width="1.5"
|
|
245
|
+
d="m19.5 5.5l-.62 10.025c-.158 2.561-.237 3.842-.88 4.763a4 4 0 0 1-1.2 1.128c-.957.584-2.24.584-4.806.584c-2.57 0-3.855 0-4.814-.585a4 4 0 0 1-1.2-1.13c-.642-.922-.72-2.205-.874-4.77L4.5 5.5M3 5.5h18m-4.944 0l-.683-1.408c-.453-.936-.68-1.403-1.071-1.695a2 2 0 0 0-.275-.172C13.594 2 13.074 2 12.035 2c-1.066 0-1.599 0-2.04.234a2 2 0 0 0-.278.18c-.395.303-.616.788-1.058 1.757L8.053 5.5m1.447 11v-6m5 6v-6"
|
|
246
|
+
color="currentColor"
|
|
247
|
+
/></svg
|
|
248
|
+
>
|
|
249
|
+
</button>
|
|
250
|
+
</div>
|
|
212
251
|
</div>
|
|
213
252
|
</Popup>
|
|
214
253
|
</Marker>
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
interface Props {
|
|
6
6
|
closeButton?: boolean
|
|
7
7
|
class?: string
|
|
8
|
+
maxWidth?: string
|
|
8
9
|
children?: Snippet
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
let { closeButton = true, class: className = undefined, children }: Props = $props()
|
|
12
|
+
let { closeButton = true, class: className = undefined, maxWidth = "none", children }: Props = $props()
|
|
12
13
|
|
|
13
14
|
let container = $state<HTMLElement | null>(null)
|
|
14
15
|
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
|
|
25
26
|
const options: any = {
|
|
26
27
|
closeButton,
|
|
28
|
+
maxWidth,
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
if (className) {
|
package/package.json
CHANGED