UNPKG

vexip-ui

Version:

A Vue 3 UI library, Highly customizability, full TypeScript, performance pretty good

498 lines (497 loc) 16.7 kB
import { isVNode as Qe, defineComponent as Ue, ref as f, reactive as Ze, computed as g, shallowRef as Ee, watch as F, onMounted as et, nextTick as ie, createVNode as l, TransitionGroup as tt, Transition as at, renderSlot as D, toRef as he, mergeProps as nt } from "vue"; import "../button/index.mjs"; import "../icon/index.mjs"; import "../spin/index.mjs"; import "../tooltip/index.mjs"; import "../form/index.mjs"; import "../renderer/index.mjs"; import "./captcha-slider.vue.mjs"; import { useProps as it, createSizeProp as lt, createIconProp as ve, useNameHelper as rt, useLocale as ot, useIcons as st, emitEvent as T } from "@vexip-ui/config"; import { useSetTimeout as ut, createSlotRender as ct } from "@vexip-ui/hooks"; import { isNull as pe, isClient as me, randomHardColor as dt, nextFrameOnce as ft, random as G, ensureArray as gt } from "@vexip-ui/utils"; import { captchaProps as ht } from "./props.mjs"; import { squarePath as be, heartPath as vt, shieldPath as pt, puzzlePath as mt } from "./hollow-paths.mjs"; import { useFieldStore as bt } from "../form/helper.mjs"; import yt from "./captcha-slider.vue2.mjs"; import M from "../renderer/renderer.mjs"; import xt from "../icon/icon.mjs"; import wt from "../spin/spin.vue2.mjs"; import St from "../button/button.mjs"; import Tt from "../tooltip/tooltip.mjs"; function ye(W) { return typeof W == "function" || Object.prototype.toString.call(W) === "[object Object]" && !Qe(W); } const jt = /* @__PURE__ */ Ue({ name: "Captcha", props: ht, emits: ["update:visible"], setup(W, { slots: P, expose: xe }) { const { idFor: we, labelId: Se, disabled: Te, loading: Pe, validateField: ke, setFieldValue: Ce } = bt(Be), t = it("captcha", W, { type: "slide", slideTarget: { default: null, validator: (e) => pe(e) ? !0 : Array.isArray(e) ? e[0] >= 0 && e[0] <= 100 && e[1] >= 0 && e[1] <= 100 : e >= 0 && e <= 100 }, title: null, tip: null, successTip: null, failTip: null, image: null, tolerance: { default: 1, validator: (e) => e >= 0 }, canvasSize: () => [1e3, 600], refreshIcon: ve(), disabled: () => Te.value, loading: () => Pe.value, loadingIcon: ve(), loadingEffect: null, onBeforeTest: { default: null, isFunc: !0 }, texts: { default: () => [], validator: (e) => !e.find((a) => a.length > 1) }, failLimit: 0, remotePoint: !1, useTrigger: !1, triggerSize: lt(), triggerText: null, transfer: !1, hideDelay: { default: 3e3, validator: (e) => e >= 0 }, hollowShape: { default: be, isFunc: !0 }, slots: () => ({}) }), n = rt("captcha"), k = ot("captcha"), J = st(), { timer: le } = ut(), R = f(ae(t.slideTarget)), N = f(!1), c = Ze([]), K = f(!1), $ = f(!1), C = f(!1), X = f(0), b = f(!1), Q = f(), y = f(), U = f(), v = f(), re = g(() => { var e; return (e = v.value) == null ? void 0 : e.track; }), r = g(() => { var e; return !!((e = v.value) != null && e.isSuccess) || $.value; }), Ie = g(() => { var e; return ((e = v.value) == null ? void 0 : e.currentLeft) || 0; }), oe = g(() => { var e; return (e = v.value) == null ? void 0 : e.resetting; }), ze = g(() => R.value[0]), A = f(!1), Y = Ee(Promise.resolve()), Z = [], se = 0.108; let O = !1, d, x; const _ = g(() => t.loading || A.value || K.value), ue = g(() => t.failLimit > 0 && X.value >= t.failLimit), Le = g(() => [n.b(), n.bs("vars"), n.bm(t.type), { [n.bm("success")]: r.value, [n.bm("fail")]: !r.value && C.value, [n.bm("dragging")]: N.value, [n.bm("disabled")]: t.disabled, [n.bm("loading")]: _.value, [n.bm("fail-locked")]: ue.value }]), Fe = g(() => ({ left: `${Ie.value}%`, [n.cv("trigger-transition")]: oe.value ? "left 250ms ease" : void 0 })), E = g(() => [t.canvasSize[0] || 1e3, t.canvasSize[1] || 600]), H = g(() => t.disabled || r.value || _.value); F(() => t.slideTarget, (e) => { R.value = ae(e); }), F([() => t.image, Q], async () => { d = void 0, await (Y.value = j()), V(); }), F([R, () => t.canvasSize[0], () => t.canvasSize[1], () => t.hollowShape], V), F([() => t.type, () => t.remotePoint], () => { t.type !== "slide" && t.remotePoint && typeof t.onBeforeTest != "function" && console.warn("[vexip-ui:Captcha] You should specify 'on-before-test' prop to valid the captcha if you are using the 'point' type in remote"); }, { immediate: !0 }), F([() => t.type, () => t.texts, () => t.texts.length, () => t.remotePoint], () => { t.type === "point" && t.texts.length && !t.remotePoint && d && V(); }), F(b, async (e) => { e && (await (Y.value = j()), V()); }), F(r, (e) => { e && t.useTrigger && b.value && (clearTimeout(le.hideTrigger), le.hideTrigger = setTimeout(() => { b.value = !1; }, t.hideDelay)); }), et(async () => { await (Y.value = j()), te(); }), xe({ dragging: N, resetting: oe, isSuccess: r, imageLoading: A, imagePromise: Y, wrapper: Q, canvas: y, subCanvas: U, slider: v, reset: Re }); let ee; async function j() { if (d) return; A.value = !0, ee = `${Date.now()}${Math.round(Math.random() * 1e7)}`; const e = ee, a = typeof t.image == "function" ? await t.image() : t.image; await new Promise((i) => { if (!me || e !== ee || !a) { i(); return; } d = new Image(), O = !1, d.src = a, a.trim().startsWith("data:image") ? (O = !0, i()) : d.onload = () => { O = !0, i(); }; }).finally(() => { A.value = !1; }); } function ce() { var L; const e = y.value, a = (L = e == null ? void 0 : e.getContext) == null ? void 0 : L.call(e, "2d"); if (!d || !e || !a) return; const { width: i, height: s } = e; if (a.drawImage(d, 0, 0, i, s), !t.texts.length || t.remotePoint) return; Z.length = 0; const o = Math.max(i, s) * se; a.textBaseline = "middle", a.textAlign = "center", a.font = `bold ${o}px sans-serif`, a.lineWidth = 2, a.strokeStyle = "#fff"; const B = (m, u, h, q = 0, ne = dt()) => { a.save(), a.translate(u, h), q && a.rotate(q * Math.PI), a.fillStyle = ne, a.fillText(m, 0, 0), a.strokeText(m, 0, 0), a.restore(); }, I = a.measureText(t.texts[0]), p = Math.max(o, I.width) * 1.2, z = Math.max(o, I.fontBoundingBoxAscent + I.fontBoundingBoxDescent) * 1.2; let w = -2 * o, S = -2 * o; for (const m of t.texts) { let u = w, h = S; for (; Math.abs(u - w) < p && Math.abs(h - S) < z; ) u = i * 0.1 + Math.random() * i * 0.8, h = s * 0.1 + Math.random() * s * 0.8; w = u, S = h, Z.push([u / i * 100, h / s * 100]), B(m, u, h, Math.random() * 2); } } function Me() { if (typeof t.hollowShape == "function") return t.hollowShape; switch (t.hollowShape) { case "puzzle": return mt; case "shield": return pt; case "heart": return vt; default: return be; } } function te() { var fe, ge; const e = y.value, a = (fe = e == null ? void 0 : e.getContext) == null ? void 0 : fe.call(e, "2d"), i = U.value, s = (ge = i == null ? void 0 : i.getContext) == null ? void 0 : ge.call(i, "2d"); if (!d || !O || !e || !a || !t.image) return; if (t.type === "point") { ce(); return; } if (!i || !s || !re.value) return; if (!x) { if (!me) return; x = document.createElement("canvas"); } x.width = e.width, x.height = e.height; const o = x.getContext("2d"); if (!o) return; a.clearRect(0, 0, e.width, e.height), s.clearRect(0, 0, i.width, i.height), o.clearRect(0, 0, x.width, x.height); const B = e.getBoundingClientRect(), I = re.value.getBoundingClientRect(), p = (B.width - I.width) / B.width * e.width, z = p / 2 + R.value[0] * (e.width - p) * 0.01, w = R.value[1] * e.height * 0.01, S = Me(); o.beginPath(), o.strokeStyle = "rgba(255, 255, 255, 0.5)", o.lineWidth = 4; const [L, m, u, h] = S({ ctx: o, x: z, y: w, width: t.canvasSize[0], height: t.canvasSize[1] }); o.stroke(), o.clip(), o.drawImage(d, 0, 0, e.width, e.height); const q = z - L, ne = (u * 0.5 - q) / u * 100; i.style.transform = `translate3d(${ne - 50}%, 0, 0)`, i.width = u, s.drawImage(x, L, m, u, h, 0, m, u, h), a.save(), a.beginPath(), a.fillStyle = "rgba(255, 255, 255, 0.75)", a.strokeStyle = "rgba(255, 255, 255, 0.5)", a.lineWidth = 10, S({ ctx: a, x: z, y: w, width: t.canvasSize[0], height: t.canvasSize[1] }), a.stroke(), a.fill(), a.restore(), a.globalCompositeOperation = "destination-over", a.drawImage(d, 0, 0, e.width, e.height); } function V() { ft(te); } async function Re(e) { var a; e && (d = void 0, await (Y.value = j()), te()), $.value = !1, C.value = !1, X.value = 0, R.value = ae(), c.length = 0, (a = v.value) == null || a.reset(), t.type === "point" && ce(); } function ae(e = t.slideTarget) { if (pe(e)) return [G(75, 25), G(75, 25)]; const [a = G(75, 25), i = G(75, 25)] = gt(e); return [a, i]; } function Be(e) { var a; (a = v.value) == null || a.focus(e); } function De(e) { N.value = !0, T(t.onDragStart, e); } function We(e) { T(t.onDrag, e); } function $e(e) { N.value = !1, T(t.onDragEnd, e); } function Ye(e) { $.value = !0, C.value = !1, T(t.onSuccess, e), Ce(e), ke(); } function _e() { C.value = !0, ++X.value, T(t.onFail); } function He() { !H.value && T(t.onRefresh); } function Ne(e) { e.stopPropagation(); } async function Xe() { if (_.value) return; c.length = t.texts.length; let e = t.remotePoint, a; if (!t.remotePoint && y.value) { const { width: i, height: s } = y.value, o = Math.max(i, s) * se, B = o / i * 50 + t.tolerance, I = o / s * 50 + t.tolerance; e = !0; for (let p = 0, z = c.length; p < z; ++p) { const [w, S] = c[p], [L, m] = Z[p]; if (Math.abs(w - L) > B || Math.abs(S - m) > I) { e = !1; break; } } } typeof t.onBeforeTest == "function" && (ie(() => { K.value = !0; }), a = await t.onBeforeTest(c.flat()), ie(() => { K.value = !1; })), !e || a === !1 ? ($.value = !1, c.length = 0, C.value = !0, ++X.value, T(t.onFail)) : ($.value = !0, C.value = !1, T(t.onSuccess, c.flat())); } function Ae(e) { if (t.type !== "point" || H.value || !y.value) return; const { clientWidth: a, clientHeight: i } = y.value, { offsetX: s, offsetY: o } = e; c.push([s / a * 100, o / i * 100]), c.length >= t.texts.length && ie(Xe); } function Oe(e, a) { a.stopPropagation(), !(H.value || e !== c.length - 1) && c.pop(); } function je() { r.value || (b.value = !0); } function Ve() { let e; return l("div", { class: [n.be("image"), H.value && n.bem("image", "locked")], onClick: Ae }, [t.image && l("div", { class: n.be("image-inner") }, [l("canvas", { ref: y, class: n.be("canvas"), width: E.value[0], height: E.value[1] }, null), t.type === "slide" && l("div", { class: n.be("sub-image") }, [l("canvas", { ref: U, class: n.be("sub-canvas"), height: E.value[1], style: Fe.value }, null)])]), t.type === "point" && l(tt, { name: n.ns("fade"), appear: !0 }, ye(e = c.map(([a, i], s) => l("span", { key: s, class: n.be("pointer"), style: { top: `${i}%`, left: `${a}%` }, onClick: Oe.bind(null, s) }, [s + 1]))) ? e : { default: () => [e] }), l(at, { name: n.ns("fade") }, { default: () => [(r.value || C.value) && l("div", { class: [n.be("image-tip"), n.bem("image-tip", r.value ? "success" : "fail")], onClick: Ne }, [r.value ? t.successTip ?? k.value.success : t.failTip ?? k.value.fail])] })]); } function qe() { return l(yt, { ref: v, class: n.bem("slider", "inner"), target: ze.value, tolerance: t.tolerance, loading: _.value, "loading-icon": t.loadingIcon, "loading-lock": !0, "loading-effect": t.loadingEffect, disabled: t.disabled || ue.value, onBeforeTest: t.onBeforeTest, onSuccess: Ye, onFail: _e, onDragStart: De, onDrag: We, onDragEnd: $e }, { tip: () => D(P, "tip", { success: r.value }, () => [l(M, { renderer: t.slots.tip, data: { success: r.value } }, { default: () => [t.tip ?? k.value.slide] })]) }); } function Ge() { return l("div", { class: n.be("text-list") }, [l("div", { class: n.be("tip") }, [D(P, "tip", { success: r.value }, () => [l(M, { renderer: t.slots.tip, data: { success: r.value } }, { default: () => [t.tip ?? k.value.pointInOrder] })])]), l("span", null, [":"]), D(P, "texts", { texts: he(t, "texts") }, () => { let e; return [l(M, { renderer: t.slots.texts, data: { texts: he(t, "texts") } }, ye(e = t.texts.map((a, i) => l("span", { key: i, class: n.be("text") }, [a]))) ? e : { default: () => [e] })]; })]); } function Je() { return t.type === "slide" ? qe() : t.type === "point" ? Ge() : null; } function de() { var e; return l("div", { ref: Q, id: we.value, class: Le.value, tabindex: -1, role: "application", "aria-labelledby": Se.value }, [l("div", { class: n.be("header") }, [l("div", { class: n.be("title") }, [D(P, "title", { success: r.value }, () => [l(M, { renderer: t.slots.title, data: { success: r.value } }, { default: () => [t.title ?? k.value.doCaptcha] })])]), l("span", { role: "none", style: "flex: auto" }, null), l("button", { class: [n.be("action"), n.be("refresh"), H.value && n.bem("action", "disabled")], type: "button", onClick: He }, [D(P, "refresh", void 0, () => [l(M, { renderer: t.slots.refresh }, { default: () => [l(xt, nt(J.value.refresh, { icon: t.refreshIcon || J.value.refresh.icon }), null)] })])])]), l(wt, { active: _.value || ((e = v.value) == null ? void 0 : e.isLoading), delay: !1 }, { default: Ve, icon: ct(P, ["loading-icon", "loadingIcon"], () => l(M, { renderer: t.slots.loadingIcon }, null)) }), Je()]); } function Ke() { return D(P, "trigger", { visible: b.value, success: r.value }, () => [l(M, { renderer: t.slots.trigger, data: { visible: b.value, success: r.value } }, { default: () => [l(St, { class: [n.be("button"), r.value && n.bem("button", "success")], type: r.value ? "success" : "primary", size: t.triggerSize, block: !0, loading: b.value && !r.value, icon: r.value ? J.value.success.icon : null, onClick: je }, { default: () => [t.triggerText ?? (r.value ? k.value.completed : k.value.trigger)] })] })]); } return () => t.useTrigger ? l(Tt, { class: n.bs("wrapper"), visible: b.value, trigger: "custom", raw: !0, wrapper: !0, transfer: t.transfer }, { trigger: Ke, default: de }) : de(); } }); export { jt as default }; //# sourceMappingURL=captcha.mjs.map