@oscarpalmer/tabela 0.13.0 → 0.14.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 (131) hide show
  1. package/dist/components/{body.component.mjs → body.component.js} +3 -6
  2. package/dist/components/{column.component.mjs → column.component.js} +5 -8
  3. package/dist/components/{footer.component.mjs → footer.component.js} +4 -7
  4. package/dist/components/group.component.js +28 -0
  5. package/dist/components/{header.component.mjs → header.component.js} +3 -6
  6. package/dist/components/{row.component.mjs → row.component.js} +7 -11
  7. package/dist/helpers/{dom.helpers.mjs → dom.helpers.js} +9 -13
  8. package/dist/helpers/{misc.helpers.mjs → misc.helpers.js} +1 -3
  9. package/dist/helpers/style.helper.js +6 -0
  10. package/dist/{index.mjs → index.js} +1 -3
  11. package/dist/managers/{column.manager.mjs → column.manager.js} +1 -6
  12. package/dist/managers/data.manager.js +181 -0
  13. package/dist/managers/{event.manager.mjs → event.manager.js} +8 -9
  14. package/dist/managers/{filter.manager.mjs → filter.manager.js} +14 -23
  15. package/dist/managers/group.manager.js +46 -0
  16. package/dist/managers/{navigation.manager.mjs → navigation.manager.js} +24 -27
  17. package/dist/managers/{render.manager.mjs → render.manager.js} +26 -35
  18. package/dist/managers/{row.manager.mjs → row.manager.js} +11 -18
  19. package/dist/managers/{selection.manager.mjs → selection.manager.js} +28 -32
  20. package/dist/managers/{sort.manager.mjs → sort.manager.js} +9 -12
  21. package/dist/models/body.model.js +0 -0
  22. package/dist/models/column.model.js +0 -0
  23. package/dist/models/data.model.js +0 -0
  24. package/dist/models/filter.model.js +0 -0
  25. package/dist/models/footer.model.js +0 -0
  26. package/dist/models/group.model.js +0 -0
  27. package/dist/models/header.model.js +0 -0
  28. package/dist/models/render.model.js +0 -0
  29. package/dist/models/selection.model.js +0 -0
  30. package/dist/models/sort.model.js +0 -0
  31. package/dist/models/tabela.model.js +0 -0
  32. package/dist/models/tabela.options.js +0 -0
  33. package/dist/{tabela.full.mjs → tabela.full.js} +860 -1049
  34. package/dist/tabela.js +105 -0
  35. package/package.json +1 -1
  36. package/src/components/body.component.ts +10 -7
  37. package/src/components/column.component.ts +19 -15
  38. package/src/components/footer.component.ts +7 -10
  39. package/src/components/group.component.ts +34 -12
  40. package/src/components/header.component.ts +6 -5
  41. package/src/components/row.component.ts +27 -19
  42. package/src/helpers/dom.helpers.ts +18 -22
  43. package/src/helpers/misc.helpers.ts +5 -0
  44. package/src/managers/data.manager.ts +80 -77
  45. package/src/managers/event.manager.ts +21 -10
  46. package/src/managers/filter.manager.ts +34 -21
  47. package/src/managers/group.manager.ts +18 -9
  48. package/src/managers/navigation.manager.ts +46 -49
  49. package/src/managers/render.manager.ts +34 -21
  50. package/src/managers/row.manager.ts +1 -1
  51. package/src/managers/selection.manager.ts +37 -35
  52. package/src/managers/sort.manager.ts +47 -34
  53. package/src/managers/style.manager.ts +40 -25
  54. package/src/models/column.model.ts +2 -6
  55. package/src/models/data.model.ts +7 -8
  56. package/src/models/dom.model.ts +33 -0
  57. package/src/models/event.model.ts +7 -0
  58. package/src/models/filter.model.ts +20 -0
  59. package/src/models/group.model.ts +4 -0
  60. package/src/models/sort.model.ts +4 -0
  61. package/src/models/style.model.ts +32 -20
  62. package/src/models/tabela.model.ts +1 -0
  63. package/src/tabela.ts +20 -22
  64. package/dist/body.component-_VDOpJhV.d.mts +0 -10
  65. package/dist/body.model-2iwsovAV.d.mts +0 -7
  66. package/dist/column.component-Bx46r3JI.d.mts +0 -16
  67. package/dist/column.model-D-aw4EU4.d.mts +0 -16
  68. package/dist/components/body.component.d.mts +0 -2
  69. package/dist/components/column.component.d.mts +0 -2
  70. package/dist/components/footer.component.d.mts +0 -2
  71. package/dist/components/group.component.d.mts +0 -2
  72. package/dist/components/group.component.mjs +0 -51
  73. package/dist/components/header.component.d.mts +0 -2
  74. package/dist/components/row.component.d.mts +0 -2
  75. package/dist/filter.model-7ukJrtil.d.mts +0 -16
  76. package/dist/footer.component-Curiab8j.d.mts +0 -12
  77. package/dist/footer.model-DhqoS6ds.d.mts +0 -8
  78. package/dist/group.component-Cq1YYbfJ.d.mts +0 -285
  79. package/dist/group.model-BsKFwHbt.d.mts +0 -10
  80. package/dist/header.component-BjjlpZIg.d.mts +0 -12
  81. package/dist/header.model-DN_KzUCV.d.mts +0 -7
  82. package/dist/helpers/dom.helpers.d.mts +0 -12
  83. package/dist/helpers/misc.helpers.d.mts +0 -6
  84. package/dist/helpers/style.helper.d.mts +0 -6
  85. package/dist/helpers/style.helper.mjs +0 -8
  86. package/dist/index.d.mts +0 -7
  87. package/dist/managers/column.manager.d.mts +0 -2
  88. package/dist/managers/data.manager.d.mts +0 -2
  89. package/dist/managers/data.manager.mjs +0 -222
  90. package/dist/managers/event.manager.d.mts +0 -2
  91. package/dist/managers/filter.manager.d.mts +0 -2
  92. package/dist/managers/group.manager.d.mts +0 -2
  93. package/dist/managers/group.manager.mjs +0 -73
  94. package/dist/managers/navigation.manager.d.mts +0 -2
  95. package/dist/managers/render.manager.d.mts +0 -2
  96. package/dist/managers/row.manager.d.mts +0 -2
  97. package/dist/managers/selection.manager.d.mts +0 -2
  98. package/dist/managers/sort.manager.d.mts +0 -2
  99. package/dist/managers/style.manager.d.mts +0 -2
  100. package/dist/managers/style.manager.mjs +0 -149
  101. package/dist/models/body.model.d.mts +0 -2
  102. package/dist/models/body.model.mjs +0 -1
  103. package/dist/models/column.model.d.mts +0 -2
  104. package/dist/models/column.model.mjs +0 -1
  105. package/dist/models/data.model.d.mts +0 -2
  106. package/dist/models/data.model.mjs +0 -1
  107. package/dist/models/filter.model.d.mts +0 -2
  108. package/dist/models/filter.model.mjs +0 -1
  109. package/dist/models/footer.model.d.mts +0 -2
  110. package/dist/models/footer.model.mjs +0 -1
  111. package/dist/models/group.model.d.mts +0 -2
  112. package/dist/models/group.model.mjs +0 -1
  113. package/dist/models/header.model.d.mts +0 -2
  114. package/dist/models/header.model.mjs +0 -1
  115. package/dist/models/render.model.d.mts +0 -2
  116. package/dist/models/render.model.mjs +0 -1
  117. package/dist/models/selection.model.d.mts +0 -2
  118. package/dist/models/selection.model.mjs +0 -1
  119. package/dist/models/sort.model.d.mts +0 -2
  120. package/dist/models/sort.model.mjs +0 -1
  121. package/dist/models/style.model.d.mts +0 -23
  122. package/dist/models/style.model.mjs +0 -23
  123. package/dist/models/tabela.model.d.mts +0 -2
  124. package/dist/models/tabela.model.mjs +0 -1
  125. package/dist/models/tabela.options.d.mts +0 -2
  126. package/dist/models/tabela.options.mjs +0 -1
  127. package/dist/selection.model-rwQe9fco.d.mts +0 -12
  128. package/dist/sort.model-CauImaLu.d.mts +0 -15
  129. package/dist/tabela.d.mts +0 -21
  130. package/dist/tabela.mjs +0 -126
  131. package/dist/tabela.options-RkZvfptB.d.mts +0 -14
