@dxos/lit-grid 0.8.4-main.5acf9ea → 0.8.4-main.5ea62a8

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 +9 -5
  12. package/dist/src/dx-grid.d.ts.map +1 -1
  13. package/dist/src/dx-grid.js +173 -144
  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 +7 -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 +3 -3
  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 +14 -1
  34. package/src/dx-grid.ts +164 -90
  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 +7 -0
  38. package/src/util.ts +13 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/lit-grid",
3
- "version": "0.8.4-main.5acf9ea",
3
+ "version": "0.8.4-main.5ea62a8",
4
4
  "description": "A grid Web Component using Lit",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -39,7 +39,7 @@
39
39
  "lit": "^3.2.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@dxos/random": "0.8.4-main.5acf9ea",
43
- "@dxos/test-utils": "0.8.4-main.5acf9ea"
42
+ "@dxos/random": "0.8.4-main.5ea62a8",
43
+ "@dxos/test-utils": "0.8.4-main.5ea62a8"
44
44
  }
45
45
  }
package/src/defs.ts CHANGED
@@ -4,3 +4,4 @@
4
4
 
5
5
  export const defaultColSize = 180;
6
6
  export const defaultRowSize = 30;
7
+ export const focusUnfurlDefault = true;
@@ -21,4 +21,10 @@ dx-grid-axis-resize-handle {
21
21
  block-size: .5rem;
22
22
  cursor: row-resize;
23
23
  }
24
+ &:focus {
25
+ outline: none;
26
+ }
27
+ &:focus-visible {
28
+ background: var(--dx-grid-resizeHandleFocus, var(--dx-accentSurface));
29
+ }
24
30
  }
@@ -6,7 +6,7 @@ import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
6
  import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
7
7
  import { preventUnhandled } from '@atlaskit/pragmatic-drag-and-drop/prevent-unhandled';
8
8
  import { type CleanupFn, type DragLocationHistory } from '@atlaskit/pragmatic-drag-and-drop/types';
9
- import { html, LitElement } from 'lit';
9
+ import { LitElement, html } from 'lit';
10
10
  import { customElement, property } from 'lit/decorators.js';
11
11
  import { ref } from 'lit/directives/ref.js';
12
12
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { html, LitElement } from 'lit';
5
+ import { LitElement, html } from 'lit';
6
6
  import { customElement, property } from 'lit/decorators.js';
7
7
 
8
8
  export type DxGridSelectValue = {
@@ -31,6 +31,7 @@ export class DxGridMultiselectCell extends LitElement {
31
31
  aria-haspopup="dialog"
32
32
  class="dx-grid__cell__multiselect"
33
33
  data-dx-grid-accessory="invoke-multiselect"
34
+ data-dx-grid-action="accessory"
34
35
  >
35
36
  ${this.values.length > 0
36
37
  ? this.values.map(({ label }) => html`<span class="dx-grid__cell__multiselect__value">${label}</span>`)
package/src/dx-grid.pcss CHANGED
@@ -130,6 +130,18 @@
130
130
  }
131
131
  }
132
132
  }
133
+
134
+ .dx-grid__row--cta__cell {
135
+ transition: background-color 100ms linear;
136
+ & > .dx-grid__cell__content {
137
+ cursor: pointer;
138
+ background: transparent !important;
139
+ }
140
+ }
141
+
142
+ &:has(.dx-grid__row--cta__cell:hover) .dx-grid__row--cta__cell {
143
+ background: var(--dx-hoverOverlay);
144
+ }
133
145
  }
134
146
 
135
147
  /* Editor and focused cell styles; be sure to keep these two blocks in-sync. */
@@ -137,7 +149,7 @@
137
149
  [role='gridcell'],
138
150
  [role='columnheader'],
