@wipco/sdui 0.21.0 → 0.22.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.
- package/dist/{chunk-HPZTFZZN.js → chunk-PNVOE5VH.js} +2 -95
- package/dist/chunk-WGRB5UL4.js +99 -0
- package/dist/{chunk-SRGXODY2.js → chunk-WZKDOZL2.js} +30 -5
- package/dist/index.d.ts +9 -3
- package/dist/index.js +358 -123
- package/dist/meta.d.ts +4 -4
- package/dist/meta.js +9 -3
- package/dist/rich.js +2 -2
- package/dist/{types-opTPYysN.d.ts → types-BNdv5-jn.d.ts} +59 -3
- package/dist/widgets/AutocompleteField.d.ts +29 -2
- package/dist/widgets/AutocompleteField.js +2 -1
- package/dist/widgets/index.js +2 -1
- package/package.json +1 -1
|
@@ -1,102 +1,9 @@
|
|
|
1
1
|
import { useDismiss, usePopoverAnchor, usePortalTo, HoistedStyle, AnchoredPopoverStyles, anchorStyle, focusStaysInside } from './chunk-XKLUCUVA.js';
|
|
2
2
|
import { Icon } from './chunk-WIDDHR7L.js';
|
|
3
3
|
import { boxed } from './chunk-SAN4IX6N.js';
|
|
4
|
-
import {
|
|
4
|
+
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
5
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
|
-
// src/env.ts
|
|
8
|
-
var DEV = (() => {
|
|
9
|
-
try {
|
|
10
|
-
const meta = import.meta;
|
|
11
|
-
return Boolean(meta.env?.DEV);
|
|
12
|
-
} catch {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
})();
|
|
16
|
-
|
|
17
|
-
// src/adapter.ts
|
|
18
|
-
function isSafeNavigationTarget(to) {
|
|
19
|
-
if (typeof to !== "string") return false;
|
|
20
|
-
const value = to.replace(/[\u0000-\u0020]/g, "");
|
|
21
|
-
if (value === "") return false;
|
|
22
|
-
if (value.startsWith("//")) return false;
|
|
23
|
-
if (value.startsWith("/") || value.startsWith("?") || value.startsWith("#")) return true;
|
|
24
|
-
return /^https?:\/\//i.test(value);
|
|
25
|
-
}
|
|
26
|
-
async function defaultFetch(path, init) {
|
|
27
|
-
const response = await fetch(path, {
|
|
28
|
-
method: init.method,
|
|
29
|
-
headers: { "content-type": "application/json", accept: "application/json" },
|
|
30
|
-
credentials: "include",
|
|
31
|
-
body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
|
|
32
|
-
signal: init.signal
|
|
33
|
-
});
|
|
34
|
-
const text = await response.text();
|
|
35
|
-
let json = null;
|
|
36
|
-
if (text) {
|
|
37
|
-
try {
|
|
38
|
-
json = JSON.parse(text);
|
|
39
|
-
} catch {
|
|
40
|
-
json = null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (!response.ok) {
|
|
44
|
-
const message = json && typeof json === "object" && typeof json.message === "string" ? json.message : `\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (${response.status})`;
|
|
45
|
-
throw new Error(message);
|
|
46
|
-
}
|
|
47
|
-
return json;
|
|
48
|
-
}
|
|
49
|
-
var defaultAdapter = {
|
|
50
|
-
navigate: (to) => {
|
|
51
|
-
if (!isSafeNavigationTarget(to)) {
|
|
52
|
-
console.error(`[sdui] \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 navigate \uB300\uC0C1: ${String(to)}`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
window.location.assign(to);
|
|
56
|
-
},
|
|
57
|
-
fetch: defaultFetch,
|
|
58
|
-
rawFetch: (path) => fetch(path, { credentials: "include" }),
|
|
59
|
-
// 항등함수. 계약을 문서화하고, 컴포넌트가 `useSduiAssetResolver()`를 조건 없이
|
|
60
|
-
// 부를 수 있게 하는 것이 전부다. **이 함수 참조 자체가 표식**이기도 하다 —
|
|
61
|
-
// `assetResolverOf`가 이것과 같은지 보고 "주입 없음"을 판정한다.
|
|
62
|
-
resolveAsset: (url) => url,
|
|
63
|
-
invalidate: (prefix) => {
|
|
64
|
-
if (DEV) console.warn(`[sdui] invalidate \uC561\uC158\uC774 \uC5B4\uB311\uD130 \uC5C6\uC774 \uD638\uCD9C\uB428 (prefix: ${prefix})`);
|
|
65
|
-
},
|
|
66
|
-
refresh: () => {
|
|
67
|
-
if (DEV) console.warn("[sdui] refresh \uC561\uC158\uC774 \uC5B4\uB311\uD130 \uC5C6\uC774 \uD638\uCD9C\uB428");
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
function resolveAdapter(adapter) {
|
|
71
|
-
const merged = { ...defaultAdapter };
|
|
72
|
-
if (adapter) {
|
|
73
|
-
for (const [key, value] of Object.entries(adapter)) {
|
|
74
|
-
if (value !== void 0) merged[key] = value;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return merged;
|
|
78
|
-
}
|
|
79
|
-
var SduiAdapterContext = createContext(null);
|
|
80
|
-
function useSduiAdapter() {
|
|
81
|
-
return useContext(SduiAdapterContext);
|
|
82
|
-
}
|
|
83
|
-
function assetResolverOf(adapter) {
|
|
84
|
-
const resolve = adapter?.resolveAsset;
|
|
85
|
-
if (!resolve || resolve === defaultAdapter.resolveAsset) return null;
|
|
86
|
-
return resolve;
|
|
87
|
-
}
|
|
88
|
-
function useSduiAssetResolver() {
|
|
89
|
-
const adapter = useContext(SduiAdapterContext);
|
|
90
|
-
return adapter?.resolveAsset ?? defaultAdapter.resolveAsset;
|
|
91
|
-
}
|
|
92
|
-
function useSduiSandbox() {
|
|
93
|
-
return useContext(SduiAdapterContext)?.sandbox === true;
|
|
94
|
-
}
|
|
95
|
-
function hostFetchOf(adapter) {
|
|
96
|
-
const hostFetch = adapter?.fetch;
|
|
97
|
-
if (!hostFetch || hostFetch === defaultAdapter.fetch) return null;
|
|
98
|
-
return hostFetch;
|
|
99
|
-
}
|
|
100
7
|
var HM_RE = /^\d{2}:\d{2}$/;
|
|
101
8
|
var LIST_HEIGHT = 260;
|
|
102
9
|
var MINUTES_PER_DAY = 24 * 60;
|
|
@@ -288,4 +195,4 @@ var TP_CSS = `
|
|
|
288
195
|
`;
|
|
289
196
|
var TimePicker = boxed(TimePickerImpl, "TimePicker");
|
|
290
197
|
|
|
291
|
-
export {
|
|
198
|
+
export { TimePicker };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/adapter.ts
|
|
4
|
+
|
|
5
|
+
// src/env.ts
|
|
6
|
+
var DEV = (() => {
|
|
7
|
+
try {
|
|
8
|
+
const meta = import.meta;
|
|
9
|
+
return Boolean(meta.env?.DEV);
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
})();
|
|
14
|
+
|
|
15
|
+
// src/adapter.ts
|
|
16
|
+
function isSafeNavigationTarget(to) {
|
|
17
|
+
if (typeof to !== "string") return false;
|
|
18
|
+
const value = to.replace(/[\u0000-\u0020]/g, "");
|
|
19
|
+
if (value === "") return false;
|
|
20
|
+
if (value.startsWith("//")) return false;
|
|
21
|
+
if (value.startsWith("/") || value.startsWith("?") || value.startsWith("#")) return true;
|
|
22
|
+
return /^https?:\/\//i.test(value);
|
|
23
|
+
}
|
|
24
|
+
async function defaultFetch(path, init) {
|
|
25
|
+
const response = await fetch(path, {
|
|
26
|
+
method: init.method,
|
|
27
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
28
|
+
credentials: "include",
|
|
29
|
+
body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
|
|
30
|
+
signal: init.signal
|
|
31
|
+
});
|
|
32
|
+
const text = await response.text();
|
|
33
|
+
let json = null;
|
|
34
|
+
if (text) {
|
|
35
|
+
try {
|
|
36
|
+
json = JSON.parse(text);
|
|
37
|
+
} catch {
|
|
38
|
+
json = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
const message = json && typeof json === "object" && typeof json.message === "string" ? json.message : `\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (${response.status})`;
|
|
43
|
+
throw new Error(message);
|
|
44
|
+
}
|
|
45
|
+
return json;
|
|
46
|
+
}
|
|
47
|
+
var defaultAdapter = {
|
|
48
|
+
navigate: (to) => {
|
|
49
|
+
if (!isSafeNavigationTarget(to)) {
|
|
50
|
+
console.error(`[sdui] \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 navigate \uB300\uC0C1: ${String(to)}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
window.location.assign(to);
|
|
54
|
+
},
|
|
55
|
+
fetch: defaultFetch,
|
|
56
|
+
rawFetch: (path) => fetch(path, { credentials: "include" }),
|
|
57
|
+
// 항등함수. 계약을 문서화하고, 컴포넌트가 `useSduiAssetResolver()`를 조건 없이
|
|
58
|
+
// 부를 수 있게 하는 것이 전부다. **이 함수 참조 자체가 표식**이기도 하다 —
|
|
59
|
+
// `assetResolverOf`가 이것과 같은지 보고 "주입 없음"을 판정한다.
|
|
60
|
+
resolveAsset: (url) => url,
|
|
61
|
+
invalidate: (prefix) => {
|
|
62
|
+
if (DEV) console.warn(`[sdui] invalidate \uC561\uC158\uC774 \uC5B4\uB311\uD130 \uC5C6\uC774 \uD638\uCD9C\uB428 (prefix: ${prefix})`);
|
|
63
|
+
},
|
|
64
|
+
refresh: () => {
|
|
65
|
+
if (DEV) console.warn("[sdui] refresh \uC561\uC158\uC774 \uC5B4\uB311\uD130 \uC5C6\uC774 \uD638\uCD9C\uB428");
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
function resolveAdapter(adapter) {
|
|
69
|
+
const merged = { ...defaultAdapter };
|
|
70
|
+
if (adapter) {
|
|
71
|
+
for (const [key, value] of Object.entries(adapter)) {
|
|
72
|
+
if (value !== void 0) merged[key] = value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return merged;
|
|
76
|
+
}
|
|
77
|
+
var SduiAdapterContext = createContext(null);
|
|
78
|
+
function useSduiAdapter() {
|
|
79
|
+
return useContext(SduiAdapterContext);
|
|
80
|
+
}
|
|
81
|
+
function assetResolverOf(adapter) {
|
|
82
|
+
const resolve = adapter?.resolveAsset;
|
|
83
|
+
if (!resolve || resolve === defaultAdapter.resolveAsset) return null;
|
|
84
|
+
return resolve;
|
|
85
|
+
}
|
|
86
|
+
function useSduiAssetResolver() {
|
|
87
|
+
const adapter = useContext(SduiAdapterContext);
|
|
88
|
+
return adapter?.resolveAsset ?? defaultAdapter.resolveAsset;
|
|
89
|
+
}
|
|
90
|
+
function useSduiSandbox() {
|
|
91
|
+
return useContext(SduiAdapterContext)?.sandbox === true;
|
|
92
|
+
}
|
|
93
|
+
function hostFetchOf(adapter) {
|
|
94
|
+
const hostFetch = adapter?.fetch;
|
|
95
|
+
if (!hostFetch || hostFetch === defaultAdapter.fetch) return null;
|
|
96
|
+
return hostFetch;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { DEV, SduiAdapterContext, assetResolverOf, defaultAdapter, hostFetchOf, isSafeNavigationTarget, resolveAdapter, useSduiAdapter, useSduiAssetResolver, useSduiSandbox };
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { includesLooseSearch } from './chunk-U7ZK7WYH.js';
|
|
2
|
+
import { useSduiAdapter, hostFetchOf } from './chunk-WGRB5UL4.js';
|
|
2
3
|
import { Icon, Button, Input, Spinner } from './chunk-WIDDHR7L.js';
|
|
3
4
|
import { boxed } from './chunk-SAN4IX6N.js';
|
|
4
|
-
import { useId, useRef, useState, useEffect
|
|
5
|
+
import { useId, useRef, useMemo, useState, useEffect } from 'react';
|
|
5
6
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
7
|
|
|
8
|
+
function endpointSearchFn(endpoint, searchParam, hostFetch) {
|
|
9
|
+
return async (keyword, signal) => {
|
|
10
|
+
const sep = endpoint.includes("?") ? "&" : "?";
|
|
11
|
+
const url = `${endpoint}${sep}${searchParam}=${encodeURIComponent(keyword)}`;
|
|
12
|
+
try {
|
|
13
|
+
return toAutocompleteOptions(await hostFetch(url, { method: "GET", signal }));
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error?.status === 503) {
|
|
16
|
+
return { options: [], status: "offline" };
|
|
17
|
+
}
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
7
22
|
function isRecord(value) {
|
|
8
23
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
24
|
}
|
|
@@ -34,6 +49,8 @@ var statusRowStyle = {
|
|
|
34
49
|
};
|
|
35
50
|
function AutocompleteFieldImpl({
|
|
36
51
|
search,
|
|
52
|
+
endpoint,
|
|
53
|
+
searchParam = "q",
|
|
37
54
|
options,
|
|
38
55
|
labelKey = "label",
|
|
39
56
|
valueKey = "value",
|
|
@@ -52,9 +69,17 @@ function AutocompleteFieldImpl({
|
|
|
52
69
|
const seqRef = useRef(0);
|
|
53
70
|
const optionsRef = useRef(options);
|
|
54
71
|
optionsRef.current = options;
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
const adapter = useSduiAdapter();
|
|
73
|
+
const endpointSearch = useMemo(() => {
|
|
74
|
+
if (!endpoint) return void 0;
|
|
75
|
+
const hostFetch = hostFetchOf(adapter);
|
|
76
|
+
if (!hostFetch) return void 0;
|
|
77
|
+
return endpointSearchFn(endpoint, searchParam, hostFetch);
|
|
78
|
+
}, [endpoint, searchParam, adapter]);
|
|
79
|
+
const effectiveSearch = search ?? endpointSearch;
|
|
80
|
+
const searchRef = useRef(effectiveSearch);
|
|
81
|
+
searchRef.current = effectiveSearch;
|
|
82
|
+
const hasSearch = effectiveSearch !== void 0;
|
|
58
83
|
const isControlled = value !== void 0;
|
|
59
84
|
const [internalValue, setInternalValue] = useState("");
|
|
60
85
|
const committed = (isControlled ? value : internalValue) ?? "";
|
|
@@ -298,4 +323,4 @@ function AutocompleteFieldImpl({
|
|
|
298
323
|
}
|
|
299
324
|
var AutocompleteField = boxed(AutocompleteFieldImpl, "AutocompleteField");
|
|
300
325
|
|
|
301
|
-
export { AutocompleteField, toAutocompleteOptions };
|
|
326
|
+
export { AutocompleteField, endpointSearchFn, toAutocompleteOptions };
|
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-
|
|
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-
|
|
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';
|
|
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';
|
|
@@ -1677,6 +1677,12 @@ interface SduiRendererProps {
|
|
|
1677
1677
|
*/
|
|
1678
1678
|
declare function SduiRenderer({ page, registry, adapter, syncUrlState, initialState, can, onCustomAction, customValidators, }: SduiRendererProps): React__default.JSX.Element;
|
|
1679
1679
|
|
|
1680
|
+
/**
|
|
1681
|
+
* 어휘 전체. **렌더러와 편집기 검증기가 같은 이 집합을 본다** — 목록이 두 벌이면
|
|
1682
|
+
* 한쪽에만 연산을 더했을 때 "렌더는 되는데 저장이 거부되는" 스펙이 생긴다.
|
|
1683
|
+
*/
|
|
1684
|
+
declare const CALC_OPS: ReadonlySet<string>;
|
|
1685
|
+
|
|
1680
1686
|
/**
|
|
1681
1687
|
* 스펙 구조 검증기 — 서버(또는 편집기)가 만든 스펙의 형태 오류를 렌더 전에 찾아낸다.
|
|
1682
1688
|
* 렌더러는 개발 모드에서 이 결과를 콘솔에 경고하고, 관리자 편집기는
|
|
@@ -1831,4 +1837,4 @@ interface ImageViewerProps {
|
|
|
1831
1837
|
*/
|
|
1832
1838
|
declare function ImageViewer({ src, alt, open, onClose }: ImageViewerProps): React__default.ReactPortal | null;
|
|
1833
1839
|
|
|
1834
|
-
export { Actions, type ActionsProps, Alert, type AlertProps, Avatar, type AvatarProps, Badge, type BadgeProps, type BadgeTone, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, type ButtonProps, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { TimePicker
|
|
1
|
+
import { TimePicker } from './chunk-PNVOE5VH.js';
|
|
2
|
+
export { TimePicker } from './chunk-PNVOE5VH.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
9
|
import { HoistedStyle, DateRangePicker, DatePicker, OverlayLayer } from './chunk-XKLUCUVA.js';
|
|
8
10
|
export { DatePicker, DateRangePicker, OverlayContainerContext, OverlayLayer, getOverlayRoot, useOverlayLayer } from './chunk-XKLUCUVA.js';
|
|
9
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';
|
|
@@ -1783,6 +1785,310 @@ function createRegistry(extensions) {
|
|
|
1783
1785
|
return { ...defaultRegistry, ...extensions };
|
|
1784
1786
|
}
|
|
1785
1787
|
|
|
1788
|
+
// src/calc.ts
|
|
1789
|
+
function isPlainObject(v) {
|
|
1790
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1791
|
+
}
|
|
1792
|
+
function toNumber2(v) {
|
|
1793
|
+
if (typeof v === "number") return v;
|
|
1794
|
+
if (typeof v === "string") {
|
|
1795
|
+
const n = Number(v.replace(/,/g, ""));
|
|
1796
|
+
return Number.isFinite(n) ? n : 0;
|
|
1797
|
+
}
|
|
1798
|
+
return 0;
|
|
1799
|
+
}
|
|
1800
|
+
function isNumericText(v) {
|
|
1801
|
+
if (typeof v === "number") return true;
|
|
1802
|
+
if (typeof v !== "string" || v === "") return false;
|
|
1803
|
+
return Number.isFinite(Number(v.replace(/,/g, "")));
|
|
1804
|
+
}
|
|
1805
|
+
function orderingCompare(a, b) {
|
|
1806
|
+
if (typeof a === "string" && typeof b === "string" && !isNumericText(a) && !isNumericText(b)) {
|
|
1807
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1808
|
+
}
|
|
1809
|
+
const x = toNumber2(a);
|
|
1810
|
+
const y = toNumber2(b);
|
|
1811
|
+
return x < y ? -1 : x > y ? 1 : 0;
|
|
1812
|
+
}
|
|
1813
|
+
function looseEquals(a, b) {
|
|
1814
|
+
return String(a ?? "") === String(b ?? "");
|
|
1815
|
+
}
|
|
1816
|
+
function isBlank(v) {
|
|
1817
|
+
return v === null || v === void 0 || v === "";
|
|
1818
|
+
}
|
|
1819
|
+
function fieldOf(row, field) {
|
|
1820
|
+
if (!field) return row;
|
|
1821
|
+
if (!isPlainObject(row)) return void 0;
|
|
1822
|
+
return Object.hasOwn(row, field) ? row[field] : void 0;
|
|
1823
|
+
}
|
|
1824
|
+
var FIELD_OPS = /* @__PURE__ */ new Set([
|
|
1825
|
+
"eq",
|
|
1826
|
+
"ne",
|
|
1827
|
+
"gt",
|
|
1828
|
+
"gte",
|
|
1829
|
+
"lt",
|
|
1830
|
+
"lte",
|
|
1831
|
+
"in",
|
|
1832
|
+
"notIn",
|
|
1833
|
+
"contains",
|
|
1834
|
+
"startsWith",
|
|
1835
|
+
"endsWith",
|
|
1836
|
+
"isNull"
|
|
1837
|
+
]);
|
|
1838
|
+
function isFieldOpObject(v) {
|
|
1839
|
+
return isPlainObject(v) && Object.keys(v).some((key) => FIELD_OPS.has(key));
|
|
1840
|
+
}
|
|
1841
|
+
function matchesFieldOps(actual, ops, resolve) {
|
|
1842
|
+
return Object.entries(ops).every(([op, rawExpected]) => {
|
|
1843
|
+
if (!FIELD_OPS.has(op)) return true;
|
|
1844
|
+
const expected = resolve(rawExpected);
|
|
1845
|
+
switch (op) {
|
|
1846
|
+
case "eq":
|
|
1847
|
+
return looseEquals(actual, expected);
|
|
1848
|
+
case "ne":
|
|
1849
|
+
return !looseEquals(actual, expected);
|
|
1850
|
+
case "gt":
|
|
1851
|
+
return orderingCompare(actual, expected) > 0;
|
|
1852
|
+
case "gte":
|
|
1853
|
+
return orderingCompare(actual, expected) >= 0;
|
|
1854
|
+
case "lt":
|
|
1855
|
+
return orderingCompare(actual, expected) < 0;
|
|
1856
|
+
case "lte":
|
|
1857
|
+
return orderingCompare(actual, expected) <= 0;
|
|
1858
|
+
case "in":
|
|
1859
|
+
return Array.isArray(expected) && expected.some((item) => looseEquals(actual, item));
|
|
1860
|
+
case "notIn":
|
|
1861
|
+
return !Array.isArray(expected) || !expected.some((item) => looseEquals(actual, item));
|
|
1862
|
+
case "contains":
|
|
1863
|
+
return String(actual ?? "").includes(String(expected ?? ""));
|
|
1864
|
+
case "startsWith":
|
|
1865
|
+
return String(actual ?? "").startsWith(String(expected ?? ""));
|
|
1866
|
+
case "endsWith":
|
|
1867
|
+
return String(actual ?? "").endsWith(String(expected ?? ""));
|
|
1868
|
+
case "isNull":
|
|
1869
|
+
return isBlank(actual) === Boolean(expected);
|
|
1870
|
+
default:
|
|
1871
|
+
return true;
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
function matchesWhere(row, where, resolve) {
|
|
1876
|
+
if (!isPlainObject(where)) return true;
|
|
1877
|
+
return Object.entries(where).every(([key, condition]) => {
|
|
1878
|
+
if (key === "AND") {
|
|
1879
|
+
return Array.isArray(condition) && condition.every((sub) => matchesWhere(row, sub, resolve));
|
|
1880
|
+
}
|
|
1881
|
+
if (key === "OR") {
|
|
1882
|
+
return Array.isArray(condition) && condition.some((sub) => matchesWhere(row, sub, resolve));
|
|
1883
|
+
}
|
|
1884
|
+
if (key === "NOT") {
|
|
1885
|
+
return !matchesWhere(row, condition, resolve);
|
|
1886
|
+
}
|
|
1887
|
+
const actual = fieldOf(row, key);
|
|
1888
|
+
if (isFieldOpObject(condition)) return matchesFieldOps(actual, condition, resolve);
|
|
1889
|
+
return looseEquals(actual, resolve(condition));
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
var CALC_OPS = /* @__PURE__ */ new Set([
|
|
1893
|
+
// 산술 (기존)
|
|
1894
|
+
"sum",
|
|
1895
|
+
"sub",
|
|
1896
|
+
"mul",
|
|
1897
|
+
"div",
|
|
1898
|
+
"percent",
|
|
1899
|
+
"count",
|
|
1900
|
+
// 문자 (기존)
|
|
1901
|
+
"concat",
|
|
1902
|
+
"encode",
|
|
1903
|
+
// 비교·논리 (기존)
|
|
1904
|
+
"eq",
|
|
1905
|
+
"gt",
|
|
1906
|
+
"gte",
|
|
1907
|
+
"lt",
|
|
1908
|
+
"lte",
|
|
1909
|
+
"not",
|
|
1910
|
+
// 논리 (신규) — 예전에는 스펙이 eq(concat(eq(a),eq(b)),"falsefalse")로 우회했다
|
|
1911
|
+
"and",
|
|
1912
|
+
"or",
|
|
1913
|
+
// 값 고르기 (신규)
|
|
1914
|
+
"if",
|
|
1915
|
+
"match",
|
|
1916
|
+
"coalesce",
|
|
1917
|
+
// 목록·객체 (신규)
|
|
1918
|
+
"get",
|
|
1919
|
+
"pluck",
|
|
1920
|
+
"filter",
|
|
1921
|
+
"some",
|
|
1922
|
+
"every",
|
|
1923
|
+
"includes",
|
|
1924
|
+
"distinct",
|
|
1925
|
+
"except",
|
|
1926
|
+
"entries",
|
|
1927
|
+
"length",
|
|
1928
|
+
"join",
|
|
1929
|
+
"split",
|
|
1930
|
+
"sortBy"
|
|
1931
|
+
]);
|
|
1932
|
+
function evaluateCalc(calc, resolved, resolve) {
|
|
1933
|
+
const op = typeof calc.op === "string" ? calc.op : "";
|
|
1934
|
+
const field = typeof calc.field === "string" ? calc.field : void 0;
|
|
1935
|
+
const first = resolved[0];
|
|
1936
|
+
const list = Array.isArray(first) ? first : void 0;
|
|
1937
|
+
const listOrEmpty = () => {
|
|
1938
|
+
if (list) return list;
|
|
1939
|
+
if (first !== void 0 && first !== null) {
|
|
1940
|
+
warnOnce(
|
|
1941
|
+
`calc:${op}:list`,
|
|
1942
|
+
`[sdui] $calc(${op}): \uCCAB \uC778\uC790\uAC00 \uBC30\uC5F4\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uBE48 \uBAA9\uB85D\uC73C\uB85C \uCC98\uB9AC\uD569\uB2C8\uB2E4`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
return [];
|
|
1946
|
+
};
|
|
1947
|
+
const nums = (v) => {
|
|
1948
|
+
if (Array.isArray(v))
|
|
1949
|
+
return v.map((row) => field && isPlainObject(row) ? toNumber2(row[field]) : toNumber2(row));
|
|
1950
|
+
return [toNumber2(v)];
|
|
1951
|
+
};
|
|
1952
|
+
switch (op) {
|
|
1953
|
+
/* ── 산술 ── */
|
|
1954
|
+
case "sum":
|
|
1955
|
+
return resolved.flatMap(nums).reduce((a, b) => a + b, 0);
|
|
1956
|
+
case "count":
|
|
1957
|
+
return resolved.reduce(
|
|
1958
|
+
(a, v) => a + (Array.isArray(v) ? v.length : v == null ? 0 : 1),
|
|
1959
|
+
0
|
|
1960
|
+
);
|
|
1961
|
+
case "sub": {
|
|
1962
|
+
const items = resolved.map((v) => toNumber2(v));
|
|
1963
|
+
return items.length === 0 ? 0 : items.reduce((a, b) => a - b);
|
|
1964
|
+
}
|
|
1965
|
+
case "mul":
|
|
1966
|
+
return resolved.map((v) => toNumber2(v)).reduce((a, b) => a * b, 1);
|
|
1967
|
+
case "div": {
|
|
1968
|
+
const [a, b] = resolved.map((v) => toNumber2(v));
|
|
1969
|
+
return b === 0 ? 0 : a / b;
|
|
1970
|
+
}
|
|
1971
|
+
case "percent": {
|
|
1972
|
+
const [a, b] = resolved.map((v) => toNumber2(v));
|
|
1973
|
+
return b === 0 ? 0 : Math.round(a / b * 1e3) / 10;
|
|
1974
|
+
}
|
|
1975
|
+
/* ── 문자 ── */
|
|
1976
|
+
case "concat":
|
|
1977
|
+
return resolved.map((v) => v == null ? "" : String(v)).join("");
|
|
1978
|
+
case "encode":
|
|
1979
|
+
return encodeURIComponent(resolved.map((v) => v == null ? "" : String(v)).join(""));
|
|
1980
|
+
/* ── 비교·논리 ── */
|
|
1981
|
+
case "eq":
|
|
1982
|
+
return looseEquals(resolved[0], resolved[1]);
|
|
1983
|
+
case "gt":
|
|
1984
|
+
return orderingCompare(resolved[0], resolved[1]) > 0;
|
|
1985
|
+
case "gte":
|
|
1986
|
+
return orderingCompare(resolved[0], resolved[1]) >= 0;
|
|
1987
|
+
case "lt":
|
|
1988
|
+
return orderingCompare(resolved[0], resolved[1]) < 0;
|
|
1989
|
+
case "lte":
|
|
1990
|
+
return orderingCompare(resolved[0], resolved[1]) <= 0;
|
|
1991
|
+
case "not":
|
|
1992
|
+
return !resolved[0];
|
|
1993
|
+
case "and":
|
|
1994
|
+
return resolved.every(Boolean);
|
|
1995
|
+
case "or":
|
|
1996
|
+
return resolved.some(Boolean);
|
|
1997
|
+
/* ── 값 고르기 ── */
|
|
1998
|
+
case "if":
|
|
1999
|
+
return resolved[0] ? resolve(calc.then ?? null) : resolve(calc.else ?? null);
|
|
2000
|
+
case "match": {
|
|
2001
|
+
const key = String(resolved[0] ?? "");
|
|
2002
|
+
const cases = isPlainObject(calc.cases) ? calc.cases : {};
|
|
2003
|
+
if (Object.hasOwn(cases, key)) return resolve(cases[key]);
|
|
2004
|
+
return resolve(calc.default ?? null);
|
|
2005
|
+
}
|
|
2006
|
+
case "coalesce":
|
|
2007
|
+
return resolved.find((v) => !isBlank(v)) ?? null;
|
|
2008
|
+
/* ── 목록·객체 ── */
|
|
2009
|
+
case "get": {
|
|
2010
|
+
const [container, rawKey] = resolved;
|
|
2011
|
+
const key = rawKey == null ? "" : String(rawKey);
|
|
2012
|
+
if (Array.isArray(container)) {
|
|
2013
|
+
const index = Number(key);
|
|
2014
|
+
if (!Number.isInteger(index) || index < 0 || index >= container.length) return null;
|
|
2015
|
+
return container[index] ?? null;
|
|
2016
|
+
}
|
|
2017
|
+
if (isPlainObject(container)) {
|
|
2018
|
+
return Object.hasOwn(container, key) ? container[key] ?? null : null;
|
|
2019
|
+
}
|
|
2020
|
+
return null;
|
|
2021
|
+
}
|
|
2022
|
+
case "pluck":
|
|
2023
|
+
return listOrEmpty().map((row) => fieldOf(row, field)).filter((v) => v !== null && v !== void 0);
|
|
2024
|
+
case "filter":
|
|
2025
|
+
return listOrEmpty().filter((row) => matchesWhere(row, calc.where, resolve));
|
|
2026
|
+
case "some":
|
|
2027
|
+
return listOrEmpty().some((row) => matchesWhere(row, calc.where, resolve));
|
|
2028
|
+
case "every":
|
|
2029
|
+
return listOrEmpty().every((row) => matchesWhere(row, calc.where, resolve));
|
|
2030
|
+
case "includes": {
|
|
2031
|
+
const needle = resolved[1];
|
|
2032
|
+
if (typeof first === "string") return first.includes(String(needle ?? ""));
|
|
2033
|
+
return listOrEmpty().some((row) => looseEquals(fieldOf(row, field), needle));
|
|
2034
|
+
}
|
|
2035
|
+
case "distinct": {
|
|
2036
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2037
|
+
return listOrEmpty().filter((row) => {
|
|
2038
|
+
const key = String(fieldOf(row, field) ?? "");
|
|
2039
|
+
if (seen.has(key)) return false;
|
|
2040
|
+
seen.add(key);
|
|
2041
|
+
return true;
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
case "except": {
|
|
2045
|
+
const remove = new Set(
|
|
2046
|
+
(Array.isArray(resolved[1]) ? resolved[1] : []).map(
|
|
2047
|
+
(row) => String(fieldOf(row, field) ?? "")
|
|
2048
|
+
)
|
|
2049
|
+
);
|
|
2050
|
+
return listOrEmpty().filter(
|
|
2051
|
+
(row) => !remove.has(String(fieldOf(row, field) ?? ""))
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
case "entries": {
|
|
2055
|
+
if (!isPlainObject(first)) return [];
|
|
2056
|
+
return Object.entries(first).map(([key, value]) => ({
|
|
2057
|
+
key,
|
|
2058
|
+
value
|
|
2059
|
+
}));
|
|
2060
|
+
}
|
|
2061
|
+
case "length":
|
|
2062
|
+
if (Array.isArray(first)) return first.length;
|
|
2063
|
+
if (typeof first === "string") return first.length;
|
|
2064
|
+
return 0;
|
|
2065
|
+
case "join": {
|
|
2066
|
+
const sep = typeof calc.sep === "string" ? calc.sep : ",";
|
|
2067
|
+
return listOrEmpty().map((row) => {
|
|
2068
|
+
const value = fieldOf(row, field);
|
|
2069
|
+
return value == null ? "" : String(value);
|
|
2070
|
+
}).join(sep);
|
|
2071
|
+
}
|
|
2072
|
+
case "split": {
|
|
2073
|
+
const sep = typeof calc.sep === "string" ? calc.sep : ",";
|
|
2074
|
+
const parts = String(first ?? "").split(sep);
|
|
2075
|
+
if (calc.take === void 0) return parts;
|
|
2076
|
+
const index = toNumber2(calc.take);
|
|
2077
|
+
const at = index < 0 ? parts.length + index : index;
|
|
2078
|
+
return parts[at] ?? null;
|
|
2079
|
+
}
|
|
2080
|
+
case "sortBy": {
|
|
2081
|
+
const desc = Boolean(calc.desc);
|
|
2082
|
+
return [...listOrEmpty()].sort((a, b) => {
|
|
2083
|
+
const order = orderingCompare(fieldOf(a, field), fieldOf(b, field));
|
|
2084
|
+
return desc ? -order : order;
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
default:
|
|
2088
|
+
return null;
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
|
|
1786
2092
|
// src/request-action.ts
|
|
1787
2093
|
function deferChainEntry(container, key) {
|
|
1788
2094
|
return typeof container.action === "string" && (key === "onSuccess" || key === "onError");
|
|
@@ -1829,22 +2135,6 @@ var ACTION_NAMES = /* @__PURE__ */ new Set([
|
|
|
1829
2135
|
"arrayUpdate",
|
|
1830
2136
|
"custom"
|
|
1831
2137
|
]);
|
|
1832
|
-
var CALC_OPS = /* @__PURE__ */ new Set([
|
|
1833
|
-
"sum",
|
|
1834
|
-
"sub",
|
|
1835
|
-
"mul",
|
|
1836
|
-
"div",
|
|
1837
|
-
"percent",
|
|
1838
|
-
"count",
|
|
1839
|
-
"concat",
|
|
1840
|
-
"encode",
|
|
1841
|
-
"eq",
|
|
1842
|
-
"gt",
|
|
1843
|
-
"gte",
|
|
1844
|
-
"lt",
|
|
1845
|
-
"lte",
|
|
1846
|
-
"not"
|
|
1847
|
-
]);
|
|
1848
2138
|
var RULE_NAMES = /* @__PURE__ */ new Set([
|
|
1849
2139
|
"required",
|
|
1850
2140
|
"min",
|
|
@@ -1864,7 +2154,7 @@ var DENIED_PROPS = /* @__PURE__ */ new Set([
|
|
|
1864
2154
|
"constructor",
|
|
1865
2155
|
"prototype"
|
|
1866
2156
|
]);
|
|
1867
|
-
function
|
|
2157
|
+
function isPlainObject2(v) {
|
|
1868
2158
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1869
2159
|
}
|
|
1870
2160
|
function checkUrlValue(key, value, path, issues) {
|
|
@@ -1874,7 +2164,7 @@ function checkUrlValue(key, value, path, issues) {
|
|
|
1874
2164
|
}
|
|
1875
2165
|
function validateSpec(page, knownComponents) {
|
|
1876
2166
|
const issues = [];
|
|
1877
|
-
if (!
|
|
2167
|
+
if (!isPlainObject2(page)) return [{ path: "$", message: "\uC2A4\uD399\uC774 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4" }];
|
|
1878
2168
|
if (page.version !== 1) issues.push({ path: "$.version", message: "version\uC740 1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" });
|
|
1879
2169
|
validatePageData(page.data, issues);
|
|
1880
2170
|
if (!Array.isArray(page.body)) {
|
|
@@ -1888,7 +2178,7 @@ function validateSpec(page, knownComponents) {
|
|
|
1888
2178
|
}
|
|
1889
2179
|
function validatePageData(data, issues) {
|
|
1890
2180
|
if (data === void 0) return;
|
|
1891
|
-
if (!
|
|
2181
|
+
if (!isPlainObject2(data)) {
|
|
1892
2182
|
issues.push({ path: "$.data", message: "data\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1893
2183
|
return;
|
|
1894
2184
|
}
|
|
@@ -1898,7 +2188,7 @@ function validatePageData(data, issues) {
|
|
|
1898
2188
|
issues.push({ path, message: "\uC0C1\uD0DC \uD0A4\uB294 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC544\uC57C \uD558\uACE0 $\uB85C \uC2DC\uC791\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" });
|
|
1899
2189
|
continue;
|
|
1900
2190
|
}
|
|
1901
|
-
if (!
|
|
2191
|
+
if (!isPlainObject2(raw)) {
|
|
1902
2192
|
issues.push({ path, message: "\uC870\uD68C\uB294 { endpoint, select?, when?, fallback? } \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1903
2193
|
continue;
|
|
1904
2194
|
}
|
|
@@ -1910,7 +2200,7 @@ function validatePageData(data, issues) {
|
|
|
1910
2200
|
} else if (!raw.endpoint.startsWith("/")) {
|
|
1911
2201
|
issues.push({ path: `${path}.endpoint`, message: "endpoint\uB294 /\uB85C \uC2DC\uC791\uD558\uB294 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1912
2202
|
}
|
|
1913
|
-
} else if (!
|
|
2203
|
+
} else if (!isPlainObject2(raw.endpoint)) {
|
|
1914
2204
|
issues.push({ path: `${path}.endpoint`, message: "endpoint\uB294 \uBB38\uC790\uC5F4\uC774\uAC70\uB098 \uAC12 \uCC38\uC870\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1915
2205
|
} else {
|
|
1916
2206
|
walkValue(raw.endpoint, `${path}.endpoint`, issues, false);
|
|
@@ -1933,7 +2223,7 @@ function validatePageData(data, issues) {
|
|
|
1933
2223
|
}
|
|
1934
2224
|
function walkChild(child, path, issues, known) {
|
|
1935
2225
|
if (typeof child === "string") return;
|
|
1936
|
-
if (!
|
|
2226
|
+
if (!isPlainObject2(child)) {
|
|
1937
2227
|
issues.push({ path, message: "children \uD56D\uBAA9\uC740 \uBB38\uC790\uC5F4 \uB610\uB294 \uB178\uB4DC \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1938
2228
|
return;
|
|
1939
2229
|
}
|
|
@@ -1945,7 +2235,7 @@ function walkChild(child, path, issues, known) {
|
|
|
1945
2235
|
if (known && !RENDERER_BUILTINS.has(node.component) && !known.has(node.component)) {
|
|
1946
2236
|
issues.push({ path, message: `\uB4F1\uB85D\uB418\uC9C0 \uC54A\uC740 \uCEF4\uD3EC\uB10C\uD2B8: ${node.component}` });
|
|
1947
2237
|
}
|
|
1948
|
-
if (node.props !== void 0 && !
|
|
2238
|
+
if (node.props !== void 0 && !isPlainObject2(node.props)) {
|
|
1949
2239
|
issues.push({ path: `${path}.props`, message: "props\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1950
2240
|
}
|
|
1951
2241
|
for (const [name, value] of Object.entries(node.props ?? {})) {
|
|
@@ -1959,9 +2249,9 @@ function walkChild(child, path, issues, known) {
|
|
|
1959
2249
|
checkUrlValue(name, value, `${path}.props.${name}`, issues);
|
|
1960
2250
|
walkValue(value, `${path}.props.${name}`, issues, name.startsWith("on") || name === "rowClick");
|
|
1961
2251
|
}
|
|
1962
|
-
if (node.component === "Form" &&
|
|
2252
|
+
if (node.component === "Form" && isPlainObject2(node.props) && node.props.rules !== void 0) {
|
|
1963
2253
|
const rules = node.props.rules;
|
|
1964
|
-
if (!
|
|
2254
|
+
if (!isPlainObject2(rules)) {
|
|
1965
2255
|
issues.push({ path: `${path}.props.rules`, message: "rules\uB294 { \uD544\uB4DC: \uADDC\uCE59\uBC30\uC5F4 } \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1966
2256
|
} else {
|
|
1967
2257
|
for (const [field, list] of Object.entries(rules)) {
|
|
@@ -1972,15 +2262,15 @@ function walkChild(child, path, issues, known) {
|
|
|
1972
2262
|
}
|
|
1973
2263
|
}
|
|
1974
2264
|
if (node.component === "Fragment") {
|
|
1975
|
-
const id =
|
|
1976
|
-
if ((typeof id !== "string" || id === "") && !
|
|
2265
|
+
const id = isPlainObject2(node.props) ? node.props.id : void 0;
|
|
2266
|
+
if ((typeof id !== "string" || id === "") && !isPlainObject2(id)) {
|
|
1977
2267
|
issues.push({
|
|
1978
2268
|
path: `${path}.props.id`,
|
|
1979
2269
|
message: "Fragment.id\uB294 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uBB38\uC790\uC5F4(\uB610\uB294 \uAC12 \uCC38\uC870)\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4"
|
|
1980
2270
|
});
|
|
1981
2271
|
}
|
|
1982
|
-
const args =
|
|
1983
|
-
if (args !== void 0 && !
|
|
2272
|
+
const args = isPlainObject2(node.props) ? node.props.args : void 0;
|
|
2273
|
+
if (args !== void 0 && !isPlainObject2(args)) {
|
|
1984
2274
|
issues.push({
|
|
1985
2275
|
path: `${path}.props.args`,
|
|
1986
2276
|
message: "Fragment.args\uB294 { \uC774\uB984: \uAC12 } \uD3C9\uBA74 \uC624\uBE0C\uC81D\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4"
|
|
@@ -1994,7 +2284,7 @@ function walkChild(child, path, issues, known) {
|
|
|
1994
2284
|
}
|
|
1995
2285
|
}
|
|
1996
2286
|
if (node.slots !== void 0) {
|
|
1997
|
-
if (!
|
|
2287
|
+
if (!isPlainObject2(node.slots)) {
|
|
1998
2288
|
issues.push({ path: `${path}.slots`, message: "slots\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1999
2289
|
} else {
|
|
2000
2290
|
for (const [slot, children] of Object.entries(node.slots)) {
|
|
@@ -2019,7 +2309,7 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2019
2309
|
value.forEach((v, i) => walkValue(v, `${path}[${i}]`, issues, actionEligible));
|
|
2020
2310
|
return;
|
|
2021
2311
|
}
|
|
2022
|
-
if (!
|
|
2312
|
+
if (!isPlainObject2(value)) return;
|
|
2023
2313
|
if (typeof value.action === "string" && (actionEligible || ACTION_NAMES.has(value.action))) {
|
|
2024
2314
|
if (actionEligible && !ACTION_NAMES.has(value.action)) {
|
|
2025
2315
|
issues.push({ path, message: `\uC54C \uC218 \uC5C6\uB294 \uC561\uC158: ${value.action}` });
|
|
@@ -2046,7 +2336,7 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2046
2336
|
}
|
|
2047
2337
|
if ("$calc" in value) {
|
|
2048
2338
|
const calc = value.$calc;
|
|
2049
|
-
if (!
|
|
2339
|
+
if (!isPlainObject2(calc) || typeof calc.op !== "string" || !CALC_OPS.has(calc.op)) {
|
|
2050
2340
|
issues.push({ path, message: "$calc.op\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4" });
|
|
2051
2341
|
} else if (!Array.isArray(calc.args)) {
|
|
2052
2342
|
issues.push({ path: `${path}.$calc.args`, message: "args\uB294 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" });
|
|
@@ -2075,12 +2365,12 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2075
2365
|
}
|
|
2076
2366
|
|
|
2077
2367
|
// src/page-data.ts
|
|
2078
|
-
function
|
|
2368
|
+
function isPlainObject3(value) {
|
|
2079
2369
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2080
2370
|
}
|
|
2081
2371
|
function planPageData(data, resolve) {
|
|
2082
2372
|
if (data === void 0) return [];
|
|
2083
|
-
if (!
|
|
2373
|
+
if (!isPlainObject3(data)) {
|
|
2084
2374
|
warnOnce("page-data:root", "[sdui] page.data\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4 \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4");
|
|
2085
2375
|
return [];
|
|
2086
2376
|
}
|
|
@@ -2090,7 +2380,7 @@ function planPageData(data, resolve) {
|
|
|
2090
2380
|
warnOnce(`page-data:${key}`, `[sdui] page.data\uC758 \uC0C1\uD0DC \uD0A4 "${key}"\uB294 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uAC74\uB108\uB701\uB2C8\uB2E4`);
|
|
2091
2381
|
continue;
|
|
2092
2382
|
}
|
|
2093
|
-
if (!
|
|
2383
|
+
if (!isPlainObject3(raw)) {
|
|
2094
2384
|
warnOnce(`page-data:${key}`, `[sdui] page.data.${key}\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4 \u2014 \uAC74\uB108\uB701\uB2C8\uB2E4`);
|
|
2095
2385
|
continue;
|
|
2096
2386
|
}
|
|
@@ -2125,7 +2415,7 @@ function pickPath(source, path) {
|
|
|
2125
2415
|
if (path === void 0 || path === "") return source;
|
|
2126
2416
|
let value = source;
|
|
2127
2417
|
for (const segment of path.split(".")) {
|
|
2128
|
-
if (!
|
|
2418
|
+
if (!isPlainObject3(value)) return void 0;
|
|
2129
2419
|
value = value[segment];
|
|
2130
2420
|
}
|
|
2131
2421
|
return value;
|
|
@@ -2156,26 +2446,26 @@ function nearestArgsFrame(scope) {
|
|
|
2156
2446
|
}
|
|
2157
2447
|
return void 0;
|
|
2158
2448
|
}
|
|
2159
|
-
function
|
|
2449
|
+
function isPlainObject4(v) {
|
|
2160
2450
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2161
2451
|
}
|
|
2162
2452
|
function isStateRef(v) {
|
|
2163
|
-
return
|
|
2453
|
+
return isPlainObject4(v) && typeof v.$state === "string";
|
|
2164
2454
|
}
|
|
2165
2455
|
function isItemRef(v) {
|
|
2166
|
-
return
|
|
2456
|
+
return isPlainObject4(v) && "$item" in v;
|
|
2167
2457
|
}
|
|
2168
2458
|
function isIndexRef(v) {
|
|
2169
|
-
return
|
|
2459
|
+
return isPlainObject4(v) && "$index" in v;
|
|
2170
2460
|
}
|
|
2171
2461
|
function isCalcRef(v) {
|
|
2172
|
-
return
|
|
2462
|
+
return isPlainObject4(v) && isPlainObject4(v.$calc);
|
|
2173
2463
|
}
|
|
2174
2464
|
function isArgRef(v) {
|
|
2175
|
-
return
|
|
2465
|
+
return isPlainObject4(v) && typeof v.$arg === "string";
|
|
2176
2466
|
}
|
|
2177
2467
|
function isAction(v) {
|
|
2178
|
-
return
|
|
2468
|
+
return isPlainObject4(v) && typeof v.action === "string";
|
|
2179
2469
|
}
|
|
2180
2470
|
function isActionList(v) {
|
|
2181
2471
|
return Array.isArray(v) && v.length > 0 && v.every(isAction);
|
|
@@ -2199,7 +2489,7 @@ function expandActionEntries(raw, scope) {
|
|
|
2199
2489
|
return [];
|
|
2200
2490
|
}
|
|
2201
2491
|
function eventValue(arg) {
|
|
2202
|
-
if (
|
|
2492
|
+
if (isPlainObject4(arg) && "target" in arg && typeof arg.preventDefault === "function") {
|
|
2203
2493
|
const target = arg.target;
|
|
2204
2494
|
if (target.type === "checkbox") return target.checked;
|
|
2205
2495
|
if (target.type === "number" || target.type === "range") {
|
|
@@ -2211,14 +2501,6 @@ function eventValue(arg) {
|
|
|
2211
2501
|
}
|
|
2212
2502
|
return arg;
|
|
2213
2503
|
}
|
|
2214
|
-
function toNumber2(v) {
|
|
2215
|
-
if (typeof v === "number") return v;
|
|
2216
|
-
if (typeof v === "string") {
|
|
2217
|
-
const n = Number(v.replace(/,/g, ""));
|
|
2218
|
-
return Number.isFinite(n) ? n : 0;
|
|
2219
|
-
}
|
|
2220
|
-
return 0;
|
|
2221
|
-
}
|
|
2222
2504
|
function toIndex(v) {
|
|
2223
2505
|
if (typeof v === "number") return Number.isInteger(v) ? v : null;
|
|
2224
2506
|
if (typeof v === "string" && /^-?\d+$/.test(v.trim())) return Number(v.trim());
|
|
@@ -2317,7 +2599,7 @@ function childrenOf(node) {
|
|
|
2317
2599
|
}
|
|
2318
2600
|
function renderChild(child, index, scope, engine) {
|
|
2319
2601
|
if (typeof child === "string") return child;
|
|
2320
|
-
if (!
|
|
2602
|
+
if (!isPlainObject4(child)) {
|
|
2321
2603
|
warnOnce2(`child:${String(child)}`, `[sdui] \uC798\uBABB\uB41C \uB178\uB4DC(\uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4): ${String(child)}`);
|
|
2322
2604
|
return /* @__PURE__ */ jsx(SpecErrorCard, { children: "[sdui] \uC798\uBABB\uB41C \uB178\uB4DC \u2014 \uBB38\uC790\uC5F4 \uB610\uB294 \uB178\uB4DC \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" }, index);
|
|
2323
2605
|
}
|
|
@@ -2332,11 +2614,11 @@ function SduiNodeRenderer({
|
|
|
2332
2614
|
engine
|
|
2333
2615
|
}) {
|
|
2334
2616
|
const { registry, resolveValue, dispatch, resolveAsset } = engine;
|
|
2335
|
-
const slots =
|
|
2617
|
+
const slots = isPlainObject4(node.slots) ? node.slots : void 0;
|
|
2336
2618
|
const childList = childrenOf(node);
|
|
2337
2619
|
if (node.component === "ForEach") {
|
|
2338
2620
|
const source = resolveValue(
|
|
2339
|
-
|
|
2621
|
+
isPlainObject4(node.props) ? node.props.source ?? null : null,
|
|
2340
2622
|
scope
|
|
2341
2623
|
);
|
|
2342
2624
|
const rows = Array.isArray(source) ? source : [];
|
|
@@ -2346,7 +2628,7 @@ function SduiNodeRenderer({
|
|
|
2346
2628
|
}
|
|
2347
2629
|
return /* @__PURE__ */ jsx(Fragment, { children: rows.map((item, rowIndex) => {
|
|
2348
2630
|
const rowScope = [...scope, { item, index: rowIndex }];
|
|
2349
|
-
const rowKey =
|
|
2631
|
+
const rowKey = isPlainObject4(item) && typeof item.id === "string" ? item.id : rowIndex;
|
|
2350
2632
|
return /* @__PURE__ */ jsx(React11.Fragment, { children: childList?.map((child, i) => renderChild(child, i, rowScope, engine)) }, rowKey);
|
|
2351
2633
|
}) });
|
|
2352
2634
|
}
|
|
@@ -2365,7 +2647,7 @@ function SduiNodeRenderer({
|
|
|
2365
2647
|
] });
|
|
2366
2648
|
}
|
|
2367
2649
|
const props = {};
|
|
2368
|
-
for (const [name, raw] of Object.entries(
|
|
2650
|
+
for (const [name, raw] of Object.entries(isPlainObject4(node.props) ? node.props : {})) {
|
|
2369
2651
|
if (DENIED_PROPS2.has(name)) {
|
|
2370
2652
|
warnOnce2(`prop:${name}`, `[sdui] \uC2A4\uD399\uC5D0\uC11C \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD504\uB86D: ${name} \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
2371
2653
|
continue;
|
|
@@ -2404,7 +2686,7 @@ function SduiNodeRenderer({
|
|
|
2404
2686
|
if (childLayout) {
|
|
2405
2687
|
for (const name of CHILD_LAYOUT_PROPS) delete props[name];
|
|
2406
2688
|
if (childLayout.style) {
|
|
2407
|
-
props.style =
|
|
2689
|
+
props.style = isPlainObject4(props.style) ? { ...childLayout.style, ...props.style } : childLayout.style;
|
|
2408
2690
|
}
|
|
2409
2691
|
if (childLayout.className) {
|
|
2410
2692
|
props.className = typeof props.className === "string" && props.className ? `${childLayout.className} ${props.className}` : childLayout.className;
|
|
@@ -2428,7 +2710,7 @@ var FRAGMENT_MAX_DEPTH = 4;
|
|
|
2428
2710
|
function SduiFragment({ node, scope, engine }) {
|
|
2429
2711
|
const adapter = useSduiAdapter();
|
|
2430
2712
|
const chain = React11.useContext(SduiFragmentChainContext);
|
|
2431
|
-
const raw =
|
|
2713
|
+
const raw = isPlainObject4(node.props) ? node.props.id : void 0;
|
|
2432
2714
|
const id = engine.resolveValue(raw, scope);
|
|
2433
2715
|
if (typeof id !== "string" || id === "") {
|
|
2434
2716
|
warnOnce2("fragment:id", "[sdui] <Fragment>\uC5D0 \uBB38\uC790\uC5F4 id\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uB80C\uB354\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
@@ -2448,8 +2730,8 @@ function SduiFragment({ node, scope, engine }) {
|
|
|
2448
2730
|
);
|
|
2449
2731
|
return null;
|
|
2450
2732
|
}
|
|
2451
|
-
const rawArgs =
|
|
2452
|
-
if (rawArgs !== void 0 && !
|
|
2733
|
+
const rawArgs = isPlainObject4(node.props) ? node.props.args : void 0;
|
|
2734
|
+
if (rawArgs !== void 0 && !isPlainObject4(rawArgs)) {
|
|
2453
2735
|
warnOnce2(`fragment:args:${id}`, `[sdui] <Fragment ${id}>\uC758 args\uB294 \uD3C9\uBA74 \uC624\uBE0C\uC81D\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4 \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
2454
2736
|
}
|
|
2455
2737
|
const useSpec = adapter?.useSpec;
|
|
@@ -2464,7 +2746,7 @@ function SduiFragment({ node, scope, engine }) {
|
|
|
2464
2746
|
SduiFragmentBody,
|
|
2465
2747
|
{
|
|
2466
2748
|
id,
|
|
2467
|
-
args:
|
|
2749
|
+
args: isPlainObject4(rawArgs) ? rawArgs : void 0,
|
|
2468
2750
|
useSpec,
|
|
2469
2751
|
chain,
|
|
2470
2752
|
scope,
|
|
@@ -2485,13 +2767,13 @@ function SduiFragmentBody({
|
|
|
2485
2767
|
const nextChain = React11.useMemo(() => [...chain, id], [chain, id]);
|
|
2486
2768
|
const bodyScope = React11.useMemo(() => [...scope, { args, callSiteScope: scope }], [scope, args]);
|
|
2487
2769
|
React11.useEffect(() => {
|
|
2488
|
-
if (!DEV || !
|
|
2770
|
+
if (!DEV || !isPlainObject4(spec) || !Array.isArray(spec.body)) return;
|
|
2489
2771
|
validateSpec(spec, new Set(Object.keys(engine.registry))).forEach(
|
|
2490
2772
|
(issue) => console.warn(`[sdui:spec:${id}] ${issue.path} \u2014 ${issue.message}`)
|
|
2491
2773
|
);
|
|
2492
2774
|
}, [spec, engine.registry, id]);
|
|
2493
2775
|
if (spec === void 0) return null;
|
|
2494
|
-
if (spec === null || !
|
|
2776
|
+
if (spec === null || !isPlainObject4(spec) || !Array.isArray(spec.body)) {
|
|
2495
2777
|
warnOnce2(`fragment:missing:${id}`, `[sdui] Fragment \uC2A4\uD399\uC744 \uD574\uC11D\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${id} \u2014 \uB80C\uB354\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`);
|
|
2496
2778
|
return null;
|
|
2497
2779
|
}
|
|
@@ -2621,7 +2903,7 @@ function SduiRenderer({
|
|
|
2621
2903
|
if (head in current) {
|
|
2622
2904
|
let value = current[head];
|
|
2623
2905
|
for (const segment of segments.slice(cut)) {
|
|
2624
|
-
if (!
|
|
2906
|
+
if (!isPlainObject4(value)) return void 0;
|
|
2625
2907
|
value = value[segment];
|
|
2626
2908
|
}
|
|
2627
2909
|
return value;
|
|
@@ -2639,7 +2921,7 @@ function SduiRenderer({
|
|
|
2639
2921
|
const frame = nearestItemFrame(scope);
|
|
2640
2922
|
if (!frame) return null;
|
|
2641
2923
|
if (value.$item === true) return frame.item;
|
|
2642
|
-
return
|
|
2924
|
+
return isPlainObject4(frame.item) ? frame.item[value.$item] ?? null : null;
|
|
2643
2925
|
}
|
|
2644
2926
|
if (isIndexRef(value)) {
|
|
2645
2927
|
return nearestItemFrame(scope)?.index ?? null;
|
|
@@ -2653,64 +2935,17 @@ function SduiRenderer({
|
|
|
2653
2935
|
return void 0;
|
|
2654
2936
|
}
|
|
2655
2937
|
if (isCalcRef(value)) {
|
|
2656
|
-
const
|
|
2657
|
-
const op = typeof
|
|
2658
|
-
if (!Array.isArray(args)) {
|
|
2938
|
+
const calc = value.$calc;
|
|
2939
|
+
const op = typeof calc.op === "string" ? calc.op : "";
|
|
2940
|
+
if (!Array.isArray(calc.args)) {
|
|
2659
2941
|
warnOnce2(`calc:${op}`, `[sdui] $calc(${op || "?"}): args\uB294 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 \u2014 \uAC12 \uC5C6\uC74C\uC73C\uB85C \uCC98\uB9AC\uD569\uB2C8\uB2E4`);
|
|
2660
2942
|
return null;
|
|
2661
2943
|
}
|
|
2662
|
-
const
|
|
2663
|
-
|
|
2664
|
-
const nums = (v) => {
|
|
2665
|
-
if (Array.isArray(v))
|
|
2666
|
-
return v.map((row) => field && isPlainObject3(row) ? toNumber2(row[field]) : toNumber2(row));
|
|
2667
|
-
return [toNumber2(v)];
|
|
2668
|
-
};
|
|
2669
|
-
switch (op) {
|
|
2670
|
-
case "sum":
|
|
2671
|
-
return resolved.flatMap(nums).reduce((a, b) => a + b, 0);
|
|
2672
|
-
case "count":
|
|
2673
|
-
return resolved.reduce((a, v) => a + (Array.isArray(v) ? v.length : v == null ? 0 : 1), 0);
|
|
2674
|
-
case "sub": {
|
|
2675
|
-
const list = resolved.map((v) => toNumber2(v));
|
|
2676
|
-
return list.length === 0 ? 0 : list.reduce((a, b) => a - b);
|
|
2677
|
-
}
|
|
2678
|
-
case "mul":
|
|
2679
|
-
return resolved.map((v) => toNumber2(v)).reduce((a, b) => a * b, 1);
|
|
2680
|
-
case "div": {
|
|
2681
|
-
const [a, b] = resolved.map((v) => toNumber2(v));
|
|
2682
|
-
return b === 0 ? 0 : a / b;
|
|
2683
|
-
}
|
|
2684
|
-
case "percent": {
|
|
2685
|
-
const [a, b] = resolved.map((v) => toNumber2(v));
|
|
2686
|
-
return b === 0 ? 0 : Math.round(a / b * 1e3) / 10;
|
|
2687
|
-
}
|
|
2688
|
-
case "concat":
|
|
2689
|
-
return resolved.map((v) => v == null ? "" : String(v)).join("");
|
|
2690
|
-
case "encode":
|
|
2691
|
-
return encodeURIComponent(resolved.map((v) => v == null ? "" : String(v)).join(""));
|
|
2692
|
-
case "eq": {
|
|
2693
|
-
const [a, b] = resolved;
|
|
2694
|
-
return String(a ?? "") === String(b ?? "");
|
|
2695
|
-
}
|
|
2696
|
-
// 수량 비교 — 정원 초과 차단(Button.disabled), 임계값 경고(Show.when) 등
|
|
2697
|
-
case "gt":
|
|
2698
|
-
return toNumber2(resolved[0]) > toNumber2(resolved[1]);
|
|
2699
|
-
case "gte":
|
|
2700
|
-
return toNumber2(resolved[0]) >= toNumber2(resolved[1]);
|
|
2701
|
-
case "lt":
|
|
2702
|
-
return toNumber2(resolved[0]) < toNumber2(resolved[1]);
|
|
2703
|
-
case "lte":
|
|
2704
|
-
return toNumber2(resolved[0]) <= toNumber2(resolved[1]);
|
|
2705
|
-
/** 부정 — 조건을 뒤집어 "없을 때만" 렌더링하는 데 쓴다 */
|
|
2706
|
-
case "not":
|
|
2707
|
-
return !resolved[0];
|
|
2708
|
-
default:
|
|
2709
|
-
return null;
|
|
2710
|
-
}
|
|
2944
|
+
const resolved = calc.args.map((arg) => resolveValue(arg, scope));
|
|
2945
|
+
return evaluateCalc(calc, resolved, (late) => resolveValue(late, scope));
|
|
2711
2946
|
}
|
|
2712
2947
|
if (Array.isArray(value)) return value.map((v) => resolveValue(v, scope));
|
|
2713
|
-
if (
|
|
2948
|
+
if (isPlainObject4(value)) {
|
|
2714
2949
|
const out = {};
|
|
2715
2950
|
for (const [k, v] of Object.entries(value)) {
|
|
2716
2951
|
out[k] = deferChainEntry(value, k) ? v : resolveValue(v, scope);
|
|
@@ -2879,7 +3114,7 @@ function SduiRenderer({
|
|
|
2879
3114
|
break;
|
|
2880
3115
|
}
|
|
2881
3116
|
const row = current[index];
|
|
2882
|
-
if (!
|
|
3117
|
+
if (!isPlainObject4(row)) break;
|
|
2883
3118
|
const nextValue = (action.fromEvent ? eventValue(arg) : action.value) ?? null;
|
|
2884
3119
|
const next = [...current];
|
|
2885
3120
|
next[index] = { ...row, [action.field]: nextValue };
|
|
@@ -3022,4 +3257,4 @@ function SduiRenderer({
|
|
|
3022
3257
|
);
|
|
3023
3258
|
}
|
|
3024
3259
|
|
|
3025
|
-
export { Actions, Can, Col, Divider, Grid, KeyValueList, NodeErrorBoundary, PageHead, RemoteSearchResults, ResultTable, Row, SduiDialog, SduiForm, SduiPermissionProvider, SduiRenderer, SduiSearchDataProvider, SduiSearchResults, SearchResults, Section, Show, SortHandle, SortableList, Spacer, Text, cellRenderer, createRegistry, defaultRegistry, imageRenderer, normalizePageSizes, sortableDropIndex, sortableMove, useRemoteSearchPage, useSduiCan, useSduiDispatch, useSduiRuntime, useSearchFetcher, validateSpec };
|
|
3260
|
+
export { Actions, CALC_OPS, Can, Col, Divider, Grid, KeyValueList, NodeErrorBoundary, PageHead, RemoteSearchResults, ResultTable, Row, SduiDialog, SduiForm, SduiPermissionProvider, SduiRenderer, SduiSearchDataProvider, SduiSearchResults, SearchResults, Section, Show, SortHandle, SortableList, Spacer, Text, cellRenderer, createRegistry, defaultRegistry, imageRenderer, normalizePageSizes, sortableDropIndex, sortableMove, useRemoteSearchPage, useSduiCan, useSduiDispatch, useSduiRuntime, useSearchFetcher, validateSpec };
|
package/dist/meta.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as SduiPropValue, i as SduiNode } from './types-
|
|
1
|
+
import { b as SduiPropValue, i as SduiNode } from './types-BNdv5-jn.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 시각 빌더용 컴포넌트 메타데이터 스키마.
|
|
@@ -405,9 +405,9 @@ declare const richComponentMeta: {
|
|
|
405
405
|
* 받지 않게 하려는 것이다. 여기도 값 import는 없다(`import type`만).
|
|
406
406
|
*
|
|
407
407
|
* 프롭 목록은 `src/widgets/*.tsx`의 **실제 프롭 인터페이스**와, ERP 스펙 84개를 훑어
|
|
408
|
-
* 뽑은 **실사용 프롭 집합**의 합집합이다.
|
|
409
|
-
*
|
|
410
|
-
*
|
|
408
|
+
* 뽑은 **실사용 프롭 집합**의 합집합이다. 스펙이 쓰는데 구현에 없는 프롭은 이제
|
|
409
|
+
* **한 개도 없다** — 마지막 하나였던 `AutocompleteField.endpoint`가 0.22.0에서
|
|
410
|
+
* 구현으로 들어왔다(소비 앱이 들고 있던 어댑터가 그만큼 사라진다).
|
|
411
411
|
*
|
|
412
412
|
* ## 함수 프롭을 메타데이터에 넣지 않은 자리
|
|
413
413
|
*
|
package/dist/meta.js
CHANGED
|
@@ -3298,7 +3298,7 @@ var AutocompleteFieldMeta = {
|
|
|
3298
3298
|
label: "\uC790\uB3D9\uC644\uC131",
|
|
3299
3299
|
category: "input",
|
|
3300
3300
|
icon: "search",
|
|
3301
|
-
description: "\uAC80\uC0C9\uD574\uC11C \uD558\uB098\uB97C \uACE0\uB974\uB294 \uC785\uB825. \uACE0\uB978 \uB4A4\uC5D0\uB294 \uC120\uD0DD \uCE74\uB4DC\uB85C \uC811\uD78C\uB2E4. \
|
|
3301
|
+
description: "\uAC80\uC0C9\uD574\uC11C \uD558\uB098\uB97C \uACE0\uB974\uB294 \uC785\uB825. \uACE0\uB978 \uB4A4\uC5D0\uB294 \uC120\uD0DD \uCE74\uB4DC\uB85C \uC811\uD78C\uB2E4. \uAC80\uC0C9 \uACBD\uB85C\uB97C \uC8FC\uBA74 \uC6D0\uACA9 \uC870\uD68C\uAE4C\uC9C0 \uD55C\uB2E4.",
|
|
3302
3302
|
props: [
|
|
3303
3303
|
{
|
|
3304
3304
|
name: "options",
|
|
@@ -3316,9 +3316,15 @@ var AutocompleteFieldMeta = {
|
|
|
3316
3316
|
{
|
|
3317
3317
|
name: "endpoint",
|
|
3318
3318
|
kind: "string",
|
|
3319
|
-
legacy: true,
|
|
3320
3319
|
label: "\uAC80\uC0C9 \uACBD\uB85C",
|
|
3321
|
-
description: "\
|
|
3320
|
+
description: "\uC8FC\uBA74 \uC5B4\uB311\uD130\uC758 fetch\uB85C \uC9C1\uC811 \uC870\uD68C\uD55C\uB2E4(`GET {\uACBD\uB85C}?q={\uD0A4\uC6CC\uB4DC}`). 0.22.0 \uC804\uC5D0\uB294 \uC774 \uD504\uB86D\uC774 \uB77C\uC774\uBE0C\uB7EC\uB9AC \uAD6C\uD604\uC5D0 \uC5C6\uC5B4\uC11C \uC18C\uBE44 \uC571\uC774 \uC5B4\uB311\uD130\uB97C \uC50C\uC6CC endpoint\uB97C search \uD568\uC218\uB85C \uBC14\uAFD4 \uB4F1\uB85D\uD588\uB2E4 \u2014 \uC774\uC81C \uD544\uC694 \uC5C6\uB2E4.",
|
|
3321
|
+
group: "data"
|
|
3322
|
+
},
|
|
3323
|
+
{
|
|
3324
|
+
name: "searchParam",
|
|
3325
|
+
kind: "string",
|
|
3326
|
+
label: "\uD0A4\uC6CC\uB4DC \uD30C\uB77C\uBBF8\uD130",
|
|
3327
|
+
description: "endpoint \uC870\uD68C\uC5D0 \uBD99\uB294 \uD0A4\uC6CC\uB4DC \uCFFC\uB9AC \uC774\uB984. \uAE30\uBCF8 'q'.",
|
|
3322
3328
|
group: "data"
|
|
3323
3329
|
},
|
|
3324
3330
|
{
|
package/dist/rich.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { TimePicker } from './chunk-PNVOE5VH.js';
|
|
2
|
+
import { useSduiAssetResolver } from './chunk-WGRB5UL4.js';
|
|
3
3
|
import { HoistedStyle } from './chunk-XKLUCUVA.js';
|
|
4
4
|
export { DatePicker, DateRangePicker } from './chunk-XKLUCUVA.js';
|
|
5
5
|
import { Spinner, EmptyState } from './chunk-WIDDHR7L.js';
|
|
@@ -64,11 +64,67 @@ interface SduiIndexRef {
|
|
|
64
64
|
interface SduiCalcRef {
|
|
65
65
|
$calc: {
|
|
66
66
|
op: 'sum' | 'sub' | 'mul' | 'div' | 'percent' | 'count' | 'concat' | 'encode'
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* 비교·논리 — Show의 when, Button의 disabled 등에 바인딩한다.
|
|
69
|
+
* 순서 비교(gt/gte/lt/lte)는 숫자로 재되, **양쪽이 모두 숫자가 아닌 문자열일
|
|
70
|
+
* 때만** 사전순으로 본다 — ISO 날짜·시각 문자열의 대소가 이 규칙으로 성립한다.
|
|
71
|
+
*/
|
|
72
|
+
| 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'not' | 'and' | 'or'
|
|
73
|
+
/**
|
|
74
|
+
* 값 고르기. `if`는 `then`/`else`, `match`는 `cases`(+`default`)를 쓴다 —
|
|
75
|
+
* 둘 다 **고른 가지만** 해석되므로 안 고른 가지의 조회는 나가지 않는다.
|
|
76
|
+
* `coalesce`는 args에서 처음으로 비지 않은 값(null·undefined·'' 제외)을 준다.
|
|
77
|
+
*/
|
|
78
|
+
| 'if' | 'match' | 'coalesce'
|
|
79
|
+
/**
|
|
80
|
+
* 목록·객체. JSON에는 람다가 없으므로 술어는 `where` 객체로 쓴다
|
|
81
|
+
* (Prisma의 where와 같은 모양 — 아래 필드 설명 참고).
|
|
82
|
+
*
|
|
83
|
+
* - `get` args[0]에서 args[1] 키/인덱스로 꺼낸다(런타임 값이 키가 될 수 있다)
|
|
84
|
+
* - `pluck` 객체 배열에서 `field` 값만 뽑아 값 배열로 만든다(빈 값은 떨어뜨린다)
|
|
85
|
+
* - `filter` `where`에 맞는 행만 남긴다
|
|
86
|
+
* - `some`/`every` `where`에 맞는 행이 있는지 / 전부인지
|
|
87
|
+
* - `includes` 목록(또는 문자열)이 args[1]을 담고 있는지
|
|
88
|
+
* - `distinct` `field`(없으면 값 자체) 기준 중복 제거
|
|
89
|
+
* - `except` args[0]에서 args[1]에 있는 것을 뺀다(`field`로 맞춘다)
|
|
90
|
+
* - `entries` 객체를 `{key, value}` 배열로 편다 — ForEach가 돌 수 있는 모양
|
|
91
|
+
* - `length` 배열·문자열의 길이
|
|
92
|
+
* - `join` `sep`(기본 ',')으로 잇는다
|
|
93
|
+
* - `split` `sep`(기본 ',')으로 가른다. `take`를 주면 그 조각 하나(음수는 뒤에서)
|
|
94
|
+
* - `sortBy` `field` 기준 정렬(`desc: true`면 내림차순). 원본은 건드리지 않는다
|
|
95
|
+
*/
|
|
96
|
+
| 'get' | 'pluck' | 'filter' | 'some' | 'every' | 'includes' | 'distinct' | 'except' | 'entries' | 'length' | 'join' | 'split' | 'sortBy';
|
|
69
97
|
args: SduiPropValue[];
|
|
70
|
-
/** sum/count
|
|
98
|
+
/** sum/count·목록 연산에서 객체 배열의 필드를 지정 */
|
|
71
99
|
field?: string;
|
|
100
|
+
/**
|
|
101
|
+
* `filter`·`some`·`every`의 술어. 키가 필드, 값이 연산 객체다.
|
|
102
|
+
*
|
|
103
|
+
* { "deptId": { "eq": { "$state": "sel" } },
|
|
104
|
+
* "level": { "gte": 3 },
|
|
105
|
+
* "OR": [ { "status": "open" }, { "owner": { "isNull": true } } ] }
|
|
106
|
+
*
|
|
107
|
+
* 필드 연산: eq · ne · gt · gte · lt · lte · in · notIn · contains ·
|
|
108
|
+
* startsWith · endsWith · isNull. 값이 연산 객체가 아니면 `eq`로 읽는다.
|
|
109
|
+
* 같은 객체의 키들은 AND이고, `AND`/`OR`는 술어 배열, `NOT`은 술어 하나다.
|
|
110
|
+
* 술어 안의 값에도 참조를 쓸 수 있는데, 이때 `$item`은 거르고 있는 행이 아니라
|
|
111
|
+
* **바깥 ForEach의 행**을 가리킨다(거르는 행은 필드 이름으로 지목한다).
|
|
112
|
+
*/
|
|
113
|
+
where?: SduiPropValue;
|
|
114
|
+
/** `if`가 참일 때의 값 (고를 때만 해석된다) */
|
|
115
|
+
then?: SduiPropValue;
|
|
116
|
+
/** `if`가 거짓일 때의 값 (고를 때만 해석된다) */
|
|
117
|
+
else?: SduiPropValue;
|
|
118
|
+
/** `match`의 대응표 — 키는 args[0]을 문자열로 눕힌 값 */
|
|
119
|
+
cases?: Record<string, SduiPropValue>;
|
|
120
|
+
/** `match`에서 어느 case에도 안 맞을 때의 값 */
|
|
121
|
+
default?: SduiPropValue;
|
|
122
|
+
/** `join`/`split`의 구분자 (기본 ',') */
|
|
123
|
+
sep?: string;
|
|
124
|
+
/** `split`에서 꺼낼 조각의 인덱스. 음수는 뒤에서부터 센다 */
|
|
125
|
+
take?: number;
|
|
126
|
+
/** `sortBy`를 내림차순으로 */
|
|
127
|
+
desc?: boolean;
|
|
72
128
|
};
|
|
73
129
|
}
|
|
74
130
|
/** 폼 필드 검증 규칙. */
|
|
@@ -26,7 +26,20 @@ interface AutocompleteFieldProps {
|
|
|
26
26
|
* ERP의 `endpoint` 프롭은 이 함수를 만드는 앱 쪽 어댑터가 계속 소유한다(위 주석 참고).
|
|
27
27
|
*/
|
|
28
28
|
search?: AutocompleteSearchFn;
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* 조회 경로. 주면 **어댑터의 `fetch`로 직접 조회한다** — 소비 앱이 `search`를
|
|
31
|
+
* 만들어 넘기지 않아도 된다(`GET {endpoint}?{searchParam}={키워드}`).
|
|
32
|
+
*
|
|
33
|
+
* `search`가 함께 오면 그쪽이 이긴다 — 응답 모양이나 오류 분기가 특수한 화면은
|
|
34
|
+
* 여전히 자기 함수를 넘길 수 있다.
|
|
35
|
+
*
|
|
36
|
+
* 어댑터가 없거나 `fetch`를 안 준 환경(편집기 캔버스 등)에서는 조회하지 않고
|
|
37
|
+
* `options` 로컬 검색으로 떨어진다 — 미리보기에서 실요청이 나가면 안 된다.
|
|
38
|
+
*/
|
|
39
|
+
endpoint?: string;
|
|
40
|
+
/** `endpoint` 조회의 키워드 파라미터 이름. 기본 'q'. */
|
|
41
|
+
searchParam?: string;
|
|
42
|
+
/** 정적 옵션(데모/폴백) — `search`·`endpoint`가 없을 때 로컬 검색 대상. */
|
|
30
43
|
options?: AutocompleteOption[];
|
|
31
44
|
/** 옵션에서 표시 라벨을 읽을 키. 기본 'label'. */
|
|
32
45
|
labelKey?: string;
|
|
@@ -48,6 +61,20 @@ interface AutocompleteFieldProps {
|
|
|
48
61
|
invalid?: boolean;
|
|
49
62
|
disabled?: boolean;
|
|
50
63
|
}
|
|
64
|
+
type HostFetch = (path: string, init: {
|
|
65
|
+
method: string;
|
|
66
|
+
signal?: AbortSignal;
|
|
67
|
+
}) => Promise<unknown>;
|
|
68
|
+
/**
|
|
69
|
+
* `endpoint` → 검색 함수. 컴포넌트 밖에 두어 DOM 없이 그대로 잰다
|
|
70
|
+
* (`page-data.ts`·`calc.ts`와 같은 이유).
|
|
71
|
+
*
|
|
72
|
+
* 503만 별도로 본다. 어댑터 계약상 실패는 Error 하나라 상태 코드가 사라지지만,
|
|
73
|
+
* 503은 "서비스가 아직 없다"는 표준 의미라 "불러왔는데 없다"와 사용자에게 다른
|
|
74
|
+
* 말을 해야 한다. 호스트 오류 객체에 `status`가 있으면 읽고, 없으면 평소대로
|
|
75
|
+
* 던진다 — 특정 앱의 오류 타입을 알지 않는다.
|
|
76
|
+
*/
|
|
77
|
+
declare function endpointSearchFn(endpoint: string, searchParam: string, hostFetch: HostFetch): AutocompleteSearchFn;
|
|
51
78
|
/**
|
|
52
79
|
* 응답이 배열이 아니면 items/data/results/rows 래핑까지 풀어 준다.
|
|
53
80
|
* 조회를 앱이 맡게 됐으므로 이 정규화도 함께 내보낸다 — 앱마다 다시 짤 규칙이 아니다.
|
|
@@ -59,4 +86,4 @@ declare function toAutocompleteOptions(payload: unknown): AutocompleteOption[];
|
|
|
59
86
|
*/
|
|
60
87
|
declare const AutocompleteField: React__default.FC<AutocompleteFieldProps & SduiBoxProps>;
|
|
61
88
|
|
|
62
|
-
export { AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type AutocompleteSearchFn, type AutocompleteSearchResult, toAutocompleteOptions };
|
|
89
|
+
export { AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type AutocompleteSearchFn, type AutocompleteSearchResult, endpointSearchFn, toAutocompleteOptions };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { AutocompleteField, toAutocompleteOptions } from '../chunk-
|
|
1
|
+
export { AutocompleteField, endpointSearchFn, toAutocompleteOptions } from '../chunk-WZKDOZL2.js';
|
|
2
2
|
import '../chunk-U7ZK7WYH.js';
|
|
3
|
+
import '../chunk-WGRB5UL4.js';
|
|
3
4
|
import '../chunk-XKLUCUVA.js';
|
|
4
5
|
import '../chunk-WIDDHR7L.js';
|
|
5
6
|
import '../chunk-SAN4IX6N.js';
|
package/dist/widgets/index.js
CHANGED
|
@@ -35,8 +35,9 @@ export { PeriodFilter } from '../chunk-PFUULIZ2.js';
|
|
|
35
35
|
import { PrintSheet } from '../chunk-MYJ2KEIP.js';
|
|
36
36
|
export { PrintSheet } from '../chunk-MYJ2KEIP.js';
|
|
37
37
|
import '../chunk-THEP3VWY.js';
|
|
38
|
-
export { AutocompleteField, toAutocompleteOptions } from '../chunk-
|
|
38
|
+
export { AutocompleteField, toAutocompleteOptions } from '../chunk-WZKDOZL2.js';
|
|
39
39
|
import '../chunk-U7ZK7WYH.js';
|
|
40
|
+
import '../chunk-WGRB5UL4.js';
|
|
40
41
|
import '../chunk-XKLUCUVA.js';
|
|
41
42
|
import { ColumnBoard } from '../chunk-C4JRSZGS.js';
|
|
42
43
|
export { ColumnBoard } from '../chunk-C4JRSZGS.js';
|