@dxos/lit-grid 0.8.4-main.c1de068 → 0.8.4-main.e098934

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 +181 -145
  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 +172 -91
  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.c1de068",
3
+ "version": "0.8.4-main.e098934",
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.c1de068",
43
- "@dxos/test-utils": "0.8.4-main.c1de068"
42
+ "@dxos/random": "0.8.4-main.e098934",
43
+ "@dxos/test-utils": "0.8.4-main.e098934"
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,23 +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';
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,
@@ -29,36 +29,35 @@ import {
29
29
  type DxGridFrozenRowsPlane,
30
30
  type DxGridMode,
31
31
  type DxGridPlane,
32
+ type DxGridPlaneCellIndex,
32
33
  type DxGridPlaneCells,
33
34
  type DxGridPlaneRange,
34
35
  type DxGridPlaneRecord,
35
36
  type DxGridPointer,
36
37
  type DxGridPosition,
37
- type DxGridAxis,
38
- type DxGridSelectionProps,
39
- type DxGridAnnotatedPanEvent,
40
38
  type DxGridRange,
39
+ type DxGridSelectionProps,
41
40
  separator,
42
41
  } from './types';
43
42
  import {
44
- toCellIndex,
45
- gap,
46
- resizeTolerance,
47
- sizeColMin,
48
- sizeColMax,
49
- sizeRowMin,
50
- sizeRowMax,
51
- shouldSelect,
52
- selectionProps,
53
43
  cellSelected,
54
44
  closestAction,
55
45
  closestCell,
56
- targetIsPlane,
57
- resolveRowPlane,
46
+ gap,
47
+ isReadonly,
48
+ isSameCell,
49
+ resizeTolerance,
58
50
  resolveColPlane,
59
51
  resolveFrozenPlane,
60
- isSameCell,
61
- isReadonly,
52
+ resolveRowPlane,
53
+ selectionProps,
54
+ shouldSelect,
55
+ sizeColMax,
56
+ sizeColMin,
57
+ sizeRowMax,
58
+ sizeRowMin,
59
+ targetIsPlane,
60
+ toCellIndex,
62
61
  } from './util';
63
62
 
64
63
  @customElement('dx-grid')
