@wipco/sdui 0.22.0 → 0.23.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.
@@ -1,5 +1,5 @@
1
1
  import { useDismiss, usePopoverAnchor, usePortalTo, HoistedStyle, AnchoredPopoverStyles, anchorStyle, focusStaysInside } from './chunk-XKLUCUVA.js';
2
- import { Icon } from './chunk-WIDDHR7L.js';
2
+ import { Icon, Spinner, EmptyState } from './chunk-WIDDHR7L.js';
3
3
  import { boxed } from './chunk-SAN4IX6N.js';
4
4
  import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -194,5 +194,66 @@ var TP_CSS = `
194
194
  .wp-tp-opt--on{background:var(--wp-action-primary-bg);color:var(--wp-action-primary-fg);font-weight:500}
195
195
  `;
196
196
  var TimePicker = boxed(TimePickerImpl, "TimePicker");
197
+ function useOptionalModule(load) {
198
+ const [state, setState] = useState({ status: "loading" });
199
+ useEffect(() => {
200
+ let alive = true;
201
+ load().then(
202
+ (module) => {
203
+ if (alive) setState({ status: "ready", module });
204
+ },
205
+ (error) => {
206
+ if (!alive) return;
207
+ console.warn("[sdui] \uC120\uD0DD \uC758\uC874\uC131 \uB85C\uB4DC \uC2E4\uD328 \u2014 \uD50C\uB808\uC774\uC2A4\uD640\uB354\uB85C \uB300\uCCB4\uD569\uB2C8\uB2E4", error);
208
+ setState({ status: "missing" });
209
+ }
210
+ );
211
+ return () => {
212
+ alive = false;
213
+ };
214
+ }, [load]);
215
+ return state;
216
+ }
217
+ var optionalModuleCache = /* @__PURE__ */ new WeakMap();
218
+ function loadOptionalModule(load, packageName) {
219
+ const cached = optionalModuleCache.get(load);
220
+ if (cached) return cached;
221
+ const pending = load().catch((error) => {
222
+ console.warn(`[sdui] \uC120\uD0DD \uC758\uC874\uC131 '${packageName}' \uB85C\uB4DC \uC2E4\uD328`, error);
223
+ optionalModuleCache.delete(load);
224
+ throw new Error(`${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC124\uCE58\uD574 \uC8FC\uC138\uC694`);
225
+ });
226
+ optionalModuleCache.set(load, pending);
227
+ return pending;
228
+ }
229
+ function DependencyPlaceholder({
230
+ packageName,
231
+ purpose,
232
+ height,
233
+ loading = false
234
+ }) {
235
+ return /* @__PURE__ */ jsx(
236
+ "div",
237
+ {
238
+ style: {
239
+ minHeight: height,
240
+ display: "flex",
241
+ alignItems: "center",
242
+ justifyContent: "center",
243
+ border: "1px solid var(--wp-border-subtle)",
244
+ borderRadius: "var(--wp-radius-md)",
245
+ background: "var(--wp-surface-sunken)"
246
+ },
247
+ children: loading ? /* @__PURE__ */ jsx(Spinner, { label: `${purpose} \uC900\uBE44 \uC911` }) : /* @__PURE__ */ jsxs(EmptyState, { tight: true, icon: "box", title: `${purpose}\uB97C \uADF8\uB9AC\uB824\uBA74 ${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4`, children: [
248
+ "\uC120\uD0DD \uC758\uC874\uC131\uC774\uB77C \uAE30\uBCF8 \uC124\uCE58\uC5D0\uB294 \uD3EC\uD568\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 ",
249
+ /* @__PURE__ */ jsxs("code", { children: [
250
+ "pnpm add ",
251
+ packageName
252
+ ] }),
253
+ " \uD6C4 \uB2E4\uC2DC \uC5F4\uC5B4 \uC8FC\uC138\uC694."
254
+ ] })
255
+ }
256
+ );
257
+ }
197
258
 
198
- export { TimePicker };
259
+ export { DependencyPlaceholder, TimePicker, loadOptionalModule, useOptionalModule };
@@ -53,7 +53,8 @@ var defaultAdapter = {
53
53
  window.location.assign(to);
54
54
  },
55
55
  fetch: defaultFetch,
56
- rawFetch: (path) => fetch(path, { credentials: "include" }),
56
+ // init을 반드시 흘린다 — 버리면 드롭인 소비자의 업로드가 본문 없는 GET이 된다.
57
+ rawFetch: (path, init) => fetch(path, { credentials: "include", ...init }),
57
58
  // 항등함수. 계약을 문서화하고, 컴포넌트가 `useSduiAssetResolver()`를 조건 없이
58
59
  // 부를 수 있게 하는 것이 전부다. **이 함수 참조 자체가 표식**이기도 하다 —
59
60
  // `assetResolverOf`가 이것과 같은지 보고 "주입 없음"을 판정한다.
@@ -1,5 +1,5 @@
1
1
  import { includesLooseSearch } from './chunk-U7ZK7WYH.js';
2
- import { useSduiAdapter, hostFetchOf } from './chunk-WGRB5UL4.js';
2
+ import { useSduiAdapter, hostFetchOf } from './chunk-DVOPZFQS.js';
3
3
  import { Icon, Button, Input, Spinner } from './chunk-WIDDHR7L.js';
4
4
  import { boxed } from './chunk-SAN4IX6N.js';
5
5
  import { useId, useRef, useMemo, useState, useEffect } from 'react';
