@oscarpalmer/tabela 0.7.0 → 0.9.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.
Files changed (42) hide show
  1. package/dist/components/body.component.js +1 -1
  2. package/dist/components/column.component.js +29 -9
  3. package/dist/components/header.component.js +1 -1
  4. package/dist/helpers/dom.helpers.js +6 -4
  5. package/dist/managers/column.manager.js +6 -1
  6. package/dist/managers/data.manager.js +15 -10
  7. package/dist/managers/event.manager.js +30 -0
  8. package/dist/managers/filter.manager.js +92 -0
  9. package/dist/managers/row.manager.js +9 -2
  10. package/dist/managers/sort.manager.js +94 -0
  11. package/dist/managers/virtualization.manager.js +4 -2
  12. package/dist/models/filter.model.js +0 -0
  13. package/dist/models/sort.model.js +0 -0
  14. package/dist/tabela.full.js +1441 -108
  15. package/dist/tabela.js +16 -0
  16. package/package.json +1 -1
  17. package/src/components/body.component.ts +1 -0
  18. package/src/components/column.component.ts +58 -19
  19. package/src/components/header.component.ts +1 -1
  20. package/src/helpers/dom.helpers.ts +6 -0
  21. package/src/managers/column.manager.ts +8 -0
  22. package/src/managers/data.manager.ts +25 -15
  23. package/src/managers/event.manager.ts +55 -0
  24. package/src/managers/filter.manager.ts +154 -0
  25. package/src/managers/row.manager.ts +16 -2
  26. package/src/managers/sort.manager.ts +149 -0
  27. package/src/managers/virtualization.manager.ts +6 -3
  28. package/src/models/filter.model.ts +17 -0
  29. package/src/models/sort.model.ts +6 -0
  30. package/src/models/tabela.model.ts +24 -0
  31. package/src/tabela.ts +25 -1
  32. package/types/components/column.component.d.ts +9 -2
  33. package/types/helpers/dom.helpers.d.ts +1 -1
  34. package/types/managers/data.manager.d.ts +10 -2
  35. package/types/managers/event.manager.d.ts +10 -0
  36. package/types/managers/filter.manager.d.ts +19 -0
  37. package/types/managers/sort.manager.d.ts +26 -0
  38. package/types/managers/virtualization.manager.d.ts +2 -2
  39. package/types/models/filter.model.d.ts +6 -0
  40. package/types/models/sort.model.d.ts +5 -0
  41. package/types/models/tabela.model.d.ts +22 -0
  42. package/types/tabela.d.ts +3 -1
