ChandimaPrabath commited on
Commit
b10c786
1 Parent(s): c37ac4d

hero section 2.0

Browse files
frontend/src/app/globals.css CHANGED
@@ -1,23 +1,28 @@
1
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&family=Noto+Sans:wght@400;500;700;900&display=swap');
2
 
3
  @tailwind base;
4
  @tailwind components;
5
  @tailwind utilities;
6
 
 
 
 
 
 
7
  body {
8
- font-family: 'Inter', 'Noto Sans', sans-serif;
9
- background-color: #0a0a10 !important;
10
- -ms-overflow-style: none; /* IE and Edge */
11
- scrollbar-width: none; /* Firefox */
12
  scroll-behavior: smooth;
13
  }
14
 
15
- .app-container{
16
- -ms-overflow-style: none; /* IE and Edge */
17
- scrollbar-width: none; /* Firefox */
18
  scroll-behavior: smooth;
19
  overflow: scroll;
20
- max-height: 100dvh;
21
  padding-top: 80px;
22
  }
23
 
 
1
+ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&family=Noto+Sans:wght@400;500;700;900&display=swap");
2
 
3
  @tailwind base;
4
  @tailwind components;
5
  @tailwind utilities;
6
 
7
+ :root {
8
+ --bg-primary: #11121f;
9
+ --bg-secondary: #0c0c16;
10
+ }
11
+
12
  body {
13
+ font-family: "Inter", "Noto Sans", sans-serif;
14
+ background-color: var(--bg-primary) !important;
15
+ -ms-overflow-style: none; /* IE and Edge */
16
+ scrollbar-width: none; /* Firefox */
17
  scroll-behavior: smooth;
18
  }
19
 
20
+ .app-container {
21
+ -ms-overflow-style: none; /* IE and Edge */
22
+ scrollbar-width: none; /* Firefox */
23
  scroll-behavior: smooth;
24
  overflow: scroll;
25
+ height: 100dvh;
26
  padding-top: 80px;
27
  }
28
 
frontend/src/app/index.css CHANGED
@@ -11,7 +11,6 @@
11
  .index-page-container {
12
  display: flex;
13
  flex-direction: column;
14
- margin: 0 auto;
15
  flex: 1;
16
  }
17
 
 
11
  .index-page-container {
12
  display: flex;
13
  flex-direction: column;
 
14
  flex: 1;
15
  }
16
 
frontend/src/components/HeroSection.css CHANGED
@@ -12,32 +12,80 @@
12
  .hero-container {
13
  position: relative;
14
  width: 100%;
15
- overflow: hidden;
16
  }
17
 
18
  .hero-section {
19
  display: flex;
20
  flex-direction: column;
21
- min-height: 480px;
 
22
  position: relative;
23
  border-radius: 12px;
24
- padding: 1rem;
25
- padding-bottom: 2.5rem;
26
  justify-content: center;
27
- animation: fadeIn 1s ease-in-out; /* Add fade-in animation */
28
  }
29
 
