@codecavepro/brand 3.2.0 → 3.2.1
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.
|
@@ -5,49 +5,68 @@ import { gsap } from "gsap";
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
title: string
|
|
7
7
|
class?: string
|
|
8
|
-
href?: string
|
|
8
|
+
href?: string | null
|
|
9
9
|
}>()
|
|
10
|
+
|
|
10
11
|
const isTouchDevice = ref(false)
|
|
11
|
-
const link = ref(null)
|
|
12
|
-
const glow = ref(null)
|
|
13
|
-
const edgeLeft = ref(null)
|
|
14
|
-
const edgeRight = ref(null)
|
|
15
|
-
|
|
12
|
+
const link = ref<HTMLElement | null>(null)
|
|
13
|
+
const glow = ref<HTMLElement | null>(null)
|
|
14
|
+
const edgeLeft = ref<HTMLElement | null>(null)
|
|
15
|
+
const edgeRight = ref<HTMLElement | null>(null)
|
|
16
|
+
|
|
17
|
+
let leaveTween: gsap.core.Tween | null = null
|
|
18
|
+
|
|
16
19
|
const handleMouseMove = (e: MouseEvent) => {
|
|
17
|
-
if (isTouchDevice.value) return
|
|
20
|
+
if (isTouchDevice.value || !link.value || !glow.value) return
|
|
18
21
|
|
|
19
22
|
const rect = link.value.getBoundingClientRect()
|
|
20
23
|
const x = e.clientX - rect.left
|
|
21
24
|
const y = e.clientY - rect.top
|
|
22
25
|
const percentX = x / rect.width
|
|
26
|
+
|
|
23
27
|
gsap.to(glow.value, {
|
|
24
28
|
left: x,
|
|
25
29
|
top: y,
|
|
26
30
|
duration: 0.5,
|
|
27
31
|
ease: 'power2.out',
|
|
28
32
|
})
|
|
33
|
+
|
|
29
34
|
const edgeZone = 0.1
|
|
30
35
|
let leftOpacity = 0
|
|
31
36
|
let rightOpacity = 0
|
|
37
|
+
|
|
32
38
|
if (percentX < edgeZone) {
|
|
33
39
|
leftOpacity = 1 - percentX / edgeZone
|
|
34
40
|
} else if (percentX > 1 - edgeZone) {
|
|
35
41
|
rightOpacity = (percentX - (1 - edgeZone)) / edgeZone
|
|
36
42
|
}
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
|
|
44
|
+
if (edgeLeft.value) {
|
|
45
|
+
edgeLeft.value.style.opacity = String(leftOpacity)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (edgeRight.value) {
|
|
49
|
+
edgeRight.value.style.opacity = String(rightOpacity)
|
|
50
|
+
}
|
|
39
51
|
}
|
|
52
|
+
|
|
40
53
|
const handleMouseEnter = () => {
|
|
41
54
|
if (isTouchDevice.value) return
|
|
55
|
+
|
|
42
56
|
if (leaveTween) {
|
|
43
57
|
leaveTween.kill()
|
|
44
58
|
leaveTween = null
|
|
45
59
|
}
|
|
46
60
|
}
|
|
61
|
+
|
|
47
62
|
const handleMouseLeave = () => {
|
|
48
|
-
edgeLeft.value.style.opacity = 1
|
|
49
|
-
edgeRight.value.style.opacity = 0
|
|
63
|
+
if (edgeLeft.value) edgeLeft.value.style.opacity = '1'
|
|
64
|
+
if (edgeRight.value) edgeRight.value.style.opacity = '0'
|
|
65
|
+
|
|
50
66
|
if (leaveTween) leaveTween.kill()
|
|
67
|
+
|
|
68
|
+
if (!glow.value) return
|
|
69
|
+
|
|
51
70
|
leaveTween = gsap.to(glow.value, {
|
|
52
71
|
left: '20%',
|
|
53
72
|
top: '50%',
|
|
@@ -56,35 +75,49 @@ const handleMouseLeave = () => {
|
|
|
56
75
|
ease: 'power2.out',
|
|
57
76
|
})
|
|
58
77
|
}
|
|
78
|
+
|
|
59
79
|
onMounted(() => {
|
|
60
|
-
isTouchDevice.value =
|
|
61
|
-
|
|
62
|
-
|
|
80
|
+
isTouchDevice.value =
|
|
81
|
+
window.matchMedia('(pointer: coarse)').matches ||
|
|
82
|
+
'ontouchstart' in window ||
|
|
83
|
+
navigator.maxTouchPoints > 0
|
|
63
84
|
})
|
|
64
85
|
</script>
|
|
65
86
|
|
|
66
87
|
<template>
|
|
67
88
|
<div :class="`relative inline-block ${props.class ?? ''}`">
|
|
68
89
|
<div ref="edgeLeft" class="edge-glow -left-2"></div>
|
|
69
|
-
|
|
70
|
-
|
|
90
|
+
|
|
91
|
+
<component
|
|
92
|
+
:is="href ? 'a' : 'span'"
|
|
93
|
+
ref="link"
|
|
94
|
+
:href="href || undefined"
|
|
95
|
+
@mousemove="handleMouseMove"
|
|
96
|
+
@mouseenter="handleMouseEnter"
|
|
97
|
+
@mouseleave="handleMouseLeave"
|
|
98
|
+
class="cursor-pointer flex items-center justify-center relative z-[1] overflow-hidden w-full h-12 px-6 py-1 rounded-full bg-glow-25"
|
|
99
|
+
>
|
|
100
|
+
<p class="relative z-[2] text-primary-800 font-bold select-none">
|
|
71
101
|
{{ title }}
|
|
72
|
-
</
|
|
102
|
+
</p>
|
|
103
|
+
|
|
73
104
|
<div ref="glow" class="glow"></div>
|
|
74
|
-
</
|
|
105
|
+
</component>
|
|
106
|
+
|
|
75
107
|
<div ref="edgeRight" class="edge-glow -right-2 opacity-0"></div>
|
|
76
108
|
</div>
|
|
77
109
|
</template>
|
|
78
110
|
|
|
79
111
|
<style scoped>
|
|
80
|
-
a {
|
|
112
|
+
span, a {
|
|
81
113
|
box-shadow: 0 0 64px 0 #7A58FFA8,
|
|
82
114
|
0 0 16px 0 #4F22FFA6,
|
|
83
115
|
0 0 4px 2px #5B34FA;
|
|
84
116
|
transition: transform 0.5s ease;
|
|
85
117
|
}
|
|
86
118
|
|
|
87
|
-
|
|
119
|
+
|
|
120
|
+
span, a:active {
|
|
88
121
|
transform: scale(0.98);
|
|
89
122
|
}
|
|
90
123
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import LinkedinIcon from "../assets/icons/linkedin-icon.vue";
|
|
3
3
|
import VerifiedIcon from "../assets/icons/verified-icon.vue";
|
|
4
4
|
import LazyImage from "./images/LazyImage.vue"
|
|
5
|
+
import { isCorrectLinkedInFormat } from "../helpers/form-validator.ts";
|
|
5
6
|
|
|
6
7
|
/* See ArticlePreview.vue: the fields read here, not the generated Strapi
|
|
7
8
|
* `Testimonial`, which a caller may still pass unchanged. */
|
|
@@ -27,14 +28,14 @@ const imageUrl = (url: string) => props.resolveImage?.(url) ?? url
|
|
|
27
28
|
<template>
|
|
28
29
|
<div :class="`mx-1 lg:mx-2 testimonial space-y-2 lg:space-y-3 py-10 px-6 lg:px-11 ${className}`">
|
|
29
30
|
<div class="flex flex-col lg:flex-row gap-5 lg:items-center">
|
|
30
|
-
<
|
|
31
|
+
<img v-if="item.photo.name !== ''"
|
|
31
32
|
class="w-12 lg:w-16 h-12 lg:h-16" :src="imageUrl(item.photo.url)"
|
|
32
33
|
:alt=item.photo.name />
|
|
33
34
|
<div>
|
|
34
35
|
<div class="flex items-center gap-2">
|
|
35
36
|
<p class="text-heading text-lg lg:text-xl">{{ item.name }}</p>
|
|
36
37
|
<a
|
|
37
|
-
v-if="item.linkedinurl
|
|
38
|
+
v-if="isCorrectLinkedInFormat(item.linkedinurl)"
|
|
38
39
|
target="_blank"
|
|
39
40
|
rel='noopener noreferrer'
|
|
40
41
|
class="text-default-transparent hover:text-action transition-colors"
|
package/package.json
CHANGED