@antv/f2
Version:
Charts for mobile visualization.
173 lines (153 loc) • 4.7 kB
text/typescript
import { isFunction } from '@antv/util';
import { Component, isEqual as equal, ShapeStyleProps } from '@antv/f-engine';
import { ChartChildProps } from '../../chart';
function isEqual(origin1, origin2, fields: string[]) {
if (origin1 === origin2) {
return true;
}
for (let i = 0, len = fields.length; i < len; i++) {
const field = fields[i];
if (origin1[field] !== origin2[field]) {
return false;
}
}
return true;
}
type StyleType = (record: any) => ShapeStyleProps;
export interface SelectionProps {
selection?: {
triggerOn?: 'click' | 'press' | string;
type?: 'single' | 'multiple';
defaultSelected?: any[];
selectedStyle?: ShapeStyleProps | StyleType;
unSelectedStyle?: ShapeStyleProps | StyleType;
cancelable?: boolean;
};
}
export interface SelectionState {
selected: any[];
}
class Selection<
P extends SelectionProps = SelectionProps,
S extends SelectionState = SelectionState
> extends Component<P & ChartChildProps, S> {
constructor(props: P, context) {
super(props, context);
const { selection } = props;
if (!selection) return;
const { defaultSelected } = selection;
this.state.selected = defaultSelected;
}
didMount() {
const { props, state } = this;
const { selection, chart } = props;
if (!selection) return;
// 默认为 click
const { triggerOn = 'click' } = selection;
chart.on(triggerOn, (ev) => {
const { points, canvasX: x, canvasY: y } = ev;
const point = triggerOn === 'click' ? { x, y } : points[0];
const records = this.getSnapRecords(point);
const { type = 'single', cancelable = true } = selection;
if (!records || !records.length) {
if (cancelable) {
this.setState({
selected: null,
} as S);
}
return;
}
const { selected } = state;
const origins = records.map((record) => record.origin);
if (!selected || !selected.length) {
this.setState({
selected: origins,
} as S);
}
if (type === 'single') {
if (!cancelable) {
this.setState({
selected: origins,
} as S);
return;
}
const newSelected = [];
records.forEach((record) => {
if (!this.isSelected(record)) {
newSelected.push(record.origin);
}
});
this.setState({
selected: newSelected,
} as S);
return;
}
// 多选
const scales = chart.getScales();
const fields = Object.keys(scales);
const selectedMap = {};
selected.forEach((item) => {
const key = fields.map((field) => item[field]).join('-');
selectedMap[key] = item;
});
records.forEach((record) => {
const { origin } = record;
const key = fields.map((field) => origin[field]).join('-');
selectedMap[key] = selectedMap[key] ? null : origin;
});
const newSelected = Object.keys(selectedMap)
.map((key) => selectedMap[key])
.filter(Boolean);
this.setState({
selected: newSelected,
} as S);
});
}
willReceiveProps(nextProps: P): void {
const { selection: nextSelection } = nextProps;
const { selection: lastSelection } = this.props;
if (!nextSelection || !lastSelection) {
return;
}
const { defaultSelected: nextDefaultSelected } = nextSelection;
const { defaultSelected: lastDefaultSelected } = lastSelection;
if (!equal(nextDefaultSelected, lastDefaultSelected)) {
this.state.selected = nextDefaultSelected;
}
}
getSnapRecords(_point) {
return null;
}
isSelected(record) {
const { state, props } = this;
const { selected } = state;
if (!selected || !selected.length) {
return false;
}
const { chart } = props;
const scales = chart.getScales();
const fields = Object.keys(scales);
for (let i = 0, len = selected.length; i < len; i++) {
const item = selected[i];
if (isEqual(record.origin, item, fields)) {
return true;
}
}
return false;
}
getSelectionStyle(record) {
const { state, props } = this;
const { selected } = state;
if (!selected || !selected.length) {
return null;
}
const { selection } = props;
const { selectedStyle, unSelectedStyle } = selection;
const isSelected = this.isSelected(record);
if (isSelected) {
return isFunction(selectedStyle) ? selectedStyle(record) : selectedStyle;
}
return isFunction(unSelectedStyle) ? unSelectedStyle(record) : unSelectedStyle;
}
}
export default Selection;