@zomako/elearning-components 1.0.0

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,134 @@
1
+ .flashcard-deck-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ gap: 2rem;
6
+ padding: 2rem;
7
+ max-width: 600px;
8
+ margin: 0 auto;
9
+ }
10
+
11
+ .flashcard-deck-header {
12
+ width: 100%;
13
+ text-align: center;
14
+ }
15
+
16
+ .flashcard-counter {
17
+ font-size: 1rem;
18
+ color: #666;
19
+ font-weight: 500;
20
+ }
21
+
22
+ .flashcard-wrapper {
23
+ perspective: 1000px;
24
+ width: 100%;
25
+ min-height: 300px;
26
+ }
27
+
28
+ .flashcard {
29
+ position: relative;
30
+ width: 100%;
31
+ height: 300px;
32
+ cursor: pointer;
33
+ transform-style: preserve-3d;
34
+ }
35
+
36
+ .flashcard-face {
37
+ position: absolute;
38
+ width: 100%;
39
+ height: 100%;
40
+ backface-visibility: hidden;
41
+ border-radius: 12px;
42
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ padding: 2rem;
47
+ box-sizing: border-box;
48
+ }
49
+
50
+ .flashcard-front {
51
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
52
+ color: white;
53
+ transform: rotateY(0deg);
54
+ }
55
+
56
+ .flashcard-back {
57
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
58
+ color: white;
59
+ transform: rotateY(180deg);
60
+ }
61
+
62
+ .flashcard-content {
63
+ font-size: 1.25rem;
64
+ text-align: center;
65
+ word-wrap: break-word;
66
+ overflow-wrap: break-word;
67
+ line-height: 1.6;
68
+ font-weight: 500;
69
+ }
70
+
71
+ .flashcard-controls {
72
+ display: flex;
73
+ gap: 1rem;
74
+ width: 100%;
75
+ justify-content: center;
76
+ }
77
+
78
+ .flashcard-button {
79
+ padding: 0.75rem 2rem;
80
+ font-size: 1rem;
81
+ font-weight: 600;
82
+ border: none;
83
+ border-radius: 8px;
84
+ cursor: pointer;
85
+ transition: all 0.3s ease;
86
+ background-color: #667eea;
87
+ color: white;
88
+ min-width: 120px;
89
+ }
90
+
91
+ .flashcard-button:hover:not(:disabled) {
92
+ background-color: #5568d3;
93
+ transform: translateY(-2px);
94
+ box-shadow: 0 4px 8px rgba(102, 126, 234, 0.3);
95
+ }
96
+
97
+ .flashcard-button:active:not(:disabled) {
98
+ transform: translateY(0);
99
+ }
100
+
101
+ .flashcard-button:disabled {
102
+ background-color: #e0e0e0;
103
+ color: #9e9e9e;
104
+ cursor: not-allowed;
105
+ opacity: 0.6;
106
+ }
107
+
108
+ .flashcard-empty-message {
109
+ text-align: center;
110
+ color: #666;
111
+ font-size: 1.1rem;
112
+ padding: 2rem;
113
+ }
114
+
115
+ /* Responsive design */
116
+ @media (max-width: 768px) {
117
+ .flashcard-deck-container {
118
+ padding: 1rem;
119
+ }
120
+
121
+ .flashcard {
122
+ height: 250px;
123
+ }
124
+
125
+ .flashcard-content {
126
+ font-size: 1.1rem;
127
+ }
128
+
129
+ .flashcard-button {
130
+ padding: 0.625rem 1.5rem;
131
+ font-size: 0.9rem;
132
+ min-width: 100px;
133
+ }
134
+ }
@@ -0,0 +1,87 @@
1
+ import React, { useState } from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import './FlashcardDeck.css';
4
+
5
+ const FlashcardDeck = ({ cards = [] }) => {
6
+ const [currentIndex, setCurrentIndex] = useState(0);
7
+ const [isFlipped, setIsFlipped] = useState(false);
8
+
9
+ if (!cards || cards.length === 0) {
10
+ return (
11
+ <div className="flashcard-deck-container">
12
+ <p className="flashcard-empty-message">No cards available</p>
13
+ </div>
14
+ );
15
+ }
16
+
17
+ const currentCard = cards[currentIndex];
18
+ const canGoPrevious = currentIndex > 0;
19
+ const canGoNext = currentIndex < cards.length - 1;
20
+
21
+ const handlePrevious = () => {
22
+ if (canGoPrevious) {
23
+ setCurrentIndex(currentIndex - 1);
24
+ setIsFlipped(false);
25
+ }
26
+ };
27
+
28
+ const handleNext = () => {
29
+ if (canGoNext) {
30
+ setCurrentIndex(currentIndex + 1);
31
+ setIsFlipped(false);
32
+ }
33
+ };
34
+
35
+ const handleCardClick = () => {
36
+ setIsFlipped(!isFlipped);
37
+ };
38
+
39
+ return (
40
+ <div className="flashcard-deck-container">
41
+ <div className="flashcard-deck-header">
42
+ <span className="flashcard-counter">
43
+ Card {currentIndex + 1} of {cards.length}
44
+ </span>
45
+ </div>
46
+
47
+ <div className="flashcard-wrapper">
48
+ <motion.div
49
+ key={currentIndex}
50
+ className="flashcard"
51
+ onClick={handleCardClick}
52
+ animate={{ rotateY: isFlipped ? 180 : 0 }}
53
+ transition={{ duration: 0.6, type: 'spring', stiffness: 100 }}
54
+ style={{ transformStyle: 'preserve-3d' }}
55
+ >
56
+ <div className="flashcard-face flashcard-front">
57
+ <div className="flashcard-content">{currentCard.front}</div>
58
+ </div>
59
+ <div className="flashcard-face flashcard-back">
60
+ <div className="flashcard-content">{currentCard.back}</div>
61
+ </div>
62
+ </motion.div>
63
+ </div>
64
+
65
+ <div className="flashcard-controls">
66
+ <button
67
+ className="flashcard-button flashcard-button-previous"
68
+ onClick={handlePrevious}
69
+ disabled={!canGoPrevious}
70
+ aria-label="Previous card"
71
+ >
72
+ Previous
73
+ </button>
74
+ <button
75
+ className="flashcard-button flashcard-button-next"
76
+ onClick={handleNext}
77
+ disabled={!canGoNext}
78
+ aria-label="Next card"
79
+ >
80
+ Next
81
+ </button>
82
+ </div>
83
+ </div>
84
+ );
85
+ };
86
+
87
+ export default FlashcardDeck;
@@ -0,0 +1,96 @@
1
+ import React, { useState } from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import './FlashcardDeck.css';
4
+
5
+ export interface Flashcard {
6
+ front: string;
7
+ back: string;
8
+ }
9
+
10
+ interface FlashcardDeckProps {
11
+ cards?: Flashcard[];
12
+ }
13
+
14
+ const FlashcardDeck: React.FC<FlashcardDeckProps> = ({ cards = [] }) => {
15
+ const [currentIndex, setCurrentIndex] = useState<number>(0);
16
+ const [isFlipped, setIsFlipped] = useState<boolean>(false);
17
+
18
+ if (!cards || cards.length === 0) {
19
+ return (
20
+ <div className="flashcard-deck-container">
21
+ <p className="flashcard-empty-message">No cards available</p>
22
+ </div>
23
+ );
24
+ }
25
+
26
+ const currentCard = cards[currentIndex];
27
+ const canGoPrevious = currentIndex > 0;
28
+ const canGoNext = currentIndex < cards.length - 1;
29
+
30
+ const handlePrevious = (): void => {
31
+ if (canGoPrevious) {
32
+ setCurrentIndex(currentIndex - 1);
33
+ setIsFlipped(false);
34
+ }
35
+ };
36
+
37
+ const handleNext = (): void => {
38
+ if (canGoNext) {
39
+ setCurrentIndex(currentIndex + 1);
40
+ setIsFlipped(false);
41
+ }
42
+ };
43
+
44
+ const handleCardClick = (): void => {
45
+ setIsFlipped(!isFlipped);
46
+ };
47
+
48
+ return (
49
+ <div className="flashcard-deck-container">
50
+ <div className="flashcard-deck-header">
51
+ <span className="flashcard-counter">
52
+ Card {currentIndex + 1} of {cards.length}
53
+ </span>
54
+ </div>
55
+
56
+ <div className="flashcard-wrapper">
57
+ <motion.div
58
+ key={currentIndex}
59
+ className="flashcard"
60
+ onClick={handleCardClick}
61
+ animate={{ rotateY: isFlipped ? 180 : 0 }}
62
+ transition={{ duration: 0.6, type: 'spring', stiffness: 100 }}
63
+ style={{ transformStyle: 'preserve-3d' }}
64
+ >
65
+ <div className="flashcard-face flashcard-front">
66
+ <div className="flashcard-content">{currentCard.front}</div>
67
+ </div>
68
+ <div className="flashcard-face flashcard-back">
69
+ <div className="flashcard-content">{currentCard.back}</div>
70
+ </div>
71
+ </motion.div>
72
+ </div>
73
+
74
+ <div className="flashcard-controls">
75
+ <button
76
+ className="flashcard-button flashcard-button-previous"
77
+ onClick={handlePrevious}
78
+ disabled={!canGoPrevious}
79
+ aria-label="Previous card"
80
+ >
81
+ Previous
82
+ </button>
83
+ <button
84
+ className="flashcard-button flashcard-button-next"
85
+ onClick={handleNext}
86
+ disabled={!canGoNext}
87
+ aria-label="Next card"
88
+ >
89
+ Next
90
+ </button>
91
+ </div>
92
+ </div>
93
+ );
94
+ };
95
+
96
+ export default FlashcardDeck;
@@ -0,0 +1,313 @@
1
+ # FlashcardDeck Component
2
+
3
+ A fully-featured, reusable React component for displaying interactive flashcards with smooth 3D flip animations. Perfect for e-learning applications, study tools, quiz systems, and any scenario where you need to present information in a card-based format with front and back sides.
4
+
5
+ ## Purpose
6
+
7
+ The `FlashcardDeck` component provides an engaging way to display educational content, study materials, or any paired information. It allows users to:
8
+
9
+ - View one card at a time from a deck of flashcards
10
+ - Click on cards to flip them and reveal the back side with a smooth 3D animation
11
+ - Navigate through the deck using Previous and Next buttons
12
+ - Track their progress with a card counter
13
+
14
+ The component is built with React and uses Framer Motion for smooth, performant animations. It's fully self-contained, responsive, and accessible.
15
+
16
+ ## Features
17
+
18
+ - **3D Flip Animation**: Smooth, spring-based 3D rotation animation powered by Framer Motion
19
+ - **Card Navigation**: Previous and Next buttons to move through the deck
20
+ - **Progress Tracking**: Displays current card position (e.g., "Card 1 of 10")
21
+ - **Responsive Design**: Optimized for both desktop and mobile devices
22
+ - **Accessibility**: Includes ARIA labels for screen readers
23
+ - **Empty State Handling**: Gracefully handles empty or missing card arrays
24
+ - **Auto-reset**: Automatically resets flip state when navigating to a new card
25
+ - **Disabled States**: Navigation buttons are properly disabled at deck boundaries
26
+
27
+ ## Installation
28
+
29
+ ### Prerequisites
30
+
31
+ - React 16.8+ (hooks support required)
32
+ - Node.js and npm (or yarn/pnpm)
33
+
34
+ ### Install Dependencies
35
+
36
+ ```bash
37
+ npm install react react-dom framer-motion
38
+ ```
39
+
40
+ Or with yarn:
41
+
42
+ ```bash
43
+ yarn add react react-dom framer-motion
44
+ ```
45
+
46
+ Or with pnpm:
47
+
48
+ ```bash
49
+ pnpm add react react-dom framer-motion
50
+ ```
51
+
52
+ ## Props
53
+
54
+ | Prop | Type | Required | Default | Description |
55
+ |------|------|----------|---------|-------------|
56
+ | `cards` | `Array<{front: string, back: string}>` | No | `[]` | An array of flashcard objects. Each object must have a `front` property (string) representing the front side text and a `back` property (string) representing the back side text. If not provided or empty, the component displays an empty state message. |
57
+
58
+ ### TypeScript Interface
59
+
60
+ If you're using TypeScript, you can import the type definitions:
61
+
62
+ ```typescript
63
+ import FlashcardDeck, { Flashcard } from './FlashcardDeck/FlashcardDeck';
64
+
65
+ // Flashcard interface
66
+ interface Flashcard {
67
+ front: string;
68
+ back: string;
69
+ }
70
+
71
+ // Component props interface
72
+ interface FlashcardDeckProps {
73
+ cards?: Flashcard[];
74
+ }
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ ### Basic Example
80
+
81
+ ```jsx
82
+ import React from 'react';
83
+ import FlashcardDeck from './FlashcardDeck/FlashcardDeck';
84
+
85
+ const App = () => {
86
+ const cards = [
87
+ {
88
+ front: 'What is React?',
89
+ back: 'React is a JavaScript library for building user interfaces, particularly web applications. It allows developers to create reusable UI components.',
90
+ },
91
+ {
92
+ front: 'What is JSX?',
93
+ back: 'JSX is a syntax extension for JavaScript that looks similar to HTML. It allows you to write HTML-like code in your JavaScript files.',
94
+ },
95
+ {
96
+ front: 'What is a component?',
97
+ back: 'A component is a reusable piece of code that returns JSX. Components can be functional or class-based and help organize your UI into manageable pieces.',
98
+ },
99
+ ];
100
+
101
+ return <FlashcardDeck cards={cards} />;
102
+ };
103
+
104
+ export default App;
105
+ ```
106
+
107
+ ### TypeScript Example
108
+
109
+ ```tsx
110
+ import React from 'react';
111
+ import FlashcardDeck, { Flashcard } from './FlashcardDeck/FlashcardDeck';
112
+
113
+ const App: React.FC = () => {
114
+ const cards: Flashcard[] = [
115
+ {
116
+ front: 'Capital of France?',
117
+ back: 'Paris',
118
+ },
119
+ {
120
+ front: 'Capital of Japan?',
121
+ back: 'Tokyo',
122
+ },
123
+ {
124
+ front: 'Capital of Australia?',
125
+ back: 'Canberra',
126
+ },
127
+ ];
128
+
129
+ return <FlashcardDeck cards={cards} />;
130
+ };
131
+
132
+ export default App;
133
+ ```
134
+
135
+ ### Dynamic Card Loading Example
136
+
137
+ ```jsx
138
+ import React, { useState, useEffect } from 'react';
139
+ import FlashcardDeck from './FlashcardDeck/FlashcardDeck';
140
+
141
+ const App = () => {
142
+ const [cards, setCards] = useState([]);
143
+ const [loading, setLoading] = useState(true);
144
+
145
+ useEffect(() => {
146
+ // Simulate API call
147
+ fetch('/api/flashcards')
148
+ .then((res) => res.json())
149
+ .then((data) => {
150
+ setCards(data);
151
+ setLoading(false);
152
+ })
153
+ .catch((error) => {
154
+ console.error('Error loading cards:', error);
155
+ setLoading(false);
156
+ });
157
+ }, []);
158
+
159
+ if (loading) {
160
+ return <div>Loading flashcards...</div>;
161
+ }
162
+
163
+ return <FlashcardDeck cards={cards} />;
164
+ };
165
+
166
+ export default App;
167
+ ```
168
+
169
+ ### Empty State Example
170
+
171
+ ```jsx
172
+ import React from 'react';
173
+ import FlashcardDeck from './FlashcardDeck/FlashcardDeck';
174
+
175
+ const App = () => {
176
+ // Component will display "No cards available" message
177
+ return <FlashcardDeck cards={[]} />;
178
+ };
179
+ ```
180
+
181
+ ## Component Structure
182
+
183
+ ```
184
+ FlashcardDeck/
185
+ ├── FlashcardDeck.jsx # Main component file (JavaScript)
186
+ ├── FlashcardDeck.tsx # Main component file (TypeScript)
187
+ ├── FlashcardDeck.css # Component styles
188
+ ├── index.js # Export file
189
+ └── README.md # This documentation
190
+ ```
191
+
192
+ ## Styling
193
+
194
+ The component includes its own CSS file with customizable styles. You can override the default styles by targeting the following CSS classes:
195
+
196
+ ### Available CSS Classes
197
+
198
+ - `.flashcard-deck-container` - Main container wrapper
199
+ - `.flashcard-deck-header` - Header section containing the card counter
200
+ - `.flashcard-counter` - Card position counter text
201
+ - `.flashcard-wrapper` - Wrapper with 3D perspective
202
+ - `.flashcard` - Card container with 3D transform
203
+ - `.flashcard-face` - Base class for both card faces
204
+ - `.flashcard-front` - Front side of the card (purple gradient)
205
+ - `.flashcard-back` - Back side of the card (pink gradient)
206
+ - `.flashcard-content` - Content wrapper inside each card face
207
+ - `.flashcard-controls` - Container for navigation buttons
208
+ - `.flashcard-button` - Base button styles
209
+ - `.flashcard-button-previous` - Previous button
210
+ - `.flashcard-button-next` - Next button
211
+ - `.flashcard-empty-message` - Empty state message
212
+
213
+ ### Custom Styling Example
214
+
215
+ ```css
216
+ /* Override card colors */
217
+ .flashcard-front {
218
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
219
+ }
220
+
221
+ .flashcard-back {
222
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
223
+ }
224
+
225
+ /* Customize button styles */
226
+ .flashcard-button {
227
+ background-color: #4CAF50;
228
+ border-radius: 20px;
229
+ }
230
+
231
+ .flashcard-button:hover:not(:disabled) {
232
+ background-color: #45a049;
233
+ }
234
+ ```
235
+
236
+ ## Animation Details
237
+
238
+ The component uses Framer Motion for animations:
239
+
240
+ - **Flip Animation**: Spring-based rotation on the Y-axis (180 degrees)
241
+ - **Duration**: 0.6 seconds
242
+ - **Animation Type**: Spring with stiffness of 100
243
+ - **3D Transform**: Uses CSS `transform-style: preserve-3d` for proper 3D rendering
244
+ - **Backface Visibility**: Hidden to ensure only the visible side is shown during rotation
245
+
246
+ ## Browser Support
247
+
248
+ Works in all modern browsers that support:
249
+ - CSS 3D Transforms
250
+ - ES6+ JavaScript
251
+ - React 16.8+ (hooks)
252
+
253
+ Tested and working in:
254
+ - Chrome/Edge (latest)
255
+ - Firefox (latest)
256
+ - Safari (latest)
257
+ - Mobile browsers (iOS Safari, Chrome Mobile)
258
+
259
+ ## Accessibility
260
+
261
+ The component includes accessibility features:
262
+
263
+ - ARIA labels on navigation buttons (`aria-label="Previous card"` and `aria-label="Next card"`)
264
+ - Semantic HTML structure
265
+ - Keyboard navigation support (buttons are focusable)
266
+ - Disabled state indicators for navigation buttons
267
+
268
+ ## Performance Considerations
269
+
270
+ - Uses React hooks for efficient state management
271
+ - Framer Motion handles GPU-accelerated animations
272
+ - Minimal re-renders (only updates when necessary)
273
+ - CSS transforms for smooth animations without layout shifts
274
+
275
+ ## Common Use Cases
276
+
277
+ 1. **Language Learning**: Vocabulary flashcards with words and translations
278
+ 2. **Study Tools**: Question and answer flashcards for exam preparation
279
+ 3. **Training Materials**: Concept explanations with detailed descriptions
280
+ 4. **Quiz Systems**: Interactive quiz questions with answers
281
+ 5. **Memory Games**: Matching games with clues and solutions
282
+
283
+ ## Troubleshooting
284
+
285
+ ### Cards not flipping
286
+
287
+ - Ensure Framer Motion is properly installed: `npm install framer-motion`
288
+ - Check that the CSS file is imported correctly
289
+ - Verify that both `front` and `back` properties exist on each card object
290
+
291
+ ### Navigation buttons not working
292
+
293
+ - Ensure cards array is not empty
294
+ - Check browser console for any JavaScript errors
295
+ - Verify that cards prop is passed correctly
296
+
297
+ ### Styling issues
298
+
299
+ - Make sure `FlashcardDeck.css` is imported in your component
300
+ - Check for CSS conflicts with your global styles
301
+ - Verify that CSS classes are not being overridden unintentionally
302
+
303
+ ## License
304
+
305
+ This component is part of the e-learning-components project. Use it freely in your projects.
306
+
307
+ ## Contributing
308
+
309
+ Contributions are welcome! Please ensure that:
310
+ - Code follows React best practices
311
+ - Animations remain smooth and performant
312
+ - Accessibility features are maintained
313
+ - Documentation is updated accordingly
@@ -0,0 +1 @@
1
+ export { default } from './FlashcardDeck';
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # E-learning Components
2
+
3
+ A collection of reusable React components for e-learning applications.
4
+
5
+ ## Components
6
+
7
+ ### FlashcardDeck
8
+
9
+ A fully-featured flashcard component with 3D flip animations. See `FlashcardDeck/README.md` for detailed documentation.
10
+
11
+ ## Getting Started
12
+
13
+ ### Installation
14
+
15
+ ```bash
16
+ npm install
17
+ ```
18
+
19
+ ### Development
20
+
21
+ ```bash
22
+ npm run dev
23
+ ```
24
+
25
+ ### Build
26
+
27
+ ```bash
28
+ npm run build
29
+ ```
30
+
31
+ ## Project Structure
32
+
33
+ ```
34
+ elearning-components/
35
+ ├── FlashcardDeck/ # FlashcardDeck component
36
+ │ ├── FlashcardDeck.jsx # Component (JavaScript)
37
+ │ ├── FlashcardDeck.tsx # Component (TypeScript)
38
+ │ ├── FlashcardDeck.css # Styles
39
+ │ ├── index.js # Export
40
+ │ └── README.md # Documentation
41
+ ├── src/ # Application source
42
+ │ ├── App.jsx # Demo app
43
+ │ └── main.jsx # Entry point
44
+ ├── package.json # Dependencies
45
+ ├── vite.config.js # Vite configuration
46
+ └── README.md # This file
47
+ ```
48
+
49
+ ## Technologies
50
+
51
+ - React 18
52
+ - Framer Motion
53
+ - Vite