@wipco/sdui 0.50.6 → 1.0.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 (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 +1096 -941
  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
@@ -1,7 +1,7 @@
1
1
  import { useOverlayLayer, OverlayContainerContext, getOverlayRoot } from './chunk-XQPTSEJC.js';
2
- import { Icon } from './chunk-TUOG7EEI.js';
3
- import { boxed } from './chunk-YQOMSWW5.js';
4
- import React, { useState, useRef, useMemo, useEffect, useCallback, useLayoutEffect } from 'react';
2
+ import { Icon } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
4
+ import React, { useState, useRef, useEffect, useMemo, useCallback, useLayoutEffect } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
  import { createPortal } from 'react-dom';
7
7
 
@@ -87,8 +87,208 @@ function useDismiss(open, hostRef, popRef, onDismiss) {
87
87
  function focusStaysInside(next, hostRef, popRef) {
88
88
  return next instanceof Node && Boolean(hostRef.current?.contains(next) || popRef.current?.contains(next));
89
89
  }
90
- var YMD_RE = /^\d{4}-\d{2}-\d{2}$/;
91
90
  var HM_RE = /^\d{2}:\d{2}$/;
91
+ var LIST_HEIGHT = 260;
92
+ var MINUTES_PER_DAY = 24 * 60;
93
+ function maskHm(text) {
94
+ const digits = text.replace(/\D/g, "").slice(0, 4);
95
+ if (digits.length <= 2) return digits;
96
+ return `${digits.slice(0, 2)}:${digits.slice(2)}`;
97
+ }
98
+ function parseHm(value) {
99
+ if (typeof value !== "string" || !HM_RE.test(value)) return void 0;
100
+ const h = Number(value.slice(0, 2));
101
+ const m = Number(value.slice(3, 5));
102
+ if (h > 23 || m > 59) return void 0;
103
+ return h * 60 + m;
104
+ }
105
+ function formatHm(minutes) {
106
+ const h = Math.floor(minutes / 60) % 24;
107
+ const m = minutes % 60;
108
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
109
+ }
110
+ function buildOptions(step, min, max) {
111
+ const safeStep = Number.isFinite(step) && step >= 1 ? Math.min(Math.floor(step), 720) : 30;
112
+ const from = min ?? 0;
113
+ const to = max ?? MINUTES_PER_DAY - 1;
114
+ const out = [];
115
+ for (let m = Math.ceil(from / safeStep) * safeStep; m <= to; m += safeStep) out.push(formatHm(m));
116
+ return out;
117
+ }
118
+ function TimePickerImpl({
119
+ value,
120
+ onChange,
121
+ min,
122
+ max,
123
+ step = 30,
124
+ placeholder,
125
+ invalid = false,
126
+ disabled = false,
127
+ size = "md",
128
+ listLabel = "\uC2DC\uAC01 \uC120\uD0DD"
129
+ }) {
130
+ const [open, setOpen] = useState(false);
131
+ const [innerValue, setInnerValue] = useState("");
132
+ const [grabFocus, setGrabFocus] = useState(false);
133
+ const hostRef = useRef(null);
134
+ const popRef = useRef(null);
135
+ const inputRef = useRef(null);
136
+ const isControlled = value !== void 0;
137
+ const currentValue = (isControlled ? value : innerValue) ?? "";
138
+ const [text, setText] = useState(currentValue);
139
+ useEffect(() => {
140
+ setText(currentValue);
141
+ }, [currentValue]);
142
+ const minMinutes = useMemo(() => parseHm(min), [min]);
143
+ const maxMinutes = useMemo(() => parseHm(max), [max]);
144
+ const options = useMemo(() => buildOptions(step, minMinutes, maxMinutes), [step, minMinutes, maxMinutes]);
145
+ const dismiss = useCallback((reason) => {
146
+ setOpen(false);
147
+ if (reason === "escape") inputRef.current?.focus();
148
+ }, []);
149
+ useDismiss(open, hostRef, popRef, dismiss);
150
+ const anchor = usePopoverAnchor(open, hostRef, popRef, LIST_HEIGHT);
151
+ const portal = usePortalTo();
152
+ const isInvalid = Boolean(invalid);
153
+ const commit = (next) => {
154
+ if (!isControlled) setInnerValue(next);
155
+ onChange?.(next);
156
+ };
157
+ const handleTextChange = (raw) => {
158
+ const masked = maskHm(raw);
159
+ setText(masked);
160
+ if (masked === "") {
161
+ commit("");
162
+ return;
163
+ }
164
+ const minutes = parseHm(masked);
165
+ if (minutes === void 0) return;
166
+ if (minMinutes !== void 0 && minutes < minMinutes) return;
167
+ if (maxMinutes !== void 0 && minutes > maxMinutes) return;
168
+ commit(masked);
169
+ };
170
+ const focusList = () => {
171
+ const list = popRef.current;
172
+ if (!list) return;
173
+ (list.querySelector('[aria-selected="true"]') ?? list.querySelector('[role="option"]'))?.focus();
174
+ };
175
+ const openList = (claimFocus) => {
176
+ setGrabFocus(claimFocus);
177
+ setOpen(true);
178
+ };
179
+ useLayoutEffect(() => {
180
+ if (!open || !grabFocus || !popRef.current) return;
181
+ focusList();
182
+ setGrabFocus(false);
183
+ }, [open, grabFocus, anchor]);
184
+ const handleKeyDown = (event) => {
185
+ if (event.key === "ArrowDown") {
186
+ event.preventDefault();
187
+ if (open) focusList();
188
+ else openList(true);
189
+ return;
190
+ }
191
+ if (event.key === "Enter" && open) {
192
+ event.preventDefault();
193
+ setOpen(false);
194
+ }
195
+ };
196
+ const handleOptionKeyDown = (index) => (event) => {
197
+ const step2 = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
198
+ if (step2 !== 0) {
199
+ event.preventDefault();
200
+ const next = Math.min(Math.max(index + step2, 0), options.length - 1);
201
+ popRef.current?.querySelectorAll('[role="option"]')[next]?.focus();
202
+ return;
203
+ }
204
+ if (event.key === "Home" || event.key === "End") {
205
+ event.preventDefault();
206
+ const target = event.key === "Home" ? 0 : options.length - 1;
207
+ popRef.current?.querySelectorAll('[role="option"]')[target]?.focus();
208
+ }
209
+ };
210
+ const pick = (option) => {
211
+ setText(option);
212
+ commit(option);
213
+ setOpen(false);
214
+ inputRef.current?.focus();
215
+ };
216
+ const handleBlur = (event) => {
217
+ if (focusStaysInside(event.relatedTarget, hostRef, popRef)) return;
218
+ if (text !== currentValue) setText(currentValue);
219
+ setOpen(false);
220
+ };
221
+ const classes = ["wp-input", "wp-input--mono", "wp-tp-input"];
222
+ if (size !== "md") classes.push(`wp-input--${size}`);
223
+ if (isInvalid) classes.push("wp-input--invalid");
224
+ return /* @__PURE__ */ jsxs("div", { ref: hostRef, className: "wp-tp-host", onBlur: handleBlur, children: [
225
+ /* @__PURE__ */ jsx(HoistedStyle, { id: "wp-timepicker", css: TP_CSS }),
226
+ /* @__PURE__ */ jsx(AnchoredPopoverStyles, {}),
227
+ /* @__PURE__ */ jsxs("div", { className: "wp-inputwrap wp-inputwrap--lead", children: [
228
+ /* @__PURE__ */ jsx("span", { className: "wp-inputwrap__lead", children: /* @__PURE__ */ jsx(Icon, { name: "clock", size: size === "lg" ? 16 : 15 }) }),
229
+ /* @__PURE__ */ jsx(
230
+ "input",
231
+ {
232
+ ref: inputRef,
233
+ type: "text",
234
+ className: classes.join(" "),
235
+ inputMode: "numeric",
236
+ autoComplete: "off",
237
+ spellCheck: false,
238
+ maxLength: 5,
239
+ placeholder: placeholder ?? "HH:MM",
240
+ value: text,
241
+ disabled,
242
+ "aria-invalid": isInvalid || void 0,
243
+ "aria-haspopup": "listbox",
244
+ "aria-expanded": open,
245
+ onChange: (event) => handleTextChange(event.target.value),
246
+ onKeyDown: handleKeyDown,
247
+ onPointerDown: () => {
248
+ if (!disabled && !open) openList(false);
249
+ }
250
+ }
251
+ )
252
+ ] }),
253
+ portal(
254
+ anchor,
255
+ /* @__PURE__ */ jsx(
256
+ "div",
257
+ {
258
+ ref: popRef,
259
+ className: "wp-anchored-pop wp-tp-pop",
260
+ style: anchorStyle(anchor),
261
+ role: "listbox",
262
+ "aria-label": listLabel,
263
+ children: options.map((option, index) => /* @__PURE__ */ jsx(
264
+ "button",
265
+ {
266
+ type: "button",
267
+ role: "option",
268
+ "aria-selected": option === currentValue,
269
+ className: option === currentValue ? "wp-tp-opt wp-tp-opt--on" : "wp-tp-opt",
270
+ onKeyDown: handleOptionKeyDown(index),
271
+ onClick: () => pick(option),
272
+ children: option
273
+ },
274
+ option
275
+ ))
276
+ }
277
+ )
278
+ )
279
+ ] });
280
+ }
281
+ var TP_CSS = `
282
+ .wp-tp-host{position:relative}
283
+ .wp-tp-input{padding-right:6px;min-width:calc(5ch + 36px)}
284
+ .wp-tp-pop{padding:var(--wp-space-1);max-height:260px;overflow-y:auto;overscroll-behavior:contain}
285
+ .wp-tp-opt{display:block;width:100%;padding:6px 10px;border:none;background:transparent;border-radius:var(--wp-radius-sm);font:inherit;font-family:var(--wp-font-mono);font-size:12.5px;color:var(--wp-text-default);text-align:left;cursor:pointer}
286
+ .wp-tp-opt:hover{background:var(--wp-surface-hover)}
287
+ .wp-tp-opt:focus-visible{outline:2px solid var(--wp-border-focus);outline-offset:-2px}
288
+ .wp-tp-opt--on{background:var(--wp-action-primary-bg);color:var(--wp-action-primary-fg);font-weight:500}
289
+ `;
290
+ var TimePicker = boxed(TimePickerImpl, "TimePicker");
291
+ var YMD_RE = /^\d{4}-\d{2}-\d{2}$/;
92
292
  function parseYmd(value) {
93
293
  if (typeof value !== "string" || !YMD_RE.test(value)) return void 0;
94
294
  const y = Number(value.slice(0, 4));
@@ -152,12 +352,10 @@ function maskYmdHm(text) {
152
352
  const digits = text.replace(/\D/g, "").slice(0, 12);
153
353
  const date = maskYmd(digits.slice(0, 8));
154
354
  if (digits.length <= 8) return date;
155
- const time = digits.slice(8);
156
- return time.length <= 2 ? `${date} ${time}` : `${date} ${time.slice(0, 2)}:${time.slice(2)}`;
355
+ return `${date} ${maskHm(digits.slice(8))}`;
157
356
  }
158
357
  function isValidHm(text) {
159
- if (!HM_RE.test(text)) return false;
160
- return Number(text.slice(0, 2)) <= 23 && Number(text.slice(3, 5)) <= 59;
358
+ return parseHm(text) !== void 0;
161
359
  }
162
360
  function splitDateTime(value) {
163
361
  if (!value) return ["", ""];
@@ -176,11 +374,32 @@ function toFieldValue(text, withTime) {
176
374
  if (!parseYmd(date) || !isValidHm(time)) return void 0;
177
375
  return `${date}T${time}`;
178
376
  }
179
- var WEEKDAYS_KO = ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"];
377
+ var KO_LOCALE = {
378
+ weekdays: ["\uC77C", "\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0"],
379
+ weekStartsOn: 0,
380
+ formatCaption: (year, month) => `${year}\uB144 ${month}\uC6D4`,
381
+ prevMonthLabel: "\uC774\uC804 \uB2EC",
382
+ nextMonthLabel: "\uB2E4\uC74C \uB2EC",
383
+ calendarLabel: "\uB0A0\uC9DC \uC120\uD0DD \uB2EC\uB825",
384
+ startLabel: "\uC2DC\uC791\uC77C",
385
+ endLabel: "\uC885\uB8CC\uC77C"
386
+ };
387
+ function resolveLocale(locale, calendarLabel) {
388
+ return {
389
+ weekdays: locale?.weekdays ?? KO_LOCALE.weekdays,
390
+ weekStartsOn: locale?.weekStartsOn ?? KO_LOCALE.weekStartsOn,
391
+ formatCaption: locale?.formatCaption ?? KO_LOCALE.formatCaption,
392
+ prevMonthLabel: locale?.prevMonthLabel ?? KO_LOCALE.prevMonthLabel,
393
+ nextMonthLabel: locale?.nextMonthLabel ?? KO_LOCALE.nextMonthLabel,
394
+ calendarLabel: locale?.calendarLabel ?? calendarLabel,
395
+ startLabel: locale?.startLabel ?? KO_LOCALE.startLabel,
396
+ endLabel: locale?.endLabel ?? KO_LOCALE.endLabel
397
+ };
398
+ }
180
399
  var WEEKS_PER_MONTH = 6;
181
- function buildMonthCells(index, min, max) {
400
+ function buildMonthCells(index, min, max, weekStartsOn = 0) {
182
401
  const first = monthStart(index);
183
- const gridStart = addDays(first, -first.getDay());
402
+ const gridStart = addDays(first, -((first.getDay() - weekStartsOn + 7) % 7));
184
403
  const weeks = [];
185
404
  for (let w = 0; w < WEEKS_PER_MONTH; w += 1) {
186
405
  const row = [];
@@ -197,6 +416,9 @@ function buildMonthCells(index, min, max) {
197
416
  }
198
417
  return weeks;
199
418
  }
419
+ function clampViewToFocus(focusIdx, viewIdx, monthCount) {
420
+ return focusIdx < viewIdx ? focusIdx : focusIdx > viewIdx + monthCount - 1 ? focusIdx - monthCount + 1 : viewIdx;
421
+ }
200
422
  function Calendar({
201
423
  monthCount,
202
424
  minDate,
@@ -205,19 +427,21 @@ function Calendar({
205
427
  onPick,
206
428
  focusDate,
207
429
  onFocusDateChange,
208
- autoFocus = false
430
+ autoFocus = false,
431
+ locale
209
432
  }) {
210
433
  const focusIdx = monthIndex(focusDate);
211
434
  const [viewIdx, setViewIdx] = useState(focusIdx);
212
435
  const gridRef = useRef(null);
213
436
  const focusYmd = formatYmd(focusDate);
214
- const visibleIdx = focusIdx < viewIdx ? focusIdx : focusIdx > viewIdx + monthCount - 1 ? focusIdx - monthCount + 1 : viewIdx;
437
+ const visibleIdx = clampViewToFocus(focusIdx, viewIdx, monthCount);
215
438
  if (visibleIdx !== viewIdx) setViewIdx(visibleIdx);
216
439
  const claimFocusRef = useRef(autoFocus);
217
440
  useLayoutEffect(() => {
218
441
  const grid = gridRef.current;
219
442
  if (!grid) return;
220
- const holdsFocus = document.activeElement instanceof Node && grid.contains(document.activeElement);
443
+ const active = document.activeElement;
444
+ const holdsFocus = active instanceof HTMLElement && grid.contains(active) && active.classList.contains("wp-dp__day-btn");
221
445
  const claim = claimFocusRef.current || holdsFocus;
222
446
  claimFocusRef.current = false;
223
447
  if (claim) grid.querySelector('[tabindex="0"]')?.focus();
@@ -230,6 +454,10 @@ function Calendar({
230
454
  const moveFocus = (next) => {
231
455
  onFocusDateChange(clampDate(next, minDate, maxDate));
232
456
  };
457
+ const moveView = (delta) => {
458
+ setViewIdx(visibleIdx + delta);
459
+ moveFocus(addMonths(focusDate, delta));
460
+ };
233
461
  const handleKeyDown = (event) => {
234
462
  const deltaDays = event.key === "ArrowLeft" ? -1 : event.key === "ArrowRight" ? 1 : event.key === "ArrowUp" ? -7 : event.key === "ArrowDown" ? 7 : 0;
235
463
  if (deltaDays !== 0) {
@@ -239,7 +467,8 @@ function Calendar({
239
467
  }
240
468
  if (event.key === "Home" || event.key === "End") {
241
469
  event.preventDefault();
242
- const offset = event.key === "Home" ? -focusDate.getDay() : 6 - focusDate.getDay();
470
+ const inWeek = (focusDate.getDay() - locale.weekStartsOn + 7) % 7;
471
+ const offset = event.key === "Home" ? -inWeek : 6 - inWeek;
243
472
  moveFocus(addDays(focusDate, offset));
244
473
  return;
245
474
  }
@@ -255,10 +484,10 @@ function Calendar({
255
484
  {
256
485
  type: "button",
257
486
  className: "wp-dp__navbtn",
258
- "aria-label": "\uC774\uC804 \uB2EC",
487
+ "aria-label": locale.prevMonthLabel,
259
488
  "aria-disabled": prevDisabled || void 0,
260
489
  disabled: prevDisabled,
261
- onClick: () => setViewIdx(visibleIdx - 1),
490
+ onClick: () => moveView(-1),
262
491
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-left", size: 16 })
263
492
  }
264
493
  ),
@@ -267,10 +496,10 @@ function Calendar({
267
496
  {
268
497
  type: "button",
269
498
  className: "wp-dp__navbtn",
270
- "aria-label": "\uB2E4\uC74C \uB2EC",
499
+ "aria-label": locale.nextMonthLabel,
271
500
  "aria-disabled": nextDisabled || void 0,
272
501
  disabled: nextDisabled,
273
- onClick: () => setViewIdx(visibleIdx + 1),
502
+ onClick: () => moveView(1),
274
503
  children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: 16 })
275
504
  }
276
505
  )
@@ -278,12 +507,25 @@ function Calendar({
278
507
  Array.from({ length: monthCount }, (_, offset) => {
279
508
  const idx = visibleIdx + offset;
280
509
  const first = monthStart(idx);
281
- const weeks = buildMonthCells(idx, minDate, maxDate);
282
- const caption = `${first.getFullYear()}\uB144 ${first.getMonth() + 1}\uC6D4`;
510
+ const weeks = buildMonthCells(idx, minDate, maxDate, locale.weekStartsOn);
511
+ const caption = locale.formatCaption(first.getFullYear(), first.getMonth() + 1);
283
512
  return /* @__PURE__ */ jsxs("div", { className: "wp-dp__month", children: [
284
513
  /* @__PURE__ */ jsx("div", { className: "wp-dp__caption", children: /* @__PURE__ */ jsx("span", { className: "wp-dp__caption-label", children: caption }) }),
285
514
  /* @__PURE__ */ jsxs("table", { className: "wp-dp__grid", role: "grid", "aria-label": caption, children: [
286
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "wp-dp__weekdays", children: WEEKDAYS_KO.map((name) => /* @__PURE__ */ jsx("th", { scope: "col", className: "wp-dp__weekday", abbr: name, children: name }, name)) }) }),
515
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "wp-dp__weekdays", children: Array.from({ length: 7 }, (_2, col) => {
516
+ const day = (locale.weekStartsOn + col) % 7;
517
+ const name = locale.weekdays[day] ?? "";
518
+ return /* @__PURE__ */ jsx(
519
+ "th",
520
+ {
521
+ scope: "col",
522
+ className: day === 0 ? "wp-dp__weekday wp-dp__weekday--sun" : "wp-dp__weekday",
523
+ abbr: name,
524
+ children: name
525
+ },
526
+ day
527
+ );
528
+ }) }) }),
287
529
  /* @__PURE__ */ jsx("tbody", { className: "wp-dp__weeks", children: weeks.map((week, w) => /* @__PURE__ */ jsx("tr", { className: "wp-dp__week", children: week.map((cell) => {
288
530
  const state = cell.disabled ? {} : dayState(cell.date);
289
531
  const classes = ["wp-dp__day"];
@@ -376,8 +618,10 @@ function DatePickerImpl({
376
618
  invalid = false,
377
619
  disabled = false,
378
620
  size = "md",
379
- withTime = false
621
+ withTime = false,
622
+ locale
380
623
  }) {
624
+ const loc = resolveLocale(locale, KO_LOCALE.calendarLabel);
381
625
  const [open, setOpen] = useState(false);
382
626
  const [innerValue, setInnerValue] = useState("");
383
627
  const hostRef = useRef(null);
@@ -474,7 +718,7 @@ function DatePickerImpl({
474
718
  className: "wp-anchored-pop wp-dp-pop",
475
719
  style: anchorStyle(anchor),
476
720
  role: "dialog",
477
- "aria-label": "\uB0A0\uC9DC \uC120\uD0DD \uB2EC\uB825",
721
+ "aria-label": loc.calendarLabel,
478
722
  children: /* @__PURE__ */ jsx("div", { className: "wp-dp-pop__body", children: /* @__PURE__ */ jsx(
479
723
  Calendar,
480
724
  {
@@ -484,6 +728,7 @@ function DatePickerImpl({
484
728
  focusDate,
485
729
  onFocusDateChange: setFocusDate,
486
730
  autoFocus: grabFocus,
731
+ locale: loc,
487
732
  dayState: (date) => ({ selected: isSameDay(date, selectedDate) }),
488
733
  onPick: (date) => {
489
734
  const ymd = formatYmd(date);
@@ -508,8 +753,10 @@ function DateRangePickerImpl({
508
753
  max,
509
754
  invalid = false,
510
755
  disabled = false,
511
- size = "md"
756
+ size = "md",
757
+ locale
512
758
  }) {
759
+ const loc = resolveLocale(locale, "\uAE30\uAC04 \uC120\uD0DD \uB2EC\uB825");
513
760
  const [open, setOpen] = useState(false);
514
761
  const [innerRange, setInnerRange] = useState({});
515
762
  const hostRef = useRef(null);
@@ -632,7 +879,7 @@ function DateRangePickerImpl({
632
879
  maxLength: 10,
633
880
  lead: true,
634
881
  expanded: open,
635
- ariaLabel: "\uC2DC\uC791\uC77C",
882
+ ariaLabel: loc.startLabel,
636
883
  onValueChange: (raw) => handleTextChange("from", raw),
637
884
  onKeyDown: handleKeyDown("from"),
638
885
  onFocus: () => setEnd("from"),
@@ -654,7 +901,7 @@ function DateRangePickerImpl({
654
901
  maxLength: 10,
655
902
  lead: false,
656
903
  expanded: open,
657
- ariaLabel: "\uC885\uB8CC\uC77C",
904
+ ariaLabel: loc.endLabel,
658
905
  onValueChange: (raw) => handleTextChange("to", raw),
659
906
  onKeyDown: handleKeyDown("to"),
660
907
  onFocus: () => setEnd("to"),
@@ -673,7 +920,7 @@ function DateRangePickerImpl({
673
920
  className: "wp-anchored-pop wp-dp-pop wp-dp-pop--wide",
674
921
  style: anchorStyle(anchor),
675
922
  role: "dialog",
676
- "aria-label": "\uAE30\uAC04 \uC120\uD0DD \uB2EC\uB825",
923
+ "aria-label": loc.calendarLabel,
677
924
  children: /* @__PURE__ */ jsx("div", { className: "wp-dp-pop__body", children: /* @__PURE__ */ jsx(
678
925
  Calendar,
679
926
  {
@@ -683,6 +930,7 @@ function DateRangePickerImpl({
683
930
  focusDate,
684
931
  onFocusDateChange: setFocusDate,
685
932
  autoFocus: grabFocus,
933
+ locale: loc,
686
934
  dayState: (date) => {
687
935
  const t = date.getTime();
688
936
  const start = fromDate?.getTime();
@@ -727,7 +975,7 @@ var DP_CSS = `
727
975
  .wp-dp__caption-label{font-size:13px;font-weight:600;color:var(--wp-text-strong)}
728
976
  .wp-dp__grid{border-collapse:collapse}
729
977
  .wp-dp__weekday{width:32px;height:28px;padding:0;font-size:12px;font-weight:500;color:var(--wp-text-muted);text-align:center}
730
- .wp-dp__weekday:first-child{color:var(--wp-text-danger)}
978
+ .wp-dp__weekday--sun{color:var(--wp-text-danger)}
731
979
  .wp-dp__day{padding:0;text-align:center}
732
980
  .wp-dp__day-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:30px;padding:0;border:none;background:transparent;border-radius:var(--wp-radius-sm);font:inherit;font-variant-numeric:tabular-nums;color:inherit;cursor:pointer;transition:background 0.12s,color 0.12s}
733
981
  .wp-dp__day:not(.wp-dp__day--selected):not(.wp-dp__day--disabled) .wp-dp__day-btn:hover{background:var(--wp-surface-hover)}
@@ -745,4 +993,4 @@ var DP_CSS = `
745
993
  var DatePicker = boxed(DatePickerImpl, "DatePicker");
746
994
  var DateRangePicker = boxed(DateRangePickerImpl, "DateRangePicker");
747
995
 
748
- export { AnchoredPopoverStyles, DatePicker, DateRangePicker, HoistedStyle, anchorStyle, focusStaysInside, useDismiss, usePopoverAnchor, usePortalTo };
996
+ export { DatePicker, DateRangePicker, HoistedStyle, TimePicker, YMD_RE, parseYmd };
@@ -1,5 +1,5 @@
1
- import { Icon, Badge, Tooltip } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Icon, Badge, Tooltip } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import React from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -1,6 +1,6 @@
1
- import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DKPNFEVU.js';
2
- import { Spinner, Badge, Icon } from './chunk-TUOG7EEI.js';
3
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-KO6OEY3G.js';
2
+ import { Spinner, Badge, Icon } from './chunk-RNOUXUBO.js';
3
+ import { boxed } from './chunk-5WW4MBRO.js';
4
4
  import { useState, useEffect } from 'react';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
6
 
@@ -16,10 +16,17 @@ var period = (item, fromKey, toKey) => {
16
16
  if (!from && !to) return "";
17
17
  return `${from || "?"} ~ ${to || "\uD604\uC7AC"}`;
18
18
  };
19
- function summarizeItem(kind, raw) {
19
+ function summarizeItem(kind, raw, fields) {
20
20
  if (typeof raw !== "object" || raw === null) return "";
21
21
  const item = raw;
22
22
  const parts = [];
23
+ if (fields) {
24
+ for (const key of fields) {
25
+ const v = text(item[key]);
26
+ if (v) parts.push(v);
27
+ }
28
+ return parts.join(" \xB7 ");
29
+ }
23
30
  switch (kind) {
24
31
  case "degrees":
25
32
  parts.push(text(item.institution), text(item.degree), text(item.field_of_study), period(item, "start_date", "end_date"));
@@ -66,25 +73,26 @@ var cellStyle = {
66
73
  whiteSpace: "pre-wrap",
67
74
  overflowWrap: "anywhere"
68
75
  };
69
- function ListCell({ kind, value }) {
76
+ function ListCell({ kind, value, fields }) {
70
77
  const items = Array.isArray(value) ? value : [];
71
78
  return /* @__PURE__ */ jsxs("div", { style: cellStyle, className: "wp-body-sm", children: [
72
79
  /* @__PURE__ */ jsxs("span", { className: "wp-caption", style: { color: "var(--wp-text-muted)" }, children: [
73
80
  items.length,
74
81
  "\uAC74"
75
82
  ] }),
76
- items.map((item, index) => /* @__PURE__ */ jsx("div", { style: { marginTop: "var(--wp-space-1)" }, children: summarizeItem(kind, item) || "(\uB0B4\uC6A9 \uC5C6\uC74C)" }, index))
83
+ items.map((item, index) => /* @__PURE__ */ jsx("div", { style: { marginTop: "var(--wp-space-1)" }, children: summarizeItem(kind, item, fields) || "(\uB0B4\uC6A9 \uC5C6\uC74C)" }, index))
77
84
  ] });
78
85
  }
79
86
  function ProfileEditDiffCardImpl({
80
87
  scalarLabels = {},
81
88
  relationLabels = {},
89
+ relationSummaryFields = {},
82
90
  sourceEndpoint
83
91
  }) {
84
92
  const adapter = useSduiAdapter();
85
93
  const api = hostFetchOf(adapter) ?? defaultAdapter.fetch;
86
94
  const [detail, setDetail] = useState(null);
87
- const [loading, setLoading] = useState(false);
95
+ const [loading, setLoading] = useState(() => Boolean(sourceEndpoint));
88
96
  const [failed, setFailed] = useState(false);
89
97
  useEffect(() => {
90
98
  if (!sourceEndpoint) return;
@@ -139,7 +147,7 @@ function ProfileEditDiffCardImpl({
139
147
  const label = scalarLabels[key] ?? relationLabels[key] ?? key;
140
148
  return /* @__PURE__ */ jsxs("div", { className: "wp-row", style: { alignItems: "flex-start", gap: "var(--wp-space-2)" }, children: [
141
149
  /* @__PURE__ */ jsx("span", { className: "wp-label", style: { flex: "none", width: 120, paddingTop: "var(--wp-space-2)" }, children: label }),
142
- isList ? /* @__PURE__ */ jsx(ListCell, { kind: key, value: current[key] }) : /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: cellStyle, children: text(current[key]) || "-" }),
150
+ isList ? /* @__PURE__ */ jsx(ListCell, { kind: key, value: current[key], fields: relationSummaryFields[key] }) : /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: cellStyle, children: text(current[key]) || "-" }),
143
151
  /* @__PURE__ */ jsx(
144
152
  "span",
145
153
  {
@@ -154,7 +162,7 @@ function ProfileEditDiffCardImpl({
154
162
  children: /* @__PURE__ */ jsx(Icon, { name: "arrow-right", size: 14 })
155
163
  }
156
164
  ),
157
- isList ? /* @__PURE__ */ jsx(ListCell, { kind: key, value: payload[key] }) : /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: { ...cellStyle, background: "var(--wp-surface-brand-tint)" }, children: text(payload[key]) || "-" })
165
+ isList ? /* @__PURE__ */ jsx(ListCell, { kind: key, value: payload[key], fields: relationSummaryFields[key] }) : /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: { ...cellStyle, background: "var(--wp-surface-brand-tint)" }, children: text(payload[key]) || "-" })
158
166
  ] }, key);
159
167
  })
160
168
  ] });
@@ -1,4 +1,4 @@
1
- import { Spinner, EmptyState } from './chunk-TUOG7EEI.js';
1
+ import { Spinner, EmptyState } from './chunk-RNOUXUBO.js';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
4
 
@@ -1,5 +1,5 @@
1
- import { Badge, Checkbox, IconButton, Icon } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Badge, Checkbox, IconButton, Icon } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -0,0 +1,41 @@
1
+ // src/widgets/job-position-fetch.ts
2
+ var LIMIT = 500;
3
+ var MAX_PAGES = 10;
4
+ var num = (a, b) => {
5
+ if (typeof a === "number" && Number.isFinite(a)) return a;
6
+ if (typeof b === "number" && Number.isFinite(b)) return b;
7
+ return 0;
8
+ };
9
+ function parseJobPositionRow(raw) {
10
+ if (typeof raw !== "object" || raw === null) return null;
11
+ const row = raw;
12
+ if (typeof row.name !== "string") return null;
13
+ const active = row.is_active ?? row.isActive;
14
+ return {
15
+ ...typeof row.id === "string" ? { id: row.id } : null,
16
+ name: row.name,
17
+ rankOrder: num(row.rank_order, row.rankOrder),
18
+ sortOrder: num(row.sort_order, row.sortOrder),
19
+ // is_active 결측을 false로 단정하면 목록이 전부 사라져 보인다 — true 폴백(v2와 동일).
20
+ isActive: typeof active === "boolean" ? active : true
21
+ };
22
+ }
23
+ async function fetchAllJobPositions(api, endpoint) {
24
+ const rows = [];
25
+ for (let page = 0; page < MAX_PAGES; page += 1) {
26
+ const sep = endpoint.includes("?") ? "&" : "?";
27
+ const res = await api(`${endpoint}${sep}limit=${LIMIT}&offset=${page * LIMIT}`, {
28
+ method: "GET"
29
+ });
30
+ const data = Array.isArray(res?.data) ? res.data : [];
31
+ for (const raw of data) {
32
+ const parsed = parseJobPositionRow(raw);
33
+ if (parsed) rows.push(parsed);
34
+ }
35
+ const total = typeof res?.total === "number" ? res.total : rows.length;
36
+ if (data.length === 0 || rows.length >= total) break;
37
+ }
38
+ return rows;
39
+ }
40
+
41
+ export { fetchAllJobPositions, parseJobPositionRow };
@@ -1,5 +1,5 @@
1
- import { Textarea, Icon } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { Textarea, Icon } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import { useState, useRef, useId, useEffect, useCallback } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,5 +1,5 @@
1
- import { SegmentedControl, IconButton, Button, Tag, EmptyState, Icon, Badge } from './chunk-TUOG7EEI.js';
2
- import { boxed } from './chunk-YQOMSWW5.js';
1
+ import { SegmentedControl, IconButton, Button, Tag, EmptyState, Icon, Badge } from './chunk-RNOUXUBO.js';
2
+ import { boxed } from './chunk-5WW4MBRO.js';
3
3
  import { useState, useMemo } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -100,7 +100,12 @@ var packWeek = (events, weekStart) => {
100
100
  }
101
101
  return { segments, lanes };
102
102
  };
103
- var statusTone = (status) => {
103
+ var STATUS_TONES = ["neutral", "info", "success", "warning", "danger"];
104
+ var statusToneOf = (status, overrides) => {
105
+ const injected = overrides?.[status];
106
+ if (typeof injected === "string" && STATUS_TONES.includes(injected)) {
107
+ return injected;
108
+ }
104
109
  if (/완료|반납|승인됨/.test(status)) return "success";
105
110
  if (/진행|수리중|대기/.test(status)) return "warning";
106
111
  if (/연체|반려|취소|분실/.test(status)) return "danger";
@@ -201,6 +206,7 @@ var EventCalendarImpl = ({
201
206
  date,
202
207
  onDateChange,
203
208
  types,
209
+ statusTones,
204
210
  onEventClick,
205
211
  selectedId
206
212
  }) => {
@@ -416,7 +422,7 @@ var EventCalendarImpl = ({
416
422
  }
417
423
  ),
418
424
  event.typeLabel && /* @__PURE__ */ jsx(Tag, { color: event.color, className: "wp-caption", children: event.typeLabel }),
419
- event.status && /* @__PURE__ */ jsx(Badge, { tone: statusTone(event.status), children: event.status }),
425
+ event.status && /* @__PURE__ */ jsx(Badge, { tone: statusToneOf(event.status, statusTones), children: event.status }),
420
426
  /* @__PURE__ */ jsx("span", { style: { flex: "none", fontSize: 13, color: "var(--wp-text-muted)" }, children: fmtRange(event.dayStart, event.dayEnd) })
421
427
  ]
422
428
  },
@@ -766,4 +772,4 @@ var EventCalendarImpl = ({
766
772
  };
767
773
  var EventCalendar = boxed(EventCalendarImpl, "EventCalendar");
768
774
 
769
- export { EventCalendar };
775
+ export { EventCalendar, statusToneOf };