poe-svelte-ui-lib 1.0.0 → 1.0.2

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.
Files changed (36) hide show
  1. package/LICENSE +3 -3
  2. package/dist/Accordion/Accordion.svelte +53 -53
  3. package/dist/Accordion/AccordionProps.svelte +70 -70
  4. package/dist/Button/Button.svelte +144 -144
  5. package/dist/Button/ButtonProps.svelte +200 -200
  6. package/dist/ColorPicker/ColorPicker.svelte +207 -207
  7. package/dist/ColorPicker/ColorPickerProps.svelte +100 -100
  8. package/dist/FileAttach/FileAttach.svelte +103 -103
  9. package/dist/Graph/Graph.svelte +270 -270
  10. package/dist/Graph/GraphProps.svelte +56 -56
  11. package/dist/Input/Input.svelte +239 -239
  12. package/dist/Input/InputProps.svelte +221 -221
  13. package/dist/Loader.svelte +12 -12
  14. package/dist/MessageModal.svelte +54 -54
  15. package/dist/ProgressBar/ProgressBar.svelte +48 -48
  16. package/dist/ProgressBar/ProgressBarProps.svelte +145 -145
  17. package/dist/Select/Select.svelte +191 -187
  18. package/dist/Select/SelectProps.svelte +260 -260
  19. package/dist/Slider/Slider.svelte +260 -260
  20. package/dist/Slider/SliderProps.svelte +161 -161
  21. package/dist/Switch/Switch.svelte +83 -83
  22. package/dist/Switch/SwitchProps.svelte +144 -144
  23. package/dist/Table/Table.svelte +276 -276
  24. package/dist/Table/TableProps.svelte +286 -286
  25. package/dist/TextField/TextField.svelte +22 -22
  26. package/dist/TextField/TextFieldProps.svelte +92 -92
  27. package/dist/{appIcons → libIcons}/ButtonAdd.svelte +10 -10
  28. package/dist/{appIcons → libIcons}/ButtonDelete.svelte +13 -13
  29. package/dist/{appIcons → libIcons}/LoaderRotate.svelte +9 -9
  30. package/dist/locales/CircleFlagsEn.svelte +14 -14
  31. package/dist/locales/CircleFlagsRu.svelte +8 -8
  32. package/dist/locales/CircleFlagsZh.svelte +8 -8
  33. package/package.json +49 -47
  34. /package/dist/{appIcons → libIcons}/ButtonAdd.svelte.d.ts +0 -0
  35. /package/dist/{appIcons → libIcons}/ButtonDelete.svelte.d.ts +0 -0
  36. /package/dist/{appIcons → libIcons}/LoaderRotate.svelte.d.ts +0 -0
