@kubex/zinc 1.1.97 → 1.1.99
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/AGENTS.md +5 -0
- package/CLAUDE.md +5 -0
- package/dist/chunks/zn.R2EAU6OS.js +1 -0
- package/dist/custom-elements.json +14747 -12528
- package/dist/vscode.html-custom-data.json +386 -61
- package/dist/web-types.json +1232 -481
- package/dist/zn.d.ts +529 -11
- package/dist/zn.min.js +649 -478
- package/docs/pages/components/background.md +143 -0
- package/docs/pages/components/button.md +11 -1
- package/docs/pages/components/chart.md +165 -3
- package/docs/pages/components/checkbox-group.md +13 -1
- package/docs/pages/components/checkbox.md +59 -1
- package/docs/pages/components/icon-picker.md +8 -0
- package/docs/pages/components/radio-group.md +12 -0
- package/docs/pages/components/radio.md +188 -0
- package/docs/pages/components/remarkd-editor.md +22 -0
- package/docs/pages/components/scroll-container.md +14 -15
- 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/background/background.component.ts +111 -0
- package/src/components/background/background.scss +236 -0
- package/src/components/background/background.test.ts +120 -0
- package/src/components/background/index.ts +12 -0
- 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/checkbox/checkbox.component.ts +62 -6
- package/src/components/checkbox/checkbox.scss +1 -0
- package/src/components/checkbox/checkbox.test.ts +56 -0
- package/src/components/checkbox-group/checkbox-group.component.ts +15 -0
- package/src/components/checkbox-group/checkbox-group.scss +23 -5
- package/src/components/checkbox-group/checkbox-group.test.ts +15 -0
- package/src/components/collapsible/collapsible.component.ts +49 -4
- package/src/components/collapsible/collapsible.scss +33 -14
- package/src/components/confirm/confirm.component.ts +3 -3
- package/src/components/confirm/confirm.test.ts +15 -0
- package/src/components/dialog/dialog.component.ts +3 -2
- package/src/components/editor/editor.component.ts +7 -6
- package/src/components/header/header.scss +2 -2
- package/src/components/icon-picker/icon-picker.component.ts +3 -0
- package/src/components/icon-picker/lucide-icons.ts +1756 -0
- package/src/components/navbar/navbar.scss +11 -0
- package/src/components/page/page.scss +1 -1
- package/src/components/radio/radio.component.ts +63 -7
- package/src/components/radio/radio.scss +1 -0
- package/src/components/radio/radio.test.ts +56 -0
- package/src/components/radio-group/radio-group.component.ts +16 -3
- package/src/components/radio-group/radio-group.scss +23 -4
- package/src/components/radio-group/radio-group.test.ts +15 -0
- package/src/components/remarkd-editor/remarkd-editor.component.ts +118 -5
- package/src/components/remarkd-editor/remarkd-editor.scss +8 -1
- package/src/components/remarkd-editor/remarkd-editor.test.ts +135 -0
- package/src/components/scroll-container/scroll-container.component.ts +15 -1
- package/src/components/scroll-container/scroll-container.scss +4 -2
- package/src/components/scroll-container/scroll-container.test.ts +13 -0
- package/src/components/slideout/slideout.component.ts +3 -2
- 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 +86 -0
- package/src/components/thumbnail-group/thumbnail-group.test.ts +151 -0
- package/src/components/tile/tile.scss +4 -2
- package/src/components/translation-group/translation-group.test.ts +22 -0
- package/src/components/translations/translations.component.ts +18 -3
- 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/form-control.scss +3 -1
- package/src/internal/conditional.ts +2 -2
- package/src/internal/form.ts +2 -1
- package/src/internal/selection-card.ts +19 -0
- package/src/selection-card.scss +187 -0
- package/src/utilities/query.test.ts +18 -1
- package/src/utilities/query.ts +8 -0
- package/src/zinc.ts +3 -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;
|
|
@@ -9,6 +9,7 @@ import {property, query, state} from 'lit/decorators.js';
|
|
|
9
9
|
import {watch} from '../../internal/watch';
|
|
10
10
|
import ZincElement from '../../internal/zinc-element';
|
|
11
11
|
import ZnIcon from "../icon";
|
|
12
|
+
import type {SelectionCardControlPosition, SelectionCardImagePosition} from '../../internal/selection-card';
|
|
12
13
|
import type {ZincFormControl} from '../../internal/zinc-element';
|
|
13
14
|
|
|
14
15
|
import styles from './checkbox.scss';
|
|
@@ -24,6 +25,7 @@ type ColorOption = 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'suc
|
|
|
24
25
|
* @dependency zn-icon
|
|
25
26
|
*
|
|
26
27
|
* @slot - The checkbox's label.
|
|
28
|
+
* @slot image - Replaces the built-in image used by contained checkboxes.
|
|
27
29
|
* @slot description - A description of the checkbox's label. Serves as help text for a checkbox item. Alternatively, you can use the `description` attribute.
|
|
28
30
|
* @slot selected-content - Use to nest rich content (like an input) inside a selected checkbox item. Use only with the contained style.
|
|
29
31
|
*
|
|
@@ -40,9 +42,22 @@ type ColorOption = 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'suc
|
|
|
40
42
|
* @csspart checked-icon - The checked icon, an `<zn-icon>` element.
|
|
41
43
|
* @csspart unchecked-icon - The unchecked icon, an `<zn-icon>` element.
|
|
42
44
|
* @csspart indeterminate-icon - The indeterminate icon, an `<zn-icon>` element.
|
|
45
|
+
* @csspart image-container - The wrapper around the built-in image or image slot.
|
|
46
|
+
* @csspart image - The built-in image.
|
|
47
|
+
* @csspart card-title - The title inside a selection card.
|
|
43
48
|
* @csspart label - The container that wraps the checkbox's label.
|
|
44
49
|
* @csspart description - The container that wraps the checkbox's description.
|
|
45
50
|
* @csspart selected-content - The container that wraps optional content that appears when a checkbox is checked.
|
|
51
|
+
*
|
|
52
|
+
* @cssproperty --zn-selection-card-image-width - Width of the built-in card image.
|
|
53
|
+
* @cssproperty --zn-selection-card-image-height - Height of the built-in card image.
|
|
54
|
+
* @cssproperty --zn-selection-card-min-height - Minimum height of a contained checkbox with an image.
|
|
55
|
+
* @cssproperty --zn-selection-card-content-min-width - Width the card text keeps before it wraps below the image.
|
|
56
|
+
* @cssproperty --zn-selection-card-title-font-size - Font size of a selection card title.
|
|
57
|
+
* @cssproperty --zn-selection-card-padding - Equal inset around the contents of a selection card.
|
|
58
|
+
* @cssproperty --zn-selection-card-gap - Space between the image, title, and indicator gutter.
|
|
59
|
+
* @cssproperty --zn-selection-card-control-offset - Distance between a positioned control and the card edge.
|
|
60
|
+
* @cssproperty --zn-selection-card-border-radius - Corner radius of a contained container.
|
|
46
61
|
*/
|
|
47
62
|
export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
48
63
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
@@ -53,7 +68,7 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
53
68
|
defaultValue: (control: ZnCheckbox) => control.defaultChecked,
|
|
54
69
|
setValue: (control: ZnCheckbox, checked: boolean) => (control.checked = checked)
|
|
55
70
|
});
|
|
56
|
-
private readonly hasSlotController = new HasSlotController(this, 'description');
|
|
71
|
+
private readonly hasSlotController = new HasSlotController(this, '[default]', 'description', 'image');
|
|
57
72
|
|
|
58
73
|
@query('input[type="checkbox"]') input: HTMLInputElement;
|
|
59
74
|
|
|
@@ -67,6 +82,9 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
67
82
|
/** The current value of the checkbox, submitted as a name/value pair with form data. */
|
|
68
83
|
@property() value: string;
|
|
69
84
|
|
|
85
|
+
/** Title rendered inside the card. The default slot takes precedence when provided. */
|
|
86
|
+
@property({attribute: 'card-title'}) cardTitle = '';
|
|
87
|
+
|
|
70
88
|
/** The unchecked value of the checkbox, submitted as a name/value pair with form data. */
|
|
71
89
|
@property({attribute: 'unchecked-value', reflect: true}) uncheckedValue: string;
|
|
72
90
|
|
|
@@ -88,6 +106,24 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
88
106
|
/** Draws a container around the checkbox. */
|
|
89
107
|
@property({type: Boolean, reflect: true}) contained = false;
|
|
90
108
|
|
|
109
|
+
/** Squares off the corners of the container drawn by `contained`, which is rounded by default. */
|
|
110
|
+
@property({type: Boolean, reflect: true}) square = false;
|
|
111
|
+
|
|
112
|
+
/** URL for the image shown in a contained checkbox. */
|
|
113
|
+
@property() src = '';
|
|
114
|
+
|
|
115
|
+
/** Accessible text for the built-in image. Leave empty when the image is decorative. */
|
|
116
|
+
@property({attribute: 'image-alt'}) imageAlt = '';
|
|
117
|
+
|
|
118
|
+
/** Places the image above, beside, or below the checkbox's text. Requires `contained`. */
|
|
119
|
+
@property({attribute: 'image-position', reflect: true}) imagePosition: SelectionCardImagePosition = 'left';
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Places the checkbox indicator within a contained card. Use `none` to hide the indicator so the card itself
|
|
123
|
+
* shows the selected state. Requires `contained`.
|
|
124
|
+
*/
|
|
125
|
+
@property({attribute: 'control-position', reflect: true}) controlPosition: SelectionCardControlPosition = 'start';
|
|
126
|
+
|
|
91
127
|
/** Removes a container around the checkbox. */
|
|
92
128
|
@property({type: Boolean, reflect: true}) borderless = false;
|
|
93
129
|
|
|
@@ -244,6 +280,9 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
244
280
|
render() {
|
|
245
281
|
const hasDescriptionSlot = this.hasSlotController.test('description');
|
|
246
282
|
const hasDescription = this.description ? true : hasDescriptionSlot;
|
|
283
|
+
const hasImage = this.contained && (Boolean(this.src) || this.hasSlotController.test('image'));
|
|
284
|
+
const hasDefaultSlot = this.hasSlotController.test('[default]');
|
|
285
|
+
const isCard = this.contained && (hasImage || Boolean(this.cardTitle) || this.controlPosition !== 'start');
|
|
247
286
|
const hasLabelSlot = this.hasSlotController.test('label');
|
|
248
287
|
const hasLabelTooltip = this.hasSlotController.test('label-tooltip');
|
|
249
288
|
const hasLabel = this.label || hasLabelSlot;
|
|
@@ -309,7 +348,11 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
309
348
|
'checkbox--warning': effectiveColor === 'warning',
|
|
310
349
|
'checkbox--transparent': effectiveColor === 'transparent',
|
|
311
350
|
'checkbox--has-description': hasDescription,
|
|
312
|
-
'checkbox--has-selected-content': this.hasSlotController.test('selected-content')
|
|
351
|
+
'checkbox--has-selected-content': this.hasSlotController.test('selected-content'),
|
|
352
|
+
'selection-card': isCard,
|
|
353
|
+
'selection-card--has-image': hasImage,
|
|
354
|
+
[`selection-card--image-${this.imagePosition}`]: hasImage,
|
|
355
|
+
[`selection-card--control-${this.controlPosition}`]: isCard && this.controlPosition !== 'start'
|
|
313
356
|
})}>
|
|
314
357
|
|
|
315
358
|
<input
|
|
@@ -323,7 +366,7 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
323
366
|
.disabled=${this.disabled}
|
|
324
367
|
.required=${this.required}
|
|
325
368
|
aria-checked=${this.checked ? 'true' : 'false'}
|
|
326
|
-
aria-describedby=${hasDescription ? '' : '
|
|
369
|
+
aria-describedby=${hasDescription ? 'description' : ''}
|
|
327
370
|
@click=${this.handleClick}
|
|
328
371
|
@input=${this.handleInput}
|
|
329
372
|
@invalid=${this.handleInvalid}
|
|
@@ -334,7 +377,7 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
334
377
|
part="control${this.checked ? ' control--checked' : ''}${this.indeterminate
|
|
335
378
|
? ' control--indeterminate'
|
|
336
379
|
: ''}"
|
|
337
|
-
class="checkbox__control">
|
|
380
|
+
class="checkbox__control selection-card__control">
|
|
338
381
|
|
|
339
382
|
${this.checked && !this.indeterminate
|
|
340
383
|
? this.checkedIcon
|
|
@@ -367,8 +410,21 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
|
|
|
367
410
|
: ''}
|
|
368
411
|
</span>
|
|
369
412
|
|
|
370
|
-
|
|
371
|
-
|
|
413
|
+
${hasImage
|
|
414
|
+
? html`
|
|
415
|
+
<span part="image-container" class="selection-card__image-container">
|
|
416
|
+
<slot name="image">
|
|
417
|
+
${this.src
|
|
418
|
+
? html`<img part="image" class="selection-card__image" src=${this.src} alt=${this.imageAlt}>`
|
|
419
|
+
: ''}
|
|
420
|
+
</slot>
|
|
421
|
+
</span>`
|
|
422
|
+
: ''}
|
|
423
|
+
|
|
424
|
+
<div part="label" class="checkbox__label selection-card__content">
|
|
425
|
+
<span part="card-title" class="selection-card__title">
|
|
426
|
+
<slot></slot>${hasDefaultSlot ? '' : this.cardTitle}
|
|
427
|
+
</span>
|
|
372
428
|
<div
|
|
373
429
|
aria-hidden=${hasDescription ? 'false' : 'true'}
|
|
374
430
|
class="checkbox__description"
|
|
@@ -7,4 +7,60 @@ describe('<zn-checkbox>', () => {
|
|
|
7
7
|
|
|
8
8
|
expect(el).to.exist;
|
|
9
9
|
});
|
|
10
|
+
|
|
11
|
+
it('renders card content and a built-in image', async () => {
|
|
12
|
+
const el = await fixture(html`
|
|
13
|
+
<zn-checkbox
|
|
14
|
+
contained
|
|
15
|
+
card-title="Payroll"
|
|
16
|
+
description="Salary and pay schedule tools."
|
|
17
|
+
src="payroll.svg"
|
|
18
|
+
image-alt="Payroll illustration">
|
|
19
|
+
</zn-checkbox>
|
|
20
|
+
`);
|
|
21
|
+
|
|
22
|
+
const image = el.shadowRoot!.querySelector('[part="image"]')!;
|
|
23
|
+
expect(image.getAttribute('src')).to.equal('payroll.svg');
|
|
24
|
+
expect(image.getAttribute('alt')).to.equal('Payroll illustration');
|
|
25
|
+
expect(el.shadowRoot!.querySelector('[part="label"]')!.textContent).to.include('Payroll');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders card-title when formatting whitespace is present in the default slot', async () => {
|
|
29
|
+
const el = await fixture(html`
|
|
30
|
+
<zn-checkbox contained card-title="Benefits">
|
|
31
|
+
<zn-icon slot="image" src="featured_seasonal_and_gifts"></zn-icon>
|
|
32
|
+
</zn-checkbox>
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
expect(el.shadowRoot!.querySelector('[part="card-title"]')!.textContent).to.include('Benefits');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('positions card images and controls', async () => {
|
|
39
|
+
const el = await fixture(html`
|
|
40
|
+
<zn-checkbox contained image-position="right" control-position="top-left">
|
|
41
|
+
Benefits
|
|
42
|
+
<zn-icon slot="image" src="featured_seasonal_and_gifts"></zn-icon>
|
|
43
|
+
</zn-checkbox>
|
|
44
|
+
`);
|
|
45
|
+
const base = el.shadowRoot!.querySelector('[part="base"]')!;
|
|
46
|
+
|
|
47
|
+
expect(base).to.have.class('selection-card--image-right');
|
|
48
|
+
expect(base).to.have.class('selection-card--control-top-left');
|
|
49
|
+
expect(el.shadowRoot!.querySelector('slot[name="image"]')).to.exist;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('leaves a plain contained checkbox without card styles', async () => {
|
|
53
|
+
const el = await fixture(html`<zn-checkbox contained description="Supporting text">Payroll</zn-checkbox>`);
|
|
54
|
+
|
|
55
|
+
expect(el.shadowRoot!.querySelector('[part="base"]')).not.to.have.class('selection-card');
|
|
56
|
+
expect(el.shadowRoot!.querySelector('[part="image-container"]')).to.be.null;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('can hide the visual control without removing the native input', async () => {
|
|
60
|
+
const el = await fixture(html`<zn-checkbox contained control-position="none">Payroll</zn-checkbox>`);
|
|
61
|
+
|
|
62
|
+
expect(el.shadowRoot!.querySelector('input[type="checkbox"]')).to.exist;
|
|
63
|
+
expect(el.shadowRoot!.querySelector('[part~="control"]')).to.exist;
|
|
64
|
+
expect(el.shadowRoot!.querySelector('[part="base"]')).to.have.class('selection-card--control-none');
|
|
65
|
+
});
|
|
10
66
|
});
|