@oscarpalmer/tabela 0.10.0 → 0.12.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 (69) hide show
  1. package/dist/components/body.component.js +1 -0
  2. package/dist/components/column.component.js +4 -4
  3. package/dist/components/group.component.js +28 -0
  4. package/dist/components/row.component.js +11 -8
  5. package/dist/helpers/dom.helpers.js +1 -1
  6. package/dist/managers/column.manager.js +9 -8
  7. package/dist/managers/data.manager.js +95 -30
  8. package/dist/managers/event.manager.js +42 -23
  9. package/dist/managers/filter.manager.js +16 -10
  10. package/dist/managers/group.manager.js +46 -0
  11. package/dist/managers/navigation.manager.js +73 -0
  12. package/dist/managers/render.manager.js +61 -31
  13. package/dist/managers/row.manager.js +7 -7
  14. package/dist/managers/selection.manager.js +49 -46
  15. package/dist/managers/sort.manager.js +37 -9
  16. package/dist/models/group.model.js +0 -0
  17. package/dist/models/selection.model.js +0 -0
  18. package/dist/tabela.full.js +682 -591
  19. package/dist/tabela.js +31 -10
  20. package/package.json +1 -1
  21. package/src/components/body.component.ts +2 -0
  22. package/src/components/column.component.ts +6 -6
  23. package/src/components/group.component.ts +43 -0
  24. package/src/components/row.component.ts +14 -9
  25. package/src/helpers/dom.helpers.ts +3 -1
  26. package/src/managers/column.manager.ts +13 -15
  27. package/src/managers/data.manager.ts +176 -38
  28. package/src/managers/event.manager.ts +68 -41
  29. package/src/managers/filter.manager.ts +29 -20
  30. package/src/managers/group.manager.ts +79 -0
  31. package/src/managers/navigation.manager.ts +146 -0
  32. package/src/managers/render.manager.ts +84 -40
  33. package/src/managers/row.manager.ts +9 -14
  34. package/src/managers/selection.manager.ts +68 -67
  35. package/src/managers/sort.manager.ts +85 -22
  36. package/src/models/column.model.ts +2 -2
  37. package/src/models/data.model.ts +14 -3
  38. package/src/models/filter.model.ts +11 -3
  39. package/src/models/group.model.ts +4 -0
  40. package/src/models/render.model.ts +3 -1
  41. package/src/models/selection.model.ts +9 -0
  42. package/src/models/sort.model.ts +11 -3
  43. package/src/models/tabela.model.ts +14 -36
  44. package/src/models/tabela.options.ts +3 -2
  45. package/src/tabela.ts +43 -19
  46. package/types/components/column.component.d.ts +3 -3
  47. package/types/components/group.component.d.ts +14 -0
  48. package/types/components/row.component.d.ts +2 -2
  49. package/types/helpers/style.helper.d.ts +1 -1
  50. package/types/managers/column.manager.d.ts +6 -7
  51. package/types/managers/data.manager.d.ts +7 -6
  52. package/types/managers/event.manager.d.ts +3 -6
  53. package/types/managers/filter.manager.d.ts +11 -11
  54. package/types/managers/group.manager.d.ts +17 -0
  55. package/types/managers/navigation.manager.d.ts +10 -0
  56. package/types/managers/render.manager.d.ts +6 -7
  57. package/types/managers/row.manager.d.ts +4 -5
  58. package/types/managers/selection.manager.d.ts +12 -7
  59. package/types/managers/sort.manager.d.ts +11 -9
  60. package/types/models/column.model.d.ts +2 -2
  61. package/types/models/data.model.d.ts +13 -3
  62. package/types/models/filter.model.d.ts +10 -3
  63. package/types/models/group.model.d.ts +4 -0
  64. package/types/models/render.model.d.ts +2 -1
  65. package/types/models/selection.model.d.ts +8 -0
  66. package/types/models/sort.model.d.ts +10 -3
  67. package/types/models/tabela.model.d.ts +14 -33
  68. package/types/models/tabela.options.d.ts +3 -2
  69. package/types/tabela.d.ts +4 -1
