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

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 +11 -6
  12. package/dist/src/dx-grid.d.ts.map +1 -1
  13. package/dist/src/dx-grid.js +193 -154
  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 +22 -9
  34. package/src/dx-grid.ts +186 -101
  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/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.c4373fc",
4
4
  "description": "A grid Web Component using Lit",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -36,10 +36,10 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
39
- "lit": "^3.2.0"
39
+ "lit": "^3.3.1"
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.c4373fc",
43
+ "@dxos/test-utils": "0.8.4-main.c4373fc"
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
@@ -4,10 +4,10 @@
4
4
  @layer dx-tokens {
5
5
  :root {
6
6
  --dx-grid-cell-padding-block: 0.2rem;
7
- --dx-grid-cell-content-padding-block: calc(var(--dx-grid-cell-padding-block) - 1px);
7
+ --dx-grid-cell-content-padding-block: calc(var(--dx-grid-cell-padding-block) - var(--dx-gridFocusIndicatorWidth));
8
8
  --dx-grid-cell-editor-padding-block: var(--dx-grid-cell-content-padding-block);
9
9
  --dx-grid-cell-padding-inline: 0.25rem;
10
- --dx-grid-cell-content-padding-inline: calc(var(--dx-grid-cell-padding-inline) - 1px);
10
+ --dx-grid-cell-content-padding-inline: calc(var(--dx-grid-cell-padding-inline) - var(--dx-gridFocusIndicatorWidth));
11
11
  --dx-grid-cell-editor-padding-inline: var(--dx-grid-cell-content-padding-inline);
12
12
  }
13
13
  }
@@ -54,7 +54,7 @@
54
54
  position: absolute;
55
55
  inset: 0;
56
56
  pointer-events: none;
57
- border: 1px solid var(--dx-accentSurface);
57
+ border: var(--dx-gridFocusIndicatorWidth) solid var(--dx-gridFocusIndicatorColor);
58
58
  }
59
59
  }
60
60
 
@@ -98,7 +98,7 @@
98
98
  white-space: nowrap;
99
99
  block-size: 100%;
100
100
  position: relative;
101
- border: 1px solid transparent;
101
+ border: var(--dx-gridFocusIndicatorWidth) solid transparent;
102
102
  border-radius: 2px;
103
103
 
104
104
  &:has(.dx-tag), &:has(dx-tag-picker-item) {
@@ -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 {
@@ -163,13 +175,13 @@
163
175
  }
164
176
 
165
177
  &:not([aria-readonly='true']) > .dx-grid__cell__content {
166
- border-color: var(--dx-accentSurface);
178
+ border-color: var(--dx-gridFocusIndicatorColor);
167
179
  }
168
180
  }
169
181
  }
170
182
 
171
183
  &:focus-visible > .dx-grid__cell__content {
172
- border-color: var(--dx-accentSurface);
184
+ border-color: var(--dx-gridFocusIndicatorColor);
173
185
  }
174
186
  }
175
187
  }
@@ -181,7 +193,7 @@
181
193
  overflow-wrap: break-word;
182
194
 
183
195
  background: var(--dx-grid-base, var(--dx-baseSurface));
184
- border: 1px solid var(--dx-accentSurface);
196
+ border: var(--dx-gridFocusIndicatorWidth) solid var(--dx-gridFocusIndicatorColor);
185
197
  border-radius: 2px;
186
198
 
187
199
  --dx-grid-cell-editor-max-block-size: min(12em, 50vh);
@@ -206,7 +218,7 @@
206
218
  [role='columnheader'],
207
219
  [role='rowheader'] {
208
220
  &[data-dx-active]:not([aria-readonly='true']) > .dx-grid__cell__content {
209
- border-color: var(--dx-accentSurface);
221
+ border-color: var(--dx-gridFocusIndicatorColor);
210
222
  }
211
223
  }
212
224
  }
