@dxos/lit-grid 0.8.3 → 0.8.4-main.16b68245aa

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 (107) hide show
  1. package/dist/src/defs.d.ts +1 -0
  2. package/dist/src/defs.d.ts.map +1 -1
  3. package/dist/src/defs.js +1 -0
  4. package/dist/src/defs.js.map +1 -1
  5. package/dist/src/dx-grid-axis-resize-handle.d.ts.map +1 -1
  6. package/dist/src/dx-grid-axis-resize-handle.js +3 -5
  7. package/dist/src/dx-grid-axis-resize-handle.js.map +1 -1
  8. package/dist/src/dx-grid-multiselect-cell.d.ts.map +1 -1
  9. package/dist/src/dx-grid-multiselect-cell.js +2 -1
  10. package/dist/src/dx-grid-multiselect-cell.js.map +1 -1
  11. package/dist/src/dx-grid.d.ts +15 -9
  12. package/dist/src/dx-grid.d.ts.map +1 -1
  13. package/dist/src/dx-grid.js +253 -172
  14. package/dist/src/dx-grid.js.map +1 -1
  15. package/dist/src/dx-grid.lit-stories.d.ts +13 -15
  16. package/dist/src/dx-grid.lit-stories.d.ts.map +1 -1
  17. package/dist/src/dx-grid.lit-stories.js +22 -23
  18. package/dist/src/dx-grid.lit-stories.js.map +1 -1
  19. package/dist/src/playwright/dx-grid.spec.d.ts.map +1 -0
  20. package/dist/src/{testing/playwright → playwright}/dx-grid.spec.js +3 -3
  21. package/dist/src/playwright/dx-grid.spec.js.map +1 -0
  22. package/dist/src/playwright/playwright.config.d.ts +3 -0
  23. package/dist/src/playwright/playwright.config.d.ts.map +1 -0
  24. package/dist/src/playwright/playwright.config.js +15 -0
  25. package/dist/src/playwright/playwright.config.js.map +1 -0
  26. package/dist/src/testing/{playwright/dx-grid-manager.d.ts → dx-grid-manager.d.ts} +1 -1
  27. package/dist/src/testing/dx-grid-manager.d.ts.map +1 -0
  28. package/dist/src/testing/dx-grid-manager.js.map +1 -0
  29. package/dist/src/testing/index.d.ts +1 -1
  30. package/dist/src/testing/index.d.ts.map +1 -1
  31. package/dist/src/testing/index.js +1 -1
  32. package/dist/src/testing/index.js.map +1 -1
  33. package/dist/src/types.d.ts +10 -1
  34. package/dist/src/types.d.ts.map +1 -1
  35. package/dist/src/types.js.map +1 -1
  36. package/dist/src/util.d.ts +8 -5
  37. package/dist/src/util.d.ts.map +1 -1
  38. package/dist/src/util.js +11 -11
  39. package/dist/src/util.js.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +15 -14
  42. package/src/defs.ts +1 -0
  43. package/src/dx-grid-axis-resize-handle.pcss +10 -3
  44. package/src/dx-grid-axis-resize-handle.ts +1 -1
  45. package/src/dx-grid-multiselect-cell.pcss +8 -6
  46. package/src/dx-grid-multiselect-cell.ts +2 -1
  47. package/src/dx-grid.lit-stories.ts +6 -4
  48. package/src/dx-grid.pcss +146 -20
  49. package/src/dx-grid.ts +268 -123
  50. package/src/{testing/playwright → playwright}/dx-grid.spec.ts +5 -5
  51. package/src/playwright/playwright.config.ts +17 -0
  52. package/src/testing/{playwright/dx-grid-manager.ts → dx-grid-manager.ts} +2 -2
  53. package/src/testing/index.ts +1 -1
  54. package/src/types.ts +11 -0
  55. package/src/util.ts +13 -9
  56. package/dist/src/testing/playwright/dx-grid-manager.d.ts.map +0 -1
  57. package/dist/src/testing/playwright/dx-grid-manager.js.map +0 -1
  58. package/dist/src/testing/playwright/dx-grid.spec.d.ts.map +0 -1
  59. package/dist/src/testing/playwright/dx-grid.spec.js.map +0 -1
  60. package/dist/types/src/defs.d.ts +0 -3
  61. package/dist/types/src/defs.d.ts.map +0 -1
  62. package/dist/types/src/defs.js +0 -6
  63. package/dist/types/src/defs.js.map +0 -1
  64. package/dist/types/src/dx-grid-axis-resize-handle.d.ts +0 -16
  65. package/dist/types/src/dx-grid-axis-resize-handle.d.ts.map +0 -1
  66. package/dist/types/src/dx-grid-axis-resize-handle.js +0 -100
  67. package/dist/types/src/dx-grid-axis-resize-handle.js.map +0 -1
  68. package/dist/types/src/dx-grid-multiselect-cell.d.ts +0 -13
  69. package/dist/types/src/dx-grid-multiselect-cell.d.ts.map +0 -1
  70. package/dist/types/src/dx-grid-multiselect-cell.js +0 -56
  71. package/dist/types/src/dx-grid-multiselect-cell.js.map +0 -1
  72. package/dist/types/src/dx-grid.d.ts +0 -170
  73. package/dist/types/src/dx-grid.d.ts.map +0 -1
  74. package/dist/types/src/dx-grid.js +0 -1347
  75. package/dist/types/src/dx-grid.js.map +0 -1
  76. package/dist/types/src/dx-grid.lit-stories.d.ts +0 -46
  77. package/dist/types/src/dx-grid.lit-stories.d.ts.map +0 -1
  78. package/dist/types/src/dx-grid.lit-stories.js +0 -179
  79. package/dist/types/src/dx-grid.lit-stories.js.map +0 -1
  80. package/dist/types/src/index.d.ts +0 -5
  81. package/dist/types/src/index.d.ts.map +0 -1
  82. package/dist/types/src/index.js +0 -8
  83. package/dist/types/src/index.js.map +0 -1
  84. package/dist/types/src/testing/index.d.ts +0 -2
  85. package/dist/types/src/testing/index.d.ts.map +0 -1
  86. package/dist/types/src/testing/index.js +0 -5
  87. package/dist/types/src/testing/index.js.map +0 -1
  88. package/dist/types/src/testing/playwright/dx-grid-manager.d.ts +0 -24
  89. package/dist/types/src/testing/playwright/dx-grid-manager.d.ts.map +0 -1
  90. package/dist/types/src/testing/playwright/dx-grid-manager.js +0 -79
  91. package/dist/types/src/testing/playwright/dx-grid-manager.js.map +0 -1
  92. package/dist/types/src/testing/playwright/dx-grid.spec.d.ts +0 -2
  93. package/dist/types/src/testing/playwright/dx-grid.spec.d.ts.map +0 -1
  94. package/dist/types/src/testing/playwright/dx-grid.spec.js +0 -92
  95. package/dist/types/src/testing/playwright/dx-grid.spec.js.map +0 -1
  96. package/dist/types/src/types.d.ts +0 -137
  97. package/dist/types/src/types.d.ts.map +0 -1
  98. package/dist/types/src/types.js +0 -46
  99. package/dist/types/src/types.js.map +0 -1
  100. package/dist/types/src/util.d.ts +0 -39
  101. package/dist/types/src/util.d.ts.map +0 -1
  102. package/dist/types/src/util.js +0 -165
  103. package/dist/types/src/util.js.map +0 -1
  104. package/dist/types/tsconfig.tsbuildinfo +0 -1
  105. package/src/testing/playwright/playwright.config.cts +0 -18
  106. /package/dist/src/{testing/playwright → playwright}/dx-grid.spec.d.ts +0 -0
  107. /package/dist/src/testing/{playwright/dx-grid-manager.js → dx-grid-manager.js} +0 -0