@@ -0,0 +1,211 @@
1
+ import { Icon, Badge, Tooltip } from './chunk-WIDDHR7L.js';
2
+ import { boxed } from './chunk-SAN4IX6N.js';
3
+ import React from 'react';
4
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
+
6
+ var STATUS_META = {
7
+ approved: {
8
+ icon: "check",
9
+ label: "\uC2B9\uC778",
10
+ bg: "var(--wp-status-success-bg)",
11
+ border: "var(--wp-status-success-border)",
12
+ fg: "var(--wp-status-success-fg)"
13
+ },
14
+ rejected: {
15
+ icon: "x",
16
+ label: "\uBC18\uB824",
17
+ bg: "var(--wp-status-danger-bg)",
18
+ border: "var(--wp-status-danger-border)",
19
+ fg: "var(--wp-status-danger-fg)"
20
+ },
21
+ skipped: {
22
+ icon: "minus",
23
+ label: "\uAC74\uB108\uB700",
24
+ bg: "var(--wp-status-neutral-bg)",
25
+ border: "var(--wp-status-neutral-border)",
26
+ fg: "var(--wp-status-neutral-fg)"
27
+ },
28
+ current: {
29
+ icon: "circle-dot",
30
+ label: "\uC9C4\uD589 \uC911",
31
+ bg: "var(--wp-surface-brand-tint)",
32
+ border: "var(--wp-border-brand)",
33
+ fg: "var(--wp-text-brand)"
34
+ },
35
+ pending: {
36
+ icon: "circle-dot",
37
+ label: "\uB300\uAE30",
38
+ bg: "var(--wp-surface-card)",
39
+ border: "var(--wp-border-subtle)",
40
+ fg: "var(--wp-text-muted)"
41
+ }
42
+ };
43
+ function stepTitle(step, index, typeLabels) {
44
+ const name = step.name?.trim();
45
+ if (name) return name;
46
+ if (step.type) return typeLabels?.[step.type] ?? step.type;
47
+ return `${index + 1}\uB2E8\uACC4`;
48
+ }
49
+ function formatDateTime(value) {
50
+ const time = Date.parse(value);
51
+ if (Number.isNaN(time)) return value;
52
+ const date = new Date(time);
53
+ const pad = (part) => String(part).padStart(2, "0");
54
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
55
+ }
56
+ var circleStyle = (bg, border, fg, ring) => ({
57
+ display: "inline-flex",
58
+ alignItems: "center",
59
+ justifyContent: "center",
60
+ width: 32,
61
+ height: 32,
62
+ flex: "none",
63
+ borderRadius: "var(--wp-radius-pill)",
64
+ background: bg,
65
+ border: `var(--wp-border-w) solid ${border}`,
66
+ color: fg,
67
+ boxShadow: ring ? "var(--wp-ring-selected)" : void 0
68
+ });
69
+ var nodeStyle = {
70
+ display: "flex",
71
+ flexDirection: "column",
72
+ alignItems: "center",
73
+ gap: "var(--wp-space-1)",
74
+ flex: "none",
75
+ minWidth: 88,
76
+ maxWidth: 140,
77
+ padding: "var(--wp-space-1)"
78
+ };
79
+ var nodeTextStyle = {
80
+ display: "block",
81
+ maxWidth: "100%",
82
+ overflow: "hidden",
83
+ textOverflow: "ellipsis",
84
+ whiteSpace: "nowrap",
85
+ textAlign: "center"
86
+ };
87
+ var connectorStyle = {
88
+ flex: "none",
89
+ width: "var(--wp-space-6)",
90
+ height: 0,
91
+ borderTop: "var(--wp-border-w) solid var(--wp-divider)",
92
+ // 원(32px) 세로 중앙에 맞춘다: 노드 padding(space-1) + 16px
93
+ marginTop: "calc(var(--wp-space-1) + 16px)"
94
+ };
95
+ function stepTooltip(step, index, typeLabels) {
96
+ const meta = STATUS_META[step.status ?? "pending"];
97
+ const lines = [`${stepTitle(step, index, typeLabels)} \u2014 ${meta.label}`];
98
+ if (step.approver) lines.push(`\uCC98\uB9AC\uC790: ${step.approver}`);
99
+ if (step.processedAt) lines.push(`\uCC98\uB9AC \uC77C\uC2DC: ${formatDateTime(step.processedAt)}`);
100
+ if (step.candidates && step.candidates.length > 0) {
101
+ lines.push(`\uACB0\uC7AC \uD6C4\uBCF4: ${step.candidates.join(", ")}`);
102
+ }
103
+ return /* @__PURE__ */ jsx(Fragment, { children: lines.map((line) => /* @__PURE__ */ jsx("span", { style: { display: "block" }, children: line }, line)) });
104
+ }
105
+ function ApprovalStepperImpl({
106
+ requester,
107
+ steps,
108
+ onStepClick,
109
+ typeLabels
110
+ }) {
111
+ const safeSteps = steps ?? [];
112
+ const clickable = typeof onStepClick === "function";
113
+ const requesterNode = requester ? /* @__PURE__ */ jsxs("div", { style: nodeStyle, children: [
114
+ /* @__PURE__ */ jsx(
115
+ "span",
116
+ {
117
+ style: circleStyle(
118
+ "var(--wp-surface-sunken)",
119
+ "var(--wp-border-subtle)",
120
+ "var(--wp-text-muted)",
121
+ false
122
+ ),
123
+ children: /* @__PURE__ */ jsx(Icon, { name: "user", size: 16 })
124
+ }
125
+ ),
126
+ /* @__PURE__ */ jsx("span", { className: "wp-body-sm", style: { ...nodeTextStyle, color: "var(--wp-text-strong)" }, children: requester.name }),
127
+ /* @__PURE__ */ jsx("span", { className: "wp-caption", style: nodeTextStyle, children: requester.sub?.trim() || "\uC2E0\uCCAD\uC790" })
128
+ ] }) : null;
129
+ if (safeSteps.length === 0) {
130
+ return /* @__PURE__ */ jsxs("div", { className: "wp-row", style: { overflowX: "auto", paddingBottom: "var(--wp-space-1)" }, children: [
131
+ requesterNode,
132
+ requesterNode ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: connectorStyle }) : null,
133
+ /* @__PURE__ */ jsxs("div", { style: nodeStyle, children: [
134
+ /* @__PURE__ */ jsxs(Badge, { tone: "success", size: "lg", children: [
135
+ /* @__PURE__ */ jsx(Icon, { name: "check-check", size: 14, style: { marginRight: "var(--wp-space-1)" } }),
136
+ "\uC790\uB3D9 \uC2B9\uC778"
137
+ ] }),
138
+ /* @__PURE__ */ jsx("span", { className: "wp-caption", style: { textAlign: "center", whiteSpace: "nowrap" }, children: "\uACB0\uC7AC \uB2E8\uACC4\uAC00 \uC5C6\uC5B4 \uC790\uB3D9\uC73C\uB85C \uC2B9\uC778\uB429\uB2C8\uB2E4" })
139
+ ] })
140
+ ] });
141
+ }
142
+ return /* @__PURE__ */ jsxs(
143
+ "div",
144
+ {
145
+ className: "wp-row",
146
+ role: "list",
147
+ "aria-label": "\uACB0\uC7AC \uC9C4\uD589 \uB2E8\uACC4",
148
+ style: {
149
+ alignItems: "flex-start",
150
+ gap: 0,
151
+ overflowX: "auto",
152
+ paddingBottom: "var(--wp-space-1)"
153
+ },
154
+ children: [
155
+ requesterNode,
156
+ safeSteps.map((step, index) => {
157
+ const status = step.status ?? "pending";
158
+ const meta = STATUS_META[status];
159
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
160
+ /* @__PURE__ */ jsx("span", { style: circleStyle(meta.bg, meta.border, meta.fg, status === "current"), children: /* @__PURE__ */ jsx(Icon, { name: meta.icon, size: 16 }) }),
161
+ /* @__PURE__ */ jsx(
162
+ "span",
163
+ {
164
+ className: "wp-body-sm",
165
+ style: {
166
+ ...nodeTextStyle,
167
+ color: status === "skipped" ? "var(--wp-text-muted)" : "var(--wp-text-strong)"
168
+ },
169
+ children: stepTitle(step, index, typeLabels)
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsx(
173
+ "span",
174
+ {
175
+ className: "wp-caption",
176
+ style: {
177
+ ...nodeTextStyle,
178
+ color: status === "rejected" ? "var(--wp-text-danger)" : status === "current" ? "var(--wp-text-brand)" : void 0
179
+ },
180
+ children: step.approver?.trim() || meta.label
181
+ }
182
+ )
183
+ ] });
184
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
185
+ (requesterNode || index > 0) && /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: connectorStyle }),
186
+ /* @__PURE__ */ jsx(Tooltip, { label: stepTooltip(step, index, typeLabels), wrap: true, placement: "top", children: clickable ? /* @__PURE__ */ jsx(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ role: "listitem",
191
+ onClick: () => onStepClick?.(step),
192
+ style: {
193
+ ...nodeStyle,
194
+ background: "none",
195
+ border: 0,
196
+ font: "inherit",
197
+ cursor: "pointer",
198
+ borderRadius: "var(--wp-radius-sm)"
199
+ },
200
+ children: body
201
+ }
202
+ ) : /* @__PURE__ */ jsx("div", { role: "listitem", style: nodeStyle, children: body }) })
203
+ ] }, step.id);
204
+ })
205
+ ]
206
+ }
207
+ );
208
+ }
209
+ var ApprovalStepper = boxed(ApprovalStepperImpl, "ApprovalStepper");
210
+
211
+ export { ApprovalStepper };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { CSSProperties, ReactNode, ComponentType } from 'react';
3
- import { S as SduiPage, a as SduiAction, b as SduiPropValue, c as SduiRule } from './types-BNdv5-jn.js';
4
- export { d as SduiArgRef, e as SduiCalcRef, f as SduiChild, g as SduiIndexRef, h as SduiItemRef, i as SduiNode, j as SduiPageQuery, k as SduiScalar, l as SduiStateRef } from './types-BNdv5-jn.js';
3
+ import { S as SduiPage, a as SduiAction, b as SduiPropValue, c as SduiRule } from './types-DmHC7HCN.js';
4
+ export { d as SduiArgRef, e as SduiCalcRef, f as SduiChild, g as SduiIndexRef, h as SduiItemRef, i as SduiNode, j as SduiPageQuery, k as SduiScalar, l as SduiStateRef } from './types-DmHC7HCN.js';
5
5
  import { S as SduiBoxProps, a as SduiContainerLayoutProps, b as SduiColumnsScale, c as SduiGapScale, d as SduiWeightScale, e as SduiTextAlignScale } from './layout-B1QrIBm6.js';
