[READ-ONLY] Mirror of https://github.com/graphieros/vue-data-ui. An open source user-empowering data visualization Vue 3 components library for eloquent data storytelling vue-data-ui.graphieros.com/
charts components-library dashboard data-storytelling data-visualization donut gauge heatmap quadrant radar rating-stars scatter screenshot skeleton-loader sparkline table vue-data-ui vue3 vuejs vuejs3
3

Configure Feed

Select the types of activity you want to include in your feed.

fix(VueUiWordCloud): various improvements

graphieros (Jun 27, 2026, 10:07 AM +0200) a02e667f 54ab8ee9

+262 -131
+4 -2
TestingArena/ArenaVueUiWordCloud.vue
··· 41 41 CHECKBOX('userOptions.useCursorPointer', { def: false }), 42 42 CHECKBOX('userOptions.show', { def: true }), 43 43 CHECKBOX('strictPixelPadding', { def: true }), 44 + SELECT('quality', ['fast', 'balanced', 'precise'], { def: 'fast' }), 45 + 44 46 CHECKBOX('userOptions.buttons.tooltip', { def: true }), 45 47 CHECKBOX('userOptions.buttons.pdf', { def: true }), 46 48 CHECKBOX('userOptions.buttons.csv', { def: true }), ··· 64 66 NUMBER('style.chart.width', { def: 512, min: 200, max: 1000 }), 65 67 NUMBER('style.chart.words.maxFontSize', { def: 100, min: 10, max: 300 }), 66 68 NUMBER('style.chart.words.minFontSize', { def: 10, min: 10, max: 300 }), 67 - NUMBER('style.chart.words.proximity', { def: 10, min: 0, max: 50 }), 68 - NUMBER('style.chart.words.packingWeight', { def: 10, min: 0, max: 50 }), 69 + NUMBER('style.chart.words.proximity', { def: 0, min: 0, max: 20 }), 70 + 69 71 COLOR('style.chart.words.color', { def: '#1A1A1A' }), 70 72 CHECKBOX('style.chart.words.usePalette', { def: true }), 71 73 NUMBER('style.chart.words.hoverOpacity', {
+7 -4
manual-testing/src/components/TodoList.vue
··· 792 792 userOptions: { 793 793 show: false, 794 794 }, 795 - responsive: true, 795 + quality: 'precise', 796 + responsive: false, 796 797 style: { 797 798 chart: { 799 + width: WIDTH.value / 3, 800 + height: HEIGHT.value / 3, 798 801 controls: { 799 802 show: false, 800 803 }, ··· 804 807 words: { 805 808 color: '#AAAAAA', 806 809 usePalette: false, 807 - proximity: 20, 808 - packingWeight: 10, 810 + proximity: 0, 809 811 }, 810 812 zoom: { 811 813 show: false, ··· 823 825 left: 300, 824 826 width: WIDTH + 'px', 825 827 height: HEIGHT + 'px', 826 - opacity: 0.1, 828 + opacity: 0.05, 827 829 pointerEvents: 'none', 828 830 userSelect: 'none', 829 831 }" ··· 833 835 v-if="wordCloudData" 834 836 :dataset="wordCloudData" 835 837 :config="wordCloudConfig" 838 + :key="`wc_${WIDTH}_${HEIGHT}`" 836 839 /> 837 840 </div> 838 841 <button class="open-btn" @click="openDialog()">
+12 -8
src/components/vue-ui-word-cloud.vue
··· 13 13 import { debounce } from '../canvas-lib'; 14 14 import { 15 15 checkNaN, 16 + convertColorToHex, 16 17 createCsvContent, 17 18 createUid, 18 19 createWordCloudDatasetFromPlainText, ··· 465 466 fontSize, 466 467 width: size.width, 467 468 height: size.height, 468 - color: FINAL_CONFIG.value.style.chart.words.usePalette 469 - ? FINAL_CONFIG.value.customPalette[i] || 470 - FINAL_CONFIG.value.customPalette[ 471 - i % FINAL_CONFIG.value.customPalette.length 472 - ] || 473 - palette[i] || 474 - palette[i % palette.length] 475 - : FINAL_CONFIG.value.style.chart.words.color, 469 + color: word.color 470 + ? convertColorToHex(word.color) 471 + : FINAL_CONFIG.value.style.chart.words.usePalette 472 + ? FINAL_CONFIG.value.customPalette[i] || 473 + FINAL_CONFIG.value.customPalette[ 474 + i % FINAL_CONFIG.value.customPalette.length 475 + ] || 476 + palette[i] || 477 + palette[i % palette.length] 478 + : FINAL_CONFIG.value.style.chart.words.color, 476 479 }; 477 480 }); 478 481 ··· 485 488 svg: svg.value, 486 489 proximity: FINAL_CONFIG.value.style.chart.words.proximity, 487 490 strictPixelPadding: FINAL_CONFIG.value.strictPixelPadding, 491 + quality: FINAL_CONFIG.value.quality, 488 492 onProgress: ({ all }) => { 489 493 for (const w of all) { 490 494 const id = w.id;
+3 -2
src/useConfig.js
··· 5186 5186 customPalette: [], 5187 5187 useCssAnimation: false, // v2 = true 5188 5188 animationDelayMs: 20, 5189 + quality: 'fast', // 'fast' | 'balanced' | 'precise' 5189 5190 strictPixelPadding: true, // If true, strict per-pixel padding is used (dilateWordMask); if false, just rectangular bounding box (or pad). 5190 5191 userOptions: USER_OPTIONS({ 5191 5192 tooltip: true, ··· 5225 5226 maxFontSize: 100, 5226 5227 minFontSize: FONT._10, 5227 5228 bold: false, 5228 - proximity: 10, 5229 - packingWeight: 1, 5229 + proximity: 0, 5230 + packingWeight: 1, // deprecated 5230 5231 color: COLOR_TEXT_PRIMARY, 5231 5232 usePalette: true, 5232 5233 hoverOpacity: 0.5,
+231 -112
src/wordcloud.js
··· 10 10 * - position words inside a given area using a spiral placement algorithm 11 11 */ 12 12 13 + const QUALITY_PRESETS = { 14 + fast: { 15 + spiralStep: 6, 16 + fallbackSpiralStep: 2, 17 + spiralRadiusStep: 2, 18 + fallbackSpiralRadiusStep: 1, 19 + primaryAttempts: 10000, 20 + fallbackAttempts: 25000, 21 + minimumVisualPadding: 2, 22 + scaleStep: 0.9, 23 + }, 24 + balanced: { 25 + spiralStep: 4, 26 + fallbackSpiralStep: 1, 27 + spiralRadiusStep: 1.5, 28 + fallbackSpiralRadiusStep: 1, 29 + primaryAttempts: 20000, 30 + fallbackAttempts: 50000, 31 + minimumVisualPadding: 3, 32 + scaleStep: 0.92, 33 + }, 34 + precise: { 35 + spiralStep: 2, 36 + fallbackSpiralStep: 1, 37 + spiralRadiusStep: 1, 38 + fallbackSpiralRadiusStep: 0.75, 39 + primaryAttempts: 50000, 40 + fallbackAttempts: 100000, 41 + minimumVisualPadding: 4, 42 + scaleStep: 0.95, 43 + }, 44 + }; 45 + 46 + const DEFAULT_QUALITY = 'fast'; 47 + 48 + function getQualityPreset(quality) { 49 + return QUALITY_PRESETS[quality] || QUALITY_PRESETS[DEFAULT_QUALITY]; 50 + } 51 + 52 + const spiralLookupCache = new Map(); 53 + 54 + function getSpiralLookup(step) { 55 + const key = String(step); 56 + const cached = spiralLookupCache.get(key); 57 + if (cached) return cached; 58 + 59 + const degreesToRadians = Math.PI / 180; 60 + const cosineArray = []; 61 + const sineArray = []; 62 + 63 + for (let angleDegrees = 0; angleDegrees < 360; angleDegrees += step) { 64 + const angle = angleDegrees * degreesToRadians; 65 + cosineArray.push(Math.cos(angle)); 66 + sineArray.push(Math.sin(angle)); 67 + } 68 + 69 + const lookup = { 70 + cosineArray, 71 + sineArray, 72 + }; 73 + 74 + spiralLookupCache.set(key, lookup); 75 + return lookup; 76 + } 77 + 13 78 /* ------------------------------------------------------------------------- */ 14 79 /* Bitmap generation */ 15 80 /* ------------------------------------------------------------------------- */ ··· 48 113 svg, 49 114 }) { 50 115 const isBold = svg.style && svg.style.bold; 51 - const font = `${isBold ? 'bold ' : ''}${fontSize}px Arial`; 116 + const fontFamily = 117 + (svg.style && svg.style.fontFamily) || 118 + 'Arial, "Noto Sans Arabic", Tahoma, sans-serif'; 119 + 120 + const font = `${isBold ? 'bold ' : ''}${fontSize}px ${fontFamily}`; 121 + const wordName = String(word.name || ''); 122 + const hasArabic = /[\u0600-\u06FF]/.test(wordName); 52 123 53 124 context.font = font; 54 - const metrics = context.measureText(word.name); 55 - const textWidth = Math.ceil(metrics.width) + 2 + (pad ? pad * 2 : 0); 56 - const textHeight = Math.ceil(fontSize) + 2 + (pad ? pad * 2 : 0); 125 + context.direction = hasArabic ? 'rtl' : 'ltr'; 126 + context.textAlign = 'left'; 127 + context.textBaseline = 'alphabetic'; 128 + 129 + const metrics = context.measureText(wordName); 130 + 131 + const actualLeft = Math.ceil(Math.abs(metrics.actualBoundingBoxLeft || 0)); 132 + const actualRight = Math.ceil( 133 + Math.abs(metrics.actualBoundingBoxRight || metrics.width), 134 + ); 135 + const actualAscent = Math.ceil(metrics.actualBoundingBoxAscent || fontSize); 136 + const actualDescent = Math.ceil( 137 + metrics.actualBoundingBoxDescent || fontSize * 0.25, 138 + ); 139 + 140 + const basePadding = Math.max(0, Math.round(pad || 0)); 141 + 142 + // Arabic glyphs, ligatures, diacritics, browser font fallback, and SVG 143 + // strokes can extend beyond the simple text advance box. A safety 144 + // padding makes the collision bitmap closer to the final rendering 145 + const visualSafetyPadding = Math.ceil(fontSize * (hasArabic ? 0.22 : 0.12)); 146 + const totalPadding = basePadding + visualSafetyPadding; 147 + 148 + const textWidth = Math.max(1, actualLeft + actualRight + totalPadding * 2); 149 + const textHeight = Math.max( 150 + 1, 151 + actualAscent + actualDescent + totalPadding * 2, 152 + ); 57 153 58 154 canvas.width = textWidth; 59 155 canvas.height = textHeight; 60 - 61 156 context.font = font; 62 - context.textAlign = 'center'; 63 - context.textBaseline = 'middle'; 157 + context.direction = hasArabic ? 'rtl' : 'ltr'; 158 + context.textAlign = 'left'; 159 + context.textBaseline = 'alphabetic'; 64 160 context.fillStyle = 'black'; 65 - context.fillText(word.name, textWidth / 2, textHeight / 2); 161 + 162 + const drawX = totalPadding + actualLeft; 163 + const drawY = totalPadding + actualAscent; 164 + 165 + context.fillText(wordName, drawX, drawY); 66 166 67 167 const image = context.getImageData(0, 0, textWidth, textHeight); 68 168 const data = image.data; ··· 505 605 /* ------------------------------------------------------------------------- */ 506 606 507 607 /** 508 - * Internal: dilate a bitmap expressed as runs and return new runs. 509 - * This avoids building a per-pixel coordinate list in the hot path. 608 + * Internal: merge per-row run buckets into a sorted, non-overlapping run list. 609 + * Each bucket entry is an `[xStart, xEnd]` pair for a given row. Runs that 610 + * overlap or touch are merged into a single `[y, xStart, xEnd]` output run 510 611 */ 511 - function dilateRunsToRuns({ runs, w: width, h: height, dilation }) { 512 - const grid = new Uint8Array(width * height); 513 - const seeds = []; 612 + function mergeRunBuckets(rowBuckets, height) { 613 + const outputRuns = []; 514 614 515 - for (let index = 0; index < runs.length; index += 1) { 516 - const row = runs[index]; 517 - const y = row[0]; 518 - const xStart = row[1]; 519 - const xEnd = row[2]; 615 + for (let y = 0; y < height; y += 1) { 616 + const bucket = rowBuckets[y]; 617 + if (!bucket || bucket.length === 0) continue; 520 618 521 - const rowOffset = y * width; 619 + bucket.sort((a, b) => a[0] - b[0]); 522 620 523 - for (let x = xStart; x <= xEnd; x += 1) { 524 - const flatIndex = rowOffset + x; 621 + let currentStart = bucket[0][0]; 622 + let currentEnd = bucket[0][1]; 525 623 526 - if (!grid[flatIndex]) { 527 - grid[flatIndex] = 1; 528 - seeds.push(flatIndex); 624 + for (let index = 1; index < bucket.length; index += 1) { 625 + const start = bucket[index][0]; 626 + const end = bucket[index][1]; 627 + 628 + if (start <= currentEnd + 1) { 629 + if (end > currentEnd) currentEnd = end; 630 + } else { 631 + outputRuns.push([y, currentStart, currentEnd]); 632 + currentStart = start; 633 + currentEnd = end; 529 634 } 530 635 } 636 + 637 + outputRuns.push([y, currentStart, currentEnd]); 531 638 } 532 639 533 - for (let seedIndex = 0; seedIndex < seeds.length; seedIndex += 1) { 534 - const flatIndex = seeds[seedIndex]; 535 - const y = (flatIndex / width) | 0; 536 - const x = flatIndex - y * width; 640 + return outputRuns; 641 + } 537 642 538 - for (let deltaY = -dilation; deltaY <= dilation; deltaY += 1) { 539 - const neighborY = y + deltaY; 540 - if (neighborY < 0 || neighborY >= height) continue; 643 + /** 644 + * Internal: dilate a bitmap expressed as runs and return new runs. 645 + * This avoids building a per-pixel coordinate list in the hot path. 646 + */ 647 + function dilateRunsToRuns({ runs, w: width, h: height, dilation }) { 648 + if (!runs.length || dilation <= 0) return runs; 541 649 542 - const neighborRowOffset = neighborY * width; 650 + const rowBuckets = new Array(height); 543 651 544 - for (let deltaX = -dilation; deltaX <= dilation; deltaX += 1) { 545 - if (deltaX === 0 && deltaY === 0) continue; 652 + for (let index = 0; index < runs.length; index += 1) { 653 + const row = runs[index]; 654 + const y = row[0]; 655 + const xStart = Math.max(0, row[1] - dilation); 656 + const xEnd = Math.min(width - 1, row[2] + dilation); 546 657 547 - const neighborX = x + deltaX; 548 - if (neighborX < 0 || neighborX >= width) continue; 549 - 550 - grid[neighborRowOffset + neighborX] = 1; 551 - } 552 - } 553 - } 658 + const yStart = Math.max(0, y - dilation); 659 + const yEnd = Math.min(height - 1, y + dilation); 554 660 555 - const outputRuns = []; 661 + for (let expandedY = yStart; expandedY <= yEnd; expandedY += 1) { 662 + let bucket = rowBuckets[expandedY]; 556 663 557 - for (let y = 0; y < height; y += 1) { 558 - const rowOffset = y * width; 559 - let runStart = -1; 560 - let isInRun = false; 561 - 562 - for (let x = 0; x < width; x += 1) { 563 - const value = grid[rowOffset + x]; 564 - 565 - if (value) { 566 - if (!isInRun) { 567 - isInRun = true; 568 - runStart = x; 569 - } 570 - } else if (isInRun) { 571 - outputRuns.push([y, runStart, x - 1]); 572 - isInRun = false; 573 - runStart = -1; 664 + if (!bucket) { 665 + bucket = []; 666 + rowBuckets[expandedY] = bucket; 574 667 } 575 - } 576 668 577 - if (isInRun) { 578 - outputRuns.push([y, runStart, width - 1]); 669 + bucket.push([xStart, xEnd]); 579 670 } 580 671 } 581 672 582 - return outputRuns; 673 + return mergeRunBuckets(rowBuckets, height); 583 674 } 584 675 585 676 /* ------------------------------------------------------------------------- */ ··· 749 840 } 750 841 751 842 /* ------------------------------------------------------------------------- */ 752 - /* Precomputed spiral angles */ 753 - /* ------------------------------------------------------------------------- */ 754 - 755 - const spiralStep = 6; 756 - const fallbackSpiralStep = 2; 757 - const degreesToRadians = Math.PI / 180; 758 - 759 - const spiralCosValues = []; 760 - const spiralSinValues = []; 761 - 762 - for (let angleDegrees = 0; angleDegrees < 360; angleDegrees += spiralStep) { 763 - const angle = angleDegrees * degreesToRadians; 764 - spiralCosValues.push(Math.cos(angle)); 765 - spiralSinValues.push(Math.sin(angle)); 766 - } 767 - 768 - const fallbackSpiralCosValues = []; 769 - const fallbackSpiralSinValues = []; 770 - 771 - for ( 772 - let angleDegrees = 0; 773 - angleDegrees < 360; 774 - angleDegrees += fallbackSpiralStep 775 - ) { 776 - const angle = angleDegrees * degreesToRadians; 777 - fallbackSpiralCosValues.push(Math.cos(angle)); 778 - fallbackSpiralSinValues.push(Math.sin(angle)); 779 - } 780 - 781 - /* ------------------------------------------------------------------------- */ 782 843 /* Layout scaling */ 783 844 /* ------------------------------------------------------------------------- */ 784 845 ··· 899 960 /* Placement helpers */ 900 961 /* ------------------------------------------------------------------------- */ 901 962 963 + function getCollisionDilation({ 964 + bitmapHeight, 965 + proximity, 966 + strictPixelPadding, 967 + minimumVisualPadding, 968 + }) { 969 + const proximityPadding = Math.max(0, Math.round(proximity || 0)); 970 + 971 + // SVG rendering can extend slightly beyond the canvas glyph mask because 972 + // of stroke, antialiasing, font hinting, and browser text rendering. 973 + const strokePadding = Math.ceil(bitmapHeight * 0.035) + 2; 974 + 975 + const strictPadding = strictPixelPadding ? 3 : 0; 976 + 977 + return Math.max( 978 + proximityPadding, 979 + strokePadding, 980 + strictPadding, 981 + minimumVisualPadding, 982 + ); 983 + } 984 + 902 985 function buildRunsForBitmap({ 903 986 currentBitmap, 904 987 strictPixelPadding, 905 988 scaleFactor, 906 989 baseBitmap, 907 990 bitmapKey, 991 + proximity, 992 + minimumVisualPadding, 908 993 }) { 909 994 let runs = currentBitmap.runs; 910 995 const bitmapWidth = currentBitmap.w; ··· 914 999 const bitmapMaximumX = currentBitmap.maxX; 915 1000 const bitmapMaximumY = currentBitmap.maxY; 916 1001 917 - if (!strictPixelPadding || !runs.length) { 1002 + if (!runs.length) { 1003 + return { 1004 + runs, 1005 + bitmapWidth, 1006 + bitmapHeight, 1007 + bitmapMinimumX, 1008 + bitmapMinimumY, 1009 + bitmapMaximumX, 1010 + bitmapMaximumY, 1011 + }; 1012 + } 1013 + 1014 + const dilation = getCollisionDilation({ 1015 + bitmapHeight, 1016 + proximity, 1017 + strictPixelPadding, 1018 + minimumVisualPadding, 1019 + }); 1020 + 1021 + if (dilation <= 0) { 918 1022 return { 919 1023 runs, 920 1024 bitmapWidth, ··· 926 1030 }; 927 1031 } 928 1032 929 - if (scaleFactor === 1) { 1033 + if (scaleFactor === 1 && dilation === 2 && strictPixelPadding) { 930 1034 const dilated = getCachedDilatedMask({ 931 1035 bitmapKey, 932 1036 wordMask: baseBitmap.wordMask, 933 1037 w: baseBitmap.w, 934 1038 h: baseBitmap.h, 935 - dilation: 2, 1039 + dilation, 936 1040 }); 937 1041 938 1042 return { ··· 950 1054 runs, 951 1055 w: bitmapWidth, 952 1056 h: bitmapHeight, 953 - dilation: 2, 1057 + dilation, 954 1058 }); 955 1059 956 1060 return { ··· 985 1089 radiusStep, 986 1090 maximumAttempts, 987 1091 maybeYield, 1092 + proximity, 1093 + minimumVisualPadding, 988 1094 }) { 989 1095 let scaleFactor = 1; 990 1096 ··· 1000 1106 scaleFactor, 1001 1107 baseBitmap, 1002 1108 bitmapKey, 1109 + proximity, 1110 + minimumVisualPadding, 1003 1111 }); 1004 1112 1005 1113 const { ··· 1117 1225 minimumFontSize, 1118 1226 rawWord, 1119 1227 maybeYield, 1120 - spiralRadiusStep, 1121 - fallbackSpiralRadiusStep, 1228 + proximity, 1229 + qualityPreset, 1230 + primarySpiral, 1231 + fallbackSpiral, 1122 1232 }) { 1123 1233 const primaryPlacement = await tryPlaceWordOnSpiral({ 1124 1234 baseBitmap, ··· 1136 1246 bitmapKey, 1137 1247 minimumFontSize, 1138 1248 rawWord, 1139 - cosineArray: spiralCosValues, 1140 - sineArray: spiralSinValues, 1141 - radiusStep: spiralRadiusStep, 1142 - maximumAttempts: 10000, 1249 + cosineArray: primarySpiral.cosineArray, 1250 + sineArray: primarySpiral.sineArray, 1251 + radiusStep: qualityPreset.spiralRadiusStep, 1252 + maximumAttempts: qualityPreset.primaryAttempts, 1143 1253 maybeYield, 1254 + proximity, 1255 + minimumVisualPadding: qualityPreset.minimumVisualPadding, 1144 1256 }); 1145 1257 1146 1258 if (primaryPlacement) { ··· 1163 1275 bitmapKey, 1164 1276 minimumFontSize, 1165 1277 rawWord, 1166 - cosineArray: fallbackSpiralCosValues, 1167 - sineArray: fallbackSpiralSinValues, 1168 - radiusStep: fallbackSpiralRadiusStep, 1169 - maximumAttempts: 25000, 1278 + cosineArray: fallbackSpiral.cosineArray, 1279 + sineArray: fallbackSpiral.sineArray, 1280 + radiusStep: qualityPreset.fallbackSpiralRadiusStep, 1281 + maximumAttempts: qualityPreset.fallbackAttempts, 1170 1282 maybeYield, 1283 + proximity, 1284 + minimumVisualPadding: qualityPreset.minimumVisualPadding, 1171 1285 }); 1172 1286 1173 1287 return fallbackPlacement; ··· 1186 1300 proximity = 0, 1187 1301 svg, 1188 1302 strictPixelPadding, 1303 + quality = DEFAULT_QUALITY, 1189 1304 onProgress, 1190 1305 debugTiming = false, 1191 1306 }) { ··· 1207 1322 const height = svg.height; 1208 1323 const maskWidth = Math.round(width); 1209 1324 const maskHeight = Math.round(height); 1325 + 1326 + const qualityPreset = getQualityPreset(quality); 1327 + const primarySpiral = getSpiralLookup(qualityPreset.spiralStep); 1328 + const fallbackSpiral = getSpiralLookup(qualityPreset.fallbackSpiralStep); 1210 1329 1211 1330 const minimumFontSize = 1; 1212 1331 const configuredMinimumFontSize = svg.minFontSize; ··· 1226 1345 canvas.width = maskWidth; 1227 1346 canvas.height = maskHeight; 1228 1347 1229 - const spiralRadiusStep = 2; 1230 - const fallbackSpiralRadiusStep = 1; 1231 1348 const maximumRadius = Math.max(maskWidth, maskHeight); 1232 1349 const centerX = Math.floor(maskWidth / 2); 1233 1350 const centerY = Math.floor(maskHeight / 2); ··· 1243 1360 ); 1244 1361 1245 1362 const positionedWords = []; 1246 - const scaleStep = 0.9; 1363 + const scaleStep = qualityPreset.scaleStep; 1247 1364 1248 1365 for ( 1249 1366 let sortedIndex = 0; ··· 1305 1422 minimumFontSize, 1306 1423 rawWord, 1307 1424 maybeYield, 1308 - spiralRadiusStep, 1309 - fallbackSpiralRadiusStep, 1425 + proximity, 1426 + qualityPreset, 1427 + primarySpiral, 1428 + fallbackSpiral, 1310 1429 }); 1311 1430 1312 1431 if (placedWord) {
+1 -1
ts-playground/src/components/charts/ts-vue-ui-word-cloud.vue
··· 149 149 maxFontSize: 100, 150 150 minFontSize: 10, 151 151 bold: false, 152 - proximity: 10, 152 + proximity: 0, 153 153 packingWeight: 1, 154 154 color: '#2D353C', 155 155 usePalette: true,
+4 -2
types/vue-data-ui.d.ts
··· 11505 11505 [key: string]: any; 11506 11506 name: string; 11507 11507 value: number; 11508 + color?: string; 11508 11509 }; 11509 11510 11510 11511 export type VueUiWordCloudDatapoint = { ··· 11547 11548 VueUiWordCloudConfig 11548 11549 >; 11549 11550 useCssAnimation?: boolean; 11550 - animationDelayMs?: number; 11551 + animationDelayMs?: number; // deprectated 11551 11552 strictPixelPadding?: boolean; 11553 + quality: 'fast' | 'balanced' | 'precise'; 11552 11554 style?: { 11553 11555 fontFamily?: string; 11554 11556 chart?: { ··· 11563 11565 minFontSize?: number; 11564 11566 bold?: boolean; 11565 11567 proximity?: number; 11566 - packingWeight?: number; 11568 + packingWeight?: number; // deprecated 11567 11569 color?: string; 11568 11570 usePalette?: boolean; 11569 11571 hoverOpacity?: number;