@milaboratories/uikit 2.2.45 → 2.2.46
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/CHANGELOG.md +6 -0
- package/dist/pl-uikit.js +7708 -5293
- package/dist/pl-uikit.umd.cjs +12 -8
- package/dist/src/components/PlChartHistogram/PlChartHistogram.vue.d.ts +6 -0
- package/dist/src/components/PlChartHistogram/createGridlines.d.ts +3 -0
- package/dist/src/components/PlChartHistogram/createLabels.d.ts +3 -0
- package/dist/src/components/PlChartHistogram/createSvgContainer.d.ts +3 -0
- package/dist/src/components/PlChartHistogram/drawBins.d.ts +2 -0
- package/dist/src/components/PlChartHistogram/drawThreshold.d.ts +2 -0
- package/dist/src/components/PlChartHistogram/histogram.d.ts +4 -0
- package/dist/src/components/PlChartHistogram/index.d.ts +1 -0
- package/dist/src/components/PlChartHistogram/logspace.d.ts +1 -0
- package/dist/src/components/PlChartHistogram/normalizeBins.d.ts +2 -0
- package/dist/src/components/PlChartHistogram/scales.spec.d.ts +1 -0
- package/dist/src/components/PlChartHistogram/types.d.ts +58 -0
- package/dist/src/composition/useComponentProp.d.ts +33 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/types.d.ts +2 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +6 -4
- package/src/colors/__tests__/colors.spec.ts +0 -2
- package/src/components/PlChartHistogram/PlChartHistogram.vue +90 -0
- package/src/components/PlChartHistogram/createGridlines.ts +42 -0
- package/src/components/PlChartHistogram/createLabels.ts +32 -0
- package/src/components/PlChartHistogram/createSvgContainer.ts +23 -0
- package/src/components/PlChartHistogram/drawBins.ts +55 -0
- package/src/components/PlChartHistogram/drawThreshold.ts +19 -0
- package/src/components/PlChartHistogram/histogram.ts +136 -0
- package/src/components/PlChartHistogram/index.ts +1 -0
- package/src/components/PlChartHistogram/logspace.ts +13 -0
- package/src/components/PlChartHistogram/normalizeBins.ts +19 -0
- package/src/components/PlChartHistogram/scales.spec.ts +10 -0
- package/src/components/PlChartHistogram/types.ts +66 -0
- package/src/components/PlDropdownMulti/PlDropdownMulti.vue +0 -2
- package/src/components/PlDropdownMultiRef/__tests__/PlDropdownMultiRef.spec.ts +1 -3
- package/src/components/PlProgressCell/PlProgressCell.vue +0 -2
- package/src/composition/useComponentProp.ts +36 -0
- package/src/helpers/index.ts +1 -1
- package/src/index.ts +2 -0
- package/src/types.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/uikit",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.46",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/pl-uikit.umd.js",
|
|
6
6
|
"module": "dist/pl-uikit.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"./*": "./*"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"vue": "^3.5.13"
|
|
21
|
+
"vue": "^3.5.13",
|
|
22
|
+
"d3": "^7.9.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@vue/test-utils": "^2.4.6",
|
|
@@ -32,9 +33,10 @@
|
|
|
32
33
|
"vue-tsc": "^2.1.10",
|
|
33
34
|
"yarpm": "^1.2.0",
|
|
34
35
|
"svgo": "^3.3.2",
|
|
35
|
-
"@
|
|
36
|
+
"@types/d3": "^7.4.3",
|
|
36
37
|
"@milaboratories/eslint-config": "^1.0.1",
|
|
37
|
-
"@platforma-sdk/model": "^1.21.20"
|
|
38
|
+
"@platforma-sdk/model": "^1.21.20",
|
|
39
|
+
"@milaboratories/helpers": "^1.6.11"
|
|
38
40
|
},
|
|
39
41
|
"scripts": {
|
|
40
42
|
"dev": "vite",
|
|
@@ -14,8 +14,6 @@ describe('Colors', () => {
|
|
|
14
14
|
|
|
15
15
|
const viridis15 = Gradient(viridis).split(15);
|
|
16
16
|
|
|
17
|
-
console.log('viridis15', JSON.stringify(viridis15));
|
|
18
|
-
|
|
19
17
|
expect(viridis.map((it) => it + 'FF').join(',')).toEqual(viridis15.map((it) => it.hex.toUpperCase()).join(','));
|
|
20
18
|
});
|
|
21
19
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
3
|
+
import { createHistogramFromBins, createHistogramLinear, createHistogramLog } from './histogram';
|
|
4
|
+
import type { ChartOptions, PlChartHistogramSettings } from './types';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
settings: PlChartHistogramSettings;
|
|
8
|
+
}>();
|
|
9
|
+
|
|
10
|
+
const chart = ref<HTMLElement>();
|
|
11
|
+
|
|
12
|
+
const options = computed<ChartOptions>(() => {
|
|
13
|
+
const { xAxisLabel, yAxisLabel, threshold, compact, totalWidth = 674, totalHeight = 252 } = props.settings;
|
|
14
|
+
|
|
15
|
+
const margin = compact ? { top: 0, right: 0, bottom: 0, left: 0 } : { top: 0, right: 30, bottom: 40, left: 85 };
|
|
16
|
+
const width = totalWidth - margin.left - margin.right;
|
|
17
|
+
const height = totalHeight - margin.top - margin.bottom;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
margin,
|
|
23
|
+
compact,
|
|
24
|
+
nBins: 'nBins' in props.settings ? props.settings.nBins : 10,
|
|
25
|
+
xAxisLabel: xAxisLabel,
|
|
26
|
+
yAxisLabel: yAxisLabel,
|
|
27
|
+
threshold: threshold,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const createHistogram = () => {
|
|
32
|
+
const settings = props.settings;
|
|
33
|
+
|
|
34
|
+
if (settings.type === 'log-bins') {
|
|
35
|
+
createHistogramFromBins(chart.value!, options.value, settings.bins);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (settings.log) {
|
|
40
|
+
createHistogramLog(chart.value!, options.value, settings.numbers);
|
|
41
|
+
} else {
|
|
42
|
+
createHistogramLinear(chart.value!, options.value, settings.numbers);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
watch(props, createHistogram);
|
|
47
|
+
|
|
48
|
+
onMounted(createHistogram);
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<div :class="$style.component">
|
|
53
|
+
<div v-if="settings.title && !settings.compact" :class="$style.title">{{ settings.title }}</div>
|
|
54
|
+
<div ref="chart" />
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style module>
|
|
59
|
+
.component {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 24px;
|
|
63
|
+
svg {
|
|
64
|
+
font-family: var(--font-family-base);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.title {
|
|
69
|
+
font-size: 20px;
|
|
70
|
+
font-weight: 500;
|
|
71
|
+
line-height: 24px; /* 120% */
|
|
72
|
+
letter-spacing: -0.2px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
:global(.svg-tooltip) {
|
|
76
|
+
font-family: var(--font-family-base);
|
|
77
|
+
background: rgba(69,77,93,.9);
|
|
78
|
+
border-radius: .1rem;
|
|
79
|
+
color: #fff;
|
|
80
|
+
display: block;
|
|
81
|
+
font-size: 14px;
|
|
82
|
+
max-width: 320px;
|
|
83
|
+
padding: .2rem .4rem;
|
|
84
|
+
position: absolute;
|
|
85
|
+
text-overflow: ellipsis;
|
|
86
|
+
white-space: pre;
|
|
87
|
+
z-index: 300;
|
|
88
|
+
visibility: hidden;
|
|
89
|
+
}
|
|
90
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as d3 from 'd3';
|
|
2
|
+
import type { ChartOptions, Scales } from './types';
|
|
3
|
+
|
|
4
|
+
export function createGridlines(
|
|
5
|
+
svg: d3.Selection<SVGGElement, unknown, null, undefined>,
|
|
6
|
+
options: ChartOptions,
|
|
7
|
+
scales: Scales,
|
|
8
|
+
xTicks: (d: d3.Axis<d3.NumberValue>) => d3.Axis<d3.NumberValue>,
|
|
9
|
+
) {
|
|
10
|
+
const { width, height } = options;
|
|
11
|
+
|
|
12
|
+
function makeYGridlines() {
|
|
13
|
+
return d3.axisLeft(scales.y) // Use the y-scale for horizontal gridlines
|
|
14
|
+
.ticks(6); // Adjust the number of gridlines
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function makeXGridlines() {
|
|
18
|
+
return xTicks(d3.axisBottom(scales.x));
|
|
19
|
+
}
|
|
20
|
+
// Append horizontal gridlines
|
|
21
|
+
svg.append('g')
|
|
22
|
+
.attr('class', 'grid') // Add a class for styling
|
|
23
|
+
.attr('font-family', '\'Manrope\', sans-serif') // Doesn't work
|
|
24
|
+
.call(makeYGridlines()
|
|
25
|
+
.tickSize(-width) // Extend gridlines across the chart width
|
|
26
|
+
.tickFormat(() => '')); // Remove tick labels
|
|
27
|
+
|
|
28
|
+
// Append vertical gridlines
|
|
29
|
+
svg.append('g')
|
|
30
|
+
.attr('class', 'grid') // Add a class for styling
|
|
31
|
+
.attr('font-family', '\'Manrope\', sans-serif')
|
|
32
|
+
.attr('transform', `translate(0,${height})`) // Position at the bottom of the chart
|
|
33
|
+
.call(makeXGridlines()
|
|
34
|
+
.tickSize(-height) // Extend gridlines across the chart height
|
|
35
|
+
.tickFormat(() => '')); // Remove tick labels
|
|
36
|
+
|
|
37
|
+
// Style the gridlines using CSS (or inline styles)
|
|
38
|
+
d3.selectAll('.grid line')
|
|
39
|
+
.style('stroke', '#E1E3EB') // Light gray gridlines
|
|
40
|
+
// .style('stroke-dasharray', '2,2') // Dashed gridlines
|
|
41
|
+
.style('opacity', 0.7); // Slightly transparent
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type * as d3 from 'd3';
|
|
2
|
+
import type { ChartOptions } from './types';
|
|
3
|
+
|
|
4
|
+
export function createLabels(
|
|
5
|
+
svg: d3.Selection<SVGGElement, unknown, null, undefined>,
|
|
6
|
+
options: ChartOptions,
|
|
7
|
+
) {
|
|
8
|
+
const { height, width, margin, xAxisLabel, yAxisLabel, compact } = options;
|
|
9
|
+
|
|
10
|
+
if (compact) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// X-axis label
|
|
15
|
+
svg.append('text')
|
|
16
|
+
.attr('class', 'x-axis-label')
|
|
17
|
+
.attr('font-weight', 500)
|
|
18
|
+
.attr('text-anchor', 'middle') // Center the text
|
|
19
|
+
.attr('x', width / 2) // Center horizontally
|
|
20
|
+
.attr('y', height + margin.bottom - 5) // Position below the X-axis
|
|
21
|
+
.text(xAxisLabel ?? 'Value Range'); // Set your custom label text
|
|
22
|
+
|
|
23
|
+
// Y-axis label
|
|
24
|
+
svg.append('text')
|
|
25
|
+
.attr('class', 'y-axis-label')
|
|
26
|
+
.attr('font-weight', 500)
|
|
27
|
+
.attr('text-anchor', 'middle') // Center the text
|
|
28
|
+
.attr('x', -height / 2) // Center vertically (rotated axis)
|
|
29
|
+
.attr('y', -margin.left + 15) // Position to the left of the Y-axis
|
|
30
|
+
.attr('transform', 'rotate(-90)') // Rotate text 90 degrees counter-clockwise
|
|
31
|
+
.text(yAxisLabel ?? 'Frequency'); // Set your custom label text
|
|
32
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as d3 from 'd3';
|
|
2
|
+
import type { ChartOptions } from './types';
|
|
3
|
+
|
|
4
|
+
export function createSvgContainer(el: HTMLElement, options: ChartOptions) {
|
|
5
|
+
const { width, height, margin, compact } = options;
|
|
6
|
+
|
|
7
|
+
el.replaceChildren();
|
|
8
|
+
|
|
9
|
+
if (compact) {
|
|
10
|
+
el.style.height = height + 'px';
|
|
11
|
+
el.style.lineHeight = height + 'px';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const svg = d3
|
|
15
|
+
.select(el) // Append the SVG element to the body
|
|
16
|
+
.append('svg')
|
|
17
|
+
.attr('width', width + margin.left + margin.right) // Set the total width
|
|
18
|
+
.attr('height', height + margin.top + margin.bottom) // Set the total height
|
|
19
|
+
.append('g') // Append a group to handle margins
|
|
20
|
+
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
21
|
+
|
|
22
|
+
return svg;
|
|
23
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { BinLike, ChartOptions, Scales, SVG } from './types';
|
|
2
|
+
import * as d3 from 'd3';
|
|
3
|
+
|
|
4
|
+
export function drawBins(
|
|
5
|
+
svg: SVG,
|
|
6
|
+
bins: BinLike[],
|
|
7
|
+
dimension: ChartOptions,
|
|
8
|
+
scales: Scales,
|
|
9
|
+
) {
|
|
10
|
+
const { height } = dimension;
|
|
11
|
+
|
|
12
|
+
const { x, y } = scales;
|
|
13
|
+
|
|
14
|
+
const tooltip = d3
|
|
15
|
+
.select('body')
|
|
16
|
+
.append('div')
|
|
17
|
+
.attr('class', 'svg-tooltip')
|
|
18
|
+
.style('position', 'absolute')
|
|
19
|
+
.style('visibility', 'hidden');
|
|
20
|
+
|
|
21
|
+
// Three function that change the tooltip when user hover / move / leave a cell
|
|
22
|
+
const mouseover = function (_event: MouseEvent, d: BinLike) {
|
|
23
|
+
tooltip
|
|
24
|
+
.style('visibility', 'visible')
|
|
25
|
+
.text(`count: ${d.length}\nx0: ${d.x0}\nx1: ${d.x1}`);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const mousemove = function (event: MouseEvent) {
|
|
29
|
+
tooltip
|
|
30
|
+
.style('top', event.pageY - 10 + 'px')
|
|
31
|
+
.style('left', event.pageX + 10 + 'px');
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const mouseout = function () {
|
|
35
|
+
tooltip.style('visibility', 'hidden');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Add rectangles for the histogram bars
|
|
39
|
+
svg.selectAll('rect')
|
|
40
|
+
.data(bins)
|
|
41
|
+
.enter()
|
|
42
|
+
.append('rect')
|
|
43
|
+
.attr('x', (d) => x(d.x0!)) // Position the bar based on the bin start
|
|
44
|
+
.attr('y', (d) => y(d.length)) // Height based on bin count
|
|
45
|
+
.attr('width', (d) => x(d.x1!) - x(d.x0!)) // Bar width based on logarithmic intervals
|
|
46
|
+
.attr('height', (d) => height - y(d.length)) // Invert height to fit SVG coordinate system
|
|
47
|
+
.style('fill', '#929BAD')
|
|
48
|
+
.attr('stroke', '#fff') // Border color
|
|
49
|
+
.attr('stroke-opacity', 0.2)
|
|
50
|
+
.attr('stroke-width', 0.5)
|
|
51
|
+
.on('mouseover', mouseover)
|
|
52
|
+
.on('mousemove', mousemove)
|
|
53
|
+
.on('mouseout', mouseout)
|
|
54
|
+
.append('title').text((d) => `[${d.x0}, ${d.x1}]\n` + d.length + '\n'); // Set bar color
|
|
55
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChartOptions, Scales, SVG } from './types';
|
|
2
|
+
|
|
3
|
+
export function drawThreshold(svg: SVG, scales: Scales, options: ChartOptions) {
|
|
4
|
+
const { threshold } = options;
|
|
5
|
+
|
|
6
|
+
if (!threshold) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
svg
|
|
11
|
+
.append('line')
|
|
12
|
+
.attr('x1', scales.x(threshold))
|
|
13
|
+
.attr('x2', scales.x(threshold))
|
|
14
|
+
.attr('y1', 0)
|
|
15
|
+
.attr('y2', options.height)
|
|
16
|
+
.style('stroke', '#F05670')
|
|
17
|
+
.style('stroke-width', '1')
|
|
18
|
+
.style('stroke-dasharray', '7.4 3.2');
|
|
19
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as d3 from 'd3';
|
|
2
|
+
import { createSvgContainer } from './createSvgContainer';
|
|
3
|
+
import { drawBins } from './drawBins';
|
|
4
|
+
import { createGridlines } from './createGridlines';
|
|
5
|
+
import { logspace } from './logspace';
|
|
6
|
+
import { createLabels } from './createLabels';
|
|
7
|
+
import type { AnyBin, BinLike, ChartOptions } from './types';
|
|
8
|
+
import { drawThreshold } from './drawThreshold';
|
|
9
|
+
import { normalizeBins } from './normalizeBins';
|
|
10
|
+
|
|
11
|
+
const gx = (svg: d3.Selection<SVGGElement, unknown, null, undefined>, height: number) => {
|
|
12
|
+
return svg.append('g')
|
|
13
|
+
.style('font-size', '14px')
|
|
14
|
+
.style('font-weight', '500')
|
|
15
|
+
.attr('transform', `translate(0,${height})`);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const gy = (svg: d3.Selection<SVGGElement, unknown, null, undefined>) => {
|
|
19
|
+
return svg.append('g')
|
|
20
|
+
.style('font-size', '14px')
|
|
21
|
+
.style('font-weight', '500');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const createYScale = (bins: BinLike[], height: number) => {
|
|
25
|
+
return d3.scaleLinear()
|
|
26
|
+
.domain([0, d3.max(bins, (d) => d.length)!]) // Max bin count for the domain
|
|
27
|
+
.range([height, 0]); // Map to chart height (invert to match SVG coordinates)
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function createHistogramLinear(el: HTMLElement, options: ChartOptions, data: number[]) {
|
|
31
|
+
const { width, height, nBins = 10 } = options;
|
|
32
|
+
|
|
33
|
+
const svg = createSvgContainer(el, options);
|
|
34
|
+
|
|
35
|
+
const min = d3.min(data) as number;
|
|
36
|
+
const max = d3.max(data) as number;
|
|
37
|
+
|
|
38
|
+
const x = d3.scaleLinear()
|
|
39
|
+
.domain([min, max])
|
|
40
|
+
.range([0, width]);
|
|
41
|
+
|
|
42
|
+
const bins: BinLike[] = normalizeBins(d3.bin()
|
|
43
|
+
.domain(x.domain() as [number, number]) // Set the input domain to match the x-scale
|
|
44
|
+
.thresholds(x.ticks(nBins))(data)); // Apply the data to create bins
|
|
45
|
+
|
|
46
|
+
const y = createYScale(bins, height);
|
|
47
|
+
|
|
48
|
+
createGridlines(svg, options, { x, y }, (x) => x.ticks(6));
|
|
49
|
+
|
|
50
|
+
drawBins(svg, bins, options, { x, y });
|
|
51
|
+
|
|
52
|
+
drawThreshold(svg, { x, y }, options);
|
|
53
|
+
|
|
54
|
+
gx(svg, height).call(d3.axisBottom(x).tickSize(0));
|
|
55
|
+
|
|
56
|
+
gy(svg).call(d3.axisLeft(y).tickSize(0));
|
|
57
|
+
|
|
58
|
+
createLabels(svg, options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function createHistogramLog(el: HTMLElement, options: ChartOptions, data: number[]) {
|
|
62
|
+
const { width, height, nBins = 10 } = options;
|
|
63
|
+
|
|
64
|
+
const svg = createSvgContainer(el, options);
|
|
65
|
+
|
|
66
|
+
const min = d3.min(data) as number;
|
|
67
|
+
const max = d3.max(data) as number;
|
|
68
|
+
|
|
69
|
+
const x = d3.scaleSymlog()
|
|
70
|
+
.domain([min, max]) // Input range (min and max values of the data)
|
|
71
|
+
.range([0, width])
|
|
72
|
+
.nice() // Output range (width of the chart)
|
|
73
|
+
;
|
|
74
|
+
|
|
75
|
+
const createThresholds = (n: number) => {
|
|
76
|
+
const res = [];
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i <= n; i++) {
|
|
79
|
+
res.push(Number(x.invert(width * (i / n)).toFixed(2)));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return res;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const bins = normalizeBins(d3.bin()
|
|
86
|
+
.domain(x.domain() as [number, number]) // Set the input domain to match the x-scale
|
|
87
|
+
.thresholds(createThresholds(nBins))(data)); // Apply the data to create bins
|
|
88
|
+
|
|
89
|
+
const y = createYScale(bins, height);
|
|
90
|
+
|
|
91
|
+
const tickValues = logspace(0, Math.ceil(Math.log10(max)), 6);
|
|
92
|
+
|
|
93
|
+
createGridlines(svg, options, { x, y }, (x) => x.tickValues(tickValues));
|
|
94
|
+
|
|
95
|
+
drawBins(svg, bins, options, { x, y });
|
|
96
|
+
|
|
97
|
+
drawThreshold(svg, { x, y }, options);
|
|
98
|
+
|
|
99
|
+
gx(svg, height).call(d3.axisBottom(x).tickValues(tickValues).tickSize(0));
|
|
100
|
+
|
|
101
|
+
gy(svg).call(d3.axisLeft(y).tickSize(0));
|
|
102
|
+
|
|
103
|
+
createLabels(svg, options);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function createHistogramFromBins(el: HTMLElement, options: ChartOptions, _bins: AnyBin[]) {
|
|
107
|
+
const { width, height } = options;
|
|
108
|
+
|
|
109
|
+
const svg = createSvgContainer(el, options);
|
|
110
|
+
|
|
111
|
+
const bins = normalizeBins(_bins);
|
|
112
|
+
|
|
113
|
+
const min = d3.min(bins, (b) => b.x0) as number;
|
|
114
|
+
const max = d3.max(bins, (b) => b.x1) as number;
|
|
115
|
+
|
|
116
|
+
const x = d3.scaleSymlog()
|
|
117
|
+
.domain([min, max])
|
|
118
|
+
.range([0, width])
|
|
119
|
+
.nice();
|
|
120
|
+
|
|
121
|
+
const y = createYScale(bins, height);
|
|
122
|
+
|
|
123
|
+
const tickValues = logspace(0, Math.ceil(Math.log10(max)), 6);
|
|
124
|
+
|
|
125
|
+
createGridlines(svg, options, { x, y }, (x) => x.tickValues(tickValues));
|
|
126
|
+
|
|
127
|
+
drawBins(svg, bins, options, { x, y });
|
|
128
|
+
|
|
129
|
+
drawThreshold(svg, { x, y }, options);
|
|
130
|
+
|
|
131
|
+
gx(svg, height).call(d3.axisBottom(x).tickValues(tickValues).tickSize(0));
|
|
132
|
+
|
|
133
|
+
gy(svg).call(d3.axisLeft(y).tickSize(0));
|
|
134
|
+
|
|
135
|
+
createLabels(svg, options);
|
|
136
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PlChartHistogram } from './PlChartHistogram.vue';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function logspace(startExp: number, stopExp: number, num = 50, base = 10) {
|
|
2
|
+
if (num <= 0) return [];
|
|
3
|
+
|
|
4
|
+
const step = (stopExp - startExp) / (num - 1);
|
|
5
|
+
const result = [];
|
|
6
|
+
|
|
7
|
+
for (let i = 0; i < num; i++) {
|
|
8
|
+
const exponent = startExp + i * step;
|
|
9
|
+
result.push(Math.pow(base, exponent));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AnyBin, BinLike } from './types';
|
|
2
|
+
|
|
3
|
+
export function normalizeBins(bins: (d3.Bin<number, number> | AnyBin)[]): BinLike[] {
|
|
4
|
+
return bins.map((it) => {
|
|
5
|
+
if ('from' in it) {
|
|
6
|
+
return {
|
|
7
|
+
x0: it.from,
|
|
8
|
+
x1: it.to,
|
|
9
|
+
length: it.weight,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
x0: it.x0!,
|
|
15
|
+
x1: it.x1!,
|
|
16
|
+
length: it.length,
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type Margin = { top: number; right: number; bottom: number; left: number };
|
|
2
|
+
|
|
3
|
+
export type ChartOptions = {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
margin: Margin;
|
|
7
|
+
nBins?: number;
|
|
8
|
+
yAxisLabel?: string;
|
|
9
|
+
xAxisLabel?: string;
|
|
10
|
+
threshold?: number;
|
|
11
|
+
compact?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type Scales = {
|
|
15
|
+
x: d3.ScaleSymLog<number, number, never> | d3.ScaleLinear<number, number>;
|
|
16
|
+
y: d3.ScaleLinear<number, number, never>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type SVG = d3.Selection<SVGGElement, unknown, null, undefined>;
|
|
20
|
+
|
|
21
|
+
export type CustomBin = {
|
|
22
|
+
from: number;
|
|
23
|
+
to: number;
|
|
24
|
+
weight: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type BinLike = {
|
|
28
|
+
x0: number;
|
|
29
|
+
x1: number;
|
|
30
|
+
length: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type AnyBin = CustomBin | BinLike;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Common case: array of numbers
|
|
37
|
+
*/
|
|
38
|
+
export type PlChartHistogramBasicSettings = {
|
|
39
|
+
type: 'basic';
|
|
40
|
+
threshold?: number;
|
|
41
|
+
numbers: number[];
|
|
42
|
+
log?: boolean;
|
|
43
|
+
nBins?: number;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* For precalculated bins on log x scale
|
|
48
|
+
*/
|
|
49
|
+
export type PlChartHistogramLogBinsSettings = {
|
|
50
|
+
type: 'log-bins';
|
|
51
|
+
threshold?: number;
|
|
52
|
+
bins: AnyBin[];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type PlChartHistogramSettings = (
|
|
56
|
+
PlChartHistogramBasicSettings |
|
|
57
|
+
PlChartHistogramLogBinsSettings
|
|
58
|
+
) & {
|
|
59
|
+
title?: string;
|
|
60
|
+
yAxisLabel?: string;
|
|
61
|
+
xAxisLabel?: string;
|
|
62
|
+
// with margins
|
|
63
|
+
totalWidth?: number;
|
|
64
|
+
totalHeight?: number;
|
|
65
|
+
compact?: boolean;
|
|
66
|
+
};
|
|
@@ -166,8 +166,6 @@ const toggleModel = () => (data.open = !data.open);
|
|
|
166
166
|
const onFocusOut = (event: FocusEvent) => {
|
|
167
167
|
const relatedTarget = event.relatedTarget as Node | null;
|
|
168
168
|
|
|
169
|
-
console.log('>>>> overlay.value?.$el', overlay.value?.$el);
|
|
170
|
-
|
|
171
169
|
if (!rootRef.value?.contains(relatedTarget) && !overlay.value?.listRef?.contains(relatedTarget)) {
|
|
172
170
|
data.search = '';
|
|
173
171
|
data.open = false;
|
|
@@ -43,11 +43,9 @@ describe('PlDropdownMultiRef', () => {
|
|
|
43
43
|
|
|
44
44
|
const options = getOptions();
|
|
45
45
|
|
|
46
|
-
console.log('options', options);
|
|
47
|
-
|
|
48
46
|
expect(options.length).toBe(2);
|
|
49
47
|
|
|
50
|
-
console.log(wrapper.props('modelValue'), 'mv');
|
|
48
|
+
// console.log(wrapper.props('modelValue'), 'mv');
|
|
51
49
|
options[0].click();
|
|
52
50
|
|
|
53
51
|
await delay(20);
|
|
@@ -17,8 +17,6 @@ const canShowWhiteBg = computed(() => props.stage !== 'not_started');
|
|
|
17
17
|
const currentProgress = computed(() => props.stage === 'done' ? 100 : Math.min(100, props.progress || 0));
|
|
18
18
|
|
|
19
19
|
const canShowInfinityLoader = computed(() => props.progress === undefined && props.stage !== 'done' && props.stage !== 'not_started' && !props.error);
|
|
20
|
-
|
|
21
|
-
console.log(props);
|
|
22
20
|
</script>
|
|
23
21
|
|
|
24
22
|
<template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { InferComponentProps } from '@/types';
|
|
2
|
+
import { computed, type Component, type ComputedRef } from 'vue';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A utility function that creates a reactive computed property
|
|
6
|
+
* based on a specific prop of a Vue component.
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
* @template C - The Vue component type.
|
|
10
|
+
* @template P - The prop name.
|
|
11
|
+
*
|
|
12
|
+
* @param cb - A factory function that returns the value of the prop from the inferred component props.
|
|
13
|
+
*
|
|
14
|
+
* @returns A `ComputedRef` of the specified prop
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { ref, defineComponent } from 'vue';
|
|
19
|
+
* import { useComponentProp } from '@platforma-sdk/ui-vue';
|
|
20
|
+
*
|
|
21
|
+
* const MyComponent = defineComponent({
|
|
22
|
+
* props: {
|
|
23
|
+
* myProp: {
|
|
24
|
+
* type: String,
|
|
25
|
+
* required: true
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const propValue = useComponentProp<typeof MyComponent, 'myProp'>(() => 'example');
|
|
31
|
+
* console.log(propValue.value); // Outputs: 'example'
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function useComponentProp<C extends Component, P extends keyof InferComponentProps<C>>(cb: () => InferComponentProps<C>[P]): ComputedRef<InferComponentProps<C>[P]> {
|
|
35
|
+
return computed(cb);
|
|
36
|
+
}
|
package/src/helpers/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -63,6 +63,7 @@ export * from './components/PlIcon16';
|
|
|
63
63
|
export * from './components/PlIcon24';
|
|
64
64
|
|
|
65
65
|
export * from './components/PlChartStackedBar';
|
|
66
|
+
export * from './components/PlChartHistogram';
|
|
66
67
|
|
|
67
68
|
export * from './colors';
|
|
68
69
|
|
|
@@ -93,6 +94,7 @@ export { useInterval } from './composition/useInterval';
|
|
|
93
94
|
export { useFormState } from './composition/useFormState';
|
|
94
95
|
export { useQuery } from './composition/useQuery.ts';
|
|
95
96
|
export { useDraggable } from './composition/useDraggable';
|
|
97
|
+
export { useComponentProp } from './composition/useComponentProp';
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
100
|
* Utils/Partials
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ImportFileHandle, Platforma, StorageHandle, PlRef as ModelRef } from '@platforma-sdk/model';
|
|
2
|
-
import type { Ref, ComputedRef } from 'vue';
|
|
2
|
+
import type { Ref, ComputedRef, Component } from 'vue';
|
|
3
3
|
import { maskIcons16 } from './generated/icons-16';
|
|
4
4
|
import { maskIcons24 } from './generated/icons-24';
|
|
5
5
|
|
|
@@ -78,6 +78,8 @@ export type ImportedFiles = {
|
|
|
78
78
|
files: ImportFileHandle[];
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
+
export type InferComponentProps<C extends Component> = C extends Component<infer P> ? P : never;
|
|
82
|
+
|
|
81
83
|
declare global {
|
|
82
84
|
const platforma: Platforma | undefined;
|
|
83
85
|
interface Window {
|