Before we start i am going to put my social media links if you would like to follow me there
Instagram
Twitter
and also my upwork profile if you want to work with me
In this blog post i am going to demonstrate how to create an asset picker on button click.
Below is how the the ui of the picker looks like and how it works
First of all we are going to create our app with next js a react framework
npx create-next-app assetpicker
make sure all the characters are in small letters because next js does not allow capital letters
and during the creation of the app select yes for typescript on the terminal pop up because we are going to use typescript in this app
After app creation we are going to install 3 npm packages which are react-icons, classnames and sass
npm i react-icons classnames sass
After installation go to pages and delete index.tsx content but not the file itself and we are going to start from scratch
we are going to have 4 items in our setState which are image, selecta, current and names
const [image, setImage] = useState<any>([])
const [selecta, setSelecta] = useState<any>([])
const [current, setCurrent] = useState(0)
const [names, setNames] = useState<any>([])
image this is the actual image **which is going to be displayed inside a div known as **selecta and current is the current selected image which by default is the first one and names contains an array of names of all selected images this is going to help us to check if the image is already selected or not
Apart from those we are going to have 3 functions addSelecta, onChange and deleteData
const addSelecta = () => {
let image1 = selecta.length
if(selecta.length > image.length){
alert('not allowed')
return
}else if( selecta.length >= 5){
alert('maximum images 5 reached')
return
}else{
selecta.push(`image${image1+1}`)
setSelecta(selecta.map((item:any)=> item))
}
}
in this function we are adding a button to select image and that div is going to display a plus icon if there is no image selected and and image otherwise.
Every Time we click a button we are going to add an item named image plus selecta.length icreament so selecta item is going to be image0, image1 etc in an array
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const one:any = e.target.files
const two = URL.createObjectURL(one[0])
if(names.includes(one[0].name)){
alert('asset available')
return
}else{
image.push(two)
names.push(one[0].name)
setImage(image.map((item:any)=> item))
setNames(names.map((item:any)=> item))
}
}
in in onchange function this is all about selecting assets but there is more to that.
we are going to have a constant value named one **which is simply an array assignment of assets.
and the second constant value is **two which is simply a url creation for selected files number one.
we are using URL.createObjectURL(one[0]) because we cannot display files as files files on the web unless they are BLOB so this method is simply a creation of a BLOB url
const deleteData = (image1:any, selecta1:any) => {
const imageIndex = image.indexOf(image1)
const selectaIndex = selecta.indexOf(selecta1)
if(selecta.includes(selecta1)){
selecta.splice(selectaIndex,1)
image.splice(imageIndex,1)
names.splice(image1.name,1)
setSelecta(selecta.map((item:any)=> item))
setImage(image.map((item:any)=> item))
setNames(names.map((item:any)=> item))
}
}
The third function deleteData we are going to pass 2 items along with it the first one is *image **which is the image index of that selected image plus the selecta index of the selecta array.
Then we are going to get indexes of those two **imageIndex **and **selectaIndex *
and then we are going to check if the selecta array includes the selected selecta if true we are going to delete the selecta, image and name and after that we are going to set them with new array so as to update the ui because if we do not do that the changes will not be reflected on the ui
return (
<div className={styles.home}>
<div className={styles.item}>
<div className={styles.hero}>
<div className={styles.thumbs}>
{
image.map((item:any, index:number)=> (
<div className={cx(styles.dot, current===index && styles.active)} onClick={()=> setCurrent(index)}></div>
))
}
</div>
{
image.length < 1
? null
: <img src={image[current]} alt="" />
}
</div>
<div className={styles.action}>
<div className={styles.left}>
{
selecta.map((item:any, index:number)=> (
<div className={styles.item}>
{
index+1 > image.length
?<div className={styles.picker}>
<input type="file"
id='file'
style={{display:'none'}}
onChange={e => onChange(e)}
/>
<label htmlFor="file">
<BiPlus className={styles.icon} size={25}/>
</label>
</div>
: <img src={image[index]} alt="" onClick={()=> deleteData(image[index], item)}/>
}
</div>
))
}
</div>
<div className={styles.right}>
<div className={styles.add} onClick={() => addSelecta()}>
<BiPlus className={styles.icon}/>
</div>
</div>
</div>
</div>
</div>
)
After all that we need to write our code we are going to have home as our main div and inside it there is item and inside item we are having hero section which is the big image.
<div className={styles.hero}>
<div className={styles.thumbs}>
{
image.map((item:any, index:number)=> (
<div className={cx(styles.dot, current===index && styles.active)} onClick={()=> setCurrent(index)}></div>
))
}
</div>
{
image.length < 1
? null
: <img src={image[current]} alt="" />
}
</div>
Inside the hero section we have thumbs which are the small dots indicating the selected image and unselected ones.
and then we have the action div with left and right divs the left is an array of selectas and the right one is the button to add selectas.
inside the selecta we are going to check if selecta has an image or not if not we are going to show a button to select the image
here is the full code
Index.tsx
import React, { useState } from 'react'
import styles from './home.module.scss'
import { BiPlus } from 'react-icons/bi'
import cx from 'classnames'
function Home() {
const [image, setImage] = useState<any>([])
const [selecta, setSelecta] = useState<any>([])
const [current, setCurrent] = useState(0)
const [names, setNames] = useState<any>([])
const addSelecta = () => {
let image1 = selecta.length
if(selecta.length > image.length){
alert('not allowed')
return
}else if( selecta.length >= 5){
alert('maximum images 5 reached')
return
}else{
selecta.push(`image${image1+1}`)
setSelecta(selecta.map((item:any)=> item))
}
}
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const one:any = e.target.files
const two = URL.createObjectURL(one[0])
if(names.includes(one[0].name)){
alert('asset available')
return
}else{
image.push(two)
names.push(one[0].name)
setImage(image.map((item:any)=> item))
setNames(names.map((item:any)=> item))
}
}
const deleteData = (image1:any, selecta1:any) => {
const imageIndex = image.indexOf(image1)
const selectaIndex = selecta.indexOf(selecta1)
if(selecta.includes(selecta1)){
selecta.splice(selectaIndex,1)
image.splice(imageIndex,1)
names.splice(image1.name,1)
setSelecta(selecta.map((item:any)=> item))
setImage(image.map((item:any)=> item))
setNames(names.map((item:any)=> item))
}
}
return (
<div className={styles.home}>
<div className={styles.item}>
<div className={styles.hero}>
<div className={styles.thumbs}>
{
image.map((item:any, index:number)=> (
<div className={cx(styles.dot, current===index && styles.active)} onClick={()=> setCurrent(index)}></div>
))
}
</div>
{
image.length < 1
? null
: <img src={image[current]} alt="" />
}
</div>
<div className={styles.action}>
<div className={styles.left}>
{
selecta.map((item:any, index:number)=> (
<div className={styles.item}>
{
index+1 > image.length
?<div className={styles.picker}>
<input type="file"
id='file'
style={{display:'none'}}
onChange={e => onChange(e)}
/>
<label htmlFor="file">
<BiPlus className={styles.icon} size={25}/>
</label>
</div>
: <img src={image[index]} alt="" onClick={()=> deleteData(image[index], item)}/>
}
</div>
))
}
</div>
<div className={styles.right}>
<div className={styles.add} onClick={() => addSelecta()}>
<BiPlus className={styles.icon}/>
</div>
</div>
</div>
</div>
</div>
)
}
export default Home
home.module.scss
.home{
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
.item{
width: 50%;
.hero{
width: 100%;
height: 400px;
background: black;
display: flex;
align-items: center;
justify-content: center;
position: relative;
img{
width: 90%;
height: 90%;
object-fit: cover;
}
.thumbs{
position: absolute;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
.dot{
width: 10px;
height: 10px;
margin: 5px;
background: white;
cursor: pointer;
&.active{
background: crimson;
}
}
}
}
.action{
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 0;
.left{
width: 90%;
display: flex;
align-items: center;
justify-content: flex-start;
.item{
width: 100px;
height: 100px;
background: crimson;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
img{
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
.right{
width: 10%;
.add{
background: crimson;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
cursor: pointer;
.icon{
color: white;
}
}
}
}
}
}
Top comments (0)