diff --git a/package-lock.json b/package-lock.json index c8b34b36..546e18ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "vue-data-ui", - "version": "2.0.28", + "version": "2.0.30", "lockfileVersion": 3, "requires": true, "dev": true, "packages": { "": { "name": "vue-data-ui", - "version": "2.0.28", + "version": "2.0.30", "license": "MIT", "devDependencies": { "@vitejs/plugin-vue": "^4.2.3", diff --git a/package.json b/package.json index cec4f3a9..c925414a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-data-ui", "private": false, - "version": "2.0.29", + "version": "2.0.30", "type": "module", "description": "A user-empowering data visualization Vue components library", "keywords": [ diff --git a/src/App.vue b/src/App.vue index 94389618..9d8edc1e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2451,6 +2451,30 @@ const xyConfig = ref({ const waffleConfig = ref({ style: { chart: { + layout: { + grid: { + vertical: false + }, + labels: { + dataLabels: { + prefix: "$", + suffix: "€" + }, + captions: { + "show": true, + "showSerieName": true, + "serieNameAbbreviation": true, + "serieNameMaxAbbreviationSize": 3, + "fontSize": 20, + "showValue": true, + "showPercentage": true, + "roundingValue": 0, + "roundingPercentage": 0, + "offsetX": 0, + "offsetY": 0 + } + } + }, tooltip: { customFormat: ({seriesIndex, datapoint, series, config}) => { console.log({seriesIndex, datapoint, series, config}) diff --git a/src/components/vue-ui-waffle.vue b/src/components/vue-ui-waffle.vue index 8e663155..ea1a448f 100644 --- a/src/components/vue-ui-waffle.vue +++ b/src/components/vue-ui-waffle.vue @@ -1,6 +1,8 @@ <script setup> import { ref, computed, nextTick } from "vue"; import { + abbreviate, + adaptColorToBackground, convertColorToHex, createCsvContent, createUid, @@ -61,6 +63,8 @@ const mutableConfig = ref({ showTable: waffleConfig.value.table.show }) +const captions = computed(() => waffleConfig.style.chart.layout.labels.captions) + const svg = computed(() => { const height = mutableConfig.value.inside ? 704 : 512; return { @@ -187,13 +191,21 @@ const rects = computed(() => { return cumulatedSet.value.flatMap((serie, s) => { return serie.rects.map((rect, i) => { return { + isFirst: i === 0, + isLongEnough: rect.length > 2, name: serie.name, color: serie.color, value: serie.value, serieIndex: s, + absoluteStartIndex: i < 3, ...serie } }) + }).map((s, i) => { + return { + ...s, + isAbsoluteFirst: i % waffleConfig.value.style.chart.layout.grid.size === 0, + } }) }); @@ -202,6 +214,8 @@ const positions = computed(() => { for(let i = 0; i < waffleConfig.value.style.chart.layout.grid.size; i += 1) { for(let j = 0; j < waffleConfig.value.style.chart.layout.grid.size; j += 1) { grid.push({ + isStartOfLine: j === 0, + position: waffleConfig.value.style.chart.layout.grid.vertical ? i : j, x: (waffleConfig.value.style.chart.layout.grid.vertical ? i : j) * (rectDimension.value + waffleConfig.value.style.chart.layout.grid.spaceBetween), y: (waffleConfig.value.style.chart.layout.grid.vertical ? j : i) * (rectDimension.value + waffleConfig.value.style.chart.layout.grid.spaceBetween) + drawingArea.value.top, }) @@ -543,6 +557,30 @@ defineExpose({ :stroke="waffleConfig.style.chart.layout.rect.stroke" :stroke-width="waffleConfig.style.chart.layout.rect.strokeWidth" /> + <template v-for="(position, i) in positions"> + <foreignObject + v-if="!waffleConfig.style.chart.layout.grid.vertical && waffleConfig.style.chart.layout.labels.captions.show && ((rects[i].isFirst && position.position < waffleConfig.style.chart.layout.grid.size - 3) || (rects[i].isAbsoluteFirst && i % waffleConfig.style.chart.layout.grid.size === 0 && rects[i].absoluteStartIndex))" + :x="position.x + waffleConfig.style.chart.layout.labels.captions.offsetX" + :y="position.y + waffleConfig.style.chart.layout.labels.captions.offsetY" + :height="absoluteRectDimension" + :width="absoluteRectDimension * 3" + > + <div class="vue-ui-waffle-caption" :style="`height: 100%; width: 100%; font-size:${waffleConfig.style.chart.layout.labels.captions.fontSize}px;display:flex;align-items:center;justify-content:flex-start;padding: 0 ${absoluteRectDimension / 12}px;color:${adaptColorToBackground(rects[i].color)};gap:2px`"> + <span v-if="waffleConfig.style.chart.layout.labels.captions.showSerieName"> + {{ waffleConfig.style.chart.layout.labels.captions.serieNameAbbreviation ? abbreviate({ source: rects[i].name, length: waffleConfig.style.chart.layout.labels.captions.serieNameMaxAbbreviationSize}) : rects[i].name }} : + </span> + <span v-if="waffleConfig.style.chart.layout.labels.captions.showPercentage"> + {{ dataLabel({ v: rects[i].proportion, s: '%', r: waffleConfig.style.chart.layout.labels.captions.roundingPercentage }) }} + </span> + <span v-if="waffleConfig.style.chart.layout.labels.captions.showPercentage && waffleConfig.style.chart.layout.labels.captions.showValue"> + ({{ dataLabel({ p: waffleConfig.style.chart.layout.labels.dataLabels.prefix, v: rects[i].value, s: waffleConfig.style.chart.layout.labels.dataLabels.suffix, r: waffleConfig.style.chart.layout.labels.captions.roundingValue }) }}) + </span> + <span v-if="!waffleConfig.style.chart.layout.labels.captions.showPercentage && waffleConfig.style.chart.layout.labels.captions.showValue"> + {{ dataLabel({ p: waffleConfig.style.chart.layout.labels.dataLabels.prefix, v: rects[i].value, s: waffleConfig.style.chart.layout.labels.dataLabels.suffix, r: waffleConfig.style.chart.layout.labels.captions.roundingValue }) }} + </span> + </div> + </foreignObject> + </template> <rect v-for="(position, i) in positions" :data-cy="`waffle-rect-${i}`" diff --git a/src/default_configs.json b/src/default_configs.json index e15a9d7e..886cea7a 100644 --- a/src/default_configs.json +++ b/src/default_configs.json @@ -593,10 +593,23 @@ "dataLabels": { "prefix": "", "suffix": "" + }, + "captions": { + "show": false, + "showSerieName": false, + "serieNameAbbreviation": true, + "serieNameMaxAbbreviationSize": 3, + "fontSize": 12, + "showValue": true, + "showPercentage": true, + "roundingValue": 0, + "roundingPercentage": 0, + "offsetX": 0, + "offsetY": 0 } }, "grid": { - "size": 20, + "size": 10, "spaceBetween": 2, "vertical": false }, diff --git a/types/vue-data-ui.d.ts b/types/vue-data-ui.d.ts index 72f5dad6..15fb9818 100644 --- a/types/vue-data-ui.d.ts +++ b/types/vue-data-ui.d.ts @@ -2468,6 +2468,19 @@ declare module 'vue-data-ui' { prefix?: string; suffix?: string; }; + captions?: { + show?: boolean; + showSerieName?: boolean; + serieNameAbbreviation?: boolean; + serieNameMaxAbbreviationSize?: number; + fontSize?: number; + showValue?: boolean; + showPercentage?: boolean; + roundingValue?: number; + roundingPercentage?: number; + offsetX?: number; + offsetY?: number; + }; }; grid?: { size?: number;