@@ -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,62 +3,63 @@
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')
@@ -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`.
@@ -213,6 +217,16 @@ export class DxGrid extends LitElement {
213
217
  @state()
214
218
  private templatefrozenRowsEnd = '';
215
219
 
220
+ //
221
+ // `frozen…Size` is used to measure space available for the non-fixed planes
222
+ //
223
+
224
+ @state()
225
+ private frozenColsSize = 0;
226
+
227
+ @state()
228
+ private frozenRowsSize = 0;
229
+
216
230
  //
217
231
  // Focus, selection, and resize states
218
232
  //
@@ -416,15 +430,13 @@ export class DxGrid extends LitElement {
416
430
  }
417
431
 
418
432
  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
- }
433
+ const colPlane = resolveColPlane(plane);
434
+ const rowPlane = resolveRowPlane(plane);
435
+ this.focusedCell = {
436
+ plane,
437
+ col: colPlane === 'grid' ? this.visColMin : 0,
438
+ row: rowPlane === 'grid' ? this.visRowMin : 0,
439
+ };
428
440
  this.focusedCellElement()?.focus({ preventScroll: true });
429
441
  }
430
442
 
@@ -481,6 +493,13 @@ export class DxGrid extends LitElement {
481
493
  event.preventDefault();
482
494
  this.dispatchEditRequest();
483
495
  break;
496
+ case 'Backspace':
497
+ case 'Delete':
498
+ if (!event.defaultPrevented) {
499
+ event.preventDefault();
500
+ this.dispatchEditRequest('');
501
+ }
502
+ break;
484
503
  default:
485
504
  if (event.key.length === 1 && event.key.match(/\P{Cc}/u) && !(event.metaKey || event.ctrlKey)) {
486
505
  this.dispatchEditRequest(event.key);
@@ -584,19 +603,18 @@ export class DxGrid extends LitElement {
584
603
  blockSize: 0,
585
604
  };
586
605
  if (
587
- Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||
588
- Math.abs(blockSize - this.sizeBlock) > resizeTolerance
606
+ Math.abs(inlineSize - this.frozenColsSize - this.sizeInline) > resizeTolerance ||
607
+ Math.abs(blockSize - this.frozenRowsSize - this.sizeBlock) > resizeTolerance
589
608
  ) {
590
609
  // console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);
591
- this.sizeInline = inlineSize;
592
- this.sizeBlock = blockSize;
610
+ this.sizeInline = inlineSize - this.frozenColsSize;
611
+ this.sizeBlock = blockSize - this.frozenRowsSize;
593
612
  this.updateVis();
594
613
  queueMicrotask(() => this.updatePos());
595
614
  }
596
615
  });
597
616
 
598
617
  private gridRef: Ref<HTMLDivElement> = createRef();
599
- private viewportRef: Ref<HTMLDivElement> = createRef();
600
618
 
601
619
  private maybeUpdateVisInline = () => {
602
620
  if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
@@ -721,6 +739,15 @@ export class DxGrid extends LitElement {
721
739
  this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
722
740
  .map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
723
741
  .join(' ');
742
+
743
+ this.frozenColsSize =
744
+ [...Array(this.frozen.frozenColsStart ?? 0)].reduce(
745
+ (sum, _, c0) => sum + this.colSize(c0, 'frozenColsStart'),
746
+ 0,
747
+ ) +
748
+ gap * Math.max(0, this.frozen.frozenColsStart ?? 0 - 1) +
749
+ [...Array(this.frozen.frozenColsEnd ?? 0)].reduce((sum, _, c0) => sum + this.colSize(c0, 'frozenColsEnd'), 0) +
750
+ gap * Math.max(0, this.frozen.frozenColsEnd ?? 0 - 1);
724
751
  }
725
752
 
726
753
  private updateVisBlock(): void {
@@ -770,6 +797,15 @@ export class DxGrid extends LitElement {
770
797
  this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
771
798
  .map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
772
799
  .join(' ');
800
+
801
+ this.frozenRowsSize =
802
+ [...Array(this.frozen.frozenRowsStart ?? 0)].reduce(
803
+ (sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsStart'),
804
+ 0,
805
+ ) +
806
+ gap * Math.max(0, this.frozen.frozenRowsStart ?? 0 - 1) +
807
+ [...Array(this.frozen.frozenRowsEnd ?? 0)].reduce((sum, _, r0) => sum + this.rowSize(r0, 'frozenRowsEnd'), 0) +
808
+ gap * Math.max(0, this.frozen.frozenRowsEnd ?? 0 - 1);
773
809
  }
774
810
 
775
811
  private updateVis(): void {
@@ -1068,16 +1104,40 @@ export class DxGrid extends LitElement {
1068
1104
  : !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
1069
1105
  }
1070
1106
 
1107
+ private clampAxisSize(
1108
+ plane: 'grid' | DxGridFrozenPlane,
1109
+ axis: DxGridAxis,
1110
+ index: number | string,
1111
+ requestedSize: number,
1112
+ ): number {
1113
+ const minSize =
1114
+ axis === 'col'
1115
+ ? (this.columns[plane]?.[index]?.minSize ??
1116
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.minSize ??
1117
+ sizeColMin)
1118
+ : (this.rows[plane]?.[index]?.minSize ??
1119
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.minSize ??
1120
+ sizeRowMin);
1121
+ const maxSize =
1122
+ axis === 'col'
1123
+ ? (this.columns[plane]?.[index]?.maxSize ??
1124
+ this.columnDefault[plane as DxGridFrozenColsPlane]?.maxSize ??
1125
+ sizeColMax)
1126
+ : (this.rows[plane]?.[index]?.maxSize ??
1127
+ this.rowDefault[plane as DxGridFrozenRowsPlane]?.maxSize ??
1128
+ sizeRowMax);
1129
+ return Math.max(minSize, Math.min(maxSize, requestedSize));
1130
+ }
1131
+
1071
1132
  private handleAxisResizeInternal(event: DxAxisResizeInternal): void {
1072
1133
  event.stopPropagation();
1073
1134
  const { plane, axis, delta, size, index, state } = event;
1135
+ const nextSize = this.clampAxisSize(plane, axis, index, size + delta);
1074
1136
  if (axis === 'col') {
1075
- const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
1076
1137
  this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
1077
1138
  this.updateVisInline();
1078
1139
  this.updateIntrinsicInlineSize();
1079
1140
  } else {
1080
- const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
1081
1141
  this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
1082
1142
  this.updateVisBlock();
1083
1143
  this.updateIntrinsicBlockSize();
@@ -1178,7 +1238,7 @@ export class DxGrid extends LitElement {
1178
1238
  ) {
1179
1239
  const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
1180
1240
  const rows = this.frozen[rowPlane];
1181
- return (rows ?? 0) > 0
1241
+ return (rows ?? 0) > 0 && this.limitColumns > 0
1182
1242
  ? html`<div