6
6
  export { C as CHILD_LAYOUT_PROPS, L as LAYOUT_CSS, f as SduiAlignScale, g as SduiChildLayoutProps, h as SduiSizeScale, i as SduiSpanScale, j as childLayoutFrom, k as columnsClass, l as containerLayoutStyle, m as joinClass, n as mergeStyle, t as typographyStyle } from './layout-B1QrIBm6.js';
7
7
  import { D as DatePickerProps, a as DateRangePickerProps, T as TimePickerProps } from './DatePicker-D2EtpvkQ.js';
@@ -19,6 +19,11 @@ interface SduiAdapter {
19
19
  * submitForm 등 JSON API 호출. 성공 시 응답 JSON을, 실패 시
20
20
  * Error(사용자에게 보여줄 메시지)를 던진다.
21
21
  *
22
+ * 던지는 오류에 **`status`(HTTP 상태)와 `body`(파싱된 응답 본문)를 얹어도 된다.**
23
+ * 필수는 아니지만, 얹으면 스펙이 상태 코드로 갈라 쓸 수 있다(`request` 액션의
24
+ * `errorKey` 참고 — 409의 위반 목록을 화면에 그리는 것이 그 자리다). 라이브러리는
25
+ * 있으면 읽고 없으면 메시지만 쓴다 — 특정 앱의 오류 타입을 알지 않는다.
26
+ *
22
27
  * `signal`은 호출부가 중단할 수 있는 조회(목록 재조회 등)에서만 넘어온다 —
23
28
  * 선택 사항이라 무시해도 계약은 성립하지만, 넘겨받은 호스트는 자기 클라이언트에
24
29
  * 그대로 흘려 보내면 파라미터가 바뀔 때 이전 요청이 실제로 끊긴다.
@@ -28,8 +33,25 @@ interface SduiAdapter {
28
33
  body?: unknown;
29
34
  signal?: AbortSignal;
30
35
  }) => Promise<unknown>;
31
- /** download 액션용 원시 fetch. 기본: fetch(path, { credentials: 'include' }) */
32
- rawFetch?: (path: string) => Promise<Response>;
36
+ /**
37
+ * 원시 fetch — **직렬화하지 않는 통로**다. `fetch`가 본문을 JSON으로 굳혀 보내는 것과
38
+ * 정반대이고, 그 차이가 이 항목의 존재 이유다: 업로드의 `FormData`는 경계 문자열
39
+ * (boundary)을 브라우저가 붙여야 하는데 `JSON.stringify`를 태우면 `"{}"`가 된다.
40
+ *
41
+ * `init`을 받는다. 예전에는 경로 하나만 받아 download 액션의 GET만 낼 수 있었고,
42
+ * multipart POST나 DELETE를 실을 자리가 없었다.
43
+ *
44
+ * ⚠ **호스트 구현을 1인자로 남겨 두면 안 된다.** `(path) => myFetch(path)`처럼 쓰면
45
+ * 타입은 통과하는데 `init`이 조용히 버려져 POST가 본문 없는 GET으로 나간다.
46
+ * 타입 검사로도 {@link hostRawFetchOf}로도 잡히지 않는 자리라 여기 적어 둔다.
47
+ */
48
+ rawFetch?: (path: string, init?: {
49
+ method?: string;
50
+ /** 직렬화하지 않고 그대로 흘린다 — FormData·Blob·string 전부 가능. */
51
+ body?: BodyInit;
52
+ headers?: Record<string, string>;
53
+ signal?: AbortSignal;
54
+ }) => Promise<Response>;
33
55
  /** invalidate 액션 — 클라이언트 조회 캐시 무효화 (TanStack Query 등) */
34
56
  invalidate?: (prefix: string, exact: boolean) => void;
35
57
  /** refresh 액션 — 페이지 스펙 재조회 */
