vexip-ui
Version:
A Vue 3 UI library, Highly customizability, full TypeScript, performance pretty good
498 lines (497 loc) • 16.7 kB
JavaScript
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