@oscarpalmer/tabela 0.12.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 (65) hide show
  1. package/dist/tabela.full.js +146 -18
  2. package/package.json +45 -37
  3. package/src/components/body.component.ts +11 -7
  4. package/src/components/column.component.ts +23 -24
  5. package/src/components/footer.component.ts +7 -5
  6. package/src/components/group.component.ts +73 -9
  7. package/src/components/header.component.ts +6 -4
  8. package/src/components/row.component.ts +28 -18
  9. package/src/helpers/dom.helpers.ts +27 -29
  10. package/src/helpers/misc.helpers.ts +5 -0
  11. package/src/helpers/style.helper.ts +1 -1
  12. package/src/managers/column.manager.ts +4 -0
  13. package/src/managers/data.manager.ts +197 -124
  14. package/src/managers/event.manager.ts +27 -17
  15. package/src/managers/filter.manager.ts +49 -25
  16. package/src/managers/group.manager.ts +73 -12
  17. package/src/managers/navigation.manager.ts +48 -50
  18. package/src/managers/render.manager.ts +56 -29
  19. package/src/managers/row.manager.ts +22 -10
  20. package/src/managers/selection.manager.ts +40 -31
  21. package/src/managers/sort.manager.ts +58 -43
  22. package/src/managers/style.manager.ts +171 -0
  23. package/src/models/column.model.ts +2 -6
  24. package/src/models/data.model.ts +12 -10
  25. package/src/models/dom.model.ts +33 -0
  26. package/src/models/event.model.ts +7 -0
  27. package/src/models/filter.model.ts +20 -0
  28. package/src/models/group.model.ts +10 -2
  29. package/src/models/sort.model.ts +4 -0
  30. package/src/models/style.model.ts +51 -0
  31. package/src/models/tabela.model.ts +11 -8
  32. package/src/tabela.ts +67 -37
  33. package/types/components/body.component.d.ts +0 -6
  34. package/types/components/column.component.d.ts +0 -13
  35. package/types/components/footer.component.d.ts +0 -8
  36. package/types/components/group.component.d.ts +0 -14
  37. package/types/components/header.component.d.ts +0 -8
  38. package/types/components/row.component.d.ts +0 -11
  39. package/types/helpers/dom.helpers.d.ts +0 -10
  40. package/types/helpers/misc.helpers.d.ts +0 -2
  41. package/types/helpers/style.helper.d.ts +0 -1
  42. package/types/index.d.ts +0 -4
  43. package/types/managers/column.manager.d.ts +0 -12
  44. package/types/managers/data.manager.d.ts +0 -29
  45. package/types/managers/event.manager.d.ts +0 -7
  46. package/types/managers/filter.manager.d.ts +0 -19
  47. package/types/managers/group.manager.d.ts +0 -17
  48. package/types/managers/navigation.manager.d.ts +0 -10
  49. package/types/managers/render.manager.d.ts +0 -17
  50. package/types/managers/row.manager.d.ts +0 -13
  51. package/types/managers/selection.manager.d.ts +0 -24
  52. package/types/managers/sort.manager.d.ts +0 -28
  53. package/types/models/body.model.d.ts +0 -4
  54. package/types/models/column.model.d.ts +0 -13
  55. package/types/models/data.model.d.ts +0 -24
  56. package/types/models/filter.model.d.ts +0 -13
  57. package/types/models/footer.model.d.ts +0 -5
  58. package/types/models/group.model.d.ts +0 -4
  59. package/types/models/header.model.d.ts +0 -4
  60. package/types/models/render.model.d.ts +0 -13
  61. package/types/models/selection.model.d.ts +0 -8
  62. package/types/models/sort.model.d.ts +0 -12
  63. package/types/models/tabela.model.d.ts +0 -39
  64. package/types/models/tabela.options.d.ts +0 -10
  65. package/types/tabela.d.ts +0 -15
@@ -5,20 +5,21 @@ 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
7
  import {createElement} from '../helpers/dom.helpers';
8
- import {getKey} from '../helpers/misc.helpers';
9
- import {dragStyling} from '../helpers/style.helper';
8
+ import {getKey, isGroupKey} from '../helpers/misc.helpers';
9
+ import {preventSelection} from '../helpers/style.helper';
10
+ import {ARIA_SELECTED, ATTRIBUTE_DATA_KEY, ELEMENT_DIV} from '../models/dom.model';
10
11
  import type {TabelaSelection} from '../models/selection.model';
