@oscarpalmer/tabela 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/column.component.js +4 -4
- package/dist/components/group.component.js +28 -0
- package/dist/helpers/dom.helpers.js +1 -1
- package/dist/managers/data.manager.js +76 -15
- package/dist/managers/event.manager.js +3 -0
- package/dist/managers/filter.manager.js +5 -0
- package/dist/managers/group.manager.js +46 -0
- package/dist/managers/navigation.manager.js +1 -1
- package/dist/managers/render.manager.js +35 -10
- package/dist/managers/selection.manager.js +32 -27
- package/dist/managers/sort.manager.js +29 -2
- package/dist/models/group.model.js +0 -0
- package/dist/models/selection.model.js +0 -0
- package/dist/tabela.full.js +364 -398
- package/dist/tabela.js +4 -1
- package/package.json +1 -1
- package/src/components/column.component.ts +6 -6
- package/src/components/group.component.ts +43 -0
- package/src/components/row.component.ts +2 -2
- package/src/helpers/dom.helpers.ts +3 -1
- package/src/managers/column.manager.ts +4 -4
- package/src/managers/data.manager.ts +155 -21
- package/src/managers/event.manager.ts +7 -3
- package/src/managers/filter.manager.ts +19 -11
- package/src/managers/group.manager.ts +79 -0
- package/src/managers/navigation.manager.ts +6 -5
- package/src/managers/render.manager.ts +55 -17
- package/src/managers/row.manager.ts +2 -2
- package/src/managers/selection.manager.ts +48 -41
- package/src/managers/sort.manager.ts +76 -13
- package/src/models/column.model.ts +2 -2
- package/src/models/data.model.ts +14 -3
- package/src/models/filter.model.ts +11 -3
- package/src/models/group.model.ts +4 -0
- package/src/models/render.model.ts +2 -2
- package/src/models/selection.model.ts +9 -0
- package/src/models/sort.model.ts +11 -3
- package/src/models/tabela.model.ts +7 -41
- package/src/models/tabela.options.ts +3 -2
- package/src/tabela.ts +11 -12
- package/types/components/column.component.d.ts +3 -3
- package/types/components/group.component.d.ts +14 -0
- package/types/components/row.component.d.ts +2 -2
- package/types/helpers/style.helper.d.ts +1 -1
- package/types/managers/column.manager.d.ts +5 -5
- package/types/managers/data.manager.d.ts +5 -3
- package/types/managers/event.manager.d.ts +3 -3
- package/types/managers/filter.manager.d.ts +11 -11
- package/types/managers/group.manager.d.ts +17 -0
- package/types/managers/navigation.manager.d.ts +3 -3
- package/types/managers/render.manager.d.ts +4 -3
- package/types/managers/row.manager.d.ts +3 -3
- package/types/managers/selection.manager.d.ts +12 -6
- package/types/managers/sort.manager.d.ts +10 -8
- package/types/models/column.model.d.ts +2 -2
- package/types/models/data.model.d.ts +13 -3
- package/types/models/filter.model.d.ts +10 -3
- package/types/models/group.model.d.ts +4 -0
- package/types/models/render.model.d.ts +2 -2
- package/types/models/selection.model.d.ts +8 -0
- package/types/models/sort.model.d.ts +10 -3
- package/types/models/tabela.model.d.ts +7 -37
- package/types/models/tabela.options.d.ts +3 -2
- package/types/tabela.d.ts +4 -1
package/dist/tabela.js
CHANGED
|
@@ -2,14 +2,15 @@ import { BodyComponent } from "./components/body.component.js";
|
|
|
2
2
|
import { FooterComponent } from "./components/footer.component.js";
|
|
3
3
|
import { HeaderComponent } from "./components/header.component.js";
|
|
4
4
|
import { ColumnManager } from "./managers/column.manager.js";
|
|
5
|
+
import { SortManager } from "./managers/sort.manager.js";
|
|
5
6
|
import { DataManager } from "./managers/data.manager.js";
|
|
6
7
|
import { EventManager } from "./managers/event.manager.js";
|
|
7
8
|
import { FilterManager } from "./managers/filter.manager.js";
|
|
9
|
+
import { GroupManager } from "./managers/group.manager.js";
|
|
8
10
|
import { NavigationManager } from "./managers/navigation.manager.js";
|
|
9
11
|
import { RenderManager } from "./managers/render.manager.js";
|
|
10
12
|
import { RowManager } from "./managers/row.manager.js";
|
|
11
13
|
import { SelectionManager } from "./managers/selection.manager.js";
|
|
12
|
-
import { SortManager } from "./managers/sort.manager.js";
|
|
13
14
|
var Tabela = class {
|
|
14
15
|
#components = {
|
|
15
16
|
header: void 0,
|
|
@@ -24,6 +25,7 @@ var Tabela = class {
|
|
|
24
25
|
data: void 0,
|
|
25
26
|
event: void 0,
|
|
26
27
|
filter: void 0,
|
|
28
|
+
group: void 0,
|
|
27
29
|
navigation: void 0,
|
|
28
30
|
render: void 0,
|
|
29
31
|
row: void 0,
|
|
@@ -59,6 +61,7 @@ var Tabela = class {
|
|
|
59
61
|
this.#managers.data = new DataManager(state);
|
|
60
62
|
this.#managers.event = new EventManager(state);
|
|
61
63
|
this.#managers.filter = new FilterManager(state);
|
|
64
|
+
this.#managers.group = new GroupManager(state);
|
|
62
65
|
this.#managers.navigation = new NavigationManager(state);
|
|
63
66
|
this.#managers.render = new RenderManager(state);
|
|
64
67
|
this.#managers.row = new RowManager(state);
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {createElement} from '../helpers/dom.helpers';
|
|
2
|
-
import type {
|
|
2
|
+
import type {Column, TabelaColumn} from '../models/column.model';
|
|
3
3
|
|
|
4
4
|
export class ColumnComponent {
|
|
5
5
|
elements: ColumnElements;
|
|
6
|
-
options:
|
|
6
|
+
options: Column;
|
|
7
7
|
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(column: TabelaColumn) {
|
|
9
9
|
const width =
|
|
10
10
|
Number.parseInt(getComputedStyle(document.body).fontSize, 10) *
|
|
11
|
-
(
|
|
11
|
+
(column.width ?? column.title.length * 1.5);
|
|
12
12
|
|
|
13
13
|
this.options = {
|
|
14
|
-
...
|
|
14
|
+
...column,
|
|
15
15
|
width,
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
this.elements = createHeading(options.field, options.title, width);
|
|
18
|
+
this.elements = createHeading(this.options.field, this.options.title, width);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
destroy(): void {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {createElement} from '../helpers/dom.helpers';
|
|
2
|
+
import type {State} from '../models/tabela.model';
|
|
3
|
+
|
|
4
|
+
export class GroupComponent {
|
|
5
|
+
element: HTMLElement | undefined;
|
|
6
|
+
|
|
7
|
+
expanded = true;
|
|
8
|
+
|
|
9
|
+
filtered = 0;
|
|
10
|
+
|
|
11
|
+
selected = 0;
|
|
12
|
+
|
|
13
|
+
total = 0;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
readonly key: string,
|
|
17
|
+
readonly label: string,
|
|
18
|
+
readonly value: unknown,
|
|
19
|
+
) {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function renderGroup(state: State, component: GroupComponent): void {
|
|
23
|
+
component.element ??= createElement(
|
|
24
|
+
'div',
|
|
25
|
+
{
|
|
26
|
+
className: 'tabela__row tabela__row--group',
|
|
27
|
+
innerHTML: `<div class="tabela__cell tabela__cell--group" role="cell">
|
|
28
|
+
<button class="tabela__button tabela__button--group" data-event="group" data-key="${component.key}" type="button">
|
|
29
|
+
<span aria-hidden="true"></span>
|
|
30
|
+
<span>Open/close</span>
|
|
31
|
+
</button>
|
|
32
|
+
<p>${component.label}</p>
|
|
33
|
+
</div>`,
|
|
34
|
+
role: 'row',
|
|
35
|
+
},
|
|
36
|
+
{},
|
|
37
|
+
{
|
|
38
|
+
height: `${state.options.rowHeight}px`,
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function updateGroup(state: State, component: GroupComponent): void {}
|
|
@@ -2,7 +2,7 @@ import type {Key} from '@oscarpalmer/atoms/models';
|
|
|
2
2
|
import {setAttributes} from '@oscarpalmer/toretto/attribute';
|
|
3
3
|
import {createCell, createRow} from '../helpers/dom.helpers';
|
|
4
4
|
import type {RenderElementPool} from '../models/render.model';
|
|
5
|
-
import type {
|
|
5
|
+
import type {State} from '../models/tabela.model';
|
|
6
6
|
|
|
7
7
|
export function removeRow(pool: RenderElementPool, row: RowComponent): void {
|
|
8
8
|
if (row.element != null) {
|
|
@@ -17,7 +17,7 @@ export function removeRow(pool: RenderElementPool, row: RowComponent): void {
|
|
|
17
17
|
row.cells = {};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function renderRow(state:
|
|
20
|
+
export function renderRow(state: State, row: RowComponent): void {
|
|
21
21
|
const element = row.element ?? state.managers.render.pool.rows.shift() ?? createRow();
|
|
22
22
|
|
|
23
23
|
row.element = element;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {ColumnComponent} from '../components/column.component';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type {TabelaColumn} from '../models/column.model';
|
|
3
|
+
import type {State} from '../models/tabela.model';
|
|
4
4
|
|
|
5
5
|
export class ColumnManager {
|
|
6
6
|
items: ColumnComponent[] = [];
|
|
7
7
|
|
|
8
|
-
constructor(public state:
|
|
8
|
+
constructor(public state: State) {
|
|
9
9
|
this.set(state.options.columns);
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -56,7 +56,7 @@ export class ColumnManager {
|
|
|
56
56
|
managers.render.removeCells(fields);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
set(columns:
|
|
59
|
+
set(columns: TabelaColumn[]): void {
|
|
60
60
|
const {items, state} = this;
|
|
61
61
|
const {footer, header} = state.components;
|
|
62
62
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {select, sort} from '@oscarpalmer/atoms/array';
|
|
2
2
|
import {toMap} from '@oscarpalmer/atoms/array/to-map';
|
|
3
|
-
import {
|
|
3
|
+
import {toRecord} from '@oscarpalmer/atoms/array/to-record';
|
|
4
4
|
import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
|
|
5
|
-
import type {DataValues} from '../models/data.model';
|
|
6
|
-
import type {
|
|
5
|
+
import type {DataValues, TabelaData} from '../models/data.model';
|
|
6
|
+
import type {State} from '../models/tabela.model';
|
|
7
|
+
import {GroupComponent} from '../components/group.component';
|
|
8
|
+
import {sortWithGroups} from './sort.manager';
|
|
9
|
+
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
7
10
|
|
|
8
11
|
export class DataManager {
|
|
9
12
|
handlers = Object.freeze({
|
|
@@ -25,20 +28,61 @@ export class DataManager {
|
|
|
25
28
|
},
|
|
26
29
|
};
|
|
27
30
|
|
|
31
|
+
get keys(): Array<GroupComponent | Key> {
|
|
32
|
+
return this.values.keys.active ?? this.values.keys.original;
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
get size(): number {
|
|
29
|
-
return this.
|
|
36
|
+
return this.keys.length;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
constructor(public state:
|
|
39
|
+
constructor(public state: State) {}
|
|
33
40
|
|
|
34
41
|
async add(data: PlainObject[], render: boolean): Promise<void> {
|
|
35
42
|
const {state, values} = this;
|
|
43
|
+
const {length} = data;
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
const updates: PlainObject[] = [];
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
for (let index = 0; index < length; index += 1) {
|
|
48
|
+
const item = data[index];
|
|
49
|
+
const key = item[state.key] as Key;
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
if (values.objects.mapped.has(key)) {
|
|
52
|
+
updates.push(item);
|
|
53
|
+
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
values.objects.array.push(item);
|
|
58
|
+
values.objects.mapped.set(key, item);
|
|
59
|
+
|
|
60
|
+
if (!state.managers.group.enabled) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const groupKey = item[state.managers.group.field] as unknown;
|
|
65
|
+
|
|
66
|
+
let group = state.managers.group.get(groupKey);
|
|
67
|
+
|
|
68
|
+
if (group == null) {
|
|
69
|
+
group = new GroupComponent(String(groupKey), String(groupKey), groupKey);
|
|
70
|
+
|
|
71
|
+
values.objects.array.push(group);
|
|
72
|
+
|
|
73
|
+
state.managers.group.add(group);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!group.expanded) {
|
|
77
|
+
state.managers.group.collapsed.add(key);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
group.total += 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (updates.length > 0) {
|
|
84
|
+
void this.update(updates);
|
|
85
|
+
} else if (render) {
|
|
42
86
|
this.render();
|
|
43
87
|
}
|
|
44
88
|
}
|
|
@@ -67,14 +111,16 @@ export class DataManager {
|
|
|
67
111
|
const {values} = this;
|
|
68
112
|
|
|
69
113
|
return (active ?? false)
|
|
70
|
-
? (
|
|
71
|
-
|
|
114
|
+
? select(
|
|
115
|
+
values.keys.active ?? [],
|
|
116
|
+
key => !(key instanceof GroupComponent),
|
|
117
|
+
key => values.objects.mapped.get(key as Key)!,
|
|
118
|
+
)
|
|
119
|
+
: (values.objects.array.filter(item => !(item instanceof GroupComponent)) as PlainObject[]);
|
|
72
120
|
}
|
|
73
121
|
|
|
74
122
|
getIndex(key: Key): number {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return (values.keys.active ?? values.keys.original).indexOf(key);
|
|
123
|
+
return this.keys.indexOf(key);
|
|
78
124
|
}
|
|
79
125
|
|
|
80
126
|
async remove(items: Array<Key | PlainObject>, render: boolean): Promise<void> {
|
|
@@ -95,15 +141,49 @@ export class DataManager {
|
|
|
95
141
|
|
|
96
142
|
values.objects.mapped.delete(key);
|
|
97
143
|
|
|
98
|
-
const arrayIndex = values.objects.array.findIndex(
|
|
144
|
+
const arrayIndex = values.objects.array.findIndex(
|
|
145
|
+
item => !(item instanceof GroupComponent) && item[state.key] === key,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
let item: PlainObject | undefined;
|
|
99
149
|
|
|
100
150
|
if (arrayIndex > -1) {
|
|
101
|
-
values.objects.array.splice(arrayIndex, 1);
|
|
151
|
+
[item] = values.objects.array.splice(arrayIndex, 1) as PlainObject[];
|
|
102
152
|
}
|
|
103
153
|
|
|
104
154
|
values.keys.original.splice(values.keys.original.indexOf(key), 1);
|
|
105
155
|
|
|
106
156
|
state.managers.row.remove(key);
|
|
157
|
+
|
|
158
|
+
if (!state.managers.group.enabled || item == null) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
state.managers.group.collapsed.delete(key);
|
|
163
|
+
|
|
164
|
+
const groupKey = item[state.managers.group.field] as unknown;
|
|
165
|
+
|
|
166
|
+
const group = state.managers.group.get(groupKey);
|
|
167
|
+
|
|
168
|
+
if (group == null) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
group.total -= 1;
|
|
173
|
+
|
|
174
|
+
if (group.total > 0) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const groupIndex = values.objects.array.findIndex(
|
|
179
|
+
item => item instanceof GroupComponent && item.value === groupKey,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (groupIndex > -1) {
|
|
183
|
+
values.objects.array.splice(groupIndex, 1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
state.managers.group.remove(group);
|
|
107
187
|
}
|
|
108
188
|
|
|
109
189
|
if (render) {
|
|
@@ -114,22 +194,74 @@ export class DataManager {
|
|
|
114
194
|
render(): void {
|
|
115
195
|
const {state, values} = this;
|
|
116
196
|
|
|
117
|
-
|
|
197
|
+
if (state.managers.group.enabled) {
|
|
198
|
+
sortWithGroups(state, values.objects.array, [
|
|
199
|
+
{
|
|
200
|
+
direction: 'ascending',
|
|
201
|
+
key: state.key,
|
|
202
|
+
},
|
|
203
|
+
]);
|
|
204
|
+
} else {
|
|
205
|
+
sort(values.objects.array as PlainObject[], [
|
|
206
|
+
{
|
|
207
|
+
direction: 'ascending',
|
|
208
|
+
key: state.key,
|
|
209
|
+
},
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
values.keys.original = values.objects.array.map(item =>
|
|
214
|
+
item instanceof GroupComponent ? item : (item[state.key] as Key),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
values.objects.mapped = toMap(
|
|
218
|
+
values.objects.array.filter(item => !(item instanceof GroupComponent)) as PlainObject[],
|
|
219
|
+
item => item[state.key] as Key,
|
|
220
|
+
);
|
|
118
221
|
|
|
119
222
|
if (Object.keys(state.managers.filter.items).length > 0) {
|
|
120
223
|
state.managers.filter.filter();
|
|
121
224
|
} else if (state.managers.sort.items.length > 0) {
|
|
122
225
|
state.managers.sort.sort();
|
|
123
226
|
} else {
|
|
124
|
-
state.managers.render.update(true);
|
|
227
|
+
state.managers.render.update(true, true);
|
|
125
228
|
}
|
|
126
229
|
}
|
|
127
230
|
|
|
128
231
|
set(data: PlainObject[]): void {
|
|
129
232
|
const {state, values} = this;
|
|
130
233
|
|
|
131
|
-
|
|
132
|
-
|
|
234
|
+
const array: Array<GroupComponent | PlainObject> = data.slice();
|
|
235
|
+
|
|
236
|
+
if (state.managers.group.enabled) {
|
|
237
|
+
const grouped = toRecord.arrays(data, state.managers.group.field) as Record<
|
|
238
|
+
string,
|
|
239
|
+
PlainObject[]
|
|
240
|
+
>;
|
|
241
|
+
|
|
242
|
+
const entries = Object.entries(grouped);
|
|
243
|
+
const {length} = entries;
|
|
244
|
+
|
|
245
|
+
const groups: GroupComponent[] = [];
|
|
246
|
+
|
|
247
|
+
for (let index = 0; index < length; index += 1) {
|
|
248
|
+
const [value, items] = entries[index];
|
|
249
|
+
|
|
250
|
+
const key = String(value);
|
|
251
|
+
|
|
252
|
+
const group = new GroupComponent(key, key, value);
|
|
253
|
+
|
|
254
|
+
group.total = items.length;
|
|
255
|
+
|
|
256
|
+
groups.push(group);
|
|
257
|
+
|
|
258
|
+
array.push(group);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
state.managers.group.set(groups);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
values.objects.array = array;
|
|
133
265
|
|
|
134
266
|
this.render();
|
|
135
267
|
}
|
|
@@ -162,7 +294,9 @@ export class DataManager {
|
|
|
162
294
|
}
|
|
163
295
|
|
|
164
296
|
if (remove ?? false) {
|
|
165
|
-
const toRemove = values.keys.original.filter(
|
|
297
|
+
const toRemove = values.keys.original.filter(
|
|
298
|
+
key => !(key instanceof GroupComponent) && !keys.has(key),
|
|
299
|
+
) as Key[];
|
|
166
300
|
|
|
167
301
|
if (toRemove.length > 0) {
|
|
168
302
|
await this.remove(toRemove, false);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {on} from '@oscarpalmer/toretto/event';
|
|
2
2
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
3
|
-
import type {
|
|
3
|
+
import type {State} from '../models/tabela.model';
|
|
4
4
|
|
|
5
5
|
export class EventManager {
|
|
6
|
-
constructor(public state:
|
|
6
|
+
constructor(public state: State) {
|
|
7
7
|
mapped.set(state.element, this);
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -40,7 +40,11 @@ function onClick(event: MouseEvent): void {
|
|
|
40
40
|
const type = target?.getAttribute('data-event');
|
|
41
41
|
|
|
42
42
|
switch (type) {
|
|
43
|
-
case '
|
|
43
|
+
case 'group':
|
|
44
|
+
manager.state.managers.group.handle(target);
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case 'heading':
|
|
44
48
|
manager.onSort(event, target);
|
|
45
49
|
break;
|
|
46
50
|
|
|
@@ -3,8 +3,9 @@ import {getNumber} from '@oscarpalmer/atoms/number';
|
|
|
3
3
|
import {getString} from '@oscarpalmer/atoms/string';
|
|
4
4
|
import {endsWith, includes, startsWith} from '@oscarpalmer/atoms/string/match';
|
|
5
5
|
import {equal} from '@oscarpalmer/atoms/value/equal';
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
6
|
+
import type {TabelaFilter, TabelaFilterComparison, TabelaFilterItem} from '../models/filter.model';
|
|
7
|
+
import type {State} from '../models/tabela.model';
|
|
8
|
+
import {GroupComponent} from '../components/group.component';
|
|
8
9
|
|
|
9
10
|
export class FilterManager {
|
|
10
11
|
handlers = Object.freeze({
|
|
@@ -14,11 +15,11 @@ export class FilterManager {
|
|
|
14
15
|
set: items => this.set(items),
|
|
15
16
|
} satisfies TabelaFilter);
|
|
16
17
|
|
|
17
|
-
items: Record<string,
|
|
18
|
+
items: Record<string, TabelaFilterItem[]> = {};
|
|
18
19
|
|
|
19
|
-
constructor(public state:
|
|
20
|
+
constructor(public state: State) {}
|
|
20
21
|
|
|
21
|
-
add(item:
|
|
22
|
+
add(item: TabelaFilterItem): void {
|
|
22
23
|
if (this.items[item.field] == null) {
|
|
23
24
|
this.items[item.field] = [];
|
|
24
25
|
} else {
|
|
@@ -51,13 +52,20 @@ export class FilterManager {
|
|
|
51
52
|
filter(): void {
|
|
52
53
|
const {state} = this;
|
|
53
54
|
|
|
54
|
-
const filtered: Key
|
|
55
|
+
const filtered: Array<GroupComponent | Key> = [];
|
|
55
56
|
const filters = Object.entries(this.items);
|
|
56
57
|
|
|
57
58
|
const keysLength = state.managers.data.values.keys.original.length;
|
|
58
59
|
|
|
59
60
|
rowLoop: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
60
61
|
const key = state.managers.data.values.keys.original[keyIndex];
|
|
62
|
+
|
|
63
|
+
if (key instanceof GroupComponent) {
|
|
64
|
+
filtered.push(key);
|
|
65
|
+
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
const row = state.managers.data.values.objects.mapped.get(key);
|
|
62
70
|
|
|
63
71
|
if (row == null) {
|
|
@@ -92,13 +100,13 @@ export class FilterManager {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
remove(value: string |
|
|
103
|
+
remove(value: string | TabelaFilterItem): void {
|
|
96
104
|
if (typeof value === 'string') {
|
|
97
105
|
if (this.items[value] == null) {
|
|
98
106
|
return;
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
const keyed: Record<string,
|
|
109
|
+
const keyed: Record<string, TabelaFilterItem[]> = {};
|
|
102
110
|
|
|
103
111
|
this.items = keyed;
|
|
104
112
|
} else {
|
|
@@ -118,8 +126,8 @@ export class FilterManager {
|
|
|
118
126
|
this.filter();
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
set(items:
|
|
122
|
-
const keyed: Record<string,
|
|
129
|
+
set(items: TabelaFilterItem[]): void {
|
|
130
|
+
const keyed: Record<string, TabelaFilterItem[]> = {};
|
|
123
131
|
|
|
124
132
|
const {length} = items;
|
|
125
133
|
|
|
@@ -137,7 +145,7 @@ export class FilterManager {
|
|
|
137
145
|
}
|
|
138
146
|
}
|
|
139
147
|
|
|
140
|
-
const comparators: Record<
|
|
148
|
+
const comparators: Record<TabelaFilterComparison, (row: unknown, filter: unknown) => boolean> = {
|
|
141
149
|
contains: (row, filter) => includes(getString(row), getString(filter), true),
|
|
142
150
|
'ends-with': (row, filter) => endsWith(getString(row), getString(filter), true),
|
|
143
151
|
equals: (row, filter) => equalizer(row, filter),
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {sort} from '@oscarpalmer/atoms/array';
|
|
2
|
+
import {toRecord} from '@oscarpalmer/atoms/array/to-record';
|
|
3
|
+
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
4
|
+
import type {Key, Simplify} from '@oscarpalmer/atoms/models';
|
|
5
|
+
import type {GroupComponent} from '../components/group.component';
|
|
6
|
+
import type {State} from '../models/tabela.model';
|
|
7
|
+
|
|
8
|
+
export class GroupManager {
|
|
9
|
+
collapsed = new Set<Key>();
|
|
10
|
+
|
|
11
|
+
enabled = false;
|
|
12
|
+
|
|
13
|
+
field!: string;
|
|
14
|
+
|
|
15
|
+
items: GroupComponent[] = [];
|
|
16
|
+
|
|
17
|
+
order: Record<never, number> = {};
|
|
18
|
+
|
|
19
|
+
constructor(readonly state: State) {
|
|
20
|
+
if (isNullableOrWhitespace(state.options.grouping)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.enabled = true;
|
|
25
|
+
this.field = state.options.grouping;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
add(group: GroupComponent): void {
|
|
29
|
+
this.set([...this.items, group]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get(value: unknown) {
|
|
33
|
+
return this.items.find(item => item.value === value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
handle(button: HTMLElement): void {
|
|
37
|
+
const key = button.dataset.key;
|
|
38
|
+
const group = this.get(key);
|
|
39
|
+
|
|
40
|
+
if (group == null) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const {collapsed, items, state} = this;
|
|
45
|
+
|
|
46
|
+
group.expanded = !group.expanded;
|
|
47
|
+
|
|
48
|
+
const index = items.indexOf(group);
|
|
49
|
+
|
|
50
|
+
let first = state.managers.data.values.keys.original.indexOf(items[index]) + 1;
|
|
51
|
+
|
|
52
|
+
const last =
|
|
53
|
+
items[index + 1] == null
|
|
54
|
+
? state.managers.data.keys.length - 1
|
|
55
|
+
: state.managers.data.values.keys.original.indexOf(items[index + 1]) - 1;
|
|
56
|
+
|
|
57
|
+
for (; first <= last; first += 1) {
|
|
58
|
+
const key = state.managers.data.values.keys.original[first] as Key;
|
|
59
|
+
|
|
60
|
+
if (group.expanded) {
|
|
61
|
+
collapsed.delete(key);
|
|
62
|
+
} else {
|
|
63
|
+
collapsed.add(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
state.managers.render.update(true, true);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
remove(group: GroupComponent): void {
|
|
71
|
+
this.set(this.items.filter(item => item !== group));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
set(items: GroupComponent[]) {
|
|
75
|
+
this.items = sort(items, item => item.label);
|
|
76
|
+
|
|
77
|
+
this.order = toRecord(items as Simplify<GroupComponent>[], 'value', (_, index) => index);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import type {Key} from '@oscarpalmer/atoms/models';
|
|
3
3
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
4
|
+
import type {GroupComponent} from '../components/group.component';
|
|
4
5
|
import {getKey} from '../helpers/misc.helpers';
|
|
5
|
-
import type {
|
|
6
|
+
import type {State} from '../models/tabela.model';
|
|
6
7
|
|
|
7
8
|
export class NavigationManager {
|
|
8
9
|
active: Key | undefined;
|
|
9
10
|
|
|
10
|
-
constructor(public state:
|
|
11
|
+
constructor(public state: State) {}
|
|
11
12
|
|
|
12
13
|
destroy(): void {
|
|
13
14
|
this.state = undefined as never;
|
|
@@ -24,7 +25,7 @@ export class NavigationManager {
|
|
|
24
25
|
|
|
25
26
|
const activeDescendant = components.body.elements.group.getAttribute('aria-activedescendant');
|
|
26
27
|
|
|
27
|
-
const keys = managers.data
|
|
28
|
+
const {keys} = managers.data;
|
|
28
29
|
const {length} = keys;
|
|
29
30
|
|
|
30
31
|
let next: number;
|
|
@@ -36,7 +37,7 @@ export class NavigationManager {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
if (next != null) {
|
|
39
|
-
this.setActive(keys.at(next)
|
|
40
|
+
this.setActive(keys.at(next) as Key);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -97,7 +98,7 @@ function getIndex(
|
|
|
97
98
|
event: KeyboardEvent,
|
|
98
99
|
active: string,
|
|
99
100
|
id: number,
|
|
100
|
-
keys: Key
|
|
101
|
+
keys: Array<GroupComponent | Key>,
|
|
101
102
|
): number | undefined {
|
|
102
103
|
const key = getKey(active.replace(`tabela_${id}_row_`, ''));
|
|
103
104
|
|