@dxos/lit-grid 0.8.4-main.84f28bd → 0.8.4-main.ae835ea

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 (38) 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 +13 -8
  12. package/dist/src/dx-grid.d.ts.map +1 -1
  13. package/dist/src/dx-grid.js +205 -166
  14. package/dist/src/dx-grid.js.map +1 -1
  15. package/dist/src/dx-grid.lit-stories.js +15 -18
  16. package/dist/src/dx-grid.lit-stories.js.map +1 -1
  17. package/dist/src/playwright/dx-grid.spec.js.map +1 -1
  18. package/dist/src/testing/dx-grid-manager.d.ts.map +1 -1
  19. package/dist/src/testing/dx-grid-manager.js.map +1 -1
  20. package/dist/src/types.d.ts +9 -0
  21. package/dist/src/types.d.ts.map +1 -1
  22. package/dist/src/types.js.map +1 -1
  23. package/dist/src/util.d.ts +4 -1
  24. package/dist/src/util.d.ts.map +1 -1
  25. package/dist/src/util.js +11 -11
  26. package/dist/src/util.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +4 -4
  29. package/src/defs.ts +1 -0
  30. package/src/dx-grid-axis-resize-handle.pcss +6 -0
  31. package/src/dx-grid-axis-resize-handle.ts +1 -1
  32. package/src/dx-grid-multiselect-cell.ts +2 -1
  33. package/src/dx-grid.pcss +47 -31
  34. package/src/dx-grid.ts +200 -114
  35. package/src/playwright/dx-grid.spec.ts +1 -1
  36. package/src/testing/dx-grid-manager.ts +1 -1
  37. package/src/types.ts +11 -0
  38. package/src/util.ts +13 -9
package/src/dx-grid.ts CHANGED
@@ -3,74 +3,75 @@
3
3
  //
4
4
 
5
5
  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';
6
+ import { customElement, property, state } from 'lit/decorators.js';
7
+ import { type Ref, createRef, ref } from 'lit/directives/ref.js';
8
8
  import { styleMap } from 'lit/directives/style-map.js';
9
- import { unsafeStatic, html as staticHtml } from 'lit/static-html.js';
9
+ import { html as staticHtml, unsafeStatic } from 'lit/static-html.js';
10
10
 
11
- import { defaultColSize, defaultRowSize } from './defs';
12
- // eslint-disable-next-line unused-imports/no-unused-imports
11
+ import { defaultColSize, defaultRowSize, focusUnfurlDefault } from './defs';
13
12
  import './dx-grid-axis-resize-handle';
14
13
  import {
15
- type DxGridAxisMetaProps,
16
- type DxGridAxisSizes,
17
- type DxGridPlaneCellIndex,
18
- type DxGridCellValue,
19
14
  DxAxisResize,
20
15
  type DxAxisResizeInternal,
21
16
  DxEditRequest,
17
+ type DxGridAnnotatedPanEvent,
18
+ type DxGridAxis,
22
19
  type DxGridAxisMeta,
20
+ type DxGridAxisMetaProps,
21
+ type DxGridAxisSizes,
22
+ type DxGridCellValue,
23
23
  type DxGridCells,
24
24
  DxGridCellsSelect,
25
25
  type DxGridFixedPlane,
26
+ type DxGridFocusIndicatorVariant,
26
27
  type DxGridFrozenAxes,
27
28
  type DxGridFrozenColsPlane,
28
29
  type DxGridFrozenPlane,
29
30
  type DxGridFrozenRowsPlane,
30
31
  type DxGridMode,
32
+ type DxGridOverscroll,
31
33
  type DxGridPlane,
34
+ type DxGridPlaneCellIndex,
32
35
  type DxGridPlaneCells,
33
36
  type DxGridPlaneRange,
34
37
  type DxGridPlaneRecord,
35
38
  type DxGridPointer,
36
39
  type DxGridPosition,
37
- type DxGridAxis,
38
- type DxGridSelectionProps,
39
- type DxGridAnnotatedPanEvent,
40
40
  type DxGridRange,
41
+ type DxGridSelectionProps,
41
42
  separator,
42
43
  } from './types';
