@wipco/sdui 0.26.0 → 0.27.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.
@@ -0,0 +1,359 @@
1
+ import { TagMultiSelect } from './chunk-QDEZXA34.js';
2
+ import { ApprovalLineEditor, stepTypeIcon, stepLabel } from './chunk-SAPJVLZM.js';
3
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DVOPZFQS.js';
4
+ import { Card, Icon, Input, IconButton, Field, Button, Badge } from './chunk-WIDDHR7L.js';
5
+ import { boxed } from './chunk-SAN4IX6N.js';
6
+ import React, { useState, useEffect } from 'react';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ function facetsFrom(rows) {
10
+ const titles = /* @__PURE__ */ new Set();
11
+ const grades = /* @__PURE__ */ new Set();
12
+ if (Array.isArray(rows)) {
13
+ for (const row of rows) {
14
+ if (typeof row !== "object" || row === null) continue;
15
+ const title = row.job_title;
16
+ const grade = row.grade;
17
+ if (typeof title === "string" && title.trim()) titles.add(title.trim());
18
+ if (typeof grade === "string" && grade.trim()) grades.add(grade.trim());
19
+ }
20
+ }
21
+ const ko = (a, b) => a.localeCompare(b, "ko");
22
+ return { titles: [...titles].sort(ko), grades: [...grades].sort(ko) };
23
+ }
24
+ var uidCounter = 0;
25
+ var uid = (prefix) => `${prefix}-${Date.now()}-${++uidCounter}`;
26
+ var describeScope = (override) => {
27
+ const identityParts = [];
28
+ if (override.jobTitles.length > 0) identityParts.push(`\uC9C1\uCC45 ${override.jobTitles.join(", ")}`);
29
+ if (override.grades.length > 0) identityParts.push(`\uC9C1\uAE09 ${override.grades.join(", ")}`);
30
+ const legacyEntities = override.entityTypes ?? [];
31
+ const identityPart = identityParts.length > 0 ? identityParts.join(" \uB610\uB294 ") : legacyEntities.length > 0 ? "\uC9C1\uCC45\xB7\uC9C1\uAE09 \uC870\uAC74 \uC5C6\uC74C (\uC804 \uC9C1\uC6D0 \uC801\uC6A9)" : "\uC9C1\uCC45\xB7\uC9C1\uAE09 \uC870\uAC74 \uC5C6\uC74C (\uB9E4\uCE6D\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)";
32
+ if (legacyEntities.length === 0) return identityPart;
33
+ return `${identityPart} \xB7 \uC5C5\uBB34\uC720\uD615 ${legacyEntities.join(", ")} \uD55C\uC815`;
34
+ };
35
+ var withSelected = (options, selected) => [...options, ...selected.filter((value) => !options.includes(value))].map((value) => ({
36
+ value,
37
+ label: value
38
+ }));
39
+ var orderCircleStyle = {
40
+ display: "inline-flex",
41
+ alignItems: "center",
42
+ justifyContent: "center",
43
+ width: 28,
44
+ height: 28,
45
+ flex: "none",
46
+ borderRadius: "var(--wp-radius-pill)",
47
+ background: "var(--wp-surface-brand-tint)",
48
+ color: "var(--wp-text-brand)"
49
+ };
50
+ function StepSummary({ steps }) {
51
+ if (steps.length === 0) {
52
+ return /* @__PURE__ */ jsx("span", { className: "wp-caption", children: "\uACB0\uC7AC \uB2E8\uACC4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uB9E4\uCE6D \uC2DC \uC790\uB3D9 \uC2B9\uC778\uB429\uB2C8\uB2E4." });
53
+ }
54
+ return /* @__PURE__ */ jsx("div", { className: "wp-row", style: { flexWrap: "wrap", gap: "var(--wp-space-1)" }, children: steps.map((step, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
55
+ /* @__PURE__ */ jsxs(Badge, { tone: "neutral", children: [
56
+ /* @__PURE__ */ jsx(
57
+ Icon,
58
+ {
59
+ name: stepTypeIcon(step.type),
60
+ size: 12,
61
+ style: { marginRight: "var(--wp-space-1)" }
62
+ }
63
+ ),
64
+ stepLabel(step)
65
+ ] }),
66
+ index < steps.length - 1 ? /* @__PURE__ */ jsx(Icon, { name: "arrow-right", size: 12, style: { color: "var(--wp-text-muted)", flex: "none" } }) : null
67
+ ] }, step.id)) });
68
+ }
69
+ function ApprovalOverridesEditorImpl({
70
+ value,
71
+ onChange,
72
+ jobTitleOptions,
73
+ gradeOptions,
74
+ facetsEndpoint,
75
+ searchEndpoint,
76
+ groupSearchEndpoint,
77
+ searchOptions,
78
+ disabled = false
79
+ }) {
80
+ const adapter = useSduiAdapter();
81
+ const api = hostFetchOf(adapter) ?? defaultAdapter.fetch;
82
+ const overrides = value ?? [];
83
+ const [fetchedFacets, setFetchedFacets] = useState(
84
+ null
85
+ );
86
+ useEffect(() => {
87
+ if (!facetsEndpoint) return;
88
+ let alive = true;
89
+ api(facetsEndpoint, { method: "GET" }).then((response) => {
90
+ if (!alive) return;
91
+ const body = response;
92
+ const rows = Array.isArray(response) ? response : body?.data ?? [];
93
+ setFetchedFacets(facetsFrom(rows));
94
+ }).catch(() => {
95
+ });
96
+ return () => {
97
+ alive = false;
98
+ };
99
+ }, [facetsEndpoint]);
100
+ const mergedTitles = [
101
+ ...jobTitleOptions ?? [],
102
+ ...(fetchedFacets?.titles ?? []).filter((t) => !(jobTitleOptions ?? []).includes(t))
103
+ ];
104
+ const mergedGrades = [
105
+ ...gradeOptions ?? [],
106
+ ...(fetchedFacets?.grades ?? []).filter((g) => !(gradeOptions ?? []).includes(g))
107
+ ];
108
+ const updateOverride = (id, patch) => {
109
+ onChange?.(
110
+ overrides.map((override) => override.id === id ? { ...override, ...patch } : override)
111
+ );
112
+ };
113
+ const addOverride = () => {
114
+ onChange?.([...overrides, { id: uid("line"), name: "", jobTitles: [], grades: [], steps: [] }]);
115
+ };
116
+ const removeOverride = (id) => {
117
+ onChange?.(overrides.filter((override) => override.id !== id));
118
+ };
119
+ const duplicateOverride = (index) => {
120
+ const source = overrides[index];
121
+ if (!source) return;
122
+ const copy = {
123
+ ...source,
124
+ id: uid("line"),
125
+ name: source.name.trim() ? `${source.name} (\uBCF5\uC0AC\uBCF8)` : "",
126
+ jobTitles: [...source.jobTitles],
127
+ grades: [...source.grades],
128
+ steps: source.steps.map((step) => ({
129
+ ...step,
130
+ id: uid("step"),
131
+ customApprovers: step.customApprovers?.map((approver) => ({ ...approver }))
132
+ }))
133
+ };
134
+ const next = [...overrides];
135
+ next.splice(index + 1, 0, copy);
136
+ onChange?.(next);
137
+ };
138
+ const moveOverride = (index, direction) => {
139
+ const target = index + direction;
140
+ if (target < 0 || target >= overrides.length) return;
141
+ const next = [...overrides];
142
+ [next[index], next[target]] = [next[target], next[index]];
143
+ onChange?.(next);
144
+ };
145
+ const [dragIndex, setDragIndex] = useState(null);
146
+ const [dropIndex, setDropIndex] = useState(null);
147
+ const finishDrag = () => {
148
+ if (dragIndex !== null && dropIndex !== null && dragIndex !== dropIndex) {
149
+ const next = [...overrides];
150
+ const [moved] = next.splice(dragIndex, 1);
151
+ next.splice(dropIndex, 0, moved);
152
+ onChange?.(next);
153
+ }
154
+ setDragIndex(null);
155
+ setDropIndex(null);
156
+ };
157
+ return /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-3)" }, children: [
158
+ /* @__PURE__ */ jsx("span", { className: "wp-caption", children: "\uC694\uCCAD\uC790\uC758 \uC9C1\uCC45\xB7\uC9C1\uAE09\uC5D0 \uB530\uB77C \uAE30\uBCF8 \uACB0\uC7AC\uC120 \uB300\uC2E0 \uC801\uC6A9\uD560 \uACB0\uC7AC\uB77C\uC778\uC785\uB2C8\uB2E4. \uC9C1\uCC45\uACFC \uC9C1\uAE09 \uC911 \uD558\uB098\uB9CC \uB9DE\uC544\uB3C4 \uC801\uC6A9\uB418\uBA70, \uC704\uC5D0\uC11C\uBD80\uD130 \uBA3C\uC800 \uB9E4\uCE6D\uB418\uB294 \uACB0\uC7AC\uB77C\uC778\uC774 \uC6B0\uC120 \uC801\uC6A9\uB429\uB2C8\uB2E4." }),
159
+ overrides.length === 0 ? /* @__PURE__ */ jsx(
160
+ "div",
161
+ {
162
+ className: "wp-body-sm",
163
+ style: {
164
+ border: "var(--wp-border-w) dashed var(--wp-border-default)",
165
+ borderRadius: "var(--wp-radius-lg)",
166
+ padding: "var(--wp-space-4)",
167
+ textAlign: "center",
168
+ color: "var(--wp-text-muted)"
169
+ },
170
+ children: "\uC124\uC815\uB41C \uC9C1\uAE09/\uC9C1\uCC45\uBCC4 \uACB0\uC7AC\uB77C\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
171
+ }
172
+ ) : overrides.map((override, index) => /* @__PURE__ */ jsx(
173
+ Card,
174
+ {
175
+ padding: "tight",
176
+ style: {
177
+ // 드롭 목적지 표시 — 삽입될 자리의 카드 위에 브랜드 선을 긋는다.
178
+ boxShadow: dropIndex === index && dragIndex !== null && dragIndex !== index ? "0 calc(-1 * var(--wp-space-1)) 0 0 var(--wp-border-brand)" : void 0,
179
+ opacity: dragIndex === index ? 0.5 : void 0
180
+ },
181
+ onDragOver: (event) => {
182
+ if (dragIndex === null) return;
183
+ event.preventDefault();
184
+ if (dropIndex !== index) setDropIndex(index);
185
+ },
186
+ onDrop: (event) => {
187
+ event.preventDefault();
188
+ finishDrag();
189
+ },
190
+ children: /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-3)" }, children: [
191
+ /* @__PURE__ */ jsxs("div", { className: "wp-row", style: { alignItems: "center" }, children: [
192
+ /* @__PURE__ */ jsx(
193
+ "span",
194
+ {
195
+ draggable: !disabled,
196
+ "aria-hidden": true,
197
+ title: "\uB04C\uC5B4\uC11C \uC6B0\uC120\uC21C\uC704 \uBCC0\uACBD",
198
+ style: {
199
+ cursor: disabled ? "default" : "grab",
200
+ color: "var(--wp-text-muted)",
201
+ display: "inline-flex",
202
+ flex: "none",
203
+ touchAction: "none"
204
+ },
205
+ onDragStart: (event) => {
206
+ event.dataTransfer.setData("text/plain", String(index));
207
+ event.dataTransfer.effectAllowed = "move";
208
+ setDragIndex(index);
209
+ },
210
+ onDragEnd: () => {
211
+ setDragIndex(null);
212
+ setDropIndex(null);
213
+ },
214
+ children: /* @__PURE__ */ jsx(Icon, { name: "grip-vertical", size: 16 })
215
+ }
216
+ ),
217
+ /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: orderCircleStyle, title: "\uB9E4\uCE6D \uC6B0\uC120\uC21C\uC704", children: index + 1 }),
218
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsx(
219
+ Input,
220
+ {
221
+ "aria-label": "\uACB0\uC7AC\uB77C\uC778 \uC774\uB984",
222
+ value: override.name,
223
+ disabled,
224
+ placeholder: "\uC608: \uD300\uC7A5 \uC2B9\uC778 \uC0DD\uB7B5 \uB77C\uC778",
225
+ onChange: (event) => updateOverride(override.id, { name: event.target.value })
226
+ }
227
+ ) }),
228
+ /* @__PURE__ */ jsxs("div", { className: "wp-row", style: { flex: "none", gap: "var(--wp-space-1)" }, children: [
229
+ /* @__PURE__ */ jsx(
230
+ IconButton,
231
+ {
232
+ type: "button",
233
+ icon: "chevron-up",
234
+ label: "\uC6B0\uC120\uC21C\uC704 \uC62C\uB9AC\uAE30",
235
+ size: "sm",
236
+ disabled: disabled || index === 0,
237
+ onClick: () => moveOverride(index, -1)
238
+ }
239
+ ),
240
+ /* @__PURE__ */ jsx(
241
+ IconButton,
242
+ {
243
+ type: "button",
244
+ icon: "chevron-down",
245
+ label: "\uC6B0\uC120\uC21C\uC704 \uB0B4\uB9AC\uAE30",
246
+ size: "sm",
247
+ disabled: disabled || index === overrides.length - 1,
248
+ onClick: () => moveOverride(index, 1)
249
+ }
250
+ ),
251
+ /* @__PURE__ */ jsx(
252
+ IconButton,
253
+ {
254
+ type: "button",
255
+ icon: "copy",
256
+ label: "\uACB0\uC7AC\uB77C\uC778 \uBCF5\uC81C",
257
+ size: "sm",
258
+ disabled,
259
+ onClick: () => duplicateOverride(index)
260
+ }
261
+ ),
262
+ /* @__PURE__ */ jsx(
263
+ IconButton,
264
+ {
265
+ type: "button",
266
+ icon: "trash-2",
267
+ label: "\uACB0\uC7AC\uB77C\uC778 \uC0AD\uC81C",
268
+ variant: "danger",
269
+ size: "sm",
270
+ disabled,
271
+ onClick: () => removeOverride(override.id)
272
+ }
273
+ )
274
+ ] })
275
+ ] }),
276
+ /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-1)" }, children: [
277
+ /* @__PURE__ */ jsx("span", { className: "wp-caption", children: describeScope(override) }),
278
+ /* @__PURE__ */ jsx(StepSummary, { steps: override.steps })
279
+ ] }),
280
+ /* @__PURE__ */ jsx(
281
+ Field,
282
+ {
283
+ label: "\uC801\uC6A9 \uC9C1\uCC45",
284
+ hint: "\uC120\uD0DD\uD55C \uC9C1\uCC45 \uC911 \uD558\uB098\uB77C\uB3C4 \uC77C\uCE58\uD558\uBA74 \uC774 \uACB0\uC7AC\uB77C\uC778\uC774 \uC801\uC6A9\uB429\uB2C8\uB2E4.",
285
+ children: /* @__PURE__ */ jsx(
286
+ TagMultiSelect,
287
+ {
288
+ options: withSelected(mergedTitles, override.jobTitles),
289
+ values: override.jobTitles,
290
+ disabled,
291
+ emptyText: "\uC120\uD0DD \uAC00\uB2A5\uD55C \uC9C1\uCC45\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
292
+ onChange: (jobTitles) => updateOverride(override.id, { jobTitles })
293
+ }
294
+ )
295
+ }
296
+ ),
297
+ /* @__PURE__ */ jsx(
298
+ Field,
299
+ {
300
+ label: "\uC801\uC6A9 \uC9C1\uAE09",
301
+ hint: "\uC9C1\uCC45 \uC870\uAC74\uACFC OR \u2014 \uC9C1\uAE09\uB9CC \uC77C\uCE58\uD574\uB3C4 \uC801\uC6A9\uB429\uB2C8\uB2E4.",
302
+ children: /* @__PURE__ */ jsx(
303
+ TagMultiSelect,
304
+ {
305
+ options: withSelected(mergedGrades, override.grades),
306
+ values: override.grades,
307
+ disabled,
308
+ emptyText: "\uC120\uD0DD \uAC00\uB2A5\uD55C \uC9C1\uAE09\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
309
+ onChange: (grades) => updateOverride(override.id, { grades })
310
+ }
311
+ )
312
+ }
313
+ ),
314
+ /* @__PURE__ */ jsxs(
315
+ "div",
316
+ {
317
+ className: "wp-col",
318
+ style: {
319
+ gap: "var(--wp-space-2)",
320
+ borderTop: "var(--wp-border-w) solid var(--wp-divider)",
321
+ paddingTop: "var(--wp-space-3)"
322
+ },
323
+ children: [
324
+ /* @__PURE__ */ jsx("span", { className: "wp-label", children: "\uC774 \uACB0\uC7AC\uB77C\uC778\uC758 \uACB0\uC7AC \uB2E8\uACC4" }),
325
+ /* @__PURE__ */ jsx(
326
+ ApprovalLineEditor,
327
+ {
328
+ value: override.steps,
329
+ onChange: (steps) => updateOverride(override.id, { steps }),
330
+ searchEndpoint,
331
+ groupSearchEndpoint,
332
+ options: searchOptions,
333
+ disabled
334
+ }
335
+ )
336
+ ]
337
+ }
338
+ )
339
+ ] })
340
+ },
341
+ override.id
342
+ )),
343
+ /* @__PURE__ */ jsx("div", { className: "wp-row", children: /* @__PURE__ */ jsx(
344
+ Button,
345
+ {
346
+ type: "button",
347
+ variant: "secondary",
348
+ icon: "plus",
349
+ disabled,
350
+ title: disabled ? "\uBE44\uD65C\uC131\uD654\uB428" : void 0,
351
+ onClick: addOverride,
352
+ children: "\uACB0\uC7AC\uB77C\uC778 \uCD94\uAC00"
353
+ }
354
+ ) })
355
+ ] });
356
+ }
357
+ var ApprovalOverridesEditor = boxed(ApprovalOverridesEditorImpl, "ApprovalOverridesEditor");
358
+
359
+ export { ApprovalOverridesEditor };
package/dist/meta.d.ts CHANGED
@@ -425,6 +425,8 @@ declare const richComponentMeta: {
425
425
 
426
426
  declare const ApprovalStepperMeta: SduiComponentMeta;
427
427
  declare const RankBoardMeta: SduiComponentMeta;
428
+ declare const ApprovalLineEditorMeta: SduiComponentMeta;
429
+ declare const ApprovalOverridesEditorMeta: SduiComponentMeta;
428
430
  declare const AutocompleteFieldMeta: SduiComponentMeta;
429
431
  declare const InlineEditMeta: SduiComponentMeta;
430
432
  declare const JsonFieldMeta: SduiComponentMeta;
@@ -458,6 +460,8 @@ declare const StripMeta: SduiComponentMeta;
458
460
  * 프롭을 못 그린다.
459
461
  */
460
462
  declare const widgetComponentMeta: {
463
+ ApprovalLineEditor: SduiComponentMeta;
464
+ ApprovalOverridesEditor: SduiComponentMeta;
461
465
  ApprovalStepper: SduiComponentMeta;
462
466
  RankBoard: SduiComponentMeta;
463
467
  AutocompleteField: SduiComponentMeta;
@@ -592,4 +596,4 @@ declare function paletteGroups(catalog?: Record<string, SduiComponentMeta>): Arr
592
596
  items: SduiComponentMeta[];
593
597
  }>;
594
598
 
595
- export { ActionsMeta, AlertMeta, ApprovalStepperMeta, AutocompleteFieldMeta, AvatarMeta, BadgeMeta, BreadcrumbMeta, ButtonMeta, CanMeta, CardMeta, ChartMeta, CheckboxMeta, ColMeta, ColumnBoardMeta, DatePickerMeta, DateRangePickerMeta, DialogMeta, DiffViewMeta, DisclosureMeta, DividerMeta, EmptyStateMeta, EventCalendarMeta, FieldMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PrintSheetMeta, ProgressBarMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RowMeta, SDUI_CATEGORIES, SELF_AXIS_PROPS, type SduiCategory, type SduiChildrenMeta, type SduiComponentMeta, type SduiDomMeta, type SduiEnumOption, type SduiPropGroup, type SduiPropKind, type SduiPropMeta, type SduiResizeMeta, type SduiSlotMeta, SearchResultsMeta, SearchableMultiPickerMeta, SectionMeta, SecureScreenMeta, SegmentedControlMeta, SelectMeta, ShowMeta, SidebarNavMeta, SortHandleMeta, SortableListMeta, SpacerMeta, SpinnerMeta, StackedMeterMeta, StatCardMeta, StripMeta, SummaryTableMeta, SwitchMeta, TableMeta, TabsMeta, TagMeta, TagMultiSelectMeta, TextMeta, TextareaMeta, TimePickerMeta, TimelineMeta, ToastMeta, TooltipMeta, TreePickerMeta, TreeViewMeta, allComponentMeta, builtinComponentMeta, coreComponentMeta, dsComponentMeta, getComponentMeta, paletteGroups, richComponentMeta, slotNamesOf, widgetComponentMeta, withSelfAxis, withSelfAxisAll };
599
+ export { ActionsMeta, AlertMeta, ApprovalLineEditorMeta, ApprovalOverridesEditorMeta, ApprovalStepperMeta, AutocompleteFieldMeta, AvatarMeta, BadgeMeta, BreadcrumbMeta, ButtonMeta, CanMeta, CardMeta, ChartMeta, CheckboxMeta, ColMeta, ColumnBoardMeta, DatePickerMeta, DateRangePickerMeta, DialogMeta, DiffViewMeta, DisclosureMeta, DividerMeta, EmptyStateMeta, EventCalendarMeta, FieldMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PrintSheetMeta, ProgressBarMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RowMeta, SDUI_CATEGORIES, SELF_AXIS_PROPS, type SduiCategory, type SduiChildrenMeta, type SduiComponentMeta, type SduiDomMeta, type SduiEnumOption, type SduiPropGroup, type SduiPropKind, type SduiPropMeta, type SduiResizeMeta, type SduiSlotMeta, SearchResultsMeta, SearchableMultiPickerMeta, SectionMeta, SecureScreenMeta, SegmentedControlMeta, SelectMeta, ShowMeta, SidebarNavMeta, SortHandleMeta, SortableListMeta, SpacerMeta, SpinnerMeta, StackedMeterMeta, StatCardMeta, StripMeta, SummaryTableMeta, SwitchMeta, TableMeta, TabsMeta, TagMeta, TagMultiSelectMeta, TextMeta, TextareaMeta, TimePickerMeta, TimelineMeta, ToastMeta, TooltipMeta, TreePickerMeta, TreeViewMeta, allComponentMeta, builtinComponentMeta, coreComponentMeta, dsComponentMeta, getComponentMeta, paletteGroups, richComponentMeta, slotNamesOf, widgetComponentMeta, withSelfAxis, withSelfAxisAll };
package/dist/meta.js CHANGED
@@ -3416,6 +3416,256 @@ var RankBoardMeta = {
3416
3416
  },
3417
3417
  resize: { kind: "none" }
3418
3418
  };