@@ -1683,6 +1705,17 @@ declare function SduiRenderer({ page, registry, adapter, syncUrlState, initialSt
1683
1705
  */
1684
1706
  declare const CALC_OPS: ReadonlySet<string>;
1685
1707
 
1708
+ /**
1709
+ * 선택적 peer 의존성(recharts · ol)을 런타임에 끌어오는 공용 장치.
1710
+ *
1711
+ * 이 진입점(@wipco/sdui/rich)의 위젯은 무거운 라이브러리를 쓰지만, 그 라이브러리는
1712
+ * optional peerDependency다 — 안 깔려 있어도 렌더가 죽으면 안 된다. 그래서
1713
+ * 모듈은 동적 import로만 만지고, 로드에 실패하면 예외를 그대로 흘리지 않고
1714
+ * "무엇을 설치해야 하는지" 알려주는 플레이스홀더로 떨어진다.
1715
+ */
1716
+
1717
+ declare function loadOptionalModule<T>(load: () => Promise<T>, packageName: string): Promise<T>;
1718
+
1686
1719
  /**
1687
1720
  * 스펙 구조 검증기 — 서버(또는 편집기)가 만든 스펙의 형태 오류를 렌더 전에 찾아낸다.
1688
1721
  * 렌더러는 개발 모드에서 이 결과를 콘솔에 경고하고, 관리자 편집기는
@@ -1837,4 +1870,4 @@ interface ImageViewerProps {
1837
1870
  */
1838
1871
  declare function ImageViewer({ src, alt, open, onClose }: ImageViewerProps): React__default.ReactPortal | null;
1839
1872
 
1840
- export { Actions, type ActionsProps, Alert, type AlertProps, Avatar, type AvatarProps, Badge, type BadgeProps, type BadgeTone, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, CALC_OPS, Can, type CanProps, Card, type CardProps, type CascadingSearchOption, Checkbox, type CheckboxProps, Col, type ColProps, type CustomActionHandler, type CustomValidator, DatePickerProps, DateRangePickerProps, Dialog, type DialogProps, Divider, EmptyState, type EmptyStateProps, Field, type FieldProps, Grid, type GridCardConfig, type GridCardRow, type GridProps, Icon, IconButton, type IconButtonProps, type IconProps, ImageViewer, type ImageViewerProps, Input, type InputProps, type Justify, type KeyValueItem, KeyValueList, type KeyValueListProps, type MeterTone, NodeErrorBoundary, OverlayContainerContext, OverlayLayer, PageHead, type PageHeadProps, Pagination, type PaginationProps, ProgressBar, type ProgressBarProps, Radio, type RadioProps, RemoteSearchResults, type RemoteSearchResultsProps, ResultTable, type ResultTableColumn, type ResultTableProps, Row, type RowProps, SduiAction, type SduiAdapter, type SduiAssetContext, type SduiAssetResolver, type SduiBadgeCell, SduiBoxProps, type SduiCanFn, type SduiCanQuery, type SduiCanSetting, type SduiCell, SduiColumnsScale, SduiContainerLayoutProps, type SduiDateCell, SduiDialog, type SduiDispatch, SduiForm, type SduiFormConfig, type SduiFormProps, SduiGapScale, type SduiGridCard, type SduiGridCardAction, type SduiGridCardRow, type SduiIconCell, type SduiImageCell, type SduiLinkCell, type SduiNumberCell, SduiPage, SduiPermissionProvider, type SduiProgressCell, SduiPropValue, type SduiRegistry, SduiRenderer, type SduiRendererProps, type SduiRowCondition, SduiRule, type SduiRuntime, type SduiSearchAction, type SduiSearchColumn, SduiSearchDataProvider, SduiSearchResults, type SduiSearchResultsProps, type SduiTagCell, SduiTextAlignScale, type SduiTextCell, type SduiUseSpec, SduiWeightScale, type SearchAction, type SearchColumn, type SearchFetcher, type SearchField, type SearchFilter, type SearchOption, type SearchPage, SearchResults, type SearchResultsProps, type SearchRow, SearchToolbar, type SearchValues, type SearchViewItem, Section, type SectionProps, SegmentedControl, type SegmentedControlProps, type SegmentedItem, Select, type SelectOption, type SelectOptionRecord, type SelectProps, Show, type ShowProps, SidebarNav, type SidebarNavItem, type SidebarNavProps, type SidebarNavSection, type SortDir, SortHandle, type SortHandleProps, SortableList, type SortableListProps, Spacer, type SpacerProps, type SpecIssue, Spinner, type SpinnerProps, StatCard, type StatCardProps, Switch, type SwitchProps, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Tag, type TagProps, Text, type TextPreset, type TextProps, Textarea, type TextareaProps, TimePickerProps, Toast, type ToastProps, Tooltip, type TooltipProps, boxed, cascadingValuePaths, cellRenderer, cleanSearchValues, createRegistry, defaultAdapter, defaultRegistry, getOverlayRoot, imageRenderer, includesLooseSearch, joinMultiValue, normalizePageSizes, normalizeSearchText, searchFieldKeys, searchValuesFromParams, sortableDropIndex, sortableMove, splitMultiValue, useOverlayLayer, useRemoteSearchPage, useSduiAssetResolver, useSduiCan, useSduiDispatch, useSduiRuntime, useSduiSandbox, useSearchFetcher, validateSpec };
1873
+ export { Actions, type ActionsProps, Alert, type AlertProps, Avatar, type AvatarProps, Badge, type BadgeProps, type BadgeTone, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, CALC_OPS, Can, type CanProps, Card, type CardProps, type CascadingSearchOption, Checkbox, type CheckboxProps, Col, type ColProps, type CustomActionHandler, type CustomValidator, DatePickerProps, DateRangePickerProps, Dialog, type DialogProps, Divider, EmptyState, type EmptyStateProps, Field, type FieldProps, Grid, type GridCardConfig, type GridCardRow, type GridProps, Icon, IconButton, type IconButtonProps, type IconProps, ImageViewer, type ImageViewerProps, Input, type InputProps, type Justify, type KeyValueItem, KeyValueList, type KeyValueListProps, type MeterTone, NodeErrorBoundary, OverlayContainerContext, OverlayLayer, PageHead, type PageHeadProps, Pagination, type PaginationProps, ProgressBar, type ProgressBarProps, Radio, type RadioProps, RemoteSearchResults, type RemoteSearchResultsProps, ResultTable, type ResultTableColumn, type ResultTableProps, Row, type RowProps, SduiAction, type SduiAdapter, type SduiAssetContext, type SduiAssetResolver, type SduiBadgeCell, SduiBoxProps, type SduiCanFn, type SduiCanQuery, type SduiCanSetting, type SduiCell, SduiColumnsScale, SduiContainerLayoutProps, type SduiDateCell, SduiDialog, type SduiDispatch, SduiForm, type SduiFormConfig, type SduiFormProps, SduiGapScale, type SduiGridCard, type SduiGridCardAction, type SduiGridCardRow, type SduiIconCell, type SduiImageCell, type SduiLinkCell, type SduiNumberCell, SduiPage, SduiPermissionProvider, type SduiProgressCell, SduiPropValue, type SduiRegistry, SduiRenderer, type SduiRendererProps, type SduiRowCondition, SduiRule, type SduiRuntime, type SduiSearchAction, type SduiSearchColumn, SduiSearchDataProvider, SduiSearchResults, type SduiSearchResultsProps, type SduiTagCell, SduiTextAlignScale, type SduiTextCell, type SduiUseSpec, SduiWeightScale, type SearchAction, type SearchColumn, type SearchFetcher, type SearchField, type SearchFilter, type SearchOption, type SearchPage, SearchResults, type SearchResultsProps, type SearchRow, SearchToolbar, type SearchValues, type SearchViewItem, Section, type SectionProps, SegmentedControl, type SegmentedControlProps, type SegmentedItem, Select, type SelectOption, type SelectOptionRecord, type SelectProps, Show, type ShowProps, SidebarNav, type SidebarNavItem, type SidebarNavProps, type SidebarNavSection, type SortDir, SortHandle, type SortHandleProps, SortableList, type SortableListProps, Spacer, type SpacerProps, type SpecIssue, Spinner, type SpinnerProps, StatCard, type StatCardProps, Switch, type SwitchProps, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Tag, type TagProps, Text, type TextPreset, type TextProps, Textarea, type TextareaProps, TimePickerProps, Toast, type ToastProps, Tooltip, type TooltipProps, boxed, cascadingValuePaths, cellRenderer, cleanSearchValues, createRegistry, defaultAdapter, defaultRegistry, getOverlayRoot, imageRenderer, includesLooseSearch, joinMultiValue, loadOptionalModule, normalizePageSizes, normalizeSearchText, searchFieldKeys, searchValuesFromParams, sortableDropIndex, sortableMove, splitMultiValue, useOverlayLayer, useRemoteSearchPage, useSduiAssetResolver, useSduiCan, useSduiDispatch, useSduiRuntime, useSduiSandbox, useSearchFetcher, validateSpec };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { TimePicker } from './chunk-PNVOE5VH.js';
2
- export { TimePicker } from './chunk-PNVOE5VH.js';
1
+ import { TimePicker } from './chunk-36MXPCJP.js';
2
+ export { TimePicker, loadOptionalModule } from './chunk-36MXPCJP.js';
3
3
  import { ImageViewer } from './chunk-THEP3VWY.js';
4
4
  export { ImageViewer } from './chunk-THEP3VWY.js';
5
5
  import { normalizeSearchFields, searchValuesFromParams, splitMultiValue, includesLooseSearch, cascadingValuePaths, SearchToolbar, searchFieldKeys, Popover } from './chunk-U7ZK7WYH.js';
6
6
  export { SearchToolbar, cascadingValuePaths, cleanSearchValues, includesLooseSearch, joinMultiValue, normalizeSearchText, searchFieldKeys, searchValuesFromParams, splitMultiValue } from './chunk-U7ZK7WYH.js';
7
- import { DEV, isSafeNavigationTarget, useSduiAdapter, hostFetchOf, useSduiSandbox, resolveAdapter, assetResolverOf, SduiAdapterContext } from './chunk-WGRB5UL4.js';
8
- export { defaultAdapter, useSduiAssetResolver, useSduiSandbox } from './chunk-WGRB5UL4.js';
7
+ import { DEV, isSafeNavigationTarget, useSduiAdapter, hostFetchOf, useSduiSandbox, resolveAdapter, assetResolverOf, SduiAdapterContext } from './chunk-DVOPZFQS.js';
8
+ export { defaultAdapter, useSduiAssetResolver, useSduiSandbox } from './chunk-DVOPZFQS.js';
9
9
  import { HoistedStyle, DateRangePicker, DatePicker, OverlayLayer } from './chunk-XKLUCUVA.js';
10
10
  export { DatePicker, DateRangePicker, OverlayContainerContext, OverlayLayer, getOverlayRoot, useOverlayLayer } from './chunk-XKLUCUVA.js';
11
11
  import { EmptyState, Button, Checkbox, SegmentedControl, Spinner, Select, Pagination, Icon, Card, Badge, ProgressBar, Tag, Toast, Textarea, Tabs, Table, Switch, StatCard, Input, IconButton, Field, Alert, warnOnce, Dialog } from './chunk-WIDDHR7L.js';
@@ -2108,6 +2108,17 @@ async function runRequestAction(action, deps) {
2108
2108
  (action.onSuccess ?? []).forEach((a) => deps.dispatch(a, response));
2109
2109
  } catch (error) {
2110
2110
  const message = error instanceof Error ? error.message : "\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4";
2111
+ if (typeof action.errorKey === "string" && action.errorKey !== "") {
2112
+ const detail = error;
2113
+ const value = { message };
2114
+ if (typeof detail?.status === "number") value.status = detail.status;
2115
+ if (detail?.body !== void 0) value.body = detail.body;
2116
+ deps.dispatch({
2117
+ action: "setState",
2118
+ key: action.errorKey,
2119
+ value
2120
+ });
2121
+ }
2111
2122
  if (action.onError?.length) {
2112
2123
  action.onError.forEach((a) => deps.dispatch(a, message));
2113
2124
  } else {
package/dist/meta.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as SduiPropValue, i as SduiNode } from './types-BNdv5-jn.js';
1
+ import { b as SduiPropValue, i as SduiNode } from './types-DmHC7HCN.js';
2
2
 
3
3
  /**
4
4
  * 시각 빌더용 컴포넌트 메타데이터 스키마.
@@ -423,6 +423,7 @@ declare const richComponentMeta: {
423
423
  * 그리지 않는 편이 낫다고 보고 'none'으로 뒀다.
424
424
  */
425
425
 
426
+ declare const ApprovalStepperMeta: SduiComponentMeta;
426
427
  declare const AutocompleteFieldMeta: SduiComponentMeta;
427
428
  declare const InlineEditMeta: SduiComponentMeta;
428
429
  declare const JsonFieldMeta: SduiComponentMeta;
@@ -456,6 +457,7 @@ declare const StripMeta: SduiComponentMeta;
456
457
  * 프롭을 못 그린다.
457
458
  */
458
459
  declare const widgetComponentMeta: {
460
+ ApprovalStepper: SduiComponentMeta;
459
461
  AutocompleteField: SduiComponentMeta;
460
462
  ColumnBoard: SduiComponentMeta;
461
463
  DiffView: SduiComponentMeta;
@@ -588,4 +590,4 @@ declare function paletteGroups(catalog?: Record<string, SduiComponentMeta>): Arr
588
590
  items: SduiComponentMeta[];
589
591
  }>;
590
592
 
591
- export { ActionsMeta, AlertMeta, 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, 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 };
593
+ 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, 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
@@ -3293,6 +3293,106 @@ var treeNodeItem = [
3293
3293
  itemKind: "object"
3294
3294
  }
3295
3295
  ];
3296
+ var ApprovalStepperMeta = {
3297
+ name: "ApprovalStepper",
3298
+ label: "\uACB0\uC7AC \uC9C4\uD589 \uC2A4\uD14C\uD37C",
3299
+ category: "display",
3300
+ icon: "workflow",
3301
+ description: '\uC2E0\uCCAD\uC790 \u2192 \uACB0\uC7AC \uB2E8\uACC4\uB97C \uAC00\uB85C\uB85C \uB098\uC5F4\uD558\uB294 \uC77D\uAE30 \uC804\uC6A9 \uC2A4\uD14C\uD37C. \uB2E8\uACC4\uAC00 \uD558\uB098\uB3C4 \uC5C6\uC73C\uBA74 "\uC790\uB3D9 \uC2B9\uC778"\uC73C\uB85C \uD45C\uC2DC\uD55C\uB2E4.',
3302
+ props: [
3303
+ {
3304
+ name: "steps",
3305
+ kind: "array",
3306
+ label: "\uACB0\uC7AC \uB2E8\uACC4",
3307
+ required: true,
3308
+ bindable: true,
3309
+ description: "\uBE48 \uBC30\uC5F4\uC774\uBA74 \uC790\uB3D9 \uC2B9\uC778\uC73C\uB85C \uADF8\uB9B0\uB2E4(\uBC31\uC5D4\uB4DC \uD574\uC11D\uAE30\uC640 \uAC19\uC740 \uC758\uBBF8).",
3310
+ group: "data",
3311
+ item: [
3312
+ { name: "id", kind: "string", label: "\uB2E8\uACC4 id", required: true },
3313
+ { name: "name", kind: "string", label: "\uB2E8\uACC4\uBA85" },
3314
+ {
3315
+ name: "type",
3316
+ kind: "string",
3317
+ label: "\uC720\uD615",
3318
+ description: "`typeLabels`\uC5D0\uC11C \uD45C\uC2DC\uBA85\uC744 \uCC3E\uB294 \uD0A4. \uB2E8\uACC4\uBA85\uC774 \uC5C6\uC744 \uB54C\uB9CC \uC4F0\uC778\uB2E4 \u2014 \uC870\uC9C1\uB9C8\uB2E4 \uC5B4\uD718\uAC00 \uB2EC\uB77C \uC5F4\uB9B0 \uBB38\uC790\uC5F4\uC774\uB2E4(\uC608: 'team_leader')."
3319
+ },
3320
+ {
3321
+ name: "status",
3322
+ kind: "enum",
3323
+ label: "\uC0C1\uD0DC",
3324
+ description: "\uBBF8\uC9C0\uC815\uC774\uBA74 \uB300\uAE30\uB85C \uBCF8\uB2E4.",
3325
+ // 스펙에는 approved·current·pending만 등장하지만(각 4·4·1), rejected·skipped도
3326
+ // 구현이 실제로 그리는 상태다(STATUS_META 5종). 값 후보는 구현 기준으로 적는다 —
3327
+ // 실사용이 없다는 이유로 빼면 편집기가 반려 상태를 만들 수단을 잃는다.
3328
+ options: [
3329
+ { value: "pending", label: "\uB300\uAE30" },
3330
+ { value: "current", label: "\uC9C4\uD589 \uC911" },
3331
+ { value: "approved", label: "\uC2B9\uC778" },
3332
+ { value: "rejected", label: "\uBC18\uB824" },
3333
+ { value: "skipped", label: "\uAC74\uB108\uB700" }
3334
+ ]
3335
+ },
3336
+ { name: "approver", kind: "string", label: "\uCC98\uB9AC\uC790", description: "\uC2B9\uC778\xB7\uBC18\uB824\uB41C \uB2E8\uACC4\uC5D0\uC11C \uD234\uD301\uC5D0 \uBCF4\uC778\uB2E4." },
3337
+ {
3338
+ name: "processedAt",
3339
+ kind: "string",
3340
+ label: "\uCC98\uB9AC \uC77C\uC2DC",
3341
+ description: "ISO \uBB38\uC790\uC5F4 \uAD8C\uC7A5 \u2014 \uD30C\uC2F1\uC5D0 \uC2E4\uD328\uD558\uBA74 \uC6D0\uBB38\uC744 \uADF8\uB300\uB85C \uBCF4\uC5EC\uC900\uB2E4."
3342
+ },
3343
+ { name: "candidates", kind: "array", label: "\uACB0\uC7AC \uD6C4\uBCF4", item: [], itemKind: "string" }
3344
+ ]
3345
+ },
3346
+ {
3347
+ name: "requester",
3348
+ kind: "object",
3349
+ label: "\uC2E0\uCCAD\uC790",
3350
+ bindable: true,
3351
+ description: "\uC788\uC73C\uBA74 \uB9E8 \uC55E\uC5D0 \uC2E0\uCCAD\uC790 \uB178\uB4DC\uB97C \uADF8\uB9B0\uB2E4.",
3352
+ group: "data",
3353
+ fields: [
3354
+ { name: "name", kind: "string", label: "\uC774\uB984", required: true },
3355
+ { name: "sub", kind: "string", label: "\uC18C\uC18D\xB7\uC9C1\uAE09", description: '\uBE44\uBA74 "\uC2E0\uCCAD\uC790"\uB85C \uB300\uC2E0 \uD45C\uC2DC\uD55C\uB2E4.' }
3356
+ ]
3357
+ },
3358
+ {
3359
+ name: "typeLabels",
3360
+ kind: "object",
3361
+ label: "\uC720\uD615 \uD45C\uC2DC\uBA85",
3362
+ bindable: true,
3363
+ description: '\uC720\uD615 \uCF54\uB4DC \u2192 \uD45C\uC2DC\uBA85. \uC548 \uC8FC\uBA74 \uCF54\uB4DC\uB97C \uADF8\uB300\uB85C \uBCF4\uC5EC\uC8FC\uACE0, \uC720\uD615\uB3C4 \uC5C6\uC73C\uBA74 "N\uB2E8\uACC4"\uB85C \uB5A8\uC5B4\uC9C4\uB2E4.',
3364
+ group: "content",
3365
+ advanced: true
3366
+ },
3367
+ {
3368
+ name: "onStepClick",
3369
+ kind: "action",
3370
+ label: "\uB2E8\uACC4 \uD074\uB9AD",
3371
+ eventArg: "\uD074\uB9AD\uD55C \uB2E8\uACC4 \uAC1D\uCCB4",
3372
+ description: "\uD578\uB4E4\uB7EC\uB97C \uC8FC\uBA74 \uB2E8\uACC4 \uB178\uB4DC\uAC00 \uD074\uB9AD \uAC00\uB2A5\uD574\uC9C4\uB2E4.",
3373
+ group: "behavior",
3374
+ advanced: true
3375
+ }
3376
+ // 실측 0/6 — 구현에는 있다
3377
+ ],
3378
+ children: { accepts: false },
3379
+ dom: { element: "div" },
3380
+ // 실측: 자동 승인 분기·일반 분기 모두 <div class="wp-row">
3381
+ // 순수 렌더라 프리셋에 위험이 없다. 세 가지 상태가 한눈에 보이게 골랐다.
3382
+ preset: {
3383
+ component: "ApprovalStepper",
3384
+ props: {
3385
+ requester: { name: "\uAE40\uB3C4\uD604", sub: "\uACBD\uC601\uC9C0\uC6D0\uD300 \xB7 \uB300\uB9AC" },
3386
+ typeLabels: { team_leader: "\uD300\uC7A5", division_head: "\uBCF8\uBD80\uC7A5\xB7\uC2E4\uC7A5", custom: "\uC0AC\uC6A9\uC790 \uC9C0\uC815" },
3387
+ steps: [
3388
+ { id: "step-1", type: "team_leader", status: "approved", approver: "\uC774\uC11C\uC5F0" },
3389
+ { id: "step-2", name: "\uBCF8\uBD80\uC7A5", type: "division_head", status: "current" },
3390
+ { id: "step-3", name: "\uACBD\uC601\uC9C0\uC6D0 \uD655\uC778", type: "custom", status: "pending" }
3391
+ ]
3392
+ }
3393
+ },
3394
+ resize: { kind: "none" }
3395
+ };
3296
3396
  var AutocompleteFieldMeta = {
3297
3397
  name: "AutocompleteField",
3298
3398
  label: "\uC790\uB3D9\uC644\uC131",
@@ -4710,6 +4810,7 @@ var StripMeta = {
4710
4810
  resize: { kind: "none" }
4711
4811
  };
4712
4812
  var widgetComponentMeta = {
4813
+ ApprovalStepper: ApprovalStepperMeta,
4713
4814
  AutocompleteField: AutocompleteFieldMeta,
4714
4815
  ColumnBoard: ColumnBoardMeta,
4715
4816
  DiffView: DiffViewMeta,
@@ -4767,4 +4868,4 @@ function paletteGroups(catalog = allComponentMeta) {
4767
4868
  })).filter((group) => group.items.length > 0);
4768
4869
  }
4769
4870
 
4770
- export { ActionsMeta, AlertMeta, 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, 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 };
4871
+ 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, 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 };
package/dist/rich.js CHANGED
@@ -1,5 +1,6 @@
1
- export { TimePicker } from './chunk-PNVOE5VH.js';
2
- import { useSduiAssetResolver } from './chunk-WGRB5UL4.js';
1
+ import { useOptionalModule, DependencyPlaceholder } from './chunk-36MXPCJP.js';
2
+ export { TimePicker } from './chunk-36MXPCJP.js';
3
+ import { useSduiAssetResolver } from './chunk-DVOPZFQS.js';
3
4
  import { HoistedStyle } from './chunk-XKLUCUVA.js';
