@operato/scene-legend 0.0.11
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/@types/global/index.d.ts +1 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/assets/legend.png +0 -0
- package/dist/editors/editor-legend-status.d.ts +4 -0
- package/dist/editors/editor-legend-status.js +313 -0
- package/dist/editors/editor-legend-status.js.map +1 -0
- package/dist/editors/index.d.ts +5 -0
- package/dist/editors/index.js +11 -0
- package/dist/editors/index.js.map +1 -0
- package/dist/editors/property-editor-legend-status.d.ts +7 -0
- package/dist/editors/property-editor-legend-status.js +13 -0
- package/dist/editors/property-editor-legend-status.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/legend-item.d.ts +15 -0
- package/dist/legend-item.js +42 -0
- package/dist/legend-item.js.map +1 -0
- package/dist/legend.d.ts +40 -0
- package/dist/legend.js +177 -0
- package/dist/legend.js.map +1 -0
- package/dist/templates/index.d.ts +18 -0
- package/dist/templates/index.js +3 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/legend.d.ts +18 -0
- package/dist/templates/legend.js +19 -0
- package/dist/templates/legend.js.map +1 -0
- package/helps/scene/component/legend.ko.md +18 -0
- package/helps/scene/component/legend.md +18 -0
- package/helps/scene/component/legend.zh.md +20 -0
- package/helps/scene/images/legend-01.png +0 -0
- package/helps/scene/images/legend-02.png +0 -0
- package/package.json +63 -0
- package/src/editors/editor-legend-status.ts +339 -0
- package/src/editors/index.ts +11 -0
- package/src/editors/property-editor-legend-status.ts +17 -0
- package/src/index.ts +6 -0
- package/src/legend-item.ts +52 -0
- package/src/legend.ts +232 -0
- package/src/templates/index.ts +3 -0
- package/src/templates/legend.ts +19 -0
- package/test/basic-test.html +67 -0
- package/test/index.html +24 -0
- package/test/unit/test-legend.js +33 -0
- package/test/unit/util.js +22 -0
- package/things-scene.config.js +7 -0
- package/tsconfig.json +23 -0
- package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,339 @@
|
|
1
|
+
/**
|
2
|
+
* @license Copyright © HatioLab Inc. All rights reserved.
|
3
|
+
*/
|
4
|
+
|
5
|
+
import { LitElement, PropertyValues, css, html } from 'lit'
|
6
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
7
|
+
|
8
|
+
@customElement('editor-legend-status')
|
9
|
+
class EditorLegendStatus extends LitElement {
|
10
|
+
static styles = [
|
11
|
+
css`
|
12
|
+
:host {
|
13
|
+
font-size: 0.8em;
|
14
|
+
display: grid;
|
15
|
+
grid-template-columns: repeat(10, 1fr);
|
16
|
+
grid-gap: 5px;
|
17
|
+
}
|
18
|
+
|
19
|
+
:host > * {
|
20
|
+
order: 2;
|
21
|
+
grid-column: 4 / -1;
|
22
|
+
}
|
23
|
+
|
24
|
+
:host > legend {
|
25
|
+
order: 1;
|
26
|
+
grid-column: 1 / -1;
|
27
|
+
font-size: 11px;
|
28
|
+
color: rgb(228, 108, 46);
|
29
|
+
font-weight: bold;
|
30
|
+
text-transform: capitalize;
|
31
|
+
padding: 5px 0px 0px 5px;
|
32
|
+
}
|
33
|
+
|
34
|
+
:host > label {
|
35
|
+
grid-column: 1 / 4;
|
36
|
+
text-align: right;
|
37
|
+
color: var(--primary-text-color);
|
38
|
+
}
|
39
|
+
|
40
|
+
div[data-record] input {
|
41
|
+
width: 20%;
|
42
|
+
}
|
43
|
+
:host > table {
|
44
|
+
grid-column: 1 / -1;
|
45
|
+
}
|
46
|
+
table input {
|
47
|
+
width: 25px;
|
48
|
+
margin: 3px 0 2px 0;
|
49
|
+
padding: 3px;
|
50
|
+
font-size: 12px;
|
51
|
+
}
|
52
|
+
table td span {
|
53
|
+
padding: 5px 0 0 0;
|
54
|
+
}
|
55
|
+
table td things-editor-color {
|
56
|
+
width: 81px;
|
57
|
+
height: 25px;
|
58
|
+
}
|
59
|
+
table td button {
|
60
|
+
margin-left: 0;
|
61
|
+
}
|
62
|
+
table th {
|
63
|
+
background-color: rgba(0, 0, 0, 0.1);
|
64
|
+
padding: 2px 0;
|
65
|
+
text-align: center;
|
66
|
+
}
|
67
|
+
|
68
|
+
table tr > th:first-child {
|
69
|
+
width: 40px;
|
70
|
+
}
|
71
|
+
|
72
|
+
table tr > th:nth-child(2) {
|
73
|
+
width: 85px;
|
74
|
+
}
|
75
|
+
|
76
|
+
table tr > th:nth-child(4) {
|
77
|
+
width: 30px;
|
78
|
+
}
|
79
|
+
|
80
|
+
table *.things-editor-legend-status {
|
81
|
+
float: none !important;
|
82
|
+
}
|
83
|
+
table td {
|
84
|
+
text-align: center;
|
85
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
86
|
+
}
|
87
|
+
table tr.stock-new {
|
88
|
+
background-color: rgba(179, 145, 117, 0.3);
|
89
|
+
}
|
90
|
+
table td input[data-description] {
|
91
|
+
width: 100%;
|
92
|
+
box-sizing: border-box;
|
93
|
+
}
|
94
|
+
`
|
95
|
+
]
|
96
|
+
|
97
|
+
@property({ type: Object }) value: any
|
98
|
+
|
99
|
+
@state() private _statusField?: string
|
100
|
+
@state() private _defaultColor?: string
|
101
|
+
@state() private _ranges: any[] = []
|
102
|
+
|
103
|
+
private boundOnChange?: any
|
104
|
+
private _changingNow: boolean = false
|
105
|
+
|
106
|
+
render() {
|
107
|
+
return html`
|
108
|
+
<legend>
|
109
|
+
<i18n-msg msgid="label.status">Status</i18n-msg>
|
110
|
+
</legend>
|
111
|
+
|
112
|
+
<label class="stock-field">
|
113
|
+
<i18n-msg msgid="label.field">Field</i18n-msg>
|
114
|
+
</label>
|
115
|
+
<input
|
116
|
+
type="text"
|
117
|
+
.value=${this._statusField || ''}
|
118
|
+
@change=${(e: Event) => {
|
119
|
+
this._statusField = (e.target as HTMLInputElement).value
|
120
|
+
}}
|
121
|
+
/>
|
122
|
+
<label class="default-color">
|
123
|
+
<i18n-msg msgid="label.default-color">Default Color</i18n-msg>
|
124
|
+
</label>
|
125
|
+
<things-editor-color
|
126
|
+
name="default-color"
|
127
|
+
.value=${this._defaultColor || ''}
|
128
|
+
placeholder="default color"
|
129
|
+
@change=${(e: Event) => {
|
130
|
+
this._defaultColor = (e.target as HTMLInputElement).value
|
131
|
+
}}
|
132
|
+
></things-editor-color>
|
133
|
+
|
134
|
+
<table>
|
135
|
+
<tr>
|
136
|
+
<th>
|
137
|
+
Min ≤ <br />Field<br />
|
138
|
+
< Max
|
139
|
+
</th>
|
140
|
+
<th>color</th>
|
141
|
+
<th>disp. text</th>
|
142
|
+
<th></th>
|
143
|
+
</tr>
|
144
|
+
${this._ranges.map(
|
145
|
+
item => html`
|
146
|
+
<tr data-record>
|
147
|
+
<td>
|
148
|
+
<input type="text" data-min placeholder="min" .value="${item.min}" />
|
149
|
+
<span>~</span>
|
150
|
+
<input type="text" data-max placeholder="max" .value="${item.max}" />
|
151
|
+
</td>
|
152
|
+
<td>
|
153
|
+
<things-editor-color data-color .value="${item.color}" placeholder="color"></things-editor-color>
|
154
|
+
</td>
|
155
|
+
<td>
|
156
|
+
<input type="text" data-description .value="${item.description || ''}" placeholder="display text" />
|
157
|
+
</td>
|
158
|
+
<td>
|
159
|
+
<button class="record-action" @tap=${(e: TouchEvent) => this._delete(e)} tabindex="-1">-</button>
|
160
|
+
</td>
|
161
|
+
</tr>
|
162
|
+
`
|
163
|
+
)}
|
164
|
+
|
165
|
+
<tr data-record-new class="stock-new">
|
166
|
+
<td>
|
167
|
+
<input type="text" data-min placeholder="min" value="" />
|
168
|
+
<span>~</span>
|
169
|
+
<input type="text" data-max placeholder="max" value="" />
|
170
|
+
</td>
|
171
|
+
<td>
|
172
|
+
<things-editor-color data-color value="" placeholder="color"></things-editor-color>
|
173
|
+
</td>
|
174
|
+
<td>
|
175
|
+
<input type="text" data-description value="" placeholder="display text" />
|
176
|
+
</td>
|
177
|
+
<td>
|
178
|
+
<button class="record-action" @tap=${() => this._add()} tabindex="-1">+</button>
|
179
|
+
</td>
|
180
|
+
</tr>
|
181
|
+
</table>
|
182
|
+
`
|
183
|
+
}
|
184
|
+
|
185
|
+
connectedCallback() {
|
186
|
+
super.connectedCallback()
|
187
|
+
if (!this.boundOnChange) this.boundOnChange = this._onChange.bind(this)
|
188
|
+
|
189
|
+
this.renderRoot.addEventListener('change', this.boundOnChange)
|
190
|
+
}
|
191
|
+
|
192
|
+
disconnectedCallback() {
|
193
|
+
super.disconnectedCallback()
|
194
|
+
this.renderRoot.removeEventListener('change', this.boundOnChange)
|
195
|
+
}
|
196
|
+
|
197
|
+
_valueChanged(value: any) {
|
198
|
+
var val = value || this._getDefaultValue()
|
199
|
+
this._statusField = val.field
|
200
|
+
this._defaultColor = val.defaultColor
|
201
|
+
this._ranges = [...val.ranges]
|
202
|
+
|
203
|
+
this.requestUpdate()
|
204
|
+
}
|
205
|
+
|
206
|
+
_onChange(e: Event) {
|
207
|
+
e.stopPropagation()
|
208
|
+
this._changingNow = true
|
209
|
+
|
210
|
+
var input = e.target as HTMLInputElement
|
211
|
+
var value = input.value
|
212
|
+
|
213
|
+
var tr = input.closest('tr')
|
214
|
+
|
215
|
+
if (tr) {
|
216
|
+
if (tr.hasAttribute('data-record')) this._build(true)
|
217
|
+
else if (tr.hasAttribute('data-record-new') && input.hasAttribute('data-color')) this._add()
|
218
|
+
}
|
219
|
+
|
220
|
+
this.value = {
|
221
|
+
field: this._statusField,
|
222
|
+
defaultColor: this._defaultColor,
|
223
|
+
ranges: this._ranges
|
224
|
+
}
|
225
|
+
|
226
|
+
this.dispatchEvent(
|
227
|
+
new CustomEvent('change', {
|
228
|
+
bubbles: true,
|
229
|
+
composed: true
|
230
|
+
})
|
231
|
+
)
|
232
|
+
this.requestUpdate()
|
233
|
+
}
|
234
|
+
|
235
|
+
_build(includeNewRecord: boolean) {
|
236
|
+
if (includeNewRecord) var records = this.renderRoot.querySelectorAll('[data-record],[data-record-new]')
|
237
|
+
else var records = this.renderRoot.querySelectorAll('[data-record]')
|
238
|
+
|
239
|
+
var newRanges = []
|
240
|
+
|
241
|
+
for (var i = 0; i < records.length; i++) {
|
242
|
+
var record = records[i]
|
243
|
+
|
244
|
+
var min = (record.querySelector('[data-min]') as HTMLInputElement).value
|
245
|
+
var max = (record.querySelector('[data-max]') as HTMLInputElement).value
|
246
|
+
var description = (record.querySelector('[data-description]') as HTMLInputElement).value
|
247
|
+
var inputs = record.querySelectorAll('[data-color]:not([style*="display: none"])') as NodeListOf<HTMLInputElement>
|
248
|
+
if (!inputs || inputs.length == 0) continue
|
249
|
+
|
250
|
+
var input = inputs[inputs.length - 1]
|
251
|
+
var color = input.value
|
252
|
+
|
253
|
+
if (min != undefined && max != undefined && color)
|
254
|
+
newRanges.push({
|
255
|
+
min: min.trim(),
|
256
|
+
max: max.trim(),
|
257
|
+
color: color.trim(),
|
258
|
+
description: description.trim()
|
259
|
+
})
|
260
|
+
}
|
261
|
+
|
262
|
+
newRanges.sort(function (range1, range2) {
|
263
|
+
var min1 = Number(range1.min)
|
264
|
+
var min2 = Number(range2.min)
|
265
|
+
|
266
|
+
var result = min1 - min2
|
267
|
+
|
268
|
+
if (Number.isNaN(result)) {
|
269
|
+
var strMin1 = String(min1)
|
270
|
+
var strMin2 = String(min2)
|
271
|
+
|
272
|
+
if (strMin1 > strMin2) result = 1
|
273
|
+
else if (strMin1 == strMin2) result = 0
|
274
|
+
else result = -1
|
275
|
+
}
|
276
|
+
|
277
|
+
return result
|
278
|
+
})
|
279
|
+
|
280
|
+
this._ranges = newRanges
|
281
|
+
this.requestUpdate()
|
282
|
+
}
|
283
|
+
|
284
|
+
_add() {
|
285
|
+
this._build(true)
|
286
|
+
|
287
|
+
var inputs = this.renderRoot.querySelectorAll(
|
288
|
+
'[data-record-new] input:not([style*="display: none"]), [data-record-new] [data-color]:not([style*="display: none"])'
|
289
|
+
) as NodeListOf<HTMLInputElement>
|
290
|
+
|
291
|
+
for (var i = 0; i < inputs.length; i++) {
|
292
|
+
let input = inputs[i]
|
293
|
+
input.value = ''
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
_delete(e: Event) {
|
298
|
+
var record = (e.target as Element).closest('tr[data-record]')
|
299
|
+
|
300
|
+
;(record!.querySelector('[data-min]') as HTMLInputElement).value = ''
|
301
|
+
;(record!.querySelector('[data-max]') as HTMLInputElement).value = ''
|
302
|
+
;(record!.querySelector('[data-color]') as HTMLInputElement).value = ''
|
303
|
+
|
304
|
+
this._build(false)
|
305
|
+
|
306
|
+
this.value = {
|
307
|
+
field: this._statusField,
|
308
|
+
defaultColor: this._defaultColor,
|
309
|
+
ranges: this._ranges
|
310
|
+
}
|
311
|
+
|
312
|
+
this.dispatchEvent(
|
313
|
+
new CustomEvent('change', {
|
314
|
+
bubbles: true,
|
315
|
+
composed: true
|
316
|
+
})
|
317
|
+
)
|
318
|
+
}
|
319
|
+
|
320
|
+
_getDefaultValue() {
|
321
|
+
return {
|
322
|
+
field: '',
|
323
|
+
defaultColor: '',
|
324
|
+
ranges: []
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
_onRepeaterChanged() {
|
329
|
+
var inputs = this.renderRoot.querySelectorAll(
|
330
|
+
'[data-record] input:not([style*="display: none"])[value=""], [data-record-new] input:not([style*="display: none"])[value=""]'
|
331
|
+
) as NodeListOf<HTMLInputElement>
|
332
|
+
|
333
|
+
inputs[0].focus()
|
334
|
+
}
|
335
|
+
|
336
|
+
updated(changes: PropertyValues<this>) {
|
337
|
+
if (changes.has('value')) this._valueChanged(this.value)
|
338
|
+
}
|
339
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import './editor-legend-status'
|
2
|
+
|
3
|
+
import { OxPropertyEditor } from '@operato/property-editor'
|
4
|
+
import { Properties } from '@hatiolab/things-scene'
|
5
|
+
import { html } from 'lit-element'
|
6
|
+
|
7
|
+
export class PropertyEditorLegendStatus extends OxPropertyEditor {
|
8
|
+
static get is() {
|
9
|
+
return 'property-editor-legend-status'
|
10
|
+
}
|
11
|
+
|
12
|
+
editorTemplate(props: Properties) {
|
13
|
+
return html` <editor-legend-status .value=${props.value} fullwidth></editor-legend-status> `
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
customElements.define(PropertyEditorLegendStatus.is, PropertyEditorLegendStatus)
|
package/src/index.ts
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright © HatioLab Inc. All rights reserved.
|
3
|
+
*/
|
4
|
+
import { Component, Model, Properties, RectPath, Shape } from '@hatiolab/things-scene'
|
5
|
+
|
6
|
+
const NATURE = {
|
7
|
+
mutable: false,
|
8
|
+
resizable: false,
|
9
|
+
rotatable: false,
|
10
|
+
properties: []
|
11
|
+
}
|
12
|
+
|
13
|
+
export default class LegendItem extends RectPath(Shape) {
|
14
|
+
render(context: CanvasRenderingContext2D) {
|
15
|
+
var { left, top, height, color } = this.model
|
16
|
+
|
17
|
+
context.beginPath()
|
18
|
+
|
19
|
+
var c = height / 2
|
20
|
+
var r = c / 2
|
21
|
+
|
22
|
+
context.save()
|
23
|
+
|
24
|
+
context.fillStyle = color
|
25
|
+
context.ellipse(left + c, top + c, r, r, 0, 0, Math.PI * 2, true)
|
26
|
+
context.shadowColor = 'rgba(0,0,0,0.15)'
|
27
|
+
context.shadowBlur = 2
|
28
|
+
context.shadowOffsetX = 1
|
29
|
+
context.shadowOffsetY = 2
|
30
|
+
context.fill()
|
31
|
+
|
32
|
+
context.restore()
|
33
|
+
}
|
34
|
+
|
35
|
+
onchange(after: Properties) {
|
36
|
+
if (after.hasOwnProperty('height')) this.set('paddingLeft', after.height)
|
37
|
+
}
|
38
|
+
|
39
|
+
get stuck() {
|
40
|
+
return true
|
41
|
+
}
|
42
|
+
|
43
|
+
get capturable() {
|
44
|
+
return false
|
45
|
+
}
|
46
|
+
|
47
|
+
get nature() {
|
48
|
+
return NATURE
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
Component.register('legend-item', LegendItem)
|
package/src/legend.ts
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright © HatioLab Inc. All rights reserved.
|
3
|
+
*/
|
4
|
+
import { Component, Container, Model, POSITION, Properties, TableLayout } from '@hatiolab/things-scene'
|
5
|
+
|
6
|
+
const NATURE = {
|
7
|
+
mutable: false,
|
8
|
+
resizable: true,
|
9
|
+
rotatable: true,
|
10
|
+
properties: [
|
11
|
+
{
|
12
|
+
type: 'number',
|
13
|
+
label: 'rows',
|
14
|
+
name: 'rows'
|
15
|
+
},
|
16
|
+
{
|
17
|
+
type: 'number',
|
18
|
+
label: 'columns',
|
19
|
+
name: 'columns'
|
20
|
+
},
|
21
|
+
{
|
22
|
+
type: 'select',
|
23
|
+
label: 'direction',
|
24
|
+
name: 'direction',
|
25
|
+
property: {
|
26
|
+
options: [
|
27
|
+
{
|
28
|
+
display: 'Horizontal',
|
29
|
+
value: 'horizontal'
|
30
|
+
},
|
31
|
+
{
|
32
|
+
display: 'Vertical',
|
33
|
+
value: 'vertical'
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
},
|
38
|
+
{
|
39
|
+
type: 'number',
|
40
|
+
label: 'round',
|
41
|
+
name: 'round'
|
42
|
+
},
|
43
|
+
{
|
44
|
+
type: 'legend-status',
|
45
|
+
label: '',
|
46
|
+
name: 'status'
|
47
|
+
}
|
48
|
+
],
|
49
|
+
help: 'scene/component/legend'
|
50
|
+
}
|
51
|
+
|
52
|
+
var controlHandler = {
|
53
|
+
ondragmove: function (point: POSITION, index: number, component: Component) {
|
54
|
+
var { left, top, width, height } = component.model
|
55
|
+
/*
|
56
|
+
* point의 좌표는 부모 레이어 기준의 x, y 값이다.
|
57
|
+
* 따라서, 도형의 회전을 감안한 좌표로의 변환이 필요하다.
|
58
|
+
* Transcoord시에는 point좌표가 부모까지 transcoord되어있는 상태이므로,
|
59
|
+
* 컴포넌트자신에 대한 transcoord만 필요하다.(마지막 파라미터를 false로).
|
60
|
+
*/
|
61
|
+
var transcoorded = component.transcoordP2S(point.x, point.y)
|
62
|
+
var round = ((transcoorded.x - left) / (width / 2)) * 100
|
63
|
+
|
64
|
+
round = roundSet(round, width, height)
|
65
|
+
|
66
|
+
component.set({ round })
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
function roundSet(round: number, width: number, height: number) {
|
71
|
+
var max = width > height ? (height / width) * 100 : 100
|
72
|
+
|
73
|
+
if (round >= max) round = max
|
74
|
+
else if (round <= 0) round = 0
|
75
|
+
|
76
|
+
return round
|
77
|
+
}
|
78
|
+
|
79
|
+
export default class Legend extends Container {
|
80
|
+
ready() {
|
81
|
+
this.rebuildLegendItems()
|
82
|
+
}
|
83
|
+
|
84
|
+
get showMoveHandle() {
|
85
|
+
return false
|
86
|
+
}
|
87
|
+
|
88
|
+
render(context: CanvasRenderingContext2D) {
|
89
|
+
var { round = 0 } = this.model
|
90
|
+
|
91
|
+
var { left, top, width, height } = this.bounds
|
92
|
+
|
93
|
+
// 박스 그리기
|
94
|
+
context.beginPath()
|
95
|
+
|
96
|
+
round = roundSet(round, width, height)
|
97
|
+
|
98
|
+
if (round > 0) {
|
99
|
+
var radius = (round / 100) * (width / 2)
|
100
|
+
|
101
|
+
context.moveTo(left + radius, top)
|
102
|
+
context.lineTo(left + width - radius, top)
|
103
|
+
context.quadraticCurveTo(left + width, top, left + width, top + radius)
|
104
|
+
context.lineTo(left + width, top + height - radius)
|
105
|
+
context.quadraticCurveTo(left + width, top + height, left + width - radius, top + height)
|
106
|
+
context.lineTo(left + radius, top + height)
|
107
|
+
context.quadraticCurveTo(left, top + height, left, top + height - radius)
|
108
|
+
context.lineTo(left, top + radius)
|
109
|
+
context.quadraticCurveTo(left, top, left + radius, top)
|
110
|
+
|
111
|
+
this.model.padding = {
|
112
|
+
top: round / 2,
|
113
|
+
left: round / 2,
|
114
|
+
right: round / 2,
|
115
|
+
bottom: round / 2
|
116
|
+
}
|
117
|
+
} else {
|
118
|
+
context.rect(left, top, width, height)
|
119
|
+
}
|
120
|
+
|
121
|
+
this.drawFill(context)
|
122
|
+
this.drawStroke(context)
|
123
|
+
}
|
124
|
+
|
125
|
+
get controls() {
|
126
|
+
var { left, top, width, round, height } = this.model
|
127
|
+
round = round == undefined ? 0 : roundSet(round, width, height)
|
128
|
+
|
129
|
+
return [
|
130
|
+
{
|
131
|
+
x: left + (width / 2) * (round / 100),
|
132
|
+
y: top,
|
133
|
+
handler: controlHandler
|
134
|
+
}
|
135
|
+
]
|
136
|
+
}
|
137
|
+
|
138
|
+
get layout() {
|
139
|
+
return TableLayout
|
140
|
+
}
|
141
|
+
|
142
|
+
get nature() {
|
143
|
+
return NATURE
|
144
|
+
}
|
145
|
+
|
146
|
+
rebuildLegendItems() {
|
147
|
+
if (this.components.length) {
|
148
|
+
this.components.slice().forEach(m => m.dispose())
|
149
|
+
}
|
150
|
+
|
151
|
+
var {
|
152
|
+
left,
|
153
|
+
top,
|
154
|
+
width,
|
155
|
+
height,
|
156
|
+
fillStyle,
|
157
|
+
strokeStyle,
|
158
|
+
fontColor,
|
159
|
+
fontFamily,
|
160
|
+
fontSize,
|
161
|
+
lineHeight,
|
162
|
+
textAlign = 'left',
|
163
|
+
round = 0,
|
164
|
+
italic,
|
165
|
+
bold,
|
166
|
+
lineWidth = 0,
|
167
|
+
rows,
|
168
|
+
columns,
|
169
|
+
status = {}
|
170
|
+
} = this.model
|
171
|
+
|
172
|
+
let statusRanges: {
|
173
|
+
min: string
|
174
|
+
max: string
|
175
|
+
description: string
|
176
|
+
color: string
|
177
|
+
}[] = status.ranges || []
|
178
|
+
|
179
|
+
var count = statusRanges.length
|
180
|
+
|
181
|
+
this.add(
|
182
|
+
statusRanges.map(range =>
|
183
|
+
Model.compile({
|
184
|
+
type: 'legend-item',
|
185
|
+
text: range.description || `${range.min || ''} ~ ${range.max || ''}`,
|
186
|
+
width: 1,
|
187
|
+
height: 1,
|
188
|
+
color: range.color,
|
189
|
+
fontColor,
|
190
|
+
fontFamily,
|
191
|
+
fontSize,
|
192
|
+
lineHeight,
|
193
|
+
italic,
|
194
|
+
bold,
|
195
|
+
textAlign
|
196
|
+
})
|
197
|
+
)
|
198
|
+
)
|
199
|
+
|
200
|
+
var rows, columns
|
201
|
+
|
202
|
+
if (!columns && !rows) {
|
203
|
+
rows = count
|
204
|
+
columns = 1
|
205
|
+
} else if (columns && !rows) {
|
206
|
+
rows = Math.ceil(count / Number(columns))
|
207
|
+
} else if (rows && !columns) {
|
208
|
+
columns = Math.ceil(count / Number(rows))
|
209
|
+
}
|
210
|
+
|
211
|
+
this.set({
|
212
|
+
layoutConfig: {
|
213
|
+
rows,
|
214
|
+
columns
|
215
|
+
}
|
216
|
+
})
|
217
|
+
}
|
218
|
+
|
219
|
+
get hasTextProperty() {
|
220
|
+
return true
|
221
|
+
}
|
222
|
+
|
223
|
+
get textHidden() {
|
224
|
+
return true
|
225
|
+
}
|
226
|
+
|
227
|
+
onchange(after: Properties, before: Properties) {
|
228
|
+
this.rebuildLegendItems()
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
Component.register('legend', Legend)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import icon from '../../assets/legend.png'
|
2
|
+
|
3
|
+
export default {
|
4
|
+
type: 'legend',
|
5
|
+
description: 'legend for visualizer',
|
6
|
+
group: 'warehouse' /* line|shape|textAndMedia|chartAndGauge|table|container|dataSource|IoT|3D|warehouse|form|etc */,
|
7
|
+
icon,
|
8
|
+
model: {
|
9
|
+
type: 'legend',
|
10
|
+
left: 100,
|
11
|
+
top: 100,
|
12
|
+
width: 200,
|
13
|
+
height: 150,
|
14
|
+
fillStyle: '#efefef',
|
15
|
+
direction: 'vertical',
|
16
|
+
strokeStyle: 'rgba(0, 0, 0, 0.3)',
|
17
|
+
lineWidth: 1
|
18
|
+
}
|
19
|
+
}
|