@danielgindi/dgtable.js 2.0.6 → 2.0.8

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 (67) hide show
  1. package/README.md +547 -282
  2. package/dist/SelectionHelper.d.ts +24 -0
  3. package/dist/SelectionHelper.d.ts.map +1 -0
  4. package/dist/by_column_filter.d.ts +14 -0
  5. package/dist/by_column_filter.d.ts.map +1 -0
  6. package/dist/cell_preview.d.ts +28 -0
  7. package/dist/cell_preview.d.ts.map +1 -0
  8. package/dist/column_collection.d.ts +41 -0
  9. package/dist/column_collection.d.ts.map +1 -0
  10. package/dist/column_resize.d.ts +25 -0
  11. package/dist/column_resize.d.ts.map +1 -0
  12. package/dist/constants.d.ts +19 -0
  13. package/dist/constants.d.ts.map +1 -0
  14. package/dist/header_events.d.ts +63 -0
  15. package/dist/header_events.d.ts.map +1 -0
  16. package/dist/helpers.d.ts +50 -0
  17. package/dist/helpers.d.ts.map +1 -0
  18. package/dist/index.d.ts +166 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/internal.d.ts +56 -0
  21. package/dist/internal.d.ts.map +1 -0
  22. package/dist/lib.cjs.js +6906 -3933
  23. package/dist/lib.cjs.js.map +1 -1
  24. package/dist/lib.cjs.min.js +2 -2
  25. package/dist/lib.cjs.min.js.map +1 -1
  26. package/dist/lib.es6.js +6908 -3935
  27. package/dist/lib.es6.js.map +1 -1
  28. package/dist/lib.es6.min.js +2 -2
  29. package/dist/lib.es6.min.js.map +1 -1
  30. package/dist/lib.umd.js +9251 -4353
  31. package/dist/lib.umd.js.map +1 -1
  32. package/dist/lib.umd.min.js +2 -2
  33. package/dist/lib.umd.min.js.map +1 -1
  34. package/dist/private_types.d.ts +145 -0
  35. package/dist/private_types.d.ts.map +1 -0
  36. package/dist/rendering.d.ts +57 -0
  37. package/dist/rendering.d.ts.map +1 -0
  38. package/dist/row_collection.d.ts +38 -0
  39. package/dist/row_collection.d.ts.map +1 -0
  40. package/dist/types.d.ts +221 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/util.d.ts +9 -0
  43. package/dist/util.d.ts.map +1 -0
  44. package/eslint.config.mjs +1 -0
  45. package/package.json +17 -12
  46. package/src/SelectionHelper.ts +90 -0
  47. package/src/by_column_filter.ts +36 -0
  48. package/src/cell_preview.ts +325 -0
  49. package/src/column_collection.ts +131 -0
  50. package/src/column_resize.ts +363 -0
  51. package/src/constants.ts +22 -0
  52. package/src/header_events.ts +369 -0
  53. package/src/helpers.ts +291 -0
  54. package/src/index.ts +1628 -0
  55. package/src/internal.ts +263 -0
  56. package/src/private_types.ts +156 -0
  57. package/src/rendering.ts +771 -0
  58. package/src/row_collection.ts +197 -0
  59. package/src/types.ts +265 -0
  60. package/src/util.ts +27 -0
  61. package/tsconfig.json +38 -0
  62. package/src/SelectionHelper.js +0 -65
  63. package/src/by_column_filter.js +0 -25
  64. package/src/column_collection.js +0 -153
  65. package/src/index.js +0 -3999
  66. package/src/row_collection.js +0 -183
  67. package/src/util.js +0 -17
