@oscarpalmer/tabela 0.10.0 → 0.11.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 (40) hide show
  1. package/dist/components/body.component.js +1 -0
  2. package/dist/components/row.component.js +11 -8
  3. package/dist/managers/column.manager.js +9 -8
  4. package/dist/managers/data.manager.js +26 -22
  5. package/dist/managers/event.manager.js +39 -23
  6. package/dist/managers/filter.manager.js +11 -10
  7. package/dist/managers/navigation.manager.js +73 -0
  8. package/dist/managers/render.manager.js +30 -25
  9. package/dist/managers/row.manager.js +7 -7
  10. package/dist/managers/selection.manager.js +19 -21
  11. package/dist/managers/sort.manager.js +9 -8
  12. package/dist/tabela.full.js +535 -410
  13. package/dist/tabela.js +27 -9
  14. package/package.json +1 -1
  15. package/src/components/body.component.ts +2 -0
  16. package/src/components/row.component.ts +14 -9
  17. package/src/managers/column.manager.ts +11 -13
  18. package/src/managers/data.manager.ts +31 -27
  19. package/src/managers/event.manager.ts +65 -42
  20. package/src/managers/filter.manager.ts +12 -11
  21. package/src/managers/navigation.manager.ts +145 -0
  22. package/src/managers/render.manager.ts +34 -28
  23. package/src/managers/row.manager.ts +9 -14
  24. package/src/managers/selection.manager.ts +24 -30
  25. package/src/managers/sort.manager.ts +14 -14
  26. package/src/models/render.model.ts +3 -1
  27. package/src/models/tabela.model.ts +12 -0
  28. package/src/tabela.ts +34 -9
  29. package/types/components/row.component.d.ts +2 -2
  30. package/types/managers/column.manager.d.ts +4 -5
  31. package/types/managers/data.manager.d.ts +5 -6
  32. package/types/managers/event.manager.d.ts +3 -6
  33. package/types/managers/filter.manager.d.ts +3 -3
  34. package/types/managers/navigation.manager.d.ts +10 -0
  35. package/types/managers/render.manager.d.ts +4 -6
  36. package/types/managers/row.manager.d.ts +4 -5
  37. package/types/managers/selection.manager.d.ts +3 -4
  38. package/types/managers/sort.manager.d.ts +4 -4
  39. package/types/models/render.model.d.ts +2 -1
  40. package/types/models/tabela.model.d.ts +11 -0
package/dist/tabela.js CHANGED
@@ -5,10 +5,11 @@ 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
7
  import { FilterManager } from "./managers/filter.manager.js";
8
+ import { NavigationManager } from "./managers/navigation.manager.js";
9
+ import { RenderManager } from "./managers/render.manager.js";
8
10
  import { RowManager } from "./managers/row.manager.js";
9
11
  import { SelectionManager } from "./managers/selection.manager.js";
10
12
  import { SortManager } from "./managers/sort.manager.js";
