swiper and clean

This commit is contained in:
2026-07-12 19:27:47 +00:00
parent 5cd55c096d
commit db9370f916
15 changed files with 250 additions and 2025 deletions

View File

@@ -1,16 +1,13 @@
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
import { lazy, Suspense } from 'react'
import Layout from './components/Layout'
import Loading from './components/Loading'
import ErrorBoundary from './components/ErrorBoundary'
import { BRAND_PRIMARY } from './config'
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
const Services = lazy(() => import('./pages/Services'))
const ServiceDetail = lazy(() => import('./pages/ServiceDetail'))
const Project = lazy(() => import('./pages/Project'))
const Contact = lazy(() => import('./pages/Contact'))
import Home from './pages/Home'
import About from './pages/About'
import Services from './pages/Services'
import ServiceDetail from './pages/ServiceDetail'
import Project from './pages/Project'
import Contact from './pages/Contact'
function NotFound() {
return (
@@ -26,19 +23,17 @@ export default function App() {
return (
<BrowserRouter>
<ErrorBoundary>
<Suspense fallback={<Loading />}>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/service" element={<Services />} />
<Route path="/service/:slug" element={<ServiceDetail />} />
<Route path="/project" element={<Project />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</Suspense>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/service" element={<Services />} />
<Route path="/service/:slug" element={<ServiceDetail />} />
<Route path="/project" element={<Project />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</ErrorBoundary>
</BrowserRouter>
)

View File

@@ -1,10 +1,13 @@
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { Outlet, useLocation } from 'react-router-dom'
import Header from './sections/Header'
import Footer from './sections/Footer'
import WhatsAppButton from './WhatsAppButton'
import useThemeAssets from '../hooks/useThemeAssets'
import useLayoutInit from '../hooks/useLayoutInit'
import useSwiper from '../hooks/useSwiper'
import useBackgroundAttrs from "../hooks/useBackgroundAttrs"
import useAnimations from "../hooks/useAnimations"
import { PAGE_IDS } from '../config'
export default function Layout() {
@@ -12,6 +15,10 @@ export default function Layout() {
useThemeAssets()
useLayoutInit()
const contentRef = useRef(null)
useBackgroundAttrs(contentRef)
useSwiper(contentRef)
useAnimations(contentRef)
useEffect(() => {
let removed = false
@@ -68,7 +75,7 @@ export default function Layout() {
return (
<>
<Header />
<Outlet />
<div ref={contentRef}><Outlet /></div>
<Footer />
<WhatsAppButton />
</>

File diff suppressed because one or more lines are too long

View File

@@ -2399,65 +2399,6 @@ textarea.is-invalid {
width: 45px;
}
.scroll-top {
position: fixed;
right: 30px;
bottom: 30px;
height: 50px;
width: 50px;
cursor: pointer;
display: block;
border-radius: 50px;
z-index: 10000;
opacity: 1;
visibility: hidden;
-webkit-transform: translateY(45px);
transform: translateY(45px);
-webkit-transition: all 300ms linear;
transition: all 300ms linear;
}
.scroll-top:after {
content: "\f062";
font-family: var(--icon-font);
position: absolute;
text-align: center;
line-height: 50px;
font-size: 20px;
color: var(--theme-color);
left: 0;
top: 0;
height: 50px;
width: 50px;
cursor: pointer;
display: block;
z-index: 1;
border: 2px solid var(--theme-color);
-webkit-box-shadow: none;
box-shadow: none;
border-radius: 50%;
}
.scroll-top svg {
color: var(--theme-color);
border-radius: 50%;
background: var(--white-color);
}
.scroll-top svg path {
fill: none;
}
.scroll-top .progress-circle path {
stroke: var(--theme-color);
stroke-width: 20px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all 400ms linear;
transition: all 400ms linear;
}
.scroll-top.show {
opacity: 1;
visibility: visible;
-webkit-transform: translateY(0);
transform: translateY(0);
}
/*------------------- 3.3. Titles -------------------*/
.sec-title {
@@ -6649,12 +6590,6 @@ ol.wp-block-rss {
padding-bottom: 48px;
}
.sticky-footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
}
.footer-links ul {
padding: 0;
@@ -12792,109 +12727,6 @@ span.select2-selection.select2-selection--single:focus {
}
/* About 2 ---------------------------------- */
.comparison-img {
position: relative;
margin: auto;
border: none;
background-color: transparent;
overflow: hidden;
height: 100%;
}
.comparison-img p {
position: absolute;
top: 40px;
background-color: var(--theme-color);
color: var(--title-color);
padding: 5px 15px;
z-index: 3;
font-weight: 500;
}
.comparison-img .before {
left: 40px;
}
.comparison-img .after {
right: 40px;
}
.comparison-img .img {
position: absolute;
inset: 0;
background-size: cover;
background-repeat: no-repeat;
background-position: left center;
/* Extra small devices */
}
@media (max-width: 375px) {
.comparison-img .img {
background-size: cover;
}
}
.comparison-img .foreground-img {
width: 50%;
}
.comparison-img .compslider {
position: absolute;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 100%;
height: 100%;
background: transparent;
outline: none;
margin: 0;
padding: 0;
border: none;
-webkit-transition: all 0.2s;
transition: all 0.2s;
text-align: center;
}
.comparison-img .compslider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 3px;
height: 800px;
background: var(--white-color);
cursor: pointer;
-webkit-transform: translateX(3px);
transform: translateX(3px);
}
.comparison-img .compslider::-moz-range-thumb {
width: 3px;
height: 800px;
background: var(--white-color);
cursor: pointer;
transform: translateX(3px);
}
.comparison-img .slider-button {
pointer-events: none;
position: absolute;
width: 56px;
height: 56px;
border-radius: 50%;
padding: 8px;
background-color: var(--theme-color);
left: calc(50% - 32px);
top: calc(50% - 28px);
line-height: 40px;
text-align: center;
}
.comparison-img .slider-button:after {
content: "";
padding: 5px;
display: inline-block;
border: solid var(--white-color);
border-width: 0 1px 1px 0;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.comparison-img .slider-button:before {
content: "";
padding: 5px;
display: inline-block;
border: solid var(--white-color);
border-width: 0 1px 1px 0;
-webkit-transform: rotate(135deg);
transform: rotate(135deg);
}
.img-box4 {
position: relative;
@@ -15922,476 +15754,6 @@ span.select2-selection.select2-selection--single:focus {
padding-right: 12px;
}
/* overlay-direction */
.overlay-direction .filter-item {
-webkit-perspective: 0;
-webkit-backface-visibility: hidden;
-webkit-transform: translate3d(0, 0, 0);
visibility: visible;
backface-visibility: hidden;
}
.overlay-direction .filter-item {
-webkit-perspective: 540px;
perspective: 540px;
}
.in-top .box-content {
-webkit-transform-origin: 50% 0%;
transform-origin: 50% 0%;
-webkit-animation: in-top 500ms ease 0ms 1 forwards;
animation: in-top 500ms ease 0ms 1 forwards;
}
.in-right .box-content {
-webkit-transform-origin: 100% 0%;
transform-origin: 100% 0%;
-webkit-animation: in-right 500ms ease 0ms 1 forwards;
animation: in-right 500ms ease 0ms 1 forwards;
}
.in-bottom .box-content {
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
-webkit-animation: in-bottom 500ms ease 0ms 1 forwards;
animation: in-bottom 500ms ease 0ms 1 forwards;
}
.in-left .box-content {
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
-webkit-animation: in-left 500ms ease 0ms 1 forwards;
animation: in-left 500ms ease 0ms 1 forwards;
}
.out-top .box-content {
-webkit-transform-origin: 50% 0%;
transform-origin: 50% 0%;
-webkit-animation: out-top 500ms ease 0ms 1 forwards;
animation: out-top 500ms ease 0ms 1 forwards;
}
.out-right .box-content {
-webkit-transform-origin: 100% 50%;
transform-origin: 100% 50%;
-webkit-animation: out-right 500ms ease 0ms 1 forwards;
animation: out-right 500ms ease 0ms 1 forwards;
}
.out-bottom .box-content {
-webkit-transform-origin: 50% 100%;
transform-origin: 50% 100%;
-webkit-animation: out-bottom 500ms ease 0ms 1 forwards;
animation: out-bottom 500ms ease 0ms 1 forwards;
}
.out-left .box-content {
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
-webkit-animation: out-left 500ms ease 0ms 1 forwards;
animation: out-left 500ms ease 0ms 1 forwards;
}
@-webkit-keyframes in-top {
from {
-webkit-transform: rotate3d(-1, 0, 0, 90deg);
transform: rotate3d(-1, 0, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@keyframes in-top {
from {
-webkit-transform: rotate3d(-1, 0, 0, 90deg);
transform: rotate3d(-1, 0, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@-webkit-keyframes in-right {
from {
-webkit-transform: rotate3d(0, -1, 0, 90deg);
transform: rotate3d(0, -1, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@keyframes in-right {
from {
-webkit-transform: rotate3d(0, -1, 0, 90deg);
transform: rotate3d(0, -1, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@-webkit-keyframes in-bottom {
from {
-webkit-transform: rotate3d(1, 0, 0, 90deg);
transform: rotate3d(1, 0, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@keyframes in-bottom {
from {
-webkit-transform: rotate3d(1, 0, 0, 90deg);
transform: rotate3d(1, 0, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@-webkit-keyframes in-left {
from {
-webkit-transform: rotate3d(0, 1, 0, 90deg);
transform: rotate3d(0, 1, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@keyframes in-left {
from {
-webkit-transform: rotate3d(0, 1, 0, 90deg);
transform: rotate3d(0, 1, 0, 90deg);
}
to {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
}
@-webkit-keyframes out-top {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(-1, 0, 0, 104deg);
transform: rotate3d(-1, 0, 0, 104deg);
}
}
@keyframes out-top {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(-1, 0, 0, 104deg);
transform: rotate3d(-1, 0, 0, 104deg);
}
}
@-webkit-keyframes out-right {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(0, -1, 0, 104deg);
transform: rotate3d(0, -1, 0, 104deg);
}
}
@keyframes out-right {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(0, -1, 0, 104deg);
transform: rotate3d(0, -1, 0, 104deg);
}
}
@-webkit-keyframes out-bottom {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(1, 0, 0, 104deg);
transform: rotate3d(1, 0, 0, 104deg);
}
}
@keyframes out-bottom {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(1, 0, 0, 104deg);
transform: rotate3d(1, 0, 0, 104deg);
}
}
@-webkit-keyframes out-left {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(0, 1, 0, 104deg);
transform: rotate3d(0, 1, 0, 104deg);
}
}
@keyframes out-left {
from {
-webkit-transform: rotate3d(0, 0, 0, 0deg);
transform: rotate3d(0, 0, 0, 0deg);
}
to {
-webkit-transform: rotate3d(0, 1, 0, 104deg);
transform: rotate3d(0, 1, 0, 104deg);
}
}
/*------------------- 4.00. CTA -------------------*/
.cta-img1 img {
height: 100%;
-o-object-fit: cover;
object-fit: cover;
-o-object-position: right center;
object-position: right center;
/* Medium devices */
}
@media (max-width: 991px) {
.cta-img1 img {
width: 100%;
}
}
.cta-sec1 {
background-size: auto;
background-position: center right;
background-color: var(--theme-color);
max-width: 1620px;
margin: -45px auto auto;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
gap: 20px;
position: relative;
z-index: 3;
/* Large devices */
/* Medium devices */
}
@media (max-width: 1199px) {
.cta-sec1 {
background-size: cover;
}
}
@media (max-width: 991px) {
.cta-sec1 {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
gap: 0;
}
}
.cta-sec1 .sec-title {
max-width: 600px;
}
.cta-sec1 .call-btn .box-label {
font-weight: 400;
}
.cta-sec1 .call-btn .box-link {
font-weight: 600;
}
.cta-sec1 .call-btn a:hover {
color: var(--title-color) !important;
}
.cta-sec1 .play-btn:before, .cta-sec1 .play-btn:after {
background-color: var(--white-color);
}
.cta-sec1 .play-btn > i {
background-color: var(--white-color);
color: var(--title-color);
}
.cta-sec1 .cta-content {
padding: 40px 12px;
-ms-flex-item-align: center;
align-self: center;
/* Medium devices */
}
@media (max-width: 991px) {
.cta-sec1 .cta-content {
text-align: center;
}
}
.video-content1 {
padding: 220px 0;
width: 100%;
max-width: 550px;
text-align: center;
margin: 0 auto;
/* Medium Large devices */
/* Medium devices */
}
.video-content1 .sec-title {
margin: 40px 0 -0.22em 0;
}
.video-content1 .play-btn {
--icon-size: 80px;
font-size: 20px;
}
@media (max-width: 1299px) {
.video-content1 {
padding: 160px 0;
}
}
@media (max-width: 991px) {
.video-content1 {
padding: 120px 0;
}
}
.link-btn2 {
font-family: var(--title-font);
font-size: 18px;
font-weight: 600;
color: var(--white-color);
}
.link-btn2 i {
width: 36px;
height: 36px;
line-height: 36px;
font-size: 18px;
text-align: center;
border: 1px solid var(--white-color);
border-radius: 8px;
color: var(--theme-color);
margin-right: 12px;
}
.link-btn2:hover {
color: var(--theme-color);
}
.video-content2 {
padding: 160px 0 110px 0;
width: 100%;
max-width: 840px;
text-align: center;
margin: 0 auto;
/* Medium devices */
}
.video-content2 .sec-title {
margin: 60px 0 26px 0;
}
.video-content2 .sec-text {
margin-bottom: 40px;
}
.video-content2 .play-btn {
--icon-size: 80px;
font-size: 20px;
}
@media (max-width: 991px) {
.video-content2 {
padding: 100px 0;
}
}
.video-sec2 .counter-card-wrap {
border-radius: 40px 40px 0 0;
/* Medium devices */
}
@media (max-width: 991px) {
.video-sec2 .counter-card-wrap {
border-radius: 20px 20px 0 0;
}
}
.video-sec3 .play-btn {
--icon-size: 80px;
font-size: 20px;
}
.video-content3 .sec-title {
margin-bottom: 26px;
/* Large devices */
}
@media (max-width: 1199px) {
.video-content3 .sec-title {
margin-top: 50px;
}
}
/*------------------- 4.00. Tab Menu -------------------*/
/* Tab Menu 1 ------------------------------------*/
.tab-menu1 {
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
border: 1px solid var(--body-color);
border-radius: 35px;
padding: 8px;
/* Large devices */
}
@media (max-width: 1199px) {
.tab-menu1 {
margin-top: -15px;
}
}
.tab-menu1 .tab-btn {
font-family: var(--title-font);
font-size: 16px;
font-weight: 500;
line-height: 44px;
background-color: transparent;
border: none;
padding: 0 28px;
color: var(--title-color);
position: relative;
z-index: 2;
overflow: hidden;
/* Small devices */
}
.tab-menu1 .tab-btn.active {
color: var(--white-color);
}
@media (max-width: 767px) {
.tab-menu1 .tab-btn {
padding: 0 18px;
}
}
.tab-menu1 .indicator {
position: absolute;
top: var(--pos-y);
left: var(--pos-x);
height: var(--height-set);
width: var(--width-set);
-webkit-transition: 0.4s ease-in-out;
transition: 0.4s ease-in-out;
background-color: var(--theme-color);
border-radius: 35px;
}
/* Tab Menu 2 ------------------------------------*/
.tab-menu2 {
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
z-index: 2;
margin-bottom: 60px;
margin-top: -20px;
/* Large devices */
}
@media (max-width: 1199px) {
.tab-menu2 {
margin-top: -15px;
@@ -18511,4 +17873,4 @@ span.select2-selection.select2-selection--single:focus {
.space-extra2-bottom {
padding-bottom: 70px;
}
}/*# sourceMappingURL=style.css.map */
}

View File

@@ -94,7 +94,7 @@
</div>
<div class="elementor-element elementor-element-2e21688 elementor-widget elementor-widget-rakargallery" data-id="2e21688" data-element_type="widget" data-e-type="widget" data-widget_type="rakargallery.default">
<div class="elementor-widget-container">
<div class="row gy-30 filter-active overlay-direction"><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_1.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_1.jpg" alt="gallery_1_1" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i> </span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_2.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_2.jpg" alt="gallery_1_2" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-6 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_3.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_3.jpg" alt="gallery_1_3" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-6 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_4.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_4.jpg" alt="gallery_1_4" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_5.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_5.jpg" alt="gallery_1_5" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_6.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_6.jpg" alt="gallery_1_6" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_7.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_7.jpg" alt="gallery_1_7" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-6 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_8.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_8.jpg" alt="gallery_1_8" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_9.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_9.jpg" alt="gallery_1_9" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div></div> </div>
<div class="row gy-30 filter-active"><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_1.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_1.jpg" alt="gallery_1_1" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i> </span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_2.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_2.jpg" alt="gallery_1_2" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-6 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_3.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_3.jpg" alt="gallery_1_3" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-6 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_4.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_4.jpg" alt="gallery_1_4" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_5.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_5.jpg" alt="gallery_1_5" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_6.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_6.jpg" alt="gallery_1_6" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_7.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_7.jpg" alt="gallery_1_7" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-6 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_8.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_8.jpg" alt="gallery_1_8" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div><div class="cat2 col-xl-3 col-md-6 filter-item th-gallery-loop"><div class="gallery-card"><a class="box-img popup-image" href="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_9.jpg"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/gallery_1_9.jpg" alt="gallery_1_9" /><div class="box-content"><span class="box-btn"><i class="fas fa-plus"></i></span></div></a></div></div></div> </div>
</div>
<div class="elementor-element elementor-element-be52d56 elementor-widget elementor-widget-rakarbutton" data-id="be52d56" data-element_type="widget" data-e-type="widget" data-widget_type="rakarbutton.default">
<div class="elementor-widget-container">
@@ -113,25 +113,25 @@
</div>
<div class="elementor-element elementor-element-96922c6 elementor-widget elementor-widget-rakarprice" data-id="96922c6" data-element_type="widget" data-e-type="widget" data-widget_type="rakarprice.default">
<div class="elementor-widget-container">
<div class="row gy-4"><div class="hover-item col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><h3 class="box-title">Basic Plan</h3><p class="box-price">$99.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
<div class="row gy-4"><div class="col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><h3 class="box-title">Basic Plan</h3><p class="box-price">$99.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
<li>Monthly Inspection</li>
<li>General Repair</li>
<li class="unavailable">Maintenance</li>
<li class="unavailable">Fixing of Faulty Wiring</li>
<li class="unavailable">Ac Condition Repair</li>
</ul></div><div class="box-footer"><a href="/contact" class="th-btn">Get Started<i class="far fa-arrow-right ms-2"></i></a></div></div></div><div class="hover-item item-active col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><span class="tag">Popular Plan</span><h3 class="box-title">Standard</h3><p class="box-price">$129.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
</ul></div><div class="box-footer"><a href="/contact" class="th-btn">Get Started<i class="far fa-arrow-right ms-2"></i></a></div></div></div><div class="col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><span class="tag">Popular Plan</span><h3 class="box-title">Standard</h3><p class="box-price">$129.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
<li>Monthly Inspection</li>
<li>General Repair</li>
<li>Maintenance</li>
<li class="unavailable">Fixing of Faulty Wiring</li>
<li class="unavailable">Ac Condition Repair</li>
</ul></div><div class="box-footer"><a href="/contact" class="th-btn">Get Started<i class="far fa-arrow-right ms-2"></i></a></div></div></div><div class="hover-item col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><h3 class="box-title">Premium</h3><p class="box-price">$199.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
</ul></div><div class="box-footer"><a href="/contact" class="th-btn">Get Started<i class="far fa-arrow-right ms-2"></i></a></div></div></div><div class="col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><h3 class="box-title">Premium</h3><p class="box-price">$199.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
<li>Monthly Inspection</li>
<li>General Repair</li>
<li>Maintenance</li>
<li>Fixing of Faulty Wiring</li>
<li class="unavailable">Ac Condition Repair</li>
</ul></div><div class="box-footer"><a href="/contact" class="th-btn">Get Started<i class="far fa-arrow-right ms-2"></i></a></div></div></div><div class="hover-item col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><h3 class="box-title">Dedicated Plan</h3><p class="box-price">$299.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
</ul></div><div class="box-footer"><a href="/contact" class="th-btn">Get Started<i class="far fa-arrow-right ms-2"></i></a></div></div></div><div class="col-xl-3 col-md-6"><div class="price-card"><div class="box-shape"><img decoding="async" src="https://wordpress.themeholy.com/rakar/assets/images/lines_3.png" alt="lines_3" /></div><h3 class="box-title">Dedicated Plan</h3><p class="box-price">$299.00<span class="duration">/Monthly</span></p><div class="box-list"><ul>
<li>Monthly Inspection</li>
<li>General Repair</li>
<li>Maintenance</li>

View File

@@ -0,0 +1,18 @@
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
export default function useAnimations(containerRef) {
const { pathname } = useLocation()
useEffect(() => {
const el = containerRef.current
if (!el) return
el.querySelectorAll('[data-ani]').forEach(el => {
el.classList.add(el.dataset.ani)
})
el.querySelectorAll('[data-ani-delay]').forEach(el => {
el.style.animationDelay = el.dataset.aniDelay
})
}, [containerRef, pathname])
}

View File

@@ -0,0 +1,36 @@
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
export default function useBackgroundAttrs(containerRef) {
const { pathname } = useLocation()
useEffect(() => {
const el = containerRef.current
if (!el) return
// data-bg-src → background-image
el.querySelectorAll('[data-bg-src]').forEach(el => {
el.style.backgroundImage = 'url(' + el.dataset.bgSrc + ')'
el.removeAttribute('data-bg-src')
el.classList.add('background-image')
})
// data-bg-color → background-color
el.querySelectorAll('[data-bg-color]').forEach(el => {
el.style.backgroundColor = el.dataset.bgColor
el.removeAttribute('data-bg-color')
})
// data-border → CSS var
el.querySelectorAll('[data-border]').forEach(el => {
el.style.setProperty('--th-border-color', el.dataset.border)
})
// data-mask-src → mask-image
el.querySelectorAll('[data-mask-src]').forEach(el => {
el.style.maskImage = 'url(' + el.dataset.maskSrc + ')'
el.style.webkitMaskImage = 'url(' + el.dataset.maskSrc + ')'
el.classList.add('bg-mask')
el.removeAttribute('data-mask-src')
})
}, [containerRef, pathname])
}

133
src/hooks/useSwiper.js Normal file
View File

@@ -0,0 +1,133 @@
import { useEffect, useRef } from 'react'
import { useLocation } from 'react-router-dom'
import Swiper from 'swiper'
import { Navigation, Pagination, Autoplay, EffectFade } from 'swiper/modules'
export default function useSwiper(containerRef) {
const { pathname } = useLocation()
const instances = useRef([])
useEffect(() => {
const container = containerRef.current
if (!container) return
// Destroy previous instances
instances.current.forEach(s => s.destroy(true, true))
instances.current = []
// Init all .th-slider elements
container.querySelectorAll('.th-slider').forEach(el => {
try {
const userOptions = JSON.parse(el.dataset.sliderOptions || '{}')
// Find pagination element — inside slider or sibling .slider-controller
let paginationEl = el.querySelector('.slider-pagination')
if (!paginationEl) {
const controller = el.closest('.th-slider')?.parentElement?.querySelector('.slider-controller')
if (controller) paginationEl = controller.querySelector('.slider-pagination')
}
const hasNumberPagi = el.querySelector('.slider-pagination.pagi-number')
const paginationType = userOptions.paginationType || 'bullets'
const swiper = new Swiper(el, {
modules: [Navigation, Pagination, Autoplay, EffectFade],
slidesPerView: userOptions.slidesPerView || 1,
spaceBetween: userOptions.spaceBetween !== undefined ? userOptions.spaceBetween : 24,
loop: userOptions.loop !== false,
speed: userOptions.speed || 1000,
autoplay: userOptions.autoplay || { delay: 6000, disableOnInteraction: false },
effect: userOptions.effect || 'slide',
centeredSlides: userOptions.centeredSlides || false,
slidesPerGroup: userOptions.slidesPerGroup || 1,
breakpoints: userOptions.breakpoints || {},
navigation: {
nextEl: el.querySelector('.slider-next'),
prevEl: el.querySelector('.slider-prev'),
},
pagination: paginationEl ? {
el: paginationEl,
type: paginationType,
clickable: true,
renderBullet: hasNumberPagi ? function (index, className) {
const num = index + 1
return '<span class="' + className + ' number">' + (num < 10 ? '0' + num : num) + '</span>'
} : undefined,
formatFractionCurrent: function (n) { return n < 10 ? '0' + n : n },
formatFractionTotal: function (n) { return n < 10 ? '0' + n : n },
} : false,
on: {
slideChange: function () {
setTimeout(function () {
if (swiper.params.mousewheel) swiper.params.mousewheel.releaseOnEdges = false
}, 500)
},
reachEnd: function () {
setTimeout(function () {
if (swiper.params.mousewheel) swiper.params.mousewheel.releaseOnEdges = true
}, 750)
},
...(userOptions.on || {}),
},
})
instances.current.push(swiper)
} catch (e) {
console.warn('useSwiper: failed to init', el.id || el.className, e)
}
})
// External controls: [data-slider-prev] and [data-slider-next]
const handleClick = (e) => {
const btn = e.target.closest('[data-slider-prev], [data-slider-next]')
if (!btn) return
const selector = btn.dataset.sliderPrev || btn.dataset.sliderNext
const sliderEl = document.querySelector(selector)
if (!sliderEl || !sliderEl.swiper) return
if (btn.dataset.sliderPrev) sliderEl.swiper.slidePrev()
else sliderEl.swiper.slideNext()
}
container.addEventListener('click', handleClick)
// Slider tabs: [data-slider-tab]
const tabContainers = container.querySelectorAll('[data-slider-tab]')
tabContainers.forEach(tc => {
const tabButtons = tc.querySelectorAll('.tab-btn')
const sliderSelector = tc.dataset.sliderTab
const sliderEl = document.querySelector(sliderSelector)
if (!sliderEl || !sliderEl.swiper || !tabButtons.length) return
const swiper = sliderEl.swiper
const setActive = (btn) => {
tabButtons.forEach(b => b.classList.remove('active'))
btn.classList.add('active')
}
tabButtons.forEach((btn, i) => {
btn.addEventListener('click', (e) => {
e.preventDefault()
setActive(btn)
swiper.slideTo(i)
})
})
swiper.on('slideChange', () => {
const idx = swiper.realIndex
if (tabButtons[idx]) setActive(tabButtons[idx])
})
// Set initial active
if (tabButtons[swiper.activeIndex]) {
tabButtons[swiper.activeIndex].classList.add('active')
}
})
return () => {
instances.current.forEach(s => s.destroy(true, true))
instances.current = []
container.removeEventListener('click', handleClick)
}
}, [containerRef, pathname])
}

View File

@@ -3,6 +3,9 @@ import { createRoot } from 'react-dom/client'
import App from './App.jsx'
// Vendors
import "swiper/css"
import "swiper/css/navigation"
import "swiper/css/pagination"
import './css/bootstrap.css'
import './css/swiper-bundle.css'
import './css/magnific-popup.css'