Skip to content

Commit f68b6a8

Browse files
committed
docs(plays): add readme for pomodoro timer
2 parents d0eb8bc + ab2bfbe commit f68b6a8

13 files changed

Lines changed: 1124 additions & 10 deletions

File tree

‎netlify.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
command = "npm run build"
33
publish = "build"
44

5+
[build.environment]
6+
NODE_OPTIONS = "--max-old-space-size=4096"
7+
58
[[headers]]
69
for = "/*"
710
[headers.values]

‎package.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@
105105
"prettier --write"
106106
]
107107
},
108+
"pnpm": {
109+
"overrides": {
110+
"json-graphql-parser@0.1.9>axios": "^0.27.2"
111+
}
112+
},
108113
"reactSnap": {
109114
"puppeteerArgs": [
110115
"--no-sandbox",
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
.character-counter-wrapper {
2+
display: flex;
3+
justify-content: center;
4+
align-items: flex-start;
5+
min-height: 500px;
6+
padding: 2rem;
7+
background-color: #f0f2f5;
8+
}
9+
10+
.counter-card {
11+
background: #fff;
12+
padding: 30px;
13+
border-radius: 12px;
14+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
15+
width: 100%;
16+
max-width: 700px;
17+
}
18+
19+
.counter-title {
20+
margin-bottom: 20px;
21+
color: #1a202c;
22+
font-size: 1.5rem;
23+
}
24+
25+
.counter-textarea {
26+
width: 100%;
27+
height: 250px;
28+
padding: 15px;
29+
border: 2px solid #e2e8f0;
30+
border-radius: 8px;
31+
font-size: 16px;
32+
line-height: 1.5;
33+
outline: none;
34+
transition: border-color 0.2s;
35+
color: black;
36+
}
37+
38+
.counter-textarea:focus {
39+
border-color: #4a90e2;
40+
}
41+
42+
.stats-container {
43+
display: flex;
44+
justify-content: space-between;
45+
margin: 25px 0;
46+
gap: 10px;
47+
}
48+
49+
.stat-item {
50+
flex: 1;
51+
background: #f8fafc;
52+
padding: 15px;
53+
border-radius: 8px;
54+
text-align: center;
55+
border: 1px solid #edf2f7;
56+
}
57+
58+
.stat-num {
59+
display: block;
60+
font-size: 1.8rem;
61+
font-weight: bold;
62+
color: #4a90e2;
63+
}
64+
65+
.stat-label {
66+
font-size: 0.75rem;
67+
color: #718096;
68+
text-transform: uppercase;
69+
letter-spacing: 1px;
70+
}
71+
72+
.reset-btn {
73+
background-color: #e53e3e;
74+
color: white;
75+
border: none;
76+
padding: 10px 20px;
77+
border-radius: 6px;
78+
cursor: pointer;
79+
font-weight: 600;
80+
transition: opacity 0.2s;
81+
}
82+
83+
.reset-btn:hover {
84+
opacity: 0.9;
85+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React, { useState } from 'react';
2+
import './CharacterCounter.css';
3+
4+
const CharacterCounter = () => {
5+
const [text, setText] = useState('');
6+
7+
const charCount = text.length;
8+
const wordCount = text.trim() === '' ? 0 : text.trim().split(/\s+/).filter(Boolean).length;
9+
const readingTime = Math.ceil(wordCount / 200);
10+
11+
return (
12+
<div className="character-counter-wrapper">
13+
<div className="counter-card">
14+
<h2 className="counter-title">Character & Word Counter</h2>
15+
16+
<textarea
17+
className="counter-textarea"
18+
placeholder="Start typing or paste your text here..."
19+
value={text}
20+
onChange={(e) => setText(e.target.value)}
21+
/>
22+
23+
<div className="stats-container">
24+
<div className="stat-item">
25+
<span className="stat-num">{charCount}</span>
26+
<span className="stat-label">Characters</span>
27+
</div>
28+
<div className="stat-item">
29+
<span className="stat-num">{wordCount}</span>
30+
<span className="stat-label">Words</span>
31+
</div>
32+
<div className="stat-item">
33+
<span className="stat-num">{readingTime} min</span>
34+
<span className="stat-label">Reading Time</span>
35+
</div>
36+
</div>
37+
38+
<button className="reset-btn" onClick={() => setText('')}>
39+
Clear All
40+
</button>
41+
</div>
42+
</div>
43+
);
44+
};
45+
46+
export default CharacterCounter;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Character Counter
2+
3+
A simple, real-time character and word counter built with React. This play helps users analyze the length of their text and estimate reading time.
4+
5+
### 🚀 Features
6+
- **Real-time Counting**: Counts characters (including spaces) and words as you type.
7+
- **Accurate Word Logic**: Uses Regex to ensure multiple spaces or new lines don't inflate the word count.
8+
- **Reading Time Estimation**: Calculates the average time a human would take to read the text (based on 200 wpm).
9+
- **Clear Action**: A one-click button to reset the text area.
10+
11+
### 💻 React Concepts Used
12+
- **useState Hook**: To manage the string state of the text input.
13+
- **Event Handling**: Capturing `onChange` events to trigger re-calculations.
14+
- **Derived State**: Calculating counts directly during the render for better performance.
15+
16+
### 🛠️ How to use
17+
1. Type or paste your text into the provided text area.
18+
2. View the statistics updated instantly in the cards below.
19+
3. Click "Clear All" to start fresh.
20+
21+
---
22+
**Author**: IamAnkit19
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"id": "character-counter",
3+
"name": "Character Counter",
4+
"description": "A clean tool to calculate character and word count in real-time.",
5+
"language": "js",
6+
"level": "Beginner",
7+
"tags": "useState, Regex",
8+
"author": "IamAnkit19",
9+
"github_id": "IamAnkit19",
10+
"cover": "https://placehold.co/600x400?text=Character+Counter",
11+
"blog": "",
12+
"video": ""
13+
}

‎src/plays/pomodoro-timer/Readme.md‎

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,76 @@
11
# Pomodoro Timer
22

3-
A customizable Pomodoro Timer built with React and TypeScript that allows users to set focus and break durations. It includes start, pause, and reset controls and automatically switches between sessions. This play demonstrates React hooks, state management, timer logic, and modular component design.
3+
A customizable Pomodoro Timer built with React and TypeScript that helps users improve productivity using the Pomodoro Technique.
4+
5+
Users can set focus, short break, and long break durations using an interactive scroll-based time picker. The timer supports start, pause, and reset controls, and automatically manages session timing.
6+
7+
This play demonstrates clean React architecture, custom hooks, state management, and interactive UI design.
8+
9+
---
410

511
## Play Demographic
612

7-
- Language: ts
13+
- Language: TypeScript
814
- Level: Intermediate
915

16+
---
17+
1018
## Creator Information
1119

1220
- User: deansereigns
13-
- Gihub Link: https://github.com/deansereigns
14-
- Blog:
15-
- Video:
21+
- GitHub: https://github.com/deansereigns
22+
23+
---
24+
25+
## Features
26+
27+
- Focus, Short Break, and Long Break sessions
28+
- Scroll-based time selection (interactive wheel picker)
29+
- Start, Pause, and Reset controls
30+
- Automatic session handling
31+
- Clean and responsive UI
32+
- Built using React functional components and hooks
33+
34+
---
35+
36+
## React Concepts Used
37+
38+
- Functional Components
39+
- useState for managing timer state
40+
- useEffect for timer interval handling
41+
- Custom Hook (usePomodoroTimer)
42+
- Component composition
43+
- Controlled components
44+
- TypeScript for type safety
45+
46+
---
1647

1748
## Implementation Details
1849

19-
Update your implementation idea and details here
50+
The timer logic is implemented using a custom hook (`usePomodoroTimer`) which manages:
51+
52+
- session state
53+
- timer countdown
54+
- start, pause, reset logic
55+
- time updates from scroll picker
56+
57+
The UI is broken into modular components:
58+
59+
- TimerDisplay
60+
- DualTimePicker
61+
- SessionSelector
62+
- TimerControls
63+
64+
This ensures clean separation of logic and presentation.
65+
66+
---
2067

21-
## Consideration
68+
## Considerations
2269

23-
Update all considerations(if any)
70+
- Timer updates immediately when time is changed
71+
- Scroll picker wraps values circularly
72+
- Timer stops correctly when paused or reset
73+
- Clean and reusable component structure
2474

25-
## Resources
75+
---
2676

27-
Update external resources(if any)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Cover Image Placeholder
2+
3+
Please add a `cover.png` file to this directory.
4+
5+
Recommended specifications:
6+
- Format: PNG
7+
- Dimensions: Approximately 800x600 pixels (or similar aspect ratio)
8+
- Content: A representative screenshot or icon for the SVG Optimizer play
9+
10+
You can use a screenshot of the SVG Optimizer interface or create a custom graphic that represents the tool's functionality.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React from 'react';
2+
3+
function OptimizationPanel({ options, onChange }) {
4+
const handleToggle = (optionKey) => {
5+
onChange({
6+
...options,
7+
[optionKey]: !options[optionKey]
8+
});
9+
};
10+
11+
return (
12+
<div className="svg-optimization-panel">
13+
<h3>Optimization Options</h3>
14+
<div className="svg-options-grid">
15+
<label className="svg-option-item">
16+
<input
17+
checked={options.removeComments}
18+
type="checkbox"
19+
onChange={() => handleToggle('removeComments')}
20+
/>
21+
<span>Remove Comments</span>
22+
</label>
23+
24+
<label className="svg-option-item">
25+
<input
26+
checked={options.removeMetadata}
27+
type="checkbox"
28+
onChange={() => handleToggle('removeMetadata')}
29+
/>
30+
<span>Remove Metadata</span>
31+
</label>
32+
33+
<label className="svg-option-item">
34+
<input
35+
checked={options.removeHiddenElements}
36+
type="checkbox"
37+
onChange={() => handleToggle('removeHiddenElements')}
38+
/>
39+
<span>Remove Hidden Elements</span>
40+
</label>
41+
42+
<label className="svg-option-item">
43+
<input
44+
checked={options.removeEmptyAttributes}
45+
type="checkbox"
46+
onChange={() => handleToggle('removeEmptyAttributes')}
47+
/>
48+
<span>Remove Empty Attributes</span>
49+
</label>
50+
51+
<label className="svg-option-item">
52+
<input
53+
checked={options.minifyColors}
54+
type="checkbox"
55+
onChange={() => handleToggle('minifyColors')}
56+
/>
57+
<span>Minify Colors</span>
58+
</label>
59+
60+
<label className="svg-option-item">
61+
<input
62+
checked={options.removeDefaultAttributes}
63+
type="checkbox"
64+
onChange={() => handleToggle('removeDefaultAttributes')}
65+
/>
66+
<span>Remove Default Values</span>
67+
</label>
68+
69+
<label className="svg-option-item">
70+
<input
71+
checked={options.removeXMLNS}
72+
type="checkbox"
73+
onChange={() => handleToggle('removeXMLNS')}
74+
/>
75+
<span>Remove XMLNS</span>
76+
</label>
77+
78+
<label className="svg-option-item">
79+
<input
80+
checked={options.prettify}
81+
type="checkbox"
82+
onChange={() => handleToggle('prettify')}
83+
/>
84+
<span>Prettify Output</span>
85+
</label>
86+
</div>
87+
</div>
88+
);
89+
}
90+
91+
export default OptimizationPanel;

0 commit comments

Comments
 (0)