diff --git a/src/functions/componentsFunctions.js b/src/functions/componentsFunctions.js new file mode 100644 index 0000000..fa6bd6a --- /dev/null +++ b/src/functions/componentsFunctions.js @@ -0,0 +1,297 @@ +import { SCREEN_SIZES } from "../Data/globalVariables"; +import { billingInputsData } from "../Data/staticData"; + +export function simpleValidationCheck(inputs) { + let isFormValid = false; + + inputs.forEach((input) => { + let addOrRemoveClass; + + if (input.name === "username") { + const isUserNameContainsTwoWords = input.value.split(" ").length === 2; + addOrRemoveClass = !isUserNameContainsTwoWords ? "add" : "remove"; + } else { + addOrRemoveClass = input.value === "" ? "add" : "remove"; + } + + input.classList[addOrRemoveClass]("invalid"); + isFormValid = true; + }); + + for (let i = 0; i < inputs.length; i++) { + const isInputContainsInvalidClass = inputs[i].classList.contains("invalid"); + if (isInputContainsInvalidClass) isFormValid = false; + } + + return isFormValid; +} + +export function repoStarsForksToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "55px"; + case "ja": + return "92px"; + } + return "77px"; +} + +export function scrollToTopToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-55px"; + case "ja": + return "-86px"; + case "ru": + return "-84px"; + case "fr": + return "-80px"; + case "hu": + return "-104px"; + case "hi": + return "-73px"; + } + return "-60px"; +} + +export function favIconToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-40px"; + case "ja": + return "-51px"; + case "ru": + return "-52px"; + case "fr": + return "-35px"; + case "hu": + return "-43px"; + case "hi": + return "-37px"; + } + return "-41px"; +} + +export function detailsIconToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-37px"; + case "ja": + return "-29px"; + case "ru": + return "-47px"; + case "fr": + return "-39px"; + case "hu": + return "-47px"; + case "hi": + return "-36px"; + } + return "-39px"; +} + +export function trashcanIconToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-26px"; + case "ja": + return "-29px"; + case "ru": + return "-42px"; + case "fr": + return "-36px"; + case "hu": + return "-48px"; + case "hi": + return "-29px"; + } + return "-41px"; +} + +export function wishlistIconToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-54px"; + case "ja": + return "-72px"; + case "ru": + return "-51px"; + case "fr": + return "-69px"; + case "hu": + return "-57px"; + case "hi": + return "-43px"; + } + return "-39px"; +} + +export function sendToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-31px"; + case "ja": + return "68px"; + case "ru": + return "89px"; + case "fr": + return "80px"; + case "hu": + return "75px"; + case "hi": + return "64px"; + } + return "70px"; +} + +export function sendingToolTipLeftPos(lang) { + switch (lang) { + case "ar": + return "-46px"; + case "ja": + return "75px"; + case "ru": + return "75px"; + case "fr": + return "71px"; + case "hu": + return "75px"; + case "hi": + return "70px"; + } + return "80px"; +} + +export function cartProductToolTipPos(lang) { + switch (lang) { + case "ar": + return "56px"; + case "ja": + return "-37px"; + case "fr": + return "-55px"; + case "hu": + return "-55px"; + case "hi": + return "-37px"; + } + return "-50px"; +} + +export function orderProductToolTipPos(lang) { + switch (lang) { + case "ru": + return "-66px"; + case "ar": + return "58px"; + case "fr": + return "-57px"; + case "hu": + return "-57px"; + case "ja": + return "-37px"; + case "hi": + return "-45px"; + } + return "-52px"; +} + +export function whatDoIDoButtonToolTipPos(lang) { + switch (lang) { + case "ar": + return "-74px"; + case "hu": + return "108px"; + case "ja": + return "144px"; + case "hi": + return "136px"; + } + return "126px"; +} + +export function hideWhatDoIDoButtonToolTipPos(lang) { + switch (lang) { + case "ar": + return "-61px"; + case "fr": + return "147px"; + case "hu": + return "108px"; + case "ja": + return "90px"; + case "hi": + return "101px"; + } + return "98px"; +} + +export function getElementWidth(element) { + return element?.getBoundingClientRect()?.width; +} + +export function getScrollSliderValue(sliderEle) { + const GAP = 30; + const { desktop, laptop } = SCREEN_SIZES; + const sliderItemEle = sliderEle.children[0]; + const sliderItemEleWidth = getElementWidth(sliderItemEle) + GAP; + + if (window.innerWidth >= desktop + 300) { + return sliderItemEleWidth * 4; + } + + if (window.innerWidth >= desktop + 100) { + return sliderItemEleWidth * 3; + } + + if (window.innerWidth >= laptop) { + return sliderItemEleWidth * 2; + } + + return sliderItemEleWidth; +} + +export function showInvalidInputAlert(event) { + const form = new FormData(event.target); + const inputs = event.target.querySelectorAll("input"); + let index = 0; + + for (const [key, value] of form.entries()) { + const regex = billingInputsData[index]?.regex; + const isValid = regex ? regex.test(value) : true; + + inputs[index].style.cssText = ``; + + if (!isValid) { + inputs[index].style.cssText = ` + border-color: var(--red); + background-color: #ffdada; + `; + + inputs[index].focus(); + break; + } + + index++; + } +} + +export function isCheckoutFormValid(event) { + const form = new FormData(event.target); + let hasInvalidInput = false; + let index = 0; + + for (const [key, value] of form.entries()) { + const regex = billingInputsData[index]?.regex; + const isValid = regex ? regex.test(value) : true; + if (!isValid) { + hasInvalidInput = true; + break; + } + index++; + } + + return !hasInvalidInput; +} + +export const blurInputs = (inputs) => inputs.forEach((input) => input.blur()); diff --git a/src/functions/conditions.js b/src/functions/conditions.js new file mode 100644 index 0000000..e056dc3 --- /dev/null +++ b/src/functions/conditions.js @@ -0,0 +1,11 @@ +export function isCurrentPassValid(currentPassInp, userPassword) { + const isCorrectPassword = currentPassInp.value === userPassword; + const isEmptyField = currentPassInp.value.length === 0; + return isCorrectPassword || isEmptyField; +} + +export function isNewPasswordValid(newPasswordInput) { + const isGraterThan8Chars = newPasswordInput.value.length >= 8; + const isEmptyField = newPasswordInput.value.length === 0; + return isGraterThan8Chars || isEmptyField; +} diff --git a/src/functions/effects.js b/src/functions/effects.js new file mode 100644 index 0000000..2f51ce9 --- /dev/null +++ b/src/functions/effects.js @@ -0,0 +1,23 @@ +export function buttonEffect(buttonEvent) { + const effectEle = document.createElement("div"); + effectEle.style.cssText = ` + border-radius: 50%; + background-color: #dedede; + position: absolute; + left: 50%; + top: 50%; + translate: -50% -50%; + width: 0; + height: 0; + transition: .3s width , .3s height, opacity .8s; + `; + + buttonEvent.currentTarget.appendChild(effectEle); + setTimeout(() => { + effectEle.style.width = "100%"; + effectEle.style.height = "100%"; + effectEle.style.opacity = "0"; + }, 0); + + setTimeout(() => effectEle.remove(), 1000); +} diff --git a/src/functions/helper.js b/src/functions/helper.js new file mode 100644 index 0000000..7de8fea --- /dev/null +++ b/src/functions/helper.js @@ -0,0 +1,201 @@ +import { SCREEN_SIZES, regexPatterns } from "../Data/globalVariables"; +import { isCurrentPassValid, isNewPasswordValid } from "./conditions"; + +export function isDecimalNumber(number) { + let singleNumber = Math.floor(number); + return singleNumber !== number; +} + +export function getTimeObj(milliseconds) { + const totalSeconds = Math.floor(milliseconds / 1000), + totalMinutes = Math.floor(totalSeconds / 60), + totalHours = Math.floor(totalMinutes / 60), + days = Math.floor(totalHours / 24), + seconds = Math.floor(totalSeconds % 60), + minutes = Math.floor(totalMinutes % 60), + hours = Math.floor(totalHours % 24), + timeObj = { + days, + hours, + minutes, + seconds, + milliseconds, + }; + return { ...timeObj }; +} + +export function getTimeInMilliseconds(days, hours, minutes, seconds) { + const millisecondsPerSecond = 1000, + millisecondsPerMinute = millisecondsPerSecond * 60, + millisecondsPerHour = millisecondsPerMinute * 60, + millisecondsPerDay = millisecondsPerHour * 24, + totalMilliseconds = + days * millisecondsPerDay + + hours * millisecondsPerHour + + minutes * millisecondsPerMinute + + seconds * millisecondsPerSecond; + return totalMilliseconds; +} + +export function getFormattedTime(time) { + return { + days: padStart(time.days), + hours: padStart(time.hours), + minutes: padStart(time.minutes), + seconds: padStart(time.seconds), + }; +} + +export const padStart = (num) => `${num}`.padStart(2, "0"); + +export function compareDataToObjValue(data, obj, key) { + const filteredData = data.filter((dataObj) => dataObj[key] === obj[key]); + return filteredData.length > 0; +} + +export function checkDateBeforeMonthToPresent(getDate) { + const monthByMilliSeconds = 2_629_056_000; + const currentDate = new Date().getTime(); + const requitedDate = new Date(getDate).getTime() + monthByMilliSeconds; + return currentDate > requitedDate; +} + +export function capitalize(str) { + const firstCapitalLetter = str?.slice(0, 1).toUpperCase(); + const restSmallLetters = str?.slice(1, str.length).toLowerCase(); + return firstCapitalLetter + restSmallLetters; +} + +export function camelCase(str) { + const regex = regexPatterns.words; + const words = str.toLowerCase().replace(regex, " ").split(" "); + const camelCasedWords = words.map((word, index) => + index !== 0 ? capitalize(word) : word + ); + return camelCasedWords.join(""); +} + +export function updateClassOnCondition( + input, + condition, + className = "invalid" +) { + const methodName = condition ? "remove" : "add"; + input.classList[methodName](className); +} + +export function checkEmptyInputs({ exceptions, formRef }) { + const formEle = formRef.current; + const inputs = formEle.querySelectorAll("input"); + + inputs.forEach((input) => { + const isExceptionInput = exceptions.includes(input.name); + const isGraterThan2 = input.value.length > 2; + updateClassOnCondition(input, isExceptionInput || isGraterThan2); + }); +} + +export function checkAreInputsValid(inputs) { + return [...inputs].every((input) => !input.classList.contains("invalid")); +} + +export function checkEmailValidation(emailInput) { + const isEmailValid = regexPatterns.email.test(emailInput.value); + updateClassOnCondition(emailInput, isEmailValid); +} + +export function checkPasswordInputs(passwordInputs, password) { + const currPassInp = passwordInputs[0]; + const newPassInp = passwordInputs[1]; + const confirmPassInput = passwordInputs[2]; + + const isCurrPassCorrect = isCurrentPassValid(currPassInp, password); + const isNewPassValid = isNewPasswordValid(newPassInp); + const isCurrPassEqualsNew = confirmPassInput.value === newPassInp.value; + + updateClassOnCondition(currPassInp, isCurrPassCorrect); + updateClassOnCondition(newPassInp, isNewPassValid); + updateClassOnCondition(confirmPassInput, isCurrPassEqualsNew); +} + +export function getDataById(arr, id) { + return arr?.find((item) => item?.id === id); +} + +export const scrollToTop = () => + window.scrollTo({ top: 0, behavior: "smooth" }); + +export function getDiscountedPrice(originalPrice, discountPercentage) { + const discountAmount = (originalPrice * discountPercentage) / 100; + const discountedPrice = originalPrice - discountAmount; + return discountedPrice.toFixed(2); +} + +export const formatePrice = (price) => + `${price}`.replace(regexPatterns.price, ","); + +export function setAfterDiscountKey(product) { + const discountedPrice = getDiscountedPrice(product.price, product.discount); + const formattedDiscountedPrice = formatePrice(discountedPrice); + product.afterDiscount = formattedDiscountedPrice; +} + +export function setFormattedPrice(product) { + const formattedPrice = formatePrice(product.price); + product.price = formattedPrice; +} + +export function getSubTotal(cartProducts, key = "quantity") { + const total = cartProducts?.reduce((acc, product) => { + const priceAfterDiscount = +product?.afterDiscount.replaceAll(",", ""); + const quantity = +product?.[key]; + const quantityPrice = quantity * priceAfterDiscount; + return (acc += quantityPrice); + }, 0); + + return total.toFixed(2); +} + +export function isQueryContainedInItem(query, item) { + const formattedQuery = query?.trim().toLowerCase(); + const formattedItem = item?.toLowerCase(); + + return ( + formattedItem.includes(formattedQuery) || + formattedItem.startsWith(formattedQuery) || + formattedItem.endsWith(formattedQuery) + ); +} + +export function searchByObjectKey({ data, key, query }) { + return data.filter((item) => isQueryContainedInItem(query, item?.[key])); +} + +export function getRandomItem(arr) { + return arr[Math.floor(Math.random() * arr.length)]; +} + +export function isItemFound(data, getItem, key) { + return data.find((item) => item[key] === getItem[key]); +} + +export function getUniqueArrayByObjectKey({ arr, newArr, key }) { + const updatedArr = [...arr]; + + newArr.forEach((item) => { + const isItemExist = !!isItemFound(arr, item, key); + if (!isItemExist) updatedArr.push(item); + }); + + return updatedArr; +} + +export function isMobileScreenWidth() { + const mobileMediaQuery = `(max-width: ${SCREEN_SIZES.mobile}px)`; + const isMobileDevice = window.matchMedia(mobileMediaQuery).matches; + return isMobileDevice; +} + +export function isEmailValid(emailInput) { + return regexPatterns.email.test(emailInput.value); +}