@antv/g2
Version:
the Grammar of Graphics in Javascript
179 lines • 8.67 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SliderFilter = exports.SLIDER_CLASS_NAME = void 0;
const util_1 = require("@antv/util");
const g_1 = require("@antv/g");
const coordinate_1 = require("../utils/coordinate");
const scale_1 = require("../utils/scale");
exports.SLIDER_CLASS_NAME = 'slider';
function filterDataByDomain(options, scaleOptions, prefix, hasState = false, channel0 = 'x', channel1 = 'y') {
const { marks } = options;
const newMarks = marks.map((mark) => {
var _a, _b;
return (0, util_1.deepMix)({
// Hide label to keep smooth transition.
axis: {
x: { transform: [{ type: 'hide' }] },
y: { transform: [{ type: 'hide' }] },
},
}, mark, {
scale: scaleOptions,
// Don't rerender sliders.
[prefix]: Object.assign(Object.assign({}, (((_a = mark[prefix]) === null || _a === void 0 ? void 0 : _a[channel0]) && {
[channel0]: Object.assign({ preserve: true }, (hasState && { ratio: null })),
})), (((_b = mark[prefix]) === null || _b === void 0 ? void 0 : _b[channel1]) && {
[channel1]: { preserve: true },
})),
animate: false,
});
});
// Rerender and update view.
return Object.assign(Object.assign({}, options), { marks: newMarks, clip: true, animate: false });
}
function abstractValue(values, scale, reverse) {
const [x, x1] = values;
const v = reverse ? (d) => 1 - d : (d) => d;
const d0 = (0, scale_1.invert)(scale, v(x), true);
const d1 = (0, scale_1.invert)(scale, v(x1), false);
return (0, scale_1.domainOf)(scale, [d0, d1]);
}
function extentOf(domain) {
return [domain[0], domain[domain.length - 1]];
}
/**
* @todo Support click to reset after fix click and dragend conflict.
*/
function SliderFilter({ initDomain = {}, className = exports.SLIDER_CLASS_NAME, prefix = 'slider', setValue = (component, values) => component.setValues(values), hasState = false, wait = 50, leading = true, trailing = false, getInitValues = (slider) => {
var _a;
const values = (_a = slider === null || slider === void 0 ? void 0 : slider.attributes) === null || _a === void 0 ? void 0 : _a.values;
if (values[0] !== 0 || values[1] !== 1)
return values;
}, }) {
return (context, _, emitter) => {
const { container, view, update, setState } = context;
const sliders = container.getElementsByClassName(className);
if (!sliders.length)
return () => { };
let filtering = false;
const { scale, coordinate, layout } = view;
const { paddingLeft, paddingTop, paddingBottom, paddingRight } = layout;
const { x: scaleX, y: scaleY } = scale;
const transposed = (0, coordinate_1.isTranspose)(coordinate);
const channelOf = (orientation) => {
const channel0 = orientation === 'vertical' ? 'y' : 'x';
const channel1 = orientation === 'vertical' ? 'x' : 'y';
if (transposed)
return [channel1, channel0];
return [channel0, channel1];
};
const sliderHandler = new Map();
const emitHandlers = new Set();
// Store current domain of x and y scale.
const channelDomain = {
x: initDomain.x || scaleX.getOptions().domain,
y: initDomain.y || scaleY.getOptions().domain,
};
for (const slider of sliders) {
const { orientation } = slider.attributes;
const [channel0, channel1] = channelOf(orientation);
const eventName = `${prefix}${(0, util_1.upperFirst)(channel0)}:filter`;
const isX = channel0 === 'x';
const { ratio: ratioX } = scaleX.getOptions();
const { ratio: ratioY } = scaleY.getOptions();
const domainsOf = (event) => {
// From abstract values.
if (event.data) {
const { selection } = event.data;
const [X = extentOf(channelDomain.x), Y = extentOf(channelDomain.y)] = selection;
return isX
? [(0, scale_1.domainOf)(scaleX, X, ratioX), (0, scale_1.domainOf)(scaleY, Y, ratioY)]
: [(0, scale_1.domainOf)(scaleY, Y, ratioY), (0, scale_1.domainOf)(scaleX, X, ratioX)];
}
// From visual values.
const { value: values } = event.detail;
const scale0 = scale[channel0];
const domain0 = abstractValue(values, scale0, transposed && orientation === 'horizontal');
const domain1 = channelDomain[channel1];
return [domain0, domain1];
};
const onValueChange = (0, util_1.throttle)((event) => __awaiter(this, void 0, void 0, function* () {
const { initValue = false } = event;
if (filtering && !initValue)
return;
filtering = true;
const { nativeEvent = true } = event;
// Get and update domain.
const [domain0, domain1] = domainsOf(event);
channelDomain[channel0] = domain0;
channelDomain[channel1] = domain1;
if (nativeEvent) {
// Emit events.
const X = isX ? domain0 : domain1;
const Y = isX ? domain1 : domain0;
emitter.emit(eventName, Object.assign(Object.assign({}, event), { nativeEvent, data: { selection: [extentOf(X), extentOf(Y)] } }));
}
setState(slider, (options) => (Object.assign(Object.assign({}, filterDataByDomain(options,
// Set nice to false to avoid modify domain.
// Only update domain of current slider / scrollbar.
{ [channel0]: { domain: domain0, nice: false } }, prefix, hasState, channel0, channel1)), { paddingLeft,
paddingTop,
paddingBottom,
paddingRight })));
yield update();
filtering = false;
}), wait, { leading, trailing });
const emitHandler = (event) => {
const { nativeEvent } = event;
if (nativeEvent)
return;
const { data } = event;
const { selection } = data;
const [X, Y] = selection;
// Update data.
slider.dispatchEvent(new g_1.CustomEvent('valuechange', {
data,
nativeEvent: false,
}));
// Update slider.
const V = isX ? (0, scale_1.abstractOf)(X, scaleX) : (0, scale_1.abstractOf)(Y, scaleY);
setValue(slider, V);
};
emitter.on(eventName, emitHandler);
slider.addEventListener('valuechange', onValueChange);
sliderHandler.set(slider, onValueChange);
emitHandlers.add([eventName, emitHandler]);
const values = getInitValues(slider);
if (values) {
// Init values.
slider.dispatchEvent(new g_1.CustomEvent('valuechange', {
detail: {
value: values,
},
nativeEvent: false,
initValue: true,
}));
}
}
// Update to async state such as scale.
update();
return () => {
for (const [slider, handler] of sliderHandler) {
slider.removeEventListener('valuechange', handler);
}
for (const [name, handler] of emitHandlers) {
emitter.off(name, handler);
}
};
};
}
exports.SliderFilter = SliderFilter;
//# sourceMappingURL=sliderFilter.js.map
;