poe-svelte-ui-lib 1.10.4 → 1.10.6
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/Graph/Graph.svelte
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
<!-- $lib/ElementsUI/Graph.svelte -->
|
|
2
|
+
<script module lang="ts">
|
|
3
|
+
import type { IOption } from "../types"
|
|
4
|
+
|
|
5
|
+
/* Константы и настройки — экспортируются для переиспользования в GraphProps.svelte (редактор конструктора) */
|
|
6
|
+
export const REFRESH_OPTIONS: IOption[] = [
|
|
7
|
+
{ id: "RefreshOption-AUTO", name: "AUTO", value: 0, class: "" },
|
|
8
|
+
{ id: "RefreshOption-10", name: "10", value: 10, class: "" },
|
|
9
|
+
{ id: "RefreshOption-25", name: "25", value: 25, class: "" },
|
|
10
|
+
{ id: "RefreshOption-50", name: "50", value: 50, class: "" },
|
|
11
|
+
{ id: "RefreshOption-100", name: "100", value: 100, class: "" },
|
|
12
|
+
{ id: "RefreshOption-250", name: "250", value: 250, class: "" },
|
|
13
|
+
{ id: "RefreshOption-500", name: "500", value: 500, class: "" },
|
|
14
|
+
{ id: "RefreshOption-1000", name: "1000", value: 1000, class: "" },
|
|
15
|
+
{ id: "RefreshOption-5000", name: "5000", value: 5000, class: "" },
|
|
16
|
+
]
|
|
17
|
+
export const SCALE_OPTIONS: IOption[] = [
|
|
18
|
+
{ id: "ScaleOption-50", name: "50", value: 50, class: "" },
|
|
19
|
+
{ id: "ScaleOption-100", name: "100", value: 100, class: "" },
|
|
20
|
+
{ id: "ScaleOption-500", name: "500", value: 500, class: "" },
|
|
21
|
+
{ id: "ScaleOption-1000", name: "1000", value: 1000, class: "" },
|
|
22
|
+
{ id: "ScaleOption-2000", name: "2000", value: 2000, class: "" },
|
|
23
|
+
]
|
|
24
|
+
</script>
|
|
25
|
+
|
|
2
26
|
<script lang="ts">
|
|
3
27
|
import { onMount } from "svelte"
|
|
4
28
|
import Select from "../Select/Select.svelte"
|
|
@@ -9,8 +33,10 @@
|
|
|
9
33
|
id = crypto.randomUUID(),
|
|
10
34
|
wrapperClass = "",
|
|
11
35
|
label = { name: "", class: "" },
|
|
12
|
-
streamingData = { data: []
|
|
36
|
+
streamingData = { data: [] },
|
|
13
37
|
isTest = false,
|
|
38
|
+
refreshRate = 0,
|
|
39
|
+
scale = 100,
|
|
14
40
|
}: IGraphProps = $props()
|
|
15
41
|
|
|
16
42
|
/* Состояние компонента */
|
|
@@ -22,31 +48,15 @@
|
|
|
22
48
|
let width = $state(600)
|
|
23
49
|
let height = $state(125)
|
|
24
50
|
|
|
25
|
-
/*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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 SCALE_OPTIONS: IOption[] = [
|
|
38
|
-
{ id: "ScaleOption-50", name: "50", value: 50, class: "" },
|
|
39
|
-
{ id: "ScaleOption-100", name: "100", value: 100, class: "" },
|
|
40
|
-
{ id: "ScaleOption-500", name: "500", value: 500, class: "" },
|
|
41
|
-
{ id: "ScaleOption-1000", name: "1000", value: 1000, class: "" },
|
|
42
|
-
{ id: "ScaleOption-2000", name: "2000", value: 2000, class: "" },
|
|
43
|
-
]
|
|
44
|
-
let selectedRefreshRate = $derived(isTest ? 50 : 0)
|
|
45
|
-
let selectedScale = $state(100)
|
|
51
|
+
/* selectedRefreshRate/selectedScale изначально берутся из пропов (сохраняются вместе с компонентом в конструкторе),
|
|
52
|
+
но остаются локально переключаемыми через Select ниже — присвоение этим $derived временно
|
|
53
|
+
переопределяет значение, пока не изменится refreshRate/scale/isTest (проп) */
|
|
54
|
+
let selectedRefreshRate = $derived(isTest ? 50 : (refreshRate ?? 0))
|
|
55
|
+
let selectedScale = $derived(scale ?? 100)
|
|
46
56
|
const maxDataPoints = $derived(selectedRefreshRate == 0 ? 20 : 100)
|
|
47
57
|
const defaultColors = ["#3b82f6", "#ef4444", "#10b981", "#f59e0b", "#8b5cf6", "#ec4899"]
|
|
48
58
|
|
|
49
|
-
let
|
|
59
|
+
let previousValues: number[] = $state([])
|
|
50
60
|
|
|
51
61
|
/* Инициализация данных графиков */
|
|
52
62
|
const initializeGraphData = () => {
|
|
@@ -56,6 +66,7 @@
|
|
|
56
66
|
if (!streamingData.data || streamingData.data.length === 0) {
|
|
57
67
|
graphData = []
|
|
58
68
|
currentValues = []
|
|
69
|
+
previousValues = []
|
|
59
70
|
return
|
|
60
71
|
}
|
|
61
72
|
const newGraphData = (streamingData.data as IGraphDataObject[]).slice(0, 6).map((d, i) => {
|
|
@@ -79,12 +90,18 @@
|
|
|
79
90
|
initializeGraphData()
|
|
80
91
|
})
|
|
81
92
|
|
|
82
|
-
/* Обработка входящих
|
|
93
|
+
/* Обработка входящих данных.
|
|
94
|
+
ВАЖНО: streamingData читается только внутри колбэков setInterval (асинхронный контекст),
|
|
95
|
+
а не в теле $effect — иначе каждое новое сообщение (у нас до 10 Гц с устройства) делает
|
|
96
|
+
streamingData зависимостью эффекта, и он пересоздаёт таймер на каждый пакет: если Refresh
|
|
97
|
+
rate медленнее, чем частота входящих данных, setInterval убивается раньше, чем успевает
|
|
98
|
+
сработать хотя бы раз — график зависает. */
|
|
83
99
|
let intervalId: ReturnType<typeof setInterval>
|
|
84
100
|
$effect(() => {
|
|
85
101
|
clearInterval(intervalId)
|
|
86
|
-
if (selectedRefreshRate > 0
|
|
102
|
+
if (selectedRefreshRate > 0) {
|
|
87
103
|
intervalId = setInterval(() => {
|
|
104
|
+
if (!isTest && (!streamingData.data || streamingData.data.length === 0)) return
|
|
88
105
|
let newValues
|
|
89
106
|
if (isTest) newValues = graphData.map(() => Math.random() * 100 - 50)
|
|
90
107
|
else newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
|
|
@@ -99,22 +116,26 @@
|
|
|
99
116
|
})
|
|
100
117
|
drawAllGraphs()
|
|
101
118
|
}, selectedRefreshRate)
|
|
102
|
-
} else if (selectedRefreshRate == 0 &&
|
|
119
|
+
} else if (selectedRefreshRate == 0 && !isTest) {
|
|
120
|
+
/* AUTO: streamingData не несёт признак "это новый пакет" (timestamp никем не передаётся —
|
|
121
|
+
DeviceStore хранит только последнее значение), поэтому опрашиваем каждые 10мс и рисуем
|
|
122
|
+
точку только когда сами значения реально изменились, а не на каждый тик поллинга. */
|
|
103
123
|
intervalId = setInterval(() => {
|
|
104
|
-
if (
|
|
105
|
-
|
|
124
|
+
if (!streamingData.data || streamingData.data.length === 0) return
|
|
125
|
+
const newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
|
|
126
|
+
if (JSON.stringify(newValues) === JSON.stringify(previousValues)) return
|
|
127
|
+
previousValues = newValues
|
|
106
128
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
129
|
+
const now = Date.now()
|
|
130
|
+
newValues.forEach((value, i) => {
|
|
131
|
+
if (!graphData[i]) return
|
|
132
|
+
graphData[i].points.push({ x: now, y: value })
|
|
133
|
+
if (graphData[i].points.length > maxDataPoints) {
|
|
134
|
+
graphData[i].points.shift()
|
|
135
|
+
}
|
|
136
|
+
currentValues[i] = value
|
|
137
|
+
})
|
|
138
|
+
drawAllGraphs()
|
|
118
139
|
}, 10)
|
|
119
140
|
}
|
|
120
141
|
return () => clearInterval(intervalId)
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { IOption } from "../types";
|
|
2
|
+
export declare const REFRESH_OPTIONS: IOption[];
|
|
3
|
+
export declare const SCALE_OPTIONS: IOption[];
|
|
1
4
|
import type { IGraphProps } from "../types";
|
|
2
5
|
declare const Graph: import("svelte").Component<IGraphProps, {}, "">;
|
|
3
6
|
type Graph = ReturnType<typeof Graph>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getContext } from "svelte"
|
|
3
3
|
import { T } from "../locales/i18n"
|
|
4
|
-
import { type UIComponent, type IGraphProps, updateProperty, type IUIComponentHandler } from "../types"
|
|
4
|
+
import { type UIComponent, type IGraphProps, type IOption, updateProperty, type IUIComponentHandler } from "../types"
|
|
5
5
|
import * as UI from ".."
|
|
6
6
|
import { optionsStore } from "../options"
|
|
7
7
|
import { twMerge } from "tailwind-merge"
|
|
8
8
|
import CommonSnippets from "../CommonSnippets.svelte"
|
|
9
|
+
import { REFRESH_OPTIONS, SCALE_OPTIONS } from "./Graph.svelte"
|
|
9
10
|
|
|
10
11
|
const {
|
|
11
12
|
component,
|
|
@@ -37,6 +38,21 @@
|
|
|
37
38
|
/>
|
|
38
39
|
{/snippet}
|
|
39
40
|
|
|
41
|
+
{#snippet GraphSettings()}
|
|
42
|
+
<UI.Select
|
|
43
|
+
label={{ name: $T("constructor.props.refreshrate") }}
|
|
44
|
+
options={REFRESH_OPTIONS}
|
|
45
|
+
value={REFRESH_OPTIONS.find((o) => o.value == (component.properties.refreshRate ?? 0))}
|
|
46
|
+
onUpdate={(value) => updateProperty("refreshRate", (value as IOption).value as number, component, onPropertyChange)}
|
|
47
|
+
/>
|
|
48
|
+
<UI.Select
|
|
49
|
+
label={{ name: $T("constructor.props.scale") }}
|
|
50
|
+
options={SCALE_OPTIONS}
|
|
51
|
+
value={SCALE_OPTIONS.find((o) => o.value == (component.properties.scale ?? 100))}
|
|
52
|
+
onUpdate={(value) => updateProperty("scale", (value as IOption).value as number, component, onPropertyChange)}
|
|
53
|
+
/>
|
|
54
|
+
{/snippet}
|
|
55
|
+
|
|
40
56
|
{#if forConstructor}
|
|
41
57
|
<div class="relative flex flex-row items-start justify-center">
|
|
42
58
|
<div class="flex w-1/3 flex-col px-2">
|
|
@@ -49,6 +65,7 @@
|
|
|
49
65
|
</div>
|
|
50
66
|
<div class="flex w-1/3 flex-col px-2">
|
|
51
67
|
{@render GraphIsTest()}
|
|
68
|
+
{@render GraphSettings()}
|
|
52
69
|
</div>
|
|
53
70
|
</div>
|
|
54
71
|
{:else}
|
|
@@ -64,6 +81,7 @@
|
|
|
64
81
|
<div class="flex w-1/3 flex-col px-2">
|
|
65
82
|
<CommonSnippets snippet="WrapperClass" {component} {onPropertyChange} />
|
|
66
83
|
{@render GraphIsTest()}
|
|
84
|
+
{@render GraphSettings()}
|
|
67
85
|
</div>
|
|
68
86
|
</div>
|
|
69
87
|
{/if}
|
|
@@ -140,6 +140,8 @@ const translations = {
|
|
|
140
140
|
"constructor.props.disabled": "Отключить",
|
|
141
141
|
"constructor.props.readonly": "Только чтение",
|
|
142
142
|
"constructor.props.istest": "Тестовые данные",
|
|
143
|
+
"constructor.props.refreshrate": "Скорость обновления",
|
|
144
|
+
"constructor.props.scale": "Масштаб",
|
|
143
145
|
"constructor.props.variable": "Имя переменной",
|
|
144
146
|
"constructor.props.widthMode": "Ширина кнопок",
|
|
145
147
|
"constructor.props.graphdata.title": "Начальные данные ",
|
|
@@ -303,6 +305,8 @@ const translations = {
|
|
|
303
305
|
"common.select_tag": "Make a choice",
|
|
304
306
|
"constructor.props.file.select": "Select a file",
|
|
305
307
|
"constructor.props.file.notselected": "File is not selected",
|
|
308
|
+
"constructor.props.refreshrate": "Refresh rate",
|
|
309
|
+
"constructor.props.scale": "Scale",
|
|
306
310
|
},
|
|
307
311
|
zh: {},
|
|
308
312
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -144,9 +144,10 @@ export interface IGraphProps {
|
|
|
144
144
|
};
|
|
145
145
|
streamingData?: {
|
|
146
146
|
data: IGraphDataObject[] | null;
|
|
147
|
-
timestamp?: number;
|
|
148
147
|
};
|
|
149
148
|
isTest?: boolean;
|
|
149
|
+
refreshRate?: number;
|
|
150
|
+
scale?: number;
|
|
150
151
|
}
|
|
151
152
|
export interface IInputProps {
|
|
152
153
|
id?: string;
|
package/package.json
CHANGED