@kubex/zinc 1.1.96 → 1.1.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/zn.R2EAU6OS.js +1 -0
- package/dist/custom-elements.json +1422 -61
- package/dist/vscode.html-custom-data.json +188 -2
- package/dist/web-types.json +462 -5
- package/dist/zn.d.ts +326 -2
- package/dist/zn.min.js +594 -464
- package/docs/pages/components/button.md +11 -1
- package/docs/pages/components/chart.md +165 -3
- package/docs/pages/components/icon-picker.md +8 -0
- package/docs/pages/components/thumbnail-group.md +216 -0
- package/docs/pages/components/thumbnail.md +349 -0
- package/docs/pages/components/well.md +34 -0
- package/package.json +1 -1
- package/src/components/button/button.component.ts +8 -0
- package/src/components/chart/builders.test.ts +190 -1
- package/src/components/chart/builders.ts +246 -4
- package/src/components/chart/chart.component.ts +33 -1
- package/src/components/chart/chart.test.ts +66 -1
- package/src/components/chart/echarts-loader.ts +10 -0
- package/src/components/collapsible/collapsible.component.ts +49 -4
- package/src/components/collapsible/collapsible.scss +33 -14
- package/src/components/data-table/data-table.component.ts +13 -8
- package/src/components/data-table/data-table.scss +1 -1
- package/src/components/header/header.scss +1 -1
- package/src/components/icon-picker/icon-picker.component.ts +3 -0
- package/src/components/icon-picker/lucide-icons.ts +1756 -0
- package/src/components/page/page.scss +1 -1
- package/src/components/remarkd-editor/remarkd-editor.test.ts +39 -0
- package/src/components/split-pane/split-pane.scss +21 -15
- package/src/components/thumbnail/index.ts +12 -0
- package/src/components/thumbnail/thumbnail.component.ts +460 -0
- package/src/components/thumbnail/thumbnail.scss +360 -0
- package/src/components/thumbnail/thumbnail.test.ts +379 -0
- package/src/components/thumbnail-group/index.ts +12 -0
- package/src/components/thumbnail-group/thumbnail-group.component.ts +268 -0
- package/src/components/thumbnail-group/thumbnail-group.scss +87 -0
- package/src/components/thumbnail-group/thumbnail-group.test.ts +151 -0
- package/src/components/well/well.component.ts +22 -4
- package/src/components/well/well.scss +24 -0
- package/src/components/well/well.test.ts +43 -0
- package/src/zinc.ts +2 -0
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
// src/components/chart/builders.ts
|
|
2
2
|
import type { EChartsOption } from 'echarts';
|
|
3
3
|
|
|
4
|
-
export type ChartType =
|
|
4
|
+
export type ChartType =
|
|
5
|
+
| 'area'
|
|
6
|
+
| 'bar'
|
|
7
|
+
| 'donut'
|
|
8
|
+
| 'funnel'
|
|
9
|
+
| 'gauge'
|
|
10
|
+
| 'heatmap'
|
|
11
|
+
| 'line'
|
|
12
|
+
| 'pie'
|
|
13
|
+
| 'radar'
|
|
14
|
+
| 'sankey'
|
|
15
|
+
| 'scatter'
|
|
16
|
+
| 'sunburst'
|
|
17
|
+
| 'treemap';
|
|
5
18
|
|
|
6
19
|
export interface SeriesItem {
|
|
7
20
|
name: string;
|
|
@@ -15,6 +28,20 @@ export interface SankeyEdge {
|
|
|
15
28
|
value: number;
|
|
16
29
|
}
|
|
17
30
|
|
|
31
|
+
export interface PieDataItem {
|
|
32
|
+
name?: string;
|
|
33
|
+
value: number;
|
|
34
|
+
color?: string;
|
|
35
|
+
itemStyle?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RadarIndicator {
|
|
39
|
+
name: string;
|
|
40
|
+
max?: number;
|
|
41
|
+
min?: number;
|
|
42
|
+
color?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
18
45
|
export interface BuilderProps {
|
|
19
46
|
type: ChartType;
|
|
20
47
|
data: SeriesItem[];
|
|
@@ -30,9 +57,16 @@ export interface BuilderProps {
|
|
|
30
57
|
scale?: boolean | number;
|
|
31
58
|
textColor?: string;
|
|
32
59
|
borderColor?: string;
|
|
60
|
+
innerRadius?: number | string;
|
|
61
|
+
outerRadius?: number | string;
|
|
62
|
+
showLabels?: boolean;
|
|
63
|
+
yCategories?: string[];
|
|
64
|
+
indicators?: RadarIndicator[];
|
|
65
|
+
minValue?: number;
|
|
66
|
+
maxValue?: number;
|
|
33
67
|
}
|
|
34
68
|
|
|
35
|
-
function
|
|
69
|
+
function baseOption(props: BuilderProps, tooltipTrigger: 'axis' | 'item'): EChartsOption {
|
|
36
70
|
const fallback = props.theme === 'dark' ? 'rgb(161, 161, 170)' : 'rgb(113, 113, 122)';
|
|
37
71
|
const textColor = props.textColor ?? fallback;
|
|
38
72
|
const animEnabled = props.enableAnimations !== false && props.enableAnimations !== 0;
|
|
@@ -44,7 +78,7 @@ function commonOption(props: BuilderProps): EChartsOption {
|
|
|
44
78
|
...(props.colors ? { color: props.colors } : {}),
|
|
45
79
|
textStyle: { color: textColor },
|
|
46
80
|
tooltip: {
|
|
47
|
-
trigger:
|
|
81
|
+
trigger: tooltipTrigger,
|
|
48
82
|
appendTo: 'body',
|
|
49
83
|
valueFormatter: props.yAxisAppend
|
|
50
84
|
? (v: number | string) => `${v}${props.yAxisAppend}`
|
|
@@ -56,6 +90,12 @@ function commonOption(props: BuilderProps): EChartsOption {
|
|
|
56
90
|
icon: 'circle',
|
|
57
91
|
textStyle: { color: textColor },
|
|
58
92
|
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function commonOption(props: BuilderProps): EChartsOption {
|
|
97
|
+
return {
|
|
98
|
+
...baseOption(props, 'axis'),
|
|
59
99
|
grid: {
|
|
60
100
|
left: 40,
|
|
61
101
|
right: 20,
|
|
@@ -120,7 +160,7 @@ function normalizeData(data: any[]): any[] {
|
|
|
120
160
|
|
|
121
161
|
function seriesFromProps(
|
|
122
162
|
props: BuilderProps,
|
|
123
|
-
seriesType: 'bar' | 'line',
|
|
163
|
+
seriesType: 'bar' | 'line' | 'scatter',
|
|
124
164
|
extra: (s: SeriesItem) => Record<string, unknown> = () => ({}),
|
|
125
165
|
) {
|
|
126
166
|
return props.data.map((s) => ({
|
|
@@ -170,6 +210,208 @@ export function buildAreaOption(props: BuilderProps): EChartsOption {
|
|
|
170
210
|
};
|
|
171
211
|
}
|
|
172
212
|
|
|
213
|
+
export function buildScatterOption(props: BuilderProps): EChartsOption {
|
|
214
|
+
return {
|
|
215
|
+
...commonOption(props),
|
|
216
|
+
xAxis: buildXAxis({ ...props, xAxisType: props.xAxisType ?? 'numeric' }),
|
|
217
|
+
yAxis: buildYAxis(props),
|
|
218
|
+
series: seriesFromProps(props, 'scatter', () => ({
|
|
219
|
+
symbolSize: props.datapointSize,
|
|
220
|
+
})),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function normalizePieData(data: any[], categories: string[]): PieDataItem[] {
|
|
225
|
+
return data.map((item: number | PieDataItem, index) => {
|
|
226
|
+
if (typeof item === 'number') {
|
|
227
|
+
return { name: categories[index] ?? `${index + 1}`, value: item };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const { color, itemStyle, ...rest } = item;
|
|
231
|
+
return {
|
|
232
|
+
...rest,
|
|
233
|
+
name: item.name ?? categories[index] ?? `${index + 1}`,
|
|
234
|
+
...(color ? { itemStyle: { ...itemStyle, color } } : itemStyle ? { itemStyle } : {}),
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function buildPieOption(props: BuilderProps): EChartsOption {
|
|
240
|
+
const first = props.data[0] ?? { name: '', data: [] };
|
|
241
|
+
const textColor = props.textColor
|
|
242
|
+
?? (props.theme === 'dark' ? 'rgb(161, 161, 170)' : 'rgb(113, 113, 122)');
|
|
243
|
+
const innerRadius = props.type === 'donut' ? (props.innerRadius ?? '50%') : 0;
|
|
244
|
+
const outerRadius = props.outerRadius ?? '70%';
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
...baseOption(props, 'item'),
|
|
248
|
+
series: [{
|
|
249
|
+
type: 'pie',
|
|
250
|
+
name: first.name,
|
|
251
|
+
data: normalizePieData(first.data ?? [], props.categories),
|
|
252
|
+
radius: [innerRadius, outerRadius],
|
|
253
|
+
center: ['50%', '55%'],
|
|
254
|
+
avoidLabelOverlap: true,
|
|
255
|
+
label: {
|
|
256
|
+
show: props.showLabels ?? false,
|
|
257
|
+
color: textColor,
|
|
258
|
+
},
|
|
259
|
+
labelLine: {
|
|
260
|
+
show: props.showLabels ?? false,
|
|
261
|
+
...(props.borderColor ? { lineStyle: { color: props.borderColor } } : {}),
|
|
262
|
+
},
|
|
263
|
+
emphasis: {
|
|
264
|
+
label: { show: props.showLabels ?? false },
|
|
265
|
+
},
|
|
266
|
+
}],
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function buildRadarOption(props: BuilderProps): EChartsOption {
|
|
271
|
+
const indicators = props.indicators?.length
|
|
272
|
+
? props.indicators
|
|
273
|
+
: props.categories.map((name) => ({ name }));
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
...baseOption(props, 'item'),
|
|
277
|
+
radar: { indicator: indicators },
|
|
278
|
+
series: [{
|
|
279
|
+
type: 'radar',
|
|
280
|
+
data: props.data.map((series) => ({
|
|
281
|
+
name: series.name,
|
|
282
|
+
value: normalizeData(series.data),
|
|
283
|
+
...(series.color ? {
|
|
284
|
+
itemStyle: { color: series.color },
|
|
285
|
+
lineStyle: { color: series.color },
|
|
286
|
+
} : {}),
|
|
287
|
+
})),
|
|
288
|
+
}],
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function buildGaugeOption(props: BuilderProps): EChartsOption {
|
|
293
|
+
const first = props.data[0] ?? { name: '', data: [] };
|
|
294
|
+
const [firstValue] = first.data as unknown[];
|
|
295
|
+
const data = typeof firstValue === 'number'
|
|
296
|
+
? [{ name: first.name, value: firstValue }]
|
|
297
|
+
: normalizePieData(first.data ?? [], props.categories);
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
...baseOption(props, 'item'),
|
|
301
|
+
series: [{
|
|
302
|
+
type: 'gauge',
|
|
303
|
+
name: first.name,
|
|
304
|
+
min: props.minValue ?? 0,
|
|
305
|
+
max: props.maxValue ?? 100,
|
|
306
|
+
data,
|
|
307
|
+
progress: { show: true },
|
|
308
|
+
detail: {
|
|
309
|
+
valueAnimation: props.enableAnimations !== false && props.enableAnimations !== 0,
|
|
310
|
+
formatter: props.yAxisAppend
|
|
311
|
+
? (value: number) => `${value}${props.yAxisAppend}`
|
|
312
|
+
: '{value}',
|
|
313
|
+
},
|
|
314
|
+
}],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function buildFunnelOption(props: BuilderProps): EChartsOption {
|
|
319
|
+
const first = props.data[0] ?? { name: '', data: [] };
|
|
320
|
+
return {
|
|
321
|
+
...baseOption(props, 'item'),
|
|
322
|
+
series: [{
|
|
323
|
+
type: 'funnel',
|
|
324
|
+
name: first.name,
|
|
325
|
+
min: props.minValue,
|
|
326
|
+
max: props.maxValue,
|
|
327
|
+
data: normalizePieData(first.data ?? [], props.categories),
|
|
328
|
+
label: { show: props.showLabels ?? true },
|
|
329
|
+
emphasis: { label: { fontWeight: 'bold' } },
|
|
330
|
+
}],
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function isHeatmapPoint(item: unknown): item is [unknown, unknown, number] {
|
|
335
|
+
if (!Array.isArray(item)) return false;
|
|
336
|
+
const point = item as unknown[];
|
|
337
|
+
return typeof point[2] === 'number';
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function heatmapExtent(data: unknown[], operation: 'max' | 'min', fallback: number): number {
|
|
341
|
+
const values = data
|
|
342
|
+
.filter(isHeatmapPoint)
|
|
343
|
+
.map((item) => item[2]);
|
|
344
|
+
return values.length ? Math[operation](...values) : fallback;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function buildHeatmapOption(props: BuilderProps): EChartsOption {
|
|
348
|
+
const first = props.data[0] ?? { name: '', data: [] };
|
|
349
|
+
const data = first.data ?? [];
|
|
350
|
+
return {
|
|
351
|
+
...baseOption(props, 'item'),
|
|
352
|
+
grid: { left: 60, right: 20, top: 40, bottom: 70 },
|
|
353
|
+
xAxis: {
|
|
354
|
+
type: 'category',
|
|
355
|
+
data: props.categories,
|
|
356
|
+
splitArea: { show: true },
|
|
357
|
+
},
|
|
358
|
+
yAxis: {
|
|
359
|
+
type: 'category',
|
|
360
|
+
data: props.yCategories ?? [],
|
|
361
|
+
splitArea: { show: true },
|
|
362
|
+
},
|
|
363
|
+
visualMap: {
|
|
364
|
+
min: props.minValue ?? heatmapExtent(data, 'min', 0),
|
|
365
|
+
max: props.maxValue ?? heatmapExtent(data, 'max', 100),
|
|
366
|
+
calculable: true,
|
|
367
|
+
orient: 'horizontal',
|
|
368
|
+
left: 'center',
|
|
369
|
+
bottom: 0,
|
|
370
|
+
},
|
|
371
|
+
series: [{
|
|
372
|
+
type: 'heatmap',
|
|
373
|
+
name: first.name,
|
|
374
|
+
data,
|
|
375
|
+
label: { show: props.showLabels ?? false },
|
|
376
|
+
emphasis: {
|
|
377
|
+
itemStyle: {
|
|
378
|
+
shadowBlur: 10,
|
|
379
|
+
shadowColor: 'rgba(0, 0, 0, 0.35)',
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
}],
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function buildTreemapOption(props: BuilderProps): EChartsOption {
|
|
387
|
+
const first = props.data[0] ?? { name: '', data: [] };
|
|
388
|
+
return {
|
|
389
|
+
...baseOption(props, 'item'),
|
|
390
|
+
series: [{
|
|
391
|
+
type: 'treemap',
|
|
392
|
+
name: first.name,
|
|
393
|
+
data: first.data ?? [],
|
|
394
|
+
label: { show: props.showLabels ?? true },
|
|
395
|
+
upperLabel: { show: props.showLabels ?? true },
|
|
396
|
+
}],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function buildSunburstOption(props: BuilderProps): EChartsOption {
|
|
401
|
+
const first = props.data[0] ?? { name: '', data: [] };
|
|
402
|
+
return {
|
|
403
|
+
...baseOption(props, 'item'),
|
|
404
|
+
series: [{
|
|
405
|
+
type: 'sunburst',
|
|
406
|
+
name: first.name,
|
|
407
|
+
data: first.data ?? [],
|
|
408
|
+
radius: [props.innerRadius ?? 0, props.outerRadius ?? '90%'],
|
|
409
|
+
label: { show: props.showLabels ?? true },
|
|
410
|
+
emphasis: { focus: 'ancestor' },
|
|
411
|
+
}],
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
173
415
|
function deriveNodes(edges: SankeyEdge[]): { name: string }[] {
|
|
174
416
|
const seen = new Set<string>();
|
|
175
417
|
const nodes: { name: string }[] = [];
|
|
@@ -2,9 +2,18 @@ import {
|
|
|
2
2
|
buildAreaOption,
|
|
3
3
|
buildBarOption,
|
|
4
4
|
type BuilderProps,
|
|
5
|
+
buildFunnelOption,
|
|
6
|
+
buildGaugeOption,
|
|
7
|
+
buildHeatmapOption,
|
|
5
8
|
buildLineOption,
|
|
9
|
+
buildPieOption,
|
|
10
|
+
buildRadarOption,
|
|
6
11
|
buildSankeyOption,
|
|
12
|
+
buildScatterOption,
|
|
13
|
+
buildSunburstOption,
|
|
14
|
+
buildTreemapOption,
|
|
7
15
|
type ChartType,
|
|
16
|
+
type RadarIndicator,
|
|
8
17
|
type SeriesItem,
|
|
9
18
|
} from './builders';
|
|
10
19
|
import { type CSSResultGroup, html, type PropertyValues, unsafeCSS } from 'lit';
|
|
@@ -29,6 +38,8 @@ export default class ZnChart extends ZincElement {
|
|
|
29
38
|
@property() type: ChartType = 'bar';
|
|
30
39
|
@property({ type: Array }) data: SeriesItem[] = [];
|
|
31
40
|
@property({ type: Array }) categories: string[] = [];
|
|
41
|
+
@property({ attribute: 'y-categories', type: Array }) yCategories: string[] = [];
|
|
42
|
+
@property({ type: Array }) indicators: RadarIndicator[] = [];
|
|
32
43
|
|
|
33
44
|
@property({ attribute: 'xaxis' }) xAxis: 'datetime' | 'category' | 'numeric';
|
|
34
45
|
@property({ type: Number, attribute: 'd-size' }) datapointSize: number = 1;
|
|
@@ -55,6 +66,11 @@ export default class ZnChart extends ZincElement {
|
|
|
55
66
|
@property({ type: Array }) colors?: string[];
|
|
56
67
|
@property({ attribute: 'sync-group' }) syncGroup?: string;
|
|
57
68
|
@property({ type: Boolean }) smooth = false;
|
|
69
|
+
@property({ attribute: 'inner-radius' }) innerRadius?: number | string;
|
|
70
|
+
@property({ attribute: 'outer-radius' }) outerRadius?: number | string;
|
|
71
|
+
@property({ attribute: 'show-labels', type: Boolean }) showLabels?: boolean;
|
|
72
|
+
@property({ attribute: 'min-value', type: Number }) minValue?: number;
|
|
73
|
+
@property({ attribute: 'max-value', type: Number }) maxValue?: number;
|
|
58
74
|
@property({
|
|
59
75
|
converter: {
|
|
60
76
|
fromAttribute: (value: string | null) => {
|
|
@@ -122,12 +138,28 @@ export default class ZnChart extends ZincElement {
|
|
|
122
138
|
scale: this.scale,
|
|
123
139
|
textColor: this.getTextColor(),
|
|
124
140
|
borderColor: this.getBorderColor(),
|
|
141
|
+
innerRadius: this.innerRadius,
|
|
142
|
+
outerRadius: this.outerRadius,
|
|
143
|
+
showLabels: this.showLabels,
|
|
144
|
+
yCategories: Array.isArray(this.yCategories) ? this.yCategories : [],
|
|
145
|
+
indicators: Array.isArray(this.indicators) ? this.indicators : [],
|
|
146
|
+
minValue: this.minValue,
|
|
147
|
+
maxValue: this.maxValue,
|
|
125
148
|
};
|
|
126
149
|
switch (this.type) {
|
|
127
150
|
case 'bar': return buildBarOption(props);
|
|
128
151
|
case 'line': return buildLineOption(props);
|
|
129
152
|
case 'area': return buildAreaOption(props);
|
|
153
|
+
case 'funnel': return buildFunnelOption(props);
|
|
154
|
+
case 'gauge': return buildGaugeOption(props);
|
|
155
|
+
case 'heatmap': return buildHeatmapOption(props);
|
|
156
|
+
case 'donut':
|
|
157
|
+
case 'pie': return buildPieOption(props);
|
|
158
|
+
case 'radar': return buildRadarOption(props);
|
|
130
159
|
case 'sankey': return buildSankeyOption(props);
|
|
160
|
+
case 'scatter': return buildScatterOption(props);
|
|
161
|
+
case 'sunburst': return buildSunburstOption(props);
|
|
162
|
+
case 'treemap': return buildTreemapOption(props);
|
|
131
163
|
default: return buildBarOption(props);
|
|
132
164
|
}
|
|
133
165
|
}
|
|
@@ -207,4 +239,4 @@ export default class ZnChart extends ZincElement {
|
|
|
207
239
|
protected render(): unknown {
|
|
208
240
|
return html`<div class="chart"><div id="chart"></div></div>`;
|
|
209
241
|
}
|
|
210
|
-
}
|
|
242
|
+
}
|
|
@@ -41,6 +41,71 @@ describe('<zn-chart>', () => {
|
|
|
41
41
|
expect(canvas).to.exist;
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
it('renders pie and donut charts', async () => {
|
|
45
|
+
for (const type of ['pie', 'donut']) {
|
|
46
|
+
const el: any = await fixture(html`
|
|
47
|
+
<zn-chart
|
|
48
|
+
type=${type}
|
|
49
|
+
.data=${[{ name: 'Insights', data: [8, 1] }]}
|
|
50
|
+
.categories=${['Configuration suggestion', 'Insecure configuration']}
|
|
51
|
+
></zn-chart>
|
|
52
|
+
`);
|
|
53
|
+
await el.updateComplete;
|
|
54
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
55
|
+
const canvas = el.shadowRoot.querySelector('canvas');
|
|
56
|
+
expect(canvas, `${type} chart canvas`).to.exist;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('renders the additional native chart types', async () => {
|
|
61
|
+
const cases = [
|
|
62
|
+
{ type: 'scatter', data: [{ name: 'Points', data: [[1, 2], [2, 4]] }] },
|
|
63
|
+
{
|
|
64
|
+
type: 'radar',
|
|
65
|
+
data: [{ name: 'Current', data: [80, 65, 90] }],
|
|
66
|
+
categories: ['Security', 'Performance', 'Reliability'],
|
|
67
|
+
},
|
|
68
|
+
{ type: 'gauge', data: [{ name: 'Health', data: [82] }] },
|
|
69
|
+
{
|
|
70
|
+
type: 'funnel',
|
|
71
|
+
data: [{ name: 'Conversion', data: [1000, 320, 85] }],
|
|
72
|
+
categories: ['Visitors', 'Trials', 'Customers'],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'heatmap',
|
|
76
|
+
data: [{ name: 'Requests', data: [[0, 0, 5], [1, 0, 12]] }],
|
|
77
|
+
categories: ['Mon', 'Tue'],
|
|
78
|
+
yCategories: ['API'],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'treemap',
|
|
82
|
+
data: [{ name: 'Insights', data: [{ name: 'Configuration', value: 8 }] }],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'sunburst',
|
|
86
|
+
data: [{
|
|
87
|
+
name: 'Insights',
|
|
88
|
+
data: [{ name: 'Security', children: [{ name: 'Configuration', value: 8 }] }],
|
|
89
|
+
}],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
for (const chartCase of cases) {
|
|
94
|
+
const el: any = await fixture(html`
|
|
95
|
+
<zn-chart
|
|
96
|
+
type=${chartCase.type}
|
|
97
|
+
.data=${chartCase.data}
|
|
98
|
+
.categories=${chartCase.categories ?? []}
|
|
99
|
+
.yCategories=${chartCase.yCategories ?? []}
|
|
100
|
+
></zn-chart>
|
|
101
|
+
`);
|
|
102
|
+
await el.updateComplete;
|
|
103
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
104
|
+
const canvas = el.shadowRoot.querySelector('canvas');
|
|
105
|
+
expect(canvas, `${chartCase.type} chart canvas`).to.exist;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
44
109
|
it('joins a sync-group when the attribute is set', async () => {
|
|
45
110
|
const a: any = await fixture(html`
|
|
46
111
|
<zn-chart sync-group="g1" type="bar"
|
|
@@ -51,4 +116,4 @@ describe('<zn-chart>', () => {
|
|
|
51
116
|
await new Promise((r) => setTimeout(r, 50));
|
|
52
117
|
expect(a.chart?.group).to.equal('g1');
|
|
53
118
|
});
|
|
54
|
-
});
|
|
119
|
+
});
|
|
@@ -15,13 +15,23 @@ export function loadECharts(): Promise<EChartsModule> {
|
|
|
15
15
|
]);
|
|
16
16
|
core.use([
|
|
17
17
|
charts.BarChart,
|
|
18
|
+
charts.FunnelChart,
|
|
19
|
+
charts.GaugeChart,
|
|
20
|
+
charts.HeatmapChart,
|
|
18
21
|
charts.LineChart,
|
|
22
|
+
charts.PieChart,
|
|
23
|
+
charts.RadarChart,
|
|
19
24
|
charts.SankeyChart,
|
|
25
|
+
charts.ScatterChart,
|
|
26
|
+
charts.SunburstChart,
|
|
27
|
+
charts.TreemapChart,
|
|
20
28
|
components.GridComponent,
|
|
21
29
|
components.TooltipComponent,
|
|
22
30
|
components.LegendComponent,
|
|
23
31
|
components.TitleComponent,
|
|
24
32
|
components.DataZoomComponent,
|
|
33
|
+
components.RadarComponent,
|
|
34
|
+
components.VisualMapComponent,
|
|
25
35
|
renderers.CanvasRenderer,
|
|
26
36
|
]);
|
|
27
37
|
return core;
|
|
@@ -52,6 +52,10 @@ export default class ZnCollapsible extends ZincElement {
|
|
|
52
52
|
|
|
53
53
|
@state() numberOfItems: number = 0;
|
|
54
54
|
|
|
55
|
+
@state() private animating: boolean = false;
|
|
56
|
+
|
|
57
|
+
private animatingTimer?: ReturnType<typeof setTimeout>;
|
|
58
|
+
|
|
55
59
|
protected _store: Store;
|
|
56
60
|
|
|
57
61
|
private readonly hasSlotController = new HasSlotController(this, '[default]', 'header', 'caption', 'label', 'description');
|
|
@@ -94,6 +98,11 @@ export default class ZnCollapsible extends ZincElement {
|
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
disconnectedCallback() {
|
|
102
|
+
super.disconnectedCallback();
|
|
103
|
+
clearTimeout(this.animatingTimer);
|
|
104
|
+
}
|
|
105
|
+
|
|
97
106
|
// this is for handling global toggles
|
|
98
107
|
public handleCaptionToggle = (e: ZnInputEvent) => {
|
|
99
108
|
const toggle = e.target as ZnToggle;
|
|
@@ -104,8 +113,38 @@ export default class ZnCollapsible extends ZincElement {
|
|
|
104
113
|
|
|
105
114
|
protected updated(changedProperties: PropertyValues) {
|
|
106
115
|
super.updated(changedProperties);
|
|
107
|
-
if (changedProperties.has('expanded')
|
|
108
|
-
|
|
116
|
+
if (changedProperties.has('expanded')) {
|
|
117
|
+
if (changedProperties.get('expanded') !== undefined) {
|
|
118
|
+
this.startAnimating();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.storeKey) {
|
|
122
|
+
this._store.set(this.storeKey, this.expanded.toString());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Content is only clipped while the height transition runs, so expanded
|
|
128
|
+
// content can overflow the host. The timer covers a transitionend that never
|
|
129
|
+
// arrives (reduced motion, hidden host).
|
|
130
|
+
private startAnimating() {
|
|
131
|
+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.animating = true;
|
|
136
|
+
clearTimeout(this.animatingTimer);
|
|
137
|
+
this.animatingTimer = setTimeout(() => (this.animating = false), 600);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private stopAnimating() {
|
|
141
|
+
clearTimeout(this.animatingTimer);
|
|
142
|
+
this.animating = false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private handleTransitionEnd = (e: TransitionEvent) => {
|
|
146
|
+
if (e.target === e.currentTarget && e.propertyName === 'grid-template-rows') {
|
|
147
|
+
this.stopAnimating();
|
|
109
148
|
}
|
|
110
149
|
}
|
|
111
150
|
|
|
@@ -160,8 +199,14 @@ export default class ZnCollapsible extends ZincElement {
|
|
|
160
199
|
<zn-icon library="material-outlined" src="expand_more" class="expand"></zn-icon>` : ''}
|
|
161
200
|
</div>
|
|
162
201
|
</slot>
|
|
163
|
-
<div class=${classMap({
|
|
164
|
-
|
|
202
|
+
<div class=${classMap({
|
|
203
|
+
'content': true,
|
|
204
|
+
'content--flush': this.flush,
|
|
205
|
+
'content--animating': this.animating,
|
|
206
|
+
})} part="content" @transitionend="${this.handleTransitionEnd}">
|
|
207
|
+
<div class="content__inner">
|
|
208
|
+
<slot @slotchange="${this.requestUpdate}"></slot>
|
|
209
|
+
</div>
|
|
165
210
|
</div>
|
|
166
211
|
</div>`;
|
|
167
212
|
}
|
|
@@ -76,29 +76,48 @@
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// Height animates via grid-template-rows 0fr -> 1fr; the inline padding is
|
|
80
|
+
// constant so the content never reflows (and re-wraps) mid-transition.
|
|
81
|
+
.content {
|
|
82
|
+
display: grid;
|
|
83
|
+
grid-template-rows: 0fr;
|
|
84
|
+
opacity: 0;
|
|
85
|
+
transition: grid-template-rows var(--zn-transition-medium) cubic-bezier(0.32, 0.72, 0, 1),
|
|
86
|
+
opacity var(--zn-transition-fast) ease-out;
|
|
87
|
+
|
|
88
|
+
&__inner {
|
|
89
|
+
min-height: 0;
|
|
90
|
+
padding: 0 var(--zn-spacing-medium) var(--zn-spacing-medium);
|
|
91
|
+
transform: translateY(-4px);
|
|
92
|
+
transition: transform var(--zn-transition-medium) cubic-bezier(0.32, 0.72, 0, 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&--flush &__inner {
|
|
96
|
+
padding: 0;
|
|
97
|
+
}
|
|
81
98
|
}
|
|
82
99
|
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
// Clipped while collapsed or mid-transition only, so expanded content can still
|
|
101
|
+
// overflow the host (dropdowns, tooltips).
|
|
102
|
+
:host(:not([expanded])) .content,
|
|
103
|
+
.content--animating {
|
|
104
|
+
overflow: hidden;
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
:host([expanded]) .content {
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
grid-template-rows: 1fr;
|
|
109
|
+
opacity: 1;
|
|
90
110
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
padding: 0;
|
|
111
|
+
.content__inner {
|
|
112
|
+
transform: none;
|
|
94
113
|
}
|
|
95
114
|
}
|
|
96
115
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
@media (prefers-reduced-motion: reduce) {
|
|
117
|
+
.content,
|
|
118
|
+
.content__inner {
|
|
119
|
+
transition-duration: 0s;
|
|
120
|
+
}
|
|
102
121
|
}
|
|
103
122
|
|
|
104
123
|
.expand {
|
|
@@ -821,12 +821,15 @@ export default class ZnDataTable extends ZincElement {
|
|
|
821
821
|
<div class="table__footer__pagination-buttons">
|
|
822
822
|
<zn-button @click="${this.page !== 1 ? this.goToFirstPage : undefined}"
|
|
823
823
|
?disabled="${this.page === 1}"
|
|
824
|
-
icon="
|
|
825
|
-
|
|
824
|
+
icon="chevrons-left@lu"
|
|
825
|
+
icon-button="small"
|
|
826
|
+
panel-bg>
|
|
826
827
|
</zn-button>
|
|
827
828
|
<zn-button @click="${this.page !== 1 ? this.goToPreviousPage : undefined}"
|
|
828
829
|
?disabled="${this.page === 1}"
|
|
829
|
-
|
|
830
|
+
icon="chevron-left@lu"
|
|
831
|
+
icon-button="small"
|
|
832
|
+
panel-bg>
|
|
830
833
|
</zn-button>
|
|
831
834
|
${this.getPageRange().map((p) => p === 'ellipsis'
|
|
832
835
|
? html`<span class="table__footer__pagination-ellipsis">…</span>`
|
|
@@ -838,13 +841,15 @@ export default class ZnDataTable extends ZincElement {
|
|
|
838
841
|
)}
|
|
839
842
|
<zn-button @click="${this.page !== this.totalPages ? this.goToNextPage : undefined}"
|
|
840
843
|
?disabled="${this.page === this.totalPages}"
|
|
841
|
-
|
|
844
|
+
icon="chevron-right@lu"
|
|
845
|
+
icon-button="small"
|
|
846
|
+
panel-bg>
|
|
842
847
|
</zn-button>
|
|
843
848
|
<zn-button @click="${this.page !== this.totalPages ? this.goToLastPage : undefined}"
|
|
844
849
|
?disabled="${this.page === this.totalPages}"
|
|
845
|
-
icon="
|
|
846
|
-
icon-
|
|
847
|
-
panel-bg>
|
|
850
|
+
icon="chevrons-right@lu"
|
|
851
|
+
icon-button="small"
|
|
852
|
+
panel-bg>
|
|
848
853
|
</zn-button>
|
|
849
854
|
</div>`;
|
|
850
855
|
}
|
|
@@ -1039,7 +1044,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1039
1044
|
if (header?.cellTemplate !== undefined) {
|
|
1040
1045
|
data = {...header.cellTemplate, ...data}
|
|
1041
1046
|
}
|
|
1042
|
-
|
|
1047
|
+
|
|
1043
1048
|
let content: TemplateResult | ZincElement = html`${data.text}`;
|
|
1044
1049
|
|
|
1045
1050
|
if (data.style || data.color) {
|