30
  .hero-image {
31
  position: absolute;
32
  top: 0;
33
  left: 0;
34
- width: 100%;
35
- height: 100%;
36
  background-size: cover;
37
  background-position: center;
38
  background-repeat: no-repeat;
39
  transition: opacity 1s ease-in-out; /* Smooth transition */
40
  opacity: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
  .hero-image.active {
@@ -47,24 +95,27 @@
47
  .hero-text {
48
  display: flex;
49
  flex-direction: column;
50
- justify-content: center;
51
  color: white;
52
  gap: 0.5rem;
53
  text-align: left;
54
  z-index: 1;
55
  height: 100%;
56
- margin: 20px;
57
- width: 49%;
 
 
58
  }
59
 
60
  .hero-title {
61
  color: white;
62
- font-size: 5rem;
63
  font-family: "Anton", sans-serif;
64
  font-weight: 400;
65
  font-style: normal;
66
  line-height: 1;
67
  transition: font-size 0.3s ease-in-out;
 
 
68
  }
69
 
70
  .hero-section-play-button {
@@ -76,7 +127,7 @@
76
 
77
  .hero-description {
78
  color: white;
79
- font-size: 1rem;
80
  font-weight: normal;
81
  line-height: 1.4;
82
  overflow: hidden; /* Hide text overflow */
@@ -85,13 +136,13 @@
85
  -webkit-box-orient: vertical; /* Set box orientation to vertical */
86
  text-overflow: ellipsis; /* Add ellipsis (...) */
87
  transition: font-size 0.3s ease-in-out;
 
88
  }
89
 
90
  .hero-indicators {
91
  position: absolute;
92
  bottom: 1rem;
93
  left: 50%;
94
- transform: translateX(-50%);
95
  display: flex;
96
  gap: 0.5rem;
97
  }
@@ -111,85 +162,30 @@
111
  }
112
 
113
  /* Responsive Styles */
114
- @media (max-width: 1200px) {
115
- .hero-title {
116
- font-size: 2rem;
117
- }
118
- .hero-description {
119
- font-size: 0.875rem;
120
- }
121
- .hero-button {
122
- height: 2rem;
123
- font-size: 0.75rem;
124
- padding: 0 0.75rem;
125
- }
126
- .indicator {
127
- width: 15px;
128
- height: 7px;
129
- }
130
- }
131
-
132
- @media (max-width: 992px) {
133
- .hero-section {
134
- min-height: 400px;
135
  }
136
  .hero-title {
137
- font-size: 1.75rem;
 
138
  }
139
- .hero-description {
140
- font-size: 0.75rem;
141
- }
142
- .hero-button {
143
- height: 1.75rem;
144
- font-size: 0.675rem;
145
- padding: 0 0.5rem;
146
- }
147
- .indicator {
148
- width: 12px;
149
- height: 6px;
150
- }
151
- }
152
-
153
- @media (max-width: 768px) {
154
  .hero-section {
155
- min-height: 300px;
156
- padding: 0.5rem;
157
- padding-bottom: 2rem;
158
  }
159
- .hero-title {
160
- font-size: 1.5rem;
161
- }
162
- .hero-description {
163
- font-size: 0.875rem;
164
- }
165
- .hero-button {
166
- height: 1.5rem;
167
- font-size: 0.75rem;
168
- padding: 0 0.5rem;
169
- }
170
- .indicator {
171
- width: 15px;
172
- height: 8px;
173
  }
 
174
  }
175
-
176
- @media (max-width: 576px) {
177
- .hero-section {
178
- min-height: 200px;
179
  }
180
- .hero-title {
181
- font-size: 1.25rem;
182
- }
183
- .hero-description {
184
- font-size: 0.75rem;
185
- }
186
- .hero-button {
187
- height: 1.25rem;
188
- font-size: 0.625rem;
189
- padding: 0 0.5rem;
190
  }
191
- .indicator {
192
- width: 12px;
193
- height: 6px;
194
- }
195
- }
 
12
  .hero-container {
13
  position: relative;
14
  width: 100%;
15
+ overflow: visible;
16
  }
17
 
18
  .hero-section {
19
  display: flex;
20
  flex-direction: column;
21
+ min-height: 30rem;
22
+ width: 100dvw;
23
  position: relative;
24
  border-radius: 12px;
25
+ animation: fadeIn 1s ease-in-out;
 
26
  justify-content: center;
 
27
  }
28
 
29
  .hero-image {
30
  position: absolute;
31
  top: 0;
32
  left: 0;
33
+ width: 100dvw;
34
+ height: 100dvh;
35
  background-size: cover;
36
  background-position: center;
37
  background-repeat: no-repeat;
38
  transition: opacity 1s ease-in-out; /* Smooth transition */
39
  opacity: 0;
40
+ z-index: -1;
41
+ animation: hero-anim-landscape 15s infinite;
42
+ }
43
+
44
+ @keyframes hero-anim-portrait {
45
+ 0% {
46
+ background-position: center;
47
+ background-size: 160% 140%;
48
+ }
49
+ 25% {
50
+ background-position: top left;
51
+ background-size: 120% 100%;
52
+ }
53
+ 50% {
54
+ background-position: bottom right;
55
+ background-size: 160% 140%;
56
+ }
57
+ 75% {
58
+ background-position: top right;
59
+ background-size: 120% 100%;
60
+ }
61
+ 100% {
62
+ background-position: center;
63
+ background-size: 160% 140%;
64
+ }
65
+ }
66
+
67
+
68
+ @keyframes hero-anim-landscape {
69
+ 0% {
70
+ background-position: center;
71
+ background-size: 130% 150%;
72
+ }
73
+ 25% {
74
+ background-position: top left;
75
+ background-size: 100% 120%;
76
+ }
77
+ 50% {
78
+ background-position: bottom right;
79
+ background-size: 130% 150%;
80
+ }
81
+ 75% {
82
+ background-position: top right;
83
+ background-size: 100% 120%;
84
+ }
85
+ 100% {
86
+ background-position: center;
87
+ background-size: 130% 150%;
88
+ }
89
  }
90
 
91
  .hero-image.active {
 
95
  .hero-text {
96
  display: flex;
97
  flex-direction: column;
 
98
  color: white;
99
  gap: 0.5rem;
100
  text-align: left;
101
  z-index: 1;
102
  height: 100%;
103
+ margin-top: 30px;
104
+ margin-left: 20px;
105
+ width: 80%;
106
+ justify-content: space-around;
107
  }
108
 
109
  .hero-title {
110
  color: white;
111
+ font-size: 7rem;
112
  font-family: "Anton", sans-serif;
113
  font-weight: 400;
114
  font-style: normal;
115
  line-height: 1;
116
  transition: font-size 0.3s ease-in-out;
117
+ user-select: none;
118
+ width: auto;
119
  }
120
 
121
  .hero-section-play-button {
 
127
 
128
  .hero-description {
129
  color: white;
130
+ font-size: 1.2rem;
131
  font-weight: normal;
132
  line-height: 1.4;
133
  overflow: hidden; /* Hide text overflow */
 
136
  -webkit-box-orient: vertical; /* Set box orientation to vertical */
137
  text-overflow: ellipsis; /* Add ellipsis (...) */
138
  transition: font-size 0.3s ease-in-out;
139
+ user-select: none;
140
  }
141
 
142
  .hero-indicators {
143
  position: absolute;
144
  bottom: 1rem;
145
  left: 50%;
 
146
  display: flex;
147
  gap: 0.5rem;
148
  }
 
162
  }
163
 
164
  /* Responsive Styles */
165
+ @media (orientation:portrait){
166
+ .hero-image{
167
+ height: 85dvw;
168
+ width: 100dvw;
169
+ animation: hero-anim-portrait 15s infinite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  }
171
  .hero-title {
172
+ color: white;
173
+ font-size: 2.5rem;
174
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  .hero-section {
176
+ min-height: 14rem;
 
 
177
  }
178
+ .hero-description{
179
+ font-size: .9rem;
 
 
 
 
 
 
 
 
 
 
 
 
180
  }
181
+
182
  }
183
+ @media (orientation:landscape){
184
+ .hero-section{
185
+ height: 3.5rem;
 
186
  }
187
+ .hero-text{
188
+ margin-top: 3rem;
189
+ margin-left: 4rem;
 
 
 
 
 
 
 
190
  }
191
+ }
 
 
 
 
frontend/src/components/HeroSection.js CHANGED
@@ -1,116 +1,159 @@
1
- 'use client';
2
- import { useState, useEffect, useRef } from 'react';
3
- import './HeroSection.css';
4
- import apiClient from '@/api/apiClient';
5
- import SkeletonLoader from '@/skeletons/HeroSection';
6
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
7
- import { faPlay } from '@fortawesome/free-solid-svg-icons';
8
- import Link from 'next/link';
9
 
10
  const HeroSection = () => {
11
- const [currentIndex, setCurrentIndex] = useState(0);
12
- const intervalRef = useRef(null);
13
- const [fadeOut, setFadeOut] = useState(false);
14
- const [items, setItems] = useState([]);
15
- const [loading, setLoading] = useState(true);
 
 
16
 
17
- useEffect(() => {
18
- const fetchRecentItems = async () => {
19
- try {
20
- const response = await apiClient.getRecent();
21
- console.log(response);
22
- const movies = response.movies.map(film => ({
23
- title: film[0],
24
- description: film[2],
25
- imageUrl: film[3],
26
- type: 'Movie'
27
- }));
28
- const series = response.series.map(serie => ({
29
- title: serie[0],
30
- description: serie[2],
31
- imageUrl: serie[3],
32
- type: 'Series'
33
- }));
34
- setItems([...movies, ...series]);
35
- } catch (error) {
36
- console.error('Error fetching recent items:', error);
37
- } finally {
38
- setLoading(false);
39
- }
40
- };
 
 
41
 
42
- fetchRecentItems();
43
- }, []);
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- const startAutoSwitch = () => {
46
- if (intervalRef.current) clearInterval(intervalRef.current);
47
- intervalRef.current = setInterval(() => {
48
- setFadeOut(true);
49
- setTimeout(() => {
50
- setCurrentIndex(prevIndex => (prevIndex + 1) % items.length);
51
- setFadeOut(false);
52
- }, 200);
53
- }, 5000);
54
  };
 
55
 
56
- useEffect(() => {
57
- if (items.length > 0) {
58
- startAutoSwitch();
 
 
 
 
 
59
  }
 
 
 
 
 
60
 
61
- return () => {
62
- clearInterval(intervalRef.current);
63
- };
64
- }, [items]);
65
-
66
- const handleIndicatorClick = (index) => {
67
- setFadeOut(true);
68
- setTimeout(() => {
69
- setCurrentIndex(index);
70
- setFadeOut(false);
71
- }, 100);
72
- startAutoSwitch();
 
73
  };
 
 
 
 
 
 
 
74
 
75
- if (loading) {
76
- return <SkeletonLoader />;
 
 
 
 
 
 
 
77
  }
 
 
 
 
 
78
 
79
- const { title, description, imageUrl, type } = items[currentIndex];
80
- const linkPath = `/${type.toLowerCase()}/${encodeURIComponent(title)}`;
 
81
 
82
- return (
83
- <div className="hero-container">
84
- <div className="hero-section">
85
- {items.map((item, index) => (
86
- <div
87
- key={index}
88
- className={`hero-image ${index === currentIndex ? 'active' : ''} ${fadeOut ? 'fade-out' : ''}`}
89
- style={{ backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.1) 0%, #11121f 100%), url("${item.imageUrl}")` }}
90
- ></div>
91
- ))}
92
- <div className="hero-text">
93
- <h1 className="hero-title">{title}</h1>
94
- <Link href={linkPath}>
95
- <button className="hero-section-play-button">
96
- <FontAwesomeIcon icon={faPlay} size="lg" /> Play
97
- </button>
98
- </Link>
99
- <p className="hero-description">{description}</p>
100
- </div>
101
- </div>
102
 
103
- <div className="hero-indicators">
104
- {items.map((_, index) => (
105
- <div
106
- key={index}
107
- className={`indicator ${index === currentIndex ? 'active' : ''}`}
108
- onClick={() => handleIndicatorClick(index)}
109
- ></div>
110
- ))}
111
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  </div>
113
- );
 
 
114
  };
115
 
116
  export default HeroSection;
 
1
+ "use client";
2
+ import { useState, useEffect, useRef } from "react";
3
+ import "./HeroSection.css";
4
+ import apiClient from "@/api/apiClient";
5
+ import SkeletonLoader from "@/skeletons/HeroSection";
6
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
7
+ import { faPlay } from "@fortawesome/free-solid-svg-icons";
8
+ import Link from "next/link";
9
 
10
  const HeroSection = () => {
11
+ const [currentIndex, setCurrentIndex] = useState(0);
12
+ const intervalRef = useRef(null);
13
+ const [fadeOut, setFadeOut] = useState(false);
14
+ const [items, setItems] = useState([]);
15
+ const [loading, setLoading] = useState(true);
16
+ const [isDragging, setIsDragging] = useState(false);
17
+ const [startX, setStartX] = useState(0);
18
 
19
+ useEffect(() => {
20
+ const fetchRecentItems = async () => {
21
+ try {
22
+ const response = await apiClient.getRecent();
23
+ const movies = response.movies.map((film) => ({
24
+ title: film[0],
25
+ description: film[2],
26
+ imageUrl: film[3],
27
+ type: "Movie",
28
+ }));
29
+ const series = response.series.map((serie) => ({
30
+ title: serie[0],
31
+ description: serie[2],
32
+ imageUrl: serie[3],
33
+ type: "Series",
34
+ }));
35
+ setItems([...movies, ...series]);
36
+ } catch (error) {
37
+ console.error("Error fetching recent items:", error);
38
+ } finally {
39
+ setLoading(false);
40
+ }
41
+ };
42
+
43
+ fetchRecentItems();
44
+ }, []);
45
 
46
+ const startAutoSwitch = () => {
47
+ if (intervalRef.current) clearInterval(intervalRef.current);
48
+ intervalRef.current = setInterval(() => {
49
+ setFadeOut(true);
50
+ setTimeout(() => {
51
+ setCurrentIndex((prevIndex) => (prevIndex + 1) % items.length);
52
+ setFadeOut(false);
53
+ }, 200);
54
+ }, 5000);
55
+ };
56
+
57
+ useEffect(() => {
58
+ if (items.length > 0) {
59
+ startAutoSwitch();
60
+ }
61
 
62
+ return () => {
63
+ clearInterval(intervalRef.current);
 
 
 
 
 
 
 
64
  };
65
+ }, [items]);
66
 
67
+ const handleSwipe = (direction) => {
68
+ setFadeOut(true);
69
+ setTimeout(() => {
70
+ setCurrentIndex((prevIndex) => {
71
+ if (direction === "left") {
72
+ return (prevIndex + 1) % items.length;
73
+ } else {
74
+ return (prevIndex - 1 + items.length) % items.length;
75
  }
76
+ });
77
+ setFadeOut(false);
78
+ }, 100);
79
+ startAutoSwitch();
80
+ };
81
 
82
+ const handleTouchStart = (e) => {
83
+ const touchStartX = e.touches[0].clientX;
84
+ setStartX(touchStartX);
85
+ const handleTouchMove = (event) => {
86
+ const touchEndX = event.touches[0].clientX;
87
+ const diffX = touchStartX - touchEndX;
88
+ if (diffX > 50) {
89
+ handleSwipe("left");
90
+ document.removeEventListener("touchmove", handleTouchMove);
91
+ } else if (diffX < -50) {
92
+ handleSwipe("right");
93
+ document.removeEventListener("touchmove", handleTouchMove);
94
+ }
95
  };
96
+ document.addEventListener("touchmove", handleTouchMove);
97
+ };
98
+
99
+ const handleMouseDown = (e) => {
100
+ setStartX(e.clientX);
101
+ setIsDragging(true);
102
+ };
103
 
104
+ const handleMouseMove = (e) => {
105
+ if (!isDragging) return;
106
+ const diffX = startX - e.clientX;
107
+ if (diffX > 50) {
108
+ handleSwipe("left");
109
+ setIsDragging(false);
110
+ } else if (diffX < -50) {
111
+ handleSwipe("right");
112
+ setIsDragging(false);
113
  }
114
+ };
115
+
116
+ const handleMouseUp = () => {
117
+ setIsDragging(false);
118
+ };
119
 
120
+ if (loading) {
121
+ return <SkeletonLoader />;
122
+ }
123
 
124
+ const { title, description, imageUrl, type } = items[currentIndex];
125
+ const linkPath = `/${type.toLowerCase()}/${encodeURIComponent(title)}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ return (
128
+ <div
129
+ className="hero-container"
130
+ onTouchStart={handleTouchStart}
131
+ onMouseDown={handleMouseDown}
132
+ onMouseMove={handleMouseMove}
133
+ onMouseUp={handleMouseUp}
134
+ onMouseLeave={handleMouseUp}
135
+ >
136
+ <div className="hero-section">
137
+ {items.map((item, index) => (
138
+ <div
139
+ key={index}
140
+ className={`hero-image ${index === currentIndex ? "active" : ""} ${
141
+ fadeOut ? "fade-out" : ""
142
+ }`}
143
+ style={{
144
+ backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.1) 20%, #11121f 80%), url("${item.imageUrl}")`,
145
+ }}
146
+ ></div>
147
+ ))}
148
+ <div className="hero-text">
149
+ <Link href={linkPath}>
150
+ <h1 className="hero-title">{title}</h1>
151
+ </Link>
152
+ <p className="hero-description">{description}</p>
153
  </div>
154
+ </div>
155
+ </div>
156
+ );
157
  };