4
5
  export { DatePicker, DateRangePicker } from './chunk-XKLUCUVA.js';
5
6
  import { Spinner, EmptyState } from './chunk-WIDDHR7L.js';
@@ -7,55 +8,6 @@ import './chunk-SAN4IX6N.js';
7
8
  import { useRef, useCallback, useMemo, useState, useEffect, useLayoutEffect } from 'react';
8
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
10
 
10
- function useOptionalModule(load) {
11
- const [state, setState] = useState({ status: "loading" });
12
- useEffect(() => {
13
- let alive = true;
14
- load().then(
15
- (module) => {
16
- if (alive) setState({ status: "ready", module });
17
- },
18
- (error) => {
19
- if (!alive) return;
20
- console.warn("[sdui] \uC120\uD0DD \uC758\uC874\uC131 \uB85C\uB4DC \uC2E4\uD328 \u2014 \uD50C\uB808\uC774\uC2A4\uD640\uB354\uB85C \uB300\uCCB4\uD569\uB2C8\uB2E4", error);
21
- setState({ status: "missing" });
22
- }
23
- );
24
- return () => {
25
- alive = false;
26
- };
27
- }, [load]);
28
- return state;
29
- }
30
- function DependencyPlaceholder({
31
- packageName,
32
- purpose,
33
- height,
34
- loading = false
35
- }) {
36
- return /* @__PURE__ */ jsx(
37
- "div",
38
- {
39
- style: {
40
- minHeight: height,
41
- display: "flex",
42
- alignItems: "center",
43
- justifyContent: "center",
44
- border: "1px solid var(--wp-border-subtle)",
45
- borderRadius: "var(--wp-radius-md)",
46
- background: "var(--wp-surface-sunken)"
47
- },
48
- children: loading ? /* @__PURE__ */ jsx(Spinner, { label: `${purpose} \uC900\uBE44 \uC911` }) : /* @__PURE__ */ jsxs(EmptyState, { tight: true, icon: "box", title: `${purpose}\uB97C \uADF8\uB9AC\uB824\uBA74 ${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4`, children: [
49
- "\uC120\uD0DD \uC758\uC874\uC131\uC774\uB77C \uAE30\uBCF8 \uC124\uCE58\uC5D0\uB294 \uD3EC\uD568\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 ",
50
- /* @__PURE__ */ jsxs("code", { children: [
51
- "pnpm add ",
52
- packageName
53
- ] }),
54
- " \uD6C4 \uB2E4\uC2DC \uC5F4\uC5B4 \uC8FC\uC138\uC694."
55
- ] })
56
- }
57
- );
58
- }
59
11
  var loadRecharts = () => import('recharts');
