@wipco/sdui 0.9.1 → 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 +58 -41
- 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;
|
|
@@ -2458,12 +2468,18 @@ function SduiRenderer({
|
|
|
2458
2468
|
if (runnable.length === 0) return;
|
|
2459
2469
|
const controller = new AbortController();
|
|
2460
2470
|
let cancelled = false;
|
|
2471
|
+
const timers = [];
|
|
2461
2472
|
setEntries(
|
|
2462
2473
|
Object.fromEntries(runnable.map((plan) => [`${plan.key}.$loading`, true]))
|
|
2463
2474
|
);
|
|
2475
|
+
const afterDebounce = (plan) => plan.debounceMs === void 0 ? Promise.resolve() : new Promise((resolveWait) => {
|
|
2476
|
+
timers.push(setTimeout(resolveWait, plan.debounceMs));
|
|
2477
|
+
});
|
|
2464
2478
|
void Promise.all(
|
|
2465
2479
|
runnable.map(async (plan) => {
|
|
2466
2480
|
try {
|
|
2481
|
+
await afterDebounce(plan);
|
|
2482
|
+
if (cancelled) return;
|
|
2467
2483
|
const response = await hostRef.current.fetch(plan.endpoint, {
|
|
2468
2484
|
method: "GET",
|
|
2469
2485
|
signal: controller.signal
|
|
@@ -2496,6 +2512,7 @@ function SduiRenderer({
|
|
|
2496
2512
|
return () => {
|
|
2497
2513
|
cancelled = true;
|
|
2498
2514
|
controller.abort();
|
|
2515
|
+
timers.forEach(clearTimeout);
|
|
2499
2516
|
};
|
|
2500
2517
|
}, [dataPlanKey, dataEpochKey, setEntries]);
|
|
2501
2518
|
const dispatchRef = useRef(() => {
|
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';
|