@wipco/sdui 0.33.0 → 0.34.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,159 @@
1
+ import { useSduiAdapter, hostFetchOf, defaultAdapter } from './chunk-DKPNFEVU.js';
2
+ import { Select, Button } from './chunk-WIDDHR7L.js';
3
+ import { boxed } from './chunk-SAN4IX6N.js';
4
+ import { useState, useEffect, useMemo } from 'react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var EMPTY_VALUE = "__wipco_position_none__";
8
+ var LOADING_VALUE = "__wipco_position_loading__";
9
+ var toNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
10
+ function parseRow(raw) {
11
+ if (typeof raw !== "object" || raw === null) return null;
12
+ const row = raw;
13
+ if (typeof row.name !== "string") return null;
14
+ return {
15
+ name: row.name,
16
+ rankOrder: toNumber(row.rank_order),
17
+ sortOrder: toNumber(row.sort_order),
18
+ // is_active 결측은 true 폴백 — 파서 사정으로 목록이 전부 사라지는 사고 방지(v2와 동일).
19
+ isActive: row.is_active === void 0 || row.is_active === null ? true : row.is_active === true
20
+ };
21
+ }
22
+ async function fetchAllPositions(api, endpoint) {
23
+ const LIMIT = 500;
24
+ const MAX_PAGES = 10;
25
+ const rows = [];
26
+ let offset = 0;
27
+ for (let page = 0; page < MAX_PAGES; page += 1) {
28
+ const sep = endpoint.includes("?") ? "&" : "?";
29
+ const res = await api(`${endpoint}${sep}limit=${LIMIT}&offset=${offset}`, {
30
+ method: "GET"
31
+ });
32
+ const data = Array.isArray(res?.data) ? res.data : [];
33
+ for (const raw of data) {
34
+ const parsed = parseRow(raw);
35
+ if (parsed) rows.push(parsed);
36
+ }
37
+ const total = typeof res?.total === "number" ? res.total : rows.length;
38
+ offset += LIMIT;
39
+ if (data.length === 0 || rows.length >= total) break;
40
+ }
41
+ return rows;
42
+ }
43
+ var inflight = /* @__PURE__ */ new Map();
44
+ function loadPositions(api, endpoint) {
45
+ const pending = inflight.get(endpoint);
46
+ if (pending) return pending;
47
+ const next = fetchAllPositions(api, endpoint).finally(() => {
48
+ inflight.delete(endpoint);
49
+ });
50
+ inflight.set(endpoint, next);
51
+ return next;
52
+ }
53
+ function PositionSelectImpl({
54
+ endpoint = "/job-positions",
55
+ value,
56
+ onChange,
57
+ allowEmpty = false,
58
+ disabled = false,
59
+ size,
60
+ invalid,
61
+ placeholder = "\uC9C1\uC704\uB97C \uC120\uD0DD\uD558\uC138\uC694",
62
+ id
63
+ }) {
64
+ const adapter = useSduiAdapter();
65
+ const api = hostFetchOf(adapter) ?? defaultAdapter.fetch;
66
+ const [positions, setPositions] = useState(null);
67
+ const [failed, setFailed] = useState(false);
68
+ const [attempt, setAttempt] = useState(0);
69
+ useEffect(() => {
70
+ let alive = true;
71
+ loadPositions(api, endpoint).then((rows2) => {
72
+ if (!alive) return;
73
+ setPositions(rows2);
74
+ setFailed(false);
75
+ }).catch(() => {
76
+ if (!alive) return;
77
+ setPositions([]);
78
+ setFailed(true);
79
+ });
80
+ return () => {
81
+ alive = false;
82
+ };
83
+ }, [attempt]);
84
+ const isLoading = positions === null && !failed;
85
+ const rows = useMemo(() => positions ?? [], [positions]);
86
+ const activeByName = useMemo(() => {
87
+ const map = /* @__PURE__ */ new Map();
88
+ for (const position of rows) {
89
+ if (position.name.trim() === "") continue;
90
+ map.set(position.name, (map.get(position.name) ?? false) || position.isActive);
91
+ }
92
+ return map;
93
+ }, [rows]);
94
+ const activeNames = useMemo(() => {
95
+ const sorted = [...rows].sort(
96
+ (a, b) => a.rankOrder - b.rankOrder || a.sortOrder - b.sortOrder || a.name.localeCompare(b.name, "ko")
97
+ );
98
+ const seen = /* @__PURE__ */ new Set();
99
+ const names = [];
100
+ for (const position of sorted) {
101
+ if (!position.isActive) continue;
102
+ if (position.name.trim() === "" || seen.has(position.name)) continue;
103
+ seen.add(position.name);
104
+ names.push(position.name);
105
+ }
106
+ return names;
107
+ }, [rows]);
108
+ const current = typeof value === "string" ? value : "";
109
+ const isOffList = current !== "" && !activeNames.includes(current);
110
+ const suffix = isLoading || failed ? "" : activeByName.has(current) ? " (\uBBF8\uC0AC\uC6A9)" : " (\uBBF8\uB4F1\uB85D \uC9C1\uC704)";
111
+ const options = useMemo(() => {
112
+ const list = [];
113
+ if (allowEmpty) list.push({ value: EMPTY_VALUE, label: "\uC120\uD0DD \uC548 \uD568" });
114
+ if (isOffList) list.push({ value: current, label: `${current}${suffix}` });
115
+ if (isLoading) list.push({ value: LOADING_VALUE, label: "\uBD88\uB7EC\uC624\uB294 \uC911\u2026", disabled: true });
116
+ for (const name of activeNames) list.push({ value: name, label: name });
117
+ return list;
118
+ }, [allowEmpty, isOffList, current, suffix, isLoading, activeNames]);
119
+ return /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-1)" }, children: [
120
+ /* @__PURE__ */ jsx(
121
+ Select,
122
+ {
123
+ id,
124
+ size,
125
+ invalid,
126
+ disabled,
127
+ value: current,
128
+ options,
129
+ placeholder,
130
+ onChange: (event) => {
131
+ const next = event.target.value;
132
+ if (next === LOADING_VALUE) return;
133
+ onChange?.(next === EMPTY_VALUE ? "" : next);
134
+ }
135
+ }
136
+ ),
137
+ !isLoading && failed && /* @__PURE__ */ jsxs("div", { className: "wp-row", style: { alignItems: "center", gap: "var(--wp-space-2)" }, children: [
138
+ /* @__PURE__ */ jsx("span", { className: "wp-caption", style: { color: "var(--wp-text-muted)" }, children: "\uC9C1\uC704 \uBAA9\uB85D\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4" }),
139
+ /* @__PURE__ */ jsx(
140
+ Button,
141
+ {
142
+ type: "button",
143
+ size: "sm",
144
+ variant: "secondary",
145
+ onClick: () => {
146
+ setPositions(null);
147
+ setFailed(false);
148
+ setAttempt((n) => n + 1);
149
+ },
150
+ children: "\uB2E4\uC2DC \uC2DC\uB3C4"
151
+ }
152
+ )
153
+ ] }),
154
+ !isLoading && !failed && activeNames.length === 0 && !isOffList && /* @__PURE__ */ jsx("span", { className: "wp-caption", style: { color: "var(--wp-text-muted)" }, children: "\uB4F1\uB85D\uB41C \uC9C1\uC704\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uC778\uC0AC > \uC9C1\uC704 \uAD00\uB9AC\uC5D0\uC11C \uB4F1\uB85D\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4." })
155
+ ] });
156
+ }
157
+ var PositionSelect = boxed(PositionSelectImpl, "PositionSelect");
158
+
159
+ export { PositionSelect };
@@ -51,10 +51,17 @@ function DiffViewImpl({ fields, lists }) {
51
51
  const added = list.added ?? [];
52
52
  const removed = list.removed ?? [];
53
53
  const kept = list.kept ?? [];
54
- const isEmpty = added.length === 0 && removed.length === 0 && kept.length === 0;
54
+ const isReplace = list.before !== void 0 || list.after !== void 0;
55
+ const before = list.before ?? [];
56
+ const after = list.after ?? [];
57
+ const isEmpty = isReplace ? before.length === 0 && after.length === 0 : added.length === 0 && removed.length === 0 && kept.length === 0;
55
58
  return /* @__PURE__ */ jsxs(React.Fragment, { children: [
56
59
  /* @__PURE__ */ jsx("span", { className: "erp-diff__label", children: list.label }),
57
- /* @__PURE__ */ jsx("span", { className: "erp-diff__badges", children: isEmpty ? /* @__PURE__ */ jsx("span", { className: "wp-caption", children: "\uBCC0\uACBD \uC5C6\uC74C" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
60
+ /* @__PURE__ */ jsx("span", { className: "erp-diff__badges", children: isEmpty ? /* @__PURE__ */ jsx("span", { className: "wp-caption", children: "\uBCC0\uACBD \uC5C6\uC74C" }) : isReplace ? /* @__PURE__ */ jsxs(Fragment, { children: [
61
+ before.map((value, i) => /* @__PURE__ */ jsx(Badge, { tone: "neutral", children: value }, `b-${value}-${i}`)),
62
+ /* @__PURE__ */ jsx("span", { className: "erp-diff__arrow", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Icon, { name: "arrow-right", size: 14 }) }),
63
+ after.length === 0 ? /* @__PURE__ */ jsx("span", { className: "wp-caption", children: "\uBE44\uC6C0" }) : after.map((value, i) => /* @__PURE__ */ jsx(Badge, { tone: "success", children: value }, `f-${value}-${i}`))
64
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
58
65
  added.map((value, i) => /* @__PURE__ */ jsxs(Badge, { tone: "success", children: [
59
66
  "+ ",
60
67
  value
package/dist/meta.d.ts CHANGED
@@ -436,6 +436,7 @@ declare const OptionSchemaEditorMeta: SduiComponentMeta;
436
436
  declare const OptionValueFieldsMeta: SduiComponentMeta;
437
437
  declare const StagePeriodEditorMeta: SduiComponentMeta;
438
438
  declare const QRDisplayMeta: SduiComponentMeta;
439
+ declare const PositionSelectMeta: SduiComponentMeta;
439
440
  declare const AutocompleteFieldMeta: SduiComponentMeta;
440
441
  declare const InlineEditMeta: SduiComponentMeta;
441
442
  declare const JsonFieldMeta: SduiComponentMeta;
@@ -472,6 +473,7 @@ declare const widgetComponentMeta: {
472
473
  ApprovalLineEditor: SduiComponentMeta;
473
474
  ApprovalOverridesEditor: SduiComponentMeta;
474
475
  ApprovalStepper: SduiComponentMeta;
476
+ PositionSelect: SduiComponentMeta;
475
477
  QRDisplay: SduiComponentMeta;
476
478
  OptionSchemaEditor: SduiComponentMeta;
477
479
  OptionValueFields: SduiComponentMeta;
@@ -614,4 +616,4 @@ declare function paletteGroups(catalog?: Record<string, SduiComponentMeta>): Arr
614
616
  items: SduiComponentMeta[];
615
617
  }>;
616
618
 
617
- 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, ExcelUploadCardMeta, FieldMeta, FileDropzoneMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OptionSchemaEditorMeta, OptionValueFieldsMeta, OrgChartBoardMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PermissionMatrixEditorMeta, PrintSheetMeta, ProgressBarMeta, QRDisplayMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RoleMemberPickerMeta, 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, StagePeriodEditorMeta, 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 };
619
+ 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, ExcelUploadCardMeta, FieldMeta, FileDropzoneMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OptionSchemaEditorMeta, OptionValueFieldsMeta, OrgChartBoardMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PermissionMatrixEditorMeta, PositionSelectMeta, PrintSheetMeta, ProgressBarMeta, QRDisplayMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RoleMemberPickerMeta, 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, StagePeriodEditorMeta, 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
@@ -4229,6 +4229,44 @@ var QRDisplayMeta = {
4229
4229
  // 죽어 있었다. resize.ts의 "같은 프롭이면 축을 합친다" 분기를 그때 넣어 참으로 만들었다.
4230
4230
  resize: { kind: "numeric", widthProp: "size", heightProp: "size", unit: "px", axis: "both" }
4231
4231
  };
4232
+ var PositionSelectMeta = {
4233
+ name: "PositionSelect",
4234
+ label: "\uC9C1\uC704 \uC120\uD0DD",
4235
+ category: "input",
4236
+ icon: "circle-user",
4237
+ description: '\uC9C1\uC704 \uB9C8\uC2A4\uD130(/job-positions) \uC140\uB809\uD2B8. \uD65C\uC131 \uC9C1\uC704\uB97C \uC11C\uC5F4(rank_order\u2192sort_order) \uC21C\uC73C\uB85C \uBCF4\uC5EC\uC8FC\uACE0, \uBAA9\uB85D \uBC16 \uD604\uC7AC \uAC12\uC740 " (\uBBF8\uC0AC\uC6A9)"/" (\uBBF8\uB4F1\uB85D \uC9C1\uC704)" \uC811\uBBF8\uC0AC\uB85C \uC5B9\uC5B4 \uBCF4\uC874\uD55C\uB2E4. \uAC12\uC740 employees.grade \uBB38\uC790\uC5F4 \uC0AC\uBCF8 \uADF8\uB300\uB85C\uB2E4.',
4238
+ props: [
4239
+ { name: "value", kind: "string", label: "\uD604\uC7AC \uC9C1\uC704", required: true, bindable: true, description: "\uC9C1\uC704 \uC774\uB984 \uBB38\uC790\uC5F4(employees.grade).", group: "data" },
4240
+ { name: "onChange", kind: "action", label: "\uC9C1\uC704 \uBCC0\uACBD", required: true, eventArg: '\uBC14\uB010 \uC9C1\uC704 \uC774\uB984 \uBB38\uC790\uC5F4 \u2014 "\uC120\uD0DD \uC548 \uD568"\uC740 \uBE48 \uBB38\uC790\uC5F4', group: "behavior" },
4241
+ { name: "allowEmpty", kind: "boolean", label: "\uC120\uD0DD \uC548 \uD568 \uB178\uCD9C", default: false, description: '\uB9E8 \uC704\uC5D0 "\uC120\uD0DD \uC548 \uD568" \uD56D\uBAA9\uC744 \uB178\uCD9C\uD55C\uB2E4. \uC120\uD0DD \uC2DC \uBE48 \uBB38\uC790\uC5F4\uC744 \uD758\uB9B0\uB2E4.', group: "behavior" },
4242
+ { name: "placeholder", kind: "string", label: "\uD50C\uB808\uC774\uC2A4\uD640\uB354", default: "\uC9C1\uC704\uB97C \uC120\uD0DD\uD558\uC138\uC694", group: "content" },
4243
+ { name: "disabled", kind: "boolean", label: "\uBE44\uD65C\uC131", default: false, bindable: true, group: "behavior" },
4244
+ { name: "invalid", kind: "boolean", label: "\uC624\uB958 \uD45C\uC2DC", bindable: true, group: "appearance", advanced: true },
4245
+ { name: "size", kind: "enum", label: "\uD06C\uAE30", options: [{ value: "sm", label: "\uC791\uAC8C" }, { value: "md", label: "\uBCF4\uD1B5" }, { value: "lg", label: "\uD06C\uAC8C" }], group: "appearance", advanced: true },
4246
+ { name: "id", kind: "string", label: "\uC694\uC18C id", group: "behavior", advanced: true },
4247
+ {
4248
+ name: "endpoint",
4249
+ kind: "string",
4250
+ label: "\uC9C1\uC704 \uBAA9\uB85D \uACBD\uB85C",
4251
+ description: "\uC751\uB2F5 {data:[\u2026], total} \uC744 500\uAC74\uC529 \uC218\uC9D1\uD55C\uB2E4. \uAE30\uBCF8 '/job-positions'.",
4252
+ group: "data",
4253
+ advanced: true
4254
+ }
4255
+ ],
4256
+ children: { accepts: false },
4257
+ dom: { element: "div" },
4258
+ // 루트 <div class="wp-col"> (Select + 안내문)
4259
+ // 마운트 즉시 GET /job-positions 1회가 나간다 — RoleTagPicker.sourceEndpoint와 같은 결(캔버스에서는 조용히 실패).
4260
+ preset: {
4261
+ component: "PositionSelect",
4262
+ props: {
4263
+ value: { $state: "empForm.grade", default: "" },
4264
+ onChange: { action: "setState", key: "empForm.grade", fromEvent: true },
4265
+ allowEmpty: true
4266
+ }
4267
+ },
4268
+ resize: { kind: "none" }
4269
+ };
4232
4270
  var AutocompleteFieldMeta = {
4233
4271
  name: "AutocompleteField",
4234
4272
  label: "\uC790\uB3D9\uC644\uC131",
@@ -5041,7 +5079,16 @@ var DiffViewMeta = {
5041
5079
  { name: "label", kind: "string", label: "\uD56D\uBAA9\uBA85", required: true },
5042
5080
  { name: "added", kind: "array", label: "\uCD94\uAC00\uB428", item: [], itemKind: "string" },
5043
5081
  { name: "removed", kind: "array", label: "\uC0AD\uC81C\uB428", item: [], itemKind: "string" },
5044
- { name: "kept", kind: "array", label: "\uC720\uC9C0\uB428", item: [], itemKind: "string" }
5082
+ { name: "kept", kind: "array", label: "\uC720\uC9C0\uB428", item: [], itemKind: "string" },
5083
+ {
5084
+ name: "before",
5085
+ kind: "array",
5086
+ label: "\uBCC0\uACBD \uC804(\uC804\uCCB4 \uAD50\uCCB4)",
5087
+ item: [],
5088
+ itemKind: "string",
5089
+ description: '\uC774 \uCD95\uC744 \uC8FC\uBA74 added/removed/kept \uB300\uC2E0 "before \u2192 after" \uB85C \uADF8\uB9B0\uB2E4 \u2014 \uC885\uB958 \uB2E8\uC704 \uC804\uCCB4 \uAD50\uCCB4.'
5090
+ },
5091
+ { name: "after", kind: "array", label: "\uBCC0\uACBD \uD6C4(\uC804\uCCB4 \uAD50\uCCB4)", item: [], itemKind: "string" }
5045
5092
  ]
5046
5093
  }
5047
5094
  ],
@@ -5649,6 +5696,7 @@ var widgetComponentMeta = {
5649
5696
  ApprovalLineEditor: ApprovalLineEditorMeta,
5650
5697
  ApprovalOverridesEditor: ApprovalOverridesEditorMeta,
5651
5698
  ApprovalStepper: ApprovalStepperMeta,
5699
+ PositionSelect: PositionSelectMeta,
5652
5700
  QRDisplay: QRDisplayMeta,
5653
5701
  OptionSchemaEditor: OptionSchemaEditorMeta,
5654
5702
  OptionValueFields: OptionValueFieldsMeta,
@@ -5716,4 +5764,4 @@ function paletteGroups(catalog = allComponentMeta) {
5716
5764
  })).filter((group) => group.items.length > 0);
5717
5765
  }
5718
5766
 
5719
- 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, ExcelUploadCardMeta, FieldMeta, FileDropzoneMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OptionSchemaEditorMeta, OptionValueFieldsMeta, OrgChartBoardMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PermissionMatrixEditorMeta, PrintSheetMeta, ProgressBarMeta, QRDisplayMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RoleMemberPickerMeta, RowMeta, SDUI_CATEGORIES, SELF_AXIS_PROPS, SearchResultsMeta, SearchableMultiPickerMeta, SectionMeta, SecureScreenMeta, SegmentedControlMeta, SelectMeta, ShowMeta, SidebarNavMeta, SortHandleMeta, SortableListMeta, SpacerMeta, SpinnerMeta, StackedMeterMeta, StagePeriodEditorMeta, 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 };
5767
+ 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, ExcelUploadCardMeta, FieldMeta, FileDropzoneMeta, ForEachMeta, FormMeta, FragmentMeta, GridMeta, IconButtonMeta, IconMeta, ImageLightboxMeta, InlineEditMeta, InputMeta, JsonFieldMeta, KeyValueListMeta, LiveClockMeta, MapPreviewMeta, OptionSchemaEditorMeta, OptionValueFieldsMeta, OrgChartBoardMeta, OrgChartMeta, PageHeadMeta, PaginationMeta, PeriodFilterMeta, PermissionMatrixEditorMeta, PositionSelectMeta, PrintSheetMeta, ProgressBarMeta, QRDisplayMeta, RadioMeta, RankBoardMeta, RecordSheetMeta, RemoteSearchResultsMeta, RoleMemberPickerMeta, RowMeta, SDUI_CATEGORIES, SELF_AXIS_PROPS, SearchResultsMeta, SearchableMultiPickerMeta, SectionMeta, SecureScreenMeta, SegmentedControlMeta, SelectMeta, ShowMeta, SidebarNavMeta, SortHandleMeta, SortableListMeta, SpacerMeta, SpinnerMeta, StackedMeterMeta, StagePeriodEditorMeta, 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 };
@@ -11,6 +11,17 @@ interface DiffList {
11
11
  added?: string[];
12
12
  removed?: string[];
13
13
  kept?: string[];
14
+ /**
15
+ * **종류 단위 전체 교체**를 그린다 — `before`/`after` 중 하나라도 있으면
16
+ * added/removed/kept 대신 이쪽으로 그린다.
17
+ *
18
+ * 항목별 추가·삭제가 아니라 "이 종류가 통째로 이 목록으로 바뀐다"가 참인 자리가
19
+ * 있다(학력·자격처럼 서버가 종류째 갈아 끼우는 관계). 그걸 added/removed 로 그리면
20
+ * **의미가 달라진다** — 같은 값이 양쪽에 있어도 "유지"가 아니라 "지웠다 다시 넣음"이
21
+ * 서버에서 실제로 일어나기 때문이다.
22
+ */
23
+ before?: string[];
24
+ after?: string[];
14
25
  }
15
26
  interface DiffViewProps {
16
27
  fields?: DiffField[];
@@ -1,3 +1,3 @@
1
- export { DiffView } from '../chunk-LDJ2CFFH.js';
1
+ export { DiffView } from '../chunk-XAYXHY7R.js';
2
2
  import '../chunk-WIDDHR7L.js';
3
3
  import '../chunk-SAN4IX6N.js';
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { S as SduiBoxProps } from '../layout-B1QrIBm6.js';
3
+
4
+ /**
5
+ * PositionSelect — 직위 마스터(/job-positions) 셀렉트. v2 position-select.tsx 이식.
6
+ *
7
+ * 옵션은 **활성 직위만**, 마스터 서열(rank_order → sort_order → 이름 ko) 순서 그대로다 —
8
+ * 서열이 곧 표시 순서라 가나다 재정렬로 덮으면 안 된다. 같은 이름이 중복되면
9
+ * 첫 등장(서열 높은 쪽)만 남긴다(같은 value 옵션이 둘 생기는 사고 방지).
10
+ *
11
+ * **off-list 값 처리**: 현재 값이 활성 목록에 없으면(미사용 전환·예전 자유 입력) 그 값을
12
+ * 항목으로 얹어 표시를 유지한다 — 안 얹으면 셀렉트가 비어 보이고 저장 시 기존 값이
13
+ * 날아간 것처럼 오해된다(v2가 고친 문제). 접미사: 마스터에 있으나 미사용이면 " (미사용)",
14
+ * 마스터에 아예 없으면 " (미등록 직위)". 로딩/에러 중에는 접미사를 생략한다 —
15
+ * 조회 한 번 실패했다고 멀쩡한 값이 "미등록"으로 낙인찍히면 안 된다.
16
+ *
17
+ * **조회는 매 마운트 1회**(limit 500씩 total까지 페이지 수집) — v2의 60초 모듈 캐시 +
18
+ * 세대 카운터 대신 단순화했다. 직위 관리 화면 변경 뒤 폼 다이얼로그가 다시 열리면
19
+ * (Show 언마운트→재마운트) 새로 읽으므로 invalidate 별도 배선이 필요 없다.
20
+ * 한 화면 다중 인스턴스(등록 폼+필터)의 중복 조회만 모듈 인플라이트 공유로 억제한다.
21
+ *
22
+ * **onChange는 문자열**을 흘린다 — "선택 안 함"은 ''(빈 문자열). v2 계약은 null이었지만
23
+ * 호출부가 전부 null→''로 접어 저장했고, SDUI setState(fromEvent)에는 값 변환 어휘가
24
+ * 없어 그 접기를 위젯이 대신한다. employees.grade는 문자열 사본 컬럼이라 ''가 "미지정"이다.
25
+ */
26
+ interface PositionSelectProps {
27
+ /** 직위 목록 경로. 응답 `{data:[…], total}` 을 500건씩 수집한다. 기본 '/job-positions'. */
28
+ endpoint?: string;
29
+ /** 현재 직위 이름(employees.grade 문자열 사본). */
30
+ value?: string | null;
31
+ /** 바뀐 직위 이름. "선택 안 함"은 ''(빈 문자열). */
32
+ onChange?: (value: string) => void;
33
+ /** "선택 안 함" 항목을 맨 위에 노출한다. 선택 시 onChange(''). */
34
+ allowEmpty?: boolean;
35
+ disabled?: boolean;
36
+ size?: 'sm' | 'md' | 'lg';
37
+ invalid?: boolean;
38
+ placeholder?: string;
39
+ id?: string;
40
+ }
41
+ /** 바깥 style·className을 뿌리에 얹는다 — 자기 축(폭·여백) 통로. */
42
+ declare const PositionSelect: React.FC<PositionSelectProps & SduiBoxProps>;
43
+
44
+ export { PositionSelect, type PositionSelectProps };
@@ -0,0 +1,4 @@
1
+ export { PositionSelect } from '../chunk-D4AILKSJ.js';
2
+ import '../chunk-DKPNFEVU.js';
3
+ import '../chunk-WIDDHR7L.js';
4
+ import '../chunk-SAN4IX6N.js';
@@ -28,6 +28,8 @@ import { QRDisplayProps } from './QRDisplay.js';
28
28
  export { QRDisplay } from './QRDisplay.js';
29
29
  import { PrintSheetProps } from './PrintSheet.js';
30
30
  export { PrintSheet } from './PrintSheet.js';
31
+ import { PositionSelectProps } from './PositionSelect.js';
32
+ export { PositionSelect } from './PositionSelect.js';
31
33
  import { PeriodFilterProps } from './PeriodFilter.js';
32
34
  export { Period, PeriodFilter, PeriodMode, PeriodModeItem } from './PeriodFilter.js';
33
35
  import { PermissionMatrixEditorProps } from './PermissionMatrixEditor.js';
@@ -92,6 +94,7 @@ declare const widgetComponents: {
92
94
  OrgChartBoard: React.FC<OrgChartBoardProps & SduiBoxProps>;
93
95
  PermissionMatrixEditor: React.FC<PermissionMatrixEditorProps & SduiBoxProps>;
94
96
  PeriodFilter: React.FC<PeriodFilterProps & SduiBoxProps>;
97
+ PositionSelect: React.FC<PositionSelectProps & SduiBoxProps>;
95
98
  PrintSheet: React.FC<PrintSheetProps & SduiBoxProps>;
96
99
  QRDisplay: React.FC<QRDisplayProps & SduiBoxProps>;
97
100
  RankBoard: React.FC<RankBoardProps & SduiBoxProps>;
@@ -111,4 +114,4 @@ declare const widgetComponents: {
111
114
  /** 위젯 어휘의 컴포넌트 이름 union — 메타데이터 대조에 쓴다(`@wipco/sdui/meta`). */
112
115
  type SduiWidgetComponentName = keyof typeof widgetComponents;
113
116
 
114
- export { ApprovalLineEditorProps, ApprovalOverridesEditorProps, ApprovalStepperProps, ColumnBoardProps, DiffViewProps, DisclosureProps, EventCalendarProps, ImageLightboxProps, InlineEditProps, JsonFieldProps, LiveClockProps, OptionSchemaEditorProps, OptionValueFieldsProps, OrgChartBoardProps, OrgChartProps, PeriodFilterProps, PermissionMatrixEditorProps, PrintSheetProps, QRDisplayProps, RankBoardProps, RecordSheetProps, RoleMemberPickerProps, type SduiWidgetComponentName, SearchableMultiPickerProps, SecureScreenProps, StackedMeterProps, StagePeriodEditorProps, StripProps, SummaryTableProps, TagMultiSelectProps, TimelineProps, TreePickerProps, TreeViewProps, widgetComponents };
117
+ export { ApprovalLineEditorProps, ApprovalOverridesEditorProps, ApprovalStepperProps, ColumnBoardProps, DiffViewProps, DisclosureProps, EventCalendarProps, ImageLightboxProps, InlineEditProps, JsonFieldProps, LiveClockProps, OptionSchemaEditorProps, OptionValueFieldsProps, OrgChartBoardProps, OrgChartProps, PeriodFilterProps, PermissionMatrixEditorProps, PositionSelectProps, PrintSheetProps, QRDisplayProps, RankBoardProps, RecordSheetProps, RoleMemberPickerProps, type SduiWidgetComponentName, SearchableMultiPickerProps, SecureScreenProps, StackedMeterProps, StagePeriodEditorProps, StripProps, SummaryTableProps, TagMultiSelectProps, TimelineProps, TreePickerProps, TreeViewProps, widgetComponents };
@@ -1,3 +1,5 @@
1
+ import { StackedMeter } from '../chunk-BMZKZGJF.js';
2
+ export { StackedMeter } from '../chunk-BMZKZGJF.js';
1
3
  import { StagePeriodEditor } from '../chunk-KVFHPBAY.js';
2
4
  export { StagePeriodEditor } from '../chunk-KVFHPBAY.js';
3
5
  import { Strip } from '../chunk-ZH5MLOZS.js';
@@ -10,6 +12,8 @@ import { TreePicker } from '../chunk-U3HYQCFA.js';
10
12
  export { TreePicker } from '../chunk-U3HYQCFA.js';
11
13
  import { TreeView } from '../chunk-X5R2HHEN.js';
12
14
  export { TreeView, buildTreeNodes } from '../chunk-X5R2HHEN.js';
15
+ import { PositionSelect } from '../chunk-D4AILKSJ.js';
16
+ export { PositionSelect } from '../chunk-D4AILKSJ.js';
13
17
  import { PrintSheet } from '../chunk-MYJ2KEIP.js';
14
18
  export { PrintSheet } from '../chunk-MYJ2KEIP.js';
15
19
  import { QRDisplay } from '../chunk-PCMJE2GO.js';
@@ -24,8 +28,6 @@ import { SearchableMultiPicker } from '../chunk-C53NGSA3.js';
24
28
  export { SearchableMultiPicker } from '../chunk-C53NGSA3.js';
25
29
  import { SecureScreen } from '../chunk-IIRENCDS.js';
26
30
  export { SecureScreen } from '../chunk-IIRENCDS.js';
27
- import { StackedMeter } from '../chunk-BMZKZGJF.js';
28
- export { StackedMeter } from '../chunk-BMZKZGJF.js';
29
31
  import { JsonField } from '../chunk-JVBZRDC2.js';
30
32
  export { JsonField } from '../chunk-JVBZRDC2.js';
31
33
  import { LiveClock } from '../chunk-Z3WGIFJP.js';
@@ -45,8 +47,8 @@ export { PeriodFilter } from '../chunk-INWASR2G.js';
45
47
  export { AutocompleteField, toAutocompleteOptions } from '../chunk-CVCP52OD.js';
46
48
  import { ColumnBoard } from '../chunk-C4JRSZGS.js';
47
49
  export { ColumnBoard } from '../chunk-C4JRSZGS.js';
48
- import { DiffView } from '../chunk-LDJ2CFFH.js';
49
- export { DiffView } from '../chunk-LDJ2CFFH.js';
50
+ import { DiffView } from '../chunk-XAYXHY7R.js';
51
+ export { DiffView } from '../chunk-XAYXHY7R.js';
50
52
  import { Disclosure } from '../chunk-HIZ2OVJA.js';
51
53
  export { Disclosure } from '../chunk-HIZ2OVJA.js';
52
54
  import { EventCalendar } from '../chunk-C3JY6GA3.js';
@@ -94,6 +96,7 @@ var widgetComponents = {
94
96
  OrgChartBoard,
95
97
  PermissionMatrixEditor,
96
98
  PeriodFilter,
99
+ PositionSelect,
97
100
  PrintSheet,
98
101
  QRDisplay,
99
102
  RankBoard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipco/sdui",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "WIPCO 서버 주도 UI(SDUI) 렌더러 — JSON 페이지 스펙을 React 컴포넌트로 렌더링",
5
5
  "keywords": [
6
6
  "sdui",
@@ -116,6 +116,10 @@
116
116
  "types": "./dist/widgets/PeriodFilter.d.ts",
117
117
  "default": "./dist/widgets/PeriodFilter.js"
118
118
  },
119
+ "./widgets/PositionSelect": {
120
+ "types": "./dist/widgets/PositionSelect.d.ts",
121
+ "default": "./dist/widgets/PositionSelect.js"
122
+ },
119
123
  "./widgets/PrintSheet": {
120
124
  "types": "./dist/widgets/PrintSheet.d.ts",
121
125
  "default": "./dist/widgets/PrintSheet.js"