43
44
  import {
44
- toCellIndex,
45
- gap,
46
- resizeTolerance,
47
- sizeColMin,
48
- sizeColMax,
49
- sizeRowMin,
50
- sizeRowMax,
51
- shouldSelect,
52
- selectionProps,
53
45
  cellSelected,
54
46
  closestAction,
55
47
  closestCell,
56
- targetIsPlane,
57
- resolveRowPlane,
48
+ gap,
49
+ isReadonly,
50
+ isSameCell,
51
+ resizeTolerance,
58
52
  resolveColPlane,
59
53
  resolveFrozenPlane,
60
- isSameCell,
61
- isReadonly,
54
+ resolveRowPlane,
55
+ selectionProps,
56
+ shouldSelect,
57
+ sizeColMax,
58
+ sizeColMin,
59
+ sizeRowMax,
60
+ sizeRowMin,
61
+ targetIsPlane,
62
+ toCellIndex,
62
63
  } from './util';
63
64
 
64
65
  @customElement('dx-grid')
65
66
  export class DxGrid extends LitElement {
66
67
  constructor() {
67
68
  super();
68
- // Wheel, top-level and element-level
69
+ // Wheel, top-level and element-level.
69
70
  document.defaultView?.addEventListener('wheel', this.handleTopLevelWheel, { passive: false });
70
71
  this.addEventListener('wheel', this.handleWheel);
71
- // Custom event(s)
72
+ // Custom event(s).
72
73
  this.addEventListener('dx-axis-resize-internal', this.handleAxisResizeInternal as EventListener);
73
- // Standard events
74
+ // Standard events.
74
75
  this.addEventListener('pointerdown', this.handlePointerDown);
75
76
  this.addEventListener('pointermove', this.handlePointerMove);
76
77
  this.addEventListener('pointerup', this.handlePointerUp);
@@ -84,12 +85,12 @@ export class DxGrid extends LitElement {
84
85
  gridId: string = 'default-grid-id';
85
86
 
86
87
  @property({ type: Object })
87
- rowDefault: DxGridPlaneRecord<DxGridFrozenRowsPlane, DxGridAxisMetaProps> = {
88
+ rowDefault: Partial<DxGridPlaneRecord<DxGridFrozenRowsPlane, Partial<DxGridAxisMetaProps>>> = {
88
89
  grid: { size: defaultRowSize },
89
90
  };
90
91
 
91
92
  @property({ type: Object })
92
- columnDefault: DxGridPlaneRecord<DxGridFrozenColsPlane, DxGridAxisMetaProps> = {
93
+ columnDefault: Partial<DxGridPlaneRecord<DxGridFrozenColsPlane, Partial<DxGridAxisMetaProps>>> = {
93
94
  grid: { size: defaultColSize },
94
95
  };
95
96
 
@@ -115,11 +116,14 @@ export class DxGrid extends LitElement {
115
116
  frozen: DxGridFrozenAxes = {};
116
117
 
117
118
  @property({ type: String })
118
- overscroll: 'inline' | 'block' | 'trap' | undefined = undefined;
119
+ overscroll: DxGridOverscroll = undefined;
119
120
 
120
121
  @property({ type: String })
121
122
  activeRefs = '';
122
123
 
124
+ @property({ type: String })
125
+ focusIndicatorVariant: DxGridFocusIndicatorVariant = 'sheet';
126
+
123
127
  /**
124
128
  * When this function is defined, it is used first to try to get a value for a cell,
125
129
  * and otherwise will fall back to `cells`.
@@ -195,6 +199,7 @@ export class DxGrid extends LitElement {
195
199
  //
196
200
  // `template` is the rendered value of `grid-{axis}-template`.
197
201
  //
202
+
198
203
  @state()
199
204
  private templateGridColumns = '0';
200
205
 
@@ -213,6 +218,16 @@ export class DxGrid extends LitElement {
213
218
  @state()
214
219
  private templatefrozenRowsEnd = '';
215
220
 
221
+ //
222
+ // `frozen…Size` is used to measure space available for the non-fixed planes
223
+ //
224
+
225
+ @state()
226
+ private frozenColsSize = 0;
227
+
228
+ @state()
229
+ private frozenRowsSize = 0;
230
+
216
231
  //
217
232
  // Focus, selection, and resize states
218
233
  //
@@ -308,7 +323,7 @@ export class DxGrid extends LitElement {
308
323
  this.dispatchSelectionChange();
309
324
  }
310
325
  if (this.mode === 'edit-select') {
311
- // Prevent focus moving when editing while selection is possible
326
+ // Prevent focus moving when editing while selection is possible.
312
327
  event.preventDefault();
313
328
  } else if (this.focusActive && isSameCell(this.focusedCell, cellCoords)) {
314
329
  this.dispatchEditRequest();
@@ -416,15 +431,13 @@ export class DxGrid extends LitElement {
416
431
  }
417
432
 
418
433
  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
- }
434
+ const colPlane = resolveColPlane(plane);
435
+ const rowPlane = resolveRowPlane(plane);
436
+ this.focusedCell = {
437
+ plane,
438
+ col: colPlane === 'grid' ? this.visColMin : 0,
439
+ row: rowPlane === 'grid' ? this.visRowMin : 0,
440
+ };
428
441
  this.focusedCellElement()?.focus({ preventScroll: true });
429
442
  }
430
443
 
@@ -481,6 +494,13 @@ export class DxGrid extends LitElement {
481
494
  event.preventDefault();
482
495
  this.dispatchEditRequest();
483
496
  break;
497
+ case 'Backspace':
498
+ case 'Delete':
499
+ if (!event.defaultPrevented) {
500
+ event.preventDefault();
501
+ this.dispatchEditRequest('');
502
+ }
503
+ break;
484
504
  default:
485
505
  if (event.key.length === 1 && event.key.match(/\P{Cc}/u) && !(event.metaKey || event.ctrlKey)) {
486
506
  this.dispatchEditRequest(event.key);
@@ -574,7 +594,7 @@ export class DxGrid extends LitElement {
574
594
  }
575
595
 
576
596
  //
577
- // Resize & reposition handlers, observer, ref
597
+ // Resize & reposition handlers, observer, ref.
578
598
  //
579
599
 
580
600
  @state()
@@ -584,19 +604,18 @@ export class DxGrid extends LitElement {
584
604
  blockSize: 0,
585
605
  };
586
606
  if (
587
- Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||
588
- Math.abs(blockSize - this.sizeBlock) > resizeTolerance
607
+ Math.abs(inlineSize - this.frozenColsSize - this.sizeInline) > resizeTolerance ||
608
+ Math.abs(blockSize - this.frozenRowsSize - this.sizeBlock) > resizeTolerance
589
609
  ) {
590
610
  // console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);
591
- this.sizeInline = inlineSize;
592
- this.sizeBlock = blockSize;
611
+ this.sizeInline = inlineSize - this.frozenColsSize;
612
+ this.sizeBlock = blockSize - this.frozenRowsSize;
593
613
  this.updateVis();
594
614
  queueMicrotask(() => this.updatePos());
595
615
  }
596
616
  });
597
617
 
598
618
  private gridRef: Ref<HTMLDivElement> = createRef();
599
- private viewportRef: Ref<HTMLDivElement> = createRef();
600
619
 
601
620
  private maybeUpdateVisInline = () => {
602
621
  if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
@@ -675,7 +694,7 @@ export class DxGrid extends LitElement {
675
694
  };
676
695
 
677
696
  private updateVisInline(): void {
678
- // todo: avoid starting from zero
697
+ // todo: avoid starting from zero.
679
698
  let axisCursor = 0;
680
699
  let pxCursor = this.colSize(axisCursor, 'grid');
681
700
 
@@ -721,10 +740,19 @@ export class DxGrid extends LitElement {
721
740
  this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
722
741
  .map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
723
742
  .join(' ');
743
+
744
+ this.frozenColsSize =
745
+ [...Array(this.frozen.frozenColsStart ?? 0)].reduce(
746
+ (sum, _, c0) => sum + this.colSize(c0, 'frozenColsStart'),
747
+ 0,
748
+ ) +
749
+ gap * Math.max(0, this.frozen.frozenColsStart ?? 0 - 1) +
750
+ [...Array(this.frozen.frozenColsEnd ?? 0)].reduce((sum, _, c0) => sum + this.colSize(c0, 'frozenColsEnd'), 0) +
751
+ gap * Math.max(0, this.frozen.frozenColsEnd ?? 0 - 1);
724
752
  }
725
753
 
726
754
  private updateVisBlock(): void {
727
- // todo: avoid starting from zero
755
+ // todo: avoid starting from zero.
728
756
  let axisCursor = 0;
729
757
  let pxCursor = this.rowSize(axisCursor, 'grid');
730
758
 
@@ -770,6 +798,15 @@ export class DxGrid extends LitElement {
770
798
  this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
771
799
  .map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
772
800
  .join(' ');
801
+
802
+ this.frozenRowsSize =
803
+ [...Array(this.frozen.frozenRowsStart ?? 0)].reduce(
804
+ (sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsStart'),
805
+ 0,
806
+ ) +
807
+ gap * Math.max(0, this.frozen.frozenRowsStart ?? 0 - 1) +
808
+ [...Array(this.frozen.frozenRowsEnd ?? 0)].reduce((sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsEnd'), 0) +
809
+ gap * Math.max(0, this.frozen.frozenRowsEnd ?? 0 - 1);
773
810
  }
774
811
 
775
812
  private updateVis(): void {
@@ -991,7 +1028,7 @@ export class DxGrid extends LitElement {
991
1028
  }
992
1029
 
993
1030
  /**
994
- * Updates `pos` so that a cell in focus is fully within the viewport
1031
+ * Updates `pos` so that a cell in focus is fully within the viewport.
995
1032
  */
996
1033
  snapPosToFocusedCell(): void {
997
1034
  const outOfVis = this.focusedCellOutOfVis();
@@ -1068,16 +1105,40 @@ export class DxGrid extends LitElement {
1068
1105
  : !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
1069
1106
  }
1070
1107
 
1108
+ private clampAxisSize(
1109
+ plane: 'grid' | DxGridFrozenPlane,
1110
+ axis: DxGridAxis,
1111
+ index: number | string,
1112
+ requestedSize: number,
1113
+ ): number {
1114
+ const minSize =
1115
+ axis === 'col'
1116
+ ? (this.columns[plane]?.[index]?.minSize ??
1117
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.minSize ??
1118
+ sizeColMin)
1119
+ : (this.rows[plane]?.[index]?.minSize ??
1120
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.minSize ??
1121
+ sizeRowMin);
1122
+ const maxSize =
1123
+ axis === 'col'
1124
+ ? (this.columns[plane]?.[index]?.maxSize ??
1125
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.maxSize ??
1126
+ sizeColMax)
1127
+ : (this.rows[plane]?.[index]?.maxSize ??
1128
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.maxSize ??
1129
+ sizeRowMax);
1130
+ return Math.max(minSize, Math.min(maxSize, requestedSize));
1131
+ }
1132
+
1071
1133
  private handleAxisResizeInternal(event: DxAxisResizeInternal): void {
1072
1134
  event.stopPropagation();
1073
1135
  const { plane, axis, delta, size, index, state } = event;
1136
+ const nextSize = this.clampAxisSize(plane, axis, index, size + delta);
1074
1137
  if (axis === 'col') {
1075
- const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
1076
1138
  this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
1077
1139
  this.updateVisInline();
1078
1140
  this.updateIntrinsicInlineSize();
1079
1141
  } else {
1080
- const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
1081
1142
  this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
1082
1143
  this.updateVisBlock();
1083
1144
  this.updateIntrinsicBlockSize();
@@ -1095,7 +1156,7 @@ export class DxGrid extends LitElement {
1095
1156
  }
1096
1157
 
1097
1158
  //
1098
- // Render and other lifecycle methods
1159
+ // Render and other lifecycle methods.
1099
1160
  //
1100
1161
 
1101
1162
  // TODO(thure): This is for rendering presentational objects superimposed onto the canonical grid (e.g. DnD drop line for #8108).
@@ -1178,7 +1239,7 @@ export class DxGrid extends LitElement {
1178
1239
  ) {
1179
1240
  const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
1180
1241
  const rows = this.frozen[rowPlane];
1181
- return (rows ?? 0) > 0
1242
+ return (rows ?? 0) > 0 && this.limitColumns > 0
1182
1243
  ? html`<div
1183
1244
  role="none"
1184
1245
  class="dx-grid__plane--frozen-row"
@@ -1212,7 +1273,7 @@ export class DxGrid extends LitElement {
1212
1273
  ) {
1213
1274
  const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
1214
1275
  const cols = this.frozen[colPlane];
1215
- return (cols ?? 0) > 0
1276
+ return (cols ?? 0) > 0 && this.limitRows > 0
1216
1277
  ? html`<div
1217
1278
  role="none"
1218
1279
  class="dx-grid__plane--frozen-col"
@@ -1238,6 +1299,40 @@ export class DxGrid extends LitElement {
1238
1299
  : null;
1239
1300
  }
1240
1301
 
1302
+ private renderMainGrid(
1303
+ visibleCols: number,
1304
+ visibleRows: number,
1305
+ offsetInline: number,
1306
+ offsetBlock: number,
1307
+ selection: DxGridSelectionProps,
1308
+ ) {
1309
+ return this.limitRows > 0 && this.limitColumns > 0
1310
+ ? html`<div
1311
+ role="grid"
1312
+ class="dx-grid__plane--grid"
1313
+ tabindex="0"
1314
+ data-dx-grid-plane="grid"
1315
+ data-dx-grid-plane-row="1"
1316
+ data-dx-grid-plane-col="1"
1317
+ >
1318
+ <div
1319
+ role="none"
1320
+ class="dx-grid__plane--grid__content"
1321
+ style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1322
+ .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1323
+ >
1324
+ ${[...Array(visibleRows)].map((_, r0) => {
1325
+ return [...Array(visibleCols)].map((_, c0) => {
1326
+ const c = c0 + this.visColMin;
1327
+ const r = r0 + this.visRowMin;
1328
+ return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1329
+ });
1330
+ })}
1331
+ </div>
1332
+ </div>`
1333
+ : null;
1334
+ }
1335
+
1241
1336
  private cellReadonly(col: number, row: number, plane: DxGridPlane): boolean {
1242
1337
  const colPlane = resolveColPlane(plane);
1243
1338
  const rowPlane = resolveRowPlane(plane);
@@ -1255,6 +1350,23 @@ export class DxGrid extends LitElement {
1255
1350
  return isReadonly(colReadOnly) || isReadonly(rowReadOnly);
1256
1351
  }
1257
1352
 
1353
+ private cellFocusUnfurl(col: number, row: number, plane: DxGridPlane): boolean {
1354
+ const colPlane = resolveColPlane(plane);
1355
+ const rowPlane = resolveRowPlane(plane);
1356
+
1357
+ // Check cell-specific setting first.
1358
+ const cellUnfurl = this.cell(col, row, plane)?.focusUnfurl;
1359
+ if (cellUnfurl !== undefined) {
1360
+ return cellUnfurl;
1361
+ }
1362
+
1363
+ // Check column/row defaults.
1364
+ const colUnfurl = this.columns?.[colPlane]?.[col]?.focusUnfurl ?? this.columnDefault?.[colPlane]?.focusUnfurl;
1365
+ const rowUnfurl = this.rows?.[rowPlane]?.[row]?.focusUnfurl ?? this.rowDefault?.[rowPlane]?.focusUnfurl;
1366
+
1367
+ return colUnfurl ?? rowUnfurl ?? focusUnfurlDefault;
1368
+ }
1369
+
1258
1370
  /**
1259
1371
  * Determines if the cell's text content should be selectable based on its readonly value.
1260
1372
  * @returns true if the cells text content is selectable, false otherwise.
@@ -1317,6 +1429,7 @@ export class DxGrid extends LitElement {
1317
1429
  const cell = this.cell(col, row, plane);
1318
1430
  const active = this.cellActive(col, row, plane);
1319
1431
  const readonly = this.cellReadonly(col, row, plane);
1432
+ const focusUnfurl = this.cellFocusUnfurl(col, row, plane);
1320
1433
  const textSelectable = this.cellTextSelectable(col, row, plane);
1321
1434
  const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
1322
1435
  const resizePlane = cell?.resizeHandle ? resolveFrozenPlane(cell.resizeHandle, plane) : undefined;
@@ -1328,16 +1441,16 @@ export class DxGrid extends LitElement {
1328
1441
  aria-readonly=${readonly ? 'true' : nothing}
1329
1442
  class=${cell?.className ?? nothing}
1330
1443
  data-refs=${cell?.dataRefs ?? nothing}
1444
+ data-focus-unfurl=${focusUnfurl ? nothing : 'false'}
1331
1445
  ?data-dx-active=${active}
1332
1446
  data-text-selectable=${textSelectable ? 'true' : 'false'}
1333
1447
  data-dx-grid-action="cell"
1334
1448
  aria-colindex=${col}
1335
1449
  aria-rowindex=${row}
1450
+ data-testid=${`${plane}.${col}.${row}`}
1336
1451
  style="grid-column:${visCol + 1};grid-row:${visRow + 1}"
1337
1452
  >
1338
- <div role="none" class="dx-grid__cell__content">
1339
- ${this.mode !== 'browse' && active ? null : cell?.value}${this.mode !== 'browse' && active ? null : accessory}
1340
- </div>
1453
+ <div role="none" class="dx-grid__cell__content">${cell?.value}${accessory}</div>
1341
1454
  ${cell?.resizeHandle &&
1342
1455
  this.mode === 'browse' &&
1343
1456
  this.axisResizeable(resizePlane!, cell.resizeHandle, resizeIndex!)
@@ -1372,13 +1485,24 @@ export class DxGrid extends LitElement {
1372
1485
  <div
1373
1486
  role="none"
1374
1487
  class="dx-grid"
1488
+ data-arrow-keys="all"
1375
1489
  style=${styleMap({
1376
- 'grid-template-columns': `${this.templatefrozenColsStart ? 'min-content ' : ''}minmax(0, ${
1377
- Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'
1378
- })${this.templatefrozenColsEnd ? ' min-content' : ''}`,
1379
- 'grid-template-rows': `${this.templatefrozenRowsStart ? 'min-content ' : ''}minmax(0, ${
1380
- Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'
1381
- })${this.templatefrozenRowsEnd ? ' min-content' : ''}`,
1490
+ 'grid-template-columns': [
1491
+ this.templatefrozenColsStart ? 'min-content' : false,
1492
+ this.limitColumns > 0 &&
1493
+ `minmax(0, ${Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'})`,
1494
+ this.templatefrozenColsEnd ? 'min-content' : false,
1495
+ ]
1496
+ .filter(Boolean)
1497
+ .join(' '),
1498
+ 'grid-template-rows': [
1499
+ this.templatefrozenRowsStart ? 'min-content' : false,
1500
+ this.limitRows > 0 &&
1501
+ `minmax(0, ${Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'})`,
1502
+ this.templatefrozenRowsEnd ? ' min-content' : false,
1503
+ ]
1504
+ .filter(Boolean)
1505
+ .join(' '),
1382
1506
  '--dx-grid-content-inline-size': Number.isFinite(this.limitColumns)
1383
1507
  ? `${Math.max(0, this.totalIntrinsicInlineSize)}px`
1384
1508
  : 'max-content',
@@ -1388,6 +1512,7 @@ export class DxGrid extends LitElement {
1388
1512
  })}
1389
1513
  data-grid=${this.gridId}
1390
1514
  data-grid-mode=${this.mode}
1515
+ data-grid-focus-indicator-variant=${this.focusIndicatorVariant}
1391
1516
  ?data-grid-select=${selection.visible}
1392
1517
  ${ref(this.gridRef)}
1393
1518
  >
@@ -1402,30 +1527,7 @@ export class DxGrid extends LitElement {
1402
1527
  offsetBlock,
1403
1528
  selection,
1404
1529
  )}
1405
- <div
1406
- role="grid"
1407
- class="dx-grid__plane--grid"
1408
- tabindex="0"
1409
- data-dx-grid-plane="grid"
1410
- data-dx-grid-plane-row="1"
1411
- data-dx-grid-plane-col="1"
1412
- ${ref(this.viewportRef)}
1413
- >
1414
- <div
1415
- role="none"
1416
- class="dx-grid__plane--grid__content"
1417
- style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1418
- .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1419
- >
1420
- ${[...Array(visibleRows)].map((_, r0) => {
1421
- return [...Array(visibleCols)].map((_, c0) => {
1422
- const c = c0 + this.visColMin;
1423
- const r = r0 + this.visRowMin;
1424
- return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1425
- });
1426
- })}
1427
- </div>
1428
- </div>
1530
+ ${this.renderMainGrid(visibleCols, visibleRows, offsetInline, offsetBlock, selection)}
1429
1531
  ${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
1430
1532
  'fixedEndStart',
1431
1533
  selection,
@@ -1439,37 +1541,19 @@ export class DxGrid extends LitElement {
1439
1541
  private updateIntrinsicInlineSize(): void {
1440
1542
  this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
1441
1543
  ? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
1442
- gap * (this.limitColumns - 1)
1544
+ gap * Math.max(0, this.limitColumns - 1)
1443
1545
  : Infinity;
1444
1546
  this.totalIntrinsicInlineSize =
1445
- this.intrinsicInlineSize +
1446
- (Number.isFinite(this.frozen.frozenColsStart)
1447
- ? [...Array(this.frozen.frozenColsStart)].reduce(
1448
- (acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsStart'),
1449
- 0,
1450
- )
1451
- : 0) +
1452
- (Number.isFinite(this.frozen.frozenColsEnd)
1453
- ? [...Array(this.frozen.frozenColsEnd)].reduce((acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsEnd'), 0)
1454
- : 0);
1547
+ this.limitColumns > 0 ? this.intrinsicInlineSize + this.frozenColsSize : this.frozenColsSize - gap;
1455
1548
  }
1456
1549
 
1457
1550
  private updateIntrinsicBlockSize(): void {
1458
1551
  this.intrinsicBlockSize = Number.isFinite(this.limitRows)
1459
1552
  ? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
1460
- gap * (this.limitRows - 1)
1553
+ gap * Math.max(0, this.limitRows - 1)
1461
1554
  : Infinity;
1462
1555
  this.totalIntrinsicBlockSize =
1463
- this.intrinsicBlockSize +
1464
- (Number.isFinite(this.frozen.frozenRowsStart)
1465
- ? [...Array(this.frozen.frozenRowsStart)].reduce(
1466
- (acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsStart'),
1467
- 0,
1468
- )
1469
- : 0) +
1470
- (Number.isFinite(this.frozen.frozenRowsEnd)
1471
- ? [...Array(this.frozen.frozenRowsEnd)].reduce((acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsEnd'), 0)
1472
- : 0);
1556
+ this.limitRows > 0 ? this.intrinsicBlockSize + this.frozenRowsSize : this.frozenRowsSize - gap;
1473
1557
  }
1474
1558
 
1475
1559
  private updateIntrinsicSizes(): void {
@@ -1517,7 +1601,7 @@ export class DxGrid extends LitElement {
1517
1601
  if (this.getCells) {
1518
1602
  this.updateCells(true);
1519
1603
  }
1520
- this.observer.observe(this.viewportRef.value!);
1604
+ this.observer.observe(this.gridRef.value!);
1521
1605
  this.computeColSizes();
1522
1606
  this.computeRowSizes();
1523
1607
  this.updateIntrinsicSizes();
@@ -1580,8 +1664,9 @@ export class DxGrid extends LitElement {
1580
1664
  }
1581
1665
  }
1582
1666
 
1583
- public updateIfWithinBounds({ col, row }: { col: number; row: number }): boolean {
1667
+ public updateIfWithinBounds({ col, row }: { col: number; row: number }, includeFixed?: boolean): boolean {
1584
1668
  if (col >= this.visColMin && col <= this.visColMax && row >= this.visRowMin && row <= this.visRowMax) {
1669
+ this.updateCells(includeFixed);
1585
1670
  this.requestUpdate();
1586
1671
  return true;
1587
1672
  }
@@ -1590,8 +1675,8 @@ export class DxGrid extends LitElement {
1590
1675
 
1591
1676
  override disconnectedCallback(): void {
1592
1677
  super.disconnectedCallback();
1593
- if (this.viewportRef.value) {
1594
- this.observer.unobserve(this.viewportRef.value);
1678
+ if (this.gridRef.value) {
1679
+ this.observer.unobserve(this.gridRef.value);
1595
1680
  }
1596
1681
  document.defaultView?.removeEventListener('wheel', this.handleTopLevelWheel);
1597
1682
  }
@@ -1609,6 +1694,7 @@ export {
1609
1694
  parseCellIndex,
1610
1695
  toPlaneCellIndex,
1611
1696
  cellQuery,
1697
+ accessoryHandlesPointerdownAttrs,
1612
1698
  } from './util';
1613
1699
 
1614
1700
  export const commentedClassName = 'dx-grid__cell--commented';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
- import { expect, test, type Page } from '@playwright/test';
5
+ import { type Page, expect, test } from '@playwright/test';
6
6
 
7
7
  import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
8
8
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { expect, type Locator, type Page } from '@playwright/test';
5
+ import { type Locator, type Page, expect } from '@playwright/test';
6
6
 
7
7
  import type { DxGridPlanePosition } from '../types';
8
8
 
package/src/types.ts CHANGED
@@ -83,6 +83,10 @@ export type DxGridCellValue = {
83
83
  * Controls the read-only state of the cell.
84
84
  */
85
85
  readonly?: DxGridReadonlyValue;
86
+ /**
87
+ * Controls whether the cell content should unfurl when the cell has focus.
88
+ */
89
+ focusUnfurl?: boolean;
86
90
  };
87
91
 
88
92
  export type DxGridAxisMetaProps = {
@@ -90,6 +94,9 @@ export type DxGridAxisMetaProps = {
90
94
  description?: string;
91
95
  resizeable?: boolean;
92
96
  readonly?: DxGridReadonlyValue;
97
+ focusUnfurl?: boolean;
98
+ minSize?: number;
99
+ maxSize?: number;
93
100
  };
94
101
 
95
102
  export type DxGridAxisSizes = DxGridPlaneRecord<DxGridFrozenPlane, Record<string, number>>;
@@ -121,6 +128,10 @@ export type DxGridSelectionProps = {
121
128
  visible?: boolean;
122
129
  };
123
130
 
131
+ export type DxGridFocusIndicatorVariant = 'sheet' | 'stack';
132
+
133
+ export type DxGridOverscroll = 'inline' | 'block' | 'trap' | undefined;
134
+
124
135
  export class DxAxisResize extends Event {
125
136
  public readonly axis: DxGridAxis;
126
137
  public readonly plane: 'grid' | DxGridFrozenPlane;