Added Dark/Light mode feature.

This commit is contained in:
Daniel 2022-06-07 16:30:16 +04:30
parent 91d72e0c0f
commit a2b1d2109f
12 changed files with 237 additions and 110 deletions

View File

@ -22,7 +22,8 @@ function App() {
[descriptionChecked, setDescriptionChecked] = useState(true), [descriptionChecked, setDescriptionChecked] = useState(true),
[tagsChecked, setTagsChecked] = useState(true), [tagsChecked, setTagsChecked] = useState(true),
[sortBy, setSortBy] = useState('Default'), [sortBy, setSortBy] = useState('Default'),
[loader, setLoader] = useState(false); [loader, setLoader] = useState(false),
[lightMode, setLightMode] = useState(false);
function SetLoader(x) { function SetLoader(x) {
setLoader(x) setLoader(x)
@ -57,7 +58,7 @@ function App() {
} }
function sortByFunc(e) { function sortByFunc(e) {
setSortBy(e) setSortBy(e);
} }
const filteredData = filter(data, searchQuery, nameChecked, tagsChecked, descriptionChecked); const filteredData = filter(data, searchQuery, nameChecked, tagsChecked, descriptionChecked);
@ -81,19 +82,28 @@ function App() {
setNumberOfResults(filteredData.length); setNumberOfResults(filteredData.length);
}, [filteredData]); }, [filteredData]);
useEffect(() => {
if (lightMode) {
document.body.classList.add("light");
} else {
document.body.classList.remove("light");
}
}, [lightMode]);
return ( return (
<div className="App"> <div className="App">
<div className='content'> <div className='content'>
<div className="head"> <div className="head">
<input className="search" type="search" placeholder="&#xf002; Search" onChange={search}/> <input className="search" type="search" placeholder="&#xf002; Search" onChange={search}/>
<button className="add-btn btn" onClick={() => setNewBox(true)}>&#xf067;</button> <button className="add-btn btn" onClick={() => setNewBox(true)}>&#xf067;</button>
<button className="dark-light-btn btn" onClick={() => setLightMode(!lightMode)}>&#xf042;</button>
</div> </div>
<p className="results">{numberOfResults > 0 ? numberOfResults + ' Bookmarks found' : null}</p> <p className="results">{numberOfResults > 0 ? numberOfResults + ' Bookmarks found' : null}</p>
<button className='btn' onClick={() => setFilterBox(true)}>&#xf0b0;</button> <button className='btn' style={{marginTop: '10px'}} onClick={() => setFilterBox(true)}>&#xf0b0;</button>
<button className='btn' style={{marginLeft: '10px'}} onClick={() => setSortBox(true)}>&#xf0dc;</button> <button className='btn' style={{marginLeft: '10px'}} onClick={() => setSortBox(true)}>&#xf0dc;</button>
<List SetLoader={SetLoader} data={filteredData} tags={tags} reFetch={fetchData} /> <List lightMode={lightMode} SetLoader={SetLoader} data={filteredData} tags={tags} reFetch={fetchData} />
{numberOfResults === 0 ? <NoResults /> : null} {numberOfResults === 0 ? <NoResults /> : null}
@ -116,10 +126,11 @@ function App() {
SetLoader={SetLoader} SetLoader={SetLoader}
onExit={exitAdding} onExit={exitAdding}
reFetch={fetchData} reFetch={fetchData}
lightMode={lightMode}
tags={() => tags} tags={() => tags}
/> : null} /> : null}
{loader ? <Loader /> : null} {loader ? <Loader lightMode={lightMode} /> : null}
</div> </div>
</div> </div>
); );

View File

