@wipco/sdui 1.6.1 → 1.7.1

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.
Files changed (95) hide show
  1. package/dist/{DatePicker-DX9juElE.d.ts → DatePicker-BOOKdd5x.d.ts} +6 -6
  2. package/dist/calendar-nav-BSGsgJc4.d.ts +54 -0
  3. package/dist/{chunk-OKNF2SBB.js → chunk-2FHNGMBT.js} +3 -3
  4. package/dist/{chunk-AJ3KDBEE.js → chunk-466W3XNK.js} +1 -1
  5. package/dist/{chunk-UC725BGW.js → chunk-5FZBH3T5.js} +1 -1
  6. package/dist/{chunk-XUVWIRZJ.js → chunk-5LIFU57A.js} +2 -2
  7. package/dist/{chunk-VAXRTBJH.js → chunk-63GM4S25.js} +1 -1
  8. package/dist/{chunk-KO3EUNWB.js → chunk-7YTZB5LQ.js} +3 -3
  9. package/dist/{chunk-SGGIWPP6.js → chunk-7ZCM6XEM.js} +2 -2
  10. package/dist/{chunk-IIRVHSNK.js → chunk-A3SO6H47.js} +2 -2
  11. package/dist/{chunk-VSNHDHGS.js → chunk-B5MB64YQ.js} +209 -25
  12. package/dist/{chunk-US3K6Q5T.js → chunk-ENZBX4AR.js} +1 -1
  13. package/dist/{chunk-CUFGH3LB.js → chunk-FGKBXUUA.js} +1 -1
  14. package/dist/{chunk-VR5MFUD2.js → chunk-FUNCQCKN.js} +2 -2
  15. package/dist/{chunk-O4EMQWCV.js → chunk-FYVDPEJU.js} +120 -26
  16. package/dist/{chunk-GKYUDTMI.js → chunk-J6YDTBKY.js} +2 -2
  17. package/dist/{chunk-UZZLMPRW.js → chunk-JCG4575S.js} +2 -2
  18. package/dist/{chunk-34BABTH4.js → chunk-JMNUFA5M.js} +1 -1
  19. package/dist/{chunk-FP32LNFK.js → chunk-KAQ2VZBM.js} +166 -193
  20. package/dist/{chunk-A57GKV5F.js → chunk-KXTQOP3E.js} +3 -3
  21. package/dist/{chunk-3PT5OBOU.js → chunk-L6ODTHTX.js} +1 -1
  22. package/dist/{chunk-OQ7A3SFM.js → chunk-LKA43GYA.js} +1 -1
  23. package/dist/{chunk-GSXD52XG.js → chunk-LKL3SCBJ.js} +2 -2
  24. package/dist/{chunk-J7BZF4T4.js → chunk-MANONABB.js} +4 -4
  25. package/dist/{chunk-XFKNU6QN.js → chunk-NI3LMETK.js} +2 -2
  26. package/dist/{chunk-ESAFQXEU.js → chunk-NJTIJAFU.js} +2 -2
  27. package/dist/{chunk-PCFH74RD.js → chunk-NQDHGK7C.js} +3 -3
  28. package/dist/{chunk-YYAGE733.js → chunk-OWM7E55U.js} +8 -0
  29. package/dist/{chunk-OCYJ2ZOV.js → chunk-P62INTQ5.js} +1 -1
  30. package/dist/{chunk-7G6J6QFC.js → chunk-Q56LZJFX.js} +1 -1
  31. package/dist/{chunk-KDKRFWIF.js → chunk-QAGJ6FRE.js} +5 -5
  32. package/dist/chunk-QVNJMZUF.js +462 -0
  33. package/dist/{chunk-BITS7UJY.js → chunk-R2ORCOTG.js} +2 -2
  34. package/dist/{chunk-VQKX5PJ4.js → chunk-RA74PUMP.js} +3 -3
  35. package/dist/{chunk-USNPJEXH.js → chunk-RJYPRWWS.js} +4 -4
  36. package/dist/{chunk-TB7G5DXQ.js → chunk-S6GJRP5T.js} +2 -2
  37. package/dist/{chunk-TWUKNUUQ.js → chunk-SGR2QDEG.js} +2 -2
  38. package/dist/{chunk-3PCR73OC.js → chunk-T5LLOSHE.js} +3 -3
  39. package/dist/{chunk-GSM664TX.js → chunk-TAVKGYH5.js} +24 -1
  40. package/dist/{chunk-BV37UCDF.js → chunk-UBVYPV7H.js} +2 -2
  41. package/dist/chunk-VR4DQTWR.js +391 -0
  42. package/dist/{chunk-6YBSBC77.js → chunk-WPAZBAUW.js} +1 -1
  43. package/dist/{chunk-4YPPL7JK.js → chunk-WTQH2NI2.js} +1 -1
  44. package/dist/{chunk-NLSR3JYU.js → chunk-X7RSKFWF.js} +1 -1
  45. package/dist/{chunk-CSZCWBOE.js → chunk-XDR6HC4U.js} +14 -7
  46. package/dist/{chunk-EUSC5NMZ.js → chunk-Z4JZUIKE.js} +3 -3
  47. package/dist/{chunk-EGFDELSF.js → chunk-Z4LFZWHH.js} +2 -2
  48. package/dist/{chunk-JHFA5RDN.js → chunk-ZBX2CAOO.js} +2 -2
  49. package/dist/index.d.ts +22 -2
  50. package/dist/index.js +27 -19
  51. package/dist/meta.js +14 -1
  52. package/dist/rich/ExcelUploadCard.js +4 -4
  53. package/dist/rich.d.ts +2 -1
  54. package/dist/rich.js +7 -6
  55. package/dist/styles.css +10 -0
  56. package/dist/widgets/ApprovalLineEditor.js +9 -8
  57. package/dist/widgets/ApprovalOverridesEditor.js +11 -10
  58. package/dist/widgets/ApprovalStepper.js +2 -2
  59. package/dist/widgets/AutocompleteField.js +9 -8
  60. package/dist/widgets/ColumnBoard.js +2 -2
  61. package/dist/widgets/DiffView.js +2 -2
  62. package/dist/widgets/Disclosure.js +2 -2
  63. package/dist/widgets/EventCalendar.d.ts +14 -10
  64. package/dist/widgets/EventCalendar.js +5 -3
  65. package/dist/widgets/FileDropzone.js +7 -7
  66. package/dist/widgets/ImageLightbox.js +5 -5
  67. package/dist/widgets/InlineEdit.js +2 -2
  68. package/dist/widgets/JsonField.js +2 -2
  69. package/dist/widgets/OptionSchemaEditor.js +3 -3
  70. package/dist/widgets/OptionValueFields.js +3 -3
  71. package/dist/widgets/OrgChart.js +3 -3
  72. package/dist/widgets/OrgChartBoard.d.ts +57 -4
  73. package/dist/widgets/OrgChartBoard.js +8 -8
  74. package/dist/widgets/PeriodFilter.js +6 -5
  75. package/dist/widgets/PermissionMatrixEditor.js +4 -4
  76. package/dist/widgets/PositionSelect.js +4 -4
  77. package/dist/widgets/PrintSheet.js +2 -2
  78. package/dist/widgets/ProfileEditDiffCard.js +4 -4
  79. package/dist/widgets/QRDisplay.js +4 -4
  80. package/dist/widgets/RankBoard.js +6 -6
  81. package/dist/widgets/RecordSheet.d.ts +48 -1
  82. package/dist/widgets/RecordSheet.js +2 -1
  83. package/dist/widgets/RoleMemberPicker.js +5 -5
  84. package/dist/widgets/SearchableMultiPicker.js +7 -6
  85. package/dist/widgets/SecureScreen.js +2 -2
  86. package/dist/widgets/StagePeriodEditor.js +6 -5
  87. package/dist/widgets/TagInput.js +2 -2
  88. package/dist/widgets/TagMultiSelect.js +4 -4
  89. package/dist/widgets/Timeline.js +3 -3
  90. package/dist/widgets/TreePicker.js +8 -7
  91. package/dist/widgets/TreeView.js +7 -6
  92. package/dist/widgets/index.d.ts +1 -0
  93. package/dist/widgets/index.js +76 -75
  94. package/package.json +1 -1
  95. package/dist/chunk-IB2ZPUTF.js +0 -172
