poe-svelte-ui-lib 1.2.25 → 1.2.27
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/Accordion/AccordionProps.svelte +11 -4
- package/dist/Button/ButtonProps.svelte +22 -21
- package/dist/ColorPicker/ColorPickerProps.svelte +16 -8
- package/dist/FileAttach/FileAttach.svelte +4 -10
- package/dist/FileAttach/FileAttachProps.svelte +2 -0
- package/dist/Graph/GraphProps.svelte +2 -1
- package/dist/Input/InputProps.svelte +18 -5
- package/dist/Joystick/Joystick.svelte +1 -2
- package/dist/Map/Map.svelte +205 -0
- package/dist/Map/Map.svelte.d.ts +4 -0
- package/dist/Map/MapProps.svelte +157 -0
- package/dist/Map/MapProps.svelte.d.ts +11 -0
- package/dist/Select/SelectProps.svelte +27 -23
- package/dist/Slider/Slider.svelte +149 -187
- package/dist/Slider/SliderProps.svelte +16 -1
- package/dist/Switch/SwitchProps.svelte +23 -6
- package/dist/Table/Table.svelte +2 -2
- package/dist/Table/TableProps.svelte +32 -6
- package/dist/TextField/TextFieldProps.svelte +12 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/locales/translations.js +6 -6
- package/dist/types.d.ts +20 -2
- package/package.json +6 -5
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
</script>
|
|
47
47
|
|
|
48
48
|
{#if forConstructor}
|
|
49
|
-
<div class="flex items-
|
|
49
|
+
<div class="flex items-start justify-center gap-8">
|
|
50
50
|
<div class="flex w-1/3 flex-col items-center px-2">
|
|
51
51
|
<UI.Select
|
|
52
|
-
label={{ name: $t('constructor.props.
|
|
52
|
+
label={{ name: $t('constructor.props.access') }}
|
|
53
53
|
type="buttons"
|
|
54
54
|
options={$optionsStore.ACCESS_OPTION}
|
|
55
55
|
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
@@ -137,13 +137,20 @@
|
|
|
137
137
|
</div>
|
|
138
138
|
</div>
|
|
139
139
|
{:else}
|
|
140
|
-
<div class="flex items-
|
|
140
|
+
<div class="flex items-start justify-center gap-8">
|
|
141
141
|
<div class="flex w-1/3 flex-col items-center px-2">
|
|
142
142
|
<UI.Input
|
|
143
143
|
label={{ name: $t('constructor.props.id') }}
|
|
144
144
|
value={component.properties.id}
|
|
145
145
|
onUpdate={(value) => updateProperty('id', value as string, component, onPropertyChange)}
|
|
146
146
|
/>
|
|
147
|
+
<UI.Select
|
|
148
|
+
label={{ name: $t('constructor.props.access') }}
|
|
149
|
+
type="buttons"
|
|
150
|
+
options={$optionsStore.ACCESS_OPTION}
|
|
151
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
152
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
153
|
+
/>
|
|
147
154
|
<UI.Select
|
|
148
155
|
label={{ name: $t('constructor.props.type') }}
|
|
149
156
|
type="buttons"
|
|
@@ -175,7 +182,7 @@
|
|
|
175
182
|
onChange={(value) => updateProperty('isOpen', value, component, onPropertyChange)}
|
|
176
183
|
/>
|
|
177
184
|
</div>
|
|
178
|
-
<div class="flex w-1/3 flex-col
|
|
185
|
+
<div class="flex w-1/3 flex-col px-2">
|
|
179
186
|
<UI.Input
|
|
180
187
|
label={{ name: $t('constructor.props.wrapperclass') }}
|
|
181
188
|
value={component.properties.wrapperClass}
|
|
@@ -76,7 +76,8 @@
|
|
|
76
76
|
help={{ info: $t('constructor.props.argument.info'), autocomplete: 'on', regExp: /^[a-zA-Z0-9\-_]{0,32}$/ }}
|
|
77
77
|
onUpdate={(value) => updateProperty('eventHandler.Argument', value as string, component, onPropertyChange)}
|
|
78
78
|
/>
|
|
79
|
-
|
|
79
|
+
</div>
|
|
80
|
+
<div class="flex w-1/3 flex-col items-center px-2">
|
|
80
81
|
{#if (component.properties.eventHandler.Argument !== 'Save' && component.properties.eventHandler.Argument !== 'NoSave') || Header.value === 'SET'}
|
|
81
82
|
<UI.Input
|
|
82
83
|
label={{ name: $t('constructor.props.value') }}
|
|
@@ -97,15 +98,15 @@
|
|
|
97
98
|
updateProperty('eventHandler.Variables', parts, component, onPropertyChange)
|
|
98
99
|
}}
|
|
99
100
|
/>
|
|
100
|
-
</div>
|
|
101
|
-
<div class="flex w-1/3 flex-col px-2">
|
|
102
101
|
<UI.Select
|
|
103
|
-
label={{ name: $t('constructor.props.
|
|
102
|
+
label={{ name: $t('constructor.props.access') }}
|
|
104
103
|
type="buttons"
|
|
105
104
|
options={$optionsStore.ACCESS_OPTION}
|
|
106
|
-
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.
|
|
107
|
-
onUpdate={(option) =>
|
|
105
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
106
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
108
107
|
/>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="flex w-1/3 flex-col items-center px-2">
|
|
109
110
|
<UI.Input
|
|
110
111
|
label={{ name: $t('constructor.props.name') }}
|
|
111
112
|
value={component.properties.content.name}
|
|
@@ -139,6 +140,13 @@
|
|
|
139
140
|
value={component.properties.id}
|
|
140
141
|
onUpdate={(value) => updateProperty('id', value as string, component, onPropertyChange)}
|
|
141
142
|
/>
|
|
143
|
+
<UI.Select
|
|
144
|
+
label={{ name: $t('constructor.props.access') }}
|
|
145
|
+
type="buttons"
|
|
146
|
+
options={$optionsStore.ACCESS_OPTION}
|
|
147
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
148
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
149
|
+
/>
|
|
142
150
|
<UI.Input
|
|
143
151
|
label={{ name: $t('constructor.props.wrapperclass') }}
|
|
144
152
|
value={component.properties.wrapperClass}
|
|
@@ -171,13 +179,6 @@
|
|
|
171
179
|
value={$optionsStore.INFO_SIDE_OPTIONS.find((h) => h.value === component.properties.content.info.side)}
|
|
172
180
|
onUpdate={(option) => updateProperty('content.info.side', option.value as string, component, onPropertyChange)}
|
|
173
181
|
/>
|
|
174
|
-
<UI.Input
|
|
175
|
-
label={{ name: $t('constructor.props.svgicon') }}
|
|
176
|
-
type="text-area"
|
|
177
|
-
maxlength={100000}
|
|
178
|
-
value={component.properties.content.icon}
|
|
179
|
-
onUpdate={(value) => updateProperty('content.icon', value as string, component, onPropertyChange)}
|
|
180
|
-
/>
|
|
181
182
|
</div>
|
|
182
183
|
<div class="flex w-1/3 flex-col px-2">
|
|
183
184
|
<UI.Input
|
|
@@ -185,14 +186,6 @@
|
|
|
185
186
|
value={component.properties.componentClass}
|
|
186
187
|
onUpdate={(value) => updateProperty('componentClass', value as string, component, onPropertyChange)}
|
|
187
188
|
/>
|
|
188
|
-
<UI.Select
|
|
189
|
-
label={{ name: $t('constructor.props.height') }}
|
|
190
|
-
type="buttons"
|
|
191
|
-
options={$optionsStore.HEIGHT_OPTIONS}
|
|
192
|
-
value={initialHeight}
|
|
193
|
-
onUpdate={(option) =>
|
|
194
|
-
updateProperty('componentClass', twMerge(component.properties.componentClass, option.value), component, onPropertyChange)}
|
|
195
|
-
/>
|
|
196
189
|
<UI.Select
|
|
197
190
|
wrapperClass="h-14"
|
|
198
191
|
label={{ name: $t('constructor.props.colors') }}
|
|
@@ -202,6 +195,14 @@
|
|
|
202
195
|
onUpdate={(option) =>
|
|
203
196
|
updateProperty('componentClass', twMerge(component.properties.componentClass, option.value), component, onPropertyChange)}
|
|
204
197
|
/>
|
|
198
|
+
|
|
199
|
+
<UI.Input
|
|
200
|
+
label={{ name: $t('constructor.props.svgicon') }}
|
|
201
|
+
type="text-area"
|
|
202
|
+
maxlength={100000}
|
|
203
|
+
value={component.properties.content.icon}
|
|
204
|
+
onUpdate={(value) => updateProperty('content.icon', value as string, component, onPropertyChange)}
|
|
205
|
+
/>
|
|
205
206
|
</div>
|
|
206
207
|
</div>
|
|
207
208
|
{/if}
|
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
options={VARIABLE_OPTIONS}
|
|
36
36
|
value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id)}
|
|
37
37
|
onUpdate={(value) => {
|
|
38
|
-
updateProperty('id', value.value as string, component, onPropertyChange
|
|
38
|
+
updateProperty('id', value.value as string, component, onPropertyChange)
|
|
39
39
|
updateProperty('eventHandler.Variables', value.value as string, component, onPropertyChange)
|
|
40
|
+
onPropertyChange(null, value.name?.split('—')[1].trim(), null)
|
|
40
41
|
}}
|
|
41
42
|
/>
|
|
42
43
|
<UI.Select
|
|
@@ -51,11 +52,11 @@
|
|
|
51
52
|
</div>
|
|
52
53
|
<div class="flex w-1/3 flex-col items-center px-2">
|
|
53
54
|
<UI.Select
|
|
54
|
-
label={{ name: $t('constructor.props.
|
|
55
|
+
label={{ name: $t('constructor.props.access') }}
|
|
55
56
|
type="buttons"
|
|
56
57
|
options={$optionsStore.ACCESS_OPTION}
|
|
57
|
-
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.
|
|
58
|
-
onUpdate={(option) =>
|
|
58
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
59
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
59
60
|
/>
|
|
60
61
|
</div>
|
|
61
62
|
<div class="flex w-1/3 flex-col px-2">
|
|
@@ -82,10 +83,12 @@
|
|
|
82
83
|
value={component.properties.id}
|
|
83
84
|
onUpdate={(value) => updateProperty('id', value as string, component, onPropertyChange)}
|
|
84
85
|
/>
|
|
85
|
-
<UI.
|
|
86
|
-
label={{ name: $t('constructor.props.
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
<UI.Select
|
|
87
|
+
label={{ name: $t('constructor.props.access') }}
|
|
88
|
+
type="buttons"
|
|
89
|
+
options={$optionsStore.ACCESS_OPTION}
|
|
90
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
91
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
89
92
|
/>
|
|
90
93
|
</div>
|
|
91
94
|
<div class="flex w-1/3 flex-col px-2">
|
|
@@ -101,6 +104,11 @@
|
|
|
101
104
|
/>
|
|
102
105
|
</div>
|
|
103
106
|
<div class="flex w-1/3 flex-col px-2">
|
|
107
|
+
<UI.Input
|
|
108
|
+
label={{ name: $t('constructor.props.wrapperclass') }}
|
|
109
|
+
value={component.properties.wrapperClass}
|
|
110
|
+
onUpdate={(value) => updateProperty('wrapperClass', value as string, component, onPropertyChange)}
|
|
111
|
+
/>
|
|
104
112
|
<UI.TextField content={{ name: $t('constructor.props.defaultcolor'), class: 'font-bold' }} />
|
|
105
113
|
<div class="flex items-center gap-3">
|
|
106
114
|
<UI.TextField wrapperClass="w-4" content={{ name: 'R', class: 'font-bold' }} />
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
onChange = () => {},
|
|
26
26
|
}: IFileInputProps = $props()
|
|
27
27
|
|
|
28
|
+
let ID = `${id}-${crypto.randomUUID().slice(0, 6)}`
|
|
28
29
|
let selectedFile = $state<File | null>(null)
|
|
29
30
|
let previewUrl = $derived(currentImage ? (currentImage.startsWith('data:') ? currentImage : `data:image/png;base64,${currentImage}`) : null)
|
|
30
31
|
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
const triggerFileInput = () => {
|
|
47
|
-
const input = document.getElementById(
|
|
48
|
+
const input = document.getElementById(ID)
|
|
48
49
|
input?.click()
|
|
49
50
|
}
|
|
50
51
|
</script>
|
|
@@ -77,19 +78,12 @@
|
|
|
77
78
|
<span class="text-sm text-gray-500">Image</span>
|
|
78
79
|
{/if}
|
|
79
80
|
</button>
|
|
80
|
-
<input
|
|
81
|
-
id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
|
|
82
|
-
type="file"
|
|
83
|
-
class="absolute -z-10 h-0 w-0 overflow-hidden opacity-0"
|
|
84
|
-
{accept}
|
|
85
|
-
{disabled}
|
|
86
|
-
onchange={handleFileChange}
|
|
87
|
-
/>
|
|
81
|
+
<input id={ID} type="file" class="absolute -z-10 h-0 w-0 overflow-hidden opacity-0" {accept} {disabled} onchange={handleFileChange} />
|
|
88
82
|
</div>
|
|
89
83
|
{:else}
|
|
90
84
|
<label class="relative inline-block w-full">
|
|
91
85
|
<input
|
|
92
|
-
id={
|
|
86
|
+
id={ID}
|
|
93
87
|
type="file"
|
|
94
88
|
class={`h-8.5 w-full rounded-2xl bg-(--back-color) font-semibold shadow-sm transition duration-250 hover:shadow-md
|
|
95
89
|
${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} invalid:shadow-[0_0_6px(--red-color) file:h-full file:w-1/3
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
value={component.properties.wrapperClass}
|
|
32
32
|
onUpdate={(value) => updateProperty('wrapperClass', value as string, component, onPropertyChange)}
|
|
33
33
|
/>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="flex w-1/3 flex-col px-2">
|
|
34
36
|
<UI.Input
|
|
35
37
|
label={{ name: $t('constructor.props.label') }}
|
|
36
38
|
value={component.properties.label.name}
|
|
@@ -27,8 +27,9 @@
|
|
|
27
27
|
options={VARIABLE_OPTIONS}
|
|
28
28
|
value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id)}
|
|
29
29
|
onUpdate={(value) => {
|
|
30
|
-
updateProperty('id', value.value as string, component, onPropertyChange
|
|
30
|
+
updateProperty('id', value.value as string, component, onPropertyChange)
|
|
31
31
|
updateProperty('eventHandler.Variables', value.value as string, component, onPropertyChange)
|
|
32
|
+
onPropertyChange(null, value.name?.split('—')[1].trim(), null)
|
|
32
33
|
}}
|
|
33
34
|
/>
|
|
34
35
|
</div>
|
|
@@ -101,11 +101,11 @@
|
|
|
101
101
|
}}
|
|
102
102
|
/>
|
|
103
103
|
<UI.Select
|
|
104
|
-
label={{ name: $t('constructor.props.
|
|
104
|
+
label={{ name: $t('constructor.props.access') }}
|
|
105
105
|
type="buttons"
|
|
106
106
|
options={$optionsStore.ACCESS_OPTION}
|
|
107
|
-
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.
|
|
108
|
-
onUpdate={(option) =>
|
|
107
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
108
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
109
109
|
/>
|
|
110
110
|
<UI.Select
|
|
111
111
|
label={{ name: $t('constructor.props.type') }}
|
|
@@ -128,7 +128,10 @@
|
|
|
128
128
|
maxlength={150}
|
|
129
129
|
help={{ info: $t('constructor.props.regexp.info') }}
|
|
130
130
|
componentClass={isValidRegExp === false ? '!border-2 !border-red-400' : ''}
|
|
131
|
-
onUpdate={(value) =>
|
|
131
|
+
onUpdate={(value) => {
|
|
132
|
+
console.log(value)
|
|
133
|
+
updateProperty('help.regExp', value as string)
|
|
134
|
+
}}
|
|
132
135
|
/>
|
|
133
136
|
{:else if component.properties.type === 'number' && !component.properties.readonly && !component.properties.disabled}
|
|
134
137
|
<UI.Input
|
|
@@ -170,7 +173,10 @@
|
|
|
170
173
|
label={{ name: $t('constructor.props.readonly') }}
|
|
171
174
|
value={component.properties.readonly}
|
|
172
175
|
options={[{ id: crypto.randomUUID(), value: 0, class: '' }]}
|
|
173
|
-
onChange={(value) =>
|
|
176
|
+
onChange={(value) => {
|
|
177
|
+
updateProperty('readonly', value)
|
|
178
|
+
console.log(component.properties)
|
|
179
|
+
}}
|
|
174
180
|
/>
|
|
175
181
|
<UI.Switch
|
|
176
182
|
label={{ name: $t('constructor.props.copy') }}
|
|
@@ -247,6 +253,13 @@
|
|
|
247
253
|
/>
|
|
248
254
|
</div>
|
|
249
255
|
<div class="flex w-1/3 flex-col px-2">
|
|
256
|
+
<UI.Select
|
|
257
|
+
label={{ name: $t('constructor.props.access') }}
|
|
258
|
+
type="buttons"
|
|
259
|
+
options={$optionsStore.ACCESS_OPTION}
|
|
260
|
+
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
261
|
+
onUpdate={(option) => onPropertyChange(null, null, option.value)}
|
|
262
|
+
/>
|
|
250
263
|
<UI.Input
|
|
251
264
|
label={{ name: $t('constructor.props.value') }}
|
|
252
265
|
value={component.properties.value}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
let {
|
|
6
6
|
id = crypto.randomUUID(),
|
|
7
|
-
|
|
8
7
|
wrapperClass = '',
|
|
9
8
|
label = { name: '', class: '' },
|
|
10
9
|
value = $bindable([0, 0, 0]),
|
|
@@ -305,7 +304,7 @@
|
|
|
305
304
|
</div>
|
|
306
305
|
</div>
|
|
307
306
|
|
|
308
|
-
<div class="
|
|
307
|
+
<div class="right-10 flex items-center md:absolute">
|
|
309
308
|
<div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="flex h-full flex-col justify-center rounded-full p-10">
|
|
310
309
|
{#each sensitivityOptions as option, index}
|
|
311
310
|
<button
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<!-- $lib/ElementsUI/Map.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { t } from '../locales/i18n'
|
|
4
|
+
import type { IDeviceGNSS, IMapProps } from '../types'
|
|
5
|
+
import { onDestroy, onMount } from 'svelte'
|
|
6
|
+
import { MapLibre, NavigationControl, ScaleControl, GeolocateControl, FullScreenControl, Marker, Popup, CustomControl } from 'svelte-maplibre-gl'
|
|
7
|
+
import { fade } from 'svelte/transition'
|
|
8
|
+
import { twMerge } from 'tailwind-merge'
|
|
9
|
+
|
|
10
|
+
let { id = crypto.randomUUID(), label = { name: '', class: '' }, data = $bindable(), markerIcon }: IMapProps = $props()
|
|
11
|
+
|
|
12
|
+
interface MapDevice extends IDeviceGNSS {
|
|
13
|
+
isFresh: boolean
|
|
14
|
+
timeoutId: number | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let devices: MapDevice[] = $state([])
|
|
18
|
+
let isCopied = $state(false)
|
|
19
|
+
let isDarkMode = $state(false)
|
|
20
|
+
let markerTimeout = $state(30_000)
|
|
21
|
+
|
|
22
|
+
const restartFreshTimer = (index: number) => {
|
|
23
|
+
const device = devices[index]
|
|
24
|
+
// Очистить старый таймер, если есть
|
|
25
|
+
if (device.timeoutId !== null) {
|
|
26
|
+
clearTimeout(device.timeoutId)
|
|
27
|
+
}
|
|
28
|
+
// Запустить новый
|
|
29
|
+
const id = setTimeout(() => {
|
|
30
|
+
if (index < devices.length && devices[index].DevSN === device.DevSN) {
|
|
31
|
+
devices[index].isFresh = false
|
|
32
|
+
devices[index].timeoutId = null
|
|
33
|
+
}
|
|
34
|
+
}, markerTimeout) as unknown as number
|
|
35
|
+
devices[index].timeoutId = id
|
|
36
|
+
devices[index].isFresh = true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Обработка входящих данных
|
|
40
|
+
$effect(() => {
|
|
41
|
+
if (data) {
|
|
42
|
+
const idx = devices.findIndex((d) => d.DevSN === data?.DevSN)
|
|
43
|
+
if (idx !== -1) {
|
|
44
|
+
// Обновление существующего
|
|
45
|
+
devices[idx] = { ...devices[idx], ...data }
|
|
46
|
+
restartFreshTimer(idx)
|
|
47
|
+
} else {
|
|
48
|
+
// Новое устройство
|
|
49
|
+
const newDevice: MapDevice = {
|
|
50
|
+
...data,
|
|
51
|
+
isFresh: true,
|
|
52
|
+
timeoutId: null,
|
|
53
|
+
}
|
|
54
|
+
devices.push(newDevice)
|
|
55
|
+
restartFreshTimer(devices.length - 1)
|
|
56
|
+
}
|
|
57
|
+
data = null
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const handleThemeChange = (event: CustomEvent) => {
|
|
62
|
+
isDarkMode = !event.detail.currentTheme
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onMount(() => {
|
|
66
|
+
if (typeof window !== 'undefined') {
|
|
67
|
+
isDarkMode = localStorage.getItem('AppTheme') !== 'light'
|
|
68
|
+
window.addEventListener('ThemeChange', handleThemeChange as EventListener)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
onDestroy(() => {
|
|
73
|
+
if (typeof window !== 'undefined') window.addEventListener('ThemeChange', handleThemeChange as EventListener)
|
|
74
|
+
|
|
75
|
+
for (const device of devices) {
|
|
76
|
+
if (device.timeoutId !== null) {
|
|
77
|
+
clearTimeout(device.timeoutId)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const timeoutOptions: { label: string; value: number }[] = [
|
|
83
|
+
{ label: '30 sec', value: 30_000 },
|
|
84
|
+
{ label: '1 min', value: 60_000 },
|
|
85
|
+
{ label: '3 min', value: 180_000 },
|
|
86
|
+
{ label: '5 min', value: 300_000 },
|
|
87
|
+
{ label: '10 min', value: 600_000 },
|
|
88
|
+
{ label: '30 min', value: 1_800_000 },
|
|
89
|
+
{ label: '1 h', value: 3_600_000 },
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
const changeTimeout = (val: number) => {
|
|
93
|
+
markerTimeout = val
|
|
94
|
+
// перезапускаем таймеры для всех устройств
|
|
95
|
+
devices.forEach((_, idx) => restartFreshTimer(idx))
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="h-full min-h-[200px]">
|
|
100
|
+
{#if label.name}
|
|
101
|
+
<h5 class={twMerge(` w-full px-4 text-center`, label.class)}>{label.name}</h5>
|
|
102
|
+
{/if}
|
|
103
|
+
<MapLibre
|
|
104
|
+
class="h-[calc(100%-2rem)] min-h-[200px]"
|
|
105
|
+
style={isDarkMode
|
|
106
|
+
? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json'
|
|
107
|
+
: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json'}
|
|
108
|
+
zoom={1.5}
|
|
109
|
+
center={{ lat: 30, lng: 0 }}
|
|
110
|
+
>
|
|
111
|
+
<NavigationControl />
|
|
112
|
+
<ScaleControl />
|
|
113
|
+
<GeolocateControl />
|
|
114
|
+
<FullScreenControl />
|
|
115
|
+
|
|
116
|
+
<CustomControl position="top-left">
|
|
117
|
+
<div class="flex items-center gap-2 px-2 py-1 text-black">
|
|
118
|
+
<label for="timeout" class="text-sm font-medium">{$t('constructor.props.map.timeout')}</label>
|
|
119
|
+
<select
|
|
120
|
+
id="timeout"
|
|
121
|
+
class="rounded px-2 py-1 text-sm"
|
|
122
|
+
bind:value={markerTimeout}
|
|
123
|
+
onchange={(e) => changeTimeout(parseInt((e.target as HTMLSelectElement).value))}
|
|
124
|
+
>
|
|
125
|
+
{#each timeoutOptions as opt}
|
|
126
|
+
<option value={opt.value}>{opt.label}</option>
|
|
127
|
+
{/each}
|
|
128
|
+
</select>
|
|
129
|
+
</div>
|
|
130
|
+
</CustomControl>
|
|
131
|
+
|
|
132
|
+
{#each devices as device}
|
|
133
|
+
<Marker lnglat={{ lng: device.NavLon, lat: device.NavLat }}>
|
|
134
|
+
{#snippet content()}
|
|
135
|
+
<div class="flex flex-col items-center justify-center leading-none">
|
|
136
|
+
<div
|
|
137
|
+
class="flex size-8 shrink-0 items-center justify-center [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full
|
|
138
|
+
{device.isFresh ? 'text-green-500' : 'text-red-500'}"
|
|
139
|
+
style="rotate: {device.NavHeading - 90}deg;"
|
|
140
|
+
>
|
|
141
|
+
{@html markerIcon ||
|
|
142
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M14.76 12H6.832m0 0c0-.275-.057-.55-.17-.808L4.285 5.814c-.76-1.72 1.058-3.442 2.734-2.591L20.8 10.217c1.46.74 1.46 2.826 0 3.566L7.02 20.777c-1.677.851-3.495-.872-2.735-2.591l2.375-5.378A2 2 0 0 0 6.83 12"/></svg>'}
|
|
143
|
+
</div>
|
|
144
|
+
<p class="font-bold">{device.DevName}</p>
|
|
145
|
+
</div>
|
|
146
|
+
{/snippet}
|
|
147
|
+
<Popup closeButton={false} class="rounded-2xl text-left">
|
|
148
|
+
<p>DevSN: {device.DevSN}</p>
|
|
149
|
+
<p>Lat: {`${device.NavLat.toFixed(3)} | Lon: ${device.NavLon.toFixed(3)} | Alt: ${device.NavAlt}`}</p>
|
|
150
|
+
<p>Heading: {device.NavHeading} | Sat Use: {device.NavSatUse}</p>
|
|
151
|
+
|
|
152
|
+
<div class="relative flex justify-between">
|
|
153
|
+
<button
|
|
154
|
+
class="absolute right-0 flex cursor-pointer border-none bg-transparent"
|
|
155
|
+
onclick={(e) => {
|
|
156
|
+
e.preventDefault()
|
|
157
|
+
navigator.clipboard.writeText(
|
|
158
|
+
`DevName: ${device.DevName}\nDevSN: ${device.DevSN}\nLat: ${device.NavLat.toFixed(3)} | Lon: ${device.NavLon.toFixed(3)} | Alt: ${device.NavAlt}\nHeading: ${device.NavHeading} | Sat Use: ${device.NavSatUse}`,
|
|
159
|
+
)
|
|
160
|
+
isCopied = true
|
|
161
|
+
setTimeout(() => (isCopied = false), 1000)
|
|
162
|
+
}}
|
|
163
|
+
aria-label="Копировать текст"
|
|
164
|
+
>
|
|
165
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1.5rem" height="1.5rem" viewBox="0 0 24 24">
|
|
166
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
167
|
+
<path
|
|
168
|
+
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"
|
|
169
|
+
/>
|
|
170
|
+
<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" />
|
|
171
|
+
</g>
|
|
172
|
+
</svg>
|
|
173
|
+
</button>
|
|
174
|
+
{#if isCopied}
|
|
175
|
+
<div
|
|
176
|
+
class="absolute top-1/2 right-0 -translate-y-1/2 transform rounded-md bg-(--green-color) px-2 py-1 text-sm shadow-lg"
|
|
177
|
+
transition:fade={{ duration: 200 }}
|
|
178
|
+
>
|
|
179
|
+
✓
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
|
182
|
+
|
|
183
|
+
<button
|
|
184
|
+
class="size-6 cursor-pointer"
|
|
185
|
+
aria-label="Удалить"
|
|
186
|
+
onclick={() => (devices = devices.filter((dev) => dev.DevSN !== device.DevSN))}
|
|
187
|
+
>
|
|
188
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1.5rem" height="1.5rem" viewBox="0 0 24 24"
|
|
189
|
+
><path
|
|
190
|
+
fill="none"
|
|
191
|
+
stroke="currentColor"
|
|
192
|
+
stroke-linecap="round"
|
|
193
|
+
stroke-linejoin="round"
|
|
194
|
+
stroke-width="1.5"
|
|
195
|
+
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"
|
|
196
|
+
color="currentColor"
|
|
197
|
+
/></svg
|
|
198
|
+
>
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
</Popup>
|
|
202
|
+
</Marker>
|
|
203
|
+
{/each}
|
|
204
|
+
</MapLibre>
|
|
205
|
+
</div>
|