@wipco/sdui 0.50.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/{DatePicker-BK_jsV7i.d.ts → DatePicker-C_TT_SOw.d.ts} +25 -0
  2. package/dist/{chunk-OEWTVLPZ.js → chunk-2HLXCKK7.js} +2 -2
  3. package/dist/{chunk-PWVHAUJC.js → chunk-2YP5N4VV.js} +6 -4
  4. package/dist/{chunk-QTHYLNJW.js → chunk-37SFFW37.js} +2 -13
  5. package/dist/{chunk-YQOMSWW5.js → chunk-5WW4MBRO.js} +1 -1
  6. package/dist/{chunk-LIOS3PF2.js → chunk-5XPZ3RSA.js} +3 -4
  7. package/dist/{chunk-ZD3VNVRF.js → chunk-7BGDSSAN.js} +5 -13
  8. package/dist/{chunk-Y2LIEWQX.js → chunk-7FZTUI6U.js} +6 -5
  9. package/dist/{chunk-HJ5VSXR4.js → chunk-7P7N43JV.js} +2 -2
  10. package/dist/{chunk-55VB6SDU.js → chunk-A5XQOEVH.js} +23 -20
  11. package/dist/{chunk-77LKJCMM.js → chunk-A6LAIGUS.js} +20 -108
  12. package/dist/{chunk-YXS5FXVS.js → chunk-AC5TI3BE.js} +7 -16
  13. package/dist/{chunk-AIOJYVJ2.js → chunk-AXGF4M57.js} +1 -1
  14. package/dist/{chunk-5MPK3NZK.js → chunk-B6WMC2AN.js} +3 -3
  15. package/dist/{chunk-C6ZV4VBN.js → chunk-C37U3GCC.js} +4 -4
  16. package/dist/{chunk-265YPW4W.js → chunk-DMLDKW7K.js} +1 -1
  17. package/dist/{chunk-FZ72IDVF.js → chunk-E6ISNQK3.js} +2 -2
  18. package/dist/{chunk-STX2AX6Y.js → chunk-FLAN7CV3.js} +7 -39
  19. package/dist/{chunk-IV5YTVHR.js → chunk-GE6BYNQJ.js} +18 -33
  20. package/dist/{chunk-ZX36LKJ7.js → chunk-GTGS2QRL.js} +7 -6
  21. package/dist/{chunk-NL3I6F2I.js → chunk-HBBTENSV.js} +3 -3
  22. package/dist/{chunk-RUU7WBGR.js → chunk-I45EUZC6.js} +19 -9
  23. package/dist/{chunk-JBKNIALK.js → chunk-I5SD7XRA.js} +9 -6
  24. package/dist/{chunk-X7GHY2RI.js → chunk-IFKYGJXP.js} +14 -42
  25. package/dist/{chunk-J3YMUR6R.js → chunk-IZ3JA2BR.js} +3 -3
  26. package/dist/{chunk-AUW5RP24.js → chunk-JIV4YHCZ.js} +7 -6
  27. package/dist/{chunk-M6KM3FJS.js → chunk-JYAR7D5U.js} +2 -2
  28. package/dist/{chunk-DKPNFEVU.js → chunk-KO6OEY3G.js} +19 -6
  29. package/dist/{chunk-JHHMFL5D.js → chunk-LFEJLOFN.js} +28 -16
  30. package/dist/chunk-MMIGOPO5.js +111 -0
  31. package/dist/{chunk-YSSNP6JM.js → chunk-N25YP7ID.js} +6 -21
  32. package/dist/{chunk-F2W2FWOT.js → chunk-N5RKM6IC.js} +7 -7
  33. package/dist/{chunk-C4HYWZML.js → chunk-O4IISNE7.js} +1 -1
  34. package/dist/chunk-Q4SWUINA.js +51 -0
  35. package/dist/{chunk-TUOG7EEI.js → chunk-RNOUXUBO.js} +418 -22
  36. package/dist/{chunk-4VZI3PLI.js → chunk-RYHEV5E3.js} +15 -39
  37. package/dist/{chunk-AVRNQOB5.js → chunk-SAPBDJJQ.js} +14 -13
  38. package/dist/{chunk-OPJ4DYNI.js → chunk-SJ3737RA.js} +156 -158
  39. package/dist/{chunk-UMANGYRD.js → chunk-TFGNKFL2.js} +1 -1
  40. package/dist/{chunk-KYGSMUUU.js → chunk-VY7MVBSE.js} +2 -2
  41. package/dist/{chunk-VDBUXYEB.js → chunk-WOYMEEPH.js} +8 -8
  42. package/dist/{chunk-I5Z2RVDF.js → chunk-WSLT32Q4.js} +278 -30
  43. package/dist/{chunk-NYUHSMUU.js → chunk-XGFDY43I.js} +2 -2
  44. package/dist/{chunk-ODKZF4IT.js → chunk-YOZN35HD.js} +17 -9
  45. package/dist/{chunk-EKP6Z3G6.js → chunk-YVTV4ZV6.js} +1 -1
  46. package/dist/{chunk-6BCWFPNJ.js → chunk-YWUDBLO6.js} +2 -2
  47. package/dist/chunk-Z4PYMTTS.js +41 -0
  48. package/dist/{chunk-WXIQLWIX.js → chunk-ZFNFPXPR.js} +2 -2
  49. package/dist/{chunk-7GOWOSX6.js → chunk-ZORQPB6B.js} +11 -5
  50. package/dist/index.d.ts +1118 -943
  51. package/dist/index.js +369 -610
  52. package/dist/meta.d.ts +13 -12
  53. package/dist/meta.js +84 -16
  54. package/dist/rich/ExcelUploadCard.d.ts +7 -0
  55. package/dist/rich/ExcelUploadCard.js +17 -13
  56. package/dist/rich.d.ts +1 -1
  57. package/dist/rich.js +25 -12
  58. package/dist/styles.css +10 -2
  59. package/dist/{types-D14eqLpf.d.ts → types-C7y9CpgF.d.ts} +7 -5
  60. package/dist/widgets/ApprovalLineEditor.js +7 -6
  61. package/dist/widgets/ApprovalOverridesEditor.d.ts +11 -1
  62. package/dist/widgets/ApprovalOverridesEditor.js +9 -8
  63. package/dist/widgets/ApprovalStepper.js +3 -3
  64. package/dist/widgets/AutocompleteField.d.ts +1 -0
  65. package/dist/widgets/AutocompleteField.js +7 -6
  66. package/dist/widgets/ColumnBoard.js +3 -3
  67. package/dist/widgets/DiffView.js +3 -3
  68. package/dist/widgets/Disclosure.js +3 -3
  69. package/dist/widgets/EventCalendar.d.ts +16 -1
  70. package/dist/widgets/EventCalendar.js +3 -3
  71. package/dist/widgets/FileDropzone.js +7 -6
  72. package/dist/widgets/ImageLightbox.js +5 -4
  73. package/dist/widgets/InlineEdit.js +3 -3
  74. package/dist/widgets/JsonField.js +3 -3
  75. package/dist/widgets/LiveClock.js +2 -2
  76. package/dist/widgets/OptionSchemaEditor.js +4 -4
  77. package/dist/widgets/OptionValueFields.d.ts +5 -0
  78. package/dist/widgets/OptionValueFields.js +4 -4
  79. package/dist/widgets/OrgChart.js +4 -3
  80. package/dist/widgets/OrgChartBoard.d.ts +93 -4
  81. package/dist/widgets/OrgChartBoard.js +9 -6
  82. package/dist/widgets/PeriodFilter.js +4 -4
  83. package/dist/widgets/PermissionMatrixEditor.d.ts +16 -0
  84. package/dist/widgets/PermissionMatrixEditor.js +4 -4
  85. package/dist/widgets/PositionSelect.d.ts +6 -1
  86. package/dist/widgets/PositionSelect.js +5 -4
  87. package/dist/widgets/PrintSheet.js +3 -3
  88. package/dist/widgets/ProfileEditDiffCard.d.ts +6 -0
  89. package/dist/widgets/ProfileEditDiffCard.js +4 -4
  90. package/dist/widgets/QRDisplay.js +4 -4
  91. package/dist/widgets/RankBoard.d.ts +6 -0
  92. package/dist/widgets/RankBoard.js +6 -5
  93. package/dist/widgets/RecordSheet.js +2 -2
  94. package/dist/widgets/RoleMemberPicker.d.ts +11 -5
  95. package/dist/widgets/RoleMemberPicker.js +5 -4
  96. package/dist/widgets/SearchableMultiPicker.js +5 -5
  97. package/dist/widgets/SecureScreen.d.ts +12 -0
  98. package/dist/widgets/SecureScreen.js +3 -3
  99. package/dist/widgets/StackedMeter.js +2 -2
  100. package/dist/widgets/StagePeriodEditor.js +4 -5
  101. package/dist/widgets/Strip.js +2 -2
  102. package/dist/widgets/SummaryTable.js +2 -2
  103. package/dist/widgets/TagMultiSelect.d.ts +0 -1
  104. package/dist/widgets/TagMultiSelect.js +4 -4
  105. package/dist/widgets/Timeline.js +4 -4
  106. package/dist/widgets/TreePicker.js +6 -6
  107. package/dist/widgets/TreeView.js +5 -5
  108. package/dist/widgets/index.d.ts +3 -3
  109. package/dist/widgets/index.js +81 -79
  110. package/package.json +1 -1
  111. package/dist/chunk-EKVA7T7P.js +0 -198
