@oscarpalmer/tabela 0.13.0 → 0.15.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 (108) hide show
  1. package/dist/components/body.component.d.mts +10 -1
  2. package/dist/components/body.component.mjs +8 -6
  3. package/dist/components/column.component.d.mts +15 -1
  4. package/dist/components/column.component.mjs +12 -10
  5. package/dist/components/footer.component.d.mts +12 -1
  6. package/dist/components/footer.component.mjs +6 -6
  7. package/dist/components/group.component.d.mts +19 -1
  8. package/dist/components/group.component.mjs +17 -11
  9. package/dist/components/header.component.d.mts +12 -1
  10. package/dist/components/header.component.mjs +5 -5
  11. package/dist/components/row.component.d.mts +14 -1
  12. package/dist/components/row.component.mjs +21 -17
  13. package/dist/helpers/dom.helpers.d.mts +3 -3
  14. package/dist/helpers/dom.helpers.mjs +12 -11
  15. package/dist/helpers/misc.helpers.d.mts +7 -1
  16. package/dist/helpers/misc.helpers.mjs +12 -1
  17. package/dist/index.d.mts +1 -1
  18. package/dist/managers/column.manager.d.mts +16 -1
  19. package/dist/managers/column.manager.mjs +7 -7
  20. package/dist/managers/data.manager.d.mts +26 -1
  21. package/dist/managers/data.manager.mjs +122 -88
  22. package/dist/managers/event.manager.d.mts +17 -1
  23. package/dist/managers/event.manager.mjs +35 -10
  24. package/dist/managers/filter.manager.d.mts +17 -1
  25. package/dist/managers/filter.manager.mjs +43 -35
  26. package/dist/managers/group.manager.d.mts +26 -1
  27. package/dist/managers/group.manager.mjs +36 -16
  28. package/dist/managers/navigation.manager.d.mts +15 -2
  29. package/dist/managers/navigation.manager.mjs +38 -34
  30. package/dist/managers/render.manager.d.mts +18 -1
  31. package/dist/managers/render.manager.mjs +44 -31
  32. package/dist/managers/row.manager.d.mts +18 -1
  33. package/dist/managers/row.manager.mjs +1 -1
  34. package/dist/managers/selection.manager.d.mts +22 -1
  35. package/dist/managers/selection.manager.mjs +26 -23
  36. package/dist/managers/sort.manager.d.mts +22 -1
  37. package/dist/managers/sort.manager.mjs +71 -49
  38. package/dist/managers/style.manager.d.mts +8 -1
  39. package/dist/managers/style.manager.mjs +57 -25
  40. package/dist/models/body.model.d.mts +6 -1
  41. package/dist/models/column.model.d.mts +13 -2
  42. package/dist/models/data.model.d.mts +28 -2
  43. package/dist/models/dom.model.d.mts +19 -0
  44. package/dist/models/dom.model.mjs +19 -0
  45. package/dist/models/event.model.d.mts +99 -0
  46. package/dist/models/event.model.mjs +53 -0
  47. package/dist/models/filter.model.d.mts +26 -2
  48. package/dist/models/filter.model.mjs +13 -1
  49. package/dist/models/footer.model.d.mts +7 -1
  50. package/dist/models/group.model.d.mts +19 -2
  51. package/dist/models/group.model.mjs +5 -1
  52. package/dist/models/header.model.d.mts +6 -1
  53. package/dist/models/render.model.d.mts +22 -2
  54. package/dist/models/selection.model.d.mts +11 -1
  55. package/dist/models/sort.model.d.mts +19 -2
  56. package/dist/models/sort.model.mjs +5 -1
  57. package/dist/models/style.model.d.mts +27 -21
  58. package/dist/models/style.model.mjs +27 -21
  59. package/dist/models/tabela.model.d.mts +45 -1
  60. package/dist/models/tabela.options.d.mts +13 -1
  61. package/dist/tabela.d.mts +9 -8
  62. package/dist/tabela.full.mjs +1083 -574
  63. package/dist/tabela.mjs +19 -19
  64. package/package.json +2 -4
  65. package/src/components/body.component.ts +10 -7
  66. package/src/components/column.component.ts +20 -16
  67. package/src/components/footer.component.ts +7 -10
  68. package/src/components/group.component.ts +39 -13
  69. package/src/components/header.component.ts +6 -5
  70. package/src/components/row.component.ts +27 -19
  71. package/src/helpers/dom.helpers.ts +18 -22
  72. package/src/helpers/misc.helpers.ts +17 -0
  73. package/src/managers/column.manager.ts +9 -9
  74. package/src/managers/data.manager.ts +196 -107
  75. package/src/managers/event.manager.ts +69 -12
  76. package/src/managers/filter.manager.ts +70 -41
  77. package/src/managers/group.manager.ts +60 -18
  78. package/src/managers/navigation.manager.ts +46 -49
  79. package/src/managers/render.manager.ts +60 -34
  80. package/src/managers/row.manager.ts +1 -1
  81. package/src/managers/selection.manager.ts +37 -35
  82. package/src/managers/sort.manager.ts +114 -72
  83. package/src/managers/style.manager.ts +73 -25
  84. package/src/models/column.model.ts +4 -8
  85. package/src/models/data.model.ts +13 -14
  86. package/src/models/dom.model.ts +31 -0
  87. package/src/models/event.model.ts +175 -0
  88. package/src/models/filter.model.ts +22 -2
  89. package/src/models/group.model.ts +13 -0
  90. package/src/models/render.model.ts +6 -0
  91. package/src/models/sort.model.ts +11 -5
  92. package/src/models/style.model.ts +32 -20
  93. package/src/models/tabela.model.ts +1 -0
  94. package/src/tabela.ts +26 -24
  95. package/dist/body.component-_VDOpJhV.d.mts +0 -10
  96. package/dist/body.model-2iwsovAV.d.mts +0 -7
  97. package/dist/column.component-Bx46r3JI.d.mts +0 -16
  98. package/dist/column.model-D-aw4EU4.d.mts +0 -16
  99. package/dist/filter.model-7ukJrtil.d.mts +0 -16
  100. package/dist/footer.component-Curiab8j.d.mts +0 -12
  101. package/dist/footer.model-DhqoS6ds.d.mts +0 -8
  102. package/dist/group.component-Cq1YYbfJ.d.mts +0 -285
  103. package/dist/group.model-BsKFwHbt.d.mts +0 -10
  104. package/dist/header.component-BjjlpZIg.d.mts +0 -12
  105. package/dist/header.model-DN_KzUCV.d.mts +0 -7
  106. package/dist/selection.model-rwQe9fco.d.mts +0 -12
  107. package/dist/sort.model-CauImaLu.d.mts +0 -15
  108. package/dist/tabela.options-RkZvfptB.d.mts +0 -14
