gudu-programs 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.
- package/README.md +22 -0
- package/dist/favicon.svg +1 -0
- package/dist/gudu-programs.cjs +6 -0
- package/dist/gudu-programs.css +2 -0
- package/dist/gudu-programs.js +859 -0
- package/dist/gudu-programs.umd.cjs +6 -0
- package/dist/icons.svg +24 -0
- package/package.json +36 -0
- package/src/App.css +228 -0
- package/src/App.jsx +54 -0
- package/src/assets/hero.png +0 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/vite.svg +1 -0
- package/src/index.css +111 -0
- package/src/index.jsx +15 -0
- package/src/main.jsx +13 -0
- package/src/pages/Home.jsx +35 -0
- package/src/program-1/Program1.jsx +20 -0
- package/src/program-10/Program10.jsx +55 -0
- package/src/program-2/Footer.jsx +8 -0
- package/src/program-2/Header.jsx +3 -0
- package/src/program-2/Program2.jsx +18 -0
- package/src/program-3/Program3.jsx +29 -0
- package/src/program-4/Program4.jsx +9 -0
- package/src/program-4/ToDoFunction.jsx +35 -0
- package/src/program-5/BasicFigure.jsx +9 -0
- package/src/program-5/FigureList.jsx +38 -0
- package/src/program-5/Program5.jsx +10 -0
- package/src/program-6/Program6.jsx +53 -0
- package/src/program-7/ProfileCard.css +14 -0
- package/src/program-7/ProfileCard.jsx +11 -0
- package/src/program-7/Program7.jsx +22 -0
- package/src/program-8/Program8.jsx +46 -0
- package/src/program-9/Program9.jsx +25 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
class Fetcher extends React.Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props)
|
|
6
|
+
this.state = { items: [], loading: false, error: null, query: '' }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
componentDidMount() {
|
|
10
|
+
this.fetchData()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
componentDidUpdate(prevProps, prevState) {
|
|
14
|
+
if (prevState.query !== this.state.query) {
|
|
15
|
+
this.fetchData()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fetchData() {
|
|
20
|
+
this.setState({ loading: true, error: null })
|
|
21
|
+
const q = this.state.query || ''
|
|
22
|
+
fetch(`https://jsonplaceholder.typicode.com/posts${q ? `?_limit=5` : `?_limit=10`}`)
|
|
23
|
+
.then((r) => r.json())
|
|
24
|
+
.then((data) => this.setState({ items: data, loading: false }))
|
|
25
|
+
.catch((err) => this.setState({ error: err.message, loading: false }))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render() {
|
|
29
|
+
const { items, loading, error } = this.state
|
|
30
|
+
return (
|
|
31
|
+
<div>
|
|
32
|
+
<h2>Program 10 — Class Component & API</h2>
|
|
33
|
+
<div style={{ marginBottom: 8 }}>
|
|
34
|
+
<label>Query (not used for API but triggers update): <input onChange={(e)=>this.setState({query: e.target.value})} /></label>
|
|
35
|
+
<button onClick={()=>this.fetchData()}>Refresh</button>
|
|
36
|
+
</div>
|
|
37
|
+
{loading && <div>Loading...</div>}
|
|
38
|
+
{error && <div style={{ color: 'red' }}>{error}</div>}
|
|
39
|
+
<ul>
|
|
40
|
+
{items.map(i => (
|
|
41
|
+
<li key={i.id}><strong>{i.title}</strong><div>{i.body}</div></li>
|
|
42
|
+
))}
|
|
43
|
+
</ul>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default function Program10() {
|
|
50
|
+
return (
|
|
51
|
+
<div className="program-page">
|
|
52
|
+
<Fetcher />
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Header from './Header'
|
|
2
|
+
import Footer from './Footer'
|
|
3
|
+
|
|
4
|
+
export default function Program2() {
|
|
5
|
+
const title = 'My Props Demo App'
|
|
6
|
+
const tagline = 'Passing data via props is simple and powerful.'
|
|
7
|
+
const copyright = '© 2026 Example Corp.'
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="program-page">
|
|
11
|
+
<Header title={title} />
|
|
12
|
+
<main>
|
|
13
|
+
<p>This app shows how props pass data from parent to children.</p>
|
|
14
|
+
</main>
|
|
15
|
+
<Footer tagline={tagline} copyright={copyright} />
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function Program3() {
|
|
4
|
+
const initial = 0
|
|
5
|
+
const [count, setCount] = useState(initial)
|
|
6
|
+
const [step, setStep] = useState(1)
|
|
7
|
+
|
|
8
|
+
const inc = () => setCount((c) => c + Number(step))
|
|
9
|
+
const dec = () => setCount((c) => Math.max(0, c - Number(step)))
|
|
10
|
+
const reset = () => setCount(initial)
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="program-page">
|
|
14
|
+
<h2>Program 3 — Counter</h2>
|
|
15
|
+
<div style={{ fontSize: 48, margin: '12px 0', fontWeight: 700 }}>{count}</div>
|
|
16
|
+
<div style={{ display: 'flex', gap: 8 }}>
|
|
17
|
+
<button onClick={dec} className="btn">-</button>
|
|
18
|
+
<button onClick={inc} className="btn">+</button>
|
|
19
|
+
<button onClick={reset} className="btn">Reset</button>
|
|
20
|
+
</div>
|
|
21
|
+
<div style={{ marginTop: 12 }}>
|
|
22
|
+
<label>
|
|
23
|
+
Step:{' '}
|
|
24
|
+
<input type="number" value={step} min="1" onChange={(e) => setStep(e.target.value)} style={{ width: 80 }} />
|
|
25
|
+
</label>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function ToDoFunction() {
|
|
4
|
+
const [tasks, setTasks] = useState([])
|
|
5
|
+
const [text, setText] = useState('')
|
|
6
|
+
|
|
7
|
+
const add = () => {
|
|
8
|
+
const t = text.trim()
|
|
9
|
+
if (!t) return
|
|
10
|
+
setTasks((s) => [...s, { id: Date.now(), text: t, done: false }])
|
|
11
|
+
setText('')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const toggle = (id) => setTasks((s) => s.map(t => t.id===id? {...t, done: !t.done}: t))
|
|
15
|
+
const remove = (id) => setTasks((s) => s.filter(t => t.id!==id))
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
<h2>Program 4 — To-Do List</h2>
|
|
20
|
+
<div style={{ display: 'flex', gap: 8 }}>
|
|
21
|
+
<input value={text} onChange={(e) => setText(e.target.value)} placeholder="New task" />
|
|
22
|
+
<button onClick={add}>Add</button>
|
|
23
|
+
</div>
|
|
24
|
+
<ul style={{ marginTop: 12 }}>
|
|
25
|
+
{tasks.map((t) => (
|
|
26
|
+
<li key={t.id} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
27
|
+
<input type="checkbox" checked={t.done} onChange={() => toggle(t.id)} />
|
|
28
|
+
<span style={{ textDecoration: t.done ? 'line-through' : 'none' }}>{t.text}</span>
|
|
29
|
+
<button onClick={() => remove(t.id)} style={{ marginLeft: 'auto' }}>Delete</button>
|
|
30
|
+
</li>
|
|
31
|
+
))}
|
|
32
|
+
</ul>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export default function BasicFigure({ src, caption, onRemove }) {
|
|
2
|
+
return (
|
|
3
|
+
<figure style={{ border: '1px solid #ddd', padding: 8, borderRadius: 6 }}>
|
|
4
|
+
<img src={src} alt={caption} style={{ maxWidth: 200, display: 'block', marginBottom: 6 }} />
|
|
5
|
+
<figcaption>{caption}</figcaption>
|
|
6
|
+
{onRemove && <button onClick={onRemove} style={{ marginTop: 6 }}>Remove</button>}
|
|
7
|
+
</figure>
|
|
8
|
+
)
|
|
9
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import BasicFigure from './BasicFigure'
|
|
3
|
+
|
|
4
|
+
const starter = [
|
|
5
|
+
{ id: 1, src: 'https://via.placeholder.com/200', caption: 'Placeholder 1' },
|
|
6
|
+
{ id: 2, src: 'https://via.placeholder.com/200?text=2', caption: 'Placeholder 2' },
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
export default function FigureList() {
|
|
10
|
+
const [items, setItems] = useState(starter)
|
|
11
|
+
const [url, setUrl] = useState('')
|
|
12
|
+
const [caption, setCaption] = useState('')
|
|
13
|
+
|
|
14
|
+
const add = () => {
|
|
15
|
+
if (!url.trim()) return
|
|
16
|
+
setItems((s) => [...s, { id: Date.now(), src: url.trim(), caption: caption.trim() }])
|
|
17
|
+
setUrl('')
|
|
18
|
+
setCaption('')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const remove = (id) => setItems((s) => s.filter(i => i.id !== id))
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
<h2>Figures</h2>
|
|
26
|
+
<div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
|
|
27
|
+
<input placeholder="Image URL" value={url} onChange={(e)=>setUrl(e.target.value)} />
|
|
28
|
+
<input placeholder="Caption" value={caption} onChange={(e)=>setCaption(e.target.value)} />
|
|
29
|
+
<button onClick={add}>Add</button>
|
|
30
|
+
</div>
|
|
31
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12 }}>
|
|
32
|
+
{items.map(i => (
|
|
33
|
+
<BasicFigure key={i.id} src={i.src} caption={i.caption} onRemove={() => remove(i.id)} />
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
4
|
+
|
|
5
|
+
export default function Program6() {
|
|
6
|
+
const [name, setName] = useState('')
|
|
7
|
+
const [email, setEmail] = useState('')
|
|
8
|
+
const [password, setPassword] = useState('')
|
|
9
|
+
const [showPw, setShowPw] = useState(false)
|
|
10
|
+
const [errors, setErrors] = useState({})
|
|
11
|
+
|
|
12
|
+
const sanitize = (s) => s.replace(/[<>]/g, '')
|
|
13
|
+
|
|
14
|
+
const submit = (e) => {
|
|
15
|
+
e.preventDefault()
|
|
16
|
+
const errs = {}
|
|
17
|
+
if (!name.trim()) errs.name = 'Name required'
|
|
18
|
+
if (!emailRe.test(email)) errs.email = 'Invalid email'
|
|
19
|
+
if (password.length < 6) errs.password = 'Password too short'
|
|
20
|
+
setErrors(errs)
|
|
21
|
+
if (Object.keys(errs).length === 0) {
|
|
22
|
+
const data = { name: sanitize(name), email: sanitize(email), password: '***' }
|
|
23
|
+
alert('Submitted: ' + JSON.stringify(data))
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="program-page">
|
|
29
|
+
<h2>Program 6 — Form Validation</h2>
|
|
30
|
+
<form onSubmit={submit} noValidate>
|
|
31
|
+
<div>
|
|
32
|
+
<label>Name</label>
|
|
33
|
+
<input value={name} onChange={(e)=>setName(e.target.value)} style={{ borderColor: errors.name? 'red':undefined }} />
|
|
34
|
+
<div style={{ color: 'red' }}>{errors.name}</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div>
|
|
37
|
+
<label>Email</label>
|
|
38
|
+
<input value={email} onChange={(e)=>setEmail(e.target.value)} style={{ borderColor: errors.email? 'red':undefined }} />
|
|
39
|
+
<div style={{ color: 'red' }}>{errors.email}</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div>
|
|
42
|
+
<label>Password</label>
|
|
43
|
+
<input type={showPw? 'text':'password'} value={password} onChange={(e)=>setPassword(e.target.value)} style={{ borderColor: errors.password? 'red':undefined }} />
|
|
44
|
+
<div style={{ color: 'red' }}>{errors.password}</div>
|
|
45
|
+
</div>
|
|
46
|
+
<label><input type="checkbox" checked={showPw} onChange={(e)=>setShowPw(e.target.checked)} /> Show Password</label>
|
|
47
|
+
<div>
|
|
48
|
+
<button type="submit">Submit</button>
|
|
49
|
+
</div>
|
|
50
|
+
</form>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.profile-card {
|
|
2
|
+
border-radius: 12px;
|
|
3
|
+
padding: 16px;
|
|
4
|
+
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
|
5
|
+
max-width: 360px;
|
|
6
|
+
}
|
|
7
|
+
.profile-card img {
|
|
8
|
+
width: 96px;
|
|
9
|
+
height: 96px;
|
|
10
|
+
border-radius: 50%;
|
|
11
|
+
object-fit: cover;
|
|
12
|
+
}
|
|
13
|
+
.profile-card h3 { margin: 8px 0 4px }
|
|
14
|
+
.profile-card p { margin: 0; color: #444 }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import ProfileCard from './ProfileCard'
|
|
3
|
+
|
|
4
|
+
export default function Program7() {
|
|
5
|
+
const [bg, setBg] = useState('#ffffff')
|
|
6
|
+
return (
|
|
7
|
+
<div className="program-page">
|
|
8
|
+
<h2>Program 7 — Profile Card</h2>
|
|
9
|
+
<div className="controls">
|
|
10
|
+
<label>Card background: <input value={bg} onChange={(e)=>setBg(e.target.value)} /></label>
|
|
11
|
+
</div>
|
|
12
|
+
<div style={{ marginTop: 12 }}>
|
|
13
|
+
<ProfileCard
|
|
14
|
+
name="Jane Doe"
|
|
15
|
+
bio="Frontend engineer and React enthusiast."
|
|
16
|
+
img="https://via.placeholder.com/150"
|
|
17
|
+
bg={bg}
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function Program8() {
|
|
4
|
+
const [tasks, setTasks] = useState([])
|
|
5
|
+
const [name, setName] = useState('')
|
|
6
|
+
const [date, setDate] = useState('')
|
|
7
|
+
const [desc, setDesc] = useState('')
|
|
8
|
+
const [filter, setFilter] = useState('all')
|
|
9
|
+
|
|
10
|
+
const add = () => {
|
|
11
|
+
if (!name.trim()) return
|
|
12
|
+
setTasks(s => [...s, { id: Date.now(), name: name.trim(), date, desc, done: false }])
|
|
13
|
+
setName(''); setDate(''); setDesc('')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const toggle = (id) => setTasks(s => s.map(t => t.id===id? {...t, done: !t.done}: t))
|
|
17
|
+
const visible = tasks.filter(t => filter==='all' ? true : filter==='done' ? t.done : !t.done)
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="program-page">
|
|
21
|
+
<h2>Program 8 — Reminder App</h2>
|
|
22
|
+
<div>
|
|
23
|
+
<input placeholder="Task" value={name} onChange={(e)=>setName(e.target.value)} />
|
|
24
|
+
<input type="date" value={date} onChange={(e)=>setDate(e.target.value)} />
|
|
25
|
+
<input placeholder="Description (optional)" value={desc} onChange={(e)=>setDesc(e.target.value)} />
|
|
26
|
+
<button onClick={add}>Add</button>
|
|
27
|
+
</div>
|
|
28
|
+
<div style={{ marginTop: 8 }}>
|
|
29
|
+
<label><input type="radio" name="f" checked={filter==='all'} onChange={()=>setFilter('all')} /> All</label>
|
|
30
|
+
<label><input type="radio" name="f" checked={filter==='done'} onChange={()=>setFilter('done')} /> Completed</label>
|
|
31
|
+
<label><input type="radio" name="f" checked={filter==='pending'} onChange={()=>setFilter('pending')} /> Pending</label>
|
|
32
|
+
</div>
|
|
33
|
+
<ul style={{ marginTop: 12 }}>
|
|
34
|
+
{visible.map(t => (
|
|
35
|
+
<li key={t.id} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
36
|
+
<input type="checkbox" checked={t.done} onChange={()=>toggle(t.id)} />
|
|
37
|
+
<div>
|
|
38
|
+
<div style={{ fontWeight: 600 }}>{t.name} <small style={{ color: '#666' }}>{t.date}</small></div>
|
|
39
|
+
<div style={{ color: '#444' }}>{t.desc}</div>
|
|
40
|
+
</div>
|
|
41
|
+
</li>
|
|
42
|
+
))}
|
|
43
|
+
</ul>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Routes, Route, NavLink } from 'react-router-dom'
|
|
2
|
+
|
|
3
|
+
function Home() { return <div><h3>Home</h3><p>Welcome to the Home page.</p></div> }
|
|
4
|
+
function About() { return <div><h3>About</h3><p>About page content.</p></div> }
|
|
5
|
+
function Contact() { return <div><h3>Contact</h3><p>Contact details here.</p></div> }
|
|
6
|
+
|
|
7
|
+
export default function Program9() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="program-page">
|
|
10
|
+
<h2>Program 9 — Routing Example</h2>
|
|
11
|
+
<nav style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
|
|
12
|
+
<NavLink to="/program-9/" end>Home</NavLink>
|
|
13
|
+
<NavLink to="/program-9/about">About</NavLink>
|
|
14
|
+
<NavLink to="/program-9/contact">Contact</NavLink>
|
|
15
|
+
</nav>
|
|
16
|
+
<div style={{ background: '#fff' }}>
|
|
17
|
+
<Routes>
|
|
18
|
+
<Route path="/" element={<Home />} />
|
|
19
|
+
<Route path="about" element={<About />} />
|
|
20
|
+
<Route path="contact" element={<Contact />} />
|
|
21
|
+
</Routes>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|