@chalabi/svelte-sheets 2.0.0

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.
@@ -0,0 +1,1167 @@
1
+ <script lang="ts">var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
21
+ import { onMount, tick } from "svelte";
22
+ import XLSX from "xlsx";
23
+ import { resizable } from "./actions/index.js";
24
+ import { draggable } from "./actions/draggable.js";
25
+ import { defaultconfig } from "./defaultconfig.js";
26
+ import hotkeys from "hotkeys-js";
27
+ import Menu from "./Menu.svelte";
28
+ import { clearSelection, computeStyles, deleteSelection, GetColSpan, GetRowSpan, mergeSelectExtends, pasteSelection, } from "./utilities.js";
29
+ export let data = [];
30
+ export let columns = [];
31
+ let xcolumns = [];
32
+ export let rows = [];
33
+ let xrows = [];
34
+ export let mergeCells = {};
35
+ // export let rows: Record<string, any> = [];
36
+ export let style = {};
37
+ export let selected = null; // either null, or coordinates [[x, y], [x, y]]
38
+ export let extended = null;
39
+ export let currentValue = "";
40
+ export let clipboard;
41
+ export let options;
42
+ export let className = "";
43
+ export let theme = "light";
44
+ const encode = ({ c, r }) => XLSX.utils.encode_cell({ c: Number(c), r: Number(r) });
45
+ const decode = XLSX.utils.decode_cell;
46
+ $: decoded = selected
47
+ ? [decode(selected[0]), decode(selected[1])]
48
+ : [
49
+ { c: 0, r: 0 },
50
+ { c: 0, r: 0 },
51
+ ];
52
+ $: config = Object.assign(Object.assign({}, defaultconfig), (options || {}));
53
+ const _a = $$restProps || {}, { class: _ignoredClass } = _a, restProps = __rest(_a, ["class"]);
54
+ let isReadOnly = false;
55
+ $: isReadOnly = !config.editable || config.readOnly;
56
+ $: rootClass = `${className} ${_ignoredClass || ""} ${theme ? `sheet-theme-${theme}` : ""}`.trim();
57
+ // Containers
58
+ let history = [];
59
+ let highlighted = [];
60
+ // Internal controllers
61
+ let cmdz = false;
62
+ let selection = false;
63
+ let extension = false;
64
+ let cursor = null;
65
+ let historyIndex = 0;
66
+ let ignoreEvents = false;
67
+ let ignoreHistory = false;
68
+ let edition = null;
69
+ let hashString = null;
70
+ let resizing = null;
71
+ let dragging = null;
72
+ let keypressed = {};
73
+ let hoverRaf = null;
74
+ let pendingHover = null;
75
+ let lastHover = null;
76
+ let lastDrag = null;
77
+ // $: ((_) => {
78
+ // history = history.slice(0, historyIndex);
79
+ // history.push(data);
80
+ // historyIndex = history.length;
81
+ // })();
82
+ // implement virtual list
83
+ export let startY = 0;
84
+ export let startX = 0;
85
+ export let endY = 0;
86
+ export let endX = 0;
87
+ // virtual list state
88
+ let height_map = [];
89
+ let width_map = [];
90
+ let rowElements;
91
+ let colElements;
92
+ let viewport;
93
+ let contents;
94
+ let viewport_height = 0;
95
+ let viewport_width = 0;
96
+ let visibleY;
97
+ let visibleX;
98
+ let mounted;
99
+ let top = 0;
100
+ let left = 0;
101
+ let top_buffer = 2500;
102
+ let bottom_buffer = 2500;
103
+ let left_buffer = 2500;
104
+ let right_buffer = 2500;
105
+ let bottom = 0;
106
+ let right = 0;
107
+ let average_height;
108
+ let average_width;
109
+ $: xrows =
110
+ endY > data.length ? Array.from({ length: endY - data.length }) : [];
111
+ $: xcolumns =
112
+ endX > columns.length
113
+ ? Array.from({ length: endX - columns.length }, (v, i) => ({}))
114
+ : [];
115
+ $: visibleY = [...data, ...xrows].slice(startY, endY).map((d, i) => {
116
+ return { i: i + startY, data: d };
117
+ });
118
+ $: visibleX = [...columns, ...xcolumns].slice(startX, endX).map((d, i) => {
119
+ return { i: i + startX, data: d };
120
+ });
121
+ // whenever `items` changes, invalidate the current heightmap
122
+ $: if (mounted)
123
+ refresh(data, viewport_height, viewport_width);
124
+ $: {
125
+ try {
126
+ currentValue = data[decoded[0].r][decoded[0].c];
127
+ }
128
+ catch (e) {
129
+ currentValue = "";
130
+ }
131
+ }
132
+ function getColumnsWidth(i) {
133
+ var _a, _b, _c;
134
+ return Number(typeof ((_a = columns[i]) === null || _a === void 0 ? void 0 : _a.width) == "string"
135
+ ? (_b = columns[i]) === null || _b === void 0 ? void 0 : _b.width.replace("px", "")
136
+ : ((_c = columns[i]) === null || _c === void 0 ? void 0 : _c.width) || config.defaultColWidth);
137
+ }
138
+ function getRowHeight(i) {
139
+ var _a, _b, _c, _d;
140
+ try {
141
+ const height = Number(typeof ((_a = rows[i]) === null || _a === void 0 ? void 0 : _a.height) == "string"
142
+ ? (_c = (_b = rows[i]) === null || _b === void 0 ? void 0 : _b.height) === null || _c === void 0 ? void 0 : _c.replace("px", "")
143
+ : ((_d = rows[i]) === null || _d === void 0 ? void 0 : _d.height) || 24 // consider adding a config.defaultRowHeight
144
+ );
145
+ return height > 24 ? height : 24;
146
+ }
147
+ catch (e) {
148
+ return 24;
149
+ }
150
+ }
151
+ export function onInputChange(value, row, column) {
152
+ if (isReadOnly)
153
+ return;
154
+ cmdz = true;
155
+ if (row.i > data.length - 1) {
156
+ data = [
157
+ ...data,
158
+ ...Array.from({ length: row.i - data.length + 1 }, (v, i) => {
159
+ if (i == row.i) {
160
+ return Array.from({ length: columns.length }, (_, i) => {
161
+ if (i == column.i) {
162
+ return value;
163
+ }
164
+ else {
165
+ return "";
166
+ }
167
+ });
168
+ }
169
+ else {
170
+ return Array.from({ length: columns.length }, (_) => "");
171
+ }
172
+ }),
173
+ ];
174
+ }
175
+ else {
176
+ data[row.i][column.i] = value;
177
+ }
178
+ }
179
+ function refresh(data, viewport_height, viewport_width) {
180
+ return __awaiter(this, void 0, void 0, function* () {
181
+ const { scrollTop, scrollLeft } = viewport;
182
+ yield tick(); // wait until the DOM is up to date
183
+ let content_height = top - scrollTop - bottom_buffer;
184
+ let content_width = left - scrollLeft - left_buffer;
185
+ // vertical
186
+ let y = startY;
187
+ while (content_height < viewport_height) {
188
+ let row = rowElements[y - startY];
189
+ if (!row) {
190
+ endY = y + 1;
191
+ yield tick(); // render the newly visible row
192
+ row = rowElements[y - startY];
193
+ }
194
+ const row_height = (height_map[y] = getRowHeight(y));
195
+ content_height += row_height;
196
+ y += 1;
197
+ }
198
+ endY = y;
199
+ let remaining = data.length - endY;
200
+ average_height = (top + content_height) / endY;
201
+ bottom = remaining * average_height;
202
+ height_map.length = data.length;
203
+ // horizontal
204
+ let x = startX;
205
+ while (content_width < viewport_width) {
206
+ let col = colElements[x - startX];
207
+ if (!col) {
208
+ endX = x + 1;
209
+ yield tick(); // render the newly visible col
210
+ col = colElements[x - startX];
211
+ }
212
+ const col_width = (width_map[x] = getColumnsWidth(x));
213
+ content_width += col_width;
214
+ x += 1;
215
+ }
216
+ endX = x;
217
+ let remains = columns.length - endX;
218
+ average_width = (left + content_width) / endX;
219
+ right = remains * average_width;
220
+ width_map.length = columns.length;
221
+ });
222
+ }
223
+ // $: scrollLeft = viewport?.scrollLeft;
224
+ // $: scrollTop = viewport?.scrollTop;
225
+ let scrollLeft;
226
+ let scrollTop;
227
+ $: (function scrollX() {
228
+ if (!scrollLeft || !colElements)
229
+ return;
230
+ // if (!scrollLeft) ;
231
+ // horizontal scrolling
232
+ for (let v = 0; v < colElements.length; v += 1) {
233
+ width_map[startX + v] = getColumnsWidth(startX + v);
234
+ }
235
+ let c = 0;
236
+ let x = 0;
237
+ while (true) {
238
+ const col_width = width_map[c] || average_width;
239
+ if (x + col_width > scrollLeft - left_buffer) {
240
+ startX = c;
241
+ left = x;
242
+ break;
243
+ }
244
+ x += col_width;
245
+ c += 1;
246
+ }
247
+ while (true) {
248
+ x += width_map[c] || average_width;
249
+ c += 1;
250
+ if (x > scrollLeft + viewport_width + right_buffer)
251
+ break;
252
+ }
253
+ endX = c;
254
+ const remaining = endX > columns.length
255
+ ? (viewport_width + right_buffer) / 24
256
+ : columns.length - endX;
257
+ average_width = x / endX;
258
+ // while (c < columns.length) width_map[c++] = average_width;
259
+ right = remaining * average_width;
260
+ })();
261
+ $: (function scrollY() {
262
+ if (!scrollTop || !rowElements)
263
+ return;
264
+ // vertical scrolling
265
+ for (let v = 0; v < rowElements.length; v += 1) {
266
+ height_map[startY + v] = getRowHeight(startY + v);
267
+ }
268
+ let r = 0;
269
+ let y = 0;
270
+ while (true) {
271
+ const row_height = height_map[r] || average_height;
272
+ if (y + row_height > scrollTop - top_buffer) {
273
+ startY = r;
274
+ top = y;
275
+ break;
276
+ }
277
+ y += row_height;
278
+ r += 1;
279
+ }
280
+ while (true) {
281
+ y += height_map[r] || average_height;
282
+ r += 1;
283
+ if (y > scrollTop + viewport_height + bottom_buffer)
284
+ break;
285
+ }
286
+ endY = r;
287
+ const remaining = endY > data.length
288
+ ? (viewport_height + bottom_buffer) / 24
289
+ : data.length - endY;
290
+ average_height = y / endY;
291
+ // while (r < data.length) height_map[r++] = average_height;
292
+ bottom = remaining * average_height;
293
+ })();
294
+ function handle_scroll(e) {
295
+ scrollTop = viewport.scrollTop;
296
+ scrollLeft = viewport.scrollLeft;
297
+ }
298
+ onMount(() => {
299
+ if (window && window.document) {
300
+ rowElements = document === null || document === void 0 ? void 0 : document.getElementsByClassName("virtual-row");
301
+ colElements = document === null || document === void 0 ? void 0 : document.getElementsByClassName("virtual-col");
302
+ mounted = true;
303
+ // document.addEventListener("mouseup", jexcel.mouseUpControls);
304
+ // document.addEventListener("mousedown", jexcel.mouseDownControls);
305
+ // document.addEventListener("mousemove", jexcel.mouseMoveControls);
306
+ // document.addEventListener("mouseover", jexcel.mouseOverControls);
307
+ // document.addEventListener("dblclick", jexcel.doubleClickControls);
308
+ // document.addEventListener("paste", jexcel.pasteControls);
309
+ // document.addEventListener("contextmenu", jexcel.contextMenuControls);
310
+ // document.addEventListener("touchstart", jexcel.touchStartControls);
311
+ // document.addEventListener("touchend", jexcel.touchEndControls);
312
+ // document.addEventListener("touchcancel", jexcel.touchEndControls);
313
+ // document.addEventListener("touchmove", jexcel.touchEndControls);
314
+ document === null || document === void 0 ? void 0 : document.addEventListener("keydown", onKeyDown);
315
+ document === null || document === void 0 ? void 0 : document.addEventListener("keyup", onKeyUp);
316
+ }
317
+ });
318
+ function onMouseDown(e) {
319
+ // if right click
320
+ if (e.which == 3)
321
+ return;
322
+ console.log("mousedown", e.which);
323
+ if (e.target.id == "square") {
324
+ if (isReadOnly)
325
+ return;
326
+ extension = true;
327
+ selection = false;
328
+ return;
329
+ }
330
+ if (!e.target.dataset.x || !e.target.dataset.y)
331
+ return;
332
+ if (keypressed[16] && selected && selected[0]) {
333
+ edition = null;
334
+ selected = [
335
+ selected[0],
336
+ encode({ c: e.target.dataset.x, r: e.target.dataset.y }),
337
+ ];
338
+ return;
339
+ }
340
+ edition = null;
341
+ // extension = false;
342
+ selection = true;
343
+ selected = [
344
+ encode({ c: e.target.dataset.x, r: e.target.dataset.y }),
345
+ encode({ c: e.target.dataset.x, r: e.target.dataset.y }),
346
+ ];
347
+ }
348
+ function onMouseUp(e) {
349
+ if (isReadOnly && extension) {
350
+ extension = false;
351
+ selection = false;
352
+ return;
353
+ }
354
+ if (!!selected && !selection && extension) {
355
+ extension = false;
356
+ data = mergeSelectExtends(data, selected, extended);
357
+ selected = extended;
358
+ return;
359
+ }
360
+ if (!!edition || !selected || !selection)
361
+ return;
362
+ selection = false;
363
+ extended = selected;
364
+ }
365
+ function onMouseOver(e) {
366
+ var _a, _b, _c, _d;
367
+ if (config.disableHover)
368
+ return;
369
+ if (!!edition)
370
+ return;
371
+ const x = Number((_b = (_a = e.target) === null || _a === void 0 ? void 0 : _a.dataset) === null || _b === void 0 ? void 0 : _b.x);
372
+ const y = Number((_d = (_c = e.target) === null || _c === void 0 ? void 0 : _c.dataset) === null || _d === void 0 ? void 0 : _d.y);
373
+ if (Number.isNaN(x) || Number.isNaN(y))
374
+ return;
375
+ pendingHover = { x, y };
376
+ if (hoverRaf)
377
+ return;
378
+ hoverRaf = requestAnimationFrame(() => {
379
+ hoverRaf = null;
380
+ if (!pendingHover)
381
+ return;
382
+ if (lastHover && lastHover.x === pendingHover.x && lastHover.y === pendingHover.y) {
383
+ pendingHover = null;
384
+ return;
385
+ }
386
+ lastHover = pendingHover;
387
+ const hovered = pendingHover;
388
+ pendingHover = null;
389
+ if (!!selected && !selection && extension) {
390
+ if (
391
+ // extended is inside selected
392
+ hovered.x >= topLeft.c &&
393
+ hovered.x < bottomRight.c &&
394
+ hovered.y >= topLeft.r &&
395
+ hovered.y < bottomRight.r) {
396
+ extended = [
397
+ encode(topLeft),
398
+ encode({
399
+ c: hovered.x,
400
+ r: hovered.y,
401
+ }),
402
+ ];
403
+ return;
404
+ }
405
+ if (hovered.y >= topLeft.r && hovered.y < bottomRight.r) {
406
+ extended = [
407
+ squareX < 0
408
+ ? encode({ c: bottomRight.c - 1, r: topLeft.r })
409
+ : selected[0],
410
+ encode({ r: decoded[1].r, c: hovered.x }),
411
+ ];
412
+ }
413
+ if (hovered.x >= topLeft.c && hovered.x < bottomRight.c) {
414
+ extended = [
415
+ squareY < 0
416
+ ? encode({ r: bottomRight.r - 1, c: topLeft.c })
417
+ : selected[0],
418
+ encode({ r: hovered.y, c: decoded[1].c }),
419
+ ];
420
+ }
421
+ return;
422
+ }
423
+ if (selection && !!selected) {
424
+ selected = [
425
+ selected[0] || encode({ c: hovered.x, r: hovered.y }),
426
+ encode({ c: hovered.x, r: hovered.y }),
427
+ ];
428
+ }
429
+ });
430
+ return;
431
+ }
432
+ function onKeyUp(e) {
433
+ // on keyup just reinitialize everything
434
+ keypressed[e.keyCode] = false;
435
+ }
436
+ hotkeys("ctrl+z, command+z", function (e) {
437
+ if (isReadOnly)
438
+ return;
439
+ e.preventDefault();
440
+ cmdz = true;
441
+ if (historyIndex == 0)
442
+ return;
443
+ historyIndex -= 1;
444
+ const res = JSON.parse(history[historyIndex]);
445
+ data = res.data;
446
+ columns = res.columns;
447
+ rows = res.rows;
448
+ style = res.style;
449
+ setTimeout((_) => (cmdz = false), 10);
450
+ });
451
+ hotkeys("ctrl+shift+z, command+shift+z", function (e) {
452
+ if (isReadOnly)
453
+ return;
454
+ console.log("redo");
455
+ e.preventDefault();
456
+ cmdz = true;
457
+ if (history.length - 1 == historyIndex)
458
+ return;
459
+ historyIndex = historyIndex + 1;
460
+ const res = JSON.parse(history[historyIndex]);
461
+ data = res.data;
462
+ columns = res.columns;
463
+ rows = res.rows;
464
+ style = res.style;
465
+ setTimeout((_) => (cmdz = false), 10);
466
+ });
467
+ hotkeys("ctrl+c, command+c, ctrl+x, command+x", function (e, handler) {
468
+ var _a;
469
+ if (isReadOnly && ((_a = handler === null || handler === void 0 ? void 0 : handler.key) === null || _a === void 0 ? void 0 : _a.includes("x")))
470
+ return;
471
+ e.preventDefault();
472
+ clipboard = JSON.stringify(selected);
473
+ });
474
+ hotkeys("ctrl+v, command+v", function (e) {
475
+ if (isReadOnly)
476
+ return;
477
+ e.preventDefault();
478
+ if (!clipboard)
479
+ return;
480
+ data = pasteSelection(data, JSON.parse(clipboard), selected);
481
+ });
482
+ function onKeyDown(e) {
483
+ keypressed[e.keyCode] = true;
484
+ if (!!edition) {
485
+ if (e.key == "Escape") {
486
+ edition = null;
487
+ }
488
+ return;
489
+ }
490
+ if (!selected)
491
+ return;
492
+ switch (e.key) {
493
+ case "ArrowDown":
494
+ var s = encode({
495
+ c: decoded[0].c,
496
+ r: decoded[0].r + 1,
497
+ });
498
+ selected = [s, s];
499
+ break;
500
+ case "ArrowUp":
501
+ var s = encode({
502
+ c: decoded[0].c,
503
+ r: decoded[0].r - 1,
504
+ });
505
+ selected = [s, s];
506
+ break;
507
+ case "ArrowLeft":
508
+ var s = encode({
509
+ c: decoded[0].c - 1,
510
+ r: decoded[0].r,
511
+ });
512
+ selected = [s, s];
513
+ break;
514
+ case "ArrowRight":
515
+ var s = encode({
516
+ c: decoded[0].c + 1,
517
+ r: decoded[0].r,
518
+ });
519
+ selected = [s, s];
520
+ break;
521
+ default:
522
+ break;
523
+ }
524
+ }
525
+ let menuX;
526
+ let menuY;
527
+ function showMenu(e) {
528
+ e.preventDefault();
529
+ // e.stopImmediatePropagation();
530
+ e.stopPropagation();
531
+ menuX = e.screenX;
532
+ menuY = e.screenY - 70;
533
+ }
534
+ // initialize and refactor data
535
+ $: (() => {
536
+ if (data[0]) {
537
+ if (!Array.isArray(data[0])) {
538
+ columns = Object.keys(data[0]).map((k) => ({ name: k }));
539
+ var d = [];
540
+ for (var j = 0; j < data.length; j++) {
541
+ var row = [];
542
+ for (var i = 0; i < columns.length; i++) {
543
+ row[i] = data[j][columns[i].name];
544
+ }
545
+ d.push(row);
546
+ }
547
+ data = d;
548
+ }
549
+ }
550
+ // Adjust minimal dimensions
551
+ var j = 0;
552
+ var i = 0;
553
+ var size_i = columns.length;
554
+ var size_j = data.length;
555
+ var min_i = config.minDimensions[0];
556
+ var min_j = config.minDimensions[1];
557
+ var max_i = min_i > size_i ? min_i : size_i;
558
+ var max_j = min_j > size_j ? min_j : size_j;
559
+ for (j = 0; j < max_j; j++) {
560
+ for (i = 0; i < max_i; i++) {
561
+ if (data[j] == undefined) {
562
+ data[j] = [];
563
+ }
564
+ if (data[j][i] == undefined) {
565
+ data[j][i] = "";
566
+ }
567
+ }
568
+ }
569
+ })();
570
+ // square selection
571
+ let tops;
572
+ let rights;
573
+ let lefts;
574
+ let bottoms;
575
+ let topextend;
576
+ let rightextend;
577
+ let leftextend;
578
+ let bottomextend;
579
+ let colLine;
580
+ let rowLine;
581
+ let square;
582
+ let squareX;
583
+ let squareY;
584
+ let topLeft;
585
+ let bottomRight;
586
+ $: {
587
+ if (extension && extended) {
588
+ let tl = (selected && decode(extended[0])) || { c: 0, r: 0 };
589
+ let br = (selected && decode(extended[1])) || { c: 0, r: 0 };
590
+ topLeft = {
591
+ c: br.c < tl.c ? br.c : tl.c,
592
+ r: br.r < tl.r ? br.r : tl.r,
593
+ };
594
+ bottomRight = {
595
+ c: br.c > tl.c ? br.c + 1 : tl.c + 1,
596
+ r: br.r > tl.r ? br.r + 1 : tl.r + 1,
597
+ };
598
+ let top = 28;
599
+ let right = 51;
600
+ let bottom = 28;
601
+ let left = 51;
602
+ for (let i = 0; i < topLeft.r; i++) {
603
+ top = top + getRowHeight(i);
604
+ }
605
+ for (let i = 0; i < topLeft.c; i++) {
606
+ left = left + getColumnsWidth(i);
607
+ }
608
+ for (let i = 0; i < bottomRight.r; i++) {
609
+ bottom = bottom + getRowHeight(i);
610
+ }
611
+ for (let i = 0; i < bottomRight.c; i++) {
612
+ right = right + getColumnsWidth(i);
613
+ }
614
+ topextend.style = `width: ${right - left}px; left: ${left}px; top: ${top}px`;
615
+ rightextend.style = `height: ${bottom - top}px; left: ${right}px; top: ${top}px`;
616
+ bottomextend.style = `width: ${right - left}px; left: ${left}px; top: ${bottom}px`;
617
+ leftextend.style = `height: ${bottom - top}px; left: ${left}px; top: ${top}px`;
618
+ }
619
+ }
620
+ let selectWidth;
621
+ let selectHeight;
622
+ $: {
623
+ if (mounted) {
624
+ let tl = (selected && decode(selected[0])) || { c: 0, r: 0 };
625
+ let br = (selected && decode(selected[1])) || { c: 0, r: 0 };
626
+ topLeft = {
627
+ c: br.c < tl.c ? br.c : tl.c,
628
+ r: br.r < tl.r ? br.r : tl.r,
629
+ };
630
+ bottomRight = {
631
+ c: br.c > tl.c ? br.c + 1 : tl.c + 1,
632
+ r: br.r > tl.r ? br.r + 1 : tl.r + 1,
633
+ };
634
+ let top = 28;
635
+ let right = 51;
636
+ let bottom = 28;
637
+ let left = 51;
638
+ for (let i = 0; i < topLeft.r; i++) {
639
+ top = top + getRowHeight(i);
640
+ }
641
+ for (let i = 0; i < topLeft.c; i++) {
642
+ left = left + getColumnsWidth(i);
643
+ }
644
+ for (let i = 0; i < bottomRight.r; i++) {
645
+ bottom = bottom + getRowHeight(i);
646
+ }
647
+ for (let i = 0; i < bottomRight.c; i++) {
648
+ right = right + getColumnsWidth(i);
649
+ }
650
+ tops.style = `width: ${right - left}px; left: ${left}px; top: ${top}px`;
651
+ rights.style = `height: ${bottom - top}px; left: ${right}px; top: ${top}px`;
652
+ bottoms.style = `width: ${right - left}px; left: ${left}px; top: ${bottom}px`;
653
+ lefts.style = `height: ${bottom - top}px; left: ${left}px; top: ${top}px`;
654
+ colLine.style = `width: ${right - left}px; left: ${left}px; top: 28px;`;
655
+ rowLine.style = `height: ${bottom - top}px; left: 51px; top: ${top}px`;
656
+ square.style = `left:${right}px; top:${bottom}px`;
657
+ selectWidth = right - left;
658
+ selectHeight = bottom - top;
659
+ }
660
+ }
661
+ // history logic
662
+ function historyPush(data, rows, columns, style) {
663
+ if (!cmdz) {
664
+ const step = { data, rows, columns, style };
665
+ if (history[historyIndex] != JSON.stringify(step)) {
666
+ history = [...history.slice(0, historyIndex + 1), JSON.stringify(step)];
667
+ historyIndex = history.length - 1;
668
+ }
669
+ }
670
+ }
671
+ </script>
672
+
673
+ <div
674
+ {...restProps}
675
+ class={`w-full sheet_container ${rootClass}`}
676
+ class:fullscreen={!!config.fullscreen}
677
+ class:with-toolbar={config.tableOverflow != true && config.toolbar}
678
+ on:contextmenu={(e) => showMenu(e)}
679
+ on:mousedown={onMouseDown}
680
+ on:mouseup={onMouseUp}
681
+ on:mouseover={onMouseOver}
682
+ tabindex="1"
683
+ >
684
+ <div
685
+ class="jexcel_content"
686
+ style={config.tableWidth
687
+ ? "overflow-x: auto; width: " + config.tableWidth + ";"
688
+ : "" + config.tableHeight
689
+ ? "overflow-y: auto; max-height: " + config.tableHeight + ";"
690
+ : ""}
691
+ bind:this={viewport}
692
+ bind:offsetHeight={viewport_height}
693
+ bind:offsetWidth={viewport_width}
694
+ on:scroll={handle_scroll}
695
+ >
696
+ <div class="sheet-layer">
697
+ <div class="overlay-layer">
698
+ <div
699
+ class="top-extend absolute"
700
+ class:hidden={!extension}
701
+ bind:this={topextend}
702
+ />
703
+ <div
704
+ class="bottom-extend absolute"
705
+ class:hidden={!extension}
706
+ bind:this={bottomextend}
707
+ />
708
+ <div
709
+ class="left-extend absolute"
710
+ class:hidden={!extension}
711
+ bind:this={leftextend}
712
+ />
713
+ <div
714
+ class="right-extend absolute"
715
+ class:hidden={!extension}
716
+ bind:this={rightextend}
717
+ />
718
+ <div class="top-select absolute" bind:this={tops} />
719
+ <div class="bottom-select absolute" bind:this={bottoms} />
720
+ <div class="left-select absolute" bind:this={lefts} />
721
+ <div class="right-select absolute" bind:this={rights} />
722
+ <div class="col-line absolute" bind:this={colLine} />
723
+ <div class="row-line absolute" bind:this={rowLine} />
724
+ <div
725
+ tabindex={-1}
726
+ use:draggable
727
+ on:dragging={(e) => {
728
+ if (isReadOnly) return;
729
+ if (lastDrag && lastDrag.x === e.detail.x && lastDrag.y === e.detail.y) {
730
+ return;
731
+ }
732
+ lastDrag = { x: e.detail.x, y: e.detail.y };
733
+ squareX = e.detail.x;
734
+ squareY = e.detail.y;
735
+ }}
736
+ class="square absolute interactive"
737
+ id="square"
738
+ bind:this={square}
739
+ />
740
+ <div class="interactive">
741
+ <Menu
742
+ show={!!menuX}
743
+ x={menuX}
744
+ y={menuY}
745
+ selected={selected}
746
+ copy={(e) => (clipboard = selected)}
747
+ cut={(e) => (clipboard = selected)}
748
+ paste={(e) => !isReadOnly && (data = pasteSelection(data, clipboard, selected))}
749
+ clear={(e) => !isReadOnly && (data = clearSelection(data, selected))}
750
+ delet={(e) => !isReadOnly && (data = deleteSelection(data, selected))}
751
+ readOnly={isReadOnly}
752
+ />
753
+ </div>
754
+ </div>
755
+ <table
756
+ cellpadding="0"
757
+ cellspacing="0"
758
+ unselectable={true}
759
+ on:click={(e) => (menuX = 0)}
760
+ style="padding-top: {top}px; padding-bottom: {bottom}px; padding-left: {left}px; padding-right: {right}px;"
761
+ bind:this={contents}
762
+ >
763
+ <colgroup>
764
+ <col width={50} />
765
+ {#each visibleX as v}
766
+ <col width={getColumnsWidth(v.i)} />
767
+ {/each}
768
+ </colgroup>
769
+ <thead
770
+ class:draggable={config.columnDrag || config.rowDrag}
771
+ class:resizable={config.columnResize || config.rowResize}
772
+ class="resizable"
773
+ >
774
+ <tr>
775
+ <th class="jexcel_selectall virtual-col" />
776
+ {#each visibleX as c, i}
777
+ <td
778
+ on:click={(_) =>
779
+ (selected =
780
+ keypressed[16] && selected && selected[0]
781
+ ? [
782
+ encode({
783
+ c: decoded[0].c,
784
+ r: 0,
785
+ }),
786
+ encode({
787
+ c: c.i,
788
+ r: data.length - 1,
789
+ }),
790
+ ]
791
+ : [
792
+ encode({
793
+ c: c.i,
794
+ r: 0,
795
+ }),
796
+ encode({ c: c.i, r: data.length - 1 }),
797
+ ])}
798
+ data-x={c.i}
799
+ title={c.data.title || ""}
800
+ class="virtual-col"
801
+ class:selected={selected &&
802
+ c.i >= topLeft.c &&
803
+ bottomRight.c - 1 >= c.i}
804
+ class:hidden={c.data.type == "hidden"}
805
+ style={`text-align: ${c.data.align || config.defaultColAlign};`}
806
+ >
807
+ {c.data.title || XLSX.utils.encode_col(c.i)}
808
+ <div
809
+ use:resizable
810
+ on:resizing={(e) =>
811
+ !isReadOnly &&
812
+ e.detail.x !== 0 &&
813
+ c.i != 0 &&
814
+ (columns[c.i - 1] = {
815
+ ...(columns[c.i - 1] || {}),
816
+ width: getColumnsWidth(c.i - 1) + e.detail.x,
817
+ })}
818
+ class="col-resize left"
819
+ />
820
+ <div
821
+ class="col-resize right"
822
+ use:resizable
823
+ on:resizing={(e) =>
824
+ !isReadOnly &&
825
+ e.detail.x !== 0 &&
826
+ (columns[c.i] = {
827
+ ...(columns[c.i] || {}),
828
+ width: getColumnsWidth(c.i) + e.detail.x,
829
+ })}
830
+ />
831
+ </td>
832
+ {/each}
833
+ </tr>
834
+ </thead>
835
+ <tbody class="draggable" bind:this={viewport} on:scroll={handle_scroll}>
836
+ {#each visibleY as r}
837
+ <tr
838
+ class="virtual-row"
839
+ data-y={r.i}
840
+ style={`height: ${getRowHeight(r.i)}px`}
841
+ >
842
+ <th
843
+ data-y={r.i}
844
+ class:selected={selected &&
845
+ r.i >= topLeft.r &&
846
+ bottomRight.r - 1 >= r.i}
847
+ style={`background-color:
848
+ var(--sheet-header-bg);
849
+ text-align:
850
+ center;
851
+ height:
852
+ ${getRowHeight(r.i)}px;`}
853
+ on:click={(e) =>
854
+ (selected =
855
+ keypressed[16] && selected && selected[0]
856
+ ? [
857
+ encode({
858
+ c: 0,
859
+ r: decoded[0].r,
860
+ }),
861
+ encode({
862
+ c: data[0].length - 1,
863
+ r: r.i,
864
+ }),
865
+ ]
866
+ : [
867
+ encode({
868
+ c: 0,
869
+ r: r.i,
870
+ }),
871
+ encode({ c: data[0].length - 1, r: r.i }),
872
+ ])}
873
+ >
874
+ <div
875
+ class="row-resize top"
876
+ use:resizable
877
+ on:resizing={(e) =>
878
+ !isReadOnly &&
879
+ e.detail.y !== 0 &&
880
+ r.i != 0 &&
881
+ (rows[r.i - 1] = {
882
+ ...(rows[r.i - 1] || {}),
883
+ height: getRowHeight(r.i - 1) + e.detail.y,
884
+ })}
885
+ />
886
+ <div
887
+ class="row-resize bottom"
888
+ use:resizable
889
+ on:resizing={(e) =>
890
+ !isReadOnly &&
891
+ e.detail.y !== 0 &&
892
+ (rows[r.i] = {
893
+ ...(rows[r.i] || {}),
894
+ height: getRowHeight(r.i) + e.detail.y,
895
+ })}
896
+ />
897
+ {r.i + 1}
898
+ </th>
899
+ {#each visibleX as x, i}
900
+ <td
901
+ tabindex="-1"
902
+ data-x={x.i}
903
+ data-y={r.i}
904
+ data-merged={GetColSpan(mergeCells, x.i, r.i) ||
905
+ GetRowSpan(mergeCells, x.i, r.i)}
906
+ colspan={GetColSpan(mergeCells, x.i, r.i)}
907
+ class:selected={x.i >= topLeft.c &&
908
+ x.i < bottomRight.c &&
909
+ r.i >= topLeft.r &&
910
+ r.i < bottomRight.r}
911
+ on:dblclick={(_) =>
912
+ !isReadOnly &&
913
+ !(columns[x.i] && columns[x.i].readOnly) &&
914
+ (edition = [x.i, r.i])}
915
+ class:readonly={columns[x.i] && columns[x.i].readOnly}
916
+ style={computeStyles(
917
+ x.i,
918
+ r.i,
919
+ rows[r.i],
920
+ style,
921
+ config,
922
+ r.data && r.data[x.i],
923
+ r.data && r.data[x.i + 1]
924
+ )}
925
+ >
926
+ {#if String(edition) == String([x.i, r.i])}
927
+ <input
928
+ autofocus
929
+ on:blur={(e) => {
930
+ cmdz = false;
931
+ historyPush(data, rows, columns, style);
932
+ }}
933
+ on:input={(e) => onInputChange(e.target.value, r, x)}
934
+ value={(data[r.i] && data[r.i][x.i]) || ""}
935
+ style={`width: ${getColumnsWidth(
936
+ x.i
937
+ )}px; height: ${getRowHeight(r.i)}px; min-height: 22px;`}
938
+ />
939
+ {:else}{(r.data && r.data[x.i]) || ""}{/if}
940
+ </td>
941
+ {/each}
942
+ </tr>
943
+ {/each}
944
+ </tbody>
945
+ </table>
946
+ </div>
947
+ </div>
948
+ </div>
949
+
950
+ <style>
951
+ *,
952
+ ::before,
953
+ ::after {
954
+ box-sizing: border-box;
955
+ border-width: 0;
956
+ border-style: solid;
957
+ border-color: var(--sheet-border-muted);
958
+ }
959
+
960
+ :root {
961
+ tab-size: 4;
962
+ }
963
+ .jexcel_content {
964
+ overflow-x: auto;
965
+ overflow-y: auto;
966
+ max-width: 100vw;
967
+ max-height: 100vh;
968
+ }
969
+ .sheet-layer {
970
+ position: relative;
971
+ min-width: max-content;
972
+ }
973
+ .overlay-layer {
974
+ position: absolute;
975
+ top: 0;
976
+ left: 0;
977
+ width: 100%;
978
+ height: 100%;
979
+ z-index: 5;
980
+ pointer-events: none;
981
+ }
982
+ .overlay-layer .interactive {
983
+ pointer-events: auto;
984
+ }
985
+ .sheet_container {
986
+ --sheet-bg: #ffffff;
987
+ --sheet-border: #cccccc;
988
+ --sheet-border-muted: #e0e0e0;
989
+ --sheet-header-bg: #f3f3f3;
990
+ --sheet-header-selected-bg: #dcdcdc;
991
+ --sheet-selection-bg: #dddddd;
992
+ --sheet-accent: #1b7f7a;
993
+ --sheet-accent-strong: #0f5e5a;
994
+ --sheet-menu-bg: #dddddd;
995
+ --sheet-muted: #aaaaaa;
996
+ --sheet-text: #222222;
997
+ display: block;
998
+ padding-right: 2px;
999
+ box-sizing: border-box;
1000
+ overscroll-behavior: contain;
1001
+ outline: none;
1002
+ position: relative;
1003
+ user-select: none;
1004
+ color: var(--sheet-text);
1005
+ }
1006
+ .sheet-theme-dark {
1007
+ --sheet-bg: #12161a;
1008
+ --sheet-border: #2b3238;
1009
+ --sheet-border-muted: #242a30;
1010
+ --sheet-header-bg: #1b2026;
1011
+ --sheet-header-selected-bg: #273039;
1012
+ --sheet-selection-bg: #2a323a;
1013
+ --sheet-accent: #4fc2c2;
1014
+ --sheet-accent-strong: #7bd9d9;
1015
+ --sheet-menu-bg: #1f252b;
1016
+ --sheet-muted: #7e8a96;
1017
+ --sheet-text: #e6edf3;
1018
+ }
1019
+ table {
1020
+ border-collapse: separate;
1021
+ table-layout: fixed;
1022
+ white-space: nowrap;
1023
+ empty-cells: show;
1024
+ border: 0px;
1025
+ background-color: var(--sheet-bg);
1026
+ width: 0;
1027
+ border-top: 1px solid transparent;
1028
+ border-left: 1px solid transparent;
1029
+ border-right: 1px solid var(--sheet-border);
1030
+ border-bottom: 1px solid var(--sheet-border);
1031
+ text-indent: 0;
1032
+ }
1033
+ /* tr.selected {
1034
+ background-color: #b8e7e3;
1035
+ } */
1036
+ thead > tr > td.selected {
1037
+ background-color: var(--sheet-header-selected-bg);
1038
+ color: var(--sheet-accent);
1039
+ }
1040
+ thead > tr > td {
1041
+ background-color: var(--sheet-header-bg);
1042
+ padding: 2px;
1043
+ cursor: s-resize;
1044
+ box-sizing: border-box;
1045
+ overflow: hidden;
1046
+ position: sticky;
1047
+ top: 0;
1048
+ z-index: 2;
1049
+ }
1050
+ td {
1051
+ outline: none;
1052
+ cursor: default;
1053
+ line-height: 14px;
1054
+ font-size: 14px;
1055
+ border-top: 1px solid var(--sheet-border);
1056
+ border-left: 1px solid var(--sheet-border);
1057
+ border-right: 1px solid transparent;
1058
+ border-bottom: 1px solid transparent;
1059
+ }
1060
+ tbody > tr > td {
1061
+ padding: 4px;
1062
+ white-space: nowrap;
1063
+ box-sizing: border-box;
1064
+ line-height: 1em;
1065
+ text-align: end;
1066
+ cursor: cell;
1067
+ }
1068
+ tbody > tr > td.selected {
1069
+ background-color: var(--sheet-selection-bg);
1070
+ transition: all 0.1s linear;
1071
+ }
1072
+ tbody > tr > th,
1073
+ thead > tr > th {
1074
+ position: sticky;
1075
+ left: 0;
1076
+ cursor: e-resize;
1077
+ top: auto;
1078
+ background: var(--sheet-header-bg);
1079
+ border-top: 1px solid var(--sheet-border);
1080
+ border-left: 1px solid var(--sheet-border);
1081
+ border-right: 1px solid var(--sheet-border);
1082
+ z-index: 10;
1083
+ font-weight: normal;
1084
+ height: 27px;
1085
+ }
1086
+
1087
+ /* tbody > tr > td:first-child {
1088
+ position: relative;
1089
+ background-color: #f3f3f3;
1090
+ text-align: center;
1091
+ } */
1092
+ tbody > tr > th.selected {
1093
+ background-color: var(--sheet-header-selected-bg) !important;
1094
+ color: var(--sheet-accent);
1095
+ }
1096
+
1097
+ div.col-resize {
1098
+ position: absolute;
1099
+ top: 0;
1100
+ cursor: col-resize;
1101
+ width: 1rem;
1102
+ height: 100%;
1103
+ }
1104
+ div.col-resize.right {
1105
+ right: 0;
1106
+ }
1107
+ div.col-resize.left {
1108
+ left: 0;
1109
+ }
1110
+
1111
+ div.row-resize {
1112
+ position: absolute;
1113
+ left: 0;
1114
+ cursor: row-resize;
1115
+ width: 100%;
1116
+ height: 0.5rem;
1117
+ }
1118
+
1119
+ div.row-resize.top {
1120
+ top: 0;
1121
+ }
1122
+ div.row-resize.bottom {
1123
+ bottom: 0;
1124
+ }
1125
+ input {
1126
+ background: none;
1127
+ margin: -4px 0;
1128
+ outline: none;
1129
+ }
1130
+ .absolute {
1131
+ position: absolute;
1132
+ z-index: 10;
1133
+ transition: all 0.1s linear;
1134
+ }
1135
+ .top-select,
1136
+ .bottom-select,
1137
+ .col-line {
1138
+ border-bottom: 2px solid var(--sheet-accent);
1139
+ }
1140
+ .left-select,
1141
+ .right-select {
1142
+ border-left: 2px solid var(--sheet-accent);
1143
+ }
1144
+
1145
+ .top-extend,
1146
+ .bottom-extend {
1147
+ border-bottom: 2px solid var(--sheet-muted);
1148
+ }
1149
+ .left-extend,
1150
+ .right-extend {
1151
+ border-left: 2px solid var(--sheet-muted);
1152
+ }
1153
+ .row-line {
1154
+ border-right: 1px solid var(--sheet-accent);
1155
+ }
1156
+ .square {
1157
+ height: 8px;
1158
+ width: 8px;
1159
+ cursor: crosshair;
1160
+ border: 1px solid var(--sheet-bg);
1161
+ background: var(--sheet-accent);
1162
+ transform: translate3D(-40%, -40%, 0);
1163
+ }
1164
+ .hidden {
1165
+ display: none;
1166
+ }
1167
+ </style>