11
- import { RenderManager } from "./managers/render.manager.js";
12
13
  var Tabela = class {
13
14
  #components = {
14
15
  header: void 0,
@@ -16,12 +17,14 @@ var Tabela = class {
16
17
  footer: void 0
17
18
  };
18
19
  #element;
20
+ #id = getId();
19
21
  #key;
20
22
  #managers = {
21
23
  column: void 0,
22
24
  data: void 0,
23
25
  event: void 0,
24
26
  filter: void 0,
27
+ navigation: void 0,
25
28
  render: void 0,
26
29
  row: void 0,
27
30
  selection: void 0,
@@ -44,14 +47,23 @@ var Tabela = class {
44
47
  this.#components.header = new HeaderComponent();
45
48
  this.#components.body = new BodyComponent();
46
49
  this.#components.footer = new FooterComponent();
47
- this.#managers.column = new ColumnManager(this.#managers, this.#components, options.columns);
48
- this.#managers.data = new DataManager(this.#managers, this.#components, options.key);
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);
52
- this.#managers.row = new RowManager(this.#managers, options.rowHeight);
53
- this.#managers.selection = new SelectionManager(this.#element, this.#managers);
54
- this.#managers.sort = new SortManager(this.#managers);
50
+ const state = {
51
+ element,
52
+ options,
53
+ components: this.#components,
54
+ id: this.#id,
55
+ key: this.#key,
56
+ managers: this.#managers
57
+ };
58
+ this.#managers.column = new ColumnManager(state);
59
+ this.#managers.data = new DataManager(state);
60
+ this.#managers.event = new EventManager(state);
61
+ this.#managers.filter = new FilterManager(state);
62
+ this.#managers.navigation = new NavigationManager(state);
63
+ this.#managers.render = new RenderManager(state);
64
+ this.#managers.row = new RowManager(state);
65
+ this.#managers.selection = new SelectionManager(state);
66
+ this.#managers.sort = new SortManager(state);
55
67
  element.append(this.#components.header.elements.group, this.#components.body.elements.group, this.#components.footer.elements.group);
56
68
  this.#managers.data.set(options.data);
57
69
  this.data = this.#managers.data.handlers;
@@ -72,6 +84,7 @@ var Tabela = class {
72
84
  managers.filter.destroy();
73
85
  managers.render.destroy();
74
86
  managers.row.destroy();
87
+ managers.selection.destroy();
75
88
  managers.sort.destroy();
76
89
  element.innerHTML = "";
77
90
  element.role = "";
@@ -81,4 +94,9 @@ var Tabela = class {
81
94
  this.#element = void 0;
82
95
  }
83
96
  };
97
+ function getId() {
98
+ id += 1;
99
+ return id;
100
+ }
101
+ var id = 0;
84
102
  export { Tabela };
package/package.json CHANGED
@@ -46,5 +46,5 @@
46
46
  },
47
47
  "type": "module",
48
48
  "types": "./types/index.d.ts",
49
- "version": "0.10.0"
49
+ "version": "0.11.0"
50
50
  }
@@ -22,6 +22,8 @@ export class BodyComponent {
22
22
 
23
23
  group.tabIndex = 0;
24
24
 
25
+ group.setAttribute('data-event', 'body');
26
+
25
27
  group.append(this.elements.faker);
26
28
  }
27
29
 
@@ -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 {TabelaManagers} from '../models/tabela.model';
5
+ import type {TabelaState} from '../models/tabela.model';
6
6
 
