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.
package/src/style.css ADDED
@@ -0,0 +1,304 @@
1
+ :root {
2
+ --dt-border-color: #d1d8dd;
3
+ --dt-primary-color: rgb(82, 146, 247);
4
+ --dt-light-bg: #f5f7fa;
5
+ --dt-light-red: #FD8B8B;
6
+ --dt-light-yellow: #fffce7;
7
+ --dt-orange: rgb(255, 160, 10);
8
+ --dt-text-color: #000000;
9
+ --dt-text-light: #dfe2e5;
10
+ --dt-spacer-1: 0.25rem;
11
+ --dt-spacer-2: 0.5rem;
12
+ --dt-spacer-3: 1rem;
13
+ --dt-border-radius: 3px;
14
+ --dt-cell-bg: #fff;
15
+ --dt-focus-border-width: 2px;
16
+ --dt-selection-highlight-color: var(--dt-light-yellow);
17
+ --dt-toast-message-border: none;
18
+ --dt-header-cell-bg: var(--dt-cell-bg);
19
+ --dt-no-data-message-width: 90px;
20
+ }
21
+
22
+ .datatable {
23
+ *, *::after, *::before {
24
+ box-sizing: border-box;
25
+ }
26
+ }
27
+
28
+ .datatable {
29
+ position: relative;
30
+ overflow: hidden;
31
+ }
32
+
33
+ .dt-scrollable {
34
+ height: 40vw;
35
+ overflow: auto;
36
+ border-top: 2px solid var(--dt-border-color);
37
+
38
+ &--highlight-all {
39
+ background-color: var(--dt-selection-highlight-color);
40
+ }
41
+
42
+ &__no-data {
43
+ text-align: center;
44
+ padding: var(--dt-spacer-3);
45
+ border-left: 1px solid var(--dt-border-color);
46
+ border-right: 1px solid var(--dt-border-color);
47
+ .no-data-message{
48
+ position: absolute;
49
+ top: 100px;
50
+ left: 50px;
51
+ border: none;
52
+ width: var(--dt-no-data-message-width);
53
+ }
54
+ }
55
+ }
56
+
57
+ .dt-row {
58
+ display: flex;
59
+
60
+ &--highlight .dt-cell {
61
+ background-color: var(--dt-selection-highlight-color);
62
+ }
63
+
64
+ &--unhighlight .dt-cell {
65
+ background-color: var(--dt-cell-bg);
66
+ }
67
+
68
+ &--hide {
69
+ display: none;
70
+ }
71
+
72
+ &:last-child:not(.dt-row-filter) {
73
+ border-bottom: 1px solid var(--dt-border-color);
74
+ }
75
+ }
76
+
77
+ .dt-cell {
78
+ border: 1px solid var(--dt-border-color);
79
+ border-bottom: none;
80
+ border-right: none;
81
+ position: relative;
82
+ outline: none;
83
+ padding: 0;
84
+ background-color: var(--dt-cell-bg);
85
+ color: var(--dt-text-color);
86
+ /*
87
+ Fix for firefox and Edge
88
+ https://stackoverflow.com/a/16337203
89
+ firefox paints td background over border
90
+ */
91
+ background-clip: padding-box;
92
+ user-select: none;
93
+
94
+ &__content {
95
+ padding: var(--dt-spacer-2);
96
+ border: var(--dt-focus-border-width) solid transparent;
97
+ height: 100%;
98
+ text-overflow: ellipsis;
99
+ white-space: nowrap;
100
+ overflow: hidden;
101
+ }
102
+
103
+ &__edit {
104
+ display: none;
105
+ padding: var(--dt-spacer-2);
106
+ background-color: var(--dt-cell-bg);
107
+ border: var(--dt-focus-border-width) solid var(--dt-orange);
108
+ z-index: 1;
109
+ height: 100%;
110
+ }
111
+
112
+ &__resize-handle {
113
+ opacity: 0;
114
+ position: absolute;
115
+ right: -3px;
116
+ top: 0;
117
+ width: 5px;
118
+ height: 100%;
119
+ cursor: col-resize;
120
+ z-index: 1;
121
+ }
122
+
123
+ &--editing &__content {
124
+ display: none;
125
+ }
126
+
127
+ &--editing &__edit {
128
+ display: block;
129
+ }
130
+
131
+ &--focus &__content {
132
+ border-color: var(--dt-primary-color);
133
+ }
134
+
135
+ &--highlight {
136
+ background-color: var(--dt-light-bg);
137
+ }
138
+
139
+ &--dragging {
140
+ background-color: var(--dt-light-bg);
141
+ }
142
+
143
+ &--header {
144
+ background-color: var(--dt-header-cell-bg);
145
+ }
146
+
147
+ &--header:last-child {
148
+ border-right: 1px solid var(--dt-border-color);
149
+ }
150
+
151
+ &--header &__content {
152
+ padding-right: var(--dt-spacer-3);
153
+ font-weight: bold;
154
+ }
155
+
156
+ &--header:hover .dt-dropdown__toggle {
157
+ opacity: 1;
158
+ }
159
+
160
+ &--tree-close {
161
+ .icon-open {
162
+ display: none;
163
+ }
164
+
165
+ .icon-close {
166
+ display: flex;
167
+ }
168
+ }
169
+
170
+ &:last-child {
171
+ border-right: 1px solid var(--dt-border-color);
172
+ }
173
+ }
174
+
175
+ .datatable[dir=rtl] .dt-cell__resize-handle {
176
+ right: unset;
177
+ left: -3px;
178
+ }
179
+
180
+ .icon-open, .icon-close {
181
+ width: 16px;
182
+ height: 16px;
183
+ }
184
+
185
+ .icon-open {
186
+ display: flex;
187
+ }
188
+
189
+ .icon-close {
190
+ display: none;
191
+ }
192
+
193
+ .dt-dropdown {
194
+ position: absolute;
195
+ right: 10px;
196
+ display: inline-flex;
197
+ vertical-align: top;
198
+ text-align: left;
199
+ font-weight: normal;
200
+ cursor: pointer;
201
+
202
+ &__toggle {
203
+ opacity: 0;
204
+ background-color: var(--dt-header-cell-bg);
205
+ }
206
+
207
+ &__list {
208
+ position: fixed;
209
+ min-width: 8rem;
210
+ z-index: 1;
211
+ cursor: pointer;
212
+ background-color: var(--dt-cell-bg);
213
+ border-radius: var(--dt-border-radius);
214
+ padding: var(--dt-spacer-2) 0;
215
+ box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
216
+ }
217
+
218
+ &__list-item {
219
+ padding: var(--dt-spacer-2) var(--dt-spacer-3);
220
+
221
+ &:hover {
222
+ background-color: var(--dt-light-bg);
223
+ }
224
+ }
225
+
226
+ &--active &__list {
227
+ display: block;
228
+ }
229
+ }
230
+
231
+ .dt-tree-node {
232
+ display: flex;
233
+ align-items: center;
234
+ position: relative;
235
+
236
+ &__toggle {
237
+ display: inline-block;
238
+ cursor: pointer;
239
+ margin-right: 0.2rem;
240
+ }
241
+ }
242
+
243
+ .dt-toast {
244
+ position: absolute;
245
+ bottom: var(--dt-spacer-3);
246
+ left: 50%;
247
+ transform: translateX(-50%);
248
+
249
+ &__message {
250
+ display: inline-block;
251
+ background-color: rgba(0, 0, 0, 0.8);
252
+ color: var(--dt-text-light);
253
+ border-radius: var(--dt-border-radius);
254
+ padding: var(--dt-spacer-2) var(--dt-spacer-3);
255
+ border: var(--dt-toast-message-border);
256
+ }
257
+ }
258
+
259
+ .dt-input {
260
+ outline: none;
261
+ width: 100%;
262
+ border: none;
263
+ overflow: visible;
264
+ font-family: inherit;
265
+ font-size: inherit;
266
+ line-height: inherit;
267
+ background-color: inherit;
268
+ color: inherit;
269
+ margin: 0;
270
+ padding: 0;
271
+ }
272
+
273
+ .dt-freeze {
274
+ display: flex;
275
+ justify-content: center;
276
+ align-content: center;
277
+ position: absolute;
278
+ left: 0;
279
+ right: 0;
280
+ top: 0;
281
+ bottom: 0;
282
+ background-color: var(--dt-light-bg);
283
+ opacity: 0.5;
284
+ font-size: 2em;
285
+
286
+ &__message {
287
+ position: absolute;
288
+ top: 50%;
289
+ transform: translateY(-50%);
290
+ }
291
+ }
292
+
293
+ .dt-paste-target {
294
+ position: fixed;
295
+ left: -999em;
296
+ }
297
+
298
+ .dt-hidden{
299
+ display: none;
300
+ }
301
+
302
+ body.dt-resize {
303
+ cursor: col-resize;
304
+ }
package/src/style.js ADDED
@@ -0,0 +1,379 @@
1
+ import $ from './dom';
2
+ import {
3
+ camelCaseToDash,
4
+ linkProperties,
5
+ throttle
6
+ } from './utils';
7
+
8
+ export default class Style {
9
+ constructor(instance) {
10
+ this.instance = instance;
11
+
12
+ linkProperties(this, this.instance, [
13
+ 'options', 'datamanager', 'columnmanager',
14
+ 'header', 'footer', 'bodyScrollable', 'datatableWrapper',
15
+ 'getColumn', 'bodyRenderer'
16
+ ]);
17
+
18
+ this.scopeClass = 'dt-instance-' + instance.constructor.instances;
19
+ instance.datatableWrapper.classList.add(this.scopeClass);
20
+
21
+ const styleEl = document.createElement('style');
22
+ instance.wrapper.insertBefore(styleEl, instance.datatableWrapper);
23
+ this.styleEl = styleEl;
24
+
25
+ this.bindResizeWindow();
26
+ this.bindScrollHeader();
27
+ }
28
+
29
+ get stylesheet() {
30
+ return this.styleEl.sheet;
31
+ }
32
+
33
+ bindResizeWindow() {
34
+ this.onWindowResize = this.onWindowResize.bind(this);
35
+ this.onWindowResize = throttle(this.onWindowResize, 300);
36
+
37
+ if (this.options.layout === 'fluid') {
38
+ $.on(window, 'resize', this.onWindowResize);
39
+ }
40
+ }
41
+
42
+ bindScrollHeader() {
43
+ this._settingHeaderPosition = false;
44
+
45
+ $.on(this.bodyScrollable, 'scroll', (e) => {
46
+ if (this._settingHeaderPosition) return;
47
+
48
+ this._settingHeaderPosition = true;
49
+
50
+ requestAnimationFrame(() => {
51
+ const left = -e.target.scrollLeft;
52
+
53
+ $.style(this.header, {
54
+ transform: `translateX(${left}px)`
55
+ });
56
+ $.style(this.footer, {
57
+ transform: `translateX(${left}px)`
58
+ });
59
+ this._settingHeaderPosition = false;
60
+ if (this.instance.noData) {
61
+ $.style($('.no-data-message'), {
62
+ left: `${this.instance.wrapper.clientWidth / 2 - (left)}px`
63
+ });
64
+ }
65
+ this._settingHeaderPosition = false;
66
+ });
67
+ });
68
+ }
69
+
70
+ onWindowResize() {
71
+ this.distributeRemainingWidth();
72
+ this.refreshColumnWidth();
73
+ this.setBodyStyle();
74
+ }
75
+
76
+ destroy() {
77
+ this.styleEl.remove();
78
+ $.off(window, 'resize', this.onWindowResize);
79
+ }
80
+
81
+ setStyle(selector, styleObject) {
82
+ if (selector.includes(',')) {
83
+ selector.split(',')
84
+ .map(s => s.trim())
85
+ .forEach(selector => {
86
+ this.setStyle(selector, styleObject);
87
+ });
88
+ return;
89
+ }
90
+
91
+ selector = selector.trim();
92
+ if (!selector) return;
93
+
94
+ this._styleRulesMap = this._styleRulesMap || {};
95
+ const prefixedSelector = this._getPrefixedSelector(selector);
96
+
97
+ if (this._styleRulesMap[prefixedSelector]) {
98
+ this.removeStyle(selector);
99
+
100
+ // merge with old styleobject
101
+ styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
102
+ }
103
+
104
+ const styleString = this._getRuleString(styleObject);
105
+ const ruleString = `${prefixedSelector} { ${styleString} }`;
106
+
107
+ this._styleRulesMap[prefixedSelector] = styleObject;
108
+ this.stylesheet.insertRule(ruleString);
109
+ }
110
+
111
+ removeStyle(selector) {
112
+ if (selector.includes(',')) {
113
+ selector.split(',')
114
+ .map(s => s.trim())
115
+ .forEach(selector => {
116
+ this.removeStyle(selector);
117
+ });
118
+ return;
119
+ }
120
+
121
+ selector = selector.trim();
122
+ if (!selector) return;
123
+
124
+ // find and remove
125
+ const prefixedSelector = this._getPrefixedSelector(selector);
126
+ const index = Array.from(this.stylesheet.cssRules)
127
+ .findIndex(rule => rule.selectorText === prefixedSelector);
128
+
129
+ if (index === -1) return;
130
+ this.stylesheet.deleteRule(index);
131
+ }
132
+
133
+ _getPrefixedSelector(selector) {
134
+ return `.${this.scopeClass} ${selector}`;
135
+ }
136
+
137
+ _getRuleString(styleObject) {
138
+ return Object.keys(styleObject)
139
+ .map(prop => {
140
+ let dashed = prop;
141
+ if (!prop.includes('-')) {
142
+ dashed = camelCaseToDash(prop);
143
+ }
144
+ return `${dashed}:${styleObject[prop]};`;
145
+ })
146
+ .join('');
147
+ }
148
+
149
+ setDimensions() {
150
+ this.setCellHeight();
151
+ this.setupMinWidth();
152
+ this.setupNaturalColumnWidth();
153
+ this.setupColumnWidth();
154
+ this.distributeRemainingWidth();
155
+ this.setColumnStyle();
156
+ this.setBodyStyle();
157
+ }
158
+
159
+ setCellHeight() {
160
+ this.setStyle('.dt-cell', {
161
+ height: this.options.cellHeight + 'px'
162
+ });
163
+ }
164
+
165
+ setupMinWidth() {
166
+ $.each('.dt-cell--header', this.header).map(col => {
167
+ const { colIndex } = $.data(col);
168
+ const column = this.getColumn(colIndex);
169
+
170
+ if (!column.minWidth) {
171
+ const width = $.style($('.dt-cell__content', col), 'width');
172
+ // only set this once
173
+ column.minWidth = width;
174
+ }
175
+ });
176
+ }
177
+
178
+ setupNaturalColumnWidth() {
179
+ if (!$('.dt-row')) return;
180
+
181
+ $.each('.dt-row-header .dt-cell', this.header).map($headerCell => {
182
+ const { colIndex } = $.data($headerCell);
183
+ const column = this.datamanager.getColumn(colIndex);
184
+ let width = $.style($('.dt-cell__content', $headerCell), 'width');
185
+ if (typeof width === 'number' && width >= this.options.minimumColumnWidth) {
186
+ column.naturalWidth = width;
187
+ } else {
188
+ column.naturalWidth = this.options.minimumColumnWidth;
189
+ }
190
+ });
191
+
192
+ // set initial width as naturally calculated by table's first row
193
+ $.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
194
+ const {
195
+ colIndex
196
+ } = $.data($cell);
197
+ const column = this.datamanager.getColumn(colIndex);
198
+
199
+ let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
200
+
201
+ if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
202
+ column.naturalWidth = naturalWidth;
203
+ } else {
204
+ column.naturalWidth = column.naturalWidth;
205
+ }
206
+ });
207
+ }
208
+
209
+ setupColumnWidth() {
210
+ if (this.options.layout === 'ratio') {
211
+ let totalWidth = $.style(this.datatableWrapper, 'width');
212
+
213
+ if (this.options.serialNoColumn) {
214
+ const rowIndexColumn = this.datamanager.getColumnById('_rowIndex');
215
+ totalWidth = totalWidth - rowIndexColumn.width - 1;
216
+ }
217
+
218
+ if (this.options.checkboxColumn) {
219
+ const rowIndexColumn = this.datamanager.getColumnById('_checkbox');
220
+ totalWidth = totalWidth - rowIndexColumn.width - 1;
221
+ }
222
+
223
+ const totalParts = this.datamanager.getColumns()
224
+ .map(column => {
225
+ if (column.id === '_rowIndex' || column.id === '_checkbox') {
226
+ return 0;
227
+ }
228
+ if (!column.width) {
229
+ column.width = 1;
230
+ }
231
+ column.ratioWidth = parseInt(column.width, 10);
232
+ return column.ratioWidth;
233
+ })
234
+ .reduce((a, c) => a + c);
235
+
236
+ const onePart = totalWidth / totalParts;
237
+
238
+ this.datamanager.getColumns()
239
+ .map(column => {
240
+ if (column.id === '_rowIndex' || column.id === '_checkbox') return;
241
+ column.width = Math.floor(onePart * column.ratioWidth) - 1;
242
+ });
243
+ } else {
244
+ this.datamanager.getColumns()
245
+ .map(column => {
246
+ if (!column.width) {
247
+ column.width = column.naturalWidth;
248
+ }
249
+ if (column.id === '_rowIndex') {
250
+ column.width = this.getRowIndexColumnWidth();
251
+ }
252
+ if (column.width < this.options.minimumColumnWidth) {
253
+ column.width = this.options.minimumColumnWidth;
254
+ }
255
+ });
256
+ }
257
+ }
258
+
259
+ distributeRemainingWidth() {
260
+ if (this.options.layout !== 'fluid') return;
261
+
262
+ const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
263
+ let firstRow = $('.dt-row', this.bodyScrollable);
264
+ let firstRowWidth = wrapperWidth;
265
+ if (!firstRow) {
266
+ let headerRow = $('.dt-row', this.instance.header);
267
+ let cellWidths = Array.from(headerRow.children)
268
+ .map(cell => cell.offsetWidth);
269
+ firstRowWidth = cellWidths.reduce((sum, a) => sum + a, 0);
270
+ } else {
271
+ firstRowWidth = $.style(firstRow, 'width');
272
+ }
273
+ const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
274
+ const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
275
+
276
+ resizableColumns.map(col => {
277
+ const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
278
+ let finalWidth = Math.floor(width + deltaWidth) - 2;
279
+
280
+ this.datamanager.updateColumn(col.colIndex, {
281
+ width: finalWidth
282
+ });
283
+ });
284
+ }
285
+
286
+ setColumnStyle() {
287
+ // align columns
288
+ this.datamanager.getColumns()
289
+ .map(column => {
290
+ // alignment
291
+ if (!column.align) {
292
+ column.align = 'left';
293
+ }
294
+ if (!['left', 'center', 'right'].includes(column.align)) {
295
+ column.align = 'left';
296
+ }
297
+ this.setStyle(`.dt-cell--col-${column.colIndex}`, {
298
+ 'text-align': column.align
299
+ });
300
+
301
+ // width
302
+ this.columnmanager.setColumnHeaderWidth(column.colIndex);
303
+ this.columnmanager.setColumnWidth(column.colIndex);
304
+ });
305
+ }
306
+
307
+ refreshColumnWidth() {
308
+ this.datamanager.getColumns()
309
+ .map(column => {
310
+ this.columnmanager.setColumnHeaderWidth(column.colIndex);
311
+ this.columnmanager.setColumnWidth(column.colIndex);
312
+ });
313
+ }
314
+
315
+ setBodyStyle() {
316
+ const bodyWidth = $.style(this.datatableWrapper, 'width');
317
+ const firstRow = $('.dt-row', this.bodyScrollable);
318
+ if (!firstRow) return;
319
+ const rowWidth = $.style(firstRow, 'width');
320
+
321
+ let width = bodyWidth > rowWidth ? rowWidth + 10 : bodyWidth;
322
+ $.style(this.bodyScrollable, {
323
+ width: width + 'px'
324
+ });
325
+
326
+ // remove the body height, so that it resets to it's original
327
+ $.removeStyle(this.bodyScrollable, 'height');
328
+
329
+ // when there are less rows than the container
330
+ // adapt the container height
331
+ let bodyHeight = $.getStyle(this.bodyScrollable, 'height');
332
+ const scrollHeight = (this.bodyRenderer.hyperlist || {})._scrollHeight || Infinity;
333
+ const hasHorizontalOverflow = $.hasHorizontalOverflow(this.bodyScrollable);
334
+
335
+ let height;
336
+
337
+ if (scrollHeight < bodyHeight) {
338
+ height = scrollHeight;
339
+
340
+ // account for scrollbar size when
341
+ // there is horizontal overflow
342
+ if (hasHorizontalOverflow) {
343
+ height += $.scrollbarSize();
344
+ }
345
+
346
+ $.style(this.bodyScrollable, {
347
+ height: height + 'px'
348
+ });
349
+ }
350
+
351
+ const verticalOverflow = this.bodyScrollable.scrollHeight - this.bodyScrollable.offsetHeight;
352
+ if (verticalOverflow < $.scrollbarSize()) {
353
+ // if verticalOverflow is less than scrollbar size
354
+ // then most likely scrollbar is causing the scroll
355
+ // which is not needed
356
+ $.style(this.bodyScrollable, {
357
+ overflowY: 'hidden'
358
+ });
359
+ }
360
+
361
+ if (this.options.layout === 'fluid') {
362
+ $.style(this.bodyScrollable, {
363
+ overflowX: 'hidden'
364
+ });
365
+ }
366
+ }
367
+
368
+ getColumnHeaderElement(colIndex) {
369
+ colIndex = +colIndex;
370
+ if (colIndex < 0) return null;
371
+ return $(`.dt-cell--col-${colIndex}`, this.header);
372
+ }
373
+
374
+ getRowIndexColumnWidth() {
375
+ const rowCount = this.datamanager.getRowCount();
376
+ const padding = 22;
377
+ return $.measureTextWidth(rowCount + '') + padding;
378
+ }
379
+ }
@@ -0,0 +1,30 @@
1
+ import { format } from './utils';
2
+ import getTranslations from './translations';
3
+
4
+ export default class TranslationManager {
5
+ constructor(language) {
6
+ this.language = language;
7
+ this.translations = getTranslations();
8
+ }
9
+
10
+ addTranslations(translations) {
11
+ this.translations = Object.assign(this.translations, translations);
12
+ }
13
+
14
+ translate(sourceText, args) {
15
+ let translation = (this.translations[this.language] &&
16
+ this.translations[this.language][sourceText]) || sourceText;
17
+
18
+ if (typeof translation === 'object') {
19
+ translation = args && args.count ?
20
+ this.getPluralizedTranslation(translation, args.count) :
21
+ sourceText;
22
+ }
23
+
24
+ return format(translation, args || {});
25
+ }
26
+
27
+ getPluralizedTranslation(translations, count) {
28
+ return translations[count] || translations['default'];
29
+ }
30
+ };