@wipco/sdui 1.5.0 → 1.6.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 (143) hide show
  1. package/dist/{DatePicker-C_TT_SOw.d.ts → DatePicker-DX9juElE.d.ts} +10 -1
  2. package/dist/{chunk-7P7N43JV.js → chunk-34BABTH4.js} +2 -2
  3. package/dist/{chunk-DMLDKW7K.js → chunk-37BNG4DT.js} +1 -1
  4. package/dist/{chunk-5WW4MBRO.js → chunk-3JTBOABA.js} +6 -2
  5. package/dist/{chunk-HBIIEYKW.js → chunk-3PCR73OC.js} +4 -4
  6. package/dist/{chunk-VY7MVBSE.js → chunk-3PT5OBOU.js} +2 -2
  7. package/dist/{chunk-YVTV4ZV6.js → chunk-4YPPL7JK.js} +1 -1
  8. package/dist/{chunk-E6ISNQK3.js → chunk-6YBSBC77.js} +31 -8
  9. package/dist/{chunk-YWUDBLO6.js → chunk-7G6J6QFC.js} +2 -2
  10. package/dist/{chunk-UV6HHFEW.js → chunk-A57GKV5F.js} +129 -68
  11. package/dist/{chunk-I45EUZC6.js → chunk-AJ3KDBEE.js} +4 -2
  12. package/dist/chunk-BITS7UJY.js +333 -0
  13. package/dist/{chunk-DOL57OOP.js → chunk-BV37UCDF.js} +3 -3
  14. package/dist/chunk-CSZCWBOE.js +256 -0
  15. package/dist/{chunk-XGFDY43I.js → chunk-CUFGH3LB.js} +88 -18
  16. package/dist/{chunk-A6LAIGUS.js → chunk-EGFDELSF.js} +7 -4
  17. package/dist/{chunk-JIV4YHCZ.js → chunk-EJIWOD7W.js} +1 -1
  18. package/dist/{chunk-CXW6KT6X.js → chunk-ESAFQXEU.js} +3 -3
  19. package/dist/{chunk-N5RKM6IC.js → chunk-EUSC5NMZ.js} +4 -4
  20. package/dist/{chunk-RWSH2MBU.js → chunk-FP32LNFK.js} +73 -19
  21. package/dist/{chunk-44TDRCKR.js → chunk-GKYUDTMI.js} +5 -5
  22. package/dist/chunk-GSM664TX.js +38 -0
  23. package/dist/{chunk-I5SD7XRA.js → chunk-GSXD52XG.js} +3 -3
  24. package/dist/{chunk-O4IISNE7.js → chunk-IB2ZPUTF.js} +51 -12
  25. package/dist/{chunk-YOZN35HD.js → chunk-IIRVHSNK.js} +3 -3
  26. package/dist/{chunk-FNMS7OB7.js → chunk-J7BZF4T4.js} +5 -5
  27. package/dist/{chunk-KO6OEY3G.js → chunk-JHFA5RDN.js} +3 -13
  28. package/dist/{chunk-DKVSM5HX.js → chunk-KDKRFWIF.js} +6 -6
  29. package/dist/chunk-KO3EUNWB.js +380 -0
  30. package/dist/{chunk-JYAR7D5U.js → chunk-NLSR3JYU.js} +2 -2
  31. package/dist/{chunk-ZORQPB6B.js → chunk-O4EMQWCV.js} +325 -360
  32. package/dist/{chunk-2HLXCKK7.js → chunk-OCYJ2ZOV.js} +2 -2
  33. package/dist/{chunk-OQ52XMGG.js → chunk-OKNF2SBB.js} +4 -4
  34. package/dist/{chunk-GNA6SX2H.js → chunk-OQ7A3SFM.js} +2 -2
  35. package/dist/{chunk-A5XQOEVH.js → chunk-PCFH74RD.js} +4 -4
  36. package/dist/{chunk-BEM2YS7U.js → chunk-SGGIWPP6.js} +3 -3
  37. package/dist/{chunk-NIJUVTEW.js → chunk-TB7G5DXQ.js} +3 -3
  38. package/dist/{chunk-GZBY7I4Y.js → chunk-TWUKNUUQ.js} +66 -10
  39. package/dist/{chunk-WPSK2CB5.js → chunk-UC725BGW.js} +16 -5
  40. package/dist/{chunk-ZFNFPXPR.js → chunk-US3K6Q5T.js} +2 -2
  41. package/dist/{chunk-MPT7WJG2.js → chunk-USNPJEXH.js} +5 -5
  42. package/dist/{chunk-SAPBDJJQ.js → chunk-UZZLMPRW.js} +2 -2
  43. package/dist/{chunk-Q4SWUINA.js → chunk-VAXRTBJH.js} +1 -1
  44. package/dist/{chunk-RYBBPFI7.js → chunk-VQKX5PJ4.js} +4 -4
  45. package/dist/chunk-VR5MFUD2.js +106 -0
  46. package/dist/{chunk-JZXU22PE.js → chunk-VSNHDHGS.js} +208 -117
  47. package/dist/{chunk-2YP5N4VV.js → chunk-VZOQRQAR.js} +1 -1
  48. package/dist/{chunk-AXGF4M57.js → chunk-XAXROQKB.js} +1 -1
  49. package/dist/{chunk-IZ3JA2BR.js → chunk-XFKNU6QN.js} +3 -3
  50. package/dist/{chunk-GTGS2QRL.js → chunk-XUVWIRZJ.js} +3 -3
  51. package/dist/{chunk-RNOUXUBO.js → chunk-YYAGE733.js} +323 -59
  52. package/dist/index.d.ts +449 -91
  53. package/dist/index.js +1060 -138
  54. package/dist/{layout-C3nRXMwb.d.ts → layout-DmRikwZb.d.ts} +5 -4
  55. package/dist/meta.d.ts +1 -1
  56. package/dist/meta.js +351 -25
  57. package/dist/rich/ExcelUploadCard.d.ts +1 -1
  58. package/dist/rich/ExcelUploadCard.js +5 -4
  59. package/dist/rich.d.ts +22 -3
  60. package/dist/rich.js +304 -35
  61. package/dist/styles.css +119 -4
  62. package/dist/{types-COjGKgmd.d.ts → types-CMikpoN5.d.ts} +65 -2
  63. package/dist/widgets/ApprovalLineEditor.d.ts +1 -1
  64. package/dist/widgets/ApprovalLineEditor.js +9 -8
  65. package/dist/widgets/ApprovalOverridesEditor.d.ts +1 -1
  66. package/dist/widgets/ApprovalOverridesEditor.js +11 -10
  67. package/dist/widgets/ApprovalStepper.d.ts +29 -4
  68. package/dist/widgets/ApprovalStepper.js +3 -3
  69. package/dist/widgets/AutocompleteField.d.ts +1 -1
  70. package/dist/widgets/AutocompleteField.js +9 -8
  71. package/dist/widgets/ColumnBoard.d.ts +1 -1
  72. package/dist/widgets/ColumnBoard.js +3 -3
  73. package/dist/widgets/DiffView.d.ts +1 -1
  74. package/dist/widgets/DiffView.js +3 -3
  75. package/dist/widgets/Disclosure.d.ts +1 -1
  76. package/dist/widgets/Disclosure.js +3 -3
  77. package/dist/widgets/EventCalendar.d.ts +5 -1
  78. package/dist/widgets/EventCalendar.js +5 -3
  79. package/dist/widgets/FileDropzone.d.ts +1 -1
  80. package/dist/widgets/FileDropzone.js +9 -7
  81. package/dist/widgets/ImageLightbox.d.ts +1 -1
  82. package/dist/widgets/ImageLightbox.js +7 -5
  83. package/dist/widgets/InlineEdit.d.ts +1 -1
  84. package/dist/widgets/InlineEdit.js +3 -3
  85. package/dist/widgets/JsonField.d.ts +1 -1
  86. package/dist/widgets/JsonField.js +3 -3
  87. package/dist/widgets/LiveClock.d.ts +1 -1
  88. package/dist/widgets/LiveClock.js +2 -2
  89. package/dist/widgets/OptionSchemaEditor.d.ts +1 -1
  90. package/dist/widgets/OptionSchemaEditor.js +4 -4
  91. package/dist/widgets/OptionValueFields.d.ts +1 -1
  92. package/dist/widgets/OptionValueFields.js +4 -4
  93. package/dist/widgets/OrgChart.d.ts +1 -1
  94. package/dist/widgets/OrgChart.js +4 -4
  95. package/dist/widgets/OrgChartBoard.d.ts +15 -33
  96. package/dist/widgets/OrgChartBoard.js +10 -8
  97. package/dist/widgets/PeriodFilter.d.ts +1 -1
  98. package/dist/widgets/PeriodFilter.js +6 -5
  99. package/dist/widgets/PermissionMatrixEditor.d.ts +34 -2
  100. package/dist/widgets/PermissionMatrixEditor.js +6 -4
  101. package/dist/widgets/PositionSelect.d.ts +1 -1
  102. package/dist/widgets/PositionSelect.js +5 -4
  103. package/dist/widgets/PrintSheet.d.ts +15 -2
  104. package/dist/widgets/PrintSheet.js +3 -3
  105. package/dist/widgets/ProfileEditDiffCard.d.ts +1 -1
  106. package/dist/widgets/ProfileEditDiffCard.js +5 -4
  107. package/dist/widgets/QRDisplay.d.ts +1 -1
  108. package/dist/widgets/QRDisplay.js +5 -4
  109. package/dist/widgets/RankBoard.d.ts +1 -1
  110. package/dist/widgets/RankBoard.js +8 -6
  111. package/dist/widgets/RecordSheet.d.ts +1 -1
  112. package/dist/widgets/RecordSheet.js +2 -2
  113. package/dist/widgets/RoleMemberPicker.d.ts +1 -1
  114. package/dist/widgets/RoleMemberPicker.js +6 -5
  115. package/dist/widgets/SearchableMultiPicker.d.ts +1 -1
  116. package/dist/widgets/SearchableMultiPicker.js +7 -6
  117. package/dist/widgets/SecureScreen.d.ts +1 -1
  118. package/dist/widgets/SecureScreen.js +3 -3
  119. package/dist/widgets/StackedMeter.d.ts +1 -1
  120. package/dist/widgets/StackedMeter.js +2 -2
  121. package/dist/widgets/StagePeriodEditor.d.ts +1 -1
  122. package/dist/widgets/StagePeriodEditor.js +6 -5
  123. package/dist/widgets/Strip.d.ts +1 -1
  124. package/dist/widgets/Strip.js +2 -2
  125. package/dist/widgets/SummaryTable.d.ts +1 -1
  126. package/dist/widgets/SummaryTable.js +2 -2
  127. package/dist/widgets/TagInput.d.ts +1 -1
  128. package/dist/widgets/TagInput.js +3 -3
  129. package/dist/widgets/TagMultiSelect.d.ts +1 -1
  130. package/dist/widgets/TagMultiSelect.js +5 -4
  131. package/dist/widgets/Timeline.d.ts +1 -1
  132. package/dist/widgets/Timeline.js +4 -4
  133. package/dist/widgets/TreePicker.d.ts +1 -1
  134. package/dist/widgets/TreePicker.js +8 -7
  135. package/dist/widgets/TreeView.d.ts +17 -3
  136. package/dist/widgets/TreeView.js +7 -6
  137. package/dist/widgets/index.d.ts +3 -3
  138. package/dist/widgets/index.js +84 -83
  139. package/package.json +1 -1
  140. package/dist/chunk-LFEJLOFN.js +0 -352
  141. package/dist/chunk-MMIGOPO5.js +0 -111
  142. package/dist/chunk-TFGNKFL2.js +0 -39
  143. package/dist/chunk-XQPTSEJC.js +0 -135