3419
+ var customApproverItem = [
3420
+ { name: "id", kind: "string", label: "\uC2DD\uBCC4\uC790", required: true },
3421
+ { name: "name", kind: "string", label: "\uD45C\uC2DC\uBA85", required: true },
3422
+ {
3423
+ name: "type",
3424
+ kind: "enum",
3425
+ label: "\uC885\uB958",
3426
+ required: true,
3427
+ options: [
3428
+ { value: "user", label: "\uC9C1\uC6D0" },
3429
+ { value: "group", label: "\uBD80\uC11C\xB7\uADF8\uB8F9" }
3430
+ ]
3431
+ }
3432
+ ];
3433
+ var approvalLineStepItem = [
3434
+ { name: "id", kind: "string", label: "\uC2A4\uD15D id", required: true },
3435
+ {
3436
+ name: "type",
3437
+ kind: "enum",
3438
+ label: "\uC720\uD615",
3439
+ description: "\uC5C6\uC73C\uBA74 \uBC31\uC5D4\uB4DC\uAC00 custom\uC73C\uB85C \uCDE8\uAE09\uD55C\uB2E4(\uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC774\uAD00 \uC2A4\uD15D \uD638\uD658).",
3440
+ options: [
3441
+ { value: "team_leader", label: "\uD300\uC7A5" },
3442
+ { value: "division_head", label: "\uBCF8\uBD80\uC7A5\xB7\uC2E4\uC7A5" },
3443
+ { value: "custom", label: "\uC0AC\uC6A9\uC790 \uC9C0\uC815" }
3444
+ ]
3445
+ },
3446
+ {
3447
+ name: "name",
3448
+ kind: "string",
3449
+ label: "\uD45C\uC2DC\uBA85",
3450
+ description: "\uBC31\uC5D4\uB4DC\uAC00 \uACB0\uC7AC \uB2E8\uACC4 \uC124\uBA85\uC73C\uB85C \uADF8\uB300\uB85C \uC4F4\uB2E4. \uC5C6\uC73C\uBA74 type\uC73C\uB85C \uB300\uC2E0\uD55C\uB2E4."
3451
+ },
3452
+ {
3453
+ name: "order",
3454
+ kind: "number",
3455
+ label: "\uC21C\uBC88",
3456
+ required: true,
3457
+ description: "1\uBD80\uD130. \uD3B8\uC9D1\uAE30\uAC00 \uAC12\uC744 \uBC14\uAFC0 \uB54C\uB9C8\uB2E4 1..n\uC73C\uB85C \uC815\uADDC\uD654\uD55C\uB2E4."
3458
+ },
3459
+ {
3460
+ name: "customApprovers",
3461
+ kind: "array",
3462
+ label: "\uC9C0\uC815 \uACB0\uC7AC\uC790",
3463
+ description: "type\uC774 custom\uC77C \uB54C\uB9CC \uC758\uBBF8\uAC00 \uC788\uB2E4. \uC2A4\uD399 \uB9AC\uD130\uB7F4\uC5D0\uB294 \uD55C \uBC88\uB3C4 \uB4F1\uC7A5\uD558\uC9C0 \uC54A\uC558\uACE0, \uD615\uD0DC\uC758 \uADFC\uAC70\uB294 \uC800\uC7A5 JSON \uACC4\uC57D\uC774\uB2E4.",
3464
+ item: customApproverItem
3465
+ }
3466
+ ];
3467
+ var ApprovalLineEditorMeta = {
3468
+ name: "ApprovalLineEditor",
3469
+ label: "\uAE30\uBCF8 \uACB0\uC7AC\uC120 \uD3B8\uC9D1\uAE30",
3470
+ category: "input",
3471
+ icon: "route",
3472
+ description: "\uACB0\uC7AC \uB2E8\uACC4\uB97C \uCD94\uAC00\xB7\uC0AD\uC81C\xB7\uC7AC\uC815\uB82C\uD558\uB294 controlled \uD3B8\uC9D1\uAE30. \uC800\uC7A5 JSON(approval_forms.default_steps)\uC758 \uD0A4\uB97C \uADF8\uB300\uB85C \uC4F4\uB2E4.",
3473
+ props: [
3474
+ {
3475
+ name: "value",
3476
+ kind: "array",
3477
+ label: "\uACB0\uC7AC\uC120",
3478
+ required: true,
3479
+ bindable: true,
3480
+ description: "\uC2A4\uD15D \uBC30\uC5F4. \uC2E4\uCE21 4/4\uAC00 \uC804\uBD80 $state \uBC14\uC778\uB529\uC774\uB2E4.",
3481
+ group: "data",
3482
+ item: approvalLineStepItem
3483
+ },
3484
+ {
3485
+ name: "onChange",
3486
+ kind: "action",
3487
+ label: "\uACB0\uC7AC\uC120 \uBCC0\uACBD",
3488
+ required: true,
3489
+ eventArg: "order\uB97C 1..n\uC73C\uB85C \uC815\uADDC\uD654\uD55C \uC2A4\uD15D \uBC30\uC5F4",
3490
+ group: "behavior"
3491
+ },
3492
+ // 실측 4/4 — 전부 setState + fromEvent
3493
+ {
3494
+ name: "options",
3495
+ kind: "array",
3496
+ label: "\uACB0\uC7AC\uC790 \uD6C4\uBCF4",
3497
+ bindable: true,
3498
+ description: "\uAC80\uC0C9 \uACBD\uB85C\uAC00 \uC5C6\uC744 \uB54C \uB85C\uCEEC \uAC80\uC0C9 \uB300\uC0C1.",
3499
+ group: "data",
3500
+ item: customApproverItem
3501
+ },
3502
+ // 실측 4/4 (원소 28개)
3503
+ {
3504
+ name: "searchEndpoint",
3505
+ kind: "string",
3506
+ label: "\uACB0\uC7AC\uC790 \uAC80\uC0C9 \uACBD\uB85C",
3507
+ description: "GET {\uACBD\uB85C}?q=\u2026 \uB85C \uC9C1\uC6D0\xB7\uBD80\uC11C\uB97C \uD1B5\uD569 \uAC80\uC0C9\uD55C\uB2E4. \uC5C6\uC73C\uBA74 options\uB85C \uB85C\uCEEC \uAC80\uC0C9\uB9CC \uD55C\uB2E4.",
3508
+ group: "data",
3509
+ advanced: true
3510
+ },
3511
+ // 실측 0/4 — 구현에는 있다
3512
+ {
3513
+ name: "groupSearchEndpoint",
3514
+ kind: "string",
3515
+ label: "\uBD80\uC11C \uAC80\uC0C9 \uACBD\uB85C",
3516
+ description: "\uC8FC\uBA74 searchEndpoint\uC640 \uBCD1\uB82C\uB85C \uC870\uD68C\uD574 \uACB0\uACFC\uB97C \uD569\uCE5C\uB2E4(v2\uC758 /employees + /departments \uBCD1\uB82C \uAC80\uC0C9\uACFC \uAC19\uC740 \uBAA8\uC591). \uC774 \uACBD\uB85C\uC758 \uC751\uB2F5\uC740 \uC804\uBD80 \uADF8\uB8F9\uC73C\uB85C \uCDE8\uAE09\uD55C\uB2E4.",
3517
+ group: "data",
3518
+ advanced: true
3519
+ },
3520
+ { name: "disabled", kind: "boolean", label: "\uBE44\uD65C\uC131", default: false, bindable: true, group: "behavior" }
3521
+ // 실측 0/4
3522
+ ],
3523
+ children: { accepts: false },
3524
+ dom: { element: "div" },
3525
+ // 실측: 루트가 <div class="wp-col">
3526
+ // 프리셋에 searchEndpoint를 넣지 않는다 — 소스가 `if (searchEndpoint)`로 감싸고 있어
3527
+ // 미지정이면 fetch 경로 자체가 없다. default에 2스텝을 넣어 빈 상태가 아닌 모습을 보여 준다.
3528
+ preset: {
3529
+ component: "ApprovalLineEditor",
3530
+ props: {
3531
+ value: {
3532
+ $state: "approvalLineForm.defaultSteps",
3533
+ default: [
3534
+ { id: "st1", name: "\uD300\uC7A5", type: "team_leader", order: 1 },
3535
+ { id: "st2", name: "\uBCF8\uBD80\uC7A5", type: "division_head", order: 2 }
3536
+ ]
3537
+ },
3538
+ onChange: { action: "setState", key: "approvalLineForm.defaultSteps", fromEvent: true },
3539
+ options: [
3540
+ { id: "u1", name: "\uAE40\uB3C4\uD604", type: "user" },
3541
+ { id: "u2", name: "\uC774\uC11C\uC5F0", type: "user" },
3542
+ { id: "g1", name: "\uC778\uC0AC\uD300", type: "group" }
3543
+ ]
3544
+ }
3545
+ },
3546
+ resize: { kind: "none" }
3547
+ };
3548
+ var ApprovalOverridesEditorMeta = {
3549
+ name: "ApprovalOverridesEditor",
3550
+ label: "\uACB0\uC7AC\uC120 \uC624\uBC84\uB77C\uC774\uB4DC \uD3B8\uC9D1\uAE30",
3551
+ category: "input",
3552
+ icon: "layers",
3553
+ description: "\uC9C1\uCC45\xB7\uC9C1\uAE09\uBCC4\uB85C \uAE30\uBCF8 \uACB0\uC7AC\uC120 \uB300\uC2E0 \uC801\uC6A9\uD560 \uACB0\uC7AC\uB77C\uC778 \uBAA9\uB85D. \uC9C1\uCC45\uACFC \uC9C1\uAE09\uC740 OR\uC774\uACE0, \uC704\uC5D0\uC11C\uBD80\uD130 \uBA3C\uC800 \uB9DE\uB294 \uAC83\uC774 \uC6B0\uC120\uD55C\uB2E4.",
3554
+ props: [
3555
+ {
3556
+ name: "value",
3557
+ kind: "array",
3558
+ label: "\uC624\uBC84\uB77C\uC774\uB4DC \uBAA9\uB85D",
3559
+ required: true,
3560
+ bindable: true,
3561
+ description: "\uC2E4\uCE21 2/2\uAC00 \uC804\uBD80 $state \uBC14\uC778\uB529\uC774\uACE0 default\uAC00 \uBE48 \uBC30\uC5F4\uC774\uB2E4. \uC6D0\uC18C \uB9AC\uD130\uB7F4\uC740 \uC2A4\uD399\uC5D0 \uD55C \uAC74\uB3C4 \uC5C6\uC5B4 \uD615\uD0DC\uC758 \uADFC\uAC70\uB294 \uC800\uC7A5 JSON \uACC4\uC57D(approval_forms.line_overrides)\uC774\uB2E4.",
3562
+ group: "data",
3563
+ item: [
3564
+ { name: "id", kind: "string", label: "\uC2DD\uBCC4\uC790", required: true },
3565
+ { name: "name", kind: "string", label: "\uADDC\uCE59 \uC774\uB984", required: true },
3566
+ { name: "jobTitles", kind: "array", label: "\uC801\uC6A9 \uC9C1\uCC45", item: [], itemKind: "string" },
3567
+ { name: "grades", kind: "array", label: "\uC801\uC6A9 \uC9C1\uAE09", item: [], itemKind: "string" },
3568
+ {
3569
+ name: "entityTypes",
3570
+ kind: "array",
3571
+ label: "\uC801\uC6A9 \uC5C5\uBB34\uC720\uD615(\uB808\uAC70\uC2DC)",
3572
+ description: '\uB808\uAC70\uC2DC \uC774\uAD00 \uD0A4 \u2014 UI\uAC00 \uD3B8\uC9D1\uD558\uC9C0 \uC54A\uACE0 spread\uB85C \uBCF4\uC874\uB9CC \uD55C\uB2E4. \uB2E4\uB9CC \uC801\uC6A9 \uC870\uAC74\uC774\uB77C, \uC9C1\uCC45\xB7\uC9C1\uAE09\uC774 \uBAA8\uB450 \uBE44\uC5C8\uC744 \uB54C \uC774 \uAC12\uC758 \uC720\uBB34\uB85C "\uC804 \uC9C1\uC6D0 \uC801\uC6A9"\uACFC "\uB9E4\uCE6D \uC548 \uB428"\uC774 \uAC08\uB9B0\uB2E4.',
3573
+ item: [],
3574
+ itemKind: "string",
3575
+ advanced: true
3576
+ },
3577
+ { name: "steps", kind: "array", label: "\uACB0\uC7AC\uC120", item: approvalLineStepItem }
3578
+ ]
3579
+ },
3580
+ {
3581
+ name: "onChange",
3582
+ kind: "action",
3583
+ label: "\uC624\uBC84\uB77C\uC774\uB4DC \uBCC0\uACBD",
3584
+ required: true,
3585
+ eventArg: "\uBCC0\uACBD\uB41C \uC624\uBC84\uB77C\uC774\uB4DC \uBC30\uC5F4(\uC54C \uC218 \uC5C6\uB294 \uD0A4\uB294 \uADF8\uB300\uB85C \uBCF4\uC874\uB41C\uB2E4)",
3586
+ group: "behavior"
3587
+ },
3588
+ // 실측 2/2
3589
+ {
3590
+ name: "jobTitleOptions",
3591
+ kind: "array",
3592
+ label: "\uC9C1\uCC45 \uD6C4\uBCF4",
3593
+ required: true,
3594
+ bindable: true,
3595
+ description: "\uC9C1\uC6D0 \uB370\uC774\uD130\uC5D0\uC11C distinct\uB85C \uBF51\uC740 \uBB38\uC790\uC5F4 \uBAA9\uB85D.",
3596
+ group: "data",
3597
+ item: [],
3598
+ itemKind: "string"
3599
+ },
3600
+ // 실측 2/2 (원소 10개)
3601
+ {
3602
+ name: "gradeOptions",
3603
+ kind: "array",
3604
+ label: "\uC9C1\uAE09 \uD6C4\uBCF4",
3605
+ required: true,
3606
+ bindable: true,
3607
+ group: "data",
3608
+ item: [],
3609
+ itemKind: "string"
3610
+ },
3611
+ // 실측 2/2 (원소 15개)
3612
+ {
3613
+ name: "searchOptions",
3614
+ kind: "array",
3615
+ label: "\uACB0\uC7AC\uC790 \uD6C4\uBCF4",
3616
+ bindable: true,
3617
+ description: "\uC911\uCCA9\uB41C \uACB0\uC7AC\uC120 \uD3B8\uC9D1\uAE30\uB85C \uADF8\uB300\uB85C \uC804\uB2EC\uB41C\uB2E4. \uAC80\uC0C9 \uACBD\uB85C\uAC00 \uC5C6\uC744 \uB54C\uC758 \uB85C\uCEEC \uAC80\uC0C9 \uB300\uC0C1.",
3618
+ group: "data",
3619
+ item: customApproverItem
3620
+ },
3621
+ // 실측 2/2 (원소 15개)
3622
+ {
3623
+ name: "facetsEndpoint",
3624
+ kind: "string",
3625
+ label: "\uC9C1\uCC45\xB7\uC9C1\uAE09 \uC635\uC158 \uACBD\uB85C",
3626
+ description: "\uC9C1\uC6D0 \uBAA9\uB85D \uACBD\uB85C(\uC608: /employees?limit=500). \uC751\uB2F5\uC758 job_title\xB7grade\uB97C distinct \uCD94\uCD9C\uD574 \uC815\uC801 \uC635\uC158 \uBAA9\uB85D\uC5D0 \uD569\uCE5C\uB2E4 \u2014 v2\uAC00 \uD3B8\uC9D1\uAE30\uC5D0\uC11C \uC9C1\uC811 \uD558\uB358 \uC870\uD68C\uB2E4.",
3627
+ group: "data",
3628
+ advanced: true
3629
+ },
3630
+ {
3631
+ name: "searchEndpoint",
3632
+ kind: "string",
3633
+ label: "\uACB0\uC7AC\uC790 \uAC80\uC0C9 \uACBD\uB85C",
3634
+ description: "\uC911\uCCA9\uB41C \uACB0\uC7AC\uC120 \uD3B8\uC9D1\uAE30\uB85C \uADF8\uB300\uB85C \uC804\uB2EC\uB41C\uB2E4.",
3635
+ group: "data",
3636
+ advanced: true
3637
+ },
3638
+ // 실측 0/2
3639
+ {
3640
+ name: "groupSearchEndpoint",
3641
+ kind: "string",
3642
+ label: "\uBD80\uC11C \uAC80\uC0C9 \uACBD\uB85C",
3643
+ description: "\uC911\uCCA9\uB41C \uACB0\uC7AC\uC120 \uD3B8\uC9D1\uAE30\uB85C \uADF8\uB300\uB85C \uC804\uB2EC\uB41C\uB2E4 \u2014 searchEndpoint\uC640 \uBCD1\uB82C \uC870\uD68C.",
3644
+ group: "data",
3645
+ advanced: true
3646
+ },
3647
+ { name: "disabled", kind: "boolean", label: "\uBE44\uD65C\uC131", default: false, bindable: true, group: "behavior" }
3648
+ // 실측 0/2
3649
+ ],
3650
+ children: { accepts: false },
3651
+ dom: { element: "div" },
3652
+ // 실측: 루트가 <div class="wp-col">
3653
+ // searchEndpoint 금지는 ApprovalLineEditor와 같은 이유다 — 중첩 편집기로 그대로 내려간다.
3654
+ preset: {
3655
+ component: "ApprovalOverridesEditor",
3656
+ props: {
3657
+ value: { $state: "approvalLineForm.lineOverrides", default: [] },
3658
+ onChange: { action: "setState", key: "approvalLineForm.lineOverrides", fromEvent: true },
3659
+ jobTitleOptions: ["\uBCF8\uBD80\uC7A5", "\uD300\uC7A5", "\uD30C\uD2B8\uC7A5"],
3660
+ gradeOptions: ["\uC0AC\uC6D0", "\uB300\uB9AC", "\uACFC\uC7A5", "\uBD80\uC7A5"],
3661
+ searchOptions: [
3662
+ { id: "u1", name: "\uAE40\uB3C4\uD604", type: "user" },
3663
+ { id: "g1", name: "\uC778\uC0AC\uD300", type: "group" }
3664
+ ]
3665
+ }
3666
+ },
3667
+ resize: { kind: "none" }
3668
+ };
3419
3669
  var AutocompleteFieldMeta = {
3420
3670
  name: "AutocompleteField",
3421
3671
  label: "\uC790\uB3D9\uC644\uC131",
@@ -4833,6 +5083,8 @@ var StripMeta = {
4833
5083
  resize: { kind: "none" }
4834
5084
  };
4835
5085
  var widgetComponentMeta = {
5086
+ ApprovalLineEditor: ApprovalLineEditorMeta,
5087
+ ApprovalOverridesEditor: ApprovalOverridesEditorMeta,
4836
5088
  ApprovalStepper: ApprovalStepperMeta,
4837
5089
  RankBoard: RankBoardMeta,
4838
5090
  AutocompleteField: AutocompleteFieldMeta,
@@ -4892,4 +5144,4 @@ function paletteGroups(catalog = allComponentMeta) {
4892
5144
  })).filter((group) => group.items.length > 0);