@@ -15,10 +15,31 @@ interface TimePickerProps {
15
15
  invalid?: boolean;
16
16
  disabled?: boolean;
17
17
  size?: 'sm' | 'md' | 'lg';
18
+ /**
19
+ * 목록(listbox)의 낭독 라벨. 값 계약(HH:MM)은 로캘과 무관하고 이 문구만 한국어라,
20
+ * 한국어권 밖 소비 프로젝트가 여기 하나만 갈아 끼우면 된다.
21
+ */
22
+ listLabel?: string;
18
23
  }
19
24
  /** 바깥에서 온 `style`·`className`을 뿌리에 얹어 내보낸다. 까닭은 {@link ./boxed.tsx}에 있다. */
20
25
  declare const TimePicker: React__default.FC<TimePickerProps & SduiBoxProps>;
21
26
 
27
+ interface DatePickerLocale {
28
+ /** 요일 머리글 — **일요일부터** 7개. 주 시작이 달라도 이 순서로 주면 내부에서 돌린다. */
29
+ weekdays?: readonly string[];
30
+ /** 주 시작 요일 (0=일 … 6=토). */
31
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
32
+ /** 달력 위 연월 캡션 (month는 1–12). */
33
+ formatCaption?: (year: number, month: number) => string;
34
+ /** 이전/다음 달 버튼의 낭독 라벨 */
35
+ prevMonthLabel?: string;
36
+ nextMonthLabel?: string;
37
+ /** 달력 패널의 낭독 라벨 */
38
+ calendarLabel?: string;
39
+ /** 기간 달력에서 시작·종료 칸의 낭독 라벨 (DateRangePicker) */
40
+ startLabel?: string;
41
+ endLabel?: string;
42
+ }
22
43
  interface DatePickerProps {
23
44
  /** 선택된 날짜 (YYYY-MM-DD, `withTime`이면 YYYY-MM-DDTHH:mm). 주어지면 controlled로 동작. */
24
45
  value?: string;
@@ -36,6 +57,8 @@ interface DatePickerProps {
36
57
  * 같은 모양이라 서버로 가는 문자열은 그대로다). 칸에는 `2026-08-18 14:30`으로 보인다.
37
58
  */
38
59
  withTime?: boolean;
60
+ /** 표시 로캘(요일·캡션·낭독 라벨·주 시작 요일). 기본 한국어·일요일 시작. 값 계약은 불변. */
61
+ locale?: DatePickerLocale;
39
62
  }
