@codesinger0/shared-components 1.0.19 → 1.0.21

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.
@@ -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,5 +1,5 @@
1
1
  import React from 'react';
2
- import RoundButton from './RoundButton.jsx'
2
+ import RoundButton from './elements/RoundButton.jsx'
3
3
  import { motion } from 'framer-motion'
4
4
 
5
5
  const LargeItemCard = ({
@@ -1,7 +1,7 @@
1
1
 
2
2
  import React from 'react';
3
3
  import { ShoppingCart } from 'lucide-react';
4
- import SmallButton from './SmallButton';
4
+ import SmallButton from './elements/SmallButton';
5
5
 
6
6
  const SmallItemCard = ({
7
7
  imageUrl,
@@ -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'
@@ -12,4 +13,4 @@ export { default as ItemDetailsModal } from './components/modals/ItemDetailsModa
12
13
  export { ItemModalProvider, useItemModal } from './context/ItemModalContext';
13
14
 
14
15
  // Hooks
15
- export { default as UseScrollLock } from './hooks/useScrollLock'
16
+ export { default as useScrollLock } from './hooks/useScrollLock'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesinger0/shared-components",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Shared React components for customer projects",
5
5
  "main": "dist/index.js",
6
6
  "files": [