1+ <script setup>
2+ import { ref , computed } from " vue" ;
3+ import { treeShake , shiftHue , opacity , convertConfigColors , palette , convertColorToHex } from " ../lib" ;
4+ import mainConfig from " ../default_configs.json" ;
5+
6+ const props = defineProps ({
7+ config: {
8+ type: Object ,
9+ default () {
10+ return {}
11+ }
12+ },
13+ dataset: {
14+ type: Array ,
15+ default () {
16+ return [];
17+ }
18+ }
19+ });
20+
21+ const uid = ref (` vue-ui-sparkhistogram-${ Math .random ()} ` );
22+ const defaultConfig = ref (mainConfig .vue_ui_sparkhistogram );
23+
24+ const histoConfig = computed (() => {
25+ if (! Object .keys (props .config || {}).length ) {
26+ return defaultConfig .value ;
27+ }
28+ const reconciled = treeShake ({
29+ defaultConfig: defaultConfig .value ,
30+ userConfig: props .config
31+ });
32+ return convertConfigColors (reconciled);
33+ });
34+
35+ const drawingArea = computed (() => {
36+ const height = histoConfig .value .style .layout .height ;
37+ const width = histoConfig .value .style .layout .width ;
38+ const top = histoConfig .value .style .layout .padding .top ;
39+ const bottom = height - histoConfig .value .style .layout .padding .bottom ;
40+ const left = histoConfig .value .style .layout .padding .left ;
41+ const right = width - histoConfig .value .style .layout .padding .right ;
42+ const centerY = top + ((height - top - histoConfig .value .style .layout .padding .bottom ) / 2 );
43+ const drawingHeight = height - histoConfig .value .style .layout .padding .top - histoConfig .value .style .layout .padding .bottom ;
44+ const drawingWidth = width - histoConfig .value .style .layout .padding .left - histoConfig .value .style .layout .padding .right ;
45+ return {
46+ bottom,
47+ centerY,
48+ drawingHeight,
49+ drawingWidth,
50+ height,
51+ left,
52+ right,
53+ top,
54+ width,
55+ }
56+ });
57+
58+ const maxVal = computed (() => {
59+ return Math .max (... props .dataset .map (ds => Math .abs (ds .value )))
60+ });
61+
62+ function toMax (val ) {
63+ return Math .abs (val) / maxVal .value ;
64+ }
65+
66+ // value, valueLabel, timeLabel
67+
68+ const computedDataset = computed (() => {
69+ return props .dataset .map ((dp ,i ) => {
70+ const proportion = toMax (dp .value );
71+ const height = drawingArea .value .drawingHeight * proportion;
72+ const unitWidth = drawingArea .value .drawingWidth / props .dataset .length ;
73+ const gap = unitWidth * (histoConfig .value .style .bars .gap / 100 )
74+ const width = unitWidth - gap;
75+ const y = drawingArea .value .centerY - height / 2 ;
76+ const x = drawingArea .value .left + (gap / 2 + (i * unitWidth));
77+ const intensity = typeof dp .intensity === ' undefined' ? 100 : Math .round (dp .intensity * 100 );
78+ const color = dp .value >= 0 ? ` ${ histoConfig .value .style .bars .colors .positive }${ opacity[intensity]} ` : ` ${ histoConfig .value .style .bars .colors .negative }${ opacity[intensity]} ` ;
79+ const stroke = dp .value >= 0 ? histoConfig .value .style .bars .colors .positive : histoConfig .value .style .bars .colors .negative ;
80+ const gradient = dp .value >= 0 ? ` url(#gradient_positive_${ i} _${ uid .value } )` : ` url(#gradient_negative_${ i} _${ uid .value } )` ;
81+ const textAnchor = x + width / 2 ;
82+ return {
83+ ... dp,
84+ color,
85+ gradient,
86+ height,
87+ intensity,
88+ proportion,
89+ stroke,
90+ textAnchor,
91+ width,
92+ x,
93+ y
94+ }
95+ })
96+ })
97+
98+ </script >
99+
100+ <template >
101+ <div :style =" `width:100%;background:${histoConfig.style.backgroundColor};font-family:${histoConfig.style.fontFamily}`" >
102+ <!-- TITLE -->
103+ <div v-if =" histoConfig.style.title.text" :style =" `width:calc(100% - 12px);background:${histoConfig.style.backgroundColor};margin:0 auto;margin:${histoConfig.style.title.margin};padding: 0 6px;text-align:${histoConfig.style.title.textAlign}`" >
104+ <div :style =" `font-size:${histoConfig.style.title.fontSize}px;color:${histoConfig.style.title.color};font-weight:${histoConfig.style.title.bold ? 'bold' : 'normal'}`" >
105+ {{ histoConfig.style.title.text }}
106+ </div >
107+ <div v-if =" histoConfig.style.title.subtitle.text" :style =" `font-size:${histoConfig.style.title.subtitle.fontSize}px;color:${histoConfig.style.title.subtitle.color};font-weight:${histoConfig.style.title.subtitle.bold ? 'bold' : 'normal'}`" >
108+ {{ histoConfig.style.title.subtitle.text }}
109+ </div >
110+ </div >
111+
112+ <svg :viewBox =" `0 0 ${drawingArea.width} ${drawingArea.height}`" >
113+
114+ <defs >
115+ <radialGradient v-for =" (posGrad, i) in computedDataset" :id =" `gradient_positive_${i}_${uid}`" cy =" 50%" cx =" 50%" r =" 50%" fx =" 50%" fy =" 50%" >
116+ <stop offset =" 0%" :stop-color =" `${shiftHue(histoConfig.style.bars.colors.positive, 0.05)}${opacity[posGrad.intensity]}`" />
117+ <stop offset =" 100%" :stop-color =" `${histoConfig.style.bars.colors.positive}${opacity[posGrad.intensity]}`" />
118+ </radialGradient >
119+
120+ <radialGradient v-for =" (negGrad, i) in computedDataset" :id =" `gradient_negative_${i}_${uid}`" cy =" 50%" cx =" 50%" r =" 50%" fx =" 50%" fy =" 50%" >
121+ <stop offset =" 0%" :stop-color =" `${shiftHue(histoConfig.style.bars.colors.negative, 0.05)}${opacity[negGrad.intensity]}`" />
122+ <stop offset =" 100%" :stop-color =" `${histoConfig.style.bars.colors.negative}${opacity[negGrad.intensity]}`" />
123+ </radialGradient >
124+ </defs >
125+
126+
127+ <rect v-for =" rect in computedDataset"
128+ :x =" rect.x"
129+ :y =" rect.y"
130+ :height =" rect.height"
131+ :width =" rect.width"
132+ :fill =" histoConfig.style.bars.colors.gradient.show ? rect.gradient : rect.color"
133+ :stroke =" rect.stroke"
134+ :stroke-width =" histoConfig.style.bars.strokeWidth"
135+ :rx =" `${histoConfig.style.bars.borderRadius * rect.proportion / 12}%`"
136+ />
137+
138+ <text v-for =" val in computedDataset"
139+ text-anchor =" middle"
140+ :x =" val.textAnchor"
141+ :y =" val.y - histoConfig.style.labels.value.fontSize / 3"
142+ :font-size =" histoConfig.style.labels.value.fontSize"
143+ :font-weight =" histoConfig.style.labels.value.bold ? 'bold' : 'normal'"
144+ :fill =" histoConfig.style.labels.value.color"
145+ >
146+ {{ histoConfig.style.labels.value.prefix }}{{ isNaN(val.value) ? '' : Number(val.value.toFixed(histoConfig.style.labels.value.rounding)).toLocaleString() }}{{ histoConfig.style.labels.value.suffix }}
147+ </text >
148+
149+ <g v-for =" label in computedDataset" >
150+ <text
151+ v-if =" label.valueLabel"
152+ :x =" label.textAnchor"
153+ :y =" label.y + label.height + histoConfig.style.labels.valueLabel.fontSize"
154+ :font-size =" histoConfig.style.labels.valueLabel.fontSize"
155+ text-anchor =" middle"
156+ :fill =" histoConfig.style.labels.valueLabel.color"
157+ >
158+ {{ label.valueLabel }}
159+ </text >
160+ </g >
161+
162+ <g v-for =" time in computedDataset" >
163+ <text
164+ v-if =" time.timeLabel"
165+ :x =" time.textAnchor"
166+ :y =" drawingArea.height - histoConfig.style.labels.timeLabel.fontSize / 2"
167+ :font-size =" histoConfig.style.labels.timeLabel.fontSize"
168+ :fill =" histoConfig.style.labels.timeLabel.color"
169+ text-anchor =" middle"
170+ >
171+ {{ time.timeLabel }}
172+ </text >
173+ </g >
174+
175+ </svg >
176+ </div >
177+ </template >
0 commit comments