40
63
  interface DateRangeValue {
41
64
  from?: string;
@@ -57,6 +80,8 @@ interface DateRangePickerProps {
57
80
  invalid?: boolean;
58
81
  disabled?: boolean;
59
82
  size?: 'sm' | 'md' | 'lg';
83
+ /** 표시 로캘(요일·캡션·낭독 라벨·주 시작 요일). 기본 한국어·일요일 시작. 값 계약은 불변. */
84
+ locale?: DatePickerLocale;
60
85
  }
61
86
  /** 바깥에서 온 `style`·`className`을 뿌리에 얹어 내보낸다. 까닭은 {@link ./boxed.tsx}에 있다. */
62
87
  declare const DatePicker: React__default.FC<DatePickerProps & SduiBoxProps>;
@@ -1,5 +1,5 @@
1
- import { Icon } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Icon } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import { useState, useId, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,4 +1,4 @@
1
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { boxed } from './chunk-5WW4MBRO.js';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
4
  var SUMMARY_TABLE_CSS = `
@@ -18,11 +18,13 @@ function cellClass(column) {
18
18
  return classes.length > 0 ? classes.join(" ") : void 0;
19
19
  }
20
20
  function SummaryTableImpl({ columns, rows, footer, dense = false }) {
21
+ const safeColumns = Array.isArray(columns) ? columns : [];
22
+ const safeRows = Array.isArray(rows) ? rows : [];
21
23
  const tableClass = dense ? "wp-table wp-table--dense erp-sumtable" : "wp-table erp-sumtable";
22
24
  return /* @__PURE__ */ jsxs("div", { className: "erp-sumtable__scroll", children: [
23
25
  /* @__PURE__ */ jsx(SummaryTableStyles, {}),
24
26
  /* @__PURE__ */ jsxs("table", { className: tableClass, children: [
25
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx(
27
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: safeColumns.map((column) => /* @__PURE__ */ jsx(
26
28
  "th",
27
29
  {
28
30
  style: column.align === "right" ? { textAlign: "right" } : void 0,
@@ -30,8 +32,8 @@ function SummaryTableImpl({ columns, rows, footer, dense = false }) {
30
32
  },
31
33
  column.key
32
34
  )) }) }),
33
- /* @__PURE__ */ jsx("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "erp-sumtable__empty", colSpan: Math.max(columns.length, 1), children: "\uD45C\uC2DC\uD560 \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) }) : rows.map((row, rowIndex) => /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx("td", { className: cellClass(column), children: row[column.key] }, column.key)) }, rowIndex)) }),
34
- footer !== void 0 ? /* @__PURE__ */ jsx("tfoot", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx("td", { className: cellClass(column), children: footer[column.key] }, column.key)) }) }) : null
35
+ /* @__PURE__ */ jsx("tbody", { children: safeRows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "erp-sumtable__empty", colSpan: Math.max(safeColumns.length, 1), children: "\uD45C\uC2DC\uD560 \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) }) : safeRows.map((row, rowIndex) => /* @__PURE__ */ jsx("tr", { children: safeColumns.map((column) => /* @__PURE__ */ jsx("td", { className: cellClass(column), children: row[column.key] }, column.key)) }, rowIndex)) }),
36
+ footer !== void 0 ? /* @__PURE__ */ jsx("tfoot", { children: /* @__PURE__ */ jsx("tr", { children: safeColumns.map((column) => /* @__PURE__ */ jsx("td", { className: cellClass(column), children: footer[column.key] }, column.key)) }) }) : null
35
37
  ] })
36
38
  ] });
37
39
  }
@@ -1,22 +1,11 @@
1
- import { mergeStyle, containerLayoutStyle, joinClass, columnsClass, typographyStyle } from './chunk-YQOMSWW5.js';
1
+ import { mergeStyle, containerLayoutStyle, joinClass, columnsClass, typographyStyle, GAP_CSS } from './chunk-5WW4MBRO.js';
2
2
  import React from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
5
5
  function paddingStyle(padding) {
6
- const value = padding === void 0 ? void 0 : PADDING_CSS[padding];
6
+ const value = padding === void 0 ? void 0 : GAP_CSS[padding];
7
7
  return value === void 0 ? {} : { padding: value };
8
8
  }
9
- var PADDING_CSS = {
10
- "0": "0px",
11
- "2": "2px",
12
- "4": "4px",
13
- "8": "8px",
14
- "12": "12px",
15
- "16": "16px",
16
- "20": "20px",
17
- "24": "24px",
18
- "32": "32px"
19
- };
20
9
  function Grid({
21
10
  columns = 3,
22
11
  template,
@@ -171,4 +171,4 @@ function boxed(Inner, displayName) {
171
171
  return Wrapped;
172
172
  }
173
173
 
174
- export { CHILD_LAYOUT_PROPS, LAYOUT_CSS, boxed, childLayoutFrom, columnsClass, containerLayoutStyle, joinClass, mergeStyle, typographyStyle };
174
+ export { CHILD_LAYOUT_PROPS, GAP_CSS, LAYOUT_CSS, boxed, childLayoutFrom, columnsClass, containerLayoutStyle, joinClass, mergeStyle, typographyStyle };
@@ -1,7 +1,6 @@
1
- import { TimePicker } from './chunk-EKVA7T7P.js';
2
- import { DatePicker } from './chunk-I5Z2RVDF.js';
3
- import { Field } from './chunk-TUOG7EEI.js';
4
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { DatePicker, TimePicker } from './chunk-WSLT32Q4.js';
2
+ import { Field } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
5
4
  import { useState, useRef, useEffect, useMemo } from 'react';
6
5
  import { jsxs, jsx } from 'react/jsx-runtime';
7
6
 
@@ -1,8 +1,8 @@
1
- import { resolveTreeNodes, TreeView } from './chunk-C6ZV4VBN.js';
1
+ import { resolveTreeNodes, TreeView } from './chunk-C37U3GCC.js';
2
2
  import { OverlayLayer } from './chunk-XQPTSEJC.js';
3
- import { Field, Dialog, Icon, Button, Input } from './chunk-TUOG7EEI.js';
4
- import { boxed } from './chunk-YQOMSWW5.js';
5
- import { useState, useMemo, useEffect } from 'react';
3
+ import { Field, Dialog, Icon, Button, Input } from './chunk-RNOUXUBO.js';
4
+ import { boxed } from './chunk-5WW4MBRO.js';
5
+ import { useState, useMemo } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  var PICKER_CSS = `
@@ -55,14 +55,6 @@ function TreePickerImpl({
55
55
  onChange?.(pendingId, node);
56
56
  setOpen(false);
57
57
  };
58
- useEffect(() => {
59
- if (!open) return;
60
- const onKeyDown = (event) => {
61
- if (event.key === "Escape") setOpen(false);
62
- };
63
- document.addEventListener("keydown", onKeyDown);
64
- return () => document.removeEventListener("keydown", onKeyDown);
65
- }, [open]);
66
58
  const commitDisabled = !allowEmpty && pendingId == null;
67
59
  const noneSelected = pendingId == null;
68
60
  const trigger = /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "var(--wp-space-2)", minWidth: 0 }, children: [
@@ -98,7 +90,7 @@ function TreePickerImpl({
98
90
  )
99
91
  ] });
