@elcrm/form 0.0.79 → 0.0.81
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/assets/styles/Color.module.css +1 -1
- package/dist/assets/styles/Field.module.css +1 -1
- package/dist/core/Field.js +1 -1
- package/dist/fields/Code.d.ts +14 -0
- package/dist/fields/Code.js +94 -0
- package/dist/fields/Color.js +133 -91
- package/dist/fields/Date.js +132 -77
- package/dist/fields/Display.d.ts +17 -0
- package/dist/fields/Display.js +49 -0
- package/dist/fields/DragDrop.d.ts +26 -0
- package/dist/fields/DragDrop.js +189 -0
- package/dist/fields/Email.d.ts +8 -0
- package/dist/fields/Email.js +80 -0
- package/dist/fields/File.d.ts +24 -0
- package/dist/fields/File.js +115 -0
- package/dist/fields/Hidden.d.ts +11 -0
- package/dist/fields/Hidden.js +33 -0
- package/dist/fields/Numeric.js +42 -24
- package/dist/fields/Percent.d.ts +13 -0
- package/dist/fields/Percent.js +100 -0
- package/dist/fields/Radio.d.ts +22 -0
- package/dist/fields/Radio.js +88 -0
- package/dist/fields/Rating.d.ts +12 -0
- package/dist/fields/Rating.js +82 -0
- package/dist/fields/RichText.d.ts +14 -0
- package/dist/fields/RichText.js +134 -0
- package/dist/fields/Tags.d.ts +12 -0
- package/dist/fields/Tags.js +97 -0
- package/dist/fields/Time.d.ts +8 -1
- package/dist/fields/Time.js +330 -156
- package/dist/fields/Url.d.ts +8 -0
- package/dist/fields/Url.js +80 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +18 -6
- package/dist/index.umd.js +2 -2
- package/dist/package.js +1 -1
- package/dist/style.css +2 -2
- package/dist/styles/Color.module.js +23 -23
- package/dist/styles/Field.module.js +72 -24
- package/package.json +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import e from "../styles/Field.module.js";
|
|
2
|
+
import t from "../core/Field.js";
|
|
3
|
+
import { useFormFieldState as n } from "../hooks/use.js";
|
|
4
|
+
import { isEmptyTextLike as r } from "./visibility.js";
|
|
5
|
+
import i from "react";
|
|
6
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
7
|
+
//#region src/lib/fields/Email.tsx
|
|
8
|
+
function s({ value: s = "", form: c, onValue: l, onBlur: u, name: d = "", placeholder: f = "", label: p = "", error: m = "", hidden: h = !1, disabled: g = !1, className: _ = "", after: v = "", before: y = "", maxLength: b, autoComplete: x = "email", id: S }) {
|
|
9
|
+
let { value: C, version: w } = n(c, d, s), T = !g, E = typeof C == "string" || typeof C == "number" ? String(C) : "", D = i.useRef(null);
|
|
10
|
+
i.useEffect(() => {
|
|
11
|
+
let e = D.current;
|
|
12
|
+
!e || e.value === E || (e.value = E);
|
|
13
|
+
}, [E, w]);
|
|
14
|
+
let O = (e, t = !1) => {
|
|
15
|
+
let n = {
|
|
16
|
+
value: e,
|
|
17
|
+
name: d
|
|
18
|
+
};
|
|
19
|
+
c && d && (t ? c.setValue(n) : c.onValue(n)), l && l !== c?.onValue && l(n);
|
|
20
|
+
};
|
|
21
|
+
return h && r(C) ? null : /* @__PURE__ */ o(t, {
|
|
22
|
+
type: "email",
|
|
23
|
+
label: p,
|
|
24
|
+
error: m,
|
|
25
|
+
element_blok: { className: [_ || ""] },
|
|
26
|
+
element_input: {
|
|
27
|
+
className: [e.w],
|
|
28
|
+
props: { "data-disabled": g || !T ? "true" : void 0 }
|
|
29
|
+
},
|
|
30
|
+
children: [
|
|
31
|
+
y ? /* @__PURE__ */ a("span", {
|
|
32
|
+
className: e.affix,
|
|
33
|
+
children: y
|
|
34
|
+
}) : null,
|
|
35
|
+
/* @__PURE__ */ a("div", { children: /* @__PURE__ */ a("input", {
|
|
36
|
+
id: S,
|
|
37
|
+
ref: D,
|
|
38
|
+
type: "email",
|
|
39
|
+
name: d || void 0,
|
|
40
|
+
defaultValue: E,
|
|
41
|
+
placeholder: f,
|
|
42
|
+
maxLength: b,
|
|
43
|
+
disabled: g,
|
|
44
|
+
readOnly: !T,
|
|
45
|
+
autoComplete: x,
|
|
46
|
+
inputMode: "email",
|
|
47
|
+
spellCheck: !1,
|
|
48
|
+
"aria-label": p || f || "Email",
|
|
49
|
+
onChange: (e) => O(e.target.value),
|
|
50
|
+
onBlur: (e) => {
|
|
51
|
+
let t = (e.target.value || "").trim();
|
|
52
|
+
O(t, !0), u?.({
|
|
53
|
+
value: t,
|
|
54
|
+
name: d
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
onKeyDown: (e) => {
|
|
58
|
+
e.key === "Enter" && e.preventDefault();
|
|
59
|
+
},
|
|
60
|
+
style: {
|
|
61
|
+
width: "100%",
|
|
62
|
+
border: "none",
|
|
63
|
+
outline: "none",
|
|
64
|
+
background: "transparent",
|
|
65
|
+
font: "inherit",
|
|
66
|
+
color: "inherit",
|
|
67
|
+
padding: 0,
|
|
68
|
+
margin: 0
|
|
69
|
+
}
|
|
70
|
+
}) }),
|
|
71
|
+
v ? /* @__PURE__ */ a("span", {
|
|
72
|
+
className: e.affix,
|
|
73
|
+
children: v
|
|
74
|
+
}) : null
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
s.displayName = "EmailField";
|
|
79
|
+
//#endregion
|
|
80
|
+
export { s as default };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TInput } from './type';
|
|
2
|
+
/** Метаданные выбранного файла (File в форме сериализовать нельзя надёжно). */
|
|
3
|
+
export type TFileMeta = {
|
|
4
|
+
name: string;
|
|
5
|
+
size: number;
|
|
6
|
+
type: string;
|
|
7
|
+
lastModified: number;
|
|
8
|
+
};
|
|
9
|
+
export type TFile = TInput & {
|
|
10
|
+
accept?: string;
|
|
11
|
+
multiple?: boolean;
|
|
12
|
+
/** Макс. размер одного файла, байт */
|
|
13
|
+
maxSize?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Если true — в форму пишутся сами `File` / `File[]`.
|
|
16
|
+
* Иначе — метаданные `TFileMeta` / `TFileMeta[]` (удобнее для превью).
|
|
17
|
+
*/
|
|
18
|
+
keepFile?: boolean;
|
|
19
|
+
};
|
|
20
|
+
declare function FileField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, accept, multiple, maxSize, keepFile, }: TFile): import("react/jsx-runtime").JSX.Element | null;
|
|
21
|
+
declare namespace FileField {
|
|
22
|
+
var displayName: string;
|
|
23
|
+
}
|
|
24
|
+
export default FileField;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import e from "../styles/Field.module.js";
|
|
2
|
+
import t from "../core/Field.js";
|
|
3
|
+
import { useFormFieldState as n } from "../hooks/use.js";
|
|
4
|
+
import r from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
//#region src/lib/fields/File.tsx
|
|
7
|
+
function o(e) {
|
|
8
|
+
return {
|
|
9
|
+
name: e.name,
|
|
10
|
+
size: e.size,
|
|
11
|
+
type: e.type,
|
|
12
|
+
lastModified: e.lastModified
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function s(e) {
|
|
16
|
+
return e ? Array.isArray(e) ? e : typeof File < "u" && e instanceof File || typeof e == "object" && e && "name" in e ? [e] : [] : [];
|
|
17
|
+
}
|
|
18
|
+
function c(e) {
|
|
19
|
+
return e < 1024 ? `${e} Б` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} КБ` : `${(e / (1024 * 1024)).toFixed(1)} МБ`;
|
|
20
|
+
}
|
|
21
|
+
function l({ value: l, form: u, onValue: d, onBlur: f, name: p = "", placeholder: m = "Выберите файл", label: h = "", error: g = "", hidden: _ = !1, disabled: v = !1, className: y = "", after: b = "", before: x = "", accept: S, multiple: C = !1, maxSize: w, keepFile: T = !0 }) {
|
|
22
|
+
let { value: E } = n(u, p, l), D = s(E), O = !v, k = r.useRef(null), [A, j] = r.useState(""), M = (e) => {
|
|
23
|
+
let t = {
|
|
24
|
+
value: e,
|
|
25
|
+
name: p
|
|
26
|
+
};
|
|
27
|
+
u && p && u.setValue(t), d && d !== u?.onValue && d(t);
|
|
28
|
+
}, N = (e) => {
|
|
29
|
+
if (!e || e.length === 0) return;
|
|
30
|
+
let t = Array.from(e);
|
|
31
|
+
if (w !== void 0) {
|
|
32
|
+
let e = t.find((e) => e.size > w);
|
|
33
|
+
if (e) {
|
|
34
|
+
j(`Файл «${e.name}» больше ${c(w)}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
j("");
|
|
39
|
+
let n = T ? C ? t : t[0] : C ? t.map(o) : o(t[0]);
|
|
40
|
+
M(n), f?.({
|
|
41
|
+
value: n,
|
|
42
|
+
name: p
|
|
43
|
+
});
|
|
44
|
+
}, P = () => {
|
|
45
|
+
O && (M(C ? [] : null), k.current && (k.current.value = ""), j(""));
|
|
46
|
+
}, F = D.length === 0;
|
|
47
|
+
return _ && F ? null : /* @__PURE__ */ a(t, {
|
|
48
|
+
type: "file",
|
|
49
|
+
label: h,
|
|
50
|
+
error: g || A,
|
|
51
|
+
element_blok: { className: [y || ""] },
|
|
52
|
+
element_input: {
|
|
53
|
+
className: [e.file],
|
|
54
|
+
props: { "data-disabled": v || !O ? "true" : void 0 }
|
|
55
|
+
},
|
|
56
|
+
children: [
|
|
57
|
+
x ? /* @__PURE__ */ i("span", {
|
|
58
|
+
className: e.affix,
|
|
59
|
+
children: x
|
|
60
|
+
}) : null,
|
|
61
|
+
/* @__PURE__ */ a("div", {
|
|
62
|
+
className: e.fileInner,
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ i("input", {
|
|
65
|
+
ref: k,
|
|
66
|
+
type: "file",
|
|
67
|
+
className: e.fileInput,
|
|
68
|
+
accept: S,
|
|
69
|
+
multiple: C,
|
|
70
|
+
disabled: v || !O,
|
|
71
|
+
"aria-label": h || m || "Файл",
|
|
72
|
+
onChange: (e) => N(e.target.files)
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ i("button", {
|
|
75
|
+
type: "button",
|
|
76
|
+
className: e.fileButton,
|
|
77
|
+
disabled: !O,
|
|
78
|
+
onClick: () => k.current?.click(),
|
|
79
|
+
children: m
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ i("div", {
|
|
82
|
+
className: e.fileList,
|
|
83
|
+
children: F ? /* @__PURE__ */ i("span", {
|
|
84
|
+
className: e.fileEmpty,
|
|
85
|
+
children: "Нет файла"
|
|
86
|
+
}) : D.map((t, n) => /* @__PURE__ */ a("span", {
|
|
87
|
+
className: e.fileItem,
|
|
88
|
+
children: [/* @__PURE__ */ i("span", {
|
|
89
|
+
className: e.fileName,
|
|
90
|
+
children: t.name
|
|
91
|
+
}), /* @__PURE__ */ i("span", {
|
|
92
|
+
className: e.fileSize,
|
|
93
|
+
children: c(t.size)
|
|
94
|
+
})]
|
|
95
|
+
}, `${t.name}-${n}`))
|
|
96
|
+
}),
|
|
97
|
+
!F && O ? /* @__PURE__ */ i("button", {
|
|
98
|
+
type: "button",
|
|
99
|
+
className: e.fileClear,
|
|
100
|
+
"aria-label": "Очистить",
|
|
101
|
+
onClick: P,
|
|
102
|
+
children: "×"
|
|
103
|
+
}) : null
|
|
104
|
+
]
|
|
105
|
+
}),
|
|
106
|
+
b ? /* @__PURE__ */ i("span", {
|
|
107
|
+
className: e.affix,
|
|
108
|
+
children: b
|
|
109
|
+
}) : null
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
l.displayName = "FileField";
|
|
114
|
+
//#endregion
|
|
115
|
+
export { l as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TInput } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* HiddenField — значение в форме без UI.
|
|
4
|
+
* Рендерит нативный `<input type="hidden">` для submit обычных форм.
|
|
5
|
+
*/
|
|
6
|
+
export type THidden = Pick<TInput, "value" | "form" | "onValue" | "name" | "id" | "hidden">;
|
|
7
|
+
declare function HiddenField({ value, form, onValue, name, id, hidden, }: THidden): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
declare namespace HiddenField {
|
|
9
|
+
var displayName: string;
|
|
10
|
+
}
|
|
11
|
+
export default HiddenField;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useFormFieldState as e } from "../hooks/use.js";
|
|
2
|
+
import t from "react";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
//#region src/lib/fields/Hidden.tsx
|
|
5
|
+
function r({ value: r = "", form: i, onValue: a, name: o = "", id: s, hidden: c = !1 }) {
|
|
6
|
+
let { value: l } = e(i, o, r);
|
|
7
|
+
if (t.useEffect(() => {
|
|
8
|
+
if (!i || !o || i.getValue(o) !== void 0) return;
|
|
9
|
+
let e = {
|
|
10
|
+
value: l,
|
|
11
|
+
name: o
|
|
12
|
+
};
|
|
13
|
+
i.onValue(e), a && a !== i.onValue && a(e);
|
|
14
|
+
}, [
|
|
15
|
+
i,
|
|
16
|
+
o,
|
|
17
|
+
l,
|
|
18
|
+
a
|
|
19
|
+
]), c) return null;
|
|
20
|
+
let u = l == null ? "" : typeof l == "string" || typeof l == "number" || typeof l == "boolean" ? String(l) : JSON.stringify(l);
|
|
21
|
+
return /* @__PURE__ */ n("input", {
|
|
22
|
+
type: "hidden",
|
|
23
|
+
id: s,
|
|
24
|
+
name: o || void 0,
|
|
25
|
+
value: u,
|
|
26
|
+
readOnly: !0,
|
|
27
|
+
"data-field": "hidden",
|
|
28
|
+
"aria-hidden": !0
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
r.displayName = "HiddenField";
|
|
32
|
+
//#endregion
|
|
33
|
+
export { r as default };
|
package/dist/fields/Numeric.js
CHANGED
|
@@ -15,39 +15,55 @@ function u({ value: u, form: d, onValue: f, name: p = "", placeholder: m = "", l
|
|
|
15
15
|
let e = j.current;
|
|
16
16
|
e && e.textContent !== A && (e.textContent = A);
|
|
17
17
|
}, [A, k]);
|
|
18
|
-
let N = w ?? T, P = !1, F = (e) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
18
|
+
let N = w ?? T, P = !1, F = (e, t) => {
|
|
19
|
+
e.textContent !== t && (e.textContent = t, queueMicrotask(() => {
|
|
20
|
+
let n = e.firstChild;
|
|
21
|
+
if (n && n.nodeType === Node.TEXT_NODE) {
|
|
22
|
+
let e = t.length, r = window.getSelection(), i = document.createRange();
|
|
23
|
+
i.setStart(n, e), i.collapse(!0), r?.removeAllRanges(), r?.addRange(i);
|
|
24
|
+
} else t || (e.innerHTML = "<br>");
|
|
26
25
|
}));
|
|
26
|
+
}, I = (e) => {
|
|
27
|
+
let t = e.currentTarget, n = (t.textContent || "").replace(/\D/g, "");
|
|
28
|
+
N > 0 && n.length > N && (n = n.slice(0, N)), F(t, n);
|
|
27
29
|
let r = {
|
|
28
30
|
value: n,
|
|
29
31
|
name: p
|
|
30
32
|
};
|
|
31
33
|
d && p && d.onValue(r), f && f !== d?.onValue && f(r);
|
|
32
|
-
},
|
|
34
|
+
}, L = (e) => {
|
|
33
35
|
M.current = !0;
|
|
34
36
|
let t = e.currentTarget;
|
|
35
37
|
t.textContent = (t.textContent || "").replace(/\D/g, ""), r.Focus({ target: e.currentTarget });
|
|
36
|
-
},
|
|
38
|
+
}, R = (e) => {
|
|
37
39
|
if (P && e.which === 86) return;
|
|
38
40
|
if (e.which === 17 || e.which === 91) return P = !0;
|
|
39
41
|
if (e.key === "Backspace") return;
|
|
40
42
|
let t = (e.currentTarget.textContent ?? "").replace(/\D/g, "").length;
|
|
41
|
-
N > 0 && t >= N && e.key.length === 1 && /\d/.test(e.key) && e.preventDefault(), e.key === "Enter" && e.preventDefault(), e.key.length === 1 && !/\d/.test(e.key) && e.preventDefault();
|
|
42
|
-
},
|
|
43
|
+
N > 0 && t >= N && e.key.length === 1 && /\d/.test(e.key) && e.preventDefault(), e.key === "Enter" && e.preventDefault(), e.key.length === 1 && !/\d/.test(e.key) && !e.metaKey && !e.ctrlKey && e.preventDefault();
|
|
44
|
+
}, z = (e) => {
|
|
43
45
|
if (e.which === 17 || e.which === 91) return P = !1;
|
|
44
|
-
},
|
|
45
|
-
r.
|
|
46
|
-
|
|
46
|
+
}, B = (e) => {
|
|
47
|
+
r.PasteDigits(N, i(e)), queueMicrotask(() => {
|
|
48
|
+
let e = j.current;
|
|
49
|
+
if (!e) return;
|
|
50
|
+
let t = (e.textContent || "").replace(/\D/g, "");
|
|
51
|
+
N > 0 && t.length > N && (t = t.slice(0, N)), F(e, t), d && p && d.onValue({
|
|
52
|
+
value: t,
|
|
53
|
+
name: p
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}, V = (e) => {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
}, H = (e) => {
|
|
59
|
+
r.Blur(e);
|
|
60
|
+
let t = (e.currentTarget.textContent || "").replace(/\D/g, "");
|
|
61
|
+
e.currentTarget.textContent !== t && (e.currentTarget.textContent = t), v?.({
|
|
62
|
+
value: t,
|
|
47
63
|
name: p
|
|
48
64
|
}), M.current = !1;
|
|
49
|
-
},
|
|
50
|
-
return D && (
|
|
65
|
+
}, U = {};
|
|
66
|
+
return D && (U.edit = ""), U.inputMode = C ?? "numeric", m && (U.placeholder = m), g && a(O) ? /* @__PURE__ */ c(s, {}) : /* @__PURE__ */ l(t, {
|
|
51
67
|
type: "numeric",
|
|
52
68
|
label: h,
|
|
53
69
|
error: S,
|
|
@@ -60,19 +76,21 @@ function u({ value: u, form: d, onValue: f, name: p = "", placeholder: m = "", l
|
|
|
60
76
|
}) : null,
|
|
61
77
|
/* @__PURE__ */ c("div", {
|
|
62
78
|
ref: j,
|
|
63
|
-
onPaste:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
onPaste: B,
|
|
80
|
+
onDrop: V,
|
|
81
|
+
onDragOver: (e) => e.preventDefault(),
|
|
82
|
+
onInput: I,
|
|
83
|
+
onFocus: L,
|
|
84
|
+
onBlur: H,
|
|
85
|
+
onKeyDown: R,
|
|
86
|
+
onKeyUp: z,
|
|
69
87
|
spellCheck: !1,
|
|
70
88
|
role: "textbox",
|
|
71
89
|
tabIndex: D ? 0 : -1,
|
|
72
90
|
"aria-label": h || m || "Число",
|
|
73
91
|
contentEditable: D ? "plaintext-only" : !1,
|
|
74
92
|
suppressContentEditableWarning: !0,
|
|
75
|
-
...
|
|
93
|
+
...U
|
|
76
94
|
}),
|
|
77
95
|
b ? /* @__PURE__ */ c("span", {
|
|
78
96
|
className: e.affix,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TInput } from './type';
|
|
2
|
+
/** PercentField — процент 0…max с суффиксом «%». */
|
|
3
|
+
export type TPercent = TInput & {
|
|
4
|
+
min?: number;
|
|
5
|
+
max?: number;
|
|
6
|
+
/** Допускать дробную часть */
|
|
7
|
+
decimals?: number;
|
|
8
|
+
};
|
|
9
|
+
declare function PercentField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, min, max, decimals, inputmode, }: TPercent): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
declare namespace PercentField {
|
|
11
|
+
var displayName: string;
|
|
12
|
+
}
|
|
13
|
+
export default PercentField;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import e from "../styles/Field.module.js";
|
|
2
|
+
import t from "../core/Field.js";
|
|
3
|
+
import { useFormFieldState as n } from "../hooks/use.js";
|
|
4
|
+
import r, { asEditableClipboard as i } from "../events/onEvent.js";
|
|
5
|
+
import a from "react";
|
|
6
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
7
|
+
//#region src/lib/fields/Percent.tsx
|
|
8
|
+
function c(e, t, n) {
|
|
9
|
+
return Number.isFinite(e) ? Math.min(n, Math.max(t, e)) : t;
|
|
10
|
+
}
|
|
11
|
+
function l(e, t) {
|
|
12
|
+
let n = e.replace(",", ".").replace(/[^\d.]/g, "");
|
|
13
|
+
if (!n) return null;
|
|
14
|
+
let r = Number(n);
|
|
15
|
+
if (!Number.isFinite(r)) return null;
|
|
16
|
+
if (t <= 0) return Math.round(r);
|
|
17
|
+
let i = 10 ** t;
|
|
18
|
+
return Math.round(r * i) / i;
|
|
19
|
+
}
|
|
20
|
+
function u(e, t) {
|
|
21
|
+
return Number.isFinite(e) ? String(t <= 0 ? Math.round(e) : Number(e.toFixed(t))) : "";
|
|
22
|
+
}
|
|
23
|
+
function d({ value: d = 0, form: f, onValue: p, onBlur: m, name: h = "", placeholder: g = "0", label: _ = "", error: v = "", hidden: y = !1, disabled: b = !1, className: x = "", after: S, before: C = "", min: w = 0, max: T = 100, decimals: E = 0, inputmode: D = "decimal" }) {
|
|
24
|
+
let { value: O, version: k } = n(f, h, d), A = !b, j = c(Number(O), w, T), M = O == null || O === "" ? "" : u(j, E), N = a.useRef(null), P = a.useRef(!1);
|
|
25
|
+
a.useLayoutEffect(() => {
|
|
26
|
+
if (P.current) return;
|
|
27
|
+
let e = N.current;
|
|
28
|
+
e && e.textContent !== M && (e.textContent = M);
|
|
29
|
+
}, [M, k]);
|
|
30
|
+
let F = (e, t = !1) => {
|
|
31
|
+
let n = {
|
|
32
|
+
value: e,
|
|
33
|
+
name: h
|
|
34
|
+
};
|
|
35
|
+
f && h && (t ? f.setValue(n) : f.onValue(n)), p && p !== f?.onValue && p(n);
|
|
36
|
+
}, I = (e) => {
|
|
37
|
+
let t = (e.currentTarget.textContent || "").replace(/[^\d.,]/g, "");
|
|
38
|
+
e.currentTarget.textContent !== t && (e.currentTarget.textContent = t);
|
|
39
|
+
let n = l(t, E);
|
|
40
|
+
if (n === null) {
|
|
41
|
+
F("");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
F(c(n, w, T));
|
|
45
|
+
}, L = (e) => {
|
|
46
|
+
r.Blur(e), P.current = !1;
|
|
47
|
+
let t = l(e.currentTarget.textContent || "", E), n = t === null ? w : c(t, w, T), i = u(n, E);
|
|
48
|
+
e.currentTarget.textContent !== i && (e.currentTarget.textContent = i), F(n, !0), m?.({
|
|
49
|
+
value: n,
|
|
50
|
+
name: h
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
if (y && (O == null || O === "")) return null;
|
|
54
|
+
let R = {};
|
|
55
|
+
return g && (R.placeholder = g), A && (R.edit = ""), R.inputMode = D || "decimal", /* @__PURE__ */ s(t, {
|
|
56
|
+
type: "percent",
|
|
57
|
+
label: _,
|
|
58
|
+
error: v,
|
|
59
|
+
element_blok: { className: [x || ""] },
|
|
60
|
+
element_input: {
|
|
61
|
+
className: [e.w],
|
|
62
|
+
props: { "data-disabled": b || !A ? "true" : void 0 }
|
|
63
|
+
},
|
|
64
|
+
children: [
|
|
65
|
+
C ? /* @__PURE__ */ o("span", {
|
|
66
|
+
className: e.affix,
|
|
67
|
+
children: C
|
|
68
|
+
}) : null,
|
|
69
|
+
/* @__PURE__ */ o("div", {
|
|
70
|
+
ref: N,
|
|
71
|
+
onInput: I,
|
|
72
|
+
onFocus: (e) => {
|
|
73
|
+
P.current = !0, r.Focus({ target: e.currentTarget });
|
|
74
|
+
},
|
|
75
|
+
onBlur: L,
|
|
76
|
+
onPaste: (e) => r.Paste(12, i(e)),
|
|
77
|
+
onKeyDown: (e) => {
|
|
78
|
+
e.key === "Enter" && e.preventDefault(), e.key.length === 1 && !/[\d.,]/.test(e.key) && !e.metaKey && !e.ctrlKey && e.preventDefault();
|
|
79
|
+
},
|
|
80
|
+
spellCheck: !1,
|
|
81
|
+
role: "textbox",
|
|
82
|
+
tabIndex: A ? 0 : -1,
|
|
83
|
+
"aria-label": _ || g || "Процент",
|
|
84
|
+
contentEditable: A ? "plaintext-only" : !1,
|
|
85
|
+
suppressContentEditableWarning: !0,
|
|
86
|
+
...R
|
|
87
|
+
}),
|
|
88
|
+
S === void 0 ? /* @__PURE__ */ o("span", {
|
|
89
|
+
className: e.affix,
|
|
90
|
+
children: "%"
|
|
91
|
+
}) : /* @__PURE__ */ o("span", {
|
|
92
|
+
className: e.affix,
|
|
93
|
+
children: S
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
d.displayName = "PercentField";
|
|
99
|
+
//#endregion
|
|
100
|
+
export { d as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TInput } from './type';
|
|
2
|
+
type RadioOptionObject = Record<string | number, {
|
|
3
|
+
n: string;
|
|
4
|
+
s?: number;
|
|
5
|
+
}>;
|
|
6
|
+
type RadioOptionArrayItem = {
|
|
7
|
+
id?: string | number;
|
|
8
|
+
i?: string | number;
|
|
9
|
+
n: string;
|
|
10
|
+
s?: number;
|
|
11
|
+
};
|
|
12
|
+
type RadioOptions = RadioOptionObject | RadioOptionArrayItem[];
|
|
13
|
+
export type TRadio = TInput & {
|
|
14
|
+
options?: RadioOptions;
|
|
15
|
+
/** Горизонтальный ряд вместо колонки */
|
|
16
|
+
inline?: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare function RadioField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, options, inline, }: TRadio): import("react/jsx-runtime").JSX.Element | null;
|
|
19
|
+
declare namespace RadioField {
|
|
20
|
+
var displayName: string;
|
|
21
|
+
}
|
|
22
|
+
export default RadioField;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import e from "../styles/Field.module.js";
|
|
2
|
+
import t from "../core/Field.js";
|
|
3
|
+
import { useFormFieldState as n } from "../hooks/use.js";
|
|
4
|
+
import r from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
//#region src/lib/fields/Radio.tsx
|
|
7
|
+
function o(e) {
|
|
8
|
+
return e ? Array.isArray(e) ? e.map((e) => ({
|
|
9
|
+
id: e.id ?? e.i ?? "",
|
|
10
|
+
label: e.n || "",
|
|
11
|
+
hidden: e.s === 0
|
|
12
|
+
})).filter((e) => e.label.length > 0 && e.id !== "") : Object.keys(e).map((t) => {
|
|
13
|
+
let n = e[t], r = Number(t);
|
|
14
|
+
return {
|
|
15
|
+
id: Number.isFinite(r) && String(r) === t ? r : t,
|
|
16
|
+
label: n?.n || "",
|
|
17
|
+
hidden: n?.s === 0
|
|
18
|
+
};
|
|
19
|
+
}).filter((e) => e.label.length > 0) : [];
|
|
20
|
+
}
|
|
21
|
+
function s(e, t) {
|
|
22
|
+
return e === t ? !0 : String(e) === String(t);
|
|
23
|
+
}
|
|
24
|
+
function c({ value: c = "", form: l, onValue: u, onBlur: d, name: f = "", placeholder: p = "", label: m = "", error: h = "", hidden: g = !1, disabled: _ = !1, className: v = "", after: y = "", before: b = "", options: x, inline: S = !1 }) {
|
|
25
|
+
let { value: C } = n(l, f, c), w = !_, T = o(x).filter((e) => !e.hidden), E = r.useId(), D = (e) => {
|
|
26
|
+
let t = {
|
|
27
|
+
value: e,
|
|
28
|
+
name: f
|
|
29
|
+
};
|
|
30
|
+
l && f && l.setValue(t), u && u !== l?.onValue && u(t);
|
|
31
|
+
};
|
|
32
|
+
return g && (C == null || C === "") ? null : /* @__PURE__ */ a(t, {
|
|
33
|
+
type: "radio",
|
|
34
|
+
label: m,
|
|
35
|
+
error: h,
|
|
36
|
+
element_blok: { className: [v || ""] },
|
|
37
|
+
element_input: {
|
|
38
|
+
className: [e.radio, S ? e.radioInline : ""],
|
|
39
|
+
props: {
|
|
40
|
+
"data-disabled": _ || !w ? "true" : void 0,
|
|
41
|
+
role: "radiogroup",
|
|
42
|
+
"aria-label": m || p || "Выбор"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
children: [
|
|
46
|
+
b ? /* @__PURE__ */ i("span", {
|
|
47
|
+
className: e.affix,
|
|
48
|
+
children: b
|
|
49
|
+
}) : null,
|
|
50
|
+
/* @__PURE__ */ i("div", { children: T.map((t) => {
|
|
51
|
+
let n = s(C, t.id);
|
|
52
|
+
return /* @__PURE__ */ a("button", {
|
|
53
|
+
type: "button",
|
|
54
|
+
className: e.radioItem,
|
|
55
|
+
role: "radio",
|
|
56
|
+
"aria-checked": n,
|
|
57
|
+
"aria-disabled": _ || !w ? !0 : void 0,
|
|
58
|
+
disabled: _ || !w,
|
|
59
|
+
name: E,
|
|
60
|
+
onClick: () => {
|
|
61
|
+
w && D(t.id);
|
|
62
|
+
},
|
|
63
|
+
onBlur: () => {
|
|
64
|
+
!d || !f || d({
|
|
65
|
+
value: l ? l.getValue(f, C) : C,
|
|
66
|
+
name: f
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
children: [/* @__PURE__ */ i("span", {
|
|
70
|
+
className: e.radioDot,
|
|
71
|
+
"aria-hidden": !0,
|
|
72
|
+
children: n ? /* @__PURE__ */ i("span", { className: e.radioDotFill }) : null
|
|
73
|
+
}), /* @__PURE__ */ i("span", {
|
|
74
|
+
className: e.radioText,
|
|
75
|
+
children: t.label
|
|
76
|
+
})]
|
|
77
|
+
}, String(t.id));
|
|
78
|
+
}) }),
|
|
79
|
+
y ? /* @__PURE__ */ i("span", {
|
|
80
|
+
className: e.affix,
|
|
81
|
+
children: y
|
|
82
|
+
}) : null
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
c.displayName = "RadioField";
|
|
87
|
+
//#endregion
|
|
88
|
+
export { c as default };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TInput } from './type';
|
|
2
|
+
/** RatingField — оценка звёздами. Значение: число 0…max. */
|
|
3
|
+
export type TRating = TInput & {
|
|
4
|
+
max?: number;
|
|
5
|
+
/** Разрешить сброс повторным кликом по текущей оценке */
|
|
6
|
+
allowClear?: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare function RatingField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, max, allowClear, }: TRating): import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
declare namespace RatingField {
|
|
10
|
+
var displayName: string;
|
|
11
|
+
}
|
|
12
|
+
export default RatingField;
|