@wipco/sdui 0.50.1 → 1.0.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/{DatePicker-BK_jsV7i.d.ts → DatePicker-C_TT_SOw.d.ts} +25 -0
- package/dist/{chunk-OEWTVLPZ.js → chunk-2HLXCKK7.js} +2 -2
- package/dist/{chunk-PWVHAUJC.js → chunk-2YP5N4VV.js} +6 -4
- package/dist/{chunk-QTHYLNJW.js → chunk-37SFFW37.js} +2 -13
- package/dist/{chunk-YQOMSWW5.js → chunk-5WW4MBRO.js} +1 -1
- package/dist/{chunk-LIOS3PF2.js → chunk-5XPZ3RSA.js} +3 -4
- package/dist/{chunk-ZD3VNVRF.js → chunk-7BGDSSAN.js} +5 -13
- package/dist/{chunk-Y2LIEWQX.js → chunk-7FZTUI6U.js} +6 -5
- package/dist/{chunk-HJ5VSXR4.js → chunk-7P7N43JV.js} +2 -2
- package/dist/{chunk-55VB6SDU.js → chunk-A5XQOEVH.js} +23 -20
- package/dist/{chunk-77LKJCMM.js → chunk-A6LAIGUS.js} +20 -108
- package/dist/{chunk-YXS5FXVS.js → chunk-AC5TI3BE.js} +7 -16
- package/dist/{chunk-AIOJYVJ2.js → chunk-AXGF4M57.js} +1 -1
- package/dist/{chunk-5MPK3NZK.js → chunk-B6WMC2AN.js} +3 -3
- package/dist/{chunk-C6ZV4VBN.js → chunk-C37U3GCC.js} +4 -4
- package/dist/{chunk-265YPW4W.js → chunk-DMLDKW7K.js} +1 -1
- package/dist/{chunk-FZ72IDVF.js → chunk-E6ISNQK3.js} +2 -2
- package/dist/{chunk-STX2AX6Y.js → chunk-FLAN7CV3.js} +7 -39
- package/dist/{chunk-IV5YTVHR.js → chunk-GE6BYNQJ.js} +18 -33
- package/dist/{chunk-ZX36LKJ7.js → chunk-GTGS2QRL.js} +7 -6
- package/dist/{chunk-NL3I6F2I.js → chunk-HBBTENSV.js} +3 -3
- package/dist/{chunk-RUU7WBGR.js → chunk-I45EUZC6.js} +19 -9
- package/dist/{chunk-JBKNIALK.js → chunk-I5SD7XRA.js} +9 -6
- package/dist/{chunk-X7GHY2RI.js → chunk-IFKYGJXP.js} +14 -42
- package/dist/{chunk-J3YMUR6R.js → chunk-IZ3JA2BR.js} +3 -3
- package/dist/{chunk-AUW5RP24.js → chunk-JIV4YHCZ.js} +7 -6
- package/dist/{chunk-M6KM3FJS.js → chunk-JYAR7D5U.js} +2 -2
- package/dist/{chunk-DKPNFEVU.js → chunk-KO6OEY3G.js} +19 -6
- package/dist/{chunk-JHHMFL5D.js → chunk-LFEJLOFN.js} +28 -16
- package/dist/chunk-MMIGOPO5.js +111 -0
- package/dist/{chunk-YSSNP6JM.js → chunk-N25YP7ID.js} +6 -21
- package/dist/{chunk-F2W2FWOT.js → chunk-N5RKM6IC.js} +7 -7
- package/dist/{chunk-C4HYWZML.js → chunk-O4IISNE7.js} +1 -1
- package/dist/chunk-Q4SWUINA.js +51 -0
- package/dist/{chunk-TUOG7EEI.js → chunk-RNOUXUBO.js} +418 -22
- package/dist/{chunk-4VZI3PLI.js → chunk-RYHEV5E3.js} +15 -39
- package/dist/{chunk-AVRNQOB5.js → chunk-SAPBDJJQ.js} +14 -13
- package/dist/{chunk-OPJ4DYNI.js → chunk-SJ3737RA.js} +156 -158
- package/dist/{chunk-UMANGYRD.js → chunk-TFGNKFL2.js} +1 -1
- package/dist/{chunk-KYGSMUUU.js → chunk-VY7MVBSE.js} +2 -2
- package/dist/{chunk-VDBUXYEB.js → chunk-WOYMEEPH.js} +8 -8
- package/dist/{chunk-I5Z2RVDF.js → chunk-WSLT32Q4.js} +278 -30
- package/dist/{chunk-NYUHSMUU.js → chunk-XGFDY43I.js} +2 -2
- package/dist/{chunk-ODKZF4IT.js → chunk-YOZN35HD.js} +17 -9
- package/dist/{chunk-EKP6Z3G6.js → chunk-YVTV4ZV6.js} +1 -1
- package/dist/{chunk-6BCWFPNJ.js → chunk-YWUDBLO6.js} +2 -2
- package/dist/chunk-Z4PYMTTS.js +41 -0
- package/dist/{chunk-WXIQLWIX.js → chunk-ZFNFPXPR.js} +2 -2
- package/dist/{chunk-7GOWOSX6.js → chunk-ZORQPB6B.js} +11 -5
- package/dist/index.d.ts +1096 -941
- package/dist/index.js +369 -610
- package/dist/meta.d.ts +13 -12
- package/dist/meta.js +84 -16
- package/dist/rich/ExcelUploadCard.d.ts +7 -0
- package/dist/rich/ExcelUploadCard.js +17 -13
- package/dist/rich.d.ts +1 -1
- package/dist/rich.js +25 -12
- package/dist/styles.css +10 -2
- package/dist/{types-D14eqLpf.d.ts → types-C7y9CpgF.d.ts} +7 -5
- package/dist/widgets/ApprovalLineEditor.js +7 -6
- package/dist/widgets/ApprovalOverridesEditor.d.ts +11 -1
- package/dist/widgets/ApprovalOverridesEditor.js +9 -8
- package/dist/widgets/ApprovalStepper.js +3 -3
- package/dist/widgets/AutocompleteField.d.ts +1 -0
- package/dist/widgets/AutocompleteField.js +7 -6
- package/dist/widgets/ColumnBoard.js +3 -3
- package/dist/widgets/DiffView.js +3 -3
- package/dist/widgets/Disclosure.js +3 -3
- package/dist/widgets/EventCalendar.d.ts +16 -1
- package/dist/widgets/EventCalendar.js +3 -3
- package/dist/widgets/FileDropzone.js +7 -6
- package/dist/widgets/ImageLightbox.js +5 -4
- package/dist/widgets/InlineEdit.js +3 -3
- package/dist/widgets/JsonField.js +3 -3
- package/dist/widgets/LiveClock.js +2 -2
- package/dist/widgets/OptionSchemaEditor.js +4 -4
- package/dist/widgets/OptionValueFields.d.ts +5 -0
- package/dist/widgets/OptionValueFields.js +4 -4
- package/dist/widgets/OrgChart.js +4 -3
- package/dist/widgets/OrgChartBoard.d.ts +93 -4
- package/dist/widgets/OrgChartBoard.js +9 -6
- package/dist/widgets/PeriodFilter.js +4 -4
- package/dist/widgets/PermissionMatrixEditor.d.ts +16 -0
- package/dist/widgets/PermissionMatrixEditor.js +4 -4
- package/dist/widgets/PositionSelect.d.ts +6 -1
- package/dist/widgets/PositionSelect.js +5 -4
- package/dist/widgets/PrintSheet.js +3 -3
- package/dist/widgets/ProfileEditDiffCard.d.ts +6 -0
- package/dist/widgets/ProfileEditDiffCard.js +4 -4
- package/dist/widgets/QRDisplay.js +4 -4
- package/dist/widgets/RankBoard.d.ts +6 -0
- package/dist/widgets/RankBoard.js +6 -5
- package/dist/widgets/RecordSheet.js +2 -2
- package/dist/widgets/RoleMemberPicker.d.ts +11 -5
- package/dist/widgets/RoleMemberPicker.js +5 -4
- package/dist/widgets/SearchableMultiPicker.js +5 -5
- package/dist/widgets/SecureScreen.d.ts +12 -0
- package/dist/widgets/SecureScreen.js +3 -3
- package/dist/widgets/StackedMeter.js +2 -2
- package/dist/widgets/StagePeriodEditor.js +4 -5
- package/dist/widgets/Strip.js +2 -2
- package/dist/widgets/SummaryTable.js +2 -2
- package/dist/widgets/TagMultiSelect.d.ts +0 -1
- package/dist/widgets/TagMultiSelect.js +4 -4
- package/dist/widgets/Timeline.js +4 -4
- package/dist/widgets/TreePicker.js +6 -6
- package/dist/widgets/TreeView.js +5 -5
- package/dist/widgets/index.d.ts +3 -3
- package/dist/widgets/index.js +81 -79
- package/package.json +1 -1
- package/dist/chunk-EKVA7T7P.js +0 -198
package/dist/index.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
export { loadOptionalModule } from './chunk-
|
|
2
|
-
import { useSduiDispatch, SduiDialog, useSduiCan, SduiDispatchContext, SduiPermissionProvider } from './chunk-
|
|
3
|
-
export { SduiDialog, SduiPermissionProvider, useSduiCan, useSduiDispatch } from './chunk-
|
|
4
|
-
import { Text, Spacer, Show, Section, Row, PageHead, KeyValueList, Grid, Divider, Col, Actions } from './chunk-
|
|
5
|
-
export { Actions, Col, Divider, Grid, KeyValueList, PageHead, Row, Section, Show, Spacer, Text } from './chunk-
|
|
6
|
-
import { ImageViewer } from './chunk-
|
|
7
|
-
export { ImageViewer } from './chunk-
|
|
8
|
-
import {
|
|
9
|
-
export {
|
|
10
|
-
import {
|
|
11
|
-
export {
|
|
12
|
-
import { HoistedStyle, DateRangePicker, DatePicker } from './chunk-I5Z2RVDF.js';
|
|
13
|
-
export { DatePicker, DateRangePicker } from './chunk-I5Z2RVDF.js';
|
|
1
|
+
export { loadOptionalModule } from './chunk-YVTV4ZV6.js';
|
|
2
|
+
import { useSduiDispatch, SduiDialog, useSduiCan, SduiDispatchContext, SduiPermissionProvider } from './chunk-TFGNKFL2.js';
|
|
3
|
+
export { SduiDialog, SduiPermissionProvider, useSduiCan, useSduiDispatch } from './chunk-TFGNKFL2.js';
|
|
4
|
+
import { Text, Spacer, Show, Section, Row, PageHead, KeyValueList, Grid, Divider, Col, Actions } from './chunk-37SFFW37.js';
|
|
5
|
+
export { Actions, Col, Divider, Grid, KeyValueList, PageHead, Row, Section, Show, Spacer, Text } from './chunk-37SFFW37.js';
|
|
6
|
+
import { ImageViewer } from './chunk-SAPBDJJQ.js';
|
|
7
|
+
export { ImageViewer } from './chunk-SAPBDJJQ.js';
|
|
8
|
+
import { normalizeSearchFields, searchValuesFromParams, splitMultiValue, includesLooseSearch, cascadingValuePaths, SearchToolbar, searchFieldKeys, Popover } from './chunk-WOYMEEPH.js';
|
|
9
|
+
export { SearchToolbar, cascadingValuePaths, cleanSearchValues, includesLooseSearch, joinMultiValue, normalizeSearchText, searchFieldKeys, searchValuesFromParams, splitMultiValue } from './chunk-WOYMEEPH.js';
|
|
10
|
+
import { HoistedStyle, parseYmd, YMD_RE, TimePicker, DateRangePicker, DatePicker } from './chunk-WSLT32Q4.js';
|
|
11
|
+
export { DatePicker, DateRangePicker, TimePicker } from './chunk-WSLT32Q4.js';
|
|
14
12
|
export { OverlayContainerContext, OverlayLayer, getOverlayRoot, useOverlayLayer } from './chunk-XQPTSEJC.js';
|
|
15
|
-
import { DEV, isSafeNavigationTarget, useSduiAdapter, hostFetchOf, useSduiSandbox, resolveAdapter, assetResolverOf, SduiAdapterContext } from './chunk-
|
|
16
|
-
export { defaultAdapter, useSduiAssetResolver, useSduiSandbox } from './chunk-
|
|
17
|
-
import { EmptyState, Button, Checkbox, SegmentedControl, Spinner, Select, Pagination, Icon, Card, Badge, ProgressBar, Tag, Toast, Textarea, Tabs, Table, Switch, StatCard, Input, IconButton, Field, Alert, warnOnce } from './chunk-
|
|
18
|
-
export { Alert, Avatar, Badge, Breadcrumb, Button, Card, Checkbox, Dialog, EmptyState, Field, Icon, IconButton, Input, Pagination, ProgressBar, Radio, SegmentedControl, Select, SidebarNav, Spinner, StatCard, Switch, Table, Tabs, Tag, Textarea, Toast, Tooltip } from './chunk-
|
|
19
|
-
import { boxed, LAYOUT_CSS, childLayoutFrom, CHILD_LAYOUT_PROPS } from './chunk-
|
|
20
|
-
export { CHILD_LAYOUT_PROPS, LAYOUT_CSS, boxed, childLayoutFrom, columnsClass, containerLayoutStyle, joinClass, mergeStyle, typographyStyle } from './chunk-
|
|
21
|
-
import React9, { createContext, useState, useMemo, useRef, useEffect,
|
|
13
|
+
import { DEV, isSafeNavigationTarget, useSduiAdapter, hostFetchOf, useSduiSandbox, resolveAdapter, assetResolverOf, SduiAdapterContext } from './chunk-KO6OEY3G.js';
|
|
14
|
+
export { defaultAdapter, useSduiAssetResolver, useSduiSandbox } from './chunk-KO6OEY3G.js';
|
|
15
|
+
import { EmptyState, Button, Checkbox, SegmentedControl, Spinner, Select, Pagination, fieldOf, Icon, Card, Badge, ProgressBar, Tag, Toast, Textarea, Tabs, Table, Switch, StatCard, Input, IconButton, Field, Alert, warnOnce, isPlainObject, CALC_OPS, evaluateCalc, ICON_NODES, toNumber } from './chunk-RNOUXUBO.js';
|
|
16
|
+
export { Alert, Avatar, Badge, Breadcrumb, Button, CALC_OPS, Card, Checkbox, Dialog, EmptyState, Field, Icon, IconButton, Input, Pagination, ProgressBar, Radio, SegmentedControl, Select, SidebarNav, Spinner, StatCard, Switch, Table, Tabs, Tag, Textarea, Toast, Tooltip } from './chunk-RNOUXUBO.js';
|
|
17
|
+
import { boxed, LAYOUT_CSS, childLayoutFrom, CHILD_LAYOUT_PROPS } from './chunk-5WW4MBRO.js';
|
|
18
|
+
export { CHILD_LAYOUT_PROPS, LAYOUT_CSS, boxed, childLayoutFrom, columnsClass, containerLayoutStyle, joinClass, mergeStyle, typographyStyle } from './chunk-5WW4MBRO.js';
|
|
19
|
+
import React9, { createContext, useState, useMemo, useRef, useEffect, useContext, useCallback } from 'react';
|
|
22
20
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
23
21
|
|
|
24
22
|
// src/asset-url.ts
|
|
@@ -115,6 +113,22 @@ function resolveAssetsIn(propName, value, resolve, component) {
|
|
|
115
113
|
return out ?? record;
|
|
116
114
|
}
|
|
117
115
|
}
|
|
116
|
+
function SpecErrorCard({ children }) {
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
role: "alert",
|
|
121
|
+
className: "wp-caption",
|
|
122
|
+
style: {
|
|
123
|
+
color: "var(--wp-text-danger)",
|
|
124
|
+
border: "1px dashed var(--wp-border-danger)",
|
|
125
|
+
borderRadius: "var(--wp-radius-md)",
|
|
126
|
+
padding: "var(--wp-space-3)"
|
|
127
|
+
},
|
|
128
|
+
children
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
118
132
|
var NodeErrorBoundary = class extends React9.Component {
|
|
119
133
|
state = { error: null };
|
|
120
134
|
static getDerivedStateFromError(error) {
|
|
@@ -132,25 +146,12 @@ var NodeErrorBoundary = class extends React9.Component {
|
|
|
132
146
|
}
|
|
133
147
|
render() {
|
|
134
148
|
if (this.state.error) {
|
|
135
|
-
return /* @__PURE__ */ jsxs(
|
|
136
|
-
"
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
color: "var(--wp-text-danger)",
|
|
142
|
-
border: "1px dashed var(--wp-border-danger)",
|
|
143
|
-
borderRadius: "var(--wp-radius-md)",
|
|
144
|
-
padding: "var(--wp-space-3)"
|
|
145
|
-
},
|
|
146
|
-
children: [
|
|
147
|
-
"[",
|
|
148
|
-
this.props.name,
|
|
149
|
-
"] \uB80C\uB354 \uC624\uB958: ",
|
|
150
|
-
this.state.error.message
|
|
151
|
-
]
|
|
152
|
-
}
|
|
153
|
-
);
|
|
149
|
+
return /* @__PURE__ */ jsxs(SpecErrorCard, { children: [
|
|
150
|
+
"[",
|
|
151
|
+
this.props.name,
|
|
152
|
+
"] \uB80C\uB354 \uC624\uB958: ",
|
|
153
|
+
this.state.error.message
|
|
154
|
+
] });
|
|
154
155
|
}
|
|
155
156
|
return this.props.children;
|
|
156
157
|
}
|
|
@@ -165,17 +166,20 @@ function Can({ entity, action = "read", scope, fallback = null, children }) {
|
|
|
165
166
|
}
|
|
166
167
|
function imageRenderer(cell, key, width = cell.width ?? 72) {
|
|
167
168
|
const height = cell.height ?? 52;
|
|
168
|
-
return (row) =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
169
|
+
return (row) => {
|
|
170
|
+
const url = fieldOf(row, key);
|
|
171
|
+
return /* @__PURE__ */ jsx(
|
|
172
|
+
ImageCellView,
|
|
173
|
+
{
|
|
174
|
+
url: typeof url === "string" ? url : "",
|
|
175
|
+
alt: cell.altKey ? String(fieldOf(row, cell.altKey) ?? "") : "",
|
|
176
|
+
width,
|
|
177
|
+
height,
|
|
178
|
+
fit: cell.fit ?? "cover",
|
|
179
|
+
fallbackIcon: cell.fallbackIcon ?? "image"
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
};
|
|
179
183
|
}
|
|
180
184
|
function ImageCellView({
|
|
181
185
|
url,
|
|
@@ -229,7 +233,7 @@ function ImageCellView({
|
|
|
229
233
|
/* @__PURE__ */ jsx(ImageViewer, { src: url, alt, open, onClose: () => setOpen(false) })
|
|
230
234
|
] });
|
|
231
235
|
}
|
|
232
|
-
function
|
|
236
|
+
function toNumber2(value) {
|
|
233
237
|
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
234
238
|
if (typeof value === "string") {
|
|
235
239
|
const plain = value.replace(/,/g, "").trim();
|
|
@@ -240,7 +244,7 @@ function toNumber(value) {
|
|
|
240
244
|
function fillTemplate(template, row) {
|
|
241
245
|
return template.replace(
|
|
242
246
|
/\{([\w.-]+)\}/g,
|
|
243
|
-
(_match, key) => encodeURIComponent(String(row
|
|
247
|
+
(_match, key) => encodeURIComponent(String(fieldOf(row, key) ?? ""))
|
|
244
248
|
);
|
|
245
249
|
}
|
|
246
250
|
var pad2 = (n) => String(n).padStart(2, "0");
|
|
@@ -250,7 +254,9 @@ function formatDateCellValue(date) {
|
|
|
250
254
|
return `${ymdOf(date)} ${hmOf(date)}`;
|
|
251
255
|
}
|
|
252
256
|
function formatDate(raw, format) {
|
|
253
|
-
const
|
|
257
|
+
const dateOnly = parseYmd(raw);
|
|
258
|
+
if (!dateOnly && YMD_RE.test(raw)) return raw;
|
|
259
|
+
const date = dateOnly ? new Date(dateOnly.getFullYear(), dateOnly.getMonth(), dateOnly.getDate()) : new Date(raw);
|
|
254
260
|
if (Number.isNaN(date.getTime())) return raw;
|
|
255
261
|
if (format === "datetime") return formatDateCellValue(date);
|
|
256
262
|
if (format === "time") return hmOf(date);
|
|
@@ -261,19 +267,19 @@ function cellRenderer(cell, key, dispatch) {
|
|
|
261
267
|
switch (cell.type) {
|
|
262
268
|
case "badge":
|
|
263
269
|
return (row) => {
|
|
264
|
-
const value = String(row
|
|
270
|
+
const value = String(fieldOf(row, key) ?? "");
|
|
265
271
|
if (!value) return null;
|
|
266
272
|
return /* @__PURE__ */ jsx(Badge, { tone: cell.toneMap?.[value] ?? "neutral", children: cell.labelMap?.[value] ?? value });
|
|
267
273
|
};
|
|
268
274
|
case "tag":
|
|
269
275
|
return (row) => {
|
|
270
|
-
const value = String(row
|
|
276
|
+
const value = String(fieldOf(row, key) ?? "");
|
|
271
277
|
if (!value) return null;
|
|
272
278
|
return /* @__PURE__ */ jsx(Tag, { color: cell.colorMap?.[value], filled: cell.filled, children: value });
|
|
273
279
|
};
|
|
274
280
|
case "link":
|
|
275
281
|
return (row) => {
|
|
276
|
-
const label = String(row
|
|
282
|
+
const label = String(fieldOf(row, cell.labelKey ?? key) ?? "");
|
|
277
283
|
if (!label) return null;
|
|
278
284
|
const to = fillTemplate(cell.to ?? "", row);
|
|
279
285
|
return (
|
|
@@ -299,14 +305,15 @@ function cellRenderer(cell, key, dispatch) {
|
|
|
299
305
|
};
|
|
300
306
|
case "progress":
|
|
301
307
|
return (row) => {
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
const raw = fieldOf(row, key);
|
|
309
|
+
const value = toNumber2(raw);
|
|
310
|
+
if (value === null) return String(raw ?? "");
|
|
311
|
+
const max = (cell.maxKey ? toNumber2(fieldOf(row, cell.maxKey)) : null) ?? cell.max ?? 100;
|
|
305
312
|
return /* @__PURE__ */ jsx(ProgressBar, { value, max: max || 100, tone: cell.tone, showValue: cell.showValue, size: "sm" });
|
|
306
313
|
};
|
|
307
314
|
case "icon":
|
|
308
315
|
return (row) => {
|
|
309
|
-
const value = String(row
|
|
316
|
+
const value = String(fieldOf(row, key) ?? "");
|
|
310
317
|
const name = cell.iconMap?.[value] ?? cell.icon;
|
|
311
318
|
if (!name) return value;
|
|
312
319
|
return (
|
|
@@ -319,8 +326,9 @@ function cellRenderer(cell, key, dispatch) {
|
|
|
319
326
|
};
|
|
320
327
|
case "number":
|
|
321
328
|
return (row) => {
|
|
322
|
-
const
|
|
323
|
-
|
|
329
|
+
const raw = fieldOf(row, key);
|
|
330
|
+
const value = toNumber2(raw);
|
|
331
|
+
if (value === null) return String(raw ?? "");
|
|
324
332
|
const digits = Number.isFinite(cell.digits) ? Math.max(0, Math.min(6, Number(cell.digits))) : 0;
|
|
325
333
|
const text = new Intl.NumberFormat("ko-KR", {
|
|
326
334
|
minimumFractionDigits: digits,
|
|
@@ -330,7 +338,7 @@ function cellRenderer(cell, key, dispatch) {
|
|
|
330
338
|
};
|
|
331
339
|
case "date":
|
|
332
340
|
return (row) => {
|
|
333
|
-
const raw = String(row
|
|
341
|
+
const raw = String(fieldOf(row, key) ?? "");
|
|
334
342
|
return raw ? formatDate(raw, cell.format) : "";
|
|
335
343
|
};
|
|
336
344
|
case "image":
|
|
@@ -352,7 +360,10 @@ function toPage(body) {
|
|
|
352
360
|
var defaultFetcher = async (endpoint, params, signal) => {
|
|
353
361
|
const response = await fetch(searchUrl(endpoint, params), {
|
|
354
362
|
headers: { accept: "application/json" },
|
|
355
|
-
|
|
363
|
+
// same-origin — endpoint는 런타임 JSON(스펙)에서 오므로, 어댑터 없이 쓰는 앱에서
|
|
364
|
+
// 스펙이 외부 origin을 가리켜도 세션 쿠키가 제3자에게 나가면 안 된다.
|
|
365
|
+
// 쿠키를 실어야 하는 교차 출처 조회는 어댑터(fetch) 주입으로 자기 정책을 편다.
|
|
366
|
+
credentials: "same-origin",
|
|
356
367
|
signal
|
|
357
368
|
});
|
|
358
369
|
const body = await response.json().catch(() => null);
|
|
@@ -374,6 +385,31 @@ function SduiSearchDataProvider({
|
|
|
374
385
|
}) {
|
|
375
386
|
return /* @__PURE__ */ jsx(SearchDataContext.Provider, { value: fetcher, children });
|
|
376
387
|
}
|
|
388
|
+
function createSearchInvalidator() {
|
|
389
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
390
|
+
return {
|
|
391
|
+
invalidate(prefix = "", exact = false) {
|
|
392
|
+
for (const entry of [...listeners]) {
|
|
393
|
+
const hit = prefix === "" ? true : exact ? entry.endpoint === prefix : entry.endpoint.startsWith(prefix);
|
|
394
|
+
if (hit) entry.reload();
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
subscribe(endpoint, reload) {
|
|
398
|
+
const entry = { endpoint, reload };
|
|
399
|
+
listeners.add(entry);
|
|
400
|
+
return () => {
|
|
401
|
+
listeners.delete(entry);
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
var SearchInvalidationContext = createContext(null);
|
|
407
|
+
function SduiSearchInvalidationProvider({
|
|
408
|
+
invalidator,
|
|
409
|
+
children
|
|
410
|
+
}) {
|
|
411
|
+
return /* @__PURE__ */ jsx(SearchInvalidationContext.Provider, { value: invalidator, children });
|
|
412
|
+
}
|
|
377
413
|
function useSearchFetcher() {
|
|
378
414
|
const injected = useContext(SearchDataContext);
|
|
379
415
|
const adapter = useSduiAdapter();
|
|
@@ -386,10 +422,15 @@ function useSearchFetcher() {
|
|
|
386
422
|
var EMPTY_PAGE = { data: [], total: 0 };
|
|
387
423
|
function useRemoteSearchPage(endpoint, params) {
|
|
388
424
|
const fetcher = useSearchFetcher();
|
|
425
|
+
const invalidator = useContext(SearchInvalidationContext);
|
|
389
426
|
const [page, setPage] = useState(EMPTY_PAGE);
|
|
390
427
|
const [isLoading, setIsLoading] = useState(true);
|
|
391
428
|
const [error, setError] = useState(null);
|
|
392
429
|
const [reloadKey, setReloadKey] = useState(0);
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
if (!invalidator) return void 0;
|
|
432
|
+
return invalidator.subscribe(endpoint, () => setReloadKey((value) => value + 1));
|
|
433
|
+
}, [invalidator, endpoint]);
|
|
393
434
|
const query = params.toString();
|
|
394
435
|
const fetcherRef = useRef(fetcher);
|
|
395
436
|
fetcherRef.current = fetcher;
|
|
@@ -494,7 +535,10 @@ function ResultTable({
|
|
|
494
535
|
if (column.align === "right") cls.push("wp-table__num");
|
|
495
536
|
if (column.mono) cls.push("wp-table__mono");
|
|
496
537
|
if (column.strong) cls.push("wp-table__strong");
|
|
497
|
-
return /* @__PURE__ */ jsx("td", { className: cls.join(" ") || void 0, children: column.render ? column.render(row, index) :
|
|
538
|
+
return /* @__PURE__ */ jsx("td", { className: cls.join(" ") || void 0, children: column.render ? column.render(row, index) : (
|
|
539
|
+
// 평면 키 우선, 없으면 점 경로 — 셀 렌더러(cells.tsx)와 같은 조회 규칙이다
|
|
540
|
+
fieldOf(row, column.key)
|
|
541
|
+
) }, column.key);
|
|
498
542
|
})
|
|
499
543
|
},
|
|
500
544
|
row.id ?? index
|
|
@@ -612,7 +656,7 @@ function SearchResults({
|
|
|
612
656
|
}, [leftFilters, rightFilters, searchParams]);
|
|
613
657
|
const hasAllRows = item.data.length >= item.total;
|
|
614
658
|
const processed = useMemo(() => {
|
|
615
|
-
const cell = (row, key) => row
|
|
659
|
+
const cell = (row, key) => fieldOf(row, key);
|
|
616
660
|
let rows = item.data;
|
|
617
661
|
if (hasAllRows) {
|
|
618
662
|
if (appliedSearch) {
|
|
@@ -894,13 +938,8 @@ function SearchResults({
|
|
|
894
938
|
] });
|
|
895
939
|
}
|
|
896
940
|
function numericValue(value) {
|
|
897
|
-
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
898
941
|
if (value instanceof Date) return value.getTime();
|
|
899
|
-
|
|
900
|
-
const plain = value.replace(/,/g, "").trim();
|
|
901
|
-
if (plain !== "" && /^-?\d+(\.\d+)?$/.test(plain)) return Number(plain);
|
|
902
|
-
}
|
|
903
|
-
return null;
|
|
942
|
+
return toNumber2(value);
|
|
904
943
|
}
|
|
905
944
|
function formatCell(value) {
|
|
906
945
|
if (value == null) return "";
|
|
@@ -1005,7 +1044,7 @@ function mapColumns(columns, dispatch) {
|
|
|
1005
1044
|
}
|
|
1006
1045
|
function matchesCondition(condition, row) {
|
|
1007
1046
|
if (!condition || typeof condition.key !== "string") return true;
|
|
1008
|
-
const value = row
|
|
1047
|
+
const value = fieldOf(row, condition.key);
|
|
1009
1048
|
if (condition.equals !== void 0) return String(value ?? "") === String(condition.equals);
|
|
1010
1049
|
if (Array.isArray(condition.in)) return condition.in.some((v) => String(v) === String(value ?? ""));
|
|
1011
1050
|
if (condition.notEmpty === false) return true;
|
|
@@ -1017,7 +1056,7 @@ function mapCardRows(rows, dispatch) {
|
|
|
1017
1056
|
return {
|
|
1018
1057
|
icon: row.icon ? /* @__PURE__ */ jsx(Icon, { name: row.icon, size: 14 }) : void 0,
|
|
1019
1058
|
label: row.label,
|
|
1020
|
-
value: render ?? ((item) => String(item
|
|
1059
|
+
value: render ?? ((item) => String(fieldOf(item, row.valueKey) ?? "")),
|
|
1021
1060
|
show: row.showWhen ? (item) => matchesCondition(row.showWhen, item) : void 0
|
|
1022
1061
|
};
|
|
1023
1062
|
});
|
|
@@ -1034,10 +1073,10 @@ function mapGridCard(gridCard, dispatch) {
|
|
|
1034
1073
|
gridCard.imageKey,
|
|
1035
1074
|
"100%"
|
|
1036
1075
|
) : void 0,
|
|
1037
|
-
title: (row) => String(row
|
|
1038
|
-
subtitle: gridCard.subtitleKey ? (row) => String(row
|
|
1076
|
+
title: (row) => String(fieldOf(row, gridCard.titleKey) ?? ""),
|
|
1077
|
+
subtitle: gridCard.subtitleKey ? (row) => String(fieldOf(row, gridCard.subtitleKey) ?? "") : void 0,
|
|
1039
1078
|
badge: gridCard.badge ? (row) => {
|
|
1040
|
-
const value = String(row
|
|
1079
|
+
const value = String(fieldOf(row, gridCard.badge.key) ?? "");
|
|
1041
1080
|
if (!value) return null;
|
|
1042
1081
|
return {
|
|
1043
1082
|
text: gridCard.badge.labelMap?.[value] ?? value,
|
|
@@ -1047,7 +1086,7 @@ function mapGridCard(gridCard, dispatch) {
|
|
|
1047
1086
|
rows: mapCardRows(gridCard.rows, dispatch),
|
|
1048
1087
|
leftRows: mapCardRows(gridCard.leftRows, dispatch),
|
|
1049
1088
|
rightRows: mapCardRows(gridCard.rightRows, dispatch),
|
|
1050
|
-
note: gridCard.noteKey ? (row) => String(row
|
|
1089
|
+
note: gridCard.noteKey ? (row) => String(fieldOf(row, gridCard.noteKey) ?? "") || null : void 0,
|
|
1051
1090
|
actions: gridCard.actions?.length ? (row) => (
|
|
1052
1091
|
// 카드 전체가 클릭 대상(rowClick)일 수 있으므로 버튼 클릭은 여기서 전파를 끊는다
|
|
1053
1092
|
/* @__PURE__ */ jsx("span", { className: "wp-row", onClick: (e) => e.stopPropagation(), children: gridCard.actions.map((action) => /* @__PURE__ */ jsx(
|
|
@@ -1148,15 +1187,22 @@ function RemoteSearchResultsImpl({
|
|
|
1148
1187
|
rowClick,
|
|
1149
1188
|
headerActions,
|
|
1150
1189
|
virtual,
|
|
1151
|
-
pageSize
|
|
1190
|
+
pageSize,
|
|
1191
|
+
searchParams: controlledParams,
|
|
1192
|
+
onParamsChange
|
|
1152
1193
|
}) {
|
|
1153
1194
|
const dispatch = useSduiDispatch();
|
|
1154
|
-
const
|
|
1195
|
+
const defaultPageSize = normalizePageSizes(pageSizes)?.[0] ?? 10;
|
|
1196
|
+
const [localParams, setLocalParams] = useState(() => {
|
|
1155
1197
|
const initial = new URLSearchParams();
|
|
1156
|
-
initial.set("limit", String(pageSize));
|
|
1198
|
+
initial.set("limit", String(pageSize ?? defaultPageSize));
|
|
1157
1199
|
return initial;
|
|
1158
1200
|
});
|
|
1159
|
-
const
|
|
1201
|
+
const params = controlledParams ?? localParams;
|
|
1202
|
+
const handleParamsChange = (next) => {
|
|
1203
|
+
if (controlledParams === void 0) setLocalParams(next);
|
|
1204
|
+
onParamsChange?.(next);
|
|
1205
|
+
};
|
|
1160
1206
|
const query = useMemo(() => {
|
|
1161
1207
|
const next = new URLSearchParams(params);
|
|
1162
1208
|
if (!next.has("page")) next.set("page", "1");
|
|
@@ -1197,7 +1243,7 @@ function RemoteSearchResultsImpl({
|
|
|
1197
1243
|
isLoading,
|
|
1198
1244
|
onRowClick: rowClick ? (row) => toList(rowClick).forEach((a) => dispatch(a, row)) : void 0,
|
|
1199
1245
|
searchParams: params,
|
|
1200
|
-
onParamsChange:
|
|
1246
|
+
onParamsChange: handleParamsChange
|
|
1201
1247
|
}
|
|
1202
1248
|
);
|
|
1203
1249
|
}
|
|
@@ -1209,6 +1255,16 @@ function useSduiRuntime() {
|
|
|
1209
1255
|
if (!runtime) throw new Error("useSduiRuntime\uC740 SduiRenderer \uB0B4\uBD80\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
1210
1256
|
return runtime;
|
|
1211
1257
|
}
|
|
1258
|
+
function prefillParams(search, hash) {
|
|
1259
|
+
const params = new URLSearchParams(search);
|
|
1260
|
+
const q = hash.indexOf("?");
|
|
1261
|
+
if (q !== -1) {
|
|
1262
|
+
new URLSearchParams(hash.slice(q + 1)).forEach((value, key) => {
|
|
1263
|
+
if (!params.has(key)) params.append(key, value);
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
return params;
|
|
1267
|
+
}
|
|
1212
1268
|
function SduiForm({
|
|
1213
1269
|
id,
|
|
1214
1270
|
initial,
|
|
@@ -1240,7 +1296,7 @@ function SduiForm({
|
|
|
1240
1296
|
if (runtime.state[`${id}.${field}`] === void 0) entries[`${id}.${field}`] = value;
|
|
1241
1297
|
}
|
|
1242
1298
|
if (initialFromUrl && typeof window !== "undefined") {
|
|
1243
|
-
const params =
|
|
1299
|
+
const params = prefillParams(window.location.search, window.location.hash);
|
|
1244
1300
|
for (const [field, param] of Object.entries(initialFromUrl)) {
|
|
1245
1301
|
const raw = params.get(param);
|
|
1246
1302
|
if (raw !== null && runtime.state[`${id}.${field}`] === void 0) {
|
|
@@ -1551,8 +1607,6 @@ var SORTABLE_CSS = `
|
|
|
1551
1607
|
.wp-sortable__status{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
|
|
1552
1608
|
overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
|
|
1553
1609
|
`;
|
|
1554
|
-
|
|
1555
|
-
// src/registry.tsx
|
|
1556
1610
|
var defaultRegistry = {
|
|
1557
1611
|
// 기본 컴포넌트
|
|
1558
1612
|
Alert,
|
|
@@ -1619,399 +1673,39 @@ var defaultRegistry = {
|
|
|
1619
1673
|
function createRegistry(extensions) {
|
|
1620
1674
|
return { ...defaultRegistry, ...extensions };
|
|
1621
1675
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
}
|
|
1627
|
-
function toNumber2(v) {
|
|
1628
|
-
if (typeof v === "number") return v;
|
|
1629
|
-
if (typeof v === "string") {
|
|
1630
|
-
const n = Number(v.replace(/,/g, ""));
|
|
1631
|
-
return Number.isFinite(n) ? n : 0;
|
|
1632
|
-
}
|
|
1633
|
-
return 0;
|
|
1634
|
-
}
|
|
1635
|
-
function isNumericText(v) {
|
|
1636
|
-
if (typeof v === "number") return true;
|
|
1637
|
-
if (typeof v !== "string" || v === "") return false;
|
|
1638
|
-
return Number.isFinite(Number(v.replace(/,/g, "")));
|
|
1639
|
-
}
|
|
1640
|
-
function orderingCompare(a, b) {
|
|
1641
|
-
if (typeof a === "string" && typeof b === "string" && !isNumericText(a) && !isNumericText(b)) {
|
|
1642
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
1643
|
-
}
|
|
1644
|
-
const x = toNumber2(a);
|
|
1645
|
-
const y = toNumber2(b);
|
|
1646
|
-
return x < y ? -1 : x > y ? 1 : 0;
|
|
1647
|
-
}
|
|
1648
|
-
function looseEquals(a, b) {
|
|
1649
|
-
return String(a ?? "") === String(b ?? "");
|
|
1650
|
-
}
|
|
1651
|
-
function isBlank(v) {
|
|
1652
|
-
return v === null || v === void 0 || v === "";
|
|
1653
|
-
}
|
|
1654
|
-
function isEmptyValue(v) {
|
|
1655
|
-
if (isBlank(v)) return true;
|
|
1656
|
-
if (Array.isArray(v)) return v.length === 0;
|
|
1657
|
-
if (isPlainObject(v)) return Object.keys(v).length === 0;
|
|
1658
|
-
return false;
|
|
1659
|
-
}
|
|
1660
|
-
function fieldOf(row, field) {
|
|
1661
|
-
if (!field) return row;
|
|
1662
|
-
if (!isPlainObject(row)) return void 0;
|
|
1663
|
-
if (Object.hasOwn(row, field)) return row[field];
|
|
1664
|
-
if (!field.includes(".")) return void 0;
|
|
1665
|
-
let current = row;
|
|
1666
|
-
for (const segment of field.split(".")) {
|
|
1667
|
-
if (!isPlainObject(current) || !Object.hasOwn(current, segment)) return void 0;
|
|
1668
|
-
current = current[segment];
|
|
1669
|
-
}
|
|
1670
|
-
return current;
|
|
1671
|
-
}
|
|
1672
|
-
var FIELD_OPS = /* @__PURE__ */ new Set([
|
|
1673
|
-
"eq",
|
|
1674
|
-
"ne",
|
|
1675
|
-
"gt",
|
|
1676
|
-
"gte",
|
|
1677
|
-
"lt",
|
|
1678
|
-
"lte",
|
|
1679
|
-
"in",
|
|
1680
|
-
"notIn",
|
|
1681
|
-
"contains",
|
|
1682
|
-
"startsWith",
|
|
1683
|
-
"endsWith",
|
|
1684
|
-
"isNull",
|
|
1685
|
-
"isEmpty",
|
|
1686
|
-
"intersects"
|
|
1687
|
-
]);
|
|
1688
|
-
function isFieldOpObject(v) {
|
|
1689
|
-
return isPlainObject(v) && Object.keys(v).some((key) => FIELD_OPS.has(key));
|
|
1690
|
-
}
|
|
1691
|
-
function matchesFieldOps(actual, ops, resolve) {
|
|
1692
|
-
return Object.entries(ops).every(([op, rawExpected]) => {
|
|
1693
|
-
if (!FIELD_OPS.has(op)) return true;
|
|
1694
|
-
const expected = resolve(rawExpected);
|
|
1695
|
-
switch (op) {
|
|
1696
|
-
case "eq":
|
|
1697
|
-
return looseEquals(actual, expected);
|
|
1698
|
-
case "ne":
|
|
1699
|
-
return !looseEquals(actual, expected);
|
|
1700
|
-
case "gt":
|
|
1701
|
-
return orderingCompare(actual, expected) > 0;
|
|
1702
|
-
case "gte":
|
|
1703
|
-
return orderingCompare(actual, expected) >= 0;
|
|
1704
|
-
case "lt":
|
|
1705
|
-
return orderingCompare(actual, expected) < 0;
|
|
1706
|
-
case "lte":
|
|
1707
|
-
return orderingCompare(actual, expected) <= 0;
|
|
1708
|
-
case "in":
|
|
1709
|
-
return Array.isArray(expected) && expected.some((item) => looseEquals(actual, item));
|
|
1710
|
-
case "notIn":
|
|
1711
|
-
return !Array.isArray(expected) || !expected.some((item) => looseEquals(actual, item));
|
|
1712
|
-
case "contains":
|
|
1713
|
-
return String(actual ?? "").includes(String(expected ?? ""));
|
|
1714
|
-
case "startsWith":
|
|
1715
|
-
return String(actual ?? "").startsWith(String(expected ?? ""));
|
|
1716
|
-
case "endsWith":
|
|
1717
|
-
return String(actual ?? "").endsWith(String(expected ?? ""));
|
|
1718
|
-
case "isNull":
|
|
1719
|
-
return isBlank(actual) === Boolean(expected);
|
|
1720
|
-
case "intersects": {
|
|
1721
|
-
const mine = Array.isArray(actual) ? actual : [];
|
|
1722
|
-
const theirs = Array.isArray(expected) ? expected : [];
|
|
1723
|
-
if (mine.length === 0 || theirs.length === 0) return false;
|
|
1724
|
-
const set = new Set(theirs.map((item) => String(item ?? "")));
|
|
1725
|
-
return mine.some((item) => set.has(String(item ?? "")));
|
|
1726
|
-
}
|
|
1727
|
-
case "isEmpty":
|
|
1728
|
-
return isEmptyValue(actual) === Boolean(expected);
|
|
1729
|
-
default:
|
|
1730
|
-
return true;
|
|
1731
|
-
}
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
function matchesWhere(row, where, resolve) {
|
|
1735
|
-
if (!isPlainObject(where)) return true;
|
|
1736
|
-
return Object.entries(where).every(([key, condition]) => {
|
|
1737
|
-
if (key === "AND") {
|
|
1738
|
-
return Array.isArray(condition) && condition.every((sub) => matchesWhere(row, sub, resolve));
|
|
1739
|
-
}
|
|
1740
|
-
if (key === "OR") {
|
|
1741
|
-
return Array.isArray(condition) && condition.some((sub) => matchesWhere(row, sub, resolve));
|
|
1742
|
-
}
|
|
1743
|
-
if (key === "NOT") {
|
|
1744
|
-
return !matchesWhere(row, condition, resolve);
|
|
1745
|
-
}
|
|
1746
|
-
const actual = fieldOf(row, key);
|
|
1747
|
-
if (isFieldOpObject(condition)) return matchesFieldOps(actual, condition, resolve);
|
|
1748
|
-
return looseEquals(actual, resolve(condition));
|
|
1749
|
-
});
|
|
1750
|
-
}
|
|
1751
|
-
var CALC_OPS = /* @__PURE__ */ new Set([
|
|
1752
|
-
// 산술 (기존)
|
|
1753
|
-
"sum",
|
|
1754
|
-
"sub",
|
|
1755
|
-
"mul",
|
|
1756
|
-
"div",
|
|
1757
|
-
"percent",
|
|
1758
|
-
"count",
|
|
1759
|
-
// 문자 (기존)
|
|
1760
|
-
"concat",
|
|
1761
|
-
"encode",
|
|
1762
|
-
// 비교·논리 (기존)
|
|
1763
|
-
"eq",
|
|
1764
|
-
"gt",
|
|
1765
|
-
"gte",
|
|
1766
|
-
"lt",
|
|
1767
|
-
"lte",
|
|
1768
|
-
"not",
|
|
1769
|
-
// 논리 (신규) — 예전에는 스펙이 eq(concat(eq(a),eq(b)),"falsefalse")로 우회했다
|
|
1770
|
-
"and",
|
|
1771
|
-
"or",
|
|
1772
|
-
// 값 고르기 (신규)
|
|
1773
|
-
"if",
|
|
1774
|
-
"match",
|
|
1775
|
-
"coalesce",
|
|
1776
|
-
// 목록·객체 (신규)
|
|
1777
|
-
"get",
|
|
1778
|
-
"pluck",
|
|
1779
|
-
"filter",
|
|
1780
|
-
"some",
|
|
1781
|
-
"every",
|
|
1782
|
-
"includes",
|
|
1783
|
-
"distinct",
|
|
1784
|
-
"except",
|
|
1785
|
-
"groupBy",
|
|
1786
|
-
"groupCount",
|
|
1787
|
-
"entries",
|
|
1788
|
-
"fromEntries",
|
|
1789
|
-
"length",
|
|
1790
|
-
"join",
|
|
1791
|
-
"split",
|
|
1792
|
-
"sortBy",
|
|
1793
|
-
"map"
|
|
1794
|
-
]);
|
|
1795
|
-
function isFieldRef(value) {
|
|
1796
|
-
return isPlainObject(value) && "$field" in value;
|
|
1797
|
-
}
|
|
1798
|
-
function substituteFields(template, row) {
|
|
1799
|
-
if (isFieldRef(template)) {
|
|
1800
|
-
return template.$field === true ? row : fieldOf(row, String(template.$field)) ?? null;
|
|
1801
|
-
}
|
|
1802
|
-
if (Array.isArray(template)) return template.map((item) => substituteFields(item, row));
|
|
1803
|
-
if (isPlainObject(template)) {
|
|
1804
|
-
const nested = isPlainObject(template.$calc) ? template.$calc : null;
|
|
1805
|
-
if (nested && nested.op === "map") {
|
|
1806
|
-
const inner = {};
|
|
1807
|
-
for (const [key, value] of Object.entries(nested)) {
|
|
1808
|
-
inner[key] = key === "select" ? value : substituteFields(value, row);
|
|
1809
|
-
}
|
|
1810
|
-
return { ...template, $calc: inner };
|
|
1811
|
-
}
|
|
1812
|
-
const out = {};
|
|
1813
|
-
for (const [key, value] of Object.entries(template)) out[key] = substituteFields(value, row);
|
|
1814
|
-
return out;
|
|
1815
|
-
}
|
|
1816
|
-
return template;
|
|
1817
|
-
}
|
|
1818
|
-
function evaluateCalc(calc, resolved, resolve) {
|
|
1819
|
-
const op = typeof calc.op === "string" ? calc.op : "";
|
|
1820
|
-
const field = typeof calc.field === "string" ? calc.field : void 0;
|
|
1821
|
-
const first = resolved[0];
|
|
1822
|
-
const list = Array.isArray(first) ? first : void 0;
|
|
1823
|
-
const listOrEmpty = () => {
|
|
1824
|
-
if (list) return list;
|
|
1825
|
-
if (first !== void 0 && first !== null) {
|
|
1826
|
-
warnOnce(
|
|
1827
|
-
`calc:${op}:list`,
|
|
1828
|
-
`[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`
|
|
1829
|
-
);
|
|
1830
|
-
}
|
|
1831
|
-
return [];
|
|
1832
|
-
};
|
|
1833
|
-
const nums = (v) => {
|
|
1834
|
-
if (Array.isArray(v))
|
|
1835
|
-
return v.map((row) => field && isPlainObject(row) ? toNumber2(row[field]) : toNumber2(row));
|
|
1836
|
-
return [toNumber2(v)];
|
|
1837
|
-
};
|
|
1838
|
-
switch (op) {
|
|
1839
|
-
/* ── 산술 ── */
|
|
1840
|
-
case "sum":
|
|
1841
|
-
return resolved.flatMap(nums).reduce((a, b) => a + b, 0);
|
|
1842
|
-
case "count":
|
|
1843
|
-
return resolved.reduce(
|
|
1844
|
-
(a, v) => a + (Array.isArray(v) ? v.length : v == null ? 0 : 1),
|
|
1845
|
-
0
|
|
1846
|
-
);
|
|
1847
|
-
case "sub": {
|
|
1848
|
-
const items = resolved.map((v) => toNumber2(v));
|
|
1849
|
-
return items.length === 0 ? 0 : items.reduce((a, b) => a - b);
|
|
1850
|
-
}
|
|
1851
|
-
case "mul":
|
|
1852
|
-
return resolved.map((v) => toNumber2(v)).reduce((a, b) => a * b, 1);
|
|
1853
|
-
case "div": {
|
|
1854
|
-
const [a, b] = resolved.map((v) => toNumber2(v));
|
|
1855
|
-
return b === 0 ? 0 : a / b;
|
|
1856
|
-
}
|
|
1857
|
-
case "percent": {
|
|
1858
|
-
const [a, b] = resolved.map((v) => toNumber2(v));
|
|
1859
|
-
return b === 0 ? 0 : Math.round(a / b * 1e3) / 10;
|
|
1860
|
-
}
|
|
1861
|
-
/* ── 문자 ── */
|
|
1862
|
-
case "concat":
|
|
1863
|
-
return resolved.map((v) => v == null ? "" : String(v)).join("");
|
|
1864
|
-
case "encode":
|
|
1865
|
-
return encodeURIComponent(resolved.map((v) => v == null ? "" : String(v)).join(""));
|
|
1866
|
-
/* ── 비교·논리 ── */
|
|
1867
|
-
case "eq":
|
|
1868
|
-
return looseEquals(resolved[0], resolved[1]);
|
|
1869
|
-
case "gt":
|
|
1870
|
-
return orderingCompare(resolved[0], resolved[1]) > 0;
|
|
1871
|
-
case "gte":
|
|
1872
|
-
return orderingCompare(resolved[0], resolved[1]) >= 0;
|
|
1873
|
-
case "lt":
|
|
1874
|
-
return orderingCompare(resolved[0], resolved[1]) < 0;
|
|
1875
|
-
case "lte":
|
|
1876
|
-
return orderingCompare(resolved[0], resolved[1]) <= 0;
|
|
1877
|
-
case "not":
|
|
1878
|
-
return !resolved[0];
|
|
1879
|
-
case "and":
|
|
1880
|
-
return resolved.every(Boolean);
|
|
1881
|
-
case "or":
|
|
1882
|
-
return resolved.some(Boolean);
|
|
1883
|
-
/* ── 값 고르기 ── */
|
|
1884
|
-
case "if":
|
|
1885
|
-
return resolved[0] ? resolve(calc.then ?? null) : resolve(calc.else ?? null);
|
|
1886
|
-
case "match": {
|
|
1887
|
-
const key = String(resolved[0] ?? "");
|
|
1888
|
-
const cases = isPlainObject(calc.cases) ? calc.cases : {};
|
|
1889
|
-
if (Object.hasOwn(cases, key)) return resolve(cases[key]);
|
|
1890
|
-
return resolve(calc.default ?? null);
|
|
1891
|
-
}
|
|
1892
|
-
case "coalesce":
|
|
1893
|
-
return resolved.find((v) => !isBlank(v)) ?? null;
|
|
1894
|
-
/* ── 목록·객체 ── */
|
|
1895
|
-
case "get": {
|
|
1896
|
-
const [container, rawKey] = resolved;
|
|
1897
|
-
if (Array.isArray(container)) {
|
|
1898
|
-
if (typeof rawKey !== "number" && (typeof rawKey !== "string" || rawKey.trim() === "")) {
|
|
1899
|
-
return null;
|
|
1900
|
-
}
|
|
1901
|
-
const index = Number(rawKey);
|
|
1902
|
-
if (!Number.isInteger(index) || index < 0 || index >= container.length) return null;
|
|
1903
|
-
return container[index] ?? null;
|
|
1904
|
-
}
|
|
1905
|
-
if (isPlainObject(container)) {
|
|
1906
|
-
const key = rawKey == null ? "" : String(rawKey);
|
|
1907
|
-
return Object.hasOwn(container, key) ? container[key] ?? null : null;
|
|
1908
|
-
}
|
|
1909
|
-
return null;
|
|
1910
|
-
}
|
|
1911
|
-
case "pluck":
|
|
1912
|
-
return listOrEmpty().map((row) => fieldOf(row, field)).filter((v) => v !== null && v !== void 0);
|
|
1913
|
-
case "filter":
|
|
1914
|
-
return listOrEmpty().filter((row) => matchesWhere(row, calc.where, resolve));
|
|
1915
|
-
case "some":
|
|
1916
|
-
return listOrEmpty().some((row) => matchesWhere(row, calc.where, resolve));
|
|
1917
|
-
case "every":
|
|
1918
|
-
return listOrEmpty().every((row) => matchesWhere(row, calc.where, resolve));
|
|
1919
|
-
case "includes": {
|
|
1920
|
-
const needle = resolved[1];
|
|
1921
|
-
if (typeof first === "string") return first.includes(String(needle ?? ""));
|
|
1922
|
-
return listOrEmpty().some((row) => looseEquals(fieldOf(row, field), needle));
|
|
1923
|
-
}
|
|
1924
|
-
case "distinct": {
|
|
1925
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1926
|
-
return listOrEmpty().filter((row) => {
|
|
1927
|
-
const key = String(fieldOf(row, field) ?? "");
|
|
1928
|
-
if (seen.has(key)) return false;
|
|
1929
|
-
seen.add(key);
|
|
1930
|
-
return true;
|
|
1931
|
-
});
|
|
1932
|
-
}
|
|
1933
|
-
case "except": {
|
|
1934
|
-
const remove = new Set(
|
|
1935
|
-
(Array.isArray(resolved[1]) ? resolved[1] : []).map(
|
|
1936
|
-
(row) => String(fieldOf(row, field) ?? "")
|
|
1937
|
-
)
|
|
1938
|
-
);
|
|
1939
|
-
return listOrEmpty().filter(
|
|
1940
|
-
(row) => !remove.has(String(fieldOf(row, field) ?? ""))
|
|
1941
|
-
);
|
|
1942
|
-
}
|
|
1943
|
-
case "groupBy":
|
|
1944
|
-
case "groupCount": {
|
|
1945
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1946
|
-
for (const row of listOrEmpty()) {
|
|
1947
|
-
const key = String(fieldOf(row, field) ?? "");
|
|
1948
|
-
const bucket = groups.get(key);
|
|
1949
|
-
if (bucket) bucket.push(row);
|
|
1950
|
-
else groups.set(key, [row]);
|
|
1951
|
-
}
|
|
1952
|
-
const out = [...groups.entries()].map(
|
|
1953
|
-
([key, items]) => op === "groupCount" ? { key, count: items.length } : { key, items }
|
|
1954
|
-
);
|
|
1955
|
-
return out;
|
|
1956
|
-
}
|
|
1957
|
-
case "entries": {
|
|
1958
|
-
if (!isPlainObject(first)) return [];
|
|
1959
|
-
return Object.entries(first).map(([key, value]) => ({
|
|
1960
|
-
key,
|
|
1961
|
-
value
|
|
1962
|
-
}));
|
|
1963
|
-
}
|
|
1964
|
-
case "fromEntries": {
|
|
1965
|
-
const out = /* @__PURE__ */ Object.create(null);
|
|
1966
|
-
for (const row of listOrEmpty()) {
|
|
1967
|
-
if (!isPlainObject(row)) continue;
|
|
1968
|
-
const key = row.key;
|
|
1969
|
-
if (key === null || key === void 0 || key === "") continue;
|
|
1970
|
-
out[String(key)] = row.value ?? null;
|
|
1971
|
-
}
|
|
1972
|
-
return { ...out };
|
|
1973
|
-
}
|
|
1974
|
-
case "length":
|
|
1975
|
-
if (Array.isArray(first)) return first.length;
|
|
1976
|
-
if (typeof first === "string") return first.length;
|
|
1977
|
-
return 0;
|
|
1978
|
-
case "join": {
|
|
1979
|
-
const sep = typeof calc.sep === "string" ? calc.sep : ",";
|
|
1980
|
-
return listOrEmpty().map((row) => {
|
|
1981
|
-
const value = fieldOf(row, field);
|
|
1982
|
-
return value == null ? "" : String(value);
|
|
1983
|
-
}).join(sep);
|
|
1984
|
-
}
|
|
1985
|
-
case "split": {
|
|
1986
|
-
const sep = typeof calc.sep === "string" ? calc.sep : ",";
|
|
1987
|
-
let parts = String(first ?? "").split(sep);
|
|
1988
|
-
if (calc.trim === true) parts = parts.map((part) => part.trim());
|
|
1989
|
-
if (calc.take === void 0) return parts;
|
|
1990
|
-
const index = toNumber2(calc.take);
|
|
1991
|
-
const at = index < 0 ? parts.length + index : index;
|
|
1992
|
-
return parts[at] ?? null;
|
|
1993
|
-
}
|
|
1994
|
-
case "map": {
|
|
1995
|
-
const template = calc.select;
|
|
1996
|
-
if (template === void 0) return listOrEmpty();
|
|
1997
|
-
return listOrEmpty().map((row) => resolve(substituteFields(template, row)));
|
|
1998
|
-
}
|
|
1999
|
-
case "sortBy": {
|
|
2000
|
-
const desc = Boolean(calc.desc);
|
|
2001
|
-
return [...listOrEmpty()].sort((a, b) => {
|
|
2002
|
-
const order = orderingCompare(fieldOf(a, field), fieldOf(b, field));
|
|
2003
|
-
return desc ? -order : order;
|
|
2004
|
-
});
|
|
2005
|
-
}
|
|
2006
|
-
default:
|
|
2007
|
-
return null;
|
|
1676
|
+
function withProps(Component, defaults) {
|
|
1677
|
+
function WithProps(props) {
|
|
1678
|
+
const merged = { ...defaults, ...props };
|
|
1679
|
+
return /* @__PURE__ */ jsx(Component, { ...merged });
|
|
2008
1680
|
}
|
|
1681
|
+
const label = Component;
|
|
1682
|
+
WithProps.displayName = `withProps(${label.displayName ?? label.name ?? "Component"})`;
|
|
1683
|
+
return WithProps;
|
|
2009
1684
|
}
|
|
2010
1685
|
|
|
2011
1686
|
// src/request-action.ts
|
|
2012
1687
|
function deferChainEntry(container, key) {
|
|
2013
1688
|
return typeof container.action === "string" && (key === "onSuccess" || key === "onError");
|
|
2014
1689
|
}
|
|
1690
|
+
function failureDetailOf(error) {
|
|
1691
|
+
const message = error instanceof Error ? error.message : "\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4";
|
|
1692
|
+
const carrier = error;
|
|
1693
|
+
const detail = { message };
|
|
1694
|
+
if (typeof carrier?.status === "number") detail.status = carrier.status;
|
|
1695
|
+
if (carrier?.body !== void 0) detail.body = carrier.body;
|
|
1696
|
+
return detail;
|
|
1697
|
+
}
|
|
1698
|
+
function runSuccessChain(kind, id, run) {
|
|
1699
|
+
try {
|
|
1700
|
+
run();
|
|
1701
|
+
} catch (chainError) {
|
|
1702
|
+
const detail = chainError instanceof Error ? chainError.message : String(chainError);
|
|
1703
|
+
warnOnce(
|
|
1704
|
+
`${kind}:chain:${id}`,
|
|
1705
|
+
`[sdui] ${kind}(${id}) \uC131\uACF5 \uD6C4 onSuccess \uCCB4\uC778 \uC2E4\uD589 \uC911 \uC624\uB958: ${detail}`
|
|
1706
|
+
);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
2015
1709
|
async function runRequestAction(action, deps) {
|
|
2016
1710
|
const pendingKey = typeof action.pendingKey === "string" && action.pendingKey !== "" ? action.pendingKey : null;
|
|
2017
1711
|
let pendingOn = false;
|
|
@@ -2027,27 +1721,25 @@ async function runRequestAction(action, deps) {
|
|
|
2027
1721
|
method: action.method ?? "POST",
|
|
2028
1722
|
...action.body === void 0 ? {} : { body: action.body }
|
|
2029
1723
|
});
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
1724
|
+
runSuccessChain("request", action.endpoint, () => {
|
|
1725
|
+
if (typeof action.invalidate === "string" && action.invalidate !== "") {
|
|
1726
|
+
deps.dispatch({ action: "invalidate", prefix: action.invalidate });
|
|
1727
|
+
}
|
|
1728
|
+
(action.onSuccess ?? []).forEach((a) => deps.dispatch(a, response));
|
|
1729
|
+
});
|
|
2034
1730
|
} catch (error) {
|
|
2035
|
-
const
|
|
1731
|
+
const failure = failureDetailOf(error);
|
|
2036
1732
|
if (typeof action.errorKey === "string" && action.errorKey !== "") {
|
|
2037
|
-
const detail = error;
|
|
2038
|
-
const value = { message };
|
|
2039
|
-
if (typeof detail?.status === "number") value.status = detail.status;
|
|
2040
|
-
if (detail?.body !== void 0) value.body = detail.body;
|
|
2041
1733
|
deps.dispatch({
|
|
2042
1734
|
action: "setState",
|
|
2043
1735
|
key: action.errorKey,
|
|
2044
|
-
value
|
|
1736
|
+
value: failure
|
|
2045
1737
|
});
|
|
2046
1738
|
}
|
|
2047
1739
|
if (action.onError?.length) {
|
|
2048
|
-
action.onError.forEach((a) => deps.dispatch(a, message));
|
|
1740
|
+
action.onError.forEach((a) => deps.dispatch(a, failure.message));
|
|
2049
1741
|
} else {
|
|
2050
|
-
deps.toast({ tone: "danger", title: "\uC694\uCCAD \uC2E4\uD328", message });
|
|
1742
|
+
deps.toast({ tone: "danger", title: "\uC694\uCCAD \uC2E4\uD328", message: failure.message });
|
|
2051
1743
|
}
|
|
2052
1744
|
} finally {
|
|
2053
1745
|
if (pendingKey && pendingOn) deps.dispatch({ action: "setState", key: pendingKey, value: false });
|
|
@@ -2056,9 +1748,7 @@ async function runRequestAction(action, deps) {
|
|
|
2056
1748
|
|
|
2057
1749
|
// src/state-path.ts
|
|
2058
1750
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
2059
|
-
|
|
2060
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2061
|
-
}
|
|
1751
|
+
var isPlainObject2 = (value) => isPlainObject(value);
|
|
2062
1752
|
function normalizeSegment(raw) {
|
|
2063
1753
|
if (typeof raw === "number") {
|
|
2064
1754
|
return Number.isInteger(raw) && raw >= 0 ? raw : null;
|
|
@@ -2170,9 +1860,6 @@ var DENIED_PROPS = /* @__PURE__ */ new Set([
|
|
|
2170
1860
|
"constructor",
|
|
2171
1861
|
"prototype"
|
|
2172
1862
|
]);
|
|
2173
|
-
function isPlainObject3(v) {
|
|
2174
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2175
|
-
}
|
|
2176
1863
|
function checkUrlValue(key, value, path, issues) {
|
|
2177
1864
|
if (typeof value !== "string" || !URL_PROPS.has(key)) return;
|
|
2178
1865
|
if (!isDangerousUrlValue(value)) return;
|
|
@@ -2180,7 +1867,7 @@ function checkUrlValue(key, value, path, issues) {
|
|
|
2180
1867
|
}
|
|
2181
1868
|
function validateSpec(page, knownComponents, slotNamesOf) {
|
|
2182
1869
|
const issues = [];
|
|
2183
|
-
if (!
|
|
1870
|
+
if (!isPlainObject(page)) return [{ path: "$", message: "\uC2A4\uD399\uC774 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4" }];
|
|
2184
1871
|
if (page.version !== 1) issues.push({ path: "$.version", message: "version\uC740 1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" });
|
|
2185
1872
|
validatePageData(page.data, issues);
|
|
2186
1873
|
if (!Array.isArray(page.body)) {
|
|
@@ -2194,7 +1881,7 @@ function validateSpec(page, knownComponents, slotNamesOf) {
|
|
|
2194
1881
|
}
|
|
2195
1882
|
function validatePageData(data, issues) {
|
|
2196
1883
|
if (data === void 0) return;
|
|
2197
|
-
if (!
|
|
1884
|
+
if (!isPlainObject(data)) {
|
|
2198
1885
|
issues.push({ path: "$.data", message: "data\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2199
1886
|
return;
|
|
2200
1887
|
}
|
|
@@ -2204,7 +1891,7 @@ function validatePageData(data, issues) {
|
|
|
2204
1891
|
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" });
|
|
2205
1892
|
continue;
|
|
2206
1893
|
}
|
|
2207
|
-
if (!
|
|
1894
|
+
if (!isPlainObject(raw)) {
|
|
2208
1895
|
issues.push({ path, message: "\uC870\uD68C\uB294 { endpoint, select?, when?, fallback? } \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2209
1896
|
continue;
|
|
2210
1897
|
}
|
|
@@ -2216,7 +1903,7 @@ function validatePageData(data, issues) {
|
|
|
2216
1903
|
} else if (!raw.endpoint.startsWith("/")) {
|
|
2217
1904
|
issues.push({ path: `${path}.endpoint`, message: "endpoint\uB294 /\uB85C \uC2DC\uC791\uD558\uB294 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2218
1905
|
}
|
|
2219
|
-
} else if (!
|
|
1906
|
+
} else if (!isPlainObject(raw.endpoint)) {
|
|
2220
1907
|
issues.push({ path: `${path}.endpoint`, message: "endpoint\uB294 \uBB38\uC790\uC5F4\uC774\uAC70\uB098 \uAC12 \uCC38\uC870\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2221
1908
|
} else {
|
|
2222
1909
|
walkValue(raw.endpoint, `${path}.endpoint`, issues, false);
|
|
@@ -2239,7 +1926,7 @@ function validatePageData(data, issues) {
|
|
|
2239
1926
|
}
|
|
2240
1927
|
function walkChild(child, path, issues, known, slotNamesOf) {
|
|
2241
1928
|
if (typeof child === "string") return;
|
|
2242
|
-
if (!
|
|
1929
|
+
if (!isPlainObject(child)) {
|
|
2243
1930
|
issues.push({ path, message: "children \uD56D\uBAA9\uC740 \uBB38\uC790\uC5F4 \uB610\uB294 \uB178\uB4DC \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2244
1931
|
return;
|
|
2245
1932
|
}
|
|
@@ -2251,7 +1938,7 @@ function walkChild(child, path, issues, known, slotNamesOf) {
|
|
|
2251
1938
|
if (known && !RENDERER_BUILTINS.has(node.component) && !known.has(node.component)) {
|
|
2252
1939
|
issues.push({ path, message: `\uB4F1\uB85D\uB418\uC9C0 \uC54A\uC740 \uCEF4\uD3EC\uB10C\uD2B8: ${node.component}` });
|
|
2253
1940
|
}
|
|
2254
|
-
if (node.props !== void 0 && !
|
|
1941
|
+
if (node.props !== void 0 && !isPlainObject(node.props)) {
|
|
2255
1942
|
issues.push({ path: `${path}.props`, message: "props\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2256
1943
|
}
|
|
2257
1944
|
for (const [name, value] of Object.entries(node.props ?? {})) {
|
|
@@ -2265,9 +1952,9 @@ function walkChild(child, path, issues, known, slotNamesOf) {
|
|
|
2265
1952
|
checkUrlValue(name, value, `${path}.props.${name}`, issues);
|
|
2266
1953
|
walkValue(value, `${path}.props.${name}`, issues, name.startsWith("on") || name === "rowClick");
|
|
2267
1954
|
}
|
|
2268
|
-
if (node.component === "Form" &&
|
|
1955
|
+
if (node.component === "Form" && isPlainObject(node.props) && node.props.rules !== void 0) {
|
|
2269
1956
|
const rules = node.props.rules;
|
|
2270
|
-
if (!
|
|
1957
|
+
if (!isPlainObject(rules)) {
|
|
2271
1958
|
issues.push({ path: `${path}.props.rules`, message: "rules\uB294 { \uD544\uB4DC: \uADDC\uCE59\uBC30\uC5F4 } \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2272
1959
|
} else {
|
|
2273
1960
|
for (const [field, list] of Object.entries(rules)) {
|
|
@@ -2278,15 +1965,15 @@ function walkChild(child, path, issues, known, slotNamesOf) {
|
|
|
2278
1965
|
}
|
|
2279
1966
|
}
|
|
2280
1967
|
if (node.component === "Fragment") {
|
|
2281
|
-
const id =
|
|
2282
|
-
if ((typeof id !== "string" || id === "") && !
|
|
1968
|
+
const id = isPlainObject(node.props) ? node.props.id : void 0;
|
|
1969
|
+
if ((typeof id !== "string" || id === "") && !isPlainObject(id)) {
|
|
2283
1970
|
issues.push({
|
|
2284
1971
|
path: `${path}.props.id`,
|
|
2285
1972
|
message: "Fragment.id\uB294 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uBB38\uC790\uC5F4(\uB610\uB294 \uAC12 \uCC38\uC870)\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4"
|
|
2286
1973
|
});
|
|
2287
1974
|
}
|
|
2288
|
-
const args =
|
|
2289
|
-
if (args !== void 0 && !
|
|
1975
|
+
const args = isPlainObject(node.props) ? node.props.args : void 0;
|
|
1976
|
+
if (args !== void 0 && !isPlainObject(args)) {
|
|
2290
1977
|
issues.push({
|
|
2291
1978
|
path: `${path}.props.args`,
|
|
2292
1979
|
message: "Fragment.args\uB294 { \uC774\uB984: \uAC12 } \uD3C9\uBA74 \uC624\uBE0C\uC81D\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4"
|
|
@@ -2300,7 +1987,7 @@ function walkChild(child, path, issues, known, slotNamesOf) {
|
|
|
2300
1987
|
}
|
|
2301
1988
|
}
|
|
2302
1989
|
if (node.slots !== void 0) {
|
|
2303
|
-
if (!
|
|
1990
|
+
if (!isPlainObject(node.slots)) {
|
|
2304
1991
|
issues.push({ path: `${path}.slots`, message: "slots\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
2305
1992
|
} else {
|
|
2306
1993
|
const slotNames = slotNamesOf?.(node.component) ?? null;
|
|
@@ -2336,7 +2023,7 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2336
2023
|
value.forEach((v, i) => walkValue(v, `${path}[${i}]`, issues, actionEligible));
|
|
2337
2024
|
return;
|
|
2338
2025
|
}
|
|
2339
|
-
if (!
|
|
2026
|
+
if (!isPlainObject(value)) return;
|
|
2340
2027
|
if (typeof value.action === "string" && (actionEligible || ACTION_NAMES.has(value.action))) {
|
|
2341
2028
|
if (actionEligible && !ACTION_NAMES.has(value.action)) {
|
|
2342
2029
|
issues.push({ path, message: `\uC54C \uC218 \uC5C6\uB294 \uC561\uC158: ${value.action}` });
|
|
@@ -2353,6 +2040,17 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2353
2040
|
}
|
|
2354
2041
|
}
|
|
2355
2042
|
}
|
|
2043
|
+
if (typeof value.endpoint === "string" && isDangerousUrlValue(value.endpoint)) {
|
|
2044
|
+
issues.push({ path: `${path}.endpoint`, message: `\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 endpoint: ${value.endpoint}` });
|
|
2045
|
+
}
|
|
2046
|
+
if (value.action === "navigate" && typeof value.to === "string" && isDangerousUrlValue(value.to)) {
|
|
2047
|
+
issues.push({ path: `${path}.to`, message: `\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 navigate \uB300\uC0C1: ${value.to}` });
|
|
2048
|
+
}
|
|
2049
|
+
for (const [k, v] of Object.entries(value)) {
|
|
2050
|
+
if (k === "action" || k === "onSuccess" || k === "onError") continue;
|
|
2051
|
+
checkUrlValue(k, v, `${path}.${k}`, issues);
|
|
2052
|
+
walkValue(v, `${path}.${k}`, issues, false);
|
|
2053
|
+
}
|
|
2356
2054
|
return;
|
|
2357
2055
|
}
|
|
2358
2056
|
if ("$state" in value && typeof value.$state !== "string") {
|
|
@@ -2363,7 +2061,7 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2363
2061
|
}
|
|
2364
2062
|
if ("$calc" in value) {
|
|
2365
2063
|
const calc = value.$calc;
|
|
2366
|
-
if (!
|
|
2064
|
+
if (!isPlainObject(calc) || typeof calc.op !== "string" || !CALC_OPS.has(calc.op)) {
|
|
2367
2065
|
issues.push({ path, message: "$calc.op\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4" });
|
|
2368
2066
|
} else if (!Array.isArray(calc.args)) {
|
|
2369
2067
|
issues.push({ path: `${path}.$calc.args`, message: "args\uB294 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" });
|
|
@@ -2392,12 +2090,9 @@ function walkValue(value, path, issues, actionEligible) {
|
|
|
2392
2090
|
}
|
|
2393
2091
|
|
|
2394
2092
|
// src/page-data.ts
|
|
2395
|
-
function isPlainObject4(value) {
|
|
2396
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2397
|
-
}
|
|
2398
2093
|
function planPageData(data, resolve) {
|
|
2399
2094
|
if (data === void 0) return [];
|
|
2400
|
-
if (!
|
|
2095
|
+
if (!isPlainObject(data)) {
|
|
2401
2096
|
warnOnce("page-data:root", "[sdui] page.data\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4 \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4");
|
|
2402
2097
|
return [];
|
|
2403
2098
|
}
|
|
@@ -2407,7 +2102,7 @@ function planPageData(data, resolve) {
|
|
|
2407
2102
|
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`);
|
|
2408
2103
|
continue;
|
|
2409
2104
|
}
|
|
2410
|
-
if (!
|
|
2105
|
+
if (!isPlainObject(raw)) {
|
|
2411
2106
|
warnOnce(`page-data:${key}`, `[sdui] page.data.${key}\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4 \u2014 \uAC74\uB108\uB701\uB2C8\uB2E4`);
|
|
2412
2107
|
continue;
|
|
2413
2108
|
}
|
|
@@ -2433,6 +2128,9 @@ function planSignature(plans) {
|
|
|
2433
2128
|
function runnablePlans(plans) {
|
|
2434
2129
|
return plans.filter((plan) => plan.enabled && plan.endpoint !== "");
|
|
2435
2130
|
}
|
|
2131
|
+
function staleLoadingKeys(plans, isLoading) {
|
|
2132
|
+
return plans.filter((plan) => (!plan.enabled || plan.endpoint === "") && isLoading(plan.key)).map((plan) => plan.key);
|
|
2133
|
+
}
|
|
2436
2134
|
function invalidateHits(plans, prefix, exact) {
|
|
2437
2135
|
return plans.filter(
|
|
2438
2136
|
(plan) => prefix === "" ? true : exact ? plan.endpoint === prefix : plan.endpoint.startsWith(prefix)
|
|
@@ -2442,7 +2140,7 @@ function pickPath(source, path) {
|
|
|
2442
2140
|
if (path === void 0 || path === "") return source;
|
|
2443
2141
|
let value = source;
|
|
2444
2142
|
for (const segment of path.split(".")) {
|
|
2445
|
-
if (!
|
|
2143
|
+
if (!isPlainObject(value) || !Object.hasOwn(value, segment)) return void 0;
|
|
2446
2144
|
value = value[segment];
|
|
2447
2145
|
}
|
|
2448
2146
|
return value;
|
|
@@ -2483,37 +2181,34 @@ function nearestArgsFrame(scope) {
|
|
|
2483
2181
|
}
|
|
2484
2182
|
return void 0;
|
|
2485
2183
|
}
|
|
2486
|
-
function isPlainObject5(v) {
|
|
2487
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2488
|
-
}
|
|
2489
2184
|
function isStateRef(v) {
|
|
2490
|
-
return
|
|
2185
|
+
return isPlainObject(v) && typeof v.$state === "string";
|
|
2491
2186
|
}
|
|
2492
2187
|
function isItemRef(v) {
|
|
2493
|
-
return
|
|
2188
|
+
return isPlainObject(v) && "$item" in v;
|
|
2494
2189
|
}
|
|
2495
2190
|
function isIndexRef(v) {
|
|
2496
|
-
return
|
|
2191
|
+
return isPlainObject(v) && "$index" in v;
|
|
2497
2192
|
}
|
|
2498
2193
|
function isEventRef(v) {
|
|
2499
|
-
return
|
|
2194
|
+
return isPlainObject(v) && "$event" in v;
|
|
2500
2195
|
}
|
|
2501
2196
|
function readEventPath(source, path) {
|
|
2502
2197
|
let current = source;
|
|
2503
2198
|
for (const segment of path.split(".")) {
|
|
2504
|
-
if (!
|
|
2199
|
+
if (!isPlainObject(current) || !Object.hasOwn(current, segment)) return null;
|
|
2505
2200
|
current = current[segment];
|
|
2506
2201
|
}
|
|
2507
2202
|
return current ?? null;
|
|
2508
2203
|
}
|
|
2509
2204
|
function isCalcRef(v) {
|
|
2510
|
-
return
|
|
2205
|
+
return isPlainObject(v) && isPlainObject(v.$calc);
|
|
2511
2206
|
}
|
|
2512
2207
|
function isArgRef(v) {
|
|
2513
|
-
return
|
|
2208
|
+
return isPlainObject(v) && typeof v.$arg === "string";
|
|
2514
2209
|
}
|
|
2515
2210
|
function isAction(v) {
|
|
2516
|
-
return
|
|
2211
|
+
return isPlainObject(v) && typeof v.action === "string";
|
|
2517
2212
|
}
|
|
2518
2213
|
function isActionList(v) {
|
|
2519
2214
|
return Array.isArray(v) && v.length > 0 && v.every(isAction);
|
|
@@ -2533,11 +2228,11 @@ function expandActionEntries(raw, scope) {
|
|
|
2533
2228
|
}
|
|
2534
2229
|
if (isAction(raw)) return [{ action: raw, scope }];
|
|
2535
2230
|
if (raw === void 0 || raw === null) return [];
|
|
2536
|
-
|
|
2231
|
+
warnOnce(`action-entry:${typeof raw}`, "[sdui] \uC561\uC158 \uC790\uB9AC\uC5D0 \uC561\uC158\uC774 \uC544\uB2CC \uAC12\uC774 \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4");
|
|
2537
2232
|
return [];
|
|
2538
2233
|
}
|
|
2539
2234
|
function eventValue(arg) {
|
|
2540
|
-
if (
|
|
2235
|
+
if (isPlainObject(arg) && "target" in arg && typeof arg.preventDefault === "function") {
|
|
2541
2236
|
const target = arg.target;
|
|
2542
2237
|
if (target.type === "checkbox") return target.checked;
|
|
2543
2238
|
if (target.type === "number" || target.type === "range") {
|
|
@@ -2557,12 +2252,6 @@ function toIndex(v) {
|
|
|
2557
2252
|
function inRange(index, length) {
|
|
2558
2253
|
return index !== null && index >= 0 && index < length;
|
|
2559
2254
|
}
|
|
2560
|
-
var warned = /* @__PURE__ */ new Set();
|
|
2561
|
-
function warnOnce2(key, message) {
|
|
2562
|
-
if (warned.has(key)) return;
|
|
2563
|
-
warned.add(key);
|
|
2564
|
-
console.error(message);
|
|
2565
|
-
}
|
|
2566
2255
|
function validateForm(values, config, customValidators) {
|
|
2567
2256
|
const errors = {};
|
|
2568
2257
|
if (!config) return errors;
|
|
@@ -2586,9 +2275,9 @@ function ruleError(rule, value, values, label, customValidators) {
|
|
|
2586
2275
|
case "required":
|
|
2587
2276
|
return empty || value === false ? rule.message ?? `${label}\uC744(\uB97C) \uC785\uB825\uD558\uC138\uC694` : null;
|
|
2588
2277
|
case "min":
|
|
2589
|
-
return !empty &&
|
|
2278
|
+
return !empty && toNumber(value) < rule.value ? rule.message ?? `${label}\uC740(\uB294) ${rule.value} \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4` : null;
|
|
2590
2279
|
case "max":
|
|
2591
|
-
return !empty &&
|
|
2280
|
+
return !empty && toNumber(value) > rule.value ? rule.message ?? `${label}\uC740(\uB294) ${rule.value} \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4` : null;
|
|
2592
2281
|
case "minLength":
|
|
2593
2282
|
return !empty && String(value).length < rule.value ? rule.message ?? `${label}\uC740(\uB294) ${rule.value}\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4` : null;
|
|
2594
2283
|
case "maxLength":
|
|
@@ -2615,22 +2304,6 @@ function ruleError(rule, value, values, label, customValidators) {
|
|
|
2615
2304
|
}
|
|
2616
2305
|
}
|
|
2617
2306
|
var EMPTY_SCOPE = [];
|
|
2618
|
-
function SpecErrorCard({ children }) {
|
|
2619
|
-
return /* @__PURE__ */ jsx(
|
|
2620
|
-
"div",
|
|
2621
|
-
{
|
|
2622
|
-
role: "alert",
|
|
2623
|
-
className: "wp-caption",
|
|
2624
|
-
style: {
|
|
2625
|
-
color: "var(--wp-text-danger)",
|
|
2626
|
-
border: "1px dashed var(--wp-border-danger)",
|
|
2627
|
-
borderRadius: "var(--wp-radius-md)",
|
|
2628
|
-
padding: "var(--wp-space-3)"
|
|
2629
|
-
},
|
|
2630
|
-
children
|
|
2631
|
-
}
|
|
2632
|
-
);
|
|
2633
|
-
}
|
|
2634
2307
|
var DENIED_PROPS2 = /* @__PURE__ */ new Set([
|
|
2635
2308
|
"dangerouslySetInnerHTML",
|
|
2636
2309
|
"ref",
|
|
@@ -2642,13 +2315,13 @@ var DENIED_PROPS2 = /* @__PURE__ */ new Set([
|
|
|
2642
2315
|
function childrenOf(node) {
|
|
2643
2316
|
if (node.children === void 0) return void 0;
|
|
2644
2317
|
if (Array.isArray(node.children)) return node.children;
|
|
2645
|
-
|
|
2318
|
+
warnOnce(`children:${node.component}`, `[sdui] <${node.component}>.children\uC740 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4`);
|
|
2646
2319
|
return typeof node.children === "string" ? [node.children] : void 0;
|
|
2647
2320
|
}
|
|
2648
2321
|
function renderChild(child, index, scope, engine) {
|
|
2649
2322
|
if (typeof child === "string") return child;
|
|
2650
|
-
if (!
|
|
2651
|
-
|
|
2323
|
+
if (!isPlainObject(child)) {
|
|
2324
|
+
warnOnce(`child:${String(child)}`, `[sdui] \uC798\uBABB\uB41C \uB178\uB4DC(\uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4): ${String(child)}`);
|
|
2652
2325
|
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);
|
|
2653
2326
|
}
|
|
2654
2327
|
const node = child;
|
|
@@ -2662,11 +2335,11 @@ function SduiNodeRenderer({
|
|
|
2662
2335
|
engine
|
|
2663
2336
|
}) {
|
|
2664
2337
|
const { registry, resolveValue, dispatch, resolveAsset } = engine;
|
|
2665
|
-
const slots =
|
|
2338
|
+
const slots = isPlainObject(node.slots) ? node.slots : void 0;
|
|
2666
2339
|
const childList = childrenOf(node);
|
|
2667
2340
|
if (node.component === "ForEach") {
|
|
2668
2341
|
const source = resolveValue(
|
|
2669
|
-
|
|
2342
|
+
isPlainObject(node.props) ? node.props.source ?? null : null,
|
|
2670
2343
|
scope
|
|
2671
2344
|
);
|
|
2672
2345
|
const rows = Array.isArray(source) ? source : [];
|
|
@@ -2676,7 +2349,7 @@ function SduiNodeRenderer({
|
|
|
2676
2349
|
}
|
|
2677
2350
|
return /* @__PURE__ */ jsx(Fragment, { children: rows.map((item, rowIndex) => {
|
|
2678
2351
|
const rowScope = [...scope, { item, index: rowIndex }];
|
|
2679
|
-
const rowKey =
|
|
2352
|
+
const rowKey = isPlainObject(item) && typeof item.id === "string" ? item.id : rowIndex;
|
|
2680
2353
|
return /* @__PURE__ */ jsx(React9.Fragment, { children: childList?.map((child, i) => renderChild(child, i, rowScope, engine)) }, rowKey);
|
|
2681
2354
|
}) });
|
|
2682
2355
|
}
|
|
@@ -2685,7 +2358,7 @@ function SduiNodeRenderer({
|
|
|
2685
2358
|
}
|
|
2686
2359
|
const Component = typeof node.component === "string" && Object.hasOwn(registry, node.component) ? registry[node.component] : void 0;
|
|
2687
2360
|
if (!Component) {
|
|
2688
|
-
|
|
2361
|
+
warnOnce(
|
|
2689
2362
|
`component:${String(node.component)}`,
|
|
2690
2363
|
`[sdui] \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC740 \uCEF4\uD3EC\uB10C\uD2B8: ${String(node.component)} \u2014 \uC774 \uB178\uB4DC\uC640 \uC790\uC2DD\uC740 \uB80C\uB354\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`
|
|
2691
2364
|
);
|
|
@@ -2695,9 +2368,9 @@ function SduiNodeRenderer({
|
|
|
2695
2368
|
] });
|
|
2696
2369
|
}
|
|
2697
2370
|
const props = {};
|
|
2698
|
-
for (const [name, raw] of Object.entries(
|
|
2371
|
+
for (const [name, raw] of Object.entries(isPlainObject(node.props) ? node.props : {})) {
|
|
2699
2372
|
if (DENIED_PROPS2.has(name)) {
|
|
2700
|
-
|
|
2373
|
+
warnOnce(`prop:${name}`, `[sdui] \uC2A4\uD399\uC5D0\uC11C \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD504\uB86D: ${name} \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
2701
2374
|
continue;
|
|
2702
2375
|
}
|
|
2703
2376
|
if (name.startsWith("on") && (isAction(raw) || isArgRef(raw) || isActionEntryList(raw))) {
|
|
@@ -2715,7 +2388,7 @@ function SduiNodeRenderer({
|
|
|
2715
2388
|
else if (name === "checked") resolved = false;
|
|
2716
2389
|
}
|
|
2717
2390
|
if (URL_PROPS.has(name) && typeof resolved === "string" && isDangerousUrlValue(resolved)) {
|
|
2718
|
-
|
|
2391
|
+
warnOnce(`url:${name}:${resolved}`, `[sdui] \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 ${name} \uAC12: ${resolved} \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
2719
2392
|
continue;
|
|
2720
2393
|
}
|
|
2721
2394
|
if (resolveAsset) {
|
|
@@ -2730,7 +2403,7 @@ function SduiNodeRenderer({
|
|
|
2730
2403
|
if (childLayout) {
|
|
2731
2404
|
for (const name of CHILD_LAYOUT_PROPS) delete props[name];
|
|
2732
2405
|
if (childLayout.style) {
|
|
2733
|
-
props.style =
|
|
2406
|
+
props.style = isPlainObject(props.style) ? { ...childLayout.style, ...props.style } : childLayout.style;
|
|
2734
2407
|
}
|
|
2735
2408
|
if (childLayout.className) {
|
|
2736
2409
|
props.className = typeof props.className === "string" && props.className ? `${childLayout.className} ${props.className}` : childLayout.className;
|
|
@@ -2738,7 +2411,7 @@ function SduiNodeRenderer({
|
|
|
2738
2411
|
}
|
|
2739
2412
|
for (const [name, slotChildren] of Object.entries(slots ?? {})) {
|
|
2740
2413
|
if (!Array.isArray(slotChildren)) {
|
|
2741
|
-
|
|
2414
|
+
warnOnce(
|
|
2742
2415
|
`slot:${node.component}.${name}`,
|
|
2743
2416
|
`[sdui] <${node.component}>.slots.${name}\uC740 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`
|
|
2744
2417
|
);
|
|
@@ -2754,33 +2427,33 @@ var FRAGMENT_MAX_DEPTH = 4;
|
|
|
2754
2427
|
function SduiFragment({ node, scope, engine }) {
|
|
2755
2428
|
const adapter = useSduiAdapter();
|
|
2756
2429
|
const chain = React9.useContext(SduiFragmentChainContext);
|
|
2757
|
-
const raw =
|
|
2430
|
+
const raw = isPlainObject(node.props) ? node.props.id : void 0;
|
|
2758
2431
|
const id = engine.resolveValue(raw, scope);
|
|
2759
2432
|
if (typeof id !== "string" || id === "") {
|
|
2760
|
-
|
|
2433
|
+
warnOnce("fragment:id", "[sdui] <Fragment>\uC5D0 \uBB38\uC790\uC5F4 id\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uB80C\uB354\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
|
|
2761
2434
|
return null;
|
|
2762
2435
|
}
|
|
2763
2436
|
if (chain.includes(id)) {
|
|
2764
|
-
|
|
2437
|
+
warnOnce(
|
|
2765
2438
|
`fragment:cycle:${id}`,
|
|
2766
2439
|
`[sdui] Fragment \uC21C\uD658 \uCC38\uC870: ${[...chain, id].join(" \u2192 ")} \u2014 \uC7AC\uC9C4\uC785\uC744 \uAC70\uBD80\uD569\uB2C8\uB2E4`
|
|
2767
2440
|
);
|
|
2768
2441
|
return null;
|
|
2769
2442
|
}
|
|
2770
2443
|
if (chain.length >= FRAGMENT_MAX_DEPTH) {
|
|
2771
|
-
|
|
2444
|
+
warnOnce(
|
|
2772
2445
|
`fragment:depth:${id}`,
|
|
2773
2446
|
`[sdui] Fragment \uC911\uCCA9\uC774 ${FRAGMENT_MAX_DEPTH}\uB2E8\uC744 \uB118\uC2B5\uB2C8\uB2E4(${[...chain, id].join(" \u2192 ")}) \u2014 \uB80C\uB354\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`
|
|
2774
2447
|
);
|
|
2775
2448
|
return null;
|
|
2776
2449
|
}
|
|
2777
|
-
const rawArgs =
|
|
2778
|
-
if (rawArgs !== void 0 && !
|
|
2779
|
-
|
|
2450
|
+
const rawArgs = isPlainObject(node.props) ? node.props.args : void 0;
|
|
2451
|
+
if (rawArgs !== void 0 && !isPlainObject(rawArgs)) {
|
|
2452
|
+
warnOnce(`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`);
|
|
2780
2453
|
}
|
|
2781
2454
|
const useSpec = adapter?.useSpec;
|
|
2782
2455
|
if (!useSpec) {
|
|
2783
|
-
|
|
2456
|
+
warnOnce(
|
|
2784
2457
|
"fragment:no-useSpec",
|
|
2785
2458
|
"[sdui] Fragment\uB97C \uC4F0\uB824\uBA74 \uC5B4\uB311\uD130\uC5D0 useSpec(id)\uC774 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uB80C\uB354\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"
|
|
2786
2459
|
);
|
|
@@ -2790,7 +2463,7 @@ function SduiFragment({ node, scope, engine }) {
|
|
|
2790
2463
|
SduiFragmentBody,
|
|
2791
2464
|
{
|
|
2792
2465
|
id,
|
|
2793
|
-
args:
|
|
2466
|
+
args: isPlainObject(rawArgs) ? rawArgs : void 0,
|
|
2794
2467
|
useSpec,
|
|
2795
2468
|
chain,
|
|
2796
2469
|
scope,
|
|
@@ -2811,19 +2484,19 @@ function SduiFragmentBody({
|
|
|
2811
2484
|
const nextChain = React9.useMemo(() => [...chain, id], [chain, id]);
|
|
2812
2485
|
const bodyScope = React9.useMemo(() => [...scope, { args, callSiteScope: scope }], [scope, args]);
|
|
2813
2486
|
React9.useEffect(() => {
|
|
2814
|
-
if (!DEV || !
|
|
2487
|
+
if (!DEV || !isPlainObject(spec) || !Array.isArray(spec.body)) return;
|
|
2815
2488
|
validateSpec(spec, new Set(Object.keys(engine.registry))).forEach(
|
|
2816
2489
|
(issue) => console.warn(`[sdui:spec:${id}] ${issue.path} \u2014 ${issue.message}`)
|
|
2817
2490
|
);
|
|
2818
2491
|
}, [spec, engine.registry, id]);
|
|
2819
2492
|
if (spec === void 0) return null;
|
|
2820
|
-
if (spec === null || !
|
|
2821
|
-
|
|
2493
|
+
if (spec === null || !isPlainObject(spec) || !Array.isArray(spec.body)) {
|
|
2494
|
+
warnOnce(`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`);
|
|
2822
2495
|
return null;
|
|
2823
2496
|
}
|
|
2824
2497
|
const page = spec;
|
|
2825
2498
|
if (page.data || page.urlState || page.subscribe) {
|
|
2826
|
-
|
|
2499
|
+
warnOnce(
|
|
2827
2500
|
`fragment:page-features:${id}`,
|
|
2828
2501
|
`[sdui] Fragment \uC2A4\uD399 ${id}\uC758 data/urlState/subscribe\uB294 \uBB34\uC2DC\uB429\uB2C8\uB2E4 \u2014 \uC870\uAC01\uC740 body\uB9CC \uB80C\uB354\uD569\uB2C8\uB2E4`
|
|
2829
2502
|
);
|
|
@@ -2843,6 +2516,8 @@ function SduiRenderer({
|
|
|
2843
2516
|
const host = useMemo(() => resolveAdapter(adapter), [adapter]);
|
|
2844
2517
|
const hostRef = useRef(host);
|
|
2845
2518
|
hostRef.current = host;
|
|
2519
|
+
const invalidatePageDataRef = useRef(() => {
|
|
2520
|
+
});
|
|
2846
2521
|
const [state, setState] = useState(() => ({ ...initialState ?? {} }));
|
|
2847
2522
|
const urlSeeded = useRef(false);
|
|
2848
2523
|
const [toasts, setToasts] = useState([]);
|
|
@@ -2912,21 +2587,25 @@ function SduiRenderer({
|
|
|
2912
2587
|
}
|
|
2913
2588
|
}, [state, page.urlState, syncUrlState]);
|
|
2914
2589
|
const subscribeChannels = page.subscribe?.join(",") ?? "";
|
|
2590
|
+
const hasSubscribe = typeof host.subscribe === "function";
|
|
2915
2591
|
useEffect(() => {
|
|
2916
|
-
if (!subscribeChannels || !
|
|
2592
|
+
if (!subscribeChannels || !hasSubscribe) return;
|
|
2593
|
+
const subscribe = hostRef.current.subscribe;
|
|
2594
|
+
if (!subscribe) return;
|
|
2917
2595
|
let timer = null;
|
|
2918
|
-
const unsubscribe =
|
|
2596
|
+
const unsubscribe = subscribe(subscribeChannels.split(","), () => {
|
|
2919
2597
|
if (timer) clearTimeout(timer);
|
|
2920
2598
|
timer = setTimeout(() => {
|
|
2921
|
-
|
|
2922
|
-
|
|
2599
|
+
hostRef.current.invalidate?.("", false);
|
|
2600
|
+
hostRef.current.refresh?.();
|
|
2601
|
+
invalidatePageDataRef.current("", false);
|
|
2923
2602
|
}, 300);
|
|
2924
2603
|
});
|
|
2925
2604
|
return () => {
|
|
2926
2605
|
if (timer) clearTimeout(timer);
|
|
2927
2606
|
unsubscribe();
|
|
2928
2607
|
};
|
|
2929
|
-
}, [subscribeChannels,
|
|
2608
|
+
}, [subscribeChannels, hasSubscribe]);
|
|
2930
2609
|
const closeToast = useCallback((id) => {
|
|
2931
2610
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
2932
2611
|
}, []);
|
|
@@ -2940,14 +2619,14 @@ function SduiRenderer({
|
|
|
2940
2619
|
);
|
|
2941
2620
|
const lookupState = useCallback((key) => {
|
|
2942
2621
|
const current = stateRef.current;
|
|
2943
|
-
if (key
|
|
2622
|
+
if (Object.hasOwn(current, key)) return current[key];
|
|
2944
2623
|
const segments = key.split(".");
|
|
2945
2624
|
for (let cut = segments.length - 1; cut >= 1; cut--) {
|
|
2946
2625
|
const head = segments.slice(0, cut).join(".");
|
|
2947
|
-
if (head
|
|
2626
|
+
if (Object.hasOwn(current, head)) {
|
|
2948
2627
|
let value = current[head];
|
|
2949
2628
|
for (const segment of segments.slice(cut)) {
|
|
2950
|
-
if (!
|
|
2629
|
+
if (!isPlainObject(value) || !Object.hasOwn(value, segment)) return void 0;
|
|
2951
2630
|
value = value[segment];
|
|
2952
2631
|
}
|
|
2953
2632
|
return value;
|
|
@@ -2965,7 +2644,7 @@ function SduiRenderer({
|
|
|
2965
2644
|
const frame = nearestItemFrame(scope);
|
|
2966
2645
|
if (!frame) return null;
|
|
2967
2646
|
if (value.$item === true) return frame.item;
|
|
2968
|
-
return
|
|
2647
|
+
return isPlainObject(frame.item) && Object.hasOwn(frame.item, value.$item) ? frame.item[value.$item] ?? null : null;
|
|
2969
2648
|
}
|
|
2970
2649
|
if (isIndexRef(value)) {
|
|
2971
2650
|
return nearestItemFrame(scope)?.index ?? null;
|
|
@@ -2987,14 +2666,14 @@ function SduiRenderer({
|
|
|
2987
2666
|
const calc = value.$calc;
|
|
2988
2667
|
const op = typeof calc.op === "string" ? calc.op : "";
|
|
2989
2668
|
if (!Array.isArray(calc.args)) {
|
|
2990
|
-
|
|
2669
|
+
warnOnce(`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`);
|
|
2991
2670
|
return null;
|
|
2992
2671
|
}
|
|
2993
2672
|
const resolved = calc.args.map((arg) => resolveValue(arg, scope));
|
|
2994
2673
|
return evaluateCalc(calc, resolved, (late) => resolveValue(late, scope));
|
|
2995
2674
|
}
|
|
2996
2675
|
if (Array.isArray(value)) return value.map((v) => resolveValue(v, scope));
|
|
2997
|
-
if (
|
|
2676
|
+
if (isPlainObject(value)) {
|
|
2998
2677
|
const out = {};
|
|
2999
2678
|
for (const [k, v] of Object.entries(value)) {
|
|
3000
2679
|
out[k] = deferChainEntry(value, k) ? v : resolveValue(v, scope);
|
|
@@ -3016,11 +2695,25 @@ function SduiRenderer({
|
|
|
3016
2695
|
const [dataEpochs, setDataEpochs] = useState({});
|
|
3017
2696
|
const dataEpochsRef = useRef(dataEpochs);
|
|
3018
2697
|
dataEpochsRef.current = dataEpochs;
|
|
2698
|
+
invalidatePageDataRef.current = (prefix, exact) => {
|
|
2699
|
+
const hit = invalidateHits(runnablePlans(dataPlanRef.current), prefix, exact);
|
|
2700
|
+
if (hit.length === 0) return;
|
|
2701
|
+
setDataEpochs((prev) => {
|
|
2702
|
+
const next = { ...prev };
|
|
2703
|
+
for (const key of hit) next[key] = (next[key] ?? 0) + 1;
|
|
2704
|
+
return next;
|
|
2705
|
+
});
|
|
2706
|
+
};
|
|
3019
2707
|
const fetchedRef = useRef(/* @__PURE__ */ new Map());
|
|
3020
2708
|
const dataEpochKey = useMemo(() => JSON.stringify(dataEpochs), [dataEpochs]);
|
|
3021
2709
|
useEffect(() => {
|
|
3022
2710
|
const epochs = dataEpochsRef.current;
|
|
3023
|
-
const
|
|
2711
|
+
const plans = dataPlanRef.current;
|
|
2712
|
+
const stale = staleLoadingKeys(plans, (key) => stateRef.current[`${key}.$loading`] === true);
|
|
2713
|
+
if (stale.length > 0) {
|
|
2714
|
+
setEntries(Object.fromEntries(stale.map((key) => [`${key}.$loading`, false])));
|
|
2715
|
+
}
|
|
2716
|
+
const runnable = runnablePlans(plans).filter((plan) => {
|
|
3024
2717
|
const stamp = planStamp(plan, epochs);
|
|
3025
2718
|
if (fetchedRef.current.get(plan.key) === stamp) return false;
|
|
3026
2719
|
fetchedRef.current.set(plan.key, stamp);
|
|
@@ -3049,7 +2742,7 @@ function SduiRenderer({
|
|
|
3049
2742
|
if (cancelled) return;
|
|
3050
2743
|
const picked = pickPath(response, plan.select);
|
|
3051
2744
|
if (picked === void 0 && plan.select !== void 0 && response !== null) {
|
|
3052
|
-
|
|
2745
|
+
warnOnce(
|
|
3053
2746
|
`data-select:${plan.key}`,
|
|
3054
2747
|
`[sdui] page.data.${plan.key}: \uC751\uB2F5\uC5D0 "${plan.select}" \uACBD\uB85C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uAC12 \uC5C6\uC74C\uC73C\uB85C \uB461\uB2C8\uB2E4 (${plan.endpoint})`
|
|
3055
2748
|
);
|
|
@@ -3063,7 +2756,7 @@ function SduiRenderer({
|
|
|
3063
2756
|
} catch (error) {
|
|
3064
2757
|
if (cancelled || controller.signal.aborted) return;
|
|
3065
2758
|
const message = error instanceof Error ? error.message : "\uC870\uD68C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4";
|
|
3066
|
-
|
|
2759
|
+
warnOnce(`data:${plan.key}:${plan.endpoint}`, `[sdui] page.data.${plan.key} \uC870\uD68C \uC2E4\uD328 (${plan.endpoint}): ${message}`);
|
|
3067
2760
|
settled.add(plan.key);
|
|
3068
2761
|
setEntries({
|
|
3069
2762
|
[plan.key]: plan.fallback ?? void 0,
|
|
@@ -3098,7 +2791,7 @@ function SduiRenderer({
|
|
|
3098
2791
|
switch (action.action) {
|
|
3099
2792
|
case "navigate":
|
|
3100
2793
|
if (!isSafeNavigationTarget(action.to)) {
|
|
3101
|
-
|
|
2794
|
+
warnOnce(`navigate:${String(action.to)}`, `[sdui] \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 navigate \uB300\uC0C1: ${String(action.to)}`);
|
|
3102
2795
|
break;
|
|
3103
2796
|
}
|
|
3104
2797
|
hostRef.current.navigate(action.to);
|
|
@@ -3115,7 +2808,7 @@ function SduiRenderer({
|
|
|
3115
2808
|
nextValue
|
|
3116
2809
|
);
|
|
3117
2810
|
if (!written.ok) {
|
|
3118
|
-
|
|
2811
|
+
warnOnce(
|
|
3119
2812
|
`setState:${action.key}:${written.reason}`,
|
|
3120
2813
|
`[sdui] setState(${action.key}) \uACBD\uB85C \uC4F0\uAE30\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4 \u2014 ${written.reason}`
|
|
3121
2814
|
);
|
|
@@ -3144,7 +2837,7 @@ function SduiRenderer({
|
|
|
3144
2837
|
if (!Array.isArray(current)) break;
|
|
3145
2838
|
const index = toIndex(action.index);
|
|
3146
2839
|
if (!inRange(index, current.length)) {
|
|
3147
|
-
|
|
2840
|
+
warnOnce(`arrayRemove:${action.key}`, `[sdui] arrayRemove(${action.key}): \uC798\uBABB\uB41C index \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
3148
2841
|
break;
|
|
3149
2842
|
}
|
|
3150
2843
|
setEntries({ [action.key]: current.filter((_, i) => i !== index) });
|
|
@@ -3153,7 +2846,7 @@ function SduiRenderer({
|
|
|
3153
2846
|
case "arrayMove": {
|
|
3154
2847
|
const current = stateRef.current[action.key];
|
|
3155
2848
|
if (!Array.isArray(current)) {
|
|
3156
|
-
|
|
2849
|
+
warnOnce(
|
|
3157
2850
|
`arrayMove:key:${action.key}`,
|
|
3158
2851
|
`[sdui] arrayMove(${action.key}): \uC0C1\uD0DC\uAC00 \uBC30\uC5F4\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uC911\uCCA9 \uACBD\uB85C\uC774\uAC70\uB098 default\uB85C\uB9CC \uADF8\uB824\uC9C4 \uBC30\uC5F4\uC785\uB2C8\uB2E4`
|
|
3159
2852
|
);
|
|
@@ -3162,7 +2855,7 @@ function SduiRenderer({
|
|
|
3162
2855
|
const from = toIndex(action.from);
|
|
3163
2856
|
const rawTo = toIndex(action.to);
|
|
3164
2857
|
if (!inRange(from, current.length) || rawTo === null) {
|
|
3165
|
-
|
|
2858
|
+
warnOnce(`arrayMove:${action.key}`, `[sdui] arrayMove(${action.key}): \uC798\uBABB\uB41C from/to \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
3166
2859
|
break;
|
|
3167
2860
|
}
|
|
3168
2861
|
const to = Math.max(0, Math.min(current.length - 1, rawTo));
|
|
@@ -3178,11 +2871,11 @@ function SduiRenderer({
|
|
|
3178
2871
|
if (!Array.isArray(current)) break;
|
|
3179
2872
|
const index = toIndex(action.index);
|
|
3180
2873
|
if (!inRange(index, current.length) || typeof action.field !== "string") {
|
|
3181
|
-
|
|
2874
|
+
warnOnce(`arrayUpdate:${action.key}`, `[sdui] arrayUpdate(${action.key}): \uC798\uBABB\uB41C index/field \u2014 \uBB34\uC2DC\uD569\uB2C8\uB2E4`);
|
|
3182
2875
|
break;
|
|
3183
2876
|
}
|
|
3184
2877
|
const row = current[index];
|
|
3185
|
-
if (!
|
|
2878
|
+
if (!isPlainObject(row)) break;
|
|
3186
2879
|
const nextValue = (action.fromEvent ? eventValue(arg) : action.value) ?? null;
|
|
3187
2880
|
const next = [...current];
|
|
3188
2881
|
next[index] = { ...row, [action.field]: nextValue };
|
|
@@ -3192,9 +2885,11 @@ function SduiRenderer({
|
|
|
3192
2885
|
case "submitForm": {
|
|
3193
2886
|
const formId = action.form;
|
|
3194
2887
|
if (stateRef.current[`${formId}.$submitting`] === true) break;
|
|
2888
|
+
const pendingKey = typeof action.pendingKey === "string" && action.pendingKey !== "" ? action.pendingKey : null;
|
|
3195
2889
|
void (async () => {
|
|
3196
2890
|
setEntries({ [`${formId}.$submitting`]: true });
|
|
3197
2891
|
let phase = "validate";
|
|
2892
|
+
let pendingOn = false;
|
|
3198
2893
|
try {
|
|
3199
2894
|
const prefix = `${formId}.`;
|
|
3200
2895
|
const values = {};
|
|
@@ -3210,23 +2905,36 @@ function SduiRenderer({
|
|
|
3210
2905
|
}
|
|
3211
2906
|
setEntries({ [`${formId}.$errors`]: {}, [`${formId}.$submitting`]: true });
|
|
3212
2907
|
phase = "submit";
|
|
2908
|
+
if (pendingKey) {
|
|
2909
|
+
pendingOn = true;
|
|
2910
|
+
setEntries({ [pendingKey]: true });
|
|
2911
|
+
}
|
|
3213
2912
|
const response = await hostRef.current.fetch(action.endpoint, {
|
|
3214
2913
|
method: action.method ?? "POST",
|
|
3215
2914
|
body: values
|
|
3216
2915
|
});
|
|
3217
2916
|
setEntries({ [`${formId}.$submitting`]: false, [`${formId}.$resetDirty`]: true });
|
|
3218
|
-
(
|
|
2917
|
+
runSuccessChain("submitForm", formId, () => {
|
|
2918
|
+
(action.onSuccess ?? []).forEach((a) => dispatchRef.current(a, response, scope));
|
|
2919
|
+
});
|
|
3219
2920
|
} catch (error) {
|
|
3220
2921
|
setEntries({ [`${formId}.$submitting`]: false });
|
|
3221
|
-
const
|
|
2922
|
+
const failure = failureDetailOf(error);
|
|
3222
2923
|
if (phase === "validate") {
|
|
3223
|
-
|
|
3224
|
-
pushToast({ tone: "danger", title: "\uAC80\uC99D \uADDC\uCE59 \uC624\uB958", message });
|
|
3225
|
-
} else if (action.onError?.length) {
|
|
3226
|
-
action.onError.forEach((a) => dispatchRef.current(a, message, scope));
|
|
2924
|
+
warnOnce(`validate:${formId}`, `[sdui] ${formId} \uAC80\uC99D \uADDC\uCE59 \uC624\uB958: ${failure.message}`);
|
|
2925
|
+
pushToast({ tone: "danger", title: "\uAC80\uC99D \uADDC\uCE59 \uC624\uB958", message: failure.message });
|
|
3227
2926
|
} else {
|
|
3228
|
-
|
|
2927
|
+
if (typeof action.errorKey === "string" && action.errorKey !== "") {
|
|
2928
|
+
setEntries({ [action.errorKey]: failure });
|
|
2929
|
+
}
|
|
2930
|
+
if (action.onError?.length) {
|
|
2931
|
+
action.onError.forEach((a) => dispatchRef.current(a, failure.message, scope));
|
|
2932
|
+
} else {
|
|
2933
|
+
pushToast({ tone: "danger", title: "\uC800\uC7A5 \uC2E4\uD328", message: failure.message });
|
|
2934
|
+
}
|
|
3229
2935
|
}
|
|
2936
|
+
} finally {
|
|
2937
|
+
if (pendingKey && pendingOn) setEntries({ [pendingKey]: false });
|
|
3230
2938
|
}
|
|
3231
2939
|
})();
|
|
3232
2940
|
break;
|
|
@@ -3243,18 +2951,12 @@ function SduiRenderer({
|
|
|
3243
2951
|
case "invalidate": {
|
|
3244
2952
|
hostRef.current.invalidate?.(action.prefix, action.exact ?? false);
|
|
3245
2953
|
const prefix = typeof action.prefix === "string" ? action.prefix : "";
|
|
3246
|
-
|
|
3247
|
-
if (hit.length > 0) {
|
|
3248
|
-
setDataEpochs((prev) => {
|
|
3249
|
-
const next = { ...prev };
|
|
3250
|
-
for (const key of hit) next[key] = (next[key] ?? 0) + 1;
|
|
3251
|
-
return next;
|
|
3252
|
-
});
|
|
3253
|
-
}
|
|
2954
|
+
invalidatePageDataRef.current(prefix, action.exact ?? false);
|
|
3254
2955
|
break;
|
|
3255
2956
|
}
|
|
3256
2957
|
case "refresh":
|
|
3257
2958
|
hostRef.current.refresh?.();
|
|
2959
|
+
invalidatePageDataRef.current("", false);
|
|
3258
2960
|
break;
|
|
3259
2961
|
case "download": {
|
|
3260
2962
|
void (async () => {
|
|
@@ -3325,4 +3027,61 @@ function SduiRenderer({
|
|
|
3325
3027
|
);
|
|
3326
3028
|
}
|
|
3327
3029
|
|
|
3328
|
-
|
|
3030
|
+
// src/spec-resolve.ts
|
|
3031
|
+
function isValidSpecId(id) {
|
|
3032
|
+
return id.length > 0 && id.split("/").every(validSegment);
|
|
3033
|
+
}
|
|
3034
|
+
function validSegment(segment) {
|
|
3035
|
+
if (segment.startsWith("{") && segment.endsWith("}")) {
|
|
3036
|
+
return /^\{[A-Za-z0-9_]+\}$/.test(segment);
|
|
3037
|
+
}
|
|
3038
|
+
return /^[A-Za-z0-9_-]+$/.test(segment);
|
|
3039
|
+
}
|
|
3040
|
+
function patternSpecCandidates(id) {
|
|
3041
|
+
const segments = id.split("/");
|
|
3042
|
+
if (segments.length < 2) return [];
|
|
3043
|
+
const candidates = [];
|
|
3044
|
+
const last = segments.length - 1;
|
|
3045
|
+
candidates.push({
|
|
3046
|
+
specId: [...segments.slice(0, last), "{id}"].join("/"),
|
|
3047
|
+
params: { id: segments[last] }
|
|
3048
|
+
});
|
|
3049
|
+
if (segments.length >= 3) {
|
|
3050
|
+
const inner = segments.length - 2;
|
|
3051
|
+
candidates.push({
|
|
3052
|
+
specId: segments.map((s, i) => i === inner ? "{id}" : s).join("/"),
|
|
3053
|
+
params: { id: segments[inner] }
|
|
3054
|
+
});
|
|
3055
|
+
candidates.push({
|
|
3056
|
+
specId: segments.map((s, i) => i === inner ? "{id}" : i === last ? "{sub}" : s).join("/"),
|
|
3057
|
+
params: { id: segments[inner], sub: segments[last] }
|
|
3058
|
+
});
|
|
3059
|
+
}
|
|
3060
|
+
if (segments.length >= 4) {
|
|
3061
|
+
const outer = segments.length - 3;
|
|
3062
|
+
candidates.push({
|
|
3063
|
+
specId: segments.map((s, i) => i === outer ? "{id}" : i === last ? "{sub}" : s).join("/"),
|
|
3064
|
+
params: { id: segments[outer], sub: segments[last] }
|
|
3065
|
+
});
|
|
3066
|
+
}
|
|
3067
|
+
const seen = /* @__PURE__ */ new Set([id]);
|
|
3068
|
+
return candidates.filter((candidate) => {
|
|
3069
|
+
if (seen.has(candidate.specId)) return false;
|
|
3070
|
+
seen.add(candidate.specId);
|
|
3071
|
+
return true;
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
async function resolvePatternSpec(id, load) {
|
|
3075
|
+
const exact = await load(id);
|
|
3076
|
+
if (exact != null) return { spec: exact, specId: id, params: {} };
|
|
3077
|
+
for (const candidate of patternSpecCandidates(id)) {
|
|
3078
|
+
const spec = await load(candidate.specId);
|
|
3079
|
+
if (spec != null) return { spec, specId: candidate.specId, params: candidate.params };
|
|
3080
|
+
}
|
|
3081
|
+
return null;
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// src/icon-names.ts
|
|
3085
|
+
var ICON_NAMES = Object.keys(ICON_NODES);
|
|
3086
|
+
|
|
3087
|
+
export { Can, ICON_NAMES, NodeErrorBoundary, RemoteSearchResults, ResultTable, SduiForm, SduiRenderer, SduiSearchDataProvider, SduiSearchInvalidationProvider, SduiSearchResults, SearchResults, SortHandle, SortableList, cellRenderer, createRegistry, createSearchInvalidator, defaultRegistry, imageRenderer, isValidSpecId, mapActions, mapCardRows, mapColumns, mapGridCard, mapViews, matchesCondition, normalizePageSizes, patternSpecCandidates, resolvePatternSpec, sortableDropIndex, sortableMove, useRemoteSearchPage, useSduiRuntime, useSearchFetcher, validateSpec, withProps };
|