158
 
159
  export default HeroSection;
frontend/src/components/Sidebar.css CHANGED
@@ -5,7 +5,7 @@
5
  top: 0;
6
  left: 0;
7
  height: 100dvh;
8
- background-color: #0c0c16;
9
  color: white;
10
  font-family: 'Inter', sans-serif;
11
  transition: width .5s ,height .5s;
 
5
  top: 0;
6
  left: 0;
7
  height: 100dvh;
8
+ background-color: var(--bg-secondary);
9
  color: white;
10
  font-family: 'Inter', sans-serif;
11
  transition: width .5s ,height .5s;
frontend/src/skeletons/HeroSection.css CHANGED
@@ -2,7 +2,7 @@
2
 
3
  .skeleton-container {
4
  position: relative;
5
- width: 100%;
6
  height: 480px; /* Set height to auto for responsiveness */
7
  display: flex;
8
  flex-direction: column;
@@ -89,7 +89,7 @@
89
 
90
  @media (max-width: 576px) {
91
  .skeleton-container {
92
- height: 200px;
93
  }
94
  .skeleton-title {
95
  width: 90%;
 
2
 
3
  .skeleton-container {
4
  position: relative;
5
+ width: 100dvw;
6
  height: 480px; /* Set height to auto for responsiveness */
7
  display: flex;
8
  flex-direction: column;
 
89
 
90
  @media (max-width: 576px) {
91
  .skeleton-container {
92
+ height: 225px;
93
  }
94
  .skeleton-title {
95
  width: 90%;