@@ -0,0 +1,391 @@
1
+ import { useIsMobile } from './chunk-TAVKGYH5.js';
2
+ import { boxed } from './chunk-3JTBOABA.js';
3
+ import React, { useRef, useState, useEffect } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ var RECORD_SHEET_CSS = `
7
+ .erp-recsheet__scroll{overflow-x:auto;min-width:0}
8
+ .erp-recsheet{width:100%;min-width:clamp(40rem,100%,44rem);border-collapse:collapse;table-layout:fixed;font-size:var(--wp-size-sm)}
9
+ .erp-recsheet td{height:var(--wp-row-h-dense);border:var(--wp-border-w) solid var(--wp-border-default);padding:var(--wp-space-1) var(--wp-space-2);color:var(--wp-text-default);vertical-align:middle;line-height:var(--wp-leading-snug);overflow-wrap:anywhere}
10
+ .erp-recsheet td.erp-recsheet__title{background:var(--wp-surface-inverse);color:var(--wp-text-inverse);text-align:center;font-size:var(--wp-size-lg);font-weight:var(--wp-weight-semibold);letter-spacing:var(--wp-tracking-heading);padding:var(--wp-space-3) var(--wp-space-2)}
11
+ .erp-recsheet td.erp-recsheet__label{background:var(--wp-surface-sunken);color:var(--wp-text-subtle);font-size:var(--wp-size-xs);font-weight:var(--wp-weight-semibold);text-align:center;white-space:nowrap}
12
+ .erp-recsheet td.erp-recsheet__section{background:var(--wp-surface-brand-tint);border-top:var(--wp-border-w-strong) solid var(--wp-border-brand);color:var(--wp-text-brand);font-size:var(--wp-size-xs);font-weight:var(--wp-weight-semibold);letter-spacing:var(--wp-tracking-heading)}
13
+ .erp-recsheet tr.erp-recsheet__gap td{border:0;padding:0;height:var(--wp-space-3)}
14
+ .erp-recsheet td.erp-recsheet__mono{font-family:var(--wp-font-mono);font-size:var(--wp-size-xs)}
15
+ .erp-recsheet td.erp-recsheet__photo{background:var(--wp-surface-sunken);padding:var(--wp-space-2);text-align:center}
16
+ .erp-recsheet__img{display:block;width:100%;height:11rem;object-fit:cover;border-radius:var(--wp-radius-sm)}
17
+ .erp-recsheet__noimg{display:flex;align-items:center;justify-content:center;width:100%;height:11rem;border:var(--wp-border-w) dashed var(--wp-border-default);border-radius:var(--wp-radius-sm);color:var(--wp-text-disabled);font-size:var(--wp-size-xs)}
18
+ .erp-recsheet__note{margin:var(--wp-space-2) 0 0;text-align:right;color:var(--wp-text-subtle);font-size:var(--wp-size-xs)}
19
+ .erp-recsheet__frame{position:relative;min-width:0}
20
+ .erp-recsheet__frame::before,.erp-recsheet__frame::after{content:"";position:absolute;top:0;bottom:0;z-index:1;width:var(--wp-space-4);pointer-events:none;opacity:0;transition:opacity var(--wp-duration-fast)}
21
+ .erp-recsheet__frame::before{left:0;background:linear-gradient(to right,color-mix(in srgb,var(--wp-text-strong) 16%,transparent),transparent)}
22
+ .erp-recsheet__frame::after{right:0;background:linear-gradient(to left,color-mix(in srgb,var(--wp-text-strong) 16%,transparent),transparent)}
23
+ .erp-recsheet__frame[data-more-left]::before,.erp-recsheet__frame[data-more-right]::after{opacity:1}
24
+ .erp-recsheet-stack{display:flex;flex-direction:column;gap:var(--wp-space-3);min-width:0;font-size:var(--wp-size-sm);color:var(--wp-text-default)}
25
+ .erp-recsheet-stack__title{padding:var(--wp-space-2) var(--wp-space-3);background:var(--wp-surface-inverse);color:var(--wp-text-inverse);font-size:var(--wp-size-lg);font-weight:var(--wp-weight-semibold);letter-spacing:var(--wp-tracking-heading);line-height:var(--wp-leading-snug);text-align:left}
26
+ .erp-recsheet-stack__block{display:flex;flex-direction:column;min-width:0}
27
+ .erp-recsheet-stack__head{padding:var(--wp-space-2);background:var(--wp-surface-brand-tint);border-top:var(--wp-border-w-strong) solid var(--wp-border-brand);color:var(--wp-text-brand);font-size:var(--wp-size-xs);font-weight:var(--wp-weight-semibold);letter-spacing:var(--wp-tracking-heading)}
28
+ .erp-recsheet-stack__pairs{width:100%;border-collapse:collapse;table-layout:fixed}
29
+ .erp-recsheet-stack__pairs :is(th,td){height:var(--wp-row-h-dense);border:var(--wp-border-w) solid var(--wp-border-default);padding:var(--wp-space-1) var(--wp-space-2);vertical-align:middle;line-height:var(--wp-leading-snug);text-align:left;overflow-wrap:anywhere}
30
+ .erp-recsheet-stack__pairs th{width:6.5rem;background:var(--wp-surface-sunken);color:var(--wp-text-subtle);font-size:var(--wp-size-xs);font-weight:var(--wp-weight-semibold);word-break:keep-all;overflow-wrap:break-word}
31
+ .erp-recsheet-stack__pairs td.erp-recsheet__mono{font-family:var(--wp-font-mono);font-size:var(--wp-size-xs)}
32
+ .erp-recsheet-stack__scroll{overflow-x:auto;min-width:0;overscroll-behavior-x:contain}
33
+ .erp-recsheet.erp-recsheet-stack__grid{min-width:0;width:100%;table-layout:auto}
34
+ .erp-recsheet.erp-recsheet-stack__grid td{word-break:keep-all;overflow-wrap:break-word}
35
+ .erp-recsheet.erp-recsheet-stack__grid td.erp-recsheet__label{white-space:normal;min-width:3.5rem}
36
+ .erp-recsheet.erp-recsheet-stack__grid td.erp-recsheet__mono{white-space:nowrap}
37
+ .erp-recsheet-stack__photo{align-self:center;width:6.5rem}
38
+ .erp-recsheet-stack__photo :is(.erp-recsheet__img,.erp-recsheet__noimg){height:8.5rem}
39
+ .erp-recsheet-stack .erp-recsheet__note{margin:0}
40
+ @media print{
41
+ .erp-recsheet__frame::before,.erp-recsheet__frame::after{display:none}
42
+ .erp-recsheet__scroll{overflow-x:visible}
43
+ .erp-recsheet{min-width:0}
44
+ .erp-recsheet tr{break-inside:avoid}
45
+ }
46
+ `;
47
+ function RecordSheetStyles() {
48
+ return /* @__PURE__ */ jsx("style", { href: "erp-recsheet", precedence: "medium", children: RECORD_SHEET_CSS });
49
+ }
50
+ function gridColumns(columns) {
51
+ if (typeof columns !== "number" || !Number.isFinite(columns)) return 24;
52
+ return Math.min(60, Math.max(1, Math.floor(columns)));
53
+ }
54
+ function cellClass(cell) {
55
+ const classes = [];
56
+ if (cell.kind === "label") classes.push("erp-recsheet__label");
57
+ else if (cell.kind === "section") classes.push("erp-recsheet__section");
58
+ else if (cell.kind === "photo") classes.push("erp-recsheet__photo");
59
+ if (cell.mono) classes.push("erp-recsheet__mono");
60
+ return classes.length > 0 ? classes.join(" ") : void 0;
61
+ }
62
+ function isBlank(value) {
63
+ return value === null || value === void 0 || value === "";
64
+ }
65
+ function pickValue(cell, item) {
66
+ if (!item || typeof item !== "object") return void 0;
67
+ const row = item;
68
+ if (cell.fields && cell.fields.length > 0) {
69
+ const parts = cell.fields.map((name) => isBlank(row[name]) ? "" : String(row[name]));
70
+ if (parts.every((part) => part === "")) return void 0;
71
+ return parts.join(cell.separator ?? " ~ ");
72
+ }
73
+ if (cell.field) return row[cell.field];
74
+ return void 0;
75
+ }
76
+ function PhotoCell({ src, alt }) {
77
+ const [failed, setFailed] = useState(false);
78
+ const label = alt ?? "\uC0AC\uC9C4";
79
+ if (!src || failed) return /* @__PURE__ */ jsx("span", { className: "erp-recsheet__noimg", children: label });
80
+ return /* @__PURE__ */ jsx("img", { className: "erp-recsheet__img", src, alt: label, onError: () => setFailed(true) });
81
+ }
82
+ function cellContent(cell, item, repeated) {
83
+ if (cell.kind === "photo") return /* @__PURE__ */ jsx(PhotoCell, { src: cell.src, alt: cell.alt });
84
+ if (!repeated) {
85
+ if (!isBlank(cell.text)) return cell.text;
86
+ if (cell.kind === "label" || cell.kind === "section") return null;
87
+ return cell.placeholder ?? "-";
88
+ }
89
+ const value = pickValue(cell, item);
90
+ if (!isBlank(value)) return value;
91
+ return item ? cell.placeholder ?? "-" : null;
92
+ }
93
+ function renderCells(cells, items, keyPrefix) {
94
+ return cells.map((cell, index) => {
95
+ const item = items ? items[cell.source ?? 0] ?? null : null;
96
+ return /* @__PURE__ */ jsx(
97
+ "td",
98
+ {
99
+ className: cellClass(cell),
100
+ colSpan: cell.span && cell.span > 1 ? cell.span : void 0,
101
+ rowSpan: cell.rowSpan && cell.rowSpan > 1 ? cell.rowSpan : void 0,
102
+ style: cell.align && cell.align !== "left" ? { textAlign: cell.align } : void 0,
103
+ children: cellContent(cell, item, items !== null)
104
+ },
105
+ `${keyPrefix}-${index}`
106
+ );
107
+ });
108
+ }
109
+ function repeatLength(sources, minRows) {
110
+ const longest = sources.reduce((max, source) => Math.max(max, source.length), 0);
111
+ return Math.max(longest, Math.max(0, Math.floor(minRows ?? 0)));
112
+ }
113
+ function spanOf(value, fallback = 1) {
114
+ return typeof value === "number" && Number.isFinite(value) && value >= 1 ? Math.floor(value) : fallback;
115
+ }
116
+ function placeRecordSheetCells(rows, width) {
117
+ const carry = new Array(width).fill(0);
118
+ return rows.map((row) => {
119
+ const cells = row && !row.gap && Array.isArray(row.cells) ? row.cells : [];
120
+ const placed = [];
121
+ let col = 0;
122
+ for (const cell of cells) {
123
+ while (col < width && carry[col] > 0) col += 1;
124
+ const start = Math.min(col, width);
125
+ const end = Math.min(width, start + spanOf(cell?.span));
126
+ placed.push({ cell: cell ?? {}, start, end });
127
+ const rowSpan = spanOf(cell?.rowSpan);
128
+ if (rowSpan > 1) for (let c = start; c < end; c += 1) carry[c] = rowSpan;
129
+ col = end;
130
+ }
131
+ for (let c = 0; c < width; c += 1) if (carry[c] > 0) carry[c] -= 1;
132
+ return placed;
133
+ });
134
+ }
135
+ var isLabelLike = (cell) => cell.kind === "label" || cell.kind === "section";
136
+ function pairsOf(cells) {
137
+ const pairs = [];
138
+ for (const cell of cells) {
139
+ if (isLabelLike(cell)) pairs.push({ label: cell, values: [] });
140
+ else if (pairs.length > 0) pairs[pairs.length - 1].values.push(cell);
141
+ else pairs.push({ label: null, values: [cell] });
142
+ }
143
+ return pairs;
144
+ }
145
+ function stackRecordSheet(rows, width) {
146
+ const list = Array.isArray(rows) ? rows : [];
147
+ const placedRows = placeRecordSheetCells(list, width);
148
+ const blocks = [];
149
+ let open = [];
150
+ const push = (block, part) => {
151
+ const last = block.parts[block.parts.length - 1];
152
+ if (last && last.kind === "pairs" && part.kind === "pairs") last.pairs.push(...part.pairs);
153
+ else if (last && last.kind === "grid" && part.kind === "grid") last.rows.push(...part.rows);
154
+ else block.parts.push(part);
155
+ };
156
+ list.forEach((row, index) => {
157
+ if (row?.gap) {
158
+ open = [];
159
+ return;
160
+ }
161
+ const placed = placedRows[index];
162
+ if (placed.length === 0) return;
163
+ if (placed.every((p) => p.cell.kind === "section")) {
164
+ open = placed.map((p) => ({ block: { section: p.cell, parts: [] }, start: p.start, end: p.end }));
165
+ blocks.push(...open.map((o) => o.block));
166
+ return;
167
+ }
168
+ if (open.length === 0) {
169
+ const block = { section: null, parts: [] };
170
+ blocks.push(block);
171
+ open = [{ block, start: 0, end: width }];
172
+ }
173
+ for (const target of open) {
174
+ const mine = placed.filter((p) => {
175
+ const owner = open.find((o) => p.start >= o.start && p.start < o.end) ?? open[0];
176
+ return owner === target;
177
+ });
178
+ if (mine.length === 0) continue;
179
+ const rest = [];
180
+ for (const p of mine) {
181
+ if (p.cell.kind === "photo") push(target.block, { kind: "photo", cell: p.cell });
182
+ else rest.push(p);
183
+ }
184
+ if (rest.length === 0) continue;
185
+ const repeated = Array.isArray(row.repeat);
186
+ const hasLabel = rest.some((p) => isLabelLike(p.cell));
187
+ const hasValue = rest.some((p) => !isLabelLike(p.cell));
188
+ if (!repeated && hasLabel && hasValue) {
189
+ push(target.block, { kind: "pairs", pairs: pairsOf(rest.map((p) => p.cell)) });
190
+ continue;
191
+ }
192
+ const cells = rest.map((p) => ({ ...p, start: p.start - target.start, end: p.end - target.start }));
193
+ push(target.block, {
194
+ kind: "grid",
195
+ rows: [
196
+ repeated ? {
197
+ cells,
198
+ repeat: row.repeat.map((source) => Array.isArray(source) ? source : []),
199
+ minRows: row.minRows
200
+ } : { cells }
201
+ ]
202
+ });
203
+ }
204
+ });
205
+ return blocks;
206
+ }
207
+ function gridBoundaries(rows) {
208
+ const set = /* @__PURE__ */ new Set();
209
+ for (const row of rows) for (const p of row.cells) set.add(p.start).add(p.end);
210
+ return [...set].sort((a, b) => a - b);
211
+ }
212
+ function StackGrid({ rows, keyPrefix }) {
213
+ const scrollRef = useRef(null);
214
+ const edges = useScrollEdges(scrollRef);
215
+ const bounds = gridBoundaries(rows);
216
+ const spanIn = (p) => {
217
+ const n = bounds.filter((b) => b >= p.start && b < p.end).length;
218
+ return n > 1 ? n : void 0;
219
+ };
220
+ const lines = [];
221
+ const cellsOf = (row, items, key) => row.cells.map((p, index) => {
222
+ const item = items ? items[p.cell.source ?? 0] ?? null : null;
223
+ return /* @__PURE__ */ jsx(
224
+ "td",
225
+ {
226
+ className: cellClass(p.cell),
227
+ colSpan: spanIn(p),
228
+ style: p.cell.align && p.cell.align !== "left" ? { textAlign: p.cell.align } : void 0,
229
+ children: cellContent(p.cell, item, items !== null)
230
+ },
231
+ `${key}-${index}`
232
+ );
233
+ });
234
+ rows.forEach((row, rowIndex) => {
235
+ if (!row.repeat) {
236
+ lines.push(/* @__PURE__ */ jsx("tr", { children: cellsOf(row, null, `${keyPrefix}-r${rowIndex}`) }, `${keyPrefix}-r${rowIndex}`));
237
+ return;
238
+ }
239
+ const sources = row.repeat;
240
+ const used = new Set(row.cells.map((p) => p.cell.source ?? 0));
241
+ const longest = [...used].reduce((max, i) => Math.max(max, sources[i]?.length ?? 0), 0);
242
+ const count = longest > 0 ? longest : Math.min(1, Math.max(0, Math.floor(row.minRows ?? 0)));
243
+ for (let line = 0; line < count; line += 1) {
244
+ const key = `${keyPrefix}-r${rowIndex}-${line}`;
245
+ lines.push(/* @__PURE__ */ jsx("tr", { children: cellsOf(row, sources.map((source) => source[line] ?? null), key) }, key));
246
+ }
247
+ });
248
+ return /* @__PURE__ */ jsx(
249
+ "div",
250
+ {
251
+ className: "erp-recsheet__frame",
252
+ "data-more-left": edges.left ? "" : void 0,
253
+ "data-more-right": edges.right ? "" : void 0,
254
+ children: /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "erp-recsheet-stack__scroll", children: /* @__PURE__ */ jsx("table", { className: "erp-recsheet erp-recsheet-stack__grid", children: /* @__PURE__ */ jsx("tbody", { children: lines }) }) })
255
+ }
256
+ );
257
+ }
258
+ function StackPairs({ pairs, keyPrefix }) {
259
+ return /* @__PURE__ */ jsx("table", { className: "erp-recsheet-stack__pairs", children: /* @__PURE__ */ jsx("tbody", { children: pairs.map((pair, index) => {
260
+ const values = pair.values.map((cell, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
261
+ i > 0 ? " " : null,
262
+ cellContent(cell, null, false)
263
+ ] }, i));
264
+ const mono = pair.values.length > 0 && pair.values.every((cell) => cell.mono);
265
+ return /* @__PURE__ */ jsx("tr", { className: "erp-recsheet-stack__pair", children: pair.label ? /* @__PURE__ */ jsxs(Fragment, { children: [
266
+ /* @__PURE__ */ jsx("th", { scope: "row", children: pair.label.text }),
267
+ /* @__PURE__ */ jsx("td", { className: mono ? "erp-recsheet__mono" : void 0, children: values })
268
+ ] }) : /* @__PURE__ */ jsx("td", { colSpan: 2, className: mono ? "erp-recsheet__mono" : void 0, children: values }) }, `${keyPrefix}-${index}`);
269
+ }) }) });
270
+ }
271
+ function RecordSheetStack({
272
+ width,
273
+ title,
274
+ rows,
275
+ note,
276
+ className,
277
+ style
278
+ }) {
279
+ const blocks = stackRecordSheet(rows, width);
280
+ return /* @__PURE__ */ jsxs("div", { className: className ? `erp-recsheet__frame ${className}` : "erp-recsheet__frame", style, children: [
281
+ /* @__PURE__ */ jsx(RecordSheetStyles, {}),
282
+ /* @__PURE__ */ jsxs("div", { className: "erp-recsheet-stack", children: [
283
+ title ? /* @__PURE__ */ jsx("div", { className: "erp-recsheet-stack__title", children: title }) : null,
284
+ blocks.map((block, blockIndex) => /* @__PURE__ */ jsxs("section", { className: "erp-recsheet-stack__block", children: [
285
+ block.section ? /* @__PURE__ */ jsx("div", { className: "erp-recsheet-stack__head", children: block.section.text }) : null,
286
+ block.parts.map((part, partIndex) => {
287
+ const key = `b${blockIndex}-p${partIndex}`;
288
+ if (part.kind === "photo") {
289
+ return /* @__PURE__ */ jsx("div", { className: "erp-recsheet-stack__photo", children: /* @__PURE__ */ jsx(PhotoCell, { src: part.cell.src, alt: part.cell.alt }) }, key);
290
+ }
291
+ if (part.kind === "pairs") return /* @__PURE__ */ jsx(StackPairs, { pairs: part.pairs, keyPrefix: key }, key);
292
+ return /* @__PURE__ */ jsx(StackGrid, { rows: part.rows, keyPrefix: key }, key);
293
+ })
294
+ ] }, blockIndex)),
295
+ note ? /* @__PURE__ */ jsx("p", { className: "erp-recsheet__note", children: note }) : null
296
+ ] })
297
+ ] });
298
+ }
299
+ function RecordSheetImpl(props) {
300
+ const isMobile = useIsMobile();
301
+ if (isMobile) {
302
+ return /* @__PURE__ */ jsx(
303
+ RecordSheetStack,
304
+ {
305
+ width: gridColumns(props.columns),
306
+ title: props.title,
307
+ rows: Array.isArray(props.rows) ? props.rows : [],
308
+ note: props.note
309
+ }
310
+ );
311
+ }
312
+ return /* @__PURE__ */ jsx(RecordSheetGrid, { ...props });
313
+ }
314
+ function RecordSheetGrid({ columns, title, rows, note, className, style }) {
315
+ const scrollRef = useRef(null);
316
+ const edges = useScrollEdges(scrollRef);
317
+ const width = gridColumns(columns);
318
+ const list = Array.isArray(rows) ? rows : [];
319
+ const body = [];
320
+ list.forEach((row, rowIndex) => {
321
+ if (row?.gap) {
322
+ body.push(
323
+ /* @__PURE__ */ jsx("tr", { className: "erp-recsheet__gap", children: /* @__PURE__ */ jsx("td", { colSpan: width }) }, `gap-${rowIndex}`)
324
+ );
325
+ return;
326
+ }
327
+ const cells = Array.isArray(row?.cells) ? row.cells : [];
328
+ if (cells.length === 0) return;
329
+ if (Array.isArray(row.repeat)) {
330
+ const sources = row.repeat.map((source) => Array.isArray(source) ? source : []);
331
+ const count = repeatLength(sources, row.minRows);
332
+ for (let line = 0; line < count; line += 1) {
333
+ body.push(
334
+ /* @__PURE__ */ jsx("tr", { children: renderCells(
335
+ cells,
336
+ sources.map((source) => source[line] ?? null),
337
+ `rep-${rowIndex}-${line}`
338
+ ) }, `rep-${rowIndex}-${line}`)
339
+ );
340
+ }
341
+ return;
342
+ }
343
+ body.push(/* @__PURE__ */ jsx("tr", { children: renderCells(cells, null, `row-${rowIndex}`) }, `row-${rowIndex}`));
344
+ });
345
+ return /* @__PURE__ */ jsx(
346
+ "div",
347
+ {
348
+ className: className ? `erp-recsheet__frame ${className}` : "erp-recsheet__frame",
349
+ style,
350
+ "data-more-left": edges.left ? "" : void 0,
351
+ "data-more-right": edges.right ? "" : void 0,
352
+ children: /* @__PURE__ */ jsxs("div", { ref: scrollRef, className: "erp-recsheet__scroll", children: [
353
+ /* @__PURE__ */ jsx(RecordSheetStyles, {}),
354
+ /* @__PURE__ */ jsxs("table", { className: "erp-recsheet", children: [
355
+ /* @__PURE__ */ jsx("colgroup", { children: Array.from({ length: width }, (_, index) => /* @__PURE__ */ jsx("col", { style: { width: `${100 / width}%` } }, index)) }),
356
+ /* @__PURE__ */ jsxs("tbody", { children: [
357
+ title ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "erp-recsheet__title", colSpan: width, children: title }) }) : null,
358
+ body
359
+ ] })
360
+ ] }),
361
+ note ? /* @__PURE__ */ jsx("p", { className: "erp-recsheet__note", children: note }) : null
362
+ ] })
363
+ }
364
+ );
365
+ }
366
+ function useScrollEdges(ref) {
367
+ const [edges, setEdges] = useState({ left: false, right: false });
368
+ useEffect(() => {
369
+ const el = ref.current;
370
+ if (!el) return;
371
+ const update = () => {
372
+ const left = el.scrollLeft > 1;
373
+ const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
374
+ setEdges((prev) => prev.left === left && prev.right === right ? prev : { left, right });
375
+ };
376
+ update();
377
+ el.addEventListener("scroll", update, { passive: true });
378
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
379
+ observer?.observe(el);
380
+ const table = el.querySelector("table");
381
+ if (table) observer?.observe(table);
382
+ return () => {
383
+ el.removeEventListener("scroll", update);
384
+ observer?.disconnect();
385
+ };
386
+ }, [ref]);
387
+ return edges;
388
+ }
389
+ var RecordSheet = boxed(RecordSheetImpl, "RecordSheet");
390
+
391
+ export { RecordSheet, placeRecordSheetCells, stackRecordSheet };
@@ -1,4 +1,4 @@
1
- import { Button } from './chunk-YYAGE733.js';
1
+ import { Button } from './chunk-OWM7E55U.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import { useEffect, useState } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { Spinner, EmptyState } from './chunk-YYAGE733.js';
1
+ import { Spinner, EmptyState } from './chunk-OWM7E55U.js';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
4
 
