@dmitryvim/form-builder 0.2.18 → 0.2.19

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.
@@ -4772,6 +4772,1027 @@ function updateGroupField(element, fieldPath, value, context) {
4772
4772
  return updateContainerField(containerElement, fieldPath, value, context);
4773
4773
  }
4774
4774
 
4775
+ // src/components/table.ts
4776
+ function createEmptyCells(rows, cols) {
4777
+ return Array.from(
4778
+ { length: rows },
4779
+ () => Array.from({ length: cols }, () => "")
4780
+ );
4781
+ }
4782
+ function getShadowingMerge(row, col, merges) {
4783
+ for (const m of merges) {
4784
+ if (m.row === row && m.col === col) {
4785
+ return null;
4786
+ }
4787
+ if (row >= m.row && row < m.row + m.rowspan && col >= m.col && col < m.col + m.colspan) {
4788
+ return m;
4789
+ }
4790
+ }
4791
+ return null;
4792
+ }
4793
+ function getMergeAt(row, col, merges) {
4794
+ var _a;
4795
+ return (_a = merges.find((m) => m.row === row && m.col === col)) != null ? _a : null;
4796
+ }
4797
+ function selectionRange(sel) {
4798
+ var _a;
4799
+ if (!sel.anchor) return null;
4800
+ const focus = (_a = sel.focus) != null ? _a : sel.anchor;
4801
+ return {
4802
+ r1: Math.min(sel.anchor.row, focus.row),
4803
+ c1: Math.min(sel.anchor.col, focus.col),
4804
+ r2: Math.max(sel.anchor.row, focus.row),
4805
+ c2: Math.max(sel.anchor.col, focus.col)
4806
+ };
4807
+ }
4808
+ function makeOverlayCircleBtn(opts) {
4809
+ const btn = document.createElement("button");
4810
+ btn.type = "button";
4811
+ btn.textContent = opts.label;
4812
+ btn.title = opts.title;
4813
+ btn.style.cssText = `
4814
+ position: absolute;
4815
+ width: ${opts.size}px;
4816
+ height: ${opts.size}px;
4817
+ border-radius: 50%;
4818
+ border: none;
4819
+ background: ${opts.color};
4820
+ color: ${opts.textColor};
4821
+ font-size: ${Math.floor(opts.size * 0.6)}px;
4822
+ line-height: ${opts.size}px;
4823
+ text-align: center;
4824
+ cursor: pointer;
4825
+ z-index: 10;
4826
+ padding: 0;
4827
+ display: flex;
4828
+ align-items: center;
4829
+ justify-content: center;
4830
+ box-shadow: 0 1px 4px rgba(0,0,0,0.2);
4831
+ transition: transform 0.1s, opacity 0.1s;
4832
+ pointer-events: all;
4833
+ `;
4834
+ btn.addEventListener("mouseenter", () => {
4835
+ btn.style.transform = "scale(1.15)";
4836
+ });
4837
+ btn.addEventListener("mouseleave", () => {
4838
+ btn.style.transform = "scale(1)";
4839
+ });
4840
+ btn.addEventListener("click", (e) => {
4841
+ e.stopPropagation();
4842
+ opts.onClick(e);
4843
+ });
4844
+ return btn;
4845
+ }
4846
+ function renderReadonlyTable(data, wrapper) {
4847
+ const { cells, merges = [] } = data;
4848
+ if (cells.length === 0) return;
4849
+ const numCols = cells[0].length;
4850
+ const tableEl = document.createElement("table");
4851
+ tableEl.style.cssText = `
4852
+ width: 100%;
4853
+ border-collapse: collapse;
4854
+ border: var(--fb-border-width) solid var(--fb-border-color);
4855
+ border-radius: var(--fb-border-radius);
4856
+ font-size: var(--fb-font-size);
4857
+ font-family: var(--fb-font-family);
4858
+ color: var(--fb-text-color);
4859
+ `;
4860
+ cells.forEach((rowData, rIdx) => {
4861
+ var _a, _b;
4862
+ const section = rIdx === 0 ? tableEl.createTHead() : (_a = tableEl.tBodies[0]) != null ? _a : tableEl.createTBody();
4863
+ const tr = section.insertRow();
4864
+ for (let cIdx = 0; cIdx < numCols; cIdx++) {
4865
+ if (getShadowingMerge(rIdx, cIdx, merges)) {
4866
+ continue;
4867
+ }
4868
+ const merge = getMergeAt(rIdx, cIdx, merges);
4869
+ const td = document.createElement(rIdx === 0 ? "th" : "td");
4870
+ if (merge) {
4871
+ if (merge.rowspan > 1) td.rowSpan = merge.rowspan;
4872
+ if (merge.colspan > 1) td.colSpan = merge.colspan;
4873
+ }
4874
+ td.textContent = (_b = rowData[cIdx]) != null ? _b : "";
4875
+ td.style.cssText = `
4876
+ padding: 6px 10px;
4877
+ border: var(--fb-border-width) solid var(--fb-border-color);
4878
+ text-align: left;
4879
+ vertical-align: top;
4880
+ ${rIdx === 0 ? "background-color: var(--fb-background-hover-color); font-weight: 600;" : ""}
4881
+ `;
4882
+ tr.appendChild(td);
4883
+ }
4884
+ });
4885
+ const scrollWrapper = document.createElement("div");
4886
+ scrollWrapper.style.cssText = "overflow-x: auto; max-width: 100%;";
4887
+ scrollWrapper.appendChild(tableEl);
4888
+ wrapper.appendChild(scrollWrapper);
4889
+ }
4890
+ function startCellEditing(span, r, c, getCells, persistValue, selectCell) {
4891
+ if (span.contentEditable === "true") return;
4892
+ span.contentEditable = "true";
4893
+ span.focus();
4894
+ const domRange = document.createRange();
4895
+ const winSel = window.getSelection();
4896
+ domRange.selectNodeContents(span);
4897
+ domRange.collapse(false);
4898
+ winSel == null ? void 0 : winSel.removeAllRanges();
4899
+ winSel == null ? void 0 : winSel.addRange(domRange);
4900
+ function commit() {
4901
+ var _a;
4902
+ span.contentEditable = "inherit";
4903
+ const cells = getCells();
4904
+ if (cells[r]) {
4905
+ cells[r][c] = (_a = span.textContent) != null ? _a : "";
4906
+ }
4907
+ persistValue();
4908
+ }
4909
+ function onKeyDown(e) {
4910
+ var _a, _b, _c, _d;
4911
+ const cells = getCells();
4912
+ const numCols = (_b = (_a = cells[0]) == null ? void 0 : _a.length) != null ? _b : 0;
4913
+ if (e.key === "Escape") {
4914
+ span.contentEditable = "inherit";
4915
+ span.textContent = (_d = (_c = cells[r]) == null ? void 0 : _c[c]) != null ? _d : "";
4916
+ span.removeEventListener("keydown", onKeyDown);
4917
+ span.removeEventListener("blur", onBlur);
4918
+ return;
4919
+ }
4920
+ if (e.key === "Enter" && !e.shiftKey) {
4921
+ e.preventDefault();
4922
+ e.stopPropagation();
4923
+ commit();
4924
+ span.removeEventListener("keydown", onKeyDown);
4925
+ span.removeEventListener("blur", onBlur);
4926
+ const nextRow = r + 1 < cells.length ? r + 1 : r;
4927
+ selectCell(nextRow, c);
4928
+ return;
4929
+ }
4930
+ if (e.key === "Tab") {
4931
+ e.preventDefault();
4932
+ e.stopPropagation();
4933
+ commit();
4934
+ span.removeEventListener("keydown", onKeyDown);
4935
+ span.removeEventListener("blur", onBlur);
4936
+ let nr = r;
4937
+ let nc = e.shiftKey ? c - 1 : c + 1;
4938
+ if (nc < 0) {
4939
+ nc = numCols - 1;
4940
+ nr = Math.max(0, r - 1);
4941
+ }
4942
+ if (nc >= numCols) {
4943
+ nc = 0;
4944
+ nr = Math.min(cells.length - 1, r + 1);
4945
+ }
4946
+ selectCell(nr, nc);
4947
+ }
4948
+ }
4949
+ function onBlur() {
4950
+ if (span.contentEditable === "true") {
4951
+ commit();
4952
+ span.removeEventListener("keydown", onKeyDown);
4953
+ span.removeEventListener("blur", onBlur);
4954
+ }
4955
+ }
4956
+ span.addEventListener("keydown", onKeyDown);
4957
+ span.addEventListener("blur", onBlur);
4958
+ }
4959
+ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
4960
+ var _a, _b;
4961
+ const state = ctx.state;
4962
+ const instance = ctx.instance;
4963
+ const cells = initialData.cells.length > 0 ? initialData.cells.map((r) => [...r]) : createEmptyCells((_a = element.rows) != null ? _a : 3, (_b = element.columns) != null ? _b : 3);
4964
+ let merges = initialData.merges ? [...initialData.merges] : [];
4965
+ const sel = { anchor: null, focus: null, dragging: false };
4966
+ const hiddenInput = document.createElement("input");
4967
+ hiddenInput.type = "hidden";
4968
+ hiddenInput.name = pathKey;
4969
+ hiddenInput.value = JSON.stringify({ cells, merges });
4970
+ wrapper.appendChild(hiddenInput);
4971
+ function persistValue() {
4972
+ hiddenInput.value = JSON.stringify({ cells, merges });
4973
+ if (instance) {
4974
+ instance.triggerOnChange(pathKey, { cells, merges });
4975
+ }
4976
+ }
4977
+ hiddenInput._applyExternalUpdate = (data) => {
4978
+ cells.length = 0;
4979
+ data.cells.forEach((row) => cells.push([...row]));
4980
+ merges.length = 0;
4981
+ if (data.merges) {
4982
+ data.merges.forEach((m) => merges.push({ ...m }));
4983
+ }
4984
+ sel.anchor = null;
4985
+ sel.focus = null;
4986
+ persistValue();
4987
+ rebuild();
4988
+ };
4989
+ const tableWrapper = document.createElement("div");
4990
+ tableWrapper.style.cssText = "position: relative; padding: 20px 20px 20px 24px; overflow-x: auto; max-width: 100%;";
4991
+ const tableEl = document.createElement("table");
4992
+ tableEl.style.cssText = `
4993
+ border-collapse: collapse;
4994
+ font-size: var(--fb-font-size);
4995
+ font-family: var(--fb-font-family);
4996
+ color: var(--fb-text-color);
4997
+ table-layout: fixed;
4998
+ `;
4999
+ tableWrapper.appendChild(tableEl);
5000
+ wrapper.appendChild(tableWrapper);
5001
+ const contextMenu = document.createElement("div");
5002
+ contextMenu.style.cssText = `
5003
+ position: fixed;
5004
+ display: none;
5005
+ background: white;
5006
+ border: 1px solid var(--fb-border-color);
5007
+ border-radius: var(--fb-border-radius);
5008
+ box-shadow: 0 2px 8px rgba(0,0,0,0.15);
5009
+ padding: 4px;
5010
+ z-index: 1000;
5011
+ gap: 4px;
5012
+ flex-direction: column;
5013
+ `;
5014
+ wrapper.appendChild(contextMenu);
5015
+ function makeContextMenuBtn(label, onClick) {
5016
+ const btn = document.createElement("button");
5017
+ btn.type = "button";
5018
+ btn.textContent = label;
5019
+ btn.style.cssText = `
5020
+ padding: 4px 10px;
5021
+ font-size: var(--fb-font-size-small);
5022
+ color: var(--fb-text-color);
5023
+ border: 1px solid var(--fb-border-color);
5024
+ border-radius: var(--fb-border-radius);
5025
+ background: transparent;
5026
+ cursor: pointer;
5027
+ white-space: nowrap;
5028
+ text-align: left;
5029
+ `;
5030
+ btn.addEventListener("mouseenter", () => {
5031
+ btn.style.background = "var(--fb-background-hover-color)";
5032
+ });
5033
+ btn.addEventListener("mouseleave", () => {
5034
+ btn.style.background = "transparent";
5035
+ });
5036
+ btn.addEventListener("click", () => {
5037
+ hideContextMenu();
5038
+ onClick();
5039
+ });
5040
+ return btn;
5041
+ }
5042
+ function showContextMenu(x, y) {
5043
+ contextMenu.innerHTML = "";
5044
+ contextMenu.style.display = "flex";
5045
+ const range = selectionRange(sel);
5046
+ const isMultiCell = range && (range.r1 !== range.r2 || range.c1 !== range.c2);
5047
+ const isSingleMerged = sel.anchor && getMergeAt(sel.anchor.row, sel.anchor.col, merges);
5048
+ if (isMultiCell) {
5049
+ contextMenu.appendChild(
5050
+ makeContextMenuBtn(t("tableMergeCells", state), mergeCells)
5051
+ );
5052
+ }
5053
+ if (isSingleMerged) {
5054
+ contextMenu.appendChild(
5055
+ makeContextMenuBtn(t("tableSplitCell", state), splitCell)
5056
+ );
5057
+ }
5058
+ if (!contextMenu.firstChild) {
5059
+ hideContextMenu();
5060
+ return;
5061
+ }
5062
+ const menuWidth = 140;
5063
+ const menuHeight = contextMenu.children.length * 32 + 8;
5064
+ const vw = window.innerWidth;
5065
+ const vh = window.innerHeight;
5066
+ const left = x + menuWidth > vw ? x - menuWidth : x;
5067
+ const top = y + menuHeight > vh ? y - menuHeight : y;
5068
+ contextMenu.style.left = `${left}px`;
5069
+ contextMenu.style.top = `${top}px`;
5070
+ }
5071
+ function hideContextMenu() {
5072
+ contextMenu.style.display = "none";
5073
+ }
5074
+ const menuDismissCtrl = new AbortController();
5075
+ document.addEventListener("mousedown", (e) => {
5076
+ if (!wrapper.isConnected) {
5077
+ menuDismissCtrl.abort();
5078
+ return;
5079
+ }
5080
+ if (!contextMenu.contains(e.target)) {
5081
+ hideContextMenu();
5082
+ }
5083
+ }, { signal: menuDismissCtrl.signal });
5084
+ function applySelectionStyles() {
5085
+ const range = selectionRange(sel);
5086
+ const allTds = tableEl.querySelectorAll("td[data-row]");
5087
+ allTds.forEach((td) => {
5088
+ const r = parseInt(td.getAttribute("data-row") || "0", 10);
5089
+ const c = parseInt(td.getAttribute("data-col") || "0", 10);
5090
+ const isAnchor = sel.anchor !== null && sel.anchor.row === r && sel.anchor.col === c;
5091
+ const inRange = range !== null && r >= range.r1 && r <= range.r2 && c >= range.c1 && c <= range.c2;
5092
+ td.style.outline = isAnchor ? "2px solid var(--fb-primary-color, #0066cc)" : "";
5093
+ td.style.outlineOffset = isAnchor ? "-2px" : "";
5094
+ if (r === 0) {
5095
+ td.style.backgroundColor = "var(--fb-background-hover-color)";
5096
+ } else {
5097
+ td.style.backgroundColor = inRange && !isAnchor ? "rgba(0,102,204,0.08)" : "";
5098
+ }
5099
+ });
5100
+ }
5101
+ function selectCell(row, col) {
5102
+ sel.anchor = { row, col };
5103
+ sel.focus = null;
5104
+ applySelectionStyles();
5105
+ tableEl.focus();
5106
+ }
5107
+ function editCell(row, col) {
5108
+ const td = tableEl.querySelector(
5109
+ `td[data-row="${row}"][data-col="${col}"]`
5110
+ );
5111
+ if (!td) return;
5112
+ const span = td.querySelector("span");
5113
+ if (!span) return;
5114
+ sel.anchor = { row, col };
5115
+ sel.focus = null;
5116
+ applySelectionStyles();
5117
+ startCellEditing(span, row, col, () => cells, persistValue, selectCell);
5118
+ }
5119
+ function addRow(afterIndex) {
5120
+ var _a2;
5121
+ const numCols = cells.length > 0 ? cells[0].length : (_a2 = element.columns) != null ? _a2 : 3;
5122
+ const newRow = Array(numCols).fill("");
5123
+ const insertAt = afterIndex !== void 0 ? afterIndex + 1 : cells.length;
5124
+ cells.splice(insertAt, 0, newRow);
5125
+ merges = merges.map((m) => {
5126
+ if (m.row >= insertAt) {
5127
+ return { ...m, row: m.row + 1 };
5128
+ }
5129
+ if (m.row < insertAt && m.row + m.rowspan > insertAt) {
5130
+ return { ...m, rowspan: m.rowspan + 1 };
5131
+ }
5132
+ return m;
5133
+ });
5134
+ persistValue();
5135
+ rebuild();
5136
+ }
5137
+ function removeRow(targetRow) {
5138
+ if (cells.length <= 1) return;
5139
+ const rowToRemove = targetRow !== void 0 ? targetRow : sel.anchor ? sel.anchor.row : cells.length - 1;
5140
+ merges = merges.map((m) => {
5141
+ const mEndRow = m.row + m.rowspan - 1;
5142
+ if (m.row === rowToRemove && m.rowspan === 1) return null;
5143
+ if (m.row === rowToRemove) {
5144
+ return { ...m, row: m.row + 1, rowspan: m.rowspan - 1 };
5145
+ }
5146
+ if (mEndRow === rowToRemove) {
5147
+ return { ...m, rowspan: m.rowspan - 1 };
5148
+ }
5149
+ if (m.row < rowToRemove && mEndRow > rowToRemove) {
5150
+ return { ...m, rowspan: m.rowspan - 1 };
5151
+ }
5152
+ if (m.row > rowToRemove) {
5153
+ return { ...m, row: m.row - 1 };
5154
+ }
5155
+ return m;
5156
+ }).filter((m) => m !== null);
5157
+ cells.splice(rowToRemove, 1);
5158
+ if (sel.anchor && sel.anchor.row >= cells.length) {
5159
+ sel.anchor = { row: cells.length - 1, col: sel.anchor.col };
5160
+ }
5161
+ persistValue();
5162
+ rebuild();
5163
+ }
5164
+ function addColumn(afterIndex) {
5165
+ var _a2, _b2;
5166
+ const insertAt = afterIndex !== void 0 ? afterIndex + 1 : (_b2 = (_a2 = cells[0]) == null ? void 0 : _a2.length) != null ? _b2 : 0;
5167
+ cells.forEach((row) => row.splice(insertAt, 0, ""));
5168
+ merges = merges.map((m) => {
5169
+ if (m.col >= insertAt) {
5170
+ return { ...m, col: m.col + 1 };
5171
+ }
5172
+ if (m.col < insertAt && m.col + m.colspan > insertAt) {
5173
+ return { ...m, colspan: m.colspan + 1 };
5174
+ }
5175
+ return m;
5176
+ });
5177
+ persistValue();
5178
+ rebuild();
5179
+ }
5180
+ function removeColumn(targetCol) {
5181
+ if (cells.length === 0 || cells[0].length <= 1) return;
5182
+ const colToRemove = targetCol !== void 0 ? targetCol : sel.anchor ? sel.anchor.col : cells[0].length - 1;
5183
+ merges = merges.map((m) => {
5184
+ const mEndCol = m.col + m.colspan - 1;
5185
+ if (m.col === colToRemove && m.colspan === 1) return null;
5186
+ if (m.col === colToRemove) {
5187
+ return { ...m, col: m.col + 1, colspan: m.colspan - 1 };
5188
+ }
5189
+ if (mEndCol === colToRemove) {
5190
+ return { ...m, colspan: m.colspan - 1 };
5191
+ }
5192
+ if (m.col < colToRemove && mEndCol > colToRemove) {
5193
+ return { ...m, colspan: m.colspan - 1 };
5194
+ }
5195
+ if (m.col > colToRemove) {
5196
+ return { ...m, col: m.col - 1 };
5197
+ }
5198
+ return m;
5199
+ }).filter((m) => m !== null);
5200
+ cells.forEach((row) => row.splice(colToRemove, 1));
5201
+ if (sel.anchor && sel.anchor.col >= cells[0].length) {
5202
+ sel.anchor = { row: sel.anchor.row, col: cells[0].length - 1 };
5203
+ }
5204
+ persistValue();
5205
+ rebuild();
5206
+ }
5207
+ function mergeCells() {
5208
+ const range = selectionRange(sel);
5209
+ if (!range) return;
5210
+ const { r1, c1, r2, c2 } = range;
5211
+ if (r1 === r2 && c1 === c2) return;
5212
+ merges = merges.filter((m) => {
5213
+ const mEndRow = m.row + m.rowspan - 1;
5214
+ const mEndCol = m.col + m.colspan - 1;
5215
+ const overlaps = m.row <= r2 && mEndRow >= r1 && m.col <= c2 && mEndCol >= c1;
5216
+ return !overlaps;
5217
+ });
5218
+ const anchorText = cells[r1][c1];
5219
+ for (let r = r1; r <= r2; r++) {
5220
+ for (let c = c1; c <= c2; c++) {
5221
+ if (r !== r1 || c !== c1) {
5222
+ cells[r][c] = "";
5223
+ }
5224
+ }
5225
+ }
5226
+ cells[r1][c1] = anchorText;
5227
+ merges.push({ row: r1, col: c1, rowspan: r2 - r1 + 1, colspan: c2 - c1 + 1 });
5228
+ sel.anchor = { row: r1, col: c1 };
5229
+ sel.focus = null;
5230
+ persistValue();
5231
+ rebuild();
5232
+ }
5233
+ function splitCell() {
5234
+ if (!sel.anchor) return;
5235
+ const { row, col } = sel.anchor;
5236
+ const mIdx = merges.findIndex((m) => m.row === row && m.col === col);
5237
+ if (mIdx === -1) return;
5238
+ merges.splice(mIdx, 1);
5239
+ sel.focus = null;
5240
+ persistValue();
5241
+ rebuild();
5242
+ }
5243
+ function rebuild() {
5244
+ var _a2, _b2;
5245
+ tableEl.innerHTML = "";
5246
+ const numRows = cells.length;
5247
+ const numCols = numRows > 0 ? cells[0].length : 0;
5248
+ const range = selectionRange(sel);
5249
+ for (let rIdx = 0; rIdx < numRows; rIdx++) {
5250
+ const section = rIdx === 0 ? (_a2 = tableEl.tHead) != null ? _a2 : tableEl.createTHead() : (_b2 = tableEl.tBodies[0]) != null ? _b2 : tableEl.createTBody();
5251
+ const tr = section.insertRow();
5252
+ for (let cIdx = 0; cIdx < numCols; cIdx++) {
5253
+ if (getShadowingMerge(rIdx, cIdx, merges)) {
5254
+ continue;
5255
+ }
5256
+ const merge = getMergeAt(rIdx, cIdx, merges);
5257
+ const td = document.createElement("td");
5258
+ td.setAttribute("data-row", String(rIdx));
5259
+ td.setAttribute("data-col", String(cIdx));
5260
+ if (merge) {
5261
+ if (merge.rowspan > 1) td.rowSpan = merge.rowspan;
5262
+ if (merge.colspan > 1) td.colSpan = merge.colspan;
5263
+ }
5264
+ const inRange = range !== null && rIdx >= range.r1 && rIdx <= range.r2 && cIdx >= range.c1 && cIdx <= range.c2;
5265
+ const isAnchor = sel.anchor !== null && sel.anchor.row === rIdx && sel.anchor.col === cIdx;
5266
+ td.style.cssText = [
5267
+ "border: var(--fb-border-width) solid var(--fb-border-color);",
5268
+ "padding: 4px 8px;",
5269
+ "min-width: 80px;",
5270
+ "vertical-align: top;",
5271
+ "cursor: text;",
5272
+ "position: relative;",
5273
+ rIdx === 0 ? "background-color: var(--fb-background-hover-color); font-weight: 600;" : "",
5274
+ inRange && !isAnchor ? "background-color: rgba(0,102,204,0.08);" : "",
5275
+ isAnchor ? "outline: 2px solid var(--fb-primary-color, #0066cc); outline-offset: -2px;" : ""
5276
+ ].join(" ");
5277
+ const content = document.createElement("span");
5278
+ content.textContent = cells[rIdx][cIdx];
5279
+ content.style.cssText = "display: block; min-height: 1.4em; white-space: pre-wrap; word-break: break-word; outline: none;";
5280
+ td.appendChild(content);
5281
+ const capturedR = rIdx;
5282
+ const capturedC = cIdx;
5283
+ td.addEventListener("mousedown", (e) => {
5284
+ if (e.target.tagName === "BUTTON") return;
5285
+ if (e.target.contentEditable === "true") return;
5286
+ if (e.button === 2) {
5287
+ const range2 = selectionRange(sel);
5288
+ if (range2 && capturedR >= range2.r1 && capturedR <= range2.r2 && capturedC >= range2.c1 && capturedC <= range2.c2) {
5289
+ return;
5290
+ }
5291
+ }
5292
+ if (e.shiftKey && sel.anchor) {
5293
+ e.preventDefault();
5294
+ sel.focus = { row: capturedR, col: capturedC };
5295
+ sel.dragging = false;
5296
+ applySelectionStyles();
5297
+ } else {
5298
+ sel.anchor = { row: capturedR, col: capturedC };
5299
+ sel.focus = null;
5300
+ sel.dragging = true;
5301
+ applySelectionStyles();
5302
+ }
5303
+ });
5304
+ td.addEventListener("mouseup", (e) => {
5305
+ if (e.target.tagName === "BUTTON") return;
5306
+ if (sel.dragging) {
5307
+ sel.dragging = false;
5308
+ const currentRange = selectionRange(sel);
5309
+ const isSingleCell = !currentRange || currentRange.r1 === currentRange.r2 && currentRange.c1 === currentRange.c2;
5310
+ if (isSingleCell) {
5311
+ editCell(capturedR, capturedC);
5312
+ }
5313
+ }
5314
+ });
5315
+ td.addEventListener("mousemove", (e) => {
5316
+ if (sel.dragging && e.buttons === 1) {
5317
+ const currentAnchor = sel.anchor;
5318
+ if (currentAnchor && (currentAnchor.row !== capturedR || currentAnchor.col !== capturedC)) {
5319
+ sel.focus = { row: capturedR, col: capturedC };
5320
+ applySelectionStyles();
5321
+ }
5322
+ }
5323
+ });
5324
+ td.addEventListener("contextmenu", (e) => {
5325
+ const currentRange = selectionRange(sel);
5326
+ const isMulti = currentRange && (currentRange.r1 !== currentRange.r2 || currentRange.c1 !== currentRange.c2);
5327
+ const isMerged = sel.anchor && getMergeAt(sel.anchor.row, sel.anchor.col, merges);
5328
+ if (isMulti || isMerged) {
5329
+ e.preventDefault();
5330
+ showContextMenu(e.clientX, e.clientY);
5331
+ }
5332
+ });
5333
+ tr.appendChild(td);
5334
+ }
5335
+ }
5336
+ buildInsertOverlays();
5337
+ tableEl.setAttribute("tabindex", "0");
5338
+ tableEl.onkeydown = (e) => {
5339
+ var _a3, _b3;
5340
+ const editing = tableEl.querySelector("[contenteditable='true']");
5341
+ if (editing) return;
5342
+ const anchor = sel.anchor;
5343
+ if (!anchor) return;
5344
+ const numRows2 = cells.length;
5345
+ const numCols2 = numRows2 > 0 ? cells[0].length : 0;
5346
+ const navDeltas = {
5347
+ ArrowUp: [-1, 0],
5348
+ ArrowDown: [1, 0],
5349
+ ArrowLeft: [0, -1],
5350
+ ArrowRight: [0, 1]
5351
+ };
5352
+ if (navDeltas[e.key]) {
5353
+ e.preventDefault();
5354
+ const [dr, dc] = navDeltas[e.key];
5355
+ const newRow = Math.max(0, Math.min(numRows2 - 1, anchor.row + dr));
5356
+ const newCol = Math.max(0, Math.min(numCols2 - 1, anchor.col + dc));
5357
+ if (e.shiftKey) {
5358
+ sel.focus = { row: newRow, col: newCol };
5359
+ applySelectionStyles();
5360
+ } else {
5361
+ selectCell(newRow, newCol);
5362
+ }
5363
+ return;
5364
+ }
5365
+ if (e.key === "Enter") {
5366
+ e.preventDefault();
5367
+ editCell(anchor.row, anchor.col);
5368
+ return;
5369
+ }
5370
+ if (e.key === "Tab") {
5371
+ e.preventDefault();
5372
+ const numCols3 = (_b3 = (_a3 = cells[0]) == null ? void 0 : _a3.length) != null ? _b3 : 0;
5373
+ let nr = anchor.row;
5374
+ let nc = e.shiftKey ? anchor.col - 1 : anchor.col + 1;
5375
+ if (nc < 0) {
5376
+ nc = numCols3 - 1;
5377
+ nr = Math.max(0, nr - 1);
5378
+ }
5379
+ if (nc >= numCols3) {
5380
+ nc = 0;
5381
+ nr = Math.min(cells.length - 1, nr + 1);
5382
+ }
5383
+ selectCell(nr, nc);
5384
+ return;
5385
+ }
5386
+ if (e.key === "m" && e.ctrlKey && !e.shiftKey) {
5387
+ e.preventDefault();
5388
+ mergeCells();
5389
+ return;
5390
+ }
5391
+ if (e.key === "M" && e.ctrlKey && e.shiftKey) {
5392
+ e.preventDefault();
5393
+ splitCell();
5394
+ }
5395
+ };
5396
+ tableEl.oncopy = (e) => {
5397
+ var _a3, _b3, _c, _d;
5398
+ const range2 = selectionRange(sel);
5399
+ if (!range2) return;
5400
+ e.preventDefault();
5401
+ const { r1, c1, r2, c2 } = range2;
5402
+ const tsvRows = [];
5403
+ const htmlRows = [];
5404
+ for (let r = r1; r <= r2; r++) {
5405
+ const tsvCols = [];
5406
+ const htmlCols = [];
5407
+ for (let c = c1; c <= c2; c++) {
5408
+ const val = (_b3 = (_a3 = cells[r]) == null ? void 0 : _a3[c]) != null ? _b3 : "";
5409
+ tsvCols.push(val);
5410
+ const escaped = val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5411
+ htmlCols.push(`<td>${escaped}</td>`);
5412
+ }
5413
+ tsvRows.push(tsvCols.join(" "));
5414
+ htmlRows.push(`<tr>${htmlCols.join("")}</tr>`);
5415
+ }
5416
+ const tsvText = tsvRows.join("\n");
5417
+ const htmlText = `<table>${htmlRows.join("")}</table>`;
5418
+ (_c = e.clipboardData) == null ? void 0 : _c.setData("text/plain", tsvText);
5419
+ (_d = e.clipboardData) == null ? void 0 : _d.setData("text/html", htmlText);
5420
+ };
5421
+ tableEl.onpaste = (e) => {
5422
+ var _a3, _b3, _c, _d, _e, _f, _g, _h, _i;
5423
+ const anchor = sel.anchor;
5424
+ if (!anchor) return;
5425
+ const text = (_b3 = (_a3 = e.clipboardData) == null ? void 0 : _a3.getData("text/plain")) != null ? _b3 : "";
5426
+ const isMultiCell = text.includes(" ") || text.split(/\r?\n/).filter((l) => l).length > 1;
5427
+ const editing = tableEl.querySelector("[contenteditable='true']");
5428
+ if (editing && !isMultiCell) return;
5429
+ e.preventDefault();
5430
+ if (editing) {
5431
+ editing.contentEditable = "inherit";
5432
+ const r = parseInt(
5433
+ (_d = (_c = editing.closest("td")) == null ? void 0 : _c.getAttribute("data-row")) != null ? _d : "0",
5434
+ 10
5435
+ );
5436
+ const c = parseInt(
5437
+ (_f = (_e = editing.closest("td")) == null ? void 0 : _e.getAttribute("data-col")) != null ? _f : "0",
5438
+ 10
5439
+ );
5440
+ if (cells[r]) {
5441
+ cells[r][c] = (_g = editing.textContent) != null ? _g : "";
5442
+ }
5443
+ }
5444
+ if (!text.trim()) return;
5445
+ const pasteRows = text.split(/\r?\n/).map((line) => line.split(" "));
5446
+ if (pasteRows.length > 1 && pasteRows[pasteRows.length - 1].length === 1 && pasteRows[pasteRows.length - 1][0] === "") {
5447
+ pasteRows.pop();
5448
+ }
5449
+ const startR = anchor.row;
5450
+ const startC = anchor.col;
5451
+ const neededRows = startR + pasteRows.length;
5452
+ while (cells.length < neededRows) {
5453
+ cells.push(Array((_i = (_h = cells[0]) == null ? void 0 : _h.length) != null ? _i : 1).fill(""));
5454
+ }
5455
+ const maxPasteCols = Math.max(...pasteRows.map((r) => r.length));
5456
+ const neededCols = startC + maxPasteCols;
5457
+ if (cells[0] && neededCols > cells[0].length) {
5458
+ const extraCols = neededCols - cells[0].length;
5459
+ cells.forEach((row) => {
5460
+ for (let i = 0; i < extraCols; i++) row.push("");
5461
+ });
5462
+ }
5463
+ for (let pr = 0; pr < pasteRows.length; pr++) {
5464
+ for (let pc = 0; pc < pasteRows[pr].length; pc++) {
5465
+ const tr = startR + pr;
5466
+ const tc = startC + pc;
5467
+ if (cells[tr]) {
5468
+ cells[tr][tc] = pasteRows[pr][pc];
5469
+ }
5470
+ }
5471
+ }
5472
+ persistValue();
5473
+ rebuild();
5474
+ };
5475
+ }
5476
+ const insertColBtn = makeOverlayCircleBtn({
5477
+ label: "+",
5478
+ title: t("tableAddColumn", state),
5479
+ size: 20,
5480
+ color: "var(--fb-primary-color, #0066cc)",
5481
+ textColor: "white",
5482
+ onClick: () => {
5483
+ var _a2;
5484
+ const afterIdx = parseInt((_a2 = insertColBtn.dataset.afterCol) != null ? _a2 : "0", 10);
5485
+ addColumn(afterIdx);
5486
+ }
5487
+ });
5488
+ insertColBtn.style.position = "absolute";
5489
+ insertColBtn.style.display = "none";
5490
+ tableWrapper.appendChild(insertColBtn);
5491
+ const insertRowBtn = makeOverlayCircleBtn({
5492
+ label: "+",
5493
+ title: t("tableAddRow", state),
5494
+ size: 20,
5495
+ color: "var(--fb-primary-color, #0066cc)",
5496
+ textColor: "white",
5497
+ onClick: () => {
5498
+ var _a2;
5499
+ const afterIdx = parseInt((_a2 = insertRowBtn.dataset.afterRow) != null ? _a2 : "0", 10);
5500
+ addRow(afterIdx);
5501
+ }
5502
+ });
5503
+ insertRowBtn.style.position = "absolute";
5504
+ insertRowBtn.style.display = "none";
5505
+ tableWrapper.appendChild(insertRowBtn);
5506
+ let colRemoveBtns = [];
5507
+ let rowRemoveBtns = [];
5508
+ const addLastColBtn = makeOverlayCircleBtn({
5509
+ label: "+",
5510
+ title: t("tableAddColumn", state),
5511
+ size: 20,
5512
+ color: "var(--fb-primary-color, #0066cc)",
5513
+ textColor: "white",
5514
+ onClick: () => addColumn()
5515
+ });
5516
+ addLastColBtn.style.position = "absolute";
5517
+ addLastColBtn.style.display = "none";
5518
+ tableWrapper.appendChild(addLastColBtn);
5519
+ const addLastRowBtn = makeOverlayCircleBtn({
5520
+ label: "+",
5521
+ title: t("tableAddRow", state),
5522
+ size: 20,
5523
+ color: "var(--fb-primary-color, #0066cc)",
5524
+ textColor: "white",
5525
+ onClick: () => addRow()
5526
+ });
5527
+ addLastRowBtn.style.position = "absolute";
5528
+ addLastRowBtn.style.display = "none";
5529
+ tableWrapper.appendChild(addLastRowBtn);
5530
+ function buildInsertOverlays() {
5531
+ var _a2, _b2;
5532
+ colRemoveBtns.forEach((b) => b.remove());
5533
+ colRemoveBtns = [];
5534
+ rowRemoveBtns.forEach((b) => b.remove());
5535
+ rowRemoveBtns = [];
5536
+ const numCols = cells.length > 0 ? cells[0].length : 0;
5537
+ const numRows = cells.length;
5538
+ if (numCols > 1) {
5539
+ const headerCells = Array.from(
5540
+ tableEl.querySelectorAll("thead td[data-col]")
5541
+ );
5542
+ for (const hc of headerCells) {
5543
+ const colIdx = parseInt((_a2 = hc.getAttribute("data-col")) != null ? _a2 : "0", 10);
5544
+ const btn = makeOverlayCircleBtn({
5545
+ label: "\xD7",
5546
+ title: t("tableRemoveColumn", state),
5547
+ size: 16,
5548
+ color: "var(--fb-error-color, #dc3545)",
5549
+ textColor: "white",
5550
+ onClick: () => removeColumn(colIdx)
5551
+ });
5552
+ btn.setAttribute("data-action", "remove-col");
5553
+ btn.setAttribute("data-col", String(colIdx));
5554
+ btn.style.position = "absolute";
5555
+ btn.style.display = "none";
5556
+ tableWrapper.appendChild(btn);
5557
+ colRemoveBtns.push(btn);
5558
+ }
5559
+ }
5560
+ if (numRows > 1) {
5561
+ const allRowElements = [
5562
+ ...tableEl.tHead ? Array.from(tableEl.tHead.rows) : [],
5563
+ ...tableEl.tBodies[0] ? Array.from(tableEl.tBodies[0].rows) : []
5564
+ ].filter((r) => r.querySelector("td[data-row]"));
5565
+ for (const rowEl of allRowElements) {
5566
+ const firstTd = rowEl.querySelector("td[data-row]");
5567
+ if (!firstTd) continue;
5568
+ const rowIdx = parseInt((_b2 = firstTd.getAttribute("data-row")) != null ? _b2 : "0", 10);
5569
+ const btn = makeOverlayCircleBtn({
5570
+ label: "\xD7",
5571
+ title: t("tableRemoveRow", state),
5572
+ size: 16,
5573
+ color: "var(--fb-error-color, #dc3545)",
5574
+ textColor: "white",
5575
+ onClick: () => removeRow(rowIdx)
5576
+ });
5577
+ btn.setAttribute("data-action", "remove-row");
5578
+ btn.setAttribute("data-row", String(rowIdx));
5579
+ btn.style.position = "absolute";
5580
+ btn.style.display = "none";
5581
+ tableWrapper.appendChild(btn);
5582
+ rowRemoveBtns.push(btn);
5583
+ }
5584
+ }
5585
+ function updateTopZoneOverlays(mx, wr, tblR, scrollL, active) {
5586
+ var _a3;
5587
+ const headerCells = active ? Array.from(tableEl.querySelectorAll("thead td[data-col]")) : [];
5588
+ let closestColIdx = -1;
5589
+ let closestColDist = Infinity;
5590
+ let closestBorderX = -1;
5591
+ let closestAfterCol = -1;
5592
+ let closestBorderDist = Infinity;
5593
+ for (let i = 0; i < headerCells.length; i++) {
5594
+ const cellRect = headerCells[i].getBoundingClientRect();
5595
+ const centerX = (cellRect.left + cellRect.right) / 2;
5596
+ const dist = Math.abs(mx - centerX);
5597
+ if (dist < closestColDist) {
5598
+ closestColDist = dist;
5599
+ closestColIdx = i;
5600
+ }
5601
+ const borderDist = Math.abs(mx - cellRect.right);
5602
+ if (borderDist < closestBorderDist && borderDist < 20) {
5603
+ closestBorderDist = borderDist;
5604
+ closestBorderX = cellRect.right - wr.left + scrollL;
5605
+ closestAfterCol = parseInt((_a3 = headerCells[i].getAttribute("data-col")) != null ? _a3 : "0", 10);
5606
+ }
5607
+ }
5608
+ colRemoveBtns.forEach((btn, idx) => {
5609
+ if (!active || idx !== closestColIdx) {
5610
+ btn.style.display = "none";
5611
+ return;
5612
+ }
5613
+ const cellRect = headerCells[idx].getBoundingClientRect();
5614
+ const centerX = (cellRect.left + cellRect.right) / 2 - wr.left + scrollL;
5615
+ btn.style.left = `${centerX - 8}px`;
5616
+ btn.style.top = "2px";
5617
+ btn.style.display = "flex";
5618
+ });
5619
+ if (active && closestAfterCol >= 0) {
5620
+ insertColBtn.style.display = "flex";
5621
+ insertColBtn.style.left = `${closestBorderX - 10}px`;
5622
+ insertColBtn.style.top = `${tblR.top - wr.top - 10}px`;
5623
+ insertColBtn.dataset.afterCol = String(closestAfterCol);
5624
+ } else {
5625
+ insertColBtn.style.display = "none";
5626
+ }
5627
+ }
5628
+ function updateLeftZoneOverlays(my, wr, tblR, scrollL, active) {
5629
+ var _a3;
5630
+ const allRowEls = [];
5631
+ if (active) {
5632
+ if (tableEl.tHead) {
5633
+ for (const row of Array.from(tableEl.tHead.rows)) {
5634
+ if (row.querySelector("td[data-row]")) allRowEls.push(row);
5635
+ }
5636
+ }
5637
+ if (tableEl.tBodies[0]) {
5638
+ for (const row of Array.from(tableEl.tBodies[0].rows)) {
5639
+ if (row.querySelector("td[data-row]")) allRowEls.push(row);
5640
+ }
5641
+ }
5642
+ }
5643
+ let closestRowIdx = -1;
5644
+ let closestRowDist = Infinity;
5645
+ let closestBorderY = -1;
5646
+ let closestAfterRow = -1;
5647
+ let closestRowBorderDist = Infinity;
5648
+ for (let i = 0; i < allRowEls.length; i++) {
5649
+ const trRect = allRowEls[i].getBoundingClientRect();
5650
+ const centerY = (trRect.top + trRect.bottom) / 2;
5651
+ const dist = Math.abs(my - centerY);
5652
+ if (dist < closestRowDist) {
5653
+ closestRowDist = dist;
5654
+ closestRowIdx = i;
5655
+ }
5656
+ const borderDist = Math.abs(my - trRect.bottom);
5657
+ if (borderDist < closestRowBorderDist && borderDist < 14) {
5658
+ closestRowBorderDist = borderDist;
5659
+ closestBorderY = trRect.bottom - wr.top;
5660
+ const firstTd = allRowEls[i].querySelector("td[data-row]");
5661
+ closestAfterRow = parseInt((_a3 = firstTd == null ? void 0 : firstTd.getAttribute("data-row")) != null ? _a3 : "0", 10);
5662
+ }
5663
+ }
5664
+ rowRemoveBtns.forEach((btn, idx) => {
5665
+ if (!active || idx !== closestRowIdx) {
5666
+ btn.style.display = "none";
5667
+ return;
5668
+ }
5669
+ const trRect = allRowEls[idx].getBoundingClientRect();
5670
+ const centerY = (trRect.top + trRect.bottom) / 2 - wr.top;
5671
+ btn.style.left = "4px";
5672
+ btn.style.top = `${centerY - 8}px`;
5673
+ btn.style.display = "flex";
5674
+ });
5675
+ if (active && closestAfterRow >= 0) {
5676
+ insertRowBtn.style.display = "flex";
5677
+ insertRowBtn.style.top = `${closestBorderY - 10}px`;
5678
+ insertRowBtn.style.left = `${tblR.left - wr.left + scrollL - 10}px`;
5679
+ insertRowBtn.dataset.afterRow = String(closestAfterRow);
5680
+ } else {
5681
+ insertRowBtn.style.display = "none";
5682
+ }
5683
+ }
5684
+ let rafPending = false;
5685
+ tableWrapper.onmousemove = (e) => {
5686
+ const target = e.target;
5687
+ if (target.tagName === "BUTTON" && target.parentElement === tableWrapper) return;
5688
+ if (rafPending) return;
5689
+ rafPending = true;
5690
+ const mx = e.clientX;
5691
+ const my = e.clientY;
5692
+ requestAnimationFrame(() => {
5693
+ rafPending = false;
5694
+ const wr = tableWrapper.getBoundingClientRect();
5695
+ const tblR = tableEl.getBoundingClientRect();
5696
+ const scrollL = tableWrapper.scrollLeft;
5697
+ const inTopZone = my >= wr.top && my < tblR.top + 4;
5698
+ const inLeftZone = mx >= wr.left && mx < tblR.left + 4;
5699
+ const visibleRight = Math.min(tblR.right, wr.right);
5700
+ const inRightZone = mx > visibleRight - 20 && mx <= wr.right;
5701
+ const inBottomZone = my > tblR.bottom - 4 && my <= wr.bottom + 20;
5702
+ updateTopZoneOverlays(mx, wr, tblR, scrollL, inTopZone);
5703
+ updateLeftZoneOverlays(my, wr, tblR, scrollL, inLeftZone);
5704
+ addLastColBtn.style.display = inRightZone ? "flex" : "none";
5705
+ if (inRightZone) {
5706
+ addLastColBtn.style.left = `${wr.right - wr.left + scrollL - 20}px`;
5707
+ addLastColBtn.style.top = `${(tblR.top + tblR.bottom) / 2 - wr.top - 10}px`;
5708
+ }
5709
+ addLastRowBtn.style.display = inBottomZone ? "flex" : "none";
5710
+ if (inBottomZone) {
5711
+ const visibleCenterX = (wr.left + wr.right) / 2 - wr.left + scrollL;
5712
+ addLastRowBtn.style.left = `${visibleCenterX - 10}px`;
5713
+ addLastRowBtn.style.top = `${tblR.bottom - wr.top - 10}px`;
5714
+ }
5715
+ });
5716
+ };
5717
+ tableWrapper.onmouseleave = () => {
5718
+ colRemoveBtns.forEach((btn) => {
5719
+ btn.style.display = "none";
5720
+ });
5721
+ rowRemoveBtns.forEach((btn) => {
5722
+ btn.style.display = "none";
5723
+ });
5724
+ insertColBtn.style.display = "none";
5725
+ insertRowBtn.style.display = "none";
5726
+ addLastColBtn.style.display = "none";
5727
+ addLastRowBtn.style.display = "none";
5728
+ };
5729
+ }
5730
+ rebuild();
5731
+ }
5732
+ function defaultTableData(element) {
5733
+ var _a, _b;
5734
+ return {
5735
+ cells: createEmptyCells((_a = element.rows) != null ? _a : 3, (_b = element.columns) != null ? _b : 3),
5736
+ merges: []
5737
+ };
5738
+ }
5739
+ function isTableData(v) {
5740
+ return v !== null && typeof v === "object" && "cells" in v && Array.isArray(v.cells);
5741
+ }
5742
+ function renderTableElement(element, ctx, wrapper, pathKey) {
5743
+ const state = ctx.state;
5744
+ const rawPrefill = ctx.prefill[element.key];
5745
+ const initialData = isTableData(rawPrefill) ? rawPrefill : isTableData(element.default) ? element.default : defaultTableData(element);
5746
+ if (state.config.readonly) {
5747
+ renderReadonlyTable(initialData, wrapper);
5748
+ } else {
5749
+ renderEditTable(element, initialData, pathKey, ctx, wrapper);
5750
+ }
5751
+ }
5752
+ function validateTableElement(element, key, context) {
5753
+ const { scopeRoot, skipValidation } = context;
5754
+ const errors = [];
5755
+ const hiddenInput = scopeRoot.querySelector(
5756
+ `[name="${key}"]`
5757
+ );
5758
+ if (!hiddenInput) {
5759
+ return { value: null, errors };
5760
+ }
5761
+ let value = null;
5762
+ try {
5763
+ value = JSON.parse(hiddenInput.value);
5764
+ } catch {
5765
+ errors.push(`${key}: invalid table data`);
5766
+ return { value: null, errors };
5767
+ }
5768
+ if (!skipValidation && element.required) {
5769
+ const hasContent = value.cells.some(
5770
+ (row) => row.some((cell) => cell.trim() !== "")
5771
+ );
5772
+ if (!hasContent) {
5773
+ errors.push(`${key}: ${t("required", context.state)}`);
5774
+ }
5775
+ }
5776
+ return { value, errors };
5777
+ }
5778
+ function updateTableField(_element, fieldPath, value, context) {
5779
+ const { scopeRoot } = context;
5780
+ const hiddenInput = scopeRoot.querySelector(
5781
+ `[name="${fieldPath}"]`
5782
+ );
5783
+ if (!hiddenInput) {
5784
+ console.warn(
5785
+ `updateTableField: no hidden input found for "${fieldPath}". Re-render to reflect new data.`
5786
+ );
5787
+ return;
5788
+ }
5789
+ if (isTableData(value) && hiddenInput._applyExternalUpdate) {
5790
+ hiddenInput._applyExternalUpdate(value);
5791
+ } else {
5792
+ hiddenInput.value = JSON.stringify(value);
5793
+ }
5794
+ }
5795
+
4775
5796
  // src/components/index.ts
