Skip to content

Commit

Permalink
feat(ui): create the form to create a new folder
Browse files Browse the repository at this point in the history
  • Loading branch information
tericcabrel committed Jul 26, 2022
1 parent 25efd14 commit 18ebe93
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 26 deletions.
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dependencies": {
"@apollo/client": "^3.6.9",
"@headlessui/react": "^1.6.5",
"@hookform/resolvers": "^2.9.3",
"@hookform/resolvers": "^2.9.6",
"@sentry/nextjs": "^7.5.0",
"@sharingan/ui": "*",
"@sharingan/utils": "*",
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Alert from './src/common/alert';
import Folder from './src/common/folders';
import Folder from './src/common/directory';
import Link from './src/common/link';
import UserAvatar from './src/common/user-avatar';
import Button from './src/forms/button';
Expand All @@ -9,4 +9,7 @@ import { classNames } from './src/utils/classnames';

export * from '@headlessui/react';

export * from './src/hooks';
export * from './src/utils/constants';

export { Alert, Button, Folder, Icon, Link, TextInput, UserAvatar, classNames };
5 changes: 4 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
"dependencies": {
"@headlessui/react": "^1.6.6",
"@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^2.9.6",
"classnames": "^2.3.1",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"react-hook-form": "^7.33.1",
"yup": "^0.32.11"
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { useBooleanState } from '../../hooks';
import { FilePath, FolderItem, SnippetItem } from '../../typings/components';
import MenuAction from '../menu-action';
import BreadCrumb from './breadcrumb';
import EmptyFolder from './empty';
import Folder from './folder';
import Snippet from './snippet';
import EmptyFolder from './folders/empty';
import Folder from './folders/folder';
import NewFolderContainer from './folders/new/new-folder-container';
import Snippet from './snippets/snippet';

type Props = {
folderId: string;
title: string;
};

const Directory = ({ folderId, title }: Props) => {
const [isNewFolderOpened, openNewFolderModal, closeNewFolderModal] = useBooleanState(false);

console.log('rootFolderId => ', folderId);

const folders: FolderItem[] = [
Expand Down Expand Up @@ -97,7 +101,7 @@ const Directory = ({ folderId, title }: Props) => {
<header>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between items-center">
<div className="text-3xl font-bold leading-tight text-gray-900">{title}</div>
<MenuAction />
<MenuAction folderId={folderId} onNewFolderClick={openNewFolderModal} />
</div>
</header>
<main>
Expand Down Expand Up @@ -126,6 +130,7 @@ const Directory = ({ folderId, title }: Props) => {
)}
</div>
</main>
{isNewFolderOpened && <NewFolderContainer closeModal={closeNewFolderModal} />}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PlusIcon } from '@heroicons/react/solid';

import Button from '../../forms/button';
import Button from '../../../forms/button';

const EmptyFolder = () => {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FolderIcon } from '@heroicons/react/solid';

import { FolderItem } from '../../typings/components';
import { numberToString, truncate } from '../../utils/text';
import { FolderItem } from '../../../typings/components';
import { numberToString, truncate } from '../../../utils/text';

type Props = {
item: FolderItem;
Expand Down
106 changes: 106 additions & 0 deletions packages/ui/src/common/directory/folders/new/new-folder-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Dialog, Transition } from '@headlessui/react';
import { yupResolver } from '@hookform/resolvers/yup';
import { Fragment, useRef } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import * as yup from 'yup';

import Button from '../../../../forms/button';
import TextInput from '../../../../forms/text-input';
import { FOLDER_NAME_REGEX, FORM_ERRORS } from '../../../../utils/constants';

type Props = {
closeModal: () => void;
};

const MIN_NAME_LENGTH = 1;
const MAX_NAME_LENGTH = 100;
const formSchema = yup.object().shape({
name: yup
.string()
.required(FORM_ERRORS.fieldRequired)
.min(MIN_NAME_LENGTH, FORM_ERRORS.minCharacters(MIN_NAME_LENGTH))
.max(MAX_NAME_LENGTH, FORM_ERRORS.minCharacters(MAX_NAME_LENGTH))
.matches(FOLDER_NAME_REGEX, { excludeEmptyString: false, message: FORM_ERRORS.folderNameInvalid }),
});

type FormValues = { name?: string };

const NewFolderContainer = ({ closeModal }: Props) => {
const cancelButtonRef = useRef(null);

const formMethods = useForm<FormValues>({
defaultValues: {
name: 'New folder',
},
resolver: yupResolver(formSchema),
});

const submitCreateFolder = (values: FormValues) => {
console.log(values);
};

return (
<Transition.Root show as={Fragment}>
<Dialog as="div" className="relative z-10" initialFocus={cancelButtonRef} onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>

<div className="fixed z-10 inset-0 overflow-y-auto">
<div className="flex items-end sm:items-center justify-center min-h-full p-4 text-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full sm:p-6">
<div className="sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:text-left w-full">
<Dialog.Title as="h3" className="text-lg leading-6 font-medium text-gray-900">
Create a new folder
</Dialog.Title>
<div className="mt-2">
<FormProvider {...formMethods}>
<TextInput className="mt-6 w-full" type="text" name="name" />
</FormProvider>
</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button
className="sm:w-auto sm:ml-3 sm:text-sm mt-0 rounded-md"
onClick={formMethods.handleSubmit(submitCreateFolder)}
>
Create
</Button>
<button
type="button"
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 sm:mt-0 sm:w-auto sm:text-sm"
onClick={closeModal}
ref={cancelButtonRef}
>
Cancel
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

export default NewFolderContainer;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Directory from './directory';
import EmptyFolder from './empty';
import EmptyFolder from './folders/empty';

export default { Directory, Empty: EmptyFolder };
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { DocumentIcon } from '@heroicons/react/solid';

import { SnippetItem } from '../../typings/components';
import { colors } from '../../utils/constants';
import { truncate } from '../../utils/text';
import { SnippetItem } from '../../../typings/components';
import { colors } from '../../../utils/constants';
import { truncate } from '../../../utils/text';

type Props = {
item: SnippetItem;
Expand Down
14 changes: 11 additions & 3 deletions packages/ui/src/common/menu-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { Fragment } from 'react';

import Link from './link';

const MenuAction = () => {
type Props = {
folderId: string;
onNewFolderClick: () => void;
};

const MenuAction = ({ folderId, onNewFolderClick }: Props) => {
return (
<Menu as="div" className="relative inline-block text-left">
<div>
Expand All @@ -26,7 +31,7 @@ const MenuAction = () => {
<Menu.Items className="origin-top-right absolute right-0 mt-2 z-10 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="py-1">
<Menu.Item>
<Link href="/snippets/new">
<Link href={`/folders/${folderId}/new-snippet`}>
<a className="text-gray-700 block px-4 py-2 text-sm flex hover:text-gray-800 hover:bg-gray-100">
<DocumentAddIcon
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
Expand All @@ -37,7 +42,10 @@ const MenuAction = () => {
</Link>
</Menu.Item>
<Menu.Item>
<button className="w-full text-gray-700 block px-4 py-2 text-sm flex hover:text-gray-800 hover:bg-gray-100">
<button
className="w-full text-gray-700 block px-4 py-2 text-sm flex hover:text-gray-800 hover:bg-gray-100"
onClick={onNewFolderClick}
>
<FolderAddIcon className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500" aria-hidden="true" />
New Folder
</button>
Expand Down
12 changes: 7 additions & 5 deletions packages/ui/src/forms/text-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getInputErrorMessage } from '../utils/forms';

type TextInputProps = {
isRequired?: boolean;
label: string;
label?: string;
} & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;

const TextInput = (props: TextInputProps) => {
Expand All @@ -21,13 +21,15 @@ const TextInput = (props: TextInputProps) => {
'block w-full px-4 py-2 text-gray-900 placeholder-gray-600 bg-white border border-gray-400 rounded-lg caret-gray-900',
className,
);
const inputLabel = `${label}${isRequired ? '*' : ''}`;
const inputLabel = label ? `${label}${isRequired ? '*' : ''}` : undefined;

return (
<div className="mb-4">
<label htmlFor={inputProps.name} className="text-base font-medium text-gray-900">
{inputLabel}
</label>
{inputLabel && (
<label htmlFor={inputProps.name} className="text-base font-medium text-gray-900">
{inputLabel}
</label>
)}
<div className="mt-1">
<input
autoComplete="off"
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import useBooleanState from './use-boolean-state';

export { useBooleanState };
13 changes: 13 additions & 0 deletions packages/ui/src/hooks/use-boolean-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useCallback, useState } from 'react';

const useBooleanState = (initialState = false) => {
const [boolean, setBoolean] = useState(initialState);

const setToTrue = useCallback(() => setBoolean(true), []);

const setToFalse = useCallback(() => setBoolean(false), []);

return [boolean, setToTrue, setToFalse] as const;
};

export default useBooleanState;
12 changes: 12 additions & 0 deletions packages/ui/src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
export const REGEX_EMAIL = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
export const FOLDER_NAME_REGEX = /^((\w)+\s?\-?)+$/;

export const FORM_ERRORS = {
emailInvalid: 'The email address is invalid.',
fieldRequired: 'This field is required.',
folderNameInvalid: 'The name must only contains alphabetical letters, numbers and _-',
maxCharacters: (numChar: number) => `Must be at most ${numChar} characters`,
minCharacters: (numChar: number) => `Must be at least ${numChar} characters`,
passwordNotMatch: "The confirm password doesn't match the password",
};

export const colors: string[] = [
'#1abc9c',
'#2ecc71',
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1542,10 +1542,10 @@
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324"
integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==

"@hookform/resolvers@^2.9.3":
version "2.9.3"
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.3.tgz#13f6934cfe705e24fac094da377e0621adcfc424"
integrity sha512-Eqc/qgjq0VX/TU0a5D2O+yR/kAKflnjaVlYFC1wI2qBm/sgjKTXxv27ijLwHUoHPIF+MUkB/VuQqHJ5DcmbCww==
"@hookform/resolvers@^2.9.6":
version "2.9.6"
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.6.tgz#db4277a96d1817d94169108167014926d8a10398"
integrity sha512-f4VxF8w9rdX0WsDCk3FW1dGPj/Sj8+1Ulcgtm5ymgWEpbA/fjY+NUDh+L9hftmxDgP8+EJFtF+qFK4gPEXVrVQ==

"@humanwhocodes/config-array@^0.9.2":
version "0.9.5"
Expand Down

0 comments on commit 18ebe93

Please sign in to comment.