@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
@@ -1,5 +1,26 @@
1
+ import {getString} from '@oscarpalmer/atoms/string';
1
2
  import {createElement} from '../helpers/dom.helpers';
3
+ import {GROUP_KEY_PREFIX, type GroupValue} from '../models/group.model';
4
+ import {
5
+ CSS_BUTTON,
6
+ CSS_BUTTON_GROUP,
7
+ CSS_CELL,
8
+ CSS_CELL_GROUP,
9
+ CSS_GROUP_SELECTED,
10
+ CSS_GROUP_TOTAL,
11
+ CSS_ROW,
12
+ CSS_ROW_GROUP,
13
+ } from '../models/style.model';
2
14
  import type {State} from '../models/tabela.model';
15
+ import {
16
+ ATTRIBUTE_DATA_EVENT,
17
+ ATTRIBUTE_DATA_KEY,
18
+ ATTRIBUTE_ROLE,
19
+ ELEMENT_DIV,
20
+ ROLE_CELL,
21
+ ROLE_ROW,
22
+ } from '../models/dom.model';
23
+ import {EVENT_GROUP} from '../models/event.model';
3
24
 
4
25
  export class GroupComponent {
5
26
  element: HTMLElement | undefined;
@@ -8,30 +29,54 @@ export class GroupComponent {
8
29
 
9
30
  filtered = 0;
10
31
 
32
+ readonly key: string;
33
+
11
34
  selected = 0;
12
35
 
13
36
  total = 0;
14
37
 
38
+ readonly value: GroupValue;
39
+
15
40
  constructor(
16
- readonly key: string,
17
41
  readonly label: string,
18
- readonly value: unknown,
19
- ) {}
42
+ value: unknown,
43
+ ) {
44
+ const stringified = getString(value);
45
+
46
+ this.key = `${GROUP_KEY_PREFIX}${stringified}`;
47
+
48
+ this.value = {
49
+ stringified,
50
+ original: value,
51
+ };
52
+ }
53
+ }
54
+
55
+ export function removeGroup(group: GroupComponent): void {
56
+ if (group.element == null) {
57
+ return;
58
+ }
59
+
60
+ group.element.innerHTML = '';
61
+
62
+ group.element.remove();
20
63
  }
21
64
 
