@wipco/sdui 0.30.0 → 0.32.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-CTSVZTIT.js +122 -0
- package/dist/chunk-KVFHPBAY.js +124 -0
- package/dist/chunk-QO6UJAQN.js +145 -0
- package/dist/chunk-RRMKCZYQ.js +67 -0
- package/dist/{chunk-WXOU7QTN.js → chunk-SNRKPEWT.js} +2 -63
- package/dist/chunk-TIY4A223.js +140 -0
- package/dist/index.js +15 -151
- package/dist/meta.d.ts +10 -2
- package/dist/meta.js +233 -1
- package/dist/rich/ExcelUploadCard.d.ts +37 -0
- package/dist/rich/ExcelUploadCard.js +606 -0
- package/dist/rich.js +2 -2
- package/dist/widgets/OptionSchemaEditor.d.ts +63 -0
- package/dist/widgets/OptionSchemaEditor.js +4 -0
- package/dist/widgets/OptionValueFields.d.ts +21 -0
- package/dist/widgets/OptionValueFields.js +4 -0
- package/dist/widgets/StagePeriodEditor.d.ts +45 -0
- package/dist/widgets/StagePeriodEditor.js +6 -0
- package/dist/widgets/index.d.ts +10 -1
- package/dist/widgets/index.js +28 -17
- package/package.json +17 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Text } from './chunk-QO6UJAQN.js';
|
|
2
|
+
import { Switch, Field, Select, Input } from './chunk-WIDDHR7L.js';
|
|
3
|
+
import { boxed } from './chunk-SAN4IX6N.js';
|
|
4
|
+
import React, { useMemo } from 'react';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function normalizeSchema(schema) {
|
|
8
|
+
if (!Array.isArray(schema)) return [];
|
|
9
|
+
return schema.filter((item) => Boolean(item) && typeof item === "object").filter((item) => typeof item.key === "string" && item.key.trim() !== "");
|
|
10
|
+
}
|
|
11
|
+
function OptionValueFieldsImpl({
|
|
12
|
+
schema,
|
|
13
|
+
value,
|
|
14
|
+
onChange,
|
|
15
|
+
disabled = false,
|
|
16
|
+
readOnly = false
|
|
17
|
+
}) {
|
|
18
|
+
const fields = useMemo(() => normalizeSchema(schema), [schema]);
|
|
19
|
+
const current = useMemo(
|
|
20
|
+
() => value && typeof value === "object" && !Array.isArray(value) ? value : {},
|
|
21
|
+
[value]
|
|
22
|
+
);
|
|
23
|
+
const strays = useMemo(() => {
|
|
24
|
+
const known = new Set(fields.map((field) => field.key));
|
|
25
|
+
return Object.entries(current).filter(([key]) => !known.has(key));
|
|
26
|
+
}, [fields, current]);
|
|
27
|
+
const set = (key, next) => {
|
|
28
|
+
const draft = { ...current };
|
|
29
|
+
if (next === "" || next === void 0 || next === null) delete draft[key];
|
|
30
|
+
else draft[key] = next;
|
|
31
|
+
onChange?.(draft);
|
|
32
|
+
};
|
|
33
|
+
if (readOnly) {
|
|
34
|
+
const filled = fields.filter((field) => {
|
|
35
|
+
const raw = current[field.key];
|
|
36
|
+
return raw !== void 0 && raw !== null && raw !== "";
|
|
37
|
+
});
|
|
38
|
+
if (fields.length === 0 || filled.length === 0) {
|
|
39
|
+
return /* @__PURE__ */ jsx(Text, { preset: "body-sm", style: { color: "var(--wp-text-muted)" }, value: "\u2014" });
|
|
40
|
+
}
|
|
41
|
+
return /* @__PURE__ */ jsx("div", { className: "wp-kv", children: filled.map((field) => {
|
|
42
|
+
const raw = current[field.key];
|
|
43
|
+
const shown = field.type === "boolean" ? raw === true ? "\uC608" : "\uC544\uB2C8\uC624" : `${String(raw)}${field.unit ? ` ${field.unit}` : ""}`;
|
|
44
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
45
|
+
/* @__PURE__ */ jsx("span", { className: "wp-kv__k", children: field.label?.trim() || field.key }),
|
|
46
|
+
/* @__PURE__ */ jsx("span", { className: "wp-kv__v", children: shown })
|
|
47
|
+
] }, field.key);
|
|
48
|
+
}) });
|
|
49
|
+
}
|
|
50
|
+
if (fields.length === 0) {
|
|
51
|
+
return /* @__PURE__ */ jsx(Text, { preset: "body-sm", style: { color: "var(--wp-text-muted)" }, children: "\uC774 \uC7A5\uBE44\uC5D0 \uC815\uD574 \uB454 \uC635\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC7A5\uBE44 \uC0C1\uC138\uC758 \uC218\uC815 \u2192 \uC635\uC158 \uD0ED\uC5D0\uC11C \uBA3C\uC800 \uC11C\uC2DD\uC744 \uC815\uD558\uC138\uC694." });
|
|
52
|
+
}
|
|
53
|
+
return /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-3)" }, children: [
|
|
54
|
+
fields.map((field) => {
|
|
55
|
+
const label = field.label?.trim() || field.key;
|
|
56
|
+
const raw = current[field.key];
|
|
57
|
+
if (field.type === "boolean") {
|
|
58
|
+
return /* @__PURE__ */ jsxs("div", { className: "wp-row", style: { alignItems: "center", gap: "var(--wp-space-3)" }, children: [
|
|
59
|
+
/* @__PURE__ */ jsx(Text, { preset: "body-sm", style: { flex: 1 }, value: label }),
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
Switch,
|
|
62
|
+
{
|
|
63
|
+
checked: raw === true,
|
|
64
|
+
disabled,
|
|
65
|
+
onChange: (event) => set(field.key, event.target.checked ? true : void 0)
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
] }, field.key);
|
|
69
|
+
}
|
|
70
|
+
if (field.type === "select") {
|
|
71
|
+
return /* @__PURE__ */ jsx(Field, { label, children: /* @__PURE__ */ jsx(
|
|
72
|
+
Select,
|
|
73
|
+
{
|
|
74
|
+
value: raw === void 0 || raw === null ? "" : String(raw),
|
|
75
|
+
disabled,
|
|
76
|
+
placeholder: "\uC120\uD0DD \uC548 \uD568",
|
|
77
|
+
options: (field.options ?? []).map((option) => ({ value: option, label: option })),
|
|
78
|
+
onChange: (event) => set(field.key, event.target.value)
|
|
79
|
+
}
|
|
80
|
+
) }, field.key);
|
|
81
|
+
}
|
|
82
|
+
if (field.type === "number") {
|
|
83
|
+
return /* @__PURE__ */ jsx(Field, { label: field.unit ? `${label} (${field.unit})` : label, children: /* @__PURE__ */ jsx(
|
|
84
|
+
Input,
|
|
85
|
+
{
|
|
86
|
+
type: "number",
|
|
87
|
+
value: raw === void 0 || raw === null ? "" : String(raw),
|
|
88
|
+
disabled,
|
|
89
|
+
onChange: (event) => {
|
|
90
|
+
const text = event.target.value;
|
|
91
|
+
if (text === "") return set(field.key, void 0);
|
|
92
|
+
const parsed = Number(text);
|
|
93
|
+
set(field.key, Number.isFinite(parsed) ? parsed : text);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
) }, field.key);
|
|
97
|
+
}
|
|
98
|
+
return /* @__PURE__ */ jsx(Field, { label, children: /* @__PURE__ */ jsx(
|
|
99
|
+
Input,
|
|
100
|
+
{
|
|
101
|
+
value: raw === void 0 || raw === null ? "" : String(raw),
|
|
102
|
+
disabled,
|
|
103
|
+
onChange: (event) => set(field.key, event.target.value)
|
|
104
|
+
}
|
|
105
|
+
) }, field.key);
|
|
106
|
+
}),
|
|
107
|
+
strays.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-1)" }, children: [
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
Text,
|
|
110
|
+
{
|
|
111
|
+
preset: "caption",
|
|
112
|
+
value: `\uC11C\uC2DD \uBC16 \uAC12 ${strays.length}\uAC1C \u2014 \uADF8\uB300\uB85C \uBCF4\uAD00\uB429\uB2C8\uB2E4`,
|
|
113
|
+
style: { color: "var(--wp-text-muted)" }
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
strays.map(([key, strayValue]) => /* @__PURE__ */ jsx(Text, { preset: "caption", value: `${key}: ${JSON.stringify(strayValue)}` }, key))
|
|
117
|
+
] }) : null
|
|
118
|
+
] });
|
|
119
|
+
}
|
|
120
|
+
var OptionValueFields = boxed(OptionValueFieldsImpl, "OptionValueFields");
|
|
121
|
+
|
|
122
|
+
export { OptionValueFields };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { TimePicker } from './chunk-SNRKPEWT.js';
|
|
2
|
+
import { DatePicker } from './chunk-EFMKWPZ3.js';
|
|
3
|
+
import { Field } from './chunk-WIDDHR7L.js';
|
|
4
|
+
import { boxed } from './chunk-SAN4IX6N.js';
|
|
5
|
+
import { useState, useRef, useEffect, useMemo } from 'react';
|
|
6
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function toHm(value) {
|
|
9
|
+
if (typeof value !== "string") return "";
|
|
10
|
+
const trimmed = value.trim();
|
|
11
|
+
return /^\d{2}:\d{2}/.test(trimmed) ? trimmed.slice(0, 5) : trimmed;
|
|
12
|
+
}
|
|
13
|
+
function toYmd(value) {
|
|
14
|
+
if (typeof value !== "string") return "";
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
return /^\d{4}-\d{2}-\d{2}/.test(trimmed) ? trimmed.slice(0, 10) : trimmed;
|
|
17
|
+
}
|
|
18
|
+
function formFromValue(value) {
|
|
19
|
+
const record = value && typeof value === "object" ? value : {};
|
|
20
|
+
const startDate = toYmd(record.startDate);
|
|
21
|
+
const endDate = toYmd(record.endDate);
|
|
22
|
+
return {
|
|
23
|
+
startDate,
|
|
24
|
+
endDate,
|
|
25
|
+
// 시간은 날짜가 있어야 의미가 있다 — 저장분이 어긋나 있어도 폼에서 정합화.
|
|
26
|
+
startTime: startDate ? toHm(record.startTime) : "",
|
|
27
|
+
endTime: endDate ? toHm(record.endTime) : ""
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function periodError(form) {
|
|
31
|
+
if (form.startDate && form.endDate && form.endDate < form.startDate) {
|
|
32
|
+
return "\uD3C9\uAC00 \uC885\uB8CC\uC77C\uC740 \uC2DC\uC791\uC77C\uBCF4\uB2E4 \uBE60\uB97C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
33
|
+
}
|
|
34
|
+
if (form.startDate && form.endDate && form.startDate === form.endDate && form.startTime && form.endTime && form.endTime <= form.startTime) {
|
|
35
|
+
return "\uC885\uB8CC \uC2DC\uAC04\uC740 \uC2DC\uC791 \uC2DC\uAC04\uBCF4\uB2E4 \uB2A6\uC5B4\uC57C \uD569\uB2C8\uB2E4.";
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function envelopeFrom(form) {
|
|
40
|
+
return {
|
|
41
|
+
startDate: form.startDate || null,
|
|
42
|
+
endDate: form.endDate || null,
|
|
43
|
+
startTime: form.startDate && form.startTime || null,
|
|
44
|
+
endTime: form.endDate && form.endTime || null,
|
|
45
|
+
error: periodError(form)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function StagePeriodEditorImpl({ value, onChange, resetKey, disabled = false }) {
|
|
49
|
+
const [form, setForm] = useState(() => formFromValue(value));
|
|
50
|
+
const seededFor = useRef(resetKey);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (seededFor.current === resetKey) return;
|
|
53
|
+
seededFor.current = resetKey;
|
|
54
|
+
setForm(formFromValue(value));
|
|
55
|
+
}, [resetKey, value]);
|
|
56
|
+
const onChangeRef = useRef(onChange);
|
|
57
|
+
onChangeRef.current = onChange;
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
onChangeRef.current?.(envelopeFrom(form));
|
|
60
|
+
}, [form]);
|
|
61
|
+
const error = useMemo(() => periodError(form), [form]);
|
|
62
|
+
const patch = (partial) => setForm((previous) => ({ ...previous, ...partial }));
|
|
63
|
+
return /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-3)" }, children: [
|
|
64
|
+
/* @__PURE__ */ jsxs(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
style: {
|
|
68
|
+
display: "grid",
|
|
69
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
70
|
+
gap: "var(--wp-space-3)"
|
|
71
|
+
},
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx(Field, { label: "\uC2DC\uC791\uC77C", children: /* @__PURE__ */ jsx(
|
|
74
|
+
DatePicker,
|
|
75
|
+
{
|
|
76
|
+
value: form.startDate,
|
|
77
|
+
disabled,
|
|
78
|
+
max: form.endDate || void 0,
|
|
79
|
+
onChange: (next) => patch({
|
|
80
|
+
startDate: next,
|
|
81
|
+
// 시간은 날짜가 있어야 의미가 있으므로 날짜를 지우면 함께 비운다.
|
|
82
|
+
startTime: next ? form.startTime : ""
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
) }),
|
|
86
|
+
/* @__PURE__ */ jsx(Field, { label: "\uC2DC\uC791 \uC2DC\uAC04 (\uC120\uD0DD)", children: /* @__PURE__ */ jsx(
|
|
87
|
+
TimePicker,
|
|
88
|
+
{
|
|
89
|
+
value: form.startTime,
|
|
90
|
+
disabled: disabled || !form.startDate,
|
|
91
|
+
onChange: (next) => patch({ startTime: next })
|
|
92
|
+
}
|
|
93
|
+
) }),
|
|
94
|
+
/* @__PURE__ */ jsx(Field, { label: "\uC885\uB8CC\uC77C", children: /* @__PURE__ */ jsx(
|
|
95
|
+
DatePicker,
|
|
96
|
+
{
|
|
97
|
+
value: form.endDate,
|
|
98
|
+
disabled,
|
|
99
|
+
min: form.startDate || void 0,
|
|
100
|
+
onChange: (next) => patch({
|
|
101
|
+
endDate: next,
|
|
102
|
+
endTime: next ? form.endTime : ""
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
) }),
|
|
106
|
+
/* @__PURE__ */ jsx(Field, { label: "\uC885\uB8CC \uC2DC\uAC04 (\uC120\uD0DD)", children: /* @__PURE__ */ jsx(
|
|
107
|
+
TimePicker,
|
|
108
|
+
{
|
|
109
|
+
min: form.endDate && form.endDate === form.startDate ? form.startTime || void 0 : void 0,
|
|
110
|
+
value: form.endTime,
|
|
111
|
+
disabled: disabled || !form.endDate,
|
|
112
|
+
onChange: (next) => patch({ endTime: next })
|
|
113
|
+
}
|
|
114
|
+
) })
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
error ? /* @__PURE__ */ jsx("span", { className: "wp-caption", style: { color: "var(--wp-text-danger)" }, children: error }) : null,
|
|
119
|
+
/* @__PURE__ */ jsx("span", { className: "wp-caption", style: { color: "var(--wp-text-muted)" }, children: "\uB2E8\uACC4 \uAE30\uAC04\uC744 \uB530\uB85C \uC815\uD558\uC9C0 \uC54A\uC73C\uBA74 \uD68C\uCC28 \uAE30\uAC04\uC744 \uB530\uB985\uB2C8\uB2E4. \uD68C\uCC28 \uAE30\uAC04\uC744 \uBC14\uAFB8\uBA74 \uD68C\uCC28 \uAE30\uAC04\uC744 \uB530\uB974\uB358 \uB2E8\uACC4\uB9CC \uD568\uAED8 \uBC14\uB01D\uB2C8\uB2E4." })
|
|
120
|
+
] });
|
|
121
|
+
}
|
|
122
|
+
var StagePeriodEditor = boxed(StagePeriodEditorImpl, "StagePeriodEditor");
|
|
123
|
+
|
|
124
|
+
export { StagePeriodEditor };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { mergeStyle, containerLayoutStyle, joinClass, columnsClass, typographyStyle } from './chunk-SAN4IX6N.js';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function paddingStyle(padding) {
|
|
6
|
+
const value = padding === void 0 ? void 0 : PADDING_CSS[padding];
|
|
7
|
+
return value === void 0 ? {} : { padding: value };
|
|
8
|
+
}
|
|
9
|
+
var PADDING_CSS = {
|
|
10
|
+
"2": "2px",
|
|
11
|
+
"4": "4px",
|
|
12
|
+
"8": "8px",
|
|
13
|
+
"12": "12px",
|
|
14
|
+
"16": "16px",
|
|
15
|
+
"20": "20px",
|
|
16
|
+
"24": "24px",
|
|
17
|
+
"32": "32px"
|
|
18
|
+
};
|
|
19
|
+
function Grid({
|
|
20
|
+
columns = 3,
|
|
21
|
+
template,
|
|
22
|
+
gap,
|
|
23
|
+
align,
|
|
24
|
+
justify,
|
|
25
|
+
wrap,
|
|
26
|
+
padding,
|
|
27
|
+
className,
|
|
28
|
+
style,
|
|
29
|
+
children
|
|
30
|
+
}) {
|
|
31
|
+
return /* @__PURE__ */ jsx(
|
|
32
|
+
"div",
|
|
33
|
+
{
|
|
34
|
+
className: joinClass("wp-grid", !template && columnsClass(columns), className),
|
|
35
|
+
style: mergeStyle(
|
|
36
|
+
{
|
|
37
|
+
...template ? { gridTemplateColumns: template } : null,
|
|
38
|
+
...containerLayoutStyle({ gap, align, justify, wrap }),
|
|
39
|
+
...paddingStyle(padding)
|
|
40
|
+
},
|
|
41
|
+
style
|
|
42
|
+
),
|
|
43
|
+
children
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function Row({ justify, gap, align, wrap, className, style, children }) {
|
|
48
|
+
return /* @__PURE__ */ jsx(
|
|
49
|
+
"div",
|
|
50
|
+
{
|
|
51
|
+
className: joinClass("wp-row", justify === "between" && "wp-row--between", className),
|
|
52
|
+
style: mergeStyle(containerLayoutStyle({ gap, align, justify, wrap }), style),
|
|
53
|
+
children
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function Actions({ justify = "end", gap, align, wrap, className, style, children }) {
|
|
58
|
+
return /* @__PURE__ */ jsx(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
className: joinClass("wp-row", className),
|
|
62
|
+
style: mergeStyle({ flex: 1, ...containerLayoutStyle({ gap, align, justify, wrap }) }, style),
|
|
63
|
+
children
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
function Col({ gap, align, justify, wrap, className, style, children }) {
|
|
68
|
+
return /* @__PURE__ */ jsx("div", { className: joinClass("wp-col", className), style: mergeStyle(containerLayoutStyle({ gap, align, justify, wrap }), style), children });
|
|
69
|
+
}
|
|
70
|
+
var SPACER_STEPS = /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 6, 8]);
|
|
71
|
+
function Spacer({ size, className, style }) {
|
|
72
|
+
const step = size !== void 0 && SPACER_STEPS.has(Number(size)) ? Number(size) : void 0;
|
|
73
|
+
return /* @__PURE__ */ jsx(
|
|
74
|
+
"div",
|
|
75
|
+
{
|
|
76
|
+
className: joinClass("wp-spacer", step !== void 0 && `wp-spacer--${step}`, className),
|
|
77
|
+
style
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
function Section({ title, gap, align, justify, wrap, className, style, children }) {
|
|
82
|
+
return /* @__PURE__ */ jsxs(
|
|
83
|
+
"section",
|
|
84
|
+
{
|
|
85
|
+
className: joinClass("wp-col", className),
|
|
86
|
+
style: mergeStyle(containerLayoutStyle({ gap, align, justify, wrap }), style),
|
|
87
|
+
children: [
|
|
88
|
+
title ? /* @__PURE__ */ jsx("h2", { className: "wp-h4", children: title }) : null,
|
|
89
|
+
children
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
var TEXT_WRAP_CSS = {
|
|
95
|
+
normal: {},
|
|
96
|
+
preserve: { whiteSpace: "pre-wrap" },
|
|
97
|
+
anywhere: { whiteSpace: "pre-wrap", wordBreak: "break-all" }
|
|
98
|
+
};
|
|
99
|
+
function Text({
|
|
100
|
+
preset = "body",
|
|
101
|
+
as: Tag = "p",
|
|
102
|
+
weight,
|
|
103
|
+
textAlign,
|
|
104
|
+
wrap,
|
|
105
|
+
className,
|
|
106
|
+
style,
|
|
107
|
+
value,
|
|
108
|
+
children
|
|
109
|
+
}) {
|
|
110
|
+
return /* @__PURE__ */ jsx(
|
|
111
|
+
Tag,
|
|
112
|
+
{
|
|
113
|
+
className: joinClass(`wp-${preset}`, className),
|
|
114
|
+
style: mergeStyle({ ...typographyStyle({ weight, textAlign }), ...TEXT_WRAP_CSS[wrap ?? "normal"] }, style),
|
|
115
|
+
children: value ?? children
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function KeyValueList({ source, items, className, style }) {
|
|
120
|
+
return /* @__PURE__ */ jsx("div", { className: joinClass("wp-kv", className), style, children: (items ?? []).map((item, index) => {
|
|
121
|
+
const raw = item.key ? source?.[item.key] : item.value;
|
|
122
|
+
const content = raw === null || raw === void 0 || raw === "" ? "\u2014" : raw;
|
|
123
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
124
|
+
/* @__PURE__ */ jsx("span", { className: "wp-kv__k", children: item.label }),
|
|
125
|
+
/* @__PURE__ */ jsx("span", { className: joinClass("wp-kv__v", item.mono && "wp-mono"), children: content })
|
|
126
|
+
] }, index);
|
|
127
|
+
}) });
|
|
128
|
+
}
|
|
129
|
+
function PageHead({ title, subtitle, actions, className, style }) {
|
|
130
|
+
return /* @__PURE__ */ jsxs("div", { className: joinClass("wp-page-head", className), style, children: [
|
|
131
|
+
/* @__PURE__ */ jsxs("div", { className: "wp-page-head__text", children: [
|
|
132
|
+
title ? /* @__PURE__ */ jsx("h1", { className: "wp-page-head__title", children: title }) : null,
|
|
133
|
+
subtitle ? /* @__PURE__ */ jsx("div", { className: "wp-page-head__sub", children: subtitle }) : null
|
|
134
|
+
] }),
|
|
135
|
+
actions ? /* @__PURE__ */ jsx("div", { className: "wp-page-head__actions", children: actions }) : null
|
|
136
|
+
] });
|
|
137
|
+
}
|
|
138
|
+
function Show({ when, children }) {
|
|
139
|
+
return when ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
|
|
140
|
+
}
|
|
141
|
+
function Divider({ style, className }) {
|
|
142
|
+
return /* @__PURE__ */ jsx("hr", { className: joinClass("wp-divider", className), style });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export { Actions, Col, Divider, Grid, KeyValueList, PageHead, Row, Section, Show, Spacer, Text };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Spinner, EmptyState } from './chunk-WIDDHR7L.js';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function useOptionalModule(load) {
|
|
6
|
+
const [state, setState] = useState({ status: "loading" });
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let alive = true;
|
|
9
|
+
load().then(
|
|
10
|
+
(module) => {
|
|
11
|
+
if (alive) setState({ status: "ready", module });
|
|
12
|
+
},
|
|
13
|
+
(error) => {
|
|
14
|
+
if (!alive) return;
|
|
15
|
+
console.warn("[sdui] \uC120\uD0DD \uC758\uC874\uC131 \uB85C\uB4DC \uC2E4\uD328 \u2014 \uD50C\uB808\uC774\uC2A4\uD640\uB354\uB85C \uB300\uCCB4\uD569\uB2C8\uB2E4", error);
|
|
16
|
+
setState({ status: "missing" });
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
return () => {
|
|
20
|
+
alive = false;
|
|
21
|
+
};
|
|
22
|
+
}, [load]);
|
|
23
|
+
return state;
|
|
24
|
+
}
|
|
25
|
+
var optionalModuleCache = /* @__PURE__ */ new WeakMap();
|
|
26
|
+
function loadOptionalModule(load, packageName) {
|
|
27
|
+
const cached = optionalModuleCache.get(load);
|
|
28
|
+
if (cached) return cached;
|
|
29
|
+
const pending = load().catch((error) => {
|
|
30
|
+
console.warn(`[sdui] \uC120\uD0DD \uC758\uC874\uC131 '${packageName}' \uB85C\uB4DC \uC2E4\uD328`, error);
|
|
31
|
+
optionalModuleCache.delete(load);
|
|
32
|
+
throw new Error(`${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC124\uCE58\uD574 \uC8FC\uC138\uC694`);
|
|
33
|
+
});
|
|
34
|
+
optionalModuleCache.set(load, pending);
|
|
35
|
+
return pending;
|
|
36
|
+
}
|
|
37
|
+
function DependencyPlaceholder({
|
|
38
|
+
packageName,
|
|
39
|
+
purpose,
|
|
40
|
+
height,
|
|
41
|
+
loading = false
|
|
42
|
+
}) {
|
|
43
|
+
return /* @__PURE__ */ jsx(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
style: {
|
|
47
|
+
minHeight: height,
|
|
48
|
+
display: "flex",
|
|
49
|
+
alignItems: "center",
|
|
50
|
+
justifyContent: "center",
|
|
51
|
+
border: "1px solid var(--wp-border-subtle)",
|
|
52
|
+
borderRadius: "var(--wp-radius-md)",
|
|
53
|
+
background: "var(--wp-surface-sunken)"
|
|
54
|
+
},
|
|
55
|
+
children: loading ? /* @__PURE__ */ jsx(Spinner, { label: `${purpose} \uC900\uBE44 \uC911` }) : /* @__PURE__ */ jsxs(EmptyState, { tight: true, icon: "box", title: `${purpose}\uB97C \uADF8\uB9AC\uB824\uBA74 ${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4`, children: [
|
|
56
|
+
"\uC120\uD0DD \uC758\uC874\uC131\uC774\uB77C \uAE30\uBCF8 \uC124\uCE58\uC5D0\uB294 \uD3EC\uD568\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 ",
|
|
57
|
+
/* @__PURE__ */ jsxs("code", { children: [
|
|
58
|
+
"pnpm add ",
|
|
59
|
+
packageName
|
|
60
|
+
] }),
|
|
61
|
+
" \uD6C4 \uB2E4\uC2DC \uC5F4\uC5B4 \uC8FC\uC138\uC694."
|
|
62
|
+
] })
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { DependencyPlaceholder, loadOptionalModule, useOptionalModule };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useDismiss, usePopoverAnchor, usePortalTo, HoistedStyle, AnchoredPopoverStyles, anchorStyle, focusStaysInside } from './chunk-EFMKWPZ3.js';
|
|
2
|
-
import { Icon
|
|
2
|
+
import { Icon } from './chunk-WIDDHR7L.js';
|
|
3
3
|
import { boxed } from './chunk-SAN4IX6N.js';
|
|
4
4
|
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
5
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
@@ -194,66 +194,5 @@ var TP_CSS = `
|
|
|
194
194
|
.wp-tp-opt--on{background:var(--wp-action-primary-bg);color:var(--wp-action-primary-fg);font-weight:500}
|
|
195
195
|
`;
|
|
196
196
|
var TimePicker = boxed(TimePickerImpl, "TimePicker");
|
|
197
|
-
function useOptionalModule(load) {
|
|
198
|
-
const [state, setState] = useState({ status: "loading" });
|
|
199
|
-
useEffect(() => {
|
|
200
|
-
let alive = true;
|
|
201
|
-
load().then(
|
|
202
|
-
(module) => {
|
|
203
|
-
if (alive) setState({ status: "ready", module });
|
|
204
|
-
},
|
|
205
|
-
(error) => {
|
|
206
|
-
if (!alive) return;
|
|
207
|
-
console.warn("[sdui] \uC120\uD0DD \uC758\uC874\uC131 \uB85C\uB4DC \uC2E4\uD328 \u2014 \uD50C\uB808\uC774\uC2A4\uD640\uB354\uB85C \uB300\uCCB4\uD569\uB2C8\uB2E4", error);
|
|
208
|
-
setState({ status: "missing" });
|
|
209
|
-
}
|
|
210
|
-
);
|
|
211
|
-
return () => {
|
|
212
|
-
alive = false;
|
|
213
|
-
};
|
|
214
|
-
}, [load]);
|
|
215
|
-
return state;
|
|
216
|
-
}
|
|
217
|
-
var optionalModuleCache = /* @__PURE__ */ new WeakMap();
|
|
218
|
-
function loadOptionalModule(load, packageName) {
|
|
219
|
-
const cached = optionalModuleCache.get(load);
|
|
220
|
-
if (cached) return cached;
|
|
221
|
-
const pending = load().catch((error) => {
|
|
222
|
-
console.warn(`[sdui] \uC120\uD0DD \uC758\uC874\uC131 '${packageName}' \uB85C\uB4DC \uC2E4\uD328`, error);
|
|
223
|
-
optionalModuleCache.delete(load);
|
|
224
|
-
throw new Error(`${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC124\uCE58\uD574 \uC8FC\uC138\uC694`);
|
|
225
|
-
});
|
|
226
|
-
optionalModuleCache.set(load, pending);
|
|
227
|
-
return pending;
|
|
228
|
-
}
|
|
229
|
-
function DependencyPlaceholder({
|
|
230
|
-
packageName,
|
|
231
|
-
purpose,
|
|
232
|
-
height,
|
|
233
|
-
loading = false
|
|
234
|
-
}) {
|
|
235
|
-
return /* @__PURE__ */ jsx(
|
|
236
|
-
"div",
|
|
237
|
-
{
|
|
238
|
-
style: {
|
|
239
|
-
minHeight: height,
|
|
240
|
-
display: "flex",
|
|
241
|
-
alignItems: "center",
|
|
242
|
-
justifyContent: "center",
|
|
243
|
-
border: "1px solid var(--wp-border-subtle)",
|
|
244
|
-
borderRadius: "var(--wp-radius-md)",
|
|
245
|
-
background: "var(--wp-surface-sunken)"
|
|
246
|
-
},
|
|
247
|
-
children: loading ? /* @__PURE__ */ jsx(Spinner, { label: `${purpose} \uC900\uBE44 \uC911` }) : /* @__PURE__ */ jsxs(EmptyState, { tight: true, icon: "box", title: `${purpose}\uB97C \uADF8\uB9AC\uB824\uBA74 ${packageName} \uD328\uD0A4\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4`, children: [
|
|
248
|
-
"\uC120\uD0DD \uC758\uC874\uC131\uC774\uB77C \uAE30\uBCF8 \uC124\uCE58\uC5D0\uB294 \uD3EC\uD568\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 ",
|
|
249
|
-
/* @__PURE__ */ jsxs("code", { children: [
|
|
250
|
-
"pnpm add ",
|
|
251
|
-
packageName
|
|
252
|
-
] }),
|
|
253
|
-
" \uD6C4 \uB2E4\uC2DC \uC5F4\uC5B4 \uC8FC\uC138\uC694."
|
|
254
|
-
] })
|
|
255
|
-
}
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
197
|
|
|
259
|
-
export {
|
|
198
|
+
export { TimePicker };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Text } from './chunk-QO6UJAQN.js';
|
|
2
|
+
import { Field, Input, Select, IconButton, Button } from './chunk-WIDDHR7L.js';
|
|
3
|
+
import { boxed } from './chunk-SAN4IX6N.js';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var OPTION_TYPES = [
|
|
8
|
+
{ value: "text", label: "\uBB38\uC790\uC5F4" },
|
|
9
|
+
{ value: "number", label: "\uC22B\uC790" },
|
|
10
|
+
{ value: "select", label: "\uC120\uD0DD" },
|
|
11
|
+
{ value: "boolean", label: "\uC608/\uC544\uB2C8\uC624" }
|
|
12
|
+
];
|
|
13
|
+
function normalize(value) {
|
|
14
|
+
if (!Array.isArray(value)) return [];
|
|
15
|
+
return value.filter((item) => Boolean(item) && typeof item === "object").map((item) => ({
|
|
16
|
+
key: typeof item.key === "string" ? item.key : "",
|
|
17
|
+
label: typeof item.label === "string" ? item.label : void 0,
|
|
18
|
+
type: OPTION_TYPES.some((t) => t.value === item.type) ? item.type : "text",
|
|
19
|
+
options: Array.isArray(item.options) ? item.options.map((o) => String(o)) : void 0,
|
|
20
|
+
unit: typeof item.unit === "string" ? item.unit : void 0
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
function OptionSchemaEditorImpl({ value, onChange, disabled = false }) {
|
|
24
|
+
const config = useMemo(
|
|
25
|
+
() => value && typeof value === "object" && !Array.isArray(value) ? value : {},
|
|
26
|
+
[value]
|
|
27
|
+
);
|
|
28
|
+
const fields = useMemo(() => normalize(config.options), [config.options]);
|
|
29
|
+
const duplicates = useMemo(() => {
|
|
30
|
+
const seen = /* @__PURE__ */ new Map();
|
|
31
|
+
for (const field of fields) {
|
|
32
|
+
const key = field.key.trim();
|
|
33
|
+
if (!key) continue;
|
|
34
|
+
seen.set(key, (seen.get(key) ?? 0) + 1);
|
|
35
|
+
}
|
|
36
|
+
return new Set([...seen.entries()].filter(([, count]) => count > 1).map(([key]) => key));
|
|
37
|
+
}, [fields]);
|
|
38
|
+
const emit = (next) => onChange?.({ ...config, options: next });
|
|
39
|
+
const patch = (index, change) => emit(fields.map((field, i) => i === index ? { ...field, ...change } : field));
|
|
40
|
+
return /* @__PURE__ */ jsxs("div", { className: "wp-col", style: { gap: "var(--wp-space-3)" }, children: [
|
|
41
|
+
fields.length === 0 ? /* @__PURE__ */ jsx(Text, { preset: "body-sm", style: { color: "var(--wp-text-muted)" }, children: "\uC544\uC9C1 \uC635\uC158\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC5EC\uAE30\uC5D0 \uC815\uD574 \uB450\uBA74 \uC774 \uC7A5\uBE44\uC758 \uC720\uB2DB\uC744 \uC218\uC815\uD560 \uB54C \uADF8 \uC11C\uC2DD\uB300\uB85C \uCE78\uC774 \uB739\uB2C8\uB2E4." }) : null,
|
|
42
|
+
fields.map((field, index) => {
|
|
43
|
+
const duplicated = Boolean(field.key.trim()) && duplicates.has(field.key.trim());
|
|
44
|
+
return /* @__PURE__ */ jsxs(
|
|
45
|
+
"div",
|
|
46
|
+
{
|
|
47
|
+
className: "wp-row",
|
|
48
|
+
style: { gap: "var(--wp-space-2)", alignItems: "flex-start" },
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
Field,
|
|
52
|
+
{
|
|
53
|
+
label: index === 0 ? "\uC774\uB984(\uD0A4)" : void 0,
|
|
54
|
+
error: !field.key.trim() ? "\uC774\uB984\uC740 \uBE44\uC6B8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4" : duplicated ? "\uC774\uB984\uC774 \uACB9\uCE69\uB2C8\uB2E4" : void 0,
|
|
55
|
+
style: { flex: 1.2 },
|
|
56
|
+
children: /* @__PURE__ */ jsx(
|
|
57
|
+
Input,
|
|
58
|
+
{
|
|
59
|
+
value: field.key,
|
|
60
|
+
placeholder: "CPU",
|
|
61
|
+
disabled,
|
|
62
|
+
invalid: !field.key.trim() || duplicated,
|
|
63
|
+
onChange: (event) => patch(index, { key: event.target.value })
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsx(Field, { label: index === 0 ? "\uD45C\uC2DC \uC774\uB984" : void 0, style: { flex: 1.2 }, children: /* @__PURE__ */ jsx(
|
|
69
|
+
Input,
|
|
70
|
+
{
|
|
71
|
+
value: field.label ?? "",
|
|
72
|
+
placeholder: field.key || "\uBE44\uC6B0\uBA74 \uC774\uB984 \uADF8\uB300\uB85C",
|
|
73
|
+
disabled,
|
|
74
|
+
onChange: (event) => patch(index, { label: event.target.value || void 0 })
|
|
75
|
+
}
|
|
76
|
+
) }),
|
|
77
|
+
/* @__PURE__ */ jsx(Field, { label: index === 0 ? "\uD615\uC2DD" : void 0, style: { flex: 1 }, children: /* @__PURE__ */ jsx(
|
|
78
|
+
Select,
|
|
79
|
+
{
|
|
80
|
+
value: field.type,
|
|
81
|
+
disabled,
|
|
82
|
+
options: OPTION_TYPES,
|
|
83
|
+
onChange: (event) => patch(index, {
|
|
84
|
+
type: event.target.value,
|
|
85
|
+
// 선택이 아니면 선택지는 의미가 없다 — 남겨 두면 다시 선택으로 바꿨을 때 옛 값이 살아난다
|
|
86
|
+
options: event.target.value === "select" ? field.options ?? [] : void 0
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
) }),
|
|
90
|
+
field.type === "select" ? /* @__PURE__ */ jsx(Field, { label: index === 0 ? "\uC120\uD0DD\uC9C0(\uC27C\uD45C\uB85C \uAD6C\uBD84)" : void 0, style: { flex: 1.6 }, children: /* @__PURE__ */ jsx(
|
|
91
|
+
Input,
|
|
92
|
+
{
|
|
93
|
+
value: (field.options ?? []).join(", "),
|
|
94
|
+
placeholder: "8GB, 16GB, 32GB",
|
|
95
|
+
disabled,
|
|
96
|
+
onChange: (event) => patch(index, {
|
|
97
|
+
options: event.target.value.split(",").map((option) => option.trim()).filter(Boolean)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
) }) : null,
|
|
101
|
+
field.type === "number" ? /* @__PURE__ */ jsx(Field, { label: index === 0 ? "\uB2E8\uC704" : void 0, style: { flex: 0.7 }, children: /* @__PURE__ */ jsx(
|
|
102
|
+
Input,
|
|
103
|
+
{
|
|
104
|
+
value: field.unit ?? "",
|
|
105
|
+
placeholder: "GB",
|
|
106
|
+
disabled,
|
|
107
|
+
onChange: (event) => patch(index, { unit: event.target.value || void 0 })
|
|
108
|
+
}
|
|
109
|
+
) }) : null,
|
|
110
|
+
/* @__PURE__ */ jsx("div", { style: { paddingTop: index === 0 ? 22 : 0, flex: "none" }, children: /* @__PURE__ */ jsx(
|
|
111
|
+
IconButton,
|
|
112
|
+
{
|
|
113
|
+
icon: "trash-2",
|
|
114
|
+
label: `${field.label || field.key || index + 1} \uC0AD\uC81C`,
|
|
115
|
+
variant: "ghost",
|
|
116
|
+
disabled,
|
|
117
|
+
onClick: () => emit(fields.filter((_, i) => i !== index))
|
|
118
|
+
}
|
|
119
|
+
) })
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
index
|
|
123
|
+
);
|
|
124
|
+
}),
|
|
125
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
126
|
+
Button,
|
|
127
|
+
{
|
|
128
|
+
size: "sm",
|
|
129
|
+
variant: "secondary",
|
|
130
|
+
icon: "plus",
|
|
131
|
+
disabled,
|
|
132
|
+
onClick: () => emit([...fields, { key: "", type: "text" }]),
|
|
133
|
+
children: "\uC635\uC158 \uCD94\uAC00"
|
|
134
|
+
}
|
|
135
|
+
) })
|
|
136
|
+
] });
|
|
137
|
+
}
|
|
138
|
+
var OptionSchemaEditor = boxed(OptionSchemaEditorImpl, "OptionSchemaEditor");
|
|
139
|
+
|
|
140
|
+
export { OPTION_TYPES, OptionSchemaEditor };
|