customizable icon color

This commit is contained in:
Daniel 2023-06-02 15:29:52 +03:30
parent 3e6bef875b
commit be180d34e2
11 changed files with 165 additions and 99 deletions

View File

@ -1,10 +1,15 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import {
import { faPenToSquare, faPlus } from "@fortawesome/free-solid-svg-icons"; faFolder,
faPenToSquare,
faPlus,
} from "@fortawesome/free-solid-svg-icons";
import useCollectionStore from "@/store/collections"; import useCollectionStore from "@/store/collections";
import { CollectionIncludingMembers } from "@/types/global"; import { CollectionIncludingMembers } from "@/types/global";
import RequiredBadge from "../../RequiredBadge"; import RequiredBadge from "../../RequiredBadge";
import SubmitButton from "@/components/SubmitButton"; import SubmitButton from "@/components/SubmitButton";
import { HexColorPicker } from "react-colorful";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
type Props = { type Props = {
toggleCollectionModal: Function; toggleCollectionModal: Function;
@ -46,6 +51,7 @@ export default function CollectionInfo({
Name Name
<RequiredBadge /> <RequiredBadge />
</p> </p>
<div className="flex flex-col gap-3">
<input <input
value={collection.name} value={collection.name}
onChange={(e) => onChange={(e) =>
@ -55,12 +61,38 @@ export default function CollectionInfo({
placeholder="e.g. Example Collection" placeholder="e.g. Example Collection"
className="w-full rounded-md p-3 border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100" className="w-full rounded-md p-3 border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
/> />
<div className="color-picker flex justify-between">
<div className="flex flex-col justify-between items-center w-32">
<p className="text-sm w-full font-bold text-sky-300 mb-2">
Icon Color
</p>
<div style={{ color: collection.color || "#7dd3fc" }}>
<FontAwesomeIcon
icon={faFolder}
className="w-12 h-12 drop-shadow"
/>
</div>
<div
className="py-1 px-2 rounded-md text-xs font-semibold cursor-pointer text-gray-500 hover:bg-slate-200 duration-100"
onClick={() =>
setCollection({ ...collection, color: "#7dd3fc" })
}
>
Reset
</div>
</div>
<HexColorPicker
color={collection.color || "#7dd3fc"}
onChange={(e) => setCollection({ ...collection, color: e })}
/>
</div>
</div>
</div> </div>
<div className="w-full"> <div className="w-full">
<p className="text-sm font-bold text-sky-300 mb-2">Description</p> <p className="text-sm font-bold text-sky-300 mb-2">Description</p>
<textarea <textarea
className="w-full h-40 resize-none border rounded-md duration-100 bg-white p-3 outline-none border-sky-100 focus:border-sky-500" className="w-full h-[11.4rem] resize-none border rounded-md duration-100 bg-white p-3 outline-none border-sky-100 focus:border-sky-500"
placeholder="The purpose of this Collection..." placeholder="The purpose of this Collection..."
value={collection.description} value={collection.description}
onChange={(e) => onChange={(e) =>

View File

@ -7,9 +7,16 @@ interface SidebarItemProps {
icon: ReactElement; icon: ReactElement;
path: string; path: string;
className?: string; className?: string;
iconColor?: string;
} }
export default function ({ text, icon, path, className }: SidebarItemProps) { export default function ({
text,
icon,
path,
className,
iconColor,
}: SidebarItemProps) {
const router = useRouter(); const router = useRouter();
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
@ -23,10 +30,13 @@ export default function ({ text, icon, path, className }: SidebarItemProps) {
<div <div
className={`${ className={`${
active ? "bg-sky-500" : "hover:bg-slate-200 bg-gray-100" active ? "bg-sky-500" : "hover:bg-slate-200 bg-gray-100"
} duration-100 py-1 px-4 cursor-pointer flex items-center gap-2 w-full ${className}`} } duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md ${className}`}
> >
{React.cloneElement(icon, { {React.cloneElement(icon, {
className: `w-4 h-4 ${active ? "text-white" : "text-sky-300"}`, className: "w-4 h-4",
style: {
color: active ? "white" : iconColor ? iconColor : "#7dd3fc",
},
})} })}
<p <p
className={`${active ? "text-white" : "text-sky-900"} truncate w-4/6`} className={`${active ? "text-white" : "text-sky-900"} truncate w-4/6`}

View File

@ -27,19 +27,20 @@ export default function ({ className }: { className?: string }) {
return ( return (
<div <div
className={`bg-gray-100 h-screen w-64 xl:w-80 overflow-y-auto border-solid border-r-sky-100 border z-20 ${className}`} className={`bg-gray-100 h-screen w-64 xl:w-80 overflow-y-auto border-solid border-r-sky-100 px-2 border z-20 ${className}`}
> >
<p className="p-4 text-sky-500 font-bold text-2xl my-2 leading-4"> <p className="p-3 text-sky-500 font-bold text-2xl my-2 leading-4">
Linkwarden Linkwarden
</p> </p>
<div className="flex flex-col gap-1">
<Link href="/dashboard"> <Link href="/dashboard">
<div <div
className={`${ className={`${
active === "/dashboard" active === "/dashboard"
? "bg-sky-500" ? "bg-sky-500"
: "hover:bg-slate-200 bg-gray-100" : "hover:bg-slate-200 bg-gray-100"
} outline-sky-100 outline-1 duration-100 py-1 px-4 cursor-pointer flex items-center gap-2`} } outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={faChartSimple} icon={faChartSimple}
@ -61,7 +62,7 @@ export default function ({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === "/collections" ? "bg-sky-500" : "hover:bg-slate-200" active === "/collections" ? "bg-sky-500" : "hover:bg-slate-200"
} outline-sky-100 outline-1 duration-100 py-1 px-4 cursor-pointer flex items-center gap-2`} } outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={faBox} icon={faBox}
@ -85,7 +86,7 @@ export default function ({ className }: { className?: string }) {
active === "/links" active === "/links"
? "bg-sky-500" ? "bg-sky-500"
: "hover:bg-slate-200 bg-gray-100" : "hover:bg-slate-200 bg-gray-100"
} outline-sky-100 outline-1 duration-100 py-1 px-4 cursor-pointer flex items-center gap-2`} } outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={faBookmark} icon={faBookmark}
@ -94,17 +95,20 @@ export default function ({ className }: { className?: string }) {
}`} }`}
/> />
<p <p
className={`${active === "/links" ? "text-white" : "text-sky-900"}`} className={`${
active === "/links" ? "text-white" : "text-sky-900"
}`}
> >
All Links All Links
</p> </p>
</div> </div>
</Link> </Link>
</div>
<div className="text-gray-500 mt-5"> <div className="text-gray-500 mt-5">
<p className="text-sm px-4 mb-2">Collections</p> <p className="text-sm mb-2 pl-3 font-semibold">Collections</p>
</div> </div>
<div> <div className="flex flex-col gap-1">
{collections {collections
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => a.name.localeCompare(b.name))
.map((e, i) => { .map((e, i) => {
@ -113,6 +117,7 @@ export default function ({ className }: { className?: string }) {
key={i} key={i}
text={e.name} text={e.name}
icon={<FontAwesomeIcon icon={faFolder} />} icon={<FontAwesomeIcon icon={faFolder} />}
iconColor={e.color}
path={`/collections/${e.id}`} path={`/collections/${e.id}`}
className="capitalize" className="capitalize"
/> />
@ -120,9 +125,9 @@ export default function ({ className }: { className?: string }) {
})} })}
</div> </div>
<div className="text-gray-500 mt-5"> <div className="text-gray-500 mt-5">
<p className="text-sm px-4 mb-2">Tags</p> <p className="text-sm mb-2 pl-3 font-semibold">Tags</p>
</div> </div>
<div> <div className="flex flex-col gap-1">
{tags {tags
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => a.name.localeCompare(b.name))
.map((e, i) => { .map((e, i) => {

View File

@ -39,6 +39,7 @@ export default async function (
}, },
name: collection.name, name: collection.name,
description: collection.description, description: collection.description,
color: collection.color,
members: { members: {
create: collection.members.map((e) => ({ create: collection.members.map((e) => ({
user: { connect: { email: e.user.email } }, user: { connect: { email: e.user.email } },

View File

@ -30,6 +30,7 @@ export default async function (
data: { data: {
name: collection.name, name: collection.name,
description: collection.description, description: collection.description,
color: collection.color,
isPublic: collection.isPublic, isPublic: collection.isPublic,
members: { members: {
create: collection.members.map((e) => ({ create: collection.members.map((e) => ({

View File

@ -35,6 +35,7 @@
"puppeteer-extra-plugin-adblocker": "^2.13.6", "puppeteer-extra-plugin-adblocker": "^2.13.6",
"puppeteer-extra-plugin-stealth": "^2.11.2", "puppeteer-extra-plugin-stealth": "^2.11.2",
"react": "18.2.0", "react": "18.2.0",
"react-colorful": "^5.6.1",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-image-file-resizer": "^0.4.8", "react-image-file-resizer": "^0.4.8",
"react-select": "^5.7.0", "react-select": "^5.7.0",

View File

@ -104,17 +104,20 @@ export default function () {
<div className="p-5 flex flex-col gap-5 w-full"> <div className="p-5 flex flex-col gap-5 w-full">
<div className="bg-gradient-to-tr from-sky-100 from-10% via-gray-100 via-20% rounded-xl shadow min-h-[10rem] p-5 flex gap-5 flex-col justify-between"> <div className="bg-gradient-to-tr from-sky-100 from-10% via-gray-100 via-20% rounded-xl shadow min-h-[10rem] p-5 flex gap-5 flex-col justify-between">
<div className="flex flex-col sm:flex-row gap-3 justify-between items-center sm:items-start"> <div className="flex flex-col sm:flex-row gap-3 justify-between items-center sm:items-start">
{activeCollection ? (
<div className="flex gap-3 items-center"> <div className="flex gap-3 items-center">
<div className="flex gap-2"> <div className="flex gap-2">
<FontAwesomeIcon <FontAwesomeIcon
icon={faFolder} icon={faFolder}
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-300" style={{ color: activeCollection?.color }}
className="sm:w-8 sm:h-8 w-6 h-6 mt-2"
/> />
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold"> <p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
{activeCollection?.name} {activeCollection?.name}
</p> </p>
</div> </div>
</div> </div>
) : null}
{activeCollection ? ( {activeCollection ? (
<div <div

View File

@ -16,6 +16,7 @@ CREATE TABLE "Collection" (
"id" SERIAL NOT NULL, "id" SERIAL NOT NULL,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
"description" TEXT NOT NULL DEFAULT '', "description" TEXT NOT NULL DEFAULT '',
"color" TEXT NOT NULL DEFAULT '#7dd3fc',
"isPublic" BOOLEAN NOT NULL DEFAULT false, "isPublic" BOOLEAN NOT NULL DEFAULT false,
"ownerId" INTEGER NOT NULL, "ownerId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

View File

@ -24,6 +24,7 @@ model Collection {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
name String name String
description String @default("") description String @default("")
color String @default("#7dd3fc")
isPublic Boolean @default(false) isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id]) owner User @relation(fields: [ownerId], references: [id])
ownerId Int ownerId Int

View File

@ -31,13 +31,6 @@
animation: fade-in-animation 100ms; animation: fade-in-animation 100ms;
} }
/* Bug: "lg:block" just didn't work... */
@media (min-width: 1024px) {
.lgblock {
display: block;
}
}
@keyframes fade-in-animation { @keyframes fade-in-animation {
0% { 0% {
opacity: 0; opacity: 0;
@ -89,3 +82,16 @@
opacity: 1; opacity: 1;
} }
} }
/* For react-colorful */
.color-picker .react-colorful {
width: 7.5rem;
height: 7.5rem;
}
.color-picker .react-colorful__hue {
height: 1rem;
}
.color-picker .react-colorful__pointer {
width: 1.3rem;
height: 1.3rem;
}

View File

@ -3132,6 +3132,11 @@ quick-lru@^5.1.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
react-colorful@^5.6.1:
version "5.6.1"
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b"
integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==
react-dom@18.2.0: react-dom@18.2.0:
version "18.2.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"