@codesinger0/shared-components 1.0.20 → 1.0.22
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.
- package/dist/components/Hero.jsx +146 -0
- package/dist/components/LargeItemCard.jsx +1 -1
- package/dist/components/SmallItemCard.jsx +1 -1
- package/dist/components/SmallItemsGrid.jsx +1 -1
- package/dist/components/elements/FixedWidthHeroVideo.jsx +86 -0
- package/dist/components/modals/ItemDetailsModal.jsx +1 -1
- package/dist/index.js +4 -3
- package/package.json +1 -1
- /package/dist/components/{CTAButton.jsx → elements/CTAButton.jsx} +0 -0
- /package/dist/components/{RoundButton.jsx → elements/RoundButton.jsx} +0 -0
- /package/dist/components/{SmallButton.jsx → elements/SmallButton.jsx} +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import CTAButton from "./elements/CTAButton";
|
|
2
|
+
import FixedWidthHeroVideo from "./elements/FixedWidthHeroVideo";
|
|
3
|
+
import { ArrowDown } from 'lucide-react'
|
|
4
|
+
import { motion } from "framer-motion";
|
|
5
|
+
|
|
6
|
+
const Hero = ({
|
|
7
|
+
// Media props
|
|
8
|
+
mediaType = 'image', // 'video' or 'image'
|
|
9
|
+
videoId = '', // YouTube video ID
|
|
10
|
+
imageUrl = '', // Image URL for static background
|
|
11
|
+
mediaHeight = '70vh', // Height of the media section
|
|
12
|
+
|
|
13
|
+
// Overlay
|
|
14
|
+
opacity = 70,
|
|
15
|
+
|
|
16
|
+
// Hero text props
|
|
17
|
+
title,
|
|
18
|
+
subtitle,
|
|
19
|
+
miniSubtitle,
|
|
20
|
+
additionalElements,
|
|
21
|
+
ctaText,
|
|
22
|
+
|
|
23
|
+
// Introduction section props
|
|
24
|
+
showIntroSection,
|
|
25
|
+
introHeight = '40vh',
|
|
26
|
+
introTitle,
|
|
27
|
+
introContent,
|
|
28
|
+
introImage,
|
|
29
|
+
onCtaClick
|
|
30
|
+
}) => {
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<section className="hero-section relative w-full overflow-hidden" style={{ height: mediaHeight }}>
|
|
34
|
+
{/* Media Background Container */}
|
|
35
|
+
<div className="absolute inset-0 w-full h-full" aria-hidden="true">
|
|
36
|
+
{mediaType === 'video' ? (
|
|
37
|
+
<FixedWidthHeroVideo useYoutube={true} youtubeVideoId={videoId} />
|
|
38
|
+
) : (
|
|
39
|
+
<div
|
|
40
|
+
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
|
41
|
+
style={{
|
|
42
|
+
backgroundImage: `url(${imageUrl})`,
|
|
43
|
+
backgroundColor: '#1a1a1a' // Fallback color
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{/* Content Overlay */}
|
|
50
|
+
<div
|
|
51
|
+
className="relative z-10 flex items-center justify-center h-full"
|
|
52
|
+
style={{ backgroundColor: `rgba(255, 255, 255, ${opacity / 100})` }}
|
|
53
|
+
>
|
|
54
|
+
<div className="text-center px-0">
|
|
55
|
+
<motion.div
|
|
56
|
+
initial={{ opacity: 0, y: 30 }}
|
|
57
|
+
animate={{ opacity: 1, y: 0 }}
|
|
58
|
+
transition={{ duration: 0.8 }}
|
|
59
|
+
className="mb-8"
|
|
60
|
+
>
|
|
61
|
+
<h1 className="main-title mb-4">
|
|
62
|
+
{title}
|
|
63
|
+
</h1>
|
|
64
|
+
<p className="main-subtitle mb-4">
|
|
65
|
+
{subtitle}
|
|
66
|
+
</p>
|
|
67
|
+
<p className="main-content mb-12">
|
|
68
|
+
{miniSubtitle}
|
|
69
|
+
</p>
|
|
70
|
+
{additionalElements}
|
|
71
|
+
<CTAButton onClick={onCtaClick} className="shadow-lg">
|
|
72
|
+
{ctaText}
|
|
73
|
+
</CTAButton>
|
|
74
|
+
</motion.div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<motion.div
|
|
78
|
+
className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-100"
|
|
79
|
+
animate={{ y: [0, 10, 0] }}
|
|
80
|
+
transition={{ duration: 2, repeat: Infinity }}
|
|
81
|
+
>
|
|
82
|
+
<ArrowDown className="w-8 h-8 text-sky-600" />
|
|
83
|
+
</motion.div>
|
|
84
|
+
</div>
|
|
85
|
+
</section>
|
|
86
|
+
|
|
87
|
+
{/* Introduction Section */}
|
|
88
|
+
{showIntroSection && (
|
|
89
|
+
<section className=" py-16 px-4" style={{ minHeight: introHeight }}>
|
|
90
|
+
<div className="max-w-6xl mx-auto" dir="rtl">
|
|
91
|
+
{introImage ? (
|
|
92
|
+
/* Layout with image */
|
|
93
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 items-center">
|
|
94
|
+
{/* Text Content */}
|
|
95
|
+
<div className="text-center lg:text-right order-2 lg:order-1">
|
|
96
|
+
<h2 className="title mb-6">
|
|
97
|
+
{introTitle}
|
|
98
|
+
</h2>
|
|
99
|
+
<p className="subtitle leading-relaxed" style={{ whiteSpace: 'pre-line' }}>
|
|
100
|
+
{introContent}
|
|
101
|
+
</p>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
{/* Image */}
|
|
105
|
+
<motion.div
|
|
106
|
+
key={'image'}
|
|
107
|
+
initial={{ opacity: 0, x: 50 }}
|
|
108
|
+
whileInView={{ opacity: 1, x: 0 }}
|
|
109
|
+
viewport={{ once: true }}
|
|
110
|
+
transition={{ delay: 1 * 0.5 }}
|
|
111
|
+
className="h-full"
|
|
112
|
+
>
|
|
113
|
+
<div className="order-1 lg:order-2 flex justify-center lg:justify-end">
|
|
114
|
+
<div className="w-full max-w-md lg:max-w-lg">
|
|
115
|
+
<img
|
|
116
|
+
src={introImage}
|
|
117
|
+
alt={introTitle || "Introduction image"}
|
|
118
|
+
className="w-full h-auto rounded-lg shadow-lg object-cover"
|
|
119
|
+
onError={(e) => {
|
|
120
|
+
e.target.style.display = 'none';
|
|
121
|
+
console.warn('Failed to load intro image:', introImage);
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</motion.div>
|
|
127
|
+
</div>
|
|
128
|
+
) : (
|
|
129
|
+
/* Text only layout (original) */
|
|
130
|
+
<div className="text-center max-w-4xl mx-auto">
|
|
131
|
+
<h2 className="title mb-6">
|
|
132
|
+
{introTitle}
|
|
133
|
+
</h2>
|
|
134
|
+
<p className="subtitle leading-relaxed" style={{ whiteSpace: 'pre-line' }}>
|
|
135
|
+
{introContent}
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
</section>
|
|
141
|
+
)}
|
|
142
|
+
</>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default Hero
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import SmallItemCard from './SmallItemCard';
|
|
3
|
-
import CTAButton from './CTAButton';
|
|
3
|
+
import CTAButton from './elements/CTAButton';
|
|
4
4
|
import useEmblaCarousel from 'embla-carousel-react';
|
|
5
5
|
import Autoplay from 'embla-carousel-autoplay';
|
|
6
6
|
import { useItemModal } from '../context/ItemModalContext'
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const FixedWidthHeroVideo = ({ youtubeVideoId = "dQw4w9WgXcQ", useYoutube = false }) => {
|
|
4
|
+
const [viewportSize, setViewportSize] = useState({ width: 0, height: 0 });
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const updateViewportSize = () => {
|
|
8
|
+
setViewportSize({
|
|
9
|
+
width: window.innerWidth,
|
|
10
|
+
height: window.innerHeight
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
updateViewportSize();
|
|
15
|
+
window.addEventListener('resize', updateViewportSize);
|
|
16
|
+
return () => window.removeEventListener('resize', updateViewportSize);
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const youtubeEmbedUrl = `https://www.youtube.com/embed/${youtubeVideoId}?autoplay=1&mute=1&loop=1&playlist=${youtubeVideoId}&controls=0&showinfo=0&rel=0&iv_load_policy=3&modestbranding=1`;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="w-full">
|
|
24
|
+
|
|
25
|
+
{/* Hero Section */}
|
|
26
|
+
<section className="relative w-full h-screen overflow-hidden flex items-center justify-center">
|
|
27
|
+
{useYoutube ? (
|
|
28
|
+
/* YouTube Embed */
|
|
29
|
+
<iframe
|
|
30
|
+
className="absolute top-[35%] left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-0 pointer-events-none"
|
|
31
|
+
style={{
|
|
32
|
+
minWidth: '1200px',
|
|
33
|
+
width: 'max(100vw, 1200px)',
|
|
34
|
+
aspectRatio: '16/9',
|
|
35
|
+
minHeight: '120%',
|
|
36
|
+
border: 'none'
|
|
37
|
+
}}
|
|
38
|
+
src={youtubeEmbedUrl}
|
|
39
|
+
title="YouTube video player"
|
|
40
|
+
frameBorder="0"
|
|
41
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
42
|
+
allowFullScreen
|
|
43
|
+
/>
|
|
44
|
+
) : (
|
|
45
|
+
/* Direct Video */
|
|
46
|
+
<video
|
|
47
|
+
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover z-0"
|
|
48
|
+
style={{
|
|
49
|
+
minWidth: '1200px',
|
|
50
|
+
width: 'max(100vw, 1200px)',
|
|
51
|
+
aspectRatio: '16/9',
|
|
52
|
+
height: 'auto'
|
|
53
|
+
}}
|
|
54
|
+
autoPlay
|
|
55
|
+
muted
|
|
56
|
+
loop
|
|
57
|
+
playsInline
|
|
58
|
+
>
|
|
59
|
+
<source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4" />
|
|
60
|
+
Your browser does not support the video tag.
|
|
61
|
+
</video>
|
|
62
|
+
)}
|
|
63
|
+
</section>
|
|
64
|
+
|
|
65
|
+
{/* CSS Animation Styles */}
|
|
66
|
+
<style jsx>{`
|
|
67
|
+
@keyframes fadeInUp {
|
|
68
|
+
from {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
transform: translateY(30px);
|
|
71
|
+
}
|
|
72
|
+
to {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
transform: translateY(0);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`}</style>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Usage examples:
|
|
83
|
+
// <FixedWidthHeroVideo useYoutube={false} />
|
|
84
|
+
// <FixedWidthHeroVideo useYoutube={true} youtubeVideoId="dQw4w9WgXcQ" />
|
|
85
|
+
|
|
86
|
+
export default FixedWidthHeroVideo;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
3
|
import { X, ShoppingCart } from 'lucide-react';
|
|
4
|
-
import SmallButton from '../SmallButton';
|
|
4
|
+
import SmallButton from '../elements/SmallButton';
|
|
5
5
|
import useScrollLock from '../../hooks/useScrollLock';
|
|
6
6
|
|
|
7
7
|
const ItemDetailsModal = ({ item, isOpen, onClose, onAddToCart }) => {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export { default as RoundButton } from './components/RoundButton';
|
|
2
|
-
export { default as CTAButton } from './components/CTAButton';
|
|
3
|
-
export { default as SmallButton } from './components/SmallButton';
|
|
1
|
+
export { default as RoundButton } from './components/elements/RoundButton';
|
|
2
|
+
export { default as CTAButton } from './components/elements/CTAButton';
|
|
3
|
+
export { default as SmallButton } from './components/elements/SmallButton';
|
|
4
4
|
export { default as LargeItemCard } from './components/LargeItemCard';
|
|
5
5
|
export { default as SmallItemsGrid } from './components/SmallItemsGrid';
|
|
6
6
|
export { default as SmallItemCard } from './components/SmallItemCard';
|
|
7
|
+
export { default as Hero } from './components/Hero'
|
|
7
8
|
|
|
8
9
|
// Modals
|
|
9
10
|
export { default as ItemDetailsModal } from './components/modals/ItemDetailsModal'
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|