canvas-datagrid 0.25.3 → 0.26.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/lib/dom.js CHANGED
@@ -123,14 +123,27 @@ export default function (self) {
123
123
  self.input.style.width = self.width - borderWidth - 1 + 'px';
124
124
  return;
125
125
  }
126
+ let groupAreaOffsetY = self.getColumnGroupAreaHeight();
127
+ let groupAreaOffsetX = self.getRowGroupAreaWidth();
128
+ if (self.parentNode) {
129
+ const { columnGroupsAreaHeight, rowGroupsAreaWidth } = self.parentNode;
130
+ if (columnGroupsAreaHeight) groupAreaOffsetY += columnGroupsAreaHeight;
131
+ if (rowGroupsAreaWidth) groupAreaOffsetX += rowGroupsAreaWidth;
132
+ }
126
133
  self.input.style.left =
127
- pos.left + cell.x + self.canvasOffsetLeft - s.left + 'px';
134
+ pos.left +
135
+ cell.x +
136
+ self.canvasOffsetLeft -
137
+ s.left +
138
+ groupAreaOffsetX +
139
+ 'px';
128
140
  self.input.style.top =
129
141
  pos.top +
130
142
  cell.y -
131
143
  self.style.cellBorderWidth +
132
144
  self.canvasOffsetTop -
133
145
  s.top +
146
+ groupAreaOffsetY +
134
147
  'px';
135
148
  self.input.style.height = cell.height - borderWidth + 'px';
136
149
  self.input.style.width = cell.width - self.style.cellPaddingLeft + 'px';
