diff --git a/TestingArena/ArenaVueUiXy.vue b/TestingArena/ArenaVueUiXy.vue index 6d7916d3..416ab00a 100644 --- a/TestingArena/ArenaVueUiXy.vue +++ b/TestingArena/ArenaVueUiXy.vue @@ -53,60 +53,97 @@ function createDs(n,m=100) { // }, // ]) - const dataset = ref([ - { - name: "Long name serie", - series: [-80, -60, -30, 0, null, 60, 80, 60, 30, 0, -30, -60, -80], - comments: ["", "", "", "", "This is a comment that can be long, or that can be short but it depends."], - type: "line", - smooth: true, - useArea: true, - dataLabels: true, - scaleSteps: 2, - suffix: '$' - }, - { - name: "Long name serie", - series: [10, 20, 12, 13, 10, -20, null, 20, 12, 16, 32, 64, 12], - comments: ["", "", "", "", "This is a comment that can be long, or that can be short but it depends."], - type: "bar", - smooth: false, - useArea: true, - dataLabels: true, - scaleSteps: 2, - prefix: '£' - }, - { - name: "S1", - series: [-20, 20, 8, 16, null, 13, -16, 55, 12, 3, 7, 12, 6], - comments: ["Some sort of negative comment", "Some sort of positive comment", "", "","", "", "", "Some sort of positive comment", "", ""], - type: "bar", - smooth: false, - useArea: true, - scaleSteps: 2, - color: "#FF000050" - }, - { - name: "S2", - series: [10,12,10,12, 25, 12, 4, 4, 3, 7, 8, 9, 12], - comments: ["", "", "", "","", "", "", "", "", "This is another comment"], - type: "plot", - smooth: false, - useArea: true, - scaleSteps: 2 - }, - { - name: "S3", - series: [23.12, 23.12, 23.05, 23.07, null, 23.69, 23.72, 23.25, 23.36, 23.41, 23.65], - type: "line", - smooth: false, - useArea: true, - scaleSteps: 5, - autoScaling: false, - stackRatio: 0.5 - }, - ]) +{ + name: 'Donut Devourers - Happiness (%)', + type: 'line', + dataLabels: true, + scaleLabel: 'percentage', // Share scale with other percentage-based series + series: [92.34, 88.37, 95.00] // Example percentages across different days +}, +{ + name: 'Donut Devourers - Donuts Eaten', + type: 'bar', + dataLabels: true, + scaleLabel: 'total', // Share scale with other total-based series + series: [12, 15, 13] // Example counts per day +}, +{ + name: 'Pizza Party - Cheesy Delight (%)', + type: 'line', + dataLabels: true, + scaleLabel: 'percentage', + series: [75.12, 80.50, 78.34] +}, +{ + name: 'Pizza Party - Slices Consumed', + type: 'bar', + dataLabels: true, + scaleLabel: 'total', + series: [8, 9, 10] +}, +{ + name: 'Lone series ', + type: 'bar', + dataLabels: true, + series: [8, 9, 100] +} +]) + + +// const dataset = ref([ +// { +// name: "Long name serie", +// series: [-80, -60, -30, 0, null, 60, 80, 60, 30, 0, -30, -60, -80], +// comments: ["", "", "", "", "This is a comment that can be long, or that can be short but it depends."], +// type: "line", +// smooth: true, +// useArea: true, +// dataLabels: true, +// scaleSteps: 2, +// suffix: '$' +// }, +// { +// name: "Long name serie", +// series: [10, 20, 12, 13, 10, -20, null, 20, 12, 16, 32, 64, 12], +// comments: ["", "", "", "", "This is a comment that can be long, or that can be short but it depends."], +// type: "bar", +// smooth: false, +// useArea: true, +// dataLabels: true, +// scaleSteps: 2, +// prefix: '£' +// }, +// { +// name: "S1", +// series: [-20, 20, 8, 16, null, 13, -16, 55, 12, 3, 7, 12, 6], +// comments: ["Some sort of negative comment", "Some sort of positive comment", "", "","", "", "", "Some sort of positive comment", "", ""], +// type: "bar", +// smooth: false, +// useArea: true, +// scaleSteps: 2, +// color: "#FF000050" +// }, +// { +// name: "S2", +// series: [10,12,10,12, 25, 12, 4, 4, 3, 7, 8, 9, 12], +// comments: ["", "", "", "","", "", "", "", "", "This is another comment"], +// type: "plot", +// smooth: false, +// useArea: true, +// scaleSteps: 2 +// }, +// { +// name: "S3", +// series: [23.12, 23.12, 23.05, 23.07, null, 23.69, 23.72, 23.25, 23.36, 23.41, 23.65], +// type: "line", +// smooth: false, +// useArea: true, +// scaleSteps: 5, +// autoScaling: false, +// stackRatio: 0.5 +// }, +// ]) const alternateDataset = ref([ { @@ -291,13 +328,14 @@ const model = ref([ { key: 'chart.grid.labels.xAxisLabels.modulo', def: 6, type: 'number'}, { key: 'chart.grid.labels.yAxis.commonScaleSteps', def: 10, min: 0, max: 100, type: 'number' }, - { key: 'chart.grid.labels.yAxis.useIndividualScale', def: false, type: "checkbox" }, + { key: 'chart.grid.labels.yAxis.useIndividualScale', def: true, type: "checkbox" }, { key: 'chart.grid.labels.yAxis.stacked', def: false, type: 'checkbox' }, { key: 'chart.grid.labels.yAxis.gap', def: 12, min: 0, max: 200, type: 'number' }, { key: 'chart.grid.labels.yAxis.labelWidth', def: 40, min: 0, max: 100, type: 'number' }, { key: 'chart.grid.labels.yAxis.showBaseline', def: true, type: 'checkbox'}, { key: 'chart.grid.labels.yAxis.scaleMin', def: -90, type: 'number', min: -1000, max: 1000 }, { key: 'chart.grid.labels.yAxis.scaleMax', def: 90, type: 'number', min: -1000, max: 1000 }, + { key: 'chart.grid.labels.yAxis.groupColor', def: '#1A1A1A', type: 'color'}, { key: 'chart.grid.labels.xAxis.showBaseline', def: true, type: 'checkbox'}, { key: 'chart.grid.labels.zeroLine.show', def: true, type: 'checkbox'}, @@ -482,6 +520,14 @@ const config = computed(() => { }, chart: { ...c.chart, + // Attempt a scale groups + scaleGroups: { + enabled: true, + groups: [ + { name: 'percentage', scaleMin: 0, scaleMax: 100 }, + { name: 'total', scaleMin: null, scaleMax: null } + ] + }, highlightArea: [{ show: true, from: 2, diff --git a/package-lock.json b/package-lock.json index 649b13dd..cc8369f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "vue-data-ui", - "version": "2.6.24", + "version": "2.6.25-beta.2", "lockfileVersion": 3, "requires": true, "dev": true, "packages": { "": { "name": "vue-data-ui", - "version": "2.6.24", + "version": "2.6.25-beta.2", "license": "MIT", "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", @@ -18,8 +18,8 @@ "remove-attr": "^0.0.13", "sass": "^1.57.1", "simple-git": "^3.24.0", - "vite": "^6.2.0", - "vitest": "^3.0.5", + "vite": "^6.2.4", + "vitest": "^3.1.1", "vue": "^3.5.13" }, "peerDependencies": { @@ -878,14 +878,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz", - "integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", + "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", "dev": true, "dependencies": { - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "@vitest/spy": "3.1.1", + "@vitest/utils": "3.1.1", + "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, "funding": { @@ -893,12 +893,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz", - "integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", + "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", "dev": true, "dependencies": { - "@vitest/spy": "3.0.5", + "@vitest/spy": "3.1.1", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -928,9 +928,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz", - "integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", + "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", "dev": true, "dependencies": { "tinyrainbow": "^2.0.0" @@ -940,36 +940,36 @@ } }, "node_modules/@vitest/runner": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz", - "integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", + "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", "dev": true, "dependencies": { - "@vitest/utils": "3.0.5", - "pathe": "^2.0.2" + "@vitest/utils": "3.1.1", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz", - "integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", + "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", "dev": true, "dependencies": { - "@vitest/pretty-format": "3.0.5", + "@vitest/pretty-format": "3.1.1", "magic-string": "^0.30.17", - "pathe": "^2.0.2" + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz", - "integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", + "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", "dev": true, "dependencies": { "tinyspy": "^3.0.2" @@ -979,13 +979,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz", - "integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", + "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", "dev": true, "dependencies": { - "@vitest/pretty-format": "3.0.5", - "loupe": "^3.1.2", + "@vitest/pretty-format": "3.1.1", + "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, "funding": { @@ -1441,9 +1441,9 @@ "dev": true }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", "dev": true, "dependencies": { "assertion-error": "^2.0.1", @@ -2045,11 +2045,10 @@ } }, "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", + "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } @@ -2975,9 +2974,9 @@ } }, "node_modules/pathe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", - "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true }, "node_modules/pathval": { @@ -3483,11 +3482,10 @@ } }, "node_modules/std-env": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", - "dev": true, - "license": "MIT" + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz", + "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==", + "dev": true }, "node_modules/string-width": { "version": "4.2.3", @@ -3756,11 +3754,10 @@ } }, "node_modules/vite": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", - "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz", + "integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", @@ -3828,15 +3825,15 @@ } }, "node_modules/vite-node": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", - "integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", + "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", "dev": true, "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", - "pathe": "^2.0.2", + "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { @@ -3850,30 +3847,30 @@ } }, "node_modules/vitest": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz", - "integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==", - "dev": true, - "dependencies": { - "@vitest/expect": "3.0.5", - "@vitest/mocker": "3.0.5", - "@vitest/pretty-format": "^3.0.5", - "@vitest/runner": "3.0.5", - "@vitest/snapshot": "3.0.5", - "@vitest/spy": "3.0.5", - "@vitest/utils": "3.0.5", - "chai": "^5.1.2", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", + "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "dev": true, + "dependencies": { + "@vitest/expect": "3.1.1", + "@vitest/mocker": "3.1.1", + "@vitest/pretty-format": "^3.1.1", + "@vitest/runner": "3.1.1", + "@vitest/snapshot": "3.1.1", + "@vitest/spy": "3.1.1", + "@vitest/utils": "3.1.1", + "chai": "^5.2.0", "debug": "^4.4.0", - "expect-type": "^1.1.0", + "expect-type": "^1.2.0", "magic-string": "^0.30.17", - "pathe": "^2.0.2", - "std-env": "^3.8.0", + "pathe": "^2.0.3", + "std-env": "^3.8.1", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.0.5", + "vite-node": "3.1.1", "why-is-node-running": "^2.3.0" }, "bin": { @@ -3889,8 +3886,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.0.5", - "@vitest/ui": "3.0.5", + "@vitest/browser": "3.1.1", + "@vitest/ui": "3.1.1", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 8babf6a8..a978b449 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-data-ui", "private": false, - "version": "2.6.24", + "version": "2.6.25-beta.2", "type": "module", "description": "A user-empowering data visualization Vue 3 components library for eloquent data storytelling", "keywords": [ @@ -103,8 +103,8 @@ "remove-attr": "^0.0.13", "sass": "^1.57.1", "simple-git": "^3.24.0", - "vite": "^6.2.0", - "vitest": "^3.0.5", + "vite": "^6.2.4", + "vitest": "^3.1.1", "vue": "^3.5.13" } } \ No newline at end of file diff --git a/src/components/vue-ui-xy.vue b/src/components/vue-ui-xy.vue index b8c4ca41..12839de0 100644 --- a/src/components/vue-ui-xy.vue +++ b/src/components/vue-ui-xy.vue @@ -311,7 +311,7 @@ <!-- BARS --> <template v-if="barSet.length"> - <g v-for="(serie, i) in barSet" :key="`serie_bar_${i}`" :class="`serie_bar_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <g v-for="(serie, i) in barSet" :key="`serie_bar_${i}`" :class="`serie_bar_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> <g v-for="(plot, j) in serie.plots" :key="`bar_plot_${i}_${j}`" @@ -454,7 +454,8 @@ text-anchor="middle" :transform="`translate(${el.x - FINAL_CONFIG.chart.grid.labels.yAxis.labelWidth + 5 + xPadding}, ${mutableConfig.isStacked ? drawingArea.bottom - el.yOffset - (el.individualHeight / 2) : drawingArea.top + drawingArea.height / 2}) rotate(-90)`" > - {{ el.name }} {{ el.scaleLabel ? `- ${el.scaleLabel}` : '' }} + {{ el.name }} {{ el.scaleLabel && el.unique && el.scaleLabel !== el.id ? `- ${el.scaleLabel}` : '' }} + </text> <line v-for="(yLabel, j) in el.yLabels" @@ -528,7 +529,7 @@ </g> <!-- PLOTS --> - <g v-for="(serie, i) in plotSet" :key="`serie_plot_${i}`" :class="`serie_plot_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <g v-for="(serie, i) in plotSet" :key="`serie_plot_${i}`" :class="`serie_plot_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> <g data-cy="datapoint-plot" v-for="(plot, j) in serie.plots" @@ -593,7 +594,7 @@ </g> <!-- LINE COATINGS --> - <g v-for="(serie, i) in lineSet" :key="`serie_line_${i}`" :class="`serie_line_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <g v-for="(serie, i) in lineSet" :key="`serie_line_${i}`" :class="`serie_line_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> <path data-cy="datapoint-line-coating-smooth" v-if="serie.smooth && serie.plots.length > 1" @@ -622,7 +623,7 @@ </defs> <!-- LINES --> - <g v-for="(serie, i) in lineSet" :key="`serie_line_${i}`" :class="`serie_line_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <g v-for="(serie, i) in lineSet" :key="`serie_line_${i}`" :class="`serie_line_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> <g v-if="serie.useArea && serie.plots.length > 1"> <template v-if="serie.smooth"> @@ -726,8 +727,8 @@ <!-- X LABELS BAR --> <g v-if="(FINAL_CONFIG.bar.labels.show || FINAL_CONFIG.bar.serieName.show) && mutableConfig.dataLabels.show"> - <template v-for="(serie, i) in barSet" :key="`xLabel_bar_${i}`" :class="`xLabel_bar_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> - <template v-for="(plot, j) in serie.plots" :key="`xLabel_bar_${i}_${j}`"> + <template v-for="(serie, i) in barSet" :key="`xLabel_bar_${i}`" :class="`xLabel_bar_${i}`" > + <template v-for="(plot, j) in serie.plots" :key="`xLabel_bar_${i}_${j}`" > <text data-cy="datapoint-bar-label" v-if="plot && (!Object.hasOwn(serie, 'dataLabels') || ((serie.dataLabels === true || (selectedSerieIndex !== null && selectedSerieIndex === j) || (selectedMinimapIndex !== null && selectedMinimapIndex === j)))) && FINAL_CONFIG.bar.labels.show" @@ -736,6 +737,7 @@ text-anchor="middle" :font-size="fontSizes.plotLabels" :fill="FINAL_CONFIG.bar.labels.color" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > {{ canShowValue(plot.value) ? applyDataLabel( FINAL_CONFIG.bar.labels.formatter, @@ -761,6 +763,7 @@ :font-size="fontSizes.plotLabels" :fill="FINAL_CONFIG.bar.serieName.useSerieColor ? serie.color : FINAL_CONFIG.bar.serieName.color" :font-weight="FINAL_CONFIG.bar.serieName.bold ? 'bold' : 'normal'" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > {{ FINAL_CONFIG.bar.serieName.useAbbreviation ? abbreviate({ source: serie.name, length: FINAL_CONFIG.bar.serieName.abbreviationSize}) : serie.name }} </text> @@ -770,7 +773,7 @@ <!-- X LABELS PLOT --> <g v-if="FINAL_CONFIG.plot.labels.show && mutableConfig.dataLabels.show"> - <template v-for="(serie, i) in plotSet" :key="`xLabel_plot_${i}`" :class="`xLabel_plot_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <template v-for="(serie, i) in plotSet" :key="`xLabel_plot_${i}`" :class="`xLabel_plot_${i}`"> <template v-for="(plot, j) in serie.plots" :key="`xLabel_plot_${i}_${j}`"> <text data-cy="datapoint-plot-label" @@ -780,6 +783,7 @@ text-anchor="middle" :font-size="fontSizes.plotLabels" :fill="FINAL_CONFIG.plot.labels.color" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > {{ canShowValue(plot.value) ? applyDataLabel( FINAL_CONFIG.plot.labels.formatter, @@ -804,7 +808,7 @@ :y="plot.y - 20" :height="24" width="150" - style="overflow: visible" + :style="`overflow: visible; opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > <div :style="`padding: 3px; background:${setOpacity(serie.color, 80)};color:${adaptColorToBackground(serie.color)};width:fit-content;font-size:${fontSizes.plotLabels}px;border-radius: 2px;`"> {{ serie.name }} @@ -817,7 +821,7 @@ :y="plot.y - 20" :height="24" width="150" - style="overflow: visible" + :style="`overflow: visible; opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > <div :style="`padding: 3px; background:${setOpacity(serie.color, 80)};color:${adaptColorToBackground(serie.color)};width:fit-content;font-size:${fontSizes.plotLabels}px;border-radius: 2px;`"> {{ serie.name }} @@ -829,7 +833,7 @@ <!-- X LABELS LINE --> <g v-if="FINAL_CONFIG.line.labels.show && mutableConfig.dataLabels.show"> - <template v-for="(serie, i) in lineSet" :key="`xLabel_line_${i}`" :class="`xLabel_line_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <template v-for="(serie, i) in lineSet" :key="`xLabel_line_${i}`" :class="`xLabel_line_${i}`"> <template v-for="(plot, j) in serie.plots" :key="`xLabel_line_${i}_${j}`"> <text data-cy="datapoint-line-label" @@ -839,6 +843,7 @@ text-anchor="middle" :font-size="fontSizes.plotLabels" :fill="FINAL_CONFIG.line.labels.color" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > {{ canShowValue(plot.value) ? applyDataLabel( FINAL_CONFIG.line.labels.formatter, @@ -863,7 +868,7 @@ :y="plot.y - 20" :height="24" width="150" - style="overflow: visible" + :style="`overflow: visible; opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > <div :style="`padding: 3px; background:${setOpacity(serie.color, 80)};color:${adaptColorToBackground(serie.color)};width:fit-content;font-size:${fontSizes.plotLabels}px;border-radius: 2px;`"> {{ serie.name }} @@ -876,7 +881,7 @@ :y="plot.y - 20" :height="24" width="150" - style="overflow: visible" + :style="`overflow: visible; opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" > <div :style="`padding: 3px; background:${setOpacity(serie.color, 80)};color:${adaptColorToBackground(serie.color)};width:fit-content;font-size:${fontSizes.plotLabels}px;border-radius: 2px;`"> {{ serie.name }} @@ -887,7 +892,7 @@ </g> <!-- SERIE NAME TAGS : LINES --> - <template v-for="(serie, i) in lineSet" :key="`xLabel_line_${i}`" :class="`xLabel_line_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <template v-for="(serie, i) in lineSet" :key="`xLabel_line_${i}`" :class="`xLabel_line_${i}`"> <template v-for="(plot, j) in serie.plots" :key="`xLabel_line_${i}_${j}`"> <text v-if="plot && j === 0 && serie.showSerieName && serie.showSerieName === 'start'" @@ -904,6 +909,7 @@ y: plot.y, maxWords: 2 })" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" /> <text v-if="plot && j === serie.plots.length - 1 && serie.showSerieName && serie.showSerieName === 'end'" @@ -920,12 +926,13 @@ y: plot.y, maxWords: 2 })" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" /> </template> </template> <!-- SERIE NAME TAGS : PLOTS --> - <template v-for="(serie, i) in plotSet" :key="`xLabel_plot_${i}`" :class="`xLabel_plot_${i}`" :style="`opacity:${selectedScale ? selectedScale === serie.id ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`"> + <template v-for="(serie, i) in plotSet" :key="`xLabel_plot_${i}`" :class="`xLabel_plot_${i}`"> <template v-for="(plot, j) in serie.plots" :key="`xLabel_plot_${i}_${j}`"> <text v-if="plot && j === 0 && serie.showSerieName && serie.showSerieName === 'start'" @@ -942,6 +949,7 @@ y: plot.y, maxWords: 2 })" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" /> <text v-if="plot && j === serie.plots.length - 1 && serie.showSerieName && serie.showSerieName === 'end'" @@ -958,6 +966,7 @@ y: plot.y, maxWords: 2 })" + :style="`opacity:${selectedScale ? selectedScale === serie.groupId ? 1 : 0.2 : 1};transition:opacity 0.2s ease-in-out`" /> </template> </template> @@ -983,8 +992,8 @@ :y="drawingArea.top" :width="FINAL_CONFIG.chart.grid.labels.yAxis.labelWidth" :height="drawingArea.height < 0 ? 10 : drawingArea.height" - :fill="selectedScale === trap.id ? `url(#individual_scale_gradient_${uniqueId}_${i})` : 'transparent'" - @mouseenter="selectedScale = trap.id" + :fill="selectedScale === trap.groupId ? `url(#individual_scale_gradient_${uniqueId}_${i})` : 'transparent'" + @mouseenter="selectedScale = trap.groupId" @mouseleave="selectedScale = null" /> </template> @@ -1571,13 +1580,20 @@ export default { individualHeight: p.individualHeight || this.drawingArea.height } }); - const len = [...lines, ...bars, ...plots].flatMap(el => el).length; - return [...lines, ...bars, ...plots].flatMap((el,i) => { + + const source = (this.mutableConfig.useIndividualScale && !this.mutableConfig.isStacked) ? Object.values(this.scaleGroups) : [...lines, ...bars, ...plots]; + + console.log(source) + + const len = source.flatMap(el => el).length; + return source.flatMap((el,i) => { return { + unique: el.unique, id: el.id, + groupId: el.groupId, scaleLabel: el.scaleLabel, - name: el.name, - color: el.color, + name: (this.mutableConfig.useIndividualScale && !this.mutableConfig.isStacked) ? el.unique ? el.name : el.groupName : el.name, + color: (this.mutableConfig.useIndividualScale && !this.mutableConfig.isStacked) ? el.unique ? el.color : el.groupColor : el.color, scale: el.scale, yOffset: el.yOffset, individualHeight: el.individualHeight, @@ -1659,6 +1675,7 @@ export default { data: datapoint.series, threshold: this.FINAL_CONFIG.downsample.threshold }) + const id = `uniqueId_${i}`; return { ...datapoint, slotAbsoluteIndex: i, @@ -1666,7 +1683,9 @@ export default { return this.isSafeValue(d) ? d : null }).slice(this.slicer.start, this.slicer.end), color: this.convertColorToHex(datapoint.color ? datapoint.color : this.customPalette[i] ? this.customPalette[i] : this.palette[i]), - id: `uniqueId_${i}` + id, + scaleLabel: datapoint.scaleLabel || id + } }); }, @@ -1728,11 +1747,30 @@ export default { } }) }, + activeSeriesLength() { + return this.absoluteDataset.length + }, + activeSeriesWithStackRatios() { + return this.assignStackRatios(this.absoluteDataset.filter(ds => !this.segregatedSeries.includes(ds.id))) + }, + scaleGroups() { + const grouped = Object.groupBy(this.activeSeriesWithStackRatios, item => item.scaleLabel); + const result = {}; + for (const [group, items] of Object.entries(grouped)) { + const allValues = items.flatMap(item => item.absoluteValues); + result[group] = { + min: Math.min(...allValues) || 0, + max: Math.max(...allValues) || 1, + groupId: `scale_group_${this.createUid()}` + }; + } + return result; + }, barSet() { return this.activeSeriesWithStackRatios.filter(s => s.type === 'bar').map((datapoint, i) => { this.checkAutoScaleError(datapoint); - const min = Math.min(...datapoint.absoluteValues.filter(v => ![null, undefined].includes(v))); - const max = Math.max(...datapoint.absoluteValues.filter(v => ![null, undefined].includes(v))); + const min = this.scaleGroups[datapoint.scaleLabel].min; + const max = this.scaleGroups[datapoint.scaleLabel].max; const autoScaledRatios = datapoint.absoluteValues.filter(v => ![null, undefined].includes(v)).map(v => { return (v - min) / (max - min) }); @@ -1838,6 +1876,20 @@ export default { } }); + this.scaleGroups[datapoint.scaleLabel].name = datapoint.name; + this.scaleGroups[datapoint.scaleLabel].groupName = datapoint.scaleLabel; + this.scaleGroups[datapoint.scaleLabel].groupColor = this.FINAL_CONFIG.chart.grid.labels.yAxis.groupColor || datapoint.color; + this.scaleGroups[datapoint.scaleLabel].color = datapoint.color; + this.scaleGroups[datapoint.scaleLabel].scaleYLabels = datapoint.autoScaling ? autoScaleYLabels : scaleYLabels; + this.scaleGroups[datapoint.scaleLabel].zeroPosition = datapoint.autoScaling ? autoScaleZeroPosition : zeroPosition; + this.scaleGroups[datapoint.scaleLabel].individualMax = datapoint.autoScaling ? autoScaleMax : individualMax; + this.scaleGroups[datapoint.scaleLabel].scaleLabel = datapoint.scaleLabel; + this.scaleGroups[datapoint.scaleLabel].id = datapoint.id; + this.scaleGroups[datapoint.scaleLabel].yOffset = yOffset; + this.scaleGroups[datapoint.scaleLabel].individualHeight = individualHeight; + this.scaleGroups[datapoint.scaleLabel].autoScaleYLabels = autoScaleYLabels; + this.scaleGroups[datapoint.scaleLabel].unique = this.activeSeriesWithStackRatios.filter(el => el.scaleLabel === datapoint.scaleLabel).length === 1 + return { ...datapoint, yOffset, @@ -1847,22 +1899,17 @@ export default { individualScale: datapoint.autoScaling ? autoScaleSteps : individualScale, individualMax: datapoint.autoScaling ? autoScaleMax : individualMax, zeroPosition: datapoint.autoScaling ? autoScaleZeroPosition : zeroPosition, - plots: datapoint.autoScaling ? autoScalePlots: plots + plots: datapoint.autoScaling ? autoScalePlots: plots, + groupId: this.scaleGroups[datapoint.scaleLabel].groupId } }) }, - activeSeriesLength() { - return this.absoluteDataset.length - }, - activeSeriesWithStackRatios() { - return this.assignStackRatios(this.absoluteDataset.filter(ds => !this.segregatedSeries.includes(ds.id))) - }, lineSet() { return this.activeSeriesWithStackRatios.filter(s => s.type === 'line').map((datapoint) => { this.checkAutoScaleError(datapoint); - const min = Math.min(...datapoint.absoluteValues.filter(v => ![undefined, null].includes(v))); - const max = Math.max(...datapoint.absoluteValues.filter(v => ![undefined, null].includes(v))) || 1; + const min = this.scaleGroups[datapoint.scaleLabel].min; + const max = this.scaleGroups[datapoint.scaleLabel].max; const autoScaledRatios = datapoint.absoluteValues.filter(v => ![null, undefined].includes(v)).map(v => { return (v - min) / (max - min) }); @@ -1964,6 +2011,20 @@ export default { } }); + this.scaleGroups[datapoint.scaleLabel].name = datapoint.name; + this.scaleGroups[datapoint.scaleLabel].groupName = datapoint.scaleLabel; + this.scaleGroups[datapoint.scaleLabel].groupColor = this.FINAL_CONFIG.chart.grid.labels.yAxis.groupColor || datapoint.color; + this.scaleGroups[datapoint.scaleLabel].color = datapoint.color; + this.scaleGroups[datapoint.scaleLabel].scaleYLabels = datapoint.autoScaling ? autoScaleYLabels : scaleYLabels; + this.scaleGroups[datapoint.scaleLabel].zeroPosition = datapoint.autoScaling ? autoScaleZeroPosition : zeroPosition; + this.scaleGroups[datapoint.scaleLabel].individualMax = datapoint.autoScaling ? autoScaleMax : individualMax; + this.scaleGroups[datapoint.scaleLabel].scaleLabel = datapoint.scaleLabel; + this.scaleGroups[datapoint.scaleLabel].id = datapoint.id; + this.scaleGroups[datapoint.scaleLabel].yOffset = yOffset; + this.scaleGroups[datapoint.scaleLabel].individualHeight = individualHeight; + this.scaleGroups[datapoint.scaleLabel].autoScaleYLabels = autoScaleYLabels; + this.scaleGroups[datapoint.scaleLabel].unique = this.activeSeriesWithStackRatios.filter(el => el.scaleLabel === datapoint.scaleLabel).length === 1 + return { ...datapoint, yOffset, @@ -1976,15 +2037,16 @@ export default { curve: datapoint.autoScaling ? autoScaleCurve : curve, plots: datapoint.autoScaling ? autoScalePlots : plots, area: !datapoint.useArea ? '' : this.mutableConfig.useIndividualScale ? this.createIndividualArea(datapoint.autoScaling ? autoScalePlots: plots, datapoint.autoScaling ? autoScaleZeroPosition : zeroPosition) : this.createArea(plots), - straight: datapoint.autoScaling ? autoScaleStraight : straight + straight: datapoint.autoScaling ? autoScaleStraight : straight, + groupId: this.scaleGroups[datapoint.scaleLabel].groupId } }) }, plotSet() { return this.activeSeriesWithStackRatios.filter(s => s.type === 'plot').map((datapoint) => { this.checkAutoScaleError(datapoint); - const min = Math.min(...datapoint.absoluteValues.filter(v => ![null, undefined].includes(v))); - const max = Math.max(...datapoint.absoluteValues.filter(v => ![null, undefined].includes(v))) || 1; + const min = this.scaleGroups[datapoint.scaleLabel].min; + const max = this.scaleGroups[datapoint.scaleLabel].max; const autoScaledRatios = datapoint.absoluteValues.filter(v => ![null, undefined].includes(v)).map(v => { return (v - min) / (max - min) }); @@ -2069,6 +2131,20 @@ export default { } }); + this.scaleGroups[datapoint.scaleLabel].name = datapoint.name; + this.scaleGroups[datapoint.scaleLabel].groupName = datapoint.scaleLabel; + this.scaleGroups[datapoint.scaleLabel].groupColor = this.FINAL_CONFIG.chart.grid.labels.yAxis.groupColor || datapoint.color; + this.scaleGroups[datapoint.scaleLabel].color = datapoint.color; + this.scaleGroups[datapoint.scaleLabel].scaleYLabels = datapoint.autoScaling ? autoScaleYLabels : scaleYLabels; + this.scaleGroups[datapoint.scaleLabel].zeroPosition = datapoint.autoScaling ? autoScaleZeroPosition : zeroPosition; + this.scaleGroups[datapoint.scaleLabel].individualMax = datapoint.autoScaling ? autoScaleMax : individualMax; + this.scaleGroups[datapoint.scaleLabel].scaleLabel = datapoint.scaleLabel; + this.scaleGroups[datapoint.scaleLabel].id = datapoint.id; + this.scaleGroups[datapoint.scaleLabel].yOffset = yOffset; + this.scaleGroups[datapoint.scaleLabel].individualHeight = individualHeight; + this.scaleGroups[datapoint.scaleLabel].autoScaleYLabels = autoScaleYLabels; + this.scaleGroups[datapoint.scaleLabel].unique = this.activeSeriesWithStackRatios.filter(el => el.scaleLabel === datapoint.scaleLabel).length === 1 + return { ...datapoint, yOffset, @@ -2078,7 +2154,8 @@ export default { individualScale: datapoint.autoScaling ? autoScaleSteps : individualScale, individualMax: datapoint.autoScaling ? autoScaleMax : individualMax, zeroPosition: datapoint.autoScaling ? autoScaleZeroPosition : zeroPosition, - plots: datapoint.autoScaling ? autoScalePlots : plots + plots: datapoint.autoScaling ? autoScalePlots : plots, + groupId: this.scaleGroups[datapoint.scaleLabel].groupId } }) }, @@ -2426,6 +2503,7 @@ export default { treeShake, useMouse, useNestedProp, + createUid, setUserOptionsVisibility(state = false) { if (!this.showUserOptionsOnChartHover) return; this.userOptionsVisible = state @@ -2509,6 +2587,12 @@ export default { mergedConfig.chart.zoom.endIndex = null; } + if (this.config && this.hasDeepProperty(this.config, 'chart.grid.labels.yAxis.groupColor')) { + mergedConfig.chart.grid.labels.yAxis.groupColor = this.config.chart.grid.labels.yAxis.groupColor; + } else { + mergedConfig.chart.grid.labels.yAxis.groupColor = null; + } + // ---------------------------------------------------------------------------- if (mergedConfig.theme) { diff --git a/src/useConfig.js b/src/useConfig.js index 23b7efdc..fde8f636 100755 --- a/src/useConfig.js +++ b/src/useConfig.js @@ -458,6 +458,7 @@ export function useConfig() { formatter: null, scaleMin: null, // Overrides auto scaling scaleMax: null, // idem + groupColor: null // force yAxis labels color }, xAxisLabels: { color: COLOR_BLACK, diff --git a/types/vue-data-ui.d.ts b/types/vue-data-ui.d.ts index 8ebcf818..ce9a4426 100644 --- a/types/vue-data-ui.d.ts +++ b/types/vue-data-ui.d.ts @@ -2625,6 +2625,7 @@ declare module "vue-data-ui" { formatter?: Formatter; scaleMin?: number | null; scaleMax?: number | null; + groupColor?: string | null; }; xAxis?: { showBaseline?: boolean;