diff --git a/demos/forms.html b/demos/forms.html
new file mode 100644
index 00000000..5d14443b
--- /dev/null
+++ b/demos/forms.html
@@ -0,0 +1,295 @@
+
+
+
+
+
+ Forms customization
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/line-paths.html b/demos/line-paths.html
index dd99090e..efdf68b5 100644
--- a/demos/line-paths.html
+++ b/demos/line-paths.html
@@ -171,7 +171,7 @@
cursor: {
points: {
size: (u, seriesIdx) => u.series[seriesIdx].points.size * 2.5,
- width: (u, seriesIdx, size) => size / 4,
+ width: (u, seriesIdx, size) => size,
stroke: (u, seriesIdx) => u.series[seriesIdx].points.stroke(u, seriesIdx) + '90',
fill: (u, seriesIdx) => "#fff",
},
diff --git a/src/dom.js b/src/dom.js
index bc794fa4..1a4a1d5b 100644
--- a/src/dom.js
+++ b/src/dom.js
@@ -90,8 +90,10 @@ export function elColor(el, background, borderColor) {
if (newColor != oldColor) {
colorCache.set(el, newColor);
- el.style.background = background;
- el.style.borderColor = borderColor;
+ for (const element of el.getElementsByTagName('path')) {
+ element.setAttribute('fill', background);
+ element.setAttribute('stroke', borderColor);
+ }
}
}
diff --git a/src/domClasses.js b/src/domClasses.js
index d3cd4de3..da1045e1 100644
--- a/src/domClasses.js
+++ b/src/domClasses.js
@@ -17,6 +17,7 @@ export const LEGEND = pre + "legend"
export const LEGEND_LIVE = pre + "live";
export const LEGEND_INLINE = pre + "inline";
export const LEGEND_SERIES = pre + "series";
-export const LEGEND_MARKER = pre + "marker";
+export const LEGEND_MARKERR = pre + "marker-r";
+export const LEGEND_MARKERL = pre + "marker-l";
export const LEGEND_LABEL = pre + "label";
export const LEGEND_VALUE = pre + "value";
\ No newline at end of file
diff --git a/src/opts.js b/src/opts.js
index f3d6172c..ef7fb301 100644
--- a/src/opts.js
+++ b/src/opts.js
@@ -400,7 +400,8 @@ export const legendOpts = {
mount: noop,
markers: {
show: true,
- width: 2,
+ before: true,
+ width: 10,
stroke: legendStroke,
fill: legendFill,
dash: "solid",
@@ -413,20 +414,29 @@ export const legendOpts = {
function cursorPointShow(self, si) {
let o = self.cursor.points;
- let pt = placeDiv();
-
- let size = o.size(self, si);
- setStylePx(pt, WIDTH, size);
- setStylePx(pt, HEIGHT, size);
-
- let mar = size / -2;
- setStylePx(pt, "marginLeft", mar);
- setStylePx(pt, "marginTop", mar);
-
- let width = o.width(self, si, size);
- width && setStylePx(pt, "borderWidth", width);
-
- return pt;
+ const svgProp = self.series[si].points.form.svg;
+ const svgURI = 'http://www.w3.org/2000/svg';
+ const svg = document.createElementNS(svgURI, 'svg');
+ const path = document.createElementNS(svgURI, 'path');
+ svg.appendChild(path);
+ path.setAttribute('d', svgProp.path);
+
+ // At this point, we consider the viewBox to be a square
+ const fullSize = o.size(self, si);
+ let width = Math.min(o.width(self, si, fullSize), fullSize);
+ setStylePx(svg, WIDTH, fullSize);
+ setStylePx(svg, HEIGHT, fullSize);
+
+ let mar = fullSize / -2;
+ setStylePx(svg, "marginLeft", mar);
+ setStylePx(svg, "marginTop", mar);
+
+ const vb = svgProp.viewBox;
+ const svgHalfWidth = Math.ceil(Math.min(vb.width, vb.height) * width / (2 * fullSize));
+ width && setStylePx(path, "stroke-width", svgHalfWidth * 2);
+ svg.setAttribute('viewBox', (vb.minX - svgHalfWidth) + ' ' + (vb.minY - svgHalfWidth) + ' ' + (vb.width + 2 * svgHalfWidth) + ' ' + (vb.height + 2 * svgHalfWidth));
+
+ return svg;
}
function cursorPointFill(self, si) {
diff --git a/src/paths/points.js b/src/paths/points.js
index 44b3a6b5..e59306b4 100644
--- a/src/paths/points.js
+++ b/src/paths/points.js
@@ -1,4 +1,4 @@
-import { orient, moveToH, moveToV, rectH, arcH, arcV, BAND_CLIP_FILL, BAND_CLIP_STROKE } from './utils';
+import { orient, rectH, BAND_CLIP_FILL, BAND_CLIP_STROKE } from './utils';
import { roundDec, PI } from '../utils';
// TODO: drawWrap(seriesIdx, drawPoints) (save, restore, translate, clip)
@@ -7,21 +7,11 @@ export function points(opts) {
// log("drawPoints()", arguments);
let { pxRatio } = u;
- return orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim) => {
+ return orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim, moveTo, lineTo, rect, arc, bezier) => {
let { pxRound, points } = series;
- let moveTo, arc;
-
- if (scaleX.ori == 0) {
- moveTo = moveToH;
- arc = arcH;
- }
- else {
- moveTo = moveToV;
- arc = arcV;
- }
-
const width = roundDec(points.width * pxRatio, 3);
+ const size = roundDec(points.size * pxRatio, 3);
let rad = (points.size - points.width) / 2 * pxRatio;
let dia = roundDec(rad * 2, 3);
@@ -43,8 +33,7 @@ export function points(opts) {
let x = pxRound(valToPosX(dataX[pi], scaleX, xDim, xOff));
let y = pxRound(valToPosY(dataY[pi], scaleY, yDim, yOff));
- moveTo(fill, x + rad, y);
- arc(fill, x, y, rad, 0, PI * 2);
+ points.form.draw(fill, x, y, size, width, moveTo, lineTo, arc, bezier);
}
};
@@ -63,4 +52,17 @@ export function points(opts) {
};
});
};
+}
+
+export const CIRCLE = {
+ name: 'CIRCLE',
+ svg: {
+ viewBox: { minX: 0, minY: 0, width: 100, height: 100 },
+ path: 'M0 50A50 50 0 11100 50 50 50 0 110 50Z'
+ },
+ draw: (path, centerX, centerY, size, strokeWidth, moveTo, lineTo, arc, bezier) => {
+ const dist = (size - strokeWidth) / 2;
+ moveTo(path, centerX + dist, centerY);
+ arc(path, centerX, centerY, dist, 0, 2 * Math.PI);
+ },
}
\ No newline at end of file
diff --git a/src/uPlot.css b/src/uPlot.css
index 5282773e..a51d6922 100644
--- a/src/uPlot.css
+++ b/src/uPlot.css
@@ -69,13 +69,20 @@
display: inline-block;
}
-.u-legend .u-marker {
+.u-legend .u-marker-l {
width: 1em;
height: 1em;
margin-right: 4px;
background-clip: padding-box !important;
}
+.u-marker-r {
+ width: 1em;
+ height: 1em;
+ margin-left: 4px;
+ background-clip: padding-box !important;
+}
+
.u-inline.u-live th::after {
content: ":";
vertical-align: middle;
@@ -128,8 +135,6 @@
position: absolute;
top: 0;
left: 0;
- border-radius: 50%;
- border: 0 solid;
pointer-events: none;
will-change: transform;
/* this has to be !important since we set inline "background" shorthand */
@@ -142,4 +147,8 @@
.u-cursor-y.u-off,
.u-cursor-pt.u-off {
display: none;
+}
+
+svg {
+ paint-order: stroke;
}
\ No newline at end of file
diff --git a/src/uPlot.js b/src/uPlot.js
index f20d9545..5ab8c173 100644
--- a/src/uPlot.js
+++ b/src/uPlot.js
@@ -106,7 +106,8 @@ import {
LEGEND_LIVE,
LEGEND_INLINE,
LEGEND_SERIES,
- LEGEND_MARKER,
+ LEGEND_MARKERR,
+ LEGEND_MARKERL,
LEGEND_LABEL,
LEGEND_VALUE,
} from './domClasses';
@@ -187,7 +188,7 @@ import {
import { _sync } from './sync';
-import { points } from './paths/points';
+import { points, CIRCLE } from './paths/points';
import { linear } from './paths/linear';
import { stepped } from './paths/stepped';
import { bars } from './paths/bars';
@@ -614,6 +615,32 @@ export default function uPlot(opts, data, then) {
const son = {show: true};
const soff = {show: false};
+ function placeMarker(seriesIndex, label, before) {
+ const svgProp = series[seriesIndex].points.form.svg;
+
+ const svgURI = 'http://www.w3.org/2000/svg';
+ const svg = document.createElementNS(svgURI, 'svg');
+ svg.classList.add(before ? LEGEND_MARKERL : LEGEND_MARKERR);
+ const path = document.createElementNS(svgURI, 'path');
+ label.appendChild(svg);
+ svg.appendChild(path);
+ path.setAttribute('d', svgProp.path);
+
+ const width = markers.width(self, seriesIndex);
+ const dw = ceil(width/2);
+ const vb = svgProp.viewBox;
+ // Adapting the viewBox to the stroke's width
+ svg.setAttribute('viewBox', (vb.minX - dw) + ' ' + (vb.minY - dw) + ' ' + (vb.width + 2 * dw) + ' ' + (vb.height + 2 * dw));
+ if (width) {
+ path.setAttribute('stroke-width', width);
+ path.setAttribute('stroke', markers.stroke(self, seriesIndex));
+ const dash = markers.dash(self, seriesIndex);
+ if (dash != 'solid') path.setAttribute('stroke-dasharray', Array.isArray(dash) ? dash.join(' ') : '35 15');
+ }
+ const fill = markers.fill(self, seriesIndex);
+ path.setAttribute('fill', fill == null ? transparent : fill);
+ }
+
function initLegendRow(s, i) {
if (i == 0 && (multiValLegend || !legend.live || mode == 2))
return nullNullTuple;
@@ -629,20 +656,9 @@ export default function uPlot(opts, data, then) {
let label = placeTag("th", null, row);
- if (markers.show) {
- let indic = placeDiv(LEGEND_MARKER, label);
-
- if (i > 0) {
- let width = markers.width(self, i);
-
- if (width)
- indic.style.border = width + "px " + markers.dash(self, i) + " " + markers.stroke(self, i);
-
- indic.style.background = markers.fill(self, i);
- }
- }
-
+ if (markers.show && markers.before && i > 0) placeMarker(i, label, true);
let text = placeDiv(LEGEND_LABEL, label);
+ if (markers.show && !markers.before && i > 0) placeMarker(i, label, false);
if (s.label instanceof HTMLElement)
text.appendChild(s.label);
@@ -1004,7 +1020,7 @@ export default function uPlot(opts, data, then) {
function initCursorPt(s, si) {
let pt = points.show(self, si);
- if (pt instanceof HTMLElement) {
+ if (pt instanceof SVGSVGElement) {
addClass(pt, CURSOR_PT);
addClass(pt, s.class);
elTrans(pt, -10, -10, plotWidCss, plotHgtCss);
@@ -1041,6 +1057,7 @@ export default function uPlot(opts, data, then) {
stroke: s.stroke,
space: _ptDia * 2,
paths: pointsPath,
+ form: CIRCLE,
_stroke: null,
_fill: null,
}, s.points);