@@ -1,46 +1,67 @@
1
+ import type {Key} from '@oscarpalmer/atoms/models';
1
2
  import {getNumber} from '@oscarpalmer/atoms/number';
2
3
  import {getString} from '@oscarpalmer/atoms/string';
3
4
  import {endsWith, includes, startsWith} from '@oscarpalmer/atoms/string/match';
4
5
  import {equal} from '@oscarpalmer/atoms/value/equal';
5
- import {GroupComponent} from '../components/group.component';
6
- import type {DataItem} from '../models/data.model';
7
- import type {TabelaFilter, TabelaFilterComparison, TabelaFilterItem} from '../models/filter.model';
6
+ import {isGroupKey} from '../helpers/misc.helpers';
7
+ import {
8
+ FILTER_CONTAINS,
9
+ FILTER_ENDS_WITH,
10
+ FILTER_EQUALS,
11
+ FILTER_GREATER_THAN,
12
+ FILTER_GREATER_THAN_OR_EQUAL,
13
+ FILTER_LESS_THAN,
14
+ FILTER_LESS_THAN_OR_EQUAL,
15
+ FILTER_NOT_CONTAINS,
16
+ FILTER_NOT_EQUALS,
17
+ FILTER_STARTS_WITH,
18
+ type TabelaFilter,
19
+ type TabelaFilterItem,
20
+ } from '../models/filter.model';
8
21
  import type {State} from '../models/tabela.model';
9
22
 