60
12
  var SERIES_FALLBACK = [
61
13
  "#1d2c60",
package/dist/styles.css CHANGED
@@ -228,6 +228,9 @@
228
228
  color var(--wp-duration-fast) var(--wp-ease-standard),
229
229
  box-shadow var(--wp-duration-fast) var(--wp-ease-standard);
230
230
  --wp-transition-transform: transform var(--wp-duration-base) var(--wp-ease-standard);
231
+ /* 면(surface)의 강조 전이 — 드롭 영역·카드처럼 배경과 그림자가 함께 바뀌는 자리. */
232
+ --wp-transition-surface: background-color var(--wp-duration-base) var(--wp-ease-standard),
233
+ box-shadow var(--wp-duration-base) var(--wp-ease-standard);
231
234
  --wp-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
232
235
 
233
236
  /* 별칭 — 이관 CSS가 의미 이름으로 참조하는 자리 */
@@ -202,6 +202,23 @@ type SduiAction = {
202
202
  onSuccess?: SduiAction[];
203
203
  /** 실패 시 순차 실행 (이벤트 값 = 에러 메시지). 생략하면 danger 토스트 */
204
204
  onError?: SduiAction[];
205
+ /**
206
+ * 실패 상세를 담을 상태 키. 주면 `onError` 체인이 돌기 **직전에**
207
+ * `{ status, message, body }` 를 여기 심는다.
208
+ *
209
+ * `onError` 의 이벤트 값은 예전 그대로 **문자열 메시지**다(기존 스펙 불변).
210
+ * 상태 코드나 응답 본문으로 갈라 써야 할 때만 이 키를 준다 — 예컨대 서버가
211
+ * 409 로 거절하며 위반 목록을 실어 보내면, 그것을 화면에 그리는 길이 여기다:
212
+ *
213
+ * { "action": "request", "endpoint": "…", "errorKey": "saveError",
214
+ * "onError": [{ "action": "toast", "tone": "danger",
215
+ * "message": { "$state": "saveError.message" } }] }
216
+ * → ForEach source: { "$state": "saveError.body.violations" }
217
+ *
218
+ * `status`·`body` 는 호스트 어댑터가 오류에 얹어 줬을 때만 채워진다
219
+ * (계약은 {@link SduiAdapter.fetch} 참고). 안 얹으면 message 만 남는다.
220
+ */
221
+ errorKey?: string;
205
222
  }
206
223
  /**
207
224
  * 단발 API 호출 — submitForm에서 폼 수집·검증을 뺀 것. 서버의 2단계 커밋 계약
@@ -218,6 +235,23 @@ type SduiAction = {
218
235
  onSuccess?: SduiAction[];
219
236
  /** 실패 시 순차 실행 (이벤트 값 = 에러 메시지). 생략하면 danger 토스트 */
220
237
  onError?: SduiAction[];
238
+ /**
239
+ * 실패 상세를 담을 상태 키. 주면 `onError` 체인이 돌기 **직전에**
240
+ * `{ status, message, body }` 를 여기 심는다.
241
+ *
242
+ * `onError` 의 이벤트 값은 예전 그대로 **문자열 메시지**다(기존 스펙 불변).
243
+ * 상태 코드나 응답 본문으로 갈라 써야 할 때만 이 키를 준다 — 예컨대 서버가
244
+ * 409 로 거절하며 위반 목록을 실어 보내면, 그것을 화면에 그리는 길이 여기다:
245
+ *
246
+ * { "action": "request", "endpoint": "…", "errorKey": "saveError",
247
+ * "onError": [{ "action": "toast", "tone": "danger",
248
+ * "message": { "$state": "saveError.message" } }] }
249
+ * → ForEach source: { "$state": "saveError.body.violations" }
250
+ *
251
+ * `status`·`body` 는 호스트 어댑터가 오류에 얹어 줬을 때만 채워진다
252
+ * (계약은 {@link SduiAdapter.fetch} 참고). 안 얹으면 message 만 남는다.
253
+ */
254
+ errorKey?: string;
221
255
  /** 성공 시 무효화할 조회 prefix — onSuccess의 invalidate 액션 단축 */
222
256
  invalidate?: string;
223
257
  }