@@ -0,0 +1,149 @@
1
+ import {sort, type ArrayKeySorter} from '@oscarpalmer/atoms/array';
2
+ import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
3
+ import {setAttribute, setAttributes} from '@oscarpalmer/toretto/attribute';
4
+ import type {SortDirection, SortItem} from '../models/sort.model';
5
+ import type {TabelaManagers, TabelaSort} from '../models/tabela.model';
6
+
7
+ export class SortManager {
8
+ handlers = Object.freeze({
9
+ add: (field, direction) => this.add(field, direction),
10
+ flip: field => this.flip(field),
11
+ clear: () => this.clear(),
12
+ remove: field => this.remove(field),
13
+ set: items => this.set(items),
14
+ } satisfies TabelaSort);
15
+
16
+ readonly items: ArrayKeySorter<PlainObject>[] = [];
17
+
18
+ constructor(readonly managers: TabelaManagers) {}
19
+
20
+ add(field: string, direction?: SortDirection): void {
21
+ const index = this.items.findIndex(item => item.key === field);
22
+
23
+ if (index > -1) {
24
+ return;
25
+ }
26
+
27
+ this.items.push({
28
+ key: field,
29
+ direction: direction ?? 'ascending',
30
+ });
31
+
32
+ this.sort();
33
+ }
34
+
35
+ addOrSet(event: MouseEvent, field: string): void {
36
+ if (event.ctrlKey || event.metaKey) {
37
+ this.add(field);
38
+ } else {
39
+ this.set([{field, direction: 'ascending'}]);
40
+ }
41
+ }
42
+
43
+ clear(): void {
44
+ if (this.items.length > 0) {
45
+ this.items.length = 0;
46
+
47
+ this.sort();
48
+ }
49
+ }
50
+
51
+ destroy(): void {
52
+ this.handlers = undefined as never;
53
+ this.items.length = 0;
54
+ }
55
+
56
+ flip(field: string): void {
57
+ const item = this.items.find(item => item.key === field);
58
+
59
+ if (item == null) {
60
+ return;
61
+ }
62
+
63
+ item.direction = item.direction === 'ascending' ? 'descending' : 'ascending';
64
+
65
+ this.sort();
66
+ }
67
+
68
+ remove(field: string): void {
69
+ const index = this.items.findIndex(item => item.key === field);
70
+
71
+ if (index > -1) {
72
+ this.items.splice(index, 1);
73
+
74
+ this.sort();
75
+ }
76
+ }
77
+
78
+ removeOrClear(event: MouseEvent, field: string): void {
79
+ if (event.ctrlKey || event.metaKey) {
80
+ this.remove(field);
81
+ } else {
82
+ this.clear();
83
+ }
84
+ }
85
+
86
+ set(items: SortItem[]): void {
87
+ this.items.splice(
88
+ 0,
89
+ this.items.length,
90
+ ...items.map(item => ({key: item.field, direction: item.direction})),
91
+ );
92
+
93
+ this.sort();
94
+ }
95
+
96
+ sort(): void {
97
+ const {items, managers} = this;
98
+
99
+ const {length} = managers.column.items;
100
+
101
+ for (let index = 0; index < length; index += 1) {
102
+ const column = managers.column.items[index];
103
+
104
+ const sorterIndex = items.findIndex(item => item.key === column.options.field);
105
+ const sorterItem = items[sorterIndex];
106
+
107
+ setAttributes(column.elements.wrapper, {
108
+ 'aria-sort':
109
+ sorterItem == null ? 'none' : items.length > 1 ? 'other' : sorterItem.direction,
110
+ 'data-sort-direction': sorterItem == null ? undefined : sorterItem.direction,
111
+ });
112
+
113
+ setAttribute(
114
+ column.elements.sorter,
115
+ 'data-sort-position',
116
+ sorterIndex > -1 && items.length > 1 ? sorterIndex + 1 : undefined,
117
+ );
118
+ }
119
+
120
+ managers.data.values.keys.active =
121
+ items.length === 0
122
+ ? undefined
123
+ : (sort(
124
+ managers.data.values.keys.active?.map(
125
+ key => managers.data.values.objects.mapped.get(key)!,
126
+ ) ??
127
+ managers.data.values.objects.array,
128
+ items,
129
+ ).map(row => row[managers.data.field]) as Key[]);
130
+
131
+ managers.virtualization.update(true, true);
132
+ }
133
+
134
+ toggle(event: MouseEvent, field: string, direction?: string | null): void {
135
+ switch (direction) {
136
+ case 'ascending':
137
+ this.flip(field);
138
+ return;
139
+
140
+ case 'descending':
141
+ this.removeOrClear(event, field);
142
+ return;
143
+
144
+ default:
145
+ this.addOrSet(event, field);
146
+ return;
147
+ }
148
+ }
149
+ }
@@ -62,7 +62,7 @@ export class VirtualizationManager {
62
62
  top: 0,
63
63
  };
64
64
 
65
- readonly visible = new Map<number, RowComponent>();
65
+ visible = new Map<number, RowComponent>();
66
66
 
