182 changed files with 22157 additions and 7083 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,151 @@ |
|||
/** |
|||
* Copyright © 2016-2023 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.server.store.util; |
|||
|
|||
import org.eclipse.leshan.core.link.Link; |
|||
import org.eclipse.leshan.core.node.LwM2mMultipleResource; |
|||
import org.eclipse.leshan.core.node.LwM2mResource; |
|||
import org.eclipse.leshan.core.node.LwM2mSingleResource; |
|||
import org.eclipse.leshan.core.request.Identity; |
|||
import org.eclipse.leshan.core.request.WriteRequest; |
|||
import org.eclipse.leshan.server.registration.Registration; |
|||
import org.junit.Assert; |
|||
import org.junit.Test; |
|||
import org.mockito.Mockito; |
|||
import org.thingsboard.server.common.data.TbResource; |
|||
import org.thingsboard.server.common.data.device.data.PowerMode; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.transport.TransportResourceCache; |
|||
import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; |
|||
import org.thingsboard.server.transport.lwm2m.server.LwM2mVersionedModelProvider; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; |
|||
|
|||
import java.net.InetSocketAddress; |
|||
import java.nio.file.Files; |
|||
import java.nio.file.Path; |
|||
import java.util.Map; |
|||
import java.util.Optional; |
|||
import java.util.UUID; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.mockito.ArgumentMatchers.any; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.mock; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
public class LwM2MClientSerDesTest { |
|||
|
|||
@Test |
|||
public void serializeDeserialize() throws Exception { |
|||
LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); |
|||
|
|||
TransportDeviceInfo tdi = new TransportDeviceInfo(); |
|||
tdi.setPowerMode(PowerMode.PSM); |
|||
tdi.setPsmActivityTimer(10000L); |
|||
tdi.setPagingTransmissionWindow(2000L); |
|||
tdi.setEdrxCycle(3000L); |
|||
tdi.setTenantId(TenantId.fromUUID(UUID.randomUUID())); |
|||
tdi.setCustomerId(new CustomerId(UUID.randomUUID())); |
|||
tdi.setDeviceId(new DeviceId(UUID.randomUUID())); |
|||
tdi.setDeviceProfileId(new DeviceProfileId(UUID.randomUUID())); |
|||
tdi.setDeviceName("testDevice"); |
|||
tdi.setDeviceType("testType"); |
|||
ValidateDeviceCredentialsResponse credentialsResponse = ValidateDeviceCredentialsResponse.builder() |
|||
.deviceInfo(tdi) |
|||
.build(); |
|||
|
|||
client.init(credentialsResponse, UUID.randomUUID()); |
|||
|
|||
Registration registration = |
|||
new Registration.Builder("test", "testEndpoint", Identity |
|||
.unsecure(new InetSocketAddress(1000))) |
|||
.supportedContentFormats() |
|||
.supportedObjects(Map.of(15, "1.0", 17, "1.0")) |
|||
.objectLinks(new Link[]{new Link("/")}) |
|||
.build(); |
|||
|
|||
client.setRegistration(registration); |
|||
client.setState(LwM2MClientState.REGISTERED); |
|||
client.getSharedAttributes().put("key1", TransportProtos.TsKvProto.newBuilder().setTs(0).setKv(TransportProtos.KeyValueProto.newBuilder().setStringV("test").build()).build()); |
|||
client.getSharedAttributes().put("key2", TransportProtos.TsKvProto.newBuilder().setTs(1).setKv(TransportProtos.KeyValueProto.newBuilder().setDoubleV(1.02).build()).build()); |
|||
|
|||
TransportResourceCache resourceCache = mock(TransportResourceCache.class); |
|||
LwM2mTransportContext context = mock(LwM2mTransportContext.class); |
|||
LwM2mClientContext clientContext = mock(LwM2mClientContext.class); |
|||
|
|||
var provider = new LwM2mVersionedModelProvider(clientContext, new LwM2mTransportServerHelper(context), context); |
|||
|
|||
TbResource resource15 = new TbResource(); |
|||
resource15.setData(Files.readAllBytes(Path.of(this.getClass().getClassLoader().getResource("15.xml").toURI()))); |
|||
TbResource resource17 = new TbResource(); |
|||
resource17.setData(Files.readAllBytes(Path.of(this.getClass().getClassLoader().getResource("17.xml").toURI()))); |
|||
|
|||
when(resourceCache.get(any(), any(), eq("15_1.0"))).thenReturn(Optional.of(resource15)); |
|||
when(resourceCache.get(any(), any(), eq("17_1.0"))).thenReturn(Optional.of(resource17)); |
|||
when(context.getTransportResourceCache()).thenReturn(resourceCache); |
|||
when(clientContext.getClientByEndpoint(any())).thenReturn(client); |
|||
|
|||
LwM2mResource singleResource = LwM2mSingleResource.newStringResource(15, "testValue"); |
|||
LwM2mResource multipleResource = LwM2mMultipleResource.newStringResource(17, Map.of(0, "testValue", 1, "testValue")); |
|||
client.saveResourceValue("/15_1.0/0/0", singleResource, provider, WriteRequest.Mode.UPDATE); |
|||
client.saveResourceValue("/17_1.0/0/0", multipleResource, provider, WriteRequest.Mode.UPDATE); |
|||
|
|||
byte[] bytes = LwM2MClientSerDes.serialize(client); |
|||
Assert.assertNotNull(bytes); |
|||
|
|||
LwM2mClient desClient = LwM2MClientSerDes.deserialize(bytes); |
|||
|
|||
assertEquals(client.getNodeId(), desClient.getNodeId()); |
|||
assertEquals(client.getEndpoint(), desClient.getEndpoint()); |
|||
assertEquals(client.getSharedAttributes(), desClient.getSharedAttributes()); |
|||
assertEquals(client.getKeyTsLatestMap(), desClient.getKeyTsLatestMap()); |
|||
assertEquals(client.getTenantId(), desClient.getTenantId()); |
|||
assertEquals(client.getProfileId(), desClient.getProfileId()); |
|||
assertEquals(client.getDeviceId(), desClient.getDeviceId()); |
|||
assertEquals(client.getState(), desClient.getState()); |
|||
assertEquals(client.getSession(), desClient.getSession()); |
|||
assertEquals(client.getPowerMode(), desClient.getPowerMode()); |
|||
assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); |
|||
assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); |
|||
assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); |
|||
assertEquals(client.getRegistration(), desClient.getRegistration()); |
|||
assertEquals(client.isAsleep(), desClient.isAsleep()); |
|||
assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); |
|||
assertEquals(client.getSleepTask(), desClient.getSleepTask()); |
|||
assertEquals(client.getClientSupportContentFormats(), desClient.getClientSupportContentFormats()); |
|||
assertEquals(client.getDefaultContentFormat(), desClient.getDefaultContentFormat()); |
|||
assertEquals(client.getRetryAttempts().get(), desClient.getRetryAttempts().get()); |
|||
assertEquals(client.getLastSentRpcId(), desClient.getLastSentRpcId()); |
|||
|
|||
Map<String, ResourceValue> expectedResources = client.getResources(); |
|||
Map<String, ResourceValue> actualResources = desClient.getResources(); |
|||
assertNotNull(actualResources); |
|||
assertEquals(expectedResources.size(), actualResources.size()); |
|||
expectedResources.forEach((key, value) -> assertEquals(value.toString(), actualResources.get(key).toString())); |
|||
} |
|||
|
|||
} |
|||
@ -1,101 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2024 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.lwm2m.server.store.util; |
|||
|
|||
import org.eclipse.leshan.core.link.Link; |
|||
import org.eclipse.leshan.core.request.Identity; |
|||
import org.eclipse.leshan.server.registration.Registration; |
|||
import org.junit.Assert; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.device.data.PowerMode; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
|||
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; |
|||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
|||
|
|||
import java.net.InetSocketAddress; |
|||
import java.util.UUID; |
|||
|
|||
public class LwM2MClientSerDesTest { |
|||
|
|||
@Test |
|||
public void serializeDeserialize() { |
|||
LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); |
|||
|
|||
TransportDeviceInfo tdi = new TransportDeviceInfo(); |
|||
tdi.setPowerMode(PowerMode.PSM); |
|||
tdi.setPsmActivityTimer(10000L); |
|||
tdi.setPagingTransmissionWindow(2000L); |
|||
tdi.setEdrxCycle(3000L); |
|||
tdi.setTenantId(TenantId.fromUUID(UUID.randomUUID())); |
|||
tdi.setCustomerId(new CustomerId(UUID.randomUUID())); |
|||
tdi.setDeviceId(new DeviceId(UUID.randomUUID())); |
|||
tdi.setDeviceProfileId(new DeviceProfileId(UUID.randomUUID())); |
|||
tdi.setDeviceName("testDevice"); |
|||
tdi.setDeviceType("testType"); |
|||
ValidateDeviceCredentialsResponse credentialsResponse = ValidateDeviceCredentialsResponse.builder() |
|||
.deviceInfo(tdi) |
|||
.build(); |
|||
|
|||
client.init(credentialsResponse, UUID.randomUUID()); |
|||
|
|||
Registration registration = |
|||
new Registration.Builder("test", "testEndpoint", Identity |
|||
.unsecure(new InetSocketAddress(1000))) |
|||
.supportedContentFormats() |
|||
.objectLinks(new Link[]{new Link("/")}) |
|||
.build(); |
|||
|
|||
client.setRegistration(registration); |
|||
client.setState(LwM2MClientState.REGISTERED); |
|||
|
|||
client.getSharedAttributes().put("key1", TransportProtos.TsKvProto.newBuilder().setTs(0).setKv(TransportProtos.KeyValueProto.newBuilder().setStringV("test").build()).build()); |
|||
client.getSharedAttributes().put("key2", TransportProtos.TsKvProto.newBuilder().setTs(1).setKv(TransportProtos.KeyValueProto.newBuilder().setDoubleV(1.02).build()).build()); |
|||
|
|||
byte[] bytes = LwM2MClientSerDes.serialize(client); |
|||
Assert.assertNotNull(bytes); |
|||
|
|||
LwM2mClient desClient = LwM2MClientSerDes.deserialize(bytes); |
|||
|
|||
Assert.assertEquals(client.getNodeId(), desClient.getNodeId()); |
|||
Assert.assertEquals(client.getEndpoint(), desClient.getEndpoint()); |
|||
Assert.assertEquals(client.getResources(), desClient.getResources()); |
|||
Assert.assertEquals(client.getSharedAttributes(), desClient.getSharedAttributes()); |
|||
Assert.assertEquals(client.getKeyTsLatestMap(), desClient.getKeyTsLatestMap()); |
|||
Assert.assertEquals(client.getTenantId(), desClient.getTenantId()); |
|||
Assert.assertEquals(client.getProfileId(), desClient.getProfileId()); |
|||
Assert.assertEquals(client.getDeviceId(), desClient.getDeviceId()); |
|||
Assert.assertEquals(client.getState(), desClient.getState()); |
|||
Assert.assertEquals(client.getSession(), desClient.getSession()); |
|||
Assert.assertEquals(client.getPowerMode(), desClient.getPowerMode()); |
|||
Assert.assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); |
|||
Assert.assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); |
|||
Assert.assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); |
|||
Assert.assertEquals(client.getRegistration(), desClient.getRegistration()); |
|||
Assert.assertEquals(client.isAsleep(), desClient.isAsleep()); |
|||
Assert.assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); |
|||
Assert.assertEquals(client.getSleepTask(), desClient.getSleepTask()); |
|||
Assert.assertEquals(client.getClientSupportContentFormats(), desClient.getClientSupportContentFormats()); |
|||
Assert.assertEquals(client.getDefaultContentFormat(), desClient.getDefaultContentFormat()); |
|||
Assert.assertEquals(client.getRetryAttempts().get(), desClient.getRetryAttempts().get()); |
|||
Assert.assertEquals(client.getLastSentRpcId(), desClient.getLastSentRpcId()); |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<LWM2M> |
|||
<Object ObjectType="MODefinition"> |
|||
<Name>Test 15</Name> |
|||
<Description1></Description1> |
|||
<ObjectID>15</ObjectID> |
|||
<ObjectURN>urn:oma:lwm2m:oma:15:1.0</ObjectURN> |
|||
<LWM2MVersion>1.0</LWM2MVersion> |
|||
<ObjectVersion>1.0</ObjectVersion> |
|||
<MultipleInstances>Multiple</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Resources> |
|||
<Item ID="0"> |
|||
<Name>Test</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Single</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Type>String</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description></Description> |
|||
</Item> |
|||
</Resources> |
|||
<Description2></Description2> |
|||
</Object> |
|||
</LWM2M> |
|||
@ -0,0 +1,26 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<LWM2M> |
|||
<Object ObjectType="MODefinition"> |
|||
<Name>Test 17</Name> |
|||
<Description1></Description1> |
|||
<ObjectID>17</ObjectID> |
|||
<ObjectURN>urn:oma:lwm2m:oma:17:1.0</ObjectURN> |
|||
<LWM2MVersion>1.0</LWM2MVersion> |
|||
<ObjectVersion>1.0</ObjectVersion> |
|||
<MultipleInstances>Multiple</MultipleInstances> |
|||
<Mandatory>Mandatory</Mandatory> |
|||
<Resources> |
|||
<Item ID="0"> |
|||
<Name>Test</Name> |
|||
<Operations>R</Operations> |
|||
<MultipleInstances>Multiple</MultipleInstances> |
|||
<Mandatory>Optional</Mandatory> |
|||
<Type>String</Type> |
|||
<RangeEnumeration></RangeEnumeration> |
|||
<Units></Units> |
|||
<Description></Description> |
|||
</Item> |
|||
</Resources> |
|||
<Description2></Description2> |
|||
</Object> |
|||
</LWM2M> |
|||
@ -0,0 +1,209 @@ |
|||
diff --git a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js
|
|||
index d6c05f3..d09baed 100644
|
|||
--- a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js
|
|||
+++ b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js
|
|||
@@ -362,7 +362,10 @@ var DataZoomModel = /** @class */function (_super) {
|
|||
return axisProxy.getDataValueWindow(); |
|||
} |
|||
} else { |
|||
- return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow();
|
|||
+ var axisProxy = this.getAxisProxy(axisDim, axisIndex);
|
|||
+ if (axisProxy) {
|
|||
+ return axisProxy.getDataValueWindow();
|
|||
+ }
|
|||
} |
|||
}; |
|||
/** |
|||
@@ -381,10 +384,10 @@ var DataZoomModel = /** @class */function (_super) {
|
|||
var axisInfo = this._targetAxisInfoMap.get(axisDim); |
|||
for (var j = 0; j < axisInfo.indexList.length; j++) { |
|||
var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]); |
|||
- if (proxy.hostedBy(this)) {
|
|||
+ if (proxy && proxy.hostedBy(this)) {
|
|||
return proxy; |
|||
} |
|||
- if (!firstProxy) {
|
|||
+ if (proxy && !firstProxy) {
|
|||
firstProxy = proxy; |
|||
} |
|||
} |
|||
diff --git a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js
|
|||
index 06469b2..ccfbfa6 100644
|
|||
--- a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js
|
|||
+++ b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js
|
|||
@@ -96,11 +96,14 @@ var getRangeHandlers = {
|
|||
range[0] = (range[0] - percentPoint) * scale + percentPoint; |
|||
range[1] = (range[1] - percentPoint) * scale + percentPoint; |
|||
// Restrict range. |
|||
- var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
|
|||
- sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);
|
|||
- this.range = range;
|
|||
- if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
|
|||
- return range;
|
|||
+ var proxy = this.dataZoomModel.findRepresentativeAxisProxy();
|
|||
+ if (proxy) {
|
|||
+ var minMaxSpan = proxy.getMinMaxSpan();
|
|||
+ sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan);
|
|||
+ this.range = range;
|
|||
+ if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) {
|
|||
+ return range;
|
|||
+ }
|
|||
} |
|||
}, |
|||
pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) { |
|||
diff --git a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js
|
|||
index 98912e0..2e809af 100644
|
|||
--- a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js
|
|||
+++ b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js
|
|||
@@ -64,7 +64,7 @@ var DEFAULT_MOVE_HANDLE_SIZE = 7;
|
|||
var HORIZONTAL = 'horizontal'; |
|||
var VERTICAL = 'vertical'; |
|||
var LABEL_GAP = 5; |
|||
-var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];
|
|||
+var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter', 'custom'];
|
|||
var REALTIME_ANIMATION_CONFIG = { |
|||
easing: 'cubicOut', |
|||
duration: 100, |
|||
@@ -359,30 +359,33 @@ var SliderZoomView = /** @class */function (_super) {
|
|||
var result; |
|||
var ecModel = this.ecModel; |
|||
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { |
|||
- var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels();
|
|||
- each(seriesModels, function (seriesModel) {
|
|||
- if (result) {
|
|||
- return;
|
|||
- }
|
|||
- if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) {
|
|||
- return;
|
|||
- }
|
|||
- var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis;
|
|||
- var otherDim = getOtherDim(axisDim);
|
|||
- var otherAxisInverse;
|
|||
- var coordSys = seriesModel.coordinateSystem;
|
|||
- if (otherDim != null && coordSys.getOtherAxis) {
|
|||
- otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;
|
|||
- }
|
|||
- otherDim = seriesModel.getData().mapDimension(otherDim);
|
|||
- result = {
|
|||
- thisAxis: thisAxis,
|
|||
- series: seriesModel,
|
|||
- thisDim: axisDim,
|
|||
- otherDim: otherDim,
|
|||
- otherAxisInverse: otherAxisInverse
|
|||
- };
|
|||
- }, this);
|
|||
+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex);
|
|||
+ if (axisProxy) {
|
|||
+ var seriesModels = axisProxy.getTargetSeriesModels();
|
|||
+ each(seriesModels, function (seriesModel) {
|
|||
+ if (result) {
|
|||
+ return;
|
|||
+ }
|
|||
+ if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) {
|
|||
+ return;
|
|||
+ }
|
|||
+ var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis;
|
|||
+ var otherDim = getOtherDim(axisDim);
|
|||
+ var otherAxisInverse;
|
|||
+ var coordSys = seriesModel.coordinateSystem;
|
|||
+ if (otherDim != null && coordSys.getOtherAxis) {
|
|||
+ otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;
|
|||
+ }
|
|||
+ otherDim = seriesModel.getData().mapDimension(otherDim);
|
|||
+ result = {
|
|||
+ thisAxis: thisAxis,
|
|||
+ series: seriesModel,
|
|||
+ thisDim: axisDim,
|
|||
+ otherDim: otherDim,
|
|||
+ otherAxisInverse: otherAxisInverse
|
|||
+ };
|
|||
+ }, this);
|
|||
+ }
|
|||
}, this); |
|||
return result; |
|||
}; |
|||
@@ -530,12 +533,17 @@ var SliderZoomView = /** @class */function (_super) {
|
|||
var dataZoomModel = this.dataZoomModel; |
|||
var handleEnds = this._handleEnds; |
|||
var viewExtend = this._getViewExtent(); |
|||
- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan();
|
|||
- var percentExtent = [0, 100];
|
|||
- sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);
|
|||
- var lastRange = this._range;
|
|||
- var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);
|
|||
- return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];
|
|||
+ var proxy = dataZoomModel.findRepresentativeAxisProxy();
|
|||
+ if (proxy) {
|
|||
+ var minMaxSpan = proxy.getMinMaxSpan();
|
|||
+ var percentExtent = [0, 100];
|
|||
+ sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null);
|
|||
+ var lastRange = this._range;
|
|||
+ var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]);
|
|||
+ return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1];
|
|||
+ } else {
|
|||
+ return false;
|
|||
+ }
|
|||
}; |
|||
SliderZoomView.prototype._updateView = function (nonRealtime) { |
|||
var displaybles = this._displayables; |
|||
diff --git a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js
|
|||
index ce98fed..361b138 100644
|
|||
--- a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js
|
|||
+++ b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js
|
|||
@@ -90,7 +90,10 @@ var dataZoomProcessor = {
|
|||
// init stage and not after action dispatch handler, because |
|||
// reset should be called after seriesData.restoreData. |
|||
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { |
|||
- dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel);
|
|||
+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex);
|
|||
+ if (axisProxy) {
|
|||
+ axisProxy.reset(dataZoomModel);
|
|||
+ }
|
|||
}); |
|||
// Caution: data zoom filtering is order sensitive when using |
|||
// percent range and no min/max/scale set on axis. |
|||
@@ -107,7 +110,10 @@ var dataZoomProcessor = {
|
|||
// So we should filter x-axis after reset x-axis immediately, |
|||
// and then reset y-axis and filter y-axis. |
|||
dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { |
|||
- dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api);
|
|||
+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex);
|
|||
+ if (axisProxy) {
|
|||
+ axisProxy.filterData(dataZoomModel, api);
|
|||
+ }
|
|||
}); |
|||
}); |
|||
ecModel.eachComponent('dataZoom', function (dataZoomModel) { |
|||
diff --git a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js
|
|||
index cf8d6bc..9b30ec1 100644
|
|||
--- a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js
|
|||
+++ b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js
|
|||
@@ -109,9 +109,12 @@ var DataZoomFeature = /** @class */function (_super) {
|
|||
var axisModel = axis.model; |
|||
var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); |
|||
// Restrict range. |
|||
- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan();
|
|||
- if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {
|
|||
- minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan);
|
|||
+ var proxy = dataZoomModel.findRepresentativeAxisProxy(axisModel);
|
|||
+ if (proxy) {
|
|||
+ var minMaxSpan = proxy.getMinMaxSpan();
|
|||
+ if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) {
|
|||
+ minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan);
|
|||
+ }
|
|||
} |
|||
dataZoomModel && (snapshot[dataZoomModel.id] = { |
|||
dataZoomId: dataZoomModel.id, |
|||
diff --git a/node_modules/echarts/lib/component/tooltip/TooltipView.js b/node_modules/echarts/lib/component/tooltip/TooltipView.js
|
|||
index b8a9b95..8e4cb2f 100644
|
|||
--- a/node_modules/echarts/lib/component/tooltip/TooltipView.js
|
|||
+++ b/node_modules/echarts/lib/component/tooltip/TooltipView.js
|
|||
@@ -360,7 +360,7 @@ var TooltipView = /** @class */function (_super) {
|
|||
each(itemCoordSys.dataByAxis, function (axisItem) { |
|||
var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex); |
|||
var axisValue = axisItem.value; |
|||
- if (!axisModel || axisValue == null) {
|
|||
+ if (!axisModel || !axisModel.axis || axisValue == null) {
|
|||
return; |
|||
} |
|||
var axisValueLabel = axisPointerViewHelper.getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt); |
|||
@ -0,0 +1,258 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2024 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<ng-container [formGroup]="timeSeriesChartWidgetConfigForm"> |
|||
<tb-timewindow-config-panel formControlName="timewindowConfig"> |
|||
</tb-timewindow-config-panel> |
|||
<tb-datasources |
|||
[configMode]="basicMode" |
|||
hideDatasourceLabel |
|||
hideDataKeys |
|||
forceSingleDatasource |
|||
formControlName="datasources"> |
|||
</tb-datasources> |
|||
<tb-time-series-chart-y-axes-panel |
|||
formControlName="yAxes" |
|||
(axisRemoved)="yAxisRemoved($event)"> |
|||
</tb-time-series-chart-y-axes-panel> |
|||
<tb-data-keys-panel |
|||
panelTitle="{{ 'widgets.chart.series' | translate }}" |
|||
addKeyTitle="{{ 'widgets.chart.add-series' | translate }}" |
|||
keySettingsTitle="{{ 'widgets.chart.series-settings' | translate }}" |
|||
removeKeyTitle="{{ 'widgets.chart.remove-series' | translate }}" |
|||
noKeysText="{{ 'widgets.chart.no-series' | translate }}" |
|||
requiredKeysText="{{ 'widgets.chart.no-series-error' | translate }}" |
|||
timeSeriesChart |
|||
[yAxisIds]="yAxisIds" |
|||
[showTimeSeriesType]="chartType === TimeSeriesChartType.default" |
|||
hideSourceSelection |
|||
[datasourceType]="datasource?.type" |
|||
[deviceId]="datasource?.deviceId" |
|||
[entityAliasId]="datasource?.entityAliasId" |
|||
formControlName="series"> |
|||
</tb-data-keys-panel> |
|||
<tb-time-series-chart-thresholds-panel |
|||
formControlName="thresholds" |
|||
[aliasController]="aliasController" |
|||
[dataKeyCallbacks]="callbacks" |
|||
[datasource]="datasource" |
|||
[widgetConfig]="widgetConfig?.config" |
|||
[yAxisIds]="yAxisIds"> |
|||
</tb-time-series-chart-thresholds-panel> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widget-config.appearance</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle"> |
|||
{{ 'widget-config.title' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
<tb-font-settings formControlName="titleFont" |
|||
clearButton |
|||
[previewText]="timeSeriesChartWidgetConfigForm.get('title').value" |
|||
[initialPreviewStyle]="widgetConfig.config.titleStyle"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput |
|||
colorClearButton |
|||
formControlName="titleColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showIcon"> |
|||
{{ 'widget-config.card-icon' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput type="number" min="0" formControlName="iconSize" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
<tb-css-unit-select fxFlex formControlName="iconSizeUnit"></tb-css-unit-select> |
|||
<tb-material-icon-select asBoxInput |
|||
iconClearButton |
|||
[color]="timeSeriesChartWidgetConfigForm.get('iconColor').value" |
|||
formControlName="icon"> |
|||
</tb-material-icon-select> |
|||
<tb-color-input asBoxInput |
|||
colorClearButton |
|||
formControlName="iconColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.time-series-chart.chart</div> |
|||
<div class="tb-form-row"> |
|||
<mat-slide-toggle class="mat-slide" formControlName="dataZoom"> |
|||
{{ 'widgets.time-series-chart.data-zoom' | translate }} |
|||
</mat-slide-toggle> |
|||
</div> |
|||
<div class="tb-form-row"> |
|||
<mat-slide-toggle class="mat-slide" formControlName="stack"> |
|||
<div tb-hint-tooltip-icon="{{'widgets.time-series-chart.stack-mode-hint' | translate}}"> |
|||
{{ 'widgets.time-series-chart.stack-mode' | translate }} |
|||
</div> |
|||
</mat-slide-toggle> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.time-series-chart.axis.x-axis</div> |
|||
<tb-time-series-chart-axis-settings |
|||
formControlName="xAxis" |
|||
axisType="xAxis"> |
|||
</tb-time-series-chart-axis-settings> |
|||
</div> |
|||
<tb-time-series-no-aggregation-bar-width-settings |
|||
*ngIf="chartType === TimeSeriesChartType.default || chartType === TimeSeriesChartType.bar" |
|||
formControlName="noAggregationBarWidthSettings"> |
|||
</tb-time-series-no-aggregation-bar-width-settings> |
|||
<div class="tb-form-panel tb-slide-toggle"> |
|||
<mat-expansion-panel class="tb-settings" [expanded]="timeSeriesChartWidgetConfigForm.get('showLegend').value" |
|||
[disabled]="!timeSeriesChartWidgetConfigForm.get('showLegend').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle class="mat-slide" formControlName="showLegend" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widget-config.legend' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'legend.label' | translate }}</div> |
|||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<tb-font-settings formControlName="legendLabelFont" |
|||
previewText="Temperature"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput |
|||
colorClearButton |
|||
formControlName="legendLabelColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<tb-legend-config hideDirection |
|||
formControlName="legendConfig"> |
|||
</tb-legend-config> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</div> |
|||
<div class="tb-form-panel tb-slide-toggle"> |
|||
<mat-expansion-panel class="tb-settings" [expanded]="timeSeriesChartWidgetConfigForm.get('showTooltip').value" |
|||
[disabled]="!timeSeriesChartWidgetConfigForm.get('showTooltip').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widget-config.tooltip' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'tooltip.trigger' | translate }}</div> |
|||
<mat-chip-listbox class="center-stretch" formControlName="tooltipTrigger"> |
|||
<mat-chip-option [selectable]="timeSeriesChartWidgetConfigForm.get('tooltipTrigger').value !== EChartsTooltipTrigger.point" |
|||
[value]="EChartsTooltipTrigger.point">{{ 'tooltip.trigger-point' | translate }}</mat-chip-option> |
|||
<mat-chip-option [selectable]="timeSeriesChartWidgetConfigForm.get('tooltipTrigger').value !== EChartsTooltipTrigger.axis" |
|||
[value]="EChartsTooltipTrigger.axis">{{ 'tooltip.trigger-axis' | translate }}</mat-chip-option> |
|||
</mat-chip-listbox> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'tooltip.value' | translate }}</div> |
|||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<tb-font-settings formControlName="tooltipValueFont" |
|||
[previewText]="tooltipValuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput |
|||
colorClearButton |
|||
formControlName="tooltipValueColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="tooltipShowDate"> |
|||
{{ 'tooltip.date' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<tb-date-format-select fxFlex excludeLastUpdateAgo formControlName="tooltipDateFormat"></tb-date-format-select> |
|||
<tb-font-settings formControlName="tooltipDateFont" |
|||
[previewText]="tooltipDatePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput |
|||
colorClearButton |
|||
formControlName="tooltipDateColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row"> |
|||
<mat-slide-toggle class="mat-slide" formControlName="tooltipDateInterval"> |
|||
<div tb-hint-tooltip-icon="{{'tooltip.show-date-time-interval-hint' | translate}}"> |
|||
{{ 'tooltip.show-date-time-interval' | translate }} |
|||
</div> |
|||
</mat-slide-toggle> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'tooltip.background-color' | translate }}</div> |
|||
<tb-color-input asBoxInput |
|||
colorClearButton |
|||
formControlName="tooltipBackgroundColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'tooltip.background-blur' | translate }}</div> |
|||
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix>px</div> |
|||
</mat-form-field> |
|||
</div> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</div> |
|||
<tb-time-series-chart-animation-settings |
|||
formControlName="animation"> |
|||
</tb-time-series-chart-animation-settings> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'widgets.background.background' | translate }}</div> |
|||
<tb-background-settings formControlName="background"> |
|||
</tb-background-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between column-lt-md"> |
|||
<div translate>widget-config.show-card-buttons</div> |
|||
<mat-chip-listbox multiple formControlName="cardButtons"> |
|||
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> |
|||
</mat-chip-listbox> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'widget-config.card-border-radius' | translate }}</div> |
|||
<mat-form-field appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="borderRadius" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'widget-config.card-padding' | translate }}</div> |
|||
<mat-form-field appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="padding" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
</div> |
|||
</div> |
|||
<tb-widget-actions-panel |
|||
formControlName="actions"> |
|||
</tb-widget-actions-panel> |
|||
</ng-container> |
|||
@ -0,0 +1,367 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, Injector } from '@angular/core'; |
|||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; |
|||
import { WidgetConfigComponentData } from '@home/models/widget-component.models'; |
|||
import { |
|||
DataKey, |
|||
Datasource, |
|||
legendPositions, |
|||
legendPositionTranslationMap, |
|||
WidgetConfig, |
|||
} from '@shared/models/widget.models'; |
|||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; |
|||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { |
|||
getTimewindowConfig, |
|||
setTimewindowConfig |
|||
} from '@home/components/widget/config/timewindow-config-panel.component'; |
|||
import { formatValue, isDefinedAndNotNull, isUndefined, mergeDeep } from '@core/utils'; |
|||
import { |
|||
cssSizeToStrSize, |
|||
DateFormatProcessor, |
|||
DateFormatSettings, |
|||
resolveCssSize |
|||
} from '@shared/models/widget-settings.models'; |
|||
import { |
|||
timeSeriesChartWidgetDefaultSettings, |
|||
TimeSeriesChartWidgetSettings |
|||
} from '@home/components/widget/lib/chart/time-series-chart-widget.models'; |
|||
import { EChartsTooltipTrigger } from '@home/components/widget/lib/chart/echarts-widget.models'; |
|||
import { |
|||
TimeSeriesChartKeySettings, TimeSeriesChartThreshold, |
|||
TimeSeriesChartType, TimeSeriesChartYAxes, |
|||
TimeSeriesChartYAxisId |
|||
} from '@home/components/widget/lib/chart/time-series-chart.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-time-series-chart-basic-config', |
|||
templateUrl: './time-series-chart-basic-config.component.html', |
|||
styleUrls: ['../basic-config.scss'] |
|||
}) |
|||
export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigComponent { |
|||
|
|||
public get datasource(): Datasource { |
|||
const datasources: Datasource[] = this.timeSeriesChartWidgetConfigForm.get('datasources').value; |
|||
if (datasources && datasources.length) { |
|||
return datasources[0]; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public get yAxisIds(): TimeSeriesChartYAxisId[] { |
|||
const yAxes: TimeSeriesChartYAxes = this.timeSeriesChartWidgetConfigForm.get('yAxes').value; |
|||
return Object.keys(yAxes); |
|||
} |
|||
|
|||
TimeSeriesChartType = TimeSeriesChartType; |
|||
|
|||
EChartsTooltipTrigger = EChartsTooltipTrigger; |
|||
|
|||
legendPositions = legendPositions; |
|||
|
|||
legendPositionTranslationMap = legendPositionTranslationMap; |
|||
|
|||
timeSeriesChartWidgetConfigForm: UntypedFormGroup; |
|||
|
|||
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
|||
|
|||
tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); |
|||
|
|||
chartType: TimeSeriesChartType = TimeSeriesChartType.default; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected widgetConfigComponent: WidgetConfigComponent, |
|||
private $injector: Injector, |
|||
private fb: UntypedFormBuilder) { |
|||
super(store, widgetConfigComponent); |
|||
} |
|||
|
|||
public yAxisRemoved(yAxisId: TimeSeriesChartYAxisId): void { |
|||
if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources.length > 1) { |
|||
for (let i = 1; i < this.widgetConfig.config.datasources.length; i++) { |
|||
const datasource = this.widgetConfig.config.datasources[i]; |
|||
this.removeYaxisId(datasource.dataKeys, yAxisId); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected configForm(): UntypedFormGroup { |
|||
return this.timeSeriesChartWidgetConfigForm; |
|||
} |
|||
|
|||
protected setupConfig(widgetConfig: WidgetConfigComponentData) { |
|||
const params = widgetConfig.typeParameters as any; |
|||
if (isDefinedAndNotNull(params.chartType)) { |
|||
this.chartType = params.chartType; |
|||
} |
|||
super.setupConfig(widgetConfig); |
|||
} |
|||
|
|||
protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { |
|||
return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries, units: '°C', decimals: 0 }]; |
|||
} |
|||
|
|||
protected onConfigSet(configData: WidgetConfigComponentData) { |
|||
const settings: TimeSeriesChartWidgetSettings = mergeDeep<TimeSeriesChartWidgetSettings>({} as TimeSeriesChartWidgetSettings, |
|||
timeSeriesChartWidgetDefaultSettings, configData.config.settings as TimeSeriesChartWidgetSettings); |
|||
const iconSize = resolveCssSize(configData.config.iconSize); |
|||
this.timeSeriesChartWidgetConfigForm = this.fb.group({ |
|||
timewindowConfig: [getTimewindowConfig(configData.config), []], |
|||
datasources: [configData.config.datasources, []], |
|||
|
|||
yAxes: [settings.yAxes, []], |
|||
series: [this.getSeries(configData.config.datasources), []], |
|||
thresholds: [settings.thresholds, []], |
|||
|
|||
showTitle: [configData.config.showTitle, []], |
|||
title: [configData.config.title, []], |
|||
titleFont: [configData.config.titleFont, []], |
|||
titleColor: [configData.config.titleColor, []], |
|||
|
|||
showIcon: [configData.config.showTitleIcon, []], |
|||
iconSize: [iconSize[0], [Validators.min(0)]], |
|||
iconSizeUnit: [iconSize[1], []], |
|||
icon: [configData.config.titleIcon, []], |
|||
iconColor: [configData.config.iconColor, []], |
|||
|
|||
dataZoom: [settings.dataZoom, []], |
|||
stack: [settings.stack, []], |
|||
|
|||
xAxis: [settings.xAxis, []], |
|||
|
|||
noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], |
|||
|
|||
showLegend: [settings.showLegend, []], |
|||
legendLabelFont: [settings.legendLabelFont, []], |
|||
legendLabelColor: [settings.legendLabelColor, []], |
|||
legendConfig: [settings.legendConfig, []], |
|||
|
|||
showTooltip: [settings.showTooltip, []], |
|||
tooltipTrigger: [settings.tooltipTrigger, []], |
|||
tooltipValueFont: [settings.tooltipValueFont, []], |
|||
tooltipValueColor: [settings.tooltipValueColor, []], |
|||
tooltipShowDate: [settings.tooltipShowDate, []], |
|||
tooltipDateFormat: [settings.tooltipDateFormat, []], |
|||
tooltipDateFont: [settings.tooltipDateFont, []], |
|||
tooltipDateColor: [settings.tooltipDateColor, []], |
|||
tooltipDateInterval: [settings.tooltipDateInterval, []], |
|||
|
|||
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
|||
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
|||
|
|||
animation: [settings.animation, []], |
|||
|
|||
background: [settings.background, []], |
|||
|
|||
cardButtons: [this.getCardButtons(configData.config), []], |
|||
borderRadius: [configData.config.borderRadius, []], |
|||
padding: [settings.padding, []], |
|||
|
|||
actions: [configData.config.actions || {}, []] |
|||
}); |
|||
} |
|||
|
|||
protected prepareOutputConfig(config: any): WidgetConfigComponentData { |
|||
setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); |
|||
this.widgetConfig.config.datasources = config.datasources; |
|||
this.setSeries(config.series, this.widgetConfig.config.datasources); |
|||
|
|||
this.widgetConfig.config.showTitle = config.showTitle; |
|||
this.widgetConfig.config.title = config.title; |
|||
this.widgetConfig.config.titleFont = config.titleFont; |
|||
this.widgetConfig.config.titleColor = config.titleColor; |
|||
|
|||
this.widgetConfig.config.showTitleIcon = config.showIcon; |
|||
this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); |
|||
this.widgetConfig.config.titleIcon = config.icon; |
|||
this.widgetConfig.config.iconColor = config.iconColor; |
|||
|
|||
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; |
|||
|
|||
this.widgetConfig.config.settings.thresholds = config.thresholds; |
|||
|
|||
this.widgetConfig.config.settings.dataZoom = config.dataZoom; |
|||
this.widgetConfig.config.settings.stack = config.stack; |
|||
|
|||
this.widgetConfig.config.settings.yAxes = config.yAxes; |
|||
this.widgetConfig.config.settings.xAxis = config.xAxis; |
|||
|
|||
this.widgetConfig.config.settings.noAggregationBarWidthSettings = config.noAggregationBarWidthSettings; |
|||
|
|||
this.widgetConfig.config.settings.showLegend = config.showLegend; |
|||
this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; |
|||
this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; |
|||
this.widgetConfig.config.settings.legendConfig = config.legendConfig; |
|||
|
|||
this.widgetConfig.config.settings.showTooltip = config.showTooltip; |
|||
this.widgetConfig.config.settings.tooltipTrigger = config.tooltipTrigger; |
|||
this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; |
|||
this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; |
|||
this.widgetConfig.config.settings.tooltipShowDate = config.tooltipShowDate; |
|||
this.widgetConfig.config.settings.tooltipDateFormat = config.tooltipDateFormat; |
|||
this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont; |
|||
this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor; |
|||
this.widgetConfig.config.settings.tooltipDateInterval = config.tooltipDateInterval; |
|||
this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; |
|||
this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; |
|||
|
|||
this.widgetConfig.config.settings.animation = config.animation; |
|||
|
|||
this.widgetConfig.config.settings.background = config.background; |
|||
|
|||
this.setCardButtons(config.cardButtons, this.widgetConfig.config); |
|||
this.widgetConfig.config.borderRadius = config.borderRadius; |
|||
this.widgetConfig.config.settings.padding = config.padding; |
|||
|
|||
this.widgetConfig.config.actions = config.actions; |
|||
return this.widgetConfig; |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate']; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean, trigger?: string) { |
|||
const showTitle: boolean = this.timeSeriesChartWidgetConfigForm.get('showTitle').value; |
|||
const showIcon: boolean = this.timeSeriesChartWidgetConfigForm.get('showIcon').value; |
|||
const showLegend: boolean = this.timeSeriesChartWidgetConfigForm.get('showLegend').value; |
|||
const showTooltip: boolean = this.timeSeriesChartWidgetConfigForm.get('showTooltip').value; |
|||
const tooltipShowDate: boolean = this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').value; |
|||
|
|||
if (showTitle) { |
|||
this.timeSeriesChartWidgetConfigForm.get('title').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('titleFont').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('titleColor').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('showIcon').enable({emitEvent: false}); |
|||
if (showIcon) { |
|||
this.timeSeriesChartWidgetConfigForm.get('iconSize').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconSizeUnit').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('icon').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconColor').enable(); |
|||
} else { |
|||
this.timeSeriesChartWidgetConfigForm.get('iconSize').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconSizeUnit').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('icon').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconColor').disable(); |
|||
} |
|||
} else { |
|||
this.timeSeriesChartWidgetConfigForm.get('title').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('titleFont').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('titleColor').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('showIcon').disable({emitEvent: false}); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconSize').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconSizeUnit').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('icon').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('iconColor').disable(); |
|||
} |
|||
|
|||
if (showLegend) { |
|||
this.timeSeriesChartWidgetConfigForm.get('legendLabelFont').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('legendLabelColor').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('legendConfig').enable(); |
|||
} else { |
|||
this.timeSeriesChartWidgetConfigForm.get('legendLabelFont').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('legendLabelColor').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('legendConfig').disable(); |
|||
} |
|||
|
|||
if (showTooltip) { |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipTrigger').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipValueFont').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipValueColor').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').enable({emitEvent: false}); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); |
|||
if (tooltipShowDate) { |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').enable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').enable(); |
|||
} else { |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').disable(); |
|||
} |
|||
} else { |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipValueFont').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipValueColor').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').disable({emitEvent: false}); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); |
|||
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); |
|||
} |
|||
} |
|||
|
|||
private getSeries(datasources?: Datasource[]): DataKey[] { |
|||
if (datasources && datasources.length) { |
|||
return datasources[0].dataKeys || []; |
|||
} |
|||
return []; |
|||
} |
|||
|
|||
private setSeries(series: DataKey[], datasources?: Datasource[]) { |
|||
if (datasources && datasources.length) { |
|||
datasources[0].dataKeys = series; |
|||
} |
|||
} |
|||
|
|||
private removeYaxisId(series: DataKey[], yAxisId: TimeSeriesChartYAxisId): boolean { |
|||
let changed = false; |
|||
if (series) { |
|||
series.forEach(key => { |
|||
const keySettings = ((key.settings || {}) as TimeSeriesChartKeySettings); |
|||
if (keySettings.yAxisId === yAxisId) { |
|||
keySettings.yAxisId = 'default'; |
|||
changed = true; |
|||
} |
|||
}); |
|||
} |
|||
return changed; |
|||
} |
|||
|
|||
private getCardButtons(config: WidgetConfig): string[] { |
|||
const buttons: string[] = []; |
|||
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { |
|||
buttons.push('fullscreen'); |
|||
} |
|||
return buttons; |
|||
} |
|||
|
|||
private setCardButtons(buttons: string[], config: WidgetConfig) { |
|||
config.enableFullscreen = buttons.includes('fullscreen'); |
|||
} |
|||
|
|||
private _tooltipValuePreviewFn(): string { |
|||
return formatValue(22, 0, '°C', false); |
|||
} |
|||
|
|||
private _tooltipDatePreviewFn(): string { |
|||
const dateFormat: DateFormatSettings = this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').value; |
|||
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); |
|||
processor.update(Date.now()); |
|||
return processor.formatted; |
|||
} |
|||
} |
|||
@ -0,0 +1,166 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient'; |
|||
import { Interval, IntervalMath } from '@shared/models/time/time.models'; |
|||
import { LabelFormatterCallback, SeriesLabelOption } from 'echarts/types/src/util/types'; |
|||
import { |
|||
TimeSeriesChartDataItem, |
|||
TimeSeriesChartNoAggregationBarWidthStrategy |
|||
} from '@home/components/widget/lib/chart/time-series-chart.models'; |
|||
import { CustomSeriesRenderItemParams } from 'echarts'; |
|||
import { CustomSeriesRenderItemAPI, CustomSeriesRenderItemReturn } from 'echarts/types/dist/shared'; |
|||
import { isNumeric } from '@core/utils'; |
|||
import * as echarts from 'echarts/core'; |
|||
|
|||
export interface BarVisualSettings { |
|||
color: string | LinearGradientObject; |
|||
borderColor: string; |
|||
borderWidth: number; |
|||
borderRadius: number; |
|||
} |
|||
|
|||
export interface BarRenderSharedContext { |
|||
timeInterval: Interval; |
|||
noAggregationBarWidthStrategy: TimeSeriesChartNoAggregationBarWidthStrategy; |
|||
noAggregationWidthRelative: boolean; |
|||
noAggregationWidth: number; |
|||
} |
|||
|
|||
export interface BarRenderContext { |
|||
shared: BarRenderSharedContext; |
|||
barsCount?: number; |
|||
barIndex?: number; |
|||
noAggregation?: boolean; |
|||
visualSettings?: BarVisualSettings; |
|||
labelOption?: SeriesLabelOption; |
|||
barStackIndex?: number; |
|||
currentStackItems?: TimeSeriesChartDataItem[]; |
|||
} |
|||
|
|||
export const renderTimeSeriesBar = (params: CustomSeriesRenderItemParams, api: CustomSeriesRenderItemAPI, |
|||
renderCtx: BarRenderContext): CustomSeriesRenderItemReturn => { |
|||
const time = api.value(0) as number; |
|||
let start = api.value(2) as number; |
|||
let end = api.value(3) as number; |
|||
let interval = end - start; |
|||
const ts = start ? start : time; |
|||
|
|||
const separateBar = renderCtx.noAggregation && |
|||
renderCtx.shared.noAggregationBarWidthStrategy === TimeSeriesChartNoAggregationBarWidthStrategy.separate; |
|||
|
|||
if (renderCtx.noAggregation) { |
|||
if (renderCtx.shared.noAggregationWidthRelative) { |
|||
const scaleWidth = api.getWidth() / api.size([1,0])[0]; |
|||
interval = scaleWidth * (renderCtx.shared.noAggregationWidth / 100); |
|||
} else { |
|||
interval = renderCtx.shared.noAggregationWidth; |
|||
} |
|||
start = time - interval / 2; |
|||
end = time + interval / 2; |
|||
} |
|||
if (!start || !end || !interval) { |
|||
interval = IntervalMath.numberValue(renderCtx.shared.timeInterval); |
|||
start = time - interval / 2; |
|||
} |
|||
|
|||
const gap = 0.3; |
|||
const barInterval = separateBar ? interval : interval / (renderCtx.barsCount + gap * (renderCtx.barsCount + 3)); |
|||
const intervalGap = barInterval * gap * 2; |
|||
const barGap = barInterval * gap; |
|||
const value = api.value(1); |
|||
const startTime = separateBar ? start : start + intervalGap + (barInterval + barGap) * renderCtx.barIndex; |
|||
const delta = barInterval; |
|||
let offset = 0; |
|||
if (renderCtx.currentStackItems?.length) { |
|||
for (let i = 0; i < renderCtx.barStackIndex; i++) { |
|||
const stackItem = renderCtx.currentStackItems[i]; |
|||
const dataName = ts + ''; |
|||
const data = stackItem.data.find(d => d.name === dataName); |
|||
if (data) { |
|||
const val = data.value[1]; |
|||
if (isNumeric(val)) { |
|||
offset += Number(val); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
let lowerLeft: number[]; |
|||
if (offset !== 0 && isNumeric(value)) { |
|||
lowerLeft = api.coord([startTime, value >= 0 ? Number(value) + offset : offset]); |
|||
} else { |
|||
lowerLeft = api.coord([startTime, value >= 0 ? value : 0]); |
|||
} |
|||
const size = api.size([delta, value]); |
|||
const width = size[0]; |
|||
const height = size[1]; |
|||
|
|||
const coordSys: {x: number; y: number; width: number; height: number} = params.coordSys as any; |
|||
|
|||
const rectShape = echarts.graphic.clipRectByRect({ |
|||
x: lowerLeft[0], |
|||
y: lowerLeft[1], |
|||
width, |
|||
height |
|||
}, { |
|||
x: coordSys.x, |
|||
y: coordSys.y, |
|||
width: coordSys.width, |
|||
height: coordSys.height |
|||
}); |
|||
|
|||
const zeroPos = api.coord([0, offset]); |
|||
|
|||
const style: any = { |
|||
fill: renderCtx.visualSettings.color, |
|||
stroke: renderCtx.visualSettings.borderColor, |
|||
lineWidth: renderCtx.visualSettings.borderWidth |
|||
}; |
|||
|
|||
if (renderCtx.labelOption.show) { |
|||
let position = renderCtx.labelOption.position; |
|||
if (value < 0) { |
|||
if (position === 'top') { |
|||
position = 'bottom'; |
|||
} else if (position === 'bottom') { |
|||
position = 'top'; |
|||
} |
|||
} |
|||
style.text = (renderCtx.labelOption.formatter as LabelFormatterCallback)({value: [null, value]} as any); |
|||
style.textDistance = 5; |
|||
style.textPosition = position; |
|||
style.rich = renderCtx.labelOption.rich; |
|||
} |
|||
|
|||
let borderRadius: number[]; |
|||
if (value < 0) { |
|||
borderRadius = [0, 0, renderCtx.visualSettings.borderRadius, renderCtx.visualSettings.borderRadius]; |
|||
} else { |
|||
borderRadius = [renderCtx.visualSettings.borderRadius, renderCtx.visualSettings.borderRadius, 0, 0]; |
|||
} |
|||
return rectShape && { |
|||
type: 'rect', |
|||
id: time + '', |
|||
shape: {...rectShape, r: borderRadius}, |
|||
style, |
|||
focus: 'series', |
|||
transition: 'all', |
|||
enterFrom: { |
|||
style: { opacity: 0 }, |
|||
shape: { height: 0, y: zeroPos[1] } |
|||
} |
|||
}; |
|||
}; |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue