@t007/input 0.0.2 → 0.0.3
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/package.json +7 -9
- package/src/ts/types/index.d.ts +7 -7
- package/dist/index.global.js +0 -434
- package/dist/index.js +0 -429
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t007/input",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "A lightweight, pure JS input system.",
|
|
5
5
|
"author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"type": "module",
|
|
17
17
|
"main": "./dist/index.js",
|
|
18
18
|
"module": "./dist/index.js",
|
|
19
|
-
"
|
|
19
|
+
"unpkg": "./dist/index.global.js",
|
|
20
|
+
"jsdelivr": "./dist/index.global.js",
|
|
20
21
|
"types": "./src/ts/types/index.d.ts",
|
|
21
22
|
"style": "./src/css/index.css",
|
|
22
23
|
"sideEffects": [
|
|
@@ -26,17 +27,14 @@
|
|
|
26
27
|
".": {
|
|
27
28
|
"types": "./src/ts/types/index.d.ts",
|
|
28
29
|
"import": "./dist/index.js",
|
|
29
|
-
"default": "./dist/index.
|
|
30
|
+
"default": "./dist/index.js"
|
|
30
31
|
},
|
|
32
|
+
"./standalone": "./dist/standalone.js",
|
|
33
|
+
"./global": "./dist/index.global.js",
|
|
31
34
|
"./style.css": "./src/css/index.css"
|
|
32
35
|
},
|
|
33
36
|
"scripts": {
|
|
34
|
-
"build": "tsup
|
|
35
|
-
},
|
|
36
|
-
"tsup": {
|
|
37
|
-
"noExternal": [
|
|
38
|
-
"@t007/utils"
|
|
39
|
-
]
|
|
37
|
+
"build": "tsup --config ../../tsup.config.ts"
|
|
40
38
|
},
|
|
41
39
|
"files": [
|
|
42
40
|
"dist",
|
package/src/ts/types/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface FieldOptions extends Partial<
|
|
|
27
27
|
passwordHiddenIcon?: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export interface
|
|
30
|
+
export interface FormManager {
|
|
31
31
|
forms: HTMLCollectionOf<HTMLFormElement>;
|
|
32
32
|
violationKeys: string[];
|
|
33
33
|
init(): void;
|
|
@@ -47,15 +47,15 @@ export interface T007FormManager {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// BUNDLE EXPORTS & GLOBAL DECLARATIONS
|
|
50
|
-
export const
|
|
51
|
-
export const field =
|
|
52
|
-
export const handleFormValidation =
|
|
50
|
+
export const formManager: FormManager;
|
|
51
|
+
export const field = FormManager["field"];
|
|
52
|
+
export const handleFormValidation = FormManager["handleFormValidation"];
|
|
53
53
|
|
|
54
54
|
declare global {
|
|
55
55
|
interface T007Namespace {
|
|
56
|
-
FM:
|
|
57
|
-
field?:
|
|
58
|
-
handleFormValidation?:
|
|
56
|
+
FM: FormManager;
|
|
57
|
+
field?: FormManager["field"];
|
|
58
|
+
handleFormValidation?: FormManager["handleFormValidation"];
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
interface Window {
|
package/dist/index.global.js
DELETED
|
@@ -1,434 +0,0 @@
|
|
|
1
|
-
(function () {
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
// ../utils/dist/index.js
|
|
5
|
-
function isSameURL(src1, src2) {
|
|
6
|
-
if (typeof src1 !== "string" || typeof src2 !== "string" || !src1 || !src2) return false;
|
|
7
|
-
try {
|
|
8
|
-
const u1 = new URL(src1, window.location.href), u2 = new URL(src2, window.location.href);
|
|
9
|
-
return decodeURIComponent(u1.origin + u1.pathname) === decodeURIComponent(u2.origin + u2.pathname);
|
|
10
|
-
} catch {
|
|
11
|
-
return src1.replace(/\\/g, "/").split("?")[0].trim() === src2.replace(/\\/g, "/").split("?")[0].trim();
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function createEl(tag, props = {}, dataset = {}, styles = {}) {
|
|
15
|
-
return assignEl(tag ? document?.createElement(tag) : void 0, props, dataset, styles) ?? null;
|
|
16
|
-
}
|
|
17
|
-
function assignEl(el, props = {}, dataset = {}, styles = {}) {
|
|
18
|
-
if (!el) return;
|
|
19
|
-
for (const k of Object.keys(props)) if (props[k] !== void 0) el[k] = props[k];
|
|
20
|
-
for (const k of Object.keys(dataset)) if (dataset[k] !== void 0) el.dataset[k] = String(dataset[k]);
|
|
21
|
-
for (const k of Object.keys(styles)) if (styles[k] !== void 0) el.style[k] = styles[k];
|
|
22
|
-
}
|
|
23
|
-
function loadResource(src, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
|
|
24
|
-
w.t007._resourceCache ??= {};
|
|
25
|
-
if (w.t007._resourceCache[src]) return w.t007._resourceCache[src];
|
|
26
|
-
const existing = type === "script" ? Array.prototype.find.call(w.document.scripts, (s) => isSameURL(s.src, src)) : type === "style" ? Array.prototype.find.call(w.document.styleSheets, (s) => isSameURL(s.href, src)) : null;
|
|
27
|
-
if (existing) return w.t007._resourceCache[src] = Promise.resolve(existing);
|
|
28
|
-
w.t007._resourceCache[src] = new Promise((resolve, reject) => {
|
|
29
|
-
(function tryLoad(remaining, el) {
|
|
30
|
-
const onerror = () => {
|
|
31
|
-
el?.remove?.();
|
|
32
|
-
if (remaining > 1) {
|
|
33
|
-
setTimeout(tryLoad, 1e3, remaining - 1);
|
|
34
|
-
console.warn(`Retrying ${type} load (${attempts - remaining + 1}): ${src}...`);
|
|
35
|
-
} else {
|
|
36
|
-
delete w.t007._resourceCache[src];
|
|
37
|
-
reject(new Error(`${type} load failed after ${attempts} attempts: ${src}`));
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const url = retryKey && remaining < attempts ? `${src}${src.includes("?") ? "&" : "?"}_${retryKey}=${Date.now()}` : src;
|
|
41
|
-
if (type === "script") w.document.body.append(el = createEl("script", { src: url, type: module ? "module" : "text/javascript", crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
|
|
42
|
-
else if (type === "style") w.document.head.append(el = createEl("link", { rel: "stylesheet", href: url, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
|
|
43
|
-
else reject(new Error(`Unsupported resource type: ${type}`));
|
|
44
|
-
})(attempts);
|
|
45
|
-
});
|
|
46
|
-
return w.t007._resourceCache[src];
|
|
47
|
-
}
|
|
48
|
-
function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "tmg-video-controls-scroll-assist", vertical = true, horizontal = true } = {}) {
|
|
49
|
-
t007._scrollers ??= /* @__PURE__ */ new WeakMap();
|
|
50
|
-
t007._scroller_r_observer ??= new ResizeObserver((entries) => entries.forEach(({ target }) => t007._scrollers.get(target)?.update()));
|
|
51
|
-
t007._scroller_m_observer ??= new MutationObserver((entries) => {
|
|
52
|
-
const els = /* @__PURE__ */ new Set();
|
|
53
|
-
for (const entry of entries) {
|
|
54
|
-
let node = entry.target instanceof Element ? entry.target : null;
|
|
55
|
-
while (node && !t007._scrollers.has(node)) node = node.parentElement;
|
|
56
|
-
if (node) els.add(node);
|
|
57
|
-
}
|
|
58
|
-
for (const el2 of els) t007._scrollers.get(el2)?.update();
|
|
59
|
-
});
|
|
60
|
-
const parent = el?.parentElement;
|
|
61
|
-
if (!parent || t007._scrollers.has(el)) return;
|
|
62
|
-
const assist = {};
|
|
63
|
-
let scrollId = null, last = performance.now(), assistWidth = 20, assistHeight = 20;
|
|
64
|
-
const update = () => {
|
|
65
|
-
const hasInteractive = !!parent.querySelector('button, a[href], input, select, textarea, [contenteditable="true"], [tabindex]:not([tabindex="-1"])');
|
|
66
|
-
if (horizontal) {
|
|
67
|
-
const w = assist.left?.offsetWidth || assistWidth, check = hasInteractive ? el.clientWidth < w * 2 : false;
|
|
68
|
-
assist.left.style.display = check ? "none" : el.scrollLeft > 0 ? "block" : "none";
|
|
69
|
-
assist.right.style.display = check ? "none" : el.scrollLeft + el.clientWidth < el.scrollWidth - 1 ? "block" : "none";
|
|
70
|
-
assistWidth = w;
|
|
71
|
-
}
|
|
72
|
-
if (vertical) {
|
|
73
|
-
const h = assist.up?.offsetHeight || assistHeight, check = hasInteractive ? el.clientHeight < h * 2 : false;
|
|
74
|
-
assist.up.style.display = check ? "none" : el.scrollTop > 0 ? "block" : "none";
|
|
75
|
-
assist.down.style.display = check ? "none" : el.scrollTop + el.clientHeight < el.scrollHeight - 1 ? "block" : "none";
|
|
76
|
-
assistHeight = h;
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
const scroll = (dir) => {
|
|
80
|
-
const frame = () => {
|
|
81
|
-
const now = performance.now(), dt = now - last;
|
|
82
|
-
last = now;
|
|
83
|
-
const d = pxPerSecond * dt / 1e3;
|
|
84
|
-
if (dir === "left") el.scrollLeft = Math.max(0, el.scrollLeft - d);
|
|
85
|
-
if (dir === "right") el.scrollLeft = Math.min(el.scrollWidth - el.clientWidth, el.scrollLeft + d);
|
|
86
|
-
if (dir === "up") el.scrollTop = Math.max(0, el.scrollTop - d);
|
|
87
|
-
if (dir === "down") el.scrollTop = Math.min(el.scrollHeight - el.clientHeight, el.scrollTop + d);
|
|
88
|
-
scrollId = requestAnimationFrame(frame);
|
|
89
|
-
};
|
|
90
|
-
last = performance.now();
|
|
91
|
-
frame();
|
|
92
|
-
};
|
|
93
|
-
const stop = () => (cancelAnimationFrame(scrollId ?? 0), scrollId = null);
|
|
94
|
-
const addAssist = (dir) => {
|
|
95
|
-
const div = createEl("div", { className: assistClassName }, { scrollDirection: dir }, { display: "none" });
|
|
96
|
-
if (!div) return;
|
|
97
|
-
["pointerenter", "dragenter"].forEach((evt) => div.addEventListener(evt, () => scroll(dir)));
|
|
98
|
-
["pointerleave", "pointerup", "pointercancel", "dragleave", "dragend"].forEach((evt) => div.addEventListener(evt, stop));
|
|
99
|
-
dir === "left" || dir === "up" ? parent.insertBefore(div, el) : parent.append(div);
|
|
100
|
-
assist[dir] = div;
|
|
101
|
-
};
|
|
102
|
-
if (horizontal) ["left", "right"].forEach(addAssist);
|
|
103
|
-
if (vertical) ["up", "down"].forEach(addAssist);
|
|
104
|
-
el.addEventListener("scroll", update);
|
|
105
|
-
t007._scroller_r_observer.observe(el);
|
|
106
|
-
t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
|
|
107
|
-
t007._scrollers.set(el, {
|
|
108
|
-
update,
|
|
109
|
-
destroy() {
|
|
110
|
-
stop();
|
|
111
|
-
el.removeEventListener("scroll", update);
|
|
112
|
-
t007._scroller_r_observer.unobserve(el);
|
|
113
|
-
t007._scrollers.delete(el);
|
|
114
|
-
Object.values(assist).forEach((a) => a.remove());
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
return update(), t007._scrollers.get(el);
|
|
118
|
-
}
|
|
119
|
-
if (typeof window !== "undefined") {
|
|
120
|
-
window.t007 ??= {};
|
|
121
|
-
window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
|
|
122
|
-
window.T007_INPUT_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest`;
|
|
123
|
-
window.T007_DIALOG_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest`;
|
|
124
|
-
window.T007_TOAST_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest/dist/style.css`;
|
|
125
|
-
window.T007_INPUT_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest/dist/style.css`;
|
|
126
|
-
window.T007_DIALOG_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest/dist/style.css`;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// src/index.js
|
|
130
|
-
var T007_Form_Manager = {
|
|
131
|
-
forms: document.getElementsByClassName("t007-input-form"),
|
|
132
|
-
violationKeys: ["valueMissing", "typeMismatch", "patternMismatch", "stepMismatch", "tooShort", "tooLong", "rangeUnderflow", "rangeOverflow", "badInput", "customError"],
|
|
133
|
-
init() {
|
|
134
|
-
t007.FM.observeDOMForFields();
|
|
135
|
-
Array.from(t007.FM.forms).forEach(t007.FM.handleFormValidation);
|
|
136
|
-
},
|
|
137
|
-
observeDOMForFields() {
|
|
138
|
-
new MutationObserver((mutations) => {
|
|
139
|
-
for (const mutation of mutations) {
|
|
140
|
-
for (const node of mutation.addedNodes) {
|
|
141
|
-
if (!node.tagName || !(node?.classList?.contains("t007-input-field") || node?.querySelector?.(".t007-input-field"))) continue;
|
|
142
|
-
for (const field of [...node.querySelector(".t007-input-field") ? node.querySelectorAll(".t007-input-field") : [node]]) t007.FM.setUpField(field);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}).observe(document.body, { childList: true, subtree: true });
|
|
146
|
-
},
|
|
147
|
-
getFilesHelper(files, opts) {
|
|
148
|
-
if (!files || !files.length) return { violation: null, message: "" };
|
|
149
|
-
const totalFiles = files.length;
|
|
150
|
-
let totalSize = 0;
|
|
151
|
-
let currFiles = 0;
|
|
152
|
-
const setMaxError = (size, max, n = 0) => ({ violation: "rangeOverflow", message: n ? `File ${files.length > 1 ? n : ""} size of ${t007.FM.formatSize(size)} exceeds the per file maximum of ${t007.FM.formatSize(max)}` : `Total files size of ${t007.FM.formatSize(size)} exceeds the total maximum of ${t007.FM.formatSize(max)}` });
|
|
153
|
-
const setMinError = (size, min, n = 0) => ({ violation: "rangeUnderflow", message: n ? `File ${files.length > 1 ? n : ""} size of ${t007.FM.formatSize(size)} is less than the per file minimum of ${t007.FM.formatSize(min)}` : `Total files size of ${t007.FM.formatSize(size)} is less than the total minimum of ${t007.FM.formatSize(min)}` });
|
|
154
|
-
for (const file of files) {
|
|
155
|
-
currFiles++;
|
|
156
|
-
totalSize += file.size;
|
|
157
|
-
if (opts.accept) {
|
|
158
|
-
const acceptedTypes = opts.accept.split(",").map((type) => type.trim().replace(/^[*\.]+|[*\.]+$/g, "")).filter(Boolean) || [];
|
|
159
|
-
if (!acceptedTypes.some((type) => file.type.includes(type))) return { violation: "typeMismatch", message: `File${currFiles > 1 ? currFiles : ""} type of '${file.type}' is not accepted.` };
|
|
160
|
-
}
|
|
161
|
-
if (opts.maxSize && file.size > opts.maxSize) return setMaxError(file.size, opts.maxSize, currFiles);
|
|
162
|
-
if (opts.minSize && file.size < opts.minSize) return setMinError(file.size, opts.minSize, currFiles);
|
|
163
|
-
if (opts.multiple) {
|
|
164
|
-
if (opts.maxTotalSize && totalSize > opts.maxTotalSize) return setMaxError(totalSize, opts.maxTotalSize);
|
|
165
|
-
if (opts.minTotalSize && totalSize < opts.minTotalSize) return setMinError(totalSize, opts.minTotalSize);
|
|
166
|
-
if (opts.maxLength && totalFiles > opts.maxLength) return { violation: "tooLong", message: `Selected ${totalFiles} files exceeds the maximum of ${opts.maxLength} allowed file${opts.maxLength == 1 ? "" : "s"}` };
|
|
167
|
-
if (opts.minLength && totalFiles < opts.minLength) return { violation: "tooShort", message: `Selected ${totalFiles} files is less than the minimum of ${opts.minLength} allowed file${opts.minLength == 1 ? "" : "s"}` };
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return { violation: null, message: "" };
|
|
171
|
-
},
|
|
172
|
-
formatSize(size, decimals = 3, base = 1e3) {
|
|
173
|
-
if (size < base) return `${size} byte${size == 1 ? "" : "s"}`;
|
|
174
|
-
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], exponent = Math.min(Math.floor(Math.log(size) / Math.log(base)), units.length - 1);
|
|
175
|
-
return `${(size / Math.pow(base, exponent)).toFixed(decimals).replace(/\.0+$/, "")} ${units[exponent]}`;
|
|
176
|
-
},
|
|
177
|
-
togglePasswordType: (input) => input.type = input.type === "password" ? "text" : "password",
|
|
178
|
-
toggleFilled: (input) => input?.toggleAttribute("data-filled", input.type === "checkbox" || input.type === "radio" ? input.checked : input.value !== "" || input.files?.length > 0),
|
|
179
|
-
setFallbackHelper(field) {
|
|
180
|
-
const helperTextWrapper = field?.querySelector(".t007-input-helper-text-wrapper");
|
|
181
|
-
if (!helperTextWrapper || helperTextWrapper.querySelector(".t007-input-helper-text[data-violation='auto']")) return;
|
|
182
|
-
helperTextWrapper.append(createEl("p", { className: "t007-input-helper-text" }, { violation: "auto" }));
|
|
183
|
-
},
|
|
184
|
-
setFieldListeners(field) {
|
|
185
|
-
if (!field) return;
|
|
186
|
-
const input = field.querySelector(".t007-input"), floatingLabel = field.querySelector(".t007-input-floating-label"), eyeOpen = field.querySelector(".t007-input-password-visible-icon"), eyeClosed = field.querySelector(".t007-input-password-hidden-icon");
|
|
187
|
-
if (input.type === "file")
|
|
188
|
-
input.addEventListener("input", async () => {
|
|
189
|
-
const file = input.files?.[0], img = new Image();
|
|
190
|
-
img.onload = () => {
|
|
191
|
-
input.style.setProperty("--t007-input-image-src", `url(${src})`);
|
|
192
|
-
input.classList.add("t007-input-image-selected");
|
|
193
|
-
setTimeout(() => URL.revokeObjectURL(src), 1e3);
|
|
194
|
-
};
|
|
195
|
-
img.onerror = () => {
|
|
196
|
-
input.style.removeProperty("--t007-input-image-src");
|
|
197
|
-
input.classList.remove("t007-input-image-selected");
|
|
198
|
-
URL.revokeObjectURL(src);
|
|
199
|
-
};
|
|
200
|
-
let src;
|
|
201
|
-
if (file?.type?.startsWith("image")) src = URL.createObjectURL(file);
|
|
202
|
-
else if (file?.type?.startsWith("video")) {
|
|
203
|
-
src = await new Promise((resolve) => {
|
|
204
|
-
let video = createEl("video"), canvas = createEl("canvas"), context = canvas.getContext("2d");
|
|
205
|
-
video.ontimeupdate = () => {
|
|
206
|
-
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
|
207
|
-
canvas.toBlob((blob) => resolve(URL.createObjectURL(blob)));
|
|
208
|
-
URL.revokeObjectURL(video.src);
|
|
209
|
-
video = video.src = video.onloadedmetadata = video.ontimeupdate = null;
|
|
210
|
-
};
|
|
211
|
-
video.onloadeddata = () => video.currentTime = 3;
|
|
212
|
-
video.src = URL.createObjectURL(file);
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (!src) {
|
|
216
|
-
input.style.removeProperty("--t007-input-image-src");
|
|
217
|
-
input.classList.remove("t007-input-image-selected");
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
img.src = src;
|
|
221
|
-
});
|
|
222
|
-
if (floatingLabel) floatingLabel.ontransitionend = () => floatingLabel.classList.remove("t007-input-shake");
|
|
223
|
-
if (eyeOpen && eyeClosed) eyeOpen.onclick = eyeClosed.onclick = () => t007.FM.togglePasswordType(input);
|
|
224
|
-
initScrollAssist(field.querySelector(".t007-input-helper-text-wrapper"), { vertical: false });
|
|
225
|
-
},
|
|
226
|
-
setUpField(field) {
|
|
227
|
-
if (field.dataset.setUp) return;
|
|
228
|
-
t007.FM.toggleFilled(field.querySelector(".t007-input"));
|
|
229
|
-
t007.FM.setFallbackHelper(field);
|
|
230
|
-
t007.FM.setFieldListeners(field);
|
|
231
|
-
field.dataset.setUp = "true";
|
|
232
|
-
},
|
|
233
|
-
field({ isWrapper = false, label = "", type = "text", placeholder = "", custom = "", minSize, maxSize, minTotalSize, maxTotalSize, options = [], indeterminate = false, eyeToggler = true, passwordMeter = true, helperText = {}, className = "", fieldClassName = "", children, startIcon = "", endIcon = "", nativeIcon = "", passwordVisibleIcon = "", passwordHiddenIcon = "", ...otherProps }) {
|
|
234
|
-
const isSelect = type === "select", isTextArea = type === "textarea", isCheckboxOrRadio = type === "checkbox" || type === "radio", field = createEl("div", { className: `t007-input-field${isWrapper ? " t007-input-is-wrapper" : ""}${indeterminate ? " t007-input-indeterminate" : ""}${!!nativeIcon ? " t007-input-icon-override" : ""}${helperText === false ? " t007-input-no-helper" : ""}${fieldClassName ? ` ${fieldClassName}` : ""}` }), labelEl = createEl("label", { className: isCheckboxOrRadio ? `t007-input-${type}-wrapper` : "t007-input-wrapper" });
|
|
235
|
-
field.append(labelEl);
|
|
236
|
-
if (isCheckboxOrRadio) {
|
|
237
|
-
labelEl.innerHTML = `
|
|
238
|
-
<span class="t007-input-${type}-box">
|
|
239
|
-
<span class="t007-input-${type}-tag"></span>
|
|
240
|
-
</span>
|
|
241
|
-
<span class="t007-input-${type}-label">${label}</span>
|
|
242
|
-
`;
|
|
243
|
-
} else {
|
|
244
|
-
const outline = createEl("span", { className: "t007-input-outline" });
|
|
245
|
-
outline.innerHTML = `
|
|
246
|
-
<span class="t007-input-outline-leading"></span>
|
|
247
|
-
<span class="t007-input-outline-notch">
|
|
248
|
-
<span class="t007-input-floating-label">${label}</span>
|
|
249
|
-
</span>
|
|
250
|
-
<span class="t007-input-outline-trailing"></span>
|
|
251
|
-
`;
|
|
252
|
-
labelEl.append(outline);
|
|
253
|
-
}
|
|
254
|
-
const inputEl = field.inputEl = createEl(isTextArea ? "textarea" : isSelect ? "select" : "input", { className: `t007-input${className ? ` ${className}` : ""}`, placeholder });
|
|
255
|
-
if (isSelect && Array.isArray(options)) inputEl.innerHTML = options.map((opt) => typeof opt === "string" ? `<option value="${opt}">${opt}</option>` : `<option value="${opt.value}">${opt.option}</option>`).join("");
|
|
256
|
-
if (!isSelect && !isTextArea) inputEl.type = type;
|
|
257
|
-
if (custom) inputEl.setAttribute("custom", custom);
|
|
258
|
-
if (minSize) inputEl.setAttribute("minsize", minSize);
|
|
259
|
-
if (maxSize) inputEl.setAttribute("maxsize", maxSize);
|
|
260
|
-
if (minTotalSize) inputEl.setAttribute("mintotalsize", minTotalSize);
|
|
261
|
-
if (maxTotalSize) inputEl.setAttribute("maxtotalsize", maxTotalSize);
|
|
262
|
-
Object.keys(otherProps).forEach((key) => inputEl[key] = otherProps[key]);
|
|
263
|
-
labelEl.append(!isWrapper ? inputEl : children);
|
|
264
|
-
const nativeTypes = ["date", "time", "month", "datetime-local"];
|
|
265
|
-
if (nativeTypes.includes(type) && nativeIcon) labelEl.append(createEl("i", { className: "t007-input-icon t007-input-native-icon", innerHTML: nativeIcon }));
|
|
266
|
-
else if (endIcon) labelEl.append(createEl("i", { className: "t007-input-icon", innerHTML: endIcon }));
|
|
267
|
-
if (type === "password" && eyeToggler) {
|
|
268
|
-
labelEl.append(createEl("i", { role: "button", ariaLabel: "Show password", className: "t007-input-icon t007-input-password-visible-icon", innerHTML: passwordVisibleIcon || `<svg width="24" height="24"><path fill="rgba(0,0,0,.54)" d="M12 16q1.875 0 3.188-1.312Q16.5 13.375 16.5 11.5q0-1.875-1.312-3.188Q13.875 7 12 7q-1.875 0-3.188 1.312Q7.5 9.625 7.5 11.5q0 1.875 1.312 3.188Q10.125 16 12 16Zm0-1.8q-1.125 0-1.912-.788Q9.3 12.625 9.3 11.5t.788-1.913Q10.875 8.8 12 8.8t1.913.787q.787.788.787 1.913t-.787 1.912q-.788.788-1.913.788Zm0 4.8q-3.65 0-6.65-2.038-3-2.037-4.35-5.462 1.35-3.425 4.35-5.463Q8.35 4 12 4q3.65 0 6.65 2.037 3 2.038 4.35 5.463-1.35 3.425-4.35 5.462Q15.65 19 12 19Z"/></svg>` }));
|
|
269
|
-
labelEl.append(createEl("i", { role: "button", ariaLabel: "Hide password", className: "t007-input-icon t007-input-password-hidden-icon", innerHTML: passwordHiddenIcon || `<svg width="24" height="24"><path fill="rgba(0,0,0,.54)" d="m19.8 22.6-4.2-4.15q-.875.275-1.762.413Q12.95 19 12 19q-3.775 0-6.725-2.087Q2.325 14.825 1 11.5q.525-1.325 1.325-2.463Q3.125 7.9 4.15 7L1.4 4.2l1.4-1.4 18.4 18.4ZM12 16q.275 0 .512-.025.238-.025.513-.1l-5.4-5.4q-.075.275-.1.513-.025.237-.025.512 0 1.875 1.312 3.188Q10.125 16 12 16Zm7.3.45-3.175-3.15q.175-.425.275-.862.1-.438.1-.938 0-1.875-1.312-3.188Q13.875 7 12 7q-.5 0-.938.1-.437.1-.862.3L7.65 4.85q1.025-.425 2.1-.638Q10.825 4 12 4q3.775 0 6.725 2.087Q21.675 8.175 23 11.5q-.575 1.475-1.512 2.738Q20.55 15.5 19.3 16.45Zm-4.625-4.6-3-3q.7-.125 1.288.112.587.238 1.012.688.425.45.613 1.038.187.587.087 1.162Z"/></svg>` }));
|
|
270
|
-
}
|
|
271
|
-
if (helperText !== false) {
|
|
272
|
-
const helperLine = createEl("div", { className: "t007-input-helper-line" }), helperWrapper = createEl("div", { className: "t007-input-helper-text-wrapper", tabIndex: "-1" });
|
|
273
|
-
if (helperText.info) helperWrapper.append(createEl("p", { className: "t007-input-helper-text", textContent: helperText.info }, { violation: "none" }));
|
|
274
|
-
t007.FM?.violationKeys?.forEach((key) => helperText[key] && helperWrapper.append(createEl("p", { className: "t007-input-helper-text", textContent: helperText[key] }, { violation: key })));
|
|
275
|
-
helperLine.append(helperWrapper);
|
|
276
|
-
field.append(helperLine);
|
|
277
|
-
}
|
|
278
|
-
if (passwordMeter && type === "password") {
|
|
279
|
-
const meter = createEl("div", { className: "t007-input-password-meter" }, { strengthLevel: "1" });
|
|
280
|
-
meter.innerHTML = `
|
|
281
|
-
<div class="t007-input-password-strength-meter">
|
|
282
|
-
<div class="t007-input-p-weak"></div>
|
|
283
|
-
<div class="t007-input-p-fair"></div>
|
|
284
|
-
<div class="t007-input-p-strong"></div>
|
|
285
|
-
<div class="t007-input-p-very-strong"></div>
|
|
286
|
-
</div>
|
|
287
|
-
`;
|
|
288
|
-
field.append(meter);
|
|
289
|
-
}
|
|
290
|
-
return field;
|
|
291
|
-
},
|
|
292
|
-
handleFormValidation(form) {
|
|
293
|
-
if (!form?.classList.contains("t007-input-form") || form.dataset?.isValidating) return;
|
|
294
|
-
form.dataset.isValidating = "true";
|
|
295
|
-
form.validateOnClient = validateFormOnClient;
|
|
296
|
-
form.toggleGlobalError = toggleFormGlobalError;
|
|
297
|
-
const fields = form.getElementsByClassName("t007-input-field"), inputs = form.getElementsByClassName("t007-input");
|
|
298
|
-
Array.from(fields).forEach(t007.FM.setUpField);
|
|
299
|
-
form.addEventListener("input", ({ target }) => {
|
|
300
|
-
t007.FM.toggleFilled(target);
|
|
301
|
-
validateInput(target);
|
|
302
|
-
});
|
|
303
|
-
form.addEventListener("focusout", ({ target }) => validateInput(target, true));
|
|
304
|
-
form.addEventListener("submit", async (e) => {
|
|
305
|
-
toggleSubmitLoader(true);
|
|
306
|
-
try {
|
|
307
|
-
e.preventDefault();
|
|
308
|
-
if (!validateFormOnClient()) return;
|
|
309
|
-
if (form.validateOnServer && !await form.validateOnServer()) {
|
|
310
|
-
toggleFormGlobalError(true);
|
|
311
|
-
form.addEventListener("input", () => toggleFormGlobalError(false), { once: true, useCapture: true });
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
form.onSubmit ? form.onSubmit() : form.submit();
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error(error);
|
|
317
|
-
}
|
|
318
|
-
toggleSubmitLoader(false);
|
|
319
|
-
});
|
|
320
|
-
function toggleSubmitLoader(bool) {
|
|
321
|
-
form.classList.toggle("t007-input-submit-loading", bool);
|
|
322
|
-
}
|
|
323
|
-
function toggleError(input, bool, flag = false) {
|
|
324
|
-
const field = input.closest(".t007-input-field"), floatingLabel = field.querySelector(".t007-input-floating-label");
|
|
325
|
-
if (bool && flag) {
|
|
326
|
-
input.setAttribute("data-error", "");
|
|
327
|
-
floatingLabel?.classList.add("t007-input-shake");
|
|
328
|
-
} else if (!bool) input.removeAttribute("data-error");
|
|
329
|
-
toggleHelper(input, input.hasAttribute("data-error"));
|
|
330
|
-
}
|
|
331
|
-
function toggleHelper(input, bool) {
|
|
332
|
-
const field = input.closest(".t007-input-field"), violation = t007.FM.violationKeys.find((violation2) => input.Validity?.[violation2] || input.validity[violation2]) ?? "", helper = field.querySelector(`.t007-input-helper-text[data-violation="${violation}"]`), fallbackHelper = field.querySelector(`.t007-input-helper-text[data-violation="auto"]`);
|
|
333
|
-
input.closest(".t007-input-field").querySelectorAll(`.t007-input-helper-text:not([data-violation="${violation}"])`).forEach((helper2) => helper2?.classList.remove("t007-input-show"));
|
|
334
|
-
if (helper) helper.classList.toggle("t007-input-show", bool);
|
|
335
|
-
else if (fallbackHelper) {
|
|
336
|
-
fallbackHelper.textContent = input.validationMessage;
|
|
337
|
-
fallbackHelper.classList.toggle("t007-input-show", bool);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
function forceRevalidate(input) {
|
|
341
|
-
input.checkValidity();
|
|
342
|
-
input.dispatchEvent(new Event("input"));
|
|
343
|
-
}
|
|
344
|
-
function updatePasswordMeter(input) {
|
|
345
|
-
const passwordMeter = input.closest(".t007-input-field").querySelector(".t007-input-password-meter");
|
|
346
|
-
if (!passwordMeter) return;
|
|
347
|
-
const value = input.value?.trim();
|
|
348
|
-
let strengthLevel = 0;
|
|
349
|
-
if (value.length < Number(input.minLength ?? 0)) strengthLevel = 1;
|
|
350
|
-
else {
|
|
351
|
-
if (/[a-z]/.test(value)) strengthLevel++;
|
|
352
|
-
if (/[A-Z]/.test(value)) strengthLevel++;
|
|
353
|
-
if (/[0-9]/.test(value)) strengthLevel++;
|
|
354
|
-
if (/[\W_]/.test(value)) strengthLevel++;
|
|
355
|
-
}
|
|
356
|
-
passwordMeter.dataset.strengthLevel = strengthLevel;
|
|
357
|
-
}
|
|
358
|
-
function validateInput(input, flag = false) {
|
|
359
|
-
if (form.dataset.globalError || !input?.classList.contains("t007-input")) return;
|
|
360
|
-
updatePasswordMeter(input);
|
|
361
|
-
let value, errorBool;
|
|
362
|
-
switch (input.custom ?? input.getAttribute("custom")) {
|
|
363
|
-
case "password":
|
|
364
|
-
value = input.value?.trim();
|
|
365
|
-
if (value === "") break;
|
|
366
|
-
const confirmPasswordInput = Array.from(inputs).find((input2) => (input2.custom ?? input2.getAttribute("custom")) === "confirm-password");
|
|
367
|
-
if (!confirmPasswordInput) break;
|
|
368
|
-
const confirmPasswordValue = confirmPasswordInput.value?.trim();
|
|
369
|
-
confirmPasswordInput.setCustomValidity(value !== confirmPasswordValue ? "Both passwords do not match" : "");
|
|
370
|
-
toggleError(confirmPasswordInput, value !== confirmPasswordValue, flag);
|
|
371
|
-
break;
|
|
372
|
-
case "confirm_password":
|
|
373
|
-
value = input.value?.trim();
|
|
374
|
-
if (value === "") break;
|
|
375
|
-
const passwordInput = Array.from(inputs).find((input2) => (input2.custom ?? input2.getAttribute("custom")) === "password");
|
|
376
|
-
if (!passwordInput) break;
|
|
377
|
-
const passwordValue = passwordInput.value?.trim();
|
|
378
|
-
errorBool = value !== passwordValue;
|
|
379
|
-
input.setCustomValidity(errorBool ? "Both passwords do not match" : "");
|
|
380
|
-
break;
|
|
381
|
-
case "onward_date":
|
|
382
|
-
if (input.min) break;
|
|
383
|
-
input.min = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
384
|
-
forceRevalidate(input);
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
if (input.type === "file") {
|
|
388
|
-
input.Validity = {};
|
|
389
|
-
const { violation, message } = t007.FM.getFilesHelper(input.files ?? [], {
|
|
390
|
-
accept: input.accept,
|
|
391
|
-
multiple: input.multiple,
|
|
392
|
-
maxSize: input.maxSize ?? Number(input.getAttribute("maxsize")),
|
|
393
|
-
minSize: input.minSize ?? Number(input.getAttribute("minsize")),
|
|
394
|
-
maxTotalSize: input.maxTotalSize ?? Number(input.getAttribute("maxtotalsize")),
|
|
395
|
-
minTotalSize: input.minTotalSize ?? Number(input.getAttribute("mintotalsize")),
|
|
396
|
-
maxLength: input.maxLength ?? Number(input.getAttribute("maxlength")),
|
|
397
|
-
minLength: input.minLength ?? Number(input.getAttribute("minLength"))
|
|
398
|
-
});
|
|
399
|
-
errorBool = !!message;
|
|
400
|
-
input.setCustomValidity(message);
|
|
401
|
-
if (violation) input.Validity[violation] = true;
|
|
402
|
-
}
|
|
403
|
-
errorBool = errorBool ?? !input.validity?.valid;
|
|
404
|
-
toggleError(input, errorBool, flag);
|
|
405
|
-
if (errorBool) return;
|
|
406
|
-
if (input.type === "radio")
|
|
407
|
-
Array.from(inputs)?.filter((i) => i.name == input.name)?.forEach((radio) => toggleError(radio, errorBool, flag));
|
|
408
|
-
}
|
|
409
|
-
function validateFormOnClient() {
|
|
410
|
-
Array.from(inputs).forEach((input) => validateInput(input, true));
|
|
411
|
-
form.querySelector("input:invalid")?.focus();
|
|
412
|
-
return Array.from(inputs).every((input) => input.checkValidity());
|
|
413
|
-
}
|
|
414
|
-
function toggleFormGlobalError(bool) {
|
|
415
|
-
form.toggleAttribute("data-global-error", bool);
|
|
416
|
-
form.querySelectorAll(".t007-input-field").forEach((field) => {
|
|
417
|
-
field.querySelector(".t007-input")?.toggleAttribute("data-error", bool);
|
|
418
|
-
if (bool) field.querySelector(".t007-input-floating-label")?.classList.add("t007-input-shake");
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
if (typeof window !== "undefined") {
|
|
424
|
-
t007.FM = T007_Form_Manager;
|
|
425
|
-
t007.field = t007.FM.field;
|
|
426
|
-
t007.handleFormValidation = t007.FM.handleFormValidation;
|
|
427
|
-
window.field ??= t007.field;
|
|
428
|
-
window.handleFormValidation ??= t007.handleFormValidation;
|
|
429
|
-
console.log("%cT007 Input helpers attached to window!", "color: darkturquoise");
|
|
430
|
-
loadResource(T007_INPUT_CSS_SRC);
|
|
431
|
-
t007.FM.init();
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
})();
|
package/dist/index.js
DELETED
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
// ../utils/dist/index.js
|
|
2
|
-
function isSameURL(src1, src2) {
|
|
3
|
-
if (typeof src1 !== "string" || typeof src2 !== "string" || !src1 || !src2) return false;
|
|
4
|
-
try {
|
|
5
|
-
const u1 = new URL(src1, window.location.href), u2 = new URL(src2, window.location.href);
|
|
6
|
-
return decodeURIComponent(u1.origin + u1.pathname) === decodeURIComponent(u2.origin + u2.pathname);
|
|
7
|
-
} catch {
|
|
8
|
-
return src1.replace(/\\/g, "/").split("?")[0].trim() === src2.replace(/\\/g, "/").split("?")[0].trim();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
function createEl(tag, props = {}, dataset = {}, styles = {}) {
|
|
12
|
-
return assignEl(tag ? document?.createElement(tag) : void 0, props, dataset, styles) ?? null;
|
|
13
|
-
}
|
|
14
|
-
function assignEl(el, props = {}, dataset = {}, styles = {}) {
|
|
15
|
-
if (!el) return;
|
|
16
|
-
for (const k of Object.keys(props)) if (props[k] !== void 0) el[k] = props[k];
|
|
17
|
-
for (const k of Object.keys(dataset)) if (dataset[k] !== void 0) el.dataset[k] = String(dataset[k]);
|
|
18
|
-
for (const k of Object.keys(styles)) if (styles[k] !== void 0) el.style[k] = styles[k];
|
|
19
|
-
}
|
|
20
|
-
function loadResource(src, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
|
|
21
|
-
w.t007._resourceCache ??= {};
|
|
22
|
-
if (w.t007._resourceCache[src]) return w.t007._resourceCache[src];
|
|
23
|
-
const existing = type === "script" ? Array.prototype.find.call(w.document.scripts, (s) => isSameURL(s.src, src)) : type === "style" ? Array.prototype.find.call(w.document.styleSheets, (s) => isSameURL(s.href, src)) : null;
|
|
24
|
-
if (existing) return w.t007._resourceCache[src] = Promise.resolve(existing);
|
|
25
|
-
w.t007._resourceCache[src] = new Promise((resolve, reject) => {
|
|
26
|
-
(function tryLoad(remaining, el) {
|
|
27
|
-
const onerror = () => {
|
|
28
|
-
el?.remove?.();
|
|
29
|
-
if (remaining > 1) {
|
|
30
|
-
setTimeout(tryLoad, 1e3, remaining - 1);
|
|
31
|
-
console.warn(`Retrying ${type} load (${attempts - remaining + 1}): ${src}...`);
|
|
32
|
-
} else {
|
|
33
|
-
delete w.t007._resourceCache[src];
|
|
34
|
-
reject(new Error(`${type} load failed after ${attempts} attempts: ${src}`));
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const url = retryKey && remaining < attempts ? `${src}${src.includes("?") ? "&" : "?"}_${retryKey}=${Date.now()}` : src;
|
|
38
|
-
if (type === "script") w.document.body.append(el = createEl("script", { src: url, type: module ? "module" : "text/javascript", crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
|
|
39
|
-
else if (type === "style") w.document.head.append(el = createEl("link", { rel: "stylesheet", href: url, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
|
|
40
|
-
else reject(new Error(`Unsupported resource type: ${type}`));
|
|
41
|
-
})(attempts);
|
|
42
|
-
});
|
|
43
|
-
return w.t007._resourceCache[src];
|
|
44
|
-
}
|
|
45
|
-
function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "tmg-video-controls-scroll-assist", vertical = true, horizontal = true } = {}) {
|
|
46
|
-
t007._scrollers ??= /* @__PURE__ */ new WeakMap();
|
|
47
|
-
t007._scroller_r_observer ??= new ResizeObserver((entries) => entries.forEach(({ target }) => t007._scrollers.get(target)?.update()));
|
|
48
|
-
t007._scroller_m_observer ??= new MutationObserver((entries) => {
|
|
49
|
-
const els = /* @__PURE__ */ new Set();
|
|
50
|
-
for (const entry of entries) {
|
|
51
|
-
let node = entry.target instanceof Element ? entry.target : null;
|
|
52
|
-
while (node && !t007._scrollers.has(node)) node = node.parentElement;
|
|
53
|
-
if (node) els.add(node);
|
|
54
|
-
}
|
|
55
|
-
for (const el2 of els) t007._scrollers.get(el2)?.update();
|
|
56
|
-
});
|
|
57
|
-
const parent = el?.parentElement;
|
|
58
|
-
if (!parent || t007._scrollers.has(el)) return;
|
|
59
|
-
const assist = {};
|
|
60
|
-
let scrollId = null, last = performance.now(), assistWidth = 20, assistHeight = 20;
|
|
61
|
-
const update = () => {
|
|
62
|
-
const hasInteractive = !!parent.querySelector('button, a[href], input, select, textarea, [contenteditable="true"], [tabindex]:not([tabindex="-1"])');
|
|
63
|
-
if (horizontal) {
|
|
64
|
-
const w = assist.left?.offsetWidth || assistWidth, check = hasInteractive ? el.clientWidth < w * 2 : false;
|
|
65
|
-
assist.left.style.display = check ? "none" : el.scrollLeft > 0 ? "block" : "none";
|
|
66
|
-
assist.right.style.display = check ? "none" : el.scrollLeft + el.clientWidth < el.scrollWidth - 1 ? "block" : "none";
|
|
67
|
-
assistWidth = w;
|
|
68
|
-
}
|
|
69
|
-
if (vertical) {
|
|
70
|
-
const h = assist.up?.offsetHeight || assistHeight, check = hasInteractive ? el.clientHeight < h * 2 : false;
|
|
71
|
-
assist.up.style.display = check ? "none" : el.scrollTop > 0 ? "block" : "none";
|
|
72
|
-
assist.down.style.display = check ? "none" : el.scrollTop + el.clientHeight < el.scrollHeight - 1 ? "block" : "none";
|
|
73
|
-
assistHeight = h;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const scroll = (dir) => {
|
|
77
|
-
const frame = () => {
|
|
78
|
-
const now = performance.now(), dt = now - last;
|
|
79
|
-
last = now;
|
|
80
|
-
const d = pxPerSecond * dt / 1e3;
|
|
81
|
-
if (dir === "left") el.scrollLeft = Math.max(0, el.scrollLeft - d);
|
|
82
|
-
if (dir === "right") el.scrollLeft = Math.min(el.scrollWidth - el.clientWidth, el.scrollLeft + d);
|
|
83
|
-
if (dir === "up") el.scrollTop = Math.max(0, el.scrollTop - d);
|
|
84
|
-
if (dir === "down") el.scrollTop = Math.min(el.scrollHeight - el.clientHeight, el.scrollTop + d);
|
|
85
|
-
scrollId = requestAnimationFrame(frame);
|
|
86
|
-
};
|
|
87
|
-
last = performance.now();
|
|
88
|
-
frame();
|
|
89
|
-
};
|
|
90
|
-
const stop = () => (cancelAnimationFrame(scrollId ?? 0), scrollId = null);
|
|
91
|
-
const addAssist = (dir) => {
|
|
92
|
-
const div = createEl("div", { className: assistClassName }, { scrollDirection: dir }, { display: "none" });
|
|
93
|
-
if (!div) return;
|
|
94
|
-
["pointerenter", "dragenter"].forEach((evt) => div.addEventListener(evt, () => scroll(dir)));
|
|
95
|
-
["pointerleave", "pointerup", "pointercancel", "dragleave", "dragend"].forEach((evt) => div.addEventListener(evt, stop));
|
|
96
|
-
dir === "left" || dir === "up" ? parent.insertBefore(div, el) : parent.append(div);
|
|
97
|
-
assist[dir] = div;
|
|
98
|
-
};
|
|
99
|
-
if (horizontal) ["left", "right"].forEach(addAssist);
|
|
100
|
-
if (vertical) ["up", "down"].forEach(addAssist);
|
|
101
|
-
el.addEventListener("scroll", update);
|
|
102
|
-
t007._scroller_r_observer.observe(el);
|
|
103
|
-
t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
|
|
104
|
-
t007._scrollers.set(el, {
|
|
105
|
-
update,
|
|
106
|
-
destroy() {
|
|
107
|
-
stop();
|
|
108
|
-
el.removeEventListener("scroll", update);
|
|
109
|
-
t007._scroller_r_observer.unobserve(el);
|
|
110
|
-
t007._scrollers.delete(el);
|
|
111
|
-
Object.values(assist).forEach((a) => a.remove());
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
return update(), t007._scrollers.get(el);
|
|
115
|
-
}
|
|
116
|
-
if (typeof window !== "undefined") {
|
|
117
|
-
window.t007 ??= {};
|
|
118
|
-
window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
|
|
119
|
-
window.T007_INPUT_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest`;
|
|
120
|
-
window.T007_DIALOG_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest`;
|
|
121
|
-
window.T007_TOAST_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest/dist/style.css`;
|
|
122
|
-
window.T007_INPUT_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest/dist/style.css`;
|
|
123
|
-
window.T007_DIALOG_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest/dist/style.css`;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// src/index.js
|
|
127
|
-
var T007_Form_Manager = {
|
|
128
|
-
forms: document.getElementsByClassName("t007-input-form"),
|
|
129
|
-
violationKeys: ["valueMissing", "typeMismatch", "patternMismatch", "stepMismatch", "tooShort", "tooLong", "rangeUnderflow", "rangeOverflow", "badInput", "customError"],
|
|
130
|
-
init() {
|
|
131
|
-
t007.FM.observeDOMForFields();
|
|
132
|
-
Array.from(t007.FM.forms).forEach(t007.FM.handleFormValidation);
|
|
133
|
-
},
|
|
134
|
-
observeDOMForFields() {
|
|
135
|
-
new MutationObserver((mutations) => {
|
|
136
|
-
for (const mutation of mutations) {
|
|
137
|
-
for (const node of mutation.addedNodes) {
|
|
138
|
-
if (!node.tagName || !(node?.classList?.contains("t007-input-field") || node?.querySelector?.(".t007-input-field"))) continue;
|
|
139
|
-
for (const field of [...node.querySelector(".t007-input-field") ? node.querySelectorAll(".t007-input-field") : [node]]) t007.FM.setUpField(field);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}).observe(document.body, { childList: true, subtree: true });
|
|
143
|
-
},
|
|
144
|
-
getFilesHelper(files, opts) {
|
|
145
|
-
if (!files || !files.length) return { violation: null, message: "" };
|
|
146
|
-
const totalFiles = files.length;
|
|
147
|
-
let totalSize = 0;
|
|
148
|
-
let currFiles = 0;
|
|
149
|
-
const setMaxError = (size, max, n = 0) => ({ violation: "rangeOverflow", message: n ? `File ${files.length > 1 ? n : ""} size of ${t007.FM.formatSize(size)} exceeds the per file maximum of ${t007.FM.formatSize(max)}` : `Total files size of ${t007.FM.formatSize(size)} exceeds the total maximum of ${t007.FM.formatSize(max)}` });
|
|
150
|
-
const setMinError = (size, min, n = 0) => ({ violation: "rangeUnderflow", message: n ? `File ${files.length > 1 ? n : ""} size of ${t007.FM.formatSize(size)} is less than the per file minimum of ${t007.FM.formatSize(min)}` : `Total files size of ${t007.FM.formatSize(size)} is less than the total minimum of ${t007.FM.formatSize(min)}` });
|
|
151
|
-
for (const file of files) {
|
|
152
|
-
currFiles++;
|
|
153
|
-
totalSize += file.size;
|
|
154
|
-
if (opts.accept) {
|
|
155
|
-
const acceptedTypes = opts.accept.split(",").map((type) => type.trim().replace(/^[*\.]+|[*\.]+$/g, "")).filter(Boolean) || [];
|
|
156
|
-
if (!acceptedTypes.some((type) => file.type.includes(type))) return { violation: "typeMismatch", message: `File${currFiles > 1 ? currFiles : ""} type of '${file.type}' is not accepted.` };
|
|
157
|
-
}
|
|
158
|
-
if (opts.maxSize && file.size > opts.maxSize) return setMaxError(file.size, opts.maxSize, currFiles);
|
|
159
|
-
if (opts.minSize && file.size < opts.minSize) return setMinError(file.size, opts.minSize, currFiles);
|
|
160
|
-
if (opts.multiple) {
|
|
161
|
-
if (opts.maxTotalSize && totalSize > opts.maxTotalSize) return setMaxError(totalSize, opts.maxTotalSize);
|
|
162
|
-
if (opts.minTotalSize && totalSize < opts.minTotalSize) return setMinError(totalSize, opts.minTotalSize);
|
|
163
|
-
if (opts.maxLength && totalFiles > opts.maxLength) return { violation: "tooLong", message: `Selected ${totalFiles} files exceeds the maximum of ${opts.maxLength} allowed file${opts.maxLength == 1 ? "" : "s"}` };
|
|
164
|
-
if (opts.minLength && totalFiles < opts.minLength) return { violation: "tooShort", message: `Selected ${totalFiles} files is less than the minimum of ${opts.minLength} allowed file${opts.minLength == 1 ? "" : "s"}` };
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return { violation: null, message: "" };
|
|
168
|
-
},
|
|
169
|
-
formatSize(size, decimals = 3, base = 1e3) {
|
|
170
|
-
if (size < base) return `${size} byte${size == 1 ? "" : "s"}`;
|
|
171
|
-
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], exponent = Math.min(Math.floor(Math.log(size) / Math.log(base)), units.length - 1);
|
|
172
|
-
return `${(size / Math.pow(base, exponent)).toFixed(decimals).replace(/\.0+$/, "")} ${units[exponent]}`;
|
|
173
|
-
},
|
|
174
|
-
togglePasswordType: (input) => input.type = input.type === "password" ? "text" : "password",
|
|
175
|
-
toggleFilled: (input) => input?.toggleAttribute("data-filled", input.type === "checkbox" || input.type === "radio" ? input.checked : input.value !== "" || input.files?.length > 0),
|
|
176
|
-
setFallbackHelper(field) {
|
|
177
|
-
const helperTextWrapper = field?.querySelector(".t007-input-helper-text-wrapper");
|
|
178
|
-
if (!helperTextWrapper || helperTextWrapper.querySelector(".t007-input-helper-text[data-violation='auto']")) return;
|
|
179
|
-
helperTextWrapper.append(createEl("p", { className: "t007-input-helper-text" }, { violation: "auto" }));
|
|
180
|
-
},
|
|
181
|
-
setFieldListeners(field) {
|
|
182
|
-
if (!field) return;
|
|
183
|
-
const input = field.querySelector(".t007-input"), floatingLabel = field.querySelector(".t007-input-floating-label"), eyeOpen = field.querySelector(".t007-input-password-visible-icon"), eyeClosed = field.querySelector(".t007-input-password-hidden-icon");
|
|
184
|
-
if (input.type === "file")
|
|
185
|
-
input.addEventListener("input", async () => {
|
|
186
|
-
const file = input.files?.[0], img = new Image();
|
|
187
|
-
img.onload = () => {
|
|
188
|
-
input.style.setProperty("--t007-input-image-src", `url(${src})`);
|
|
189
|
-
input.classList.add("t007-input-image-selected");
|
|
190
|
-
setTimeout(() => URL.revokeObjectURL(src), 1e3);
|
|
191
|
-
};
|
|
192
|
-
img.onerror = () => {
|
|
193
|
-
input.style.removeProperty("--t007-input-image-src");
|
|
194
|
-
input.classList.remove("t007-input-image-selected");
|
|
195
|
-
URL.revokeObjectURL(src);
|
|
196
|
-
};
|
|
197
|
-
let src;
|
|
198
|
-
if (file?.type?.startsWith("image")) src = URL.createObjectURL(file);
|
|
199
|
-
else if (file?.type?.startsWith("video")) {
|
|
200
|
-
src = await new Promise((resolve) => {
|
|
201
|
-
let video = createEl("video"), canvas = createEl("canvas"), context = canvas.getContext("2d");
|
|
202
|
-
video.ontimeupdate = () => {
|
|
203
|
-
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
|
204
|
-
canvas.toBlob((blob) => resolve(URL.createObjectURL(blob)));
|
|
205
|
-
URL.revokeObjectURL(video.src);
|
|
206
|
-
video = video.src = video.onloadedmetadata = video.ontimeupdate = null;
|
|
207
|
-
};
|
|
208
|
-
video.onloadeddata = () => video.currentTime = 3;
|
|
209
|
-
video.src = URL.createObjectURL(file);
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
if (!src) {
|
|
213
|
-
input.style.removeProperty("--t007-input-image-src");
|
|
214
|
-
input.classList.remove("t007-input-image-selected");
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
img.src = src;
|
|
218
|
-
});
|
|
219
|
-
if (floatingLabel) floatingLabel.ontransitionend = () => floatingLabel.classList.remove("t007-input-shake");
|
|
220
|
-
if (eyeOpen && eyeClosed) eyeOpen.onclick = eyeClosed.onclick = () => t007.FM.togglePasswordType(input);
|
|
221
|
-
initScrollAssist(field.querySelector(".t007-input-helper-text-wrapper"), { vertical: false });
|
|
222
|
-
},
|
|
223
|
-
setUpField(field) {
|
|
224
|
-
if (field.dataset.setUp) return;
|
|
225
|
-
t007.FM.toggleFilled(field.querySelector(".t007-input"));
|
|
226
|
-
t007.FM.setFallbackHelper(field);
|
|
227
|
-
t007.FM.setFieldListeners(field);
|
|
228
|
-
field.dataset.setUp = "true";
|
|
229
|
-
},
|
|
230
|
-
field({ isWrapper = false, label = "", type = "text", placeholder = "", custom = "", minSize, maxSize, minTotalSize, maxTotalSize, options = [], indeterminate = false, eyeToggler = true, passwordMeter = true, helperText = {}, className = "", fieldClassName = "", children, startIcon = "", endIcon = "", nativeIcon = "", passwordVisibleIcon = "", passwordHiddenIcon = "", ...otherProps }) {
|
|
231
|
-
const isSelect = type === "select", isTextArea = type === "textarea", isCheckboxOrRadio = type === "checkbox" || type === "radio", field = createEl("div", { className: `t007-input-field${isWrapper ? " t007-input-is-wrapper" : ""}${indeterminate ? " t007-input-indeterminate" : ""}${!!nativeIcon ? " t007-input-icon-override" : ""}${helperText === false ? " t007-input-no-helper" : ""}${fieldClassName ? ` ${fieldClassName}` : ""}` }), labelEl = createEl("label", { className: isCheckboxOrRadio ? `t007-input-${type}-wrapper` : "t007-input-wrapper" });
|
|
232
|
-
field.append(labelEl);
|
|
233
|
-
if (isCheckboxOrRadio) {
|
|
234
|
-
labelEl.innerHTML = `
|
|
235
|
-
<span class="t007-input-${type}-box">
|
|
236
|
-
<span class="t007-input-${type}-tag"></span>
|
|
237
|
-
</span>
|
|
238
|
-
<span class="t007-input-${type}-label">${label}</span>
|
|
239
|
-
`;
|
|
240
|
-
} else {
|
|
241
|
-
const outline = createEl("span", { className: "t007-input-outline" });
|
|
242
|
-
outline.innerHTML = `
|
|
243
|
-
<span class="t007-input-outline-leading"></span>
|
|
244
|
-
<span class="t007-input-outline-notch">
|
|
245
|
-
<span class="t007-input-floating-label">${label}</span>
|
|
246
|
-
</span>
|
|
247
|
-
<span class="t007-input-outline-trailing"></span>
|
|
248
|
-
`;
|
|
249
|
-
labelEl.append(outline);
|
|
250
|
-
}
|
|
251
|
-
const inputEl = field.inputEl = createEl(isTextArea ? "textarea" : isSelect ? "select" : "input", { className: `t007-input${className ? ` ${className}` : ""}`, placeholder });
|
|
252
|
-
if (isSelect && Array.isArray(options)) inputEl.innerHTML = options.map((opt) => typeof opt === "string" ? `<option value="${opt}">${opt}</option>` : `<option value="${opt.value}">${opt.option}</option>`).join("");
|
|
253
|
-
if (!isSelect && !isTextArea) inputEl.type = type;
|
|
254
|
-
if (custom) inputEl.setAttribute("custom", custom);
|
|
255
|
-
if (minSize) inputEl.setAttribute("minsize", minSize);
|
|
256
|
-
if (maxSize) inputEl.setAttribute("maxsize", maxSize);
|
|
257
|
-
if (minTotalSize) inputEl.setAttribute("mintotalsize", minTotalSize);
|
|
258
|
-
if (maxTotalSize) inputEl.setAttribute("maxtotalsize", maxTotalSize);
|
|
259
|
-
Object.keys(otherProps).forEach((key) => inputEl[key] = otherProps[key]);
|
|
260
|
-
labelEl.append(!isWrapper ? inputEl : children);
|
|
261
|
-
const nativeTypes = ["date", "time", "month", "datetime-local"];
|
|
262
|
-
if (nativeTypes.includes(type) && nativeIcon) labelEl.append(createEl("i", { className: "t007-input-icon t007-input-native-icon", innerHTML: nativeIcon }));
|
|
263
|
-
else if (endIcon) labelEl.append(createEl("i", { className: "t007-input-icon", innerHTML: endIcon }));
|
|
264
|
-
if (type === "password" && eyeToggler) {
|
|
265
|
-
labelEl.append(createEl("i", { role: "button", ariaLabel: "Show password", className: "t007-input-icon t007-input-password-visible-icon", innerHTML: passwordVisibleIcon || `<svg width="24" height="24"><path fill="rgba(0,0,0,.54)" d="M12 16q1.875 0 3.188-1.312Q16.5 13.375 16.5 11.5q0-1.875-1.312-3.188Q13.875 7 12 7q-1.875 0-3.188 1.312Q7.5 9.625 7.5 11.5q0 1.875 1.312 3.188Q10.125 16 12 16Zm0-1.8q-1.125 0-1.912-.788Q9.3 12.625 9.3 11.5t.788-1.913Q10.875 8.8 12 8.8t1.913.787q.787.788.787 1.913t-.787 1.912q-.788.788-1.913.788Zm0 4.8q-3.65 0-6.65-2.038-3-2.037-4.35-5.462 1.35-3.425 4.35-5.463Q8.35 4 12 4q3.65 0 6.65 2.037 3 2.038 4.35 5.463-1.35 3.425-4.35 5.462Q15.65 19 12 19Z"/></svg>` }));
|
|
266
|
-
labelEl.append(createEl("i", { role: "button", ariaLabel: "Hide password", className: "t007-input-icon t007-input-password-hidden-icon", innerHTML: passwordHiddenIcon || `<svg width="24" height="24"><path fill="rgba(0,0,0,.54)" d="m19.8 22.6-4.2-4.15q-.875.275-1.762.413Q12.95 19 12 19q-3.775 0-6.725-2.087Q2.325 14.825 1 11.5q.525-1.325 1.325-2.463Q3.125 7.9 4.15 7L1.4 4.2l1.4-1.4 18.4 18.4ZM12 16q.275 0 .512-.025.238-.025.513-.1l-5.4-5.4q-.075.275-.1.513-.025.237-.025.512 0 1.875 1.312 3.188Q10.125 16 12 16Zm7.3.45-3.175-3.15q.175-.425.275-.862.1-.438.1-.938 0-1.875-1.312-3.188Q13.875 7 12 7q-.5 0-.938.1-.437.1-.862.3L7.65 4.85q1.025-.425 2.1-.638Q10.825 4 12 4q3.775 0 6.725 2.087Q21.675 8.175 23 11.5q-.575 1.475-1.512 2.738Q20.55 15.5 19.3 16.45Zm-4.625-4.6-3-3q.7-.125 1.288.112.587.238 1.012.688.425.45.613 1.038.187.587.087 1.162Z"/></svg>` }));
|
|
267
|
-
}
|
|
268
|
-
if (helperText !== false) {
|
|
269
|
-
const helperLine = createEl("div", { className: "t007-input-helper-line" }), helperWrapper = createEl("div", { className: "t007-input-helper-text-wrapper", tabIndex: "-1" });
|
|
270
|
-
if (helperText.info) helperWrapper.append(createEl("p", { className: "t007-input-helper-text", textContent: helperText.info }, { violation: "none" }));
|
|
271
|
-
t007.FM?.violationKeys?.forEach((key) => helperText[key] && helperWrapper.append(createEl("p", { className: "t007-input-helper-text", textContent: helperText[key] }, { violation: key })));
|
|
272
|
-
helperLine.append(helperWrapper);
|
|
273
|
-
field.append(helperLine);
|
|
274
|
-
}
|
|
275
|
-
if (passwordMeter && type === "password") {
|
|
276
|
-
const meter = createEl("div", { className: "t007-input-password-meter" }, { strengthLevel: "1" });
|
|
277
|
-
meter.innerHTML = `
|
|
278
|
-
<div class="t007-input-password-strength-meter">
|
|
279
|
-
<div class="t007-input-p-weak"></div>
|
|
280
|
-
<div class="t007-input-p-fair"></div>
|
|
281
|
-
<div class="t007-input-p-strong"></div>
|
|
282
|
-
<div class="t007-input-p-very-strong"></div>
|
|
283
|
-
</div>
|
|
284
|
-
`;
|
|
285
|
-
field.append(meter);
|
|
286
|
-
}
|
|
287
|
-
return field;
|
|
288
|
-
},
|
|
289
|
-
handleFormValidation(form) {
|
|
290
|
-
if (!form?.classList.contains("t007-input-form") || form.dataset?.isValidating) return;
|
|
291
|
-
form.dataset.isValidating = "true";
|
|
292
|
-
form.validateOnClient = validateFormOnClient;
|
|
293
|
-
form.toggleGlobalError = toggleFormGlobalError;
|
|
294
|
-
const fields = form.getElementsByClassName("t007-input-field"), inputs = form.getElementsByClassName("t007-input");
|
|
295
|
-
Array.from(fields).forEach(t007.FM.setUpField);
|
|
296
|
-
form.addEventListener("input", ({ target }) => {
|
|
297
|
-
t007.FM.toggleFilled(target);
|
|
298
|
-
validateInput(target);
|
|
299
|
-
});
|
|
300
|
-
form.addEventListener("focusout", ({ target }) => validateInput(target, true));
|
|
301
|
-
form.addEventListener("submit", async (e) => {
|
|
302
|
-
toggleSubmitLoader(true);
|
|
303
|
-
try {
|
|
304
|
-
e.preventDefault();
|
|
305
|
-
if (!validateFormOnClient()) return;
|
|
306
|
-
if (form.validateOnServer && !await form.validateOnServer()) {
|
|
307
|
-
toggleFormGlobalError(true);
|
|
308
|
-
form.addEventListener("input", () => toggleFormGlobalError(false), { once: true, useCapture: true });
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
form.onSubmit ? form.onSubmit() : form.submit();
|
|
312
|
-
} catch (error) {
|
|
313
|
-
console.error(error);
|
|
314
|
-
}
|
|
315
|
-
toggleSubmitLoader(false);
|
|
316
|
-
});
|
|
317
|
-
function toggleSubmitLoader(bool) {
|
|
318
|
-
form.classList.toggle("t007-input-submit-loading", bool);
|
|
319
|
-
}
|
|
320
|
-
function toggleError(input, bool, flag = false) {
|
|
321
|
-
const field = input.closest(".t007-input-field"), floatingLabel = field.querySelector(".t007-input-floating-label");
|
|
322
|
-
if (bool && flag) {
|
|
323
|
-
input.setAttribute("data-error", "");
|
|
324
|
-
floatingLabel?.classList.add("t007-input-shake");
|
|
325
|
-
} else if (!bool) input.removeAttribute("data-error");
|
|
326
|
-
toggleHelper(input, input.hasAttribute("data-error"));
|
|
327
|
-
}
|
|
328
|
-
function toggleHelper(input, bool) {
|
|
329
|
-
const field = input.closest(".t007-input-field"), violation = t007.FM.violationKeys.find((violation2) => input.Validity?.[violation2] || input.validity[violation2]) ?? "", helper = field.querySelector(`.t007-input-helper-text[data-violation="${violation}"]`), fallbackHelper = field.querySelector(`.t007-input-helper-text[data-violation="auto"]`);
|
|
330
|
-
input.closest(".t007-input-field").querySelectorAll(`.t007-input-helper-text:not([data-violation="${violation}"])`).forEach((helper2) => helper2?.classList.remove("t007-input-show"));
|
|
331
|
-
if (helper) helper.classList.toggle("t007-input-show", bool);
|
|
332
|
-
else if (fallbackHelper) {
|
|
333
|
-
fallbackHelper.textContent = input.validationMessage;
|
|
334
|
-
fallbackHelper.classList.toggle("t007-input-show", bool);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
function forceRevalidate(input) {
|
|
338
|
-
input.checkValidity();
|
|
339
|
-
input.dispatchEvent(new Event("input"));
|
|
340
|
-
}
|
|
341
|
-
function updatePasswordMeter(input) {
|
|
342
|
-
const passwordMeter = input.closest(".t007-input-field").querySelector(".t007-input-password-meter");
|
|
343
|
-
if (!passwordMeter) return;
|
|
344
|
-
const value = input.value?.trim();
|
|
345
|
-
let strengthLevel = 0;
|
|
346
|
-
if (value.length < Number(input.minLength ?? 0)) strengthLevel = 1;
|
|
347
|
-
else {
|
|
348
|
-
if (/[a-z]/.test(value)) strengthLevel++;
|
|
349
|
-
if (/[A-Z]/.test(value)) strengthLevel++;
|
|
350
|
-
if (/[0-9]/.test(value)) strengthLevel++;
|
|
351
|
-
if (/[\W_]/.test(value)) strengthLevel++;
|
|
352
|
-
}
|
|
353
|
-
passwordMeter.dataset.strengthLevel = strengthLevel;
|
|
354
|
-
}
|
|
355
|
-
function validateInput(input, flag = false) {
|
|
356
|
-
if (form.dataset.globalError || !input?.classList.contains("t007-input")) return;
|
|
357
|
-
updatePasswordMeter(input);
|
|
358
|
-
let value, errorBool;
|
|
359
|
-
switch (input.custom ?? input.getAttribute("custom")) {
|
|
360
|
-
case "password":
|
|
361
|
-
value = input.value?.trim();
|
|
362
|
-
if (value === "") break;
|
|
363
|
-
const confirmPasswordInput = Array.from(inputs).find((input2) => (input2.custom ?? input2.getAttribute("custom")) === "confirm-password");
|
|
364
|
-
if (!confirmPasswordInput) break;
|
|
365
|
-
const confirmPasswordValue = confirmPasswordInput.value?.trim();
|
|
366
|
-
confirmPasswordInput.setCustomValidity(value !== confirmPasswordValue ? "Both passwords do not match" : "");
|
|
367
|
-
toggleError(confirmPasswordInput, value !== confirmPasswordValue, flag);
|
|
368
|
-
break;
|
|
369
|
-
case "confirm_password":
|
|
370
|
-
value = input.value?.trim();
|
|
371
|
-
if (value === "") break;
|
|
372
|
-
const passwordInput = Array.from(inputs).find((input2) => (input2.custom ?? input2.getAttribute("custom")) === "password");
|
|
373
|
-
if (!passwordInput) break;
|
|
374
|
-
const passwordValue = passwordInput.value?.trim();
|
|
375
|
-
errorBool = value !== passwordValue;
|
|
376
|
-
input.setCustomValidity(errorBool ? "Both passwords do not match" : "");
|
|
377
|
-
break;
|
|
378
|
-
case "onward_date":
|
|
379
|
-
if (input.min) break;
|
|
380
|
-
input.min = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
381
|
-
forceRevalidate(input);
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
if (input.type === "file") {
|
|
385
|
-
input.Validity = {};
|
|
386
|
-
const { violation, message } = t007.FM.getFilesHelper(input.files ?? [], {
|
|
387
|
-
accept: input.accept,
|
|
388
|
-
multiple: input.multiple,
|
|
389
|
-
maxSize: input.maxSize ?? Number(input.getAttribute("maxsize")),
|
|
390
|
-
minSize: input.minSize ?? Number(input.getAttribute("minsize")),
|
|
391
|
-
maxTotalSize: input.maxTotalSize ?? Number(input.getAttribute("maxtotalsize")),
|
|
392
|
-
minTotalSize: input.minTotalSize ?? Number(input.getAttribute("mintotalsize")),
|
|
393
|
-
maxLength: input.maxLength ?? Number(input.getAttribute("maxlength")),
|
|
394
|
-
minLength: input.minLength ?? Number(input.getAttribute("minLength"))
|
|
395
|
-
});
|
|
396
|
-
errorBool = !!message;
|
|
397
|
-
input.setCustomValidity(message);
|
|
398
|
-
if (violation) input.Validity[violation] = true;
|
|
399
|
-
}
|
|
400
|
-
errorBool = errorBool ?? !input.validity?.valid;
|
|
401
|
-
toggleError(input, errorBool, flag);
|
|
402
|
-
if (errorBool) return;
|
|
403
|
-
if (input.type === "radio")
|
|
404
|
-
Array.from(inputs)?.filter((i) => i.name == input.name)?.forEach((radio) => toggleError(radio, errorBool, flag));
|
|
405
|
-
}
|
|
406
|
-
function validateFormOnClient() {
|
|
407
|
-
Array.from(inputs).forEach((input) => validateInput(input, true));
|
|
408
|
-
form.querySelector("input:invalid")?.focus();
|
|
409
|
-
return Array.from(inputs).every((input) => input.checkValidity());
|
|
410
|
-
}
|
|
411
|
-
function toggleFormGlobalError(bool) {
|
|
412
|
-
form.toggleAttribute("data-global-error", bool);
|
|
413
|
-
form.querySelectorAll(".t007-input-field").forEach((field) => {
|
|
414
|
-
field.querySelector(".t007-input")?.toggleAttribute("data-error", bool);
|
|
415
|
-
if (bool) field.querySelector(".t007-input-floating-label")?.classList.add("t007-input-shake");
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
if (typeof window !== "undefined") {
|
|
421
|
-
t007.FM = T007_Form_Manager;
|
|
422
|
-
t007.field = t007.FM.field;
|
|
423
|
-
t007.handleFormValidation = t007.FM.handleFormValidation;
|
|
424
|
-
window.field ??= t007.field;
|
|
425
|
-
window.handleFormValidation ??= t007.handleFormValidation;
|
|
426
|
-
console.log("%cT007 Input helpers attached to window!", "color: darkturquoise");
|
|
427
|
-
loadResource(T007_INPUT_CSS_SRC);
|
|
428
|
-
t007.FM.init();
|
|
429
|
-
}
|