diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f36c95180..a94b2538a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -157,4 +157,16 @@ This way, when later working on a feature or fix, `npm run test` command will de We'd love for you to contribute your changes back to `apexcharts.js`! To do that, it would be great if you could commit your changes to a separate feature branch and open a Pull Request for those changes. -Point your feature branch to use the `main` +Point your feature branch to use the `main` branch as the base of this PR. The exact commands used depends on how you've setup your local git copy, but the flow could look like this: + +```sh +# Inside your own copy of `apexcharts.js` package... +git checkout --branch feature/branch-name-here upstream/main +# Then hack away, and commit your changes: +git add -A +git commit -m "Few words about the changes I did" +# Push your local changes back to your fork +git push --set-upstream origin feature/branch-name-here +``` + +After these steps, you should be able to create a new Pull Request for this repository. If you hit any issues following these instructions, please open an issue and we'll see if we can improve these instructions even further. diff --git a/samples/react/area/area-single-data.html b/samples/react/area/area-single-data.html new file mode 100644 index 000000000..4457f1683 --- /dev/null +++ b/samples/react/area/area-single-data.html @@ -0,0 +1,283 @@ + + + + + + + Area Chart with Single Data Point - React + + + + + + + + + + + + +
+ + + + diff --git a/samples/vanilla-js/area/area-single-data.html b/samples/vanilla-js/area/area-single-data.html new file mode 100644 index 000000000..3bbd7c245 --- /dev/null +++ b/samples/vanilla-js/area/area-single-data.html @@ -0,0 +1,242 @@ + + + + + + + Area Chart with Single Data Point + + + + + + + + + + + +
Area Chart with Single Data Point
+ +
+

About this demo

+

+ This demo shows the + plotOptions.area.forceSingleDataAsArea option in action + using Vanilla JS. +

+

true: Single data point is shown as an area

+

false: Single data point is shown as a marker

+
+ +
+
Chart with forceSingleDataAsArea = true
+
+ plotOptions.area.forceSingleDataAsArea: true +
+
+
+ +
+
Chart with forceSingleDataAsArea = false
+
+ plotOptions.area.forceSingleDataAsArea: false +
+
+
+ +
+
+ Chart with forceSingleDataAsArea = undefined +
+
+ plotOptions.area.forceSingleDataAsArea: undefined +
+
+
+ + + + diff --git a/samples/vue/area/area-single-data.html b/samples/vue/area/area-single-data.html new file mode 100644 index 000000000..93ce46910 --- /dev/null +++ b/samples/vue/area/area-single-data.html @@ -0,0 +1,250 @@ + + + + + + + Area Chart with Single Data Point - Vue + + + + + + + + + + +
+
Area Chart with Single Data Point - Vue
+ +
+

About this demo

+

+ This demo shows the + plotOptions.area.forceSingleDataAsArea option in action + using Vue.js. +

+

true: Single data point is shown as an area

+

false: Single data point is shown as a marker

+
+ +
+
Chart with forceSingleDataAsArea = true
+
+ plotOptions.area.forceSingleDataAsArea: true +
+
+
+ +
+
Chart with forceSingleDataAsArea = false
+
+ plotOptions.area.forceSingleDataAsArea: false +
+
+
+ +
+
+ Chart with forceSingleDataAsArea = undefined +
+
+ plotOptions.area.forceSingleDataAsArea: undefined +
+
+
+
+ + + + diff --git a/src/charts/Line.js b/src/charts/Line.js index cafd7f83d..250d152d7 100644 --- a/src/charts/Line.js +++ b/src/charts/Line.js @@ -1,12 +1,12 @@ +import { spline, svgPath } from '../libs/monotone-cubic' import CoreUtils from '../modules/CoreUtils' -import Graphics from '../modules/Graphics' -import Fill from '../modules/Fill' import DataLabels from '../modules/DataLabels' +import Fill from '../modules/Fill' +import Graphics from '../modules/Graphics' import Markers from '../modules/Markers' -import Scatter from './Scatter' import Utils from '../utils/Utils' import Helpers from './common/line/Helpers' -import { svgPath, spline } from '../libs/monotone-cubic' +import Scatter from './Scatter' /** * ApexCharts Line Class responsible for drawing Line / Area / RangeArea Charts. * This class is also responsible for generating values for Bubble/Scatter charts, so need to rename it to Axis Charts to avoid confusions @@ -576,6 +576,97 @@ class Line { : w.globals.dataPoints } + const isSinglePointSeries = series[i].length === 1 + + // force show single data point area series (covers both iterations === 0 and + // the common case where global iterations>0 but current series length == 1) + if ( + isSinglePointSeries && + type === 'area' && + w.config.plotOptions.area.forceSingleDataAsArea + ) { + const startX = xArrj[0] + const startY = yArrj[0] !== null ? yArrj[0] : this.zeroY + + // Use xDivision for category axis, and a fraction of the x-ratio for numeric axis + const areaWidth = w.globals.isXNumeric + ? (1 / this.xRatio) * 0.5 + : this.xDivision * 0.5 + const endX = startX + areaWidth + + // A small drop towards the baseline for better gradient visibility + const dropRatio = 0.12 + const endY = startY + (this.areaBottomY - startY) * dropRatio + + // Build complete paths overriding previous partial ones. + // Respect stroke.curve option for visual consistency. + + let topSegment = '' + const curveStyle = w.config.stroke.curve + if (curveStyle === 'smooth' || curveStyle === 'monotoneCubic') { + const cLen = (endX - startX) * 0.5 + // Cubic Bézier curve from (startX,startY) to (endX,areaBottomY) + topSegment = + graphics.move(startX, startY) + + graphics.curve(startX + cLen, startY, endX - cLen, endY, endX, endY) + } else if (curveStyle === 'stepline') { + topSegment = + graphics.move(startX, startY) + + graphics.line(null, endY, 'V') + + graphics.line(endX, null, 'H') + } else if (curveStyle === 'linestep') { + topSegment = + graphics.move(startX, startY) + + graphics.line(endX, null, 'H') + + graphics.line(null, endY, 'V') + } else { + // straight + topSegment = graphics.move(startX, startY) + graphics.line(endX, endY) + } + + const singleLinePath = topSegment + + const singleAreaPath = + topSegment + + graphics.line(endX, this.areaBottomY) + + graphics.line(startX, this.areaBottomY) + + 'z' + + linePaths.push(singleLinePath) + areaPaths.push(singleAreaPath) + + // a single point chart still needs to have markers and labels + let pointsPos = this.lineHelpers.calculatePoints({ + series, + x: startX, + y: startY, + realIndex, + i, + j: 0, + prevY, + }) + + this._handleMarkersAndLabels({ + type, + pointsPos, + i, + j: 0, + realIndex, + isRangeStart, + }) + + return { + yArrj, + xArrj, + pathFromArea, + areaPaths, + pathFromLine, + linePaths, + linePath, + areaPath, + } + } + const getY = (_y, lineYPos) => { return ( lineYPos - diff --git a/src/modules/settings/Options.js b/src/modules/settings/Options.js index 50b95fd9c..0621d9a9b 100644 --- a/src/modules/settings/Options.js +++ b/src/modules/settings/Options.js @@ -414,6 +414,7 @@ export default class Options { }, area: { fillTo: 'origin', + forceSingleDataAsArea: false, }, bar: { horizontal: false,