@@ -1,4 +1,4 @@
1
- import { mergeStyle, containerLayoutStyle, joinClass } from './chunk-5WW4MBRO.js';
1
+ import { mergeStyle, containerLayoutStyle, joinClass } from './chunk-3JTBOABA.js';
2
2
  import React from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
@@ -183,8 +183,135 @@ var CALC_OPS = /* @__PURE__ */ new Set([
183
183
  "join",
184
184
  "split",
185
185
  "sortBy",
186
- "map"
186
+ "map",
187
+ // 서식 (신규) — 원시 ISO 일시·금액을 Text 로 찍던 자리를 사람이 읽는 글자로
188
+ "formatDate",
189
+ "formatNumber",
190
+ // 지금 — 인쇄 양식의 "출력일"처럼 스펙이 오늘 날짜를 박아 두던 자리(고정값 2026-08-03 실측)
191
+ "today"
187
192
  ]);
193
+ var KST_OFFSET_MINUTES = 9 * 60;
194
+ var ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
195
+ var ISO_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:[.,]\d+)?)?(Z|[+-]\d{2}(?::?\d{2})?)?$/i;
196
+ var pad2 = (n) => String(n).padStart(2, "0");
197
+ function isRealDate(year, month, day) {
198
+ if (month < 1 || month > 12 || day < 1) return false;
199
+ const probe = new Date(Date.UTC(year, month - 1, day));
200
+ return probe.getUTCFullYear() === year && probe.getUTCMonth() === month - 1 && probe.getUTCDate() === day;
201
+ }
202
+ function offsetMinutes(zone) {
203
+ if (zone === "Z" || zone === "z") return 0;
204
+ const match = /^([+-])(\d{2}):?(\d{2})?$/.exec(zone);
205
+ if (!match) return null;
206
+ const hours = Number(match[2]);
207
+ const minutes = Number(match[3] ?? "0");
208
+ if (hours > 23 || minutes > 59) return null;
209
+ const total = hours * 60 + minutes;
210
+ return match[1] === "-" ? -total : total;
211
+ }
212
+ function kstClockOf(utcMs) {
213
+ const shifted = new Date(utcMs + KST_OFFSET_MINUTES * 6e4);
214
+ return {
215
+ year: shifted.getUTCFullYear(),
216
+ month: shifted.getUTCMonth() + 1,
217
+ day: shifted.getUTCDate(),
218
+ hour: shifted.getUTCHours(),
219
+ minute: shifted.getUTCMinutes(),
220
+ hasTime: true
221
+ };
222
+ }
223
+ function wallClockOf(text) {
224
+ const trimmed = text.trim();
225
+ const dateOnly = ISO_DATE_RE.exec(trimmed);
226
+ if (dateOnly) {
227
+ const [year2, month2, day2] = [Number(dateOnly[1]), Number(dateOnly[2]), Number(dateOnly[3])];
228
+ if (!isRealDate(year2, month2, day2)) return null;
229
+ return { year: year2, month: month2, day: day2, hour: 0, minute: 0, hasTime: false };
230
+ }
231
+ const match = ISO_DATETIME_RE.exec(trimmed);
232
+ if (!match) return null;
233
+ const year = Number(match[1]);
234
+ const month = Number(match[2]);
235
+ const day = Number(match[3]);
236
+ const hour = Number(match[4]);
237
+ const minute = Number(match[5]);
238
+ const second = match[6] === void 0 ? 0 : Number(match[6]);
239
+ if (!isRealDate(year, month, day) || hour > 23 || minute > 59 || second > 60) return null;
240
+ const zone = match[7];
241
+ if (zone === void 0) return { year, month, day, hour, minute, hasTime: true };
242
+ const offset = offsetMinutes(zone);
243
+ if (offset === null) return null;
244
+ return kstClockOf(Date.UTC(year, month - 1, day, hour, minute, second) - offset * 6e4);
245
+ }
246
+ function isZonedIsoDateTime(value) {
247
+ if (typeof value !== "string") return false;
248
+ const match = ISO_DATETIME_RE.exec(value.trim());
249
+ return match !== null && match[7] !== void 0 && wallClockOf(value) !== null;
250
+ }
251
+ function formatDateValue(value, format = "datetime") {
252
+ if (value === null || value === void 0 || value === "") return "";
253
+ let clock;
254
+ if (value instanceof Date) {
255
+ clock = Number.isNaN(value.getTime()) ? null : kstClockOf(value.getTime());
256
+ } else if (typeof value === "string") {
257
+ clock = wallClockOf(value);
258
+ } else {
259
+ return String(value);
260
+ }
261
+ if (!clock) return typeof value === "string" ? value : "";
262
+ const ymd = `${String(clock.year).padStart(4, "0")}-${pad2(clock.month)}-${pad2(clock.day)}`;
263
+ if (!clock.hasTime) return ymd;
264
+ const hm = `${pad2(clock.hour)}:${pad2(clock.minute)}`;
265
+ if (format === "date") return ymd;
266
+ if (format === "time") return hm;
267
+ return `${ymd} ${hm}`;
268
+ }
269
+ function parseNumeric(value) {
270
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
271
+ if (typeof value !== "string") return null;
272
+ const plain = value.replace(/,/g, "").trim();
273
+ if (!/^[+-]?(\d+(\.\d*)?|\.\d+)$/.test(plain)) return null;
274
+ const parsed = Number(plain);
275
+ return Number.isFinite(parsed) ? parsed : null;
276
+ }
277
+ function groupThousands(value) {
278
+ const text = String(value);
279
+ if (!/^-?\d+(\.\d+)?$/.test(text)) return text;
280
+ const negative = text.startsWith("-");
281
+ const [integer, fraction] = (negative ? text.slice(1) : text).split(".");
282
+ const grouped = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
283
+ return `${negative ? "-" : ""}${grouped}${fraction === void 0 ? "" : `.${fraction}`}`;
284
+ }
285
+ function formatNumberValue(value, digits) {
286
+ if (value === null || value === void 0 || value === "") return "";
287
+ const numeric = parseNumeric(value);
288
+ if (numeric === null) return typeof value === "string" ? value : String(value);
289
+ const wanted = digits === null || digits === void 0 || digits === "" ? NaN : Number(digits);
290
+ if (!Number.isFinite(wanted)) return groupThousands(numeric);
291
+ const fixed = Math.max(0, Math.min(6, Math.trunc(wanted)));
292
+ return new Intl.NumberFormat("ko-KR", {
293
+ minimumFractionDigits: fixed,
294
+ maximumFractionDigits: fixed
295
+ }).format(numeric);
296
+ }
297
+ var DISPLAY_FORMATS = /* @__PURE__ */ new Set(["date", "datetime", "time", "number", "currency"]);
298
+ function formatDisplayValue(value, format) {
299
+ if (typeof value !== "string" && typeof value !== "number") return value;
300
+ if (value === "") return value;
301
+ const chosen = typeof format === "string" && DISPLAY_FORMATS.has(format) ? format : void 0;
302
+ switch (chosen) {
303
+ case "date":
304
+ case "datetime":
305
+ case "time":
306
+ return typeof value === "string" ? formatDateValue(value, chosen) : value;
307
+ case "number":
308
+ return formatNumberValue(value);
309
+ case "currency":
310
+ return parseNumeric(value) === null ? value : `${formatNumberValue(value, 0)}\uC6D0`;
311
+ default:
312
+ return isZonedIsoDateTime(value) ? formatDateValue(value, "datetime") : value;
313
+ }
314
+ }
188
315
  function isFieldRef(value) {
189
316
  return isPlainObject(value) && "$field" in value;
190
317
  }
