Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/meta/play-meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MovieContainer,
WhyReact,
CounterApp,
SocialCard,
//import play here
} from "plays";

Expand Down Expand Up @@ -75,6 +76,18 @@ export const plays = [
tags: 'JSX, State, Props',
github: 'murtuzaalisurti',
featured: true
}, {
id: 'pl-social-card',
name: 'Social Card',
description: 'The Social Card helps you telling who you are using photo, name, and other social footprints.',
component: () => {return <SocialCard />},
path: '/plays/social-card',
level: 'Intermediate',
tags: 'Form,Events,Complex State',
github: 'atapas',
cover: '',
blog: 'https://blog.greenroots.info/how-to-create-react-form-with-a-single-change-event-handler',
video: ''
}, //replace new play item here
];

Expand Down
1 change: 1 addition & 0 deletions src/plays/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { default as BasicTree } from 'plays/org-tree/BasicTree';
export { default as MovieContainer } from 'plays/movies/MovieContainer';
export { default as WhyReact } from 'plays/why-react/WhyReact';
export { default as CounterApp } from 'plays/counter/CounterApp';
export { default as SocialCard } from 'plays/social-card/SocialCard';
//add export here
157 changes: 157 additions & 0 deletions src/plays/social-card/CardDetails.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { useState, useContext, useEffect } from "react";
import { SocialContext } from "./context/SocialContext";
import { BsGithub, BsTwitter, BsLinkedin } from "react-icons/bs";
import { CgWebsite } from "react-icons/cg";
import { AiOutlineMail } from "react-icons/ai";

const CardDetails = () => {
// Get the user-filled values from the context
const { social } = useContext(SocialContext);

// Destructure the values from the object.
const { name, email, photo, bio, website, twitter, linkedIn, github } =
social;

// We need a file reader to handle the photo uploaded by the user.
// We have to perform some computations and extract the actual picture
// from the file. So created a state variable to take care of this.
const [picture, setPicture] = useState();

// We have an option to set the theme of the social card.
// This state variable is the theme object.
const [cardTheme, setCardTheme] = useState({
bc: "#f2d6d6",
fc: "#212121",
link: "#000000",
});

// Perform all calculations to get the profile photo
// from the file reader.
useEffect(() => {
if (photo) {
const reader = new FileReader();
reader.readAsDataURL(photo[0]);
reader.onload = () => {
setPicture(reader.result);
};
}
}, [photo]);

// This method will be called when the user changes the theme
const applyTheme = theme => {
let bc = '';
let fc = '';
let link = '';
switch (theme) {
case "dark":
bc = '#212121';
fc = '#f2d6d6';
link = '#ffffff';
break;
case "light":
bc = '#f2d6d6';
fc = '#212121';
link = '#000000';
break;
case "red":
bc = '#f44336';
fc = '#ffffff';
link = '#ffffff';
break;
case "green":
bc = '#4caf50';
fc = '#ffffff';
link = '#ffffff';
break;
case "blue":
bc = '#2196f3';
fc = '#ffffff';
link = '#ffffff';
break;
case "yellow":
bc = '#ffeb3b';
fc = '#000000';
link = '#000000';
break;
default:
break;
}
setCardTheme({
...cardTheme,
'bc': bc,
'fc': fc,
'link': link
});
}

return (
<div className="card-details">
<div className="card-details-theme">
<button className="light" onClick={ (theme) => applyTheme("light") }></button>
<button className="dark" onClick={ (theme) => applyTheme("dark")}></button>
<button className="red" onClick={ (theme) => applyTheme("red")}></button>
<button className="blue" onClick={ (theme) => applyTheme("blue")}></button>
<button className="yellow" onClick={ (theme) => applyTheme("yellow")}></button>
<button className="green" onClick={ (theme) => applyTheme("green")}></button>
</div>

<div className="card" style={{ backgroundColor: cardTheme.bc, color: cardTheme.fc}}>

<div className="card-header">
{picture && (
<div className="card-header-image">
<img src={picture} alt="profile" />
</div>
)}
<div className="card-header-name">
<h3>{name}</h3>
</div>
</div>
<div className="card-body">
<div className="card-body-bio">
<p>{bio}</p>
</div>
<div className="card-body-social">
{twitter && (
<div className="card-body-social-item">
<a href={twitter} style={{color: cardTheme.link}} target="_blank" rel="noopener noreferrer">
<BsTwitter size="24px"/>
</a>
</div>
)}
{linkedIn && (
<div className="card-body-social-item">
<a href={linkedIn} style={{color: cardTheme.link}} target="_blank" rel="noopener noreferrer">
<BsLinkedin size="24px" />
</a>
</div>
)}
{github && (
<div className="card-body-social-item">
<a href={github} style={{color: cardTheme.link}} target="_blank" rel="noopener noreferrer">
<BsGithub size="24px" />
</a>
</div>
)}
{website && (
<div className="card-body-social-item">
<a href={website} style={{color: cardTheme.link}} target="_blank" rel="noopener noreferrer">
<CgWebsite size="24px" />
</a>
</div>
)}
{email && (
<div className="card-body-social-item">
<a href={`mailto:${email}`} style={{color: cardTheme.link}}>
<AiOutlineMail size="24px" />
</a>
</div>
)}
</div>
</div>
</div>
</div>
);
};

