@dxos/lit-grid 0.6.13 → 0.6.14-main.1366248
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-multiselect-cell.d.ts +13 -0
- package/dist/src/dx-grid-multiselect-cell.d.ts.map +1 -0
- package/dist/src/dx-grid-multiselect-cell.js +56 -0
- package/dist/src/dx-grid-multiselect-cell.js.map +1 -0
- package/dist/src/dx-grid.d.ts +147 -0
- package/dist/src/dx-grid.d.ts.map +1 -0
- package/dist/src/dx-grid.js +1375 -0
- package/dist/src/dx-grid.js.map +1 -0
- package/dist/src/dx-grid.lit-stories.d.ts +43 -0
- package/dist/src/dx-grid.lit-stories.d.ts.map +1 -0
- package/dist/src/dx-grid.lit-stories.js +176 -0
- package/dist/src/dx-grid.lit-stories.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types.d.ts +123 -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-multiselect-cell.d.ts +13 -0
- package/dist/types/src/dx-grid-multiselect-cell.d.ts.map +1 -0
- package/dist/types/src/dx-grid-multiselect-cell.js +56 -0
- package/dist/types/src/dx-grid-multiselect-cell.js.map +1 -0
- package/dist/types/src/dx-grid.d.ts +121 -57
- package/dist/types/src/dx-grid.d.ts.map +1 -1
- package/dist/types/src/dx-grid.js +1375 -0
- package/dist/types/src/dx-grid.js.map +1 -0
- package/dist/types/src/dx-grid.lit-stories.d.ts +28 -2
- package/dist/types/src/dx-grid.lit-stories.d.ts.map +1 -1
- package/dist/types/src/dx-grid.lit-stories.js +176 -0
- package/dist/types/src/dx-grid.lit-stories.js.map +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/index.js +7 -0
- package/dist/types/src/index.js.map +1 -0
- package/dist/types/src/types.d.ts +115 -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 +7 -7
- package/src/dx-grid-axis-resize-handle.pcss +23 -0
- package/src/dx-grid-axis-resize-handle.ts +87 -0
- package/src/dx-grid-multiselect-cell.pcss +32 -0
- package/src/dx-grid-multiselect-cell.ts +46 -0
- package/src/dx-grid.lit-stories.ts +159 -21
- package/src/dx-grid.pcss +70 -71
- package/src/dx-grid.ts +1307 -363
- package/src/index.ts +1 -0
- package/src/types.ts +165 -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,44 @@
|
|
|
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
|
+
type DxGridAnnotatedWheelEvent,
|
|
40
|
+
type DxGridRange,
|
|
41
|
+
} from './types';
|
|
42
|
+
import { separator, toCellIndex } from './util';
|
|
10
43
|
|
|
11
44
|
/**
|
|
12
45
|
* The size in pixels of the gap between cells
|
|
@@ -14,135 +47,258 @@ import { DxAxisResize, type DxAxisResizeProps, type DxGridAxis } from './types';
|
|
|
14
47
|
const gap = 1;
|
|
15
48
|
|
|
16
49
|
/**
|
|
17
|
-
*
|
|
18
|
-
|
|
50
|
+
* ResizeObserver notices even subpixel changes, only respond to changes of at least 1px.
|
|
51
|
+
*/
|
|
52
|
+
const resizeTolerance = 1;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The amount of pixels the primary pointer has to move after PointerDown to engage in selection.
|
|
19
56
|
*/
|
|
20
|
-
const
|
|
57
|
+
const selectTolerance = 4;
|
|
21
58
|
|
|
22
59
|
//
|
|
23
|
-
// `
|
|
60
|
+
// `defaultSize`, the final fallbacks
|
|
24
61
|
//
|
|
25
|
-
const
|
|
26
|
-
const
|
|
62
|
+
const defaultSizeRow = 32;
|
|
63
|
+
const defaultSizeCol = 180;
|
|
27
64
|
|
|
28
65
|
//
|
|
29
66
|
// `size`, when suffixed with ‘row’ or ‘col’, are limits on size applied when resizing
|
|
30
67
|
//
|
|
31
68
|
const sizeColMin = 32;
|
|
32
69
|
const sizeColMax = 1024;
|
|
33
|
-
const sizeRowMin =
|
|
70
|
+
const sizeRowMin = 32;
|
|
34
71
|
const sizeRowMax = 1024;
|
|
35
72
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
73
|
+
const shouldSelect = (pointer: DxGridPointer, { pageX, pageY }: PointerEvent) => {
|
|
74
|
+
if (pointer?.state === 'maybeSelecting') {
|
|
75
|
+
return Math.hypot(Math.abs(pointer.pageX - pageX), Math.abs(pointer.pageY - pageY)) >= selectTolerance;
|
|
76
|
+
} else {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
40
80
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
81
|
+
const selectionProps = (selectionStart: DxGridPosition, selectionEnd: DxGridPosition): DxGridSelectionProps => {
|
|
82
|
+
const colMin = Math.min(selectionStart.col, selectionEnd.col);
|
|
83
|
+
const colMax = Math.max(selectionStart.col, selectionEnd.col);
|
|
84
|
+
const rowMin = Math.min(selectionStart.row, selectionEnd.row);
|
|
85
|
+
const rowMax = Math.max(selectionStart.row, selectionEnd.row);
|
|
86
|
+
const plane = selectionStart.plane;
|
|
87
|
+
const visible = colMin !== colMax || rowMin !== rowMax;
|
|
88
|
+
return { colMin, colMax, rowMin, rowMax, plane, visible };
|
|
89
|
+
};
|
|
44
90
|
|
|
45
|
-
const
|
|
91
|
+
const cellSelected = (col: number, row: number, plane: DxGridPlane, selection: DxGridSelectionProps): boolean => {
|
|
46
92
|
return (
|
|
47
|
-
|
|
48
|
-
|
|
93
|
+
plane === selection.plane &&
|
|
94
|
+
col >= selection.colMin &&
|
|
95
|
+
col <= selection.colMax &&
|
|
96
|
+
row >= selection.rowMin &&
|
|
97
|
+
row <= selection.rowMax
|
|
49
98
|
);
|
|
50
99
|
};
|
|
51
100
|
|
|
52
|
-
const
|
|
53
|
-
|
|
101
|
+
const closestAction = (target: EventTarget | null): { action: string | null; actionEl: HTMLElement | null } => {
|
|
102
|
+
const actionEl: HTMLElement | null = (target as HTMLElement | null)?.closest('[data-dx-grid-action]') ?? null;
|
|
103
|
+
return { actionEl, action: actionEl?.getAttribute('data-dx-grid-action') ?? null };
|
|
54
104
|
};
|
|
55
105
|
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
106
|
+
export const closestCell = (target: EventTarget | null, actionEl?: HTMLElement | null): DxGridPositionNullable => {
|
|
107
|
+
let cellElement = actionEl;
|
|
108
|
+
if (!cellElement) {
|
|
109
|
+
const { action, actionEl } = closestAction(target);
|
|
110
|
+
if (action === 'cell') {
|
|
111
|
+
cellElement = actionEl as HTMLElement;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (cellElement) {
|
|
115
|
+
const col = parseInt(cellElement.getAttribute('aria-colindex') ?? 'never');
|
|
116
|
+
const row = parseInt(cellElement.getAttribute('aria-rowindex') ?? 'never');
|
|
117
|
+
const plane = (cellElement.closest('[data-dx-grid-plane]')?.getAttribute('data-dx-grid-plane') ??
|
|
118
|
+
'grid') as DxGridPlane;
|
|
119
|
+
return { plane, col, row };
|
|
120
|
+
} else {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
69
123
|
};
|
|
70
124
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
description?: string;
|
|
74
|
-
resizeable?: boolean;
|
|
125
|
+
const targetIsPlane = (target: EventTarget | null): DxGridPlane | null => {
|
|
126
|
+
return ((target as HTMLElement | null)?.getAttribute('data-dx-grid-plane') as DxGridPlane | undefined | null) ?? null;
|
|
75
127
|
};
|
|
76
128
|
|
|
77
|
-
|
|
129
|
+
const resolveRowPlane = (plane: DxGridPlane): 'grid' | DxGridFrozenRowsPlane => {
|
|
130
|
+
switch (plane) {
|
|
131
|
+
case 'fixedStartStart':
|
|
132
|
+
case 'fixedStartEnd':
|
|
133
|
+
case 'frozenRowsStart':
|
|
134
|
+
return 'frozenRowsStart';
|
|
135
|
+
case 'fixedEndStart':
|
|
136
|
+
case 'fixedEndEnd':
|
|
137
|
+
case 'frozenRowsEnd':
|
|
138
|
+
return 'frozenRowsEnd';
|
|
139
|
+
default:
|
|
140
|
+
return 'grid';
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const resolveColPlane = (plane: DxGridPlane): 'grid' | DxGridFrozenColsPlane => {
|
|
145
|
+
switch (plane) {
|
|
146
|
+
case 'fixedStartStart':
|
|
147
|
+
case 'fixedEndStart':
|
|
148
|
+
case 'frozenColsStart':
|
|
149
|
+
return 'frozenColsStart';
|
|
150
|
+
case 'fixedStartEnd':
|
|
151
|
+
case 'fixedEndEnd':
|
|
152
|
+
case 'frozenColsEnd':
|
|
153
|
+
return 'frozenColsEnd';
|
|
154
|
+
default:
|
|
155
|
+
return 'grid';
|
|
156
|
+
}
|
|
157
|
+
};
|
|
78
158
|
|
|
79
|
-
const
|
|
80
|
-
|
|
159
|
+
const resolveFrozenPlane = (axis: DxGridAxis, cellPlane: DxGridPlane): 'grid' | DxGridFrozenPlane => {
|
|
160
|
+
switch (cellPlane) {
|
|
161
|
+
case 'fixedStartStart':
|
|
162
|
+
return axis === 'col' ? 'frozenColsStart' : 'frozenRowsStart';
|
|
163
|
+
case 'fixedStartEnd':
|
|
164
|
+
return axis === 'col' ? 'frozenColsEnd' : 'frozenRowsStart';
|
|
165
|
+
case 'fixedEndStart':
|
|
166
|
+
return axis === 'col' ? 'frozenColsStart' : 'frozenRowsEnd';
|
|
167
|
+
case 'fixedEndEnd':
|
|
168
|
+
return axis === 'col' ? 'frozenColsEnd' : 'frozenRowsEnd';
|
|
169
|
+
case 'frozenColsStart':
|
|
170
|
+
case 'frozenColsEnd':
|
|
171
|
+
return axis === 'col' ? cellPlane : 'grid';
|
|
172
|
+
case 'frozenRowsStart':
|
|
173
|
+
case 'frozenRowsEnd':
|
|
174
|
+
return axis === 'row' ? cellPlane : 'grid';
|
|
175
|
+
default:
|
|
176
|
+
return cellPlane;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
81
179
|
|
|
82
|
-
const
|
|
180
|
+
const isSameCell = (a: DxGridPositionNullable, b: DxGridPositionNullable) =>
|
|
181
|
+
a &&
|
|
182
|
+
b &&
|
|
183
|
+
a.plane === b.plane &&
|
|
184
|
+
Number.isFinite(a.col) &&
|
|
185
|
+
Number.isFinite(a.row) &&
|
|
186
|
+
a.col === b.col &&
|
|
187
|
+
a.row === b.row;
|
|
83
188
|
|
|
84
189
|
@customElement('dx-grid')
|
|
85
190
|
export class DxGrid extends LitElement {
|
|
191
|
+
constructor() {
|
|
192
|
+
super();
|
|
193
|
+
// Wheel, top-level and element-level
|
|
194
|
+
document.defaultView?.addEventListener('wheel', this.handleTopLevelWheel, { passive: false });
|
|
195
|
+
this.addEventListener('wheel', this.handleWheel);
|
|
196
|
+
// Custom event(s)
|
|
197
|
+
this.addEventListener('dx-axis-resize-internal', this.handleAxisResizeInternal as EventListener);
|
|
198
|
+
// Standard events
|
|
199
|
+
this.addEventListener('pointerdown', this.handlePointerDown);
|
|
200
|
+
this.addEventListener('pointermove', this.handlePointerMove);
|
|
201
|
+
this.addEventListener('pointerup', this.handlePointerUp);
|
|
202
|
+
this.addEventListener('pointerleave', this.handlePointerUp);
|
|
203
|
+
this.addEventListener('focus', this.handleFocus, { capture: true });
|
|
204
|
+
this.addEventListener('blur', this.handleBlur, { capture: true });
|
|
205
|
+
this.addEventListener('keydown', this.handleKeydown);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@property({ type: String })
|
|
209
|
+
gridId: string = 'default-grid-id';
|
|
210
|
+
|
|
86
211
|
@property({ type: Object })
|
|
87
|
-
rowDefault:
|
|
212
|
+
rowDefault: DxGridPlaneRecord<DxGridFrozenRowsPlane, DxGridAxisMetaProps> = {
|
|
213
|
+
grid: { size: defaultSizeRow },
|
|
214
|
+
};
|
|
88
215
|
|
|
89
216
|
@property({ type: Object })
|
|
90
|
-
columnDefault:
|
|
217
|
+
columnDefault: DxGridPlaneRecord<DxGridFrozenColsPlane, DxGridAxisMetaProps> = {
|
|
218
|
+
grid: { size: defaultSizeCol },
|
|
219
|
+
};
|
|
91
220
|
|
|
92
221
|
@property({ type: Object })
|
|
93
|
-
rows:
|
|
222
|
+
rows: DxGridAxisMeta = { grid: {} };
|
|
94
223
|
|
|
95
224
|
@property({ type: Object })
|
|
96
|
-
columns:
|
|
225
|
+
columns: DxGridAxisMeta = { grid: {} };
|
|
226
|
+
|
|
227
|
+
@property({ type: Object })
|
|
228
|
+
initialCells: DxGridCells = { grid: {} };
|
|
229
|
+
|
|
230
|
+
@property({ type: String })
|
|
231
|
+
mode: DxGridMode = 'browse';
|
|
232
|
+
|
|
233
|
+
@property({ type: Number })
|
|
234
|
+
limitColumns: number = Infinity;
|
|
235
|
+
|
|
236
|
+
@property({ type: Number })
|
|
237
|
+
limitRows: number = Infinity;
|
|
97
238
|
|
|
98
239
|
@property({ type: Object })
|
|
99
|
-
|
|
240
|
+
frozen: DxGridFrozenAxes = {};
|
|
241
|
+
|
|
242
|
+
@property({ type: String })
|
|
243
|
+
overscroll: 'inline' | 'block' | undefined = undefined;
|
|
244
|
+
|
|
245
|
+
@property({ type: String })
|
|
246
|
+
activeRefs = '';
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* When this function is defined, it is used first to try to get a value for a cell, and otherwise will fall back
|
|
250
|
+
* to `cells`.
|
|
251
|
+
*/
|
|
252
|
+
getCells: ((nextRange: DxGridPlaneRange, plane: DxGridPlane) => DxGridPlaneCells) | null = null;
|
|
253
|
+
|
|
254
|
+
@state()
|
|
255
|
+
private cells: DxGridCells = { grid: {} };
|
|
100
256
|
|
|
101
257
|
//
|
|
102
258
|
// `pos`, short for ‘position’, is the position in pixels of the viewport from the origin.
|
|
103
259
|
//
|
|
104
260
|
|
|
105
261
|
@state()
|
|
106
|
-
posInline = 0;
|
|
262
|
+
private posInline = 0;
|
|
107
263
|
|
|
108
264
|
@state()
|
|
109
|
-
posBlock = 0;
|
|
265
|
+
private posBlock = 0;
|
|
110
266
|
|
|
111
267
|
//
|
|
112
268
|
// `size` (when not suffixed with ‘row’ or ‘col’, see above) is the size in pixels of the viewport.
|
|
113
269
|
//
|
|
114
270
|
|
|
115
271
|
@state()
|
|
116
|
-
sizeInline = 0;
|
|
272
|
+
private sizeInline = 0;
|
|
117
273
|
|
|
118
274
|
@state()
|
|
119
|
-
sizeBlock = 0;
|
|
275
|
+
private sizeBlock = 0;
|
|
120
276
|
|
|
121
277
|
//
|
|
122
278
|
// `overscan` is the amount in pixels to offset the grid content due to the number of overscanned columns or rows.
|
|
123
279
|
//
|
|
124
280
|
|
|
125
281
|
@state()
|
|
126
|
-
|
|
282
|
+
private visColMinStart = 0;
|
|
127
283
|
|
|
128
284
|
@state()
|
|
129
|
-
|
|
285
|
+
private visRowMinStart = 0;
|
|
130
286
|
|
|
131
287
|
//
|
|
132
288
|
// `bin`, not short for anything, is the range in pixels within which virtualization does not need to reassess.
|
|
133
289
|
//
|
|
134
290
|
|
|
135
291
|
@state()
|
|
136
|
-
binInlineMin = 0;
|
|
292
|
+
private binInlineMin = 0;
|
|
137
293
|
|
|
138
294
|
@state()
|
|
139
|
-
binInlineMax =
|
|
295
|
+
private binInlineMax = defaultSizeCol;
|
|
140
296
|
|
|
141
297
|
@state()
|
|
142
|
-
binBlockMin = 0;
|
|
298
|
+
private binBlockMin = 0;
|
|
143
299
|
|
|
144
300
|
@state()
|
|
145
|
-
binBlockMax =
|
|
301
|
+
private binBlockMax = defaultSizeRow;
|
|
146
302
|
|
|
147
303
|
//
|
|
148
304
|
// `vis`, short for ‘visible’, is the range in numeric index of the columns or rows which should be rendered within
|
|
@@ -150,97 +306,383 @@ export class DxGrid extends LitElement {
|
|
|
150
306
|
//
|
|
151
307
|
|
|
152
308
|
@state()
|
|
153
|
-
visColMin = 0;
|
|
309
|
+
private visColMin = 0;
|
|
154
310
|
|
|
155
311
|
@state()
|
|
156
|
-
visColMax = 1;
|
|
312
|
+
private visColMax = 1;
|
|
157
313
|
|
|
158
314
|
@state()
|
|
159
|
-
visRowMin = 0;
|
|
315
|
+
private visRowMin = 0;
|
|
160
316
|
|
|
161
317
|
@state()
|
|
162
|
-
visRowMax = 1;
|
|
318
|
+
private visRowMax = 1;
|
|
163
319
|
|
|
164
320
|
//
|
|
165
321
|
// `template` is the rendered value of `grid-{axis}-template`.
|
|
166
322
|
//
|
|
167
323
|
@state()
|
|
168
|
-
|
|
324
|
+
private templateGridColumns = '0';
|
|
325
|
+
|
|
326
|
+
@state()
|
|
327
|
+
private templatefrozenColsStart = '';
|
|
169
328
|
|
|
170
329
|
@state()
|
|
171
|
-
|
|
330
|
+
private templatefrozenColsEnd = '';
|
|
331
|
+
|
|
332
|
+
@state()
|
|
333
|
+
private templateGridRows = '0';
|
|
334
|
+
|
|
335
|
+
@state()
|
|
336
|
+
private templatefrozenRowsStart = '';
|
|
337
|
+
|
|
338
|
+
@state()
|
|
339
|
+
private templatefrozenRowsEnd = '';
|
|
172
340
|
|
|
173
341
|
//
|
|
174
|
-
//
|
|
342
|
+
// Focus, selection, and resize states
|
|
175
343
|
//
|
|
176
344
|
|
|
177
345
|
@state()
|
|
178
|
-
|
|
346
|
+
private pointer: DxGridPointer = null;
|
|
179
347
|
|
|
180
348
|
@state()
|
|
181
|
-
|
|
349
|
+
private colSizes: DxGridAxisSizes = { grid: {} };
|
|
182
350
|
|
|
183
351
|
@state()
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
352
|
+
private rowSizes: DxGridAxisSizes = { grid: {} };
|
|
353
|
+
|
|
354
|
+
@state()
|
|
355
|
+
private focusActive: boolean = false;
|
|
356
|
+
|
|
357
|
+
@state()
|
|
358
|
+
private focusedCell: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
|
|
359
|
+
|
|
360
|
+
@state()
|
|
361
|
+
private selectionStart: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
|
|
362
|
+
|
|
363
|
+
@state()
|
|
364
|
+
private selectionEnd: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
|
|
365
|
+
|
|
366
|
+
//
|
|
367
|
+
// Limits
|
|
368
|
+
//
|
|
369
|
+
|
|
370
|
+
@state()
|
|
371
|
+
private intrinsicInlineSize: number = Infinity;
|
|
372
|
+
|
|
373
|
+
@state()
|
|
374
|
+
private intrinsicBlockSize: number = Infinity;
|
|
375
|
+
|
|
376
|
+
//
|
|
377
|
+
// Primary pointer and keyboard handlers
|
|
378
|
+
//
|
|
379
|
+
|
|
380
|
+
private dispatchEditRequest(initialContent?: string) {
|
|
381
|
+
this.snapPosToFocusedCell();
|
|
382
|
+
if (!this.cellReadonly(this.focusedCell.col, this.focusedCell.row, this.focusedCell.plane)) {
|
|
383
|
+
// Without deferring, the event dispatches before `focusedCellBox` can get updated bounds of the cell, hence:
|
|
384
|
+
queueMicrotask(() =>
|
|
385
|
+
this.dispatchEvent(
|
|
386
|
+
new DxEditRequest({
|
|
387
|
+
cellIndex: toCellIndex(this.focusedCell),
|
|
388
|
+
cellBox: this.focusedCellBox(),
|
|
389
|
+
initialContent,
|
|
390
|
+
}),
|
|
391
|
+
),
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private dispatchSelectionChange() {
|
|
397
|
+
return this.dispatchEvent(
|
|
398
|
+
new DxGridCellsSelect({
|
|
399
|
+
start: this.selectionStart,
|
|
400
|
+
end: this.selectionEnd,
|
|
401
|
+
}),
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private handlePointerDown = (event: PointerEvent) => {
|
|
406
|
+
if (event.isPrimary) {
|
|
407
|
+
const { action, actionEl } = closestAction(event.target);
|
|
408
|
+
if (action && action === 'cell') {
|
|
409
|
+
if (event.shiftKey) {
|
|
410
|
+
// Prevent focus moving so the pointerup handler can move selectionEnd.
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
this.pointer = { state: 'selecting' };
|
|
413
|
+
} else {
|
|
414
|
+
const cellCoords = closestCell(event.target, actionEl);
|
|
415
|
+
if (
|
|
416
|
+
cellCoords &&
|
|
417
|
+
this.mode !== 'edit' &&
|
|
418
|
+
!this.cellReadonly(cellCoords.col, cellCoords.row, cellCoords.plane)
|
|
419
|
+
) {
|
|
420
|
+
this.pointer = { state: 'maybeSelecting', pageX: event.pageX, pageY: event.pageY };
|
|
421
|
+
this.selectionStart = cellCoords;
|
|
422
|
+
this.selectionEnd = cellCoords;
|
|
423
|
+
this.dispatchSelectionChange();
|
|
424
|
+
}
|
|
425
|
+
if (this.mode === 'edit-select') {
|
|
426
|
+
// Prevent focus moving when editing while selection is possible
|
|
427
|
+
event.preventDefault();
|
|
428
|
+
} else {
|
|
429
|
+
if (this.focusActive && isSameCell(this.focusedCell, cellCoords)) {
|
|
430
|
+
this.dispatchEditRequest();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
199
434
|
}
|
|
200
435
|
}
|
|
201
436
|
};
|
|
202
437
|
|
|
203
|
-
handlePointerUp = (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
index: this.resizing.index,
|
|
208
|
-
size: this[this.resizing.axis === 'col' ? 'colSize' : 'rowSize'](this.resizing.index),
|
|
209
|
-
});
|
|
210
|
-
this.dispatchEvent(resizeEvent);
|
|
211
|
-
this.resizing = null;
|
|
438
|
+
private handlePointerUp = (event: PointerEvent) => {
|
|
439
|
+
const cell = closestCell(event.target);
|
|
440
|
+
if (cell && this.pointer?.state === 'selecting') {
|
|
441
|
+
this.setSelectionEnd(cell);
|
|
212
442
|
}
|
|
443
|
+
this.pointer = null;
|
|
213
444
|
};
|
|
214
445
|
|
|
215
|
-
handlePointerMove = (event: PointerEvent) => {
|
|
216
|
-
if (this.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.
|
|
446
|
+
private handlePointerMove = (event: PointerEvent) => {
|
|
447
|
+
if (shouldSelect(this.pointer, event)) {
|
|
448
|
+
this.pointer = { state: 'selecting' };
|
|
449
|
+
} else if (this.pointer?.state === 'selecting') {
|
|
450
|
+
const cell = closestCell(event.target);
|
|
451
|
+
if (
|
|
452
|
+
cell &&
|
|
453
|
+
cell.plane === this.selectionStart.plane &&
|
|
454
|
+
(cell.col !== this.selectionEnd.col || cell.row !== this.selectionEnd.row)
|
|
455
|
+
) {
|
|
456
|
+
this.setSelectionEnd(cell);
|
|
226
457
|
}
|
|
227
458
|
}
|
|
228
459
|
};
|
|
229
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Increments focus among all theoretically possible cells in a plane, cycling as tab would but accounting for the
|
|
463
|
+
* theoretical bounds of the grid plane (handling infinite planes heuristically).
|
|
464
|
+
*/
|
|
465
|
+
private incrementFocusWithinPlane(event: KeyboardEvent) {
|
|
466
|
+
const reverse = event.shiftKey;
|
|
467
|
+
const colPlane = resolveColPlane(this.focusedCell.plane);
|
|
468
|
+
const rowPlane = resolveRowPlane(this.focusedCell.plane);
|
|
469
|
+
const colMax = (colPlane === 'grid' ? this.limitColumns : this.frozen[colPlane]!) - 1;
|
|
470
|
+
const rowMax = (rowPlane === 'grid' ? this.limitRows : this.frozen[rowPlane]!) - 1;
|
|
471
|
+
if (reverse ? this.focusedCell.col - 1 < 0 : this.focusedCell.col + 1 > colMax) {
|
|
472
|
+
if (reverse ? this.focusedCell.row - 1 < 0 : this.focusedCell.row + 1 > rowMax) {
|
|
473
|
+
this.setFocusedCell({
|
|
474
|
+
plane: this.focusedCell.plane,
|
|
475
|
+
row: reverse && Number.isFinite(rowMax) ? rowMax : 0,
|
|
476
|
+
col: reverse && Number.isFinite(colMax) ? colMax : 0,
|
|
477
|
+
});
|
|
478
|
+
} else {
|
|
479
|
+
this.setFocusedCell({
|
|
480
|
+
plane: this.focusedCell.plane,
|
|
481
|
+
row: this.focusedCell.row + (reverse ? -1 : 1),
|
|
482
|
+
col: reverse && Number.isFinite(colMax) ? colMax : 0,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
this.setFocusedCell({ ...this.focusedCell, col: this.focusedCell.col + (reverse ? -1 : 1) });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Increments focus in a specific direction without cycling.
|
|
492
|
+
*/
|
|
493
|
+
private moveFocusOrSelectionEndWithinPlane(event: KeyboardEvent) {
|
|
494
|
+
const current = event.shiftKey ? this.selectionEnd : this.focusedCell;
|
|
495
|
+
const deltaCol = event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0;
|
|
496
|
+
const deltaRow = event.key === 'ArrowUp' ? -1 : event.key === 'ArrowDown' ? 1 : 0;
|
|
497
|
+
|
|
498
|
+
const colPlane = resolveColPlane(current.plane);
|
|
499
|
+
const colMax = (colPlane === 'grid' ? this.limitColumns : this.frozen[colPlane]!) - 1;
|
|
500
|
+
const nextCol = Math.max(0, Math.min(colMax, current.col + deltaCol));
|
|
501
|
+
|
|
502
|
+
const rowPlane = resolveRowPlane(current.plane);
|
|
503
|
+
const rowMax = (rowPlane === 'grid' ? this.limitRows : this.frozen[rowPlane]!) - 1;
|
|
504
|
+
const nextRow = Math.max(0, Math.min(rowMax, current.row + deltaRow));
|
|
505
|
+
|
|
506
|
+
if (event.shiftKey) {
|
|
507
|
+
this.setSelectionEnd({ ...this.selectionEnd, col: nextCol, row: nextRow });
|
|
508
|
+
} else {
|
|
509
|
+
this.setFocusedCell({ ...this.focusedCell, row: nextRow, col: nextCol });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private moveFocusBetweenPlanes(event: KeyboardEvent, plane: DxGridPlane) {
|
|
514
|
+
const planeElement = this.gridRef.value?.querySelector(`[data-dx-grid-plane="${plane}"]`) as HTMLElement | null;
|
|
515
|
+
if (planeElement) {
|
|
516
|
+
const axis = event.key === 'ArrowUp' || event.key === 'ArrowDown' ? 'col' : 'row';
|
|
517
|
+
const delta = event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 1;
|
|
518
|
+
|
|
519
|
+
const planeAxis = planeElement?.getAttribute(`data-dx-grid-plane-${axis}`);
|
|
520
|
+
const adjacentPlanes = Array.from(
|
|
521
|
+
this.gridRef.value?.querySelectorAll(`[data-dx-grid-plane-${axis}="${planeAxis}"]`) ?? [planeElement],
|
|
522
|
+
).filter((el) => !!el) as HTMLElement[];
|
|
523
|
+
|
|
524
|
+
adjacentPlanes[
|
|
525
|
+
(adjacentPlanes.length + adjacentPlanes.indexOf(planeElement!) + delta) % adjacentPlanes.length
|
|
526
|
+
]?.focus({ preventScroll: true });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private moveFocusIntoPlane(plane: DxGridPlane) {
|
|
531
|
+
if (this.focusedCell.plane !== plane) {
|
|
532
|
+
const colPlane = resolveColPlane(plane);
|
|
533
|
+
const rowPlane = resolveRowPlane(plane);
|
|
534
|
+
this.focusedCell = {
|
|
535
|
+
plane,
|
|
536
|
+
col: colPlane === 'grid' ? this.visColMin : 0,
|
|
537
|
+
row: rowPlane === 'grid' ? this.visRowMin : 0,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
this.focusedCellElement()?.focus({ preventScroll: true });
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
private moveFocusToPlane() {
|
|
544
|
+
this.focusedPlaneElement()?.focus({ preventScroll: true });
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private handleKeydown(event: KeyboardEvent) {
|
|
548
|
+
if (this.focusActive && this.mode === 'browse') {
|
|
549
|
+
const plane = targetIsPlane(event.target);
|
|
550
|
+
if (plane) {
|
|
551
|
+
switch (event.key) {
|
|
552
|
+
case 'ArrowDown':
|
|
553
|
+
case 'ArrowUp':
|
|
554
|
+
case 'ArrowRight':
|
|
555
|
+
case 'ArrowLeft':
|
|
556
|
+
event.preventDefault();
|
|
557
|
+
this.moveFocusBetweenPlanes(event, plane);
|
|
558
|
+
break;
|
|
559
|
+
case 'Enter':
|
|
560
|
+
event.preventDefault();
|
|
561
|
+
this.moveFocusIntoPlane(plane);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
} else {
|
|
565
|
+
// Adjust cell-scope state
|
|
566
|
+
switch (event.key) {
|
|
567
|
+
case 'ArrowDown':
|
|
568
|
+
case 'ArrowUp':
|
|
569
|
+
case 'ArrowRight':
|
|
570
|
+
case 'ArrowLeft':
|
|
571
|
+
event.preventDefault();
|
|
572
|
+
this.moveFocusOrSelectionEndWithinPlane(event);
|
|
573
|
+
break;
|
|
574
|
+
case 'Tab':
|
|
575
|
+
event.preventDefault();
|
|
576
|
+
this.incrementFocusWithinPlane(event);
|
|
577
|
+
break;
|
|
578
|
+
case 'Escape':
|
|
579
|
+
// Handle escape if selection is a superset of the focused cell.
|
|
580
|
+
event.preventDefault();
|
|
581
|
+
if (
|
|
582
|
+
this.selectionStart.col !== this.selectionEnd.col ||
|
|
583
|
+
this.selectionStart.row !== this.selectionEnd.row
|
|
584
|
+
) {
|
|
585
|
+
this.selectionStart = this.focusedCell;
|
|
586
|
+
this.selectionEnd = this.focusedCell;
|
|
587
|
+
this.dispatchSelectionChange();
|
|
588
|
+
} else {
|
|
589
|
+
this.moveFocusToPlane();
|
|
590
|
+
}
|
|
591
|
+
break;
|
|
592
|
+
case 'Enter':
|
|
593
|
+
event.preventDefault();
|
|
594
|
+
this.dispatchEditRequest();
|
|
595
|
+
break;
|
|
596
|
+
default:
|
|
597
|
+
if (event.key.length === 1 && event.key.match(/\P{Cc}/u) && !(event.metaKey || event.ctrlKey)) {
|
|
598
|
+
this.dispatchEditRequest(event.key);
|
|
599
|
+
}
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
230
606
|
//
|
|
231
607
|
// Accessors
|
|
232
608
|
//
|
|
233
609
|
|
|
234
|
-
private colSize(c: number | string) {
|
|
235
|
-
|
|
610
|
+
private colSize(c: number | string, plane: DxGridPlane) {
|
|
611
|
+
const resolvedPlane = resolveColPlane(plane);
|
|
612
|
+
return this.colSizes?.[resolvedPlane]?.[c] ?? this.columnDefault[resolvedPlane]?.size ?? defaultSizeCol;
|
|
236
613
|
}
|
|
237
614
|
|
|
238
|
-
private rowSize(r: number | string) {
|
|
239
|
-
|
|
615
|
+
private rowSize(r: number | string, plane: DxGridPlane) {
|
|
616
|
+
const resolvedPlane = resolveRowPlane(plane);
|
|
617
|
+
return this.rowSizes?.[resolvedPlane]?.[r] ?? this.rowDefault[resolvedPlane]?.size ?? defaultSizeRow;
|
|
240
618
|
}
|
|
241
619
|
|
|
242
|
-
private
|
|
243
|
-
|
|
620
|
+
private cell(c: number | string, r: number | string, plane: DxGridPlane): DxGridCellValue | undefined {
|
|
621
|
+
const index: DxGridCellIndex = `${c}${separator}${r}`;
|
|
622
|
+
return this.cells?.[plane]?.[index] ?? this.initialCells?.[plane]?.[index];
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private cellActive(c: number | string, r: number | string, plane: DxGridPlane): boolean {
|
|
626
|
+
return this.focusedCell.plane === plane && this.focusedCell.col === c && this.focusedCell.row === r;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
private setFocusedCell(nextCoords: DxGridPosition) {
|
|
630
|
+
if (
|
|
631
|
+
this.focusedCell.plane !== nextCoords.plane ||
|
|
632
|
+
this.focusedCell.col !== nextCoords.col ||
|
|
633
|
+
this.focusedCell.row !== nextCoords.row
|
|
634
|
+
) {
|
|
635
|
+
this.focusedCell = nextCoords;
|
|
636
|
+
this.selectionStart = nextCoords;
|
|
637
|
+
this.selectionEnd = nextCoords;
|
|
638
|
+
this.snapPosToFocusedCell();
|
|
639
|
+
this.dispatchSelectionChange();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Internal utility for setting selection end.
|
|
644
|
+
|
|
645
|
+
private setSelectionEnd(nextCoords: DxGridPosition) {
|
|
646
|
+
if (
|
|
647
|
+
this.selectionEnd.plane !== nextCoords.plane ||
|
|
648
|
+
this.selectionEnd.col !== nextCoords.col ||
|
|
649
|
+
this.selectionEnd.row !== nextCoords.row
|
|
650
|
+
) {
|
|
651
|
+
this.selectionEnd = nextCoords;
|
|
652
|
+
this.dispatchSelectionChange();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Selection setter for consumers
|
|
657
|
+
|
|
658
|
+
setSelection(range: DxGridRange) {
|
|
659
|
+
if (this.mode !== 'edit') {
|
|
660
|
+
this.selectionStart = range.start;
|
|
661
|
+
this.selectionEnd = range.end;
|
|
662
|
+
this.dispatchSelectionChange();
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private focusedCellBox(): DxEditRequest['cellBox'] {
|
|
667
|
+
const cellElement = this.focusedCellElement();
|
|
668
|
+
const cellSize = {
|
|
669
|
+
inlineSize: this.colSize(this.focusedCell.col, this.focusedCell.plane),
|
|
670
|
+
blockSize: this.rowSize(this.focusedCell.row, this.focusedCell.plane),
|
|
671
|
+
};
|
|
672
|
+
if (!cellElement) {
|
|
673
|
+
return { insetInlineStart: NaN, insetBlockStart: NaN, ...cellSize };
|
|
674
|
+
}
|
|
675
|
+
const contentElement = cellElement.offsetParent as HTMLElement;
|
|
676
|
+
// Note that storing `offset` in state causes performance issues, so instead the transform is parsed here.
|
|
677
|
+
const [_translate3d, inlineStr, blockStr] = contentElement.style.transform.split(/[()]|px,?\s?/);
|
|
678
|
+
const contentOffsetInline = parseFloat(inlineStr);
|
|
679
|
+
const contentOffsetBlock = parseFloat(blockStr);
|
|
680
|
+
const offsetParent = contentElement.offsetParent as HTMLElement;
|
|
681
|
+
return {
|
|
682
|
+
insetInlineStart: cellElement.offsetLeft + contentOffsetInline + offsetParent.offsetLeft,
|
|
683
|
+
insetBlockStart: cellElement.offsetTop + contentOffsetBlock + offsetParent.offsetTop,
|
|
684
|
+
...cellSize,
|
|
685
|
+
};
|
|
244
686
|
}
|
|
245
687
|
|
|
246
688
|
//
|
|
@@ -248,7 +690,7 @@ export class DxGrid extends LitElement {
|
|
|
248
690
|
//
|
|
249
691
|
|
|
250
692
|
@state()
|
|
251
|
-
observer = new ResizeObserver((entries) => {
|
|
693
|
+
private observer = new ResizeObserver((entries) => {
|
|
252
694
|
const { inlineSize, blockSize } = entries?.[0]?.contentBoxSize?.[0] ?? {
|
|
253
695
|
inlineSize: 0,
|
|
254
696
|
blockSize: 0,
|
|
@@ -261,97 +703,167 @@ export class DxGrid extends LitElement {
|
|
|
261
703
|
this.sizeInline = inlineSize;
|
|
262
704
|
this.sizeBlock = blockSize;
|
|
263
705
|
this.updateVis();
|
|
706
|
+
queueMicrotask(() => this.updatePos());
|
|
264
707
|
}
|
|
265
708
|
});
|
|
266
709
|
|
|
267
|
-
|
|
710
|
+
private gridRef: Ref<HTMLDivElement> = createRef();
|
|
711
|
+
private viewportRef: Ref<HTMLDivElement> = createRef();
|
|
712
|
+
|
|
713
|
+
private maybeUpdateVisInline = () => {
|
|
714
|
+
if (this.posInline < this.binInlineMin || this.posInline >= this.binInlineMax) {
|
|
715
|
+
this.updateVisInline();
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
private maybeUpdateVisBlock = () => {
|
|
720
|
+
if (this.posBlock < this.binBlockMin || this.posBlock >= this.binBlockMax) {
|
|
721
|
+
this.updateVisBlock();
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
private maxPosInline() {
|
|
726
|
+
return this.intrinsicInlineSize - this.sizeInline;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
private maxPosBlock() {
|
|
730
|
+
return this.intrinsicBlockSize - this.sizeBlock;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
private updatePosInline(inline?: number, maxInline: number = this.maxPosInline()) {
|
|
734
|
+
this.posInline = Math.max(0, Math.min(maxInline, inline ?? this.posInline));
|
|
735
|
+
this.maybeUpdateVisInline();
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
private updatePosBlock(block?: number, maxBlock: number = this.maxPosBlock()) {
|
|
739
|
+
this.posBlock = Math.max(0, Math.min(maxBlock, block ?? this.posBlock));
|
|
740
|
+
this.maybeUpdateVisBlock();
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
private updatePos(inline?: number, block?: number, maxInline?: number, maxBlock?: number) {
|
|
744
|
+
this.updatePosInline(inline, maxInline);
|
|
745
|
+
this.updatePosBlock(block, maxBlock);
|
|
746
|
+
}
|
|
268
747
|
|
|
269
|
-
|
|
270
|
-
this.posInline = Math.max(0, this.posInline + deltaX);
|
|
271
|
-
this.posBlock = Math.max(0, this.posBlock + deltaY);
|
|
748
|
+
private handleTopLevelWheel = (event: DxGridAnnotatedWheelEvent) => {
|
|
272
749
|
if (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this.posBlock >= this.binBlockMin &&
|
|
276
|
-
this.posBlock < this.binBlockMax
|
|
750
|
+
(Number.isFinite(event.overscrollInline) && this.overscroll === 'inline' && event.overscrollInline === 0) ||
|
|
751
|
+
(Number.isFinite(event.overscrollBlock) && this.overscroll === 'block' && event.overscrollBlock === 0)
|
|
277
752
|
) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
this.
|
|
753
|
+
event.preventDefault();
|
|
754
|
+
event.stopPropagation();
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
private handleWheel = (event: DxGridAnnotatedWheelEvent) => {
|
|
759
|
+
if (this.mode === 'browse') {
|
|
760
|
+
const nextPosInline = this.posInline + event.deltaX;
|
|
761
|
+
const nextPosBlock = this.posBlock + event.deltaY;
|
|
762
|
+
const maxPosInline = this.maxPosInline();
|
|
763
|
+
const maxPosBlock = this.maxPosBlock();
|
|
764
|
+
this.updatePos(nextPosInline, nextPosBlock, maxPosInline, maxPosBlock);
|
|
765
|
+
event.overscrollInline =
|
|
766
|
+
nextPosInline <= 0 ? nextPosInline : nextPosInline > maxPosInline ? nextPosInline - maxPosInline : 0;
|
|
767
|
+
event.overscrollBlock =
|
|
768
|
+
nextPosBlock <= 0 ? nextPosBlock : nextPosBlock > maxPosBlock ? nextPosBlock - maxPosBlock : 0;
|
|
287
769
|
}
|
|
288
770
|
};
|
|
289
771
|
|
|
290
772
|
private updateVisInline() {
|
|
291
773
|
// todo: avoid starting from zero
|
|
292
|
-
let
|
|
293
|
-
let
|
|
774
|
+
let axisCursor = 0;
|
|
775
|
+
let pxCursor = this.colSize(axisCursor, 'grid');
|
|
294
776
|
|
|
295
|
-
while (
|
|
296
|
-
|
|
297
|
-
|
|
777
|
+
while (pxCursor < this.posInline) {
|
|
778
|
+
axisCursor += 1;
|
|
779
|
+
pxCursor += this.colSize(axisCursor, 'grid') + gap;
|
|
298
780
|
}
|
|
299
781
|
|
|
300
|
-
this.visColMin =
|
|
782
|
+
this.visColMin = axisCursor;
|
|
301
783
|
|
|
302
|
-
this.
|
|
303
|
-
|
|
784
|
+
this.visColMinStart = pxCursor - this.colSize(axisCursor, 'grid') - gap;
|
|
785
|
+
const visColMinEnd = pxCursor;
|
|
304
786
|
|
|
305
|
-
this.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}, 0) +
|
|
310
|
-
gap * (overscanCol - 1);
|
|
787
|
+
while (pxCursor < this.posInline + this.sizeInline) {
|
|
788
|
+
axisCursor += 1;
|
|
789
|
+
pxCursor += this.colSize(axisCursor, 'grid') + gap;
|
|
790
|
+
}
|
|
311
791
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
792
|
+
this.visColMax = Math.min(this.limitColumns, axisCursor + 1);
|
|
793
|
+
const visColMaxStart = pxCursor - this.colSize(axisCursor, 'grid') - gap;
|
|
794
|
+
const visColMaxEnd = pxCursor;
|
|
795
|
+
|
|
796
|
+
const bifurcateStart = visColMaxStart - this.sizeInline;
|
|
797
|
+
const bifurcateEnd = visColMaxEnd - this.sizeInline;
|
|
798
|
+
|
|
799
|
+
const bounds = [this.visColMinStart, visColMinEnd, bifurcateStart, bifurcateEnd].sort((a, b) => a - b);
|
|
800
|
+
let boundsCursor = 1;
|
|
801
|
+
while (bounds[boundsCursor] < this.posInline && boundsCursor < 3) {
|
|
802
|
+
boundsCursor += 1;
|
|
315
803
|
}
|
|
316
804
|
|
|
317
|
-
this.
|
|
805
|
+
this.binInlineMin = bounds[boundsCursor - 1];
|
|
806
|
+
this.binInlineMax = bounds[boundsCursor];
|
|
807
|
+
|
|
808
|
+
this.templateGridColumns = [...Array(this.visColMax - this.visColMin)]
|
|
809
|
+
.map((_, c0) => `${this.colSize(this.visColMin + c0, 'grid')}px`)
|
|
810
|
+
.join(' ');
|
|
811
|
+
|
|
812
|
+
this.templatefrozenColsStart = [...Array(this.frozen.frozenColsStart ?? 0)]
|
|
813
|
+
.map((_, c0) => `${this.colSize(c0, 'frozenColsStart')}px`)
|
|
814
|
+
.join(' ');
|
|
318
815
|
|
|
319
|
-
this.
|
|
320
|
-
.map((_, c0) => `${this.colSize(
|
|
816
|
+
this.templatefrozenColsEnd = [...Array(this.frozen.frozenColsEnd ?? 0)]
|
|
817
|
+
.map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`)
|
|
321
818
|
.join(' ');
|
|
322
819
|
}
|
|
323
820
|
|
|
324
821
|
private updateVisBlock() {
|
|
325
822
|
// todo: avoid starting from zero
|
|
326
|
-
let
|
|
327
|
-
let
|
|
823
|
+
let axisCursor = 0;
|
|
824
|
+
let pxCursor = this.rowSize(axisCursor, 'grid');
|
|
328
825
|
|
|
329
|
-
while (
|
|
330
|
-
|
|
331
|
-
|
|
826
|
+
while (pxCursor < this.posBlock) {
|
|
827
|
+
axisCursor += 1;
|
|
828
|
+
pxCursor += this.rowSize(axisCursor, 'grid') + gap;
|
|
332
829
|
}
|
|
333
830
|
|
|
334
|
-
this.visRowMin =
|
|
831
|
+
this.visRowMin = axisCursor;
|
|
335
832
|
|
|
336
|
-
this.
|
|
337
|
-
|
|
833
|
+
this.visRowMinStart = pxCursor - this.rowSize(axisCursor, 'grid') - gap;
|
|
834
|
+
const visRowMinEnd = pxCursor;
|
|
338
835
|
|
|
339
|
-
this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}, 0) +
|
|
344
|
-
gap * (overscanRow - 1);
|
|
836
|
+
while (pxCursor < this.posBlock + this.sizeBlock) {
|
|
837
|
+
axisCursor += 1;
|
|
838
|
+
pxCursor += this.rowSize(axisCursor, 'grid') + gap;
|
|
839
|
+
}
|
|
345
840
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
841
|
+
this.visRowMax = Math.min(this.limitRows, axisCursor + 1);
|
|
842
|
+
const visRowMaxStart = pxCursor - this.rowSize(axisCursor, 'grid') - gap;
|
|
843
|
+
const visRowMaxEnd = pxCursor;
|
|
844
|
+
|
|
845
|
+
const bifurcateStart = visRowMaxStart - this.sizeBlock;
|
|
846
|
+
const bifurcateEnd = visRowMaxEnd - this.sizeBlock;
|
|
847
|
+
|
|
848
|
+
const bounds = [this.visRowMinStart, visRowMinEnd, bifurcateStart, bifurcateEnd].sort((a, b) => a - b);
|
|
849
|
+
let boundsCursor = 1;
|
|
850
|
+
while (bounds[boundsCursor] < this.posBlock && boundsCursor < 3) {
|
|
851
|
+
boundsCursor += 1;
|
|
349
852
|
}
|
|
350
853
|
|
|
351
|
-
this.
|
|
854
|
+
this.binBlockMin = bounds[boundsCursor - 1];
|
|
855
|
+
this.binBlockMax = bounds[boundsCursor];
|
|
352
856
|
|
|
353
|
-
this.
|
|
354
|
-
.map((_, r0) => `${this.rowSize(this.visRowMin + r0)}px`)
|
|
857
|
+
this.templateGridRows = [...Array(this.visRowMax - this.visRowMin)]
|
|
858
|
+
.map((_, r0) => `${this.rowSize(this.visRowMin + r0, 'grid')}px`)
|
|
859
|
+
.join(' ');
|
|
860
|
+
|
|
861
|
+
this.templatefrozenRowsStart = [...Array(this.frozen.frozenRowsStart ?? 0)]
|
|
862
|
+
.map((_, r0) => `${this.rowSize(r0, 'frozenRowsStart')}px`)
|
|
863
|
+
.join(' ');
|
|
864
|
+
|
|
865
|
+
this.templatefrozenRowsEnd = [...Array(this.frozen.frozenRowsEnd ?? 0)]
|
|
866
|
+
.map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`)
|
|
355
867
|
.join(' ');
|
|
356
868
|
}
|
|
357
869
|
|
|
@@ -360,130 +872,284 @@ export class DxGrid extends LitElement {
|
|
|
360
872
|
this.updateVisBlock();
|
|
361
873
|
}
|
|
362
874
|
|
|
363
|
-
|
|
875
|
+
public updateCells(includeFixed?: boolean) {
|
|
876
|
+
this.cells.grid = this.getCells!(
|
|
877
|
+
{
|
|
878
|
+
start: { col: this.visColMin, row: this.visRowMin },
|
|
879
|
+
end: { col: this.visColMax, row: this.visRowMax },
|
|
880
|
+
},
|
|
881
|
+
'grid',
|
|
882
|
+
);
|
|
883
|
+
Object.entries(this.frozen)
|
|
884
|
+
.filter(([_, limit]) => limit && limit > 0)
|
|
885
|
+
.forEach(([plane, limit]) => {
|
|
886
|
+
this.cells[plane as DxGridFrozenPlane] = this.getCells!(
|
|
887
|
+
plane.startsWith('frozenRows')
|
|
888
|
+
? {
|
|
889
|
+
start: { col: this.visColMin, row: 0 },
|
|
890
|
+
end: { col: this.visColMax, row: limit },
|
|
891
|
+
}
|
|
892
|
+
: {
|
|
893
|
+
start: { col: 0, row: this.visRowMin },
|
|
894
|
+
end: { col: limit, row: this.visRowMax },
|
|
895
|
+
},
|
|
896
|
+
plane as DxGridFrozenPlane,
|
|
897
|
+
);
|
|
898
|
+
});
|
|
899
|
+
if (includeFixed) {
|
|
900
|
+
if ((this.frozen.frozenColsStart ?? 0) > 0 && (this.frozen.frozenRowsStart ?? 0) > 0) {
|
|
901
|
+
this.cells.fixedStartStart = this.getCells!(
|
|
902
|
+
{
|
|
903
|
+
start: { col: 0, row: 0 },
|
|
904
|
+
end: { col: this.frozen.frozenColsStart!, row: this.frozen.frozenRowsStart! },
|
|
905
|
+
},
|
|
906
|
+
'fixedStartStart',
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
if ((this.frozen.frozenColsEnd ?? 0) > 0 && (this.frozen.frozenRowsStart ?? 0) > 0) {
|
|
910
|
+
this.cells.fixedStartEnd = this.getCells!(
|
|
911
|
+
{
|
|
912
|
+
start: { col: 0, row: 0 },
|
|
913
|
+
end: { col: this.frozen.frozenColsEnd!, row: this.frozen.frozenRowsStart! },
|
|
914
|
+
},
|
|
915
|
+
'fixedStartEnd',
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if ((this.frozen.frozenColsStart ?? 0) > 0 && (this.frozen.frozenRowsEnd ?? 0) > 0) {
|
|
919
|
+
this.cells.fixedEndStart = this.getCells!(
|
|
920
|
+
{
|
|
921
|
+
start: { col: 0, row: 0 },
|
|
922
|
+
end: { col: this.frozen.frozenColsStart!, row: this.frozen.frozenRowsEnd! },
|
|
923
|
+
},
|
|
924
|
+
'fixedEndStart',
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
if ((this.frozen.frozenColsEnd ?? 0) > 0 && (this.frozen.frozenRowsEnd ?? 0) > 0) {
|
|
928
|
+
this.cells.fixedEndEnd = this.getCells!(
|
|
929
|
+
{
|
|
930
|
+
start: { col: 0, row: 0 },
|
|
931
|
+
end: { col: this.frozen.frozenColsEnd!, row: this.frozen.frozenRowsEnd! },
|
|
932
|
+
},
|
|
933
|
+
'fixedEndEnd',
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
364
938
|
|
|
365
|
-
|
|
366
|
-
focusedCell: Record<DxGridAxis, number> = { col: 0, row: 0 };
|
|
939
|
+
// Focus handlers
|
|
367
940
|
|
|
368
|
-
|
|
369
|
-
|
|
941
|
+
setFocus(coords: DxGridPosition, snap = true) {
|
|
942
|
+
this.setFocusedCell(coords);
|
|
943
|
+
this.focusActive = true;
|
|
944
|
+
if (snap) {
|
|
945
|
+
this.snapPosToFocusedCell();
|
|
946
|
+
}
|
|
947
|
+
}
|
|
370
948
|
|
|
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 };
|
|
949
|
+
private handleFocus(event: FocusEvent) {
|
|
950
|
+
const cellCoords = closestCell(event.target);
|
|
951
|
+
if (cellCoords) {
|
|
379
952
|
this.focusActive = true;
|
|
953
|
+
this.setFocusedCell(cellCoords);
|
|
380
954
|
}
|
|
381
955
|
}
|
|
382
956
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (
|
|
387
|
-
!event.relatedTarget ||
|
|
388
|
-
(event.relatedTarget as HTMLElement).closest('.dx-grid__viewport') !== this.viewportRef.value
|
|
389
|
-
) {
|
|
957
|
+
private handleBlur(event: FocusEvent) {
|
|
958
|
+
// Only unset `focusActive` if focus is moving to an element outside the grid.
|
|
959
|
+
if (event.relatedTarget && !(event.relatedTarget as HTMLElement).closest(`[data-grid="${this.gridId}"]`)) {
|
|
390
960
|
this.focusActive = false;
|
|
391
961
|
}
|
|
392
962
|
}
|
|
393
963
|
|
|
964
|
+
private focusedCellQuery() {
|
|
965
|
+
return `[data-dx-grid-plane=${this.focusedCell.plane}] [aria-colindex="${this.focusedCell.col}"][aria-rowindex="${this.focusedCell.row}"]`;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
private focusedPlaneQuery() {
|
|
969
|
+
return `[data-dx-grid-plane=${this.focusedCell.plane}]`;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
private focusedCellElement() {
|
|
973
|
+
return this.gridRef.value?.querySelector(this.focusedCellQuery()) as HTMLElement | null;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
private focusedPlaneElement() {
|
|
977
|
+
return this.gridRef.value?.querySelector(this.focusedPlaneQuery()) as HTMLElement | null;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
//
|
|
981
|
+
// `outOfVis` returns by how many rows/cols the focused cell is outside of the `vis` range for an axis, inset by a
|
|
982
|
+
// `delta`, otherwise zero if it is within that range.
|
|
983
|
+
//
|
|
984
|
+
|
|
985
|
+
private focusedCellRowOutOfVis() {
|
|
986
|
+
return this.focusedCell.row <= this.visRowMin
|
|
987
|
+
? this.focusedCell.row - this.visRowMin - 1
|
|
988
|
+
: this.focusedCell.row >= this.visRowMax - 1
|
|
989
|
+
? this.focusedCell.row + 2 - this.visRowMax
|
|
990
|
+
: 0;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
private focusedCellColOutOfVis() {
|
|
994
|
+
return this.focusedCell.col <= this.visColMin
|
|
995
|
+
? this.focusedCell.col - this.visColMin - 1
|
|
996
|
+
: this.focusedCell.col >= this.visColMax - 1
|
|
997
|
+
? this.focusedCell.col + 2 - this.visColMax
|
|
998
|
+
: 0;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
private focusedCellOutOfVis(): { col: number; row: number } {
|
|
1002
|
+
switch (this.focusedCell.plane) {
|
|
1003
|
+
case 'grid':
|
|
1004
|
+
return { row: this.focusedCellRowOutOfVis(), col: this.focusedCellColOutOfVis() };
|
|
1005
|
+
case 'frozenRowsStart':
|
|
1006
|
+
case 'frozenRowsEnd':
|
|
1007
|
+
return { col: this.focusedCellColOutOfVis(), row: 0 };
|
|
1008
|
+
case 'frozenColsStart':
|
|
1009
|
+
case 'frozenColsEnd':
|
|
1010
|
+
return { col: 0, row: this.focusedCellRowOutOfVis() };
|
|
1011
|
+
default:
|
|
1012
|
+
return { col: 0, row: 0 };
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private focusPlaneElement() {}
|
|
1017
|
+
|
|
394
1018
|
/**
|
|
395
1019
|
* Moves focus to the cell with actual focus, otherwise moves focus to the viewport.
|
|
396
1020
|
*/
|
|
397
|
-
refocus() {
|
|
398
|
-
(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
1021
|
+
refocus(increment?: 'col' | 'row', delta: 1 | -1 | 0 = 1) {
|
|
1022
|
+
if (increment) {
|
|
1023
|
+
switch (increment) {
|
|
1024
|
+
case 'col': {
|
|
1025
|
+
this.focusedCell.col += delta;
|
|
1026
|
+
break;
|
|
1027
|
+
}
|
|
1028
|
+
case 'row': {
|
|
1029
|
+
this.focusedCell.row += delta;
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
this.snapPosToFocusedCell();
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
queueMicrotask(() => {
|
|
1037
|
+
const cellElement = this.focusedCellElement();
|
|
1038
|
+
if (cellElement) {
|
|
1039
|
+
if (cellElement !== document.activeElement) {
|
|
1040
|
+
cellElement.focus({ preventScroll: true });
|
|
1041
|
+
}
|
|
1042
|
+
} else {
|
|
1043
|
+
this.moveFocusToPlane();
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
private clampPosInline(nextPos: number) {
|
|
1049
|
+
return Math.max(0, Math.min(this.intrinsicInlineSize - this.sizeInline, nextPos));
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
private clampPosBlock(nextPos: number) {
|
|
1053
|
+
return Math.max(0, Math.min(this.intrinsicBlockSize - this.sizeBlock, nextPos));
|
|
407
1054
|
}
|
|
408
1055
|
|
|
409
1056
|
/**
|
|
410
1057
|
* Updates `pos` so that a cell in focus is fully within the viewport
|
|
411
1058
|
*/
|
|
412
1059
|
snapPosToFocusedCell() {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
this.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
this.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
this.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
}
|
|
1060
|
+
const outOfVis = this.focusedCellOutOfVis();
|
|
1061
|
+
if (outOfVis.col < 0) {
|
|
1062
|
+
// align viewport start edge with focused cell start edge
|
|
1063
|
+
this.posInline = this.clampPosInline(
|
|
1064
|
+
[...Array(this.focusedCell.col)].reduce((acc, _, c0) => {
|
|
1065
|
+
return acc + this.colSize(c0, 'grid') + gap;
|
|
1066
|
+
}, 0),
|
|
1067
|
+
);
|
|
1068
|
+
this.updateVisInline();
|
|
1069
|
+
} else if (outOfVis.col > 0) {
|
|
1070
|
+
// align viewport end edge with focused cell end edge
|
|
1071
|
+
this.posInline = this.clampPosInline(
|
|
1072
|
+
[...Array(this.focusedCell.col + 1)].reduce((acc, _, c0) => {
|
|
1073
|
+
return acc + this.colSize(c0, 'grid') + gap;
|
|
1074
|
+
}, -this.sizeInline) - gap,
|
|
1075
|
+
);
|
|
1076
|
+
this.updateVisInline();
|
|
1077
|
+
}
|
|
444
1078
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
1079
|
+
if (outOfVis.row < 0) {
|
|
1080
|
+
// align viewport start edge with focused cell start edge
|
|
1081
|
+
this.posBlock = this.clampPosBlock(
|
|
1082
|
+
[...Array(this.focusedCell.row)].reduce((acc, _, r0) => {
|
|
1083
|
+
return acc + this.rowSize(r0, 'grid') + gap;
|
|
1084
|
+
}, 0),
|
|
1085
|
+
);
|
|
1086
|
+
this.updateVisBlock();
|
|
1087
|
+
} else if (outOfVis.row > 0) {
|
|
1088
|
+
// align viewport end edge with focused cell end edge
|
|
1089
|
+
this.posBlock = this.clampPosBlock(
|
|
1090
|
+
[...Array(this.focusedCell.row + 1)].reduce((acc, _, r0) => {
|
|
1091
|
+
return acc + this.rowSize(r0, 'grid') + gap;
|
|
1092
|
+
}, -this.sizeBlock) - gap,
|
|
1093
|
+
);
|
|
1094
|
+
this.updateVisBlock();
|
|
456
1095
|
}
|
|
457
1096
|
}
|
|
458
1097
|
|
|
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
|
-
|
|
1098
|
+
//
|
|
1099
|
+
// Map scroll DOM methods to virtualized value.
|
|
1100
|
+
//
|
|
1101
|
+
|
|
1102
|
+
override get scrollLeft() {
|
|
1103
|
+
return this.posInline;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
override set scrollLeft(nextValue: number) {
|
|
1107
|
+
this.posInline = nextValue;
|
|
1108
|
+
this.maybeUpdateVisInline();
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
override get scrollTop() {
|
|
1112
|
+
return this.posBlock;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
override set scrollTop(nextValue: number) {
|
|
1116
|
+
this.posBlock = nextValue;
|
|
1117
|
+
this.maybeUpdateVisBlock();
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
//
|
|
1121
|
+
// Resize handlers
|
|
1122
|
+
//
|
|
1123
|
+
|
|
1124
|
+
private axisResizeable(plane: 'grid' | DxGridFrozenPlane, axis: DxGridAxis, index: number | string) {
|
|
1125
|
+
return axis === 'col'
|
|
1126
|
+
? !!(this.columns[plane]?.[index]?.resizeable ?? this.columnDefault[plane as DxGridFrozenColsPlane]?.resizeable)
|
|
1127
|
+
: !!(this.rows[plane]?.[index]?.resizeable ?? this.rowDefault[plane as DxGridFrozenRowsPlane]?.resizeable);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
private handleAxisResizeInternal(event: DxAxisResizeInternal) {
|
|
1131
|
+
event.stopPropagation();
|
|
1132
|
+
const { plane, axis, delta, size, index, state } = event;
|
|
1133
|
+
if (axis === 'col') {
|
|
1134
|
+
const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, size + delta));
|
|
1135
|
+
this.colSizes = { ...this.colSizes, [plane]: { ...this.colSizes[plane], [index]: nextSize } };
|
|
1136
|
+
this.updateVisInline();
|
|
1137
|
+
this.updateIntrinsicInlineSize();
|
|
1138
|
+
} else {
|
|
1139
|
+
const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, size + delta));
|
|
1140
|
+
this.rowSizes = { ...this.colSizes, [plane]: { ...this.rowSizes[plane], [index]: nextSize } };
|
|
1141
|
+
this.updateVisBlock();
|
|
1142
|
+
this.updateIntrinsicBlockSize();
|
|
1143
|
+
}
|
|
1144
|
+
if (state === 'dropped') {
|
|
1145
|
+
this.dispatchEvent(
|
|
1146
|
+
new DxAxisResize({
|
|
1147
|
+
plane,
|
|
1148
|
+
axis,
|
|
1149
|
+
index,
|
|
1150
|
+
size: this[`${axis}Size`](index, plane),
|
|
1151
|
+
}),
|
|
1152
|
+
);
|
|
487
1153
|
}
|
|
488
1154
|
}
|
|
489
1155
|
|
|
@@ -491,138 +1157,416 @@ export class DxGrid extends LitElement {
|
|
|
491
1157
|
// Render and other lifecycle methods
|
|
492
1158
|
//
|
|
493
1159
|
|
|
494
|
-
|
|
1160
|
+
// TODO(thure): This is for rendering presentational objects superimposed onto the canonical grid (e.g. DnD drop line for #8108).
|
|
1161
|
+
private renderPresentationLayer(offsetInline: number, offsetBlock: number) {
|
|
495
1162
|
const visibleCols = this.visColMax - this.visColMin;
|
|
496
1163
|
const visibleRows = this.visRowMax - this.visRowMin;
|
|
497
|
-
const offsetInline = gap + this.binInlineMin - this.posInline - this.overscanInline;
|
|
498
|
-
const offsetBlock = gap + this.binBlockMin - this.posBlock - this.overscanBlock;
|
|
499
|
-
|
|
500
1164
|
return html`<div
|
|
501
1165
|
role="none"
|
|
502
|
-
class="dx-grid"
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
1166
|
+
class="dx-grid-layer--presentation"
|
|
1167
|
+
style=${styleMap({
|
|
1168
|
+
gridTemplateColumns: [
|
|
1169
|
+
...[...Array(this.frozen.frozenColsStart ?? 0)].map((_, c0) => `${this.colSize(c0, 'frozenColsStart')}px`),
|
|
1170
|
+
...[...Array(visibleCols)].map((_, c0) =>
|
|
1171
|
+
c0 === visibleCols - 1
|
|
1172
|
+
? '1fr'
|
|
1173
|
+
: `${this.colSize(this.visColMin + c0, 'grid') + (c0 === 0 ? offsetInline : 0)}px`,
|
|
1174
|
+
),
|
|
1175
|
+
...[...Array(this.frozen.frozenColsEnd ?? 0)].map((_, c0) => `${this.colSize(c0, 'frozenColsEnd')}px`),
|
|
1176
|
+
].join(' '),
|
|
1177
|
+
gridTemplateRows: [
|
|
1178
|
+
...[...Array(this.frozen.frozenRowsStart ?? 0)].map((_, r0) => `${this.rowSize(r0, 'frozenRowsStart')}px`),
|
|
1179
|
+
...[...Array(visibleRows)].map((_, r0) =>
|
|
1180
|
+
r0 === visibleRows - 1
|
|
1181
|
+
? '1fr'
|
|
1182
|
+
: `${this.rowSize(this.visRowMin + r0, 'grid') + (r0 === 0 ? offsetBlock : 0)}px`,
|
|
1183
|
+
),
|
|
1184
|
+
...[...Array(this.frozen.frozenRowsEnd ?? 0)].map((_, r0) => `${this.rowSize(r0, 'frozenRowsEnd')}px`),
|
|
1185
|
+
].join(' '),
|
|
1186
|
+
})}
|
|
509
1187
|
>
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
1188
|
+
${
|
|
1189
|
+
/* TODO(thure): These are debug cells, remove when rendering actual overlay content. */ [
|
|
1190
|
+
...Array((this.frozen.frozenRowsStart ?? 0) + visibleRows + (this.frozen.frozenRowsEnd ?? 0)),
|
|
1191
|
+
].map((_, r0) =>
|
|
1192
|
+
[...Array((this.frozen.frozenColsStart ?? 0) + visibleCols + (this.frozen.frozenColsEnd ?? 0))].map(
|
|
1193
|
+
(_, c0) =>
|
|
1194
|
+
html`<div
|
|
1195
|
+
role="none"
|
|
1196
|
+
class="dx-grid-layer--presentation__cell"
|
|
1197
|
+
style="grid-column:${c0 + 1};grid-row:${r0 + 1}"
|
|
1198
|
+
></div>`,
|
|
1199
|
+
),
|
|
1200
|
+
)
|
|
1201
|
+
}
|
|
1202
|
+
</div>`;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
private renderFixed(plane: DxGridFixedPlane, selection: DxGridSelectionProps) {
|
|
1206
|
+
const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
|
|
1207
|
+
const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
|
|
1208
|
+
const cols = this.frozen[colPlane];
|
|
1209
|
+
const rows = this.frozen[rowPlane];
|
|
1210
|
+
return (cols ?? 0) > 0 && (rows ?? 0) > 0
|
|
1211
|
+
? html`<div
|
|
513
1212
|
role="none"
|
|
514
|
-
|
|
515
|
-
|
|
1213
|
+
tabindex="0"
|
|
1214
|
+
data-dx-grid-plane=${plane}
|
|
1215
|
+
data-dx-grid-plane-row=${plane === 'fixedStartStart' || plane === 'fixedStartEnd' ? 0 : 2}
|
|
1216
|
+
data-dx-grid-plane-col=${plane === 'fixedStartStart' || plane === 'fixedEndStart' ? 0 : 2}
|
|
1217
|
+
class="dx-grid__plane--fixed"
|
|
1218
|
+
style=${styleMap({
|
|
1219
|
+
'grid-template-columns': this[`template${colPlane}`],
|
|
1220
|
+
'grid-template-rows': this[`template${rowPlane}`],
|
|
1221
|
+
})}
|
|
516
1222
|
>
|
|
517
|
-
${[...Array(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
?inert=${c < 0}
|
|
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>`;
|
|
1223
|
+
${[...Array(rows)].map((_, r) => {
|
|
1224
|
+
return [...Array(cols)].map((_, c) => {
|
|
1225
|
+
return this.renderCell(c, r, plane, cellSelected(c, r, plane, selection));
|
|
1226
|
+
});
|
|
530
1227
|
})}
|
|
531
|
-
</div
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
1228
|
+
</div>`
|
|
1229
|
+
: null;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
private renderFrozenRows(
|
|
1233
|
+
plane: DxGridFrozenRowsPlane,
|
|
1234
|
+
visibleCols: number,
|
|
1235
|
+
offsetInline: number,
|
|
1236
|
+
selection: DxGridSelectionProps,
|
|
1237
|
+
) {
|
|
1238
|
+
const rowPlane = resolveRowPlane(plane) as DxGridFrozenPlane;
|
|
1239
|
+
const rows = this.frozen[rowPlane];
|
|
1240
|
+
return (rows ?? 0) > 0
|
|
1241
|
+
? html`<div
|
|
536
1242
|
role="none"
|
|
537
|
-
class="dx-
|
|
538
|
-
|
|
1243
|
+
class="dx-grid__plane--frozen-row"
|
|
1244
|
+
tabindex="0"
|
|
1245
|
+
data-dx-grid-plane=${plane}
|
|
1246
|
+
data-dx-grid-plane-row=${plane === 'frozenRowsStart' ? 0 : 2}
|
|
1247
|
+
data-dx-grid-plane-col="1"
|
|
539
1248
|
>
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
1249
|
+
<div
|
|
1250
|
+
role="none"
|
|
1251
|
+
class="dx-grid__plane--frozen-row__content"
|
|
1252
|
+
style="transform:translate3d(${offsetInline}px,0,0);grid-template-columns:${this
|
|
1253
|
+
.templateGridColumns};grid-template-rows:${this[`template${rowPlane}`]}"
|
|
1254
|
+
>
|
|
1255
|
+
${[...Array(rows)].map((_, r) => {
|
|
1256
|
+
return [...Array(visibleCols)].map((_, c0) => {
|
|
1257
|
+
const c = this.visColMin + c0;
|
|
1258
|
+
return this.renderCell(c, r, plane, cellSelected(c, r, plane, selection), c0, r);
|
|
1259
|
+
});
|
|
1260
|
+
})}
|
|
1261
|
+
</div>
|
|
1262
|
+
</div>`
|
|
1263
|
+
: null;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
private renderFrozenColumns(
|
|
1267
|
+
plane: DxGridFrozenColsPlane,
|
|
1268
|
+
visibleRows: number,
|
|
1269
|
+
offsetBlock: number,
|
|
1270
|
+
selection: DxGridSelectionProps,
|
|
1271
|
+
) {
|
|
1272
|
+
const colPlane = resolveColPlane(plane) as DxGridFrozenPlane;
|
|
1273
|
+
const cols = this.frozen[colPlane];
|
|
1274
|
+
return (cols ?? 0) > 0
|
|
1275
|
+
? html`<div
|
|
554
1276
|
role="none"
|
|
555
|
-
class="dx-
|
|
556
|
-
|
|
557
|
-
|
|
1277
|
+
class="dx-grid__plane--frozen-col"
|
|
1278
|
+
tabindex="0"
|
|
1279
|
+
data-dx-grid-plane=${plane}
|
|
1280
|
+
data-dx-grid-plane-col=${plane === 'frozenColsStart' ? 0 : 2}
|
|
1281
|
+
data-dx-grid-plane-row="1"
|
|
558
1282
|
>
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1283
|
+
<div
|
|
1284
|
+
role="none"
|
|
1285
|
+
class="dx-grid__plane--frozen-col__content"
|
|
1286
|
+
style="transform:translate3d(0,${offsetBlock}px,0);grid-template-rows:${this
|
|
1287
|
+
.templateGridRows};grid-template-columns:${this[`template${colPlane}`]}"
|
|
1288
|
+
>
|
|
1289
|
+
${[...Array(visibleRows)].map((_, r0) => {
|
|
1290
|
+
return [...Array(cols)].map((_, c) => {
|
|
1291
|
+
const r = this.visRowMin + r0;
|
|
1292
|
+
return this.renderCell(c, r, plane, cellSelected(c, r, plane, selection), c, r0);
|
|
1293
|
+
});
|
|
1294
|
+
})}
|
|
1295
|
+
</div>
|
|
1296
|
+
</div>`
|
|
1297
|
+
: null;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
private cellReadonly(col: number, row: number, plane: DxGridPlane) {
|
|
1301
|
+
const colPlane = resolveColPlane(plane);
|
|
1302
|
+
const rowPlane = resolveRowPlane(plane);
|
|
1303
|
+
|
|
1304
|
+
const cellReadonly = this.cell(col, row, plane)?.readonly;
|
|
1305
|
+
if (cellReadonly !== undefined) {
|
|
1306
|
+
return cellReadonly;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
return (
|
|
1310
|
+
(this.columns?.[colPlane]?.[col]?.readonly ?? this.columnDefault?.[colPlane]?.readonly) ||
|
|
1311
|
+
(this.rows?.[rowPlane]?.[row]?.readonly ?? this.rowDefault?.[rowPlane]?.readonly)
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
private renderCell(col: number, row: number, plane: DxGridPlane, selected?: boolean, visCol = col, visRow = row) {
|
|
1316
|
+
const cell = this.cell(col, row, plane);
|
|
1317
|
+
const active = this.cellActive(col, row, plane);
|
|
1318
|
+
const readonly = this.cellReadonly(col, row, plane);
|
|
1319
|
+
const resizeIndex = cell?.resizeHandle ? (cell.resizeHandle === 'col' ? col : row) : undefined;
|
|
1320
|
+
const resizePlane = cell?.resizeHandle ? resolveFrozenPlane(cell.resizeHandle, plane) : undefined;
|
|
1321
|
+
const accessory = cell?.accessoryHtml ? staticHtml`${unsafeStatic(cell.accessoryHtml)}` : null;
|
|
1322
|
+
return html`<div
|
|
1323
|
+
role="gridcell"
|
|
1324
|
+
tabindex="0"
|
|
1325
|
+
aria-selected=${selected ? 'true' : nothing}
|
|
1326
|
+
aria-readonly=${readonly ? 'true' : nothing}
|
|
1327
|
+
class=${cell?.className ?? nothing}
|
|
1328
|
+
data-refs=${cell?.dataRefs ?? nothing}
|
|
1329
|
+
?data-dx-active=${active}
|
|
1330
|
+
data-dx-grid-action="cell"
|
|
1331
|
+
aria-colindex=${col}
|
|
1332
|
+
aria-rowindex=${row}
|
|
1333
|
+
style="grid-column:${visCol + 1};grid-row:${visRow + 1}"
|
|
1334
|
+
>
|
|
1335
|
+
${this.mode !== 'browse' && active ? null : cell?.value}${this.mode !== 'browse' && active
|
|
1336
|
+
? null
|
|
1337
|
+
: accessory}${cell?.resizeHandle &&
|
|
1338
|
+
this.mode === 'browse' &&
|
|
1339
|
+
this.axisResizeable(resizePlane!, cell.resizeHandle, resizeIndex!)
|
|
1340
|
+
? html`<dx-grid-axis-resize-handle
|
|
1341
|
+
axis=${cell.resizeHandle}
|
|
1342
|
+
plane=${resizePlane}
|
|
1343
|
+
index=${resizeIndex}
|
|
1344
|
+
size=${this[`${cell.resizeHandle}Size`](resizeIndex!, plane)}
|
|
1345
|
+
></dx-grid-axis-resize-handle>`
|
|
1346
|
+
: null}
|
|
587
1347
|
</div>`;
|
|
588
1348
|
}
|
|
589
1349
|
|
|
1350
|
+
override render() {
|
|
1351
|
+
const visibleCols = this.visColMax - this.visColMin;
|
|
1352
|
+
const visibleRows = this.visRowMax - this.visRowMin;
|
|
1353
|
+
const offsetInline = this.visColMinStart - this.posInline + gap;
|
|
1354
|
+
const offsetBlock = this.visRowMinStart - this.posBlock + gap;
|
|
1355
|
+
const selection = selectionProps(this.selectionStart, this.selectionEnd);
|
|
1356
|
+
|
|
1357
|
+
return html`<style>
|
|
1358
|
+
${this.activeRefs
|
|
1359
|
+
.split(' ')
|
|
1360
|
+
.filter((value) => value)
|
|
1361
|
+
.map(
|
|
1362
|
+
(activeRef) =>
|
|
1363
|
+
`[data-refs~="${activeRef}"] { background: var(--dx-grid-commented-active, var(--dx-gridCommentedActive)) !important; }`,
|
|
1364
|
+
)
|
|
1365
|
+
.join('\n')}
|
|
1366
|
+
</style>
|
|
1367
|
+
<div
|
|
1368
|
+
role="none"
|
|
1369
|
+
class="dx-grid"
|
|
1370
|
+
style=${styleMap({
|
|
1371
|
+
'grid-template-columns': `${this.templatefrozenColsStart ? 'min-content ' : ''}minmax(0, ${
|
|
1372
|
+
Number.isFinite(this.limitColumns) ? `${this.intrinsicInlineSize}px` : '1fr'
|
|
1373
|
+
})${this.templatefrozenColsEnd ? ' min-content' : ''}`,
|
|
1374
|
+
'grid-template-rows': `${this.templatefrozenRowsStart ? 'min-content ' : ''}minmax(0, ${
|
|
1375
|
+
Number.isFinite(this.limitRows) ? `${this.intrinsicBlockSize}px` : '1fr'
|
|
1376
|
+
})${this.templatefrozenRowsEnd ? ' min-content' : ''}`,
|
|
1377
|
+
})}
|
|
1378
|
+
data-grid=${this.gridId}
|
|
1379
|
+
data-grid-mode=${this.mode}
|
|
1380
|
+
?data-grid-select=${selection.visible}
|
|
1381
|
+
${ref(this.gridRef)}
|
|
1382
|
+
>
|
|
1383
|
+
${this.renderFixed('fixedStartStart', selection)}${this.renderFrozenRows(
|
|
1384
|
+
'frozenRowsStart',
|
|
1385
|
+
visibleCols,
|
|
1386
|
+
offsetInline,
|
|
1387
|
+
selection,
|
|
1388
|
+
)}${this.renderFixed('fixedStartEnd', selection)}${this.renderFrozenColumns(
|
|
1389
|
+
'frozenColsStart',
|
|
1390
|
+
visibleRows,
|
|
1391
|
+
offsetBlock,
|
|
1392
|
+
selection,
|
|
1393
|
+
)}
|
|
1394
|
+
<div
|
|
1395
|
+
role="grid"
|
|
1396
|
+
class="dx-grid__plane--grid"
|
|
1397
|
+
tabindex="0"
|
|
1398
|
+
data-dx-grid-plane="grid"
|
|
1399
|
+
data-dx-grid-plane-row="1"
|
|
1400
|
+
data-dx-grid-plane-col="1"
|
|
1401
|
+
${ref(this.viewportRef)}
|
|
1402
|
+
>
|
|
1403
|
+
<div
|
|
1404
|
+
role="none"
|
|
1405
|
+
class="dx-grid__plane--grid__content"
|
|
1406
|
+
style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
|
|
1407
|
+
.templateGridColumns};grid-template-rows:${this.templateGridRows};"
|
|
1408
|
+
>
|
|
1409
|
+
${[...Array(visibleRows)].map((_, r0) => {
|
|
1410
|
+
return [...Array(visibleCols)].map((_, c0) => {
|
|
1411
|
+
const c = c0 + this.visColMin;
|
|
1412
|
+
const r = r0 + this.visRowMin;
|
|
1413
|
+
return this.renderCell(c, r, 'grid', cellSelected(c, r, 'grid', selection), c0, r0);
|
|
1414
|
+
});
|
|
1415
|
+
})}
|
|
1416
|
+
</div>
|
|
1417
|
+
</div>
|
|
1418
|
+
${this.renderFrozenColumns('frozenColsEnd', visibleRows, offsetBlock, selection)}${this.renderFixed(
|
|
1419
|
+
'fixedEndStart',
|
|
1420
|
+
selection,
|
|
1421
|
+
)}${this.renderFrozenRows('frozenRowsEnd', visibleCols, offsetInline, selection)}${this.renderFixed(
|
|
1422
|
+
'fixedEndEnd',
|
|
1423
|
+
selection,
|
|
1424
|
+
)}
|
|
1425
|
+
</div>`;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
private updateIntrinsicInlineSize() {
|
|
1429
|
+
this.intrinsicInlineSize = Number.isFinite(this.limitColumns)
|
|
1430
|
+
? [...Array(this.limitColumns)].reduce((acc, _, c0) => acc + this.colSize(c0, 'grid'), 0) +
|
|
1431
|
+
gap * (this.limitColumns - 1)
|
|
1432
|
+
: Infinity;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
private updateIntrinsicBlockSize() {
|
|
1436
|
+
this.intrinsicBlockSize = Number.isFinite(this.limitRows)
|
|
1437
|
+
? [...Array(this.limitRows)].reduce((acc, _, r0) => acc + this.rowSize(r0, 'grid'), 0) +
|
|
1438
|
+
gap * (this.limitRows - 1)
|
|
1439
|
+
: Infinity;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
private updateIntrinsicSizes() {
|
|
1443
|
+
this.updateIntrinsicInlineSize();
|
|
1444
|
+
this.updateIntrinsicBlockSize();
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
private computeColSizes() {
|
|
1448
|
+
this.colSizes = Object.entries(this.columns ?? {}).reduce(
|
|
1449
|
+
(acc: DxGridAxisSizes, [plane, planeColMeta]) => {
|
|
1450
|
+
acc[plane as 'grid' | DxGridFrozenPlane] = Object.entries(planeColMeta).reduce(
|
|
1451
|
+
(planeAcc: Record<string, number>, [col, colMeta]) => {
|
|
1452
|
+
if (colMeta?.size) {
|
|
1453
|
+
planeAcc[col] = colMeta.size;
|
|
1454
|
+
}
|
|
1455
|
+
return planeAcc;
|
|
1456
|
+
},
|
|
1457
|
+
{},
|
|
1458
|
+
);
|
|
1459
|
+
return acc;
|
|
1460
|
+
},
|
|
1461
|
+
{ grid: {} },
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
private computeRowSizes() {
|
|
1466
|
+
this.rowSizes = Object.entries(this.rows ?? {}).reduce(
|
|
1467
|
+
(acc: DxGridAxisSizes, [plane, planeRowMeta]) => {
|
|
1468
|
+
acc[plane as 'grid' | DxGridFrozenPlane] = Object.entries(planeRowMeta).reduce(
|
|
1469
|
+
(planeAcc: Record<string, number>, [row, rowMeta]) => {
|
|
1470
|
+
if (rowMeta?.size) {
|
|
1471
|
+
planeAcc[row] = rowMeta.size;
|
|
1472
|
+
}
|
|
1473
|
+
return planeAcc;
|
|
1474
|
+
},
|
|
1475
|
+
{},
|
|
1476
|
+
);
|
|
1477
|
+
return acc;
|
|
1478
|
+
},
|
|
1479
|
+
{ grid: {} },
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
590
1483
|
override firstUpdated() {
|
|
1484
|
+
if (this.getCells) {
|
|
1485
|
+
this.updateCells(true);
|
|
1486
|
+
}
|
|
591
1487
|
this.observer.observe(this.viewportRef.value!);
|
|
592
|
-
this.
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
1488
|
+
this.computeColSizes();
|
|
1489
|
+
this.computeRowSizes();
|
|
1490
|
+
this.updateIntrinsicSizes();
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
override willUpdate(changedProperties: Map<string, any>) {
|
|
1494
|
+
if (
|
|
1495
|
+
this.getCells &&
|
|
1496
|
+
(changedProperties.has('initialCells') ||
|
|
1497
|
+
changedProperties.has('visColMin') ||
|
|
1498
|
+
changedProperties.has('visColMax') ||
|
|
1499
|
+
changedProperties.has('visRowMin') ||
|
|
1500
|
+
changedProperties.has('visRowMax') ||
|
|
1501
|
+
changedProperties.has('columns') ||
|
|
1502
|
+
changedProperties.has('rows') ||
|
|
1503
|
+
changedProperties.has('limitColumns') ||
|
|
1504
|
+
changedProperties.has('limitRows'))
|
|
1505
|
+
) {
|
|
1506
|
+
this.updateCells(true);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
if (changedProperties.has('rowDefault') || changedProperties.has('rows') || changedProperties.has('limitRows')) {
|
|
1510
|
+
this.updateIntrinsicBlockSize();
|
|
1511
|
+
this.updatePosBlock();
|
|
1512
|
+
this.updateVisBlock();
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
if (
|
|
1516
|
+
changedProperties.has('colDefault') ||
|
|
1517
|
+
changedProperties.has('columns') ||
|
|
1518
|
+
changedProperties.has('limitColumns')
|
|
1519
|
+
) {
|
|
1520
|
+
this.updateIntrinsicInlineSize();
|
|
1521
|
+
this.updatePosInline();
|
|
1522
|
+
this.updateVisInline();
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (changedProperties.has('columns')) {
|
|
1526
|
+
this.computeColSizes();
|
|
1527
|
+
this.updateIntrinsicInlineSize();
|
|
1528
|
+
}
|
|
1529
|
+
if (changedProperties.has('rows')) {
|
|
1530
|
+
this.computeRowSizes();
|
|
1531
|
+
this.updateIntrinsicBlockSize();
|
|
1532
|
+
}
|
|
604
1533
|
}
|
|
605
1534
|
|
|
606
1535
|
override updated(changedProperties: Map<string, any>) {
|
|
607
1536
|
// Update the focused element if there is a change in bounds (otherwise Lit keeps focus on the relative element).
|
|
608
1537
|
if (
|
|
609
1538
|
this.focusActive &&
|
|
610
|
-
(changedProperties.has('
|
|
1539
|
+
(changedProperties.has('visColMin') ||
|
|
1540
|
+
changedProperties.has('visColMax') ||
|
|
1541
|
+
changedProperties.has('visRowMin') ||
|
|
1542
|
+
changedProperties.has('visRowMax') ||
|
|
1543
|
+
changedProperties.has('focusedCell'))
|
|
611
1544
|
) {
|
|
612
1545
|
this.refocus();
|
|
613
1546
|
}
|
|
614
1547
|
}
|
|
615
1548
|
|
|
1549
|
+
public updateIfWithinBounds({ col, row }: { col: number; row: number }): boolean {
|
|
1550
|
+
if (col >= this.visColMin && col <= this.visColMax && row >= this.visRowMin && row <= this.visRowMax) {
|
|
1551
|
+
this.requestUpdate();
|
|
1552
|
+
return true;
|
|
1553
|
+
}
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
616
1557
|
override disconnectedCallback() {
|
|
617
1558
|
super.disconnectedCallback();
|
|
618
|
-
// console.log('[disconnected]', this.viewportRef.value);
|
|
619
|
-
// TODO(thure): Will this even work?
|
|
620
1559
|
if (this.viewportRef.value) {
|
|
621
1560
|
this.observer.unobserve(this.viewportRef.value);
|
|
622
1561
|
}
|
|
1562
|
+
document.defaultView?.removeEventListener('wheel', this.handleTopLevelWheel);
|
|
623
1563
|
}
|
|
624
1564
|
|
|
625
1565
|
override createRenderRoot() {
|
|
626
1566
|
return this;
|
|
627
1567
|
}
|
|
628
1568
|
}
|
|
1569
|
+
|
|
1570
|
+
export { rowToA1Notation, colToA1Notation } from './util';
|
|
1571
|
+
|
|
1572
|
+
export const commentedClassName = 'dx-grid__cell--commented';
|