7
7
  export function removeRow(pool: RenderElementPool, row: RowComponent): void {
8
8
  if (row.element != null) {
@@ -17,19 +17,23 @@ export function removeRow(pool: RenderElementPool, row: RowComponent): void {
17
17
  row.cells = {};
18
18
  }
19
19
 
20
- export function renderRow(managers: TabelaManagers, row: RowComponent): void {
21
- const element = row.element ?? managers.render.pool.rows.shift() ?? createRow();
20
+ export function renderRow(state: TabelaState, row: RowComponent): void {
21
+ const element = row.element ?? state.managers.render.pool.rows.shift() ?? createRow();
22
22
 
23
23
  row.element = element;
24
24
 
25
25
  element.innerHTML = '';
26
26
 
27
- const selected = managers.selection.items.has(row.key);
27
+ const selected = state.managers.selection.items.has(row.key);
28
+
29
+ const key = String(row.key);
28
30
 
29
31
  setAttributes(element, {
30
32
  'aria-selected': String(selected),
33
+ 'data-active': String(state.managers.navigation.active === row.key),
31
34
  'data-event': 'row',
32
- 'data-key': String(row.key),
35
+ 'data-key': key,
36
+ id: `tabela_${state.id}_row_${key}`,
33
37
  });
34
38
 
35
39
  element.classList.add('tabela__row--body');
@@ -40,10 +44,10 @@ export function renderRow(managers: TabelaManagers, row: RowComponent): void {
40
44
  element.classList.remove('tabela__row--selected');
41
45
  }
42
46
 
43
- const columns = managers.column.items;
47
+ const columns = state.managers.column.items;
44
48
  const {length} = columns;
45
49
 
46
- const data = managers.data.values.objects.mapped.get(row.key);
50
+ const data = state.managers.data.values.objects.mapped.get(row.key);
47
51
 
48
52
  if (data == null) {
49
53
  return;
@@ -52,10 +56,11 @@ export function renderRow(managers: TabelaManagers, row: RowComponent): void {
52
56
  for (let index = 0; index < length; index += 1) {
53
57
  const {options} = columns[index];
54
58
 
55
- managers.render.pool.cells[options.field] ??= [];
59
+ state.managers.render.pool.cells[options.field] ??= [];
56
60
 
57
61
  const cell =
58
- managers.render.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
62
+ state.managers.render.pool.cells[columns[index].options.field].shift() ??
63
+ createCell(options.width);
59
64
 
60
65
  cell.textContent = String(data[options.field]);
61
66
 
@@ -1,16 +1,12 @@
1
1
  import {ColumnComponent} from '../components/column.component';
2
2
  import type {TabelaColumnOptions} from '../models/column.model';
3
- import type {TabelaComponents, TabelaManagers} from '../models/tabela.model';
3
+ import type {TabelaState} from '../models/tabela.model';
4
4
 
5
5
  export class ColumnManager {
6
- readonly items: ColumnComponent[] = [];
7
-
8
- constructor(
9
- public managers: TabelaManagers,
10
- public components: TabelaComponents,
11
- columns: TabelaColumnOptions[],
12
- ) {
13
- this.set(columns);
6
+ items: ColumnComponent[] = [];
7
+
8
+ constructor(public state: TabelaState) {
9
+ this.set(state.options.columns);
14
10
  }
15
11
 
16
12
  destroy(): void {
@@ -20,7 +16,8 @@ export class ColumnManager {
20
16
  this.items[index].destroy();
21
17
  }
22
18
 
23
- this.items.length = 0;
19
+ this.items = undefined as never;
20
+ this.state = undefined as never;
24
21
  }
25
22
 
26
23
  remove(field: string): void;
@@ -28,7 +25,8 @@ export class ColumnManager {
28
25
  remove(fields: string[]): void;
29
26
 
30
27
  remove(value: unknown): void {
31
- const {components, items, managers} = this;
28
+ const {items, state} = this;
29
+ const {components, managers} = state;
32
30
 
33
31
  const fields = (Array.isArray(value) ? value : [value]).filter(
34
32
  item => typeof item === 'string',
@@ -59,8 +57,8 @@ export class ColumnManager {
59
57
  }
60
58
 
61
59
  set(columns: TabelaColumnOptions[]): void {
62
- const {components, items} = this;
63
- const {footer, header} = components;
60
+ const {items, state} = this;
61
+ const {footer, header} = state.components;
64
62
 
65
63
  items.splice(0, items.length, ...columns.map(column => new ColumnComponent(column)));
66
64
 
@@ -3,7 +3,7 @@ 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';
5
5
  import type {DataValues} from '../models/data.model';
6
- import type {TabelaComponents, TabelaData, TabelaManagers} from '../models/tabela.model';
6
+ import type {TabelaData, TabelaState} from '../models/tabela.model';
7
7
 
8
8
  export class DataManager {
9
9
  handlers = Object.freeze({
@@ -15,7 +15,7 @@ export class DataManager {
15
15
  update: data => void this.update(data),
16
16
  } satisfies TabelaData);
17
17
 
18
- readonly values: DataValues = {
18
+ values: DataValues = {
19
19
  keys: {
20
20
  original: [],
21
21
  },
@@ -29,18 +29,14 @@ export class DataManager {
29
29
  return this.values.keys.active?.length ?? this.values.keys.original.length;
30
30
  }
31
31
 
32
- constructor(
33
- public managers: TabelaManagers,
34
- public components: TabelaComponents,
35
- public field: string,
36
- ) {}
32
+ constructor(public state: TabelaState) {}
37
33
 
38
34
  async add(data: PlainObject[], render: boolean): Promise<void> {
39
- const {field, values} = this;
35
+ const {state, values} = this;
40
36
 
41
37
  push(values.objects.array, data);
42
38
 
43
- values.objects.mapped = toMap(values.objects.array, field) as Map<Key, PlainObject>;
39
+ values.objects.mapped = toMap(values.objects.array, state.key) as Map<Key, PlainObject>;
44
40
 
45
41
  if (render) {
46
42
  this.render();
@@ -63,6 +59,8 @@ export class DataManager {
63
59
  values.objects.array.length = 0;
64
60
 
65
61
  this.handlers = undefined as never;
62
+ this.state = undefined as never;
63
+ this.values = undefined as never;
66
64
  }
67
65
 
68
66
  get(active?: boolean): PlainObject[] {
@@ -73,11 +71,17 @@ export class DataManager {
73
71
  : values.objects.array;
74
72
  }
75
73
 
74
+ getIndex(key: Key): number {
75
+ const {values} = this;
76
+
77
+ return (values.keys.active ?? values.keys.original).indexOf(key);
78
+ }
79
+
76
80
  async remove(items: Array<Key | PlainObject>, render: boolean): Promise<void> {
77
- const {field, managers, values} = this;
81
+ const {state, values} = this;
78
82
 
79
83
  const keys = items
80
- .map(value => (isPlainObject(value) ? value[field] : value) as Key)
84
+ .map(value => (isPlainObject(value) ? value[state.key] : value) as Key)
81
85
  .filter(key => values.objects.mapped.has(key)) as Key[];
82
86
 
83
87
  const {length} = keys;
@@ -91,7 +95,7 @@ export class DataManager {
91
95
 
92
96
  values.objects.mapped.delete(key);
93
97
 
94
- const arrayIndex = values.objects.array.findIndex(object => object[field] === key);
98
+ const arrayIndex = values.objects.array.findIndex(object => object[state.key] === key);
95
99
 
96
100
  if (arrayIndex > -1) {
97
101
  values.objects.array.splice(arrayIndex, 1);
@@ -99,7 +103,7 @@ export class DataManager {
99
103
 
100
104
  values.keys.original.splice(values.keys.original.indexOf(key), 1);
101
105
 
102
- managers.row.remove(key);
106
+ state.managers.row.remove(key);
103
107
  }
104
108
 
105
109
  if (render) {
@@ -108,30 +112,30 @@ export class DataManager {
108
112
  }
109
113
 
110
114
  render(): void {
111
- const {field, managers, values} = this;
115
+ const {state, values} = this;
112
116
 
113
- values.keys.original = sort(values.objects.array.map(item => item[field] as Key));
117
+ values.keys.original = sort(values.objects.array.map(item => item[state.key] as Key));
114
118
 
115
- if (Object.keys(managers.filter.items).length > 0) {
116
- managers.filter.filter();
117
- } else if (managers.sort.items.length > 0) {
118
- managers.sort.sort();
119
+ if (Object.keys(state.managers.filter.items).length > 0) {
120
+ state.managers.filter.filter();
121
+ } else if (state.managers.sort.items.length > 0) {
122
+ state.managers.sort.sort();
119
123
  } else {
120
- managers.render.update(true);
124
+ state.managers.render.update(true);
121
125
  }
122
126
  }
123
127
 
124
128
  set(data: PlainObject[]): void {
125
- const {field, values} = this;
129
+ const {state, values} = this;
126
130
 
127
- values.objects.mapped = toMap(data, field) as Map<Key, PlainObject>;
131
+ values.objects.mapped = toMap(data, state.key) as Map<Key, PlainObject>;
128
132
  values.objects.array = data;
129
133
 
130
134
  this.render();
131
135
  }
132
136
 
133
137
  async synchronize(data: PlainObject[], remove?: boolean): Promise<void> {
134
- const {field, values} = this;
138
+ const {state, values} = this;
135
139
 
136
140
  const add: PlainObject[] = [];
137
141
  const updated: PlainObject[] = [];
@@ -142,7 +146,7 @@ export class DataManager {
142
146
 
143
147
  for (let index = 0; index < length; index += 1) {
144
148
  const object = data[index];
145
- const key = object[field] as Key;
149
+ const key = object[state.key] as Key;
146
150
 
147
151
  if (values.objects.mapped.has(key)) {
148
152
  updated.push(object);
@@ -177,19 +181,19 @@ export class DataManager {
177
181
  }
178
182
 
179
183
  async update(data: PlainObject[]): Promise<void> {
180
- const {field, managers, values} = this;
184
+ const {state, values} = this;
181
185
 
182
186
  const {length} = data;
183
187
 
184
188
  for (let index = 0; index < length; index += 1) {
185
189
  const object = data[index];
186
- const key = object[field] as Key;
190
+ const key = object[state.key] as Key;
187
191
  const value = values.objects.mapped.get(key);
188
192
 
189
193
  if (value != null) {
190
194
  values.objects.mapped.set(key, {...value, ...object} as PlainObject);
191
195
 
192
- managers.row.update(key);
196
+ state.managers.row.update(key);
193
197
  }
194
198
  }
195
199
  }
@@ -1,62 +1,85 @@
1
1
  import {on} from '@oscarpalmer/toretto/event';
2
- import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
3
- import type {TabelaManagers} from '../models/tabela.model';
4
- import {findAncestor} from '@oscarpalmer/toretto';
2
+ import {findAncestor} from '@oscarpalmer/toretto/find';
3
+ import type {TabelaState} from '../models/tabela.model';
5
4
 
6
5
  export class EventManager {
7
- listener: RemovableEventListener;
8
-
9
- constructor(
10
- element: HTMLElement,
11
- readonly managers: TabelaManagers,
12
- ) {
13
- this.listener = on(
14
- element,
15
- 'click',
16
- event => {
17
- this.onClick(event);
18
- },
19
- {
20
- passive: false,
21
- },
22
- );
6
+ constructor(public state: TabelaState) {
7
+ mapped.set(state.element, this);
23
8
  }
24
9
 
25
10
  destroy(): void {
26
- this.listener();
11
+ mapped.delete(this.state.element);
12
+
13
+ this.state = undefined as never;
27
14
  }
28
15
 
29
- onClick(event: MouseEvent): void {
30
- const target = findAncestor(event, '[data-event]');
16
+ onSort(event: MouseEvent, target: HTMLElement): void {
17
+ const direction = target.getAttribute('data-sort-direction');
18
+ const field = target.getAttribute('data-field');
31
19
 
32
- if (!(target instanceof HTMLElement)) {
33
- return;
20
+ if (field != null) {
21
+ this.state.managers.sort.toggle(event, field, direction);
34
22
  }
23
+ }
24
+ }
35
25
 
36
- const type = target?.getAttribute('data-event');
26
+ function onClick(event: MouseEvent): void {
27
+ const target = findAncestor(event, '[data-event]');
28
+ const table = findAncestor(event, '.tabela');
37
29
 
38
- switch (type) {
39
- case 'heading':
40
- this.onSort(event, target);
41
- break;
30
+ if (!(target instanceof HTMLElement) || !(table instanceof HTMLElement)) {
31
+ return;
32
+ }
42
33
 
43
- case 'row':
44
- this.managers.selection.handle(event, target);
45
- break;
34
+ const manager = mapped.get(table);
46
35
 
47
- default:
48
- break;
49
- }
36
+ if (manager == null) {
37
+ return;
50
38
  }
51
39
 
52
- onSort(event: MouseEvent, target: HTMLElement): void {
53
- const {managers} = this;
40
+ const type = target?.getAttribute('data-event');
54
41
 
55
- const direction = target.getAttribute('data-sort-direction');
56
- const field = target.getAttribute('data-field');
42
+ switch (type) {
43
+ case 'heading':
44
+ manager.onSort(event, target);
45
+ break;
57
46
 
58
- if (field != null) {
59
- managers.sort.toggle(event, field, direction);
60
- }
47
+ case 'row':
48
+ manager.state.managers.selection.handle(event, target);
49
+ break;
50
+
51
+ default:
52
+ break;
53
+ }
54
+ }
55
+
56
+ function onKeydown(event: KeyboardEvent): void {
57
+ const target = findAncestor(event, '[data-event]');
58
+ const table = findAncestor(event, '.tabela');
59
+
60
+ if (!(target instanceof HTMLElement) || !(table instanceof HTMLElement)) {
61
+ return;
62
+ }
63
+
64
+ const manager = mapped.get(table);
65
+
66
+ if (manager == null) {
67
+ return;
68
+ }
69
+
70
+ const type = target?.getAttribute('data-event');
71
+
72
+ switch (type) {
73
+ case 'body':
74
+ manager.state.managers.navigation.handle(event);
75
+ break;
76
+
77
+ default:
78
+ break;
61
79
  }
62
80
  }
81
+
82
+ const mapped = new WeakMap<HTMLElement, EventManager>();
83
+
84
+ on(document, 'click', onClick);
85
+ on(document, 'keydown', onKeydown, {passive: false});
@@ -4,7 +4,7 @@ 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
6
  import type {FilterComparison, FilterItem} from '../models/filter.model';
7
- import type {TabelaFilter, TabelaManagers} from '../models/tabela.model';
7
+ import type {TabelaFilter, TabelaState} from '../models/tabela.model';
8
8
 
9
9
  export class FilterManager {
10
10
  handlers = Object.freeze({
@@ -16,7 +16,7 @@ export class FilterManager {
16
16
 
17
17
  items: Record<string, FilterItem[]> = {};
18
18
 
19
- constructor(readonly managers: TabelaManagers) {}
19
+ constructor(public state: TabelaState) {}
20
20
 
21
21
  add(item: FilterItem): void {
22
22
  if (this.items[item.field] == null) {
@@ -44,20 +44,21 @@ export class FilterManager {
44
44
 
45
45
  destroy(): void {
46
46
  this.handlers = undefined as never;
47
- this.items = {};
47
+ this.items = undefined as never;
48
+ this.state = undefined as never;
48
49
  }
49
50
 
50
51
  filter(): void {
51
- const {managers} = this;
52
+ const {state} = this;
52
53
 
53
54
  const filtered: Key[] = [];
54
55
  const filters = Object.entries(this.items);
55
56
 
56
- const keysLength = managers.data.values.keys.original.length;
57
+ const keysLength = state.managers.data.values.keys.original.length;
57
58
 
58
59
  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);
60
+ const key = state.managers.data.values.keys.original[keyIndex];
61
+ const row = state.managers.data.values.objects.mapped.get(key);
61
62
 
62
63
  if (row == null) {
63
64
  continue;
@@ -82,12 +83,12 @@ export class FilterManager {
82
83
  filtered.push(key);
83
84
  }
84
85
 
85
- managers.data.values.keys.active = filtered;
86
+ state.managers.data.values.keys.active = filtered;
86
87
 
87
- if (managers.sort.items.length > 0) {
88
- managers.sort.sort();
88
+ if (state.managers.sort.items.length > 0) {
89
+ state.managers.sort.sort();
89
90
  } else {
90
- managers.render.update(true, true);
91
+ state.managers.render.update(true, true);
91
92
  }
92
93
  }
93
94