@wipco/sdui 0.31.0 → 0.33.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-PCMJE2GO.js +109 -0
- package/dist/chunk-QO6UJAQN.js +145 -0
- package/dist/chunk-TIY4A223.js +140 -0
- package/dist/index.js +14 -151
- package/dist/meta.d.ts +9 -1
- package/dist/meta.js +174 -1
- package/dist/rich.js +1 -1
- 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/QRDisplay.d.ts +45 -0
- package/dist/widgets/QRDisplay.js +4 -0
- package/dist/widgets/StagePeriodEditor.d.ts +45 -0
- package/dist/widgets/StagePeriodEditor.js +6 -0
- package/dist/widgets/index.d.ts +13 -1
- package/dist/widgets/index.js +22 -8
- 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,109 @@
|
|
|
1
|
+
import { useSduiAssetResolver } from './chunk-DKPNFEVU.js';
|
|
2
|
+
import { Spinner, Icon } from './chunk-WIDDHR7L.js';
|
|
3
|
+
import { boxed } from './chunk-SAN4IX6N.js';
|
|
4
|
+
import { useState, useEffect } from 'react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var FALLBACK_NOTE = "QR \uC774\uBBF8\uC9C0\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uC11C\uBC84 \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694";
|
|
8
|
+
var boxStyle = (size) => ({
|
|
9
|
+
width: size,
|
|
10
|
+
height: size,
|
|
11
|
+
display: "flex",
|
|
12
|
+
flexDirection: "column",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
justifyContent: "center",
|
|
15
|
+
gap: "var(--wp-space-2)"
|
|
16
|
+
});
|
|
17
|
+
function QRDisplayImpl({
|
|
18
|
+
path,
|
|
19
|
+
url,
|
|
20
|
+
size = 128,
|
|
21
|
+
endpoint = "/qr",
|
|
22
|
+
caption
|
|
23
|
+
}) {
|
|
24
|
+
const [absoluteUrl, setAbsoluteUrl] = useState(null);
|
|
25
|
+
const [failed, setFailed] = useState(false);
|
|
26
|
+
const resolveAsset = useSduiAssetResolver();
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
setFailed(false);
|
|
29
|
+
try {
|
|
30
|
+
const base = window.location.origin;
|
|
31
|
+
const target = url ?? path ?? window.location.href;
|
|
32
|
+
setAbsoluteUrl(new URL(target, base).toString());
|
|
33
|
+
} catch {
|
|
34
|
+
setAbsoluteUrl(null);
|
|
35
|
+
setFailed(true);
|
|
36
|
+
}
|
|
37
|
+
}, [path, url]);
|
|
38
|
+
const raw = absoluteUrl !== null ? `${endpoint}${endpoint.includes("?") ? "&" : "?"}data=${encodeURIComponent(absoluteUrl)}&size=${size}` : null;
|
|
39
|
+
const src = raw === null ? null : resolveAsset(raw, { component: "QRDisplay", prop: "src", kind: "attribute" });
|
|
40
|
+
const pending = absoluteUrl === null && !failed;
|
|
41
|
+
return /* @__PURE__ */ jsxs(
|
|
42
|
+
"figure",
|
|
43
|
+
{
|
|
44
|
+
style: {
|
|
45
|
+
display: "inline-flex",
|
|
46
|
+
flexDirection: "column",
|
|
47
|
+
alignItems: "center",
|
|
48
|
+
gap: "var(--wp-space-3)",
|
|
49
|
+
margin: 0,
|
|
50
|
+
padding: "var(--wp-space-4)",
|
|
51
|
+
background: "var(--wp-surface-card)",
|
|
52
|
+
border: "1px solid var(--wp-border-subtle)",
|
|
53
|
+
borderRadius: "var(--wp-radius-md)",
|
|
54
|
+
boxShadow: "var(--wp-shadow-xs)"
|
|
55
|
+
},
|
|
56
|
+
children: [
|
|
57
|
+
failed || src === null ? /* @__PURE__ */ jsx(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
role: "img",
|
|
61
|
+
"aria-label": FALLBACK_NOTE,
|
|
62
|
+
style: {
|
|
63
|
+
...boxStyle(size),
|
|
64
|
+
border: "1px dashed var(--wp-border-strong)",
|
|
65
|
+
borderRadius: "var(--wp-radius-md)",
|
|
66
|
+
background: "var(--wp-surface-sunken)",
|
|
67
|
+
color: "var(--wp-text-subtle)"
|
|
68
|
+
},
|
|
69
|
+
children: pending ? /* @__PURE__ */ jsx(Spinner, { label: "QR \uC900\uBE44 \uC911" }) : /* @__PURE__ */ jsx(Icon, { name: "scan", size: 24 })
|
|
70
|
+
}
|
|
71
|
+
) : /* @__PURE__ */ jsx(
|
|
72
|
+
"img",
|
|
73
|
+
{
|
|
74
|
+
src,
|
|
75
|
+
width: size,
|
|
76
|
+
height: size,
|
|
77
|
+
alt: caption ? `QR \uCF54\uB4DC \u2014 ${caption}` : "QR \uCF54\uB4DC",
|
|
78
|
+
onError: () => setFailed(true),
|
|
79
|
+
style: {
|
|
80
|
+
display: "block",
|
|
81
|
+
borderRadius: "var(--wp-radius-xs)",
|
|
82
|
+
maxWidth: "100%"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
/* @__PURE__ */ jsxs(
|
|
87
|
+
"figcaption",
|
|
88
|
+
{
|
|
89
|
+
style: {
|
|
90
|
+
display: "flex",
|
|
91
|
+
flexDirection: "column",
|
|
92
|
+
alignItems: "center",
|
|
93
|
+
gap: "var(--wp-space-1)",
|
|
94
|
+
maxWidth: Math.max(size, 160),
|
|
95
|
+
textAlign: "center"
|
|
96
|
+
},
|
|
97
|
+
children: [
|
|
98
|
+
caption && /* @__PURE__ */ jsx("span", { className: "wp-caption", style: { color: "var(--wp-text-muted)" }, children: caption }),
|
|
99
|
+
failed && /* @__PURE__ */ jsx("span", { className: "wp-caption", style: { color: "var(--wp-text-subtle)" }, children: FALLBACK_NOTE })
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
var QRDisplay = boxed(QRDisplayImpl, "QRDisplay");
|
|
108
|
+
|
|
109
|
+
export { QRDisplay };
|
|
@@ -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,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 };
|