@ -3,7 +3,7 @@ import '../styles/SendItem.css';
import TagSelection from './TagSelection'; import TagSelection from './TagSelection';
import addItem from '../modules/send'; import addItem from '../modules/send';
const AddItem = ({onExit, reFetch, tags, SetLoader}) => { const AddItem = ({onExit, reFetch, tags, SetLoader, lightMode}) => {
const [name, setName] = useState(''); const [name, setName] = useState('');
const [link, setLink] = useState(''); const [link, setLink] = useState('');
const [tag, setTag] = useState([]); const [tag, setTag] = useState([]);
@ -43,7 +43,7 @@ const AddItem = ({onExit, reFetch, tags, SetLoader}) => {
<h3>Name: <span className='optional'>(Optional)</span></h3> <h3>Name: <span className='optional'>(Optional)</span></h3>
<input onChange={SetName} className="AddItem-input" type="search" placeholder="e.g. Example Tutorial"/> <input onChange={SetName} className="AddItem-input" type="search" placeholder="e.g. Example Tutorial"/>
<h3>Tags: <span className='optional'>(Optional)</span></h3> <h3>Tags: <span className='optional'>(Optional)</span></h3>
<TagSelection setTags={SetTags} tags={tags} /> <TagSelection setTags={SetTags} tags={tags} lightMode={lightMode} />
<button onClick={newItem} className="send-btn">Add &#xf067;</button> <button onClick={newItem} className="send-btn">Add &#xf067;</button>
</div> </div>
</fieldset> </fieldset>

View File

@ -4,7 +4,7 @@ import '../styles/SendItem.css';
import TagSelection from './TagSelection'; import TagSelection from './TagSelection';
import editItem from '../modules/send'; import editItem from '../modules/send';
const EditItem = ({tags, item, onExit, SetLoader, reFetch}) => { const EditItem = ({tags, item, onExit, SetLoader, reFetch, lightMode }) => {
const [name, setName] = useState(item.name); const [name, setName] = useState(item.name);
const [tag, setTag] = useState(item.tag); const [tag, setTag] = useState(item.tag);
@ -40,7 +40,7 @@ const EditItem = ({tags, item, onExit, SetLoader, reFetch}) => {
<div className='add-overlay' onClick={abort}></div> <div className='add-overlay' onClick={abort}></div>
<fieldset className='box'> <fieldset className='box'>
<legend >Edit bookmark</legend> <legend >Edit bookmark</legend>
<button className="edit-btn delete" onClick={deleteItem}>&#xf2ed;</button> <button className="delete" onClick={deleteItem}>&#xf2ed;</button>
<div className='AddItem-content'> <div className='AddItem-content'>
<h3>Link: <a target="_blank" rel="noreferrer" href={item.link}>{url.hostname}</a></h3> <h3>Link: <a target="_blank" rel="noreferrer" href={item.link}>{url.hostname}</a></h3>
<h3 className='title'><b>{item.title}</b></h3> <h3 className='title'><b>{item.title}</b></h3>
@ -48,7 +48,7 @@ const EditItem = ({tags, item, onExit, SetLoader, reFetch}) => {
<h3>Name: <span className='optional'>(Optional)</span></h3> <h3>Name: <span className='optional'>(Optional)</span></h3>
<input onChange={SetName} className="AddItem-input" type="search" value={name} placeholder={"e.g. Example Tutorial"} /> <input onChange={SetName} className="AddItem-input" type="search" value={name} placeholder={"e.g. Example Tutorial"} />
<h3>Tags: <span className='optional'>(Optional)</span></h3> <h3>Tags: <span className='optional'>(Optional)</span></h3>
<TagSelection setTags={SetTags} tags={tags} tag={tag} /> <TagSelection setTags={SetTags} tags={tags} tag={tag} lightMode={lightMode} />
<button onClick={EditItem} className="send-btn">Update &#xf303;</button> <button onClick={EditItem} className="send-btn">Update &#xf303;</button>
</div> </div>
</fieldset> </fieldset>

View File

@ -4,7 +4,7 @@ import ViewArchived from './ViewArchived';
import EditItem from './EditItem'; import EditItem from './EditItem';
import { useState } from 'react' import { useState } from 'react'
const List = ({data, tags, reFetch, SetLoader}) => { const List = ({data, tags, reFetch, SetLoader, lightMode}) => {
const [editBox, setEditBox] = useState(false) const [editBox, setEditBox] = useState(false)
const [editIndex, setEditIndex] = useState(0) const [editIndex, setEditIndex] = useState(0)
@ -42,7 +42,7 @@ const List = ({data, tags, reFetch, SetLoader}) => {
</div> </div>
<div className='etc'> <div className='etc'>
<ViewArchived className='view-archived' id={e._id} /> <ViewArchived className='view-archived' id={e._id} />
<button className="edit-btn" onClick={() => edit(i)}>&#xf303;</button> <button className="edit-btn btn" onClick={() => edit(i)}>&#xf303;</button>
</div> </div>
</div> </div>
</LazyLoad>) </LazyLoad>)
@ -50,7 +50,7 @@ const List = ({data, tags, reFetch, SetLoader}) => {
console.log(e); console.log(e);
} }
})} })}
{editBox ? <EditItem tags={() => tags} onExit={exitEditing} SetLoader={SetLoader} reFetch={reFetch} item={data[editIndex]} /> : null} {editBox ? <EditItem lightMode={lightMode} tags={() => tags} onExit={exitEditing} SetLoader={SetLoader} reFetch={reFetch} item={data[editIndex]} /> : null}
</div> </div>
) )
} }

View File

@ -2,10 +2,10 @@ import '../styles/Loader.css';
import { InfinitySpin } from 'react-loader-spinner' import { InfinitySpin } from 'react-loader-spinner'
const Loader = () => { const Loader = ({ lightMode }) => {
return ( return (
<div className='loader'> <div className='loader'>
<InfinitySpin color="white" /> <InfinitySpin color={lightMode ? "Black" : "White"} />
</div> </div>
) )
} }

