@record-evolution/widget-value 1.0.10

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.
@@ -0,0 +1,272 @@
1
+ import { html, css, LitElement, PropertyValueMap } from 'lit';
2
+ import { repeat } from 'lit/directives/repeat.js'
3
+ import { property, state } from 'lit/decorators.js';
4
+ import { InputData, Data, Dataseries } from './types.js'
5
+
6
+ export class WidgetValue extends LitElement {
7
+
8
+ @property({type: Object})
9
+ inputData?: InputData = undefined
10
+
11
+ @state()
12
+ private dataSets: Map<string, Dataseries> = new Map()
13
+
14
+ @state()
15
+ private textActive: boolean = true
16
+
17
+ @state()
18
+ private numberText?: HTMLDivElement[]
19
+
20
+ @state()
21
+ private labelText?: HTMLDivElement[]
22
+
23
+ version: string = 'versionplaceholder'
24
+
25
+ resizeObserver: ResizeObserver
26
+
27
+ valueContainer?: HTMLDivElement
28
+ boxes?: HTMLDivElement[]
29
+ origWidth: number = 0
30
+ origHeight: number = 0
31
+ constructor() {
32
+ super()
33
+
34
+ this.resizeObserver = new ResizeObserver((ev) => this.adjustSizes())
35
+ this.resizeObserver.observe(this)
36
+ }
37
+
38
+ protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>) {
39
+ this.applyInputData()
40
+ }
41
+
42
+ update(changedProperties: Map<string, any>) {
43
+ changedProperties.forEach((oldValue, propName) => {
44
+ if (propName === 'inputData') {
45
+ this.applyInputData()
46
+ }
47
+ })
48
+
49
+ this.sizingSetup()
50
+
51
+ super.update(changedProperties)
52
+ }
53
+
54
+ sizingSetup() {
55
+ if (this.origWidth !== 0 && this.origHeight !== 0) return
56
+
57
+ this.boxes = Array.from(this?.shadowRoot?.querySelectorAll('.single-value') as NodeListOf<HTMLDivElement>)
58
+ this.numberText = Array.from(this?.shadowRoot?.querySelectorAll('.current-value') as NodeListOf<HTMLDivElement>)
59
+ this.labelText = Array.from(this?.shadowRoot?.querySelectorAll('.label') as NodeListOf<HTMLDivElement>)
60
+ this.valueContainer = this?.shadowRoot?.querySelector('.value-container') as HTMLDivElement
61
+
62
+ this.origWidth = this.boxes?.map(b => b.getBoundingClientRect().width).reduce((p, c) => c > p ? c : p, 0 ) ?? 0
63
+ this.origHeight = this.boxes?.map(b => b.getBoundingClientRect().height).reduce((p, c) => c > p ? c : p, 0 ) ?? 0
64
+ if (this.origWidth > 0) this.origWidth += 16
65
+ if (this.origHeight > 0) this.origHeight += 16
66
+
67
+ this.adjustSizes()
68
+
69
+ }
70
+
71
+ adjustSizes() {
72
+ const userWidth = this.valueContainer?.getBoundingClientRect().width
73
+ const userHeight = this.valueContainer?.getBoundingClientRect().height
74
+ const count = this.dataSets.size
75
+
76
+ const width = this.origWidth
77
+ const height = this.origHeight
78
+ if (!userWidth || !userHeight || !width || !height) return
79
+
80
+ const fits = []
81
+ for (let c = 1; c <= count; c++) {
82
+ const r = Math.ceil(count/c)
83
+ const uwgap = (userWidth - 12 * (c-1))
84
+ const uhgap = (userHeight - 12 * (r-1))
85
+ const m = uwgap / width / c
86
+ const size = m * m * width * height * count
87
+ if (r * m * height < uhgap) fits.push({c, m, size, width, height, userWidth, userHeight})
88
+ }
89
+
90
+ for (let r = 1; r <= count; r++) {
91
+ const c = Math.ceil(count/r)
92
+ const uwgap = (userWidth - 12 * (c-1))
93
+ const uhgap = (userHeight - 12 * (r-1))
94
+ const m = uhgap / height / r
95
+ const size = m * m * width * height * count
96
+ if (c * m * width < uwgap) fits.push({r, m, size, width, height, userWidth, userHeight})
97
+ }
98
+
99
+ const maxSize = fits.reduce((p, c) => c.size < p ? p : c.size, 0)
100
+ const fit = fits.find(f => f.size === maxSize)
101
+ const modifier = (fit?.m ?? 1)
102
+ // console.log('FITS', fits, 'modifier', modifier, 'cols',fit?.c, 'rows', fit?.r, 'new size', fit?.size.toFixed(0), 'total space', (userWidth* userHeight).toFixed(0))
103
+
104
+ this.boxes?.forEach(box => box.setAttribute("style", `width:${modifier*width}px; height:${modifier*height}px; padding:${modifier*6}px`))
105
+ this.numberText?.forEach(n => {
106
+ const label: string | null = n.getAttribute('label')
107
+ const ds: Dataseries | undefined = this.dataSets.get(label ?? '')
108
+ n.setAttribute("style", `font-size: ${(ds?.valueFontSize ?? 32) * modifier}px; ${ds?.valueColor ? "color: " + ds?.valueColor: ''}`)
109
+ })
110
+
111
+ this.labelText?.forEach(n => {
112
+ const label: string | null = n.getAttribute('label')
113
+ const ds: Dataseries | undefined = this.dataSets.get(label ?? '')
114
+ n.setAttribute("style", `font-size: ${(ds?.labelFontSize ?? 26) * modifier}px; ${ds?.labelColor ? "color: " + ds?.labelColor: ''}`)
115
+ })
116
+
117
+ this.textActive = true
118
+
119
+ }
120
+
121
+ async applyInputData() {
122
+
123
+ if(!this.inputData) return
124
+
125
+ this.dataSets = new Map()
126
+ this.inputData.dataseries.sort((a, b) => a.order - b.order).forEach(ds => {
127
+
128
+ // pivot data
129
+ const distincts = [...new Set(ds.data.map((d: Data) => d.pivot))]
130
+ if (distincts.length > 1 || distincts[0] !== undefined) {
131
+ distincts.forEach((piv) => {
132
+ const pds: any = {
133
+ label: `${ds.label} ${piv}`,
134
+ order: ds.order,
135
+ unit: ds.unit,
136
+ averageLatest: ds.averageLatest,
137
+ labelColor: ds.labelColor,
138
+ valueColor: ds.valueColor,
139
+ data: ds.data.filter(d => d.pivot === piv)
140
+ }
141
+ this.dataSets.set(pds.label, pds)
142
+ })
143
+ } else {
144
+ this.dataSets.set(ds.label, ds)
145
+ }
146
+ })
147
+
148
+ // filter latest values and calculate average
149
+ this.dataSets.forEach((ds, label) => {
150
+ if (typeof ds.averageLatest !== 'number' || !isNaN(ds.averageLatest)) ds.averageLatest = 1
151
+ ds.data = ds.data.splice(-ds.averageLatest ?? -1)
152
+ ds.needleValue = ds.data.map(d => d.value).reduce(( p, c ) => p + c, 0) / ds.data.length
153
+ })
154
+
155
+ this.requestUpdate(); await this.updateComplete
156
+
157
+ // console.log('Value Datasets', this.dataSets)
158
+
159
+ }
160
+
161
+ static styles = css`
162
+ :host {
163
+ display: block;
164
+ font-family: sans-serif;
165
+ box-sizing: border-box;
166
+ position: relative;
167
+ margin: auto;
168
+ }
169
+
170
+ .paging:not([active]) { display: none !important; }
171
+
172
+ .wrapper {
173
+ display: flex;
174
+ flex-direction: column;
175
+ height: 100%;
176
+ width: 100%;
177
+ padding: 16px;
178
+ box-sizing: border-box;
179
+ }
180
+
181
+ h3 {
182
+ margin: 0;
183
+ max-width: 300px;
184
+ overflow: hidden;
185
+ text-overflow: ellipsis;
186
+ white-space: nowrap;
187
+ color: var(--re-text-color, #000) !important;
188
+ }
189
+ p {
190
+ margin: 10px 0 0 0;
191
+ max-width: 300px;
192
+ font-size: 14px;
193
+ line-height: 17px;
194
+ overflow: hidden;
195
+ text-overflow: ellipsis;
196
+ white-space: nowrap;
197
+ color: var(--re-text-color, #000) !important;
198
+ }
199
+
200
+ .value-container {
201
+ display: flex;
202
+ flex-wrap: wrap;
203
+ align-items: center;
204
+ justify-content: center;
205
+ flex: 1;
206
+ overflow: hidden;
207
+ position: relative;
208
+ gap: 12px;
209
+ }
210
+
211
+ .single-value {
212
+ overflow: hidden;
213
+ position: relative;
214
+ align-items: end;
215
+ font-size: 26px;
216
+ padding: 6px;
217
+ box-sizing: border-box;
218
+ /* border-left: 4px solid #ddd; */
219
+ }
220
+
221
+ .current-value {
222
+ font-size: 32px;
223
+ font-weight: 600;
224
+ white-space: nowrap;
225
+ color: var(--re-text-color, #000);
226
+ }
227
+
228
+ .label {
229
+ font-weight: 300;
230
+ font-size: 26px;
231
+ color: var(--re-text-color, #000);
232
+ white-space: nowrap;
233
+ }
234
+ `;
235
+
236
+ render() {
237
+ return html`
238
+ <div class="wrapper">
239
+ <header>
240
+ <h3 class="paging" ?active=${this.inputData?.settings?.title}>${this.inputData?.settings?.title}</h3>
241
+ <p class="paging" ?active=${this.inputData?.settings?.subTitle}>${this.inputData?.settings?.subTitle}</p>
242
+ </header>
243
+ <div class="value-container">
244
+ ${repeat(// @ts-ignore
245
+ this.dataSets, ([label]) => label, ([label, ds]) => {
246
+ return html`
247
+ <div class="single-value">
248
+ <div
249
+ class="label paging"
250
+ ?active=${this.textActive}
251
+ label="${label}"
252
+ >
253
+ ${label}
254
+ </div>
255
+ <span
256
+ class="current-value paging"
257
+ ?active=${this.textActive}
258
+ label="${label}">
259
+ ${isNaN(ds.needleValue) ? '' : ds.needleValue.toFixed(0)}
260
+ </span>
261
+ <span class="label paging" label="${label}" ?active=${this.textActive} >
262
+ ${ds.unit}
263
+ </span>
264
+ </div>
265
+ `})}
266
+ </div>
267
+ </div>
268
+ `;
269
+ }
270
+ }
271
+
272
+ window.customElements.define('widget-value-versionplaceholder', WidgetValue);