1183
1243
  role="none"
1184
1244
  class="dx-grid__plane--frozen-row"
@@ -1212,7 +1272,7 @@ export class DxGrid extends LitElement {
1212
1272
  ) {
1213
1273
  const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
1214
1274
  const cols = this.frozen[colPlane];
1215
- return (cols ?? 0) > 0
1275
+ return (cols ?? 0) > 0 && this.limitRows > 0
1216
1276
  ? html`<div
1217
1277
  role="none"
1218
1278
  class="dx-grid__plane--frozen-col"
@@ -1238,6 +1298,40 @@ export class DxGrid extends LitElement {
1238
1298
  : null;
1239
1299
  }
1240
1300
 
1301
+ private renderMainGrid(
1302
+ visibleCols: number,
1303
+ visibleRows: number,
1304
+ offsetInline: number,
1305
+ offsetBlock: number,
1306
+ selection: DxGridSelectionProps,
1307
+ ) {
1308
+ return this.limitRows > 0 && this.limitColumns > 0
1309
+ ? html`<div
1310
+ role="grid"
1311
+ class="dx-grid__plane--grid"
1312
+ tabindex="0"
1313
+ data-dx-grid-plane="grid"
1314
+ data-dx-grid-plane-row="1"
1315
+ data-dx-grid-plane-col="1"
1316
+ >
1317
+ <div
1318
+ role="none"
1319
+ class="dx-grid__plane--grid__content"
1320
+ style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
1321
+ .templateGridColumns};grid-template-rows:${this.templateGridRows};"
1322
+ >
1323
+ ${[...Array(visibleRows)].map((_, r0) => {
1324
+ return [...Array(visibleCols)].map((_, c0) => {
1325
+ const c = c0 + this.visColMin;
1326
+ const r = r0 + this.visRowMin;
1327
+ return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
1328
+ });
1329
+ })}
1330
+ </div>
1331
+ </div>`
1332
+ : null;
1333
+ }
1334
+
1241
1335
  private cellReadonly(col: number, row: number, plane: DxGridPlane): boolean {
1242
1336
  const colPlane = resolveColPlane(plane);
1243
1337
  const rowPlane = resolveRowPlane(plane);
@@ -1255,6 +1349,23 @@ export class DxGrid extends LitElement {
1255
1349
  return isReadonly(colReadOnly) || isReadonly(rowReadOnly);
1256
1350
  }
1257
1351
 
1352
+ private cellFocusUnfurl(col: number, row: number, plane: DxGridPlane): boolean {
1353
+ const colPlane = resolveColPlane(plane);
1354
+ const rowPlane = resolveRowPlane(plane);
1355
+
1356
+ // Check cell-specific setting first.
1357
+ const cellUnfurl = this.cell(col, row, plane)?.focusUnfurl;
1358
+ if (cellUnfurl !== undefined) {
1359
+ return cellUnfurl;
1360
+ }
1361
+
1362
+ // Check column/row defaults.
1363
+ const colUnfurl = this.columns?.[colPlane]?.[col]?.focusUnfurl ?? this.columnDefault?.[colPlane]?.focusUnfurl;
1364
+ const rowUnfurl = this.rows?.[rowPlane]?.[row]?.focusUnfurl ?? this.rowDefault?.[rowPlane]?.focusUnfurl;
1365
+
1366
+ return colUnfurl ?? rowUnfurl ?? focusUnfurlDefault;
1367
+ }
1368
+
1258
1369
  /**
1259
1370
  * Determines if the cell's text content should be selectable based on its readonly value.
1260
1371
  * @returns true if the cells text content is selectable, false otherwise.
@@ -1317,6 +1428,7 @@ export class DxGrid extends LitElement {
1317
1428
  const cell = this.cell(col, row, plane);
1318
1429
  const active = this.cellActive(col, row, plane);
1319
1430
  const readonly = this.cellReadonly(col, row, plane);
1431
+ const focusUnfurl = this.cellFocusUnfurl(col, row, plane);
1320
1432
  const textSelectable = this.cellTextSelectable(col, row, plane);
1321
1433
  const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
1322
1434
  const resizePlane = cell?.resizeHandle ? resolveFrozenPlane(cell.resizeHandle, plane) : undefined;
@@ -1328,6 +1440,7 @@ export class DxGrid extends LitElement {
1328
1440
  aria-readonly=${readonly ? 'true' : nothing}
1329
1441
  class=${cell?.className ?? nothing}
1330
1442
  data-refs=${cell?.dataRefs ?? nothing}
1443
+ data-focus-unfurl=${focusUnfurl ? nothing : 'false'}
1331
1444
  ?data-dx-active=${active}
1332
1445
  data-text-selectable=${textSelectable ? 'true' : 'false'}
1333
1446
  data-dx-grid-action="cell"
@@ -1370,13 +1483,24 @@ export class DxGrid extends LitElement {
1370
1483
  <div
1371
1484
  role="none"
1372
1485
  class="dx-grid"
1486
+ data-arrow-keys="all"
1373
1487
  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' : ''}`,