View File

@ -1,6 +1,8 @@
import CreatableSelect from "react-select/creatable"; import CreatableSelect from "react-select/creatable";
const customStyles = { // lightMode ? "Black" : "White"
export default function TagSelection({setTags, tags, tag=[], lightMode}) {
const customStyles = {
placeholder: (provided) => ({ placeholder: (provided) => ({
...provided, ...provided,
color: '#a9a9a9', color: '#a9a9a9',
@ -24,25 +26,24 @@ const customStyles = {
borderColor: 'rgb(141, 141, 141)', borderColor: 'rgb(141, 141, 141)',
opacity: '90%', opacity: '90%',
color: 'gray', color: 'gray',
background: '#273949', background: lightMode ? "lightyellow" : "#273949",
boxShadow: 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px', boxShadow: 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px',
}), }),
input: (provided) => ({ input: (provided) => ({
...provided, ...provided,
color: 'white', color: lightMode ? "rgb(64, 64, 64)" : "white",
}), }),
control: (provided, state) => ({ control: (provided, state) => ({
...provided, ...provided,
background: '#273949', background: lightMode ? "lightyellow" : "#273949",
border: 'none', border: 'none',
borderRadius: '0px', borderRadius: '0px',
boxShadow: state.isFocused ? 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px' : 'rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset', boxShadow: state.isFocused ? 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px' : 'rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset',
}), }),
} }
export default function TagSelection({setTags, tags, tag=[]}) {
const data = tags().map((e) => { const data = tags().map((e) => {
return { value: e, label: e } return { value: e, label: e }
}) })

View File

@ -1,7 +1,5 @@
.App { .App {
min-height: 100vh; min-height: 100vh;
background-color: #1f2c38;
color: white;
display: flex; display: flex;
} }
@ -25,8 +23,6 @@
border: none; border: none;
width: 35%; width: 35%;
min-width: 300px; min-width: 300px;
color: white;
background-color:#273949;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
} }
@ -44,24 +40,17 @@
font-size: 1.1rem; font-size: 1.1rem;
cursor: pointer; cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
color: #ffffffb6;
background-color:#273949; background-color:#273949;
border: none; border: none;
transition: background-color 0.1s; transition: background-color 0.1s;
} }
.btn:active { box-shadow: 0px 0px 10px rgb(83, 143, 255); }
.add-btn { .add-btn {
margin-left: auto; margin-left: auto;
} }
.btn:hover {
background-color: rgb(76, 117, 170);
}
.btn:active {
box-shadow: 0px 0px 10px rgb(83, 143, 255);
}
textarea:focus, input:focus{ textarea:focus, input:focus{
outline: none; outline: none;
} }
@ -75,7 +64,11 @@ textarea:focus, input:focus{
text-align: center; text-align: center;
padding-top: 5%; padding-top: 5%;
padding-bottom: 5%; padding-bottom: 5%;
background-color:#273949;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
margin: 20px; margin-top: 10px;
}
.dark-light-btn {
margin-left: 10px;
font-size: 1.3em;
} }

View File

@ -18,7 +18,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px; box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
background-color: #1f2c38;
padding: 5px; padding: 5px;
top: 15%; top: 15%;
left: 30%; left: 30%;
@ -42,8 +41,6 @@
font-size: 1.1rem; font-size: 1.1rem;
cursor: pointer; cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
color: #ffffffb6;
background-color:#273949;
border: none; border: none;
} }

View File