4776
5797
  function showTooltip(tooltipId, button) {
4777
5798
  const tooltip = document.getElementById(tooltipId);
@@ -5121,6 +6142,9 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
5121
6142
  renderSingleContainerElement(element, ctx, wrapper, pathKey);
5122
6143
  }
5123
6144
  break;
6145
+ case "table":
6146
+ renderTableElement(element, ctx, wrapper, pathKey);
6147
+ break;
5124
6148
  default: {
5125
6149
  const unsupported = document.createElement("div");
5126
6150
  unsupported.className = "text-red-500 text-sm";
@@ -5211,7 +6235,13 @@ var defaultConfig = {
5211
6235
  minFiles: "Minimum {min} files required",
5212
6236
  maxFiles: "Maximum {max} files allowed",
5213
6237
  unsupportedFieldType: "Unsupported field type: {type}",
5214
- invalidOption: "Invalid option"
6238
+ invalidOption: "Invalid option",
6239
+ tableAddRow: "Add row",
6240
+ tableAddColumn: "Add column",
6241
+ tableRemoveRow: "Remove row",
6242
+ tableRemoveColumn: "Remove column",
6243
+ tableMergeCells: "Merge cells (Ctrl+M)",
6244
+ tableSplitCell: "Split cell (Ctrl+Shift+M)"
5215
6245
  },
5216
6246
  ru: {
5217
6247
  // UI texts
@@ -5257,7 +6287,13 @@ var defaultConfig = {
5257
6287
  minFiles: "\u041C\u0438\u043D\u0438\u043C\u0443\u043C {min} \u0444\u0430\u0439\u043B\u043E\u0432",
5258
6288
  maxFiles: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {max} \u0444\u0430\u0439\u043B\u043E\u0432",
5259
6289
  unsupportedFieldType: "\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0442\u0438\u043F \u043F\u043E\u043B\u044F: {type}",
5260
- invalidOption: "\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"
6290
+ invalidOption: "\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435",
6291
+ tableAddRow: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
6292
+ tableAddColumn: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0442\u043E\u043B\u0431\u0435\u0446",
6293
+ tableRemoveRow: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
6294
+ tableRemoveColumn: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u043E\u043B\u0431\u0435\u0446",
6295
+ tableMergeCells: "\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0438 (Ctrl+M)",
6296
+ tableSplitCell: "\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0443 (Ctrl+Shift+M)"
5261
6297
  }
5262
6298
  },
5263
6299
  theme: {}
@@ -5531,6 +6567,10 @@ var componentRegistry = {
5531
6567
  // Deprecated type - delegates to container
5532
6568
  validate: validateGroupElement,
5533
6569
  update: updateGroupField
6570
+ },
6571
+ table: {
6572
+ validate: validateTableElement,
6573
+ update: updateTableField
5534
6574
  }
5535
6575
  };
5536
6576
  function getComponentOperations(elementType) {