package/src/dx-grid.ts CHANGED
@@ -2,75 +2,77 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import './dx-grid-axis-resize-handle';
6
+
5
7
  import { LitElement, html, nothing } from 'lit';
6
- import { customElement, state, property } from 'lit/decorators.js';
7
- import { ref, createRef, type Ref } from 'lit/directives/ref.js';
8
+ import { customElement, property, state } from 'lit/decorators.js';
9
+ import { type Ref, createRef, ref } from 'lit/directives/ref.js';
8
10
  import { styleMap } from 'lit/directives/style-map.js';
9
- import { unsafeStatic, html as staticHtml } from 'lit/static-html.js';
11
+ import { html as staticHtml, unsafeStatic } from 'lit/static-html.js';
10
12
 
11
- import { defaultColSize, defaultRowSize } from './defs';
12
- // eslint-disable-next-line unused-imports/no-unused-imports
13
- import './dx-grid-axis-resize-handle';
13
+ import { defaultColSize, defaultRowSize, focusUnfurlDefault } from './defs';
14
14
  import {
15
- type DxGridAxisMetaProps,
16
- type DxGridAxisSizes,
17
- type DxGridPlaneCellIndex,
18
- type DxGridCellValue,
19
15
  DxAxisResize,
20
16
  type DxAxisResizeInternal,
21
17
  DxEditRequest,
18
+ type DxGridAnnotatedPanEvent,
19
+ type DxGridAxis,
22
20
  type DxGridAxisMeta,
21
+ type DxGridAxisMetaProps,
22
+ type DxGridAxisSizes,
23
+ type DxGridCellValue,
23
24
  type DxGridCells,
24
25
  DxGridCellsSelect,
25
26
  type DxGridFixedPlane,
27
+ type DxGridFocusIndicatorVariant,
26
28
  type DxGridFrozenAxes,
27
29
  type DxGridFrozenColsPlane,
28
30
  type DxGridFrozenPlane,
29
31
  type DxGridFrozenRowsPlane,
30
32
  type DxGridMode,
33
+ type DxGridOverscroll,
31
34
  type DxGridPlane,
35
+ type DxGridPlaneCellIndex,
32
36
  type DxGridPlaneCells,
33
37
  type DxGridPlaneRange,
34
38
  type DxGridPlaneRecord,
35
39
  type DxGridPointer,
36
40
  type DxGridPosition,
37
- type DxGridAxis,
38
- type DxGridSelectionProps,
39
- type DxGridAnnotatedPanEvent,
40
41
  type DxGridRange,
42
+ type DxGridSelectionProps,
41
43
  separator,
42
44
  } from './types';