@@ -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]);
@@ -1,8 +1,9 @@
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 {GroupComponent, renderGroup} from '../components/group.component';
4
+ import {renderGroup} from '../components/group.component';
4
5
  import {removeRow, renderRow} from '../components/row.component';
5
- import type {DataItem} from '../models/data.model';
6
+ import {isGroupKey} from '../helpers/misc.helpers';
6
7
  import type {RenderElementPool, RenderRange, RenderState} from '../models/render.model';
7
8
  import type {State} from '../models/tabela.model';
8
9
 
@@ -10,10 +11,10 @@ function getRange(state: State, down: boolean): RenderRange {
10
11
  const {element, managers, options} = state;
11
12
  const {clientHeight, scrollTop} = element;
12
13
 
13
- const {items} = managers.data;
14
+ const {keys} = managers.data;
14
15
 
15
16
  const firstIndex = Math.floor(scrollTop / options.rowHeight);
16
- const lastIndex = items.length - managers.group.collapsed.size - 1;
17
+ const lastIndex = keys.length - managers.group.collapsed.size - 1;
17
18
 
18
19
  const last = Math.min(lastIndex, Math.ceil((scrollTop + clientHeight) / options.rowHeight) - 1);
19
20
 
@@ -57,7 +58,7 @@ export class RenderManager {
57
58
 
58
59
  state: RenderState;
59
60
 
60
- visible = new Map<number, DataItem>();
61
+ visible = new Map<number, Key>();
61
62
 
62
63
  constructor(state: State) {
63
64
  this.listener = on(state.element, 'scroll', onScroll.bind(this));
@@ -108,7 +109,7 @@ export class RenderManager {
108
109
  }
109
110
 
110
111
  for (const [, key] of visible) {
111
- if (key instanceof GroupComponent) {
112
+ if (isGroupKey(key)) {
112
113
  continue;
113
114
  }
114
115
 
@@ -153,11 +154,11 @@ export class RenderManager {
153
154
  let remove = rerender ?? false;
154
155
 
155
156
  for (const [index, key] of visible) {
156
- if (key instanceof GroupComponent) {
157
+ if (isGroupKey(key)) {
157
158
  if (remove || !indices.has(index)) {
158
159
  visible.delete(index);
159
160
 
160
- key.element?.remove();
161
+ state.managers.group.getForKey(key as string)?.element?.remove();
161
162
  }
162
163
 
163
164
  continue;
@@ -176,7 +177,7 @@ export class RenderManager {
176
177
 
177
178
  const fragment = this.getFragment();
178
179
 
179
- const {items} = managers.data;
180
+ const {keys} = managers.data;
180
181
 
181
182
  let count = 0;
182
183
  let offset = 0;
@@ -186,31 +187,37 @@ export class RenderManager {
186
187
  continue;
187
188
  }
188
189
 
189
- const item = items[index];
190
+ const key = keys[index];
191
+
192
+ if (isGroupKey(key)) {
193
+ const group = managers.group.getForKey(key as string);
194
+
195
+ if (group == null) {
196
+ continue;
197
+ }
190
198
 
191
- if (item instanceof GroupComponent) {
192
199
  count += 1;
193
200
 
194
- renderGroup(state, item);
201
+ renderGroup(state, group);
195
202
 
196
- visible.set(index, item);
203
+ visible.set(index, group.key);
197
204
 
198
- if (item.element != null) {
199
- item.element.style.transform = `translateY(${(index - offset) * options.rowHeight}px)`;
205
+ if (group.element != null) {
206
+ group.element.style.transform = `translateY(${(index - offset) * options.rowHeight}px)`;
200
207
 
201
- fragment.append(item.element);
208
+ fragment.append(group.element);
202
209
  }
203
210
 
204
211
  continue;
205
212
  }
206
213
 
207
- const row = managers.row.get(item, true);
214
+ const row = managers.row.get(key, true);
208
215
 
209
216
  if (row == null) {
210
217
  continue;
211
218
  }
212
219
 
213
- if (managers.group.collapsed.has(item)) {
220
+ if (managers.group.collapsed.has(key)) {
214
221
  offset += 1;
215
222
 
216
223
  continue;
@@ -220,7 +227,7 @@ export class RenderManager {
220
227
 
221
228
  renderRow(state, row);
222
229
 
223
- visible.set(index, item);
230
+ visible.set(index, key);
224
231
 
225
232
  if (row.element != null) {
226
233
  row.element.style.transform = `translateY(${(index - offset) * options.rowHeight}px)`;
@@ -229,8 +236,14 @@ export class RenderManager {
229
236
  }
230
237
  }
231
238
 
232
- if (count > 0) {
233
- components.body.elements.group[down ? 'append' : 'prepend'](fragment);
239
+ if (count === 0) {
240
+ return;
241
+ }
242
+
243
+ if (down) {
244
+ components.body.elements.group.append(fragment);
245
+ } else {
246
+ components.body.elements.group.prepend(fragment);
234
247
  }
235
248
  }
236
249
  }
@@ -62,7 +62,7 @@ export class RowManager {
62
62
  update(key: Key): void {
63
63
  const row = this.components.get(key);
64
64
 
65
- if (row != null) {
65
+ if (row?.element != null) {
66
66
  renderRow(this.state, row);
67
67
  }
68
68
  }
@@ -4,26 +4,22 @@ import {setAttribute} from '@oscarpalmer/toretto/attribute';
4
4
  import {getPosition, on} from '@oscarpalmer/toretto/event';
5
5
  import {findAncestor} from '@oscarpalmer/toretto/find';
6
6
  import type {EventPosition} from '@oscarpalmer/toretto/models';
7
- import {GroupComponent} from '../components/group.component';
8
7
  import {createElement} from '../helpers/dom.helpers';
9
- import {getKey} from '../helpers/misc.helpers';
8
+ import {getKey, isGroupKey} from '../helpers/misc.helpers';
10
9
  import {preventSelection} from '../helpers/style.helper';
10
+ import {ARIA_SELECTED, ATTRIBUTE_DATA_KEY, ELEMENT_DIV} from '../models/dom.model';
11
11
  import type {TabelaSelection} from '../models/selection.model';
12
- import {
13
- CSS_TABELA_ROW_BODY,
14
- CSS_TABELA_ROW_SELECTED,
15
- CSS_TABELA_SELECTION,
16
- } from '../models/style.model';
12
+ import {CSS_ROW_BODY, CSS_ROW_SELECTED, CSS_SELECTION, CSS_TABLE} from '../models/style.model';
17
13
  import type {State} from '../models/tabela.model';
18
14
 
19
15
  export class SelectionManager {
20
- handlers = Object.freeze({
16
+ handlers: TabelaSelection = {
21
17
  add: keys => this.add(keys),
22
18
  clear: () => this.clear(),
23
19
  remove: keys => this.remove(keys),
24
20
  set: keys => this.set(keys),
25
21
  toggle: () => this.toggle(),
26
- } satisfies TabelaSelection);
22
+ };
27
23
 
28
24
  items = new Set<Key>();
29
25
 
@@ -75,7 +71,7 @@ export class SelectionManager {
75
71
  }
76
72
 
77
73
  handle(event: MouseEvent, target: HTMLElement): void {
78
- const key = getKey(target.getAttribute('data-key'));
74
+ const key = getKey(target.getAttribute(ATTRIBUTE_DATA_KEY));
79
75
 
80
76
  if (key == null) {
81
77
  return;
@@ -115,14 +111,19 @@ export class SelectionManager {
115
111
 
116
112
  const keyed = isKey(from) && isKey(to);
117
113
 
118
- const fromKey = keyed ? (from as Key) : getKey((from as HTMLElement).getAttribute('data-key'))!;
119
- const toKey = keyed ? (to as Key) : getKey((to as HTMLElement).getAttribute('data-key'))!;
114
+ const fromKey = keyed
115
+ ? (from as Key)
116
+ : getKey((from as HTMLElement).getAttribute(ATTRIBUTE_DATA_KEY))!;
117
+
118
+ const toKey = keyed
119
+ ? (to as Key)
120
+ : getKey((to as HTMLElement).getAttribute(ATTRIBUTE_DATA_KEY))!;
120
121
 
121
122
  if (fromKey === toKey) {
122
123
  return;
123
124
  }
124
125
 
125
- const {items} = state.managers.data;
126
+ const {keys} = state.managers.data;
126
127
 
127
128
  const fromIndex = state.managers.data.getIndex(fromKey);
128
129
  const toIndex = state.managers.data.getIndex(toKey);
@@ -136,10 +137,10 @@ export class SelectionManager {
136
137
  const selected: Key[] = [];
137
138
 
138
139
  for (let index = start; index <= end; index += 1) {
139
- const item = items[index];
140
+ const key = keys[index];
140
141
 
141
- if (!(item instanceof GroupComponent)) {
142
- selected.push(item as Key);
142
+ if (!isGroupKey(key)) {
143
+ selected.push(key);
143
144
  }
144
145
  }
145
146
 
@@ -189,12 +190,12 @@ export class SelectionManager {
189
190
 
190
191
  toggle(): void {
191
192
  const {items, state} = this;
192
- const data = state.managers.data.items;
193
+ const {keys} = state.managers.data;
193
194
 
194
- if (items.size === data.length - state.managers.group.items.length) {
195
+ if (items.size === keys.length - state.managers.group.items.length) {
195
196
  this.clear();
196
197
  } else {
197
- this.set(data.filter(key => !(key instanceof GroupComponent)) as Key[]);
198
+ this.set(keys.filter(key => !isGroupKey(key)));
198
199
  }
199
200
  }
200
201
 
@@ -217,33 +218,28 @@ export class SelectionManager {
217
218
  continue;
218
219
  }
219
220
 
220
- setAttribute(element, 'aria-selected', String(!removed));
221
+ setAttribute(element, ARIA_SELECTED, String(!removed));
221
222
 
222
223
  if (removed) {
223
- element.classList.remove(CSS_TABELA_ROW_SELECTED);
224
+ element.classList.remove(CSS_ROW_SELECTED);
224
225
  } else {
225
- element.classList.add(CSS_TABELA_ROW_SELECTED);
226
+ element.classList.add(CSS_ROW_SELECTED);
226
227
  }
227
228
  }
228
229
  }
229
230
  }
230
231
 
231
232
  function getPlaceholder(): HTMLElement {
232
- placeholder ??= createElement(
233
- 'div',
234
- {
235
- className: CSS_TABELA_SELECTION,
236
- },
237
- {},
238
- {},
239
- );
233
+ placeholder ??= createElement(ELEMENT_DIV, {
234
+ className: CSS_SELECTION,
235
+ });
240
236
 
241
237
  return placeholder;
242
238
  }
243
239
 
244
240
  function onMouseDown(event: MouseEvent): void {
245
241
  if (shifted) {
246
- const row = findAncestor(event.target as HTMLElement, `.${CSS_TABELA_ROW_BODY}`);
242
+ const row = findAncestor(event.target as HTMLElement, `.${CSS_ROW_BODY}`);
247
243
 
248
244
  if (!(row instanceof HTMLElement)) {
249
245
  return;
@@ -298,13 +294,13 @@ function onMouseUp(event: MouseEvent): void {
298
294
 
299
295
  getPlaceholder().remove();
300
296
 
301
- const row = findAncestor(event.target as HTMLElement, '.tabela__row--body');
297
+ const row = findAncestor(event.target as HTMLElement, bodyRowSelector);
302
298
 
303
299
  if (row instanceof HTMLElement) {
304
300
  endElement = row;
305
301
 
306
- const endTable = findAncestor(endElement, '.tabela__table');
307
- const startTable = findAncestor(startElement, '.tabela__table');
302
+ const endTable = findAncestor(endElement, tableSelector);
303
+ const startTable = findAncestor(startElement, tableSelector);
308
304
 
309
305
  if (startTable != null && startTable === endTable) {
310
306
  mapped.get(startTable)?.range(startElement, endElement);
@@ -317,7 +313,7 @@ function onMouseUp(event: MouseEvent): void {
317
313
  }
318
314
 
319
315
  function onShift(event: KeyboardEvent, value: boolean): void {
320
- if (event.key === 'Shift') {
316
+ if (event.key === KEY_SHIFT) {
321
317
  shifted = value;
322
318
  }
323
319
  }
@@ -330,8 +326,14 @@ function onShiftUp(event: KeyboardEvent): void {
330
326
  onShift(event, false);
331
327
  }
332
328
 
329
+ const KEY_SHIFT = 'Shift';
330
+
333
331
  const mapped = new WeakMap<Element, SelectionManager>();
334
332
 
333
+ const bodyRowSelector = `.${CSS_ROW_BODY}`;
334
+
335
+ const tableSelector = `.${CSS_TABLE}`;
336
+
335
337
  let shifted = false;
336
338
 
337
339
  let endElement: HTMLElement | undefined;
@@ -3,19 +3,30 @@ import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
3
3
  import {compare} from '@oscarpalmer/atoms/value/compare';
4
4
  import {getValue} from '@oscarpalmer/atoms/value/handle';
5
5
  import {setAttribute, setAttributes} from '@oscarpalmer/toretto/attribute';
6
- import {GroupComponent} from '../components/group.component';
7
- import type {DataItem} from '../models/data.model';
8
- import type {TabelaSort, TabelaSortDirection, TabelaSortItem} from '../models/sort.model';
6
+ import {
7
+ ARIA_SORT,
8
+ ATTRIBUTE_DATA_SORT_DIRECTION,
9
+ ATTRIBUTE_DATA_SORT_POSITION,
10
+ } from '../models/dom.model';
11
+ import {
12
+ SORT_ASCENDING,
13
+ SORT_DESCENDING,
14
+ type TabelaSort,
15
+ type TabelaSortDirection,
16
+ type TabelaSortItem,
17
+ } from '../models/sort.model';
9
18
  import type {State} from '../models/tabela.model';
19
+ import type {DataValue} from '../models/data.model';
20
+ import {isGroupKey} from '../helpers/misc.helpers';
10
21
 
11
22
  export class SortManager {
12
- handlers = Object.freeze({
23
+ handlers: TabelaSort = {
13
24
  add: (field, direction) => this.add(field, direction),
14
25
  flip: field => this.flip(field),
15
26
  clear: () => this.clear(),
16
27
  remove: field => this.remove(field),
17
28
  set: items => this.set(items),
18
- } satisfies TabelaSort);
29
+ };
19
30
 
20
31
  items: PlainObject[] = [];
21
32
 
@@ -30,7 +41,7 @@ export class SortManager {
30
41
 
31
42
  this.items.push({
32
43
  key: field,
33
- direction: direction ?? 'ascending',
44
+ direction: direction ?? SORT_ASCENDING,
34
45
  });
35
46
 
36
47
  this.sort();
@@ -40,7 +51,7 @@ export class SortManager {
40
51
  if (event.ctrlKey || event.metaKey) {
41
52
  this.add(field);
42
53
  } else {
43
- this.set([{field, direction: 'ascending'}]);
54
+ this.set([{field, direction: SORT_ASCENDING}]);
44
55
  }
45
56
  }
46
57
 
@@ -65,7 +76,7 @@ export class SortManager {
65
76
  return;
66
77
  }
67
78
 
68
- item.direction = item.direction === 'ascending' ? 'descending' : 'ascending';
79
+ item.direction = item.direction === SORT_ASCENDING ? SORT_DESCENDING : SORT_ASCENDING;
69
80
 
70
81
  this.sort();
71
82
  }
@@ -110,19 +121,19 @@ export class SortManager {
110
121
  const sorterItem = items[sorterIndex];
111
122
 
112
123
  setAttributes(column.elements.wrapper, {
113
- 'aria-sort':
114
- sorterItem == null ? 'none' : items.length > 1 ? 'other' : sorterItem.direction,
115
- 'data-sort-direction': sorterItem == null ? undefined : sorterItem.direction,
124
+ [ARIA_SORT]:
125
+ sorterItem == null ? SORT_NONE : items.length > 1 ? SORT_OTHER : sorterItem.direction,
126
+ [ATTRIBUTE_DATA_SORT_DIRECTION]: sorterItem == null ? undefined : sorterItem.direction,
116
127
  });
117
128
 
118
129
  setAttribute(
119
130
  column.elements.sorter,
120
- 'data-sort-position',
131
+ ATTRIBUTE_DATA_SORT_POSITION,
121
132
  sorterIndex > -1 && items.length > 1 ? sorterIndex + 1 : undefined,
122
133
  );
123
134
  }
124
135
 
125
- state.managers.data.state.items.active =
136
+ state.managers.data.state.keys.active =
126
137
  items.length === 0 ? undefined : getSortedItems(state, items);
127
138
 
128
139
  state.managers.render.update(true, true);
@@ -130,11 +141,11 @@ export class SortManager {
130
141
 
131
142
  toggle(event: MouseEvent, field: string, direction?: string | null): void {
132
143
  switch (direction) {
133
- case 'ascending':
144
+ case SORT_ASCENDING:
134
145
  this.flip(field);
135
146
  return;
136
147
 
137
- case 'descending':
148
+ case SORT_DESCENDING:
138
149
  this.removeOrClear(event, field);
139
150
  return;
140
151
 
@@ -145,11 +156,11 @@ export class SortManager {
145
156
  }
146
157
  }
147
158
 
148
- function getSortedItems(state: State, sorters: PlainObject[]): DataItem[] {
159
+ function getSortedItems(state: State, sorters: PlainObject[]): Key[] {
149
160
  const data =
150
- state.managers.data.state.items.active?.map(key =>
151
- key instanceof GroupComponent ? key : state.managers.data.state.values.mapped.get(key)!,
152
- ) ?? state.managers.data.state.values.array.slice();
161
+ (state.managers.data.state.keys.active?.map(key =>
162
+ isGroupKey(key) ? key : state.managers.data.state.values.mapped.get(key)!,
163
+ ) as DataValue[]) ?? state.managers.data.state.values.array;
153
164
 
154
165
  if (!state.managers.group.enabled) {
155
166
  return sort(data as PlainObject[], sorters as never).map(
@@ -158,27 +169,28 @@ function getSortedItems(state: State, sorters: PlainObject[]): DataItem[] {
158
169
  }
159
170
 
160
171
  return sortWithGroups(state, data, sorters).map(item =>
161
- item instanceof GroupComponent ? item : (getValue(item, state.key) as Key),
172
+ typeof item === 'string' ? item : (getValue(item, state.key) as Key),
162
173
  );
163
174
  }
164
175
 
165
176
  export function sortWithGroups(
166
177
  state: State,
167
- data: Array<GroupComponent | PlainObject>,
178
+ data: DataValue[],
168
179
  sorters: PlainObject[],
169
- ): Array<GroupComponent | PlainObject> {
180
+ ): DataValue[] {
170
181
  const {length} = sorters;
171
182
 
172
183
  return data.sort((first, second) => {
173
- const firstValue =
174
- first instanceof GroupComponent
175
- ? first.value.stringified
176
- : getValue(first, state.managers.group.field);
184
+ const firstIsGroup = typeof first === 'string';
185
+ const secondIsGroup = typeof second === 'string';
177
186
 
178
- const secondValue =
179
- second instanceof GroupComponent
180
- ? second.value.stringified
181
- : getValue(second, state.managers.group.field);
187
+ const firstValue = firstIsGroup
188
+ ? state.managers.group.getForKey(first)?.value.stringified
189
+ : getValue(first, state.managers.group.field);
190
+
191
+ const secondValue = secondIsGroup
192
+ ? state.managers.group.getForKey(second)?.value.stringified
193
+ : getValue(second, state.managers.group.field);
182
194
 
183
195
  const firstOrder = state.managers.group.order[firstValue as never];
184
196
  const secondOrder = state.managers.group.order[secondValue as never];
@@ -189,9 +201,6 @@ export function sortWithGroups(
189
201
  return groupComparison;
190
202
  }
191
203
 
192
- const firstIsGroup = first instanceof GroupComponent;
193
- const secondIsGroup = second instanceof GroupComponent;
194
-
195
204
  if (firstIsGroup || secondIsGroup) {
196
205
  return firstIsGroup && secondIsGroup ? 0 : firstIsGroup ? -1 : 1;
197
206
  }
@@ -205,10 +214,14 @@ export function sortWithGroups(
205
214
  );
206
215
 
207
216
  if (comparison !== 0) {
208
- return comparison * (sorter.direction === 'ascending' ? 1 : -1);
217
+ return comparison * (sorter.direction === SORT_ASCENDING ? 1 : -1);
209
218
  }
210
219
  }
211
220
 
212
221
  return 0;
213
222
  });
214
223
  }
224
+
225
+ const SORT_NONE = 'none';
226
+
227
+ const SORT_OTHER = 'other';