adore-datatable 2.0.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.
@@ -0,0 +1,296 @@
1
+ import $ from './dom';
2
+ import DataManager from './datamanager';
3
+ import CellManager from './cellmanager';
4
+ import ColumnManager from './columnmanager';
5
+ import RowManager from './rowmanager';
6
+ import BodyRenderer from './body-renderer';
7
+ import Style from './style';
8
+ import Keyboard from './keyboard';
9
+ import TranslationManager from './translationmanager';
10
+ import getDefaultOptions from './defaults';
11
+
12
+ let defaultComponents = {
13
+ DataManager,
14
+ CellManager,
15
+ ColumnManager,
16
+ RowManager,
17
+ BodyRenderer,
18
+ Style,
19
+ Keyboard
20
+ };
21
+
22
+ class DataTable {
23
+ constructor(wrapper, options) {
24
+ DataTable.instances++;
25
+
26
+ if (typeof wrapper === 'string') {
27
+ // css selector
28
+ wrapper = document.querySelector(wrapper);
29
+ }
30
+ this.wrapper = wrapper;
31
+ if (!(this.wrapper instanceof HTMLElement)) {
32
+ throw new Error('Invalid argument given for `wrapper`');
33
+ }
34
+
35
+ this.initializeTranslations(options);
36
+ this.setDefaultOptions();
37
+ this.buildOptions(options);
38
+ this.prepare();
39
+ this.initializeComponents();
40
+
41
+ if (this.options.data) {
42
+ this.refresh();
43
+ this.columnmanager.applyDefaultSortOrder();
44
+ if (this.options.saveSorting) {
45
+ this.setupSaveSorting();
46
+ this.columnmanager.applySavedSortOrder();
47
+ }
48
+ }
49
+ }
50
+
51
+ initializeTranslations(options) {
52
+ this.language = options.language || 'en';
53
+ this.translationManager = new TranslationManager(this.language);
54
+
55
+ if (options.translations) {
56
+ this.translationManager.addTranslations(options.translations);
57
+ }
58
+ }
59
+
60
+ setDefaultOptions() {
61
+ this.DEFAULT_OPTIONS = getDefaultOptions(this);
62
+ }
63
+
64
+ buildOptions(options) {
65
+ this.options = this.options || {};
66
+
67
+ this.options = Object.assign(
68
+ {}, this.DEFAULT_OPTIONS,
69
+ this.options || {}, options
70
+ );
71
+
72
+ options.headerDropdown = options.headerDropdown || [];
73
+ this.options.headerDropdown = [
74
+ ...this.DEFAULT_OPTIONS.headerDropdown,
75
+ ...options.headerDropdown
76
+ ];
77
+
78
+ // custom user events
79
+ this.events = Object.assign(
80
+ {}, this.DEFAULT_OPTIONS.events,
81
+ this.options.events || {},
82
+ options.events || {}
83
+ );
84
+ this.fireEvent = this.fireEvent.bind(this);
85
+ }
86
+
87
+ prepare() {
88
+ this.prepareDom();
89
+ this.unfreeze();
90
+ }
91
+
92
+ initializeComponents() {
93
+ let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
94
+ let {
95
+ Style,
96
+ Keyboard,
97
+ DataManager,
98
+ RowManager,
99
+ ColumnManager,
100
+ CellManager,
101
+ BodyRenderer
102
+ } = components;
103
+
104
+ this.style = new Style(this);
105
+ this.keyboard = new Keyboard(this.wrapper);
106
+ this.datamanager = new DataManager(this.options);
107
+ this.rowmanager = new RowManager(this);
108
+ this.columnmanager = new ColumnManager(this);
109
+ this.cellmanager = new CellManager(this);
110
+ this.bodyRenderer = new BodyRenderer(this);
111
+ }
112
+
113
+ prepareDom() {
114
+ this.wrapper.innerHTML = `
115
+ <div class="datatable" dir="${this.options.direction}">
116
+ <div class="dt-header"></div>
117
+ <div class="dt-scrollable"></div>
118
+ <div class="dt-footer"></div>
119
+ <div class="dt-freeze">
120
+ <span class="dt-freeze__message">
121
+ ${this.options.freezeMessage}
122
+ </span>
123
+ </div>
124
+ <div class="dt-toast"></div>
125
+ <div class="dt-dropdown-container"></div>
126
+ <textarea class="dt-paste-target"></textarea>
127
+ </div>
128
+ `;
129
+
130
+ this.datatableWrapper = $('.datatable', this.wrapper);
131
+ this.header = $('.dt-header', this.wrapper);
132
+ this.footer = $('.dt-footer', this.wrapper);
133
+ this.bodyScrollable = $('.dt-scrollable', this.wrapper);
134
+ this.freezeContainer = $('.dt-freeze', this.wrapper);
135
+ this.toastMessage = $('.dt-toast', this.wrapper);
136
+ this.pasteTarget = $('.dt-paste-target', this.wrapper);
137
+ this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
138
+ }
139
+
140
+ refresh(data, columns) {
141
+ this.datamanager.init(data, columns);
142
+ this.render();
143
+ this.setDimensions();
144
+ }
145
+
146
+ destroy() {
147
+ this.wrapper.innerHTML = '';
148
+ this.style.destroy();
149
+ this.fireEvent('onDestroy');
150
+ }
151
+
152
+ appendRows(rows) {
153
+ this.datamanager.appendRows(rows);
154
+ this.rowmanager.refreshRows();
155
+ }
156
+
157
+ refreshRow(row, rowIndex) {
158
+ this.rowmanager.refreshRow(row, rowIndex);
159
+ }
160
+
161
+ render() {
162
+ this.renderHeader();
163
+ this.renderBody();
164
+ }
165
+
166
+ renderHeader() {
167
+ this.columnmanager.renderHeader();
168
+ }
169
+
170
+ renderBody() {
171
+ this.bodyRenderer.render();
172
+ }
173
+
174
+ setDimensions() {
175
+ this.style.setDimensions();
176
+ }
177
+
178
+ showToastMessage(message, hideAfter) {
179
+ this.bodyRenderer.showToastMessage(message, hideAfter);
180
+ }
181
+
182
+ clearToastMessage() {
183
+ this.bodyRenderer.clearToastMessage();
184
+ }
185
+
186
+ getColumn(colIndex) {
187
+ return this.datamanager.getColumn(colIndex);
188
+ }
189
+
190
+ getColumns() {
191
+ return this.datamanager.getColumns();
192
+ }
193
+
194
+ getRows() {
195
+ return this.datamanager.getRows();
196
+ }
197
+
198
+ getCell(colIndex, rowIndex) {
199
+ return this.datamanager.getCell(colIndex, rowIndex);
200
+ }
201
+
202
+ getColumnHeaderElement(colIndex) {
203
+ return this.columnmanager.getColumnHeaderElement(colIndex);
204
+ }
205
+
206
+ getViewportHeight() {
207
+ if (!this.viewportHeight) {
208
+ this.viewportHeight = $.style(this.bodyScrollable, 'height');
209
+ }
210
+
211
+ return this.viewportHeight;
212
+ }
213
+
214
+ sortColumn(colIndex, sortOrder) {
215
+ this.columnmanager.sortColumn(colIndex, sortOrder);
216
+ }
217
+ saveSorting(colIndex, nextSortOrder) {
218
+ this.columnmanager.saveSorting(colIndex, nextSortOrder);
219
+ }
220
+
221
+ removeColumn(colIndex) {
222
+ this.columnmanager.removeColumn(colIndex);
223
+ }
224
+
225
+ scrollToLastColumn() {
226
+ this.datatableWrapper.scrollLeft = 9999;
227
+ }
228
+
229
+ freeze() {
230
+ $.style(this.freezeContainer, {
231
+ display: ''
232
+ });
233
+ }
234
+
235
+ unfreeze() {
236
+ $.style(this.freezeContainer, {
237
+ display: 'none'
238
+ });
239
+ }
240
+
241
+ updateOptions(options) {
242
+ this.buildOptions(options);
243
+ }
244
+
245
+ fireEvent(eventName, ...args) {
246
+ // fire internalEventHandlers if any
247
+ // and then user events
248
+ const handlers = [
249
+ ...(this._internalEventHandlers[eventName] || []),
250
+ this.events[eventName]
251
+ ].filter(Boolean);
252
+
253
+ for (let handler of handlers) {
254
+ handler.apply(this, args);
255
+ }
256
+ }
257
+
258
+ on(event, handler) {
259
+ this._internalEventHandlers = this._internalEventHandlers || {};
260
+ this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
261
+ this._internalEventHandlers[event].push(handler);
262
+ }
263
+
264
+ log() {
265
+ if (this.options.logs) {
266
+ console.log.apply(console, arguments);
267
+ }
268
+ }
269
+
270
+ translate(str, args) {
271
+ return this.translationManager.translate(str, args);
272
+ }
273
+ setupSaveSorting() {
274
+ // add options in default headerdropdown
275
+ let action = {
276
+ label: this.translate('Save Sorting'),
277
+ action: function (column) {
278
+ this.saveSorting(column.colIndex, column.sotOrder);
279
+ },
280
+ display: 'hidden'
281
+ };
282
+ this.options.headerDropdown.push(action);
283
+ this.columnmanager.bindDropdown();
284
+ // add events for onSortColumn
285
+ this.on('onSortColumn', function (column) {
286
+ this.columnmanager.toggleDropdownItem(4);
287
+ if (column.sortOrder === 'none') {
288
+ localStorage.removeItem(this.columnmanager.sortingKey);
289
+ }
290
+ });
291
+ }
292
+ }
293
+
294
+ DataTable.instances = 0;
295
+
296
+ export default DataTable;
@@ -0,0 +1,73 @@
1
+ import filterRows from './filterRows';
2
+ import icons from './icons';
3
+
4
+ export default function getDefaultOptions(instance) {
5
+ return {
6
+ columns: [],
7
+ data: [],
8
+ dropdownButton: icons.chevronDown,
9
+ headerDropdown: [
10
+ {
11
+ label: instance.translate('Sort Ascending'),
12
+ action: function (column) {
13
+ this.sortColumn(column.colIndex, 'asc');
14
+ }
15
+ },
16
+ {
17
+ label: instance.translate('Sort Descending'),
18
+ action: function (column) {
19
+ this.sortColumn(column.colIndex, 'desc');
20
+ }
21
+ },
22
+ {
23
+ label: instance.translate('Reset sorting'),
24
+ action: function (column) {
25
+ this.sortColumn(column.colIndex, 'none');
26
+ }
27
+ },
28
+ {
29
+ label: instance.translate('Remove column'),
30
+ action: function (column) {
31
+ this.removeColumn(column.colIndex);
32
+ }
33
+ }
34
+ ],
35
+ events: {
36
+ onRemoveColumn(column) {},
37
+ onSwitchColumn(column1, column2) {},
38
+ onSortColumn(column) {},
39
+ onCheckRow(row) {},
40
+ onDestroy() {}
41
+ },
42
+ hooks: {
43
+ columnTotal: null
44
+ },
45
+ sortIndicator: {
46
+ asc: '↑',
47
+ desc: '↓',
48
+ none: ''
49
+ },
50
+ overrideComponents: {
51
+ // ColumnManager: CustomColumnManager
52
+ },
53
+ filterRows: filterRows,
54
+ freezeMessage: '',
55
+ getEditor: null,
56
+ serialNoColumn: true,
57
+ checkboxColumn: false,
58
+ clusterize: true,
59
+ logs: false,
60
+ layout: 'fixed', // fixed, fluid, ratio
61
+ noDataMessage: instance.translate('No Data'),
62
+ cellHeight: 40,
63
+ minimumColumnWidth: 30,
64
+ inlineFilters: false,
65
+ treeView: false,
66
+ checkedRowStatus: true,
67
+ dynamicRowHeight: false,
68
+ pasteFromClipboard: false,
69
+ showTotalRow: false,
70
+ direction: 'ltr',
71
+ disableReorderColumn: false
72
+ };
73
+ };
package/src/dom.js ADDED
@@ -0,0 +1,235 @@
1
+ export default function $(expr, con) {
2
+ return typeof expr === 'string' ?
3
+ (con || document).querySelector(expr) :
4
+ expr || null;
5
+ }
6
+
7
+ $.each = (expr, con) => {
8
+ return typeof expr === 'string' ?
9
+ Array.from((con || document).querySelectorAll(expr)) :
10
+ expr || null;
11
+ };
12
+
13
+ $.create = (tag, o) => {
14
+ let element = document.createElement(tag);
15
+
16
+ for (let i in o) {
17
+ let val = o[i];
18
+
19
+ if (i === 'inside') {
20
+ $(val).appendChild(element);
21
+ } else
22
+ if (i === 'around') {
23
+ let ref = $(val);
24
+ ref.parentNode.insertBefore(element, ref);
25
+ element.appendChild(ref);
26
+ } else
27
+ if (i === 'styles') {
28
+ if (typeof val === 'object') {
29
+ Object.keys(val).map(prop => {
30
+ element.style[prop] = val[prop];
31
+ });
32
+ }
33
+ } else
34
+ if (i in element) {
35
+ element[i] = val;
36
+ } else {
37
+ element.setAttribute(i, val);
38
+ }
39
+ }
40
+
41
+ return element;
42
+ };
43
+
44
+ $.on = (element, event, selector, callback) => {
45
+ if (!callback) {
46
+ callback = selector;
47
+ $.bind(element, event, callback);
48
+ } else {
49
+ $.delegate(element, event, selector, callback);
50
+ }
51
+ };
52
+
53
+ $.off = (element, event, handler) => {
54
+ element.removeEventListener(event, handler);
55
+ };
56
+
57
+ $.bind = (element, event, callback) => {
58
+ event.split(/\s+/).forEach(function (event) {
59
+ element.addEventListener(event, callback);
60
+ });
61
+ };
62
+
63
+ $.delegate = (element, event, selector, callback) => {
64
+ element.addEventListener(event, function (e) {
65
+ const delegatedTarget = e.target.closest(selector);
66
+ if (delegatedTarget) {
67
+ e.delegatedTarget = delegatedTarget;
68
+ callback.call(this, e, delegatedTarget);
69
+ }
70
+ });
71
+ };
72
+
73
+ $.unbind = (element, o) => {
74
+ if (element) {
75
+ for (let event in o) {
76
+ let callback = o[event];
77
+
78
+ event.split(/\s+/).forEach(function (event) {
79
+ element.removeEventListener(event, callback);
80
+ });
81
+ }
82
+ }
83
+ };
84
+
85
+ $.fire = (target, type, properties) => {
86
+ let evt = document.createEvent('HTMLEvents');
87
+
88
+ evt.initEvent(type, true, true);
89
+
90
+ for (let j in properties) {
91
+ evt[j] = properties[j];
92
+ }
93
+
94
+ return target.dispatchEvent(evt);
95
+ };
96
+
97
+ $.data = (element, attrs) => { // eslint-disable-line
98
+ if (!attrs) {
99
+ return element.dataset;
100
+ }
101
+
102
+ for (const attr in attrs) {
103
+ element.dataset[attr] = attrs[attr];
104
+ }
105
+ };
106
+
107
+ $.style = (elements, styleMap) => { // eslint-disable-line
108
+
109
+ if (typeof styleMap === 'string') {
110
+ return $.getStyle(elements, styleMap);
111
+ }
112
+
113
+ if (!Array.isArray(elements)) {
114
+ elements = [elements];
115
+ }
116
+
117
+ elements.map(element => {
118
+ for (const prop in styleMap) {
119
+ element.style[prop] = styleMap[prop];
120
+ }
121
+ });
122
+ };
123
+
124
+ $.removeStyle = (elements, styleProps) => {
125
+ if (!Array.isArray(elements)) {
126
+ elements = [elements];
127
+ }
128
+
129
+ if (!Array.isArray(styleProps)) {
130
+ styleProps = [styleProps];
131
+ }
132
+
133
+ elements.map(element => {
134
+ for (const prop of styleProps) {
135
+ element.style[prop] = '';
136
+ }
137
+ });
138
+ };
139
+
140
+ $.getStyle = (element, prop) => {
141
+ if (!prop) {
142
+ return getComputedStyle(element);
143
+ }
144
+
145
+ let val = getComputedStyle(element)[prop];
146
+
147
+ if (['width', 'height'].includes(prop)) {
148
+ val = parseFloat(val);
149
+ }
150
+
151
+ return val;
152
+ };
153
+
154
+ $.closest = (selector, element) => {
155
+ if (!element) return null;
156
+
157
+ if (element.matches(selector)) {
158
+ return element;
159
+ }
160
+
161
+ return $.closest(selector, element.parentNode);
162
+ };
163
+
164
+ $.inViewport = (el, parentEl) => {
165
+ const {
166
+ top,
167
+ left,
168
+ bottom,
169
+ right
170
+ } = el.getBoundingClientRect();
171
+ const {
172
+ top: pTop,
173
+ left: pLeft,
174
+ bottom: pBottom,
175
+ right: pRight
176
+ } = parentEl.getBoundingClientRect();
177
+
178
+ return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
179
+ };
180
+
181
+ $.scrollTop = function scrollTop(element, pixels) {
182
+ requestAnimationFrame(() => {
183
+ element.scrollTop = pixels;
184
+ });
185
+ };
186
+
187
+ $.scrollbarSize = function scrollbarSize() {
188
+ if (!$.scrollBarSizeValue) {
189
+ $.scrollBarSizeValue = getScrollBarSize();
190
+ }
191
+ return $.scrollBarSizeValue;
192
+ };
193
+
194
+ function getScrollBarSize() {
195
+ // assume scrollbar width and height would be the same
196
+
197
+ // Create the measurement node
198
+ const scrollDiv = document.createElement('div');
199
+ $.style(scrollDiv, {
200
+ width: '100px',
201
+ height: '100px',
202
+ overflow: 'scroll',
203
+ position: 'absolute',
204
+ top: '-9999px'
205
+ });
206
+ document.body.appendChild(scrollDiv);
207
+
208
+ // Get the scrollbar width
209
+ const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
210
+
211
+ // Delete the DIV
212
+ document.body.removeChild(scrollDiv);
213
+
214
+ return scrollbarWidth;
215
+ }
216
+
217
+ $.hasVerticalOverflow = function (element) {
218
+ return element.scrollHeight > element.offsetHeight + 10;
219
+ };
220
+
221
+ $.hasHorizontalOverflow = function (element) {
222
+ return element.scrollWidth > element.offsetWidth + 10;
223
+ };
224
+
225
+ $.measureTextWidth = function (text) {
226
+ const div = document.createElement('div');
227
+ div.style.position = 'absolute';
228
+ div.style.visibility = 'hidden';
229
+ div.style.height = 'auto';
230
+ div.style.width = 'auto';
231
+ div.style.whiteSpace = 'nowrap';
232
+ div.innerText = text;
233
+ document.body.appendChild(div);
234
+ return div.clientWidth + 1;
235
+ };