@@ -12,6 +12,7 @@ var BodyComponent = class {
12
12
  this.elements.group = group;
13
13
  group.className += " tabela__rowgroup--body";
14
14
  group.tabIndex = 0;
15
+ group.setAttribute("data-event", "body");
15
16
  group.append(this.elements.faker);
16
17
  }
17
18
  destroy() {
@@ -2,13 +2,13 @@ import { createElement } from "../helpers/dom.helpers.js";
2
2
  var ColumnComponent = class {
3
3
  elements;
4
4
  options;
5
- constructor(options) {
6
- const width = Number.parseInt(getComputedStyle(document.body).fontSize, 10) * (options.width ?? options.title.length * 1.5);
5
+ constructor(column) {
6
+ const width = Number.parseInt(getComputedStyle(document.body).fontSize, 10) * (column.width ?? column.title.length * 1.5);
7
7
  this.options = {
8
- ...options,
8
+ ...column,
9
9
  width
10
10
  };
11
- this.elements = createHeading(options.field, options.title, width);
11
+ this.elements = createHeading(this.options.field, this.options.title, width);
12
12
  }
13
13
  destroy() {
14
14
  this.elements.content.remove();
@@ -0,0 +1,28 @@
1
+ import { createElement } from "../helpers/dom.helpers.js";
2
+ var GroupComponent = class {
3
+ element;
4
+ expanded = true;
5
+ filtered = 0;
6
+ selected = 0;
7
+ total = 0;
8
+ constructor(key, label, value) {
9
+ this.key = key;
10
+ this.label = label;
11
+ this.value = value;
12
+ }
13
+ };
14
+ function renderGroup(state, component) {
15
+ component.element ??= createElement("div", {
16
+ className: "tabela__row tabela__row--group",
17
+ innerHTML: `<div class="tabela__cell tabela__cell--group" role="cell">
18
+ <button class="tabela__button tabela__button--group" data-event="group" data-key="${component.key}" type="button">
19
+ <span aria-hidden="true"></span>
20
+ <span>Open/close</span>
21
+ </button>
22
+ <p>${component.label}</p>
23
+ </div>`,
24
+ role: "row"
25
+ }, {}, { height: `${state.options.rowHeight}px` });
26
+ }
27
+ function updateGroup(state, component) {}
28
+ export { GroupComponent, renderGroup, updateGroup };
@@ -9,27 +9,30 @@ function removeRow(pool, row) {
9
9
  }
10
10
  row.cells = {};
11
11
  }
12
- function renderRow(managers, row) {
13
- const element = row.element ?? managers.render.pool.rows.shift() ?? createRow();
12
+ function renderRow(state, row) {
13
+ const element = row.element ?? state.managers.render.pool.rows.shift() ?? createRow();
14
14
  row.element = element;
15
15
  element.innerHTML = "";
16
- const selected = managers.selection.items.has(row.key);
16
+ const selected = state.managers.selection.items.has(row.key);
17
+ const key = String(row.key);
17
18
  setAttributes(element, {
18
19
  "aria-selected": String(selected),
20
+ "data-active": String(state.managers.navigation.active === row.key),
19
21
  "data-event": "row",
20
- "data-key": String(row.key)
22
+ "data-key": key,
23
+ id: `tabela_${state.id}_row_${key}`
21
24
  });
22
25
  element.classList.add("tabela__row--body");
23
26
  if (selected) element.classList.add("tabela__row--selected");
24
27
  else element.classList.remove("tabela__row--selected");
25
- const columns = managers.column.items;
28
+ const columns = state.managers.column.items;
26
29
  const { length } = columns;
27
- const data = managers.data.values.objects.mapped.get(row.key);
30
+ const data = state.managers.data.values.objects.mapped.get(row.key);
28
31
  if (data == null) return;
29
32
  for (let index = 0; index < length; index += 1) {
30
33
  const { options } = columns[index];
31
- managers.render.pool.cells[options.field] ??= [];
32
- const cell = managers.render.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
34
+ state.managers.render.pool.cells[options.field] ??= [];
35
+ const cell = state.managers.render.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
33
36
  cell.textContent = String(data[options.field]);
34
37
  row.cells[options.field] = cell;
35
38
  element.append(cell);
@@ -33,6 +33,6 @@ function createRow() {
33
33
  return createElement("div", {
34
34
  className: "tabela__row",
35
35
  role: "row"
36
- }, {}, {});
36
+ }, {}, { height: "32px" });
37
37
  }
38
38
  export { createCell, createElement, createRow, createRowGroup };
@@ -1,18 +1,19 @@
1
1
  import { ColumnComponent } from "../components/column.component.js";
2
2
  var ColumnManager = class {
3
3
  items = [];
4
- constructor(managers, components, columns) {
5
- this.managers = managers;
6
- this.components = components;
7
- this.set(columns);
4
+ constructor(state) {
5
+ this.state = state;
6
+ this.set(state.options.columns);
8
7
  }
9
8
  destroy() {
10
9
  const { length } = this.items;
11
10
  for (let index = 0; index < length; index += 1) this.items[index].destroy();
12
- this.items.length = 0;
11
+ this.items = void 0;
12
+ this.state = void 0;
13
13
  }
14
14
  remove(value) {
15
- const { components, items, managers } = this;
15
+ const { items, state } = this;
16
+ const { components, managers } = state;
16
17
  const fields = (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string");
17
18
  const { length } = fields;
18
19
  if (length === 0) return;
@@ -28,8 +29,8 @@ var ColumnManager = class {
28
29
  managers.render.removeCells(fields);
29
30
  }
30
31
  set(columns) {
31
- const { components, items } = this;
32
- const { footer, header } = components;
32
+ const { items, state } = this;
33
+ const { footer, header } = state.components;
33
34
  items.splice(0, items.length, ...columns.map((column) => new ColumnComponent(column)));
34
35
  header.update(items);
35
36
  footer.update(items);
@@ -1,5 +1,8 @@
1
- import { push, sort } from "@oscarpalmer/atoms/array";
1
+ import { GroupComponent } from "../components/group.component.js";
2
+ import { sortWithGroups } from "./sort.manager.js";
3
+ import { select, sort } from "@oscarpalmer/atoms/array";
2
4
  import { toMap } from "@oscarpalmer/atoms/array/to-map";
5
+ import { toRecord } from "@oscarpalmer/atoms/array/to-record";
3
6
  import { isPlainObject } from "@oscarpalmer/atoms/is";
4
7
  var DataManager = class {
5
8
  handlers = Object.freeze({
@@ -17,19 +20,41 @@ var DataManager = class {
17
20
  array: []
18
21
  }
19
22
  };
23
+ get keys() {
24
+ return this.values.keys.active ?? this.values.keys.original;
25
+ }
20
26
  get size() {
21
- return this.values.keys.active?.length ?? this.values.keys.original.length;
27
+ return this.keys.length;
22
28
  }
23
- constructor(managers, components, field) {
24
- this.managers = managers;
25
- this.components = components;
26
- this.field = field;
29
+ constructor(state) {
30
+ this.state = state;
27
31
  }
28
32
  async add(data, render) {
29
- const { field, values } = this;
30
- push(values.objects.array, data);
31
- values.objects.mapped = toMap(values.objects.array, field);
32
- if (render) this.render();
33
+ const { state, values } = this;
34
+ const { length } = data;
35
+ const updates = [];
36
+ for (let index = 0; index < length; index += 1) {
37
+ const item = data[index];
38
+ const key = item[state.key];
39
+ if (values.objects.mapped.has(key)) {
40
+ updates.push(item);
41
+ continue;
42
+ }
43
+ values.objects.array.push(item);
44
+ values.objects.mapped.set(key, item);
45
+ if (!state.managers.group.enabled) continue;
46
+ const groupKey = item[state.managers.group.field];
47
+ let group = state.managers.group.get(groupKey);
48
+ if (group == null) {
49
+ group = new GroupComponent(String(groupKey), String(groupKey), groupKey);
50
+ values.objects.array.push(group);
51
+ state.managers.group.add(group);
52
+ }
53
+ if (!group.expanded) state.managers.group.collapsed.add(key);
54
+ group.total += 1;
55
+ }
56
+ if (updates.length > 0) this.update(updates);
57
+ else if (render) this.render();
33
58
  }
34
59
  clear() {
35
60
  if (this.values.objects.array.length > 0) this.set([]);
@@ -41,55 +66,95 @@ var DataManager = class {
41
66
  values.keys.original.length = 0;
42
67
  values.objects.array.length = 0;
43
68
  this.handlers = void 0;
69
+ this.state = void 0;
70
+ this.values = void 0;
44
71
  }
45
72
  get(active) {
46
73
  const { values } = this;
47
- return active ?? false ? values.keys.active?.map((key) => values.objects.mapped.get(key)) ?? [] : values.objects.array;
74
+ return active ?? false ? select(values.keys.active ?? [], (key) => !(key instanceof GroupComponent), (key) => values.objects.mapped.get(key)) : values.objects.array.filter((item) => !(item instanceof GroupComponent));
75
+ }
76
+ getIndex(key) {
77
+ return this.keys.indexOf(key);
48
78
  }
49
79
  async remove(items, render) {
50
- const { field, managers, values } = this;
51
- const keys = items.map((value) => isPlainObject(value) ? value[field] : value).filter((key) => values.objects.mapped.has(key));
80
+ const { state, values } = this;
81
+ const keys = items.map((value) => isPlainObject(value) ? value[state.key] : value).filter((key) => values.objects.mapped.has(key));
52
82
  const { length } = keys;
53
83
  if (length === 0) return;
54
84
  for (let keyIndex = 0; keyIndex < length; keyIndex += 1) {
55
85
  const key = keys[keyIndex];
56
86
  values.objects.mapped.delete(key);
57
- const arrayIndex = values.objects.array.findIndex((object) => object[field] === key);
58
- if (arrayIndex > -1) values.objects.array.splice(arrayIndex, 1);
87
+ const arrayIndex = values.objects.array.findIndex((item) => !(item instanceof GroupComponent) && item[state.key] === key);
88
+ let item;
89
+ if (arrayIndex > -1) [item] = values.objects.array.splice(arrayIndex, 1);
59
90
  values.keys.original.splice(values.keys.original.indexOf(key), 1);
60
- managers.row.remove(key);
91
+ state.managers.row.remove(key);
92
+ if (!state.managers.group.enabled || item == null) continue;
93
+ state.managers.group.collapsed.delete(key);
94
+ const groupKey = item[state.managers.group.field];
95
+ const group = state.managers.group.get(groupKey);
96
+ if (group == null) continue;
97
+ group.total -= 1;
98
+ if (group.total > 0) continue;
99
+ const groupIndex = values.objects.array.findIndex((item) => item instanceof GroupComponent && item.value === groupKey);
100
+ if (groupIndex > -1) values.objects.array.splice(groupIndex, 1);
101
+ state.managers.group.remove(group);
61
102
  }
62
103
  if (render) this.render();
63
104
  }
64
105
  render() {
65
- const { field, managers, values } = this;
66
- values.keys.original = sort(values.objects.array.map((item) => item[field]));
67
- if (Object.keys(managers.filter.items).length > 0) managers.filter.filter();
68
- else if (managers.sort.items.length > 0) managers.sort.sort();
69
- else managers.render.update(true);
106
+ const { state, values } = this;
107
+ if (state.managers.group.enabled) sortWithGroups(state, values.objects.array, [{
108
+ direction: "ascending",
109
+ key: state.key
110
+ }]);
111
+ else sort(values.objects.array, [{
112
+ direction: "ascending",
113
+ key: state.key
114
+ }]);
115
+ values.keys.original = values.objects.array.map((item) => item instanceof GroupComponent ? item : item[state.key]);
116
+ values.objects.mapped = toMap(values.objects.array.filter((item) => !(item instanceof GroupComponent)), (item) => item[state.key]);
117
+ if (Object.keys(state.managers.filter.items).length > 0) state.managers.filter.filter();
118
+ else if (state.managers.sort.items.length > 0) state.managers.sort.sort();
119
+ else state.managers.render.update(true, true);
70
120
  }
71
121
  set(data) {
72
- const { field, values } = this;
73
- values.objects.mapped = toMap(data, field);
74
- values.objects.array = data;
122
+ const { state, values } = this;
123
+ const array = data.slice();
124
+ if (state.managers.group.enabled) {
125
+ const grouped = toRecord.arrays(data, state.managers.group.field);
126
+ const entries = Object.entries(grouped);
127
+ const { length } = entries;
128
+ const groups = [];
129
+ for (let index = 0; index < length; index += 1) {
130
+ const [value, items] = entries[index];
131
+ const key = String(value);
132
+ const group = new GroupComponent(key, key, value);
133
+ group.total = items.length;
134
+ groups.push(group);
135
+ array.push(group);
136
+ }
137
+ state.managers.group.set(groups);
138
+ }
139
+ values.objects.array = array;
75
140
  this.render();
76
141
  }
77
142
  async synchronize(data, remove) {
78
- const { field, values } = this;
143
+ const { state, values } = this;
79
144
  const add = [];
80
145
  const updated = [];
81
146
  const keys = /* @__PURE__ */ new Set([]);
82
147
  const { length } = data;
83
148
  for (let index = 0; index < length; index += 1) {
84
149
  const object = data[index];
85
- const key = object[field];
150
+ const key = object[state.key];
86
151
  if (values.objects.mapped.has(key)) updated.push(object);
87
152
  else add.push(object);
88
153
  keys.add(key);
89
154
  }
90
155
  if (keys.size === 0) return;
91
156
  if (remove ?? false) {
92
- const toRemove = values.keys.original.filter((key) => !keys.has(key));
157
+ const toRemove = values.keys.original.filter((key) => !(key instanceof GroupComponent) && !keys.has(key));
93
158
  if (toRemove.length > 0) await this.remove(toRemove, false);
94
159
  }
95
160
  await this.update(updated);
@@ -97,18 +162,18 @@ var DataManager = class {
97
162
  if (add.length > 0 || (remove ?? false)) this.render();
98
163
  }
99
164
  async update(data) {
100
- const { field, managers, values } = this;
165
+ const { state, values } = this;
101
166
  const { length } = data;
102
167
  for (let index = 0; index < length; index += 1) {
103
168
  const object = data[index];
104
- const key = object[field];
169
+ const key = object[state.key];
105
170
  const value = values.objects.mapped.get(key);
106
171
  if (value != null) {
107
172
  values.objects.mapped.set(key, {
108
173
  ...value,
109
174
  ...object
110
175
  });
111
- managers.row.update(key);
176
+ state.managers.row.update(key);
112
177
  }
113
178
  }
114
179
  }
@@ -1,34 +1,53 @@
1
1
  import { on } from "@oscarpalmer/toretto/event";
2
- import { findAncestor } from "@oscarpalmer/toretto";
2
+ import { findAncestor } from "@oscarpalmer/toretto/find";
3
3
  var EventManager = class {
4
- listener;
5
- constructor(element, managers) {
6
- this.managers = managers;
7
- this.listener = on(element, "click", (event) => {
8
- this.onClick(event);
9
- }, { passive: false });
4
+ constructor(state) {
5
+ this.state = state;
6
+ mapped.set(state.element, this);
10
7
  }
11
8
  destroy() {
12
- this.listener();
13
- }
14
- onClick(event) {
15
- const target = findAncestor(event, "[data-event]");
16
- if (!(target instanceof HTMLElement)) return;
17
- switch (target?.getAttribute("data-event")) {
18
- case "heading":
19
- this.onSort(event, target);
20
- break;
21
- case "row":
22
- this.managers.selection.handle(event, target);
23
- break;
24
- default: break;
25
- }
9
+ mapped.delete(this.state.element);
10
+ this.state = void 0;
26
11
  }
27
12
  onSort(event, target) {
28
- const { managers } = this;
29
13
  const direction = target.getAttribute("data-sort-direction");
30
14
  const field = target.getAttribute("data-field");
31
- if (field != null) managers.sort.toggle(event, field, direction);
15
+ if (field != null) this.state.managers.sort.toggle(event, field, direction);
32
16
  }
33
17
  };
18
+ function onClick(event) {
19
+ const target = findAncestor(event, "[data-event]");
20
+ const table = findAncestor(event, ".tabela");
21
+ if (!(target instanceof HTMLElement) || !(table instanceof HTMLElement)) return;
22
+ const manager = mapped.get(table);
23
+ if (manager == null) return;
24
+ switch (target?.getAttribute("data-event")) {
25
+ case "group":
26
+ manager.state.managers.group.handle(target);
27
+ break;
28
+ case "heading":
29
+ manager.onSort(event, target);
30
+ break;
31
+ case "row":
32
+ manager.state.managers.selection.handle(event, target);
33
+ break;
34
+ default: break;
35
+ }
36
+ }
37
+ function onKeydown(event) {
38
+ const target = findAncestor(event, "[data-event]");
39
+ const table = findAncestor(event, ".tabela");
40
+ if (!(target instanceof HTMLElement) || !(table instanceof HTMLElement)) return;
41
+ const manager = mapped.get(table);
42
+ if (manager == null) return;
43
+ switch (target?.getAttribute("data-event")) {
44
+ case "body":
45
+ manager.state.managers.navigation.handle(event);
46
+ break;
47
+ default: break;
48
+ }
49
+ }
50
+ var mapped = /* @__PURE__ */ new WeakMap();
51
+ on(document, "click", onClick);
52
+ on(document, "keydown", onKeydown, { passive: false });
34
53
  export { EventManager };
@@ -1,3 +1,4 @@
1
+ import { GroupComponent } from "../components/group.component.js";
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";
@@ -10,8 +11,8 @@ var FilterManager = class {
10
11
  set: (items) => this.set(items)
11
12
  });
12
13
  items = {};
13
- constructor(managers) {
14
- this.managers = managers;
14
+ constructor(state) {
15
+ this.state = state;
15
16
  }
16
17
  add(item) {
17
18
  if (this.items[item.field] == null) this.items[item.field] = [];
@@ -27,16 +28,21 @@ var FilterManager = class {
27
28
  }
28
29
  destroy() {
29
30
  this.handlers = void 0;
30
- this.items = {};
31
+ this.items = void 0;
32
+ this.state = void 0;
31
33
  }
32
34
  filter() {
33
- const { managers } = this;
35
+ const { state } = this;
34
36
  const filtered = [];
35
37
  const filters = Object.entries(this.items);
36
- const keysLength = managers.data.values.keys.original.length;
38
+ const keysLength = state.managers.data.values.keys.original.length;
37
39
  rowLoop: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
38
- const key = managers.data.values.keys.original[keyIndex];
39
- const row = managers.data.values.objects.mapped.get(key);
40
+ const key = state.managers.data.values.keys.original[keyIndex];
41
+ if (key instanceof GroupComponent) {
42
+ filtered.push(key);
43
+ continue;
44
+ }
45
+ const row = state.managers.data.values.objects.mapped.get(key);
40
46
  if (row == null) continue;
41
47
  filterLoop: for (let filterIndex = 0; filterIndex < filters.length; filterIndex += 1) {
42
48
  const [field, items] = filters[filterIndex];
@@ -49,9 +55,9 @@ var FilterManager = class {
49
55
  }
50
56
  filtered.push(key);
51
57
  }
52
- managers.data.values.keys.active = filtered;
53
- if (managers.sort.items.length > 0) managers.sort.sort();
54
- else managers.render.update(true, true);
58
+ state.managers.data.values.keys.active = filtered;
59
+ if (state.managers.sort.items.length > 0) state.managers.sort.sort();
60
+ else state.managers.render.update(true, true);
55
61
  }
56
62
  remove(value) {
57
63
  if (typeof value === "string") {
@@ -0,0 +1,46 @@
1
+ import { sort } from "@oscarpalmer/atoms/array";
2
+ import { toRecord } from "@oscarpalmer/atoms/array/to-record";
3
+ import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
4
+ var GroupManager = class {
5
+ collapsed = /* @__PURE__ */ new Set();
6
+ enabled = false;
7
+ field;
8
+ items = [];
9
+ order = {};
10
+ constructor(state) {
11
+ this.state = state;
12
+ if (isNullableOrWhitespace(state.options.grouping)) return;
13
+ this.enabled = true;
14
+ this.field = state.options.grouping;
15
+ }
16
+ add(group) {
17
+ this.set([...this.items, group]);
18
+ }
19
+ get(value) {
20
+ return this.items.find((item) => item.value === value);
21
+ }
22
+ handle(button) {
23
+ const key = button.dataset.key;
24
+ const group = this.get(key);
25
+ if (group == null) return;
26
+ const { collapsed, items, state } = this;
27
+ group.expanded = !group.expanded;
28
+ const index = items.indexOf(group);
29
+ let first = state.managers.data.values.keys.original.indexOf(items[index]) + 1;
30
+ const last = items[index + 1] == null ? state.managers.data.keys.length - 1 : state.managers.data.values.keys.original.indexOf(items[index + 1]) - 1;
31
+ for (; first <= last; first += 1) {
32
+ const key = state.managers.data.values.keys.original[first];
33
+ if (group.expanded) collapsed.delete(key);
34
+ else collapsed.add(key);
35
+ }
36
+ state.managers.render.update(true, true);
37
+ }
38
+ remove(group) {
39
+ this.set(this.items.filter((item) => item !== group));
40
+ }
41
+ set(items) {
42
+ this.items = sort(items, (item) => item.label);
43
+ this.order = toRecord(items, "value", (_, index) => index);
44
+ }
45
+ };
46
+ export { GroupManager };
@@ -0,0 +1,73 @@
1
+ import { getKey } from "../helpers/misc.helpers.js";
2
+ import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
3
+ import { clamp } from "@oscarpalmer/atoms/number";
4
+ var NavigationManager = class {
5
+ active;
6
+ constructor(state) {
7
+ this.state = state;
8
+ }
9
+ destroy() {
10
+ this.state = void 0;
11
+ }
12
+ handle(event) {
13
+ if (!allKeys.has(event.key)) return;
14
+ event.preventDefault();
15
+ const { components, id, managers } = this.state;
16
+ const activeDescendant = components.body.elements.group.getAttribute("aria-activedescendant");
17
+ const { keys } = managers.data;
18
+ const { length } = keys;
19
+ let next;
20
+ if (isNullableOrWhitespace(activeDescendant)) next = getDefaultIndex(event.key, length);
21
+ else next = getIndex(event, activeDescendant, id, keys);
22
+ if (next != null) this.setActive(keys.at(next));
23
+ }
24
+ setActive(key, scroll) {
25
+ const { components, managers, options } = this.state;
26
+ this.active = key;
27
+ const active = components.body.elements.group.querySelectorAll("[data-active=\"true\"]");
28
+ for (const item of active) item.setAttribute("data-active", "false");
29
+ const row = managers.row.get(key);
30
+ if (row != null) {
31
+ row.element?.setAttribute("data-active", "true");
32
+ if (scroll ?? true) if (row.element == null) components.body.elements.group.scrollTo({
33
+ top: managers.data.getIndex(key) * options.rowHeight,
34
+ behavior: "smooth"
35
+ });
36
+ else row.element.scrollIntoView({ block: "nearest" });
37
+ }
38
+ components.body.elements.group.setAttribute("aria-activedescendant", row == null ? "" : `tabela_${this.state.id}_row_${key}`);
39
+ }
40
+ };
41
+ function getDefaultIndex(key, max) {
42
+ switch (true) {
43
+ case negativeDefaultKeys.has(key): return -1;
44
+ case key === "PageDown": return Math.min(9, max - 1);
45
+ case key === "PageUp": return max < 10 ? 0 : max - 10;
46
+ default: return 0;
47
+ }
48
+ }
49
+ function getIndex(event, active, id, keys) {
50
+ const key = getKey(active.replace(`tabela_${id}_row_`, ""));
51
+ if (key == null) return;
52
+ if (absoluteKeys.has(event.key)) return event.key === "Home" ? 0 : keys.length - 1;
53
+ return clamp(keys.indexOf(key) + getOffset(event.key), 0, keys.length - 1, true);
54
+ }
55
+ function getOffset(key) {
56
+ switch (key) {
57
+ case "ArrowDown": return 1;
58
+ case "ArrowUp": return -1;
59
+ case "PageDown": return 10;
60
+ case "PageUp": return -10;
61
+ default: return 0;
62
+ }
63
+ }
64
+ var absoluteKeys = new Set(["End", "Home"]);
65
+ var arrowKeys = new Set(["ArrowDown", "ArrowUp"]);
66
+ var negativeDefaultKeys = new Set(["ArrowUp", "End"]);
67
+ var pageKeys = new Set(["PageDown", "PageUp"]);
68
+ var allKeys = new Set([
69
+ ...absoluteKeys,
70
+ ...arrowKeys,
71
+ ...pageKeys
72
+ ]);
73
+ export { NavigationManager };