@@ -0,0 +1,41 @@
1
+ import { S as SduiBoxProps } from '../layout-B1QrIBm6.js';
2
+ import React__default from 'react';
3
+
4
+ interface ApprovalStepperStep {
5
+ id: string;
6
+ name?: string;
7
+ /**
8
+ * 단계 유형. `typeLabels`에서 표시명을 찾는 키다 — 열린 문자열이라 조직마다
9
+ * 자기 어휘를 쓴다. 이름(`name`)이 있으면 그쪽이 이긴다.
10
+ */
11
+ type?: string;
12
+ status?: 'pending' | 'current' | 'approved' | 'rejected' | 'skipped';
13
+ /** 처리한 결재자 이름(승인/반려된 단계). */
14
+ approver?: string;
15
+ /** 처리 일시(ISO 문자열 권장 — 파싱 실패 시 원문 그대로 표시). */
16
+ processedAt?: string;
17
+ /** 이 단계에서 결재할 수 있는 후보자 이름들. */
18
+ candidates?: string[];
19
+ }
20
+ interface ApprovalStepperProps {
21
+ /** 결재를 올린 사람. 있으면 맨 앞에 신청자 노드를 그린다. */
22
+ requester?: {
23
+ name: string;
24
+ sub?: string;
25
+ };
26
+ steps: ApprovalStepperStep[];
27
+ /** 단계 클릭 핸들러 — 첫 인자(step 객체)가 SDUI 이벤트 값으로 저장된다. */
28
+ onStepClick?: (step: ApprovalStepperStep) => void;
29
+ /**
30
+ * 유형 코드 → 표시명. 단계에 `name`이 없을 때만 쓰인다.
31
+ *
32
+ * 결재선의 유형 어휘는 조직마다 다르므로(팀장/본부장은 한 회사의 직제다) 여기서
33
+ * 받는다. 안 주면 유형 코드를 그대로 보여주고, 유형도 없으면 `N단계`로 떨어진다 —
34
+ * 어느 쪽이든 화면이 서고, 라이브러리가 특정 회사의 직제를 알지 않는다.
35
+ */
36
+ typeLabels?: Record<string, string>;
37
+ }
38
+ /** 바깥에서 온 `style`·`className`을 뿌리에 얹어 내보낸다 — 자기 축(폭·여백)이 닿게 하는 통로. */
39
+ declare const ApprovalStepper: React__default.FC<ApprovalStepperProps & SduiBoxProps>;
40
+
41
+ export { ApprovalStepper, type ApprovalStepperProps, type ApprovalStepperStep };
@@ -0,0 +1,3 @@
1
+ export { ApprovalStepper } from '../chunk-ZTQEP3TR.js';
2
+ import '../chunk-WIDDHR7L.js';
3
+ import '../chunk-SAN4IX6N.js';
@@ -1,6 +1,6 @@
1
- export { AutocompleteField, endpointSearchFn, toAutocompleteOptions } from '../chunk-WZKDOZL2.js';
1
+ export { AutocompleteField, endpointSearchFn, toAutocompleteOptions } from '../chunk-OG62LSFH.js';
2
2
  import '../chunk-U7ZK7WYH.js';
3
- import '../chunk-WGRB5UL4.js';
3
+ import '../chunk-DVOPZFQS.js';
4
4
  import '../chunk-XKLUCUVA.js';
5
5
  import '../chunk-WIDDHR7L.js';
6
6
  import '../chunk-SAN4IX6N.js';
@@ -38,10 +38,12 @@ import { DisclosureProps } from './Disclosure.js';
38
38
  export { Disclosure } from './Disclosure.js';
39
39
  import { DiffViewProps } from './DiffView.js';
40
40
  export { DiffField, DiffList, DiffView } from './DiffView.js';
41
- import * as React from 'react';
42
- import { S as SduiBoxProps } from '../layout-B1QrIBm6.js';
43
41
  import { ColumnBoardProps } from './ColumnBoard.js';
44
42
  export { ColumnBoard, ColumnBoardColumn, ColumnBoardItem, ColumnBoardTone } from './ColumnBoard.js';
