@codecavepro/brand 3.2.3 → 3.2.5

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.
@@ -24,6 +24,10 @@ const handleMouseMove = (e: MouseEvent) => {
24
24
  const y = e.clientY - rect.top
25
25
  const percentX = x / rect.width
26
26
 
27
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
28
+ return
29
+ }
30
+
27
31
  gsap.to(glow.value, {
28
32
  left: x,
29
33
  top: y,
@@ -1,18 +1,31 @@
1
1
  <script setup lang="ts">
2
- import { gsap } from "gsap";
3
- import { SplitText } from "gsap/SplitText";
4
- import { nextTick, onMounted, watch } from "vue";
2
+ import { nextTick, onMounted, ref, watch } from 'vue'
3
+ import gsap from 'gsap'
4
+ import { SplitText } from 'gsap/SplitText'
5
5
 
6
6
  const props = defineProps<{
7
7
  text1: string
8
8
  text2: string
9
9
  }>()
10
10
 
11
+ const typingEffectContainer = ref<HTMLElement | null>(null)
11
12
  const splitAnimation = () => {
13
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
14
+ return
15
+ }
16
+
17
+ if (!typingEffectContainer.value) {
18
+ return
19
+ }
20
+
12
21
  gsap.registerPlugin(SplitText)
13
- const split = SplitText.create('.split-text span', {
14
- type: "words, chars"
15
- })
22
+
23
+ const split = SplitText.create(
24
+ typingEffectContainer.value.querySelectorAll('.split-text span'),
25
+ {
26
+ type: 'words, chars'
27
+ }
28
+ )
16
29
  gsap.from(split.chars, {
17
30
  opacity: 0.5,
18
31
  duration: 0.1,
@@ -29,7 +42,7 @@ watch(() => [props.text1, props.text2],
29
42
  </script>
30
43
 
31
44
  <template>
32
- <div class="flex flex-col items-center leading-[130%] text-heading-md md:text-heading-lg font-bold">
45
+ <div ref="typingEffectContainer" class="flex flex-col items-center leading-[130%] text-heading-md md:text-heading-lg font-bold">
33
46
  <h2 :key="text1" class="split-text">
34
47
  <span class="block text-heading">{{ text1 }}</span>
35
48
  <span class="block text-action">{{ text2 }}</span>
@@ -54,6 +54,7 @@ const createFormField = <T,>(name: keyof ContactFormDefinition, value: T): FormF
54
54
  const formData = ref({
55
55
  email: createFormField('email', ''),
56
56
  firstName: createFormField('firstName', ''),
57
+ lastName: createFormField('lastName', ''),
57
58
  companyName: createFormField('companyName', ''),
58
59
  linkedinCompanyPage: createFormField('linkedinCompanyPage', ''),
59
60
  services: createFormField('services', ''),
@@ -122,6 +123,7 @@ const validateRequiredFields = () => {
122
123
  const resetForm = () => {
123
124
  formData.value.email.value = '';
124
125
  formData.value.firstName.value = '';
126
+ formData.value.lastName.value = '';
125
127
  formData.value.companyName.value = '';
126
128
  formData.value.linkedinCompanyPage.value = '';
127
129
  formData.value.description.value = '';
@@ -134,6 +136,7 @@ const resetForm = () => {
134
136
  const currentValues = (): ContactFormValues => ({
135
137
  email: formData.value.email.value,
136
138
  firstName: formData.value.firstName.value,
139
+ lastName: formData.value.lastName.value,
137
140
  companyName: formData.value.companyName.value,
138
141
  linkedinCompanyPage: formData.value.linkedinCompanyPage.value,
139
142
  services: formData.value.services.value,
@@ -231,14 +234,14 @@ fields.forEach((key) => {
231
234
  })
232
235
 
233
236
  const companyNamePattern = /[^\p{L}\p{N} &]/u;
234
- const firstNamePattern = /[^\p{L} ]/u;
237
+ const namePattern = /[^\p{L} ]/u;
235
238
 
236
239
  const createSanitizer = (pattern) => {
237
240
  return (value) => value.replace(pattern, '');
238
241
  };
239
242
 
240
243
  const sanitizeCompanyName = createSanitizer(companyNamePattern);
241
- const sanitizeFirstName = createSanitizer(firstNamePattern);
244
+ const sanitizeName = createSanitizer(namePattern);
242
245
 
243
246
  const preventInvalidInput = (event, pattern) => {
244
247
  if (event.data && pattern.test(event.data)) {
@@ -254,10 +257,11 @@ const preventInvalidInput = (event, pattern) => {
254
257
  <fieldset class="space-y-2">
255
258
  <InputText id="email" v-model="formData.email.value" :isRequired=formData.email.required :label=formData.email.label type="email" :isError="!!formData.email.error" :errorMessage="formData.email.error" :placeholder="formData.email.placeholder ?? ''" autocomplete="email" />
256
259
  <div class="flex flex-col xl:flex-row gap-2">
257
- <InputText id="firstname" v-model="formData.firstName.value" @beforeinput="event => preventInvalidInput(event, firstNamePattern)" @update:model-value="value => { formData.firstName.value = sanitizeFirstName(value) }" :isRequired="formData.firstName.required" :label="formData.firstName.label" type="text" :isError="!!formData.firstName.error" :errorMessage="formData.firstName.error" :placeholder="formData.firstName.placeholder ?? ''" autocomplete="given-name" />
258
- <InputText id="companyName" v-model="formData.companyName.value" @beforeinput="event => preventInvalidInput(event, companyNamePattern)" @update:model-value="value => { formData.companyName.value = sanitizeCompanyName(value) }" :isRequired="formData.companyName.required" :label="formData.companyName.label" type="text" :isError="!!formData.companyName.error" :errorMessage="formData.companyName.error" :placeholder="formData.companyName.placeholder ?? ''" />
260
+ <InputText id="firstname" v-model="formData.firstName.value" @beforeinput="event => preventInvalidInput(event, namePattern)" @update:model-value="value => { formData.firstName.value = sanitizeName(value) }" :isRequired="formData.firstName.required" :label="formData.firstName.label" type="text" :isError="!!formData.firstName.error" :errorMessage="formData.firstName.error" :placeholder="formData.firstName.placeholder ?? ''" autocomplete="given-name" />
261
+ <InputText id="lastname" v-model="formData.lastName.value" @beforeinput="event => preventInvalidInput(event, namePattern)" @update:model-value="value => { formData.lastName.value = sanitizeName(value) }" :isRequired="formData.lastName.required" :label="formData.lastName.label" type="text" :isError="!!formData.lastName.error" :errorMessage="formData.lastName.error" :placeholder="formData.lastName.placeholder ?? ''" autocomplete="family-name" />
259
262
  </div>
260
263
  <div class="flex flex-col xl:flex-row gap-2">
264
+ <InputText id="companyName" v-model="formData.companyName.value" @beforeinput="event => preventInvalidInput(event, companyNamePattern)" @update:model-value="value => { formData.companyName.value = sanitizeCompanyName(value) }" :isRequired="formData.companyName.required" :label="formData.companyName.label" type="text" :isError="!!formData.companyName.error" :errorMessage="formData.companyName.error" :placeholder="formData.companyName.placeholder ?? ''" />
261
265
  <InputText id="linkedinCompanyPage" v-model="formData.linkedinCompanyPage.value" :isRequired=formData.linkedinCompanyPage.required :label=formData.linkedinCompanyPage.label type="text" :isError="!!formData.linkedinCompanyPage.error" :errorMessage="formData.linkedinCompanyPage.error" :placeholder="formData.linkedinCompanyPage.placeholder ?? ''" />
262
266
  </div>
263
267
  </fieldset>
@@ -26,6 +26,7 @@ export interface CrmFormFieldSpec {
26
26
  export type ContactFormFieldName =
27
27
  | 'email'
28
28
  | 'firstName'
29
+ | 'lastName'
29
30
  | 'companyName'
30
31
  | 'linkedinCompanyPage'
31
32
  | 'services'
@@ -40,6 +41,7 @@ export type ContactFormDefinition = Record<ContactFormFieldName, CrmFormFieldSpe
40
41
  export interface ContactFormValues {
41
42
  email: string;
42
43
  firstName: string;
44
+ lastName: string;
43
45
  companyName: string;
44
46
  linkedinCompanyPage: string;
45
47
  services: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codecavepro/brand",
3
- "version": "3.2.3",
3
+ "version": "3.2.5",
4
4
  "description": "CODECAVE design system — colour, typography and layout tokens as CSS custom properties and as a typed module.",
5
5
  "license": "Unlicense",
6
6
  "type": "module",