import { useState } from 'react' import './App.css' // ───────────────────────────────────────────────────────────────────────────── // REUSABLE COMPONENTS — these are what students learn to write today // ───────────────────────────────────────────────────────────────────────────── // A simple Badge that receives text and color as props function Badge({ text, color }) { return ( <span className="badge" style={{ background: color }}> {text} </span> ) } // A team member card — receives all its data through props function MemberCard({ name, role, emoji, color }) { return ( <div className="member-card" style={{ borderTop: `3px solid ${color}` }}> <div className="member-avatar">{emoji}</div> <h3 className="member-name">{name}</h3> <Badge text={role} color={color} /> </div> ) } // A Card wrapper — demonstrates the children prop function Card({ title, children }) { return ( <div className="card"> {title && <p className="card-heading">{title}</p>} <div className="card-body">{children}</div> </div> ) } // A simple stat display component function Stat({ label, value }) { return ( <div className="stat"> <span className="stat-value">{value}</span> <span className="stat-label">{label}</span> </div> ) } // ───────────────────────────────────────────────────────────────────────────── // PAGE SECTIONS // ───────────────────────────────────────────────────────────────────────────── function Header() { return ( <header className="header"> <p className="header-badge">Lesson 38</p> <h1 className="header-title">Props & Component Tree</h1> <p className="header-sub"> How to pass data into components — and how to build a UI out of nested pieces </p> </header> ) } // Expandable concept cards function ConceptCards() { const [open, setOpen] = useState(null) const concepts = [ { id: 'a', title: 'Props', short: 'Props are arguments you pass into a component — like function parameters.', code: `function Greeting({ name }) { return <h2>Hello, {name}!</h2> } <Greeting name="Ana" /> // renders: Hello, Ana!`, }, { id: 'b', title: 'Destructuring', short: 'Use { } in the parameter list to extract props cleanly.', code: `// Without destructuring: function Card(props) { return <h2>{props.title}</h2> } // With destructuring (preferred): function Card({ title }) { return <h2>{title}</h2> }`, }, { id: 'c', title: 'children', short: 'A special prop — everything between the opening and closing tag.', code: `function Box({ children }) { return <div className="box">{children}</div> } <Box> <p>This text is the children prop.</p> <button>Click me</button> </Box>`, }, { id: 'd', title: 'Reuse', short: 'Write a component once. Render it many times with different props.', code: `// One component definition: function MemberCard({ name, role }) { return <div>{name} — {role}</div> } // Used 3 times with different data: <MemberCard name="Ana" role="Designer" /> <MemberCard name="Radu" role="Developer" /> <MemberCard name="Elena" role="Manager" />`, }, ] return ( <div className="concept-grid"> {concepts.map((c) => ( <div key={c.id} className={`concept-card ${open === c.id ? 'concept-card--open' : ''}`} onClick={() => setOpen(open === c.id ? null : c.id)} > <div className="concept-top"> <h3 className="concept-title">{c.title}</h3> <span className="concept-arrow">{open === c.id ? '▲' : '▼'}</span> </div> <p className="concept-short">{c.short}</p> {open === c.id && ( <pre className="concept-code"> <code>{c.code}</code> </pre> )} </div> ))} </div> ) } // ── Demo 1: Component Reuse ──────────────────────────────────────────────── const MEMBERS = [ { name: 'Ana', role: 'Designer', emoji: '🎨', color: '#e879f9' }, { name: 'Radu', role: 'Developer', emoji: '💻', color: '#6c63ff' }, { name: 'Elena', role: 'Manager', emoji: '📋', color: '#34d399' }, ] function ReuseDemo() { return ( <section className="demo-section"> <div className="demo-label"> <span className="demo-tag">Component Reuse</span> <p className="demo-desc"> The same <code><MemberCard></code> component rendered three times — each with different props. </p> </div> <div className="member-grid"> {MEMBERS.map((m) => ( <MemberCard key={m.name} name={m.name} role={m.role} emoji={m.emoji} color={m.color} /> ))} </div> <pre className="demo-code"> <code>{`<MemberCard name="Ana" role="Designer" emoji="🎨" color="#e879f9" /> <MemberCard name="Radu" role="Developer" emoji="💻" color="#6c63ff" /> <MemberCard name="Elena" role="Manager" emoji="📋" color="#34d399" />`}</code> </pre> </section> ) } // ── Demo 2: children Prop ───────────────────────────────────────────────── function ChildrenDemo() { return ( <section className="demo-section"> <div className="demo-label"> <span className="demo-tag">children Prop</span> <p className="demo-desc"> The <code><Card></code> wrapper renders whatever you put inside it — that content is{' '} <code>children</code>. </p> </div> <div className="children-grid"> <Card title="Team Stats"> <div className="stats-row"> <Stat label="Members" value="12" /> <Stat label="Projects" value="5" /> <Stat label="Lessons" value="38" /> </div> </Card> <Card title="What is children?"> <p className="card-text"> The <strong>children</strong> prop is special — React automatically fills it with everything written between the opening and closing tag of your component. </p> <p className="card-text" style={{ marginTop: 10 }}> This lets you build <strong>wrapper</strong> components that add styling or layout around any content. </p> </Card> </div> <pre className="demo-code"> <code>{`function Card({ title, children }) { return ( <div className="card"> <p>{title}</p> {children} {/* ← renders whatever is inside the tags */} </div> ) } <Card title="Team Stats"> <Stat label="Members" value="12" /> {/* ← this becomes children */} <Stat label="Projects" value="5" /> </Card>`}</code> </pre> </section> ) } // ── Demo 3: Component Tree ──────────────────────────────────────────────── function TreeDemo() { const tree = [ { depth: 0, tag: 'App', color: '#f472b6' }, { depth: 1, tag: 'Header', color: '#6c63ff', props: null }, { depth: 1, tag: 'main', color: '#6c63ff', props: null }, { depth: 2, tag: 'ConceptCards', color: '#34d399', props: null }, { depth: 2, tag: 'ReuseDemo', color: '#34d399', props: null }, { depth: 3, tag: 'MemberCard', color: '#fb923c', props: 'name="Ana" role="Designer"' }, { depth: 3, tag: 'MemberCard', color: '#fb923c', props: 'name="Radu" role="Developer"' }, { depth: 3, tag: 'MemberCard', color: '#fb923c', props: 'name="Elena" role="Manager"' }, { depth: 2, tag: 'ChildrenDemo', color: '#34d399', props: null }, { depth: 3, tag: 'Card', color: '#fb923c', props: 'title="Team Stats"' }, { depth: 4, tag: 'Stat', color: '#38bdf8', props: 'label="Members" value="12"' }, { depth: 4, tag: 'Stat', color: '#38bdf8', props: 'label="Projects" value="5"' }, { depth: 2, tag: 'TreeDemo', color: '#34d399', props: null }, { depth: 2, tag: 'Playground', color: '#34d399', props: null }, { depth: 1, tag: 'Footer', color: '#6c63ff', props: null }, ] return ( <section className="demo-section"> <div className="demo-label"> <span className="demo-tag">Component Tree</span> <p className="demo-desc"> Every React app is a tree of components. Props always flow <strong>down</strong> — from parent to child, never the other way. </p> </div> <div className="tree"> {tree.map((node, i) => ( <div key={i} className="tree-row" style={{ paddingLeft: node.depth * 22 }}> <span className="tree-connector">{node.depth > 0 ? '└ ' : ''}</span> <span className="tree-bracket"><</span> <span className="tree-tag-name" style={{ color: node.color }}> {node.tag} </span> {node.props && <span className="tree-props"> {node.props}</span>} <span className="tree-bracket"> /></span> </div> ))} </div> <div className="tree-legend"> {[ { color: '#f472b6', label: 'Root (App)' }, { color: '#6c63ff', label: 'Page sections' }, { color: '#34d399', label: 'Feature components' }, { color: '#fb923c', label: 'Reusable components' }, { color: '#38bdf8', label: 'Leaf components' }, ].map((item) => ( <div key={item.label} className="legend-item"> <span className="legend-dot" style={{ background: item.color }} /> <span>{item.label}</span> </div> ))} </div> </section> ) } // ── Live Playground ─────────────────────────────────────────────────────── const ROLES = ['Designer', 'Developer', 'Manager', 'Tester', 'Mentor', 'Founder'] const EMOJIS = ['🎨', '💻', '📋', '🧪', '🎓', '🚀', '⚡', '🌟'] const COLORS = ['#6c63ff', '#e879f9', '#34d399', '#fb923c', '#38bdf8', '#f43f5e'] function Playground() { const [name, setName] = useState('Your Name') const [role, setRole] = useState('Developer') const [emoji, setEmoji] = useState('🚀') const [color, setColor] = useState('#6c63ff') const displayName = name.trim() || 'Your Name' return ( <section className="playground"> <div className="demo-label"> <span className="demo-tag">Live Playground</span> <p className="demo-desc"> Edit the props below and watch the <code><MemberCard></code> update instantly. </p> </div> <div className="pg-body"> {/* Controls — left column */} <div className="pg-controls"> <div className="pg-field"> <label>name</label> <input type="text" value={name} maxLength={20} placeholder="Enter a name..." onChange={(e) => setName(e.target.value)} className="pg-input" /> </div> <div className="pg-field"> <label>role</label> <select value={role} onChange={(e) => setRole(e.target.value)} className="pg-select" > {ROLES.map((r) => <option key={r}>{r}</option>)} </select> </div> <div className="pg-field"> <label>emoji</label> <div className="emoji-grid"> {EMOJIS.map((e) => ( <button key={e} className={`emoji-btn ${emoji === e ? 'emoji-btn--active' : ''}`} onClick={() => setEmoji(e)} > {e} </button> ))} </div> </div> <div className="pg-field"> <label>color</label> <div className="color-grid"> {COLORS.map((c) => ( <button key={c} className={`color-btn ${color === c ? 'color-btn--active' : ''}`} style={{ background: c, outline: color === c ? `2px solid ${c}` : 'none', outlineOffset: 3, }} onClick={() => setColor(c)} /> ))} </div> </div> </div> {/* Preview — right column */} <div className="pg-preview"> <p className="preview-hint">← Component output</p> <MemberCard name={displayName} role={role} emoji={emoji} color={color} /> <pre className="pg-code"> <code>{`<MemberCard name="${displayName}" role="${role}" emoji="${emoji}" color="${color}" />`}</code> </pre> </div> </div> </section> ) } function Footer() { return ( <footer className="footer"> <p>Impact Academy · Year 2 · Phase 5</p> </footer> ) } // ───────────────────────────────────────────────────────────────────────────── // APP — the root component; composes everything together // ───────────────────────────────────────────────────────────────────────────── function App() { return ( <div className="app"> <Header /> <main className="main"> <ConceptCards /> <ReuseDemo /> <ChildrenDemo /> <TreeDemo /> <Playground /> </main> <Footer /> </div> ) } export default App
Note.ms
/code