From e96a755a2e1764841ee1251559a533fca069e35e Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 2 Mar 2023 17:06:21 +0530 Subject: [PATCH 1/2] style: redesigned create project modal style: changed image picker to pop-over instread of modal --- .../components/core/image-picker-popover.tsx | 152 +++++++++++ apps/app/components/core/index.ts | 2 +- apps/app/components/core/unsplash-modal.tsx | 108 -------- .../components/emoji-icon-picker/index.tsx | 2 +- .../project/create-project-modal.tsx | 247 +++++++++--------- 5 files changed, 278 insertions(+), 233 deletions(-) create mode 100644 apps/app/components/core/image-picker-popover.tsx delete mode 100644 apps/app/components/core/unsplash-modal.tsx diff --git a/apps/app/components/core/image-picker-popover.tsx b/apps/app/components/core/image-picker-popover.tsx new file mode 100644 index 000000000..7a8ca12bb --- /dev/null +++ b/apps/app/components/core/image-picker-popover.tsx @@ -0,0 +1,152 @@ +import React, { useEffect, useState, useRef } from "react"; + +// next +import Image from "next/image"; + +// swr +import useSWR from "swr"; + +// headless ui +import { Tab, Transition, Popover } from "@headlessui/react"; + +// services +import fileService from "services/file.service"; + +// components +import { Input, Spinner } from "components/ui"; +import { PrimaryButton } from "components/ui/button/primary-button"; +// hooks +import useOutsideClickDetector from "hooks/use-outside-click-detector"; + +const tabOptions = [ + { + key: "unsplash", + title: "Unsplash", + }, + { + key: "upload", + title: "Upload", + }, +]; + +type Props = { + label: string | React.ReactNode; + value: string | null; + onChange: (data: string) => void; +}; + +export const ImagePickerPopover: React.FC = ({ label, value, onChange }) => { + const ref = useRef(null); + + const [isOpen, setIsOpen] = useState(false); + const [searchParams, setSearchParams] = useState(""); + const [formData, setFormData] = useState({ + search: "", + }); + + const { data: images } = useSWR(`UNSPLASH_IMAGES_${searchParams}`, () => + fileService.getUnsplashImages(1, searchParams) + ); + + useOutsideClickDetector(ref, () => { + setIsOpen(false); + }); + + useEffect(() => { + if (!images || value !== null) return; + onChange(images[0].urls.regular); + }, [value, onChange, images]); + + return ( + + setIsOpen((prev) => !prev)} + > + {label} + + + +
+ + + {tabOptions.map((tab) => ( + + `rounded py-1 px-4 text-center text-sm outline-none transition-colors ${ + selected ? "bg-theme text-white" : "text-black" + }` + } + > + {tab.title} + + ))} + + + +
{ + e.preventDefault(); + setSearchParams(formData.search); + }} + className="flex gap-x-2 pt-7" + > + setFormData({ ...formData, search: e.target.value })} + placeholder="Search for images" + /> + + Search + +
+ {images ? ( +
+ {images.map((image) => ( +
+ {image.alt_description} { + setIsOpen(false); + onChange(image.urls.regular); + }} + /> +
+ ))} +
+ ) : ( +
+ +
+ )} +
+ +

Coming Soon...