100
92
  return /* @__PURE__ */ jsxs("div", { className: "erp-treepicker", style: { minWidth: 0 }, children: [
101
- /* @__PURE__ */ jsx("style", { children: PICKER_CSS }),
93
+ /* @__PURE__ */ jsx("style", { href: "erp-treepicker", precedence: "medium", children: PICKER_CSS }),
102
94
  label ? /* @__PURE__ */ jsx(Field, { label, children: trigger }) : trigger,
103
95
  open ? /* @__PURE__ */ jsx(OverlayLayer, { onClose: closeDialog, children: /* @__PURE__ */ jsx(
104
96
  Dialog,
@@ -1,6 +1,6 @@
1
- import { Text } from './chunk-QTHYLNJW.js';
2
- import { Switch, Field, Select, Input } from './chunk-TUOG7EEI.js';
3
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Text } from './chunk-37SFFW37.js';
2
+ import { Switch, Field, Select, Input } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
4
4
  import React, { useMemo } from 'react';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
6
 
@@ -13,7 +13,8 @@ function OptionValueFieldsImpl({
13
13
  value,
14
14
  onChange,
15
15
  disabled = false,
16
- readOnly = false
16
+ readOnly = false,
17
+ emptySchemaText = "\uC774 \uC7A5\uBE44\uC5D0 \uC815\uD574 \uB454 \uC635\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC7A5\uBE44 \uC0C1\uC138\uC758 \uC218\uC815 \u2192 \uC635\uC158 \uD0ED\uC5D0\uC11C \uBA3C\uC800 \uC11C\uC2DD\uC744 \uC815\uD558\uC138\uC694."
17
18
  }) {
18
19
  const fields = useMemo(() => normalizeSchema(schema), [schema]);
19
20
  const current = useMemo(
@@ -48,7 +49,7 @@ function OptionValueFieldsImpl({
48
49
  }) });
49
50
  }
50
51
  if (fields.length === 0) {
51
- return /* @__PURE__ */ jsx(Text, { preset: "body-sm", style: { color: "var(--wp-text-muted)" }, children: "\uC774 \uC7A5\uBE44\uC5D0 \uC815\uD574 \uB454 \uC635\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC7A5\uBE44 \uC0C1\uC138\uC758 \uC218\uC815 \u2192 \uC635\uC158 \uD0ED\uC5D0\uC11C \uBA3C\uC800 \uC11C\uC2DD\uC744 \uC815\uD558\uC138\uC694." });
52
+ return /* @__PURE__ */ jsx(Text, { preset: "body-sm", style: { color: "var(--wp-text-muted)" }, children: emptySchemaText });
52
53
  }
53
54
  return /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-3)" }, children: [
54
55
  fields.map((field) => {
@@ -1,5 +1,5 @@
1
- import { IconButton, Input } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { IconButton, Input } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import { useState, useRef, useEffect, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,7 +1,8 @@
1
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DKPNFEVU.js';
2
- import { Alert, Badge, Spinner, Button, Input, Checkbox } from './chunk-TUOG7EEI.js';
3
- import { boxed } from './chunk-YQOMSWW5.js';
4
- import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
1
+ import { fetchAllPages } from './chunk-Q4SWUINA.js';
2
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-KO6OEY3G.js';
3
+ import { Alert, Badge, Spinner, Button, Input, Checkbox } from './chunk-RNOUXUBO.js';
4
+ import { boxed } from './chunk-5WW4MBRO.js';
5
+ import { useRef, useMemo, useState, useCallback, useEffect } from 'react';
5
6
  import { jsxs, jsx } from 'react/jsx-runtime';
6
7
 
7
8
  var deptOf = (row) => row.department_name ?? row.department?.name ?? null;
@@ -24,10 +25,13 @@ function RoleMemberPickerImpl({
24
25
  catalogEndpoint = "/employees",
25
26
  employees,
26
27
  sourceEndpoint,
27
- disabled = false
28
+ disabled = false,
29
+ silentStatuses
28
30
  }) {
29
31
  const adapter = useSduiAdapter();
30
32
  const api = hostFetchOf(adapter) ?? defaultAdapter.fetch;
33
+ const apiRef = useRef(api);
34
+ apiRef.current = api;
31
35
  const base = useMemo(() => sourceEndpoint ? sourceEndpoint.split("?")[0] : null, [sourceEndpoint]);
32
36
  const [members, setMembers] = useState(null);
33
37
  const [membersLoading, setMembersLoading] = useState(false);
@@ -38,16 +42,20 @@ function RoleMemberPickerImpl({
38
42
  const [saving, setSaving] = useState(false);
39
43
  const [removingId, setRemovingId] = useState(null);
40
44
  const requestSeq = useRef(0);
45
+ const silentStatusesRef = useRef(silentStatuses);
46
+ silentStatusesRef.current = silentStatuses;
41
47
  const fetchMembers = useCallback((endpoint) => {
42
48
  const seq = ++requestSeq.current;
43
49
  setMembersLoading(true);
44
- api(endpoint, { method: "GET" }).then((response) => {
50
+ apiRef.current(endpoint, { method: "GET" }).then((response) => {
45
51
  if (requestSeq.current !== seq) return;
46
52
  setMembers(parseMembers(response));
47
53
  }).catch((err) => {
48
54
  if (requestSeq.current !== seq) return;
49
55
  setMembers((prev) => prev ?? []);
50
- if (!(err?.status === 503)) {
56
+ const status = err?.status;
57
+ const silent = typeof status === "number" && (silentStatusesRef.current ?? []).includes(status);
58
+ if (!silent) {
51
59
  setError(messageOf(err, "\uC5ED\uD560\uC5D0 \uBD80\uC5EC\uB41C \uC0AC\uC6A9\uC790\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
52
60
  }
53
61
  }).finally(() => {
@@ -72,25 +80,20 @@ function RoleMemberPickerImpl({
72
80
  useEffect(() => {
73
81
  if (givenCatalog.length > 0 || fetchedCatalog !== null) return;
74
82
  let alive = true;
75
- (async () => {
76
- const rows = [];
77
- for (let page = 0; page < 10; page += 1) {
78
- const response = await api(`${catalogEndpoint}?limit=500&offset=${rows.length}`, {
79
- method: "GET"
80
- });
81
- const batch = Array.isArray(response?.data) ? response.data : [];
82
- rows.push(...batch.filter((row) => row && typeof row.id === "string"));
83
- const total = typeof response?.total === "number" ? response.total : rows.length;
84
- if (batch.length === 0 || rows.length >= total) break;
85
- }
83
+ fetchAllPages(apiRef.current, catalogEndpoint, (body) => {
84
+ const batch = Array.isArray(body.data) ? body.data : [];
85
+ return batch.filter(
86
+ (row) => typeof row === "object" && row !== null && typeof row.id === "string"
87
+ );
88
+ }).then((rows) => {
86
89
  if (alive) setFetchedCatalog(rows);
87
- })().catch(() => {
90
+ }).catch(() => {
88
91
  if (alive) setFetchedCatalog([]);
89
92
  });
90
93
  return () => {
91
94
  alive = false;
92
95
  };
93
- }, [givenCatalog, fetchedCatalog, api, catalogEndpoint]);
96
+ }, [givenCatalog, fetchedCatalog, catalogEndpoint]);
94
97
  const catalog = givenCatalog.length > 0 ? givenCatalog : fetchedCatalog ?? [];
95
98
  const catalogLoading = givenCatalog.length === 0 && fetchedCatalog === null;
96
99
  const memberIds = useMemo(() => new Set((members ?? []).map((row) => row.id)), [members]);
@@ -1,6 +1,7 @@
1
- import { IconButton, Badge } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
3
- import { useRef, useState, useMemo, useCallback, useEffect } from 'react';
1
+ import { usePanZoomCanvas, MIN_ZOOM, MAX_ZOOM } from './chunk-MMIGOPO5.js';
2
+ import { IconButton, Badge } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
4
+ import { useState, useMemo, useCallback, useEffect } from 'react';
4
5
  import { jsxs, jsx } from 'react/jsx-runtime';
5
6
 
6
7
  var NODE_W = 176;
@@ -8,10 +9,7 @@ var NODE_H = 64;
8
9
  var SIBLING_GAP = 24;
9
10
  var LEVEL_GAP = 48;
10
11
  var CANVAS_PADDING = 48;
11
- var MIN_ZOOM = 0.35;
12
- var MAX_ZOOM = 1.8;
13
12
  var ZOOM_STEP = 1.2;
14
- var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
15
13
  function buildBlock(node) {
16
14
  const childBlocks = (node.children ?? []).map(buildBlock);
17
15
  const childAreaWidth = childBlocks.reduce(
@@ -108,11 +106,20 @@ function OrgChartImpl({
108
106
  selectedId,
109
107
  height = 520
110
108
  }) {
111
- const viewportRef = useRef(null);
112
- const dragRef = useRef(null);
113
- const [view, setView] = useState({ scale: 1, x: 0, y: 0 });
114
- const [dragging, setDragging] = useState(false);
115
- const [isFullscreen, setIsFullscreen] = useState(false);
109
+ const {
110
+ viewportRef,
111
+ view,
112
+ setView,
113
+ dragging,
114
+ isFullscreen,
115
+ viewportCenter,
116
+ zoomAt,
117
+ toggleFullscreen,
118
+ pointerHandlers
119
+ } = usePanZoomCanvas({
120
+ stopSelector: "[data-orgchart-stop]",
121
+ initialView: { scale: 1, x: 0, y: 0 }
122
+ });
116
123
  const [internalSelected, setInternalSelected] = useState(
117
124
  void 0
118
125
  );
@@ -128,103 +135,11 @@ function OrgChartImpl({
128
135
  y: 0
129
136
  });
130
137
  },
131
- [layout.width]
138
+ [layout.width, setView, viewportRef]
132
139
  );
133
140
  useEffect(() => {
134
141
  centerView(1);
135
142
  }, [centerView]);
136
- useEffect(() => {
137
- const handleChange = () => {
138
- setIsFullscreen(document.fullscreenElement === viewportRef.current);
139
- };
140
- document.addEventListener("fullscreenchange", handleChange);
141
- return () => document.removeEventListener("fullscreenchange", handleChange);
142
- }, []);
143
- const zoomAt = useCallback(
144
- (nextScale, focal) => {
145
- setView((current) => {
146
- const scale = clamp(nextScale, MIN_ZOOM, MAX_ZOOM);
147
- return {
148
- scale,
149
- x: focal.x - (focal.x - current.x) / current.scale * scale,
150
- y: focal.y - (focal.y - current.y) / current.scale * scale
151
- };
152
- });
153
- },
154
- []
155
- );
156
- useEffect(() => {
157
- const el = viewportRef.current;
158
- if (!el) return;
159
- const handleWheel = (event) => {
160
- event.preventDefault();
161
- const rect = el.getBoundingClientRect();
162
- const focal = {
163
- x: event.clientX - rect.left,
164
- y: event.clientY - rect.top
165
- };
166
- setView((current) => {
167
- const scale = clamp(
168
- current.scale * (event.deltaY > 0 ? 0.9 : 1.1),
169
- MIN_ZOOM,
170
- MAX_ZOOM
171
- );
172
- return {
173
- scale,
174
- x: focal.x - (focal.x - current.x) / current.scale * scale,
175
- y: focal.y - (focal.y - current.y) / current.scale * scale
176
- };
177
- });
178
- };
179
- el.addEventListener("wheel", handleWheel, { passive: false });
180
- return () => el.removeEventListener("wheel", handleWheel);
181
- }, []);
182
- const viewportCenter = useCallback(() => {
183
- const rect = viewportRef.current?.getBoundingClientRect();
184
- if (!rect) return { x: 0, y: 0 };
185
- return { x: rect.width / 2, y: rect.height / 2 };
186
- }, []);
187
- const handlePointerDown = (event) => {
188
- if (event.target.closest("[data-orgchart-stop]")) return;
189
- dragRef.current = {
190
- pointerId: event.pointerId,
191
- startX: event.clientX,
192
- startY: event.clientY,
193
- originX: view.x,
194
- originY: view.y
195
- };
196
- setDragging(true);
197
- event.currentTarget.setPointerCapture(event.pointerId);
198
- };
199
- const handlePointerMove = (event) => {
200
- const drag = dragRef.current;
201
- if (!drag || drag.pointerId !== event.pointerId) return;
202
- setView((current) => ({
203
- ...current,
204
- x: drag.originX + event.clientX - drag.startX,
205
- y: drag.originY + event.clientY - drag.startY
206
- }));
207
- };
208
- const handlePointerUp = (event) => {
209
- if (dragRef.current?.pointerId !== event.pointerId) return;
210
- dragRef.current = null;
211
- setDragging(false);
212
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
213
- event.currentTarget.releasePointerCapture(event.pointerId);
214
- }
215
- };
216
- const toggleFullscreen = async () => {
217
- const viewport = viewportRef.current;
218
- if (!viewport) return;
219
- try {
220
- if (document.fullscreenElement === viewport) {
221
- await document.exitFullscreen();
222
- } else {
223
- await viewport.requestFullscreen();
224
- }
225
- } catch {
226
- }
227
- };
228
143
  const handleNodeClick = (node) => {
229
144
  if (selectedId === void 0) setInternalSelected(node.id);
230
145
  onNodeClick?.(node);
@@ -235,10 +150,7 @@ function OrgChartImpl({
235
150
  ref: viewportRef,
236
151
  role: "application",
237
152
  "aria-label": "\uC870\uC9C1\uB3C4 \uCE94\uBC84\uC2A4",
238
- onPointerDown: handlePointerDown,
239
- onPointerMove: handlePointerMove,
240
- onPointerUp: handlePointerUp,
241
- onPointerCancel: handlePointerUp,
153
+ ...pointerHandlers,
242
154
  style: {
243
155
  position: "relative",
244
156
  height: isFullscreen ? "100vh" : height,
@@ -1,8 +1,8 @@
1
- import { includesLooseSearch } from './chunk-VDBUXYEB.js';
1
+ import { includesLooseSearch } from './chunk-WOYMEEPH.js';
2
2
  import { OverlayLayer } from './chunk-XQPTSEJC.js';
3
- import { Button, Dialog, Input, Tag, Checkbox } from './chunk-TUOG7EEI.js';
4
- import { boxed } from './chunk-YQOMSWW5.js';
5
- import React, { useMemo, useState, useEffect } from 'react';
3
+ import { Button, Dialog, Input, Tag, Checkbox } from './chunk-RNOUXUBO.js';
4
+ import { boxed } from './chunk-5WW4MBRO.js';
5
+ import React, { useMemo, useState } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  function SearchableMultiPickerImpl({
@@ -16,7 +16,7 @@ function SearchableMultiPickerImpl({
16
16
  const safeOptions = useMemo(() => options ?? [], [options]);
17
17
  const isControlled = values !== void 0 && typeof onChange === "function";
18
18
  const [internalValues, setInternalValues] = useState(values ?? []);
19
- const committed = isControlled ? values : internalValues;
19
+ const committed = (isControlled ? values : internalValues) ?? [];
20
20
  const [open, setOpen] = useState(false);
21
21
  const [query, setQuery] = useState("");
22
22
  const [draft, setDraft] = useState([]);
@@ -33,9 +33,8 @@ function SearchableMultiPickerImpl({
33
33
  };
34
34
  const openDialog = () => {
35
35
  if (disabled) return;
36
- const current = committed ?? [];
37
- setDraft(sortByOptionOrder(current));
38
- setSelectedAtOpen(new Set(current));
36
+ setDraft(sortByOptionOrder(committed));
37
+ setSelectedAtOpen(new Set(committed));
39
38
  setQuery("");
40
39
  setOpen(true);
41
40
  };
@@ -51,14 +50,6 @@ function SearchableMultiPickerImpl({
51
50
  (current) => sortByOptionOrder(checked ? [...current, value] : current.filter((item) => item !== value))
52
51
  );
53
52
  };
54
- useEffect(() => {
55
- if (!open) return;
56
- const closeOnEscape = (event) => {
57
- if (event.key === "Escape") setOpen(false);
58
- };
59
- document.addEventListener("keydown", closeOnEscape);
60
- return () => document.removeEventListener("keydown", closeOnEscape);
61
- }, [open]);
62
53
  const visibleOptions = useMemo(() => {
63
54
  const selectedFirst = [
64
55
  ...safeOptions.filter((option) => selectedAtOpen.has(option.value)),
@@ -1,4 +1,4 @@
1
- import { boxed, containerLayoutStyle } from './chunk-YQOMSWW5.js';
1
+ import { boxed, containerLayoutStyle } from './chunk-5WW4MBRO.js';
2
2
  import React from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -1,6 +1,6 @@
1
- import { Text } from './chunk-QTHYLNJW.js';
2
- import { Field, Input, Select, IconButton, Button } from './chunk-TUOG7EEI.js';
3
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Text } from './chunk-37SFFW37.js';
2
+ import { Field, Input, Select, IconButton, Button } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
4
4
  import { useMemo } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
@@ -1,6 +1,6 @@
1
- import { buildCascadingTree } from './chunk-VDBUXYEB.js';
2
- import { Input, IconButton, EmptyState, Icon, Badge } from './chunk-TUOG7EEI.js';
3
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { buildCascadingTree } from './chunk-WOYMEEPH.js';
2
+ import { Input, IconButton, EmptyState, Icon, Badge } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
4
4
  import React, { useMemo, useState, useRef, useEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
@@ -239,7 +239,7 @@ function TreeViewImpl({
239
239
  className: "erp-treeview",
240
240
  style: { display: "flex", flexDirection: "column", gap: "var(--wp-space-2)", minWidth: 0 },
241
241
  children: [
242
- /* @__PURE__ */ jsx("style", { children: TREE_CSS }),
242
+ /* @__PURE__ */ jsx("style", { href: "erp-treeview", precedence: "medium", children: TREE_CSS }),
243
243
  searchable ? /* @__PURE__ */ jsx(
244
244
  Input,
245
245
  {
@@ -1,4 +1,4 @@
1
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { boxed } from './chunk-5WW4MBRO.js';
2
2
  import { useSyncExternalStore } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -1,5 +1,5 @@
1
- import { Button } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Button } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import { useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,7 +1,8 @@
1
- import { includesLooseSearch } from './chunk-VDBUXYEB.js';
2
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DKPNFEVU.js';
3
- import { Icon, Badge, IconButton, Field, Select, Input, Tag, Spinner, Button } from './chunk-TUOG7EEI.js';
4
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { orderCircleStyle, ellipsisStyle, unwrapRows, uid } from './chunk-Q4SWUINA.js';
2
+ import { includesLooseSearch } from './chunk-WOYMEEPH.js';
3
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-KO6OEY3G.js';
4
+ import { Icon, Badge, IconButton, Field, Select, Input, Tag, Spinner, Button, isPlainObject } from './chunk-RNOUXUBO.js';
5
+ import { boxed } from './chunk-5WW4MBRO.js';
5
6
  import React, { useState, useId, useRef, useEffect } from 'react';
6
7
  import { jsxs, jsx } from 'react/jsx-runtime';
7
8
 
@@ -47,29 +48,13 @@ var stepLabel = (step) => {
47
48
  }
48
49
  };
49
50
  var renumber = (steps) => steps.map((step, index) => ({ ...step, order: index + 1 }));
50
- var uidCounter = 0;
51
- var uid = (prefix) => `${prefix}-${Date.now()}-${++uidCounter}`;
52
- function isRecord(value) {
53
- return typeof value === "object" && value !== null && !Array.isArray(value);
54
- }
55
51
  function toSearchResults(payload) {
56
- let rows = [];
57
- if (Array.isArray(payload)) rows = payload;
58
- else if (isRecord(payload)) {
59
- for (const key of ["items", "data", "results", "rows"]) {
60
- const inner = payload[key];
61
- if (Array.isArray(inner)) {
62
- rows = inner;
63
- break;
64
- }
65
- }
66
- }
67
- return rows.filter(isRecord).flatMap((row) => {
52
+ return unwrapRows(payload).flatMap((row) => {
68
53
  const id = row.id;
69
54
  const name = row.name ?? row.label;
70
55
  if (id === null || id === void 0 || name === null || name === void 0) return [];
71
56
  const subRaw = row.sub ?? row.email ?? row.workEmail ?? row.department;
72
- const sub = isRecord(subRaw) ? subRaw.name : subRaw;
57
+ const sub = isPlainObject(subRaw) ? subRaw.name : subRaw;
73
58
  return [
74
59
  {
75
60
  id: String(id),
@@ -80,23 +65,6 @@ function toSearchResults(payload) {
80
65
  ];
81
66
  });
82
67
  }
83
- var ellipsisStyle = {
84
- display: "block",
85
- overflow: "hidden",
86
- textOverflow: "ellipsis",
87
- whiteSpace: "nowrap"
88
- };
89
- var orderCircleStyle = {
90
- display: "inline-flex",
91
- alignItems: "center",
92
- justifyContent: "center",
93
- width: 28,
94
- height: 28,
95
- flex: "none",
96
- borderRadius: "var(--wp-radius-pill)",
97
- background: "var(--wp-surface-brand-tint)",
98
- color: "var(--wp-text-brand)"
99
- };
100
68
  var dashedEmptyStyle = {
101
69
  border: "var(--wp-border-w) dashed var(--wp-border-default)",
102
70
  borderRadius: "var(--wp-radius-lg)",