4893
5145
  }
4894
5146
 
4895
- export { ActionsMeta, AlertMeta, ApprovalStepperMeta, AutocompleteFieldMeta, AvatarMeta, BadgeMeta, BreadcrumbMeta, ButtonMeta, CanMeta, CardMeta, ChartMeta, CheckboxMeta, ColMeta, ColumnBoardMeta, DatePickerMeta, DateRangePickerMeta, DialogMeta, DiffViewMeta, DisclosureMeta, DividerMeta, EmptyStateMeta, EventCalendarMeta, FieldMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PrintSheetMeta, ProgressBarMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RowMeta, SDUI_CATEGORIES, SELF_AXIS_PROPS, SearchResultsMeta, SearchableMultiPickerMeta, SectionMeta, SecureScreenMeta, SegmentedControlMeta, SelectMeta, ShowMeta, SidebarNavMeta, SortHandleMeta, SortableListMeta, SpacerMeta, SpinnerMeta, StackedMeterMeta, StatCardMeta, StripMeta, SummaryTableMeta, SwitchMeta, TableMeta, TabsMeta, TagMeta, TagMultiSelectMeta, TextMeta, TextareaMeta, TimePickerMeta, TimelineMeta, ToastMeta, TooltipMeta, TreePickerMeta, TreeViewMeta, allComponentMeta, builtinComponentMeta, coreComponentMeta, dsComponentMeta, getComponentMeta, paletteGroups, richComponentMeta, slotNamesOf, widgetComponentMeta, withSelfAxis, withSelfAxisAll };
5147
+ export { ActionsMeta, AlertMeta, ApprovalLineEditorMeta, ApprovalOverridesEditorMeta, ApprovalStepperMeta, AutocompleteFieldMeta, AvatarMeta, BadgeMeta, BreadcrumbMeta, ButtonMeta, CanMeta, CardMeta, ChartMeta, CheckboxMeta, ColMeta, ColumnBoardMeta, DatePickerMeta, DateRangePickerMeta, DialogMeta, DiffViewMeta, DisclosureMeta, DividerMeta, EmptyStateMeta, EventCalendarMeta, FieldMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PrintSheetMeta, ProgressBarMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RowMeta, SDUI_CATEGORIES, SELF_AXIS_PROPS, SearchResultsMeta, SearchableMultiPickerMeta, SectionMeta, SecureScreenMeta, SegmentedControlMeta, SelectMeta, ShowMeta, SidebarNavMeta, SortHandleMeta, SortableListMeta, SpacerMeta, SpinnerMeta, StackedMeterMeta, StatCardMeta, StripMeta, SummaryTableMeta, SwitchMeta, TableMeta, TabsMeta, TagMeta, TagMultiSelectMeta, TextMeta, TextareaMeta, TimePickerMeta, TimelineMeta, ToastMeta, TooltipMeta, TreePickerMeta, TreeViewMeta, allComponentMeta, builtinComponentMeta, coreComponentMeta, dsComponentMeta, getComponentMeta, paletteGroups, richComponentMeta, slotNamesOf, widgetComponentMeta, withSelfAxis, withSelfAxisAll };