@antv/g2
Version:
the Grammar of Graphics in Javascript
182 lines • 8.68 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
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.ChartIndex = void 0;
const g_1 = require("@antv/g");
const util_1 = require("@antv/util");
const d3_array_1 = require("d3-array");
const helper_1 = require("../utils/helper");
const runtime_1 = require("../runtime");
const utils_1 = require("./utils");
function maybeTransform(options) {
const { transform = [] } = options;
const normalizeY = transform.find((d) => d.type === 'normalizeY');
if (normalizeY)
return normalizeY;
const newNormalizeY = { type: 'normalizeY' };
transform.push(newNormalizeY);
options.transform = transform;
return newNormalizeY;
}
function markValue(markState, markName, channels) {
const [value] = Array.from(markState.entries())
.filter(([mark]) => mark.type === markName)
.map(([mark]) => {
const { encode } = mark;
const channel = (name) => {
const channel = encode[name];
return [name, channel ? channel.value : undefined];
};
return Object.fromEntries(channels.map(channel));
});
return value;
}
/**
* @todo Perf
*/
function ChartIndex(_a) {
var { wait = 20, leading, trailing = false, labelFormatter = (date) => `${date}` } = _a, style = __rest(_a, ["wait", "leading", "trailing", "labelFormatter"]);
return (context) => {
const { view, container, update, setState } = context;
const { markState, scale, coordinate } = view;
// Get line mark value, exit if it is not existed.
const value = markValue(markState, 'line', ['x', 'y', 'series']);
if (!value)
return;
// Prepare channel value.
const { y: Y, x: X, series: S = [] } = value;
const I = Y.map((_, i) => i);
const sortedX = (0, d3_array_1.sort)(I.map((i) => X[i]));
// Prepare shapes.
const plotArea = (0, utils_1.selectPlotArea)(container);
const lines = container.getElementsByClassName(runtime_1.ELEMENT_CLASS_NAME);
const labels = container.getElementsByClassName(runtime_1.LABEL_CLASS_NAME);
// The format of label key: `${elementKey}-index`,
// group labels by elementKey.
const keyofLabel = (d) => d.__data__.key.split('-')[0];
const keyLabels = (0, d3_array_1.group)(labels, keyofLabel);
const rule = new g_1.Line({
style: Object.assign({ x1: 0, y1: 0, x2: 0, y2: plotArea.getAttribute('height'), stroke: 'black', lineWidth: 1 }, (0, helper_1.subObject)(style, 'rule')),
});
const text = new g_1.Text({
style: Object.assign({ x: 0, y: plotArea.getAttribute('height'), text: '', fontSize: 10 }, (0, helper_1.subObject)(style, 'label')),
});
rule.append(text);
plotArea.appendChild(rule);
// Get the closet date to the rule.
const dateByFocus = (coordinate, scaleX, focus) => {
const [normalizedX] = coordinate.invert(focus);
const date = scaleX.invert(normalizedX);
return sortedX[(0, d3_array_1.bisectCenter)(sortedX, date)];
};
// Update rule and label content.
const updateRule = (focus, date) => {
rule.setAttribute('x1', focus[0]);
rule.setAttribute('x2', focus[0]);
text.setAttribute('text', labelFormatter(date));
};
// Store the new inner state alter rerender the view.
let newView;
// Rerender the view to update basis for each line.
const updateBasisByRerender = (focus) => __awaiter(this, void 0, void 0, function* () {
// Find the closetDate to the rule.
const { x: scaleX } = scale;
const date = dateByFocus(coordinate, scaleX, focus);
updateRule(focus, date);
setState('chartIndex', (options) => {
// Clone options and get line mark.
const clonedOptions = (0, util_1.deepMix)({}, options);
const lineMark = clonedOptions.marks.find((d) => d.type === 'line');
// Update domain of y scale for the line mark.
const r = (I) => (0, d3_array_1.max)(I, (i) => +Y[i]) / (0, d3_array_1.min)(I, (i) => +Y[i]);
const k = (0, d3_array_1.max)((0, d3_array_1.rollup)(I, r, (i) => S[i]).values());
const domainY = [1 / k, k];
(0, util_1.deepMix)(lineMark, {
scale: { y: { domain: domainY } },
});
// Update normalize options.
const normalizeY = maybeTransform(lineMark);
normalizeY.groupBy = 'color';
normalizeY.basis = (I, Y) => {
const i = I[(0, d3_array_1.bisector)((i) => X[+i]).center(I, date)];
return Y[i];
};
// Disable animation.
for (const mark of clonedOptions.marks)
mark.animate = false;
return clonedOptions;
});
const newState = yield update('chartIndex');
newView = newState.view;
});
// Only apply translate to update basis for each line.
// If performance is ok, there is no need to use this
// strategy to update basis.
const updateBasisByTranslate = (focus) => {
// Find the closetDate to the rule.
const { scale, coordinate } = newView;
const { x: scaleX, y: scaleY } = scale;
const date = dateByFocus(coordinate, scaleX, focus);
updateRule(focus, date);
// Translate mark and label for better performance.
for (const line of lines) {
// Compute transform in y direction.
const { seriesIndex: SI, key } = line.__data__;
const i = SI[(0, d3_array_1.bisector)((i) => X[+i]).center(SI, date)];
const p0 = [0, scaleY.map(1)]; // basis point
const p1 = [0, scaleY.map(Y[i] / Y[SI[0]])];
const [, y0] = coordinate.map(p0);
const [, y1] = coordinate.map(p1);
const dy = y0 - y1;
line.setAttribute('transform', `translate(0, ${dy})`);
// Update line and related label.
const labels = keyLabels.get(key) || [];
for (const label of labels) {
// @todo Replace with style.transform.
// It now has unexpected behavior.
label.setAttribute('dy', dy);
}
}
};
const updateBasis = (0, util_1.throttle)((event) => {
const focus = (0, utils_1.mousePosition)(plotArea, event);
if (!focus)
return;
updateBasisByTranslate(focus);
}, wait, { leading, trailing });
updateBasisByRerender([0, 0]);
plotArea.addEventListener('pointerenter', updateBasis);
plotArea.addEventListener('pointermove', updateBasis);
plotArea.addEventListener('pointerleave', updateBasis);
return () => {
rule.remove();
plotArea.removeEventListener('pointerenter', updateBasis);
plotArea.removeEventListener('pointermove', updateBasis);
plotArea.removeEventListener('pointerleave', updateBasis);
};
};
}
exports.ChartIndex = ChartIndex;
ChartIndex.props = {
reapplyWhenUpdate: true,
};
//# sourceMappingURL=chartIndex.js.map
;