@ -28,7 +28,6 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
background-color:#273949;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
} }
@ -50,7 +49,6 @@
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free';
pointer-events: all; pointer-events: all;
text-decoration: none; text-decoration: none;
color: rgb(194, 193, 193);
font-size: 1rem; font-size: 1rem;
} }
@ -66,20 +64,10 @@
.edit-btn { .edit-btn {
position: relative;
border-radius: 100%;
margin: 20px 20px 20px 0px; margin: 20px 20px 20px 0px;
font-family: 'Font Awesome 5 Free';
width: 50px; width: 50px;
height: 50px; height: 50px;
padding: 10px; font-size: 1.5rem;
font-size: 1.3rem;
cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
color: #ffffffb6;
background-color: #1f2c38;
border: none;
transition: background-color 0.1s;
} }
.edit-btn:hover { .edit-btn:hover {
@ -106,7 +94,6 @@
.tags div { .tags div {
margin-right: 10px; margin-right: 10px;
color: rgb(126, 208, 255);
} }
.tags div::before { .tags div::before {
@ -125,15 +112,29 @@
} }
.delete { .delete {
margin: 20px 20px 20px 0px;
background-color:#273949; background-color:#273949;
float: right; float: right;
font-size: 1.1rem; font-size: 1.1rem;
width: 40px; width: 40px;
height: 40px; height: 40px;
position: relative;
border-radius: 100%;
font-family: 'Font Awesome 5 Free';
width: 40px;
height: 40px;
padding: 10px;
font-size: 1.1rem;
cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
background-color:#273949;
border: none;
transition: background-color 0.1s;
} }
.delete:hover { .delete:hover {
background-color: rgba(255, 65, 65, 0.8); background-color: rgba(255, 75, 75, 0.8);
color: #d8d8d8;
} }
.delete:active { .delete:active {

View File

@ -57,8 +57,6 @@
padding: 10px; padding: 10px;
cursor: pointer; cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
color: #ffffffb6;
background-color:#273949;
border: none; border: none;
margin-top: 20px; margin-top: 20px;
display: block; display: block;
@ -67,13 +65,7 @@
transition: background-color 0.1s; transition: background-color 0.1s;
} }
.send-btn:hover { .send-btn:active { box-shadow: 0px 0px 10px rgb(83, 143, 255); }
background-color: rgb(76, 117, 170);
}
.send-btn:active {
box-shadow: 0px 0px 10px rgb(83, 143, 255);
}
@keyframes fadein { @keyframes fadein {
from { opacity: 0%; } from { opacity: 0%; }
@ -87,6 +79,5 @@
} }
.title { .title {
color: darkgray;
font-size: 0.9em; font-size: 0.9em;
} }

View File

@ -18,7 +18,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px; box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
background-color: #1f2c38;
padding: 5px; padding: 5px;
top: 15%; top: 15%;
left: 25%; left: 25%;
@ -41,16 +40,10 @@
font-size: 1.1rem; font-size: 1.1rem;
cursor: pointer; cursor: pointer;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
color: #ffffffb6;
background-color:#273949;
border: none; border: none;
transition: background-color 0.1s; transition: background-color 0.1s;
} }
.sort-by-btn:hover {
background-color: rgb(76, 117, 170);
}
.sort-by-btn:active { .sort-by-btn:active {
box-shadow: 0px 0px 10px rgb(83, 143, 255); box-shadow: 0px 0px 10px rgb(83, 143, 255);
} }

View File

@ -6,9 +6,149 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
background-color: #1f2c38; background-color: #1f2c38;
text-shadow: 0px 1px 1px #1f2c38; text-shadow: 0px 1px 2px #000000;
color: white;
transition: background-color 0.1s;
} }
*::selection { *::selection {
background-color: rgba(115, 192, 255, 0.9); background-color: rgba(120, 120, 120, 0.9);
color: white;
text-shadow: none;
}
/* Dark Mode settings (Default) */
.delete {
background-color: #1f2c38;
color: #ffffffb6;
}
.no-results {
background-color: #1f2c38;
}
.send-btn {
background-color:#273949;
color: #ffffffb6;
}
.sort {
background-color: #1f2c38;
}
.sort-by-btn {
background-color:#273949;
color: #ffffffb6;
}
.btn:hover, .sort-by-btn:hover, .send-btn:hover {
background-color: rgb(76, 117, 170);
}
.tags div {
color: rgb(126, 208, 255);
}
.title {
color: darkgray;
}
.list a {
color: rgb(194, 193, 193);
}
.btn {
color: #ffffffb6;
}
.no-results, .list-row {
transition: background-color 0.1s;
background-color:#273949;
}
.search {
transition: background-color 0.1s;
background-color:#273949;
color: white;
}
.filter {
background-color: #1f2c38;
}
.filter > label {
background-color:#273949;
color: #ffffffb6;
}
/* Light Mode settings */
.light {
text-shadow: 0px 1px 2px #ffffff;
background-color:rgb(233, 220, 179);
color: rgb(64, 64, 64);
}
.light .list-row {
background-color: lightyellow;
}
.light .btn {
background-color: lightyellow;
color: gray;
}
.light .delete {
background-color: lightyellow;
color: rgb(176, 176, 176);
}
.light input {
background-color: lightyellow;
color: black;
}
.light .box, .light .edit-btn {
background-color:rgb(233, 220, 179);
}
.light .title {
color: rgb(105, 105, 105);
}
.light .list a {
color: rgb(102, 102, 102);
}
.light .tags div {
color: rgb(9, 139, 214);
}
.light .filter, .light .sort {
background-color: rgb(233, 220, 179);
}
.light .filter > label {
background-color: lightyellow;
color: #4b4b4bb6;
}
.light .sort-by-btn {
background-color:lightyellow;
color: #4b4b4bb6;
}
.light .send-btn {
background-color: lightyellow;
color: #717171b6;
}
.light .sort-by-btn:hover, .light .btn:hover, .light .send-btn:hover {
background-color: rgb(55, 131, 237);
color: #d8d8d8;
}
.light .no-results {
background-color: lightyellow;
} }