@dxos/lit-grid 0.6.12 → 0.6.13-main.09887cd
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.
- package/dist/src/dx-grid-axis-resize-handle.d.ts +16 -0
- package/dist/src/dx-grid-axis-resize-handle.d.ts.map +1 -0
- package/dist/src/dx-grid-axis-resize-handle.js +96 -0
- package/dist/src/dx-grid-axis-resize-handle.js.map +1 -0
- package/dist/src/dx-grid.d.ts +117 -0
- package/dist/src/dx-grid.d.ts.map +1 -0
- package/dist/src/dx-grid.js +1070 -0
- package/dist/src/dx-grid.js.map +1 -0
- package/dist/src/dx-grid.lit-stories.d.ts +42 -0
- package/dist/src/dx-grid.lit-stories.d.ts.map +1 -0
- package/dist/src/dx-grid.lit-stories.js +166 -0
- package/dist/src/dx-grid.lit-stories.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types.d.ts +110 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +44 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/util.d.ts +9 -0
- package/dist/src/util.d.ts.map +1 -0
- package/dist/src/util.js +19 -0
- package/dist/src/util.js.map +1 -0
- package/dist/types/src/dx-grid-axis-resize-handle.d.ts +16 -0
- package/dist/types/src/dx-grid-axis-resize-handle.d.ts.map +1 -0
- package/dist/types/src/dx-grid-axis-resize-handle.js +96 -0
- package/dist/types/src/dx-grid-axis-resize-handle.js.map +1 -0
- package/dist/types/src/dx-grid.d.ts +95 -61
- package/dist/types/src/dx-grid.d.ts.map +1 -1
- package/dist/types/src/dx-grid.js +1070 -0
- package/dist/types/src/dx-grid.js.map +1 -0
- package/dist/types/src/dx-grid.lit-stories.d.ts +27 -2
- package/dist/types/src/dx-grid.lit-stories.d.ts.map +1 -1
- package/dist/types/src/dx-grid.lit-stories.js +166 -0
- package/dist/types/src/dx-grid.lit-stories.js.map +1 -0
- package/dist/types/src/index.js +6 -0
- package/dist/types/src/index.js.map +1 -0
- package/dist/types/src/types.d.ts +102 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/types.js +44 -0
- package/dist/types/src/types.js.map +1 -0
- package/dist/types/src/util.d.ts +9 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/dist/types/src/util.js +19 -0
- package/dist/types/src/util.js.map +1 -0
- package/package.json +5 -6
- package/src/dx-grid-axis-resize-handle.ts +87 -0
- package/src/dx-grid.lit-stories.ts +148 -21
- package/src/dx-grid.pcss +72 -68
- package/src/dx-grid.ts +934 -329
- package/src/types.ts +139 -1
- package/src/util.ts +28 -0
- package/dist/lib/browser/index.mjs +0 -578
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
package/src/dx-grid.ts
CHANGED
|
@@ -2,11 +2,42 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { LitElement, html } from 'lit';
|
|
6
|
-
import { customElement, state, property
|
|
5
|
+
import { LitElement, html, nothing } from 'lit';
|
|
6
|
+
import { customElement, state, property } from 'lit/decorators.js';
|
|
7
7
|
import { ref, createRef, type Ref } from 'lit/directives/ref.js';
|
|
8
|
-
|
|
9
|
-
import {
|
|
8
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
9
|
+
import { unsafeStatic, html as staticHtml } from 'lit/static-html.js';
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line unused-imports/no-unused-imports
|
|
12
|
+
import './dx-grid-axis-resize-handle';
|
|
13
|
+
import {
|
|
14
|
+
type DxGridAxisMetaProps,
|
|
15
|
+
type DxGridAxisSizes,
|
|
16
|
+
type DxGridCellIndex,
|
|
17
|
+
type DxGridCellValue,
|
|
18
|
+
DxAxisResize,
|
|
19
|
+
type DxAxisResizeInternal,
|
|
20
|
+
DxEditRequest,
|
|
21
|
+
type DxGridAxisMeta,
|
|
22
|
+
type DxGridCells,
|
|
23
|
+
DxGridCellsSelect,
|
|
24
|
+
type DxGridFixedPlane,
|
|
25
|
+
type DxGridFrozenAxes,
|
|
26
|
+
type DxGridFrozenColsPlane,
|
|
27
|
+
type DxGridFrozenPlane,
|
|
28
|
+
type DxGridFrozenRowsPlane,
|
|
29
|
+
type DxGridMode,
|
|
30
|
+
type DxGridPlane,
|
|
31
|
+
type DxGridPlaneCells,
|
|
32
|
+
type DxGridPlaneRange,
|
|
33
|
+
type DxGridPlaneRecord,
|
|
34
|
+
type DxGridPointer,
|
|
35
|
+
type DxGridPosition,
|
|
36
|
+
type DxGridPositionNullable,
|
|
37
|
+
type DxGridAxis,
|
|
38
|
+
type DxGridSelectionProps,
|
|
39
|
+
} from './types';
|
|
40
|
+
import { separator, toCellIndex } from './util';
|
|
10
41
|
|
|
11
42
|
/**
|
|
12
43
|
* The size in pixels of the gap between cells
|
|
@@ -14,10 +45,14 @@ import { DxAxisResize, type DxAxisResizeProps, type DxGridAxis } from './types';
|
|
|
14
45
|
const gap = 1;
|
|
15
46
|
|
|
16
47
|
/**
|
|
17
|
-
*
|
|
18
|
-
* changes when scrolling vertically.
|
|
48
|
+
* ResizeObserver notices even subpixel changes, only respond to changes of at least 1px.
|
|
19
49
|
*/
|
|
20
|
-
const resizeTolerance =
|
|
50
|
+
const resizeTolerance = 1;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The amount of pixels the primary pointer has to move after PointerDown to engage in selection.
|
|
54
|
+
*/
|
|
55
|
+
const selectTolerance = 4;
|
|
21
56
|
|
|
22
57
|
//
|
|
23
58
|
// `overscan` is the number of columns or rows to render outside of the viewport
|
|
@@ -33,116 +68,225 @@ const sizeColMax = 1024;
|
|
|
33
68
|
const sizeRowMin = 16;
|
|
34
69
|
const sizeRowMax = 1024;
|
|
35
70
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
const shouldSelect = (pointer: DxGridPointer, { pageX, pageY }: PointerEvent) => {
|
|
72
|
+
if (pointer?.state === 'maybeSelecting') {
|
|
73
|
+
return Math.hypot(Math.abs(pointer.pageX - pageX), Math.abs(pointer.pageY - pageY)) >= selectTolerance;
|
|
74
|
+
} else {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
40
78
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
79
|
+
const selectionProps = (selectionStart: DxGridPosition, selectionEnd: DxGridPosition): DxGridSelectionProps => {
|
|
80
|
+
const colMin = Math.min(selectionStart.col, selectionEnd.col);
|
|
81
|
+
const colMax = Math.max(selectionStart.col, selectionEnd.col);
|
|
82
|
+
const rowMin = Math.min(selectionStart.row, selectionEnd.row);
|
|
83
|
+
const rowMax = Math.max(selectionStart.row, selectionEnd.row);
|
|
84
|
+
const plane = selectionStart.plane;
|
|
85
|
+
const visible = colMin !== colMax || rowMin !== rowMax;
|
|
86
|
+
return { colMin, colMax, rowMin, rowMax, plane, visible };
|
|
87
|
+
};
|
|
44
88
|
|
|
45
|
-
const
|
|
89
|
+
const cellSelected = (col: number, row: number, plane: DxGridPlane, selection: DxGridSelectionProps): boolean => {
|
|
46
90
|
return (
|
|
47
|
-
|
|
48
|
-
|
|
91
|
+
plane === selection.plane &&
|
|
92
|
+
col >= selection.colMin &&
|
|
93
|
+
col <= selection.colMax &&
|
|
94
|
+
row >= selection.rowMin &&
|
|
95
|
+
row <= selection.rowMax
|
|
49
96
|
);
|
|
50
97
|
};
|
|
51
98
|
|
|
52
|
-
const
|
|
53
|
-
|
|
99
|
+
const closestAction = (target: EventTarget | null): { action: string | null; actionEl: HTMLElement | null } => {
|
|
100
|
+
const actionEl: HTMLElement | null = (target as HTMLElement | null)?.closest('[data-dx-grid-action]') ?? null;
|
|
101
|
+
return { actionEl, action: actionEl?.getAttribute('data-dx-grid-action') ?? null };
|
|
54
102
|
};
|
|
55
103
|
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
104
|
+
export const closestCell = (target: EventTarget | null, actionEl?: HTMLElement | null): DxGridPositionNullable => {
|
|
105
|
+
let cellElement = actionEl;
|
|
106
|
+
if (!cellElement) {
|
|
107
|
+
const { action, actionEl } = closestAction(target);
|
|
108
|
+
if (action === 'cell') {
|
|
109
|
+
cellElement = actionEl as HTMLElement;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (cellElement) {
|
|
113
|
+
const col = parseInt(cellElement.getAttribute('aria-colindex') ?? 'never');
|
|
114
|
+
const row = parseInt(cellElement.getAttribute('aria-rowindex') ?? 'never');
|
|
115
|
+
const plane = (cellElement.closest('[data-dx-grid-plane]')?.getAttribute('data-dx-grid-plane') ??
|
|
116
|
+
'grid') as DxGridPlane;
|
|
117
|
+
return { plane, col, row };
|
|
118
|
+
} else {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const resolveRowPlane = (plane: DxGridPlane): 'grid' | DxGridFrozenRowsPlane => {
|
|
124
|
+
switch (plane) {
|
|
125
|
+
case 'fixedStartStart':
|
|
126
|
+
case 'fixedStartEnd':
|
|
127
|
+
case 'frozenRowsStart':
|
|
128
|
+
return 'frozenRowsStart';
|
|
129
|
+
case 'fixedEndStart':
|
|
130
|
+
case 'fixedEndEnd':
|
|
131
|
+
case 'frozenRowsEnd':
|
|
132
|
+
return 'frozenRowsEnd';
|
|
133
|
+
default:
|
|
134
|
+
return 'grid';
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const resolveColPlane = (plane: DxGridPlane): 'grid' | DxGridFrozenColsPlane => {
|
|
139
|
+
switch (plane) {
|
|
140
|
+
case 'fixedStartStart':
|
|
141
|
+
case 'fixedEndStart':
|
|
142
|
+
case 'frozenColsStart':
|
|
143
|
+
return 'frozenColsStart';
|
|
144
|
+
case 'fixedStartEnd':
|
|
145
|
+
case 'fixedEndEnd':
|
|
146
|
+
case 'frozenColsEnd':
|
|
147
|
+
return 'frozenColsEnd';
|
|
148
|
+
default:
|
|
149
|
+
return 'grid';
|
|
150
|
+
}
|
|
69
151
|
};
|
|
70
152
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
153
|
+
const resolveResizePlane = (resizeAxis: DxGridAxis, cellPlane: DxGridPlane): 'grid' | DxGridFrozenPlane => {
|
|
154
|
+
switch (cellPlane) {
|
|
155
|
+
case 'fixedStartStart':
|
|
156
|
+
return resizeAxis === 'col' ? 'frozenColsStart' : 'frozenRowsStart';
|
|
157
|
+
case 'fixedStartEnd':
|
|
158
|
+
return resizeAxis === 'col' ? 'frozenColsEnd' : 'frozenRowsStart';
|
|
159
|
+
case 'fixedEndStart':
|
|
160
|
+
return resizeAxis === 'col' ? 'frozenColsStart' : 'frozenRowsEnd';
|
|
161
|
+
case 'fixedEndEnd':
|
|
162
|
+
return resizeAxis === 'col' ? 'frozenColsEnd' : 'frozenRowsEnd';
|
|
163
|
+
case 'frozenColsStart':
|
|
164
|
+
case 'frozenColsEnd':
|
|
165
|
+
return resizeAxis === 'col' ? cellPlane : 'grid';
|
|
166
|
+
case 'frozenRowsStart':
|
|
167
|
+
case 'frozenRowsEnd':
|
|
168
|
+
return resizeAxis === 'row' ? cellPlane : 'grid';
|
|
169
|
+
default:
|
|
170
|
+
return cellPlane;
|
|
171
|
+
}
|
|
75
172
|
};
|
|
76
173
|
|
|
77
|
-
|
|
174
|
+
const isSameCell = (a: DxGridPositionNullable, b: DxGridPositionNullable) =>
|
|
175
|
+
a &&
|
|
176
|
+
b &&
|
|
177
|
+
a.plane === b.plane &&
|
|
178
|
+
Number.isFinite(a.col) &&
|
|
179
|
+
Number.isFinite(a.row) &&
|
|
180
|
+
a.col === b.col &&
|
|
181
|
+
a.row === b.row;
|
|
78
182
|
|
|
79
|
-
const
|
|
80
|
-
const localRhId = (r0: number) => `rh--${r0}`;
|
|
183
|
+
const defaultRowSize = 32;
|
|
81
184
|
|
|
82
|
-
const
|
|
185
|
+
const defaultColSize = 180;
|
|
83
186
|
|
|
84
187
|
@customElement('dx-grid')
|
|
85
188
|
export class DxGrid extends LitElement {
|
|
189
|
+
constructor() {
|
|
190
|
+
super();
|
|
191
|
+
this.addEventListener('dx-axis-resize-internal', this.handleAxisResizeInternal as EventListener);
|
|
192
|
+
this.addEventListener('wheel', this.handleWheel, { passive: true });
|
|
193
|
+
this.addEventListener('pointerdown', this.handlePointerDown);
|
|
194
|
+
this.addEventListener('pointermove', this.handlePointerMove);
|
|
195
|
+
this.addEventListener('pointerup', this.handlePointerUp);
|
|
196
|
+
this.addEventListener('pointerleave', this.handlePointerUp);
|
|
197
|
+
this.addEventListener('focus', this.handleFocus, { capture: true });
|
|
198
|
+
this.addEventListener('blur', this.handleBlur, { capture: true });
|
|
199
|
+
this.addEventListener('keydown', this.handleKeydown);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@property({ type: String })
|
|
203
|
+
gridId: string = 'default-grid-id';
|
|
204
|
+
|
|
86
205
|
@property({ type: Object })
|
|
87
|
-
rowDefault:
|
|
206
|
+
rowDefault: DxGridPlaneRecord<DxGridFrozenRowsPlane, DxGridAxisMetaProps> = {
|
|
207
|
+
grid: { size: defaultRowSize },
|
|
208
|
+
};
|
|
88
209
|
|
|
89
210
|
@property({ type: Object })
|
|
90
|
-
columnDefault:
|
|
211
|
+
columnDefault: DxGridPlaneRecord<DxGridFrozenColsPlane, DxGridAxisMetaProps> = {
|
|
212
|
+
grid: { size: defaultColSize },
|
|
213
|
+
};
|
|
91
214
|
|
|
92
215
|
@property({ type: Object })
|
|
93
|
-
rows:
|
|
216
|
+
rows: DxGridAxisMeta = { grid: {} };
|
|
94
217
|
|
|
95
218
|
@property({ type: Object })
|
|
96
|
-
columns:
|
|
219
|
+
columns: DxGridAxisMeta = { grid: {} };
|
|
97
220
|
|
|
98
221
|
@property({ type: Object })
|
|
99
|
-
|
|
222
|
+
initialCells: DxGridCells = { grid: {} };
|
|
223
|
+
|
|
224
|
+
@property({ type: String })
|
|
225
|
+
mode: DxGridMode = 'browse';
|
|
226
|
+
|
|
227
|
+
@property({ type: Number })
|
|
228
|
+
limitColumns: number = Infinity;
|
|
229
|
+
|
|
230
|
+
@property({ type: Number })
|
|
231
|
+
limitRows: number = Infinity;
|
|
232
|
+
|
|
233
|
+
@property({ type: Object })
|
|
234
|
+
frozen: DxGridFrozenAxes = {};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* When this function is defined, it is used first to try to get a value for a cell, and otherwise will fall back
|
|
238
|
+
* to `cells`.
|
|
239
|
+
*/
|
|
240
|
+
getCells: ((nextRange: DxGridPlaneRange, plane: DxGridPlane) => DxGridPlaneCells) | null = null;
|
|
241
|
+
|
|
242
|
+
@state()
|
|
243
|
+
private cells: DxGridCells = { grid: {} };
|
|
100
244
|
|
|
101
245
|
//
|
|
102
246
|
// `pos`, short for ‘position’, is the position in pixels of the viewport from the origin.
|
|
103
247
|
//
|
|
104
248
|
|
|
105
249
|
@state()
|
|
106
|
-
posInline = 0;
|
|
250
|
+
private posInline = 0;
|
|
107
251
|
|
|
108
252
|
@state()
|
|
109
|
-
posBlock = 0;
|
|
253
|
+
private posBlock = 0;
|
|
110
254
|
|
|
111
255
|
//
|
|
112
256
|
// `size` (when not suffixed with ‘row’ or ‘col’, see above) is the size in pixels of the viewport.
|
|
113
257
|
//
|
|
114
258
|
|
|
115
259
|
@state()
|
|
116
|
-
sizeInline = 0;
|
|
260
|
+
private sizeInline = 0;
|
|
117
261
|
|
|
118
262
|
@state()
|
|
119
|
-
sizeBlock = 0;
|
|
263
|
+
private sizeBlock = 0;
|
|
120
264
|
|
|
121
265
|
//
|
|
122
266
|
// `overscan` is the amount in pixels to offset the grid content due to the number of overscanned columns or rows.
|
|
123
267
|
//
|
|
124
268
|
|
|
125
269
|
@state()
|
|
126
|
-
overscanInline = 0;
|
|
270
|
+
private overscanInline = 0;
|
|
127
271
|
|
|
128
272
|
@state()
|
|
129
|
-
overscanBlock = 0;
|
|
273
|
+
private overscanBlock = 0;
|
|
130
274
|
|
|
131
275
|
//
|
|
132
276
|
// `bin`, not short for anything, is the range in pixels within which virtualization does not need to reassess.
|
|
133
277
|
//
|
|
134
278
|
|
|
135
279
|
@state()
|
|
136
|
-
binInlineMin = 0;
|
|
280
|
+
private binInlineMin = 0;
|
|
137
281
|
|
|
138
282
|
@state()
|
|
139
|
-
binInlineMax =
|
|
283
|
+
private binInlineMax = defaultColSize;
|
|
140
284
|
|
|
141
285
|
@state()
|
|
142
|
-
binBlockMin = 0;
|
|
286
|
+
private binBlockMin = 0;
|
|
143
287
|
|
|
144
288
|
@state()
|
|
145
|
-
binBlockMax =
|
|
289
|
+
private binBlockMax = defaultRowSize;
|
|
146
290
|
|
|
147
291
|
//
|
|
148
292
|
// `vis`, short for ‘visible’, is the range in numeric index of the columns or rows which should be rendered within
|
|
@@ -150,97 +294,229 @@ export class DxGrid extends LitElement {
|
|
|
150
294
|
//
|
|
151
295
|
|
|
152
296
|
@state()
|
|
153
|
-
visColMin = 0;
|
|
297
|
+
private visColMin = 0;
|
|
154
298
|
|
|
155
299
|
@state()
|
|
156
|
-
visColMax = 1;
|
|
300
|
+
private visColMax = 1;
|
|
157
301
|
|
|
158
302
|
@state()
|
|
159
|
-
visRowMin = 0;
|
|
303
|
+
private visRowMin = 0;
|
|
160
304
|
|
|
161
305
|
@state()
|
|
162
|
-
visRowMax = 1;
|
|
306
|
+
private visRowMax = 1;
|
|
163
307
|
|
|
164
308
|
//
|
|
165
309
|
// `template` is the rendered value of `grid-{axis}-template`.
|
|
166
310
|
//
|
|
167
311
|
@state()
|
|
168
|
-
|
|
312
|
+
private templateGridColumns = '0';
|
|
313
|
+
|
|
314
|
+
@state()
|
|
315
|
+
private templatefrozenColsStart = '';
|
|
169
316
|
|
|
170
317
|
@state()
|
|
171
|
-
|
|
318
|
+
private templatefrozenColsEnd = '';
|
|
319
|
+
|
|
320
|
+
@state()
|
|
321
|
+
private templateGridRows = '0';
|
|
322
|
+
|
|
323
|
+
@state()
|
|
324
|
+
private templatefrozenRowsStart = '';
|
|
325
|
+
|
|
326
|
+
@state()
|
|
327
|
+
private templatefrozenRowsEnd = '';
|
|
172
328
|
|
|
173
329
|
//
|
|
174
|
-
//
|
|
330
|
+
// Focus, selection, and resize states
|
|
175
331
|
//
|
|
176
332
|
|
|
177
333
|
@state()
|
|
178
|
-
|
|
334
|
+
private pointer: DxGridPointer = null;
|
|
179
335
|
|
|
180
336
|
@state()
|
|
181
|
-
|
|
337
|
+
private colSizes: DxGridAxisSizes = { grid: {} };
|
|
182
338
|
|
|
183
339
|
@state()
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
340
|
+
private rowSizes: DxGridAxisSizes = { grid: {} };
|
|
341
|
+
|
|
342
|
+
@state()
|
|
343
|
+
private focusActive: boolean = false;
|
|
344
|
+
|
|
345
|
+
@state()
|
|
346
|
+
private focusedCell: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
|
|
347
|
+
|
|
348
|
+
@state()
|
|
349
|
+
private selectionStart: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
|
|
350
|
+
|
|
351
|
+
@state()
|
|
352
|
+
private selectionEnd: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
|
|
353
|
+
|
|
354
|
+
//
|
|
355
|
+
// Limits
|
|
356
|
+
//
|
|
357
|
+
|
|
358
|
+
@state()
|
|
359
|
+
private intrinsicInlineSize: number = Infinity;
|
|
360
|
+
|
|
361
|
+
@state()
|
|
362
|
+
private intrinsicBlockSize: number = Infinity;
|
|
363
|
+
|
|
364
|
+
//
|
|
365
|
+
// Primary pointer and keyboard handlers
|
|
366
|
+
//
|
|
367
|
+
|
|
368
|
+
private dispatchEditRequest(initialContent?: string) {
|
|
369
|
+
this.snapPosToFocusedCell();
|
|
370
|
+
// Without deferring, the event dispatches before `focusedCellBox` can get updated bounds of the cell, hence:
|
|
371
|
+
queueMicrotask(() =>
|
|
372
|
+
this.dispatchEvent(
|
|
373
|
+
new DxEditRequest({
|
|
374
|
+
cellIndex: toCellIndex(this.focusedCell),
|
|
375
|
+
cellBox: this.focusedCellBox(),
|
|
376
|
+
initialContent,
|
|
377
|
+
}),
|
|
378
|
+
),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private handlePointerDown = (event: PointerEvent) => {
|
|
383
|
+
if (event.isPrimary) {
|
|
384
|
+
const { action, actionEl } = closestAction(event.target);
|
|
385
|
+
if (action) {
|
|
386
|
+
if (action === 'cell') {
|
|
387
|
+
const cellCoords = closestCell(event.target, actionEl);
|
|
388
|
+
if (cellCoords) {
|
|
389
|
+
this.pointer = { state: 'maybeSelecting', pageX: event.pageX, pageY: event.pageY };
|
|
390
|
+
this.selectionStart = cellCoords;
|
|
391
|
+
this.selectionEnd = cellCoords;
|
|
392
|
+
}
|
|
393
|
+
if (this.mode === 'edit') {
|
|
394
|
+
event.preventDefault();
|
|
395
|
+
} else {
|
|
396
|
+
if (this.focusActive && isSameCell(this.focusedCell, cellCoords)) {
|
|
397
|
+
this.dispatchEditRequest();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
199
401
|
}
|
|
200
402
|
}
|
|
201
403
|
};
|
|
202
404
|
|
|
203
|
-
handlePointerUp = (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
405
|
+
private handlePointerUp = (event: PointerEvent) => {
|
|
406
|
+
const cell = closestCell(event.target);
|
|
407
|
+
if (cell) {
|
|
408
|
+
this.selectionEnd = cell;
|
|
409
|
+
this.dispatchEvent(
|
|
410
|
+
new DxGridCellsSelect({
|
|
411
|
+
start: this.selectionStart,
|
|
412
|
+
end: this.selectionEnd,
|
|
413
|
+
}),
|
|
414
|
+
);
|
|
212
415
|
}
|
|
416
|
+
this.pointer = null;
|
|
213
417
|
};
|
|
214
418
|
|
|
215
|
-
handlePointerMove = (event: PointerEvent) => {
|
|
216
|
-
if (this.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.
|
|
419
|
+
private handlePointerMove = (event: PointerEvent) => {
|
|
420
|
+
if (shouldSelect(this.pointer, event)) {
|
|
421
|
+
this.pointer = { state: 'selecting' };
|
|
422
|
+
} else if (this.pointer?.state === 'selecting') {
|
|
423
|
+
const cell = closestCell(event.target);
|
|
424
|
+
if (
|
|
425
|
+
cell &&
|
|
426
|
+
cell.plane === this.selectionStart.plane &&
|
|
427
|
+
(cell.col !== this.selectionEnd.col || cell.row !== this.selectionEnd.row)
|
|
428
|
+
) {
|
|
429
|
+
this.selectionEnd = cell;
|
|
226
430
|
}
|
|
227
431
|
}
|
|
228
432
|
};
|
|
229
433
|
|
|
434
|
+
private handleKeydown(event: KeyboardEvent) {
|
|
435
|
+
if (this.focusActive && this.mode === 'browse') {
|
|
436
|
+
// Adjust state
|
|
437
|
+
switch (event.key) {
|
|
438
|
+
case 'ArrowDown':
|
|
439
|
+
this.focusedCell = { ...this.focusedCell, row: Math.min(this.limitRows - 1, this.focusedCell.row + 1) };
|
|
440
|
+
break;
|
|
441
|
+
case 'ArrowUp':
|
|
442
|
+
this.focusedCell = { ...this.focusedCell, row: Math.max(0, this.focusedCell.row - 1) };
|
|
443
|
+
break;
|
|
444
|
+
case 'ArrowRight':
|
|
445
|
+
this.focusedCell = { ...this.focusedCell, col: Math.min(this.limitColumns - 1, this.focusedCell.col + 1) };
|
|
446
|
+
break;
|
|
447
|
+
case 'ArrowLeft':
|
|
448
|
+
this.focusedCell = { ...this.focusedCell, col: Math.max(0, this.focusedCell.col - 1) };
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
// Emit edit request if relevant
|
|
452
|
+
switch (event.key) {
|
|
453
|
+
case 'Enter':
|
|
454
|
+
this.dispatchEditRequest();
|
|
455
|
+
break;
|
|
456
|
+
default:
|
|
457
|
+
if (event.key.length === 1 && event.key.match(/\P{Cc}/u)) {
|
|
458
|
+
this.dispatchEditRequest(event.key);
|
|
459
|
+
}
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
// Handle virtualization & focus consequences
|
|
463
|
+
switch (event.key) {
|
|
464
|
+
case 'ArrowDown':
|
|
465
|
+
case 'ArrowUp':
|
|
466
|
+
case 'ArrowRight':
|
|
467
|
+
case 'ArrowLeft':
|
|
468
|
+
event.preventDefault();
|
|
469
|
+
this.snapPosToFocusedCell();
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
230
475
|
//
|
|
231
476
|
// Accessors
|
|
232
477
|
//
|
|
233
478
|
|
|
234
|
-
private colSize(c: number | string) {
|
|
235
|
-
|
|
479
|
+
private colSize(c: number | string, plane: DxGridPlane) {
|
|
480
|
+
const resolvedPlane = resolveColPlane(plane);
|
|
481
|
+
return this.colSizes?.[resolvedPlane]?.[c] ?? this.columnDefault[resolvedPlane]?.size ?? defaultColSize;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
private rowSize(r: number | string, plane: DxGridPlane) {
|
|
485
|
+
const resolvedPlane = resolveRowPlane(plane);
|
|
486
|
+
return this.rowSizes?.[resolvedPlane]?.[r] ?? this.rowDefault[resolvedPlane]?.size ?? defaultRowSize;
|
|
236
487
|
}
|
|
237
488
|
|
|
238
|
-
private
|
|
239
|
-
|
|
489
|
+
private cell(c: number | string, r: number | string, plane: DxGridPlane): DxGridCellValue | undefined {
|
|
490
|
+
const index: DxGridCellIndex = `${c}${separator}${r}`;
|
|
491
|
+
return this.cells?.[plane]?.[index] ?? this.initialCells?.[plane]?.[index];
|
|
240
492
|
}
|
|
241
493
|
|
|
242
|
-
private
|
|
243
|
-
return
|
|
494
|
+
private cellActive(c: number | string, r: number | string, plane: DxGridPlane): boolean {
|
|
495
|
+
return (
|
|
496
|
+
this.focusActive && this.focusedCell.plane === plane && this.focusedCell.col === c && this.focusedCell.row === r
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private focusedCellBox(): DxEditRequest['cellBox'] {
|
|
501
|
+
const cellElement = this.focusedCellElement();
|
|
502
|
+
const cellSize = {
|
|
503
|
+
inlineSize: this.colSize(this.focusedCell.col, this.focusedCell.plane),
|
|
504
|
+
blockSize: this.rowSize(this.focusedCell.row, this.focusedCell.plane),
|
|
505
|
+
};
|
|
506
|
+
if (!cellElement) {
|
|
507
|
+
return { insetInlineStart: NaN, insetBlockStart: NaN, ...cellSize };
|
|
508
|
+
}
|
|
509
|
+
const contentElement = cellElement.offsetParent as HTMLElement;
|
|
510
|
+
// Note that storing `offset` in state causes performance issues, so instead the transform is parsed here.
|
|
511
|
+
const [_translate3d, inlineStr, blockStr] = contentElement.style.transform.split(/[()]|px,?\s?/);
|
|
512
|
+
const contentOffsetInline = parseFloat(inlineStr);
|
|
513
|
+
const contentOffsetBlock = parseFloat(blockStr);
|
|
514
|
+
const offsetParent = contentElement.offsetParent as HTMLElement;
|
|
515
|
+
return {
|
|
516
|
+
insetInlineStart: cellElement.offsetLeft + contentOffsetInline + offsetParent.offsetLeft,
|
|
517
|
+
insetBlockStart: cellElement.offsetTop + contentOffsetBlock + offsetParent.offsetTop,
|
|
518
|
+
...cellSize,
|
|
519
|
+
};
|
|
244
520
|
}
|
|
245
521
|
|
|
246
522
|
//
|
|
@@ -248,7 +524,7 @@ export class DxGrid extends LitElement {
|
|
|
248
524
|
//
|
|
249
525
|
|
|
250
526
|
@state()
|
|
251
|
-
observer = new ResizeObserver((entries) => {
|
|
527
|
+
private observer = new ResizeObserver((entries) => {
|
|
252
528
|
const { inlineSize, blockSize } = entries?.[0]?.contentBoxSize?.[0] ?? {
|
|
253
529
|
inlineSize: 0,
|
|
254
530
|
blockSize: 0,
|
|
@@ -261,97 +537,126 @@ export class DxGrid extends LitElement {
|
|
|
261
537
|
this.sizeInline = inlineSize;
|
|
262
538
|
this.sizeBlock = blockSize;
|
|
263
539
|
this.updateVis();
|
|
540
|
+
queueMicrotask(() => this.updatePos());
|
|
264
541
|
}
|
|
265
542
|
});
|
|
266
543
|
|
|
267
|
-
viewportRef: Ref<HTMLDivElement> = createRef();
|
|
544
|
+
private viewportRef: Ref<HTMLDivElement> = createRef();
|
|
268
545
|
|
|
269
|
-
|
|
270
|
-
this.posInline
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
546
|
+
private maybeUpdateVisInline = () => {
|
|
547
|
+
if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
|
|
548
|
+
this.updateVisInline();
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
private maybeUpdateVisBlock = () => {
|
|
553
|
+
if (this.posBlock < this.binBlockMin || this.posBlock >= this.binBlockMax) {
|
|
554
|
+
this.updateVisBlock();
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
private updatePosInline(inline?: number) {
|
|
559
|
+
this.posInline = Math.max(0, Math.min(this.intrinsicInlineSize - this.sizeInline, inline ?? this.posInline));
|
|
560
|
+
this.maybeUpdateVisInline();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
private updatePosBlock(block?: number) {
|
|
564
|
+
this.posBlock = Math.max(0, Math.min(this.intrinsicBlockSize - this.sizeBlock, block ?? this.posBlock));
|
|
565
|
+
this.maybeUpdateVisBlock();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
private updatePos(inline?: number, block?: number) {
|
|
569
|
+
this.updatePosInline(inline);
|
|
570
|
+
this.updatePosBlock(block);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private handleWheel = ({ deltaX, deltaY }: Pick<WheelEvent, 'deltaX' | 'deltaY'>) => {
|
|
574
|
+
if (this.mode === 'browse') {
|
|
575
|
+
this.updatePos(this.posInline + deltaX, this.posBlock + deltaY);
|
|
287
576
|
}
|
|
288
577
|
};
|
|
289
578
|
|
|
290
579
|
private updateVisInline() {
|
|
291
580
|
// todo: avoid starting from zero
|
|
292
581
|
let colIndex = 0;
|
|
293
|
-
let pxInline = this.colSize(colIndex);
|
|
582
|
+
let pxInline = this.colSize(colIndex, 'grid');
|
|
294
583
|
|
|
295
584
|
while (pxInline < this.posInline) {
|
|
296
585
|
colIndex += 1;
|
|
297
|
-
pxInline += this.colSize(colIndex) + gap;
|
|
586
|
+
pxInline += this.colSize(colIndex, 'grid') + gap;
|
|
298
587
|
}
|
|
299
588
|
|
|
300
589
|
this.visColMin = colIndex - overscanCol;
|
|
301
590
|
|
|
302
|
-
this.binInlineMin = pxInline - this.colSize(colIndex) - gap;
|
|
591
|
+
this.binInlineMin = pxInline - this.colSize(colIndex, 'grid') - gap;
|
|
303
592
|
this.binInlineMax = pxInline + gap;
|
|
304
593
|
|
|
305
594
|
this.overscanInline =
|
|
306
595
|
[...Array(overscanCol)].reduce((acc, _, c0) => {
|
|
307
|
-
acc += this.colSize(this.visColMin + c0);
|
|
596
|
+
acc += this.colSize(this.visColMin + c0, 'grid');
|
|
308
597
|
return acc;
|
|
309
598
|
}, 0) +
|
|
310
599
|
gap * (overscanCol - 1);
|
|
311
600
|
|
|
312
|
-
while (pxInline < this.binInlineMax + this.sizeInline
|
|
601
|
+
while (pxInline < this.binInlineMax + this.sizeInline - gap * 2) {
|
|
313
602
|
colIndex += 1;
|
|
314
|
-
pxInline += this.colSize(colIndex) + gap;
|
|
603
|
+
pxInline += this.colSize(colIndex, 'grid') + gap;
|
|
315
604
|
}
|
|
316
605
|
|
|
317
|
-
this.visColMax = colIndex + overscanCol;
|
|
606
|
+
this.visColMax = Math.min(this.limitColumns, colIndex + overscanCol);
|
|
607
|
+
|
|
608
|
+
this.templateGridColumns = [...Array(this.visColMax - this.visColMin)]
|
|
609
|
+
.map((_, c0) => `${this.colSize(this.visColMin + c0, 'grid')}px`)
|
|
610
|
+
.join(' ');
|
|
318
611
|
|
|
319
|
-
this.
|
|
320
|
-
.map((_, c0) => `${this.colSize(
|
|
612
|
+
this.templatefrozenColsStart = [...Array(this.frozen.frozenColsStart ?? 0)]
|
|
613
|
+
.map((_, c0) => `${this.colSize(c0, 'frozenColsStart')}px`)
|
|
614
|
+
.join(' ');
|
|
615
|
+
|
|
616
|
+
this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
|
|
617
|
+
.map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
|
|
321
618
|
.join(' ');
|
|
322
619
|
}
|
|
323
620
|
|
|
324
621
|
private updateVisBlock() {
|
|
325
622
|
// todo: avoid starting from zero
|
|
326
623
|
let rowIndex = 0;
|
|
327
|
-
let pxBlock = this.rowSize(rowIndex);
|
|
624
|
+
let pxBlock = this.rowSize(rowIndex, 'grid');
|
|
328
625
|
|
|
329
626
|
while (pxBlock < this.posBlock) {
|
|
330
627
|
rowIndex += 1;
|
|
331
|
-
pxBlock += this.rowSize(rowIndex) + gap;
|
|
628
|
+
pxBlock += this.rowSize(rowIndex, 'grid') + gap;
|
|
332
629
|
}
|
|
333
630
|
|
|
334
631
|
this.visRowMin = rowIndex - overscanRow;
|
|
335
632
|
|
|
336
|
-
this.binBlockMin = pxBlock - this.rowSize(rowIndex) - gap;
|
|
633
|
+
this.binBlockMin = pxBlock - this.rowSize(rowIndex, 'grid') - gap;
|
|
337
634
|
this.binBlockMax = pxBlock + gap;
|
|
338
635
|
|
|
339
636
|
this.overscanBlock =
|
|
340
637
|
[...Array(overscanRow)].reduce((acc, _, r0) => {
|
|
341
|
-
acc += this.rowSize(this.visRowMin + r0);
|
|
638
|
+
acc += this.rowSize(this.visRowMin + r0, 'grid');
|
|
342
639
|
return acc;
|
|
343
640
|
}, 0) +
|
|
344
641
|
gap * (overscanRow - 1);
|
|
345
642
|
|
|
346
|
-
while (pxBlock < this.binBlockMax + this.sizeBlock) {
|
|
643
|
+
while (pxBlock < this.binBlockMax + this.sizeBlock - gap * 2) {
|
|
347
644
|
rowIndex += 1;
|
|
348
|
-
pxBlock += this.rowSize(rowIndex) + gap;
|
|
645
|
+
pxBlock += this.rowSize(rowIndex, 'grid') + gap;
|
|
349
646
|
}
|
|
350
647
|
|
|
351
|
-
this.visRowMax = rowIndex + overscanRow;
|
|
648
|
+
this.visRowMax = Math.min(this.limitRows, rowIndex + overscanRow);
|
|
649
|
+
|
|
650
|
+
this.templateGridRows = [...Array(this.visRowMax - this.visRowMin)]
|
|
651
|
+
.map((_, r0) => `${this.rowSize(this.visRowMin + r0, 'grid')}px`)
|
|
652
|
+
.join(' ');
|
|
352
653
|
|
|
353
|
-
this.
|
|
354
|
-
.map((_, r0) => `${this.rowSize(
|
|
654
|
+
this.templatefrozenRowsStart = [...Array(this.frozen.frozenRowsStart ?? 0)]
|
|
655
|
+
.map((_, r0) => `${this.rowSize(r0, 'frozenRowsStart')}px`)
|
|
656
|
+
.join(' ');
|
|
657
|
+
|
|
658
|
+
this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
|
|
659
|
+
.map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
|
|
355
660
|
.join(' ');
|
|
356
661
|
}
|
|
357
662
|
|
|
@@ -360,130 +665,277 @@ export class DxGrid extends LitElement {
|
|
|
360
665
|
this.updateVisBlock();
|
|
361
666
|
}
|
|
362
667
|
|
|
363
|
-
|
|
668
|
+
private updateCells(includeFixed?: boolean) {
|
|
669
|
+
this.cells.grid = this.getCells!(
|
|
670
|
+
{
|
|
671
|
+
start: { col: this.visColMin, row: this.visRowMin },
|
|
672
|
+
end: { col: this.visColMax, row: this.visRowMax },
|
|
673
|
+
},
|
|
674
|
+
'grid',
|
|
675
|
+
);
|
|
676
|
+
Object.entries(this.frozen)
|
|
677
|
+
.filter(([_, limit]) => limit && limit > 0)
|
|
678
|
+
.forEach(([plane, limit]) => {
|
|
679
|
+
this.cells[plane as DxGridFrozenPlane] = this.getCells!(
|
|
680
|
+
plane.startsWith('frozenRows')
|
|
681
|
+
? {
|
|
682
|
+
start: { col: this.visColMin, row: 0 },
|
|
683
|
+
end: { col: this.visColMax, row: limit },
|
|
684
|
+
}
|
|
685
|
+
: {
|
|
686
|
+
start: { col: 0, row: this.visRowMin },
|
|
687
|
+
end: { col: limit, row: this.visRowMax },
|
|
688
|
+
},
|
|
689
|
+
plane as DxGridFrozenPlane,
|
|
690
|
+
);
|
|
691
|
+
});
|
|
692
|
+
if (includeFixed) {
|
|
693
|
+
if ((this.frozen.frozenColsStart ?? 0) > 0 && (this.frozen.frozenRowsStart ?? 0) > 0) {
|
|
694
|
+
this.cells.fixedStartStart = this.getCells!(
|
|
695
|
+
{
|
|
696
|
+
start: { col: 0, row: 0 },
|
|
697
|
+
end: { col: this.frozen.frozenColsStart!, row: this.frozen.frozenRowsStart! },
|
|
698
|
+
},
|
|
699
|
+
'fixedStartStart',
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
if ((this.frozen.frozenColsEnd ?? 0) > 0 && (this.frozen.frozenRowsStart ?? 0) > 0) {
|
|
703
|
+
this.cells.fixedStartEnd = this.getCells!(
|
|
704
|
+
{
|
|
705
|
+
start: { col: 0, row: 0 },
|
|
706
|
+
end: { col: this.frozen.frozenColsEnd!, row: this.frozen.frozenRowsStart! },
|
|
707
|
+
},
|
|
708
|
+
'fixedStartEnd',
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
if ((this.frozen.frozenColsStart ?? 0) > 0 && (this.frozen.frozenRowsEnd ?? 0) > 0) {
|
|
712
|
+
this.cells.fixedEndStart = this.getCells!(
|
|
713
|
+
{
|
|
714
|
+
start: { col: 0, row: 0 },
|
|
715
|
+
end: { col: this.frozen.frozenColsStart!, row: this.frozen.frozenRowsEnd! },
|
|
716
|
+
},
|
|
717
|
+
'fixedEndStart',
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
if ((this.frozen.frozenColsEnd ?? 0) > 0 && (this.frozen.frozenRowsEnd ?? 0) > 0) {
|
|
721
|
+
this.cells.fixedEndEnd = this.getCells!(
|
|
722
|
+
{
|
|
723
|
+
start: { col: 0, row: 0 },
|
|
724
|
+
end: { col: this.frozen.frozenColsEnd!, row: this.frozen.frozenRowsEnd! },
|
|
725
|
+
},
|
|
726
|
+
'fixedEndEnd',
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
364
731
|
|
|
365
|
-
|
|
366
|
-
focusedCell: Record<DxGridAxis, number> = { col: 0, row: 0 };
|
|
732
|
+
// Focus handlers
|
|
367
733
|
|
|
368
|
-
|
|
369
|
-
|
|
734
|
+
setFocus(coords: DxGridPosition, snap = true) {
|
|
735
|
+
this.focusedCell = coords;
|
|
736
|
+
this.focusActive = true;
|
|
737
|
+
if (snap) {
|
|
738
|
+
this.snapPosToFocusedCell();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
370
741
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const action = target.getAttribute('data-dx-grid-action');
|
|
375
|
-
if (action === 'cell') {
|
|
376
|
-
const c = parseInt(target.getAttribute('aria-colindex') ?? 'never');
|
|
377
|
-
const r = parseInt(target.getAttribute('aria-rowindex') ?? 'never');
|
|
378
|
-
this.focusedCell = { col: c, row: r };
|
|
742
|
+
private handleFocus(event: FocusEvent) {
|
|
743
|
+
const cellCoords = closestCell(event.target);
|
|
744
|
+
if (cellCoords) {
|
|
379
745
|
this.focusActive = true;
|
|
746
|
+
if (
|
|
747
|
+
this.focusedCell.plane !== cellCoords.plane ||
|
|
748
|
+
this.focusedCell.col !== cellCoords.col ||
|
|
749
|
+
this.focusedCell.row !== cellCoords.row
|
|
750
|
+
) {
|
|
751
|
+
this.focusedCell = cellCoords;
|
|
752
|
+
}
|
|
380
753
|
}
|
|
381
754
|
}
|
|
382
755
|
|
|
383
|
-
|
|
384
|
-
handleBlur(event: FocusEvent) {
|
|
756
|
+
private handleBlur(event: FocusEvent) {
|
|
385
757
|
// Only unset `focusActive` if focus is not moving to an element within the grid.
|
|
386
|
-
if (
|
|
387
|
-
!event.relatedTarget ||
|
|
388
|
-
(event.relatedTarget as HTMLElement).closest('.dx-grid__viewport') !== this.viewportRef.value
|
|
389
|
-
) {
|
|
758
|
+
if (!event.relatedTarget || !(event.relatedTarget as HTMLElement).closest(`[data-grid="${this.gridId}"]`)) {
|
|
390
759
|
this.focusActive = false;
|
|
391
760
|
}
|
|
392
761
|
}
|
|
393
762
|
|
|
763
|
+
private focusedCellQuery() {
|
|
764
|
+
return `[data-dx-grid-plane=${this.focusedCell.plane}] > [aria-colindex="${this.focusedCell.col}"][aria-rowindex="${this.focusedCell.row}"]`;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
private focusedCellElement() {
|
|
768
|
+
return this.viewportRef.value?.querySelector(this.focusedCellQuery()) as HTMLElement | null;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
//
|
|
772
|
+
// `outOfVis` returns by how many rows/cols the focused cell is outside of the `vis` range for an axis, inset by a
|
|
773
|
+
// `delta`, otherwise zero if it is within that range.
|
|
774
|
+
//
|
|
775
|
+
|
|
776
|
+
private focusedCellRowOutOfVis(minDelta = 0, maxDelta = minDelta) {
|
|
777
|
+
return this.focusedCell.row <= this.visRowMin + minDelta
|
|
778
|
+
? this.focusedCell.row - (this.visRowMin + minDelta)
|
|
779
|
+
: this.focusedCell.row >= this.visRowMax - maxDelta
|
|
780
|
+
? -(this.focusedCell.row - this.visRowMax - maxDelta)
|
|
781
|
+
: 0;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
private focusedCellColOutOfVis(minDelta = 0, maxDelta = minDelta) {
|
|
785
|
+
return this.focusedCell.col <= this.visColMin + minDelta
|
|
786
|
+
? this.focusedCell.col - (this.visColMin + minDelta)
|
|
787
|
+
: this.focusedCell.col >= this.visColMax - maxDelta
|
|
788
|
+
? -(this.focusedCell.col - this.visColMax - maxDelta)
|
|
789
|
+
: 0;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
private focusedCellOutOfVis(colDelta = 0, rowDelta = colDelta): { col: number; row: number } {
|
|
793
|
+
switch (this.focusedCell.plane) {
|
|
794
|
+
case 'grid':
|
|
795
|
+
return { row: this.focusedCellRowOutOfVis(rowDelta), col: this.focusedCellColOutOfVis(colDelta) };
|
|
796
|
+
case 'frozenRowsStart':
|
|
797
|
+
case 'frozenRowsEnd':
|
|
798
|
+
return { col: this.focusedCellColOutOfVis(colDelta), row: 0 };
|
|
799
|
+
case 'frozenColsStart':
|
|
800
|
+
case 'frozenColsEnd':
|
|
801
|
+
return { col: 0, row: this.focusedCellRowOutOfVis(rowDelta) };
|
|
802
|
+
default:
|
|
803
|
+
return { col: 0, row: 0 };
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
394
807
|
/**
|
|
395
808
|
* Moves focus to the cell with actual focus, otherwise moves focus to the viewport.
|
|
396
809
|
*/
|
|
397
|
-
refocus() {
|
|
398
|
-
(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
810
|
+
refocus(increment?: 'col' | 'row', delta: 1 | -1 = 1) {
|
|
811
|
+
switch (increment) {
|
|
812
|
+
case 'row':
|
|
813
|
+
this.focusedCell = { ...this.focusedCell, row: this.focusedCell.row + delta };
|
|
814
|
+
break;
|
|
815
|
+
case 'col':
|
|
816
|
+
this.focusedCell = { ...this.focusedCell, col: this.focusedCell.col + delta };
|
|
817
|
+
}
|
|
818
|
+
if (increment) {
|
|
819
|
+
this.snapPosToFocusedCell();
|
|
820
|
+
}
|
|
821
|
+
queueMicrotask(() => {
|
|
822
|
+
const outOfVis = this.focusedCellOutOfVis(overscanCol, overscanRow);
|
|
823
|
+
if (outOfVis.col !== 0 || outOfVis.row !== 0) {
|
|
824
|
+
this.viewportRef.value?.focus({ preventScroll: true });
|
|
825
|
+
} else {
|
|
826
|
+
const activeFocusedCell = document.activeElement?.closest(this.focusedCellQuery());
|
|
827
|
+
if (!activeFocusedCell) {
|
|
828
|
+
this.focusedCellElement()?.focus({ preventScroll: true });
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
private findPosInlineFromVisColMin(deltaCols: number) {
|
|
835
|
+
return [...Array(deltaCols)].reduce(
|
|
836
|
+
(acc, _, c0) => acc - this.colSize(this.visColMin - c0, 'grid') - gap,
|
|
837
|
+
this.binInlineMin + gap,
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
private findPosBlockFromVisRowMin(deltaRows: number) {
|
|
842
|
+
return [...Array(deltaRows)].reduce(
|
|
843
|
+
(acc, _, r0) => acc - this.rowSize(this.visRowMin - r0, 'grid') - gap,
|
|
844
|
+
this.binBlockMin + gap,
|
|
845
|
+
);
|
|
407
846
|
}
|
|
408
847
|
|
|
409
848
|
/**
|
|
410
849
|
* Updates `pos` so that a cell in focus is fully within the viewport
|
|
411
850
|
*/
|
|
412
851
|
snapPosToFocusedCell() {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
this.
|
|
416
|
-
this.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
this.
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
// this.focusedCell,
|
|
429
|
-
// [this.visColMin, this.visColMax, overscanCol],
|
|
430
|
-
// [this.visRowMin, this.visRowMax, overscanRow],
|
|
431
|
-
// );
|
|
432
|
-
} else {
|
|
433
|
-
if (this.focusedCell.col <= this.visColMin + overscanCol) {
|
|
434
|
-
this.posInline = this.binInlineMin;
|
|
435
|
-
this.updateVisInline();
|
|
436
|
-
} else if (this.focusedCell.col >= this.visColMax - overscanCol - 1) {
|
|
437
|
-
const sizeSumCol = [...Array(this.focusedCell.col - this.visColMin)].reduce((acc, _, c0) => {
|
|
438
|
-
acc += this.colSize(this.visColMin + overscanCol + c0) + gap;
|
|
439
|
-
return acc;
|
|
440
|
-
}, 0);
|
|
441
|
-
this.posInline = this.binInlineMin + sizeSumCol + gap * 2 - this.sizeInline;
|
|
442
|
-
this.updateVisInline();
|
|
443
|
-
}
|
|
852
|
+
const outOfVis = this.focusedCellOutOfVis(overscanCol, overscanRow);
|
|
853
|
+
if (outOfVis.col < 0) {
|
|
854
|
+
this.posInline = this.findPosInlineFromVisColMin(-outOfVis.col);
|
|
855
|
+
this.updateVisInline();
|
|
856
|
+
} else if (outOfVis.col > 0) {
|
|
857
|
+
const sizeSumCol = [...Array(this.focusedCell.col - this.visColMin)].reduce((acc, _, c0) => {
|
|
858
|
+
acc += this.colSize(this.visColMin + overscanCol + c0, 'grid') + gap;
|
|
859
|
+
return acc;
|
|
860
|
+
}, 0);
|
|
861
|
+
this.posInline = Math.max(
|
|
862
|
+
0,
|
|
863
|
+
Math.min(this.intrinsicInlineSize - this.sizeInline, this.binInlineMin + sizeSumCol - this.sizeInline),
|
|
864
|
+
);
|
|
865
|
+
this.updateVisInline();
|
|
866
|
+
}
|
|
444
867
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
868
|
+
if (outOfVis.row < 0) {
|
|
869
|
+
this.posBlock = this.findPosBlockFromVisRowMin(-outOfVis.row);
|
|
870
|
+
this.updateVisBlock();
|
|
871
|
+
} else if (outOfVis.row > 0) {
|
|
872
|
+
const sizeSumRow = [...Array(this.focusedCell.row - this.visRowMin)].reduce((acc, _, r0) => {
|
|
873
|
+
acc += this.rowSize(this.visRowMin + overscanRow + r0, 'grid') + gap;
|
|
874
|
+
return acc;
|
|
875
|
+
}, 0);
|
|
876
|
+
this.posBlock = Math.max(
|
|
877
|
+
0,
|
|
878
|
+
Math.min(this.intrinsicBlockSize - this.sizeBlock, this.binBlockMin + sizeSumRow - this.sizeBlock),
|
|
879
|
+
);
|
|
880
|
+
this.updateVisBlock();
|
|
456
881
|
}
|
|
457
882
|
}
|
|
458
883
|
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
884
|
+
//
|
|
885
|
+
// Map scroll DOM methods to virtualized value.
|
|
886
|
+
//
|
|
887
|
+
|
|
888
|
+
override get scrollLeft() {
|
|
889
|
+
return this.posInline;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
override set scrollLeft(nextValue: number) {
|
|
893
|
+
this.posInline = nextValue;
|
|
894
|
+
this.maybeUpdateVisInline();
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
override get scrollTop() {
|
|
898
|
+
return this.posBlock;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
override set scrollTop(nextValue: number) {
|
|
902
|
+
this.posBlock = nextValue;
|
|
903
|
+
this.maybeUpdateVisBlock();
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
//
|
|
907
|
+
// Resize handlers
|
|
908
|
+
//
|
|
909
|
+
|
|
910
|
+
private axisResizeable(plane: 'grid' | DxGridFrozenPlane, axis: DxGridAxis, index: number | string) {
|
|
911
|
+
return axis === 'col'
|
|
912
|
+
? !!(this.columns[plane]?.[index]?.resizeable ?? this.columnDefault[plane as DxGridFrozenColsPlane]?.resizeable)
|
|
913
|
+
: !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
private handleAxisResizeInternal(event: DxAxisResizeInternal) {
|
|
917
|
+
event.stopPropagation();
|
|
918
|
+
const { plane, axis, delta, size, index, state } = event;
|
|
919
|
+
if (axis === 'col') {
|
|
920
|
+
const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
|
|
921
|
+
this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
|
|
922
|
+
this.updateVisInline();
|
|
923
|
+
this.updateIntrinsicInlineSize();
|
|
924
|
+
} else {
|
|
925
|
+
const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
|
|
926
|
+
this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
|
|
927
|
+
this.updateVisBlock();
|
|
928
|
+
this.updateIntrinsicBlockSize();
|
|
929
|
+
}
|
|
930
|
+
if (state === 'dropped') {
|
|
931
|
+
this.dispatchEvent(
|
|
932
|
+
new DxAxisResize({
|
|
933
|
+
plane,
|
|
934
|
+
axis,
|
|
935
|
+
index,
|
|
936
|
+
size: this[`${axis}Size`](index, plane),
|
|
937
|
+
}),
|
|
938
|
+
);
|
|
487
939
|
}
|
|
488
940
|
}
|
|
489
941
|
|
|
@@ -491,116 +943,261 @@ export class DxGrid extends LitElement {
|
|
|
491
943
|
// Render and other lifecycle methods
|
|
492
944
|
//
|
|
493
945
|
|
|
946
|
+
private renderFixed(plane: DxGridFixedPlane, selection: DxGridSelectionProps) {
|
|
947
|
+
const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
|
|
948
|
+
const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
|
|
949
|
+
const cols = this.frozen[colPlane];
|
|
950
|
+
const rows = this.frozen[rowPlane];
|
|
951
|
+
return (cols ?? 0) > 0 && (rows ?? 0) > 0
|
|
952
|
+
? html`<div
|
|
953
|
+
role="none"
|
|
954
|
+
data-dx-grid-plane=${plane}
|
|
955
|
+
class="dx-grid__plane--fixed"
|
|
956
|
+
style=${styleMap({
|
|
957
|
+
'grid-template-columns': this[`template${colPlane}`],
|
|
958
|
+
'grid-template-rows': this[`template${rowPlane}`],
|
|
959
|
+
})}
|
|
960
|
+
>
|
|
961
|
+
${[...Array(cols)].map((_, c) => {
|
|
962
|
+
return [...Array(rows)].map((_, r) => {
|
|
963
|
+
return this.renderCell(c, r, plane, cellSelected(c, r, plane, selection));
|
|
964
|
+
});
|
|
965
|
+
})}
|
|
966
|
+
</div>`
|
|
967
|
+
: null;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
private renderFrozenRows(
|
|
971
|
+
plane: DxGridFrozenRowsPlane,
|
|
972
|
+
visibleCols: number,
|
|
973
|
+
offsetInline: number,
|
|
974
|
+
selection: DxGridSelectionProps,
|
|
975
|
+
) {
|
|
976
|
+
const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
|
|
977
|
+
const rows = this.frozen[rowPlane];
|
|
978
|
+
return (rows ?? 0) > 0
|
|
979
|
+
? html`<div role="none" class="dx-grid__plane--frozen-row">
|
|
980
|
+
<div
|
|
981
|
+
role="none"
|
|
982
|
+
data-dx-grid-plane=${plane}
|
|
983
|
+
class="dx-grid__plane--frozen-row__content"
|
|
984
|
+
style="transform:translate3d(${offsetInline}px,0,0);grid-template-columns:${this
|
|
985
|
+
.templateGridColumns};grid-template-rows:${this[`template${rowPlane}`]}"
|
|
986
|
+
>
|
|
987
|
+
${[...Array(visibleCols)].map((_, c0) => {
|
|
988
|
+
return [...Array(rows)].map((_, r) => {
|
|
989
|
+
const c = this.visColMin + c0;
|
|
990
|
+
return this.renderCell(c, r, plane, cellSelected(c, r, plane, selection), c0, r);
|
|
991
|
+
});
|
|
992
|
+
})}
|
|
993
|
+
</div>
|
|
994
|
+
</div>`
|
|
995
|
+
: null;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
private renderFrozenColumns(
|
|
999
|
+
plane: DxGridFrozenColsPlane,
|
|
1000
|
+
visibleRows: number,
|
|
1001
|
+
offsetBlock: number,
|
|
1002
|
+
selection: DxGridSelectionProps,
|
|
1003
|
+
) {
|
|
1004
|
+
const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
|
|
1005
|
+
const cols = this.frozen[colPlane];
|
|
1006
|
+
return (cols ?? 0) > 0
|
|
1007
|
+
? html`<div role="none" class="dx-grid__plane--frozen-col">
|
|
1008
|
+
<div
|
|
1009
|
+
role="none"
|
|
1010
|
+
data-dx-grid-plane=${plane}
|
|
1011
|
+
class="dx-grid__plane--frozen-col__content"
|
|
1012
|
+
style="transform:translate3d(0,${offsetBlock}px,0);grid-template-rows:${this
|
|
1013
|
+
.templateGridRows};grid-template-columns:${this[`template${colPlane}`]}"
|
|
1014
|
+
>
|
|
1015
|
+
${[...Array(cols)].map((_, c) => {
|
|
1016
|
+
return [...Array(visibleRows)].map((_, r0) => {
|
|
1017
|
+
const r = this.visRowMin + r0;
|
|
1018
|
+
return this.renderCell(c, r, plane, cellSelected(c, r, plane, selection), c, r0);
|
|
1019
|
+
});
|
|
1020
|
+
})}
|
|
1021
|
+
</div>
|
|
1022
|
+
</div>`
|
|
1023
|
+
: null;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
private renderCell(col: number, row: number, plane: DxGridPlane, selected?: boolean, visCol = col, visRow = row) {
|
|
1027
|
+
const cell = this.cell(col, row, plane);
|
|
1028
|
+
const active = this.cellActive(col, row, plane);
|
|
1029
|
+
const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
|
|
1030
|
+
const resizePlane = cell?.resizeHandle ? resolveResizePlane(cell.resizeHandle, plane) : undefined;
|
|
1031
|
+
const accessory = cell?.accessoryHtml ? staticHtml`${unsafeStatic(cell.accessoryHtml)}` : null;
|
|
1032
|
+
return html`<div
|
|
1033
|
+
role="gridcell"
|
|
1034
|
+
tabindex="0"
|
|
1035
|
+
?inert=${col < 0 || row < 0}
|
|
1036
|
+
?aria-selected=${selected}
|
|
1037
|
+
class=${cell || active
|
|
1038
|
+
? (cell?.className ? cell.className + ' ' : '') + (active ? 'dx-grid__cell--active' : '')
|
|
1039
|
+
: nothing}
|
|
1040
|
+
aria-colindex=${col}
|
|
1041
|
+
aria-rowindex=${row}
|
|
1042
|
+
data-dx-grid-action="cell"
|
|
1043
|
+
style="grid-column:${visCol + 1};grid-row:${visRow + 1}"
|
|
1044
|
+
>
|
|
1045
|
+
${cell?.value}${accessory}${cell?.resizeHandle &&
|
|
1046
|
+
this.axisResizeable(resizePlane!, cell.resizeHandle, resizeIndex!)
|
|
1047
|
+
? html`<dx-grid-axis-resize-handle
|
|
1048
|
+
axis=${cell.resizeHandle}
|
|
1049
|
+
plane=${resizePlane}
|
|
1050
|
+
index=${resizeIndex}
|
|
1051
|
+
size=${this[`${cell.resizeHandle}Size`](resizeIndex!, plane)}
|
|
1052
|
+
></dx-grid-axis-resize-handle>`
|
|
1053
|
+
: null}
|
|
1054
|
+
</div>`;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
494
1057
|
override render() {
|
|
495
1058
|
const visibleCols = this.visColMax - this.visColMin;
|
|
496
1059
|
const visibleRows = this.visRowMax - this.visRowMin;
|
|
497
|
-
const offsetInline =
|
|
498
|
-
const offsetBlock =
|
|
1060
|
+
const offsetInline = this.binInlineMin - this.posInline - this.overscanInline;
|
|
1061
|
+
const offsetBlock = this.binBlockMin - this.posBlock - this.overscanBlock;
|
|
1062
|
+
const selection = selectionProps(this.selectionStart, this.selectionEnd);
|
|
499
1063
|
|
|
500
1064
|
return html`<div
|
|
501
1065
|
role="none"
|
|
502
1066
|
class="dx-grid"
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
1067
|
+
style=${styleMap({
|
|
1068
|
+
'grid-template-columns': `${this.templatefrozenColsStart ? 'min-content ' : ''}minmax(0, ${
|
|
1069
|
+
Number.isFinite(this.limitColumns) ? `${this.intrinsicInlineSize}px` : '1fr'
|
|
1070
|
+
})${this.templatefrozenColsEnd ? ' min-content' : ''}`,
|
|
1071
|
+
'grid-template-rows': `${this.templatefrozenRowsStart ? 'min-content ' : ''}minmax(0, ${
|
|
1072
|
+
Number.isFinite(this.limitRows) ? `${this.intrinsicBlockSize}px` : '1fr'
|
|
1073
|
+
})${this.templatefrozenRowsEnd ? ' min-content' : ''}`,
|
|
1074
|
+
})}
|
|
1075
|
+
data-grid=${this.gridId}
|
|
1076
|
+
data-grid-mode=${this.mode}
|
|
1077
|
+
?data-grid-select=${selection.visible}
|
|
509
1078
|
>
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
style="block-size:${this.rowDefault.size}px;grid-column:${c0 + 1}/${c0 + 2};"
|
|
523
|
-
>
|
|
524
|
-
<span id=${localChId(c0)}>${colToA1Notation(c)}</span>
|
|
525
|
-
${(this.columns[c]?.resizeable ?? this.columnDefault.resizeable) &&
|
|
526
|
-
html`<button class="dx-grid__resize-handle" data-dx-grid-action=${`resize-col,${c}`}>
|
|
527
|
-
<span class="sr-only">Resize</span>
|
|
528
|
-
</button>`}
|
|
529
|
-
</div>`;
|
|
530
|
-
})}
|
|
531
|
-
</div>
|
|
532
|
-
</div>
|
|
533
|
-
<div role="none" class="dx-grid__corner"></div>
|
|
534
|
-
<div role="none" class="dx-grid__rowheader">
|
|
1079
|
+
${this.renderFixed('fixedStartStart', selection)}${this.renderFrozenRows(
|
|
1080
|
+
'frozenRowsStart',
|
|
1081
|
+
visibleCols,
|
|
1082
|
+
offsetInline,
|
|
1083
|
+
selection,
|
|
1084
|
+
)}${this.renderFixed('fixedStartEnd', selection)}${this.renderFrozenColumns(
|
|
1085
|
+
'frozenColsStart',
|
|
1086
|
+
visibleRows,
|
|
1087
|
+
offsetBlock,
|
|
1088
|
+
selection,
|
|
1089
|
+
)}
|
|
1090
|
+
<div role="grid" class="dx-grid__plane--grid" tabindex="0" ${ref(this.viewportRef)}>
|
|
535
1091
|
<div
|
|
536
1092
|
role="none"
|
|
537
|
-
class="dx-
|
|
538
|
-
|
|
539
|
-
>
|
|
540
|
-
${[...Array(visibleRows)].map((_, r0) => {
|
|
541
|
-
const r = this.visRowMin + r0;
|
|
542
|
-
return html`<div role="rowheader" ?inert=${r < 0} style="grid-row:${r0 + 1}/${r0 + 2}">
|
|
543
|
-
<span id=${localRhId(r0)}>${rowToA1Notation(r)}</span>
|
|
544
|
-
${(this.rows[r]?.resizeable ?? this.rowDefault.resizeable) &&
|
|
545
|
-
html`<button class="dx-grid__resize-handle" data-dx-grid-action=${`resize-row,${r}`}>
|
|
546
|
-
<span class="sr-only">Resize</span>
|
|
547
|
-
</button>`}
|
|
548
|
-
</div>`;
|
|
549
|
-
})}
|
|
550
|
-
</div>
|
|
551
|
-
</div>
|
|
552
|
-
<div role="grid" class="dx-grid__viewport" tabindex="0" @wheel=${this.handleWheel} ${ref(this.viewportRef)}>
|
|
553
|
-
<div
|
|
554
|
-
role="none"
|
|
555
|
-
class="dx-grid__content"
|
|
1093
|
+
class="dx-grid__plane--grid__content"
|
|
1094
|
+
data-dx-grid-plane="grid"
|
|
556
1095
|
style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
|
|
557
|
-
.
|
|
1096
|
+
.templateGridColumns};grid-template-rows:${this.templateGridRows};"
|
|
558
1097
|
>
|
|
559
1098
|
${[...Array(visibleCols)].map((_, c0) => {
|
|
560
1099
|
return [...Array(visibleRows)].map((_, r0) => {
|
|
561
1100
|
const c = c0 + this.visColMin;
|
|
562
1101
|
const r = r0 + this.visRowMin;
|
|
563
|
-
|
|
564
|
-
return html`<div
|
|
565
|
-
role="gridcell"
|
|
566
|
-
tabindex="0"
|
|
567
|
-
?inert=${c < 0 || r < 0}
|
|
568
|
-
aria-rowindex=${r}
|
|
569
|
-
aria-colindex=${c}
|
|
570
|
-
data-dx-grid-action="cell"
|
|
571
|
-
style="grid-column:${c0 + 1};grid-row:${r0 + 1}"
|
|
572
|
-
>
|
|
573
|
-
${cell?.value}
|
|
574
|
-
</div>`;
|
|
1102
|
+
return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
|
|
575
1103
|
});
|
|
576
1104
|
})}
|
|
577
1105
|
</div>
|
|
578
1106
|
</div>
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
<div role="none" class="dx-grid__corner"></div>
|
|
1107
|
+
${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
|
|
1108
|
+
'fixedEndStart',
|
|
1109
|
+
selection,
|
|
1110
|
+
)}${this.renderFrozenRows('frozenRowsEnd', visibleCols, offsetInline, selection)}${this.renderFixed(
|
|
1111
|
+
'fixedEndEnd',
|
|
1112
|
+
selection,
|
|
1113
|
+
)}
|
|
587
1114
|
</div>`;
|
|
588
1115
|
}
|
|
589
1116
|
|
|
1117
|
+
private updateIntrinsicInlineSize() {
|
|
1118
|
+
this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
|
|
1119
|
+
? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
|
|
1120
|
+
gap * (this.limitColumns - 1)
|
|
1121
|
+
: Infinity;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
private updateIntrinsicBlockSize() {
|
|
1125
|
+
this.intrinsicBlockSize = Number.isFinite(this.limitRows)
|
|
1126
|
+
? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
|
|
1127
|
+
gap * (this.limitRows - 1)
|
|
1128
|
+
: Infinity;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
private updateIntrinsicSizes() {
|
|
1132
|
+
this.updateIntrinsicInlineSize();
|
|
1133
|
+
this.updateIntrinsicBlockSize();
|
|
1134
|
+
}
|
|
1135
|
+
|
|
590
1136
|
override firstUpdated() {
|
|
1137
|
+
if (this.getCells) {
|
|
1138
|
+
this.updateCells(true);
|
|
1139
|
+
}
|
|
591
1140
|
this.observer.observe(this.viewportRef.value!);
|
|
592
|
-
this.colSizes = Object.entries(this.columns).reduce(
|
|
593
|
-
|
|
594
|
-
acc[
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
1141
|
+
this.colSizes = Object.entries(this.columns).reduce(
|
|
1142
|
+
(acc: DxGridAxisSizes, [plane, planeColMeta]) => {
|
|
1143
|
+
acc[plane as 'grid' | DxGridFrozenPlane] = Object.entries(planeColMeta).reduce(
|
|
1144
|
+
(planeAcc: Record<string, number>, [col, colMeta]) => {
|
|
1145
|
+
if (colMeta?.size) {
|
|
1146
|
+
planeAcc[col] = colMeta.size;
|
|
1147
|
+
}
|
|
1148
|
+
return planeAcc;
|
|
1149
|
+
},
|
|
1150
|
+
{},
|
|
1151
|
+
);
|
|
1152
|
+
return acc;
|
|
1153
|
+
},
|
|
1154
|
+
{ grid: {} },
|
|
1155
|
+
);
|
|
1156
|
+
this.rowSizes = Object.entries(this.rows).reduce(
|
|
1157
|
+
(acc: DxGridAxisSizes, [plane, planeRowMeta]) => {
|
|
1158
|
+
acc[plane as 'grid' | DxGridFrozenPlane] = Object.entries(planeRowMeta).reduce(
|
|
1159
|
+
(planeAcc: Record<string, number>, [row, rowMeta]) => {
|
|
1160
|
+
if (rowMeta?.size) {
|
|
1161
|
+
planeAcc[row] = rowMeta.size;
|
|
1162
|
+
}
|
|
1163
|
+
return planeAcc;
|
|
1164
|
+
},
|
|
1165
|
+
{},
|
|
1166
|
+
);
|
|
1167
|
+
return acc;
|
|
1168
|
+
},
|
|
1169
|
+
{ grid: {} },
|
|
1170
|
+
);
|
|
1171
|
+
this.updateIntrinsicSizes();
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
override willUpdate(changedProperties: Map<string, any>) {
|
|
1175
|
+
if (
|
|
1176
|
+
this.getCells &&
|
|
1177
|
+
(changedProperties.has('initialCells') ||
|
|
1178
|
+
changedProperties.has('visColMin') ||
|
|
1179
|
+
changedProperties.has('visColMax') ||
|
|
1180
|
+
changedProperties.has('visRowMin') ||
|
|
1181
|
+
changedProperties.has('visRowMax'))
|
|
1182
|
+
) {
|
|
1183
|
+
this.updateCells();
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
if (changedProperties.has('rowDefault') || changedProperties.has('rows') || changedProperties.has('limitRows')) {
|
|
1187
|
+
this.updateIntrinsicBlockSize();
|
|
1188
|
+
this.updatePosBlock();
|
|
1189
|
+
this.updateVisBlock();
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
if (
|
|
1193
|
+
changedProperties.has('colDefault') ||
|
|
1194
|
+
changedProperties.has('columns') ||
|
|
1195
|
+
changedProperties.has('limitColumns')
|
|
1196
|
+
) {
|
|
1197
|
+
this.updateIntrinsicInlineSize();
|
|
1198
|
+
this.updatePosInline();
|
|
1199
|
+
this.updateVisInline();
|
|
1200
|
+
}
|
|
604
1201
|
}
|
|
605
1202
|
|
|
606
1203
|
override updated(changedProperties: Map<string, any>) {
|
|
@@ -613,10 +1210,16 @@ export class DxGrid extends LitElement {
|
|
|
613
1210
|
}
|
|
614
1211
|
}
|
|
615
1212
|
|
|
1213
|
+
public updateIfWithinBounds({ col, row }: { col: number; row: number }): boolean {
|
|
1214
|
+
if (col >= this.visColMin && col <= this.visColMax && row >= this.visRowMin && row <= this.visRowMax) {
|
|
1215
|
+
this.requestUpdate();
|
|
1216
|
+
return true;
|
|
1217
|
+
}
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
616
1221
|
override disconnectedCallback() {
|
|
617
1222
|
super.disconnectedCallback();
|
|
618
|
-
// console.log('[disconnected]', this.viewportRef.value);
|
|
619
|
-
// TODO(thure): Will this even work?
|
|
620
1223
|
if (this.viewportRef.value) {
|
|
621
1224
|
this.observer.unobserve(this.viewportRef.value);
|
|
622
1225
|
}
|
|
@@ -626,3 +1229,5 @@ export class DxGrid extends LitElement {
|
|
|
626
1229
|
return this;
|
|
627
1230
|
}
|
|
628
1231
|
}
|
|
1232
|
+
|
|
1233
|
+
export { rowToA1Notation, colToA1Notation } from './util';
|