1488
+ 'grid-template-columns': [
1489
+ this.templatefrozenColsStart ? 'min-content' : false,
1490
+ this.limitColumns > 0 &&
1491
+ `minmax(0, ${Number.isFinite(this.limitColumns) ? `${Math.max(0, this.intrinsicInlineSize)}px` : '1fr'})`,
1492
+ this.templatefrozenColsEnd ? 'min-content' : false,
1493
+ ]
1494
+ .filter(Boolean)
1495
+ .join(' '),
1496
+ 'grid-template-rows': [
1497
+ this.templatefrozenRowsStart ? 'min-content' : false,
1498
+ this.limitRows > 0 &&
1499
+ `minmax(0, ${Number.isFinite(this.limitRows) ? `${Math.max(0, this.intrinsicBlockSize)}px` : '1fr'})`,
1500
+ this.templatefrozenRowsEnd ? ' min-content' : false,
1501
+ ]
1502
+ .filter(Boolean)
1503
+ .join(' '),
1380
1504
  '--dx-grid-content-inline-size': Number.isFinite(this.limitColumns)
1381
1505
  ? `${Math.max(0, this.totalIntrinsicInlineSize)}px`
1382
1506
  : 'max-content',
@@ -1386,6 +1510,7 @@ export class DxGrid extends LitElement {
1386
1510
  })}