10
23
  export class FilterManager {
11
- handlers = Object.freeze({
24
+ handlers: TabelaFilter = {
12
25
  add: item => this.add(item),
13
26
  clear: () => this.clear(),
14
27
  remove: value => this.remove(value),
15
28
  set: items => this.set(items),
16
- } satisfies TabelaFilter);
29
+ };
17
30
 
18
31
  items: Record<string, TabelaFilterItem[]> = {};
19
32
 
20
33
  constructor(public state: State) {}
21
34
 
22
35
  add(item: TabelaFilterItem): void {
23
- if (this.items[item.field] == null) {
24
- this.items[item.field] = [];
36
+ const {items, state} = this;
37
+
38
+ if (items[item.key] == null) {
39
+ items[item.key] = [];
25
40
  } else {
26
- const index = this.items[item.field].findIndex(existing => equal(existing, item));
41
+ const index = items[item.key].findIndex(existing => equal(existing, item));
27
42
 
28
43
  if (index > -1) {
29
44
  return;
30
45
  }
31
46
  }
32
47
 
33
- this.items[item.field].push(item);
48
+ items[item.key].push(item);
49
+
50
+ state.managers.event.emit('filter:add', [item]);
34
51
 
35
52
  this.filter();
36
53
  }
37
54
 
38
55
  clear(): void {
39
- if (Object.keys(this.items).length > 0) {
40
- this.items = {};
41
-
42
- this.filter();
56
+ if (Object.keys(this.items).length === 0) {
57
+ return;
43
58
  }
59
+
60
+ this.items = {};
61
+
62
+ this.state.managers.event.emit('filter:clear');
63
+
64
+ this.filter();
44
65
  }
45
66
 
46
67
  destroy(): void {
@@ -52,15 +73,15 @@ export class FilterManager {
52
73
  filter(): void {
53
74
  const {state} = this;
54
75
 
55
- const filtered: DataItem[] = [];
76
+ const filtered: Key[] = [];
56
77
  const filters = Object.entries(this.items);
57
78
 
58
- const itemsLength = state.managers.data.state.items.original.length;
79
+ const itemsLength = state.managers.data.state.keys.original.length;
59
80
 
60
81
  rowLoop: for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
61
- const item = state.managers.data.state.items.original[itemIndex];
82
+ const item = state.managers.data.state.keys.original[itemIndex];
62
83
 
63
- if (item instanceof GroupComponent) {
84
+ if (isGroupKey(item)) {
64
85
  filtered.push(item);
65
86
 
66
87
  continue;
@@ -73,9 +94,9 @@ export class FilterManager {
73
94
  }
74
95
 
75
96
  filterLoop: for (let filterIndex = 0; filterIndex < filters.length; filterIndex += 1) {
76
- const [field, items] = filters[filterIndex];
97
+ const [key, items] = filters[filterIndex];
77
98
 
78
- const value = row[field];
99
+ const value = row[key];
79
100
 
80
101
  for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
81
102
  const filter = items[itemIndex];
@@ -91,7 +112,7 @@ export class FilterManager {
91
112
  filtered.push(item);
92
113
  }
93
114
 
94
- state.managers.data.state.items.active = filtered;
115
+ state.managers.data.state.keys.active = filtered;
95
116
 
96
117
  if (state.managers.sort.items.length > 0) {
97
118
  state.managers.sort.sort();
@@ -101,6 +122,8 @@ export class FilterManager {
101
122
  }
102
123
 
103
124
  remove(value: string | TabelaFilterItem): void {
125
+ const removed: TabelaFilterItem[] = [];
126
+
104
127
  if (typeof value === 'string') {
105
128
  if (this.items[value] == null) {
106
129
  return;
@@ -108,32 +131,38 @@ export class FilterManager {
108
131
 
109
132
  const keyed: Record<string, TabelaFilterItem[]> = {};
110
133
 
111
- const filters = Object.keys(this.items);
112
- const {length} = filters;
134
+ const keys = Object.keys(this.items);
135
+ const {length} = keys;
113
136
 
114
137
  for (let index = 0; index < length; index += 1) {
115
- const field = filters[index];
138
+ const key = keys[index];
116
139
 
117
- if (field !== value) {
118
- keyed[field] = this.items[field];
140
+ if (key === value) {
141
+ removed.push(...this.items[key]);
142
+ } else {
143
+ keyed[key] = this.items[key];
119
144
  }
120
145
  }
121
146
 
122
147
  this.items = keyed;
123
148
  } else {
124
- const {field} = value;
149
+ const {key} = value;
125
150
 
126
- if (this.items[field] == null) {
151
+ if (this.items[key] == null) {
127
152
  return;
128
153
  }
129
154
 
130
- const index = this.items[field].findIndex(item => equal(item, value));
155
+ const index = this.items[key].findIndex(item => equal(item, value));
131
156
 
132
157
  if (index === -1) {
133
158
  return;
134
159
  }
160
+
161
+ removed.push(this.items[key][index]);
135
162
  }
136
163
 
164
+ this.state.managers.event.emit('filter:remove', removed);
165
+
137
166
  this.filter();
138
167
  }
139
168
 
@@ -145,9 +174,9 @@ export class FilterManager {
145
174
  for (let index = 0; index < length; index += 1) {
146
175
  const item = items[index];
147
176
 
148
- keyed[item.field] ??= [];
177
+ keyed[item.key] ??= [];
149
178
 
150
- keyed[item.field].push(item);
179
+ keyed[item.key].push(item);
151
180
  }
152
181
 
153
182
  this.items = keyed;
@@ -156,17 +185,17 @@ export class FilterManager {
156
185
  }
157
186
  }
158
187
 
159
- const comparators: Record<TabelaFilterComparison, (row: unknown, filter: unknown) => boolean> = {
160
- contains: (row, filter) => includes(getString(row), getString(filter), true),
161
- 'ends-with': (row, filter) => endsWith(getString(row), getString(filter), true),
162
- equals: (row, filter) => equalizer(row, filter),
163
- 'greater-than': (row, filter) => getNumber(row) > getNumber(filter),
164
- 'greater-than-or-equal': (row, filter) => getNumber(row) >= getNumber(filter),
165
- 'less-than': (row, filter) => getNumber(row) < getNumber(filter),
166
- 'less-than-or-equal': (row, filter) => getNumber(row) <= getNumber(filter),
167
- 'not-contains': (row, filter) => !includes(getString(row), getString(filter), true),
168
- 'not-equals': (row, filter) => !equalizer(row, filter),
169
- 'starts-with': (row, filter) => startsWith(getString(row), getString(filter), true),
188
+ const comparators: Record<string, (row: unknown, filter: unknown) => boolean> = {
189
+ [FILTER_CONTAINS]: (row, filter) => includes(getString(row), getString(filter), true),
190
+ [FILTER_ENDS_WITH]: (row, filter) => endsWith(getString(row), getString(filter), true),
191
+ [FILTER_EQUALS]: (row, filter) => equalizer(row, filter),
192
+ [FILTER_GREATER_THAN]: (row, filter) => getNumber(row) > getNumber(filter),
193
+ [FILTER_GREATER_THAN_OR_EQUAL]: (row, filter) => getNumber(row) >= getNumber(filter),
194
+ [FILTER_LESS_THAN]: (row, filter) => getNumber(row) < getNumber(filter),
195
+ [FILTER_LESS_THAN_OR_EQUAL]: (row, filter) => getNumber(row) <= getNumber(filter),
196
+ [FILTER_NOT_CONTAINS]: (row, filter) => !includes(getString(row), getString(filter), true),
197
+ [FILTER_NOT_EQUALS]: (row, filter) => !equalizer(row, filter),
198
+ [FILTER_STARTS_WITH]: (row, filter) => startsWith(getString(row), getString(filter), true),
170
199
  };
171
200
 
172
201
  const equalizer = equal.initialize({
@@ -1,34 +1,45 @@
1
1
  import {sort} from '@oscarpalmer/atoms/array';
2
+ import {toMap} from '@oscarpalmer/atoms/array/to-map';
2
3
  import {toRecord} from '@oscarpalmer/atoms/array/to-record';
3
4
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
4
5
  import type {Key, Simplify} from '@oscarpalmer/atoms/models';
5
6
  import {getString} from '@oscarpalmer/atoms/string';
6
7
  import {removeGroup, type GroupComponent} from '../components/group.component';
7
- import type {TabelaGroup} from '../models/group.model';
8
+ import {
9
+ EVENT_GROUP_ADD,
10
+ EVENT_GROUP_CLEAR,
11
+ EVENT_GROUP_REMOVE,
12
+ EVENT_GROUP_TOGGLE,
13
+ } from '../models/event.model';
14
+ import type {TabelaGroupHandlers, TabelaGroupToggle} from '../models/group.model';
8
15
  import type {State} from '../models/tabela.model';
16
+ import {compare} from '@oscarpalmer/atoms/value/compare';
17
+ import {getGroup} from '../helpers/misc.helpers';
9
18
 
10
19
  export class GroupManager {
11
20
  collapsed = new Set<Key>();
12
21
 
13
22
  enabled = false;
14
23
 
15
- field!: string;
24
+ key!: string;
16
25
 
17
- handlers = Object.freeze({
26
+ handlers: TabelaGroupHandlers = {
18
27
  set: (group?: string) => {
19
- if (group === this.field) {
28
+ if (group === this.key) {
20
29
  return;
21
30
  }
22
31
 
23
32
  this.enabled = !isNullableOrWhitespace(group);
24
- this.field = group ?? '';
33
+ this.key = group ?? '';
25
34
 
26
35
  this.state.managers.data.set(this.state.managers.data.get());
27
36
  },
28
- } satisfies TabelaGroup);
37
+ };
29
38
 
30
39
  items: GroupComponent[] = [];
31
40
 
41
+ mapped = new Map<string, GroupComponent>();
42
+
32
43
  order: Record<never, number> = {};
33
44
 
34
45
  constructor(public state: State) {
@@ -37,20 +48,34 @@ export class GroupManager {
37
48
  }
38
49
 
39
50
  this.enabled = true;
40
- this.field = state.options.grouping;
51
+ this.key = state.options.grouping;
41
52
  }
42
53
 
43
- add(group: GroupComponent): void {
54
+ add(group: GroupComponent, emit: boolean): void {
44
55
  this.set([...this.items, group]);
56
+
57
+ if (emit) {
58
+ this.state.managers.event.emit(EVENT_GROUP_ADD, [getGroup(group)]);
59
+ }
45
60
  }
46
61
 
47
62
  clear(): void {
63
+ if (this.items.length === 0) {
64
+ return;
65
+ }
66
+
48
67
  const groups = this.items.splice(0);
49
68
  const {length} = groups;
50
69
 
51
70
  for (let index = 0; index < length; index += 1) {
52
- this.remove(groups[index]);
71
+ this.remove(groups[index], false);
53
72
  }
73
+
74
+ this.collapsed.clear();
75
+
76
+ this.set([]);
77
+
78
+ this.state.managers.event.emit(EVENT_GROUP_CLEAR);
54
79
  }
55
80
 
56
81
  destroy(): void {
@@ -67,15 +92,19 @@ export class GroupManager {
67
92
  this.state = undefined as never;
68
93
  }
69
94
 
70
- get(value: unknown) {
95
+ getForKey(key: string): GroupComponent | undefined {
96
+ return this.mapped.get(key);
97
+ }
98
+
99
+ getForValue(value: unknown): GroupComponent | undefined {
71
100
  const asString = getString(value);
72
101
 
73
102
  return this.items.find(item => item.value.stringified === asString);
74
103
  }
75
104
 
76
105
  handle(button: HTMLElement): void {
77
- const value = button.dataset.key?.replace(`tabela_${this.state.id}_group:`, '');
78
- const group = this.get(value);
106
+ const key = button.dataset.key?.replace(`${this.state.prefix}_`, '');
107
+ const group = this.getForKey(key ?? '');
79
108
 
80
109
  if (group == null) {
81
110
  return;
@@ -87,15 +116,15 @@ export class GroupManager {
87
116
 
88
117
  const index = items.indexOf(group);
89
118
 
90
- let first = state.managers.data.state.items.original.indexOf(items[index]) + 1;
119
+ let first = state.managers.data.state.keys.original.indexOf(group.key) + 1;
91
120
 
92
121
  const last =
93
122
  items[index + 1] == null
94
- ? state.managers.data.state.items.original.length - 1
95
- : state.managers.data.state.items.original.indexOf(items[index + 1]) - 1;
123
+ ? state.managers.data.state.keys.original.length - 1
124
+ : state.managers.data.state.keys.original.indexOf(items[index + 1].key) - 1;
96
125
 
97
126
  for (; first <= last; first += 1) {
98
- const key = state.managers.data.state.items.original[first] as Key;
127
+ const key = state.managers.data.state.keys.original[first] as Key;
99
128
 
100
129
  if (group.expanded) {
101
130
  collapsed.delete(key);
@@ -104,6 +133,11 @@ export class GroupManager {
104
133
  }
105
134
  }
106
135
 
136
+ state.managers.event.emit(EVENT_GROUP_TOGGLE, {
137
+ collapsed: group.expanded ? [] : [getGroup(group)],
138
+ expanded: group.expanded ? [getGroup(group)] : [],
139
+ });
140
+
107
141
  if (Object.keys(state.managers.filter.items).length > 0) {
108
142
  state.managers.filter.filter();
109
143
  } else if (state.managers.sort.items.length > 0) {
@@ -113,14 +147,22 @@ export class GroupManager {
113
147
  }
114
148
  }
115
149
 
116
- remove(group: GroupComponent): void {
150
+ remove(group: GroupComponent, update: boolean): void {
117
151
  removeGroup(group);
118
152
 
153
+ if (!update) {
154
+ return;
155
+ }
156
+
119
157
  this.set(this.items.filter(item => item !== group));
158
+
159
+ this.state.managers.event.emit(EVENT_GROUP_REMOVE, [getGroup(group)]);
120
160
  }
121
161
 
122
162
  set(items: GroupComponent[]) {
123
- this.items = sort(items, item => item.label);
163
+ this.items = sort(items, (first, second) => compare(first.label, second.label));
164
+
165
+ this.mapped = toMap(items, group => group.key);
124
166
 
125
167
  this.order = toRecord(
126
168
  items as Simplify<GroupComponent>[],
@@ -1,12 +1,12 @@
1
1
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
+ import type {Key} from '@oscarpalmer/atoms/models';
2
3
  import {clamp} from '@oscarpalmer/atoms/number';
3
- import {getKey} from '../helpers/misc.helpers';
4
- import type {DataItem} from '../models/data.model';
4
+ import {getKey, isGroupKey} from '../helpers/misc.helpers';
5
+ import {ARIA_ACTIVEDESCENDANT, ATTRIBUTE_DATA_ACTIVE} from '../models/dom.model';
5
6
  import type {State} from '../models/tabela.model';
6
- import {GroupComponent} from '../components/group.component';
7
7
 
8
8
  export class NavigationManager {
9
- active: DataItem | undefined;
9
+ active: Key | undefined;
10
10
 
11
11
  constructor(public state: State) {}
12
12
 
@@ -21,41 +21,43 @@ export class NavigationManager {
21
21
 
22
22
  event.preventDefault();
23
23
 
24
- const {components, id, managers} = this.state;
24
+ const {components, managers} = this.state;
25
25
 
26
- const activeDescendant = components.body.elements.group.getAttribute('aria-activedescendant');
26
+ const activeDescendant = components.body.elements.group.getAttribute(ARIA_ACTIVEDESCENDANT);
27
27
 
28
- const {items} = managers.data;
29
- const {length} = items;
28
+ const {keys} = managers.data;
29
+ const {length} = keys;
30
30
 
31
31
  let next: number;
32
32
 
33
33
  if (isNullableOrWhitespace(activeDescendant)) {
34
34
  next = getDefaultIndex(event.key, length);
35
35
  } else {
36
- next = getIndex(this.state, event, activeDescendant, id)!;
36
+ next = getIndex(this.state, event, activeDescendant)!;
37
37
  }
38
38
 
39
39
  if (next != null) {
40
- this.setActive(items.at(next));
40
+ this.setActive(keys.at(next));
41
41
  }
42
42
  }
43
43
 
44
- setActive(item: DataItem | undefined, scroll?: boolean): void {
45
- const {components, id, managers, options} = this.state;
44
+ setActive(item: Key | undefined, scroll?: boolean): void {
45
+ const {components, managers, options, prefix} = this.state;
46
46
 
47
47
  this.active = item;
48
48
 
49
- const active = components.body.elements.group.querySelectorAll('[data-active="true"]');
49
+ const active = components.body.elements.group.querySelectorAll(attributeDataActiveTrue);
50
50
 
51
51
  for (const item of active) {
52
- item.setAttribute('data-active', 'false');
52
+ item.setAttribute(ATTRIBUTE_DATA_ACTIVE, 'false');
53
53
  }
54
54
 
55
- const component = item instanceof GroupComponent ? item : managers.row.get(item!, false);
55
+ const component = isGroupKey(item)
56
+ ? managers.group.getForKey(item as string)
57
+ : managers.row.get(item!, false);
56
58
 
57
59
  if (component != null) {
58
- component.element?.setAttribute('data-active', 'true');
60
+ component.element?.setAttribute(ATTRIBUTE_DATA_ACTIVE, 'true');
59
61
 
60
62
  if (scroll ?? true) {
61
63
  if (component.element == null) {
@@ -72,8 +74,8 @@ export class NavigationManager {
72
74
  }
73
75
 
74
76
  components.body.elements.group.setAttribute(
75
- 'aria-activedescendant',
76
- component == null ? '' : `tabela_${id}_${component.key}`,
77
+ ARIA_ACTIVEDESCENDANT,
78
+ component == null ? '' : `${prefix}${component.key}`,
77
79
  );
78
80
  }
79
81
  }
@@ -83,10 +85,10 @@ function getDefaultIndex(key: string, max: number): number {
83
85
  case negativeDefaultKeys.has(key):
84
86
  return -1;
85
87
 
86
- case key === 'PageDown':
88
+ case key === KEY_PAGE_DOWN:
87
89
  return Math.min(9, max - 1);
88
90
 
89
- case key === 'PageUp':
91
+ case key === KEY_PAGE_UP:
90
92
  return max < 10 ? 0 : max - 10;
91
93
 
92
94
  default:
@@ -94,54 +96,49 @@ function getDefaultIndex(key: string, max: number): number {
94
96
  }
95
97
  }
96
98
 
97
- function getIndex(
98
- state: State,
99
- event: KeyboardEvent,
100
- active: string,
101
- id: number,
102
- ): number | undefined {
103
- const key = getKey(active.replace(`tabela_${id}_`, ''));
99
+ function getIndex(state: State, event: KeyboardEvent, active: string): number | undefined {
100
+ const key = getKey(active.replace(state.prefix, ''));
104
101
 
105
102
  if (key == null) {
106
103
  return;
107
104
  }
108
105
 
109
106
  if (absoluteKeys.has(event.key)) {
110
- return event.key === 'Home' ? 0 : state.managers.data.size - 1;
107
+ return event.key === KEY_HOME ? 0 : state.managers.data.size - 1;
111
108
  }
112
109
 
113
110
  const index = state.managers.data.getIndex(key);
114
111
 
115
- const offset = getOffset(event.key);
116
-
117
- return clamp(index + offset, 0, state.managers.data.size - 1, true);
112
+ return clamp(index + (offset[event.key] ?? 0), 0, state.managers.data.size - 1, true);
118
113
  }
119
114
 
120
- function getOffset(key: string): number {
121
- switch (key) {
122
- case 'ArrowDown':
123
- return 1;
115
+ const KEY_ARROW_DOWN = 'ArrowDown';
124
116
 
125
- case 'ArrowUp':
126
- return -1;
117
+ const KEY_ARROW_UP = 'ArrowUp';
127
118
 
128
- case 'PageDown':
129
- return 10;
119
+ const KEY_END = 'End';
130
120
 
131
- case 'PageUp':
132
- return -10;
121
+ const KEY_HOME = 'Home';
133
122
 
134
- default:
135
- return 0;
136
- }
137
- }
123
+ const KEY_PAGE_DOWN = 'PageDown';
124
+
125
+ const KEY_PAGE_UP = 'PageUp';
126
+
127
+ const absoluteKeys = new Set([KEY_END, KEY_HOME]);
128
+
129
+ const arrowKeys = new Set([KEY_ARROW_DOWN, KEY_ARROW_UP]);
138
130
 
139
- const absoluteKeys = new Set(['End', 'Home']);
131
+ export const attributeDataActiveTrue = `[${ATTRIBUTE_DATA_ACTIVE}="true"]`;
140
132
 
141
- const arrowKeys = new Set(['ArrowDown', 'ArrowUp']);
133
+ const negativeDefaultKeys = new Set([KEY_ARROW_UP, KEY_END]);
142
134
 
143
- const negativeDefaultKeys = new Set(['ArrowUp', 'End']);
135
+ const offset: Record<string, number> = {
136
+ [KEY_ARROW_DOWN]: 1,
137
+ [KEY_ARROW_UP]: -1,
138
+ [KEY_PAGE_DOWN]: 10,
139
+ [KEY_PAGE_UP]: -10,
140
+ };
144
141
 
145
- const pageKeys = new Set(['PageDown', 'PageUp']);
142
+ const pageKeys = new Set([KEY_PAGE_DOWN, KEY_PAGE_UP]);
146
143
 
147
144
  const allKeys = new Set([...absoluteKeys, ...arrowKeys, ...pageKeys]);