export default CardDetails;
152 changes: 152 additions & 0 deletions src/plays/social-card/CardForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { useState, useContext, useEffect, useRef } from "react";
import { SocialContext } from "./context/SocialContext";

const CardForm = () => {
// Declaring the state value with an object that contains
// all the information of the social card.
const [state, setState] = useState({
name: "",
email: "",
photo: "",
bio: "",
website: "",
twitter: "",
linkedIn: "",
github: "",
});

// We get the updater function from the context.
// It will help us to set the user filled information as the context data
// so that, other component can access it.
const { setSocial } = useContext(SocialContext);

// We create a reference to focus on the
// first input field of the form.
const nameInputRef = useRef(null);

// This method will be called for the value
// change of each of the form fields. Please
// note how we handle the file input differently.
const handleChange = (evt) => {
const name = evt.target.name;
const value =
evt.target.type === "file" ? evt.target.files : evt.target.value;
setState({
...state,
[name]: value,
});
};

// We set the focus of the name input field.
useEffect(() => {
nameInputRef.current.focus();
}, []);

// Whenever the state value changes, we set the value
// of the context data.
useEffect(() => {
setSocial(state);
}, [state, setSocial]);

return (
<form className="social-card-form">
<div className="form-group">
<label htmlFor="name"></label>
<input
ref = {nameInputRef}
type="text"
placeholder="Enter Name"
className="form-control"
id="name"
name="name"
value={state.name}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="photo"></label>
<input
type="file"
className="form-control"
id="photo"
name="photo"
accept=".jpg, .jpeg, .png"
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="bio"></label>
<textarea
placeholder="Enter Bio"
className="form-control"
id="bio"
name="bio"
value={state.bio}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="twitter"></label>
<input
type="text"
placeholder="Enter Twitter URL"
className="form-control"
id="twitter"
name="twitter"
value={state.twitter}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="linkedIn"></label>
<input
type="text"
placeholder="Enter LinkedIn URL"
className="form-control"
id="linkedIn"
name="linkedIn"
value={state.linkedIn}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="github"></label>
<input
type="text"
placeholder="Enter GitHub URL"
className="form-control"
id="github"
name="github"
value={state.github}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="website"></label>
<input
type="text"
placeholder="Enter Website URL"
className="form-control"
id="website"
name="website"
value={state.website}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="email"></label>
<input
type="email"
placeholder="Enter Email"
className="form-control"
id="email"
name="email"
value={state.email}
onChange={handleChange}
/>
</div>
</form>
);
};

export default CardForm;
16 changes: 16 additions & 0 deletions src/plays/social-card/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Social Card
The `Social Card` project demonstrate many key concepts of ReactJS.

- Form Handling.
- Managing Complex state data like object and how to update it with the single handler.
- Usage of React Context with the `useContext` hook.
- Usage of adding focus to the input element with the `useRef` hook.

If you are planning to look into the code in details, please start with the `SocialCard.jsx` file. You will find that we are initializing the context there and wrapping the form and details component with it.

Please check the `CardForm.jsx` file to understand how form handling works with the object
as a the state value. We have used a single handler to handle every change in the form.

The `CardDetails.jsx` file is the one that is responsible for the rendering of the details. We also use the theming option there.

Happy Learning!
Loading