1387
1511
  data-grid=${this.gridId}
1388
1512
  data-grid-mode=${this.mode}
1513
+ data-grid-focus-indicator-variant=${this.focusIndicatorVariant}
1389
1514
  ?data-grid-select=${selection.visible}
1390
1515
  ${ref(this.gridRef)}
1391
1516
  >
@@ -1400,30 +1525,7 @@ export class DxGrid extends LitElement {
1400
1525
  offsetBlock,
1401
1526
  selection,
1402
1527
  )}
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>
1528
+ ${this.renderMainGrid(visibleCols, visibleRows, offsetInline, offsetBlock, selection)}
1427
1529
  ${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
1428
1530
  'fixedEndStart',
1429
1531
  selection,
@@ -1437,37 +1539,19 @@ export class DxGrid extends LitElement {
1437
1539
  private updateIntrinsicInlineSize(): void {
1438
1540
  this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
1439
1541
  ? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
1440
- gap * (this.limitColumns - 1)
1542
+ gap * Math.max(0, this.limitColumns - 1)
1441
1543
  : Infinity;
1442
1544
  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);
1545
+ this.limitColumns > 0 ? this.intrinsicInlineSize + this.frozenColsSize : this.frozenColsSize - gap;
1453
1546
  }
1454
1547
 
1455
1548
  private updateIntrinsicBlockSize(): void {
1456
1549
  this.intrinsicBlockSize = Number.isFinite(this.limitRows)
1457
1550
  ? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
1458
- gap * (this.limitRows - 1)
1551
+ gap * Math.max(0, this.limitRows - 1)
1459
1552
  : Infinity;
1460
1553
  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);
1554
+ this.limitRows > 0 ? this.intrinsicBlockSize + this.frozenRowsSize : this.frozenRowsSize - gap;
1471
1555
  }
1472
1556
 
1473
1557
  private updateIntrinsicSizes(): void {
@@ -1515,7 +1599,7 @@ export class DxGrid extends LitElement {
1515
1599
  if (this.getCells) {
1516
1600
  this.updateCells(true);
1517
1601
  }
1518
- this.observer.observe(this.viewportRef.value!);
1602
+ this.observer.observe(this.gridRef.value!);
1519
1603
  this.computeColSizes();
1520
1604
  this.computeRowSizes();
1521
1605
  this.updateIntrinsicSizes();
@@ -1589,8 +1673,8 @@ export class DxGrid extends LitElement {
1589
1673
 
1590
1674
  override disconnectedCallback(): void {
1591
1675
  super.disconnectedCallback();
1592
- if (this.viewportRef.value) {
1593
- this.observer.unobserve(this.viewportRef.value);
1676
+ if (this.gridRef.value) {
1677
+ this.observer.unobserve(this.gridRef.value);
1594
1678
  }
1595
1679
  document.defaultView?.removeEventListener('wheel', this.handleTopLevelWheel);
1596
1680
  }
@@ -1608,6 +1692,7 @@ export {
1608
1692
  parseCellIndex,
1609
1693
  toPlaneCellIndex,
1610
1694
  cellQuery,
1695
+ accessoryHandlesPointerdownAttrs,
1611
1696
  } from './util';
1612
1697
 
1613
1698
  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