@svar-ui/react-editor
Version:
React component for building inline, popup, or sidebar forms to edit structured data
640 lines (639 loc) • 17 kB
JavaScript
import { jsxs as $, jsx as u } from "react/jsx-runtime";
import { context as F, Field as le, Locale as ae, ModalArea as ye, SideArea as be, Material as te, Willow as ne, WillowDark as oe, Text as ve, TextArea as xe, Checkbox as ke } from "@svar-ui/react-core";
import { useRef as ue, useEffect as L, useContext as P, useMemo as j, useState as G, useCallback as Ce } from "react";
import { uid as Ee, isSame as fe, deepCopy as ie } from "@svar-ui/lib-state";
import { useWritableProp as Ne } from "@svar-ui/lib-react";
import { Toolbar as Se } from "@svar-ui/react-toolbar";
import { setEnv as je, env as Ae } from "@svar-ui/lib-dom";
const Y = {};
function se(t) {
return typeof t < "u" ? Y[t] || t : Y.text;
}
function K(t, e) {
Y[t] = e;
}
const de = {
editor: {}
};
function H(t) {
const {
editors: e,
data: i,
css: l = "",
errors: o,
focus: a = !1,
onClick: n,
children: s,
onChange: c
} = t, d = ue(null);
L(() => {
if (a) {
const f = document.activeElement;
if (f && d.current && d.current.contains(f)) return;
const p = d.current ? d.current.querySelector(
"input:not([disabled]), textarea:not([disabled]), select:not([disabled])"
) : null;
p && setTimeout(() => {
typeof p.select == "function" && p.select(), typeof p.focus == "function" && p.focus();
}, 300);
}
}, []);
const r = P(F.i18n), w = j(() => r.getGroup("editor"), [r]), g = j(
() => e.config[0].comp === "readonly" && e.config.every((f) => !Object.keys(i).includes(f.key)),
[e, i]
);
return /* @__PURE__ */ $("div", { className: "wx-s2aE1xdZ wx-sections " + l, ref: d, children: [
s,
g ? /* @__PURE__ */ u("div", { className: "wx-s2aE1xdZ wx-overlay", children: w("No data") }) : null,
e.config.map((f) => {
if (!f.hidden) {
const { key: p, onChange: k, ...v } = f;
if (f.comp === "readonly" || f.comp === "section") {
const x = se(f.comp);
return /* @__PURE__ */ u(
x,
{
fieldKey: p,
label: f.label,
value: i[p],
...v,
onClick: n
},
p
);
} else {
const x = se(f.comp);
return /* @__PURE__ */ $("div", { children: [
/* @__PURE__ */ u(
le,
{
label: f.labelTemplate ? f.labelTemplate(i[p]) : f.label ?? "",
required: f.required,
children: ({ id: D }) => /* @__PURE__ */ u(
x,
{
fieldKey: p,
...v,
onChange: k || ((y) => {
c && c({
value: y.value,
key: p,
input: y.input
});
}),
id: D,
label: void 0,
error: o && o[p],
value: i[p]
},
p
)
}
),
o && o[p] && f.validationMessage ? /* @__PURE__ */ u("div", { className: "wx-s2aE1xdZ wx-message", children: f.validationMessage }) : null
] }, p);
}
}
return null;
})
] });
}
function De(t) {
if (typeof t == "string" && t.includes(".")) {
const e = t.split(".");
return (i) => {
let l = i;
return e.forEach((o) => {
l = l[o];
}), l;
};
}
return (e) => e[t];
}
function Me(t) {
if (typeof t == "string" && t.includes(".")) {
const e = t.split(".");
return (i, l) => {
let o = i;
e.forEach((a, n) => {
n === e.length - 1 ? o[a] = l : o = o[a];
});
};
}
return (e, i) => e[t] = i;
}
function Te(t) {
const e = t.map((n) => {
const s = { ...n };
return n.config && Object.assign(s, n.config), s.key = n.key || Ee(), s.setter = n.setter || Me(n.key), s.getter = n.getter || De(n.key), s;
}), i = (n) => {
const s = {};
return e.forEach((c) => {
c.comp !== "section" && (c.getter ? s[c.key] = c.getter(n) : s[c.key] = n[c.key]);
}), s;
}, l = (n, s, c) => ((c.length ? c.map((r) => e.find((w) => w.key === r)) : e).forEach((r) => {
r.setter ? r.setter(n, s[r.key]) : n[r.key] = s[r.key];
}), n), o = (n, s) => {
const c = i(n), d = [];
return e.forEach((r) => {
const w = c[r.key], g = s[r.key];
!fe(w, g) && (w !== void 0 || g) && d.push(r.key);
}), d;
}, a = (n, s, c) => {
let d = 0;
const r = {};
return (s?.length ? s.map((g) => e.find((f) => f.key === g)) : e).forEach((g) => {
g.required && !n[g.key] ? (r[g.key] = {
errorType: "required"
}, g.validationMessage = g.validationMessage ?? c("This field is required"), d++) : g.validation && !g.validation(n[g.key]) && (r[g.key] = {
errorType: "validation"
}, g.validationMessage = g.validationMessage ?? c("Invalid value"), d++);
}), d > 0 ? r : null;
};
return {
config: e.filter((n) => n.comp !== "hidden"),
getValues: i,
setValues: l,
diff: o,
validateValues: a
};
}
function me({
values: t,
items: e,
css: i,
activeBatch: l,
autoSave: o,
focus: a,
readonly: n,
topBar: s = !0,
bottomBar: c = !0,
layout: d = "default",
placement: r = "inline",
view: w,
children: g,
onChange: f,
onSave: p,
onAction: k,
onValidation: v
}) {
const D = P(F.i18n).getGroup("editor"), [y, T] = Ne(t), [M, O] = G(null), b = j(() => {
const h = Te(e);
M && h.config.forEach((m) => {
m.comp === "section" && m.key === M && (m.sectionMode === "accordion" ? m.activeSection || (h.config.forEach((V) => {
V.comp === "section" && V.key !== m.key && (V.activeSection = !1);
}), m.activeSection = !0) : m.activeSection = !m.activeSection);
});
let E = /* @__PURE__ */ new Set(), S = null;
return h.config.forEach((m) => {
m.sectionMode === "exclusive" && m.activeSection && (S = m.key), m.activeSection && E.add(m.key);
}), h.config.forEach((m) => {
m.hidden = m.hidden || l && l !== m.batch || S && m.key != S && m.section !== S || m.section && !E.has(m.section);
}), n ? {
...h,
config: h.config.map((m) => ({ ...m, comp: "readonly" })),
diff: () => []
} : h;
}, [e, M, l, n]), [C, N] = G({}), [W, J] = G({});
L(() => {
y !== void 0 && (N(ie(y)), J(ie(y)));
}, [y]);
const A = y, [z, B] = G([]);
L(() => {
y && B([]);
}, [y]);
function X(h) {
return [...new Set(h)];
}
function U(h) {
const E = b.validateValues(C, h, D);
return fe(E, A.errors) || v && v({ errors: E, values: C }), E;
}
function pe(h, E) {
if (o && !A.errors) {
const S = b.setValues(y, E ?? W, h);
T(S), p && p({ changes: h, values: S });
} else
B(h);
}
function he({ value: h, key: E, input: S }) {
let m = { ...W || {}, [E]: h };
const V = {
key: E,
value: h,
update: m
};
if (S && (V.input = S), f && f(V), !y) return;
m = V.update, J(m);
const I = b.diff(y, m), we = b.setValues(
{ ...C || {} },
m,
X([...I, E])
);
if (N(we), I.length) {
const R = o ? [] : X([...I, ...Object.keys(A.errors ?? {}), E]);
A.errors = U(R), pe(I, m);
} else {
const R = Object.keys(A.errors ?? {});
R.length && (A.errors = U(R)), B([]);
}
}
function ge() {
if (z.length && (o || (A.errors = U()), !A.errors)) {
p && p({
changes: z,
values: C
});
const h = b.setValues(y, W, z);
T(h), B([]), T({ ...C });
}
}
function ee({ item: h }) {
h.id === "save" ? ge() : h.id === "toggle-section" && O(h.key), k && k({ item: h, values: C, changes: z });
}
return /* @__PURE__ */ u(
w,
{
topBar: s,
bottomBar: c,
placement: r,
layout: d,
readonly: n,
autoSave: o,
css: i,
data: W,
editors: b,
focus: a,
errors: A.errors,
onClick: ee,
onKeyDown: ee,
onChange: he,
children: g
}
);
}
function Le({
values: t = {},
items: e = [],
css: i = "",
activeBatch: l = null,
focus: o = !1,
autoSave: a = !0,
readonly: n = !1,
children: s,
...c
}) {
const d = {};
for (const r in c) {
if (/^on[a-z]/.test(r)) {
const w = "on" + r.charAt(2).toUpperCase() + r.slice(3);
if (w in c)
continue;
d[w] = c[r];
continue;
}
d[r] = c[r];
}
return /* @__PURE__ */ u(ae, { words: de, optional: !0, children: /* @__PURE__ */ u("div", { className: "wx-H902AF2Y wx-content", children: /* @__PURE__ */ u(
me,
{
view: H,
values: t,
items: e,
css: i,
focus: o,
activeBatch: l,
autoSave: a,
readonly: n,
...d,
children: s
}
) }) });
}
function Ve(t, { keys: e, action: i }) {
function l(o) {
let a = o.code.replace("Key", "").toLowerCase();
const n = `${o.ctrlKey || o.metaKey ? "ctrl+" : ""}${a}`, s = e[n];
if (!s) return;
const c = o.target.closest(".wx-combo") || o.target.closest(".wx-multicombo") || o.target.closest(".wx-richselect");
c && c.querySelector(".wx-list") || (o.preventDefault(), i(s));
}
return t.addEventListener("keydown", l), {
destroy: () => {
t.removeEventListener("keydown", l);
}
};
}
function $e(t) {
const { editors: e, data: i, layout: l, errors: o, focus: a, onClick: n, onChange: s } = t, c = j(() => {
let d = [];
if (l === "columns" && (d = [
{ ...e, config: [] },
{ ...e, config: [] }
], e.config.forEach((r) => {
const w = r.column === "left" ? 0 : 1;
d[w].config.push(r);
}), d[0].config.length)) {
const r = d[0].config[0];
r.comp === "text" && (d[0][0] = {
...r,
css: "title",
label: ""
});
}
return d;
}, [l, e]);
return l === "columns" ? /* @__PURE__ */ $("div", { className: "wx-bNrSbszs wx-cols", children: [
/* @__PURE__ */ u("div", { className: "wx-bNrSbszs wx-left", children: /* @__PURE__ */ u(
H,
{
editors: c[0],
data: i,
errors: o,
onClick: n,
onChange: s
}
) }),
/* @__PURE__ */ u("div", { className: "wx-bNrSbszs wx-right", children: /* @__PURE__ */ u(
H,
{
editors: c[1],
data: i,
focus: a,
errors: o,
onClick: n,
onChange: s
}
) })
] }) : /* @__PURE__ */ u(
H,
{
editors: e,
data: i,
focus: a,
errors: o,
onClick: n,
onChange: s
}
);
}
function re({
items: t,
values: e = null,
top: i = !0,
onClick: l,
onChange: o
}) {
const a = Ce(
({ item: n, value: s }) => {
o && o({ key: n.key, value: s });
},
[o]
);
return t.length ? /* @__PURE__ */ u(
"div",
{
className: `wx-66OW1j0R wx-editor-toolbar ${i ? "wx-topbar" : "wx-bottom"}`,
children: /* @__PURE__ */ u(
Se,
{
items: t,
values: e,
onClick: l,
onChange: a
}
)
}
) : null;
}
const q = () => ({ comp: "spacer" }), Z = (t) => ({
comp: "button",
text: t("Cancel"),
id: "cancel"
}), _ = (t) => ({
type: "primary",
comp: "button",
text: t("Save"),
id: "save"
}), ce = () => ({
comp: "icon",
icon: "wxi-close",
id: "close"
});
function Q(t) {
const {
data: e,
editors: i,
focus: l,
css: o,
topBar: a,
bottomBar: n,
layout: s,
placement: c,
errors: d,
readonly: r,
autoSave: w,
children: g,
onClick: f,
onKeyDown: p,
onChange: k
} = t, v = P(F.i18n), x = j(() => v.getGroup("editor"), [v]), D = j(
() => a === !0 && n === !0,
[a, n]
), y = j(() => {
let b = a && a.items ? a.items.map((C) => ({ ...C })) : [];
return D && (r ? b = [q(), ce()] : (w ? b = [q(), ce()] : c !== "modal" && (b = [q(), Z(x), _(x)]), s === "columns" && !b.length && (b = [q(), _(x), Z(x)]))), b;
}, [a, D, r, w, c, s, x]), T = j(() => {
let b = n && n.items ? n.items.map((C) => ({ ...C })) : [];
return D && (r || (c === "modal" && !w && (b = [q(), _(x), Z(x)]), s === "columns" && y.length && (b = []))), b;
}, [n, D, r, c, w, s, y, x]), M = j(() => [...y, ...T], [y, T]), O = ue(null);
return L(() => {
const b = O.current;
if (!b) return;
const C = Ve(b, {
keys: {
"ctrl+s": M.find((N) => N.id === "save"),
escape: M.find((N) => N.id === "cancel" || N.id === "close"),
"ctrl+d": M.find((N) => N.id === "delete")
},
action: (N) => {
p && p({ item: N });
}
});
return () => {
typeof C == "function" && C();
};
}, [M, p]), /* @__PURE__ */ $("div", { className: o ? "wx-85HDaNoA " + o : "wx-85HDaNoA", ref: O, children: [
/* @__PURE__ */ u(
re,
{
...a && typeof a == "object" ? a : {},
items: y,
values: e,
onClick: f,
onChange: k
}
),
/* @__PURE__ */ $(
"div",
{
className: `wx-85HDaNoA wx-content${s === "columns" ? " wx-layout-columns" : ""}`,
children: [
g,
/* @__PURE__ */ u(
$e,
{
editors: i,
layout: s,
data: e,
focus: l,
errors: d,
onClick: f,
onChange: k
}
),
/* @__PURE__ */ u(
re,
{
...n && typeof n == "object" ? n : {},
items: T,
values: e,
top: !1,
onClick: f,
onChange: k
}
)
]
}
)
] });
}
function qe(t) {
const { css: e, onClick: i, placement: l, ...o } = t;
function a() {
i && i({ item: { id: "close" } });
}
return l === "modal" ? /* @__PURE__ */ u(ye, { children: /* @__PURE__ */ u(
Q,
{
css: `wx-panel ${e}`,
onClick: i,
placement: l,
...o
}
) }) : l === "sidebar" ? /* @__PURE__ */ u(be, { onCancel: a, children: /* @__PURE__ */ u(
Q,
{
css: `wx-panel ${e}`,
onClick: i,
placement: l,
...o
}
) }) : /* @__PURE__ */ u(
Q,
{
css: `wx-inline-form ${e}`,
onClick: i,
placement: l,
...o
}
);
}
function Fe(t) {
const {
values: e = {},
items: i = [],
css: l = "",
activeBatch: o = null,
topBar: a = !0,
bottomBar: n = !0,
focus: s = !1,
autoSave: c = !1,
layout: d = "default",
readonly: r = !1,
placement: w = "inline",
children: g,
...f
} = t, p = Object.keys(f).reduce((k, v) => {
if (/^on[a-z]/.test(v)) {
const x = "on" + v.charAt(2).toUpperCase() + v.slice(3);
x in f ? k[v] = f[v] : k[x] = f[v];
} else
k[v] = f[v];
return k;
}, {});
return /* @__PURE__ */ u(ae, { words: de, optional: !0, children: /* @__PURE__ */ u(
me,
{
view: qe,
values: e,
items: i,
css: l,
activeBatch: o,
topBar: a,
bottomBar: n,
focus: s,
autoSave: c,
layout: d,
readonly: r,
placement: w,
...p,
children: g
}
) });
}
function Pe({ fonts: t = !0, children: e }) {
return e ? /* @__PURE__ */ u(te, { fonts: t, children: e }) : /* @__PURE__ */ u(te, { fonts: t });
}
function Ue({ fonts: t = !0, children: e }) {
return e ? /* @__PURE__ */ u(ne, { fonts: t, children: e }) : /* @__PURE__ */ u(ne, { fonts: t });
}
function Ze(t) {
const { fonts: e = !0, children: i } = t;
return i ? /* @__PURE__ */ u(oe, { fonts: e, children: i }) : /* @__PURE__ */ u(oe, { fonts: e });
}
function Ke({ value: t, options: e, label: i }) {
const o = P(F.i18n).getGroup("editor"), a = j(() => {
let n = t;
if (typeof t == "boolean" && (n = o(t ? "Yes" : "No")), e) {
const s = e.find((c) => c.id === t);
s && (n = s.label);
}
return n;
}, [t, e, o]);
return a || a === 0 ? /* @__PURE__ */ u(le, { label: i, children: a }) : null;
}
function Oe({ fieldKey: t, label: e, activeSection: i, onClick: l }) {
return /* @__PURE__ */ $(
"div",
{
className: `wx-OmgQq65I wx-section${i ? " wx-section-active" : ""}`,
onClick: () => l && l({
item: { id: "toggle-section", key: i ? null : t }
}),
children: [
/* @__PURE__ */ u("h3", { children: e }),
/* @__PURE__ */ u(
"i",
{
className: `wx-OmgQq65I wxi-angle-${i ? "down" : "right"} wx-icon`
}
)
]
}
);
}
K("text", ve);
K("textarea", xe);
K("checkbox", ke);
K("readonly", Ke);
K("section", Oe);
je(Ae);
export {
Fe as Editor,
Le as Form,
Pe as Material,
Ue as Willow,
Ze as WillowDark,
K as registerEditorItem
};