139
151
  [role='rowheader'] {
140
- &:not(.dx-grid__cell--no-focus-unfurl){
152
+ &:not([data-focus-unfurl="false"]):not(.dx-grid__cell--no-focus-unfurl) {
141
153
  &:focus,
142
154
  &:focus-within {
143
155
  & > .dx-grid__cell__content {
@@ -235,6 +247,7 @@
235
247
  block-size: 0;
236
248
  border-inline-start: 0.5em solid transparent;
237
249
  border-block-start: 0.5em solid var(--dx-warningText);
250
+ z-index: 1;
238
251
  }
239
252
  }
240
253
  }
package/src/dx-grid.ts CHANGED
@@ -3,22 +3,23 @@
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';
11
+ import { defaultColSize, defaultRowSize, focusUnfurlDefault } from './defs';
12
12
  import './dx-grid-axis-resize-handle';
13
13
  import {
14
- type DxGridAxisMetaProps,
15
- type DxGridAxisSizes,
16
- type DxGridPlaneCellIndex,
17
- type DxGridCellValue,
18
14
  DxAxisResize,
19
15
  type DxAxisResizeInternal,
20
16
  DxEditRequest,
17
+ type DxGridAnnotatedPanEvent,
18
+ type DxGridAxis,
21
19
  type DxGridAxisMeta,
20
+ type DxGridAxisMetaProps,
21
+ type DxGridAxisSizes,
22
+ type DxGridCellValue,
22
23
  type DxGridCells,
23
24
  DxGridCellsSelect,
24
25
  type DxGridFixedPlane,
@@ -28,36 +29,35 @@ import {
28
29
  type DxGridFrozenRowsPlane,
29
30
  type DxGridMode,
30
31
  type DxGridPlane,
32
+ type DxGridPlaneCellIndex,
31
33
  type DxGridPlaneCells,
32
34
  type DxGridPlaneRange,
33
35
  type DxGridPlaneRecord,
34
36
  type DxGridPointer,
35
37
  type DxGridPosition,
36
- type DxGridAxis,
37
- type DxGridSelectionProps,
38
- type DxGridAnnotatedPanEvent,
39
38
  type DxGridRange,
39
+ type DxGridSelectionProps,
40
40
  separator,
41
41
  } from './types';
42
42
  import {
43
- toCellIndex,
44
- gap,
45
- resizeTolerance,
46
- sizeColMin,
47
- sizeColMax,
48
- sizeRowMin,
49
- sizeRowMax,
50
- shouldSelect,
51
- selectionProps,
52
43
  cellSelected,
53
44
  closestAction,
54
45
  closestCell,
55
- targetIsPlane,
56
- resolveRowPlane,
46
+ gap,
47
+ isReadonly,
48
+ isSameCell,
49
+ resizeTolerance,
57
50
  resolveColPlane,
58
51
  resolveFrozenPlane,
59
- isSameCell,
60
- isReadonly,
52
+ resolveRowPlane,
53
+ selectionProps,
54
+ shouldSelect,
55
+ sizeColMax,
56
+ sizeColMin,
57
+ sizeRowMax,
58
+ sizeRowMin,
59
+ targetIsPlane,
60
+ toCellIndex,
61
61
  } from './util';
62
62
 
63
63
  @customElement('dx-grid')
@@ -83,12 +83,12 @@ export class DxGrid extends LitElement {
83
83
  gridId: string = 'default-grid-id';
84
84
 
85
85
  @property({ type: Object })
86
- rowDefault: DxGridPlaneRecord<DxGridFrozenRowsPlane, DxGridAxisMetaProps> = {
86
+ rowDefault: Partial<DxGridPlaneRecord<DxGridFrozenRowsPlane, Partial<DxGridAxisMetaProps>>> = {
87
87
  grid: { size: defaultRowSize },
88
88
  };
89
89
 
90
90
  @property({ type: Object })
91
- columnDefault: DxGridPlaneRecord<DxGridFrozenColsPlane, DxGridAxisMetaProps> = {
91
+ columnDefault: Partial<DxGridPlaneRecord<DxGridFrozenColsPlane, Partial<DxGridAxisMetaProps>>> = {
92
92
  grid: { size: defaultColSize },
93
93
  };
94
94
 
@@ -212,6 +212,16 @@ export class DxGrid extends LitElement {
212
212
  @state()
213
213
  private templatefrozenRowsEnd = '';
214
214
 
215
+ //
216
+ // `frozen…Size` is used to measure space available for the non-fixed planes
217
+ //
218
+
219
+ @state()
220
+ private frozenColsSize = 0;
221
+
222
+ @state()
223
+ private frozenRowsSize = 0;
224
+
215
225
  //
216
226
  // Focus, selection, and resize states
217
227
  //
@@ -590,19 +600,18 @@ export class DxGrid extends LitElement {
590
600
  blockSize: 0,
591
601
  };
592
602
  if (
593
- Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||
594
- Math.abs(blockSize - this.sizeBlock) > resizeTolerance
603
+ Math.abs(inlineSize - this.frozenColsSize - this.sizeInline) > resizeTolerance ||
604
+ Math.abs(blockSize - this.frozenRowsSize - this.sizeBlock) > resizeTolerance
595
605
  ) {
596
606
  // console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);
597
- this.sizeInline = inlineSize;
598
- this.sizeBlock = blockSize;
607
+ this.sizeInline = inlineSize - this.frozenColsSize;
608
+ this.sizeBlock = blockSize - this.frozenRowsSize;
599
609
  this.updateVis();
600
610
  queueMicrotask(() => this.updatePos());
601
611
  }
602
612
  });
603
613
 
604
614
  private gridRef: Ref<HTMLDivElement> = createRef();
605
- private viewportRef: Ref<HTMLDivElement> = createRef();
606
615
 
607
616
  private maybeUpdateVisInline = () => {
608
617
  if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
@@ -727,6 +736,15 @@ export class DxGrid extends LitElement {
727
736
  this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
728
737
  .map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
729
738
  .join(' ');
739
+
740
+ this.frozenColsSize =
741
+ [...Array(this.frozen.frozenColsStart ?? 0)].reduce(
742
+ (sum, _, c0) => sum + this.colSize(c0, 'frozenColsStart'),
743
+ 0,
744
+ ) +
745
+ gap * Math.max(0, this.frozen.frozenColsStart ?? 0 - 1) +
746
+ [...Array(this.frozen.frozenColsEnd ?? 0)].reduce((sum, _, c0) => sum + this.colSize(c0, 'frozenColsEnd'), 0) +
747
+ gap * Math.max(0, this.frozen.frozenColsEnd ?? 0 - 1);
730
748
  }
731
749
 
732
750
  private updateVisBlock(): void {
@@ -776,6 +794,15 @@ export class DxGrid extends LitElement {
776
794
  this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
777
795
  .map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
778
796
  .join(' ');
797
+
798
+ this.frozenRowsSize =
799
+ [...Array(this.frozen.frozenRowsStart ?? 0)].reduce(
800
+ (sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsStart'),
801
+ 0,
802
+ ) +
803
+ gap * Math.max(0, this.frozen.frozenRowsStart ?? 0 - 1) +
804
+ [...Array(this.frozen.frozenRowsEnd ?? 0)].reduce((sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsEnd'), 0) +
805
+ gap * Math.max(0, this.frozen.frozenRowsEnd ?? 0 - 1);
779
806
  }
780
807
 
781
808
  private updateVis(): void {
@@ -1074,16 +1101,40 @@ export class DxGrid extends LitElement {
1074
1101
  : !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
1075
1102
  }
1076
1103
 
1104
+ private clampAxisSize(
1105
+ plane: 'grid' | DxGridFrozenPlane,
1106
+ axis: DxGridAxis,
1107
+ index: number | string,
1108
+ requestedSize: number,
1109
+ ): number {
1110
+ const minSize =
1111
+ axis === 'col'
1112
+ ? (this.columns[plane]?.[index]?.minSize ??
1113
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.minSize ??
1114
+ sizeColMin)
1115
+ : (this.rows[plane]?.[index]?.minSize ??
1116
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.minSize ??
1117
+ sizeRowMin);
1118
+ const maxSize =
1119
+ axis === 'col'
1120
+ ? (this.columns[plane]?.[index]?.maxSize ??
1121
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.maxSize ??
1122
+ sizeColMax)
1123
+ : (this.rows[plane]?.[index]?.maxSize ??
1124
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.maxSize ??
1125
+ sizeRowMax);
1126
+ return Math.max(minSize, Math.min(maxSize, requestedSize));
1127
+ }
1128
+
1077
1129
  private handleAxisResizeInternal(event: DxAxisResizeInternal): void {
1078
1130
  event.stopPropagation();
1079
1131
  const { plane, axis, delta, size, index, state } = event;
1132
+ const nextSize = this.clampAxisSize(plane, axis, index, size + delta);
1080
1133
  if (axis === 'col') {
1081
- const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
1082
1134
  this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
1083
1135
  this.updateVisInline();
1084
1136
  this.updateIntrinsicInlineSize();
1085
1137
  } else {
1086
- const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
1087
1138
  this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
1088
1139
  this.updateVisBlock();
1089
1140
  this.updateIntrinsicBlockSize();
@@ -1184,7 +1235,7 @@ export class DxGrid extends LitElement {
1184
1235
  ) {
1185
1236
  const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
1186
1237
  const rows = this.frozen[rowPlane];
1187
- return (rows ?? 0) > 0
1238
+ return (rows ?? 0) > 0 && this.limitColumns > 0
1188
1239
  ? html`<div
1189
1240
  role="none"
1190
1241
  class="dx-grid__plane--frozen-row"
@@ -1218,7 +1269,7 @@ export class DxGrid extends LitElement {
1218
1269
  ) {
1219
1270
  const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
1220
1271
  const cols = this.frozen[colPlane];
1221
- return (cols ?? 0) > 0
1272
+ return (cols ?? 0) > 0 && this.limitRows > 0
1222
1273
  ? html`<div
1223
1274
  role="none"
1224
1275
  class="dx-grid__plane--frozen-col"
@@ -1244,6 +1295,40 @@ export class DxGrid extends LitElement {
1244
1295
  : null;
1245
1296
  }
1246
1297
 
1298
+ private renderMainGrid(
1299
+ visibleCols: number,
1300
+ visibleRows: number,
1301
+ offsetInline: number,
1302
+ offsetBlock: number,
1303
+ selection: DxGridSelectionProps,
1304
+ ) {
1305
+ return this.limitRows > 0 && this.limitColumns > 0
1306
+ ? html`<div
1307
+ role="grid"
1308
+ class="dx-grid__plane--grid"
1309
+ tabindex="0"
1310
+ data-dx-grid-plane="grid"
1311
+ data-dx-grid-plane-row="1"
1312
+ data-dx-grid-plane-col="1"
1313
+ >
1314
+ <div
1315
+ role="none"
1316
+ class="dx-grid__plane--grid__content"
1317
+ style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1318
+ .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1319
+ >
1320
+ ${[...Array(visibleRows)].map((_, r0) => {
1321
+ return [...Array(visibleCols)].map((_, c0) => {
1322
+ const c = c0 + this.visColMin;
1323
+ const r = r0 + this.visRowMin;
1324
+ return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1325
+ });
1326
+ })}
1327
+ </div>
1328
+ </div>`
1329
+ : null;
1330
+ }
1331
+
1247
1332
  private cellReadonly(col: number, row: number, plane: DxGridPlane): boolean {
1248
1333
  const colPlane = resolveColPlane(plane);
1249
1334
  const rowPlane = resolveRowPlane(plane);
@@ -1261,6 +1346,23 @@ export class DxGrid extends LitElement {
1261
1346
  return isReadonly(colReadOnly) || isReadonly(rowReadOnly);
1262
1347
  }
1263
1348
 
1349
+ private cellFocusUnfurl(col: number, row: number, plane: DxGridPlane): boolean {
1350
+ const colPlane = resolveColPlane(plane);
1351
+ const rowPlane = resolveRowPlane(plane);
1352
+
1353
+ // Check cell-specific setting first.
1354
+ const cellUnfurl = this.cell(col, row, plane)?.focusUnfurl;
1355
+ if (cellUnfurl !== undefined) {
1356
+ return cellUnfurl;
1357
+ }
1358
+
1359
+ // Check column/row defaults.
1360
+ const colUnfurl = this.columns?.[colPlane]?.[col]?.focusUnfurl ?? this.columnDefault?.[colPlane]?.focusUnfurl;
1361
+ const rowUnfurl = this.rows?.[rowPlane]?.[row]?.focusUnfurl ?? this.rowDefault?.[rowPlane]?.focusUnfurl;
1362
+
1363
+ return colUnfurl ?? rowUnfurl ?? focusUnfurlDefault;
1364
+ }
1365
+
1264
1366
  /**
1265
1367
  * Determines if the cell's text content should be selectable based on its readonly value.
1266
1368
  * @returns true if the cells text content is selectable, false otherwise.
@@ -1323,6 +1425,7 @@ export class DxGrid extends LitElement {
1323
1425
  const cell = this.cell(col, row, plane);
1324
1426
  const active = this.cellActive(col, row, plane);
1325
1427
  const readonly = this.cellReadonly(col, row, plane);
1428
+ const focusUnfurl = this.cellFocusUnfurl(col, row, plane);
1326
1429
  const textSelectable = this.cellTextSelectable(col, row, plane);
1327
1430
  const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
1328
1431
  const resizePlane = cell?.resizeHandle ? resolveFrozenPlane(cell.resizeHandle, plane) : undefined;
@@ -1334,6 +1437,7 @@ export class DxGrid extends LitElement {
1334
1437
  aria-readonly=${readonly ? 'true' : nothing}
1335
1438
  class=${cell?.className ?? nothing}
1336
1439
  data-refs=${cell?.dataRefs ?? nothing}
1440
+ data-focus-unfurl=${focusUnfurl ? nothing : 'false'}
1337
1441
  ?data-dx-active=${active}
1338
1442
  data-text-selectable=${textSelectable ? 'true' : 'false'}
1339
1443
  data-dx-grid-action="cell"
@@ -1377,12 +1481,22 @@ export class DxGrid extends LitElement {
1377
1481
  role="none"
1378
1482
  class="dx-grid"
1379
1483
  style=${styleMap({
1380
- 'grid-template-columns': `${this.templatefrozenColsStart ? 'min-content ' : ''}minmax(0, ${
1381
- Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'
1382
- })${this.templatefrozenColsEnd ? ' min-content' : ''}`,
1383
- 'grid-template-rows': `${this.templatefrozenRowsStart ? 'min-content ' : ''}minmax(0, ${
1384
- Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'
1385
- })${this.templatefrozenRowsEnd ? ' min-content' : ''}`,
1484
+ 'grid-template-columns': [
1485
+ this.templatefrozenColsStart ? 'min-content' : false,
1486
+ this.limitColumns > 0 &&
1487
+ `minmax(0, ${Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'})`,
1488
+ this.templatefrozenColsEnd ? 'min-content' : false,
1489
+ ]
1490
+ .filter(Boolean)
1491
+ .join(' '),
1492
+ 'grid-template-rows': [
1493
+ this.templatefrozenRowsStart ? 'min-content' : false,
1494
+ this.limitRows > 0 &&
1495
+ `minmax(0, ${Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'})`,
1496
+ this.templatefrozenRowsEnd ? ' min-content' : false,
1497
+ ]
1498
+ .filter(Boolean)
1499
+ .join(' '),
1386
1500
  '--dx-grid-content-inline-size': Number.isFinite(this.limitColumns)
1387
1501
  ? `${Math.max(0, this.totalIntrinsicInlineSize)}px`
1388
1502
  : 'max-content',
@@ -1406,30 +1520,7 @@ export class DxGrid extends LitElement {
1406
1520
  offsetBlock,
1407
1521
  selection,
1408
1522
  )}
1409
- <div
1410
- role="grid"
1411
- class="dx-grid__plane--grid"
1412
- tabindex="0"
1413
- data-dx-grid-plane="grid"
1414
- data-dx-grid-plane-row="1"
1415
- data-dx-grid-plane-col="1"
1416
- ${ref(this.viewportRef)}
1417
- >
1418
- <div
1419
- role="none"
1420
- class="dx-grid__plane--grid__content"
1421
- style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1422
- .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1423
- >
1424
- ${[...Array(visibleRows)].map((_, r0) => {
1425
- return [...Array(visibleCols)].map((_, c0) => {
1426
- const c = c0 + this.visColMin;
1427
- const r = r0 + this.visRowMin;
1428
- return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1429
- });
1430
- })}
1431
- </div>
1432
- </div>
1523
+ ${this.renderMainGrid(visibleCols, visibleRows, offsetInline, offsetBlock, selection)}
1433
1524
  ${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
1434
1525
  'fixedEndStart',
1435
1526
  selection,
@@ -1443,37 +1534,19 @@ export class DxGrid extends LitElement {
1443
1534
  private updateIntrinsicInlineSize(): void {
1444
1535
  this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
1445
1536
  ? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
1446
- gap * (this.limitColumns - 1)
1537
+ gap * Math.max(0, this.limitColumns - 1)
1447
1538
  : Infinity;
1448
1539
  this.totalIntrinsicInlineSize =
1449
- this.intrinsicInlineSize +
1450
- (Number.isFinite(this.frozen.frozenColsStart)
1451
- ? [...Array(this.frozen.frozenColsStart)].reduce(
1452
- (acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsStart'),
1453
- 0,
1454
- )
1455
- : 0) +
1456
- (Number.isFinite(this.frozen.frozenColsEnd)
1457
- ? [...Array(this.frozen.frozenColsEnd)].reduce((acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsEnd'), 0)
1458
- : 0);
1540
+ this.limitColumns > 0 ? this.intrinsicInlineSize + this.frozenColsSize : this.frozenColsSize - gap;
1459
1541
  }
1460
1542
 
1461
1543
  private updateIntrinsicBlockSize(): void {
1462
1544
  this.intrinsicBlockSize = Number.isFinite(this.limitRows)
1463
1545
  ? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
1464
- gap * (this.limitRows - 1)
1546
+ gap * Math.max(0, this.limitRows - 1)
1465
1547
  : Infinity;
1466
1548
  this.totalIntrinsicBlockSize =
1467
- this.intrinsicBlockSize +
1468
- (Number.isFinite(this.frozen.frozenRowsStart)
1469
- ? [...Array(this.frozen.frozenRowsStart)].reduce(
1470
- (acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsStart'),
1471
- 0,
1472
- )
1473
- : 0) +
1474
- (Number.isFinite(this.frozen.frozenRowsEnd)
1475
- ? [...Array(this.frozen.frozenRowsEnd)].reduce((acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsEnd'), 0)
1476
- : 0);
1549
+ this.limitRows > 0 ? this.intrinsicBlockSize + this.frozenRowsSize : this.frozenRowsSize - gap;
1477
1550
  }
1478
1551
 
1479
1552
  private updateIntrinsicSizes(): void {
@@ -1521,7 +1594,7 @@ export class DxGrid extends LitElement {
1521
1594
  if (this.getCells) {
1522
1595
  this.updateCells(true);
1523
1596
  }
1524
- this.observer.observe(this.viewportRef.value!);
1597
+ this.observer.observe(this.gridRef.value!);
1525
1598
  this.computeColSizes();
1526
1599
  this.computeRowSizes();
1527
1600
  this.updateIntrinsicSizes();
@@ -1595,8 +1668,8 @@ export class DxGrid extends LitElement {
1595
1668
 
1596
1669
  override disconnectedCallback(): void {
1597
1670
  super.disconnectedCallback();
1598
- if (this.viewportRef.value) {
1599
- this.observer.unobserve(this.viewportRef.value);
1671
+ if (this.gridRef.value) {
1672
+ this.observer.unobserve(this.gridRef.value);
1600
1673
  }
1601
1674
  document.defaultView?.removeEventListener('wheel', this.handleTopLevelWheel);
1602
1675
  }
@@ -1614,6 +1687,7 @@ export {
1614
1687
  parseCellIndex,
1615
1688
  toPlaneCellIndex,
1616
1689
  cellQuery,
1690
+ accessoryHandlesPointerdownAttrs,
1617
1691
  } from './util';
1618
1692
 
1619
1693
  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>>;
package/src/util.ts CHANGED
@@ -4,19 +4,19 @@
4
4
 
5
5
  import { defaultRowSize } from './defs';
6
6
  import {
7
- type DxGridPlaneCellIndex,
7
+ type DxGridAxis,
8
8
  type DxGridCellIndex,
9
- type DxGridPosition,
10
- type DxGridPointer,
11
- type DxGridSelectionProps,
12
- type DxGridPositionNullable,
13
- type DxGridPlane,
14
- type DxGridFrozenRowsPlane,
15
9
  type DxGridFrozenColsPlane,
16
10
  type DxGridFrozenPlane,
17
- type DxGridAxis,
11
+ type DxGridFrozenRowsPlane,
12
+ type DxGridPlane,
13
+ type DxGridPlaneCellIndex,
18
14
  type DxGridPlanePosition,
15
+ type DxGridPointer,
16
+ type DxGridPosition,
17
+ type DxGridPositionNullable,
19
18
  type DxGridReadonlyValue,
19
+ type DxGridSelectionProps,
20
20
  separator,
21
21
  } from './types';
22
22
 
@@ -25,7 +25,7 @@ export const toPlaneCellIndex = (cellCoords: Partial<DxGridPosition> & DxGridPla
25
25
 
26
26
  export function parseCellIndex(index: DxGridCellIndex): DxGridPosition;
27
27
  export function parseCellIndex(index: DxGridPlaneCellIndex): DxGridPlanePosition;
28
- // eslint-disable-next-line prefer-arrow-functions/prefer-arrow-functions
28
+
29
29
  export function parseCellIndex(index: DxGridPlaneCellIndex | DxGridCellIndex): DxGridPlanePosition | DxGridPosition {
30
30
  const coords = index.split(separator);
31
31
  if (coords.length === 3) {
@@ -123,6 +123,10 @@ export const closestAction = (target: EventTarget | null): { action: string | nu
123
123
  return { actionEl, action: actionEl?.getAttribute('data-dx-grid-action') ?? null };
124
124
  };
125
125
 
126
+ export const accessoryHandlesPointerdownAttrs = {
127
+ 'data-dx-grid-action': 'accessory',
128
+ };
129
+
126
130
  export const closestCell = (target: EventTarget | null, actionEl?: HTMLElement | null): DxGridPositionNullable => {
127
131
  let cellElement = actionEl;
128
132
  if (!cellElement) {