site/node_modules/d3-brush/dist/d3-brush.js

657 lines
19 KiB
JavaScript
Raw Permalink Normal View History

2024-10-14 06:09:33 +00:00
// https://d3js.org/d3-brush/ v3.0.0 Copyright 2010-2021 Mike Bostock
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-dispatch'), require('d3-drag'), require('d3-interpolate'), require('d3-selection'), require('d3-transition')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-dispatch', 'd3-drag', 'd3-interpolate', 'd3-selection', 'd3-transition'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.d3, global.d3));
}(this, (function (exports, d3Dispatch, d3Drag, d3Interpolate, d3Selection, d3Transition) { 'use strict';
var constant = x => () => x;
function BrushEvent(type, {
sourceEvent,
target,
selection,
mode,
dispatch
}) {
Object.defineProperties(this, {
type: {value: type, enumerable: true, configurable: true},
sourceEvent: {value: sourceEvent, enumerable: true, configurable: true},
target: {value: target, enumerable: true, configurable: true},
selection: {value: selection, enumerable: true, configurable: true},
mode: {value: mode, enumerable: true, configurable: true},
_: {value: dispatch}
});
}
function nopropagation(event) {
event.stopImmediatePropagation();
}
function noevent(event) {
event.preventDefault();
event.stopImmediatePropagation();
}
var MODE_DRAG = {name: "drag"},
MODE_SPACE = {name: "space"},
MODE_HANDLE = {name: "handle"},
MODE_CENTER = {name: "center"};
const {abs, max, min} = Math;
function number1(e) {
return [+e[0], +e[1]];
}
function number2(e) {
return [number1(e[0]), number1(e[1])];
}
var X = {
name: "x",
handles: ["w", "e"].map(type),
input: function(x, e) { return x == null ? null : [[+x[0], e[0][1]], [+x[1], e[1][1]]]; },
output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }
};
var Y = {
name: "y",
handles: ["n", "s"].map(type),
input: function(y, e) { return y == null ? null : [[e[0][0], +y[0]], [e[1][0], +y[1]]]; },
output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }
};
var XY = {
name: "xy",
handles: ["n", "w", "e", "s", "nw", "ne", "sw", "se"].map(type),
input: function(xy) { return xy == null ? null : number2(xy); },
output: function(xy) { return xy; }
};
var cursors = {
overlay: "crosshair",
selection: "move",
n: "ns-resize",
e: "ew-resize",
s: "ns-resize",
w: "ew-resize",
nw: "nwse-resize",
ne: "nesw-resize",
se: "nwse-resize",
sw: "nesw-resize"
};
var flipX = {
e: "w",
w: "e",
nw: "ne",
ne: "nw",
se: "sw",
sw: "se"
};
var flipY = {
n: "s",
s: "n",
nw: "sw",
ne: "se",
se: "ne",
sw: "nw"
};
var signsX = {
overlay: +1,
selection: +1,
n: null,
e: +1,
s: null,
w: -1,
nw: -1,
ne: +1,
se: +1,
sw: -1
};
var signsY = {
overlay: +1,
selection: +1,
n: -1,
e: null,
s: +1,
w: null,
nw: -1,
ne: -1,
se: +1,
sw: +1
};
function type(t) {
return {type: t};
}
// Ignore right-click, since that should open the context menu.
function defaultFilter(event) {
return !event.ctrlKey && !event.button;
}
function defaultExtent() {
var svg = this.ownerSVGElement || this;
if (svg.hasAttribute("viewBox")) {
svg = svg.viewBox.baseVal;
return [[svg.x, svg.y], [svg.x + svg.width, svg.y + svg.height]];
}
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
}
function defaultTouchable() {
return navigator.maxTouchPoints || ("ontouchstart" in this);
}
// Like d3.local, but with the name “__brush” rather than auto-generated.
function local(node) {
while (!node.__brush) if (!(node = node.parentNode)) return;
return node.__brush;
}
function empty(extent) {
return extent[0][0] === extent[1][0]
|| extent[0][1] === extent[1][1];
}
function brushSelection(node) {
var state = node.__brush;
return state ? state.dim.output(state.selection) : null;
}
function brushX() {
return brush$1(X);
}
function brushY() {
return brush$1(Y);
}
function brush() {
return brush$1(XY);
}
function brush$1(dim) {
var extent = defaultExtent,
filter = defaultFilter,
touchable = defaultTouchable,
keys = true,
listeners = d3Dispatch.dispatch("start", "brush", "end"),
handleSize = 6,
touchending;
function brush(group) {
var overlay = group
.property("__brush", initialize)
.selectAll(".overlay")
.data([type("overlay")]);
overlay.enter().append("rect")
.attr("class", "overlay")
.attr("pointer-events", "all")
.attr("cursor", cursors.overlay)
.merge(overlay)
.each(function() {
var extent = local(this).extent;
d3Selection.select(this)
.attr("x", extent[0][0])
.attr("y", extent[0][1])
.attr("width", extent[1][0] - extent[0][0])
.attr("height", extent[1][1] - extent[0][1]);
});
group.selectAll(".selection")
.data([type("selection")])
.enter().append("rect")
.attr("class", "selection")
.attr("cursor", cursors.selection)
.attr("fill", "#777")
.attr("fill-opacity", 0.3)
.attr("stroke", "#fff")
.attr("shape-rendering", "crispEdges");
var handle = group.selectAll(".handle")
.data(dim.handles, function(d) { return d.type; });
handle.exit().remove();
handle.enter().append("rect")
.attr("class", function(d) { return "handle handle--" + d.type; })
.attr("cursor", function(d) { return cursors[d.type]; });
group
.each(redraw)
.attr("fill", "none")
.attr("pointer-events", "all")
.on("mousedown.brush", started)
.filter(touchable)
.on("touchstart.brush", started)
.on("touchmove.brush", touchmoved)
.on("touchend.brush touchcancel.brush", touchended)
.style("touch-action", "none")
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
}
brush.move = function(group, selection, event) {
if (group.tween) {
group
.on("start.brush", function(event) { emitter(this, arguments).beforestart().start(event); })
.on("interrupt.brush end.brush", function(event) { emitter(this, arguments).end(event); })
.tween("brush", function() {
var that = this,
state = that.__brush,
emit = emitter(that, arguments),
selection0 = state.selection,
selection1 = dim.input(typeof selection === "function" ? selection.apply(this, arguments) : selection, state.extent),
i = d3Interpolate.interpolate(selection0, selection1);
function tween(t) {
state.selection = t === 1 && selection1 === null ? null : i(t);
redraw.call(that);
emit.brush();
}
return selection0 !== null && selection1 !== null ? tween : tween(1);
});
} else {
group
.each(function() {
var that = this,
args = arguments,
state = that.__brush,
selection1 = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent),
emit = emitter(that, args).beforestart();
d3Transition.interrupt(that);
state.selection = selection1 === null ? null : selection1;
redraw.call(that);
emit.start(event).brush(event).end(event);
});
}
};
brush.clear = function(group, event) {
brush.move(group, null, event);
};
function redraw() {
var group = d3Selection.select(this),
selection = local(this).selection;
if (selection) {
group.selectAll(".selection")
.style("display", null)
.attr("x", selection[0][0])
.attr("y", selection[0][1])
.attr("width", selection[1][0] - selection[0][0])
.attr("height", selection[1][1] - selection[0][1]);
group.selectAll(".handle")
.style("display", null)
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; })
.attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; })
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; })
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; });
}
else {
group.selectAll(".selection,.handle")
.style("display", "none")
.attr("x", null)
.attr("y", null)
.attr("width", null)
.attr("height", null);
}
}
function emitter(that, args, clean) {
var emit = that.__brush.emitter;
return emit && (!clean || !emit.clean) ? emit : new Emitter(that, args, clean);
}
function Emitter(that, args, clean) {
this.that = that;
this.args = args;
this.state = that.__brush;
this.active = 0;
this.clean = clean;
}
Emitter.prototype = {
beforestart: function() {
if (++this.active === 1) this.state.emitter = this, this.starting = true;
return this;
},
start: function(event, mode) {
if (this.starting) this.starting = false, this.emit("start", event, mode);
else this.emit("brush", event);
return this;
},
brush: function(event, mode) {
this.emit("brush", event, mode);
return this;
},
end: function(event, mode) {
if (--this.active === 0) delete this.state.emitter, this.emit("end", event, mode);
return this;
},
emit: function(type, event, mode) {
var d = d3Selection.select(this.that).datum();
listeners.call(
type,
this.that,
new BrushEvent(type, {
sourceEvent: event,
target: brush,
selection: dim.output(this.state.selection),
mode,
dispatch: listeners
}),
d
);
}
};
function started(event) {
if (touchending && !event.touches) return;
if (!filter.apply(this, arguments)) return;
var that = this,
type = event.target.__data__.type,
mode = (keys && event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (keys && event.altKey ? MODE_CENTER : MODE_HANDLE),
signX = dim === Y ? null : signsX[type],
signY = dim === X ? null : signsY[type],
state = local(that),
extent = state.extent,
selection = state.selection,
W = extent[0][0], w0, w1,
N = extent[0][1], n0, n1,
E = extent[1][0], e0, e1,
S = extent[1][1], s0, s1,
dx = 0,
dy = 0,
moving,
shifting = signX && signY && keys && event.shiftKey,
lockX,
lockY,
points = Array.from(event.touches || [event], t => {
const i = t.identifier;
t = d3Selection.pointer(t, that);
t.point0 = t.slice();
t.identifier = i;
return t;
});
d3Transition.interrupt(that);
var emit = emitter(that, arguments, true).beforestart();
if (type === "overlay") {
if (selection) moving = true;
const pts = [points[0], points[1] || points[0]];
state.selection = selection = [[
w0 = dim === Y ? W : min(pts[0][0], pts[1][0]),
n0 = dim === X ? N : min(pts[0][1], pts[1][1])
], [
e0 = dim === Y ? E : max(pts[0][0], pts[1][0]),
s0 = dim === X ? S : max(pts[0][1], pts[1][1])
]];
if (points.length > 1) move(event);
} else {
w0 = selection[0][0];
n0 = selection[0][1];
e0 = selection[1][0];
s0 = selection[1][1];
}
w1 = w0;
n1 = n0;
e1 = e0;
s1 = s0;
var group = d3Selection.select(that)
.attr("pointer-events", "none");
var overlay = group.selectAll(".overlay")
.attr("cursor", cursors[type]);
if (event.touches) {
emit.moved = moved;
emit.ended = ended;
} else {
var view = d3Selection.select(event.view)
.on("mousemove.brush", moved, true)
.on("mouseup.brush", ended, true);
if (keys) view
.on("keydown.brush", keydowned, true)
.on("keyup.brush", keyupped, true);
d3Drag.dragDisable(event.view);
}
redraw.call(that);
emit.start(event, mode.name);
function moved(event) {
for (const p of event.changedTouches || [event]) {
for (const d of points)
if (d.identifier === p.identifier) d.cur = d3Selection.pointer(p, that);
}
if (shifting && !lockX && !lockY && points.length === 1) {
const point = points[0];
if (abs(point.cur[0] - point[0]) > abs(point.cur[1] - point[1]))
lockY = true;
else
lockX = true;
}
for (const point of points)
if (point.cur) point[0] = point.cur[0], point[1] = point.cur[1];
moving = true;
noevent(event);
move(event);
}
function move(event) {
const point = points[0], point0 = point.point0;
var t;
dx = point[0] - point0[0];
dy = point[1] - point0[1];
switch (mode) {
case MODE_SPACE:
case MODE_DRAG: {
if (signX) dx = max(W - w0, min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;
if (signY) dy = max(N - n0, min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;
break;
}
case MODE_HANDLE: {
if (points[1]) {
if (signX) w1 = max(W, min(E, points[0][0])), e1 = max(W, min(E, points[1][0])), signX = 1;
if (signY) n1 = max(N, min(S, points[0][1])), s1 = max(N, min(S, points[1][1])), signY = 1;
} else {
if (signX < 0) dx = max(W - w0, min(E - w0, dx)), w1 = w0 + dx, e1 = e0;
else if (signX > 0) dx = max(W - e0, min(E - e0, dx)), w1 = w0, e1 = e0 + dx;
if (signY < 0) dy = max(N - n0, min(S - n0, dy)), n1 = n0 + dy, s1 = s0;
else if (signY > 0) dy = max(N - s0, min(S - s0, dy)), n1 = n0, s1 = s0 + dy;
}
break;
}
case MODE_CENTER: {
if (signX) w1 = max(W, min(E, w0 - dx * signX)), e1 = max(W, min(E, e0 + dx * signX));
if (signY) n1 = max(N, min(S, n0 - dy * signY)), s1 = max(N, min(S, s0 + dy * signY));
break;
}
}
if (e1 < w1) {
signX *= -1;
t = w0, w0 = e0, e0 = t;
t = w1, w1 = e1, e1 = t;
if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]);
}
if (s1 < n1) {
signY *= -1;
t = n0, n0 = s0, s0 = t;
t = n1, n1 = s1, s1 = t;
if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]);
}
if (state.selection) selection = state.selection; // May be set by brush.move!
if (lockX) w1 = selection[0][0], e1 = selection[1][0];
if (lockY) n1 = selection[0][1], s1 = selection[1][1];
if (selection[0][0] !== w1
|| selection[0][1] !== n1
|| selection[1][0] !== e1
|| selection[1][1] !== s1) {
state.selection = [[w1, n1], [e1, s1]];
redraw.call(that);
emit.brush(event, mode.name);
}
}
function ended(event) {
nopropagation(event);
if (event.touches) {
if (event.touches.length) return;
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
} else {
d3Drag.dragEnable(event.view, moving);
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
}
group.attr("pointer-events", "all");
overlay.attr("cursor", cursors.overlay);
if (state.selection) selection = state.selection; // May be set by brush.move (on start)!
if (empty(selection)) state.selection = null, redraw.call(that);
emit.end(event, mode.name);
}
function keydowned(event) {
switch (event.keyCode) {
case 16: { // SHIFT
shifting = signX && signY;
break;
}
case 18: { // ALT
if (mode === MODE_HANDLE) {
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
mode = MODE_CENTER;
move(event);
}
break;
}
case 32: { // SPACE; takes priority over ALT
if (mode === MODE_HANDLE || mode === MODE_CENTER) {
if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;
if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;
mode = MODE_SPACE;
overlay.attr("cursor", cursors.selection);
move(event);
}
break;
}
default: return;
}
noevent(event);
}
function keyupped(event) {
switch (event.keyCode) {
case 16: { // SHIFT
if (shifting) {
lockX = lockY = shifting = false;
move(event);
}
break;
}
case 18: { // ALT
if (mode === MODE_CENTER) {
if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
mode = MODE_HANDLE;
move(event);
}
break;
}
case 32: { // SPACE
if (mode === MODE_SPACE) {
if (event.altKey) {
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
mode = MODE_CENTER;
} else {
if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
mode = MODE_HANDLE;
}
overlay.attr("cursor", cursors[type]);
move(event);
}
break;
}
default: return;
}
noevent(event);
}
}
function touchmoved(event) {
emitter(this, arguments).moved(event);
}
function touchended(event) {
emitter(this, arguments).ended(event);
}
function initialize() {
var state = this.__brush || {selection: null};
state.extent = number2(extent.apply(this, arguments));
state.dim = dim;
return state;
}
brush.extent = function(_) {
return arguments.length ? (extent = typeof _ === "function" ? _ : constant(number2(_)), brush) : extent;
};
brush.filter = function(_) {
return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), brush) : filter;
};
brush.touchable = function(_) {
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), brush) : touchable;
};
brush.handleSize = function(_) {
return arguments.length ? (handleSize = +_, brush) : handleSize;
};
brush.keyModifiers = function(_) {
return arguments.length ? (keys = !!_, brush) : keys;
};
brush.on = function() {
var value = listeners.on.apply(listeners, arguments);
return value === listeners ? brush : value;
};
return brush;
}
exports.brush = brush;
exports.brushSelection = brushSelection;
exports.brushX = brushX;
exports.brushY = brushY;
Object.defineProperty(exports, '__esModule', { value: true });
})));