43
+ import * as React from 'react';
44
+ import { S as SduiBoxProps } from '../layout-B1QrIBm6.js';
45
+ import { ApprovalStepperProps } from './ApprovalStepper.js';
46
+ export { ApprovalStepper, ApprovalStepperStep } from './ApprovalStepper.js';
45
47
  export { AutocompleteField, AutocompleteFieldProps, AutocompleteOption, AutocompleteSearchFn, AutocompleteSearchResult, toAutocompleteOptions } from './AutocompleteField.js';
46
48
 
47
49
  /**
@@ -52,6 +54,7 @@ export { AutocompleteField, AutocompleteFieldProps, AutocompleteOption, Autocomp
52
54
  * `SduiWidgetComponentName`이 어휘 목록이 되게 한다.
53
55
  */
54
56
  declare const widgetComponents: {
57
+ ApprovalStepper: React.FC<ApprovalStepperProps & SduiBoxProps>;
55
58
  ColumnBoard: React.FC<ColumnBoardProps & SduiBoxProps>;
56
59
  DiffView: React.FC<DiffViewProps & SduiBoxProps>;
57
60
  Disclosure: React.FC<DisclosureProps & SduiBoxProps>;
@@ -77,4 +80,4 @@ declare const widgetComponents: {
77
80
  /** 위젯 어휘의 컴포넌트 이름 union — 메타데이터 대조에 쓴다(`@wipco/sdui/meta`). */
78
81
  type SduiWidgetComponentName = keyof typeof widgetComponents;
79
82
 
80
- export { ColumnBoardProps, DiffViewProps, DisclosureProps, EventCalendarProps, ImageLightboxProps, InlineEditProps, JsonFieldProps, LiveClockProps, OrgChartProps, PeriodFilterProps, PrintSheetProps, RecordSheetProps, type SduiWidgetComponentName, SearchableMultiPickerProps, SecureScreenProps, StackedMeterProps, StripProps, SummaryTableProps, TagMultiSelectProps, TimelineProps, TreePickerProps, TreeViewProps, widgetComponents };
83
+ export { ApprovalStepperProps, ColumnBoardProps, DiffViewProps, DisclosureProps, EventCalendarProps, ImageLightboxProps, InlineEditProps, JsonFieldProps, LiveClockProps, OrgChartProps, PeriodFilterProps, PrintSheetProps, RecordSheetProps, type SduiWidgetComponentName, SearchableMultiPickerProps, SecureScreenProps, StackedMeterProps, StripProps, SummaryTableProps, TagMultiSelectProps, TimelineProps, TreePickerProps, TreeViewProps, widgetComponents };
@@ -1,7 +1,11 @@
1
+ import { Timeline } from '../chunk-EFYEOT47.js';
2
+ export { Timeline } from '../chunk-EFYEOT47.js';
1
3
  import { TreePicker } from '../chunk-EQW24SJC.js';
2
4
  export { TreePicker } from '../chunk-EQW24SJC.js';
3
5
  import { TreeView } from '../chunk-IDUN2ESE.js';
4
6
  export { TreeView, buildTreeNodes } from '../chunk-IDUN2ESE.js';
7
+ import { PrintSheet } from '../chunk-MYJ2KEIP.js';
8
+ export { PrintSheet } from '../chunk-MYJ2KEIP.js';
5
9
  import { RecordSheet } from '../chunk-K3JBSWFI.js';
6
10
  export { RecordSheet } from '../chunk-K3JBSWFI.js';
7
11
  import { SearchableMultiPicker } from '../chunk-N5IATDUF.js';
@@ -16,8 +20,8 @@ import { SummaryTable } from '../chunk-O4U7Y3XL.js';
16
20
  export { SummaryTable } from '../chunk-O4U7Y3XL.js';
17
21
  import { TagMultiSelect } from '../chunk-QDEZXA34.js';
18
22
  export { TagMultiSelect } from '../chunk-QDEZXA34.js';
19
- import { Timeline } from '../chunk-EFYEOT47.js';
20
- export { Timeline } from '../chunk-EFYEOT47.js';
23
+ import { Disclosure } from '../chunk-HIZ2OVJA.js';
24
+ export { Disclosure } from '../chunk-HIZ2OVJA.js';
21
25
  import { EventCalendar } from '../chunk-C3JY6GA3.js';
22
26
  export { EventCalendar } from '../chunk-C3JY6GA3.js';
23
27
  import { ImageLightbox } from '../chunk-XPBZMECW.js';
@@ -32,24 +36,23 @@ import { OrgChart } from '../chunk-QWJ3OBBL.js';
32
36
  export { OrgChart } from '../chunk-QWJ3OBBL.js';
33
37
  import { PeriodFilter } from '../chunk-PFUULIZ2.js';
34
38
  export { PeriodFilter } from '../chunk-PFUULIZ2.js';
35
- import { PrintSheet } from '../chunk-MYJ2KEIP.js';
36
- export { PrintSheet } from '../chunk-MYJ2KEIP.js';
37
39
  import '../chunk-THEP3VWY.js';
38
- export { AutocompleteField, toAutocompleteOptions } from '../chunk-WZKDOZL2.js';
40
+ import { ApprovalStepper } from '../chunk-ZTQEP3TR.js';
41
+ export { ApprovalStepper } from '../chunk-ZTQEP3TR.js';
42
+ export { AutocompleteField, toAutocompleteOptions } from '../chunk-OG62LSFH.js';
39
43
  import '../chunk-U7ZK7WYH.js';
40
- import '../chunk-WGRB5UL4.js';
44
+ import '../chunk-DVOPZFQS.js';
41
45
  import '../chunk-XKLUCUVA.js';
42
46
  import { ColumnBoard } from '../chunk-C4JRSZGS.js';
43
47
  export { ColumnBoard } from '../chunk-C4JRSZGS.js';
44
48
  import { DiffView } from '../chunk-LDJ2CFFH.js';
45
49
  export { DiffView } from '../chunk-LDJ2CFFH.js';
46
- import { Disclosure } from '../chunk-HIZ2OVJA.js';
47
- export { Disclosure } from '../chunk-HIZ2OVJA.js';
48
50
  import '../chunk-WIDDHR7L.js';
49
51
  import '../chunk-SAN4IX6N.js';
50
52
 
51
53
  // src/widgets/index.ts
52
54
  var widgetComponents = {
55
+ ApprovalStepper,
53
56
  ColumnBoard,
54
57
  DiffView,
55
58
  Disclosure,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipco/sdui",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "WIPCO 서버 주도 UI(SDUI) 렌더러 — JSON 페이지 스펙을 React 컴포넌트로 렌더링",
5
5
  "keywords": [
6
6
  "sdui",
@@ -36,6 +36,10 @@
36
36
  "types": "./dist/widgets/index.d.ts",
37
37
  "default": "./dist/widgets/index.js"
38
38
  },
39
+ "./widgets/ApprovalStepper": {
40
+ "types": "./dist/widgets/ApprovalStepper.d.ts",
41
+ "default": "./dist/widgets/ApprovalStepper.js"
42
+ },
39
43
  "./widgets/AutocompleteField": {
40
44
  "types": "./dist/widgets/AutocompleteField.d.ts",
41
45
  "default": "./dist/widgets/AutocompleteField.js"
@@ -155,7 +159,8 @@
155
159
  "ol": ">=9",
156
160
  "react": ">=18",
157
161
  "react-dom": ">=18",
158
- "recharts": ">=3"
162
+ "recharts": ">=3",
163
+ "xlsx": ">=0.18"
159
164
  },
160
165
  "peerDependenciesMeta": {
161
166
  "ol": {
@@ -163,6 +168,9 @@
163
168
  },
164
169
  "recharts": {
165
170
  "optional": true
171
+ },
172
+ "xlsx": {
173
+ "optional": true
166
174
  }
167
175
  },
168
176
  "devDependencies": {