@@ -1,4 +1,4 @@
1
- import { Icon, Badge } from './chunk-YYAGE733.js';
1
+ import { Icon, Badge } from './chunk-OWM7E55U.js';
2
2
  import { boxed } from './chunk-3JTBOABA.js';
3
3
  import React from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -6,21 +6,25 @@ var MAX_ZOOM = 1.8;
6
6
  var PINCH_CLICK_GRACE_MS = 400;
7
7
  var PAN_SLOP_PX = 6;
8
8
  var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
9
- function applyZoom(current, nextScale, focal) {
10
- const scale = clamp(nextScale, MIN_ZOOM, MAX_ZOOM);
9
+ function applyZoom(current, nextScale, focal, minScale = MIN_ZOOM) {
10
+ const scale = clamp(nextScale, Math.min(MIN_ZOOM, minScale), MAX_ZOOM);
11
11
  return {
12
12
  scale,
13
13
  x: focal.x - (focal.x - current.x) / current.scale * scale,
14
14
  y: focal.y - (focal.y - current.y) / current.scale * scale
15
15
  };
16
16
  }
17
- function applyPinch(start, startDistance, startMid, distance, mid) {
17
+ function applyPinch(start, startDistance, startMid, distance, mid, minScale = MIN_ZOOM) {
18
18
  const ratio = startDistance > 0 ? distance / startDistance : 1;
19
- const zoomed = applyZoom(start, start.scale * ratio, startMid);
19
+ const zoomed = applyZoom(start, start.scale * ratio, startMid, minScale);
20
20
  return { ...zoomed, x: zoomed.x + mid.x - startMid.x, y: zoomed.y + mid.y - startMid.y };
21
21
  }
22
22
  function usePanZoomCanvas(options) {
23
23
  const { stopSelector, initialView } = options;
24
+ const minScaleRef = useRef(options.minScale ?? MIN_ZOOM);
25
+ minScaleRef.current = options.minScale ?? MIN_ZOOM;
26
+ const wheelNeedsCtrlRef = useRef(Boolean(options.wheelNeedsCtrl));
27
+ wheelNeedsCtrlRef.current = Boolean(options.wheelNeedsCtrl);
24
28
  const viewportRef = useRef(null);
25
29
  const dragRef = useRef(null);
26
30
  const interactedRef = useRef(false);
@@ -42,6 +46,8 @@ function usePanZoomCanvas(options) {
42
46
  const [nativeFullscreen, setNativeFullscreen] = useState(false);
43
47
  const [pseudoFullscreen, setPseudoFullscreen] = useState(false);
44
48
  const isFullscreen = nativeFullscreen || pseudoFullscreen;
49
+ const isFullscreenRef = useRef(isFullscreen);
50
+ isFullscreenRef.current = isFullscreen;
45
51
  useEffect(() => {
46
52
  const handleFullscreenChange = () => {
47
53
  setNativeFullscreen(document.fullscreenElement === viewportRef.current);
@@ -70,12 +76,13 @@ function usePanZoomCanvas(options) {
70
76
  const viewport = viewportRef.current;
71
77
  if (!viewport) return;
72
78
  const onWheel = (event) => {
79
+ if (wheelNeedsCtrlRef.current && !isFullscreenRef.current && !event.ctrlKey) return;
73
80
  event.preventDefault();
74
81
  interactedRef.current = true;
75
82
  const rect = viewport.getBoundingClientRect();
76
83
  const focal = { x: event.clientX - rect.left, y: event.clientY - rect.top };
77
84
  setView(
78
- (current) => applyZoom(current, current.scale * (event.deltaY > 0 ? 0.9 : 1.1), focal)
85
+ (current) => applyZoom(current, current.scale * (event.deltaY > 0 ? 0.9 : 1.1), focal, minScaleRef.current)
79
86
  );
80
87
  };
81
88
  viewport.addEventListener("wheel", onWheel, { passive: false });
@@ -88,7 +95,7 @@ function usePanZoomCanvas(options) {
88
95
  }, []);
89
96
  const zoomAt = useCallback((nextScale, focal) => {
90
97
  interactedRef.current = true;
91
- setView((current) => applyZoom(current, nextScale, focal));
98
+ setView((current) => applyZoom(current, nextScale, focal, minScaleRef.current));
92
99
  }, []);
93
100
  const localPoint = (event) => {
94
101
  const rect = event.currentTarget.getBoundingClientRect();
@@ -165,7 +172,7 @@ function usePanZoomCanvas(options) {
165
172
  const pinch = pinchRef.current;
166
173
  if (pinch && pointersRef.current.size >= 2) {
167
174
  const { distance, mid } = pinchGeometry();
168
- setView(applyPinch(pinch.start, pinch.distance, pinch.mid, distance, mid));
175
+ setView(applyPinch(pinch.start, pinch.distance, pinch.mid, distance, mid, minScaleRef.current));
169
176
  return;
170
177
  }
171
178
  const pending = pendingRef.current;
@@ -1,6 +1,6 @@
1
- import { ImageLightbox } from './chunk-3PT5OBOU.js';
2
- import { useSduiSandbox, useSduiAdapter, hostRawFetchOf, defaultAdapter, useSduiAssetResolver } from './chunk-JHFA5RDN.js';
3
- import { Spinner, Icon, IconButton } from './chunk-YYAGE733.js';
1
+ import { ImageLightbox } from './chunk-L6ODTHTX.js';
2
+ import { useSduiSandbox, useSduiAdapter, hostRawFetchOf, defaultAdapter, useSduiAssetResolver } from './chunk-ZBX2CAOO.js';
3
+ import { Spinner, Icon, IconButton } from './chunk-OWM7E55U.js';
4
4
  import { boxed } from './chunk-3JTBOABA.js';
5
5
  import { useState, useRef, useEffect, useCallback } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { usePanZoomCanvas, MIN_ZOOM, MAX_ZOOM } from './chunk-CSZCWBOE.js';
2
- import { IconButton, Badge } from './chunk-YYAGE733.js';
1
+ import { usePanZoomCanvas, MIN_ZOOM, MAX_ZOOM } from './chunk-XDR6HC4U.js';
2
+ import { IconButton, Badge } from './chunk-OWM7E55U.js';
3
3
  import { boxed } from './chunk-3JTBOABA.js';
4
4
  import { useState, useMemo, useCallback, useEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
- import { DEV } from './chunk-GSM664TX.js';
2
- import { warnOnce } from './chunk-YYAGE733.js';
1
+ import { DEV } from './chunk-TAVKGYH5.js';
2
+ import { warnOnce } from './chunk-OWM7E55U.js';
3
3
  import { createContext, useContext } from 'react';
4
4
 
5
5
  function isSafeNavigationTarget(to) {
package/dist/index.d.ts CHANGED
@@ -4,8 +4,9 @@ import { S as SduiPage, a as SduiAction, b as SduiPropValue, c as SduiRule } fro
4
4
  export { d as SduiArgRef, e as SduiCalcRef, f as SduiChild, g as SduiIndexRef, h as SduiItemRef, i as SduiNode, j as SduiPageQuery, k as SduiScalar, l as SduiStateRef } from './types-CMikpoN5.js';
5
5
  import { S as SduiBoxProps, a as SduiContainerLayoutProps, b as SduiColumnsScale, c as SduiGapScale, d as SduiWeightScale, e as SduiTextAlignScale } from './layout-DmRikwZb.js';
6
6
  export { C as CHILD_LAYOUT_PROPS, L as LAYOUT_CSS, f as SduiAlignScale, g as SduiChildLayoutProps, h as SduiSizeScale, i as SduiSpanScale, j as childLayoutFrom, k as columnsClass, l as containerLayoutStyle, m as joinClass, n as mergeStyle, t as typographyStyle } from './layout-DmRikwZb.js';
7
- import { D as DatePickerProps, a as DateRangePickerProps, T as TimePickerProps } from './DatePicker-DX9juElE.js';
8
- export { b as DatePicker, c as DateRangePicker, d as DateRangeValue, e as TimePicker } from './DatePicker-DX9juElE.js';
7
+ import { D as DatePickerProps, a as DateRangePickerProps, T as TimePickerProps } from './DatePicker-BOOKdd5x.js';
8
+ export { b as DatePicker, c as DateRangePicker, d as DateRangeValue, e as TimePicker } from './DatePicker-BOOKdd5x.js';
9
+ import './calendar-nav-BSGsgJc4.js';
9
10
 
10
11
  /**
11
12
  * 호스트 앱 연결점 — ERP에서는 라우터·erpFetch·SSE가 하던 일을
@@ -773,6 +774,8 @@ interface ResultTableColumn<T> {
773
774
  strong?: boolean;
774
775
  /** CSS width — "180px", "22%" */
775
776
  width?: string;
777
+ /** 한 줄로 둔다 — `wp-table__nowrap`(white-space:nowrap). 날짜·번호처럼 갈라지면 읽기 어려운 짧은 값. */
778
+ nowrap?: boolean;
776
779
  /**
777
780
  * 칸 이름(문자열) — 각 `<td data-label>` 로 나간다. 좁은 화면에서 표를 행 카드로 쌓을 때
778
781
  * 칸마다 "무슨 값인지"를 CSS(`attr(data-label)`)가 붙인다. 생략하면 `header` 가 문자열일
@@ -784,6 +787,12 @@ interface ResultTableColumn<T> {
784
787
  * `title`: 카드 머리(라벨 없이 굵게), `hide`: 모바일 카드에서 숨김.
785
788
  */
786
789
  mobile?: 'title' | 'hide';
790
+ /**
791
+ * 태블릿(768–1023.98px) 표에서의 역할 — `data-tablet` 속성으로 th·td 둘 다에 나간다(모양은 소비자 CSS).
792
+ * `hide`: 태블릿 폭의 표에서 이 열을 뺀다. 모바일 행 카드(`mobile`)와는 따로다 — 태블릿에서 빼고 싶은 긴
793
+ * 목록 열이 폰 카드에서는 값이 있어야 하는 경우가 있다(예전에는 `mobile:'hide'` 를 태블릿에도 빌려 썼다).
794
+ */
795
+ tablet?: 'hide';
787
796
  render?: (row: T, index: number) => React__default.ReactNode;
788
797
  }
789
798
  type SortDir = 'asc' | 'desc';
@@ -1153,6 +1162,10 @@ interface SearchColumn<T> {
1153
1162
  * 아무 열도 `title` 이 아니면 `primaryKey` 열, 그다음 첫 `strong` 열이 머리가 된다.
1154
1163
  */
1155
1164
  mobile?: 'title' | 'hide';
1165
+ /** 태블릿(768–1023.98px) 표에서 이 열을 뺀다(`hide`) — 모바일 카드의 `mobile` 과 따로 정한다. */
1166
+ tablet?: 'hide';
1167
+ /** 표 칸을 한 줄로 둔다(`wp-table__nowrap`). */
1168
+ nowrap?: boolean;
1156
1169
  accessor?: (item: T) => string | number | Date | null | undefined;
1157
1170
  render?: (item: T) => React__default.ReactNode;
1158
1171
  }
@@ -1498,6 +1511,13 @@ interface SduiSearchColumn {
1498
1511
  * `hide`: 모바일에서 숨김(식별자·보조 열). 생략하면 보통 칸(라벨 + 값).
1499
1512
  */
1500
1513
  mobile?: 'title' | 'hide';
1514
+ /**
1515
+ * 태블릿(768–1023.98px) 표에서의 역할 — `hide`: 이 폭의 표에서 열을 뺀다. 모바일 카드(`mobile`)와 따로다:
1516
+ * 태블릿 표에서는 넘치는 긴 목록 열이라도 폰 카드에서는 보여 줄 수 있다. 생략하면 태블릿에서도 보인다.
1517
+ */
1518
+ tablet?: 'hide';
1519
+ /** 표 칸을 한 줄로 둔다(white-space:nowrap) — 날짜·번호처럼 두 줄로 갈라지면 읽기 어려운 짧은 값. */
1520
+ nowrap?: boolean;
1501
1521
  /** 셀 표현. 생략하면 row[key]를 그대로 출력한다 — 어휘는 `cells.tsx` 참고 */
1502
1522
  cell?: SduiCell;
1503
1523
  }