UNPKG

4.66 kBPlain TextView Raw
1import { isFunction } from '@antv/util';
2import Component from '../../base/component';
3import { ShapeAttrs, Point } from '../../types';
4import equal from '../../base/equal';
5
6function isEqual(origin1, origin2, fields: string[]) {
7 if (origin1 === origin2) {
8 return true;
9 }
10 for (let i = 0, len = fields.length; i < len; i++) {
11 const field = fields[i];
12 if (origin1[field] !== origin2[field]) {
13 return false;
14 }
15 }
16 return true;
17}
18
19type StyleType = (record: any) => ShapeAttrs;
20
21export interface SelectionProps {
22 selection?: {
23 triggerOn?: 'click' | 'press' | string;
24 type?: 'single' | 'multiple';
25 defaultSelected?: any[];
26 selectedStyle?: ShapeAttrs | StyleType;
27 unSelectedStyle?: ShapeAttrs | StyleType;
28 cancelable?: boolean;
29 };
30 [k: string]: any;
31}
32
33export interface SelectionState {
34 selected: any[];
35}
36
37class Selection<
38 P extends SelectionProps = SelectionProps,
39 S extends SelectionState = SelectionState
40> extends Component<P, S> {
41 constructor(props: P, context) {
42 super(props, context);
43
44 const { selection } = props;
45 if (!selection) return;
46 const { defaultSelected } = selection;
47 this.state.selected = defaultSelected;
48 }
49
50 didMount() {
51 const { props, state, container } = this;
52 const canvas = container.get('canvas');
53 const { selection, chart } = props;
54 if (!selection) return;
55 // 默认为 click
56 const { triggerOn = 'click' } = selection;
57 canvas.on(triggerOn, (ev) => {
58 const { points } = ev;
59 const records = this.getSnapRecords(points[0]);
60 const { type = 'single', cancelable = true } = selection;
61 if (!records || !records.length) {
62 if (cancelable) {
63 this.setState({
64 selected: null,
65 } as S);
66 }
67 return;
68 }
69
70 const { selected } = state;
71 const origins = records.map((record) => record.origin);
72 if (!selected || !selected.length) {
73 this.setState({
74 selected: origins,
75 } as S);
76 }
77
78 if (type === 'single') {
79 if (!cancelable) {
80 this.setState({
81 selected: origins,
82 } as S);
83 return;
84 }
85 const newSelected = [];
86 records.forEach((record) => {
87 if (!this.isSelected(record)) {
88 newSelected.push(record.origin);
89 }
90 });
91 this.setState({
92 selected: newSelected,
93 } as S);
94 return;
95 }
96
97 // 多选
98 const scales = chart.getScales();
99 const fields = Object.keys(scales);
100 const selectedMap = {};
101 selected.forEach((item) => {
102 const key = fields.map((field) => item[field]).join('-');
103 selectedMap[key] = item;
104 });
105 records.forEach((record) => {
106 const { origin } = record;
107 const key = fields.map((field) => origin[field]).join('-');
108 selectedMap[key] = selectedMap[key] ? null : origin;
109 });
110
111 const newSelected = Object.keys(selectedMap)
112 .map((key) => selectedMap[key])
113 .filter(Boolean);
114
115 this.setState({
116 selected: newSelected,
117 } as S);
118 });
119 }
120
121 willReceiveProps(nextProps: P): void {
122 const { selection: nextSelection } = nextProps;
123 const { selection: lastSelection } = this.props;
124 if (!nextSelection || !lastSelection) {
125 return;
126 }
127 const { defaultSelected: nextDefaultSelected } = nextSelection;
128 const { defaultSelected: lastDefaultSelected } = lastSelection;
129 if (!equal(nextDefaultSelected, lastDefaultSelected)) {
130 this.state.selected = nextDefaultSelected;
131 }
132 }
133
134 getSnapRecords(_point: Point) {
135 return null;
136 }
137
138 isSelected(record) {
139 const { state, props } = this;
140 const { selected } = state;
141 if (!selected || !selected.length) {
142 return false;
143 }
144 const { chart } = props;
145 const scales = chart.getScales();
146 const fields = Object.keys(scales);
147 for (let i = 0, len = selected.length; i < len; i++) {
148 const item = selected[i];
149 if (isEqual(record.origin, item, fields)) {
150 return true;
151 }
152 }
153 return false;
154 }
155
156 getSelectionStyle(record) {
157 const { state, props } = this;
158 const { selected } = state;
159 if (!selected || !selected.length) {
160 return null;
161 }
162 const { selection } = props;
163 const { selectedStyle, unSelectedStyle } = selection;
164 const isSelected = this.isSelected(record);
165 if (isSelected) {
166 return isFunction(selectedStyle) ? selectedStyle(record) : selectedStyle;
167 }
168 return isFunction(unSelectedStyle) ? unSelectedStyle(record) : unSelectedStyle;
169 }
170}
171
172export default Selection;