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.
@@ -46,10 +46,10 @@
46
46
  </script>
47
47
 
48
48
  {#if forConstructor}
49
- <div class="flex items-center justify-center gap-8">
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.icon.access') }}
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-center justify-center gap-8">
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 items-center px-2">
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.icon.access') }}
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.properties.access)}
107
- onUpdate={(option) => updateProperty('acces', option.value as string, component, onPropertyChange)}
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, value.name?.split('—')[1].trim())
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.icon.access') }}
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.properties.access)}
58
- onUpdate={(option) => updateProperty('acces', option.value as string, component, onPropertyChange)}
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.Input
86
- label={{ name: $t('constructor.props.wrapperclass') }}
87
- value={component.properties.wrapperClass}
88
- onUpdate={(value) => updateProperty('wrapperClass', value as string, component, onPropertyChange)}
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(id)
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={`${id}-${crypto.randomUUID().slice(0, 6)}`}
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, value.name?.split('—')[1].trim())
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.icon.access') }}
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.properties.access)}
108
- onUpdate={(option) => updateProperty('acces', option.value as string)}
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) => updateProperty('help.regExp', 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) => updateProperty('readonly', 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="absolute right-10 flex items-center">
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>
@@ -0,0 +1,4 @@
1
+ import type { IMapProps } from '../types';
2
+ declare const Map: import("svelte").Component<IMapProps, {}, "data">;
3
+ type Map = ReturnType<typeof Map>;
4
+ export default Map;