UNPKG

4.04 kBJavaScriptView Raw
1export default class Selection {
2 constructor({
3 data = [],
4 selected = new Set(),
5 focused = null,
6 getKey = item => item.id,
7 getChildren = () => [],
8 isItemSelectable = () => true
9 } = {}) {
10 this._rawData = data;
11 this._getChildren = getChildren;
12
13 this._data = this._buildData(data);
14
15 this._selected = selected;
16 this._focused = focused;
17 this._getKey = getKey;
18 this._isItemSelectable = isItemSelectable;
19 }
20
21 _buildData(data) {
22 return new Set(data);
23 }
24
25 _buildSelected(data, selected) {
26 return new Set(selected);
27 }
28
29 cloneWith({data, selected, focused}) {
30 const newData = data || this._rawData;
31
32 let newSelected;
33 if (data && !selected) {
34 newSelected = new Set([...this._buildData(newData)].
35 filter(item => [...this._selected].some(it => this._getKey(item) === this._getKey(it))));
36 newSelected = this._buildSelected(this._buildData(newData), newSelected);
37 } else if (selected) {
38 newSelected = selected;
39 } else {
40 newSelected = this._selected;
41 }
42 newSelected = new Set([...newSelected].filter(item => this._isItemSelectable(item)));
43
44 const cloneFocus = () => [...this._buildData(data)].filter(
45 item => this._focused && this._getKey(item) === this._getKey(this._focused)
46 )[0];
47
48 const newFocused = focused === undefined ? this._focused : focused;
49
50 return new this.constructor({
51 data: newData,
52 selected: newSelected,
53 focused: (data && !focused) ? cloneFocus() : newFocused,
54 getKey: this._getKey,
55 getChildren: this._getChildren,
56 isItemSelectable: this._isItemSelectable
57 });
58 }
59
60 focus(value) {
61 return this.cloneWith({focused: value});
62 }
63
64 moveUp() {
65 const focused = this._focused;
66 const data = [...this._data];
67
68 if (!focused) {
69 return this.cloneWith({focused: data[data.length - 1]});
70 }
71
72 const nextItem = data[data.indexOf(focused) - 1];
73 if (nextItem) {
74 return this.cloneWith({focused: nextItem});
75 }
76
77 return undefined;
78 }
79
80 moveDown() {
81 const focused = this._focused;
82 const data = [...this._data];
83
84 if (!focused) {
85 return this.cloneWith({focused: data[0]});
86 }
87
88 const nextItem = data[data.indexOf(focused) + 1];
89 if (nextItem) {
90 return this.cloneWith({focused: nextItem});
91 }
92
93 return undefined;
94 }
95
96 moveStart() {
97 const data = [...this._data];
98
99 if (data.length) {
100 return this.cloneWith({focused: data[0]});
101 }
102
103 return undefined;
104 }
105
106 moveEnd() {
107 const data = [...this._data];
108
109 if (data.length) {
110 return this.cloneWith({focused: data.pop()});
111 }
112
113 return undefined;
114 }
115
116 select(value = this._focused) {
117 if (!value || !this._isItemSelectable(value)) {
118 return this;
119 }
120
121 const selected = new Set(this._selected);
122 selected.add(value);
123 return this.cloneWith({selected});
124 }
125
126 deselect(value = this._focused) {
127 if (!value || !this._isItemSelectable(value)) {
128 return this;
129 }
130
131 const selected = new Set(this._selected);
132 selected.delete(value);
133 return this.cloneWith({selected});
134 }
135
136 toggleSelection(value = this._focused) {
137 if (this.isSelected(value)) {
138 return this.deselect(value);
139 } else {
140 return this.select(value);
141 }
142 }
143
144 selectAll() {
145 return this.cloneWith({selected: [...this._data]});
146 }
147
148 resetFocus() {
149 return this.cloneWith({focused: null});
150 }
151
152 resetSelection() {
153 return this.cloneWith({selected: new Set()});
154 }
155
156 reset() {
157 return this.resetFocus().resetSelection();
158 }
159
160 isFocused(value) {
161 return this._focused === value;
162 }
163
164 isSelected(value) {
165 return this._selected.has(value);
166 }
167
168 getFocused() {
169 return this._focused;
170 }
171
172 getSelected() {
173 return new Set(this._selected);
174 }
175
176 getActive() {
177 if (this._selected.size) {
178 return new Set(this._selected);
179 } else if (this._focused) {
180 return new Set([this._focused]);
181 } else {
182 return new Set();
183 }
184 }
185}