+
+
+
+
+
+
+
+ ); +}; diff --git a/apps/app/components/core/index.ts b/apps/app/components/core/index.ts index e7fffedde..c2d2f8929 100644 --- a/apps/app/components/core/index.ts +++ b/apps/app/components/core/index.ts @@ -9,4 +9,4 @@ export * from "./issues-view"; export * from "./link-modal"; export * from "./not-authorized-view"; export * from "./multi-level-select"; -export * from "./unsplash-modal"; +export * from "./image-picker-popover"; diff --git a/apps/app/components/core/unsplash-modal.tsx b/apps/app/components/core/unsplash-modal.tsx deleted file mode 100644 index 1a2270ea8..000000000 --- a/apps/app/components/core/unsplash-modal.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React, { useState } from "react"; - -import Image from "next/image"; - -import useSWR from "swr"; - -import { Dialog, Transition } from "@headlessui/react"; - -// services -import fileService from "services/file.service"; -// ui -import { Button, Input, Spinner } from "components/ui"; - -type Props = { - isOpen: boolean; - handleClose: () => void; - onSelect: (url: string) => void; -}; - -export const UnsplashImageModal: React.FC = (props) => { - const { isOpen, handleClose, onSelect } = props; - - const [searchQuery, setSearchQuery] = useState(""); - const [formData, setFormData] = useState({ - search: "", - }); - - const { data: images } = useSWR(`UNSPLASH_IMAGES_${searchQuery.toUpperCase()}`, () => - fileService.getUnsplashImages(1, searchQuery) - ); - - const onClose = () => { - handleClose(); - }; - - return ( - - - -
- - -
-
- - - - Select an image - - -
{ - e.preventDefault(); - setSearchQuery(formData.search); - }} - className="flex items-center" - > - setFormData({ ...formData, search: e.target.value })} - placeholder="Search for images" - /> - -
- - {images ? ( -
- {images.map((image) => ( - - ))} -
- ) : ( -
- -
- )} -
-
-
-
-
-
- ); -}; diff --git a/apps/app/components/emoji-icon-picker/index.tsx b/apps/app/components/emoji-icon-picker/index.tsx index 711be870d..4f254408a 100644 --- a/apps/app/components/emoji-icon-picker/index.tsx +++ b/apps/app/components/emoji-icon-picker/index.tsx @@ -44,7 +44,7 @@ const EmojiIconPicker: React.FC = ({ label, value, onChange }) => { return ( setIsOpen((prev) => !prev)} > {label} diff --git a/apps/app/components/project/create-project-modal.tsx b/apps/app/components/project/create-project-modal.tsx index 7a1fa7a92..c39610807 100644 --- a/apps/app/components/project/create-project-modal.tsx +++ b/apps/app/components/project/create-project-modal.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from "react"; +import Image from "next/image"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; @@ -7,17 +8,19 @@ import useSWR, { mutate } from "swr"; import { useForm, Controller } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; + // services import projectServices from "services/project.service"; import workspaceService from "services/workspace.service"; // hooks import useToast from "hooks/use-toast"; // ui +import { PrimaryButton } from "components/ui/button/primary-button"; import { Button, Input, TextArea, CustomSelect } from "components/ui"; // icons -import { PhotoIcon } from "@heroicons/react/24/outline"; +import { XMarkIcon } from "@heroicons/react/24/outline"; // components -import { UnsplashImageModal } from "components/core"; +import { ImagePickerPopover } from "components/core"; import EmojiIconPicker from "components/emoji-icon-picker"; // helpers import { getRandomEmoji } from "helpers/common.helper"; @@ -62,7 +65,6 @@ const IsGuestCondition: React.FC<{ export const CreateProjectModal: React.FC = (props) => { const { isOpen, setIsOpen } = props; - const [isUnsplashModalOpen, setIsUnsplashModalOpen] = useState(false); const [isChangeIdentifierRequired, setIsChangeIdentifierRequired] = useState(true); const { setToastAlert } = useToast(); @@ -177,150 +179,149 @@ export const CreateProjectModal: React.FC = (props) => { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
- + +
+ {watch("cover_image") !== null && ( + cover image + )} + +
+ +
+
+
+

Create Project

+
+ { + setValue("cover_image", image); + }} + value={watch("cover_image")} + /> +
+
-
- - Create Project - -
-

- Create a new project to start working on it. -

-
-
-
-
- - ( - - )} - /> -
-
- -
-
+
+
-
Network
- ( - k === value.toString()) - ? NETWORK_CHOICES[ - value.toString() as keyof typeof NETWORK_CHOICES - ] - : "Select network" - } - input - > - {Object.keys(NETWORK_CHOICES).map((key) => ( - - {NETWORK_CHOICES[key as keyof typeof NETWORK_CHOICES]} - - ))} - - )} + { + setValue("icon", emoji); + }} + value={watch("icon")} />
-
-