@@ -143,11 +156,16 @@ export default function (self) {
143
156
  s = e,
144
157
  h,
145
158
  w;
159
+ let calculatedTree = false;
146
160
  while (e.offsetParent && e.nodeName !== 'CANVAS-DATAGRID') {
147
- x += e.offsetLeft;
148
- y += e.offsetTop;
149
- h = e.offsetHeight;
150
- w = e.offsetWidth;
161
+ const isTree = e.nodeType === 'canvas-datagrid-tree';
162
+ if (!isTree || !calculatedTree) {
163
+ x += e.offsetLeft;
164
+ y += e.offsetTop;
165
+ h = e.offsetHeight;
166
+ w = e.offsetWidth;
167
+ }
168
+ if (isTree) calculatedTree = true;
151
169
  e = e.offsetParent;
152
170
  }
153
171
  if (ignoreScrollOffset) {
@@ -166,6 +184,10 @@ export default function (self) {
166
184
  if (self.isChildGrid) {
167
185
  pos.x -= self.canvasOffsetLeft;
168
186
  pos.y -= self.canvasOffsetTop;
187
+ if (self.parentNode.rowGroupsAreaWidth)
188
+ pos.x -= self.parentNode.rowGroupsAreaWidth;
189
+ if (self.parentNode.columnGroupsAreaHeight)
190
+ pos.y -= self.parentNode.columnGroupsAreaHeight;
169
191
  }
170
192
  return {
171
193
  x: pos.x,
@@ -173,6 +195,62 @@ export default function (self) {
173
195
  rect: rect,
174
196
  };
175
197
  };
198
+ /**
199
+ * Watches the host element and its parent for size changes so a grid sized
200
+ * with auto/percentage dimensions follows layout changes (flex, splitters,
201
+ * tab switches) without an explicit `resize()` call (#289, #239).
202
+ * Falls back to the `window` resize listener when ResizeObserver is missing.
203
+ */
204
+ self.initResizeObserver = function () {
205
+ if (self.isChildGrid || typeof window.ResizeObserver !== 'function') {
206
+ return;
207
+ }
208
+ self.observedSizes = new Map();
209
+ self.resizeObserver = new window.ResizeObserver(function (entries) {
210
+ var changed = false;
211
+ entries.forEach(function (entry) {
212
+ var rect = entry.contentRect;
213
+ var last = self.observedSizes.get(entry.target);
214
+ if (!last || last.width !== rect.width || last.height !== rect.height) {
215
+ self.observedSizes.set(entry.target, {
216
+ width: rect.width,
217
+ height: rect.height,
218
+ });
219
+ changed = true;
220
+ }
221
+ });
222
+ if (!changed || self.disposed || self.resizeObserverFrame) {
223
+ return;
224
+ }
225
+ // coalesce bursts of notifications into one resize per frame
226
+ self.resizeObserverFrame = requestAnimationFrame(function () {
227
+ self.resizeObserverFrame = undefined;
228
+ if (self.disposed) {
229
+ return;
230
+ }
231
+ self.resize(true);
232
+ });
233
+ });
234
+ self.observeResizeTargets();
235
+ };
236
+ self.observeResizeTargets = function () {
237
+ if (!self.resizeObserver) {
238
+ return;
239
+ }
240
+ self.resizeObserver.disconnect();
241
+ self.observedSizes = new Map();
242
+ var host = self.intf && self.intf.nodeType === 1 ? self.intf : self.canvas;
243
+ [host, host && host.parentNode].forEach(function (target) {
244
+ if (target && target.nodeType === 1) {
245
+ self.resizeObserver.observe(target);
246
+ }
247
+ });
248
+ };
249
+ self.unobserveResizeTargets = function () {
250
+ if (self.resizeObserver) {
251
+ self.resizeObserver.disconnect();
252
+ }
253
+ };
176
254
  /**
177
255
  * Ends editing, optionally aborting the edit.
178
256
  * @memberof canvasDatagrid
@@ -182,7 +260,11 @@ export default function (self) {
182
260
  */
183
261
  self.endEdit = function (abort) {
184
262
  var cell = self.input.editCell,
185
- y = cell.rowIndex;
263
+ y = cell.rowIndex,
264
+ // Decide up front whether this is the "new row" placeholder. A
265
+ // beforeendedit handler may add or delete rows (#583), which would
266
+ // otherwise make the last real row look like the placeholder.
267
+ isNewRow = y === self.viewData.length;
186
268
  function abortEdit() {
187
269
  abort = true;
188
270
  }
@@ -198,14 +280,18 @@ export default function (self) {
198
280
  return false;
199
281
  }
200
282
  if (self.input.value !== cell.value && !abort) {
201
- self.changes[y] = self.changes[y] || {};
202
- self.changes[y][cell.header.name] = self.input.value;
283
+ // key the change log by the bound (data) row index rather than the
284
+ // view index so sorting/filtering cannot misattribute edits (#418)
285
+ var changeIndex =
286
+ cell.boundRowIndex === undefined ? y : cell.boundRowIndex;
287
+ self.changes[changeIndex] = self.changes[changeIndex] || {};
288
+ self.changes[changeIndex][cell.header.name] = self.input.value;
203
289
  if (!cell.data) {
204
- self.data[cell.rowIndex] = {};
205
- cell.data = self.data[cell.rowIndex];
290
+ self.originalData[cell.boundRowIndex] = {};
291
+ cell.data = self.originalData[cell.boundRowIndex];
206
292
  }
207
293
  cell.data[cell.header.name] = self.input.value;
208
- if (y === self.data.length) {
294
+ if (isNewRow) {
209
295
  if (
210
296
  self.dispatchEvent('newrow', {
211
297
  value: self.input.value,
@@ -220,6 +306,10 @@ export default function (self) {
220
306
  self.addRow(cell.data);
221
307
  self.createNewRowData();
222
308
  }
309
+ self.dispatchDataChanged('edit', {
310
+ cell: cell,
311
+ value: self.input.value,
312
+ });
223
313
  self.draw(true);
224
314
  }
225
315
  if (self.input.parentNode) {
@@ -340,6 +430,15 @@ export default function (self) {
340
430
  self.input.style.position = 'absolute';
341
431
  self.input.editCell = cell;
342
432
  self.resizeEditInput();
433
+ if (enu && typeof self.input.showPicker === 'function') {
434
+ // open the enum <select> immediately (#230); browsers only allow this
435
+ // from a user gesture, so treat failure as harmless
436
+ try {
437
+ self.input.showPicker();
438
+ } catch (err) {
439
+ // no user activation
440
+ }
441
+ }
343
442
  self.input.style.zIndex = self.style.editCellZIndex;
344
443
  self.input.style.fontSize =
345
444
  parseInt(self.style.editCellFontSize, 10) * self.scale + 'px';
@@ -348,6 +447,19 @@ export default function (self) {
348
447
  var shouldClearCellValue = cellValueIsEmpty || inEnterMode;
349
448
 
350
449
  self.input.value = shouldClearCellValue ? '' : cell.value;
450
+ let width = Math.round(
451
+ self.input.value.length *
452
+ parseInt(self.style.editCellFontSize, 10) *
453
+ 0.7 *
454
+ self.scale,
455
+ );
456
+ if (width + self.style.cellPaddingLeft < cell.width) {
457
+ width = cell.width - self.style.cellPaddingLeft;
458
+ } else if (width > self.width - cell.x - self.style.scrollBarWidth) {
459
+ width = self.width - cell.x - self.style.scrollBarWidth;
460
+ self.input.style.height = 'auto';
461
+ }
462
+ self.input.style.width = width + 'px';
351
463
  self.input.focus();
352
464
  self.input.addEventListener('click', self.stopPropagation);
353
465
  self.input.addEventListener('dblclick', self.stopPropagation);
@@ -369,7 +481,7 @@ export default function (self) {
369
481
  // Move to cell in next or previous row
370
482
  var nextRowIndex = e.shiftKey
371
483
  ? Math.max(0, ny - 1)
372
- : Math.min(ny + 1, self.data.length - 1);
484
+ : Math.min(ny + 1, self.viewData.length - 1);
373
485
 
374
486
  if (nextRowIndex !== ny) {
375
487
  self.scrollIntoView(nx, nextRowIndex);
@@ -386,7 +498,7 @@ export default function (self) {
386
498
  ny = Math.max(0, ny - 1);
387
499
  break;
388
500
  case 'ArrowDown':
389
- ny = Math.min(ny + 1, self.data.length - 1);
501
+ ny = Math.min(ny + 1, self.viewData.length - 1);
390
502
  break;
391
503
  case 'ArrowLeft':
392
504
  nx = Math.max(nx - 1, 0);
@@ -421,13 +533,27 @@ export default function (self) {
421
533
  ny += 1;
422
534
  }
423
535
  if (ny < 0) {
424
- ny = self.data.length - 1;
536
+ ny = self.viewData.length - 1;
425
537
  }
426
- if (ny > self.data.length - 1) {
538
+ if (ny > self.viewData.length - 1) {
427
539
  ny = 0;
428
540
  }
429
541
  self.scrollIntoView(nx, ny);
430
542
  self.beginEditAt(nx, ny, e);
543
+ } else {
544
+ let width = Math.round(
545
+ self.input.value.length *
546
+ parseInt(self.style.editCellFontSize, 10) *
547
+ 0.7 *
548
+ self.scale,
549
+ );
550
+ if (width + self.style.cellPaddingLeft < cell.width) {
551
+ width = cell.width - self.style.cellPaddingLeft;
552
+ } else if (width > self.width - cell.x - self.style.scrollBarWidth) {
553
+ width = self.width - cell.x - self.style.scrollBarWidth;
554
+ self.input.style.height = 'auto';
555
+ }
556
+ self.input.style.width = width + 'px';
431
557
  }
432
558
  });
433
559
 
@@ -435,6 +561,96 @@ export default function (self) {
435
561
  };
436
562
  self.createInlineStyle = function (el, className) {
437
563
  var css = {
564
+ 'canvas-datagrid-button-wrapper': {
565
+ display: 'inline-block',
566
+ padding: self.style.buttonPadding,
567
+ borderWidth: '1px',
568
+ borderStyle: 'solid',
569
+ borderColor: self.style.buttonBorderColor,
570
+ cursor: 'pointer',
571
+ background: self.style.buttonBackgroundColor,
572
+ userSelect: 'none',
573
+ },
574
+ 'canvas-datagrid-button-wrapper:hover': {
575
+ borderColor: self.style.buttonBorderColor,
576
+ background: self.style.buttonHoverBackgroundColor,
577
+ },
578
+ 'canvas-datagrid-button-wrapper:active': {
579
+ borderColor: self.style.buttonActiveBorderColor,
580
+ background: self.style.buttonActiveBackgroundColor,
581
+ },
582
+ 'canvas-datagrid-button-icon': {
583
+ width: '18px',
584
+ height: '18px',
585
+ display: 'inline-block',
586
+ verticalAlign: 'middle',
587
+ },
588
+ 'canvas-datagrid-button-arrow': {
589
+ display: 'inline-block',
590
+ color: self.style.buttonArrowColor,
591
+ fontSize: '9px',
592
+ },
593
+ 'canvas-datagrid-button-menu-item-mobile': {
594
+ lineHeight: 'normal',
595
+ fontWeight: 'normal',
596
+ fontFamily: self.style.contextMenuFontFamily,
597
+ fontSize: self.style.contextMenuFontSize,
598
+ color: 'inherit',
599
+ background: 'inherit',
600
+ margin: self.style.contextMenuItemMargin,
601
+ borderRadius: self.style.contextMenuItemBorderRadius,
602
+ verticalAlign: 'middle',
603
+ },
604
+ 'canvas-datagrid-button-menu-item': {
605
+ lineHeight: 'normal',
606
+ fontWeight: 'normal',
607
+ fontFamily: self.style.contextMenuFontFamily,
608
+ fontSize: self.style.contextMenuFontSize,
609
+ color: 'inherit',
610
+ background: 'inherit',
611
+ margin: self.style.contextMenuItemMargin,
612
+ borderRadius: self.style.contextMenuItemBorderRadius,
613
+ verticalAlign: 'middle',
614
+ },
615
+ 'canvas-datagrid-button-menu-item:hover': {
616
+ background: self.style.contextMenuHoverBackground,
617
+ color: self.style.contextMenuHoverColor,
618
+ },
619
+ 'canvas-datagrid-button-menu-label': {
620
+ margin: self.style.contextMenuLabelMargin,
621
+ display: self.style.contextMenuLabelDisplay,
622
+ minWidth: self.style.contextMenuLabelMinWidth,
623
+ maxWidth: self.style.contextMenuLabelMaxWidth,
624
+ },
625
+ 'canvas-datagrid-button-menu-mobile': {
626
+ lineHeight: 'normal',
627
+ fontWeight: 'normal',
628
+ fontFamily: self.style.contextMenuFontFamily,
629
+ fontSize: self.style.contextMenuFontSize,
630
+ background: self.style.contextMenuBackground,
631
+ color: self.style.contextMenuColor,
632
+ border: self.style.contextMenuBorder,
633
+ padding: self.style.contextMenuPadding,
634
+ borderRadius: self.style.contextMenuBorderRadius,
635
+ opacity: self.style.contextMenuOpacity,
636
+ overflow: 'hidden',
637
+ whiteSpace: 'nowrap',
638
+ },
639
+ 'canvas-datagrid-button-menu': {
640
+ lineHeight: 'normal',
641
+ fontWeight: 'normal',
642
+ fontFamily: self.style.contextMenuFontFamily,
643
+ fontSize: self.style.contextMenuFontSize,
644
+ background: self.style.contextMenuBackground,
645
+ color: self.style.contextMenuColor,
646
+ border: self.style.contextMenuBorder,
647
+ padding: self.style.contextMenuPadding,
648
+ borderRadius: self.style.contextMenuBorderRadius,
649
+ opacity: self.style.contextMenuOpacity,
650
+ overflow: 'hidden',
651
+ whiteSpace: 'nowrap',
652
+ cursor: self.style.contextMenuCursor,
653
+ },
438
654
  'canvas-datagrid-context-menu-filter-input': {
439
655
  height: '19px',
440
656
  verticalAlign: 'bottom',
@@ -616,6 +832,7 @@ export default function (self) {
616
832
  Object.keys(css[className]).map(function (prop) {
617
833
  el.style[prop] = css[className][prop];
618
834
  });
835
+ el.className = className;
619
836
  }
620
837
  return;
621
838
  };
@@ -660,12 +877,15 @@ export default function (self) {
660
877
  }
661
878
  });
662
879
  self.eventParent.addEventListener('scroll', self.resize, false);
663
- self.eventParent.addEventListener('touchstart', self.touchstart, false);
664
- self.eventParent.addEventListener('mouseup', self.mouseup, false);
880
+ self.eventParent.addEventListener('touchstart', self.touchstart, {
881
+ passive: true,
882
+ });
665
883
  self.eventParent.addEventListener('mousedown', self.mousedown, false);
666
884
  self.eventParent.addEventListener('dblclick', self.dblclick, false);
667
885
  self.eventParent.addEventListener('click', self.click, false);
668
- self.eventParent.addEventListener('mousemove', self.mousemove);
886
+ self.eventParent.addEventListener('mouseup', self.mouseup, false);
887
+ window.addEventListener('mouseup', self.mouseupOutsideGrid, false);
888
+ window.addEventListener('mousemove', self.mousemove);
669
889
  self[self.isChildGrid ? 'parentGrid' : 'eventParent'].addEventListener(
670
890
  'wheel',
671
891
  self.scrollWheel,
@@ -679,5 +899,6 @@ export default function (self) {
679
899
  self.controlInput.addEventListener('keyup', self.keyup, false);
680
900
  self.controlInput.addEventListener('keydown', self.keydown, false);
681
901
  window.addEventListener('resize', self.resize);
902
+ self.initResizeObserver();
682
903
  };
683
904
  }