mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-12 15:52:57 +00:00
Merge remote-tracking branch 'origin' into feature/img
This commit is contained in:
commit
221db67e31
@ -235,12 +235,10 @@ limitations under the License. -->
|
|||||||
.no-data {
|
.no-data {
|
||||||
font-size: $font-size-smaller;
|
font-size: $font-size-smaller;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
align-items: center;
|
||||||
display: -webkit-box;
|
justify-content: center;
|
||||||
-webkit-box-orient: horizontal;
|
display: flex;
|
||||||
-webkit-box-pack: center;
|
color: var(--text-color-placeholder);
|
||||||
-webkit-box-align: center;
|
|
||||||
color: #666;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart {
|
.chart {
|
||||||
|
@ -112,22 +112,7 @@ export async function useExpressionsQueryProcessor(config: Indexable) {
|
|||||||
tips.push(obj.error);
|
tips.push(obj.error);
|
||||||
typesOfMQE.push(type);
|
typesOfMQE.push(type);
|
||||||
if (!obj.error) {
|
if (!obj.error) {
|
||||||
if (type === ExpressionResultType.TIME_SERIES_VALUES) {
|
if ([ExpressionResultType.SINGLE_VALUE, ExpressionResultType.TIME_SERIES_VALUES].includes(type)) {
|
||||||
if (results.length === 1) {
|
|
||||||
const label = results[0].metric && results[0].metric.labels[0] && results[0].metric.labels[0].value;
|
|
||||||
source[c.label || label || name] = results[0].values.map((d: { value: unknown }) => d.value) || [];
|
|
||||||
} else {
|
|
||||||
for (const item of results) {
|
|
||||||
const values = item.values.map((d: { value: unknown }) => d.value) || [];
|
|
||||||
const label = item.metric.labels
|
|
||||||
.map((d: { key: string; value: string }) => `${d.key}=${d.value}`)
|
|
||||||
.join(",");
|
|
||||||
|
|
||||||
source[label] = values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type === ExpressionResultType.SINGLE_VALUE) {
|
|
||||||
for (const item of results) {
|
for (const item of results) {
|
||||||
const label =
|
const label =
|
||||||
item.metric &&
|
item.metric &&
|
||||||
|
@ -23,7 +23,6 @@ import type { AxiosResponse } from "axios";
|
|||||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
import { useSelectorStore } from "@/store/modules/selectors";
|
import { useSelectorStore } from "@/store/modules/selectors";
|
||||||
import { QueryOrders } from "@/views/dashboard/data";
|
import { QueryOrders } from "@/views/dashboard/data";
|
||||||
|
|
||||||
interface TraceState {
|
interface TraceState {
|
||||||
services: Service[];
|
services: Service[];
|
||||||
instances: Instance[];
|
instances: Instance[];
|
||||||
@ -168,6 +167,7 @@ export const traceStore = defineStore({
|
|||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
const data = res.data.data.trace.spans;
|
const data = res.data.data.trace.spans;
|
||||||
|
|
||||||
this.setTraceSpans(data || []);
|
this.setTraceSpans(data || []);
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
@ -391,6 +391,8 @@ limitations under the License. -->
|
|||||||
delete child.label;
|
delete child.label;
|
||||||
delete child.value;
|
delete child.value;
|
||||||
delete child.filters;
|
delete child.filters;
|
||||||
|
delete child.typesOfMQE;
|
||||||
|
delete child.subTypesOfMQE;
|
||||||
if (isEmptyObject(child.graph)) {
|
if (isEmptyObject(child.graph)) {
|
||||||
delete child.graph;
|
delete child.graph;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ limitations under the License. -->
|
|||||||
.trace-line {
|
.trace-line {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 2px;
|
width: 2px;
|
||||||
height: 900px;
|
height: 100%;
|
||||||
background-color: #e8e8e8;
|
background-color: #e8e8e8;
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chart-table">
|
<div class="chart-table" v-if="dataKeys.length">
|
||||||
<div class="row header flex-h">
|
<div class="row header flex-h">
|
||||||
<div
|
<div
|
||||||
v-for="key in dataKeys[0]"
|
v-for="key in dataKeys[0]"
|
||||||
@ -41,6 +41,7 @@ limitations under the License. -->
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="no-data" v-else>No Data</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
@ -131,4 +132,13 @@ limitations under the License. -->
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-data {
|
||||||
|
font-size: $font-size-smaller;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
color: var(--text-color-placeholder);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -25,11 +25,14 @@ limitations under the License. -->
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, onUnmounted, watch, toRaw } from "vue";
|
import { onMounted, ref, onUnmounted, watch, toRaw } from "vue";
|
||||||
import { useDemandLogStore } from "@/store/modules/demand-log";
|
import { useDemandLogStore } from "@/store/modules/demand-log";
|
||||||
|
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||||
|
import { Themes } from "@/constants/data";
|
||||||
|
|
||||||
/*global Nullable */
|
/*global Nullable */
|
||||||
const demandLogStore = useDemandLogStore();
|
const demandLogStore = useDemandLogStore();
|
||||||
const monacoInstance = ref();
|
const monacoInstance = ref();
|
||||||
const logContent = ref<Nullable<HTMLDivElement>>(null);
|
const logContent = ref<Nullable<HTMLDivElement>>(null);
|
||||||
|
const appStore = useAppStoreWithOut();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init();
|
init();
|
||||||
@ -50,6 +53,7 @@ limitations under the License. -->
|
|||||||
wordWrap: true,
|
wordWrap: true,
|
||||||
minimap: { enabled: false },
|
minimap: { enabled: false },
|
||||||
readonly: true,
|
readonly: true,
|
||||||
|
theme: getTheme(),
|
||||||
});
|
});
|
||||||
toRaw(monacoInstance.value).updateOptions({ readOnly: true });
|
toRaw(monacoInstance.value).updateOptions({ readOnly: true });
|
||||||
editorLayout();
|
editorLayout();
|
||||||
@ -64,6 +68,9 @@ limitations under the License. -->
|
|||||||
width: width,
|
width: width,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function getTheme() {
|
||||||
|
return appStore.theme === Themes.Dark ? "vs-dark" : "vs";
|
||||||
|
}
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (!toRaw(monacoInstance.value)) {
|
if (!toRaw(monacoInstance.value)) {
|
||||||
return;
|
return;
|
||||||
@ -72,6 +79,12 @@ limitations under the License. -->
|
|||||||
monacoInstance.value = null;
|
monacoInstance.value = null;
|
||||||
demandLogStore.setLogs("");
|
demandLogStore.setLogs("");
|
||||||
});
|
});
|
||||||
|
watch(
|
||||||
|
() => appStore.theme,
|
||||||
|
() => {
|
||||||
|
toRaw(monacoInstance.value).updateOptions({ theme: getTheme() });
|
||||||
|
},
|
||||||
|
);
|
||||||
watch(
|
watch(
|
||||||
() => demandLogStore.logs,
|
() => demandLogStore.logs,
|
||||||
() => {
|
() => {
|
||||||
|
@ -125,11 +125,13 @@ limitations under the License. -->
|
|||||||
const html = exprssions.map((m: string, index: number) => {
|
const html = exprssions.map((m: string, index: number) => {
|
||||||
const metric =
|
const metric =
|
||||||
topologyStore.hierarchyInstanceNodeMetrics[data.layer || ""][m].values.find(
|
topologyStore.hierarchyInstanceNodeMetrics[data.layer || ""][m].values.find(
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id,
|
(val: { id: string; value: string }) => val.id === data.id,
|
||||||
) || {};
|
) || {};
|
||||||
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||||
|
|
||||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
|
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
||||||
|
opt.unit || ""
|
||||||
|
}</div>`;
|
||||||
});
|
});
|
||||||
const tipHtml = [
|
const tipHtml = [
|
||||||
`<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`,
|
`<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`,
|
||||||
|
@ -133,10 +133,12 @@ limitations under the License. -->
|
|||||||
const html = exprssions.map((m: string, index: number) => {
|
const html = exprssions.map((m: string, index: number) => {
|
||||||
const metric =
|
const metric =
|
||||||
topologyStore.hierarchyNodeMetrics[data.layer || ""][m].values.find(
|
topologyStore.hierarchyNodeMetrics[data.layer || ""][m].values.find(
|
||||||
(val: { id: string; value: unknown }) => val.id === data.id,
|
(val: { id: string; value: string }) => val.id === data.id,
|
||||||
) || {};
|
) || {};
|
||||||
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
|
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
||||||
|
opt.unit || ""
|
||||||
|
}</div>`;
|
||||||
});
|
});
|
||||||
const tipHtml = [
|
const tipHtml = [
|
||||||
`<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`,
|
`<div class="mb-5"><span class="grey">name: </span>${data.name}</div><div class="mb-5"><span class="grey">layer: </span>${data.layer}</div>`,
|
||||||
|
@ -314,7 +314,7 @@ limitations under the License. -->
|
|||||||
const html = nodeMetrics.map((m, index) => {
|
const html = nodeMetrics.map((m, index) => {
|
||||||
const metric =
|
const metric =
|
||||||
(topologyStore.nodeMetricValue[m] &&
|
(topologyStore.nodeMetricValue[m] &&
|
||||||
topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: unknown }) => val.id === data.id)) ||
|
topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: string }) => val.id === data.id)) ||
|
||||||
{};
|
{};
|
||||||
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
||||||
@ -344,7 +344,7 @@ limitations under the License. -->
|
|||||||
);
|
);
|
||||||
if (metric) {
|
if (metric) {
|
||||||
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
|
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
|
||||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${
|
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
||||||
opt.unit || ""
|
opt.unit || ""
|
||||||
}</div>`;
|
}</div>`;
|
||||||
}
|
}
|
||||||
@ -355,7 +355,7 @@ limitations under the License. -->
|
|||||||
(val: { id: string; value: unknown }) => val.id === data.id,
|
(val: { id: string; value: unknown }) => val.id === data.id,
|
||||||
);
|
);
|
||||||
if (metric) {
|
if (metric) {
|
||||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${
|
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
||||||
opt.unit || ""
|
opt.unit || ""
|
||||||
}</div>`;
|
}</div>`;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,8 @@ limitations under the License. -->
|
|||||||
}
|
}
|
||||||
|
|
||||||
.trace-t-wrapper {
|
.trace-t-wrapper {
|
||||||
overflow: hidden;
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
border-right: 1px solid var(--sw-trace-list-border);
|
border-right: 1px solid var(--sw-trace-list-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ limitations under the License. -->
|
|||||||
if (!traceGraph.value) {
|
if (!traceGraph.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
d3.selectAll(".d3-tip").remove();
|
||||||
if (props.type === "List") {
|
if (props.type === "List") {
|
||||||
tree.value = new ListGraph(traceGraph.value, handleSelectSpan);
|
tree.value = new ListGraph(traceGraph.value, handleSelectSpan);
|
||||||
tree.value.init({ label: "TRACE_ROOT", children: segmentId.value }, props.data, fixSpansSize.value);
|
tree.value.init({ label: "TRACE_ROOT", children: segmentId.value }, props.data, fixSpansSize.value);
|
||||||
@ -96,10 +97,8 @@ limitations under the License. -->
|
|||||||
node.children.push(data);
|
node.children.push(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node.children && node.children.length > 0) {
|
for (const nodeItem of node.children || []) {
|
||||||
node.children.forEach((nodeItem: Recordable) => {
|
traverseTree(nodeItem, spanId, segmentId, data);
|
||||||
traverseTree(nodeItem, spanId, segmentId, data);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function changeTree() {
|
function changeTree() {
|
||||||
@ -108,7 +107,7 @@ limitations under the License. -->
|
|||||||
}
|
}
|
||||||
segmentId.value = [];
|
segmentId.value = [];
|
||||||
const segmentGroup: Recordable = {};
|
const segmentGroup: Recordable = {};
|
||||||
const segmentIdGroup: Recordable = [];
|
const segmentIdGroup: string[] = [];
|
||||||
const fixSpans: Span[] = [];
|
const fixSpans: Span[] = [];
|
||||||
const segmentHeaders: Span[] = [];
|
const segmentHeaders: Span[] = [];
|
||||||
for (const span of props.data) {
|
for (const span of props.data) {
|
||||||
@ -118,7 +117,9 @@ limitations under the License. -->
|
|||||||
if (span.parentSpanId === -1) {
|
if (span.parentSpanId === -1) {
|
||||||
segmentHeaders.push(span);
|
segmentHeaders.push(span);
|
||||||
} else {
|
} else {
|
||||||
const item = props.data.find((i: Span) => i.segmentId === span.segmentId && i.spanId === span.spanId - 1);
|
const item = props.data.find(
|
||||||
|
(i: Span) => i.traceId === span.traceId && i.segmentId === span.segmentId && i.spanId === span.spanId - 1,
|
||||||
|
);
|
||||||
const fixSpanKeyContent = {
|
const fixSpanKeyContent = {
|
||||||
traceId: span.traceId,
|
traceId: span.traceId,
|
||||||
segmentId: span.segmentId,
|
segmentId: span.segmentId,
|
||||||
@ -145,18 +146,18 @@ limitations under the License. -->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
segmentHeaders.forEach((span: Span) => {
|
for (const span of segmentHeaders) {
|
||||||
if (span.refs.length) {
|
if (span.refs.length) {
|
||||||
let exit = 0;
|
let exit = null;
|
||||||
span.refs.forEach((ref) => {
|
for (const ref of span.refs) {
|
||||||
const i = props.data.findIndex(
|
const e = props.data.find(
|
||||||
(i: Recordable) => ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId,
|
(i: Recordable) =>
|
||||||
|
ref.traceId === i.traceId && ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId,
|
||||||
);
|
);
|
||||||
if (i > -1) {
|
if (e) {
|
||||||
exit = 1;
|
exit = e;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
if (!exit) {
|
if (!exit) {
|
||||||
const ref = span.refs[0];
|
const ref = span.refs[0];
|
||||||
// create a known broken node.
|
// create a known broken node.
|
||||||
@ -214,23 +215,23 @@ limitations under the License. -->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
[...fixSpans, ...props.data].forEach((i) => {
|
for (const i of [...fixSpans, ...props.data]) {
|
||||||
i.label = i.endpointName || "no operation name";
|
i.label = i.endpointName || "no operation name";
|
||||||
i.children = [];
|
i.children = [];
|
||||||
if (!segmentGroup[i.segmentId]) {
|
if (segmentGroup[i.segmentId]) {
|
||||||
|
segmentGroup[i.segmentId].push(i);
|
||||||
|
} else {
|
||||||
segmentIdGroup.push(i.segmentId);
|
segmentIdGroup.push(i.segmentId);
|
||||||
segmentGroup[i.segmentId] = [i];
|
segmentGroup[i.segmentId] = [i];
|
||||||
} else {
|
|
||||||
segmentGroup[i.segmentId].push(i);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
fixSpansSize.value = fixSpans.length;
|
fixSpansSize.value = fixSpans.length;
|
||||||
segmentIdGroup.forEach((id: string) => {
|
for (const id of segmentIdGroup) {
|
||||||
const currentSegment = segmentGroup[id].sort((a: Span, b: Span) => b.parentSpanId - a.parentSpanId);
|
const currentSegment = segmentGroup[id].sort((a: Span, b: Span) => b.parentSpanId - a.parentSpanId);
|
||||||
currentSegment.forEach((s: Recordable) => {
|
for (const s of currentSegment) {
|
||||||
const index = currentSegment.findIndex((i: Span) => i.spanId === s.parentSpanId);
|
const index = currentSegment.findIndex((i: Span) => i.spanId === s.parentSpanId);
|
||||||
if (index !== -1) {
|
if (index > -1) {
|
||||||
if (
|
if (
|
||||||
(currentSegment[index].isBroken && currentSegment[index].parentSpanId === -1) ||
|
(currentSegment[index].isBroken && currentSegment[index].parentSpanId === -1) ||
|
||||||
!currentSegment[index].isBroken
|
!currentSegment[index].isBroken
|
||||||
@ -251,37 +252,53 @@ limitations under the License. -->
|
|||||||
s.children.push(...children);
|
s.children.push(...children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
segmentGroup[id] = currentSegment[currentSegment.length - 1];
|
segmentGroup[id] = currentSegment[currentSegment.length - 1];
|
||||||
});
|
}
|
||||||
segmentIdGroup.forEach((id: string) => {
|
for (const id of segmentIdGroup) {
|
||||||
segmentGroup[id].refs.forEach((ref: Recordable) => {
|
for (const ref of segmentGroup[id].refs) {
|
||||||
if (ref.traceId === props.traceId) {
|
if (ref.traceId === props.traceId) {
|
||||||
traverseTree(segmentGroup[ref.parentSegmentId], ref.parentSpanId, ref.parentSegmentId, segmentGroup[id]);
|
traverseTree(segmentGroup[ref.parentSegmentId], ref.parentSpanId, ref.parentSegmentId, segmentGroup[id]);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
for (const i in segmentGroup) {
|
for (const i in segmentGroup) {
|
||||||
if (segmentGroup[i].refs.length === 0) {
|
if (segmentGroup[i].refs.length) {
|
||||||
|
let exit = null;
|
||||||
|
for (const ref of segmentGroup[i].refs) {
|
||||||
|
const e = props.data.find(
|
||||||
|
(i: Recordable) =>
|
||||||
|
ref.traceId === i.traceId && ref.parentSegmentId === i.segmentId && ref.parentSpanId === i.spanId,
|
||||||
|
);
|
||||||
|
if (e) {
|
||||||
|
exit = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exit) {
|
||||||
|
segmentId.value.push(segmentGroup[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
segmentId.value.push(segmentGroup[i]);
|
segmentId.value.push(segmentGroup[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
segmentId.value.forEach((i: Span | Recordable) => {
|
for (const i of segmentId.value) {
|
||||||
collapse(i);
|
collapse(i);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
function collapse(d: Span | Recordable) {
|
function collapse(d: Span | Recordable) {
|
||||||
if (d.children) {
|
if (d.children) {
|
||||||
const item = refSpans.value.find((s: Ref) => s.parentSpanId === d.spanId && s.parentSegmentId === d.segmentId);
|
const item = refSpans.value.find((s: Ref) => s.parentSpanId === d.spanId && s.parentSegmentId === d.segmentId);
|
||||||
let dur = d.endTime - d.startTime;
|
let dur = d.endTime - d.startTime;
|
||||||
d.children.forEach((i: Span) => {
|
for (const i of d.children) {
|
||||||
dur -= i.endTime - i.startTime;
|
dur -= i.endTime - i.startTime;
|
||||||
});
|
}
|
||||||
d.dur = dur < 0 ? 0 : dur;
|
d.dur = dur < 0 ? 0 : dur;
|
||||||
if (item) {
|
if (item) {
|
||||||
d.children = d.children.sort(compare("startTime"));
|
d.children = d.children.sort(compare("startTime"));
|
||||||
}
|
}
|
||||||
d.children.forEach((i: Span) => collapse(i));
|
for (const i of d.children) {
|
||||||
|
collapse(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function compare(p: string) {
|
function compare(p: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user