@schukai/monster 4.108.0 → 4.110.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,1346 @@
1
+ /**
2
+ * Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
3
+ * Node module: @schukai/monster
4
+ *
5
+ * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
6
+ * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
7
+ *
8
+ * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
9
+ * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
10
+ * For more information about purchasing a commercial license, please contact Volker Schukai.
11
+ *
12
+ * SPDX-License-Identifier: AGPL-3.0
13
+ */
14
+
15
+ import { instanceSymbol } from "../../constants.mjs";
16
+ import { addAttributeToken } from "../../dom/attributes.mjs";
17
+ import {
18
+ ATTRIBUTE_ERRORMESSAGE,
19
+ ATTRIBUTE_ROLE,
20
+ } from "../../dom/constants.mjs";
21
+ import { CustomControl } from "../../dom/customcontrol.mjs";
22
+ import {
23
+ assembleMethodSymbol,
24
+ registerCustomElement,
25
+ } from "../../dom/customelement.mjs";
26
+ import { fireCustomEvent } from "../../dom/events.mjs";
27
+ import { getLocaleOfDocument } from "../../dom/locale.mjs";
28
+ import { Observer } from "../../types/observer.mjs";
29
+ import { isArray, isObject, isString } from "../../types/is.mjs";
30
+ import { ID } from "../../types/id.mjs";
31
+ import { clone } from "../../util/clone.mjs";
32
+
33
+ import { SheetStyleSheet } from "./stylesheet/sheet.mjs";
34
+
35
+ export { Sheet };
36
+
37
+ const gridElementSymbol = Symbol("sheetGrid");
38
+ const gridWrapperSymbol = Symbol("sheetGridWrapper");
39
+ const spacerElementSymbol = Symbol("sheetGridSpacer");
40
+ const addRowButtonSymbol = Symbol("sheetAddRowButton");
41
+ const addColumnButtonSymbol = Symbol("sheetAddColumnButton");
42
+ const lastSnapshotSymbol = Symbol("sheetLastSnapshot");
43
+ const resizeStateSymbol = Symbol("sheetResizeState");
44
+ const skipRenderSymbol = Symbol("sheetSkipRender");
45
+ const lastViewportSymbol = Symbol("sheetLastViewport");
46
+ const scrollFrameSymbol = Symbol("sheetScrollFrame");
47
+ const forceRenderSymbol = Symbol("sheetForceRender");
48
+ const resizeFrameSymbol = Symbol("sheetResizeFrame");
49
+
50
+ class Sheet extends CustomControl {
51
+ static get [instanceSymbol]() {
52
+ return Symbol.for("@schukai/monster/components/form/sheet@@instance");
53
+ }
54
+
55
+ [assembleMethodSymbol]() {
56
+ super[assembleMethodSymbol]();
57
+ initControlReferences.call(this);
58
+ initEventHandler.call(this);
59
+ initOptionObserver.call(this);
60
+ updateControl.call(this);
61
+ return this;
62
+ }
63
+
64
+ get defaults() {
65
+ return Object.assign({}, super.defaults, {
66
+ templates: { main: getTemplate() },
67
+ labels: getTranslations(),
68
+ classes: {
69
+ button: "monster-button-outline-primary",
70
+ },
71
+ features: {
72
+ addRows: false,
73
+ addColumns: false,
74
+ editable: true,
75
+ resizeRows: true,
76
+ resizeColumns: true,
77
+ virtualize: false,
78
+ },
79
+ virtualization: {
80
+ rowBuffer: 4,
81
+ columnBuffer: 2,
82
+ },
83
+ columns: defaultColumns(3),
84
+ rows: defaultRows(3),
85
+ sizes: {
86
+ columns: {},
87
+ rows: {},
88
+ rowHeaderWidth: 56,
89
+ headerHeight: 32,
90
+ },
91
+ constraints: {
92
+ minColumnWidth: 64,
93
+ maxColumnWidth: 360,
94
+ minRowHeight: 28,
95
+ maxRowHeight: 120,
96
+ },
97
+ value: { cells: {}, formulas: {} },
98
+ disabled: false,
99
+ cell: {
100
+ placeholder: "",
101
+ },
102
+ });
103
+ }
104
+
105
+ static getTag() {
106
+ return "monster-sheet";
107
+ }
108
+
109
+ static getCSSStyleSheet() {
110
+ return [SheetStyleSheet];
111
+ }
112
+
113
+ get value() {
114
+ return this.getOption("value");
115
+ }
116
+
117
+ set value(value) {
118
+ this.setOption("value", value);
119
+ this[forceRenderSymbol] = true;
120
+ setFormValueSafe.call(this);
121
+ updateControl.call(this);
122
+ }
123
+ }
124
+
125
+ function initControlReferences() {
126
+ const root = this.shadowRoot;
127
+ this[gridElementSymbol] = root.querySelector(`[${ATTRIBUTE_ROLE}=grid]`);
128
+ this[gridWrapperSymbol] = root.querySelector(
129
+ `[${ATTRIBUTE_ROLE}=grid-wrapper]`,
130
+ );
131
+ if (this[gridWrapperSymbol]) {
132
+ let spacer = this[gridWrapperSymbol].querySelector(
133
+ `[${ATTRIBUTE_ROLE}=spacer]`,
134
+ );
135
+ if (!spacer) {
136
+ spacer = document.createElement("div");
137
+ spacer.setAttribute(ATTRIBUTE_ROLE, "spacer");
138
+ spacer.setAttribute("part", "spacer");
139
+ this[gridWrapperSymbol].prepend(spacer);
140
+ }
141
+ this[spacerElementSymbol] = spacer;
142
+ }
143
+ this[addRowButtonSymbol] = root.querySelector(`[${ATTRIBUTE_ROLE}=add-row]`);
144
+ this[addColumnButtonSymbol] = root.querySelector(
145
+ `[${ATTRIBUTE_ROLE}=add-column]`,
146
+ );
147
+ }
148
+
149
+ function initEventHandler() {
150
+ this[addRowButtonSymbol].addEventListener("click", () => {
151
+ if (!canAddRows.call(this)) return;
152
+ addRow.call(this);
153
+ });
154
+
155
+ this[addColumnButtonSymbol].addEventListener("click", () => {
156
+ if (!canAddColumns.call(this)) return;
157
+ addColumn.call(this);
158
+ });
159
+
160
+ this[gridElementSymbol].addEventListener("input", (event) => {
161
+ const input = event.target;
162
+ if (!(input instanceof HTMLInputElement)) return;
163
+ const cell = input.closest(`[${ATTRIBUTE_ROLE}=cell]`);
164
+ if (!cell) return;
165
+ const rowId = cell.dataset.rowId;
166
+ const colId = cell.dataset.colId;
167
+ if (!rowId || !colId) return;
168
+ setCellValue.call(this, rowId, colId, input.value);
169
+ });
170
+
171
+ this[gridElementSymbol].addEventListener("focusin", (event) => {
172
+ const input = event.target;
173
+ if (!(input instanceof HTMLInputElement)) return;
174
+ const cell = input.closest(`[${ATTRIBUTE_ROLE}=cell]`);
175
+ if (!cell) return;
176
+ const rowId = cell.dataset.rowId;
177
+ const colId = cell.dataset.colId;
178
+ if (!rowId || !colId) return;
179
+ const data = normalizeValue.call(this);
180
+ const formula = data.formulas?.[rowId]?.[colId];
181
+ if (isString(formula)) {
182
+ input.value = formula;
183
+ }
184
+ });
185
+
186
+ this[gridElementSymbol].addEventListener("focusout", (event) => {
187
+ const input = event.target;
188
+ if (!(input instanceof HTMLInputElement)) return;
189
+ const cell = input.closest(`[${ATTRIBUTE_ROLE}=cell]`);
190
+ if (!cell) return;
191
+ const rowId = cell.dataset.rowId;
192
+ const colId = cell.dataset.colId;
193
+ if (!rowId || !colId) return;
194
+ refreshDisplayValues.call(this);
195
+ });
196
+
197
+ this[gridElementSymbol].addEventListener("pointerdown", (event) => {
198
+ const columnHandle = event.target.closest(
199
+ `[${ATTRIBUTE_ROLE}=column-resize]`,
200
+ );
201
+ if (columnHandle) {
202
+ if (!this.getOption("features.resizeColumns", true)) return;
203
+ startColumnResize.call(this, event, columnHandle);
204
+ return;
205
+ }
206
+
207
+ const rowHandle = event.target.closest(`[${ATTRIBUTE_ROLE}=row-resize]`);
208
+ if (rowHandle) {
209
+ if (!this.getOption("features.resizeRows", true)) return;
210
+ startRowResize.call(this, event, rowHandle);
211
+ }
212
+ });
213
+
214
+ if (this[gridWrapperSymbol]) {
215
+ this[gridWrapperSymbol].addEventListener("scroll", () => {
216
+ if (!this.getOption("features.virtualize", false)) return;
217
+ if (this[scrollFrameSymbol]) return;
218
+ this[scrollFrameSymbol] = requestAnimationFrame(() => {
219
+ this[scrollFrameSymbol] = null;
220
+ updateControl.call(this);
221
+ });
222
+ });
223
+ }
224
+ }
225
+
226
+ function initOptionObserver() {
227
+ this[lastSnapshotSymbol] = "";
228
+ this.attachObserver(
229
+ new Observer(() => {
230
+ const snapshot = JSON.stringify({
231
+ value: this.getOption("value"),
232
+ columns: this.getOption("columns"),
233
+ rows: this.getOption("rows"),
234
+ sizes: this.getOption("sizes"),
235
+ features: this.getOption("features"),
236
+ disabled: this.getOption("disabled"),
237
+ classes: this.getOption("classes"),
238
+ labels: this.getOption("labels"),
239
+ cell: this.getOption("cell"),
240
+ });
241
+ if (this[skipRenderSymbol]) {
242
+ this[lastSnapshotSymbol] = snapshot;
243
+ this[skipRenderSymbol] = false;
244
+ return;
245
+ }
246
+ if (snapshot === this[lastSnapshotSymbol]) return;
247
+ this[lastSnapshotSymbol] = snapshot;
248
+ updateControl.call(this);
249
+ }),
250
+ );
251
+ }
252
+
253
+ function updateControl() {
254
+ const normalized = normalizeValue.call(this);
255
+ const current = this.getOption("value");
256
+ if (!isSameValue(current, normalized)) {
257
+ this.setOption("value", normalized);
258
+ setFormValueSafe.call(this);
259
+ }
260
+
261
+ const virtualize = this.getOption("features.virtualize", false) === true;
262
+ if (virtualize) {
263
+ this.setAttribute("data-virtualized", "");
264
+ if (this[forceRenderSymbol]) {
265
+ this[lastViewportSymbol] = null;
266
+ this[forceRenderSymbol] = false;
267
+ }
268
+ renderVirtual.call(this, normalized);
269
+ } else {
270
+ if (this.hasAttribute("data-virtualized")) {
271
+ this.removeAttribute("data-virtualized");
272
+ }
273
+ if (this[spacerElementSymbol]) {
274
+ this[spacerElementSymbol].style.width = "0px";
275
+ this[spacerElementSymbol].style.height = "0px";
276
+ }
277
+ this[lastViewportSymbol] = null;
278
+ const displayCells = buildDisplayCells.call(this, normalized);
279
+ renderGrid.call(this, normalized, displayCells);
280
+ }
281
+ syncToolbarState.call(this);
282
+ applyDisabledState.call(this);
283
+ }
284
+
285
+ function syncToolbarState() {
286
+ const addRow = canAddRows.call(this);
287
+ const addCol = canAddColumns.call(this);
288
+ const labels = this.getOption("labels", {});
289
+ const buttonClass = this.getOption("classes.button", "");
290
+
291
+ this[addRowButtonSymbol].hidden = !addRow;
292
+ this[addColumnButtonSymbol].hidden = !addCol;
293
+ this[addRowButtonSymbol].disabled =
294
+ this.getOption("disabled", false) || !addRow;
295
+ this[addColumnButtonSymbol].disabled =
296
+ this.getOption("disabled", false) || !addCol;
297
+
298
+ this[addRowButtonSymbol].className = buttonClass;
299
+ this[addColumnButtonSymbol].className = buttonClass;
300
+ if (isString(labels.addRow)) {
301
+ this[addRowButtonSymbol].textContent = labels.addRow;
302
+ }
303
+ if (isString(labels.addColumn)) {
304
+ this[addColumnButtonSymbol].textContent = labels.addColumn;
305
+ }
306
+ }
307
+
308
+ function applyDisabledState() {
309
+ const disabled = this.getOption("disabled", false);
310
+ const editable = this.getOption("features.editable", true);
311
+ const inputs = this.shadowRoot.querySelectorAll(
312
+ `[${ATTRIBUTE_ROLE}=cell-input]`,
313
+ );
314
+ inputs.forEach((input) => {
315
+ input.disabled = disabled || !editable;
316
+ });
317
+ }
318
+
319
+ function renderGrid(value, displayCells) {
320
+ const grid = this[gridElementSymbol];
321
+ if (!grid) return;
322
+ grid.style.position = "";
323
+ grid.style.left = "";
324
+ grid.style.top = "";
325
+ grid.style.transform = "";
326
+ grid.textContent = "";
327
+
328
+ const fragment = document.createDocumentFragment();
329
+ fragment.appendChild(buildCornerCell.call(this));
330
+
331
+ const lastColumnId =
332
+ value.columns.length > 0
333
+ ? value.columns[value.columns.length - 1].id
334
+ : null;
335
+
336
+ for (const col of value.columns) {
337
+ fragment.appendChild(
338
+ buildColumnHeader.call(this, col, col.id === lastColumnId),
339
+ );
340
+ }
341
+
342
+ for (const row of value.rows) {
343
+ fragment.appendChild(buildRowHeader.call(this, row));
344
+ for (const col of value.columns) {
345
+ fragment.appendChild(
346
+ buildCell.call(this, row, col, displayCells, col.id === lastColumnId),
347
+ );
348
+ }
349
+ }
350
+
351
+ grid.appendChild(fragment);
352
+ applyGridTemplate.call(this, value);
353
+ }
354
+
355
+ function renderVirtual(value) {
356
+ const grid = this[gridElementSymbol];
357
+ const wrapper = this[gridWrapperSymbol];
358
+ const spacer = this[spacerElementSymbol];
359
+ if (!grid || !wrapper || !spacer) return;
360
+
361
+ const sizes = getVirtualSizes.call(this, value);
362
+ const viewportWidth = wrapper.clientWidth;
363
+ const viewportHeight = wrapper.clientHeight;
364
+ if (viewportWidth === 0 || viewportHeight === 0) {
365
+ if (!this[scrollFrameSymbol]) {
366
+ this[scrollFrameSymbol] = requestAnimationFrame(() => {
367
+ this[scrollFrameSymbol] = null;
368
+ updateControl.call(this);
369
+ });
370
+ }
371
+ return;
372
+ }
373
+ const scrollLeft = wrapper.scrollLeft;
374
+ const scrollTop = wrapper.scrollTop;
375
+ const bufferCols = getSizeNumber(
376
+ this.getOption("virtualization.columnBuffer"),
377
+ 2,
378
+ );
379
+ const bufferRows = getSizeNumber(
380
+ this.getOption("virtualization.rowBuffer"),
381
+ 4,
382
+ );
383
+
384
+ const visible = getVisibleRange(
385
+ sizes.columnOffsets,
386
+ sizes.rowOffsets,
387
+ scrollLeft - sizes.rowHeaderWidth,
388
+ scrollTop - sizes.headerHeight,
389
+ viewportWidth,
390
+ viewportHeight,
391
+ );
392
+
393
+ const colStart = Math.max(0, visible.colStart - bufferCols);
394
+ const colEnd = Math.min(
395
+ value.columns.length - 1,
396
+ visible.colEnd + bufferCols,
397
+ );
398
+ const rowStart = Math.max(0, visible.rowStart - bufferRows);
399
+ const rowEnd = Math.min(value.rows.length - 1, visible.rowEnd + bufferRows);
400
+
401
+ const viewportKey = `${colStart}-${colEnd}-${rowStart}-${rowEnd}-${sizes.totalWidth}-${sizes.totalHeight}`;
402
+ if (this[lastViewportSymbol] === viewportKey) return;
403
+ this[lastViewportSymbol] = viewportKey;
404
+
405
+ spacer.style.width = `${sizes.totalWidth}px`;
406
+ spacer.style.height = `${sizes.totalHeight}px`;
407
+
408
+ const offsetX = sizes.rowHeaderWidth + sizes.columnOffsets[colStart];
409
+ const offsetY = sizes.headerHeight + sizes.rowOffsets[rowStart];
410
+
411
+ grid.style.position = "absolute";
412
+ grid.style.left = "0";
413
+ grid.style.top = "0";
414
+ grid.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
415
+
416
+ const visibleColumns = value.columns.slice(colStart, colEnd + 1);
417
+ const visibleRows = value.rows.slice(rowStart, rowEnd + 1);
418
+
419
+ const visibleWidths = visibleColumns.map(
420
+ (col, index) => `${sizes.columnWidths[colStart + index]}px`,
421
+ );
422
+ const visibleHeights = visibleRows.map(
423
+ (row, index) => `${sizes.rowHeights[rowStart + index]}px`,
424
+ );
425
+
426
+ grid.style.gridTemplateColumns = `${sizes.rowHeaderWidth}px ${visibleWidths.join(" ")}`;
427
+ grid.style.gridTemplateRows = `${sizes.headerHeight}px ${visibleHeights.join(" ")}`;
428
+
429
+ grid.textContent = "";
430
+ const fragment = document.createDocumentFragment();
431
+ fragment.appendChild(buildCornerCell.call(this));
432
+
433
+ const lastVisibleColumnId =
434
+ visibleColumns.length > 0
435
+ ? visibleColumns[visibleColumns.length - 1].id
436
+ : null;
437
+
438
+ for (const col of visibleColumns) {
439
+ fragment.appendChild(
440
+ buildColumnHeader.call(this, col, col.id === lastVisibleColumnId),
441
+ );
442
+ }
443
+
444
+ const errors = [];
445
+ const getDisplayValue = (rowId, colId) => {
446
+ const formula = value.formulas?.[rowId]?.[colId];
447
+ if (isString(formula)) {
448
+ const evaluated = evaluateFormula.call(this, value, formula);
449
+ if (Number.isFinite(evaluated)) return evaluated;
450
+ errors.push({ rowId, colId, formula });
451
+ return "#ERR";
452
+ }
453
+ const raw = value.cells?.[rowId]?.[colId];
454
+ return raw === undefined || raw === null ? "" : raw;
455
+ };
456
+
457
+ for (const row of visibleRows) {
458
+ fragment.appendChild(buildRowHeader.call(this, row));
459
+ for (const col of visibleColumns) {
460
+ fragment.appendChild(
461
+ buildCell.call(
462
+ this,
463
+ row,
464
+ col,
465
+ getDisplayValue,
466
+ col.id === lastVisibleColumnId,
467
+ ),
468
+ );
469
+ }
470
+ }
471
+
472
+ if (errors.length > 0) {
473
+ fireCustomEvent(this, "monster-sheet-formula-error", { errors });
474
+ }
475
+
476
+ grid.appendChild(fragment);
477
+ }
478
+
479
+ function buildCornerCell() {
480
+ const labels = this.getOption("labels", {});
481
+ const cell = document.createElement("div");
482
+ cell.setAttribute(ATTRIBUTE_ROLE, "corner");
483
+ cell.setAttribute("part", "corner");
484
+ cell.textContent = isString(labels.corner) ? labels.corner : "";
485
+ return cell;
486
+ }
487
+
488
+ function buildColumnHeader(column, isLastColumn) {
489
+ const cell = document.createElement("div");
490
+ cell.setAttribute(ATTRIBUTE_ROLE, "column-header");
491
+ cell.setAttribute("part", "column-header");
492
+ cell.dataset.colId = column.id;
493
+ if (isLastColumn) {
494
+ cell.dataset.lastColumn = "true";
495
+ }
496
+ cell.textContent = column.label ?? column.id;
497
+
498
+ if (this.getOption("features.resizeColumns", true)) {
499
+ const handle = document.createElement("span");
500
+ handle.setAttribute(ATTRIBUTE_ROLE, "column-resize");
501
+ handle.setAttribute("part", "column-resize");
502
+ handle.dataset.colId = column.id;
503
+ cell.appendChild(handle);
504
+ }
505
+ return cell;
506
+ }
507
+
508
+ function buildRowHeader(row) {
509
+ const cell = document.createElement("div");
510
+ cell.setAttribute(ATTRIBUTE_ROLE, "row-header");
511
+ cell.setAttribute("part", "row-header");
512
+ cell.dataset.rowId = row.id;
513
+ cell.textContent = row.label ?? row.id;
514
+
515
+ if (this.getOption("features.resizeRows", true)) {
516
+ const handle = document.createElement("span");
517
+ handle.setAttribute(ATTRIBUTE_ROLE, "row-resize");
518
+ handle.setAttribute("part", "row-resize");
519
+ handle.dataset.rowId = row.id;
520
+ cell.appendChild(handle);
521
+ }
522
+ return cell;
523
+ }
524
+
525
+ function buildCell(row, column, cells, isLastColumn) {
526
+ const wrapper = document.createElement("div");
527
+ wrapper.setAttribute(ATTRIBUTE_ROLE, "cell");
528
+ wrapper.setAttribute("part", "cell");
529
+ wrapper.dataset.rowId = row.id;
530
+ wrapper.dataset.colId = column.id;
531
+ if (isLastColumn) {
532
+ wrapper.dataset.lastColumn = "true";
533
+ }
534
+
535
+ const input = document.createElement("input");
536
+ input.setAttribute(ATTRIBUTE_ROLE, "cell-input");
537
+ input.setAttribute("part", "cell-input");
538
+ input.type = "text";
539
+ input.autocomplete = "off";
540
+ input.inputMode = "text";
541
+ input.placeholder = this.getOption("cell.placeholder", "");
542
+ const value =
543
+ typeof cells === "function"
544
+ ? cells(row.id, column.id)
545
+ : cells?.[row.id]?.[column.id];
546
+ input.value = value === undefined || value === null ? "" : String(value);
547
+ input.disabled =
548
+ this.getOption("disabled", false) ||
549
+ !this.getOption("features.editable", true);
550
+
551
+ wrapper.appendChild(input);
552
+ return wrapper;
553
+ }
554
+
555
+ function applyGridTemplate(value) {
556
+ const sizes = this.getOption("sizes", {});
557
+ const rowHeaderWidth = getRowHeaderWidthNumber.call(
558
+ this,
559
+ sizes.rowHeaderWidth,
560
+ value.rows,
561
+ );
562
+ const headerHeight = getSizeNumber(sizes.headerHeight, 32);
563
+
564
+ const columnSizes = value.columns.map((col) =>
565
+ getColumnWidth.call(this, col.id),
566
+ );
567
+ const rowSizes = value.rows.map((row) => getRowHeight.call(this, row.id));
568
+
569
+ this[gridElementSymbol].style.gridTemplateColumns =
570
+ `${rowHeaderWidth}px ${columnSizes.join(" ")}`;
571
+ this[gridElementSymbol].style.gridTemplateRows =
572
+ `${headerHeight}px ${rowSizes.join(" ")}`;
573
+ }
574
+
575
+ function getVirtualSizes(value) {
576
+ const sizes = this.getOption("sizes", {});
577
+ const rowHeaderWidth = getRowHeaderWidthNumber.call(
578
+ this,
579
+ sizes.rowHeaderWidth,
580
+ value.rows,
581
+ );
582
+ const headerHeight = getSizeNumber(sizes.headerHeight, 32);
583
+ const columnWidths = value.columns.map((col) =>
584
+ getColumnWidthNumber.call(this, col.id),
585
+ );
586
+ const rowHeights = value.rows.map((row) =>
587
+ getRowHeightNumber.call(this, row.id),
588
+ );
589
+
590
+ const columnOffsets = buildOffsets(columnWidths);
591
+ const rowOffsets = buildOffsets(rowHeights);
592
+
593
+ const totalWidth =
594
+ rowHeaderWidth +
595
+ (columnWidths.length > 0
596
+ ? columnOffsets[columnOffsets.length - 1] +
597
+ columnWidths[columnWidths.length - 1]
598
+ : 0);
599
+ const totalHeight =
600
+ headerHeight +
601
+ (rowHeights.length > 0
602
+ ? rowOffsets[rowOffsets.length - 1] + rowHeights[rowHeights.length - 1]
603
+ : 0);
604
+
605
+ return {
606
+ rowHeaderWidth,
607
+ headerHeight,
608
+ columnWidths,
609
+ rowHeights,
610
+ columnOffsets,
611
+ rowOffsets,
612
+ totalWidth,
613
+ totalHeight,
614
+ };
615
+ }
616
+
617
+ function buildOffsets(values) {
618
+ const offsets = [];
619
+ let current = 0;
620
+ for (const value of values) {
621
+ offsets.push(current);
622
+ current += value;
623
+ }
624
+ return offsets;
625
+ }
626
+
627
+ function getVisibleRange(
628
+ columnOffsets,
629
+ rowOffsets,
630
+ scrollLeft,
631
+ scrollTop,
632
+ viewportWidth,
633
+ viewportHeight,
634
+ ) {
635
+ const colStart = findStartIndex(columnOffsets, Math.max(0, scrollLeft));
636
+ const colEnd = findEndIndex(
637
+ columnOffsets,
638
+ Math.max(0, scrollLeft) + viewportWidth,
639
+ );
640
+ const rowStart = findStartIndex(rowOffsets, Math.max(0, scrollTop));
641
+ const rowEnd = findEndIndex(
642
+ rowOffsets,
643
+ Math.max(0, scrollTop) + viewportHeight,
644
+ );
645
+ return { colStart, colEnd, rowStart, rowEnd };
646
+ }
647
+
648
+ function findStartIndex(offsets, value) {
649
+ let low = 0;
650
+ let high = offsets.length - 1;
651
+ let result = 0;
652
+ while (low <= high) {
653
+ const mid = Math.floor((low + high) / 2);
654
+ if (offsets[mid] <= value) {
655
+ result = mid;
656
+ low = mid + 1;
657
+ } else {
658
+ high = mid - 1;
659
+ }
660
+ }
661
+ return result;
662
+ }
663
+
664
+ function findEndIndex(offsets, value) {
665
+ let low = 0;
666
+ let high = offsets.length - 1;
667
+ let result = offsets.length - 1;
668
+ while (low <= high) {
669
+ const mid = Math.floor((low + high) / 2);
670
+ if (offsets[mid] < value) {
671
+ low = mid + 1;
672
+ } else {
673
+ result = mid;
674
+ high = mid - 1;
675
+ }
676
+ }
677
+ return Math.max(0, result);
678
+ }
679
+
680
+ function normalizeValue() {
681
+ const optionsValue = this.getOption("value");
682
+ const columns = normalizeColumns(
683
+ optionsValue?.columns ?? this.getOption("columns"),
684
+ );
685
+ const rows = normalizeRows(optionsValue?.rows ?? this.getOption("rows"));
686
+ const cells = isObject(optionsValue?.cells) ? clone(optionsValue.cells) : {};
687
+ const formulas = isObject(optionsValue?.formulas)
688
+ ? clone(optionsValue.formulas)
689
+ : {};
690
+
691
+ return { columns, rows, cells, formulas };
692
+ }
693
+
694
+ function normalizeColumns(columns) {
695
+ const list = isArray(columns) ? columns : [];
696
+ return list.map((col, index) => {
697
+ if (isString(col)) {
698
+ return { id: col, label: col };
699
+ }
700
+ if (isObject(col)) {
701
+ const id = isString(col.id) ? col.id : nextColumnLabel(index);
702
+ return { id, label: col.label ?? id };
703
+ }
704
+ const id = nextColumnLabel(index);
705
+ return { id, label: id };
706
+ });
707
+ }
708
+
709
+ function normalizeRows(rows) {
710
+ const list = isArray(rows) ? rows : [];
711
+ return list.map((row, index) => {
712
+ if (isString(row)) {
713
+ return { id: row, label: row };
714
+ }
715
+ if (isObject(row)) {
716
+ const id = isString(row.id) ? row.id : nextRowLabel(index);
717
+ return { id, label: row.label ?? id };
718
+ }
719
+ const id = nextRowLabel(index);
720
+ return { id, label: id };
721
+ });
722
+ }
723
+
724
+ function setCellValue(rowId, colId, value) {
725
+ const data = normalizeValue.call(this);
726
+ if (!data.cells[rowId]) data.cells[rowId] = {};
727
+ if (!data.formulas[rowId]) data.formulas[rowId] = {};
728
+ const next = isString(value) ? value : String(value);
729
+ if (next.trim().startsWith("=")) {
730
+ data.formulas[rowId][colId] = next.trim();
731
+ delete data.cells[rowId][colId];
732
+ } else {
733
+ delete data.formulas[rowId][colId];
734
+ data.cells[rowId][colId] = value;
735
+ }
736
+ this.setOption("value", data);
737
+ this[skipRenderSymbol] = true;
738
+ this[forceRenderSymbol] = true;
739
+ setFormValueSafe.call(this);
740
+ fireCustomEvent(this, "monster-sheet-change", {
741
+ value: data,
742
+ cell: { rowId, colId, value },
743
+ });
744
+ }
745
+
746
+ function addRow() {
747
+ const data = normalizeValue.call(this);
748
+ const newRow = createRow(data.rows.length, data.rows);
749
+ data.rows.push(newRow);
750
+ this.setOption("value", data);
751
+ this[skipRenderSymbol] = true;
752
+ setFormValueSafe.call(this);
753
+ fireCustomEvent(this, "monster-sheet-add-row", { row: newRow, value: data });
754
+ updateControl.call(this);
755
+ }
756
+
757
+ function addColumn() {
758
+ const data = normalizeValue.call(this);
759
+ const newColumn = createColumn(data.columns.length, data.columns);
760
+ data.columns.push(newColumn);
761
+ this.setOption("value", data);
762
+ this[skipRenderSymbol] = true;
763
+ setFormValueSafe.call(this);
764
+ fireCustomEvent(this, "monster-sheet-add-column", {
765
+ column: newColumn,
766
+ value: data,
767
+ });
768
+ updateControl.call(this);
769
+ }
770
+
771
+ function canAddRows() {
772
+ return this.getOption("features.addRows", false) === true;
773
+ }
774
+
775
+ function canAddColumns() {
776
+ return this.getOption("features.addColumns", false) === true;
777
+ }
778
+
779
+ function createColumn(index, columns) {
780
+ const label = nextColumnLabel(index);
781
+ const existing = new Set(columns.map((col) => col.id));
782
+ const id = existing.has(label) ? new ID("column-").toString() : label;
783
+ return { id, label };
784
+ }
785
+
786
+ function createRow(index, rows) {
787
+ const label = nextRowLabel(index);
788
+ const existing = new Set(rows.map((row) => row.id));
789
+ const id = existing.has(label) ? new ID("row-").toString() : label;
790
+ return { id, label };
791
+ }
792
+
793
+ function nextColumnLabel(index) {
794
+ let value = index + 1;
795
+ let label = "";
796
+ while (value > 0) {
797
+ const mod = (value - 1) % 26;
798
+ label = String.fromCharCode(65 + mod) + label;
799
+ value = Math.floor((value - 1) / 26);
800
+ }
801
+ return label;
802
+ }
803
+
804
+ function nextRowLabel(index) {
805
+ return String(index + 1);
806
+ }
807
+
808
+ function defaultColumns(count) {
809
+ return Array.from({ length: count }, (_, i) => {
810
+ const label = nextColumnLabel(i);
811
+ return { id: label, label };
812
+ });
813
+ }
814
+
815
+ function defaultRows(count) {
816
+ return Array.from({ length: count }, (_, i) => {
817
+ const label = nextRowLabel(i);
818
+ return { id: label, label };
819
+ });
820
+ }
821
+
822
+ function getSizeNumber(value, fallback) {
823
+ const n = Number(value);
824
+ return Number.isFinite(n) ? n : fallback;
825
+ }
826
+
827
+ function getColumnWidth(columnId) {
828
+ const sizes = this.getOption("sizes.columns", {});
829
+ const width = getSizeNumber(sizes?.[columnId], 120);
830
+ const min = getSizeNumber(this.getOption("constraints.minColumnWidth"), 64);
831
+ const max = getSizeNumber(this.getOption("constraints.maxColumnWidth"), 360);
832
+ return `${clamp(width, min, max)}px`;
833
+ }
834
+
835
+ function getColumnWidthNumber(columnId) {
836
+ const sizes = this.getOption("sizes.columns", {});
837
+ const width = getSizeNumber(sizes?.[columnId], 120);
838
+ const min = getSizeNumber(this.getOption("constraints.minColumnWidth"), 64);
839
+ const max = getSizeNumber(this.getOption("constraints.maxColumnWidth"), 360);
840
+ return clamp(width, min, max);
841
+ }
842
+
843
+ function getRowHeight(rowId) {
844
+ const sizes = this.getOption("sizes.rows", {});
845
+ const height = getSizeNumber(sizes?.[rowId], 32);
846
+ const min = getSizeNumber(this.getOption("constraints.minRowHeight"), 28);
847
+ const max = getSizeNumber(this.getOption("constraints.maxRowHeight"), 120);
848
+ return `${clamp(height, min, max)}px`;
849
+ }
850
+
851
+ function getRowHeightNumber(rowId) {
852
+ const sizes = this.getOption("sizes.rows", {});
853
+ const height = getSizeNumber(sizes?.[rowId], 32);
854
+ const min = getSizeNumber(this.getOption("constraints.minRowHeight"), 28);
855
+ const max = getSizeNumber(this.getOption("constraints.maxRowHeight"), 120);
856
+ return clamp(height, min, max);
857
+ }
858
+
859
+ function getRowHeaderWidthNumber(value, rows) {
860
+ if (value === "auto" || value === null || value === undefined) {
861
+ const maxLen = getMaxRowLabelLength(rows);
862
+ const base = 16;
863
+ const charWidth = 8;
864
+ return Math.max(56, base + maxLen * charWidth);
865
+ }
866
+ return getSizeNumber(value, 56);
867
+ }
868
+
869
+ function getMaxRowLabelLength(rows) {
870
+ if (!isArray(rows) || rows.length === 0) return 1;
871
+ const last = rows[rows.length - 1];
872
+ const label = isString(last?.label) ? last.label : last?.id;
873
+ const text = label === undefined || label === null ? "" : String(label);
874
+ if (/^\d+$/.test(text)) {
875
+ return String(rows.length).length;
876
+ }
877
+ return text.length;
878
+ }
879
+
880
+ function clamp(value, min, max) {
881
+ return Math.min(Math.max(value, min), max);
882
+ }
883
+
884
+ function startColumnResize(event, handle) {
885
+ const colId = handle.dataset.colId;
886
+ if (!colId) return;
887
+ event.preventDefault();
888
+ event.stopPropagation();
889
+ const grid = this[gridElementSymbol];
890
+ const headerCell = handle.parentElement;
891
+ if (!headerCell) return;
892
+ const startWidth = headerCell.getBoundingClientRect().width;
893
+ this.setAttribute("data-resizing", "");
894
+ this[resizeStateSymbol] = {
895
+ kind: "column",
896
+ id: colId,
897
+ start: event.clientX,
898
+ startSize: startWidth,
899
+ };
900
+ grid.setPointerCapture(event.pointerId);
901
+ grid.addEventListener("pointermove", handleResizeMove);
902
+ grid.addEventListener("pointerup", handleResizeEnd);
903
+ grid.addEventListener("pointercancel", handleResizeEnd);
904
+ }
905
+
906
+ function startRowResize(event, handle) {
907
+ const rowId = handle.dataset.rowId;
908
+ if (!rowId) return;
909
+ event.preventDefault();
910
+ event.stopPropagation();
911
+ const headerCell = handle.parentElement;
912
+ if (!headerCell) return;
913
+ const startHeight = headerCell.getBoundingClientRect().height;
914
+ const grid = this[gridElementSymbol];
915
+ this.setAttribute("data-resizing", "");
916
+ this[resizeStateSymbol] = {
917
+ kind: "row",
918
+ id: rowId,
919
+ start: event.clientY,
920
+ startSize: startHeight,
921
+ };
922
+ grid.setPointerCapture(event.pointerId);
923
+ grid.addEventListener("pointermove", handleResizeMove);
924
+ grid.addEventListener("pointerup", handleResizeEnd);
925
+ grid.addEventListener("pointercancel", handleResizeEnd);
926
+ }
927
+
928
+ function handleResizeMove(event) {
929
+ const grid = event.currentTarget;
930
+ const sheet = grid.getRootNode().host;
931
+ if (!sheet || !sheet[resizeStateSymbol]) return;
932
+ const state = sheet[resizeStateSymbol];
933
+ if (state.kind === "column") {
934
+ const delta = event.clientX - state.start;
935
+ const size = state.startSize + delta;
936
+ setColumnSize.call(sheet, state.id, size);
937
+ } else if (state.kind === "row") {
938
+ const delta = event.clientY - state.start;
939
+ const size = state.startSize + delta;
940
+ setRowSize.call(sheet, state.id, size);
941
+ }
942
+ }
943
+
944
+ function handleResizeEnd(event) {
945
+ const grid = event.currentTarget;
946
+ const sheet = grid.getRootNode().host;
947
+ if (!sheet) return;
948
+ if (sheet.hasAttribute("data-resizing")) {
949
+ sheet.removeAttribute("data-resizing");
950
+ }
951
+ grid.releasePointerCapture(event.pointerId);
952
+ grid.removeEventListener("pointermove", handleResizeMove);
953
+ grid.removeEventListener("pointerup", handleResizeEnd);
954
+ grid.removeEventListener("pointercancel", handleResizeEnd);
955
+ sheet[resizeStateSymbol] = null;
956
+ }
957
+
958
+ function setColumnSize(columnId, size) {
959
+ const min = getSizeNumber(this.getOption("constraints.minColumnWidth"), 64);
960
+ const max = getSizeNumber(this.getOption("constraints.maxColumnWidth"), 360);
961
+ const next = clamp(size, min, max);
962
+ this.setOption(`sizes.columns.${columnId}`, next);
963
+ this[skipRenderSymbol] = true;
964
+ if (this.getOption("features.virtualize", false) === true) {
965
+ scheduleVirtualResizeRender.call(this);
966
+ } else {
967
+ applyGridTemplate.call(this, normalizeValue.call(this));
968
+ }
969
+ fireCustomEvent(this, "monster-sheet-resize-column", {
970
+ columnId,
971
+ width: next,
972
+ });
973
+ }
974
+
975
+ function setRowSize(rowId, size) {
976
+ const min = getSizeNumber(this.getOption("constraints.minRowHeight"), 28);
977
+ const max = getSizeNumber(this.getOption("constraints.maxRowHeight"), 120);
978
+ const next = clamp(size, min, max);
979
+ this.setOption(`sizes.rows.${rowId}`, next);
980
+ this[skipRenderSymbol] = true;
981
+ if (this.getOption("features.virtualize", false) === true) {
982
+ scheduleVirtualResizeRender.call(this);
983
+ } else {
984
+ applyGridTemplate.call(this, normalizeValue.call(this));
985
+ }
986
+ fireCustomEvent(this, "monster-sheet-resize-row", {
987
+ rowId,
988
+ height: next,
989
+ });
990
+ }
991
+
992
+ function setFormValueSafe() {
993
+ try {
994
+ this.setFormValue(this.value);
995
+ } catch (e) {
996
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
997
+ }
998
+ }
999
+
1000
+ function isSameValue(a, b) {
1001
+ try {
1002
+ return JSON.stringify(a) === JSON.stringify(b);
1003
+ } catch (e) {
1004
+ return false;
1005
+ }
1006
+ }
1007
+
1008
+ function refreshDisplayValues() {
1009
+ const data = normalizeValue.call(this);
1010
+ const virtualize = this.getOption("features.virtualize", false) === true;
1011
+ const displayCells = virtualize ? null : buildDisplayCells.call(this, data);
1012
+ const active = this.shadowRoot?.activeElement;
1013
+ const inputs = this.shadowRoot.querySelectorAll(
1014
+ `[${ATTRIBUTE_ROLE}=cell-input]`,
1015
+ );
1016
+
1017
+ inputs.forEach((input) => {
1018
+ if (!(input instanceof HTMLInputElement)) return;
1019
+ if (input === active) return;
1020
+ const cell = input.closest(`[${ATTRIBUTE_ROLE}=cell]`);
1021
+ if (!cell) return;
1022
+ const rowId = cell.dataset.rowId;
1023
+ const colId = cell.dataset.colId;
1024
+ if (!rowId || !colId) return;
1025
+ const value = virtualize
1026
+ ? getCellDisplayValue.call(this, data, rowId, colId)
1027
+ : displayCells?.[rowId]?.[colId];
1028
+ input.value = value === undefined || value === null ? "" : String(value);
1029
+ });
1030
+ }
1031
+
1032
+ function scheduleVirtualResizeRender() {
1033
+ if (this[resizeFrameSymbol]) return;
1034
+ this[resizeFrameSymbol] = requestAnimationFrame(() => {
1035
+ this[resizeFrameSymbol] = null;
1036
+ this[forceRenderSymbol] = true;
1037
+ updateControl.call(this);
1038
+ });
1039
+ }
1040
+
1041
+ function getCellDisplayValue(data, rowId, colId) {
1042
+ const formula = data.formulas?.[rowId]?.[colId];
1043
+ if (isString(formula)) {
1044
+ const evaluated = evaluateFormula.call(this, data, formula);
1045
+ return Number.isFinite(evaluated) ? evaluated : "#ERR";
1046
+ }
1047
+ const raw = data.cells?.[rowId]?.[colId];
1048
+ return raw === undefined || raw === null ? "" : raw;
1049
+ }
1050
+
1051
+ function buildDisplayCells(data) {
1052
+ const result = clone(data.cells);
1053
+ const errors = [];
1054
+
1055
+ for (const rowId of Object.keys(data.formulas || {})) {
1056
+ for (const colId of Object.keys(data.formulas[rowId] || {})) {
1057
+ const formula = data.formulas[rowId][colId];
1058
+ if (!isString(formula)) continue;
1059
+ const value = evaluateFormula.call(this, data, formula);
1060
+ if (!result[rowId]) result[rowId] = {};
1061
+ if (Number.isFinite(value)) {
1062
+ result[rowId][colId] = value;
1063
+ } else {
1064
+ result[rowId][colId] = "#ERR";
1065
+ errors.push({ rowId, colId, formula });
1066
+ }
1067
+ }
1068
+ }
1069
+
1070
+ if (errors.length > 0) {
1071
+ fireCustomEvent(this, "monster-sheet-formula-error", { errors });
1072
+ }
1073
+
1074
+ return result;
1075
+ }
1076
+
1077
+ function evaluateFormula(data, formula, stack = []) {
1078
+ const expr = formula.trim().startsWith("=")
1079
+ ? formula.trim().slice(1)
1080
+ : formula.trim();
1081
+ if (expr === "") return NaN;
1082
+ try {
1083
+ const tokens = tokenizeFormula(expr);
1084
+ const rpn = toRpn(tokens);
1085
+ const value = evalRpn.call(this, data, rpn, stack);
1086
+ return Number.isFinite(value) ? value : NaN;
1087
+ } catch (e) {
1088
+ return NaN;
1089
+ }
1090
+ }
1091
+
1092
+ function tokenizeFormula(expr) {
1093
+ const tokens = [];
1094
+ const pattern = /\s*([A-Za-z]+[0-9]+|\d+(?:\.\d+)?|[()+\-*/])\s*/g;
1095
+ let match;
1096
+ while ((match = pattern.exec(expr)) !== null) {
1097
+ tokens.push(match[1]);
1098
+ }
1099
+ return tokens;
1100
+ }
1101
+
1102
+ function toRpn(tokens) {
1103
+ const output = [];
1104
+ const ops = [];
1105
+ const precedence = { "+": 1, "-": 1, "*": 2, "/": 2 };
1106
+
1107
+ for (const token of tokens) {
1108
+ if (isNumberToken(token) || isRefToken(token)) {
1109
+ output.push(token);
1110
+ continue;
1111
+ }
1112
+
1113
+ if (token === "(") {
1114
+ ops.push(token);
1115
+ continue;
1116
+ }
1117
+ if (token === ")") {
1118
+ while (ops.length > 0 && ops[ops.length - 1] !== "(") {
1119
+ output.push(ops.pop());
1120
+ }
1121
+ ops.pop();
1122
+ continue;
1123
+ }
1124
+
1125
+ while (
1126
+ ops.length > 0 &&
1127
+ ops[ops.length - 1] !== "(" &&
1128
+ precedence[ops[ops.length - 1]] >= precedence[token]
1129
+ ) {
1130
+ output.push(ops.pop());
1131
+ }
1132
+ ops.push(token);
1133
+ }
1134
+
1135
+ while (ops.length > 0) {
1136
+ output.push(ops.pop());
1137
+ }
1138
+
1139
+ return output;
1140
+ }
1141
+
1142
+ function evalRpn(data, rpn, stack) {
1143
+ const s = [];
1144
+ for (const token of rpn) {
1145
+ if (isNumberToken(token)) {
1146
+ s.push(Number(token));
1147
+ continue;
1148
+ }
1149
+ if (isRefToken(token)) {
1150
+ const { rowId, colId } = parseRefToken(token);
1151
+ const value = getCellNumericValue.call(this, data, rowId, colId, stack);
1152
+ s.push(value);
1153
+ continue;
1154
+ }
1155
+ const b = s.pop();
1156
+ const a = s.pop();
1157
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return NaN;
1158
+ switch (token) {
1159
+ case "+":
1160
+ s.push(a + b);
1161
+ break;
1162
+ case "-":
1163
+ s.push(a - b);
1164
+ break;
1165
+ case "*":
1166
+ s.push(a * b);
1167
+ break;
1168
+ case "/":
1169
+ s.push(b === 0 ? NaN : a / b);
1170
+ break;
1171
+ default:
1172
+ return NaN;
1173
+ }
1174
+ }
1175
+ return s.length === 1 ? s[0] : NaN;
1176
+ }
1177
+
1178
+ function getCellNumericValue(data, rowId, colId, stack) {
1179
+ const key = `${rowId}:${colId}`;
1180
+ if (stack.includes(key)) return NaN;
1181
+
1182
+ const formula = data.formulas?.[rowId]?.[colId];
1183
+ if (isString(formula)) {
1184
+ const nextStack = [...stack, key];
1185
+ return evaluateFormula.call(this, data, formula, nextStack);
1186
+ }
1187
+
1188
+ const raw = data.cells?.[rowId]?.[colId];
1189
+ const number = Number(raw);
1190
+ return Number.isFinite(number) ? number : NaN;
1191
+ }
1192
+
1193
+ function isNumberToken(token) {
1194
+ return /^-?\d+(?:\.\d+)?$/.test(token);
1195
+ }
1196
+
1197
+ function isRefToken(token) {
1198
+ return /^[A-Za-z]+[0-9]+$/.test(token);
1199
+ }
1200
+
1201
+ function parseRefToken(token) {
1202
+ const match = /^([A-Za-z]+)([0-9]+)$/.exec(token);
1203
+ return { colId: match[1].toUpperCase(), rowId: match[2] };
1204
+ }
1205
+
1206
+ function getTranslations() {
1207
+ const locale = getLocaleOfDocument();
1208
+ switch (locale.language) {
1209
+ case "de":
1210
+ return {
1211
+ addRow: "Zeile hinzufügen",
1212
+ addColumn: "Spalte hinzufügen",
1213
+ corner: "",
1214
+ };
1215
+ case "fr":
1216
+ return {
1217
+ addRow: "Ajouter une ligne",
1218
+ addColumn: "Ajouter une colonne",
1219
+ corner: "",
1220
+ };
1221
+ case "es":
1222
+ return {
1223
+ addRow: "Agregar fila",
1224
+ addColumn: "Agregar columna",
1225
+ corner: "",
1226
+ };
1227
+ case "zh":
1228
+ return {
1229
+ addRow: "添加行",
1230
+ addColumn: "添加列",
1231
+ corner: "",
1232
+ };
1233
+ case "hi":
1234
+ return {
1235
+ addRow: "पंक्ति जोड़ें",
1236
+ addColumn: "स्तंभ जोड़ें",
1237
+ corner: "",
1238
+ };
1239
+ case "bn":
1240
+ return {
1241
+ addRow: "সারি যোগ করুন",
1242
+ addColumn: "কলাম যোগ করুন",
1243
+ corner: "",
1244
+ };
1245
+ case "pt":
1246
+ return {
1247
+ addRow: "Adicionar linha",
1248
+ addColumn: "Adicionar coluna",
1249
+ corner: "",
1250
+ };
1251
+ case "ru":
1252
+ return {
1253
+ addRow: "Добавить строку",
1254
+ addColumn: "Добавить столбец",
1255
+ corner: "",
1256
+ };
1257
+ case "ja":
1258
+ return {
1259
+ addRow: "行を追加",
1260
+ addColumn: "列を追加",
1261
+ corner: "",
1262
+ };
1263
+ case "pa":
1264
+ return {
1265
+ addRow: "ਕਤਾਰ ਸ਼ਾਮਲ ਕਰੋ",
1266
+ addColumn: "ਕਾਲਮ ਸ਼ਾਮਲ ਕਰੋ",
1267
+ corner: "",
1268
+ };
1269
+ case "mr":
1270
+ return {
1271
+ addRow: "ओळ जोडा",
1272
+ addColumn: "स्तंभ जोडा",
1273
+ corner: "",
1274
+ };
1275
+ case "it":
1276
+ return {
1277
+ addRow: "Aggiungi riga",
1278
+ addColumn: "Aggiungi colonna",
1279
+ corner: "",
1280
+ };
1281
+ case "nl":
1282
+ return {
1283
+ addRow: "Rij toevoegen",
1284
+ addColumn: "Kolom toevoegen",
1285
+ corner: "",
1286
+ };
1287
+ case "sv":
1288
+ return {
1289
+ addRow: "Lägg till rad",
1290
+ addColumn: "Lägg till kolumn",
1291
+ corner: "",
1292
+ };
1293
+ case "pl":
1294
+ return {
1295
+ addRow: "Dodaj wiersz",
1296
+ addColumn: "Dodaj kolumnę",
1297
+ corner: "",
1298
+ };
1299
+ case "da":
1300
+ return {
1301
+ addRow: "Tilføj række",
1302
+ addColumn: "Tilføj kolonne",
1303
+ corner: "",
1304
+ };
1305
+ case "fi":
1306
+ return {
1307
+ addRow: "Lisää rivi",
1308
+ addColumn: "Lisää sarake",
1309
+ corner: "",
1310
+ };
1311
+ case "no":
1312
+ return {
1313
+ addRow: "Legg til rad",
1314
+ addColumn: "Legg til kolonne",
1315
+ corner: "",
1316
+ };
1317
+ case "cs":
1318
+ return {
1319
+ addRow: "Přidat řádek",
1320
+ addColumn: "Přidat sloupec",
1321
+ corner: "",
1322
+ };
1323
+ default:
1324
+ return {
1325
+ addRow: "Add row",
1326
+ addColumn: "Add column",
1327
+ corner: "",
1328
+ };
1329
+ }
1330
+ }
1331
+
1332
+ function getTemplate() {
1333
+ return `
1334
+ <div data-monster-role="control" part="control">
1335
+ <div data-monster-role="toolbar" part="toolbar">
1336
+ <button type="button" data-monster-role="add-row" part="add-row"></button>
1337
+ <button type="button" data-monster-role="add-column" part="add-column"></button>
1338
+ </div>
1339
+ <div data-monster-role="grid-wrapper" part="grid-wrapper">
1340
+ <div data-monster-role="grid" part="grid"></div>
1341
+ </div>
1342
+ </div>
1343
+ `;
1344
+ }
1345
+
1346
+ registerCustomElement(Sheet);