@@ -1,270 +1,270 @@
1
- <!-- $lib/ElementsUI/Graph.svelte -->
2
- <script lang="ts">
3
- import { onMount } from 'svelte'
4
- import Select from '../Select/Select.svelte'
5
- import type { IGraphDataObject, IGraphProps, ISelectOption } from '../types'
6
-
7
- /* Инициализация пропсов с дефолтными значениями */
8
- let {
9
- id = { name: '', value: crypto.randomUUID() },
10
- wrapperClass = '',
11
- label = { name: '', class: '' },
12
- streamingData = { data: [], timestamp: Date.now() },
13
- isTest = false,
14
- }: IGraphProps = $props()
15
-
16
- /* Состояние компонента */
17
- let graphData = $state<{ id: string; points: { x: number; y: number }[]; color: string; name: string }[]>([])
18
- let currentValues = $state<number[]>([])
19
- let container: HTMLDivElement
20
- let canvas: HTMLCanvasElement
21
- let ctx: CanvasRenderingContext2D
22
- let width = $state(600)
23
- let height = $state(125)
24
-
25
- /* Константы и настройки */
26
- const REFRESH_OPTIONS: ISelectOption[] = [
27
- { id: 'RefreshOption-AUTO', name: 'AUTO', value: 0, class: '' },
28
- { id: 'RefreshOption-10', name: '10', value: 10, class: '' },
29
- { id: 'RefreshOption-25', name: '25', value: 25, class: '' },
30
- { id: 'RefreshOption-50', name: '50', value: 50, class: '' },
31
- { id: 'RefreshOption-100', name: '100', value: 100, class: '' },
32
- { id: 'RefreshOption-250', name: '250', value: 250, class: '' },
33
- { id: 'RefreshOption-500', name: '500', value: 500, class: '' },
34
- { id: 'RefreshOption-1000', name: '1000', value: 1000, class: '' },
35
- { id: 'RefreshOption-5000', name: '5000', value: 5000, class: '' },
36
- ]
37
- // const REFRESH_OPTIONS = [10, 25, 50, 100, 250, 500, 1000, 5000]
38
- const SCALE_OPTIONS: ISelectOption[] = [
39
- { id: 'ScaleOption-50', name: '50', value: 50, class: '' },
40
- { id: 'ScaleOption-100', name: '100', value: 100, class: '' },
41
- { id: 'ScaleOption-500', name: '500', value: 500, class: '' },
42
- { id: 'ScaleOption-1000', name: '1000', value: 1000, class: '' },
43
- { id: 'ScaleOption-2000', name: '2000', value: 2000, class: '' },
44
- ]
45
- // const SCALE_OPTIONS = [50, 100, 500, 1000, 2000]
46
- let selectedRefreshRate = $state(0)
47
- let selectedScale = $state(100)
48
- const maxDataPoints = $derived(selectedRefreshRate == 0 ? 20 : 100)
49
- const defaultColors = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899']
50
-
51
- let previousDataTimestamp: number = $state(0)
52
-
53
- /* Инициализация данных графиков */
54
- const initializeGraphData = () => {
55
- if (typeof streamingData === 'string') {
56
- streamingData = JSON.parse(streamingData)
57
- }
58
- if (!streamingData.data || streamingData.data.length === 0) {
59
- graphData = []
60
- currentValues = []
61
- return
62
- }
63
- const newGraphData = (streamingData.data as IGraphDataObject[]).slice(0, 6).map((d, i) => {
64
- const existingData = graphData.find((g) => g.name === d.name) || graphData[i]
65
- return {
66
- id: existingData?.id || crypto.randomUUID(),
67
- points: existingData?.points || [],
68
- color: d.color || defaultColors[i % defaultColors.length],
69
- name: d.name || `Value ${i}`,
70
- }
71
- })
72
- if (JSON.stringify(graphData) !== JSON.stringify(newGraphData)) {
73
- graphData = newGraphData
74
- currentValues = (streamingData.data as IGraphDataObject[]).map((d) => d.value || 0)
75
- }
76
- }
77
-
78
- /* Вызываем инициализацию при монтировании и при изменении streamingData */
79
- onMount(initializeGraphData)
80
- $effect(() => {
81
- initializeGraphData()
82
- })
83
-
84
- /* Обработка входящих данных */
85
- let intervalId: ReturnType<typeof setInterval>
86
- $effect(() => {
87
- clearInterval(intervalId)
88
- if (selectedRefreshRate > 0 && streamingData.data && streamingData.data.length > 0) {
89
- intervalId = setInterval(() => {
90
- let newValues
91
- if (isTest) newValues = graphData.map(() => Math.random() * 100 - 50)
92
- else newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
93
- const now = Date.now()
94
- newValues.forEach((value, i) => {
95
- if (!graphData[i]) return
96
- graphData[i].points.push({ x: now, y: value })
97
- if (graphData[i].points.length > maxDataPoints) {
98
- graphData[i].points.shift()
99
- }
100
- currentValues[i] = value
101
- })
102
- drawAllGraphs()
103
- }, selectedRefreshRate)
104
- } else if (selectedRefreshRate == 0 && streamingData.data && streamingData.data.length > 0 && !isTest) {
105
- intervalId = setInterval(() => {
106
- if (previousDataTimestamp < (streamingData.timestamp ?? Date.now())) {
107
- let newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
108
-
109
- newValues.forEach((value, i) => {
110
- if (!graphData[i]) return
111
- graphData[i].points.push({ x: streamingData.timestamp ?? Date.now(), y: value })
112
- if (graphData[i].points.length > maxDataPoints) {
113
- graphData[i].points.shift()
114
- }
115
- currentValues[i] = value
116
- })
117
- drawAllGraphs()
118
- previousDataTimestamp = streamingData.timestamp ?? Date.now()
119
- }
120
- }, 10)
121
- }
122
- console.log(streamingData.data)
123
- return () => clearInterval(intervalId)
124
- })
125
-
126
- let resizeObserver: ResizeObserver
127
- $effect(() => {
128
- if (!container || !canvas) return
129
- const dpr = window.devicePixelRatio || 1
130
- resizeObserver = new ResizeObserver(() => {
131
- const rect = container.getBoundingClientRect()
132
- width = rect.width
133
- height = rect.height
134
- canvas.width = width * dpr
135
- canvas.height = height * dpr
136
- canvas.style.width = `${width}px`
137
- canvas.style.height = `${height}px`
138
- ctx = canvas.getContext('2d')!
139
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
140
- drawAllGraphs()
141
- })
142
- resizeObserver.observe(container)
143
- return () => resizeObserver.disconnect()
144
- })
145
-
146
- const drawAllGraphs = () => {
147
- if (!ctx) return
148
- ctx.clearRect(0, 0, width, height)
149
-
150
- const padding = {
151
- top: 10,
152
- right: 10,
153
- bottom: 20,
154
- left: 35,
155
- }
156
-
157
- const graphWidth = width - padding.left - padding.right
158
- const graphHeight = height - padding.top - padding.bottom
159
-
160
- const allPoints = graphData.flatMap((g) => g.points)
161
- const minX = Math.min(...allPoints.map((p) => p.x))
162
- const maxX = Math.max(...allPoints.map((p) => p.x))
163
- const timeSpan = maxX - minX || 1
164
-
165
- // Функции преобразования координат с учетом отступов
166
- const getX = (x: number) => padding.left + ((x - minX) / timeSpan) * graphWidth
167
- const getY = (y: number) => padding.top + graphHeight - (((y / selectedScale) * graphHeight) / 2 + graphHeight / 2)
168
-
169
- /* Сетка X */
170
- ctx.strokeStyle = '#777'
171
- ctx.fillStyle = '#777'
172
- ctx.lineWidth = 0.5
173
- ctx.font = '10px monospace'
174
- ctx.textAlign = 'center'
175
-
176
- const now = Date.now()
177
- for (let i = 0; i <= 10; i++) {
178
- const t = minX + (i / 10) * timeSpan
179
- const x = getX(t)
180
- const secondsAgo = ((t - now) / 1000).toFixed(0)
181
- ctx.beginPath()
182
- ctx.moveTo(x, padding.top)
183
- ctx.lineTo(x, height - padding.bottom)
184
- ctx.stroke()
185
- ctx.textBaseline = 'top'
186
- ctx.fillText(`${secondsAgo}s`, x, height - padding.bottom + 2)
187
- }
188
-
189
- /* Сетка Y */
190
- ctx.textAlign = 'right'
191
- ctx.textBaseline = 'middle'
192
- const ySteps = 8
193
- for (let i = 0; i <= ySteps; i++) {
194
- const yVal = selectedScale - (i * 2 * selectedScale) / ySteps
195
- const y = getY(yVal)
196
- ctx.beginPath()
197
- ctx.moveTo(padding.left, y)
198
- ctx.lineTo(width - padding.right, y)
199
- ctx.stroke()
200
- ctx.fillText(`${yVal.toFixed(0)}`, padding.left - 5, y)
201
- }
202
-
203
- /* Отрисовка графиков */
204
- ctx.lineWidth = 2
205
- graphData.forEach(({ points, color }) => {
206
- if (points.length < 2) return
207
- ctx.strokeStyle = color
208
- ctx.beginPath()
209
- ctx.moveTo(getX(points[0].x), getY(points[0].y))
210
- for (let i = 1; i < points.length - 1; i++) {
211
- const p1 = points[i]
212
- const p2 = points[i + 1]
213
- const xc2 = (p1.x + p2.x) / 2
214
- const yc2 = (p1.y + p2.y) / 2
215
- ctx.quadraticCurveTo(getX(p1.x), getY(p1.y), getX(xc2), getY(yc2))
216
- }
217
- const last = points[points.length - 1]
218
- ctx.lineTo(getX(last.x), getY(last.y))
219
- ctx.stroke()
220
- })
221
- }
222
- </script>
223
-
224
- <div id={id.value} class={`relative flex w-full flex-col items-center justify-center ${wrapperClass}`}>
225
- {#if label.name}
226
- <h5 class={`w-full px-4 text-center ${label.class}`}>{label.name}</h5>
227
- {/if}
228
-
229
- <div class="flex w-full flex-row gap-4">
230
- <!-- График -->
231
- <div bind:this={container} class="h-64 flex-grow overflow-hidden rounded-md border border-gray-200">
232
- <canvas class="h-full w-full bg-[var(--back-color)]" bind:this={canvas}></canvas>
233
- </div>
234
-
235
- <!-- Панель настроек -->
236
- <div class="flex w-48 flex-col gap-2">
237
- <!-- Развертка по горизонтали -->
238
- <Select
239
- label={{ name: 'Refresh rate', class: '' }}
240
- options={REFRESH_OPTIONS}
241
- value={REFRESH_OPTIONS.find((o) => o.value == selectedRefreshRate)}
242
- onUpdate={(value) => (selectedRefreshRate = value.value as number)}
243
- />
244
-
245
- <!-- Масштаб по вертикали -->
246
- <Select
247
- label={{ name: 'Scale' }}
248
- options={SCALE_OPTIONS}
249
- value={REFRESH_OPTIONS.find((o) => o.value == selectedScale)}
250
- onUpdate={(value) => (selectedScale = value.value as number)}
251
- />
252
-
253
- <!-- Переменные и их значение -->
254
- <div>
255
- <h5 class="px-4">Values</h5>
256
- <table class="w-full font-mono text-sm">
257
- <tbody>
258
- {#each graphData as data, i (i)}
259
- <tr>
260
- <td><div class="mr-2 h-4 w-4 rounded-full" style="background-color: {data.color}"></div></td>
261
- <td class="w-24 truncate text-left font-semibold">{(streamingData.data as IGraphDataObject[])?.[i]?.name}</td>
262
- <td class="w-16 text-right">{currentValues[i].toFixed(2)}</td>
263
- </tr>
264
- {/each}
265
- </tbody>
266
- </table>
267
- </div>
268
- </div>
269
- </div>
270
- </div>
1
+ <!-- $lib/ElementsUI/Graph.svelte -->
2
+ <script lang="ts">
3
+ import { onMount } from 'svelte'
4
+ import Select from '../Select/Select.svelte'
5
+ import type { IGraphDataObject, IGraphProps, ISelectOption } from '../types'
6
+
7
+ /* Инициализация пропсов с дефолтными значениями */
8
+ let {
9
+ id = { name: '', value: crypto.randomUUID() },
10
+ wrapperClass = '',
11
+ label = { name: '', class: '' },
12
+ streamingData = { data: [], timestamp: Date.now() },
13
+ isTest = false,
14
+ }: IGraphProps = $props()
15
+
16
+ /* Состояние компонента */
17
+ let graphData = $state<{ id: string; points: { x: number; y: number }[]; color: string; name: string }[]>([])
18
+ let currentValues = $state<number[]>([])
19
+ let container: HTMLDivElement
20
+ let canvas: HTMLCanvasElement
21
+ let ctx: CanvasRenderingContext2D
22
+ let width = $state(600)
23
+ let height = $state(125)
24
+
25
+ /* Константы и настройки */
26
+ const REFRESH_OPTIONS: ISelectOption[] = [
27
+ { id: 'RefreshOption-AUTO', name: 'AUTO', value: 0, class: '' },
28
+ { id: 'RefreshOption-10', name: '10', value: 10, class: '' },
29
+ { id: 'RefreshOption-25', name: '25', value: 25, class: '' },
30
+ { id: 'RefreshOption-50', name: '50', value: 50, class: '' },
31
+ { id: 'RefreshOption-100', name: '100', value: 100, class: '' },
32
+ { id: 'RefreshOption-250', name: '250', value: 250, class: '' },
33
+ { id: 'RefreshOption-500', name: '500', value: 500, class: '' },
34
+ { id: 'RefreshOption-1000', name: '1000', value: 1000, class: '' },
35
+ { id: 'RefreshOption-5000', name: '5000', value: 5000, class: '' },
36
+ ]
37
+ // const REFRESH_OPTIONS = [10, 25, 50, 100, 250, 500, 1000, 5000]
38
+ const SCALE_OPTIONS: ISelectOption[] = [
39
+ { id: 'ScaleOption-50', name: '50', value: 50, class: '' },
40
+ { id: 'ScaleOption-100', name: '100', value: 100, class: '' },
41
+ { id: 'ScaleOption-500', name: '500', value: 500, class: '' },
42
+ { id: 'ScaleOption-1000', name: '1000', value: 1000, class: '' },
43
+ { id: 'ScaleOption-2000', name: '2000', value: 2000, class: '' },
44
+ ]
45
+ // const SCALE_OPTIONS = [50, 100, 500, 1000, 2000]
46
+ let selectedRefreshRate = $state(0)
47
+ let selectedScale = $state(100)
48
+ const maxDataPoints = $derived(selectedRefreshRate == 0 ? 20 : 100)
49
+ const defaultColors = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899']
50
+
51
+ let previousDataTimestamp: number = $state(0)
52
+
53
+ /* Инициализация данных графиков */
54
+ const initializeGraphData = () => {
55
+ if (typeof streamingData === 'string') {
56
+ streamingData = JSON.parse(streamingData)
57
+ }
58
+ if (!streamingData.data || streamingData.data.length === 0) {
59
+ graphData = []
60
+ currentValues = []
61
+ return
62
+ }
63
+ const newGraphData = (streamingData.data as IGraphDataObject[]).slice(0, 6).map((d, i) => {
64
+ const existingData = graphData.find((g) => g.name === d.name) || graphData[i]
65
+ return {
66
+ id: existingData?.id || crypto.randomUUID(),
67
+ points: existingData?.points || [],
68
+ color: d.color || defaultColors[i % defaultColors.length],
69
+ name: d.name || `Value ${i}`,
70
+ }
71
+ })
72
+ if (JSON.stringify(graphData) !== JSON.stringify(newGraphData)) {
73
+ graphData = newGraphData
74
+ currentValues = (streamingData.data as IGraphDataObject[]).map((d) => d.value || 0)
75
+ }
76
+ }
77
+
78
+ /* Вызываем инициализацию при монтировании и при изменении streamingData */
79
+ onMount(initializeGraphData)
80
+ $effect(() => {
81
+ initializeGraphData()
82
+ })
83
+
84
+ /* Обработка входящих данных */
85
+ let intervalId: ReturnType<typeof setInterval>
86
+ $effect(() => {
87
+ clearInterval(intervalId)
88
+ if (selectedRefreshRate > 0 && streamingData.data && streamingData.data.length > 0) {
89
+ intervalId = setInterval(() => {
90
+ let newValues
91
+ if (isTest) newValues = graphData.map(() => Math.random() * 100 - 50)
92
+ else newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
93
+ const now = Date.now()
94
+ newValues.forEach((value, i) => {
95
+ if (!graphData[i]) return
96
+ graphData[i].points.push({ x: now, y: value })
97
+ if (graphData[i].points.length > maxDataPoints) {
98
+ graphData[i].points.shift()
99
+ }
100
+ currentValues[i] = value
101
+ })
102
+ drawAllGraphs()
103
+ }, selectedRefreshRate)
104
+ } else if (selectedRefreshRate == 0 && streamingData.data && streamingData.data.length > 0 && !isTest) {
105
+ intervalId = setInterval(() => {
106
+ if (previousDataTimestamp < (streamingData.timestamp ?? Date.now())) {
107
+ let newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
108
+
109
+ newValues.forEach((value, i) => {
110
+ if (!graphData[i]) return
111
+ graphData[i].points.push({ x: streamingData.timestamp ?? Date.now(), y: value })
112
+ if (graphData[i].points.length > maxDataPoints) {
113
+ graphData[i].points.shift()
114
+ }
115
+ currentValues[i] = value
116
+ })
117
+ drawAllGraphs()
118
+ previousDataTimestamp = streamingData.timestamp ?? Date.now()
119
+ }
120
+ }, 10)
121
+ }
122
+ console.log(streamingData.data)
123
+ return () => clearInterval(intervalId)
124
+ })
125
+
126
+ let resizeObserver: ResizeObserver
127
+ $effect(() => {
128
+ if (!container || !canvas) return
129
+ const dpr = window.devicePixelRatio || 1
130
+ resizeObserver = new ResizeObserver(() => {
131
+ const rect = container.getBoundingClientRect()
132
+ width = rect.width
133
+ height = rect.height
134
+ canvas.width = width * dpr
135
+ canvas.height = height * dpr
136
+ canvas.style.width = `${width}px`
137
+ canvas.style.height = `${height}px`
138
+ ctx = canvas.getContext('2d')!
139
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
140
+ drawAllGraphs()
141
+ })
142
+ resizeObserver.observe(container)
143
+ return () => resizeObserver.disconnect()
144
+ })
145
+
146
+ const drawAllGraphs = () => {
147
+ if (!ctx) return
148
+ ctx.clearRect(0, 0, width, height)
149
+
150
+ const padding = {
151
+ top: 10,
152
+ right: 10,
153
+ bottom: 20,
154
+ left: 35,
155
+ }
156
+
157
+ const graphWidth = width - padding.left - padding.right
158
+ const graphHeight = height - padding.top - padding.bottom
159
+
160
+ const allPoints = graphData.flatMap((g) => g.points)
161
+ const minX = Math.min(...allPoints.map((p) => p.x))
162
+ const maxX = Math.max(...allPoints.map((p) => p.x))
163
+ const timeSpan = maxX - minX || 1
164
+
165
+ // Функции преобразования координат с учетом отступов
166
+ const getX = (x: number) => padding.left + ((x - minX) / timeSpan) * graphWidth
167
+ const getY = (y: number) => padding.top + graphHeight - (((y / selectedScale) * graphHeight) / 2 + graphHeight / 2)
168
+
169
+ /* Сетка X */
170
+ ctx.strokeStyle = '#777'
171
+ ctx.fillStyle = '#777'
172
+ ctx.lineWidth = 0.5
173
+ ctx.font = '10px monospace'
174
+ ctx.textAlign = 'center'
175
+
176
+ const now = Date.now()
177
+ for (let i = 0; i <= 10; i++) {
178
+ const t = minX + (i / 10) * timeSpan
179
+ const x = getX(t)
180
+ const secondsAgo = ((t - now) / 1000).toFixed(0)
181
+ ctx.beginPath()
182
+ ctx.moveTo(x, padding.top)
183
+ ctx.lineTo(x, height - padding.bottom)
184
+ ctx.stroke()
185
+ ctx.textBaseline = 'top'
186
+ ctx.fillText(`${secondsAgo}s`, x, height - padding.bottom + 2)
187
+ }
188
+
189
+ /* Сетка Y */
190
+ ctx.textAlign = 'right'
191
+ ctx.textBaseline = 'middle'
192
+ const ySteps = 8
193
+ for (let i = 0; i <= ySteps; i++) {
194
+ const yVal = selectedScale - (i * 2 * selectedScale) / ySteps
195
+ const y = getY(yVal)
196
+ ctx.beginPath()
197
+ ctx.moveTo(padding.left, y)
198
+ ctx.lineTo(width - padding.right, y)
199
+ ctx.stroke()
200
+ ctx.fillText(`${yVal.toFixed(0)}`, padding.left - 5, y)
201
+ }
202
+
203
+ /* Отрисовка графиков */
204
+ ctx.lineWidth = 2
205
+ graphData.forEach(({ points, color }) => {
206
+ if (points.length < 2) return
207
+ ctx.strokeStyle = color
208
+ ctx.beginPath()
209
+ ctx.moveTo(getX(points[0].x), getY(points[0].y))
210
+ for (let i = 1; i < points.length - 1; i++) {
211
+ const p1 = points[i]
212
+ const p2 = points[i + 1]
213
+ const xc2 = (p1.x + p2.x) / 2
214
+ const yc2 = (p1.y + p2.y) / 2
215
+ ctx.quadraticCurveTo(getX(p1.x), getY(p1.y), getX(xc2), getY(yc2))
216
+ }
217
+ const last = points[points.length - 1]
218
+ ctx.lineTo(getX(last.x), getY(last.y))
219
+ ctx.stroke()
220
+ })
221
+ }
222
+ </script>
223
+
224
+ <div id={id.value} class={`relative flex w-full flex-col items-center justify-center ${wrapperClass}`}>
225
+ {#if label.name}
226
+ <h5 class={`w-full px-4 text-center ${label.class}`}>{label.name}</h5>
227
+ {/if}
228
+
229
+ <div class="flex w-full flex-row gap-4">
230
+ <!-- График -->
231
+ <div bind:this={container} class="h-64 flex-grow overflow-hidden rounded-md border border-gray-200">
232
+ <canvas class="h-full w-full bg-[var(--back-color)]" bind:this={canvas}></canvas>
233
+ </div>
234
+
235
+ <!-- Панель настроек -->
236
+ <div class="flex w-48 flex-col gap-2">
237
+ <!-- Развертка по горизонтали -->
238
+ <Select
239
+ label={{ name: 'Refresh rate', class: '' }}
240
+ options={REFRESH_OPTIONS}
241
+ value={REFRESH_OPTIONS.find((o) => o.value == selectedRefreshRate)}
242
+ onUpdate={(value) => (selectedRefreshRate = value.value as number)}
243
+ />
244
+
245
+ <!-- Масштаб по вертикали -->
246
+ <Select
247
+ label={{ name: 'Scale' }}
248
+ options={SCALE_OPTIONS}
249
+ value={REFRESH_OPTIONS.find((o) => o.value == selectedScale)}
250
+ onUpdate={(value) => (selectedScale = value.value as number)}
251
+ />
252
+
253
+ <!-- Переменные и их значение -->
254
+ <div>
255
+ <h5 class="px-4">Values</h5>
256
+ <table class="w-full font-mono text-sm">
257
+ <tbody>
258
+ {#each graphData as data, i (i)}
259
+ <tr>
260
+ <td><div class="mr-2 h-4 w-4 rounded-full" style="background-color: {data.color}"></div></td>
261
+ <td class="w-24 truncate text-left font-semibold">{(streamingData.data as IGraphDataObject[])?.[i]?.name}</td>
262
+ <td class="w-16 text-right">{currentValues[i].toFixed(2)}</td>
263
+ </tr>
264
+ {/each}
265
+ </tbody>
266
+ </table>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
@@ -1,56 +1,56 @@
1
- <!-- $lib/ElementsUI/SwitchProps.svelte -->
2
- <script lang="ts">
3
- import { getContext } from 'svelte'
4
- import { t } from '../locales/i18n'
5
- import type { UIComponent, IGraphProps } from '../types'
6
- import * as UI from '../index'
7
-
8
- const { component, onPropertyChange } = $props<{
9
- component: UIComponent & { properties: Partial<IGraphProps> }
10
- onPropertyChange: (value: string | object) => void
11
- }>()
12
-
13
- const DeviceVariables = getContext<{ value: string; name: string }[]>('DeviceVariables')
14
- let VARIABLE_OPTIONS = $derived(
15
- DeviceVariables.map((variable: { value: string; name: string }) => ({
16
- id: variable.name,
17
- value: variable.value,
18
- name: `${variable.value} | ${variable.name}`,
19
- })),
20
- )
21
-
22
- /* Обновление свойства */
23
- const updateProperty = (path: string, value: string | object) => {
24
- const newProperties = JSON.parse(JSON.stringify(component.properties))
25
- const parts = path.split('.')
26
- let obj = newProperties
27
-
28
- for (let i = 0; i < parts.length - 1; i++) {
29
- const part = parts[i]
30
- if (!obj[part]) obj[part] = {}
31
- obj = obj[part]
32
- }
33
-
34
- obj[parts[parts.length - 1]] = value
35
- onPropertyChange(newProperties)
36
- }
37
- </script>
38
-
39
- {#if component && component.properties}
40
- <div class="relative flex flex-row items-start justify-center">
41
- <!-- Сообщение для отправки в ws по нажатию кнопки -->
42
- <div class="flex w-1/3 flex-col items-center px-2">
43
- <UI.Select
44
- label={{ name: $t('service.constructor.props.variable') }}
45
- options={VARIABLE_OPTIONS}
46
- value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
47
- onUpdate={(value) => {
48
- updateProperty('id.name', (value.name as string).split('|')[1].trim())
49
- updateProperty('id.value', value.value as string)
50
- }}
51
- />
52
- </div>
53
- <div class="flex w-1/3 flex-col px-2"></div>
54
- <div class="flex w-1/3 flex-col px-2"></div>
55
- </div>
56
- {/if}
1
+ <!-- $lib/ElementsUI/SwitchProps.svelte -->
2
+ <script lang="ts">
3
+ import { getContext } from 'svelte'
4
+ import { t } from '../locales/i18n'
5
+ import type { UIComponent, IGraphProps } from '../types'
6
+ import * as UI from '../index'
7
+
8
+ const { component, onPropertyChange } = $props<{
9
+ component: UIComponent & { properties: Partial<IGraphProps> }
10
+ onPropertyChange: (value: string | object) => void
11
+ }>()
12
+
13
+ const DeviceVariables = getContext<{ value: string; name: string }[]>('DeviceVariables')
14
+ let VARIABLE_OPTIONS = $derived(
15
+ DeviceVariables.map((variable: { value: string; name: string }) => ({
16
+ id: variable.name,
17
+ value: variable.value,
18
+ name: `${variable.value} | ${variable.name}`,
19
+ })),
20
+ )
21
+
22
+ /* Обновление свойства */
23
+ const updateProperty = (path: string, value: string | object) => {
24
+ const newProperties = JSON.parse(JSON.stringify(component.properties))
25
+ const parts = path.split('.')
26
+ let obj = newProperties
27
+
28
+ for (let i = 0; i < parts.length - 1; i++) {
29
+ const part = parts[i]
30
+ if (!obj[part]) obj[part] = {}
31
+ obj = obj[part]
32
+ }
33
+
34
+ obj[parts[parts.length - 1]] = value
35
+ onPropertyChange(newProperties)
36
+ }
37
+ </script>
38
+
39
+ {#if component && component.properties}
40
+ <div class="relative flex flex-row items-start justify-center">
41
+ <!-- Сообщение для отправки в ws по нажатию кнопки -->
42
+ <div class="flex w-1/3 flex-col items-center px-2">
43
+ <UI.Select
44
+ label={{ name: $t('service.constructor.props.variable') }}
45
+ options={VARIABLE_OPTIONS}
46
+ value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
47
+ onUpdate={(value) => {
48
+ updateProperty('id.name', (value.name as string).split('|')[1].trim())
49
+ updateProperty('id.value', value.value as string)
50
+ }}
51
+ />
52
+ </div>
53
+ <div class="flex w-1/3 flex-col px-2"></div>
54
+ <div class="flex w-1/3 flex-col px-2"></div>
55
+ </div>
56
+ {/if}