@wipco/sdui 0.9.0 → 0.10.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-JONNWN6N.js → chunk-FDVI3PWQ.js} +6 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +66 -43
- package/dist/meta.d.ts +1 -1
- package/dist/meta.js +9 -0
- package/dist/{types-Dl4rPyan.d.ts → types-DO8_hC6A.d.ts} +6 -0
- package/dist/widgets/JsonField.d.ts +7 -0
- package/dist/widgets/JsonField.js +1 -1
- package/dist/widgets/index.js +2 -2
- package/package.json +1 -1
|
@@ -45,6 +45,7 @@ function describeParseError(err, text) {
|
|
|
45
45
|
function JsonFieldImpl({
|
|
46
46
|
value,
|
|
47
47
|
onChange,
|
|
48
|
+
emptyAsNull = false,
|
|
48
49
|
rows = 6,
|
|
49
50
|
invalid = false,
|
|
50
51
|
disabled = false,
|
|
@@ -67,6 +68,10 @@ function JsonFieldImpl({
|
|
|
67
68
|
focusedRef.current = false;
|
|
68
69
|
if (text.trim() === "") {
|
|
69
70
|
setParseError(null);
|
|
71
|
+
if (emptyAsNull) {
|
|
72
|
+
lastFormattedRef.current = "";
|
|
73
|
+
onChange?.(null);
|
|
74
|
+
}
|
|
70
75
|
return;
|
|
71
76
|
}
|
|
72
77
|
try {
|
|
@@ -79,7 +84,7 @@ function JsonFieldImpl({
|
|
|
79
84
|
} catch (err) {
|
|
80
85
|
setParseError(describeParseError(err, text));
|
|
81
86
|
}
|
|
82
|
-
}, [text, onChange]);
|
|
87
|
+
}, [text, onChange, emptyAsNull]);
|
|
83
88
|
return /* @__PURE__ */ jsxs(
|
|
84
89
|
"div",
|
|
85
90
|
{
|
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 SduiAction, a as SduiPropValue, b as SduiRule, c as SduiPage } from './types-
|
|
4
|
-
export { d as SduiCalcRef, e as SduiChild, f as SduiIndexRef, g as SduiItemRef, h as SduiNode, i as SduiPageQuery, j as SduiScalar, k as SduiStateRef } from './types-
|
|
3
|
+
import { S as SduiAction, a as SduiPropValue, b as SduiRule, c as SduiPage } from './types-DO8_hC6A.js';
|
|
4
|
+
export { d as SduiCalcRef, e as SduiChild, f as SduiIndexRef, g as SduiItemRef, h as SduiNode, i as SduiPageQuery, j as SduiScalar, k as SduiStateRef } from './types-DO8_hC6A.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 } from './DatePicker-BndreCPl.js';
|
package/dist/index.js
CHANGED
|
@@ -688,47 +688,52 @@ function SearchResults({
|
|
|
688
688
|
}
|
|
689
689
|
return entries;
|
|
690
690
|
}, [leftFilters, rightFilters, searchParams]);
|
|
691
|
+
const hasAllRows = item.data.length >= item.total;
|
|
691
692
|
const processed = useMemo(() => {
|
|
692
693
|
const cell = (row, key) => row[key];
|
|
693
694
|
let rows = item.data;
|
|
694
|
-
if (
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
695
|
+
if (hasAllRows) {
|
|
696
|
+
if (appliedSearch) {
|
|
697
|
+
rows = rows.filter(
|
|
698
|
+
(row) => includesLooseSearch(
|
|
699
|
+
Object.values(row).filter((v) => typeof v === "string" || typeof v === "number").join(" "),
|
|
700
|
+
appliedSearch
|
|
701
|
+
)
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
for (const { key, values } of activeFilters) {
|
|
705
|
+
rows = rows.filter((row) => values.includes(String(cell(row, key))));
|
|
706
|
+
}
|
|
707
|
+
for (const field of advancedSearchFields) {
|
|
708
|
+
if (field.type === "select") {
|
|
709
|
+
const wanted = splitMultiValue(advancedValues[field.key] ?? "");
|
|
710
|
+
if (wanted.length > 0) rows = rows.filter((row) => wanted.includes(String(cell(row, field.key))));
|
|
711
|
+
} else if (field.type === "cascading") {
|
|
712
|
+
const wanted = splitMultiValue(advancedValues[field.key] ?? "");
|
|
713
|
+
if (wanted.length > 0) {
|
|
714
|
+
const lineages = cascadingValuePaths(field.options);
|
|
715
|
+
rows = rows.filter((row) => {
|
|
716
|
+
const own = String(cell(row, field.key));
|
|
717
|
+
const lineage = lineages.get(own) ?? [own];
|
|
718
|
+
return lineage.some((value) => wanted.includes(value));
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
} else if (field.type === "dateRange") {
|
|
722
|
+
const from = advancedValues[field.fromKey ?? `${field.key}From`];
|
|
723
|
+
const to = advancedValues[field.toKey ?? `${field.key}To`];
|
|
724
|
+
const datePart = (v) => String(v ?? "").slice(0, 10);
|
|
725
|
+
if (from) rows = rows.filter((row) => datePart(cell(row, field.key)) >= from.slice(0, 10));
|
|
726
|
+
if (to) rows = rows.filter((row) => datePart(cell(row, field.key)) <= to.slice(0, 10));
|
|
727
|
+
} else if (field.type === "numberRange") {
|
|
728
|
+
const min = numericValue(advancedValues[field.fromKey ?? `${field.key}Min`]);
|
|
729
|
+
const max = numericValue(advancedValues[field.toKey ?? `${field.key}Max`]);
|
|
730
|
+
if (min !== null) rows = rows.filter((row) => (numericValue(cell(row, field.key)) ?? -Infinity) >= min);
|
|
731
|
+
if (max !== null) rows = rows.filter((row) => (numericValue(cell(row, field.key)) ?? Infinity) <= max);
|
|
732
|
+
} else {
|
|
733
|
+
const keyword = advancedValues[field.key];
|
|
734
|
+
const baseKey = field.key.endsWith("Contains") ? field.key.slice(0, -"Contains".length) : field.key;
|
|
735
|
+
if (keyword) rows = rows.filter((row) => includesLooseSearch(String(cell(row, baseKey) ?? ""), keyword));
|
|
718
736
|
}
|
|
719
|
-
} else if (field.type === "dateRange") {
|
|
720
|
-
const from = advancedValues[field.fromKey ?? `${field.key}From`];
|
|
721
|
-
const to = advancedValues[field.toKey ?? `${field.key}To`];
|
|
722
|
-
if (from) rows = rows.filter((row) => String(cell(row, field.key) ?? "") >= from);
|
|
723
|
-
if (to) rows = rows.filter((row) => String(cell(row, field.key) ?? "") <= to);
|
|
724
|
-
} else if (field.type === "numberRange") {
|
|
725
|
-
const min = numericValue(advancedValues[field.fromKey ?? `${field.key}Min`]);
|
|
726
|
-
const max = numericValue(advancedValues[field.toKey ?? `${field.key}Max`]);
|
|
727
|
-
if (min !== null) rows = rows.filter((row) => (numericValue(cell(row, field.key)) ?? -Infinity) >= min);
|
|
728
|
-
if (max !== null) rows = rows.filter((row) => (numericValue(cell(row, field.key)) ?? Infinity) <= max);
|
|
729
|
-
} else {
|
|
730
|
-
const keyword = advancedValues[field.key];
|
|
731
|
-
if (keyword) rows = rows.filter((row) => includesLooseSearch(String(cell(row, field.key) ?? ""), keyword));
|
|
732
737
|
}
|
|
733
738
|
}
|
|
734
739
|
if (sort) {
|
|
@@ -746,8 +751,8 @@ function SearchResults({
|
|
|
746
751
|
});
|
|
747
752
|
}
|
|
748
753
|
return rows;
|
|
749
|
-
}, [item.data, appliedSearch, activeFilters, advancedSearchFields, advancedValues, sort, columns]);
|
|
750
|
-
const clientPaginated =
|
|
754
|
+
}, [item.data, hasAllRows, appliedSearch, activeFilters, advancedSearchFields, advancedValues, sort, columns]);
|
|
755
|
+
const clientPaginated = hasAllRows;
|
|
751
756
|
const totalCount = clientPaginated ? processed.length : item.total;
|
|
752
757
|
const pageCount = Math.max(1, Math.ceil(totalCount / pageSize));
|
|
753
758
|
const safePage = Math.min(Math.max(1, page), pageCount);
|
|
@@ -1808,8 +1813,11 @@ function validatePageData(data, issues) {
|
|
|
1808
1813
|
if (raw.select !== void 0 && typeof raw.select !== "string") {
|
|
1809
1814
|
issues.push({ path: `${path}.select`, message: "select\uB294 \uBB38\uC790\uC5F4 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1810
1815
|
}
|
|
1816
|
+
if (raw.debounceMs !== void 0 && typeof raw.debounceMs !== "number") {
|
|
1817
|
+
issues.push({ path: `${path}.debounceMs`, message: "debounceMs\uB294 \uC22B\uC790(ms)\uC5EC\uC57C \uD569\uB2C8\uB2E4" });
|
|
1818
|
+
}
|
|
1811
1819
|
for (const [k, v] of Object.entries(raw)) {
|
|
1812
|
-
if (k === "endpoint" || k === "select") continue;
|
|
1820
|
+
if (k === "endpoint" || k === "select" || k === "debounceMs") continue;
|
|
1813
1821
|
if (k !== "when" && k !== "fallback") {
|
|
1814
1822
|
issues.push({ path: `${path}.${k}`, message: `\uC54C \uC218 \uC5C6\uB294 \uD56D\uBAA9: ${k}` });
|
|
1815
1823
|
continue;
|
|
@@ -1960,12 +1968,14 @@ function planPageData(data, resolve) {
|
|
|
1960
1968
|
const resolved = resolve(query.endpoint);
|
|
1961
1969
|
const endpoint = typeof resolved === "string" ? resolved.trim() : "";
|
|
1962
1970
|
const enabled = "when" in query ? Boolean(resolve(query.when)) : true;
|
|
1971
|
+
const debounceMs = typeof query.debounceMs === "number" && Number.isFinite(query.debounceMs) && query.debounceMs > 0 ? query.debounceMs : void 0;
|
|
1963
1972
|
plans.push({
|
|
1964
1973
|
key,
|
|
1965
1974
|
endpoint,
|
|
1966
1975
|
enabled,
|
|
1967
1976
|
select: typeof query.select === "string" ? query.select : void 0,
|
|
1968
|
-
fallback: query.fallback
|
|
1977
|
+
fallback: query.fallback,
|
|
1978
|
+
debounceMs
|
|
1969
1979
|
});
|
|
1970
1980
|
}
|
|
1971
1981
|
return plans;
|
|
@@ -2185,6 +2195,8 @@ function SduiNodeRenderer({
|
|
|
2185
2195
|
props[name] = (arg) => actions.forEach(
|
|
2186
2196
|
(a) => dispatch(resolveValue(a, scope), arg)
|
|
2187
2197
|
);
|
|
2198
|
+
} else if (name === "rowClick" && (isAction(raw) || isActionList(raw))) {
|
|
2199
|
+
props[name] = raw;
|
|
2188
2200
|
} else {
|
|
2189
2201
|
let resolved = resolveValue(raw, scope);
|
|
2190
2202
|
if (resolved === null) {
|
|
@@ -2456,12 +2468,18 @@ function SduiRenderer({
|
|
|
2456
2468
|
if (runnable.length === 0) return;
|
|
2457
2469
|
const controller = new AbortController();
|
|
2458
2470
|
let cancelled = false;
|
|
2471
|
+
const timers = [];
|
|
2459
2472
|
setEntries(
|
|
2460
2473
|
Object.fromEntries(runnable.map((plan) => [`${plan.key}.$loading`, true]))
|
|
2461
2474
|
);
|
|
2475
|
+
const afterDebounce = (plan) => plan.debounceMs === void 0 ? Promise.resolve() : new Promise((resolveWait) => {
|
|
2476
|
+
timers.push(setTimeout(resolveWait, plan.debounceMs));
|
|
2477
|
+
});
|
|
2462
2478
|
void Promise.all(
|
|
2463
2479
|
runnable.map(async (plan) => {
|
|
2464
2480
|
try {
|
|
2481
|
+
await afterDebounce(plan);
|
|
2482
|
+
if (cancelled) return;
|
|
2465
2483
|
const response = await hostRef.current.fetch(plan.endpoint, {
|
|
2466
2484
|
method: "GET",
|
|
2467
2485
|
signal: controller.signal
|
|
@@ -2494,12 +2512,17 @@ function SduiRenderer({
|
|
|
2494
2512
|
return () => {
|
|
2495
2513
|
cancelled = true;
|
|
2496
2514
|
controller.abort();
|
|
2515
|
+
timers.forEach(clearTimeout);
|
|
2497
2516
|
};
|
|
2498
2517
|
}, [dataPlanKey, dataEpochKey, setEntries]);
|
|
2499
2518
|
const dispatchRef = useRef(() => {
|
|
2500
2519
|
});
|
|
2501
2520
|
const dispatch = useCallback(
|
|
2502
|
-
(
|
|
2521
|
+
(rawAction, arg) => {
|
|
2522
|
+
const action = resolveValue(
|
|
2523
|
+
rawAction,
|
|
2524
|
+
EMPTY_SCOPE
|
|
2525
|
+
);
|
|
2503
2526
|
switch (action.action) {
|
|
2504
2527
|
case "navigate":
|
|
2505
2528
|
if (!isSafeNavigationTarget(action.to)) {
|
|
@@ -2668,7 +2691,7 @@ function SduiRenderer({
|
|
|
2668
2691
|
break;
|
|
2669
2692
|
}
|
|
2670
2693
|
},
|
|
2671
|
-
[pushToast, onCustomAction, customValidators, setEntries]
|
|
2694
|
+
[pushToast, onCustomAction, customValidators, setEntries, resolveValue]
|
|
2672
2695
|
);
|
|
2673
2696
|
dispatchRef.current = dispatch;
|
|
2674
2697
|
const engine = useMemo(
|
package/dist/meta.d.ts
CHANGED
package/dist/meta.js
CHANGED
|
@@ -3308,6 +3308,15 @@ var JsonFieldMeta = {
|
|
|
3308
3308
|
{ name: "rows", kind: "number", label: "\uC904 \uC218", default: 6, group: "appearance" },
|
|
3309
3309
|
{ name: "placeholder", kind: "string", label: "\uC548\uB0B4 \uBB38\uAD6C", default: "JSON\uC744 \uC785\uB825\uD558\uC138\uC694", group: "content" },
|
|
3310
3310
|
{ name: "invalid", kind: "boolean", label: "\uC624\uB958 \uD45C\uC2DC", default: false, bindable: true, group: "validation" },
|
|
3311
|
+
{
|
|
3312
|
+
name: "emptyAsNull",
|
|
3313
|
+
kind: "boolean",
|
|
3314
|
+
label: "\uBE44\uC6B0\uBA74 null \uC800\uC7A5",
|
|
3315
|
+
default: false,
|
|
3316
|
+
description: "\uBE48 \uD14D\uC2A4\uD2B8\uB85C blur \uD558\uBA74 onChange(null) \u2014 \uC9C0\uC6CC\uC11C \uAC12\uC744 \uC0AD\uC81C\uD558\uB294 \uD3FC\uC5D0\uC11C \uCF20\uB2E4.",
|
|
3317
|
+
group: "behavior",
|
|
3318
|
+
advanced: true
|
|
3319
|
+
},
|
|
3311
3320
|
{ name: "disabled", kind: "boolean", label: "\uBE44\uD65C\uC131", default: false, bindable: true, group: "behavior" }
|
|
3312
3321
|
],
|
|
3313
3322
|
children: { accepts: false },
|
|
@@ -216,6 +216,12 @@ interface SduiPageQuery {
|
|
|
216
216
|
when?: SduiPropValue;
|
|
217
217
|
/** 실패했거나 아직 조회 전일 때 상태에 들어갈 값. 생략하면 `undefined`. */
|
|
218
218
|
fallback?: SduiPropValue;
|
|
219
|
+
/**
|
|
220
|
+
* 경로가 바뀐 뒤 이 시간(ms)만큼 조용해야 조회를 낸다. 경로가 입력값을 참조하는
|
|
221
|
+
* 조회(관리번호 실시간 검증 등)가 키 입력마다 GET 을 내는 것을 막는 자리다 —
|
|
222
|
+
* v2 는 400ms 를 썼다. 생략하면 즉시.
|
|
223
|
+
*/
|
|
224
|
+
debounceMs?: number;
|
|
219
225
|
}
|
|
220
226
|
interface SduiPage {
|
|
221
227
|
version: 1;
|
|
@@ -14,6 +14,13 @@ interface JsonFieldProps {
|
|
|
14
14
|
/** 객체 또는 JSON 문자열 */
|
|
15
15
|
value?: unknown;
|
|
16
16
|
onChange?: (parsed: unknown) => void;
|
|
17
|
+
/**
|
|
18
|
+
* 빈 텍스트로 blur 하면 onChange(null) 을 낸다 — "지워서 값 삭제"용.
|
|
19
|
+
* 기본은 종전대로 무동작이다: 빈 칸이 곧 null 저장이 되면, 잠깐 지우고 다시 쓰려던
|
|
20
|
+
* 사용자의 미완성 상태가 그대로 저장 후보가 되기 때문에 옵트인으로 둔다.
|
|
21
|
+
* (ERP 유닛 수정의 '옵션 비우기 (null 저장)' 버튼이 이 옵션으로 대체된다.)
|
|
22
|
+
*/
|
|
23
|
+
emptyAsNull?: boolean;
|
|
17
24
|
/** 기본 6 */
|
|
18
25
|
rows?: number;
|
|
19
26
|
invalid?: boolean;
|
package/dist/widgets/index.js
CHANGED
|
@@ -22,8 +22,8 @@ import { ImageLightbox } from '../chunk-3PYMVHL3.js';
|
|
|
22
22
|
export { ImageLightbox } from '../chunk-3PYMVHL3.js';
|
|
23
23
|
import { InlineEdit } from '../chunk-7AHNAX6T.js';
|
|
24
24
|
export { InlineEdit } from '../chunk-7AHNAX6T.js';
|
|
25
|
-
import { JsonField } from '../chunk-
|
|
26
|
-
export { JsonField } from '../chunk-
|
|
25
|
+
import { JsonField } from '../chunk-FDVI3PWQ.js';
|
|
26
|
+
export { JsonField } from '../chunk-FDVI3PWQ.js';
|
|
27
27
|
import { LiveClock } from '../chunk-Z3WGIFJP.js';
|
|
28
28
|
export { LiveClock } from '../chunk-Z3WGIFJP.js';
|
|
29
29
|
import { OrgChart } from '../chunk-BT4HC3WR.js';
|