@@ -0,0 +1,197 @@
1
+ import type { RowData, ComparatorFunction, OnComparatorRequired, CustomSortingProvider } from './types';
2
+
3
+ // Private types
4
+ import {
5
+ OriginalRowIndex,
6
+ SortColumn,
7
+ } from './private_types';
8
+
9
+ /**
10
+ * Options for RowCollection initialization
11
+ */
12
+ interface RowCollectionOptions {
13
+ sortColumn?: SortColumn[];
14
+ onComparatorRequired?: OnComparatorRequired | null;
15
+ customSortingProvider?: CustomSortingProvider | null;
16
+ }
17
+
18
+ /**
19
+ * A collection of rows that extends Array functionality with sorting and filtering
20
+ */
21
+ class RowCollection extends Array<RowData> {
22
+ sortColumn: SortColumn[];
23
+ onComparatorRequired: OnComparatorRequired | null = null;
24
+ customSortingProvider: CustomSortingProvider | null = null;
25
+
26
+ constructor(options?: RowCollectionOptions) {
27
+ super();
28
+ options = options || {};
29
+ this.sortColumn = options.sortColumn ?? [];
30
+ this.onComparatorRequired = options.onComparatorRequired ?? null;
31
+ this.customSortingProvider = options.customSortingProvider ?? null;
32
+ }
33
+
34
+ /**
35
+ * Add a row or array of rows to this collection
36
+ */
37
+ add(rows: RowData | RowData[], at?: number): void {
38
+ const isArray = Array.isArray(rows);
39
+ if (isArray) {
40
+ const rowArray = rows as RowData[];
41
+ if (typeof at === 'number') {
42
+ for (let i = 0, len = rowArray.length; i < len; i++) {
43
+ this.splice(at++, 0, rowArray[i]);
44
+ }
45
+ } else {
46
+ for (let i = 0, len = rowArray.length; i < len; i++) {
47
+ this.push(rowArray[i]);
48
+ }
49
+ }
50
+ } else {
51
+ const row = rows as RowData;
52
+ if (typeof at === 'number') {
53
+ this.splice(at, 0, row);
54
+ } else {
55
+ this.push(row);
56
+ }
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Reset the collection with optional new rows
62
+ */
63
+ reset(rows?: RowData | RowData[]): void {
64
+ this.length = 0;
65
+ if (rows) {
66
+ this.add(rows);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Create a filtered collection based on a filter function
72
+ */
73
+ filteredCollection(
74
+ filterFunc: (row: RowData, args: unknown) => boolean,
75
+ args: unknown
76
+ ): RowCollection | null {
77
+ if (filterFunc && args) {
78
+ const rows = new RowCollection({
79
+ sortColumn: this.sortColumn,
80
+ onComparatorRequired: this.onComparatorRequired,
81
+ customSortingProvider: this.customSortingProvider,
82
+ });
83
+
84
+ for (let i = 0, len = this.length; i < len; i++) {
85
+ const row = this[i];
86
+ if (filterFunc(row, args)) {
87
+ (row as any)[OriginalRowIndex] = i;
88
+ rows.push(row);
89
+ }
90
+ }
91
+ return rows;
92
+ } else {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Sort the collection based on the current sort columns
99
+ * @returns the comparator function used, if any
100
+ */
101
+ sort(compareFn?: (a: RowData, b: RowData) => number): this {
102
+ let comparator: ComparatorFunction | undefined;
103
+
104
+ // If a compare function is passed directly (from Array.sort), use native
105
+ if (typeof compareFn === 'function') {
106
+ return super.sort(compareFn);
107
+ }
108
+
109
+ if (this.sortColumn.length) {
110
+ const comparators: ComparatorFunction[] = [];
111
+
112
+ for (let i = 0; i < this.sortColumn.length; i++) {
113
+ const defaultComparator = getDefaultComparator(this.sortColumn[i], this.sortColumn[i].descending);
114
+ let comp: ComparatorFunction | null = null;
115
+ if (this.onComparatorRequired) {
116
+ comp = this.onComparatorRequired(
117
+ this.sortColumn[i].column,
118
+ this.sortColumn[i].descending,
119
+ defaultComparator
120
+ );
121
+ }
122
+ if (!comp) {
123
+ comp = defaultComparator;
124
+ }
125
+ comparators.push(comp.bind(this));
126
+ }
127
+
128
+ if (comparators.length === 1) {
129
+ comparator = comparators[0];
130
+ } else {
131
+ const len = comparators.length;
132
+ comparator = (leftRow: RowData, rightRow: RowData): number => {
133
+ let value = 0;
134
+ for (let i = 0; i < len; i++) {
135
+ value = comparators[i](leftRow, rightRow);
136
+ if (value !== 0) {
137
+ return value;
138
+ }
139
+ }
140
+ return value;
141
+ };
142
+ }
143
+
144
+ const sorter = (data: RowData[]): RowData[] => {
145
+ data.sort(comparator);
146
+ return data;
147
+ };
148
+
149
+ if (this.customSortingProvider) {
150
+ const results = this.customSortingProvider(this as unknown as RowData[], sorter);
151
+ if (results !== (this as unknown as RowData[])) {
152
+ this.splice(0, this.length, ...results);
153
+ }
154
+ } else {
155
+ sorter(this as unknown as RowData[]);
156
+ }
157
+ }
158
+
159
+ return this;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get a default comparator for a sort column
165
+ */
166
+ function getDefaultComparator(column: SortColumn, descending: boolean): ComparatorFunction {
167
+ let comparePath = column.comparePath;
168
+ if (typeof comparePath === 'string') {
169
+ comparePath = (comparePath as unknown as string).split('.');
170
+ }
171
+ const pathLength = comparePath.length;
172
+ const hasPath = pathLength > 1;
173
+
174
+ const lessVal = descending ? 1 : -1;
175
+ const moreVal = descending ? -1 : 1;
176
+
177
+ return function (leftRow: RowData, rightRow: RowData): number {
178
+ let leftVal: unknown = leftRow[comparePath[0]];
179
+ let rightVal: unknown = rightRow[comparePath[0]];
180
+
181
+ if (hasPath) {
182
+ for (let i = 1; i < pathLength; i++) {
183
+ leftVal = leftVal && (leftVal as Record<string, unknown>)[comparePath[i]];
184
+ rightVal = rightVal && (rightVal as Record<string, unknown>)[comparePath[i]];
185
+ }
186
+ }
187
+
188
+ if (leftVal === rightVal) return 0;
189
+ if (leftVal == null) return lessVal;
190
+ if (rightVal == null) return moreVal;
191
+ if (leftVal < rightVal) return lessVal;
192
+ return moreVal;
193
+ };
194
+ }
195
+
196
+ export default RowCollection;
197
+
package/src/types.ts ADDED
@@ -0,0 +1,265 @@
1
+ import type { WidthType } from './constants';
2
+
3
+ /**
4
+ * Column sort specification
5
+ */
6
+ export interface ColumnSortOptions {
7
+ column: string;
8
+ descending?: boolean;
9
+ }
10
+
11
+ /**
12
+ * Serialized column sort for external use
13
+ */
14
+ export interface SerializedColumnSort {
15
+ column: string;
16
+ descending: boolean;
17
+ }
18
+
19
+ /**
20
+ * Serialized column configuration
21
+ */
22
+ export interface SerializedColumn {
23
+ order?: number | null;
24
+ width?: string | number | null;
25
+ visible?: boolean | null;
26
+ label?: string;
27
+ }
28
+
29
+ /**
30
+ * Column definition options
31
+ */
32
+ export interface ColumnOptions {
33
+ name: string;
34
+ label?: string | null;
35
+ width?: number | string | null;
36
+ dataPath?: string | string[] | null;
37
+ comparePath?: string | string[] | null;
38
+ resizable?: boolean | null;
39
+ movable?: boolean | null;
40
+ sortable?: boolean | null;
41
+ visible?: boolean | null;
42
+ cellClasses?: string | null;
43
+ ignoreMin?: boolean | null;
44
+ sticky?: 'start' | 'end' | false | null;
45
+ order?: number;
46
+ }
47
+
48
+ /**
49
+ * Row data type - can be any object with string keys
50
+ */
51
+ export type RowData = Record<string, unknown>
52
+
53
+ /**
54
+ * Cell formatter function
55
+ */
56
+ export type CellFormatter = ((value: unknown, columnName: string, rowData: RowData) => string) & {
57
+ [key: symbol]: boolean;
58
+ };
59
+
60
+ /**
61
+ * Header cell formatter function
62
+ */
63
+ export type HeaderCellFormatter = (label: string, columnName: string) => string;
64
+
65
+ /**
66
+ * Filter function
67
+ */
68
+ export type FilterFunction = (row: RowData, args: unknown) => boolean;
69
+
70
+ /**
71
+ * Comparator function
72
+ */
73
+ export type ComparatorFunction = (a: RowData, b: RowData) => number;
74
+
75
+ /**
76
+ * Comparator callback
77
+ */
78
+ export type OnComparatorRequired = (
79
+ columnName: string,
80
+ descending: boolean,
81
+ defaultComparator: ComparatorFunction
82
+ ) => ComparatorFunction;
83
+
84
+ /**
85
+ * Custom sorting provider
86
+ */
87
+ export type CustomSortingProvider = (
88
+ data: RowData[],
89
+ sort: (data: RowData[]) => RowData[]
90
+ ) => RowData[];
91
+
92
+ /**
93
+ * DGTable initialization options
94
+ */
95
+ export interface DGTableOptions {
96
+ el?: Element | null;
97
+ className?: string | null;
98
+ columns?: ColumnOptions[];
99
+ height?: number;
100
+ width?: WidthType;
101
+ virtualTable?: boolean | null;
102
+ estimatedRowHeight?: number | null;
103
+ resizableColumns?: boolean | null;
104
+ movableColumns?: boolean | null;
105
+ sortableColumns?: number | null;
106
+ adjustColumnWidthForSortArrow?: boolean | null;
107
+ relativeWidthGrowsToFillWidth?: boolean | null;
108
+ relativeWidthShrinksToFillWidth?: boolean | null;
109
+ convertColumnWidthsToRelative?: boolean | null;
110
+ autoFillTableWidth?: boolean | null;
111
+ allowCancelSort?: boolean | null;
112
+ cellClasses?: string | null;
113
+ sortColumn?: string | string[] | ColumnSortOptions | ColumnSortOptions[];
114
+ cellFormatter?: CellFormatter | null;
115
+ headerCellFormatter?: HeaderCellFormatter | null;
116
+ rowsBufferSize?: number | null;
117
+ minColumnWidth?: number | null;
118
+ resizeAreaWidth?: number | null;
119
+ onComparatorRequired?: OnComparatorRequired | null;
120
+ comparatorCallback?: OnComparatorRequired | null; // deprecated
121
+ customSortingProvider?: CustomSortingProvider | null;
122
+ resizerClassName?: string | null;
123
+ tableClassName?: string | null;
124
+ allowCellPreview?: boolean | null;
125
+ allowHeaderCellPreview?: boolean | null;
126
+ cellPreviewClassName?: string | null;
127
+ cellPreviewAutoBackground?: boolean | null;
128
+ filter?: FilterFunction | null;
129
+ }
130
+
131
+ // ============================================================================
132
+ // Event Types
133
+ // ============================================================================
134
+
135
+ /**
136
+ * Event data for 'rowcreate' event
137
+ */
138
+ export interface RowCreateEvent {
139
+ filteredRowIndex: number;
140
+ rowIndex: number;
141
+ rowEl: HTMLElement;
142
+ rowData: RowData;
143
+ }
144
+
145
+ /**
146
+ * Event data for 'rowclick' event
147
+ */
148
+ export interface RowClickEvent {
149
+ event: MouseEvent;
150
+ filteredRowIndex: number;
151
+ rowIndex: number;
152
+ rowEl: HTMLElement;
153
+ rowData: RowData;
154
+ }
155
+
156
+ /**
157
+ * Event data for 'cellpreview' event
158
+ */
159
+ export interface CellPreviewEvent {
160
+ el: Element | null;
161
+ name: string;
162
+ rowIndex: number | null;
163
+ rowData: RowData | null;
164
+ cell: HTMLElement;
165
+ cellEl: HTMLElement;
166
+ }
167
+
168
+ /**
169
+ * Event data for 'cellpreviewdestroy' event
170
+ */
171
+ export interface CellPreviewDestroyEvent {
172
+ el: ChildNode | null;
173
+ name: string;
174
+ rowIndex: number | null;
175
+ rowData: RowData | null;
176
+ cell: HTMLElement | null;
177
+ cellEl: ChildNode | null;
178
+ }
179
+
180
+ /**
181
+ * Event data for 'headercontextmenu' event
182
+ */
183
+ export interface HeaderContextMenuEvent {
184
+ columnName: string;
185
+ pageX: number;
186
+ pageY: number;
187
+ bounds: {
188
+ left: number;
189
+ top: number;
190
+ width: number;
191
+ height: number;
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Event data for 'movecolumn' event
197
+ */
198
+ export interface MoveColumnEvent {
199
+ name: string;
200
+ src: number;
201
+ dest: number;
202
+ }
203
+
204
+ /**
205
+ * Event data for 'columnwidth' event
206
+ */
207
+ export interface ColumnWidthEvent {
208
+ name: string;
209
+ width: number;
210
+ oldWidth: number;
211
+ }
212
+
213
+ /**
214
+ * Event data for 'addrows' event
215
+ */
216
+ export interface AddRowsEvent {
217
+ count: number;
218
+ clear: boolean;
219
+ }
220
+
221
+ /**
222
+ * Event data for 'sort' event
223
+ */
224
+ export interface SortEvent {
225
+ sorts: SerializedColumnSort[];
226
+ resort?: boolean;
227
+ }
228
+
229
+ /**
230
+ * Map of all DGTable events to their data types.
231
+ * Used for type-safe event handlers with autocompletion.
232
+ */
233
+ export interface DGTableEventMap {
234
+ // Rendering events
235
+ 'render': undefined;
236
+ 'renderskeleton': undefined;
237
+
238
+ // Row events
239
+ 'rowcreate': RowCreateEvent;
240
+ 'rowclick': RowClickEvent;
241
+ 'rowdestroy': HTMLElement;
242
+
243
+ // Cell preview events
244
+ 'cellpreview': CellPreviewEvent;
245
+ 'cellpreviewdestroy': CellPreviewDestroyEvent;
246
+
247
+ // Header events
248
+ 'headerrowcreate': HTMLElement;
249
+ 'headercontextmenu': HeaderContextMenuEvent;
250
+
251
+ // Column events
252
+ 'addcolumn': string;
253
+ 'removecolumn': string;
254
+ 'movecolumn': MoveColumnEvent;
255
+ 'showcolumn': string;
256
+ 'hidecolumn': string;
257
+ 'columnwidth': ColumnWidthEvent;
258
+
259
+ // Data events
260
+ 'addrows': AddRowsEvent;
261
+ 'sort': SortEvent;
262
+ 'filter': unknown;
263
+ 'filterclear': Record<string, never>;
264
+ }
265
+
package/src/util.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Find an element in an array using a predicate function
3
+ */
4
+ export function find<T>(
5
+ array: T[],
6
+ predicate: (item: T, index: number, array: T[]) => boolean
7
+ ): T | undefined {
8
+ for (let i = 0, len = array.length; i >= 0 && i < len; i += 1) {
9
+ if (predicate(array[i], i, array))
10
+ return array[i];
11
+ }
12
+ return undefined;
13
+ }
14
+
15
+ /**
16
+ * Encode text for safe HTML display
17
+ */
18
+ export function htmlEncode(text: string): string {
19
+ return text
20
+ .replace(/&/g, '&amp;')
21
+ .replace(/</g, '&lt;')
22
+ .replace(/>/g, '&gt;')
23
+ .replace(/'/g, '&#39;')
24
+ .replace(/"/g, '&quot;')
25
+ .replace(/\n/g, '<br />');
26
+ }
27
+
package/tsconfig.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "noImplicitAny": true,
9
+ "strictNullChecks": false,
10
+ "strictFunctionTypes": true,
11
+ "strictBindCallApply": true,
12
+ "strictPropertyInitialization": false,
13
+ "noImplicitThis": true,
14
+ "useUnknownInCatchVariables": false,
15
+ "alwaysStrict": true,
16
+ "noUnusedLocals": false,
17
+ "noUnusedParameters": false,
18
+ "noImplicitReturns": false,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "noUncheckedIndexedAccess": false,
21
+ "esModuleInterop": true,
22
+ "allowSyntheticDefaultImports": true,
23
+ "forceConsistentCasingInFileNames": true,
24
+ "skipLibCheck": true,
25
+ "declaration": true,
26
+ "declarationMap": true,
27
+ "sourceMap": true,
28
+ "outDir": "./dist",
29
+ "rootDir": "./src",
30
+ "baseUrl": ".",
31
+ "paths": {
32
+ "@/*": ["src/*"]
33
+ }
34
+ },
35
+ "include": ["src/**/*"],
36
+ "exclude": ["node_modules", "dist"]
37
+ }
38
+
@@ -1,65 +0,0 @@
1
- /* eslint-env browser */
2
-
3
- 'use strict';
4
-
5
- // saveSelection/restoreSelection courtesy of Tim Down, with my improvements
6
- // https://stackoverflow.com/questions/13949059/persisting-the-changes-of-range-objects-after-selection-in-html/13950376#13950376
7
-
8
- function isChildOf(child, parent) {
9
- while ((child = child.parentNode) && child !== parent);
10
- return !!child;
11
- }
12
-
13
- class SelectionHelper {
14
-
15
- static saveSelection(el) {
16
- let range = window.getSelection().getRangeAt(0);
17
-
18
- if (el !== range.commonAncestorContainer && !isChildOf(range.commonAncestorContainer, el))
19
- return null;
20
-
21
- let preSelectionRange = range.cloneRange();
22
- preSelectionRange.selectNodeContents(el);
23
- preSelectionRange.setEnd(range.startContainer, range.startOffset);
24
- let start = preSelectionRange.toString().length;
25
-
26
- return {
27
- start: start,
28
- end: start + range.toString().length,
29
- };
30
- }
31
-
32
- static restoreSelection(el, savedSel) {
33
- let charIndex = 0;
34
- let nodeStack = [el], node, foundStart = false, stop = false;
35
- let range = document.createRange();
36
- range.setStart(el, 0);
37
- range.collapse(true);
38
-
39
- while (!stop && (node = nodeStack.pop())) {
40
- if (node.nodeType === 3) {
41
- let nextCharIndex = charIndex + node.length;
42
- if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
43
- range.setStart(node, savedSel.start - charIndex);
44
- foundStart = true;
45
- }
46
- if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
47
- range.setEnd(node, savedSel.end - charIndex);
48
- stop = true;
49
- }
50
- charIndex = nextCharIndex;
51
- } else {
52
- let i = node.childNodes.length;
53
- while (i--) {
54
- nodeStack.push(node.childNodes[i]);
55
- }
56
- }
57
- }
58
-
59
- let sel = window.getSelection();
60
- sel.removeAllRanges();
61
- sel.addRange(range);
62
- }
63
- }
64
-
65
- export default SelectionHelper;
@@ -1,25 +0,0 @@
1
- 'use strict';
2
-
3
- function ByColumnFilter (row, args) {
4
-
5
- let column = args.column;
6
- let keyword = args.keyword == null ? '' : args.keyword.toString();
7
-
8
- if (!keyword || !column) return true;
9
-
10
- let actualVal = row[column];
11
- if (actualVal == null) {
12
- return false;
13
- }
14
-
15
- actualVal = actualVal.toString();
16
-
17
- if (!args.caseSensitive) {
18
- actualVal = actualVal.toLowerCase();
19
- keyword = keyword.toLowerCase();
20
- }
21
-
22
- return actualVal.indexOf(keyword) !== -1;
23
- }
24
-
25
- export default ByColumnFilter;