12
+ import {CSS_ROW_BODY, CSS_ROW_SELECTED, CSS_SELECTION, CSS_TABLE} from '../models/style.model';
11
13
  import type {State} from '../models/tabela.model';
12
- import {GroupComponent} from '../components/group.component';
13
14
 
14
15
  export class SelectionManager {
15
- handlers = Object.freeze({
16
+ handlers: TabelaSelection = {
16
17
  add: keys => this.add(keys),
17
18
  clear: () => this.clear(),
18
19
  remove: keys => this.remove(keys),
19
20
  set: keys => this.set(keys),
20
21
  toggle: () => this.toggle(),
21
- } satisfies TabelaSelection);
22
+ };
22
23
 
23
24
  items = new Set<Key>();
24
25
 
@@ -70,7 +71,7 @@ export class SelectionManager {
70
71
  }
71
72
 
72
73
  handle(event: MouseEvent, target: HTMLElement): void {
73
- const key = getKey(target.getAttribute('data-key'));
74
+ const key = getKey(target.getAttribute(ATTRIBUTE_DATA_KEY));
74
75
 
75
76
  if (key == null) {
76
77
  return;
@@ -110,8 +111,13 @@ export class SelectionManager {
110
111
 
111
112
  const keyed = isKey(from) && isKey(to);
112
113
 
113
- const fromKey = keyed ? (from as Key) : getKey((from as HTMLElement).getAttribute('data-key'))!;
114
- 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))!;
115
121
 
116
122
  if (fromKey === toKey) {
117
123
  return;
@@ -133,7 +139,7 @@ export class SelectionManager {
133
139
  for (let index = start; index <= end; index += 1) {
134
140
  const key = keys[index];
135
141
 
136
- if (!(key instanceof GroupComponent)) {
142
+ if (!isGroupKey(key)) {
137
143
  selected.push(key);
138
144
  }
139
145
  }
@@ -189,54 +195,51 @@ export class SelectionManager {
189
195
  if (items.size === keys.length - state.managers.group.items.length) {
190
196
  this.clear();
191
197
  } else {
192
- this.set(keys.filter(key => !(key instanceof GroupComponent)) as Key[]);
198
+ this.set(keys.filter(key => !isGroupKey(key)));
193
199
  }
194
200
  }
195
201
 
196
202
  update(removed: Key[]): void {
203
+ const {state} = this;
204
+
197
205
  const items = [
198
206
  ...removed.map(key => ({key, removed: true})),
199
207
  ...[...this.items].map(key => ({key, removed: false})),
200
208
  ];
201
209
 
202
- const {length} = items;
210
+ let {length} = items;
203
211
 
204
212
  for (let index = 0; index < length; index += 1) {
205
213
  const {key, removed} = items[index];
206
214
 
207
- const row = this.state.managers.row.get(key);
215
+ const element = state.managers.row.get(key, false)?.element;
208
216
 
209
- if (row == null || row.element == null) {
217
+ if (element == null) {
210
218
  continue;
211
219
  }
212
220
 
213
- setAttribute(row.element, 'aria-selected', String(!removed));
221
+ setAttribute(element, ARIA_SELECTED, String(!removed));
214
222
 
215
223
  if (removed) {
216
- row.element.classList.remove('tabela__row--selected');
224
+ element.classList.remove(CSS_ROW_SELECTED);
217
225
  } else {
218
- row.element.classList.add('tabela__row--selected');
226
+ element.classList.add(CSS_ROW_SELECTED);
219
227
  }
220
228
  }
221
229
  }
222
230
  }
223
231
 
224
232
  function getPlaceholder(): HTMLElement {
225
- placeholder ??= createElement(
226
- 'div',
227
- {
228
- className: 'tabela__selection--placeholder',
229
- },
230
- {},
231
- {},
232
- );
233
+ placeholder ??= createElement(ELEMENT_DIV, {
234
+ className: CSS_SELECTION,
235
+ });
233
236
 
234
237
  return placeholder;
235
238
  }
236
239
 
237
240
  function onMouseDown(event: MouseEvent): void {
238
241
  if (shifted) {
239
- const row = findAncestor(event.target as HTMLElement, '.tabela__row--body');
242
+ const row = findAncestor(event.target as HTMLElement, `.${CSS_ROW_BODY}`);
240
243
 
241
244
  if (!(row instanceof HTMLElement)) {
242
245
  return;
@@ -245,7 +248,7 @@ function onMouseDown(event: MouseEvent): void {
245
248
  startElement = row;
246
249
  startPosition = getPosition(event)!;
247
250
 
248
- dragStyling.set();
251
+ preventSelection.set();
249
252
  }
250
253
  }
251
254
 
@@ -286,18 +289,18 @@ function onMouseUp(event: MouseEvent): void {
286
289
  if (!event.shiftKey) {
287
290
  shifted = false;
288
291
 
289
- dragStyling.remove();
292
+ preventSelection.remove();
290
293
  }
291
294
 
292
295
  getPlaceholder().remove();
293
296
 
294
- const row = findAncestor(event.target as HTMLElement, '.tabela__row--body');
297
+ const row = findAncestor(event.target as HTMLElement, bodyRowSelector);
295
298
 
296
299
  if (row instanceof HTMLElement) {
297
300
  endElement = row;
298
301
 
299
- const endTable = findAncestor(endElement, '.tabela');
300
- const startTable = findAncestor(startElement, '.tabela');
302
+ const endTable = findAncestor(endElement, tableSelector);
303
+ const startTable = findAncestor(startElement, tableSelector);
301
304
 
302
305
  if (startTable != null && startTable === endTable) {
303
306
  mapped.get(startTable)?.range(startElement, endElement);
@@ -310,7 +313,7 @@ function onMouseUp(event: MouseEvent): void {
310
313
  }
311
314
 
312
315
  function onShift(event: KeyboardEvent, value: boolean): void {
313
- if (event.key === 'Shift') {
316
+ if (event.key === KEY_SHIFT) {
314
317
  shifted = value;
315
318
  }
316
319
  }
@@ -323,8 +326,14 @@ function onShiftUp(event: KeyboardEvent): void {
323
326
  onShift(event, false);
324
327
  }
325
328
 
329
+ const KEY_SHIFT = 'Shift';
330
+
326
331
  const mapped = new WeakMap<Element, SelectionManager>();
327
332
 
333
+ const bodyRowSelector = `.${CSS_ROW_BODY}`;
334
+
335
+ const tableSelector = `.${CSS_TABLE}`;
336
+
328
337
  let shifted = false;
329
338
 
330
339
  let endElement: HTMLElement | undefined;
@@ -1,21 +1,34 @@
1
- import {sort, type ArrayKeySorter} from '@oscarpalmer/atoms/array';
1
+ import {sort} from '@oscarpalmer/atoms/array';
2
2
  import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
3
3
  import {compare} from '@oscarpalmer/atoms/value/compare';
4
+ import {getValue} from '@oscarpalmer/atoms/value/handle';
4
5
  import {setAttribute, setAttributes} from '@oscarpalmer/toretto/attribute';
5
- import {GroupComponent} from '../components/group.component';
6
- 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';
7
18
  import type {State} from '../models/tabela.model';
19
+ import type {DataValue} from '../models/data.model';
20
+ import {isGroupKey} from '../helpers/misc.helpers';
8
21
 
9
22
  export class SortManager {
10
- handlers = Object.freeze({
23
+ handlers: TabelaSort = {
11
24
  add: (field, direction) => this.add(field, direction),
12
25
  flip: field => this.flip(field),
13
26
  clear: () => this.clear(),
14
27
  remove: field => this.remove(field),
15
28
  set: items => this.set(items),
16
- } satisfies TabelaSort);
29
+ };
17
30
 
18
- items: ArrayKeySorter<PlainObject>[] = [];
31
+ items: PlainObject[] = [];
19
32
 
20
33
  constructor(public state: State) {}
21
34
 
@@ -28,7 +41,7 @@ export class SortManager {
28
41
 
29
42
  this.items.push({
30
43
  key: field,
31
- direction: direction ?? 'ascending',
44
+ direction: direction ?? SORT_ASCENDING,
32
45
  });
33
46
 
34
47
  this.sort();
@@ -38,7 +51,7 @@ export class SortManager {
38
51
  if (event.ctrlKey || event.metaKey) {
39
52
  this.add(field);
40
53
  } else {
41
- this.set([{field, direction: 'ascending'}]);
54
+ this.set([{field, direction: SORT_ASCENDING}]);
42
55
  }
43
56
  }
44
57
 
@@ -63,7 +76,7 @@ export class SortManager {
63
76
  return;
64
77
  }
65
78
 
66
- item.direction = item.direction === 'ascending' ? 'descending' : 'ascending';
79
+ item.direction = item.direction === SORT_ASCENDING ? SORT_DESCENDING : SORT_ASCENDING;
67
80
 
68
81
  this.sort();
69
82
  }
@@ -108,31 +121,31 @@ export class SortManager {
108
121
  const sorterItem = items[sorterIndex];
109
122
 
110
123
  setAttributes(column.elements.wrapper, {
111
- 'aria-sort':
112
- sorterItem == null ? 'none' : items.length > 1 ? 'other' : sorterItem.direction,
113
- '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,
114
127
  });
115
128
 
116
129
  setAttribute(
117
130
  column.elements.sorter,
118
- 'data-sort-position',
131
+ ATTRIBUTE_DATA_SORT_POSITION,
119
132
  sorterIndex > -1 && items.length > 1 ? sorterIndex + 1 : undefined,
120
133
  );
121
134
  }
122
135
 
123
- state.managers.data.values.keys.active =
124
- items.length === 0 ? undefined : getSortedKeys(state, items);
136
+ state.managers.data.state.keys.active =
137
+ items.length === 0 ? undefined : getSortedItems(state, items);
125
138
 
126
139
  state.managers.render.update(true, true);
127
140
  }
128
141
 
129
142
  toggle(event: MouseEvent, field: string, direction?: string | null): void {
130
143
  switch (direction) {
131
- case 'ascending':
144
+ case SORT_ASCENDING:
132
145
  this.flip(field);
133
146
  return;
134
147
 
135
- case 'descending':
148
+ case SORT_DESCENDING:
136
149
  this.removeOrClear(event, field);
137
150
  return;
138
151
 
@@ -143,43 +156,41 @@ export class SortManager {
143
156
  }
144
157
  }
145
158
 
146
- function getSortedKeys(
147
- state: State,
148
- sorters: ArrayKeySorter<PlainObject>[],
149
- ): Array<GroupComponent | Key> {
159
+ function getSortedItems(state: State, sorters: PlainObject[]): Key[] {
150
160
  const data =
151
- state.managers.data.values.keys.active?.map(key =>
152
- key instanceof GroupComponent ? key : state.managers.data.values.objects.mapped.get(key)!,
153
- ) ?? state.managers.data.values.objects.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;
154
164
 
155
165
  if (!state.managers.group.enabled) {
156
- return sort(data as PlainObject[], sorters).map(
157
- item => (item as PlainObject)[state.key] as Key,
166
+ return sort(data as PlainObject[], sorters as never).map(
167
+ item => getValue(item, state.key) as Key,
158
168
  );
159
169
  }
160
170
 
161
171
  return sortWithGroups(state, data, sorters).map(item =>
162
- item instanceof GroupComponent ? item : (item[state.key] as Key),
172
+ typeof item === 'string' ? item : (getValue(item, state.key) as Key),
163
173
  );
164
174
  }
165
175
 
166
176
  export function sortWithGroups(
167
177
  state: State,
168
- data: Array<GroupComponent | PlainObject>,
169
- sorters: ArrayKeySorter<PlainObject>[],
170
- ): Array<GroupComponent | PlainObject> {
178
+ data: DataValue[],
179
+ sorters: PlainObject[],
180
+ ): DataValue[] {
171
181
  const {length} = sorters;
172
182
 
173
183
  return data.sort((first, second) => {
174
- const firstValue =
175
- first instanceof GroupComponent
176
- ? first.value
177
- : (first as PlainObject)[state.managers.group.field];
184
+ const firstIsGroup = typeof first === 'string';
185
+ const secondIsGroup = typeof second === 'string';
186
+
187
+ const firstValue = firstIsGroup
188
+ ? state.managers.group.getForKey(first)?.value.stringified
189
+ : getValue(first, state.managers.group.field);
178
190
 
179
- const secondValue =
180
- second instanceof GroupComponent
181
- ? second.value
182
- : (second as PlainObject)[state.managers.group.field];
191
+ const secondValue = secondIsGroup
192
+ ? state.managers.group.getForKey(second)?.value.stringified
193
+ : getValue(second, state.managers.group.field);
183
194
 
184
195
  const firstOrder = state.managers.group.order[firstValue as never];
185
196
  const secondOrder = state.managers.group.order[secondValue as never];
@@ -190,9 +201,6 @@ export function sortWithGroups(
190
201
  return groupComparison;
191
202
  }
192
203
 
193
- const firstIsGroup = first instanceof GroupComponent;
194
- const secondIsGroup = second instanceof GroupComponent;
195
-
196
204
  if (firstIsGroup || secondIsGroup) {
197
205
  return firstIsGroup && secondIsGroup ? 0 : firstIsGroup ? -1 : 1;
198
206
  }
@@ -200,13 +208,20 @@ export function sortWithGroups(
200
208
  for (let index = 0; index < length; index += 1) {
201
209
  const sorter = sorters[index];
202
210
 
203
- const comparison = compare(first[sorter.key], second[sorter.key]);
211
+ const comparison = compare(
212
+ getValue(first, (sorter as any).key),
213
+ getValue(second, (sorter as any).key),
214
+ );
204
215
 
205
216
  if (comparison !== 0) {
206
- return comparison * (sorter.direction === 'ascending' ? 1 : -1);
217
+ return comparison * (sorter.direction === SORT_ASCENDING ? 1 : -1);
207
218
  }
208
219
  }
209
220
 
210
221
  return 0;
211
222
  });
212
223
  }
224
+
225
+ const SORT_NONE = 'none';
226
+
227
+ const SORT_OTHER = 'other';
@@ -0,0 +1,171 @@
1
+ import {
2
+ CSS_BUTTON,
3
+ CSS_CELL,
4
+ CSS_CELL_FOOTER,
5
+ CSS_CELL_GROUP,
6
+ CSS_HEADING,
7
+ CSS_ROW,
8
+ CSS_ROW_SELECTED,
9
+ CSS_ROWGROUP_BODY,
10
+ CSS_ROWGROUP_FOOTER,
11
+ CSS_ROWGROUP_HEADER,
12
+ CSS_SELECTION,
13
+ CSS_TABLE,
14
+ CSS_WRAPPER,
15
+ } from '../models/style.model';
16
+ import type {State} from '../models/tabela.model';
17
+
18
+ export class StyleManager {
19
+ constructor(readonly state: State) {
20
+ if (appended) {
21
+ return;
22
+ }
23
+
24
+ appended = true;
25
+
26
+ const style = document.createElement('style');
27
+
28
+ style.textContent = styling;
29
+
30
+ document.head.appendChild(style);
31
+ }
32
+ }
33
+
34
+ const styling = //css
35
+ `/** Table */
36
+
37
+ :where(.${CSS_WRAPPER}) {
38
+ flex: 1;
39
+ position: relative;
40
+ background-color: var(--oui-absolute);
41
+ border: 1px solid grey;
42
+ }
43
+
44
+ :where(.${CSS_TABLE}) {
45
+ min-height: 24em;
46
+ display: flex;
47
+ flex-flow: column nowrap;
48
+ flex: 1;
49
+ overflow: auto;
50
+ position: absolute;
51
+ inset: 0;
52
+ }
53
+
54
+ /** Row group */
55
+
56
+ :where(.${CSS_ROWGROUP_HEADER}),
57
+ :where(.${CSS_ROWGROUP_FOOTER}) {
58
+ background-color: white;
59
+ position: sticky;
60
+ left: 0;
61
+ z-index: 10;
62
+ }
63
+
64
+ :where(.${CSS_ROWGROUP_HEADER}) {
65
+ top: 0;
66
+ }
67
+
68
+ :where(.${CSS_ROWGROUP_FOOTER}) {
69
+ bottom: 0;
70
+ }
71
+
72
+ :where(.${CSS_ROWGROUP_BODY}) {
73
+ display: flex;
74
+ flex-flow: column nowrap;
75
+ flex: 1;
76
+ }
77
+
78
+ :where(.${CSS_ROWGROUP_BODY}:focus) {
79
+ outline: none;
80
+ }
81
+
82
+ :where(.${CSS_WRAPPER}:has(.${CSS_ROWGROUP_BODY}:focus-visible)) {
83
+ outline: 2px solid var(--oui-blue-6);
84
+ outline-offset: 2px;
85
+ }
86
+
87
+ /** Row */
88
+
89
+ :where(.${CSS_ROW}) {
90
+ width: 100%;
91
+ display: flex;
92
+ flex-flow: row nowrap;
93
+ }
94
+
95
+ :where(.${CSS_ROW}:last-child .${CSS_CELL}) {
96
+ border-bottom-width: 0;
97
+ }
98
+
99
+ :where(.${CSS_ROW}--body),
100
+ :where(.${CSS_ROW}--group) {
101
+ flex: 1;
102
+ position: absolute;
103
+ }
104
+
105
+ :where(.${CSS_ROW_SELECTED}) {
106
+ background-color: var(--oui-blue-1);
107
+ color: var(--oui-blue-9);
108
+ }
109
+
110
+ :where(.${CSS_WRAPPER}:has(.${CSS_ROWGROUP_BODY}:focus-visible) .${CSS_ROW}[data-active="true"]) {
111
+ outline: 2px solid var(--oui-blue-6);
112
+ outline-offset: 2px;
113
+ }
114
+
115
+ /** Cells */
116
+
117
+ :where(.${CSS_CELL}),
118
+ :where(.${CSS_HEADING}) {
119
+ padding: 0.5em;
120
+ border-color: gray;
121
+ border-style: solid;
122
+ border-width: 0 1px 1px 0;
123
+ line-height: 1;
124
+ }
125
+
126
+ :where(.${CSS_WRAPPER} .${CSS_CELL}:last-child),
127
+ :where(.${CSS_ROW} .${CSS_HEADING}:last-child) {
128
+ flex: 1;
129
+ border-right-width: 0;
130
+ }
131
+
132
+ :where(.${CSS_CELL}) {
133
+ overflow: hidden;
134
+ text-overflow: ellipsis;
135
+ white-space: nowrap;
136
+ }
137
+
138
+ :where(.${CSS_CELL_FOOTER}) {
139
+ border-top-width: 1px;
140
+ border-bottom-width: 0;
141
+ }
142
+
143
+ :where(.${CSS_CELL_GROUP}) {
144
+ padding: 0;
145
+ display: flex;
146
+ flex-flow: row nowrap;
147
+ align-items: center;
148
+ gap: 0.5em;
149
+ }
150
+
151
+ :where(.${CSS_CELL_GROUP} .${CSS_BUTTON}) {
152
+ margin: 0 0 0 .25rem;
153
+ }
154
+
155
+ /** Misc. */
156
+
157
+ :where(.${CSS_BUTTON}) {
158
+ font-size: .75rem;
159
+ font-weight: bold;
160
+ }
161
+
162
+ :where(.${CSS_SELECTION}) {
163
+ background-color: color-mix(in oklch, var(--oui-blue-6), transparent);
164
+ border: 1px solid var(--oui-blue-6);
165
+ border-radius: .25rem;
166
+ position: fixed;
167
+ z-index: 1000;
168
+ }
169
+ `.replace(/^\s+|\s+|\s+$/g, ' ');
170
+
171
+ let appended = false;
@@ -1,15 +1,11 @@
1
1
  export type Column = {
2
2
  field: string;
3
- title: string;
4
- type: TabelaColumnType;
3
+ label: string;
5
4
  width: number;
6
5
  };
7
6
 
8
7
  export type TabelaColumn = {
9
8
  field: string;
10
- title: string;
11
- type: TabelaColumnType;
9
+ label: string;
12
10
  width?: number;
13
11
  };
14
-
15
- export type TabelaColumnType = 'boolean' | 'date' | 'date-time' | 'number' | 'string' | 'time';
@@ -1,18 +1,20 @@
1
1
  import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
2
- import type {GroupComponent} from '../components/group.component';
2
+ import type {State} from './tabela.model';
3
3
 
4
- export type DataValues = {
5
- keys: DataValuesKeys;
6
- objects: DataValuesObjects;
4
+ type DataKeys = {
5
+ active?: Key[];
6
+ original: Key[];
7
7
  };
8
8
 
9
- type DataValuesKeys = {
10
- active?: Array<GroupComponent | Key>;
11
- original: Array<GroupComponent | Key>;
12
- };
9
+ export type DataState = {
10
+ keys: DataKeys;
11
+ values: DataValues;
12
+ } & State;
13
+
14
+ export type DataValue = string | PlainObject;
13
15
 
14
- type DataValuesObjects = {
15
- array: Array<GroupComponent | PlainObject>;
16
+ type DataValues = {
17
+ array: DataValue[];
16
18
  mapped: Map<Key, PlainObject>;
17
19
  };
18
20
 
@@ -0,0 +1,33 @@
1
+ export const ARIA_ACTIVEDESCENDANT = 'aria-activedescendant';
2
+
3
+ export const ARIA_LABEL = 'aria-label';
4
+
5
+ export const ARIA_SELECTED = 'aria-selected';
6
+
7
+ export const ARIA_SORT = 'aria-sort';
8
+
9
+ export const ATTRIBUTE_DATA_ACTIVE = 'data-active';
10
+
11
+ export const ATTRIBUTE_DATA_EVENT = 'data-event';
12
+
13
+ export const ATTRIBUTE_DATA_FIELD = 'data-field';
14
+
15
+ export const ATTRIBUTE_DATA_KEY = 'data-key';
16
+
17
+ export const ATTRIBUTE_DATA_SORT_DIRECTION = 'data-sort-direction';
18
+
19
+ export const ATTRIBUTE_DATA_SORT_POSITION = 'data-sort-position';
20
+
21
+ export const ATTRIBUTE_ROLE = 'role';
22
+
23
+ export const ELEMENT_DIV = 'div';
24
+
25
+ export const ROLE_CELL = 'cell';
26
+
27
+ export const ROLE_COLUMNHEADER = 'columnheader';
28
+
29
+ export const ROLE_ROW = 'row';
30
+
31
+ export const ROLE_ROWGROUP = 'rowgroup';
32
+
33
+ export const ROLE_TABLE = 'table';
@@ -0,0 +1,7 @@
1
+ export const EVENT_BODY = 'body';
2
+
3
+ export const EVENT_GROUP = 'group';
4
+
5
+ export const EVENT_HEADING = 'heading';
6
+
7
+ export const EVENT_ROW = 'row';
@@ -23,3 +23,23 @@ export type TabelaFilterItem = {
23
23
  field: string;
24
24
  value: unknown;
25
25
  };
26
+
27
+ export const FILTER_CONTAINS: TabelaFilterComparison = 'contains';
28
+
29
+ export const FILTER_ENDS_WITH: TabelaFilterComparison = 'ends-with';
30
+
31
+ export const FILTER_EQUALS: TabelaFilterComparison = 'equals';
32
+
33
+ export const FILTER_GREATER_THAN: TabelaFilterComparison = 'greater-than';
34
+
35
+ export const FILTER_GREATER_THAN_OR_EQUAL: TabelaFilterComparison = 'greater-than-or-equal';
36
+
37
+ export const FILTER_LESS_THAN: TabelaFilterComparison = 'less-than';
38
+
39
+ export const FILTER_LESS_THAN_OR_EQUAL: TabelaFilterComparison = 'less-than-or-equal';
40
+
41
+ export const FILTER_NOT_CONTAINS: TabelaFilterComparison = 'not-contains';
42
+
43
+ export const FILTER_NOT_EQUALS: TabelaFilterComparison = 'not-equals';
44
+
45
+ export const FILTER_STARTS_WITH: TabelaFilterComparison = 'starts-with';
@@ -1,4 +1,12 @@
1
+ export type GroupValue = {
2
+ original: unknown;
3
+ stringified: string;
4
+ };
5
+
1
6
  export type TabelaGroup = {
2
- label: string;
3
- value: unknown;
7
+ set(group?: string): void;
4
8
  };
9
+
10
+ export const GROUP_KEY_EXPRESSION = /^group:(.+)$/;
11
+
12
+ export const GROUP_KEY_PREFIX = 'group:';
@@ -12,3 +12,7 @@ export type TabelaSortItem = {
12
12
  direction: TabelaSortDirection;
13
13
  field: string;
14
14
  };
15
+
16
+ export const SORT_ASCENDING: TabelaSortDirection = 'ascending';
17
+
18
+ export const SORT_DESCENDING: TabelaSortDirection = 'descending';