@codesinger0/shared-components 1.1.55 → 1.1.57
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.
|
@@ -7,10 +7,12 @@ const ArticlesList = ({
|
|
|
7
7
|
title,
|
|
8
8
|
subtitle,
|
|
9
9
|
articles = [],
|
|
10
|
+
articleOpenType = 'modal',
|
|
10
11
|
className = '',
|
|
11
12
|
...props
|
|
12
13
|
}) => {
|
|
13
14
|
const [selectedArticle, setSelectedArticle] = useState(null);
|
|
15
|
+
const [expandedArticleId, setExpandedArticleId] = useState(null);
|
|
14
16
|
|
|
15
17
|
useScrollLock(selectedArticle !== null);
|
|
16
18
|
|
|
@@ -26,6 +28,14 @@ const ArticlesList = ({
|
|
|
26
28
|
);
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
const handleArticleClick = (article, index) => {
|
|
32
|
+
if (articleOpenType === 'modal') {
|
|
33
|
+
setSelectedArticle(article);
|
|
34
|
+
} else {
|
|
35
|
+
setExpandedArticleId(expandedArticleId === (article.id || index) ? null : (article.id || index));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
29
39
|
// Function to get first 3 lines of text
|
|
30
40
|
const getExcerpt = (text) => {
|
|
31
41
|
const lines = text?.trim().split('\n').filter(line => line.trim());
|
|
@@ -50,9 +60,9 @@ const ArticlesList = ({
|
|
|
50
60
|
{title}
|
|
51
61
|
</h2>
|
|
52
62
|
)}
|
|
53
|
-
|
|
63
|
+
|
|
54
64
|
<div className="w-20 h-1 bg-gradient-to-r from-primary to-primary-bright mx-auto mb-6"></div>
|
|
55
|
-
|
|
65
|
+
|
|
56
66
|
{subtitle && (
|
|
57
67
|
<p className="subtitle max-w-3xl mx-auto">
|
|
58
68
|
{subtitle}
|
|
@@ -64,6 +74,7 @@ const ArticlesList = ({
|
|
|
64
74
|
{/* Articles Grid */}
|
|
65
75
|
<div className="grid lg:grid-cols-2 gap-8" dir="rtl">
|
|
66
76
|
{articles.map((article, index) => {
|
|
77
|
+
const isExpanded = expandedArticleId === (article.id || index);
|
|
67
78
|
const isEven = index % 2 === 0;
|
|
68
79
|
const cardBg = isEven ? 'bg-gradient-to-br from-white to-green-50' : 'bg-gradient-to-br from-white to-sky-50';
|
|
69
80
|
const iconGradient = isEven ? 'from-green-500 to-green-600' : 'from-sky-400 to-sky-500';
|
|
@@ -78,10 +89,11 @@ const ArticlesList = ({
|
|
|
78
89
|
viewport={{ once: true }}
|
|
79
90
|
transition={{ delay: index * 0.1 }}
|
|
80
91
|
>
|
|
81
|
-
<div
|
|
92
|
+
<div
|
|
82
93
|
className={`h-full cursor-pointer border-none shadow-lg hover:shadow-xl transition-all duration-300 ${cardBg} group rounded-xl`}
|
|
83
|
-
onClick={() =>
|
|
94
|
+
onClick={() => handleArticleClick(article, index)}
|
|
84
95
|
>
|
|
96
|
+
{/* Card Header - Always visible */}
|
|
85
97
|
<div className="p-6 pb-4">
|
|
86
98
|
<div className="flex items-start gap-4">
|
|
87
99
|
<div className={`w-12 h-12 bg-gradient-to-br ${iconGradient} rounded-full flex items-center justify-center flex-shrink-0`}>
|
|
@@ -98,17 +110,36 @@ const ArticlesList = ({
|
|
|
98
110
|
</div>
|
|
99
111
|
</div>
|
|
100
112
|
</div>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
|
|
114
|
+
{/* Excerpt and "Read More" - Hidden when expanded in inline mode */}
|
|
115
|
+
{!(articleOpenType === 'inline' && isExpanded) && (
|
|
116
|
+
<div className="px-6 pb-6">
|
|
117
|
+
<p className="text-gray-600 leading-relaxed whitespace-pre-line" dir="rtl">
|
|
118
|
+
{article.excerpt}
|
|
119
|
+
</p>
|
|
120
|
+
<div className="mt-6">
|
|
121
|
+
<span className={`inline-flex items-center ${textColor} font-medium ${textHover} transition-colors`}>
|
|
122
|
+
קרא עוד
|
|
123
|
+
<ArrowDown className="w-4 h-4 mr-2 rotate-180 group-hover:transform group-hover:-translate-y-1 transition-transform" />
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
110
126
|
</div>
|
|
111
|
-
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
{/* Full Content - Only shown when expanded in inline mode */}
|
|
130
|
+
{articleOpenType === 'inline' && isExpanded && (
|
|
131
|
+
<div className="px-6 pb-6 border-t border-gray-200">
|
|
132
|
+
<div className="prose prose-lg max-w-none">
|
|
133
|
+
<ArticleFormatter text={article.content} />
|
|
134
|
+
</div>
|
|
135
|
+
<div className="mt-6">
|
|
136
|
+
<span className={`inline-flex items-center ${textColor} font-medium ${textHover} transition-colors`}>
|
|
137
|
+
סגור
|
|
138
|
+
<ArrowDown className="w-4 h-4 mr-2 group-hover:transform group-hover:translate-y-1 transition-transform" />
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
112
143
|
</div>
|
|
113
144
|
</motion.div>
|
|
114
145
|
);
|
|
@@ -118,66 +149,67 @@ const ArticlesList = ({
|
|
|
118
149
|
</section>
|
|
119
150
|
|
|
120
151
|
{/* Article Modal */}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/>
|
|
133
|
-
|
|
134
|
-
{/* Modal Content */}
|
|
135
|
-
<motion.div
|
|
136
|
-
initial={{ opacity: 0, scale: 0.9, y: 20 }}
|
|
137
|
-
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
138
|
-
exit={{ opacity: 0, scale: 0.9, y: 20 }}
|
|
139
|
-
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
|
140
|
-
className="relative w-full max-w-4xl mx-4 max-h-[90vh] bg-white rounded-xl shadow-2xl overflow-hidden"
|
|
141
|
-
onClick={(e) => e.stopPropagation()}
|
|
142
|
-
>
|
|
143
|
-
{/* Close Button */}
|
|
144
|
-
<button
|
|
152
|
+
{articleOpenType === 'modal' && (
|
|
153
|
+
<AnimatePresence>
|
|
154
|
+
{selectedArticle && (
|
|
155
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center supports-[height:100dvh]:h-[100dvh]">
|
|
156
|
+
{/* Backdrop */}
|
|
157
|
+
<motion.div
|
|
158
|
+
initial={{ opacity: 0 }}
|
|
159
|
+
animate={{ opacity: 1 }}
|
|
160
|
+
exit={{ opacity: 0 }}
|
|
161
|
+
transition={{ duration: 0.2 }}
|
|
162
|
+
className="absolute inset-0 bg-black bg-opacity-50 backdrop-blur-sm"
|
|
145
163
|
onClick={() => setSelectedArticle(null)}
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
{/* Modal Content */}
|
|
167
|
+
<motion.div
|
|
168
|
+
initial={{ opacity: 0, scale: 0.9, y: 20 }}
|
|
169
|
+
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
170
|
+
exit={{ opacity: 0, scale: 0.9, y: 20 }}
|
|
171
|
+
transition={{ type: "spring", stiffness: 300, damping: 30 }}
|
|
172
|
+
className="relative w-full max-w-4xl mx-4 max-h-[90vh] bg-white rounded-xl shadow-2xl overflow-hidden"
|
|
173
|
+
onClick={(e) => e.stopPropagation()}
|
|
148
174
|
>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
{/* Close Button */}
|
|
176
|
+
<button
|
|
177
|
+
onClick={() => setSelectedArticle(null)}
|
|
178
|
+
className="absolute top-4 left-4 z-10 bg-white hover:bg-gray-100 text-gray-700 p-2 rounded-full shadow-lg transition-colors duration-200"
|
|
179
|
+
aria-label="סגור"
|
|
180
|
+
>
|
|
181
|
+
<X size={24} />
|
|
182
|
+
</button>
|
|
183
|
+
|
|
184
|
+
{/* Scrollable Content */}
|
|
185
|
+
<div className="overflow-y-auto max-h-[90vh] p-8 md:p-12" dir="rtl">
|
|
186
|
+
{/* Article Header */}
|
|
187
|
+
<div className="mb-8">
|
|
188
|
+
<div className="flex items-center gap-4 mb-6">
|
|
189
|
+
<div className="w-16 h-16 bg-gradient-to-br from-green-500 to-green-600 rounded-full flex items-center justify-center flex-shrink-0">
|
|
190
|
+
{selectedArticle.icon ? (
|
|
191
|
+
<selectedArticle.icon className="w-8 h-8 text-white" />
|
|
192
|
+
) : (
|
|
193
|
+
<BookOpen className="w-8 h-8 text-white" />
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
<h2 className="text-3xl md:text-4xl font-bold text-green-900">
|
|
197
|
+
{selectedArticle.title}
|
|
198
|
+
</h2>
|
|
163
199
|
</div>
|
|
164
|
-
<
|
|
165
|
-
{selectedArticle.title}
|
|
166
|
-
</h2>
|
|
200
|
+
<div className="w-20 h-1 bg-gradient-to-r from-green-500 to-sky-400"></div>
|
|
167
201
|
</div>
|
|
168
|
-
<div className="w-20 h-1 bg-gradient-to-r from-green-500 to-sky-400"></div>
|
|
169
|
-
</div>
|
|
170
202
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
203
|
+
{/* Article Content */}
|
|
204
|
+
<div className="prose prose-lg max-w-none">
|
|
205
|
+
<ArticleFormatter text={selectedArticle.content} />
|
|
206
|
+
</div>
|
|
174
207
|
</div>
|
|
175
|
-
</div>
|
|
176
|
-
</
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
208
|
+
</motion.div>
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
</AnimatePresence>
|
|
212
|
+
)}
|
|
181
213
|
{/* Custom Styles */}
|
|
182
214
|
<style jsx>{`
|
|
183
215
|
.from-primary {
|
|
@@ -202,7 +234,7 @@ const ArticleFormatter = ({ text }) => {
|
|
|
202
234
|
{sections.map((section, index) => {
|
|
203
235
|
// Find the first newline to separate a potential heading from its paragraph
|
|
204
236
|
const firstNewlineIndex = section.indexOf('\n');
|
|
205
|
-
|
|
237
|
+
|
|
206
238
|
let heading = '';
|
|
207
239
|
let paragraph = '';
|
|
208
240
|
|
|
@@ -79,7 +79,7 @@ const LargeItemCard = ({
|
|
|
79
79
|
{/* Price Section */}
|
|
80
80
|
{price && (
|
|
81
81
|
<div className="text-center lg:text-right mt-4">
|
|
82
|
-
{discountPrice && discountPrice < price ? (
|
|
82
|
+
{(discountPrice ?? null) !== null && discountPrice < price ? (
|
|
83
83
|
<div className="space-y-1">
|
|
84
84
|
<div className="flex items-center justify-center lg:justify-start gap-2">
|
|
85
85
|
<span className={"text-2xl font-bold " + priceClass}>
|