67
67
  constructor(
68
68
  public managers: TabelaManagers,
@@ -82,6 +82,9 @@ export class VirtualizationManager {
82
82
 
83
83
  this.pool.cells = {};
84
84
  this.pool.rows = [];
85
+
86
+ this.listener = undefined as never;
87
+ this.visible = undefined as never;
85
88
  }
86
89
 
87
90
  removeCells(fields: string[]): void {
@@ -110,7 +113,7 @@ export class VirtualizationManager {
110
113
  return this.fragment;
111
114
  }
112
115
 
113
- update(down: boolean): void {
116
+ update(down: boolean, rerender?: boolean): void {
114
117
  const {components, managers, pool, visible} = this;
115
118
 
116
119
  components.body.elements.faker.style.height = `${managers.data.size * managers.row.height}px`;
@@ -122,7 +125,7 @@ export class VirtualizationManager {
122
125
  indices.add(index);
123
126
  }
124
127
 
125
- let remove = false;
128
+ let remove = rerender ?? false;
126
129
 
127
130
  for (const [index, row] of visible) {
128
131
  if (!managers.row.has(row.key) || !indices.has(index)) {
@@ -0,0 +1,17 @@
1
+ export type FilterComparison =
2
+ | 'contains'
3
+ | 'ends-with'
4
+ | 'equals'
5
+ | 'greater-than'
6
+ | 'greater-than-or-equal'
7
+ | 'less-than'
8
+ | 'less-than-or-equal'
9
+ | 'not-contains'
10
+ | 'not-equals'
11
+ | 'starts-with';
12
+
13
+ export type FilterItem = {
14
+ comparison: FilterComparison;
15
+ field: string;
16
+ value: unknown;
17
+ };
@@ -0,0 +1,6 @@
1
+ export type SortDirection = 'ascending' | 'descending';
2
+
3
+ export type SortItem = {
4
+ direction: SortDirection;
5
+ field: string;
6
+ };
@@ -4,8 +4,13 @@ import type {FooterComponent} from '../components/footer.component';
4
4
  import type {HeaderComponent} from '../components/header.component';
5
5
  import type {ColumnManager} from '../managers/column.manager';
6
6
  import type {DataManager} from '../managers/data.manager';
7
+ import type {EventManager} from '../managers/event.manager';
8
+ import type {FilterManager} from '../managers/filter.manager';
7
9
  import type {RowManager} from '../managers/row.manager';
10
+ import type {SortManager} from '../managers/sort.manager';
8
11
  import type {VirtualizationManager} from '../managers/virtualization.manager';
12
+ import type {FilterItem} from './filter.model';
13
+ import type {SortDirection, SortItem} from './sort.model';
9
14
 
10
15
  export type TabelaComponents = {
11
16
  body: BodyComponent;
@@ -23,9 +28,28 @@ export type TabelaData = {
23
28
  update(data: PlainObject[]): void;
24
29
  };
25
30
 
31
+ export type TabelaFilter = {
32
+ add(item: FilterItem): void;
33
+ clear(): void;
34
+ remove(field: string): void;
35
+ remove(item: FilterItem): void;
36
+ set(items: FilterItem[]): void;
37
+ };
38
+
26
39
  export type TabelaManagers = {
27
40
  column: ColumnManager;
28
41
  data: DataManager;
42
+ event: EventManager;
43
+ filter: FilterManager;
29
44
  row: RowManager;
45
+ sort: SortManager;
30
46
  virtualization: VirtualizationManager;
31
47
  };
48
+
49
+ export type TabelaSort = {
50
+ add(field: string, direction?: SortDirection): void;
51
+ clear(): void;
52
+ flip(field: string): void;
53
+ remove(field: string): void;
54
+ set(items: SortItem[]): void;
55
+ };
package/src/tabela.ts CHANGED
@@ -3,9 +3,18 @@ import {FooterComponent} from './components/footer.component';
3
3
  import {HeaderComponent} from './components/header.component';
4
4
  import {ColumnManager} from './managers/column.manager';
5
5
  import {DataManager} from './managers/data.manager';
6
+ import {EventManager} from './managers/event.manager';
7
+ import {FilterManager} from './managers/filter.manager';
6
8
  import {RowManager} from './managers/row.manager';
9
+ import {SortManager} from './managers/sort.manager';
7
10
  import {VirtualizationManager} from './managers/virtualization.manager';
8
- import type {TabelaComponents, TabelaData, TabelaManagers} from './models/tabela.model';
11
+ import type {
12
+ TabelaComponents,
13
+ TabelaData,
14
+ TabelaFilter,
15
+ TabelaManagers,
16
+ TabelaSort,
17
+ } from './models/tabela.model';
9
18
  import type {TabelaOptions} from './models/tabela.options';
10
19
 
11
20
  export class Tabela {
@@ -22,12 +31,19 @@ export class Tabela {
22
31
  readonly #managers: TabelaManagers = {
23
32
  column: undefined as never,
24
33
  data: undefined as never,
34
+ event: undefined as never,
35
+ filter: undefined as never,
25
36
  row: undefined as never,
37
+ sort: undefined as never,
26
38
  virtualization: undefined as never,
27
39
  };
28
40
 
29
41
  readonly data: TabelaData;
30
42
 
43
+ readonly filter: TabelaFilter;
44
+
45
+ readonly sort: TabelaSort;
46
+
31
47
  get key(): string {
32
48
  return this.#key;
33
49
  }
@@ -50,7 +66,10 @@ export class Tabela {
50
66
 
51
67
  this.#managers.column = new ColumnManager(this.#managers, this.#components, options.columns);
52
68
  this.#managers.data = new DataManager(this.#managers, this.#components, options.key);
69
+ this.#managers.event = new EventManager(this.#managers, this.#element);
70
+ this.#managers.filter = new FilterManager(this.#managers);
53
71
  this.#managers.row = new RowManager(this.#managers, options.rowHeight);
72
+ this.#managers.sort = new SortManager(this.#managers);
54
73
  this.#managers.virtualization = new VirtualizationManager(this.#managers, this.#components);
55
74
 
56
75
  element.append(
@@ -62,6 +81,8 @@ export class Tabela {
62
81
  this.#managers.data.set(options.data);
63
82
 
64
83
  this.data = this.#managers.data.handlers;
84
+ this.filter = this.#managers.filter.handlers;
85
+ this.sort = this.#managers.sort.handlers;
65
86
  }
66
87
 
67
88
  destroy(): void {
@@ -75,7 +96,10 @@ export class Tabela {
75
96
 
76
97
  managers.column.destroy();
77
98
  managers.data.destroy();
99
+ managers.event.destroy();
100
+ managers.filter.destroy();
78
101
  managers.row.destroy();
102
+ managers.sort.destroy();
79
103
  managers.virtualization.destroy();
80
104
 
81
105
  element.innerHTML = '';
@@ -1,6 +1,13 @@
1
1
  import type { TabelaColumn, TabelaColumnOptions } from '../models/column.model';
2
2
  export declare class ColumnComponent {
3
- readonly element: HTMLDivElement;
4
- readonly options: TabelaColumn;
3
+ elements: ColumnElements;
4
+ options: TabelaColumn;
5
5
  constructor(options: TabelaColumnOptions);
6
+ destroy(): void;
6
7
  }
8
+ type ColumnElements = {
9
+ content: HTMLDivElement;
10
+ sorter: HTMLDivElement;
11
+ wrapper: HTMLDivElement;
12
+ };
13
+ export {};
@@ -3,7 +3,7 @@ type RowGroupWithRow = {
3
3
  row: HTMLDivElement;
4
4
  };
5
5
  export declare function createCell(width: number, body?: boolean): HTMLDivElement;
6
- export declare function createElement<TagName extends keyof HTMLElementTagNameMap>(tagName: TagName, properties: Partial<HTMLElementTagNameMap[TagName]>, style: Partial<CSSStyleDeclaration>): HTMLElementTagNameMap[TagName];
6
+ export declare function createElement<TagName extends keyof HTMLElementTagNameMap>(tagName: TagName, properties: Partial<HTMLElementTagNameMap[TagName]>, attributes: Record<string, string>, style: Partial<CSSStyleDeclaration>): HTMLElementTagNameMap[TagName];
7
7
  export declare function createRowGroup(): RowGroupWithRow;
8
8
  export declare function createRowGroup(withRow: boolean): HTMLDivElement;
9
9
  export declare function createRow(withStyle?: boolean): HTMLDivElement;
@@ -1,11 +1,18 @@
1
1
  import type { Key, PlainObject } from '@oscarpalmer/atoms/models';
2
2
  import type { DataValues } from '../models/data.model';
3
- import type { TabelaComponents, TabelaData, TabelaManagers } from '../models/tabela.model';
3
+ import type { TabelaComponents, TabelaManagers } from '../models/tabela.model';
4
4
  export declare class DataManager {
5
5
  managers: TabelaManagers;
6
6
  components: TabelaComponents;
7
7
  field: string;
8
- readonly handlers: TabelaData;
8
+ handlers: Readonly<{
9
+ add: (data: PlainObject[]) => undefined;
10
+ clear: () => undefined;
11
+ get: (active: boolean | undefined) => PlainObject[];
12
+ remove: (items: PlainObject[] | Key[]) => undefined;
13
+ synchronize: (data: PlainObject[], remove: boolean | undefined) => undefined;
14
+ update: (data: PlainObject[]) => undefined;
15
+ }>;
9
16
  readonly values: DataValues;
10
17
  get size(): number;
11
18
  constructor(managers: TabelaManagers, components: TabelaComponents, field: string);
@@ -14,6 +21,7 @@ export declare class DataManager {
14
21
  destroy(): void;
15
22
  get(active?: boolean): PlainObject[];
16
23
  remove(items: Array<Key | PlainObject>, render: boolean): Promise<void>;
24
+ render(): void;
17
25
  set(data: PlainObject[]): void;
18
26
  synchronize(data: PlainObject[], remove?: boolean): Promise<void>;
19
27
  update(data: PlainObject[]): Promise<void>;
@@ -0,0 +1,10 @@
1
+ import type { RemovableEventListener } from '@oscarpalmer/toretto/models';
2
+ import type { TabelaManagers } from '../models/tabela.model';
3
+ export declare class EventManager {
4
+ readonly managers: TabelaManagers;
5
+ listener: RemovableEventListener;
6
+ constructor(managers: TabelaManagers, element: HTMLElement);
7
+ destroy(): void;
8
+ onClick(event: MouseEvent): void;
9
+ onSort(event: MouseEvent, target: HTMLElement): void;
10
+ }
@@ -0,0 +1,19 @@
1
+ import type { FilterItem } from '../models/filter.model';
2
+ import type { TabelaManagers } from '../models/tabela.model';
3
+ export declare class FilterManager {
4
+ readonly managers: TabelaManagers;
5
+ handlers: Readonly<{
6
+ add: (item: FilterItem) => void;
7
+ clear: () => void;
8
+ remove: (value: string | FilterItem) => void;
9
+ set: (items: FilterItem[]) => void;
10
+ }>;
11
+ items: Record<string, FilterItem[]>;
12
+ constructor(managers: TabelaManagers);
13
+ add(item: FilterItem): void;
14
+ clear(): void;
15
+ destroy(): void;
16
+ filter(): void;
17
+ remove(value: string | FilterItem): void;
18
+ set(items: FilterItem[]): void;
19
+ }
@@ -0,0 +1,26 @@
1
+ import { type ArrayKeySorter } from '@oscarpalmer/atoms/array';
2
+ import type { PlainObject } from '@oscarpalmer/atoms/models';
3
+ import type { SortDirection, SortItem } from '../models/sort.model';
4
+ import type { TabelaManagers } from '../models/tabela.model';
5
+ export declare class SortManager {
6
+ readonly managers: TabelaManagers;
7
+ handlers: Readonly<{
8
+ add: (field: string, direction: SortDirection | undefined) => void;
9
+ flip: (field: string) => void;
10
+ clear: () => void;
11
+ remove: (field: string) => void;
12
+ set: (items: SortItem[]) => void;
13
+ }>;
14
+ readonly items: ArrayKeySorter<PlainObject>[];
15
+ constructor(managers: TabelaManagers);
16
+ add(field: string, direction?: SortDirection): void;
17
+ addOrSet(event: MouseEvent, field: string): void;
18
+ clear(): void;
19
+ destroy(): void;
20
+ flip(field: string): void;
21
+ remove(field: string): void;
22
+ removeOrClear(event: MouseEvent, field: string): void;
23
+ set(items: SortItem[]): void;
24
+ sort(): void;
25
+ toggle(event: MouseEvent, field: string, direction?: string | null): void;
26
+ }
@@ -9,10 +9,10 @@ export declare class VirtualizationManager {
9
9
  listener: RemovableEventListener;
10
10
  readonly pool: VirtualizationPool;
11
11
  readonly state: VirtualizationState;
12
- readonly visible: Map<number, RowComponent>;
12
+ visible: Map<number, RowComponent>;
13
13
  constructor(managers: TabelaManagers, components: TabelaComponents);
14
14
  destroy(): void;
15
15
  removeCells(fields: string[]): void;
16
16
  getFragment(): DocumentFragment;
17
- update(down: boolean): void;
17
+ update(down: boolean, rerender?: boolean): void;
18
18
  }
@@ -0,0 +1,6 @@
1
+ export type FilterComparison = 'contains' | 'ends-with' | 'equals' | 'greater-than' | 'greater-than-or-equal' | 'less-than' | 'less-than-or-equal' | 'not-contains' | 'not-equals' | 'starts-with';
2
+ export type FilterItem = {
3
+ comparison: FilterComparison;
4
+ field: string;
5
+ value: unknown;
6
+ };
@@ -0,0 +1,5 @@
1
+ export type SortDirection = 'ascending' | 'descending';
2
+ export type SortItem = {
3
+ direction: SortDirection;
4
+ field: string;
5
+ };
@@ -4,8 +4,13 @@ import type { FooterComponent } from '../components/footer.component';
4
4
  import type { HeaderComponent } from '../components/header.component';
5
5
  import type { ColumnManager } from '../managers/column.manager';
6
6
  import type { DataManager } from '../managers/data.manager';
7
+ import type { EventManager } from '../managers/event.manager';
8
+ import type { FilterManager } from '../managers/filter.manager';
7
9
  import type { RowManager } from '../managers/row.manager';
10
+ import type { SortManager } from '../managers/sort.manager';
8
11
  import type { VirtualizationManager } from '../managers/virtualization.manager';
12
+ import type { FilterItem } from './filter.model';
13
+ import type { SortDirection, SortItem } from './sort.model';
9
14
  export type TabelaComponents = {
10
15
  body: BodyComponent;
11
16
  footer: FooterComponent;
@@ -20,9 +25,26 @@ export type TabelaData = {
20
25
  synchronize(data: PlainObject[], remove?: boolean): void;
21
26
  update(data: PlainObject[]): void;
22
27
  };
28
+ export type TabelaFilter = {
29
+ add(item: FilterItem): void;
30
+ clear(): void;
31
+ remove(field: string): void;
32
+ remove(item: FilterItem): void;
33
+ set(items: FilterItem[]): void;
34
+ };
23
35
  export type TabelaManagers = {
24
36
  column: ColumnManager;
25
37
  data: DataManager;
38
+ event: EventManager;
39
+ filter: FilterManager;
26
40
  row: RowManager;
41
+ sort: SortManager;
27
42
  virtualization: VirtualizationManager;
28
43
  };
44
+ export type TabelaSort = {
45
+ add(field: string, direction?: SortDirection): void;
46
+ clear(): void;
47
+ flip(field: string): void;
48
+ remove(field: string): void;
49
+ set(items: SortItem[]): void;
50
+ };
package/types/tabela.d.ts CHANGED
@@ -1,8 +1,10 @@
1
- import type { TabelaData } from './models/tabela.model';
1
+ import type { TabelaData, TabelaFilter, TabelaSort } from './models/tabela.model';
2
2
  import type { TabelaOptions } from './models/tabela.options';
3
3
  export declare class Tabela {
4
4
  #private;
5
5
  readonly data: TabelaData;
6
+ readonly filter: TabelaFilter;
7
+ readonly sort: TabelaSort;
6
8
  get key(): string;
7
9
  constructor(element: HTMLElement, options: TabelaOptions);
8
10
  destroy(): void;