@dxos/lit-grid 0.6.10-main.3cfcc89
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/LICENSE +8 -0
- package/README.md +3 -0
- package/dist/lib/browser/index.mjs +429 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/types/src/dx-grid.d.ts +71 -0
- package/dist/types/src/dx-grid.d.ts.map +1 -0
- package/dist/types/src/dx-grid.lit-stories.d.ts +17 -0
- package/dist/types/src/dx-grid.lit-stories.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/package.json +24 -0
- package/src/dx-grid.lit-stories.ts +47 -0
- package/src/dx-grid.pcss +101 -0
- package/src/dx-grid.ts +479 -0
- package/src/index.ts +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
Copyright (c) 2022 DXOS
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
// packages/ui/lit-grid/src/dx-grid.ts
|
|
2
|
+
import { LitElement, html } from "lit";
|
|
3
|
+
import { customElement, state, property } from "lit/decorators.js";
|
|
4
|
+
import { ref, createRef } from "lit/directives/ref.js";
|
|
5
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
6
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
7
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
8
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
9
|
+
else
|
|
10
|
+
for (var i = decorators.length - 1; i >= 0; i--)
|
|
11
|
+
if (d = decorators[i])
|
|
12
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
13
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
14
|
+
}
|
|
15
|
+
var gap = 1;
|
|
16
|
+
var resizeTolerance = 8;
|
|
17
|
+
var overscanCol = 1;
|
|
18
|
+
var overscanRow = 1;
|
|
19
|
+
var sizeColMin = 32;
|
|
20
|
+
var sizeColMax = 1024;
|
|
21
|
+
var sizeRowMin = 16;
|
|
22
|
+
var sizeRowMax = 1024;
|
|
23
|
+
var separator = ",";
|
|
24
|
+
var colToA1Notation = (col) => {
|
|
25
|
+
return (col >= 26 ? String.fromCharCode("A".charCodeAt(0) + Math.floor(col / 26) - 1) : "") + String.fromCharCode("A".charCodeAt(0) + col % 26);
|
|
26
|
+
};
|
|
27
|
+
var rowToA1Notation = (row) => {
|
|
28
|
+
return `${row + 1}`;
|
|
29
|
+
};
|
|
30
|
+
var localChId = (c0) => `ch--${c0}`;
|
|
31
|
+
var localRhId = (r0) => `rh--${r0}`;
|
|
32
|
+
var getPage = (axis, event) => axis === "col" ? event.pageX : event.pageY;
|
|
33
|
+
var DxGrid = class extends LitElement {
|
|
34
|
+
constructor() {
|
|
35
|
+
super(...arguments);
|
|
36
|
+
this.rowDefault = {
|
|
37
|
+
size: 32
|
|
38
|
+
};
|
|
39
|
+
this.columnDefault = {
|
|
40
|
+
size: 180
|
|
41
|
+
};
|
|
42
|
+
this.rows = {};
|
|
43
|
+
this.columns = {};
|
|
44
|
+
this.cells = {};
|
|
45
|
+
//
|
|
46
|
+
// `pos`, short for ‘position’, is the position in pixels of the viewport from the origin.
|
|
47
|
+
//
|
|
48
|
+
this.posInline = 0;
|
|
49
|
+
this.posBlock = 0;
|
|
50
|
+
//
|
|
51
|
+
// `size` (when not suffixed with ‘row’ or ‘col’, see above) is the size in pixels of the viewport.
|
|
52
|
+
//
|
|
53
|
+
this.sizeInline = 0;
|
|
54
|
+
this.sizeBlock = 0;
|
|
55
|
+
//
|
|
56
|
+
// `overscan` is the amount in pixels to offset the grid content due to the number of overscanned columns or rows.
|
|
57
|
+
//
|
|
58
|
+
this.overscanInline = 0;
|
|
59
|
+
this.overscanBlock = 0;
|
|
60
|
+
//
|
|
61
|
+
// `bin`, not short for anything, is the range in pixels within which virtualization does not need to reassess.
|
|
62
|
+
//
|
|
63
|
+
this.binInlineMin = 0;
|
|
64
|
+
this.binInlineMax = this.colSize(0);
|
|
65
|
+
this.binBlockMin = 0;
|
|
66
|
+
this.binBlockMax = this.rowSize(0);
|
|
67
|
+
//
|
|
68
|
+
// `vis`, short for ‘visible’, is the range in numeric index of the columns or rows which should be rendered within
|
|
69
|
+
// the viewport. These start with naïve values that are updated before first contentful render.
|
|
70
|
+
//
|
|
71
|
+
this.visColMin = 0;
|
|
72
|
+
this.visColMax = 1;
|
|
73
|
+
this.visRowMin = 0;
|
|
74
|
+
this.visRowMax = 1;
|
|
75
|
+
//
|
|
76
|
+
// `template` is the rendered value of `grid-{axis}-template`.
|
|
77
|
+
//
|
|
78
|
+
this.templateColumns = `${this.colSize(0)}px`;
|
|
79
|
+
this.templateRows = `${this.rowSize(0)}px`;
|
|
80
|
+
//
|
|
81
|
+
// Resize state and handlers
|
|
82
|
+
//
|
|
83
|
+
this.colSizes = {};
|
|
84
|
+
this.rowSizes = {};
|
|
85
|
+
this.resizing = null;
|
|
86
|
+
this.handlePointerDown = (event) => {
|
|
87
|
+
const actionEl = event.target?.closest("[data-dx-grid-action]");
|
|
88
|
+
const action = actionEl?.getAttribute("data-dx-grid-action");
|
|
89
|
+
if (action) {
|
|
90
|
+
if (action.startsWith("resize")) {
|
|
91
|
+
const [resize, index] = action.split(",");
|
|
92
|
+
const [_, axis] = resize.split("-");
|
|
93
|
+
this.resizing = {
|
|
94
|
+
axis,
|
|
95
|
+
size: axis === "col" ? this.colSize(index) : this.rowSize(index),
|
|
96
|
+
page: getPage(axis, event),
|
|
97
|
+
index
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
this.handlePointerUp = (_event) => {
|
|
103
|
+
this.resizing = null;
|
|
104
|
+
};
|
|
105
|
+
this.handlePointerMove = (event) => {
|
|
106
|
+
if (this.resizing) {
|
|
107
|
+
const delta = getPage(this.resizing.axis, event) - this.resizing.page;
|
|
108
|
+
if (this.resizing.axis === "col") {
|
|
109
|
+
const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, this.resizing.size + delta));
|
|
110
|
+
this.colSizes = {
|
|
111
|
+
...this.colSizes,
|
|
112
|
+
[this.resizing.index]: nextSize
|
|
113
|
+
};
|
|
114
|
+
this.updateVisInline();
|
|
115
|
+
} else {
|
|
116
|
+
const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, this.resizing.size + delta));
|
|
117
|
+
this.rowSizes = {
|
|
118
|
+
...this.rowSizes,
|
|
119
|
+
[this.resizing.index]: nextSize
|
|
120
|
+
};
|
|
121
|
+
this.updateVisBlock();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
//
|
|
126
|
+
// Resize & reposition handlers, observer, ref
|
|
127
|
+
//
|
|
128
|
+
this.observer = new ResizeObserver((entries) => {
|
|
129
|
+
const { inlineSize, blockSize } = entries?.[0]?.contentBoxSize?.[0] ?? {
|
|
130
|
+
inlineSize: 0,
|
|
131
|
+
blockSize: 0
|
|
132
|
+
};
|
|
133
|
+
if (Math.abs(inlineSize - this.sizeInline) > resizeTolerance || Math.abs(blockSize - this.sizeBlock) > resizeTolerance) {
|
|
134
|
+
this.sizeInline = inlineSize;
|
|
135
|
+
this.sizeBlock = blockSize;
|
|
136
|
+
this.updateVis();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
this.viewportRef = createRef();
|
|
140
|
+
this.handleWheel = ({ deltaX, deltaY }) => {
|
|
141
|
+
this.posInline = Math.max(0, this.posInline + deltaX);
|
|
142
|
+
this.posBlock = Math.max(0, this.posBlock + deltaY);
|
|
143
|
+
if (this.posInline >= this.binInlineMin && this.posInline < this.binInlineMax && this.posBlock >= this.binBlockMin && this.posBlock < this.binBlockMax) {
|
|
144
|
+
} else {
|
|
145
|
+
this.updateVis();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//
|
|
150
|
+
// Accessors
|
|
151
|
+
//
|
|
152
|
+
colSize(c) {
|
|
153
|
+
return this.colSizes?.[c] ?? this.columnDefault.size;
|
|
154
|
+
}
|
|
155
|
+
rowSize(r) {
|
|
156
|
+
return this.rowSizes?.[r] ?? this.rowDefault.size;
|
|
157
|
+
}
|
|
158
|
+
getCell(c, r) {
|
|
159
|
+
return this.cells[`${c}${separator}${r}`];
|
|
160
|
+
}
|
|
161
|
+
updateVisInline() {
|
|
162
|
+
let colIndex = 0;
|
|
163
|
+
let pxInline = this.colSize(colIndex);
|
|
164
|
+
while (pxInline < this.posInline) {
|
|
165
|
+
colIndex += 1;
|
|
166
|
+
pxInline += this.colSize(colIndex) + gap;
|
|
167
|
+
}
|
|
168
|
+
this.visColMin = colIndex - overscanCol;
|
|
169
|
+
this.binInlineMin = pxInline - this.colSize(colIndex) - gap;
|
|
170
|
+
this.binInlineMax = pxInline + gap;
|
|
171
|
+
this.overscanInline = [
|
|
172
|
+
...Array(overscanCol)
|
|
173
|
+
].reduce((acc, _, c0) => {
|
|
174
|
+
acc += this.colSize(this.visColMin + c0);
|
|
175
|
+
return acc;
|
|
176
|
+
}, 0) + gap * (overscanCol - 1);
|
|
177
|
+
while (pxInline < this.binInlineMax + this.sizeInline) {
|
|
178
|
+
colIndex += 1;
|
|
179
|
+
pxInline += this.colSize(colIndex) + gap;
|
|
180
|
+
}
|
|
181
|
+
this.visColMax = colIndex + overscanCol + 1;
|
|
182
|
+
this.templateColumns = [
|
|
183
|
+
...Array(this.visColMax - this.visColMin)
|
|
184
|
+
].map((_, c0) => `${this.colSize(this.visColMin + c0)}px`).join(" ");
|
|
185
|
+
}
|
|
186
|
+
updateVisBlock() {
|
|
187
|
+
let rowIndex = 0;
|
|
188
|
+
let pxBlock = this.rowSize(rowIndex);
|
|
189
|
+
while (pxBlock < this.posBlock) {
|
|
190
|
+
rowIndex += 1;
|
|
191
|
+
pxBlock += this.rowSize(rowIndex) + gap;
|
|
192
|
+
}
|
|
193
|
+
this.visRowMin = rowIndex - overscanRow;
|
|
194
|
+
this.binBlockMin = pxBlock - this.rowSize(rowIndex) - gap;
|
|
195
|
+
this.binBlockMax = pxBlock + gap;
|
|
196
|
+
this.overscanBlock = [
|
|
197
|
+
...Array(overscanRow)
|
|
198
|
+
].reduce((acc, _, r0) => {
|
|
199
|
+
acc += this.rowSize(this.visRowMin + r0);
|
|
200
|
+
return acc;
|
|
201
|
+
}, 0) + gap * (overscanRow - 1);
|
|
202
|
+
while (pxBlock < this.binBlockMax + this.sizeBlock) {
|
|
203
|
+
rowIndex += 1;
|
|
204
|
+
pxBlock += this.rowSize(rowIndex) + gap;
|
|
205
|
+
}
|
|
206
|
+
this.visRowMax = rowIndex + overscanRow + 1;
|
|
207
|
+
this.templateRows = [
|
|
208
|
+
...Array(this.visRowMax - this.visRowMin)
|
|
209
|
+
].map((_, r0) => `${this.rowSize(this.visRowMin + r0)}px`).join(" ");
|
|
210
|
+
}
|
|
211
|
+
updateVis() {
|
|
212
|
+
this.updateVisInline();
|
|
213
|
+
this.updateVisBlock();
|
|
214
|
+
}
|
|
215
|
+
//
|
|
216
|
+
// Render and other lifecycle methods
|
|
217
|
+
//
|
|
218
|
+
render() {
|
|
219
|
+
const visibleCols = this.visColMax - this.visColMin;
|
|
220
|
+
const visibleRows = this.visRowMax - this.visRowMin;
|
|
221
|
+
const offsetInline = this.binInlineMin - this.posInline - this.overscanInline;
|
|
222
|
+
const offsetBlock = this.binBlockMin - this.posBlock - this.overscanBlock;
|
|
223
|
+
return html`<div
|
|
224
|
+
role="none"
|
|
225
|
+
class="dx-grid"
|
|
226
|
+
@pointerdown=${this.handlePointerDown}
|
|
227
|
+
@pointerup=${this.handlePointerUp}
|
|
228
|
+
@pointermove=${this.handlePointerMove}
|
|
229
|
+
>
|
|
230
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
231
|
+
<div role="none" class="dx-grid__columnheader">
|
|
232
|
+
<div
|
|
233
|
+
role="none"
|
|
234
|
+
class="dx-grid__columnheader__content"
|
|
235
|
+
style="transform:translate3d(${offsetInline}px,0,0);grid-template-columns:${this.templateColumns};"
|
|
236
|
+
>
|
|
237
|
+
${[
|
|
238
|
+
...Array(visibleCols)
|
|
239
|
+
].map((_, c0) => {
|
|
240
|
+
const c = this.visColMin + c0;
|
|
241
|
+
return html`<div
|
|
242
|
+
role="columnheader"
|
|
243
|
+
?inert=${c < 0}
|
|
244
|
+
style="inline-size:${this.colSize(c)}px;block-size:${this.rowDefault.size}px;grid-column:${c0 + 1}/${c0 + 2};"
|
|
245
|
+
>
|
|
246
|
+
<span id=${localChId(c0)}>${colToA1Notation(c)}</span>
|
|
247
|
+
${(this.columns[c]?.resizeable ?? this.columnDefault.resizeable) && html`<button class="dx-grid__resize-handle" data-dx-grid-action=${`resize-col,${c}`}>
|
|
248
|
+
<span class="sr-only">Resize</span>
|
|
249
|
+
</button>`}
|
|
250
|
+
</div>`;
|
|
251
|
+
})}
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
255
|
+
<div role="none" class="dx-grid__rowheader">
|
|
256
|
+
<div role="none" class="dx-grid__rowheader__content" style="transform:translate3d(0,${offsetBlock}px,0);">
|
|
257
|
+
${[
|
|
258
|
+
...Array(visibleRows)
|
|
259
|
+
].map((_, r0) => {
|
|
260
|
+
const r = this.visRowMin + r0;
|
|
261
|
+
return html`<div
|
|
262
|
+
role="rowheader"
|
|
263
|
+
?inert=${r < 0}
|
|
264
|
+
style="block-size:${this.rowSize(r)}px;grid-row:${r0 + 1}/${r0 + 2}"
|
|
265
|
+
>
|
|
266
|
+
<span id=${localRhId(r0)}>${rowToA1Notation(r)}</span>
|
|
267
|
+
${(this.rows[r]?.resizeable ?? this.rowDefault.resizeable) && html`<button class="dx-grid__resize-handle" data-dx-grid-action=${`resize-row,${r}`}>
|
|
268
|
+
<span class="sr-only">Resize</span>
|
|
269
|
+
</button>`}
|
|
270
|
+
</div>`;
|
|
271
|
+
})}
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
<div role="none" class="dx-grid__viewport" @wheel="${this.handleWheel}" ${ref(this.viewportRef)}>
|
|
275
|
+
<div
|
|
276
|
+
role="grid"
|
|
277
|
+
class="dx-grid__content"
|
|
278
|
+
style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this.templateColumns};grid-template-rows:${this.templateRows};"
|
|
279
|
+
>
|
|
280
|
+
${[
|
|
281
|
+
...Array(visibleCols)
|
|
282
|
+
].map((_, c0) => {
|
|
283
|
+
return [
|
|
284
|
+
...Array(visibleRows)
|
|
285
|
+
].map((_2, r0) => {
|
|
286
|
+
const c = c0 + this.visColMin;
|
|
287
|
+
const r = r0 + this.visRowMin;
|
|
288
|
+
const cell = this.getCell(c, r);
|
|
289
|
+
return html`<div
|
|
290
|
+
role="gridcell"
|
|
291
|
+
?inert=${c < 0 || r < 0}
|
|
292
|
+
aria-rowindex=${r}
|
|
293
|
+
aria-colindex=${c}
|
|
294
|
+
data-dx-grid-action="cell"
|
|
295
|
+
style="grid-column:${c0 + 1};grid-row:${r0 + 1}"
|
|
296
|
+
>
|
|
297
|
+
${cell?.value}
|
|
298
|
+
</div>`;
|
|
299
|
+
});
|
|
300
|
+
})}
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
<div role="none" class="dx-grid__scrollbar" aria-orientation="vertical">
|
|
304
|
+
<div role="none" class="dx-grid__scrollbar__thumb"></div>
|
|
305
|
+
</div>
|
|
306
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
307
|
+
<div role="none" class="dx-grid__scrollbar" aria-orientation="horizontal">
|
|
308
|
+
<div role="none" class="dx-grid__scrollbar__thumb"></div>
|
|
309
|
+
</div>
|
|
310
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
311
|
+
</div>`;
|
|
312
|
+
}
|
|
313
|
+
firstUpdated() {
|
|
314
|
+
this.observer.observe(this.viewportRef.value);
|
|
315
|
+
this.colSizes = Object.entries(this.columns).reduce((acc, [colId, colMeta]) => {
|
|
316
|
+
if (colMeta?.size) {
|
|
317
|
+
acc[colId] = colMeta.size;
|
|
318
|
+
}
|
|
319
|
+
return acc;
|
|
320
|
+
}, {});
|
|
321
|
+
this.rowSizes = Object.entries(this.rows).reduce((acc, [rowId, rowMeta]) => {
|
|
322
|
+
if (rowMeta?.size) {
|
|
323
|
+
acc[rowId] = rowMeta.size;
|
|
324
|
+
}
|
|
325
|
+
return acc;
|
|
326
|
+
}, {});
|
|
327
|
+
}
|
|
328
|
+
disconnectedCallback() {
|
|
329
|
+
super.disconnectedCallback();
|
|
330
|
+
if (this.viewportRef.value) {
|
|
331
|
+
this.observer.unobserve(this.viewportRef.value);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
createRenderRoot() {
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
_ts_decorate([
|
|
339
|
+
property({
|
|
340
|
+
type: Object
|
|
341
|
+
})
|
|
342
|
+
], DxGrid.prototype, "rowDefault", void 0);
|
|
343
|
+
_ts_decorate([
|
|
344
|
+
property({
|
|
345
|
+
type: Object
|
|
346
|
+
})
|
|
347
|
+
], DxGrid.prototype, "columnDefault", void 0);
|
|
348
|
+
_ts_decorate([
|
|
349
|
+
property({
|
|
350
|
+
type: Object
|
|
351
|
+
})
|
|
352
|
+
], DxGrid.prototype, "rows", void 0);
|
|
353
|
+
_ts_decorate([
|
|
354
|
+
property({
|
|
355
|
+
type: Object
|
|
356
|
+
})
|
|
357
|
+
], DxGrid.prototype, "columns", void 0);
|
|
358
|
+
_ts_decorate([
|
|
359
|
+
property({
|
|
360
|
+
type: Object
|
|
361
|
+
})
|
|
362
|
+
], DxGrid.prototype, "cells", void 0);
|
|
363
|
+
_ts_decorate([
|
|
364
|
+
state()
|
|
365
|
+
], DxGrid.prototype, "posInline", void 0);
|
|
366
|
+
_ts_decorate([
|
|
367
|
+
state()
|
|
368
|
+
], DxGrid.prototype, "posBlock", void 0);
|
|
369
|
+
_ts_decorate([
|
|
370
|
+
state()
|
|
371
|
+
], DxGrid.prototype, "sizeInline", void 0);
|
|
372
|
+
_ts_decorate([
|
|
373
|
+
state()
|
|
374
|
+
], DxGrid.prototype, "sizeBlock", void 0);
|
|
375
|
+
_ts_decorate([
|
|
376
|
+
state()
|
|
377
|
+
], DxGrid.prototype, "overscanInline", void 0);
|
|
378
|
+
_ts_decorate([
|
|
379
|
+
state()
|
|
380
|
+
], DxGrid.prototype, "overscanBlock", void 0);
|
|
381
|
+
_ts_decorate([
|
|
382
|
+
state()
|
|
383
|
+
], DxGrid.prototype, "binInlineMin", void 0);
|
|
384
|
+
_ts_decorate([
|
|
385
|
+
state()
|
|
386
|
+
], DxGrid.prototype, "binInlineMax", void 0);
|
|
387
|
+
_ts_decorate([
|
|
388
|
+
state()
|
|
389
|
+
], DxGrid.prototype, "binBlockMin", void 0);
|
|
390
|
+
_ts_decorate([
|
|
391
|
+
state()
|
|
392
|
+
], DxGrid.prototype, "binBlockMax", void 0);
|
|
393
|
+
_ts_decorate([
|
|
394
|
+
state()
|
|
395
|
+
], DxGrid.prototype, "visColMin", void 0);
|
|
396
|
+
_ts_decorate([
|
|
397
|
+
state()
|
|
398
|
+
], DxGrid.prototype, "visColMax", void 0);
|
|
399
|
+
_ts_decorate([
|
|
400
|
+
state()
|
|
401
|
+
], DxGrid.prototype, "visRowMin", void 0);
|
|
402
|
+
_ts_decorate([
|
|
403
|
+
state()
|
|
404
|
+
], DxGrid.prototype, "visRowMax", void 0);
|
|
405
|
+
_ts_decorate([
|
|
406
|
+
state()
|
|
407
|
+
], DxGrid.prototype, "templateColumns", void 0);
|
|
408
|
+
_ts_decorate([
|
|
409
|
+
state()
|
|
410
|
+
], DxGrid.prototype, "templateRows", void 0);
|
|
411
|
+
_ts_decorate([
|
|
412
|
+
state()
|
|
413
|
+
], DxGrid.prototype, "colSizes", void 0);
|
|
414
|
+
_ts_decorate([
|
|
415
|
+
state()
|
|
416
|
+
], DxGrid.prototype, "rowSizes", void 0);
|
|
417
|
+
_ts_decorate([
|
|
418
|
+
state()
|
|
419
|
+
], DxGrid.prototype, "resizing", void 0);
|
|
420
|
+
_ts_decorate([
|
|
421
|
+
state()
|
|
422
|
+
], DxGrid.prototype, "observer", void 0);
|
|
423
|
+
DxGrid = _ts_decorate([
|
|
424
|
+
customElement("dx-grid")
|
|
425
|
+
], DxGrid);
|
|
426
|
+
export {
|
|
427
|
+
DxGrid
|
|
428
|
+
};
|
|
429
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/dx-grid.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { LitElement, html } from 'lit';\nimport { customElement, state, property } from 'lit/decorators.js';\nimport { ref, createRef, type Ref } from 'lit/directives/ref.js';\n\n/**\n * The size in pixels of the gap between cells\n */\nconst gap = 1;\n\n/**\n * This should be about the width of the `1` numeral so resize is triggered as the row header column’s intrinsic size\n * changes when scrolling vertically.\n */\nconst resizeTolerance = 8;\n\n//\n// `overscan` is the number of columns or rows to render outside of the viewport\n//\nconst overscanCol = 1;\nconst overscanRow = 1;\n\n//\n// `size`, when suffixed with ‘row’ or ‘col’, are limits on size applied when resizing\n//\nconst sizeColMin = 32;\nconst sizeColMax = 1024;\nconst sizeRowMin = 16;\nconst sizeRowMax = 1024;\n\n/**\n * Separator for serializing cell position vectors\n */\nconst separator = ',';\n\n//\n// A1 notation is the fallback for numbering columns and rows.\n//\n\nconst colToA1Notation = (col: number): string => {\n return (\n (col >= 26 ? String.fromCharCode('A'.charCodeAt(0) + Math.floor(col / 26) - 1) : '') +\n String.fromCharCode('A'.charCodeAt(0) + (col % 26))\n );\n};\n\nconst rowToA1Notation = (row: number): string => {\n return `${row + 1}`;\n};\n\nexport type CellValue = {\n /**\n * The content value\n */\n value: string;\n /**\n * If this is a merged cell, the bottomright-most of the range in numeric notation, otherwise undefined.\n */\n end?: string;\n /**\n * CSS inline styles to apply to the gridcell element\n */\n style?: string;\n};\n\ntype AxisMeta = {\n size: number;\n description?: string;\n resizeable?: boolean;\n};\n\nexport type DxGridProps = Pick<DxGrid, 'cells' | 'rows' | 'columns' | 'rowDefault' | 'columnDefault'>;\n\nconst localChId = (c0: number) => `ch--${c0}`;\nconst localRhId = (r0: number) => `rh--${r0}`;\n\nconst getPage = (axis: string, event: PointerEvent) => (axis === 'col' ? event.pageX : event.pageY);\n\n@customElement('dx-grid')\nexport class DxGrid extends LitElement {\n @property({ type: Object })\n rowDefault: AxisMeta = { size: 32 };\n\n @property({ type: Object })\n columnDefault: AxisMeta = { size: 180 };\n\n @property({ type: Object })\n rows: Record<string, AxisMeta> = {};\n\n @property({ type: Object })\n columns: Record<string, AxisMeta> = {};\n\n @property({ type: Object })\n cells: Record<string, CellValue> = {};\n\n //\n // `pos`, short for ‘position’, is the position in pixels of the viewport from the origin.\n //\n\n @state()\n posInline = 0;\n\n @state()\n posBlock = 0;\n\n //\n // `size` (when not suffixed with ‘row’ or ‘col’, see above) is the size in pixels of the viewport.\n //\n\n @state()\n sizeInline = 0;\n\n @state()\n sizeBlock = 0;\n\n //\n // `overscan` is the amount in pixels to offset the grid content due to the number of overscanned columns or rows.\n //\n\n @state()\n overscanInline = 0;\n\n @state()\n overscanBlock = 0;\n\n //\n // `bin`, not short for anything, is the range in pixels within which virtualization does not need to reassess.\n //\n\n @state()\n binInlineMin = 0;\n\n @state()\n binInlineMax = this.colSize(0);\n\n @state()\n binBlockMin = 0;\n\n @state()\n binBlockMax = this.rowSize(0);\n\n //\n // `vis`, short for ‘visible’, is the range in numeric index of the columns or rows which should be rendered within\n // the viewport. These start with naïve values that are updated before first contentful render.\n //\n\n @state()\n visColMin = 0;\n\n @state()\n visColMax = 1;\n\n @state()\n visRowMin = 0;\n\n @state()\n visRowMax = 1;\n\n //\n // `template` is the rendered value of `grid-{axis}-template`.\n //\n @state()\n templateColumns = `${this.colSize(0)}px`;\n\n @state()\n templateRows = `${this.rowSize(0)}px`;\n\n //\n // Resize state and handlers\n //\n\n @state()\n colSizes: Record<string, number> = {};\n\n @state()\n rowSizes: Record<string, number> = {};\n\n @state()\n resizing: null | { axis: 'col' | 'row'; page: number; size: number; index: string } = null;\n\n handlePointerDown = (event: PointerEvent) => {\n const actionEl = (event.target as HTMLElement)?.closest('[data-dx-grid-action]');\n const action = actionEl?.getAttribute('data-dx-grid-action');\n if (action) {\n if (action.startsWith('resize')) {\n const [resize, index] = action.split(',');\n const [_, axis] = resize.split('-');\n this.resizing = {\n axis: axis as 'col' | 'row',\n size: axis === 'col' ? this.colSize(index) : this.rowSize(index),\n page: getPage(axis, event),\n index,\n };\n }\n }\n };\n\n handlePointerUp = (_event: PointerEvent) => {\n this.resizing = null;\n };\n\n handlePointerMove = (event: PointerEvent) => {\n if (this.resizing) {\n const delta = getPage(this.resizing.axis, event) - this.resizing.page;\n if (this.resizing.axis === 'col') {\n const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, this.resizing.size + delta));\n this.colSizes = { ...this.colSizes, [this.resizing.index]: nextSize };\n this.updateVisInline();\n } else {\n const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, this.resizing.size + delta));\n this.rowSizes = { ...this.rowSizes, [this.resizing.index]: nextSize };\n this.updateVisBlock();\n }\n }\n };\n\n //\n // Accessors\n //\n\n private colSize(c: number | string) {\n return this.colSizes?.[c] ?? this.columnDefault.size;\n }\n\n private rowSize(r: number | string) {\n return this.rowSizes?.[r] ?? this.rowDefault.size;\n }\n\n private getCell(c: number | string, r: number | string) {\n return this.cells[`${c}${separator}${r}`];\n }\n\n //\n // Resize & reposition handlers, observer, ref\n //\n\n @state()\n observer = new ResizeObserver((entries) => {\n const { inlineSize, blockSize } = entries?.[0]?.contentBoxSize?.[0] ?? {\n inlineSize: 0,\n blockSize: 0,\n };\n if (\n Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||\n Math.abs(blockSize - this.sizeBlock) > resizeTolerance\n ) {\n // console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);\n this.sizeInline = inlineSize;\n this.sizeBlock = blockSize;\n this.updateVis();\n }\n });\n\n viewportRef: Ref<HTMLDivElement> = createRef();\n\n handleWheel = ({ deltaX, deltaY }: WheelEvent) => {\n this.posInline = Math.max(0, this.posInline + deltaX);\n this.posBlock = Math.max(0, this.posBlock + deltaY);\n if (\n this.posInline >= this.binInlineMin &&\n this.posInline < this.binInlineMax &&\n this.posBlock >= this.binBlockMin &&\n this.posBlock < this.binBlockMax\n ) {\n // do nothing\n } else {\n // console.info(\n // '[updating bounds]',\n // 'wheel',\n // [this.binInlineMin, this.posInline, this.binInlineMax],\n // [this.binBlockMin, this.posBlock, this.binBlockMax],\n // );\n this.updateVis();\n }\n };\n\n private updateVisInline() {\n // todo: avoid starting from zero\n let colIndex = 0;\n let pxInline = this.colSize(colIndex);\n\n while (pxInline < this.posInline) {\n colIndex += 1;\n pxInline += this.colSize(colIndex) + gap;\n }\n\n this.visColMin = colIndex - overscanCol;\n\n this.binInlineMin = pxInline - this.colSize(colIndex) - gap;\n this.binInlineMax = pxInline + gap;\n\n this.overscanInline =\n [...Array(overscanCol)].reduce((acc, _, c0) => {\n acc += this.colSize(this.visColMin + c0);\n return acc;\n }, 0) +\n gap * (overscanCol - 1);\n\n while (pxInline < this.binInlineMax + this.sizeInline) {\n colIndex += 1;\n pxInline += this.colSize(colIndex) + gap;\n }\n\n this.visColMax = colIndex + overscanCol + 1;\n\n this.templateColumns = [...Array(this.visColMax - this.visColMin)]\n .map((_, c0) => `${this.colSize(this.visColMin + c0)}px`)\n .join(' ');\n }\n\n private updateVisBlock() {\n // todo: avoid starting from zero\n let rowIndex = 0;\n let pxBlock = this.rowSize(rowIndex);\n\n while (pxBlock < this.posBlock) {\n rowIndex += 1;\n pxBlock += this.rowSize(rowIndex) + gap;\n }\n\n this.visRowMin = rowIndex - overscanRow;\n\n this.binBlockMin = pxBlock - this.rowSize(rowIndex) - gap;\n this.binBlockMax = pxBlock + gap;\n\n this.overscanBlock =\n [...Array(overscanRow)].reduce((acc, _, r0) => {\n acc += this.rowSize(this.visRowMin + r0);\n return acc;\n }, 0) +\n gap * (overscanRow - 1);\n\n while (pxBlock < this.binBlockMax + this.sizeBlock) {\n rowIndex += 1;\n pxBlock += this.rowSize(rowIndex) + gap;\n }\n\n this.visRowMax = rowIndex + overscanRow + 1;\n\n this.templateRows = [...Array(this.visRowMax - this.visRowMin)]\n .map((_, r0) => `${this.rowSize(this.visRowMin + r0)}px`)\n .join(' ');\n }\n\n private updateVis() {\n this.updateVisInline();\n this.updateVisBlock();\n }\n\n //\n // Render and other lifecycle methods\n //\n\n override render() {\n const visibleCols = this.visColMax - this.visColMin;\n const visibleRows = this.visRowMax - this.visRowMin;\n // TODO NEXT -> ensure offset is using the right components\n const offsetInline = this.binInlineMin - this.posInline - this.overscanInline;\n const offsetBlock = this.binBlockMin - this.posBlock - this.overscanBlock;\n\n return html`<div\n role=\"none\"\n class=\"dx-grid\"\n @pointerdown=${this.handlePointerDown}\n @pointerup=${this.handlePointerUp}\n @pointermove=${this.handlePointerMove}\n >\n <div role=\"none\" class=\"dx-grid__corner\"></div>\n <div role=\"none\" class=\"dx-grid__columnheader\">\n <div\n role=\"none\"\n class=\"dx-grid__columnheader__content\"\n style=\"transform:translate3d(${offsetInline}px,0,0);grid-template-columns:${this.templateColumns};\"\n >\n ${[...Array(visibleCols)].map((_, c0) => {\n const c = this.visColMin + c0;\n return html`<div\n role=\"columnheader\"\n ?inert=${c < 0}\n style=\"inline-size:${this.colSize(c)}px;block-size:${this.rowDefault.size}px;grid-column:${c0 + 1}/${c0 +\n 2};\"\n >\n <span id=${localChId(c0)}>${colToA1Notation(c)}</span>\n ${(this.columns[c]?.resizeable ?? this.columnDefault.resizeable) &&\n html`<button class=\"dx-grid__resize-handle\" data-dx-grid-action=${`resize-col,${c}`}>\n <span class=\"sr-only\">Resize</span>\n </button>`}\n </div>`;\n })}\n </div>\n </div>\n <div role=\"none\" class=\"dx-grid__corner\"></div>\n <div role=\"none\" class=\"dx-grid__rowheader\">\n <div role=\"none\" class=\"dx-grid__rowheader__content\" style=\"transform:translate3d(0,${offsetBlock}px,0);\">\n ${[...Array(visibleRows)].map((_, r0) => {\n const r = this.visRowMin + r0;\n return html`<div\n role=\"rowheader\"\n ?inert=${r < 0}\n style=\"block-size:${this.rowSize(r)}px;grid-row:${r0 + 1}/${r0 + 2}\"\n >\n <span id=${localRhId(r0)}>${rowToA1Notation(r)}</span>\n ${(this.rows[r]?.resizeable ?? this.rowDefault.resizeable) &&\n html`<button class=\"dx-grid__resize-handle\" data-dx-grid-action=${`resize-row,${r}`}>\n <span class=\"sr-only\">Resize</span>\n </button>`}\n </div>`;\n })}\n </div>\n </div>\n <div role=\"none\" class=\"dx-grid__viewport\" @wheel=\"${this.handleWheel}\" ${ref(this.viewportRef)}>\n <div\n role=\"grid\"\n class=\"dx-grid__content\"\n style=\"transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this\n .templateColumns};grid-template-rows:${this.templateRows};\"\n >\n ${[...Array(visibleCols)].map((_, c0) => {\n return [...Array(visibleRows)].map((_, r0) => {\n const c = c0 + this.visColMin;\n const r = r0 + this.visRowMin;\n const cell = this.getCell(c, r);\n return html`<div\n role=\"gridcell\"\n ?inert=${c < 0 || r < 0}\n aria-rowindex=${r}\n aria-colindex=${c}\n data-dx-grid-action=\"cell\"\n style=\"grid-column:${c0 + 1};grid-row:${r0 + 1}\"\n >\n ${cell?.value}\n </div>`;\n });\n })}\n </div>\n </div>\n <div role=\"none\" class=\"dx-grid__scrollbar\" aria-orientation=\"vertical\">\n <div role=\"none\" class=\"dx-grid__scrollbar__thumb\"></div>\n </div>\n <div role=\"none\" class=\"dx-grid__corner\"></div>\n <div role=\"none\" class=\"dx-grid__scrollbar\" aria-orientation=\"horizontal\">\n <div role=\"none\" class=\"dx-grid__scrollbar__thumb\"></div>\n </div>\n <div role=\"none\" class=\"dx-grid__corner\"></div>\n </div>`;\n }\n\n override firstUpdated() {\n this.observer.observe(this.viewportRef.value!);\n this.colSizes = Object.entries(this.columns).reduce((acc: Record<string, number>, [colId, colMeta]) => {\n if (colMeta?.size) {\n acc[colId] = colMeta.size;\n }\n return acc;\n }, {});\n this.rowSizes = Object.entries(this.rows).reduce((acc: Record<string, number>, [rowId, rowMeta]) => {\n if (rowMeta?.size) {\n acc[rowId] = rowMeta.size;\n }\n return acc;\n }, {});\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n // console.log('[disconnected]', this.viewportRef.value);\n // TODO(thure): Will this even work?\n if (this.viewportRef.value) {\n this.observer.unobserve(this.viewportRef.value);\n }\n }\n\n override createRenderRoot() {\n return this;\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAIA,SAASA,YAAYC,YAAY;AACjC,SAASC,eAAeC,OAAOC,gBAAgB;AAC/C,SAASC,KAAKC,iBAA2B;;;;;;;;;;;AAKzC,IAAMC,MAAM;AAMZ,IAAMC,kBAAkB;AAKxB,IAAMC,cAAc;AACpB,IAAMC,cAAc;AAKpB,IAAMC,aAAa;AACnB,IAAMC,aAAa;AACnB,IAAMC,aAAa;AACnB,IAAMC,aAAa;AAKnB,IAAMC,YAAY;AAMlB,IAAMC,kBAAkB,CAACC,QAAAA;AACvB,UACGA,OAAO,KAAKC,OAAOC,aAAa,IAAIC,WAAW,CAAA,IAAKC,KAAKC,MAAML,MAAM,EAAA,IAAM,CAAA,IAAK,MACjFC,OAAOC,aAAa,IAAIC,WAAW,CAAA,IAAMH,MAAM,EAAA;AAEnD;AAEA,IAAMM,kBAAkB,CAACC,QAAAA;AACvB,SAAO,GAAGA,MAAM,CAAA;AAClB;AAyBA,IAAMC,YAAY,CAACC,OAAe,OAAOA,EAAAA;AACzC,IAAMC,YAAY,CAACC,OAAe,OAAOA,EAAAA;AAEzC,IAAMC,UAAU,CAACC,MAAcC,UAAyBD,SAAS,QAAQC,MAAMC,QAAQD,MAAME;AAGtF,IAAMC,SAAN,cAAqBC,WAAAA;EAArB;;AAELC,sBAAuB;MAAEC,MAAM;IAAG;AAGlCC,yBAA0B;MAAED,MAAM;IAAI;AAGtCE,gBAAiC,CAAC;AAGlCC,mBAAoC,CAAC;AAGrCC,iBAAmC,CAAC;AAOpCC;;;qBAAY;AAGZC,oBAAW;AAOXC;;;sBAAa;AAGbC,qBAAY;AAOZC;;;0BAAiB;AAGjBC,yBAAgB;AAOhBC;;;wBAAe;AAGfC,wBAAe,KAAKC,QAAQ,CAAA;AAG5BC,uBAAc;AAGdC,uBAAc,KAAKC,QAAQ,CAAA;AAQ3BC;;;;qBAAY;AAGZC,qBAAY;AAGZC,qBAAY;AAGZC,qBAAY;AAMZC;;;2BAAkB,GAAG,KAAKR,QAAQ,CAAA,CAAA;AAGlCS,wBAAe,GAAG,KAAKN,QAAQ,CAAA,CAAA;AAO/BO;;;oBAAmC,CAAC;AAGpCC,oBAAmC,CAAC;AAGpCC,oBAAsF;AAEtFC,6BAAoB,CAAChC,UAAAA;AACnB,YAAMiC,WAAYjC,MAAMkC,QAAwBC,QAAQ,uBAAA;AACxD,YAAMC,SAASH,UAAUI,aAAa,qBAAA;AACtC,UAAID,QAAQ;AACV,YAAIA,OAAOE,WAAW,QAAA,GAAW;AAC/B,gBAAM,CAACC,QAAQC,KAAAA,IAASJ,OAAOK,MAAM,GAAA;AACrC,gBAAM,CAACC,GAAG3C,IAAAA,IAAQwC,OAAOE,MAAM,GAAA;AAC/B,eAAKV,WAAW;YACdhC;YACAO,MAAMP,SAAS,QAAQ,KAAKoB,QAAQqB,KAAAA,IAAS,KAAKlB,QAAQkB,KAAAA;YAC1DG,MAAM7C,QAAQC,MAAMC,KAAAA;YACpBwC;UACF;QACF;MACF;IACF;AAEAI,2BAAkB,CAACC,WAAAA;AACjB,WAAKd,WAAW;IAClB;AAEAe,6BAAoB,CAAC9C,UAAAA;AACnB,UAAI,KAAK+B,UAAU;AACjB,cAAMgB,QAAQjD,QAAQ,KAAKiC,SAAShC,MAAMC,KAAAA,IAAS,KAAK+B,SAASY;AACjE,YAAI,KAAKZ,SAAShC,SAAS,OAAO;AAChC,gBAAMiD,WAAW1D,KAAK2D,IAAIrE,YAAYU,KAAK4D,IAAIrE,YAAY,KAAKkD,SAASzB,OAAOyC,KAAAA,CAAAA;AAChF,eAAKlB,WAAW;YAAE,GAAG,KAAKA;YAAU,CAAC,KAAKE,SAASS,KAAK,GAAGQ;UAAS;AACpE,eAAKG,gBAAe;QACtB,OAAO;AACL,gBAAMH,WAAW1D,KAAK2D,IAAInE,YAAYQ,KAAK4D,IAAInE,YAAY,KAAKgD,SAASzB,OAAOyC,KAAAA,CAAAA;AAChF,eAAKjB,WAAW;YAAE,GAAG,KAAKA;YAAU,CAAC,KAAKC,SAASS,KAAK,GAAGQ;UAAS;AACpE,eAAKI,eAAc;QACrB;MACF;IACF;AAuBAC;;;oBAAW,IAAIC,eAAe,CAACC,YAAAA;AAC7B,YAAM,EAAEC,YAAYC,UAAS,IAAKF,UAAU,CAAA,GAAIG,iBAAiB,CAAA,KAAM;QACrEF,YAAY;QACZC,WAAW;MACb;AACA,UACEnE,KAAKqE,IAAIH,aAAa,KAAK3C,UAAU,IAAIpC,mBACzCa,KAAKqE,IAAIF,YAAY,KAAK3C,SAAS,IAAIrC,iBACvC;AAEA,aAAKoC,aAAa2C;AAClB,aAAK1C,YAAY2C;AACjB,aAAKG,UAAS;MAChB;IACF,CAAA;AAEAC,uBAAmCC,UAAAA;AAEnCC,uBAAc,CAAC,EAAEC,QAAQC,OAAM,MAAc;AAC3C,WAAKtD,YAAYrB,KAAK2D,IAAI,GAAG,KAAKtC,YAAYqD,MAAAA;AAC9C,WAAKpD,WAAWtB,KAAK2D,IAAI,GAAG,KAAKrC,WAAWqD,MAAAA;AAC5C,UACE,KAAKtD,aAAa,KAAKM,gBACvB,KAAKN,YAAY,KAAKO,gBACtB,KAAKN,YAAY,KAAKQ,eACtB,KAAKR,WAAW,KAAKS,aACrB;MAEF,OAAO;AAOL,aAAKuC,UAAS;MAChB;IACF;;;;;EAtDQzC,QAAQ+C,GAAoB;AAClC,WAAO,KAAKrC,WAAWqC,CAAAA,KAAM,KAAK3D,cAAcD;EAClD;EAEQgB,QAAQ6C,GAAoB;AAClC,WAAO,KAAKrC,WAAWqC,CAAAA,KAAM,KAAK9D,WAAWC;EAC/C;EAEQ8D,QAAQF,GAAoBC,GAAoB;AACtD,WAAO,KAAKzD,MAAM,GAAGwD,CAAAA,GAAIlF,SAAAA,GAAYmF,CAAAA,EAAG;EAC1C;EA8CQhB,kBAAkB;AAExB,QAAIkB,WAAW;AACf,QAAIC,WAAW,KAAKnD,QAAQkD,QAAAA;AAE5B,WAAOC,WAAW,KAAK3D,WAAW;AAChC0D,kBAAY;AACZC,kBAAY,KAAKnD,QAAQkD,QAAAA,IAAY7F;IACvC;AAEA,SAAK+C,YAAY8C,WAAW3F;AAE5B,SAAKuC,eAAeqD,WAAW,KAAKnD,QAAQkD,QAAAA,IAAY7F;AACxD,SAAK0C,eAAeoD,WAAW9F;AAE/B,SAAKuC,iBACH;SAAIwD,MAAM7F,WAAAA;MAAc8F,OAAO,CAACC,KAAK/B,GAAG/C,OAAAA;AACtC8E,aAAO,KAAKtD,QAAQ,KAAKI,YAAY5B,EAAAA;AACrC,aAAO8E;IACT,GAAG,CAAA,IACHjG,OAAOE,cAAc;AAEvB,WAAO4F,WAAW,KAAKpD,eAAe,KAAKL,YAAY;AACrDwD,kBAAY;AACZC,kBAAY,KAAKnD,QAAQkD,QAAAA,IAAY7F;IACvC;AAEA,SAAKgD,YAAY6C,WAAW3F,cAAc;AAE1C,SAAKiD,kBAAkB;SAAI4C,MAAM,KAAK/C,YAAY,KAAKD,SAAS;MAC7DmD,IAAI,CAAChC,GAAG/C,OAAO,GAAG,KAAKwB,QAAQ,KAAKI,YAAY5B,EAAAA,CAAAA,IAAO,EACvDgF,KAAK,GAAA;EACV;EAEQvB,iBAAiB;AAEvB,QAAIwB,WAAW;AACf,QAAIC,UAAU,KAAKvD,QAAQsD,QAAAA;AAE3B,WAAOC,UAAU,KAAKjE,UAAU;AAC9BgE,kBAAY;AACZC,iBAAW,KAAKvD,QAAQsD,QAAAA,IAAYpG;IACtC;AAEA,SAAKiD,YAAYmD,WAAWjG;AAE5B,SAAKyC,cAAcyD,UAAU,KAAKvD,QAAQsD,QAAAA,IAAYpG;AACtD,SAAK6C,cAAcwD,UAAUrG;AAE7B,SAAKwC,gBACH;SAAIuD,MAAM5F,WAAAA;MAAc6F,OAAO,CAACC,KAAK/B,GAAG7C,OAAAA;AACtC4E,aAAO,KAAKnD,QAAQ,KAAKG,YAAY5B,EAAAA;AACrC,aAAO4E;IACT,GAAG,CAAA,IACHjG,OAAOG,cAAc;AAEvB,WAAOkG,UAAU,KAAKxD,cAAc,KAAKP,WAAW;AAClD8D,kBAAY;AACZC,iBAAW,KAAKvD,QAAQsD,QAAAA,IAAYpG;IACtC;AAEA,SAAKkD,YAAYkD,WAAWjG,cAAc;AAE1C,SAAKiD,eAAe;SAAI2C,MAAM,KAAK7C,YAAY,KAAKD,SAAS;MAC1DiD,IAAI,CAAChC,GAAG7C,OAAO,GAAG,KAAKyB,QAAQ,KAAKG,YAAY5B,EAAAA,CAAAA,IAAO,EACvD8E,KAAK,GAAA;EACV;EAEQf,YAAY;AAClB,SAAKT,gBAAe;AACpB,SAAKC,eAAc;EACrB;;;;EAMS0B,SAAS;AAChB,UAAMC,cAAc,KAAKvD,YAAY,KAAKD;AAC1C,UAAMyD,cAAc,KAAKtD,YAAY,KAAKD;AAE1C,UAAMwD,eAAe,KAAKhE,eAAe,KAAKN,YAAY,KAAKI;AAC/D,UAAMmE,cAAc,KAAK9D,cAAc,KAAKR,WAAW,KAAKI;AAE5D,WAAOmE;;;qBAGU,KAAKnD,iBAAiB;mBACxB,KAAKY,eAAe;qBAClB,KAAKE,iBAAiB;;;;;;;yCAOFmC,YAAAA,iCAA6C,KAAKtD,eAAe;;YAE9F;SAAI4C,MAAMQ,WAAAA;MAAcL,IAAI,CAAChC,GAAG/C,OAAAA;AAChC,YAAMuE,IAAI,KAAK3C,YAAY5B;AAC3B,aAAOwF;;uBAEIjB,IAAI,CAAA;mCACQ,KAAK/C,QAAQ+C,CAAAA,CAAAA,iBAAmB,KAAK7D,WAAWC,IAAI,kBAAkBX,KAAK,CAAA,IAAKA,KACrG,CAAA;;yBAEWD,UAAUC,EAAAA,CAAAA,IAAOV,gBAAgBiF,CAAAA,CAAAA;iBACzC,KAAKzD,QAAQyD,CAAAA,GAAIkB,cAAc,KAAK7E,cAAc6E,eACrDD,kEAAkE,cAAcjB,CAAAA,EAAG;;wBAEzE;;IAEd,CAAA,CAAA;;;;;8FAKoFgB,WAAAA;YAClF;SAAIX,MAAMS,WAAAA;MAAcN,IAAI,CAAChC,GAAG7C,OAAAA;AAChC,YAAMsE,IAAI,KAAK1C,YAAY5B;AAC3B,aAAOsF;;uBAEIhB,IAAI,CAAA;kCACO,KAAK7C,QAAQ6C,CAAAA,CAAAA,eAAiBtE,KAAK,CAAA,IAAKA,KAAK,CAAA;;yBAEtDD,UAAUC,EAAAA,CAAAA,IAAOL,gBAAgB2E,CAAAA,CAAAA;iBACzC,KAAK3D,KAAK2D,CAAAA,GAAIiB,cAAc,KAAK/E,WAAW+E,eAC/CD,kEAAkE,cAAchB,CAAAA,EAAG;;wBAEzE;;IAEd,CAAA,CAAA;;;2DAGiD,KAAKJ,WAAW,KAAKsB,IAAI,KAAKxB,WAAW,CAAA;;;;yCAI3DoB,YAAAA,MAAkBC,WAAAA,+BAA0C,KACxFvD,eAAe,uBAAuB,KAAKC,YAAY;;YAExD;SAAI2C,MAAMQ,WAAAA;MAAcL,IAAI,CAAChC,GAAG/C,OAAAA;AAChC,aAAO;WAAI4E,MAAMS,WAAAA;QAAcN,IAAI,CAAChC,IAAG7C,OAAAA;AACrC,cAAMqE,IAAIvE,KAAK,KAAK4B;AACpB,cAAM4C,IAAItE,KAAK,KAAK4B;AACpB,cAAM6D,OAAO,KAAKlB,QAAQF,GAAGC,CAAAA;AAC7B,eAAOgB;;yBAEIjB,IAAI,KAAKC,IAAI,CAAA;gCACNA,CAAAA;gCACAD,CAAAA;;qCAEKvE,KAAK,CAAA,aAAcE,KAAK,CAAA;;kBAE3CyF,MAAMC,KAAAA;;MAEZ,CAAA;IACF,CAAA,CAAA;;;;;;;;;;;;EAYR;EAESC,eAAe;AACtB,SAAKnC,SAASoC,QAAQ,KAAK5B,YAAY0B,KAAK;AAC5C,SAAK1D,WAAW6D,OAAOnC,QAAQ,KAAK9C,OAAO,EAAE+D,OAAO,CAACC,KAA6B,CAACkB,OAAOC,OAAAA,MAAQ;AAChG,UAAIA,SAAStF,MAAM;AACjBmE,YAAIkB,KAAAA,IAASC,QAAQtF;MACvB;AACA,aAAOmE;IACT,GAAG,CAAC,CAAA;AACJ,SAAK3C,WAAW4D,OAAOnC,QAAQ,KAAK/C,IAAI,EAAEgE,OAAO,CAACC,KAA6B,CAACoB,OAAOC,OAAAA,MAAQ;AAC7F,UAAIA,SAASxF,MAAM;AACjBmE,YAAIoB,KAAAA,IAASC,QAAQxF;MACvB;AACA,aAAOmE;IACT,GAAG,CAAC,CAAA;EACN;EAESsB,uBAAuB;AAC9B,UAAMA,qBAAAA;AAGN,QAAI,KAAKlC,YAAY0B,OAAO;AAC1B,WAAKlC,SAAS2C,UAAU,KAAKnC,YAAY0B,KAAK;IAChD;EACF;EAESU,mBAAmB;AAC1B,WAAO;EACT;AACF;;EA3YGC,SAAS;IAAEC,MAAMT;EAAO,CAAA;GADdvF,OAAAA,WAAAA,cAAAA,MAAAA;;EAIV+F,SAAS;IAAEC,MAAMT;EAAO,CAAA;GAJdvF,OAAAA,WAAAA,iBAAAA,MAAAA;;EAOV+F,SAAS;IAAEC,MAAMT;EAAO,CAAA;GAPdvF,OAAAA,WAAAA,QAAAA,MAAAA;;EAUV+F,SAAS;IAAEC,MAAMT;EAAO,CAAA;GAVdvF,OAAAA,WAAAA,WAAAA,MAAAA;;EAaV+F,SAAS;IAAEC,MAAMT;EAAO,CAAA;GAbdvF,OAAAA,WAAAA,SAAAA,MAAAA;;EAoBViG,MAAAA;GApBUjG,OAAAA,WAAAA,aAAAA,MAAAA;;EAuBViG,MAAAA;GAvBUjG,OAAAA,WAAAA,YAAAA,MAAAA;;EA8BViG,MAAAA;GA9BUjG,OAAAA,WAAAA,cAAAA,MAAAA;;EAiCViG,MAAAA;GAjCUjG,OAAAA,WAAAA,aAAAA,MAAAA;;EAwCViG,MAAAA;GAxCUjG,OAAAA,WAAAA,kBAAAA,MAAAA;;EA2CViG,MAAAA;GA3CUjG,OAAAA,WAAAA,iBAAAA,MAAAA;;EAkDViG,MAAAA;GAlDUjG,OAAAA,WAAAA,gBAAAA,MAAAA;;EAqDViG,MAAAA;GArDUjG,OAAAA,WAAAA,gBAAAA,MAAAA;;EAwDViG,MAAAA;GAxDUjG,OAAAA,WAAAA,eAAAA,MAAAA;;EA2DViG,MAAAA;GA3DUjG,OAAAA,WAAAA,eAAAA,MAAAA;;EAmEViG,MAAAA;GAnEUjG,OAAAA,WAAAA,aAAAA,MAAAA;;EAsEViG,MAAAA;GAtEUjG,OAAAA,WAAAA,aAAAA,MAAAA;;EAyEViG,MAAAA;GAzEUjG,OAAAA,WAAAA,aAAAA,MAAAA;;EA4EViG,MAAAA;GA5EUjG,OAAAA,WAAAA,aAAAA,MAAAA;;EAkFViG,MAAAA;GAlFUjG,OAAAA,WAAAA,mBAAAA,MAAAA;;EAqFViG,MAAAA;GArFUjG,OAAAA,WAAAA,gBAAAA,MAAAA;;EA4FViG,MAAAA;GA5FUjG,OAAAA,WAAAA,YAAAA,MAAAA;;EA+FViG,MAAAA;GA/FUjG,OAAAA,WAAAA,YAAAA,MAAAA;;EAkGViG,MAAAA;GAlGUjG,OAAAA,WAAAA,YAAAA,MAAAA;;EA6JViG,MAAAA;GA7JUjG,OAAAA,WAAAA,YAAAA,MAAAA;AAAAA,SAAAA,aAAAA;EADZkG,cAAc,SAAA;GACFlG,MAAAA;",
|
|
6
|
+
"names": ["LitElement", "html", "customElement", "state", "property", "ref", "createRef", "gap", "resizeTolerance", "overscanCol", "overscanRow", "sizeColMin", "sizeColMax", "sizeRowMin", "sizeRowMax", "separator", "colToA1Notation", "col", "String", "fromCharCode", "charCodeAt", "Math", "floor", "rowToA1Notation", "row", "localChId", "c0", "localRhId", "r0", "getPage", "axis", "event", "pageX", "pageY", "DxGrid", "LitElement", "rowDefault", "size", "columnDefault", "rows", "columns", "cells", "posInline", "posBlock", "sizeInline", "sizeBlock", "overscanInline", "overscanBlock", "binInlineMin", "binInlineMax", "colSize", "binBlockMin", "binBlockMax", "rowSize", "visColMin", "visColMax", "visRowMin", "visRowMax", "templateColumns", "templateRows", "colSizes", "rowSizes", "resizing", "handlePointerDown", "actionEl", "target", "closest", "action", "getAttribute", "startsWith", "resize", "index", "split", "_", "page", "handlePointerUp", "_event", "handlePointerMove", "delta", "nextSize", "max", "min", "updateVisInline", "updateVisBlock", "observer", "ResizeObserver", "entries", "inlineSize", "blockSize", "contentBoxSize", "abs", "updateVis", "viewportRef", "createRef", "handleWheel", "deltaX", "deltaY", "c", "r", "getCell", "colIndex", "pxInline", "Array", "reduce", "acc", "map", "join", "rowIndex", "pxBlock", "render", "visibleCols", "visibleRows", "offsetInline", "offsetBlock", "html", "resizeable", "ref", "cell", "value", "firstUpdated", "observe", "Object", "colId", "colMeta", "rowId", "rowMeta", "disconnectedCallback", "unobserve", "createRenderRoot", "property", "type", "state", "customElement"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"inputs":{"packages/ui/lit-grid/src/dx-grid.ts":{"bytes":50156,"imports":[{"path":"lit","kind":"import-statement","external":true},{"path":"lit/decorators.js","kind":"import-statement","external":true},{"path":"lit/directives/ref.js","kind":"import-statement","external":true}],"format":"esm"},"packages/ui/lit-grid/src/index.ts":{"bytes":560,"imports":[{"path":"packages/ui/lit-grid/src/dx-grid.ts","kind":"import-statement","original":"./dx-grid"}],"format":"esm"}},"outputs":{"packages/ui/lit-grid/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":23985},"packages/ui/lit-grid/dist/lib/browser/index.mjs":{"imports":[{"path":"lit","kind":"import-statement","external":true},{"path":"lit/decorators.js","kind":"import-statement","external":true},{"path":"lit/directives/ref.js","kind":"import-statement","external":true}],"exports":["DxGrid"],"entryPoint":"packages/ui/lit-grid/src/index.ts","inputs":{"packages/ui/lit-grid/src/dx-grid.ts":{"bytesInOutput":13730},"packages/ui/lit-grid/src/index.ts":{"bytesInOutput":0}},"bytes":13825}}}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { type Ref } from 'lit/directives/ref.js';
|
|
3
|
+
export type CellValue = {
|
|
4
|
+
/**
|
|
5
|
+
* The content value
|
|
6
|
+
*/
|
|
7
|
+
value: string;
|
|
8
|
+
/**
|
|
9
|
+
* If this is a merged cell, the bottomright-most of the range in numeric notation, otherwise undefined.
|
|
10
|
+
*/
|
|
11
|
+
end?: string;
|
|
12
|
+
/**
|
|
13
|
+
* CSS inline styles to apply to the gridcell element
|
|
14
|
+
*/
|
|
15
|
+
style?: string;
|
|
16
|
+
};
|
|
17
|
+
type AxisMeta = {
|
|
18
|
+
size: number;
|
|
19
|
+
description?: string;
|
|
20
|
+
resizeable?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type DxGridProps = Pick<DxGrid, 'cells' | 'rows' | 'columns' | 'rowDefault' | 'columnDefault'>;
|
|
23
|
+
export declare class DxGrid extends LitElement {
|
|
24
|
+
rowDefault: AxisMeta;
|
|
25
|
+
columnDefault: AxisMeta;
|
|
26
|
+
rows: Record<string, AxisMeta>;
|
|
27
|
+
columns: Record<string, AxisMeta>;
|
|
28
|
+
cells: Record<string, CellValue>;
|
|
29
|
+
posInline: number;
|
|
30
|
+
posBlock: number;
|
|
31
|
+
sizeInline: number;
|
|
32
|
+
sizeBlock: number;
|
|
33
|
+
overscanInline: number;
|
|
34
|
+
overscanBlock: number;
|
|
35
|
+
binInlineMin: number;
|
|
36
|
+
binInlineMax: number;
|
|
37
|
+
binBlockMin: number;
|
|
38
|
+
binBlockMax: number;
|
|
39
|
+
visColMin: number;
|
|
40
|
+
visColMax: number;
|
|
41
|
+
visRowMin: number;
|
|
42
|
+
visRowMax: number;
|
|
43
|
+
templateColumns: string;
|
|
44
|
+
templateRows: string;
|
|
45
|
+
colSizes: Record<string, number>;
|
|
46
|
+
rowSizes: Record<string, number>;
|
|
47
|
+
resizing: null | {
|
|
48
|
+
axis: 'col' | 'row';
|
|
49
|
+
page: number;
|
|
50
|
+
size: number;
|
|
51
|
+
index: string;
|
|
52
|
+
};
|
|
53
|
+
handlePointerDown: (event: PointerEvent) => void;
|
|
54
|
+
handlePointerUp: (_event: PointerEvent) => void;
|
|
55
|
+
handlePointerMove: (event: PointerEvent) => void;
|
|
56
|
+
private colSize;
|
|
57
|
+
private rowSize;
|
|
58
|
+
private getCell;
|
|
59
|
+
observer: ResizeObserver;
|
|
60
|
+
viewportRef: Ref<HTMLDivElement>;
|
|
61
|
+
handleWheel: ({ deltaX, deltaY }: WheelEvent) => void;
|
|
62
|
+
private updateVisInline;
|
|
63
|
+
private updateVisBlock;
|
|
64
|
+
private updateVis;
|
|
65
|
+
render(): import("lit").TemplateResult<1>;
|
|
66
|
+
firstUpdated(): void;
|
|
67
|
+
disconnectedCallback(): void;
|
|
68
|
+
createRenderRoot(): this;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=dx-grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dx-grid.d.ts","sourceRoot":"","sources":["../../../src/dx-grid.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAQ,MAAM,KAAK,CAAC;AAEvC,OAAO,EAAkB,KAAK,GAAG,EAAE,MAAM,uBAAuB,CAAC;AA+CjE,MAAM,MAAM,SAAS,GAAG;IACtB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,eAAe,CAAC,CAAC;AAOtG,qBACa,MAAO,SAAQ,UAAU;IAEpC,UAAU,EAAE,QAAQ,CAAgB;IAGpC,aAAa,EAAE,QAAQ,CAAiB;IAGxC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAM;IAGpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAM;IAGvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAM;IAOtC,SAAS,SAAK;IAGd,QAAQ,SAAK;IAOb,UAAU,SAAK;IAGf,SAAS,SAAK;IAOd,cAAc,SAAK;IAGnB,aAAa,SAAK;IAOlB,YAAY,SAAK;IAGjB,YAAY,SAAmB;IAG/B,WAAW,SAAK;IAGhB,WAAW,SAAmB;IAQ9B,SAAS,SAAK;IAGd,SAAS,SAAK;IAGd,SAAS,SAAK;IAGd,SAAS,SAAK;IAMd,eAAe,SAA0B;IAGzC,YAAY,SAA0B;IAOtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAGtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAGtC,QAAQ,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAQ;IAE3F,iBAAiB,UAAW,YAAY,UAetC;IAEF,eAAe,WAAY,YAAY,UAErC;IAEF,iBAAiB,UAAW,YAAY,UAatC;IAMF,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,OAAO;IASf,QAAQ,iBAcL;IAEH,WAAW,EAAE,GAAG,CAAC,cAAc,CAAC,CAAe;IAE/C,WAAW,uBAAwB,UAAU,UAmB3C;IAEF,OAAO,CAAC,eAAe;IAkCvB,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,SAAS;IASR,MAAM;IA8FN,YAAY;IAgBZ,oBAAoB;IASpB,gBAAgB;CAG1B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import './dx-grid.ts';
|
|
2
|
+
import './dx-grid.pcss';
|
|
3
|
+
import { type DxGridProps } from './dx-grid';
|
|
4
|
+
declare const _default: {
|
|
5
|
+
title: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const Basic: {
|
|
9
|
+
(props: DxGridProps): import("lit").TemplateResult<1>;
|
|
10
|
+
args: {
|
|
11
|
+
cells: string;
|
|
12
|
+
columnDefault: string;
|
|
13
|
+
rowDefault: string;
|
|
14
|
+
columns: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=dx-grid.lit-stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dx-grid.lit-stories.d.ts","sourceRoot":"","sources":["../../../src/dx-grid.lit-stories.ts"],"names":[],"mappings":"AAIA,OAAO,cAAc,CAAC;AACtB,OAAO,gBAAgB,CAAC;AAIxB,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;;;;AAE7C,wBAEE;AAEF,eAAO,MAAM,KAAK;YAAW,WAAW;;;;;;;CAOvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/lit-grid",
|
|
3
|
+
"version": "0.6.10-main.3cfcc89",
|
|
4
|
+
"description": "A grid webcomponent using Lit",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "DXOS.org",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"browser": "./dist/lib/browser/index.mjs",
|
|
12
|
+
"types": "./dist/types/src/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./dx-grid.pcss": "./src/dx-grid.pcss"
|
|
15
|
+
},
|
|
16
|
+
"main": "src/index.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"lit": "^3.2.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import './dx-grid.ts';
|
|
6
|
+
import './dx-grid.pcss';
|
|
7
|
+
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
|
|
10
|
+
import { type DxGridProps } from './dx-grid';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: 'dx-grid',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const Basic = (props: DxGridProps) => {
|
|
17
|
+
return html`<dx-grid
|
|
18
|
+
cells=${props.cells ?? nothing}
|
|
19
|
+
columnDefault=${props.columnDefault ?? nothing}
|
|
20
|
+
rowDefault=${props.rowDefault ?? nothing}
|
|
21
|
+
columns=${props.columns ?? nothing}
|
|
22
|
+
></dx-grid>`;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
Basic.args = {
|
|
26
|
+
cells: JSON.stringify({
|
|
27
|
+
'1,1': {
|
|
28
|
+
// end: '8,1',
|
|
29
|
+
value: 'Weekly sales report',
|
|
30
|
+
},
|
|
31
|
+
} satisfies DxGridProps['cells']),
|
|
32
|
+
columnDefault: JSON.stringify({
|
|
33
|
+
size: 180,
|
|
34
|
+
resizeable: true,
|
|
35
|
+
} satisfies DxGridProps['columnDefault']),
|
|
36
|
+
rowDefault: JSON.stringify({
|
|
37
|
+
size: 32,
|
|
38
|
+
resizeable: true,
|
|
39
|
+
} satisfies DxGridProps['rowDefault']),
|
|
40
|
+
columns: JSON.stringify({
|
|
41
|
+
0: { size: 200 },
|
|
42
|
+
1: { size: 210 },
|
|
43
|
+
2: { size: 230 },
|
|
44
|
+
3: { size: 250 },
|
|
45
|
+
4: { size: 270 },
|
|
46
|
+
} satisfies DxGridProps['columns']),
|
|
47
|
+
};
|
package/src/dx-grid.pcss
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
dx-grid {
|
|
2
|
+
display: contents;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.sr-only {
|
|
6
|
+
position: absolute;
|
|
7
|
+
width: 1px;
|
|
8
|
+
height: 1px;
|
|
9
|
+
padding: 0;
|
|
10
|
+
margin: -1px;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
clip: rect(0, 0, 0, 0);
|
|
13
|
+
white-space: nowrap;
|
|
14
|
+
border-width: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.dx-grid {
|
|
18
|
+
position: fixed;
|
|
19
|
+
inset: 0;
|
|
20
|
+
display: grid;
|
|
21
|
+
grid-template-columns: min-content 1fr min-content;
|
|
22
|
+
grid-template-rows: min-content 1fr min-content;
|
|
23
|
+
font-variant-numeric: tabular-nums;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.dx-grid__scrollbar__thumb {
|
|
27
|
+
height: 1rem;
|
|
28
|
+
width: 1rem;
|
|
29
|
+
background: var(--dx-grid-thumb, var(--dx-separator));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.dx-grid__corner,
|
|
33
|
+
.dx-grid__scrollbar {
|
|
34
|
+
background: var(--dx-grid-corner, var(--dx-hoverSurface));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.dx-grid__columnheader__content,
|
|
38
|
+
.dx-grid__rowheader__content,
|
|
39
|
+
.dx-grid__content {
|
|
40
|
+
display: grid;
|
|
41
|
+
gap: 1px;
|
|
42
|
+
background: var(--dx-separator);
|
|
43
|
+
inline-size: min-content;
|
|
44
|
+
block-size: min-content;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.dx-grid__columnheader,
|
|
48
|
+
.dx-grid__rowheader,
|
|
49
|
+
.dx-grid__viewport {
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.dx-grid__columnheader__content {
|
|
54
|
+
border-block-end: 2px solid var(--dx-separator);
|
|
55
|
+
grid-template-columns: repeat(99, 64px);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.dx-grid__rowheader__content {
|
|
59
|
+
border-inline-end: 2px solid var(--dx-separator);
|
|
60
|
+
grid-template-columns: min-content;
|
|
61
|
+
text-align: end;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.dx-grid {
|
|
65
|
+
[role='gridcell'], [role='columnheader'], [role='rowheader'] {
|
|
66
|
+
background: var(--dx-base);
|
|
67
|
+
padding: 2px;
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
&[inert] {
|
|
70
|
+
visibility: hidden;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.dx-grid__columnheader__content,
|
|
76
|
+
.dx-grid__rowheader__content {
|
|
77
|
+
& > [role='columnheader'], &> [role='rowheader'] {
|
|
78
|
+
position: relative;
|
|
79
|
+
& > button.dx-grid__resize-handle {
|
|
80
|
+
position: absolute;
|
|
81
|
+
background: transparent;
|
|
82
|
+
&:hover {
|
|
83
|
+
background: var(--dx-hoverSurface)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.dx-grid__columnheader__content > [role='columnheader'] > button.dx-grid__resize-handle {
|
|
90
|
+
inset-block: 0;
|
|
91
|
+
inset-inline-end: 0;
|
|
92
|
+
inline-size: .5rem;
|
|
93
|
+
cursor: col-resize;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.dx-grid__rowheader__content > [role='rowheader'] > button.dx-grid__resize-handle {
|
|
97
|
+
inset-inline: 0;
|
|
98
|
+
inset-block-end: 0;
|
|
99
|
+
block-size: .5rem;
|
|
100
|
+
cursor: row-resize;
|
|
101
|
+
}
|
package/src/dx-grid.ts
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { LitElement, html } from 'lit';
|
|
6
|
+
import { customElement, state, property } from 'lit/decorators.js';
|
|
7
|
+
import { ref, createRef, type Ref } from 'lit/directives/ref.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The size in pixels of the gap between cells
|
|
11
|
+
*/
|
|
12
|
+
const gap = 1;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* This should be about the width of the `1` numeral so resize is triggered as the row header column’s intrinsic size
|
|
16
|
+
* changes when scrolling vertically.
|
|
17
|
+
*/
|
|
18
|
+
const resizeTolerance = 8;
|
|
19
|
+
|
|
20
|
+
//
|
|
21
|
+
// `overscan` is the number of columns or rows to render outside of the viewport
|
|
22
|
+
//
|
|
23
|
+
const overscanCol = 1;
|
|
24
|
+
const overscanRow = 1;
|
|
25
|
+
|
|
26
|
+
//
|
|
27
|
+
// `size`, when suffixed with ‘row’ or ‘col’, are limits on size applied when resizing
|
|
28
|
+
//
|
|
29
|
+
const sizeColMin = 32;
|
|
30
|
+
const sizeColMax = 1024;
|
|
31
|
+
const sizeRowMin = 16;
|
|
32
|
+
const sizeRowMax = 1024;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Separator for serializing cell position vectors
|
|
36
|
+
*/
|
|
37
|
+
const separator = ',';
|
|
38
|
+
|
|
39
|
+
//
|
|
40
|
+
// A1 notation is the fallback for numbering columns and rows.
|
|
41
|
+
//
|
|
42
|
+
|
|
43
|
+
const colToA1Notation = (col: number): string => {
|
|
44
|
+
return (
|
|
45
|
+
(col >= 26 ? String.fromCharCode('A'.charCodeAt(0) + Math.floor(col / 26) - 1) : '') +
|
|
46
|
+
String.fromCharCode('A'.charCodeAt(0) + (col % 26))
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const rowToA1Notation = (row: number): string => {
|
|
51
|
+
return `${row + 1}`;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type CellValue = {
|
|
55
|
+
/**
|
|
56
|
+
* The content value
|
|
57
|
+
*/
|
|
58
|
+
value: string;
|
|
59
|
+
/**
|
|
60
|
+
* If this is a merged cell, the bottomright-most of the range in numeric notation, otherwise undefined.
|
|
61
|
+
*/
|
|
62
|
+
end?: string;
|
|
63
|
+
/**
|
|
64
|
+
* CSS inline styles to apply to the gridcell element
|
|
65
|
+
*/
|
|
66
|
+
style?: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type AxisMeta = {
|
|
70
|
+
size: number;
|
|
71
|
+
description?: string;
|
|
72
|
+
resizeable?: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type DxGridProps = Pick<DxGrid, 'cells' | 'rows' | 'columns' | 'rowDefault' | 'columnDefault'>;
|
|
76
|
+
|
|
77
|
+
const localChId = (c0: number) => `ch--${c0}`;
|
|
78
|
+
const localRhId = (r0: number) => `rh--${r0}`;
|
|
79
|
+
|
|
80
|
+
const getPage = (axis: string, event: PointerEvent) => (axis === 'col' ? event.pageX : event.pageY);
|
|
81
|
+
|
|
82
|
+
@customElement('dx-grid')
|
|
83
|
+
export class DxGrid extends LitElement {
|
|
84
|
+
@property({ type: Object })
|
|
85
|
+
rowDefault: AxisMeta = { size: 32 };
|
|
86
|
+
|
|
87
|
+
@property({ type: Object })
|
|
88
|
+
columnDefault: AxisMeta = { size: 180 };
|
|
89
|
+
|
|
90
|
+
@property({ type: Object })
|
|
91
|
+
rows: Record<string, AxisMeta> = {};
|
|
92
|
+
|
|
93
|
+
@property({ type: Object })
|
|
94
|
+
columns: Record<string, AxisMeta> = {};
|
|
95
|
+
|
|
96
|
+
@property({ type: Object })
|
|
97
|
+
cells: Record<string, CellValue> = {};
|
|
98
|
+
|
|
99
|
+
//
|
|
100
|
+
// `pos`, short for ‘position’, is the position in pixels of the viewport from the origin.
|
|
101
|
+
//
|
|
102
|
+
|
|
103
|
+
@state()
|
|
104
|
+
posInline = 0;
|
|
105
|
+
|
|
106
|
+
@state()
|
|
107
|
+
posBlock = 0;
|
|
108
|
+
|
|
109
|
+
//
|
|
110
|
+
// `size` (when not suffixed with ‘row’ or ‘col’, see above) is the size in pixels of the viewport.
|
|
111
|
+
//
|
|
112
|
+
|
|
113
|
+
@state()
|
|
114
|
+
sizeInline = 0;
|
|
115
|
+
|
|
116
|
+
@state()
|
|
117
|
+
sizeBlock = 0;
|
|
118
|
+
|
|
119
|
+
//
|
|
120
|
+
// `overscan` is the amount in pixels to offset the grid content due to the number of overscanned columns or rows.
|
|
121
|
+
//
|
|
122
|
+
|
|
123
|
+
@state()
|
|
124
|
+
overscanInline = 0;
|
|
125
|
+
|
|
126
|
+
@state()
|
|
127
|
+
overscanBlock = 0;
|
|
128
|
+
|
|
129
|
+
//
|
|
130
|
+
// `bin`, not short for anything, is the range in pixels within which virtualization does not need to reassess.
|
|
131
|
+
//
|
|
132
|
+
|
|
133
|
+
@state()
|
|
134
|
+
binInlineMin = 0;
|
|
135
|
+
|
|
136
|
+
@state()
|
|
137
|
+
binInlineMax = this.colSize(0);
|
|
138
|
+
|
|
139
|
+
@state()
|
|
140
|
+
binBlockMin = 0;
|
|
141
|
+
|
|
142
|
+
@state()
|
|
143
|
+
binBlockMax = this.rowSize(0);
|
|
144
|
+
|
|
145
|
+
//
|
|
146
|
+
// `vis`, short for ‘visible’, is the range in numeric index of the columns or rows which should be rendered within
|
|
147
|
+
// the viewport. These start with naïve values that are updated before first contentful render.
|
|
148
|
+
//
|
|
149
|
+
|
|
150
|
+
@state()
|
|
151
|
+
visColMin = 0;
|
|
152
|
+
|
|
153
|
+
@state()
|
|
154
|
+
visColMax = 1;
|
|
155
|
+
|
|
156
|
+
@state()
|
|
157
|
+
visRowMin = 0;
|
|
158
|
+
|
|
159
|
+
@state()
|
|
160
|
+
visRowMax = 1;
|
|
161
|
+
|
|
162
|
+
//
|
|
163
|
+
// `template` is the rendered value of `grid-{axis}-template`.
|
|
164
|
+
//
|
|
165
|
+
@state()
|
|
166
|
+
templateColumns = `${this.colSize(0)}px`;
|
|
167
|
+
|
|
168
|
+
@state()
|
|
169
|
+
templateRows = `${this.rowSize(0)}px`;
|
|
170
|
+
|
|
171
|
+
//
|
|
172
|
+
// Resize state and handlers
|
|
173
|
+
//
|
|
174
|
+
|
|
175
|
+
@state()
|
|
176
|
+
colSizes: Record<string, number> = {};
|
|
177
|
+
|
|
178
|
+
@state()
|
|
179
|
+
rowSizes: Record<string, number> = {};
|
|
180
|
+
|
|
181
|
+
@state()
|
|
182
|
+
resizing: null | { axis: 'col' | 'row'; page: number; size: number; index: string } = null;
|
|
183
|
+
|
|
184
|
+
handlePointerDown = (event: PointerEvent) => {
|
|
185
|
+
const actionEl = (event.target as HTMLElement)?.closest('[data-dx-grid-action]');
|
|
186
|
+
const action = actionEl?.getAttribute('data-dx-grid-action');
|
|
187
|
+
if (action) {
|
|
188
|
+
if (action.startsWith('resize')) {
|
|
189
|
+
const [resize, index] = action.split(',');
|
|
190
|
+
const [_, axis] = resize.split('-');
|
|
191
|
+
this.resizing = {
|
|
192
|
+
axis: axis as 'col' | 'row',
|
|
193
|
+
size: axis === 'col' ? this.colSize(index) : this.rowSize(index),
|
|
194
|
+
page: getPage(axis, event),
|
|
195
|
+
index,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
handlePointerUp = (_event: PointerEvent) => {
|
|
202
|
+
this.resizing = null;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
handlePointerMove = (event: PointerEvent) => {
|
|
206
|
+
if (this.resizing) {
|
|
207
|
+
const delta = getPage(this.resizing.axis, event) - this.resizing.page;
|
|
208
|
+
if (this.resizing.axis === 'col') {
|
|
209
|
+
const nextSize = Math.max(sizeColMin, Math.min(sizeColMax, this.resizing.size + delta));
|
|
210
|
+
this.colSizes = { ...this.colSizes, [this.resizing.index]: nextSize };
|
|
211
|
+
this.updateVisInline();
|
|
212
|
+
} else {
|
|
213
|
+
const nextSize = Math.max(sizeRowMin, Math.min(sizeRowMax, this.resizing.size + delta));
|
|
214
|
+
this.rowSizes = { ...this.rowSizes, [this.resizing.index]: nextSize };
|
|
215
|
+
this.updateVisBlock();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
//
|
|
221
|
+
// Accessors
|
|
222
|
+
//
|
|
223
|
+
|
|
224
|
+
private colSize(c: number | string) {
|
|
225
|
+
return this.colSizes?.[c] ?? this.columnDefault.size;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private rowSize(r: number | string) {
|
|
229
|
+
return this.rowSizes?.[r] ?? this.rowDefault.size;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private getCell(c: number | string, r: number | string) {
|
|
233
|
+
return this.cells[`${c}${separator}${r}`];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
//
|
|
237
|
+
// Resize & reposition handlers, observer, ref
|
|
238
|
+
//
|
|
239
|
+
|
|
240
|
+
@state()
|
|
241
|
+
observer = new ResizeObserver((entries) => {
|
|
242
|
+
const { inlineSize, blockSize } = entries?.[0]?.contentBoxSize?.[0] ?? {
|
|
243
|
+
inlineSize: 0,
|
|
244
|
+
blockSize: 0,
|
|
245
|
+
};
|
|
246
|
+
if (
|
|
247
|
+
Math.abs(inlineSize - this.sizeInline) > resizeTolerance ||
|
|
248
|
+
Math.abs(blockSize - this.sizeBlock) > resizeTolerance
|
|
249
|
+
) {
|
|
250
|
+
// console.info('[updating bounds]', 'resize', [inlineSize - this.sizeInline, blockSize - this.sizeBlock]);
|
|
251
|
+
this.sizeInline = inlineSize;
|
|
252
|
+
this.sizeBlock = blockSize;
|
|
253
|
+
this.updateVis();
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
viewportRef: Ref<HTMLDivElement> = createRef();
|
|
258
|
+
|
|
259
|
+
handleWheel = ({ deltaX, deltaY }: WheelEvent) => {
|
|
260
|
+
this.posInline = Math.max(0, this.posInline + deltaX);
|
|
261
|
+
this.posBlock = Math.max(0, this.posBlock + deltaY);
|
|
262
|
+
if (
|
|
263
|
+
this.posInline >= this.binInlineMin &&
|
|
264
|
+
this.posInline < this.binInlineMax &&
|
|
265
|
+
this.posBlock >= this.binBlockMin &&
|
|
266
|
+
this.posBlock < this.binBlockMax
|
|
267
|
+
) {
|
|
268
|
+
// do nothing
|
|
269
|
+
} else {
|
|
270
|
+
// console.info(
|
|
271
|
+
// '[updating bounds]',
|
|
272
|
+
// 'wheel',
|
|
273
|
+
// [this.binInlineMin, this.posInline, this.binInlineMax],
|
|
274
|
+
// [this.binBlockMin, this.posBlock, this.binBlockMax],
|
|
275
|
+
// );
|
|
276
|
+
this.updateVis();
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
private updateVisInline() {
|
|
281
|
+
// todo: avoid starting from zero
|
|
282
|
+
let colIndex = 0;
|
|
283
|
+
let pxInline = this.colSize(colIndex);
|
|
284
|
+
|
|
285
|
+
while (pxInline < this.posInline) {
|
|
286
|
+
colIndex += 1;
|
|
287
|
+
pxInline += this.colSize(colIndex) + gap;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this.visColMin = colIndex - overscanCol;
|
|
291
|
+
|
|
292
|
+
this.binInlineMin = pxInline - this.colSize(colIndex) - gap;
|
|
293
|
+
this.binInlineMax = pxInline + gap;
|
|
294
|
+
|
|
295
|
+
this.overscanInline =
|
|
296
|
+
[...Array(overscanCol)].reduce((acc, _, c0) => {
|
|
297
|
+
acc += this.colSize(this.visColMin + c0);
|
|
298
|
+
return acc;
|
|
299
|
+
}, 0) +
|
|
300
|
+
gap * (overscanCol - 1);
|
|
301
|
+
|
|
302
|
+
while (pxInline < this.binInlineMax + this.sizeInline) {
|
|
303
|
+
colIndex += 1;
|
|
304
|
+
pxInline += this.colSize(colIndex) + gap;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this.visColMax = colIndex + overscanCol + 1;
|
|
308
|
+
|
|
309
|
+
this.templateColumns = [...Array(this.visColMax - this.visColMin)]
|
|
310
|
+
.map((_, c0) => `${this.colSize(this.visColMin + c0)}px`)
|
|
311
|
+
.join(' ');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private updateVisBlock() {
|
|
315
|
+
// todo: avoid starting from zero
|
|
316
|
+
let rowIndex = 0;
|
|
317
|
+
let pxBlock = this.rowSize(rowIndex);
|
|
318
|
+
|
|
319
|
+
while (pxBlock < this.posBlock) {
|
|
320
|
+
rowIndex += 1;
|
|
321
|
+
pxBlock += this.rowSize(rowIndex) + gap;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.visRowMin = rowIndex - overscanRow;
|
|
325
|
+
|
|
326
|
+
this.binBlockMin = pxBlock - this.rowSize(rowIndex) - gap;
|
|
327
|
+
this.binBlockMax = pxBlock + gap;
|
|
328
|
+
|
|
329
|
+
this.overscanBlock =
|
|
330
|
+
[...Array(overscanRow)].reduce((acc, _, r0) => {
|
|
331
|
+
acc += this.rowSize(this.visRowMin + r0);
|
|
332
|
+
return acc;
|
|
333
|
+
}, 0) +
|
|
334
|
+
gap * (overscanRow - 1);
|
|
335
|
+
|
|
336
|
+
while (pxBlock < this.binBlockMax + this.sizeBlock) {
|
|
337
|
+
rowIndex += 1;
|
|
338
|
+
pxBlock += this.rowSize(rowIndex) + gap;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this.visRowMax = rowIndex + overscanRow + 1;
|
|
342
|
+
|
|
343
|
+
this.templateRows = [...Array(this.visRowMax - this.visRowMin)]
|
|
344
|
+
.map((_, r0) => `${this.rowSize(this.visRowMin + r0)}px`)
|
|
345
|
+
.join(' ');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private updateVis() {
|
|
349
|
+
this.updateVisInline();
|
|
350
|
+
this.updateVisBlock();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
//
|
|
354
|
+
// Render and other lifecycle methods
|
|
355
|
+
//
|
|
356
|
+
|
|
357
|
+
override render() {
|
|
358
|
+
const visibleCols = this.visColMax - this.visColMin;
|
|
359
|
+
const visibleRows = this.visRowMax - this.visRowMin;
|
|
360
|
+
// TODO NEXT -> ensure offset is using the right components
|
|
361
|
+
const offsetInline = this.binInlineMin - this.posInline - this.overscanInline;
|
|
362
|
+
const offsetBlock = this.binBlockMin - this.posBlock - this.overscanBlock;
|
|
363
|
+
|
|
364
|
+
return html`<div
|
|
365
|
+
role="none"
|
|
366
|
+
class="dx-grid"
|
|
367
|
+
@pointerdown=${this.handlePointerDown}
|
|
368
|
+
@pointerup=${this.handlePointerUp}
|
|
369
|
+
@pointermove=${this.handlePointerMove}
|
|
370
|
+
>
|
|
371
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
372
|
+
<div role="none" class="dx-grid__columnheader">
|
|
373
|
+
<div
|
|
374
|
+
role="none"
|
|
375
|
+
class="dx-grid__columnheader__content"
|
|
376
|
+
style="transform:translate3d(${offsetInline}px,0,0);grid-template-columns:${this.templateColumns};"
|
|
377
|
+
>
|
|
378
|
+
${[...Array(visibleCols)].map((_, c0) => {
|
|
379
|
+
const c = this.visColMin + c0;
|
|
380
|
+
return html`<div
|
|
381
|
+
role="columnheader"
|
|
382
|
+
?inert=${c < 0}
|
|
383
|
+
style="inline-size:${this.colSize(c)}px;block-size:${this.rowDefault.size}px;grid-column:${c0 + 1}/${c0 +
|
|
384
|
+
2};"
|
|
385
|
+
>
|
|
386
|
+
<span id=${localChId(c0)}>${colToA1Notation(c)}</span>
|
|
387
|
+
${(this.columns[c]?.resizeable ?? this.columnDefault.resizeable) &&
|
|
388
|
+
html`<button class="dx-grid__resize-handle" data-dx-grid-action=${`resize-col,${c}`}>
|
|
389
|
+
<span class="sr-only">Resize</span>
|
|
390
|
+
</button>`}
|
|
391
|
+
</div>`;
|
|
392
|
+
})}
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
396
|
+
<div role="none" class="dx-grid__rowheader">
|
|
397
|
+
<div role="none" class="dx-grid__rowheader__content" style="transform:translate3d(0,${offsetBlock}px,0);">
|
|
398
|
+
${[...Array(visibleRows)].map((_, r0) => {
|
|
399
|
+
const r = this.visRowMin + r0;
|
|
400
|
+
return html`<div
|
|
401
|
+
role="rowheader"
|
|
402
|
+
?inert=${r < 0}
|
|
403
|
+
style="block-size:${this.rowSize(r)}px;grid-row:${r0 + 1}/${r0 + 2}"
|
|
404
|
+
>
|
|
405
|
+
<span id=${localRhId(r0)}>${rowToA1Notation(r)}</span>
|
|
406
|
+
${(this.rows[r]?.resizeable ?? this.rowDefault.resizeable) &&
|
|
407
|
+
html`<button class="dx-grid__resize-handle" data-dx-grid-action=${`resize-row,${r}`}>
|
|
408
|
+
<span class="sr-only">Resize</span>
|
|
409
|
+
</button>`}
|
|
410
|
+
</div>`;
|
|
411
|
+
})}
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
<div role="none" class="dx-grid__viewport" @wheel="${this.handleWheel}" ${ref(this.viewportRef)}>
|
|
415
|
+
<div
|
|
416
|
+
role="grid"
|
|
417
|
+
class="dx-grid__content"
|
|
418
|
+
style="transform:translate3d(${offsetInline}px,${offsetBlock}px,0);grid-template-columns:${this
|
|
419
|
+
.templateColumns};grid-template-rows:${this.templateRows};"
|
|
420
|
+
>
|
|
421
|
+
${[...Array(visibleCols)].map((_, c0) => {
|
|
422
|
+
return [...Array(visibleRows)].map((_, r0) => {
|
|
423
|
+
const c = c0 + this.visColMin;
|
|
424
|
+
const r = r0 + this.visRowMin;
|
|
425
|
+
const cell = this.getCell(c, r);
|
|
426
|
+
return html`<div
|
|
427
|
+
role="gridcell"
|
|
428
|
+
?inert=${c < 0 || r < 0}
|
|
429
|
+
aria-rowindex=${r}
|
|
430
|
+
aria-colindex=${c}
|
|
431
|
+
data-dx-grid-action="cell"
|
|
432
|
+
style="grid-column:${c0 + 1};grid-row:${r0 + 1}"
|
|
433
|
+
>
|
|
434
|
+
${cell?.value}
|
|
435
|
+
</div>`;
|
|
436
|
+
});
|
|
437
|
+
})}
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
<div role="none" class="dx-grid__scrollbar" aria-orientation="vertical">
|
|
441
|
+
<div role="none" class="dx-grid__scrollbar__thumb"></div>
|
|
442
|
+
</div>
|
|
443
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
444
|
+
<div role="none" class="dx-grid__scrollbar" aria-orientation="horizontal">
|
|
445
|
+
<div role="none" class="dx-grid__scrollbar__thumb"></div>
|
|
446
|
+
</div>
|
|
447
|
+
<div role="none" class="dx-grid__corner"></div>
|
|
448
|
+
</div>`;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
override firstUpdated() {
|
|
452
|
+
this.observer.observe(this.viewportRef.value!);
|
|
453
|
+
this.colSizes = Object.entries(this.columns).reduce((acc: Record<string, number>, [colId, colMeta]) => {
|
|
454
|
+
if (colMeta?.size) {
|
|
455
|
+
acc[colId] = colMeta.size;
|
|
456
|
+
}
|
|
457
|
+
return acc;
|
|
458
|
+
}, {});
|
|
459
|
+
this.rowSizes = Object.entries(this.rows).reduce((acc: Record<string, number>, [rowId, rowMeta]) => {
|
|
460
|
+
if (rowMeta?.size) {
|
|
461
|
+
acc[rowId] = rowMeta.size;
|
|
462
|
+
}
|
|
463
|
+
return acc;
|
|
464
|
+
}, {});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
override disconnectedCallback() {
|
|
468
|
+
super.disconnectedCallback();
|
|
469
|
+
// console.log('[disconnected]', this.viewportRef.value);
|
|
470
|
+
// TODO(thure): Will this even work?
|
|
471
|
+
if (this.viewportRef.value) {
|
|
472
|
+
this.observer.unobserve(this.viewportRef.value);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
override createRenderRoot() {
|
|
477
|
+
return this;
|
|
478
|
+
}
|
|
479
|
+
}
|