88 convertColorToHex ,
99 convertCustomPalette ,
1010 createCsvContent ,
11+ createTSpans ,
1112 createUid ,
1213 darkenHexColor ,
1314 dataLabel ,
@@ -46,6 +47,8 @@ const props = defineProps({
4647 },
4748});
4849
50+ const emit = defineEmits ([' selectDatapoint' ]);
51+
4952const { vue_ui_circle_pack: DEFAULT_CONFIG } = useConfig ();
5053
5154const isDataset = computed (() => {
@@ -151,10 +154,10 @@ function findInitialPosition(placedCircles, radius, width, height) {
151154 const spacing = radius * 2 ;
152155
153156 for (let circle of placedCircles) {
154- for (let angle = 0 ; angle < 360 ; angle += 30 ) {
157+ for (let angle = 0 ; angle < 360 ; angle += 1 ) {
155158 let rad = (angle * Math .PI ) / 180 ;
156159 let x = circle .x + spacing * Math .cos (rad);
157- let y = circle .y + spacing * Math .sin (rad);
160+ let y = circle .y - spacing * Math .sin (rad);
158161
159162 let candidate = { x, y, radius };
160163
@@ -169,16 +172,22 @@ function findInitialPosition(placedCircles, radius, width, height) {
169172 }
170173 }
171174 }
172-
173175 return null ;
174176}
175177
176- function packCircles (dp , width , height , maxRadius , offsetX = 0 , offsetY = 0 ) {
177- const maxDataPoint = Math .max (... dp .map (d => d .value ));
178-
179- const radii = dp .map ((d , index ) => ({
180- ... d,
181- radius: (d .value / maxDataPoint) * maxRadius,
178+ function packCircles ({
179+ datapoints,
180+ width,
181+ height,
182+ maxRadius,
183+ offsetX = 0 ,
184+ offsetY = 0
185+ }) {
186+ const maxDataPoint = Math .max (... datapoints .map (dp => dp .value ));
187+
188+ const radii = datapoints .map ((dp , index ) => ({
189+ ... dp,
190+ radius: (dp .value / maxDataPoint) * maxRadius,
182191 index
183192 }));
184193
@@ -195,11 +204,11 @@ function packCircles(dp, width, height, maxRadius, offsetX = 0, offsetY = 0) {
195204 });
196205
197206 for (let circleData of sortedCircles .slice (1 )) {
198- let { radius, ... el } = circleData;
207+ let { radius, ... dp } = circleData;
199208 let position = findInitialPosition (placedCircles, radius, width, height);
200209
201210 if (position) {
202- placedCircles .push ({ ... el , x: position .x , y: position .y , radius });
211+ placedCircles .push ({ ... dp , x: position .x , y: position .y , radius });
203212 } else {
204213 let bestFit = null ;
205214 let minOverlap = Infinity ;
@@ -210,7 +219,7 @@ function packCircles(dp, width, height, maxRadius, offsetX = 0, offsetY = 0) {
210219 let x = circle .x + (radius + circle .radius ) * Math .cos (rad);
211220 let y = circle .y + (radius + circle .radius ) * Math .sin (rad);
212221
213- let candidate = { ... el , x, y, radius };
222+ let candidate = { ... dp , x, y, radius };
214223
215224 if (
216225 x - radius >= 0 &&
@@ -263,13 +272,12 @@ function calcOffsetY(radius, offset) {
263272
264273async function packSingleSet () {
265274 circles .value =
266- packCircles (
267- formattedDataset .value ,
268- // Huge plane size to place all datapoints
269- 10000 ,
270- 10000 ,
271- 32
272- );
275+ packCircles ({
276+ datapoints: formattedDataset .value ,
277+ width: 10000 ,
278+ height: 10000 ,
279+ maxRadius: 32
280+ });
273281}
274282
275283const svg = computed (() => {
@@ -665,6 +673,7 @@ defineExpose({
665673 : rx= " circle.radius"
666674 @mouseenter= " () => zoomTo(circle)"
667675 @mouseout= " zoom = null"
676+ @click= " emit('selectDatapoint', circle)"
668677 / >
669678 < rect
670679 v- if = " $slots.pattern"
@@ -681,41 +690,45 @@ defineExpose({
681690 }"
682691 / >
683692
684- <!-- LABEL NAME -->
685- < text
686- v- if = " FINAL_CONFIG.style.chart.circles.labels.name.show && circle.name"
687- : style= " {
688- pointerEvents: 'none',
689- transition: 'opacity 0.2s ease-in-out'
690- }"
691- : opacity= " zoom ? 0.2 : 1"
692- : x= " circle.x"
693- : y= " circle.y + calcOffsetY(circle.radius, FINAL_CONFIG.style.chart.circles.labels.name.offsetY) - circle.radius / 6"
694- : font- size= " (circle.radius / 3) * FINAL_CONFIG.style.chart.circles.labels.name.fontSizeRatio"
695- : fill= " !FINAL_CONFIG.style.chart.circles.labels.name.color ? adaptColorToBackground(circle.color) : FINAL_CONFIG.style.chart.circles.labels.name.color"
696- : font- weight= " FINAL_CONFIG.style.chart.circles.labels.name.bold ? 'bold' : 'normal'"
697- text- anchor= " middle"
698- >
699- {{ circle .name }}
700- < / text>
701-
702- <!-- LABEL VALUE -->
703- < text
704- v- if = " FINAL_CONFIG.style.chart.circles.labels.value.show"
705- : style= " {
706- pointerEvents: 'none',
707- transition: 'opacity 0.2s ease-in-out'
708- }"
709- : opacity= " zoom ? 0.2 : 1"
710- : x= " circle.x"
711- : y= " circle.y + calcOffsetY(circle.radius, FINAL_CONFIG.style.chart.circles.labels.value.offsetY) + circle.radius / 3"
712- : font- size= " getValueFontSize(circle) * FINAL_CONFIG.style.chart.circles.labels.value.fontSizeRatio"
713- : fill= " !FINAL_CONFIG.style.chart.circles.labels.value.color ? adaptColorToBackground(circle.color) : FINAL_CONFIG.style.chart.circles.labels.value.color"
714- : font- weight= " FINAL_CONFIG.style.chart.circles.labels.value.bold ? 'bold' : 'normal'"
715- text- anchor= " middle"
716- >
717- {{ getCircleLabel (circle) }}
718- < / text>
693+ < slot name= " data-label" v- if = " $slots['data-label']" v- bind= " { ...circle, createTSpans, fontSize: { name: (circle.radius / 3) * FINAL_CONFIG.style.chart.circles.labels.name.fontSizeRatio, value: getValueFontSize(circle) * FINAL_CONFIG.style.chart.circles.labels.value.fontSizeRatio}, color: !FINAL_CONFIG.style.chart.circles.labels.name.color ? adaptColorToBackground(circle.color) : FINAL_CONFIG.style.chart.circles.labels.name.color }" / >
694+
695+ < template v- else >
696+ <!-- LABEL NAME -->
697+ < text
698+ v- if = " FINAL_CONFIG.style.chart.circles.labels.name.show && circle.name"
699+ : style= " {
700+ pointerEvents: 'none',
701+ transition: 'opacity 0.2s ease-in-out'
702+ }"
703+ : opacity= " zoom ? 0.2 : 1"
704+ : x= " circle.x"
705+ : y= " circle.y + calcOffsetY(circle.radius, FINAL_CONFIG.style.chart.circles.labels.name.offsetY) - circle.radius / 6"
706+ : font- size= " (circle.radius / 3) * FINAL_CONFIG.style.chart.circles.labels.name.fontSizeRatio"
707+ : fill= " !FINAL_CONFIG.style.chart.circles.labels.name.color ? adaptColorToBackground(circle.color) : FINAL_CONFIG.style.chart.circles.labels.name.color"
708+ : font- weight= " FINAL_CONFIG.style.chart.circles.labels.name.bold ? 'bold' : 'normal'"
709+ text- anchor= " middle"
710+ >
711+ {{ circle .name }}
712+ < / text>
713+
714+ <!-- LABEL VALUE -->
715+ < text
716+ v- if = " FINAL_CONFIG.style.chart.circles.labels.value.show"
717+ : style= " {
718+ pointerEvents: 'none',
719+ transition: 'opacity 0.2s ease-in-out'
720+ }"
721+ : opacity= " zoom ? 0.2 : 1"
722+ : x= " circle.x"
723+ : y= " circle.y + calcOffsetY(circle.radius, FINAL_CONFIG.style.chart.circles.labels.value.offsetY) + circle.radius / 3"
724+ : font- size= " getValueFontSize(circle) * FINAL_CONFIG.style.chart.circles.labels.value.fontSizeRatio"
725+ : fill= " !FINAL_CONFIG.style.chart.circles.labels.value.color ? adaptColorToBackground(circle.color) : FINAL_CONFIG.style.chart.circles.labels.value.color"
726+ : font- weight= " FINAL_CONFIG.style.chart.circles.labels.value.bold ? 'bold' : 'normal'"
727+ text- anchor= " middle"
728+ >
729+ {{ getCircleLabel (circle) }}
730+ < / text>
731+ < / template>
719732
720733 <!-- DONUTS -->
721734 < template v- for = " donut in donuts" >
@@ -737,7 +750,7 @@ defineExpose({
737750 : r= " currentRadius"
738751 : opacity= " zoomOpacity"
739752 : stroke= " FINAL_CONFIG.style.chart.circles.stroke"
740- : fill= " FINAL_CONFIG.style.chart.circles.gradient.show ? `url(#${zoom.id})`: zoom.color"
753+ : fill= " FINAL_CONFIG.style.chart.circles.gradient.show ? `url(#${zoom.id})`: zoom.color"
741754 / >
742755
743756 < g v- if = " $slots['zoom-label']" : style= " { pointerEvents: 'none' }" >
0 commit comments