| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- "use strict";
- var __rest = (this && this.__rest) || function (s, e) {
- var t = {};
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
- t[p] = s[p];
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
- t[p[i]] = s[p[i]];
- }
- return t;
- };
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.BrushHighlight = exports.brushHighlight = exports.brush = void 0;
- const g_1 = require("@antv/g");
- const helper_1 = require("../utils/helper");
- const scale_1 = require("../utils/scale");
- const createElement_1 = require("../utils/createElement");
- const selection_1 = require("../utils/selection");
- const utils_1 = require("./utils");
- function intersect(bbox1, bbox2) {
- const [minX1, minY1, maxX1, maxY1] = bbox1;
- const [minX2, minY2, maxX2, maxY2] = bbox2;
- return !(minX2 > maxX1 || maxX2 < minX1 || minY2 > maxY1 || maxY2 < minY1);
- }
- function normalizeBounds(x, y, x1, y1, extent) {
- const [minX, minY, maxX, maxY] = extent;
- return [
- Math.max(minX, Math.min(x, x1)),
- Math.max(minY, Math.min(y, y1)),
- Math.min(maxX, Math.max(x, x1)),
- Math.min(maxY, Math.max(y, y1)),
- ];
- }
- function bboxOf(root) {
- const { width, height } = root.getBBox();
- return [0, 0, width, height];
- }
- function applyStyle(selection, style) {
- for (const [key, value] of Object.entries(style)) {
- selection.style(key, value);
- }
- }
- const ResizableMask = (0, createElement_1.createElement)((g) => {
- const _a = g.attributes, { x, y, width, height, class: className, renders = {}, handleSize: size = 10, document } = _a, style = __rest(_a, ["x", "y", "width", "height", "class", "renders", "handleSize", "document"]);
- if (!document ||
- width === undefined ||
- height === undefined ||
- x === undefined ||
- y === undefined)
- return;
- const half = size / 2;
- const renderRect = (g, options, document) => {
- if (!g.handle) {
- g.handle = document.createElement('rect');
- g.append(g.handle);
- }
- const { handle } = g;
- handle.attr(options);
- return handle;
- };
- const _b = (0, helper_1.subObject)((0, helper_1.omitPrefixObject)(style, 'handleNW', 'handleNE'), 'handleN'), { render: handleNRender = renderRect } = _b, handleNStyle = __rest(_b, ["render"]);
- const _c = (0, helper_1.subObject)(style, 'handleE'), { render: handleERender = renderRect } = _c, handleEStyle = __rest(_c, ["render"]);
- const _d = (0, helper_1.subObject)((0, helper_1.omitPrefixObject)(style, 'handleSE', 'handleSW'), 'handleS'), { render: handleSRender = renderRect } = _d, handleSStyle = __rest(_d, ["render"]);
- const _e = (0, helper_1.subObject)(style, 'handleW'), { render: handleWRender = renderRect } = _e, handleWStyle = __rest(_e, ["render"]);
- const _f = (0, helper_1.subObject)(style, 'handleNW'), { render: handleNWRender = renderRect } = _f, handleNWStyle = __rest(_f, ["render"]);
- const _g = (0, helper_1.subObject)(style, 'handleNE'), { render: handleNERender = renderRect } = _g, handleNEStyle = __rest(_g, ["render"]);
- const _h = (0, helper_1.subObject)(style, 'handleSE'), { render: handleSERender = renderRect } = _h, handleSEStyle = __rest(_h, ["render"]);
- const _j = (0, helper_1.subObject)(style, 'handleSW'), { render: handleSWRender = renderRect } = _j, handleSWStyle = __rest(_j, ["render"]);
- const renderHandle = (g, renderNode) => {
- const { id } = g;
- const _a = g.attributes, { x, y } = _a, style = __rest(_a, ["x", "y"]);
- const handle = renderNode(g, Object.assign({ x: 0, y: 0 }, style), document);
- handle.id = id;
- handle.style.draggable = true;
- };
- const appendHandle = (handleRender) => {
- return () => {
- const Node = (0, createElement_1.createElement)((g) => renderHandle(g, handleRender));
- return new Node({});
- };
- };
- const container = (0, selection_1.select)(g)
- .attr('className', className)
- .style('x', x)
- .style('y', y)
- .style('draggable', true);
- container
- .maybeAppend('selection', 'rect')
- .style('draggable', true)
- .style('fill', 'transparent')
- .call(applyStyle, Object.assign({ width, height }, (0, helper_1.omitPrefixObject)(style, 'handle')));
- container
- .maybeAppend('handle-n', appendHandle(handleNRender))
- .style('x', half)
- .style('y', -half)
- .style('width', width - size)
- .style('height', size)
- .style('fill', 'transparent')
- .call(applyStyle, handleNStyle);
- container
- .maybeAppend('handle-e', appendHandle(handleERender))
- .style('x', width - half)
- .style('y', half)
- .style('width', size)
- .style('height', height - size)
- .style('fill', 'transparent')
- .call(applyStyle, handleEStyle);
- container
- .maybeAppend('handle-s', appendHandle(handleSRender))
- .style('x', half)
- .style('y', height - half)
- .style('width', width - size)
- .style('height', size)
- .style('fill', 'transparent')
- .call(applyStyle, handleSStyle);
- container
- .maybeAppend('handle-w', appendHandle(handleWRender))
- .style('x', -half)
- .style('y', half)
- .style('width', size)
- .style('height', height - size)
- .style('fill', 'transparent')
- .call(applyStyle, handleWStyle);
- container
- .maybeAppend('handle-nw', appendHandle(handleNWRender))
- .style('x', -half)
- .style('y', -half)
- .style('width', size)
- .style('height', size)
- .style('fill', 'transparent')
- .call(applyStyle, handleNWStyle);
- container
- .maybeAppend('handle-ne', appendHandle(handleNERender))
- .style('x', width - half)
- .style('y', -half)
- .style('width', size)
- .style('height', size)
- .style('fill', 'transparent')
- .call(applyStyle, handleNEStyle);
- container
- .maybeAppend('handle-se', appendHandle(handleSERender))
- .style('x', width - half)
- .style('y', height - half)
- .style('width', size)
- .style('height', size)
- .style('fill', 'transparent')
- .call(applyStyle, handleSEStyle);
- container
- .maybeAppend('handle-sw', appendHandle(handleSWRender))
- .style('x', -half)
- .style('y', height - half)
- .style('width', size)
- .style('height', size)
- .style('fill', 'transparent')
- .call(applyStyle, handleSWStyle);
- });
- function brush(root, _a) {
- var { brushed = () => { }, brushended = () => { }, brushcreated = () => { }, extent = bboxOf(root), brushRegion = (x, y, x1, y1, extent) => [x, y, x1, y1], reverse = false, fill = '#777', fillOpacity = '0.3', stroke = '#fff', selectedHandles = [
- 'handle-n',
- 'handle-e',
- 'handle-s',
- 'handle-w',
- 'handle-nw',
- 'handle-ne',
- 'handle-se',
- 'handle-sw',
- ] } = _a, style = __rest(_a, ["brushed", "brushended", "brushcreated", "extent", "brushRegion", "reverse", "fill", "fillOpacity", "stroke", "selectedHandles"]);
- let start = null; // Start point of mask.
- let end = null; // End point of mask.
- let moveStart = null; // Start point of moving mask.
- let mask = null; // Mask instance.
- let background = null;
- let creating = false;
- const [originX, originY, width, height] = extent;
- (0, utils_1.setCursor)(root, 'crosshair');
- root.style.draggable = true; // Make it response to drag event.
- // Remove old mask and init new mask.
- const initMask = (x, y) => {
- if (mask)
- mask.remove();
- if (background)
- background.remove();
- start = [x, y];
- if (reverse)
- return initReverseMask();
- initNormalMask();
- };
- const initReverseMask = () => {
- background = new g_1.Path({
- style: Object.assign(Object.assign({}, style), { fill,
- fillOpacity,
- stroke, pointerEvents: 'none' }),
- });
- mask = new ResizableMask({
- // @ts-ignore
- style: {
- x: 0,
- y: 0,
- width: 0,
- height: 0,
- draggable: true,
- document: root.ownerDocument,
- },
- className: 'mask',
- });
- root.appendChild(background);
- root.appendChild(mask);
- };
- const initNormalMask = () => {
- mask = new ResizableMask({
- // @ts-ignore
- style: Object.assign(Object.assign({ document: root.ownerDocument, x: 0, y: 0 }, style), { fill,
- fillOpacity,
- stroke, draggable: true }),
- className: 'mask',
- });
- root.appendChild(mask);
- };
- // Remove mask and reset states.
- const removeMask = (emit = true) => {
- if (mask)
- mask.remove();
- if (background)
- background.remove();
- start = null;
- end = null;
- moveStart = null;
- creating = false;
- mask = null;
- background = null;
- brushended(emit);
- };
- // Update mask and invoke brushended callback.
- const updateMask = (start, end, emit = true) => {
- const [x, y, x1, y1] = normalizeBounds(start[0], start[1], end[0], end[1], extent);
- const [fx, fy, fx1, fy1] = brushRegion(x, y, x1, y1, extent);
- if (reverse)
- updateReverseMask(fx, fy, fx1, fy1);
- else
- updateNormalMask(fx, fy, fx1, fy1);
- brushed(fx, fy, fx1, fy1, emit);
- return [fx, fy, fx1, fy1];
- };
- const updateNormalMask = (x, y, x1, y1) => {
- mask.style.x = x;
- mask.style.y = y;
- mask.style.width = x1 - x;
- mask.style.height = y1 - y;
- };
- const updateReverseMask = (x, y, x1, y1) => {
- background.style.d = `
- M${originX},${originY}L${width},${originY}L${width},${height}L${originX},${height}Z
- M${x},${y}L${x},${y1}L${x1},${y1}L${x1},${y}Z
- `;
- mask.style.x = x;
- mask.style.y = y;
- mask.style.width = x1 - x;
- mask.style.height = y1 - y;
- };
- // Move and update mask.
- const moveMask = (current) => {
- const clip = (dt, start, end, min, max) => {
- if (dt + start < min)
- return min - start;
- if (dt + end > max)
- return max - end;
- return dt;
- };
- const dx = current[0] - moveStart[0];
- const dy = current[1] - moveStart[1];
- const dx1 = clip(dx, start[0], end[0], originX, width);
- const dy1 = clip(dy, start[1], end[1], originY, height);
- const currentStart = [start[0] + dx1, start[1] + dy1];
- const currentEnd = [end[0] + dx1, end[1] + dy1];
- updateMask(currentStart, currentEnd);
- };
- const handles = {
- 'handle-n': { vector: [0, 1, 0, 0], cursor: 'ns-resize' },
- 'handle-e': { vector: [0, 0, 1, 0], cursor: 'ew-resize' },
- 'handle-s': { vector: [0, 0, 0, 1], cursor: 'ns-resize' },
- 'handle-w': { vector: [1, 0, 0, 0], cursor: 'ew-resize' },
- 'handle-nw': { vector: [1, 1, 0, 0], cursor: 'nwse-resize' },
- 'handle-ne': { vector: [0, 1, 1, 0], cursor: 'nesw-resize' },
- 'handle-se': { vector: [0, 0, 1, 1], cursor: 'nwse-resize' },
- 'handle-sw': { vector: [1, 0, 0, 1], cursor: 'nesw-resize' },
- };
- const isMask = (target) => {
- return isSelection(target) || isHandle(target);
- };
- const isHandle = (target) => {
- const { id } = target;
- if (selectedHandles.indexOf(id) === -1)
- return false;
- return new Set(Object.keys(handles)).has(id);
- };
- const isSelection = (target) => {
- return target === mask.getElementById('selection');
- };
- // If target is plot area, create mask.
- // If target is mask, about to update position.
- const dragstart = (event) => {
- const { target } = event;
- const [offsetX, offsetY] = (0, utils_1.brushMousePosition)(root, event);
- if (!mask || !isMask(target)) {
- initMask(offsetX, offsetY);
- creating = true;
- return;
- }
- if (isMask(target)) {
- moveStart = [offsetX, offsetY];
- }
- };
- const drag = (event) => {
- const { target } = event;
- const mouse = (0, utils_1.brushMousePosition)(root, event);
- if (!start)
- return;
- // If target is plot area, resize mask.
- if (!moveStart)
- return updateMask(start, mouse);
- // If target is selection area, move mask.
- if (isSelection(target))
- return moveMask(mouse);
- // If target is handle area, resize mask.
- const [dx, dy] = [mouse[0] - moveStart[0], mouse[1] - moveStart[1]];
- const { id } = target;
- if (handles[id]) {
- const [sx, sy, ex, ey] = handles[id].vector;
- return updateMask([start[0] + dx * sx, start[1] + dy * sy], [end[0] + dx * ex, end[1] + dy * ey]);
- }
- };
- // If target is plot area, finish creating.
- // If target is mask, finish moving mask.
- const dragend = (event) => {
- if (moveStart) {
- moveStart = null;
- // Update start and end;
- const { x, y, width, height } = mask.style;
- start = [x, y];
- end = [x + width, y + height];
- return;
- }
- end = (0, utils_1.brushMousePosition)(root, event);
- const [fx, fy, fx1, fy1] = updateMask(start, end);
- creating = false;
- brushcreated(fx, fy, fx1, fy1, event);
- };
- // Hide mask.
- const click = (event) => {
- const { target } = event;
- if (mask && !isMask(target))
- removeMask();
- };
- // Update cursor depends on hovered element.
- const pointermove = (event) => {
- const { target } = event;
- if (!mask || !isMask(target) || creating)
- (0, utils_1.setCursor)(root, 'crosshair');
- else if (isSelection(target))
- (0, utils_1.setCursor)(root, 'move');
- else if (isHandle(target))
- (0, utils_1.setCursor)(root, handles[target.id].cursor);
- };
- const pointerleave = () => {
- (0, utils_1.setCursor)(root, 'default');
- };
- root.addEventListener('dragstart', dragstart);
- root.addEventListener('drag', drag);
- root.addEventListener('dragend', dragend);
- root.addEventListener('click', click);
- root.addEventListener('pointermove', pointermove);
- root.addEventListener('pointerleave', pointerleave);
- return {
- mask,
- move(x, y, x1, y1, emit = true) {
- if (!mask)
- initMask(x, y);
- start = [x, y];
- end = [x1, y1];
- updateMask([x, y], [x1, y1], emit);
- },
- remove() {
- if (mask)
- removeMask(false);
- },
- destroy() {
- // Do not emit brush:end event.
- if (mask)
- removeMask(false);
- (0, utils_1.setCursor)(root, 'default');
- root.removeEventListener('dragstart', dragstart);
- root.removeEventListener('drag', drag);
- root.removeEventListener('dragend', dragend);
- root.removeEventListener('click', click);
- root.removeEventListener('pointermove', pointermove);
- root.removeEventListener('pointerleave', pointerleave);
- },
- };
- }
- exports.brush = brush;
- function selectSiblingViews(target, viewInstances, brushKey) {
- return viewInstances.filter((d) => {
- if (d === target)
- return false;
- const { interaction = {} } = d.options;
- return Object.values(interaction).find((d) => d.brushKey === brushKey);
- });
- }
- function selectSiblingContainers(target, viewInstances, brushKey) {
- return selectSiblingViews(target, viewInstances, brushKey).map((d) => (0, utils_1.selectPlotArea)(d.container));
- }
- function selectSiblingOptions(target, viewInstances, brushKey) {
- return selectSiblingViews(target, viewInstances, brushKey).map((d) => d.options);
- }
- /**
- * @todo Brush over view for series view.
- * @todo Test perf.
- */
- function brushHighlight(root, _a) {
- var { elements: elementof, selectedHandles, siblings: siblingsof = (root) => [], datum, brushRegion, extent: optionalExtent, reverse, scale, coordinate, series = false, key = (d) => d, bboxOf = (root) => {
- const { x, y, width, height } = root.style;
- return { x, y, width, height };
- }, state = {}, emitter } = _a, rest = __rest(_a, ["elements", "selectedHandles", "siblings", "datum", "brushRegion", "extent", "reverse", "scale", "coordinate", "series", "key", "bboxOf", "state", "emitter"]);
- const elements = elementof(root);
- const siblings = siblingsof(root);
- const siblingElements = siblings.flatMap(elementof);
- const valueof = (0, utils_1.createValueof)(elements, datum);
- const brushStyle = (0, helper_1.subObject)(rest, 'mask');
- const { setState, removeState } = (0, utils_1.useState)(state, valueof);
- const clonedElement = new Map();
- const { width: rootWidth, height: rootHeight, x: ordinalX = 0, y: ordinalY = 0, } = bboxOf(root);
- const extent = optionalExtent
- ? optionalExtent
- : [0, 0, rootWidth, rootHeight];
- const brushended = () => {
- for (const element of [...elements, ...siblingElements]) {
- removeState(element, 'active', 'inactive');
- }
- };
- const brushed = (x, y, x1, y1) => {
- var _a;
- // Hide brush for the sibling view.
- for (const sibling of siblings)
- (_a = sibling.brush) === null || _a === void 0 ? void 0 : _a.remove();
- // Store the key of the active element.
- const keys = new Set();
- // Highlight and store selected elements.
- for (const element of elements) {
- const { min, max } = element.getLocalBounds();
- const [ex, ey] = min;
- const [ex1, ey1] = max;
- if (!intersect([ex, ey, ex1, ey1], [x, y, x1, y1])) {
- setState(element, 'inactive');
- }
- else {
- setState(element, 'active');
- keys.add(key(element));
- }
- }
- // Highlight elements with same key in sibling view.
- for (const element of siblingElements) {
- if (keys.has(key(element)))
- setState(element, 'active');
- else
- setState(element, 'inactive');
- }
- };
- const seriesBrushend = () => {
- for (const element of elements)
- removeState(element, 'inactive');
- for (const cloned of clonedElement.values())
- cloned.remove();
- clonedElement.clear();
- };
- const seriesBrushed = (x, y, x1, y1) => {
- const clone = (element) => {
- const cloned = element.cloneNode();
- cloned.__data__ = element.__data__;
- element.parentNode.appendChild(cloned);
- clonedElement.set(element, cloned);
- return cloned;
- };
- for (const element of elements) {
- const cloned = clonedElement.get(element) || clone(element);
- cloned.style.clipPath = new g_1.Rect({
- style: {
- x: x + ordinalX,
- y: y + ordinalY,
- width: x1 - x,
- height: y1 - y,
- },
- });
- setState(element, 'inactive');
- setState(cloned, 'active');
- }
- };
- const brushHandler = brush(root, Object.assign(Object.assign({}, brushStyle), { extent,
- brushRegion,
- reverse,
- selectedHandles, brushended: (emit) => {
- const handler = series ? seriesBrushend : brushended;
- if (emit) {
- emitter.emit('brush:remove', { nativeEvent: true });
- }
- handler();
- }, brushed: (x, y, x1, y1, emit) => {
- const selection = (0, scale_1.selectionOf)(x, y, x1, y1, scale, coordinate);
- if (emit) {
- emitter.emit('brush:highlight', {
- nativeEvent: true,
- data: { selection },
- });
- }
- const handler = series ? seriesBrushed : brushed;
- handler(x, y, x1, y1);
- } }));
- // Move brush and highlight data.
- const onHighlight = ({ nativeEvent, data }) => {
- if (nativeEvent)
- return;
- const { selection } = data;
- const [x, y, x1, y1] = (0, scale_1.pixelsOf)(selection, scale, coordinate);
- brushHandler.move(x, y, x1, y1, false);
- };
- emitter.on('brush:highlight', onHighlight);
- // Remove brush and reset data.
- const onRemove = () => brushHandler.remove();
- emitter.on('brush:remove', onRemove);
- // Remove event handlers.
- const preBrushDestroy = brushHandler.destroy.bind(brushHandler);
- brushHandler.destroy = () => {
- emitter.off('brush:highlight', onHighlight);
- emitter.off('brush:remove', onRemove);
- preBrushDestroy();
- };
- return brushHandler;
- }
- exports.brushHighlight = brushHighlight;
- function BrushHighlight(_a) {
- var { facet, brushKey } = _a, rest = __rest(_a, ["facet", "brushKey"]);
- return (target, viewInstances, emitter) => {
- const { container, view, options } = target;
- const plotArea = (0, utils_1.selectPlotArea)(container);
- const defaultOptions = {
- maskFill: '#777',
- maskFillOpacity: '0.3',
- maskStroke: '#fff',
- reverse: false,
- };
- const defaultStates = ['active', ['inactive', { opacity: 0.5 }]];
- const { scale, coordinate } = view;
- if (facet) {
- const bbox = plotArea.getBounds();
- const x = bbox.min[0];
- const y = bbox.min[1];
- const x1 = bbox.max[0];
- const y1 = bbox.max[1];
- return brushHighlight(plotArea.parentNode.parentNode, Object.assign(Object.assign({ elements: () => (0, utils_1.selectFacetG2Elements)(target, viewInstances), datum: (0, utils_1.createDatumof)((0, utils_1.selectFacetViews)(target, viewInstances).map((d) => d.view)), brushRegion: (x, y, x1, y1) => [x, y, x1, y1], extent: [x, y, x1, y1], state: (0, utils_1.mergeState)((0, utils_1.selectFacetViews)(target, viewInstances).map((d) => d.options), defaultStates), emitter,
- scale,
- coordinate, selectedHandles: undefined }, defaultOptions), rest));
- }
- const brush = brushHighlight(plotArea, Object.assign(Object.assign({ elements: utils_1.selectG2Elements, key: (element) => element.__data__.key, siblings: () => selectSiblingContainers(target, viewInstances, brushKey), datum: (0, utils_1.createDatumof)([
- view,
- ...selectSiblingViews(target, viewInstances, brushKey).map((d) => d.view),
- ]), brushRegion: (x, y, x1, y1) => [x, y, x1, y1], extent: undefined, state: (0, utils_1.mergeState)([options, ...selectSiblingOptions(target, viewInstances, brushKey)], defaultStates), emitter,
- scale,
- coordinate, selectedHandles: undefined }, defaultOptions), rest));
- // Bind brush to the view it belongs to.
- //@ts-ignore
- plotArea.brush = brush;
- return () => brush.destroy();
- };
- }
- exports.BrushHighlight = BrushHighlight;
- //# sourceMappingURL=brushHighlight.js.map
|