@@ -84,12 +83,12 @@ export class DxGrid extends LitElement {
84
83
  gridId: string = 'default-grid-id';
85
84
 
86
85
  @property({ type: Object })
87
- rowDefault: DxGridPlaneRecord<DxGridFrozenRowsPlane, DxGridAxisMetaProps> = {
86
+ rowDefault: Partial<DxGridPlaneRecord<DxGridFrozenRowsPlane, Partial<DxGridAxisMetaProps>>> = {
88
87
  grid: { size: defaultRowSize },
89
88
  };
90
89
 
91
90
  @property({ type: Object })
92
- columnDefault: DxGridPlaneRecord<DxGridFrozenColsPlane, DxGridAxisMetaProps> = {
91
+ columnDefault: Partial<DxGridPlaneRecord<DxGridFrozenColsPlane, Partial<DxGridAxisMetaProps>>> = {
93
92
  grid: { size: defaultColSize },
94
93
  };
95
94
 
@@ -213,6 +212,16 @@ export class DxGrid extends LitElement {
213
212
  @state()
214
213
  private templatefrozenRowsEnd = '';
215
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
+
216
225
  //
217
226
  // Focus, selection, and resize states
218
227
  //
@@ -481,6 +490,13 @@ export class DxGrid extends LitElement {
481
490
  event.preventDefault();
482
491
  this.dispatchEditRequest();
483
492
  break;
493
+ case 'Backspace':
494
+ case 'Delete':
495
+ if (!event.defaultPrevented) {
496
+ event.preventDefault();
497
+ this.dispatchEditRequest('');
498
+ }
499
+ break;
484
500
  default:
485
501
  if (event.key.length === 1 && event.key.match(/\P{Cc}/u) && !(event.metaKey || event.ctrlKey)) {
486
502
  this.dispatchEditRequest(event.key);
@@ -584,19 +600,18 @@ export class DxGrid extends LitElement {
584
600
  blockSize: 0,
585
601
  };
586
602
  if (
587
- Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||
588
- Math.abs(blockSize - this.sizeBlock) > resizeTolerance
603
+ Math.abs(inlineSize - this.frozenColsSize - this.sizeInline) > resizeTolerance ||
604
+ Math.abs(blockSize - this.frozenRowsSize - this.sizeBlock) > resizeTolerance
589
605
  ) {
590
606
  // console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);
591
- this.sizeInline = inlineSize;
592
- this.sizeBlock = blockSize;
607
+ this.sizeInline = inlineSize - this.frozenColsSize;
608
+ this.sizeBlock = blockSize - this.frozenRowsSize;
593
609
  this.updateVis();
594
610
  queueMicrotask(() => this.updatePos());
595
611
  }
596
612
  });
597
613
 
598
614
  private gridRef: Ref<HTMLDivElement> = createRef();
599
- private viewportRef: Ref<HTMLDivElement> = createRef();
600
615
 
601
616
  private maybeUpdateVisInline = () => {
602
617
  if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
@@ -721,6 +736,15 @@ export class DxGrid extends LitElement {
721
736
  this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
722
737
  .map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
723
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);
724
748
  }
725
749
 
726
750
  private updateVisBlock(): void {
@@ -770,6 +794,15 @@ export class DxGrid extends LitElement {
770
794
  this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
771
795
  .map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
772
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);
773
806
  }
774
807
 
775
808
  private updateVis(): void {
@@ -1068,16 +1101,40 @@ export class DxGrid extends LitElement {
1068
1101
  : !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
1069
1102
  }
1070
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
+
1071
1129
  private handleAxisResizeInternal(event: DxAxisResizeInternal): void {
1072
1130
  event.stopPropagation();
1073
1131
  const { plane, axis, delta, size, index, state } = event;
1132
+ const nextSize = this.clampAxisSize(plane, axis, index, size + delta);
1074
1133
  if (axis === 'col') {
1075
- const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
1076
1134
  this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
1077
1135
  this.updateVisInline();
1078
1136
  this.updateIntrinsicInlineSize();
1079
1137
  } else {
1080
- const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
1081
1138
  this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
1082
1139
  this.updateVisBlock();
1083
1140
  this.updateIntrinsicBlockSize();
@@ -1178,7 +1235,7 @@ export class DxGrid extends LitElement {
1178
1235
  ) {
1179
1236
  const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
1180
1237
  const rows = this.frozen[rowPlane];
1181
- return (rows ?? 0) > 0
1238
+ return (rows ?? 0) > 0 && this.limitColumns > 0
1182
1239
  ? html`<div
1183
1240
  role="none"
1184
1241
  class="dx-grid__plane--frozen-row"
@@ -1212,7 +1269,7 @@ export class DxGrid extends LitElement {
1212
1269
  ) {
1213
1270
  const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
1214
1271
  const cols = this.frozen[colPlane];
1215
- return (cols ?? 0) > 0
1272
+ return (cols ?? 0) > 0 && this.limitRows > 0
1216
1273
  ? html`<div
1217
1274
  role="none"
1218
1275
  class="dx-grid__plane--frozen-col"
@@ -1238,6 +1295,40 @@ export class DxGrid extends LitElement {
1238
1295
  : null;
1239
1296
  }
1240
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
+
1241
1332
  private cellReadonly(col: number, row: number, plane: DxGridPlane): boolean {
1242
1333
  const colPlane = resolveColPlane(plane);
1243
1334
  const rowPlane = resolveRowPlane(plane);
@@ -1255,6 +1346,23 @@ export class DxGrid extends LitElement {
1255
1346
  return isReadonly(colReadOnly) || isReadonly(rowReadOnly);
1256
1347
  }
1257
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
+
1258
1366
  /**
1259
1367
  * Determines if the cell's text content should be selectable based on its readonly value.
1260
1368
  * @returns true if the cells text content is selectable, false otherwise.
@@ -1317,6 +1425,7 @@ export class DxGrid extends LitElement {
1317
1425
  const cell = this.cell(col, row, plane);
1318
1426
  const active = this.cellActive(col, row, plane);
1319
1427
  const readonly = this.cellReadonly(col, row, plane);
1428
+ const focusUnfurl = this.cellFocusUnfurl(col, row, plane);
1320
1429
  const textSelectable = this.cellTextSelectable(col, row, plane);
1321
1430
  const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
1322
1431
  const resizePlane = cell?.resizeHandle ? resolveFrozenPlane(cell.resizeHandle, plane) : undefined;
@@ -1328,6 +1437,7 @@ export class DxGrid extends LitElement {
1328
1437
  aria-readonly=${readonly ? 'true' : nothing}
1329
1438
  class=${cell?.className ?? nothing}
1330
1439
  data-refs=${cell?.dataRefs ?? nothing}
1440
+ data-focus-unfurl=${focusUnfurl ? nothing : 'false'}
1331
1441
  ?data-dx-active=${active}
1332
1442
  data-text-selectable=${textSelectable ? 'true' : 'false'}
1333
1443
  data-dx-grid-action="cell"
@@ -1370,13 +1480,24 @@ export class DxGrid extends LitElement {
1370
1480
  <div
1371
1481
  role="none"
1372
1482
  class="dx-grid"
1483
+ data-arrow-keys="all"
1373
1484
  style=${styleMap({
1374
- 'grid-template-columns': `${this.templatefrozenColsStart ? 'min-content ' : ''}minmax(0, ${
1375
- Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'
1376
- })${this.templatefrozenColsEnd ? ' min-content' : ''}`,
1377
- 'grid-template-rows': `${this.templatefrozenRowsStart ? 'min-content ' : ''}minmax(0, ${
1378
- Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'
1379
- })${this.templatefrozenRowsEnd ? ' min-content' : ''}`,
1485
+ 'grid-template-columns': [
1486
+ this.templatefrozenColsStart ? 'min-content' : false,
1487
+ this.limitColumns > 0 &&
1488
+ `minmax(0, ${Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'})`,
1489
+ this.templatefrozenColsEnd ? 'min-content' : false,
1490
+ ]
1491
+ .filter(Boolean)
1492
+ .join(' '),
1493
+ 'grid-template-rows': [
1494
+ this.templatefrozenRowsStart ? 'min-content' : false,
1495
+ this.limitRows > 0 &&
1496
+ `minmax(0, ${Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'})`,
1497
+ this.templatefrozenRowsEnd ? ' min-content' : false,
1498
+ ]
1499
+ .filter(Boolean)
1500
+ .join(' '),
1380
1501
  '--dx-grid-content-inline-size': Number.isFinite(this.limitColumns)
1381
1502
  ? `${Math.max(0, this.totalIntrinsicInlineSize)}px`
1382
1503
  : 'max-content',
@@ -1400,30 +1521,7 @@ export class DxGrid extends LitElement {
1400
1521
  offsetBlock,
1401
1522
  selection,
1402
1523
  )}
1403
- <div
1404
- role="grid"
1405
- class="dx-grid__plane--grid"
1406
- tabindex="0"
1407
- data-dx-grid-plane="grid"
1408
- data-dx-grid-plane-row="1"
1409
- data-dx-grid-plane-col="1"
1410
- ${ref(this.viewportRef)}
1411
- >
1412
- <div
1413
- role="none"
1414
- class="dx-grid__plane--grid__content"
1415
- style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1416
- .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1417
- >
1418
- ${[...Array(visibleRows)].map((_, r0) => {
1419
- return [...Array(visibleCols)].map((_, c0) => {
1420
- const c = c0 + this.visColMin;
1421
- const r = r0 + this.visRowMin;
1422
- return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1423
- });
1424
- })}
1425
- </div>
1426
- </div>
1524
+ ${this.renderMainGrid(visibleCols, visibleRows, offsetInline, offsetBlock, selection)}
1427
1525
  ${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
1428
1526
  'fixedEndStart',
1429
1527
  selection,
@@ -1437,37 +1535,19 @@ export class DxGrid extends LitElement {
1437
1535
  private updateIntrinsicInlineSize(): void {
1438
1536
  this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
1439
1537
  ? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
1440
- gap * (this.limitColumns - 1)
1538
+ gap * Math.max(0, this.limitColumns - 1)
1441
1539
  : Infinity;
1442
1540
  this.totalIntrinsicInlineSize =
1443
- this.intrinsicInlineSize +
1444
- (Number.isFinite(this.frozen.frozenColsStart)
1445
- ? [...Array(this.frozen.frozenColsStart)].reduce(
1446
- (acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsStart'),
1447
- 0,
1448
- )
1449
- : 0) +
1450
- (Number.isFinite(this.frozen.frozenColsEnd)
1451
- ? [...Array(this.frozen.frozenColsEnd)].reduce((acc, _, c0) => acc + gap + this.colSize(c0, 'frozenColsEnd'), 0)
1452
- : 0);
1541
+ this.limitColumns > 0 ? this.intrinsicInlineSize + this.frozenColsSize : this.frozenColsSize - gap;
1453
1542
  }
1454
1543
 
1455
1544
  private updateIntrinsicBlockSize(): void {
1456
1545
  this.intrinsicBlockSize = Number.isFinite(this.limitRows)
1457
1546
  ? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
1458
- gap * (this.limitRows - 1)
1547
+ gap * Math.max(0, this.limitRows - 1)
1459
1548
  : Infinity;
1460
1549
  this.totalIntrinsicBlockSize =
1461
- this.intrinsicBlockSize +
1462
- (Number.isFinite(this.frozen.frozenRowsStart)
1463
- ? [...Array(this.frozen.frozenRowsStart)].reduce(
1464
- (acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsStart'),
1465
- 0,
1466
- )
1467
- : 0) +
1468
- (Number.isFinite(this.frozen.frozenRowsEnd)
1469
- ? [...Array(this.frozen.frozenRowsEnd)].reduce((acc, _, r0) => acc + gap + this.rowSize(r0, 'frozenRowsEnd'), 0)
1470
- : 0);
1550
+ this.limitRows > 0 ? this.intrinsicBlockSize + this.frozenRowsSize : this.frozenRowsSize - gap;
1471
1551
  }
1472
1552
 
1473
1553
  private updateIntrinsicSizes(): void {
@@ -1515,7 +1595,7 @@ export class DxGrid extends LitElement {
1515
1595
  if (this.getCells) {
1516
1596
  this.updateCells(true);
1517
1597
  }
1518
- this.observer.observe(this.viewportRef.value!);
1598
+ this.observer.observe(this.gridRef.value!);
1519
1599
  this.computeColSizes();
1520
1600
  this.computeRowSizes();
1521
1601
  this.updateIntrinsicSizes();
@@ -1589,8 +1669,8 @@ export class DxGrid extends LitElement {
1589
1669
 
1590
1670
  override disconnectedCallback(): void {
1591
1671
  super.disconnectedCallback();
1592
- if (this.viewportRef.value) {
1593
- this.observer.unobserve(this.viewportRef.value);
1672
+ if (this.gridRef.value) {
1673
+ this.observer.unobserve(this.gridRef.value);
1594
1674
  }
1595
1675
  document.defaultView?.removeEventListener('wheel', this.handleTopLevelWheel);
1596
1676
  }
@@ -1608,6 +1688,7 @@ export {
1608
1688
  parseCellIndex,
1609
1689
  toPlaneCellIndex,
1610
1690
  cellQuery,
1691
+ accessoryHandlesPointerdownAttrs,
1611
1692
  } from './util';
1612
1693
 
1613
1694
  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) {