@@ -395,6 +522,17 @@ function evaluateCalc(calc, resolved, resolve) {
395
522
  return desc ? -order : order;
396
523
  });
397
524
  }
525
+ /* ── 서식 ── */
526
+ case "formatDate": {
527
+ const format = resolved[1];
528
+ return formatDateValue(first, format === "date" || format === "time" ? format : "datetime");
529
+ }
530
+ case "formatNumber":
531
+ return formatNumberValue(first, resolved[1]);
532
+ case "today": {
533
+ const format = first === "datetime" || first === "time" ? first : "date";
534
+ return formatDateValue(/* @__PURE__ */ new Date(), format);
535
+ }
398
536
  default:
399
537
  return null;
400
538
  }
@@ -1219,6 +1357,48 @@ function pickOptionText(option, key) {
1219
1357
  if (raw === null || raw === void 0) return void 0;
1220
1358
  return String(raw);
1221
1359
  }
1360
+ var SELECT_BOX_STYLE_KEYS = /* @__PURE__ */ new Set([
1361
+ "width",
1362
+ "minWidth",
1363
+ "maxWidth",
1364
+ "flex",
1365
+ "flexGrow",
1366
+ "flexShrink",
1367
+ "flexBasis",
1368
+ "alignSelf",
1369
+ "justifySelf",
1370
+ "order",
1371
+ "gridColumn",
1372
+ "gridColumnStart",
1373
+ "gridColumnEnd",
1374
+ "gridRow",
1375
+ "gridRowStart",
1376
+ "gridRowEnd",
1377
+ "gridArea",
1378
+ "margin",
1379
+ "marginTop",
1380
+ "marginBottom",
1381
+ "marginLeft",
1382
+ "marginRight",
1383
+ "marginInline",
1384
+ "marginInlineStart",
1385
+ "marginInlineEnd",
1386
+ "marginBlock",
1387
+ "marginBlockStart",
1388
+ "marginBlockEnd"
1389
+ ]);
1390
+ function splitSelectStyle(style) {
1391
+ if (!style) return {};
1392
+ const box = {};
1393
+ const control = {};
1394
+ for (const [key, value] of Object.entries(style)) {
1395
+ (SELECT_BOX_STYLE_KEYS.has(key) ? box : control)[key] = value;
1396
+ }
1397
+ return {
1398
+ box: Object.keys(box).length > 0 ? box : void 0,
1399
+ control: Object.keys(control).length > 0 ? control : void 0
1400
+ };
1401
+ }
1222
1402
  function Select({
1223
1403
  options,
1224
1404
  valueKey = "value",
@@ -1227,15 +1407,17 @@ function Select({
1227
1407
  invalid = false,
1228
1408
  placeholder,
1229
1409
  className = "",
1410
+ style,
1230
1411
  ...rest
1231
1412
  }) {
1413
+ const { box: boxStyle, control: controlStyle } = splitSelectStyle(style);
1232
1414
  const classes = ["wp-select"];
1233
1415
  if (size !== "md") classes.push(`wp-select--${size}`);
1234
1416
  if (invalid) classes.push("wp-select--invalid");
1235
1417
  if (className) classes.push(className);
1236
1418
  const list = Array.isArray(options) ? options : [];
1237
- return /* @__PURE__ */ jsxs("div", { className: "wp-selectwrap", children: [
1238
- /* @__PURE__ */ jsxs("select", { className: classes.join(" "), "aria-invalid": invalid || void 0, ...rest, children: [
1419
+ return /* @__PURE__ */ jsxs("div", { className: "wp-selectwrap", style: boxStyle, children: [
1420
+ /* @__PURE__ */ jsxs("select", { className: classes.join(" "), "aria-invalid": invalid || void 0, style: controlStyle, ...rest, children: [
1239
1421
  placeholder ? /* @__PURE__ */ jsx("option", { value: "", children: placeholder }) : null,
1240
1422
  list.map((option, index) => {
1241
1423
  const value = pickOptionText(option, valueKey);
@@ -1262,12 +1444,12 @@ function Select({
1262
1444
  /* @__PURE__ */ jsx("span", { className: "wp-selectwrap__caret", children: /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: 15 }) })
1263
1445
  ] });
1264
1446
  }
1265
- function Checkbox({ label, description, indeterminate = false, disabled = false, className = "", ...rest }) {
1447
+ function Checkbox({ label, description, indeterminate = false, disabled = false, className = "", style, ...rest }) {
1266
1448
  const ref = React.useRef(null);
1267
1449
  React.useEffect(() => {
1268
1450
  if (ref.current) ref.current.indeterminate = indeterminate;
1269
1451
  }, [indeterminate]);
1270
- return /* @__PURE__ */ jsxs("label", { className: joinClass("wp-check", disabled && "wp-check--disabled", className), children: [
1452
+ return /* @__PURE__ */ jsxs("label", { className: joinClass("wp-check", disabled && "wp-check--disabled", className), style, children: [
1271
1453
  /* @__PURE__ */ jsx("input", { ref, type: "checkbox", disabled, ...rest }),
1272
1454
  /* @__PURE__ */ jsx("span", { className: "wp-check__box", children: /* @__PURE__ */ jsx(Icon, { name: indeterminate ? "minus" : "check", size: 12, strokeWidth: 2.5 }) }),
1273
1455
  label || description ? /* @__PURE__ */ jsxs("span", { className: "wp-check__text", children: [
@@ -1276,8 +1458,8 @@ function Checkbox({ label, description, indeterminate = false, disabled = false,
1276
1458
  ] }) : null
1277
1459
  ] });
1278
1460
  }
1279
- function Switch({ label, size = "md", disabled = false, className = "", ...rest }) {
1280
- return /* @__PURE__ */ jsxs("label", { className: joinClass("wp-switch", size === "sm" && "wp-switch--sm", className), children: [
1461
+ function Switch({ label, size = "md", disabled = false, className = "", style, ...rest }) {
1462
+ return /* @__PURE__ */ jsxs("label", { className: joinClass("wp-switch", size === "sm" && "wp-switch--sm", className), style, children: [
1281
1463
  /* @__PURE__ */ jsx("input", { type: "checkbox", role: "switch", disabled, ...rest }),
1282
1464
  /* @__PURE__ */ jsx("span", { className: "wp-switch__track" }),
1283
1465
  label ? /* @__PURE__ */ jsx("span", { children: label }) : null
@@ -1318,24 +1500,60 @@ function Card({
1318
1500
  if (selected) classes.push("wp-card--selected");
1319
1501
  if (className) classes.push(className);
1320
1502
  const bodyClass = padding === "tight" ? "wp-card__body wp-card__body--tight" : "wp-card__body";
1321
- return /* @__PURE__ */ jsxs("div", { className: classes.join(" "), ...rest, children: [
1322
- cover ? /* @__PURE__ */ jsx("div", { className: "wp-card__cover", children: cover }) : null,
1323
- title || actions ? /* @__PURE__ */ jsxs("div", { className: "wp-card__head", children: [
1324
- /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
1325
- /* @__PURE__ */ jsx("div", { className: "wp-card__title", children: title }),
1326
- subtitle ? /* @__PURE__ */ jsx("div", { className: "wp-card__sub", children: subtitle }) : null
1327
- ] }),
1328
- actions ? /* @__PURE__ */ jsx("div", { className: "wp-row", style: { gap: 6, flex: "none" }, children: actions }) : null
1329
- ] }) : null,
1330
- padding === "none" ? children : /* @__PURE__ */ jsx("div", { className: bodyClass, children }),
1331
- footer ? /* @__PURE__ */ jsx("div", { className: "wp-card__foot", children: footer }) : null
1332
- ] });
1503
+ const { onClick, onKeyDown, role, tabIndex, ...divRest } = rest;
1504
+ const pressable = interactive && typeof onClick === "function";
1505
+ const handleKeyDown = pressable ? (event) => {
1506
+ onKeyDown?.(event);
1507
+ if (event.defaultPrevented || event.target !== event.currentTarget) return;
1508
+ if (event.key !== "Enter" && event.key !== " ") return;
1509
+ event.preventDefault();
1510
+ if (event.repeat) return;
1511
+ event.currentTarget.click();
1512
+ } : onKeyDown;
1513
+ return /* @__PURE__ */ jsxs(
1514
+ "div",
1515
+ {
1516
+ className: classes.join(" "),
1517
+ ...divRest,
1518
+ onClick,
1519
+ onKeyDown: handleKeyDown,
1520
+ role: role ?? (pressable ? "button" : void 0),
1521
+ tabIndex: tabIndex ?? (pressable ? 0 : void 0),
1522
+ children: [
1523
+ cover ? /* @__PURE__ */ jsx("div", { className: "wp-card__cover", children: cover }) : null,
1524
+ title || actions ? /* @__PURE__ */ jsxs("div", { className: "wp-card__head", children: [
1525
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
1526
+ /* @__PURE__ */ jsx("div", { className: "wp-card__title", children: title }),
1527
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "wp-card__sub", children: subtitle }) : null
1528
+ ] }),
1529
+ actions ? /* @__PURE__ */ jsx("div", { className: "wp-row", style: { gap: 6, flex: "none" }, children: actions }) : null
1530
+ ] }) : null,
1531
+ padding === "none" ? children : /* @__PURE__ */ jsx("div", { className: bodyClass, children }),
1532
+ footer ? /* @__PURE__ */ jsx("div", { className: "wp-card__foot", children: footer }) : null
1533
+ ]
1534
+ }
1535
+ );
1333
1536
  }
1334
- function Badge({ tone = "neutral", dot = false, size = "md", children, className = "", ...rest }) {
1537
+ function Badge({
1538
+ tone = "neutral",
1539
+ dot = false,
1540
+ size = "md",
1541
+ wrap = false,
1542
+ children,
1543
+ className = "",
1544
+ ...rest
1545
+ }) {
1335
1546
  return /* @__PURE__ */ jsx(
1336
1547
  "span",
1337
1548
  {
1338
- className: joinClass("wp-badge", `wp-badge--${tone}`, dot && "wp-badge--dot", size === "lg" && "wp-badge--lg", className),
1549
+ className: joinClass(
1550
+ "wp-badge",
1551
+ `wp-badge--${tone}`,
1552
+ dot && "wp-badge--dot",
1553
+ size === "lg" && "wp-badge--lg",
1554
+ wrap && "wp-badge--wrap",
1555
+ className
1556
+ ),
1339
1557
  ...rest,
1340
1558
  children
1341
1559
  }
@@ -1385,14 +1603,33 @@ function Toast({ tone = "info", title, children, action, onClose, className = ""
1385
1603
  onClose ? /* @__PURE__ */ jsx("button", { type: "button", className: "wp-toast__x", "aria-label": "\uB2EB\uAE30", onClick: onClose, children: /* @__PURE__ */ jsx(Icon, { name: "x", size: 13 }) }) : null
1386
1604
  ] });
1387
1605
  }
1606
+ function revealSelectedTab(list) {
1607
+ if (!list || list.scrollWidth <= list.clientWidth + 1) return;
1608
+ const tab = list.querySelector('[role="tab"][aria-selected="true"]');
1609
+ if (!tab) return;
1610
+ const listBox = list.getBoundingClientRect();
1611
+ const tabBox = tab.getBoundingClientRect();
1612
+ const margin = 24;
1613
+ if (tabBox.left < listBox.left) {
1614
+ list.scrollLeft -= listBox.left - tabBox.left + margin;
1615
+ } else if (tabBox.right > listBox.right) {
1616
+ list.scrollLeft += tabBox.right - listBox.right + margin;
1617
+ }
1618
+ }
1388
1619
  function Tabs({ items = [], value, onChange, size = "md", onChrome = false, className = "", ...rest }) {
1620
+ const listRef = React.useRef(null);
1621
+ const itemList = asArray(items);
1622
+ React.useEffect(() => {
1623
+ revealSelectedTab(listRef.current);
1624
+ }, [value, itemList.length]);
1389
1625
  return /* @__PURE__ */ jsx(
1390
1626
  "div",
1391
1627
  {
1392
1628
  className: joinClass("wp-tabs", size === "lg" && "wp-tabs--lg", onChrome && "wp-tabs--onchrome", className),
1393
1629
  role: "tablist",
1394
1630
  ...rest,
1395
- children: asArray(items).map((item) => /* @__PURE__ */ jsxs(
1631
+ ref: listRef,
1632
+ children: itemList.map((item) => /* @__PURE__ */ jsxs(
1396
1633
  "button",
1397
1634
  {
1398
1635
  type: "button",
@@ -1423,19 +1660,25 @@ function Table({
1423
1660
  rowKey = "id",
1424
1661
  onRowClick,
1425
1662
  empty,
1663
+ emptyText,
1664
+ stackable: stackableProp,
1426
1665
  className = "",
1666
+ style,
1427
1667
  ...rest
1428
1668
  }) {
1669
+ const stackable = stackableProp ?? !sticky;
1670
+ const wrapped = !sticky;
1429
1671
  const classes = ["wp-table"];
1430
1672
  if (dense) classes.push("wp-table--dense");
1431
1673
  if (hover) classes.push("wp-table--hover");
1432
1674
  if (sticky) classes.push("wp-table--sticky");
1433
- if (className) classes.push(className);
1675
+ if (stackable) classes.push("wp-table--stackable");
1676
+ if (!wrapped && className) classes.push(className);
1434
1677
  const columnList = asArray(columns);
1435
1678
  const rowList = asArray(rows);
1436
1679
  const selectedList = asArray(selectedIds);
1437
1680
  if (!rowList.length && empty) return /* @__PURE__ */ jsx(Fragment, { children: empty });
1438
- return /* @__PURE__ */ jsxs("table", { className: classes.join(" "), ...rest, children: [
1681
+ const table = /* @__PURE__ */ jsxs("table", { className: classes.join(" "), style: wrapped ? void 0 : style, ...rest, children: [
1439
1682
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columnList.map((column) => /* @__PURE__ */ jsx("th", { style: { width: column.width, textAlign: column.align === "right" ? "right" : void 0 }, children: column.sortable && onSort ? /* @__PURE__ */ jsxs("button", { type: "button", className: "wp-table__sort", onClick: () => onSort(column.key), children: [
1440
1683
  column.header,
1441
1684
  /* @__PURE__ */ jsx(
@@ -1446,34 +1689,53 @@ function Table({
1446
1689
  }
1447
1690
  )
1448
1691
  ] }) : column.header }, column.key)) }) }),
1449
- /* @__PURE__ */ jsx("tbody", { children: rowList.map((row, index) => /* @__PURE__ */ jsx(
1450
- "tr",
1451
- {
1452
- "aria-selected": selectedList.indexOf(row?.[rowKey]) > -1 || void 0,
1453
- onClick: onRowClick ? () => onRowClick(row) : void 0,
1454
- style: onRowClick ? { cursor: "pointer" } : void 0,
1455
- children: columnList.map((column) => {
1456
- const cls = [];
1457
- if (column.align === "right") cls.push("wp-table__num");
1458
- if (column.mono) cls.push("wp-table__mono");
1459
- if (column.strong) cls.push("wp-table__strong");
1460
- const raw = column.key ? fieldOf(row, column.key) : void 0;
1461
- let content;
1462
- if (column.render) {
1463
- content = column.render(row, index);
1464
- } else if (column.badge) {
1465
- const key = raw == null ? "" : String(raw);
1466
- const tone = column.badge.toneMap?.[key] ?? "neutral";
1467
- content = /* @__PURE__ */ jsx(Badge, { tone, children: column.badge.labelMap?.[key] ?? key });
1468
- } else {
1469
- content = raw;
1470
- }
1471
- return /* @__PURE__ */ jsx("td", { className: cls.join(" ") || void 0, children: content }, column.key);
1472
- })
1473
- },
1474
- row?.[rowKey] != null ? String(row[rowKey]) : index
1475
- )) })
1692
+ /* @__PURE__ */ jsxs("tbody", { children: [
1693
+ rowList.length === 0 ? (
1694
+ // 머리글은 남겨 "무슨 표인지"는 보이게 하고, 비었다는 사실은 한 줄로 말한다.
1695
+ // data-label="" — 행 카드 모드에서 라벨 없이 문구만 서게(선택 칸과 같은 규칙).
1696
+ /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "wp-table__empty", colSpan: Math.max(1, columnList.length), "data-label": "", children: emptyText || "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4" }) })
1697
+ ) : null,
1698
+ rowList.map((row, index) => /* @__PURE__ */ jsx(
1699
+ "tr",
1700
+ {
1701
+ "aria-selected": selectedList.indexOf(row?.[rowKey]) > -1 || void 0,
1702
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
1703
+ style: onRowClick ? { cursor: "pointer" } : void 0,
1704
+ children: columnList.map((column) => {
1705
+ const cls = [];
1706
+ if (column.align === "right") cls.push("wp-table__num");
1707
+ if (column.mono) cls.push("wp-table__mono");
1708
+ if (column.strong) cls.push("wp-table__strong");
1709
+ const raw = column.key ? fieldOf(row, column.key) : void 0;
1710
+ let content;
1711
+ if (column.render) {
1712
+ content = column.render(row, index);
1713
+ } else if (column.badge) {
1714
+ const key = raw == null ? "" : String(raw);
1715
+ const tone = column.badge.toneMap?.[key] ?? "neutral";
1716
+ content = /* @__PURE__ */ jsx(Badge, { tone, children: column.badge.labelMap?.[key] ?? key });
1717
+ } else {
1718
+ content = formatDisplayValue(raw, column.format);
1719
+ }
1720
+ return /* @__PURE__ */ jsx(
1721
+ "td",
1722
+ {
1723
+ className: cls.join(" ") || void 0,
1724
+ "data-label": stackable ? typeof column.header === "string" ? column.header : "" : void 0,
1725
+ children: content
1726
+ },
1727
+ column.key
1728
+ );
1729
+ })
1730
+ },
1731
+ row?.[rowKey] != null ? String(row[rowKey]) : index
1732
+ ))
1733
+ ] })
1476
1734
  ] });
1735
+ if (!wrapped) return table;
1736
+ const boxClasses = ["wp-table-scroll", "wp-table-scroll--plain"];
1737
+ if (className) boxClasses.push(className);
1738
+ return /* @__PURE__ */ jsx("div", { className: boxClasses.join(" "), style, children: table });
1477
1739
  }
1478
1740
  function StatCard({ label, value, unit, delta, trend = "flat", icon, footer, size = "md", className = "", ...rest }) {
1479
1741
  const arrow = trend === "up" ? "arrow-up-right" : trend === "down" ? "arrow-down" : "minus";
@@ -1483,7 +1745,7 @@ function StatCard({ label, value, unit, delta, trend = "flat", icon, footer, siz
1483
1745
  label
1484
1746
  ] }),
1485
1747
  /* @__PURE__ */ jsxs("div", { className: "wp-stat__value", children: [
1486
- value,
1748
+ typeof value === "number" ? formatNumberValue(value) : value,
1487
1749
  unit ? /* @__PURE__ */ jsx("span", { className: "wp-stat__unit", children: unit }) : null
1488
1750
  ] }),
1489
1751
  delta ? /* @__PURE__ */ jsxs("div", { className: `wp-stat__delta wp-stat__delta--${trend}`, children: [
@@ -1501,18 +1763,20 @@ function EmptyState({ icon = "square-dashed", title, children, actions, tight =
1501
1763
  actions ? /* @__PURE__ */ jsx("div", { className: "wp-empty__actions", children: actions }) : null
1502
1764
  ] });
1503
1765
  }
1504
- function Spinner({ size = "md", inverse = false, label, className = "", ...rest }) {
1766
+ function Spinner({ size = "md", inverse = false, label, className = "", style, ...rest }) {
1767
+ const labeled = Boolean(label);
1505
1768
  const el = /* @__PURE__ */ jsx(
1506
1769
  "span",
1507
1770
  {
1508
- className: joinClass("wp-spinner", size !== "md" && `wp-spinner--${size}`, inverse && "wp-spinner--inverse", className),
1771
+ className: joinClass("wp-spinner", size !== "md" && `wp-spinner--${size}`, inverse && "wp-spinner--inverse", !labeled && className),
1772
+ style: labeled ? void 0 : style,
1509
1773
  role: "status",
1510
1774
  "aria-label": label || "\uB85C\uB529 \uC911",
1511
1775
  ...rest
1512
1776
  }
1513
1777
  );
1514
- if (!label) return el;
1515
- return /* @__PURE__ */ jsxs("span", { className: "wp-row", style: { gap: 8 }, children: [
1778
+ if (!labeled) return el;
1779
+ return /* @__PURE__ */ jsxs("span", { className: joinClass("wp-row", className), style: { gap: 8, ...style }, children: [
1516
1780
  el,
1517
1781
  /* @__PURE__ */ jsx("span", { className: "wp-caption", children: label })
1518
1782
  ] });
@@ -1583,7 +1847,7 @@ function Pagination({ page = 1, pageCount = 1, total, pageSize, onChange, classN
1583
1847
  const from = total != null && pageSize ? (page - 1) * pageSize + 1 : null;
1584
1848
  const to = total != null && pageSize ? Math.min(total, page * pageSize) : null;
1585
1849
  return /* @__PURE__ */ jsxs("div", { className: joinClass("wp-pager", className), ...rest, children: [
1586
- from != null ? /* @__PURE__ */ jsxs("span", { className: "wp-num", children: [
1850
+ from != null && total === 0 ? /* @__PURE__ */ jsx("span", { className: "wp-num", children: "0\uAC74" }) : from != null ? /* @__PURE__ */ jsxs("span", { className: "wp-num", children: [
1587
1851
  from,
1588
1852
  "\u2013",
1589
1853
  to,
@@ -1730,4 +1994,4 @@ function Tooltip({ label, shortcut, placement = "top", wrap = false, children, c
1730
1994
  );
1731
1995
  }
1732
1996
 
1733
- export { Alert, Avatar, Badge, Breadcrumb, Button, CALC_OPS, Card, Checkbox, Dialog, EmptyState, Field, ICON_NODES, Icon, IconButton, Input, Pagination, ProgressBar, Radio, SegmentedControl, Select, SidebarNav, Spinner, StatCard, Switch, Table, Tabs, Tag, Textarea, Toast, Tooltip, evaluateCalc, fieldOf, isPlainObject, toNumber, warnOnce };
1997
+ export { Alert, Avatar, Badge, Breadcrumb, Button, CALC_OPS, Card, Checkbox, Dialog, EmptyState, Field, ICON_NODES, Icon, IconButton, Input, Pagination, ProgressBar, Radio, SegmentedControl, Select, SidebarNav, Spinner, StatCard, Switch, Table, Tabs, Tag, Textarea, Toast, Tooltip, evaluateCalc, fieldOf, formatDateValue, formatDisplayValue, formatNumberValue, isPlainObject, isZonedIsoDateTime, parseNumeric, toNumber, warnOnce };