@oscarpalmer/tabela 0.8.0 → 0.10.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/body.component.js +2 -15
- package/dist/components/footer.component.js +3 -3
- package/dist/components/header.component.js +2 -2
- package/dist/components/row.component.js +13 -4
- package/dist/helpers/dom.helpers.js +5 -10
- package/dist/helpers/misc.helpers.js +7 -0
- package/dist/helpers/style.helper.js +6 -0
- package/dist/managers/column.manager.js +7 -2
- package/dist/managers/data.manager.js +6 -4
- package/dist/managers/event.manager.js +6 -2
- package/dist/managers/filter.manager.js +92 -0
- package/dist/managers/{virtualization.manager.js → render.manager.js} +28 -22
- package/dist/managers/row.manager.js +9 -2
- package/dist/managers/selection.manager.js +191 -0
- package/dist/managers/sort.manager.js +17 -6
- package/dist/models/render.model.js +0 -0
- package/dist/tabela.full.js +1253 -105
- package/dist/tabela.js +19 -6
- package/package.json +1 -1
- package/src/components/body.component.ts +4 -21
- package/src/components/footer.component.ts +3 -3
- package/src/components/header.component.ts +2 -2
- package/src/components/row.component.ts +22 -7
- package/src/helpers/dom.helpers.ts +3 -10
- package/src/helpers/misc.helpers.ts +15 -0
- package/src/helpers/style.helper.ts +6 -0
- package/src/managers/column.manager.ts +9 -1
- package/src/managers/data.manager.ts +9 -5
- package/src/managers/event.manager.ts +10 -3
- package/src/managers/filter.manager.ts +154 -0
- package/src/managers/{virtualization.manager.ts → render.manager.ts} +36 -31
- package/src/managers/row.manager.ts +16 -2
- package/src/managers/selection.manager.ts +338 -0
- package/src/managers/sort.manager.ts +35 -16
- package/src/models/filter.model.ts +17 -0
- package/src/models/{virtualization.model.ts → render.model.ts} +3 -3
- package/src/models/sort.model.ts +1 -1
- package/src/models/tabela.model.ts +22 -2
- package/src/tabela.ts +28 -6
- package/types/components/row.component.d.ts +2 -2
- package/types/helpers/dom.helpers.d.ts +1 -1
- package/types/helpers/misc.helpers.d.ts +2 -0
- package/types/helpers/style.helper.d.ts +1 -0
- package/types/managers/data.manager.d.ts +1 -1
- package/types/managers/event.manager.d.ts +1 -1
- package/types/managers/filter.manager.d.ts +19 -0
- package/types/managers/{virtualization.manager.d.ts → render.manager.d.ts} +6 -6
- package/types/managers/selection.manager.d.ts +19 -0
- package/types/managers/sort.manager.d.ts +5 -2
- package/types/models/filter.model.d.ts +6 -0
- package/types/models/{virtualization.model.d.ts → render.model.d.ts} +3 -3
- package/types/models/sort.model.d.ts +1 -1
- package/types/models/tabela.model.d.ts +20 -2
- package/types/tabela.d.ts +3 -1
- /package/dist/models/{virtualization.model.js → filter.model.js} +0 -0
package/dist/tabela.js
CHANGED
|
@@ -4,9 +4,11 @@ import { HeaderComponent } from "./components/header.component.js";
|
|
|
4
4
|
import { ColumnManager } from "./managers/column.manager.js";
|
|
5
5
|
import { DataManager } from "./managers/data.manager.js";
|
|
6
6
|
import { EventManager } from "./managers/event.manager.js";
|
|
7
|
+
import { FilterManager } from "./managers/filter.manager.js";
|
|
7
8
|
import { RowManager } from "./managers/row.manager.js";
|
|
9
|
+
import { SelectionManager } from "./managers/selection.manager.js";
|
|
8
10
|
import { SortManager } from "./managers/sort.manager.js";
|
|
9
|
-
import {
|
|
11
|
+
import { RenderManager } from "./managers/render.manager.js";
|
|
10
12
|
var Tabela = class {
|
|
11
13
|
#components = {
|
|
12
14
|
header: void 0,
|
|
@@ -19,11 +21,15 @@ var Tabela = class {
|
|
|
19
21
|
column: void 0,
|
|
20
22
|
data: void 0,
|
|
21
23
|
event: void 0,
|
|
24
|
+
filter: void 0,
|
|
25
|
+
render: void 0,
|
|
22
26
|
row: void 0,
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
selection: void 0,
|
|
28
|
+
sort: void 0
|
|
25
29
|
};
|
|
26
30
|
data;
|
|
31
|
+
filter;
|
|
32
|
+
selection;
|
|
27
33
|
sort;
|
|
28
34
|
get key() {
|
|
29
35
|
return this.#key;
|
|
@@ -40,13 +46,17 @@ var Tabela = class {
|
|
|
40
46
|
this.#components.footer = new FooterComponent();
|
|
41
47
|
this.#managers.column = new ColumnManager(this.#managers, this.#components, options.columns);
|
|
42
48
|
this.#managers.data = new DataManager(this.#managers, this.#components, options.key);
|
|
43
|
-
this.#managers.event = new EventManager(this.#
|
|
49
|
+
this.#managers.event = new EventManager(this.#element, this.#managers);
|
|
50
|
+
this.#managers.filter = new FilterManager(this.#managers);
|
|
51
|
+
this.#managers.render = new RenderManager(this.#managers, this.#components);
|
|
44
52
|
this.#managers.row = new RowManager(this.#managers, options.rowHeight);
|
|
53
|
+
this.#managers.selection = new SelectionManager(this.#element, this.#managers);
|
|
45
54
|
this.#managers.sort = new SortManager(this.#managers);
|
|
46
|
-
this.#managers.virtualization = new VirtualizationManager(this.#managers, this.#components);
|
|
47
55
|
element.append(this.#components.header.elements.group, this.#components.body.elements.group, this.#components.footer.elements.group);
|
|
48
56
|
this.#managers.data.set(options.data);
|
|
49
57
|
this.data = this.#managers.data.handlers;
|
|
58
|
+
this.filter = this.#managers.filter.handlers;
|
|
59
|
+
this.selection = this.#managers.selection.handlers;
|
|
50
60
|
this.sort = this.#managers.sort.handlers;
|
|
51
61
|
}
|
|
52
62
|
destroy() {
|
|
@@ -58,8 +68,11 @@ var Tabela = class {
|
|
|
58
68
|
components.header.destroy();
|
|
59
69
|
managers.column.destroy();
|
|
60
70
|
managers.data.destroy();
|
|
71
|
+
managers.event.destroy();
|
|
72
|
+
managers.filter.destroy();
|
|
73
|
+
managers.render.destroy();
|
|
61
74
|
managers.row.destroy();
|
|
62
|
-
managers.
|
|
75
|
+
managers.sort.destroy();
|
|
63
76
|
element.innerHTML = "";
|
|
64
77
|
element.role = "";
|
|
65
78
|
element.classList.remove("tabela");
|
package/package.json
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import {setStyles} from '@oscarpalmer/toretto/style';
|
|
2
1
|
import {createElement, createRowGroup} from '../helpers/dom.helpers';
|
|
3
2
|
import type {BodyElements} from '../models/body.model';
|
|
4
3
|
|
|
5
4
|
function createFaker(): HTMLDivElement {
|
|
6
|
-
return createElement(
|
|
7
|
-
'
|
|
8
|
-
|
|
9
|
-
{},
|
|
10
|
-
{
|
|
11
|
-
height: '0',
|
|
12
|
-
inset: '0 auto auto 0',
|
|
13
|
-
opacity: '0',
|
|
14
|
-
pointerEvents: 'none',
|
|
15
|
-
position: 'absolute',
|
|
16
|
-
width: '1px',
|
|
17
|
-
},
|
|
18
|
-
);
|
|
5
|
+
return createElement('div', {
|
|
6
|
+
className: 'tabela__faker',
|
|
7
|
+
}, {}, {});
|
|
19
8
|
}
|
|
20
9
|
|
|
21
10
|
export class BodyComponent {
|
|
@@ -29,16 +18,10 @@ export class BodyComponent {
|
|
|
29
18
|
|
|
30
19
|
this.elements.group = group;
|
|
31
20
|
|
|
32
|
-
group.className += ' tabela__rowgroup
|
|
21
|
+
group.className += ' tabela__rowgroup--body';
|
|
33
22
|
|
|
34
23
|
group.tabIndex = 0;
|
|
35
24
|
|
|
36
|
-
setStyles(group, {
|
|
37
|
-
height: '100%',
|
|
38
|
-
overflow: 'auto',
|
|
39
|
-
position: 'relative',
|
|
40
|
-
});
|
|
41
|
-
|
|
42
25
|
group.append(this.elements.faker);
|
|
43
26
|
}
|
|
44
27
|
|
|
@@ -14,8 +14,8 @@ export class FooterComponent {
|
|
|
14
14
|
cells: [],
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
group.className += ' tabela__rowgroup
|
|
18
|
-
row.className += ' tabela__row
|
|
17
|
+
group.className += ' tabela__rowgroup--footer';
|
|
18
|
+
row.className += ' tabela__row--footer';
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
destroy(): void {
|
|
@@ -35,7 +35,7 @@ export class FooterComponent {
|
|
|
35
35
|
for (let index = 0; index < length; index += 1) {
|
|
36
36
|
const cell = createCell(columns[index].options.width ?? 4, false);
|
|
37
37
|
|
|
38
|
-
cell.className += ' tabela__cell
|
|
38
|
+
cell.className += ' tabela__cell--footer';
|
|
39
39
|
cell.innerHTML = ' ';
|
|
40
40
|
|
|
41
41
|
elements.cells.push(cell);
|
|
@@ -10,8 +10,8 @@ export class HeaderComponent {
|
|
|
10
10
|
|
|
11
11
|
this.elements = {group, row};
|
|
12
12
|
|
|
13
|
-
group.className += ' tabela__rowgroup
|
|
14
|
-
row.className += ' tabela__row
|
|
13
|
+
group.className += ' tabela__rowgroup--header';
|
|
14
|
+
row.className += ' tabela__row--header';
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
destroy(): void {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type {Key} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {setAttributes} from '@oscarpalmer/toretto/attribute';
|
|
2
3
|
import {createCell, createRow} from '../helpers/dom.helpers';
|
|
4
|
+
import type {RenderElementPool} from '../models/render.model';
|
|
3
5
|
import type {TabelaManagers} from '../models/tabela.model';
|
|
4
|
-
import type {VirtualizationPool} from '../models/virtualization.model';
|
|
5
6
|
|
|
6
|
-
export function removeRow(pool:
|
|
7
|
+
export function removeRow(pool: RenderElementPool, row: RowComponent): void {
|
|
7
8
|
if (row.element != null) {
|
|
8
9
|
row.element.innerHTML = '';
|
|
9
10
|
|
|
@@ -17,13 +18,28 @@ export function removeRow(pool: VirtualizationPool, row: RowComponent): void {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function renderRow(managers: TabelaManagers, row: RowComponent): void {
|
|
20
|
-
const element = row.element ?? managers.
|
|
21
|
+
const element = row.element ?? managers.render.pool.rows.shift() ?? createRow();
|
|
21
22
|
|
|
22
23
|
row.element = element;
|
|
23
24
|
|
|
24
|
-
element.dataset.key = String(row.key);
|
|
25
25
|
element.innerHTML = '';
|
|
26
26
|
|
|
27
|
+
const selected = managers.selection.items.has(row.key);
|
|
28
|
+
|
|
29
|
+
setAttributes(element, {
|
|
30
|
+
'aria-selected': String(selected),
|
|
31
|
+
'data-event': 'row',
|
|
32
|
+
'data-key': String(row.key),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
element.classList.add('tabela__row--body');
|
|
36
|
+
|
|
37
|
+
if (selected) {
|
|
38
|
+
element.classList.add('tabela__row--selected');
|
|
39
|
+
} else {
|
|
40
|
+
element.classList.remove('tabela__row--selected');
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
const columns = managers.column.items;
|
|
28
44
|
const {length} = columns;
|
|
29
45
|
|
|
@@ -36,11 +52,10 @@ export function renderRow(managers: TabelaManagers, row: RowComponent): void {
|
|
|
36
52
|
for (let index = 0; index < length; index += 1) {
|
|
37
53
|
const {options} = columns[index];
|
|
38
54
|
|
|
39
|
-
managers.
|
|
55
|
+
managers.render.pool.cells[options.field] ??= [];
|
|
40
56
|
|
|
41
57
|
const cell =
|
|
42
|
-
managers.
|
|
43
|
-
createCell(options.width);
|
|
58
|
+
managers.render.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
|
|
44
59
|
|
|
45
60
|
cell.textContent = String(data[options.field]);
|
|
46
61
|
|
|
@@ -20,7 +20,7 @@ export function createCell(width: number, body?: boolean): HTMLDivElement {
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
if (body ?? true) {
|
|
23
|
-
cell.classList.add('tabela__cell
|
|
23
|
+
cell.classList.add('tabela__cell--body');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
return cell;
|
|
@@ -65,14 +65,14 @@ export function createRowGroup(withRow?: boolean) {
|
|
|
65
65
|
return group;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const row = createRow(
|
|
68
|
+
const row = createRow();
|
|
69
69
|
|
|
70
70
|
group.append(row);
|
|
71
71
|
|
|
72
72
|
return {group, row};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export function createRow(
|
|
75
|
+
export function createRow(): HTMLDivElement {
|
|
76
76
|
const row = createElement(
|
|
77
77
|
'div',
|
|
78
78
|
{
|
|
@@ -83,12 +83,5 @@ export function createRow(withStyle?: boolean): HTMLDivElement {
|
|
|
83
83
|
{},
|
|
84
84
|
);
|
|
85
85
|
|
|
86
|
-
if (withStyle ?? true) {
|
|
87
|
-
setStyles(row, {
|
|
88
|
-
inset: '0 auto auto 0',
|
|
89
|
-
position: 'absolute',
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
86
|
return row;
|
|
94
87
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type {Key} from '@oscarpalmer/atoms/models';
|
|
2
|
+
|
|
3
|
+
export function getKey(value: unknown): Key | undefined {
|
|
4
|
+
if (typeof value === 'number') {
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (typeof value !== 'string') {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return integerExpression.test(value) ? Number.parseInt(value, 10) : value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const integerExpression = /^\d+$/;
|
|
@@ -14,6 +14,12 @@ export class ColumnManager {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
destroy(): void {
|
|
17
|
+
const {length} = this.items;
|
|
18
|
+
|
|
19
|
+
for (let index = 0; index < length; index += 1) {
|
|
20
|
+
this.items[index].destroy();
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
this.items.length = 0;
|
|
18
24
|
}
|
|
19
25
|
|
|
@@ -40,6 +46,8 @@ export class ColumnManager {
|
|
|
40
46
|
);
|
|
41
47
|
|
|
42
48
|
if (itemIndex > -1) {
|
|
49
|
+
items[itemIndex].destroy();
|
|
50
|
+
|
|
43
51
|
items.splice(itemIndex, 1);
|
|
44
52
|
}
|
|
45
53
|
}
|
|
@@ -47,7 +55,7 @@ export class ColumnManager {
|
|
|
47
55
|
components.header.update(items);
|
|
48
56
|
components.footer.update(items);
|
|
49
57
|
|
|
50
|
-
managers.
|
|
58
|
+
managers.render.removeCells(fields);
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
set(columns: TabelaColumnOptions[]): void {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {sort} from '@oscarpalmer/atoms/array';
|
|
1
|
+
import {push, sort} from '@oscarpalmer/atoms/array';
|
|
2
2
|
import {toMap} from '@oscarpalmer/atoms/array/to-map';
|
|
3
3
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
4
4
|
import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
|
|
@@ -6,7 +6,7 @@ import type {DataValues} from '../models/data.model';
|
|
|
6
6
|
import type {TabelaComponents, TabelaData, TabelaManagers} from '../models/tabela.model';
|
|
7
7
|
|
|
8
8
|
export class DataManager {
|
|
9
|
-
|
|
9
|
+
handlers = Object.freeze({
|
|
10
10
|
add: data => void this.add(data, true),
|
|
11
11
|
clear: () => void this.clear(),
|
|
12
12
|
get: active => this.get(active),
|
|
@@ -38,7 +38,7 @@ export class DataManager {
|
|
|
38
38
|
async add(data: PlainObject[], render: boolean): Promise<void> {
|
|
39
39
|
const {field, values} = this;
|
|
40
40
|
|
|
41
|
-
values.objects.array
|
|
41
|
+
push(values.objects.array, data);
|
|
42
42
|
|
|
43
43
|
values.objects.mapped = toMap(values.objects.array, field) as Map<Key, PlainObject>;
|
|
44
44
|
|
|
@@ -61,6 +61,8 @@ export class DataManager {
|
|
|
61
61
|
values.keys.active = undefined;
|
|
62
62
|
values.keys.original.length = 0;
|
|
63
63
|
values.objects.array.length = 0;
|
|
64
|
+
|
|
65
|
+
this.handlers = undefined as never;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
get(active?: boolean): PlainObject[] {
|
|
@@ -110,10 +112,12 @@ export class DataManager {
|
|
|
110
112
|
|
|
111
113
|
values.keys.original = sort(values.objects.array.map(item => item[field] as Key));
|
|
112
114
|
|
|
113
|
-
if (managers.
|
|
115
|
+
if (Object.keys(managers.filter.items).length > 0) {
|
|
116
|
+
managers.filter.filter();
|
|
117
|
+
} else if (managers.sort.items.length > 0) {
|
|
114
118
|
managers.sort.sort();
|
|
115
119
|
} else {
|
|
116
|
-
managers.
|
|
120
|
+
managers.render.update(true);
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
|
|
@@ -4,13 +4,13 @@ import type {TabelaManagers} from '../models/tabela.model';
|
|
|
4
4
|
import {findAncestor} from '@oscarpalmer/toretto';
|
|
5
5
|
|
|
6
6
|
export class EventManager {
|
|
7
|
-
listener
|
|
7
|
+
listener: RemovableEventListener;
|
|
8
8
|
|
|
9
9
|
constructor(
|
|
10
|
-
readonly managers: TabelaManagers,
|
|
11
10
|
element: HTMLElement,
|
|
11
|
+
readonly managers: TabelaManagers,
|
|
12
12
|
) {
|
|
13
|
-
on(
|
|
13
|
+
this.listener = on(
|
|
14
14
|
element,
|
|
15
15
|
'click',
|
|
16
16
|
event => {
|
|
@@ -39,6 +39,13 @@ export class EventManager {
|
|
|
39
39
|
case 'heading':
|
|
40
40
|
this.onSort(event, target);
|
|
41
41
|
break;
|
|
42
|
+
|
|
43
|
+
case 'row':
|
|
44
|
+
this.managers.selection.handle(event, target);
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
default:
|
|
48
|
+
break;
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type {Key} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {getNumber} from '@oscarpalmer/atoms/number';
|
|
3
|
+
import {getString} from '@oscarpalmer/atoms/string';
|
|
4
|
+
import {endsWith, includes, startsWith} from '@oscarpalmer/atoms/string/match';
|
|
5
|
+
import {equal} from '@oscarpalmer/atoms/value/equal';
|
|
6
|
+
import type {FilterComparison, FilterItem} from '../models/filter.model';
|
|
7
|
+
import type {TabelaFilter, TabelaManagers} from '../models/tabela.model';
|
|
8
|
+
|
|
9
|
+
export class FilterManager {
|
|
10
|
+
handlers = Object.freeze({
|
|
11
|
+
add: item => this.add(item),
|
|
12
|
+
clear: () => this.clear(),
|
|
13
|
+
remove: value => this.remove(value),
|
|
14
|
+
set: items => this.set(items),
|
|
15
|
+
} satisfies TabelaFilter);
|
|
16
|
+
|
|
17
|
+
items: Record<string, FilterItem[]> = {};
|
|
18
|
+
|
|
19
|
+
constructor(readonly managers: TabelaManagers) {}
|
|
20
|
+
|
|
21
|
+
add(item: FilterItem): void {
|
|
22
|
+
if (this.items[item.field] == null) {
|
|
23
|
+
this.items[item.field] = [];
|
|
24
|
+
} else {
|
|
25
|
+
const index = this.items[item.field].findIndex(existing => equal(existing, item));
|
|
26
|
+
|
|
27
|
+
if (index !== -1) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.items[item.field].push(item);
|
|
33
|
+
|
|
34
|
+
this.filter();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
clear(): void {
|
|
38
|
+
if (Object.keys(this.items).length > 0) {
|
|
39
|
+
this.items = {};
|
|
40
|
+
|
|
41
|
+
this.filter();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
destroy(): void {
|
|
46
|
+
this.handlers = undefined as never;
|
|
47
|
+
this.items = {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
filter(): void {
|
|
51
|
+
const {managers} = this;
|
|
52
|
+
|
|
53
|
+
const filtered: Key[] = [];
|
|
54
|
+
const filters = Object.entries(this.items);
|
|
55
|
+
|
|
56
|
+
const keysLength = managers.data.values.keys.original.length;
|
|
57
|
+
|
|
58
|
+
rowLoop: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
59
|
+
const key = managers.data.values.keys.original[keyIndex];
|
|
60
|
+
const row = managers.data.values.objects.mapped.get(key);
|
|
61
|
+
|
|
62
|
+
if (row == null) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
filterLoop: for (let filterIndex = 0; filterIndex < filters.length; filterIndex += 1) {
|
|
67
|
+
const [field, items] = filters[filterIndex];
|
|
68
|
+
|
|
69
|
+
const value = row[field];
|
|
70
|
+
|
|
71
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
|
|
72
|
+
const filter = items[itemIndex];
|
|
73
|
+
|
|
74
|
+
if (comparators[filter.comparison](value, filter.value)) {
|
|
75
|
+
continue filterLoop;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
continue rowLoop;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
filtered.push(key);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
managers.data.values.keys.active = filtered;
|
|
86
|
+
|
|
87
|
+
if (managers.sort.items.length > 0) {
|
|
88
|
+
managers.sort.sort();
|
|
89
|
+
} else {
|
|
90
|
+
managers.render.update(true, true);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
remove(value: string | FilterItem): void {
|
|
95
|
+
if (typeof value === 'string') {
|
|
96
|
+
if (this.items[value] == null) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const keyed: Record<string, FilterItem[]> = {};
|
|
101
|
+
|
|
102
|
+
this.items = keyed;
|
|
103
|
+
} else {
|
|
104
|
+
const {field} = value;
|
|
105
|
+
|
|
106
|
+
if (this.items[field] == null) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const index = this.items[field].findIndex(item => equal(item, value));
|
|
111
|
+
|
|
112
|
+
if (index === -1) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.filter();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
set(items: FilterItem[]): void {
|
|
121
|
+
const keyed: Record<string, FilterItem[]> = {};
|
|
122
|
+
|
|
123
|
+
const {length} = items;
|
|
124
|
+
|
|
125
|
+
for (let index = 0; index < length; index += 1) {
|
|
126
|
+
const item = items[index];
|
|
127
|
+
|
|
128
|
+
keyed[item.field] ??= [];
|
|
129
|
+
|
|
130
|
+
keyed[item.field].push(item);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.items = keyed;
|
|
134
|
+
|
|
135
|
+
this.filter();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const comparators: Record<FilterComparison, (row: unknown, filter: unknown) => boolean> = {
|
|
140
|
+
contains: (row, filter) => includes(getString(row), getString(filter), true),
|
|
141
|
+
'ends-with': (row, filter) => endsWith(getString(row), getString(filter), true),
|
|
142
|
+
equals: (row, filter) => equalizer(row, filter),
|
|
143
|
+
'greater-than': (row, filter) => getNumber(row) > getNumber(filter),
|
|
144
|
+
'greater-than-or-equal': (row, filter) => getNumber(row) >= getNumber(filter),
|
|
145
|
+
'less-than': (row, filter) => getNumber(row) < getNumber(filter),
|
|
146
|
+
'less-than-or-equal': (row, filter) => getNumber(row) <= getNumber(filter),
|
|
147
|
+
'not-contains': (row, filter) => !includes(getString(row), getString(filter), true),
|
|
148
|
+
'not-equals': (row, filter) => !equalizer(row, filter),
|
|
149
|
+
'starts-with': (row, filter) => startsWith(getString(row), getString(filter), true),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const equalizer = equal.initialize({
|
|
153
|
+
ignoreCase: true,
|
|
154
|
+
});
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
+
import type {Key} from '@oscarpalmer/atoms/models';
|
|
1
2
|
import {on} from '@oscarpalmer/toretto/event';
|
|
2
3
|
import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
|
|
3
|
-
import {removeRow, renderRow
|
|
4
|
+
import {removeRow, renderRow} from '../components/row.component';
|
|
5
|
+
import type {RenderElementPool, RenderRange, RenderState} from '../models/render.model';
|
|
4
6
|
import type {TabelaComponents, TabelaManagers} from '../models/tabela.model';
|
|
5
|
-
import type {
|
|
6
|
-
VirtualizationPool,
|
|
7
|
-
VirtualizationRange,
|
|
8
|
-
VirtualizationState,
|
|
9
|
-
} from '../models/virtualization.model';
|
|
10
7
|
|
|
11
|
-
function getRange(this:
|
|
8
|
+
function getRange(this: RenderManager, down: boolean): RenderRange {
|
|
12
9
|
const {components, managers} = this;
|
|
13
10
|
const {clientHeight, scrollTop} = components.body.elements.group;
|
|
14
11
|
|
|
15
12
|
const first = Math.floor(scrollTop / managers.row.height);
|
|
16
13
|
|
|
17
14
|
const last = Math.min(
|
|
18
|
-
managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length - 1,
|
|
15
|
+
(managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length) - 1,
|
|
19
16
|
Math.ceil((scrollTop + clientHeight) / managers.row.height) - 1,
|
|
20
17
|
);
|
|
21
18
|
|
|
@@ -25,14 +22,14 @@ function getRange(this: VirtualizationManager, down: boolean): VirtualizationRan
|
|
|
25
22
|
const start = Math.max(0, first - before);
|
|
26
23
|
|
|
27
24
|
const end = Math.min(
|
|
28
|
-
managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length - 1,
|
|
25
|
+
(managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length) - 1,
|
|
29
26
|
last + after,
|
|
30
27
|
);
|
|
31
28
|
|
|
32
29
|
return {end, start};
|
|
33
30
|
}
|
|
34
31
|
|
|
35
|
-
function onScroll(this:
|
|
32
|
+
function onScroll(this: RenderManager): void {
|
|
36
33
|
if (!this.state.active) {
|
|
37
34
|
requestAnimationFrame(() => {
|
|
38
35
|
const top = this.components.body.elements.group.scrollTop;
|
|
@@ -47,22 +44,22 @@ function onScroll(this: VirtualizationManager): void {
|
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
export class
|
|
47
|
+
export class RenderManager {
|
|
51
48
|
fragment!: DocumentFragment;
|
|
52
49
|
|
|
53
50
|
listener: RemovableEventListener;
|
|
54
51
|
|
|
55
|
-
readonly pool:
|
|
52
|
+
readonly pool: RenderElementPool = {
|
|
56
53
|
cells: {},
|
|
57
54
|
rows: [],
|
|
58
55
|
};
|
|
59
56
|
|
|
60
|
-
readonly state:
|
|
57
|
+
readonly state: RenderState = {
|
|
61
58
|
active: false,
|
|
62
59
|
top: 0,
|
|
63
60
|
};
|
|
64
61
|
|
|
65
|
-
|
|
62
|
+
visible = new Map<number, Key>();
|
|
66
63
|
|
|
67
64
|
constructor(
|
|
68
65
|
public managers: TabelaManagers,
|
|
@@ -72,26 +69,33 @@ export class VirtualizationManager {
|
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
destroy(): void {
|
|
75
|
-
this
|
|
72
|
+
const {listener, pool, visible} = this;
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
listener();
|
|
75
|
+
visible.clear();
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
pool.cells = {};
|
|
78
|
+
pool.rows = [];
|
|
82
79
|
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
80
|
+
this.listener = undefined as never;
|
|
81
|
+
this.visible = undefined as never;
|
|
85
82
|
}
|
|
86
83
|
|
|
87
84
|
removeCells(fields: string[]): void {
|
|
85
|
+
const {managers, pool, visible} = this;
|
|
88
86
|
const {length} = fields;
|
|
89
87
|
|
|
90
88
|
for (let index = 0; index < length; index += 1) {
|
|
91
|
-
delete
|
|
89
|
+
delete pool.cells[fields[index]];
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
for (const [,
|
|
92
|
+
for (const [, key] of visible) {
|
|
93
|
+
const row = managers.row.get(key);
|
|
94
|
+
|
|
95
|
+
if (row == null || row.element == null) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
95
99
|
for (let index = 0; index < length; index += 1) {
|
|
96
100
|
row.cells[fields[index]].innerHTML = '';
|
|
97
101
|
|
|
@@ -124,15 +128,15 @@ export class VirtualizationManager {
|
|
|
124
128
|
|
|
125
129
|
let remove = rerender ?? false;
|
|
126
130
|
|
|
127
|
-
for (const [index,
|
|
128
|
-
|
|
129
|
-
remove = true;
|
|
130
|
-
}
|
|
131
|
+
for (const [index, key] of visible) {
|
|
132
|
+
const row = managers.row.get(key);
|
|
131
133
|
|
|
132
|
-
if (remove) {
|
|
134
|
+
if (remove || row == null || !indices.has(index)) {
|
|
133
135
|
visible.delete(index);
|
|
134
136
|
|
|
135
|
-
|
|
137
|
+
if (row != null) {
|
|
138
|
+
removeRow(pool, row);
|
|
139
|
+
}
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
|
|
@@ -147,7 +151,8 @@ export class VirtualizationManager {
|
|
|
147
151
|
continue;
|
|
148
152
|
}
|
|
149
153
|
|
|
150
|
-
const
|
|
154
|
+
const key = keys[index];
|
|
155
|
+
const row = managers.row.get(key);
|
|
151
156
|
|
|
152
157
|
if (row == null) {
|
|
153
158
|
continue;
|
|
@@ -157,7 +162,7 @@ export class VirtualizationManager {
|
|
|
157
162
|
|
|
158
163
|
renderRow(managers, row);
|
|
159
164
|
|
|
160
|
-
visible.set(index,
|
|
165
|
+
visible.set(index, key);
|
|
161
166
|
|
|
162
167
|
if (row.element != null) {
|
|
163
168
|
row.element.style.transform = `translateY(${index * managers.row.height}px)`;
|