22
65
  export function renderGroup(state: State, component: GroupComponent): void {
23
66
  component.element ??= createElement(
24
- 'div',
67
+ ELEMENT_DIV,
25
68
  {
26
- className: 'tabela__row tabela__row--group',
27
- innerHTML: `<div class="tabela__cell tabela__cell--group" role="cell">
28
- <button class="tabela__button tabela__button--group" data-event="group" data-key="${component.key}" type="button">
69
+ className: `${CSS_ROW} ${CSS_ROW_GROUP}`,
70
+ innerHTML: `<div class="${CSS_CELL} ${CSS_CELL_GROUP}" role="${ROLE_CELL}">
71
+ <button class="${CSS_BUTTON} ${CSS_BUTTON_GROUP}" ${ATTRIBUTE_DATA_EVENT}="${EVENT_GROUP}" ${ATTRIBUTE_DATA_KEY}="${state.prefix}_${component.key}" type="button">
29
72
  <span aria-hidden="true"></span>
30
73
  <span>Open/close</span>
31
74
  </button>
32
75
  <p>${component.label}</p>
76
+ <span class="${CSS_GROUP_TOTAL}">${component.total}</span>
77
+ <span class="${CSS_GROUP_SELECTED}">${component.selected === 0 ? '' : component.selected}</span>
33
78
  </div>`,
34
- role: 'row',
79
+ [ATTRIBUTE_ROLE]: ROLE_ROW,
35
80
  },
36
81
  {},
37
82
  {
@@ -40,4 +85,23 @@ export function renderGroup(state: State, component: GroupComponent): void {
40
85
  );
41
86
  }
42
87
 
43
- export function updateGroup(state: State, component: GroupComponent): void {}
88
+ export function updateGroup(state: State, component: GroupComponent): void {
89
+ if (component.element == null) {
90
+ return;
91
+ }
92
+
93
+ const selected = component.element.querySelector<HTMLSpanElement>(selectedSelector);
94
+ const total = component.element.querySelector<HTMLSpanElement>(totalSelector);
95
+
96
+ if (selected != null) {
97
+ selected.textContent = component.selected === 0 ? '' : String(component.selected);
98
+ }
99
+
100
+ if (total != null) {
101
+ total.textContent = String(component.total);
102
+ }
103
+ }
104
+
105
+ const selectedSelector = `.${CSS_GROUP_SELECTED}`;
106
+
107
+ const totalSelector = `.${CSS_GROUP_TOTAL}`;
@@ -1,17 +1,19 @@
1
1
  import {createRowGroup} from '../helpers/dom.helpers';
2
2
  import type {HeaderElements} from '../models/header.model';
3
+ import {CSS_ROW_HEADER, CSS_ROWGROUP_HEADER} from '../models/style.model';
4
+ import type {State} from '../models/tabela.model';
3
5
  import type {ColumnComponent} from './column.component';
4
6
 
5
7
  export class HeaderComponent {
6
8
  readonly elements: HeaderElements;
7
9
 
8
- constructor() {
9
- const {group, row} = createRowGroup();
10
+ constructor(state: State) {
11
+ const {group, row} = createRowGroup(state.options.rowHeight);
10
12
 
11
13
  this.elements = {group, row};
12
14
 
13
- group.className += ' tabela__rowgroup--header';
14
- row.className += ' tabela__row--header';
15
+ group.className += ` ${CSS_ROWGROUP_HEADER}`;
16
+ row.className += ` ${CSS_ROW_HEADER}`;
15
17
  }
16
18
 
17
19
  destroy(): void {
@@ -1,8 +1,17 @@
1
1
  import type {Key} from '@oscarpalmer/atoms/models';
2
+ import {getValue} from '@oscarpalmer/atoms/value/handle';
2
3
  import {setAttributes} from '@oscarpalmer/toretto/attribute';
3
4
  import {createCell, createRow} from '../helpers/dom.helpers';
4
5
  import type {RenderElementPool} from '../models/render.model';
6
+ import {CSS_ROW_BODY, CSS_ROW_SELECTED} from '../models/style.model';
5
7
  import type {State} from '../models/tabela.model';
8
+ import {
9
+ ARIA_SELECTED,
10
+ ATTRIBUTE_DATA_ACTIVE,
11
+ ATTRIBUTE_DATA_EVENT,
12
+ ATTRIBUTE_DATA_KEY,
13
+ } from '../models/dom.model';
14
+ import {EVENT_ROW} from '../models/event.model';
6
15
 
7
16
  export function removeRow(pool: RenderElementPool, row: RowComponent): void {
8
17
  if (row.element != null) {
@@ -18,36 +27,38 @@ export function removeRow(pool: RenderElementPool, row: RowComponent): void {
18
27
  }
19
28
 
20
29
  export function renderRow(state: State, row: RowComponent): void {
21
- const element = row.element ?? state.managers.render.pool.rows.shift() ?? createRow();
30
+ const {managers, options, prefix} = state;
31
+
32
+ const element = row.element ?? managers.render.pool.rows.shift() ?? createRow(options.rowHeight);
22
33
 
23
34
  row.element = element;
24
35
 
25
36
  element.innerHTML = '';
26
37
 
27
- const selected = state.managers.selection.items.has(row.key);
38
+ const selected = managers.selection.items.has(row.key);
28
39
 
29
40
  const key = String(row.key);
30
41
 
31
42
  setAttributes(element, {
32
- 'aria-selected': String(selected),
33
- 'data-active': String(state.managers.navigation.active === row.key),
34
- 'data-event': 'row',
35
- 'data-key': key,
36
- id: `tabela_${state.id}_row_${key}`,
43
+ [ARIA_SELECTED]: String(selected),
44
+ [ATTRIBUTE_DATA_ACTIVE]: String(managers.navigation.active === row.key),
45
+ [ATTRIBUTE_DATA_EVENT]: EVENT_ROW,
46
+ [ATTRIBUTE_DATA_KEY]: key,
47
+ id: `${prefix}${key}`,
37
48
  });
38
49
 
39
- element.classList.add('tabela__row--body');
50
+ element.classList.add(CSS_ROW_BODY);
40
51
 
41
52
  if (selected) {
42
- element.classList.add('tabela__row--selected');
53
+ element.classList.add(CSS_ROW_SELECTED);
43
54
  } else {
44
- element.classList.remove('tabela__row--selected');
55
+ element.classList.remove(CSS_ROW_SELECTED);
45
56
  }
46
57
 
47
- const columns = state.managers.column.items;
58
+ const columns = managers.column.items;
48
59
  const {length} = columns;
49
60
 
50
- const data = state.managers.data.values.objects.mapped.get(row.key);
61
+ const data = managers.data.state.values.mapped.get(row.key);
51
62
 
52
63
  if (data == null) {
53
64
  return;
@@ -55,16 +66,15 @@ export function renderRow(state: State, row: RowComponent): void {
55
66
 
56
67
  for (let index = 0; index < length; index += 1) {
57
68
  const {options} = columns[index];
69
+ const {field, width} = options;
58
70
 
59
- state.managers.render.pool.cells[options.field] ??= [];
71
+ managers.render.pool.cells[field] ??= [];
60
72
 
61
- const cell =
62
- state.managers.render.pool.cells[columns[index].options.field].shift() ??
63
- createCell(options.width);
73
+ const cell = managers.render.pool.cells[field].shift() ?? createCell(width);
64
74
 
65
- cell.textContent = String(data[options.field]);
75
+ cell.textContent = String(getValue(data, field));
66
76
 
67
- row.cells[options.field] = cell;
77
+ row.cells[field] = cell;
68
78
 
69
79
  element.append(cell);
70
80
  }
@@ -1,5 +1,7 @@
1
1
  import {setAttributes} from '@oscarpalmer/toretto/attribute';
2
2
  import {setStyles} from '@oscarpalmer/toretto/style';
3
+ import {ELEMENT_DIV, ROLE_CELL, ROLE_ROW, ROLE_ROWGROUP} from '../models/dom.model';
4
+ import {CSS_CELL, CSS_CELL_BODY, CSS_ROW, CSS_ROWGROUP} from '../models/style.model';
3
5
 
4
6
  type RowGroupWithRow = {
5
7
  group: HTMLDivElement;
@@ -8,10 +10,10 @@ type RowGroupWithRow = {
8
10
 
9
11
  export function createCell(width: number, body?: boolean): HTMLDivElement {
10
12
  const cell = createElement(
11
- 'div',
13
+ ELEMENT_DIV,
12
14
  {
13
- className: 'tabela__cell',
14
- role: 'cell',
15
+ className: CSS_CELL,
16
+ role: ROLE_CELL,
15
17
  },
16
18
  {},
17
19
  {
@@ -20,7 +22,7 @@ export function createCell(width: number, body?: boolean): HTMLDivElement {
20
22
  );
21
23
 
22
24
  if (body ?? true) {
23
- cell.classList.add('tabela__cell--body');
25
+ cell.classList.add(CSS_CELL_BODY);
24
26
  }
25
27
 
26
28
  return cell;
@@ -28,60 +30,56 @@ export function createCell(width: number, body?: boolean): HTMLDivElement {
28
30
 
29
31
  export function createElement<TagName extends keyof HTMLElementTagNameMap>(
30
32
  tagName: TagName,
31
- properties: Partial<HTMLElementTagNameMap[TagName]>,
32
- attributes: Record<string, string>,
33
- style: Partial<CSSStyleDeclaration>,
33
+ properties?: Partial<HTMLElementTagNameMap[TagName]>,
34
+ attributes?: Record<string, string>,
35
+ style?: Partial<CSSStyleDeclaration>,
34
36
  ): HTMLElementTagNameMap[TagName] {
35
37
  const element = document.createElement(tagName);
36
38
 
37
- const keys = Object.keys(properties);
39
+ const props = properties ?? {};
40
+ const keys = Object.keys(props);
38
41
 
39
42
  for (const key of keys) {
40
- (element as any)[key] = properties[key as keyof typeof properties];
43
+ (element as any)[key] = props[key as keyof typeof props];
41
44
  }
42
45
 
43
- setAttributes(element, attributes);
44
- setStyles(element, style);
46
+ setAttributes(element, attributes ?? {});
47
+ setStyles(element, style ?? {});
45
48
 
46
49
  return element;
47
50
  }
48
51
 
49
- export function createRowGroup(): RowGroupWithRow;
52
+ export function createRowGroup(height: number): RowGroupWithRow;
50
53
 
51
- export function createRowGroup(withRow: boolean): HTMLDivElement;
54
+ export function createRowGroup(height: number, withRow: boolean): HTMLDivElement;
52
55
 
53
- export function createRowGroup(withRow?: boolean) {
54
- const group = createElement(
55
- 'div',
56
- {
57
- className: 'tabela__rowgroup',
58
- role: 'rowgroup',
59
- },
60
- {},
61
- {},
62
- );
56
+ export function createRowGroup(height: number, withRow?: boolean) {
57
+ const group = createElement(ELEMENT_DIV, {
58
+ className: CSS_ROWGROUP,
59
+ role: ROLE_ROWGROUP,
60
+ });
63
61
 
64
62
  if (!(withRow ?? true)) {
65
63
  return group;
66
64
  }
67
65
 
68
- const row = createRow();
66
+ const row = createRow(height);
69
67
 
70
68
  group.append(row);
71
69
 
72
70
  return {group, row};
73
71
  }
74
72
 
75
- export function createRow(): HTMLDivElement {
73
+ export function createRow(height: number): HTMLDivElement {
76
74
  const row = createElement(
77
- 'div',
75
+ ELEMENT_DIV,
78
76
  {
79
- className: 'tabela__row',
80
- role: 'row',
77
+ className: CSS_ROW,
78
+ role: ROLE_ROW,
81
79
  },
82
80
  {},
83
81
  {
84
- height: '32px',
82
+ height: `${height}px`,
85
83
  },
86
84
  );
87
85
 
@@ -1,4 +1,5 @@
1
1
  import type {Key} from '@oscarpalmer/atoms/models';
2
+ import {GROUP_KEY_EXPRESSION} from '../models/group.model';
2
3
 
3
4
  export function getKey(value: unknown): Key | undefined {
4
5
  if (typeof value === 'number') {
@@ -12,4 +13,8 @@ export function getKey(value: unknown): Key | undefined {
12
13
  return integerExpression.test(value) ? Number.parseInt(value, 10) : value;
13
14
  }
14
15
 
16
+ export function isGroupKey(key: unknown): boolean {
17
+ return typeof key === 'string' && GROUP_KEY_EXPRESSION.test(key);
18
+ }
19
+
15
20
  const integerExpression = /^\d+$/;
@@ -1,6 +1,6 @@
1
1
  import {toggleStyles} from '@oscarpalmer/toretto/style';
2
2
 
3
- export const dragStyling = toggleStyles(document.body, {
3
+ export const preventSelection = toggleStyles(document.body, {
4
4
  userSelect: 'none',
5
5
  webkitUserSelect: 'none',
6
6
  });
@@ -20,6 +20,10 @@ export class ColumnManager {
20
20
  this.state = undefined as never;
21
21
  }
22
22
 
23
+ get(field: string): ColumnComponent | undefined {
24
+ return this.items.find(item => item.options.field === field);
25
+ }
26
+
23
27
  remove(field: string): void;
24
28
 
25
29
  remove(fields: string[]): void;