43
45
  import {
44
- toCellIndex,
45
- gap,
46
- resizeTolerance,
47
- sizeColMin,
48
- sizeColMax,
49
- sizeRowMin,
50
- sizeRowMax,
51
- shouldSelect,
52
- selectionProps,
53
46
  cellSelected,
54
47
  closestAction,
55
48
  closestCell,
56
- targetIsPlane,
57
- resolveRowPlane,
49
+ gap,
50
+ isReadonly,
51
+ isSameCell,
52
+ resizeTolerance,
58
53
  resolveColPlane,
59
54
  resolveFrozenPlane,
60
- isSameCell,
61
- isReadonly,
55
+ resolveRowPlane,
56
+ selectionProps,
57
+ shouldSelect,
58
+ sizeColMax,
59
+ sizeColMin,
60
+ sizeRowMax,
61
+ sizeRowMin,
62
+ targetIsPlane,
63
+ toCellIndex,
62
64
  } from './util';
63
65
 
64
66
  @customElement('dx-grid')
65
67
  export class DxGrid extends LitElement {
66
68
  constructor() {
67
69
  super();
68
- // Wheel, top-level and element-level
70
+ // Wheel, top-level and element-level.
69
71
  document.defaultView?.addEventListener('wheel', this.handleTopLevelWheel, { passive: false });
70
72
  this.addEventListener('wheel', this.handleWheel);
71
- // Custom event(s)
73
+ // Custom event(s).
72
74
  this.addEventListener('dx-axis-resize-internal', this.handleAxisResizeInternal as EventListener);
73
- // Standard events
75
+ // Standard events.
74
76
  this.addEventListener('pointerdown', this.handlePointerDown);
75
77
  this.addEventListener('pointermove', this.handlePointerMove);
76
78
  this.addEventListener('pointerup', this.handlePointerUp);
@@ -84,12 +86,12 @@ export class DxGrid extends LitElement {
84
86
  gridId: string = 'default-grid-id';
85
87
 
86
88
  @property({ type: Object })
87
- rowDefault: DxGridPlaneRecord<DxGridFrozenRowsPlane, DxGridAxisMetaProps> = {
89
+ rowDefault: Partial<DxGridPlaneRecord<DxGridFrozenRowsPlane, Partial<DxGridAxisMetaProps>>> = {
88
90
  grid: { size: defaultRowSize },
89
91
  };
90
92
 
91
93
  @property({ type: Object })
92
- columnDefault: DxGridPlaneRecord<DxGridFrozenColsPlane, DxGridAxisMetaProps> = {
94
+ columnDefault: Partial<DxGridPlaneRecord<DxGridFrozenColsPlane, Partial<DxGridAxisMetaProps>>> = {
93
95
  grid: { size: defaultColSize },
94
96
  };
95
97
 
@@ -115,11 +117,14 @@ export class DxGrid extends LitElement {
115
117
  frozen: DxGridFrozenAxes = {};
116
118
 
117
119
  @property({ type: String })
118
- overscroll: 'inline' | 'block' | 'trap' | undefined = undefined;
120
+ overscroll: DxGridOverscroll = undefined;
119
121
 
120
122
  @property({ type: String })
121
123
  activeRefs = '';
122
124
 
125
+ @property({ type: String })
126
+ focusIndicatorVariant: DxGridFocusIndicatorVariant = 'sheet';
127
+
123
128
  /**
124
129
  * When this function is defined, it is used first to try to get a value for a cell,
125
130
  * and otherwise will fall back to `cells`.
@@ -195,6 +200,7 @@ export class DxGrid extends LitElement {
195
200
  //
196
201
  // `template` is the rendered value of `grid-{axis}-template`.
197
202
  //
203
+
198
204
  @state()
199
205
  private templateGridColumns = '0';
200
206
 
@@ -213,6 +219,16 @@ export class DxGrid extends LitElement {
213
219
  @state()
214
220
  private templatefrozenRowsEnd = '';
215
221
 
222
+ //
223
+ // `frozen…Size` is used to measure space available for the non-fixed planes
224
+ //
225
+
226
+ @state()
227
+ private frozenColsSize = 0;
228
+
229
+ @state()
230
+ private frozenRowsSize = 0;
231
+
216
232
  //
217
233
  // Focus, selection, and resize states
218
234
  //
@@ -308,7 +324,7 @@ export class DxGrid extends LitElement {
308
324
  this.dispatchSelectionChange();
309
325
  }
310
326
  if (this.mode === 'edit-select') {
311
- // Prevent focus moving when editing while selection is possible
327
+ // Prevent focus moving when editing while selection is possible.
312
328
  event.preventDefault();
313
329
  } else if (this.focusActive && isSameCell(this.focusedCell, cellCoords)) {
314
330
  this.dispatchEditRequest();
@@ -416,15 +432,13 @@ export class DxGrid extends LitElement {
416
432
  }
417
433
 
418
434
  private moveFocusIntoPlane(plane: DxGridPlane): void {
419
- if (this.focusedCell.plane !== plane) {
420
- const colPlane = resolveColPlane(plane);
421
- const rowPlane = resolveRowPlane(plane);
422
- this.focusedCell = {
423
- plane,
424
- col: colPlane === 'grid' ? this.visColMin : 0,
425
- row: rowPlane === 'grid' ? this.visRowMin : 0,
426
- };
427
- }
435
+ const colPlane = resolveColPlane(plane);
436
+ const rowPlane = resolveRowPlane(plane);
437
+ this.focusedCell = {
438
+ plane,
439
+ col: colPlane === 'grid' ? this.visColMin : 0,
440
+ row: rowPlane === 'grid' ? this.visRowMin : 0,
441
+ };
428
442
  this.focusedCellElement()?.focus({ preventScroll: true });
429
443
  }
430
444
 
@@ -481,6 +495,13 @@ export class DxGrid extends LitElement {
481
495
  event.preventDefault();
482
496
  this.dispatchEditRequest();
483
497
  break;
498
+ case 'Backspace':
499
+ case 'Delete':
500
+ if (!event.defaultPrevented) {
501
+ event.preventDefault();
502
+ this.dispatchEditRequest('');
503
+ }
504
+ break;
484
505
  default:
485
506
  if (event.key.length === 1 && event.key.match(/\P{Cc}/u) && !(event.metaKey || event.ctrlKey)) {
486
507
  this.dispatchEditRequest(event.key);
@@ -574,7 +595,7 @@ export class DxGrid extends LitElement {
574
595
  }
575
596
 
576
597
  //
577
- // Resize & reposition handlers, observer, ref
598
+ // Resize & reposition handlers, observer, ref.
578
599
  //
579
600
 
580
601
  @state()
@@ -584,19 +605,18 @@ export class DxGrid extends LitElement {
584
605
  blockSize: 0,
585
606
  };
586
607
  if (
587
- Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||
588
- Math.abs(blockSize - this.sizeBlock) > resizeTolerance
608
+ Math.abs(inlineSize - this.frozenColsSize - this.sizeInline) > resizeTolerance ||
609
+ Math.abs(blockSize - this.frozenRowsSize - this.sizeBlock) > resizeTolerance
589
610
  ) {
590
611
  // console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);
591
- this.sizeInline = inlineSize;
592
- this.sizeBlock = blockSize;
612
+ this.sizeInline = inlineSize - this.frozenColsSize;
613
+ this.sizeBlock = blockSize - this.frozenRowsSize;
593
614
  this.updateVis();
594
615
  queueMicrotask(() => this.updatePos());
595
616
  }
596
617
  });
597
618
 
598
619
  private gridRef: Ref<HTMLDivElement> = createRef();
599
- private viewportRef: Ref<HTMLDivElement> = createRef();
600
620
 
601
621
  private maybeUpdateVisInline = () => {
602
622
  if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
@@ -640,16 +660,29 @@ export class DxGrid extends LitElement {
640
660
  (this.overscroll === 'inline' && event.overscrollInline === 0) ||
641
661
  (this.overscroll === 'block' && event.overscrollBlock === 0)
642
662
  ) {
643
- event.preventDefault();
644
- event.stopPropagation();
663
+ const element = event.target as HTMLElement;
664
+ const activeCell = element.closest('[data-dx-active]');
665
+ const contentEl = element.closest('.dx-grid__cell__content');
666
+ if (
667
+ !(
668
+ element &&
669
+ activeCell &&
670
+ contentEl &&
671
+ (contentEl.scrollWidth > contentEl.clientWidth || contentEl.scrollHeight > contentEl.clientHeight)
672
+ )
673
+ ) {
674
+ event.preventDefault();
675
+ event.stopPropagation();
676
+ }
645
677
  }
646
678
  }
647
679
  };
648
680
 
649
681
  private handleWheel = (event: DxGridAnnotatedPanEvent) => {
650
682
  if (this.mode === 'browse') {
651
- const nextPosInline = this.posInline + event.deltaX;
652
- const nextPosBlock = this.posBlock + event.deltaY;
683
+ const { deltaX, deltaY } = this.getOverflowingCellModifiedDeltas(event);
684
+ const nextPosInline = this.posInline + deltaX;
685
+ const nextPosBlock = this.posBlock + deltaY;
653
686
  const maxPosInline = this.maxPosInline();
654
687
  const maxPosBlock = this.maxPosBlock();
655
688
  this.updatePos(nextPosInline, nextPosBlock, maxPosInline, maxPosBlock);
@@ -662,7 +695,7 @@ export class DxGrid extends LitElement {
662
695
  };
663
696
 
664
697
  private updateVisInline(): void {
665
- // todo: avoid starting from zero
698
+ // todo: avoid starting from zero.
666
699
  let axisCursor = 0;
667
700
  let pxCursor = this.colSize(axisCursor, 'grid');
668
701
 
@@ -708,10 +741,19 @@ export class DxGrid extends LitElement {
708
741
  this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
709
742
  .map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
710
743
  .join(' ');
744
+
745
+ this.frozenColsSize =
746
+ [...Array(this.frozen.frozenColsStart ?? 0)].reduce(
747
+ (sum, _, c0) => sum + this.colSize(c0, 'frozenColsStart'),
748
+ 0,
749
+ ) +
750
+ gap * Math.max(0, this.frozen.frozenColsStart ?? 0 - 1) +
751
+ [...Array(this.frozen.frozenColsEnd ?? 0)].reduce((sum, _, c0) => sum + this.colSize(c0, 'frozenColsEnd'), 0) +
752
+ gap * Math.max(0, this.frozen.frozenColsEnd ?? 0 - 1);
711
753
  }
712
754
 
713
755
  private updateVisBlock(): void {
714
- // todo: avoid starting from zero
756
+ // todo: avoid starting from zero.
715
757
  let axisCursor = 0;
716
758
  let pxCursor = this.rowSize(axisCursor, 'grid');
717
759
 
@@ -757,6 +799,15 @@ export class DxGrid extends LitElement {
757
799
  this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
758
800
  .map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
759
801
  .join(' ');
802
+
803
+ this.frozenRowsSize =
804
+ [...Array(this.frozen.frozenRowsStart ?? 0)].reduce(
805
+ (sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsStart'),
806
+ 0,
807
+ ) +
808
+ gap * Math.max(0, this.frozen.frozenRowsStart ?? 0 - 1) +
809
+ [...Array(this.frozen.frozenRowsEnd ?? 0)].reduce((sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsEnd'), 0) +
810
+ gap * Math.max(0, this.frozen.frozenRowsEnd ?? 0 - 1);
760
811
  }
761
812
 
762
813
  private updateVis(): void {
@@ -978,7 +1029,7 @@ export class DxGrid extends LitElement {
978
1029
  }
979
1030
 
980
1031
  /**
981
- * Updates `pos` so that a cell in focus is fully within the viewport
1032
+ * Updates `pos` so that a cell in focus is fully within the viewport.
982
1033
  */
983
1034
  snapPosToFocusedCell(): void {
984
1035
  const outOfVis = this.focusedCellOutOfVis();
@@ -1055,16 +1106,40 @@ export class DxGrid extends LitElement {
1055
1106
  : !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
1056
1107
  }
1057
1108
 
1109
+ private clampAxisSize(
1110
+ plane: 'grid' | DxGridFrozenPlane,
1111
+ axis: DxGridAxis,
1112
+ index: number | string,
1113
+ requestedSize: number,
1114
+ ): number {
1115
+ const minSize =
1116
+ axis === 'col'
1117
+ ? (this.columns[plane]?.[index]?.minSize ??
1118
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.minSize ??
1119
+ sizeColMin)
1120
+ : (this.rows[plane]?.[index]?.minSize ??
1121
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.minSize ??
1122
+ sizeRowMin);
1123
+ const maxSize =
1124
+ axis === 'col'
1125
+ ? (this.columns[plane]?.[index]?.maxSize ??
1126
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.maxSize ??
1127
+ sizeColMax)
1128
+ : (this.rows[plane]?.[index]?.maxSize ??
1129
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.maxSize ??
1130
+ sizeRowMax);
1131
+ return Math.max(minSize, Math.min(maxSize, requestedSize));
1132
+ }
1133
+
1058
1134
  private handleAxisResizeInternal(event: DxAxisResizeInternal): void {
1059
1135
  event.stopPropagation();
1060
1136
  const { plane, axis, delta, size, index, state } = event;
1137
+ const nextSize = this.clampAxisSize(plane, axis, index, size + delta);
1061
1138
  if (axis === 'col') {
1062
- const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
1063
1139
  this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
1064
1140
  this.updateVisInline();
1065
1141
  this.updateIntrinsicInlineSize();
1066
1142
  } else {
1067
- const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
1068
1143
  this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
1069
1144
  this.updateVisBlock();
1070
1145
  this.updateIntrinsicBlockSize();
@@ -1082,7 +1157,7 @@ export class DxGrid extends LitElement {
1082
1157
  }
1083
1158
 
1084
1159
  //
1085
- // Render and other lifecycle methods
1160
+ // Render and other lifecycle methods.
1086
1161
  //
1087
1162
 
1088
1163
  // TODO(thure): This is for rendering presentational objects superimposed onto the canonical grid (e.g. DnD drop line for #8108).
@@ -1165,7 +1240,7 @@ export class DxGrid extends LitElement {
1165
1240
  ) {
1166
1241
  const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
1167
1242
  const rows = this.frozen[rowPlane];
1168
- return (rows ?? 0) > 0
1243
+ return (rows ?? 0) > 0 && this.limitColumns > 0
1169
1244
  ? html`<div
1170
1245
  role="none"
1171
1246
  class="dx-grid__plane--frozen-row"
@@ -1199,7 +1274,7 @@ export class DxGrid extends LitElement {
1199
1274
  ) {
1200
1275
  const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
1201
1276
  const cols = this.frozen[colPlane];
1202
- return (cols ?? 0) > 0
1277
+ return (cols ?? 0) > 0 && this.limitRows > 0
1203
1278
  ? html`<div
1204
1279
  role="none"
1205
1280
  class="dx-grid__plane--frozen-col"
@@ -1225,6 +1300,40 @@ export class DxGrid extends LitElement {
1225
1300
  : null;
1226
1301
  }
1227
1302
 
1303
+ private renderMainGrid(
1304
+ visibleCols: number,
1305
+ visibleRows: number,
1306
+ offsetInline: number,
1307
+ offsetBlock: number,
1308
+ selection: DxGridSelectionProps,
1309
+ ) {
1310
+ return this.limitRows > 0 && this.limitColumns > 0
1311
+ ? html`<div
1312
+ role="grid"
1313
+ class="dx-grid__plane--grid"
1314
+ tabindex="0"
1315
+ data-dx-grid-plane="grid"
1316
+ data-dx-grid-plane-row="1"
1317
+ data-dx-grid-plane-col="1"
1318
+ >
1319
+ <div
1320
+ role="none"
1321
+ class="dx-grid__plane--grid__content"
1322
+ style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1323
+ .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1324
+ >
1325
+ ${[...Array(visibleRows)].map((_, r0) => {
1326
+ return [...Array(visibleCols)].map((_, c0) => {
1327
+ const c = c0 + this.visColMin;
1328
+ const r = r0 + this.visRowMin;
1329
+ return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1330
+ });
1331
+ })}
1332
+ </div>
1333
+ </div>`
1334
+ : null;
1335
+ }
1336
+
1228
1337
  private cellReadonly(col: number, row: number, plane: DxGridPlane): boolean {
1229
1338
  const colPlane = resolveColPlane(plane);
1230
1339
  const rowPlane = resolveRowPlane(plane);
@@ -1242,6 +1351,23 @@ export class DxGrid extends LitElement {
1242
1351
  return isReadonly(colReadOnly) || isReadonly(rowReadOnly);
1243
1352
  }
1244
1353
 
1354
+ private cellFocusUnfurl(col: number, row: number, plane: DxGridPlane): boolean {
1355
+ const colPlane = resolveColPlane(plane);
1356
+ const rowPlane = resolveRowPlane(plane);
1357
+
1358
+ // Check cell-specific setting first.
1359
+ const cellUnfurl = this.cell(col, row, plane)?.focusUnfurl;
1360
+ if (cellUnfurl !== undefined) {
1361
+ return cellUnfurl;
1362
+ }
1363
+
1364
+ // Check column/row defaults.
1365
+ const colUnfurl = this.columns?.[colPlane]?.[col]?.focusUnfurl ?? this.columnDefault?.[colPlane]?.focusUnfurl;
1366
+ const rowUnfurl = this.rows?.[rowPlane]?.[row]?.focusUnfurl ?? this.rowDefault?.[rowPlane]?.focusUnfurl;
1367
+
1368
+ return colUnfurl ?? rowUnfurl ?? focusUnfurlDefault;
1369
+ }
1370
+
1245
1371
  /**
1246
1372
  * Determines if the cell's text content should be selectable based on its readonly value.
1247
1373
  * @returns true if the cells text content is selectable, false otherwise.
@@ -1262,10 +1388,49 @@ export class DxGrid extends LitElement {
1262
1388
  return colReadonly === 'text-select' || rowReadonly === 'text-select';
1263
1389
  }
1264
1390
 
1391
+ private getOverflowingCellModifiedDeltas(
1392
+ event: DxGridAnnotatedPanEvent,
1393
+ ): Pick<DxGridAnnotatedPanEvent, 'deltaX' | 'deltaY'> {
1394
+ if (!event.target) {
1395
+ return event;
1396
+ }
1397
+ const element = event.target as HTMLElement;
1398
+ const activeCell = element.closest('[data-dx-active]');
1399
+ const contentEl = element.closest('.dx-grid__cell__content');
1400
+
1401
+ if (!activeCell || !contentEl || !document.activeElement?.contains(element)) {
1402
+ return event;
1403
+ }
1404
+
1405
+ // Commented-out code will let the event delta through unmodified if the cell can scroll but is scrolled to the end
1406
+ // in the same direction as the wheel event, a.k.a. “overscroll”; this is probably undesirable, though.
1407
+
1408
+ const { scrollWidth, clientWidth, scrollHeight, clientHeight /*, scrollLeft, scrollTop */ } = contentEl;
1409
+
1410
+ if (scrollWidth <= clientWidth && scrollHeight <= clientHeight) {
1411
+ return event;
1412
+ }
1413
+
1414
+ const deltaX =
1415
+ scrollWidth > clientWidth /* &&
1416
+ ((event.deltaX < 0 && scrollLeft > 0) || (event.deltaX > 0 && scrollLeft < scrollWidth - clientWidth)) */
1417
+ ? 0
1418
+ : event.deltaX;
1419
+
1420
+ const deltaY =
1421
+ scrollHeight > clientHeight /* &&
1422
+ ((event.deltaY < 0 && scrollTop > 0) || (event.deltaY > 0 && scrollTop < scrollHeight - clientHeight)) */
1423
+ ? 0
1424
+ : event.deltaY;
1425
+
1426
+ return { deltaX, deltaY };
1427
+ }
1428
+
1265
1429
  private renderCell(col: number, row: number, plane: DxGridPlane, selected?: boolean, visCol = col, visRow = row) {
1266
1430
  const cell = this.cell(col, row, plane);
1267
1431
  const active = this.cellActive(col, row, plane);
1268
1432
  const readonly = this.cellReadonly(col, row, plane);
1433
+ const focusUnfurl = this.cellFocusUnfurl(col, row, plane);
1269
1434
  const textSelectable = this.cellTextSelectable(col, row, plane);
1270
1435
  const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
1271
1436
  const resizePlane = cell?.resizeHandle ? resolveFrozenPlane(cell.resizeHandle, plane) : undefined;
@@ -1277,16 +1442,17 @@ export class DxGrid extends LitElement {
1277
1442
  aria-readonly=${readonly ? 'true' : nothing}
1278
1443
  class=${cell?.className ?? nothing}
1279
1444
  data-refs=${cell?.dataRefs ?? nothing}
1445
+ data-focus-unfurl=${focusUnfurl ? nothing : 'false'}
1280
1446
  ?data-dx-active=${active}
1281
1447
  data-text-selectable=${textSelectable ? 'true' : 'false'}
1282
1448
  data-dx-grid-action="cell"
1283
1449
  aria-colindex=${col}
1284
1450
  aria-rowindex=${row}
1451
+ data-testid=${`${plane}.${col}.${row}`}
1285
1452
  style="grid-column:${visCol + 1};grid-row:${visRow + 1}"
1286
1453
  >
1287
- ${this.mode !== 'browse' && active ? null : cell?.value}${this.mode !== 'browse' && active
1288
- ? null
1289
- : accessory}${cell?.resizeHandle &&
1454
+ <div role="none" class="dx-grid__cell__content">${cell?.value}${accessory}</div>
1455
+ ${cell?.resizeHandle &&
1290
1456
  this.mode === 'browse' &&
1291
1457
  this.axisResizeable(resizePlane!, cell.resizeHandle, resizeIndex!)
1292
1458
  ? html`<dx-grid-axis-resize-handle
@@ -1311,22 +1477,31 @@ export class DxGrid extends LitElement {
1311
1477
  .split(' ')
1312
1478
  .filter((value) => value)
1313
1479
  .map(
1314
- // TODO(burdon): Consistent camelCase?
1315
- (activeRef) =>
1316
- `[data-refs~="${activeRef}"] { background: var(--dx-grid-commented-active, var(--dx-gridCommentedActive)) !important; }`,
1480
+ (activeRef) => `[data-refs~="${activeRef}"] { background: var(--color-grid-comment-active) !important; }`,
1317
1481
  )
1318
1482
  .join('\n')}
1319
1483
  </style>
1320
1484
  <div
1321
1485
  role="none"
1322
1486
  class="dx-grid"
1487
+ data-arrow-keys="all"
1323
1488
  style=${styleMap({
1324
- 'grid-template-columns': `${this.templatefrozenColsStart ? 'min-content ' : ''}minmax(0, ${
1325
- Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'
1326
- })${this.templatefrozenColsEnd ? ' min-content' : ''}`,
1327
- 'grid-template-rows': `${this.templatefrozenRowsStart ? 'min-content ' : ''}minmax(0, ${
1328
- Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'
1329
- })${this.templatefrozenRowsEnd ? ' min-content' : ''}`,
1489
+ 'grid-template-columns': [
1490
+ this.templatefrozenColsStart ? 'min-content' : false,
1491
+ this.limitColumns > 0 &&
1492
+ `minmax(0, ${Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'})`,
1493
+ this.templatefrozenColsEnd ? 'min-content' : false,
1494
+ ]
1495
+ .filter(Boolean)
1496
+ .join(' '),
1497
+ 'grid-template-rows': [
1498
+ this.templatefrozenRowsStart ? 'min-content' : false,
1499
+ this.limitRows > 0 &&
1500
+ `minmax(0, ${Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'})`,
1501
+ this.templatefrozenRowsEnd ? ' min-content' : false,
1502
+ ]
1503
+ .filter(Boolean)
1504
+ .join(' '),
1330
1505
  '--dx-grid-content-inline-size': Number.isFinite(this.limitColumns)
1331
1506
  ? `${Math.max(0, this.totalIntrinsicInlineSize)}px`
1332
1507
  : 'max-content',
@@ -1336,6 +1511,7 @@ export class DxGrid extends LitElement {
1336
1511
  })}
1337
1512
  data-grid=${this.gridId}
1338
1513
  data-grid-mode=${this.mode}
1514
+ data-grid-focus-indicator-variant=${this.focusIndicatorVariant}
1339
1515
  ?data-grid-select=${selection.visible}
1340
1516
  ${ref(this.gridRef)}
1341
1517
  >
@@ -1350,30 +1526,7 @@ export class DxGrid extends LitElement {
1350
1526
  offsetBlock,
1351
1527
  selection,
1352
1528
  )}
1353
- <div
1354
- role="grid"
1355
- class="dx-grid__plane--grid"
1356
- tabindex="0"
1357
- data-dx-grid-plane="grid"
1358
- data-dx-grid-plane-row="1"
1359
- data-dx-grid-plane-col="1"
1360
- ${ref(this.viewportRef)}
1361
- >
1362
- <div
1363
- role="none"
1364
- class="dx-grid__plane--grid__content"
1365
- style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1366
- .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1367
- >
1368
- ${[...Array(visibleRows)].map((_, r0) => {
1369
- return [...Array(visibleCols)].map((_, c0) => {
1370
- const c = c0 + this.visColMin;
1371
- const r = r0 + this.visRowMin;
1372
- return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1373
- });
1374
- })}
1375
- </div>
1376
- </div>
1529
+ ${this.renderMainGrid(visibleCols, visibleRows, offsetInline, offsetBlock, selection)}
1377
1530
  ${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
1378
1531
  'fixedEndStart',
1379
1532
  selection,
@@ -1387,37 +1540,19 @@ export class DxGrid extends LitElement {
1387
1540
  private updateIntrinsicInlineSize(): void {
1388
1541
  this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
1389
1542
  ? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
1390
- gap * (this.limitColumns - 1)
1543
+ gap * Math.max(0, this.limitColumns - 1)
1391
1544
  : Infinity;
1392
1545
  this.totalIntrinsicInlineSize =
1393
- this.intrinsicInlineSize +
1394
- (Number.isFinite(this.frozen.frozenColsStart)
1395
- ? [...Array(this.frozen.frozenColsStart)].reduce(
1396
- (acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsStart'),
1397
- 0,
1398
- )
1399
- : 0) +
1400
- (Number.isFinite(this.frozen.frozenColsEnd)
1401
- ? [...Array(this.frozen.frozenColsEnd)].reduce((acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsEnd'), 0)
1402
- : 0);
1546
+ this.limitColumns > 0 ? this.intrinsicInlineSize + this.frozenColsSize : this.frozenColsSize - gap;
1403
1547
  }
1404
1548
 
1405
1549
  private updateIntrinsicBlockSize(): void {
1406
1550
  this.intrinsicBlockSize = Number.isFinite(this.limitRows)
1407
1551
  ? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
1408
- gap * (this.limitRows - 1)
1552
+ gap * Math.max(0, this.limitRows - 1)
1409
1553
  : Infinity;
1410
1554
  this.totalIntrinsicBlockSize =
1411
- this.intrinsicBlockSize +
1412
- (Number.isFinite(this.frozen.frozenRowsStart)
1413
- ? [...Array(this.frozen.frozenRowsStart)].reduce(
1414
- (acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsStart'),
1415
- 0,
1416
- )
1417
- : 0) +
1418
- (Number.isFinite(this.frozen.frozenRowsEnd)
1419
- ? [...Array(this.frozen.frozenRowsEnd)].reduce((acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsEnd'), 0)
1420
- : 0);
1555
+ this.limitRows > 0 ? this.intrinsicBlockSize + this.frozenRowsSize : this.frozenRowsSize - gap;
1421
1556
  }
1422
1557
 
1423
1558
  private updateIntrinsicSizes(): void {
@@ -1465,7 +1600,7 @@ export class DxGrid extends LitElement {
1465
1600
  if (this.getCells) {
1466
1601
  this.updateCells(true);
1467
1602
  }
1468
- this.observer.observe(this.viewportRef.value!);
1603
+ this.observer.observe(this.gridRef.value!);
1469
1604
  this.computeColSizes();
1470
1605
  this.computeRowSizes();
1471
1606
  this.updateIntrinsicSizes();
@@ -1490,6 +1625,13 @@ export class DxGrid extends LitElement {
1490
1625
  this.updateVisInline();
1491
1626
  }
1492
1627
 
1628
+ if (changedProperties.has('frozen')) {
1629
+ this.updateIntrinsicBlockSize();
1630
+ this.updateIntrinsicInlineSize();
1631
+ this.updateVisBlock();
1632
+ this.updateVisInline();
1633
+ }
1634
+
1493
1635
  if (
1494
1636
  this.getCells &&
1495
1637
  (changedProperties.has('initialCells') ||
@@ -1500,7 +1642,8 @@ export class DxGrid extends LitElement {
1500
1642
  changedProperties.has('columns') ||
1501
1643
  changedProperties.has('rows') ||
1502
1644
  changedProperties.has('limitColumns') ||
1503
- changedProperties.has('limitRows'))
1645
+ changedProperties.has('limitRows') ||
1646
+ changedProperties.has('frozen'))
1504
1647
  ) {
1505
1648
  this.updateCells(true);
1506
1649
  }
@@ -1520,8 +1663,9 @@ export class DxGrid extends LitElement {
1520
1663
  }
1521
1664
  }
1522
1665
 
1523
- public updateIfWithinBounds({ col, row }: { col: number; row: number }): boolean {
1666
+ public updateIfWithinBounds({ col, row }: { col: number; row: number }, includeFixed?: boolean): boolean {
1524
1667
  if (col >= this.visColMin && col <= this.visColMax && row >= this.visRowMin && row <= this.visRowMax) {
1668
+ this.updateCells(includeFixed);
1525
1669
  this.requestUpdate();
1526
1670
  return true;
1527
1671
  }
@@ -1530,8 +1674,8 @@ export class DxGrid extends LitElement {
1530
1674
 
1531
1675
  override disconnectedCallback(): void {
1532
1676
  super.disconnectedCallback();
1533
- if (this.viewportRef.value) {
1534
- this.observer.unobserve(this.viewportRef.value);
1677
+ if (this.gridRef.value) {
1678
+ this.observer.unobserve(this.gridRef.value);
1535
1679
  }
1536
1680
  document.defaultView?.removeEventListener('wheel', this.handleTopLevelWheel);
1537
1681
  }
@@ -1549,6 +1693,7 @@ export {
1549
1693
  parseCellIndex,
1550
1694
  toPlaneCellIndex,
1551
1695
  cellQuery,
1696
+ accessoryHandlesPointerdownAttrs,
1552
1697
  } from './util';
1553
1698
 
1554
1699
  export const commentedClassName = 'dx-grid__cell--commented';