move move frontend to progress-test

This commit is contained in:
João Geonizeli
2022-07-21 21:16:59 -03:00
parent f8d5d08447
commit 386050d4ad
129 changed files with 159374 additions and 39 deletions

View File

@@ -0,0 +1,15 @@
import React, { FC } from "react";
type Props = {
children: any
}
export const Alert: FC<Props> = ({ children }) => (
<div
className="w-full md:my-2 p-2 bg-red-600 items-center text-red-100 leading-none lg:rounded flex lg:inline-flex"
role="alert"
>
<span className="flex px-2 py-1 font-bold">Oops!</span>
<span className="font-semibold mr-2 text-left flex-auto">{children}</span>
</div>
);

View File

@@ -0,0 +1 @@
export { Alert } from "./Alert";

View File

@@ -0,0 +1,38 @@
import React, { FC } from 'react'
import { MdDone, MdError, MdInfo, MdWarning } from 'react-icons/md'
type AlertSeverity = 'error' | 'warning' | 'info' | 'success'
const ICONS = {
error: <MdError />,
warning: <MdWarning />,
info: <MdInfo />,
success: <MdDone />
}
const COLOR_CLASSES = {
error: 'bg-red-300 border-red-600 text-red-800',
warning: 'bg-orange-300 border-orange-600 text-orange-800',
info: 'bg-blue-300 border-blue-600 text-blue-800',
success: 'bg-green-300 border-green-600 text-green-800',
}
export type Props = {
severity?: AlertSeverity
text?: string
}
export const AlertV2: FC<Props> = ({
severity = 'info',
text = '',
}) => (
<div className={`flex rounded shadow p-4 mx-auto my-2 ${COLOR_CLASSES[severity]}`}>
<div className="text-xl my-auto pr-2">
{ICONS[severity]}
</div>
<span>
{text}
</span>
</div>
)

View File

@@ -0,0 +1,2 @@
export { AlertV2 } from "./AlertV2";
export type { Props as AlertV2Props } from "./AlertV2";

View File

@@ -0,0 +1,221 @@
import React, { FC, Fragment, useState } from 'react'
import { useHistory, useLocation } from 'react-router';
import { Menu, Transition } from '@headlessui/react'
import { ChartBarIcon, ClipboardListIcon } from '@heroicons/react/outline'
import { Dialog } from '../Dialog'
import { useDispatch, useSelector } from 'react-redux';
import { useCurrentUser } from '../../contexts';
import { RootState } from '../../services/store';
import { classNames } from '../../utils';
import { DashboardRoutePaths, QuestionRoutePaths, SessionRoutePaths } from '../../routes'
import { turnOff } from '../../services/store/unsavedChanges';
import { CurrentUserAvatar } from "../CurrentUserAvatar";
const UserMenu: FC = () => {
const { user } = useCurrentUser();
const history = useHistory();
const [confirmLogout, setConfirmLogout] = useState(false)
const unsavedChanges = useSelector((state: RootState) => state.unsavedChanges)
const dispatch = useDispatch()
const doLogout = () => {
setConfirmLogout(false)
dispatch(turnOff())
history.push('/')
}
const handleLogout = () => {
if (unsavedChanges && !confirmLogout) {
setConfirmLogout(true)
} else {
doLogout()
}
}
const [newPath, setNewPath] = useState<string>()
const handleForcedRedirect = () => {
if (!newPath) return
dispatch(turnOff())
setNewPath(undefined)
history.push(newPath)
}
const handleLinkClick = (pathname: string) => {
if (unsavedChanges) {
setNewPath(pathname)
} else {
history.push(pathname)
}
}
const menuItems = [
{
onClick: () => { handleLinkClick(SessionRoutePaths.show) },
label: 'Perfil'
},
{
onClick: handleLogout,
label: 'Sair'
}
]
return (
<>
<Dialog
isOpen={!!newPath}
setIsOpen={(value) => setNewPath(value ? newPath : undefined)}
onConfirmation={handleForcedRedirect}
title="Modificações não Salvas"
text="Todas as alterações serão descartadas. Deseja continuar?"
/>
<Dialog
isOpen={confirmLogout}
setIsOpen={setConfirmLogout}
onConfirmation={handleLogout}
title="Modificações não Salvas"
text="Todas as alterações serão descartadas. Deseja continuar?"
/>
<Menu as="div" className="relative h-full">
{({ open }) => (
<>
<Menu.Button
className="h-full flex flex-row px-2 items-center hover:bg-primary-dark text-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white"
>
<span className="hidden md:block pr-2">
{user?.name}
</span>
<div className="w-12">
<CurrentUserAvatar />
</div>
</Menu.Button>
<Transition
show={open}
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
static
className="z-50 origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none cursor-pointer"
>
{menuItems.map((item) => (
<Menu.Item key={`menu-item-${item.label}`} onClick={item.onClick}>
{({ active }) => (
<span
className={classNames(
active ? 'bg-gray-100' : '',
'block px-4 py-2 text-sm text-gray-900'
)}
>
{item.label}
</span>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</>
)}
</Menu>
</>
)
}
const Links: FC = () => {
const unsavedChanges = useSelector((state: RootState) => state.unsavedChanges)
const dispatch = useDispatch()
const location = useLocation()
const history = useHistory()
const [newPath, setNewPath] = useState<string>()
const handleForcedRedirect = () => {
if (!newPath) return
dispatch(turnOff())
setNewPath(undefined)
history.push(newPath)
}
const handleLinkClick = (pathname: string) => {
if (unsavedChanges) {
setNewPath(pathname)
} else {
history.push(pathname)
}
}
const links = [{
icon: <ChartBarIcon className="w-6" />,
tabel: 'Painel',
pathname: DashboardRoutePaths.index,
isCurrent: location.pathname.includes('dashboard'),
},
{
icon: <ClipboardListIcon className="w-6" />,
tabel: 'Edição',
pathname: QuestionRoutePaths.index,
isCurrent: location.pathname.includes('question'),
}]
return (
<>
<Dialog
isOpen={!!newPath}
setIsOpen={(value) => setNewPath(value ? newPath : undefined)}
onConfirmation={handleForcedRedirect}
title="Modificações não Salvas"
text="Todas as alterações serão descartadas. Deseja continuar?"
/>
<div className="h-full flex items-center pl-4">
{links.map((link) => (
<button
className={`h-full flex items-center px-2 mx-2 text-gray-300 hover:bg-primary-dark ${link.isCurrent ? 'underline bg-primary-dark' : ''}`}
key={`navbar-link-${link.pathname}`}
onClick={() => handleLinkClick(link.pathname)}
>
<span className="pr-2 ">
{link.icon}
</span>
{link.tabel}
</button>
))}
</div>
</>
)
}
const Logo: FC = () => (
<div className="h-full grid place-items-center">
<img
alt="Símbolo do Unifeso"
className="hidden md:block h-12 w-auto"
src={'unifesoLogo'}
/>
<img
alt="Logotipo do Unifeso"
className="md:hidden h-12 w-auto"
src={'unifesoLogoCompact'}
/>
</div>
)
export const Appbar = () => {
return (
<div className="px-4 bg-primary-normal flex items-center justify-between h-16 shadow-md">
<div className="flex h-full">
<Logo />
<Links />
</div>
<UserMenu />
</div>
)
}

View File

@@ -0,0 +1 @@
export * from "./Appbar";

View File

@@ -0,0 +1,63 @@
import React, { FC, useState } from "react";
import axios from "axios";
import { Alert } from "../Alert";
import { Button } from "../Button";
import { PhotoCrop } from "./PhotoCrop";
import { useCurrentUser } from "../../contexts";
import { Modal } from "../Modal";
type Props = {
isOpen: boolean
setIsOpen: (value: boolean) => void;
};
export const AvatarEditor: FC<Props> = ({ isOpen, setIsOpen }) => {
const [croppedImage, setCroppedImage] = useState<any>()
const [alert, setAlert] = useState<boolean>()
const { refetch, authToken } = useCurrentUser()
const instance = axios.create({
});
instance.defaults.headers.common.Authorization = `Bearer ${authToken}`;
const onSubmit = () => {
instance
.post("/update_avatar", {
upload: croppedImage,
})
.then((res) => {
if (res.status === 200) {
setIsOpen(false)
refetch()
} else {
setAlert(true);
}
})
.catch(() => {
setAlert(true);
});
};
return (
<Modal
title="Alterar Imagem de Perfil"
isOpen={isOpen}
setIsOpen={setIsOpen}
buttons={
<>
<Button onClick={() => setIsOpen(false)}>
Cancelar
</Button>
<Button type="primary" onClick={() => onSubmit()}>
Salvar
</Button>
</>
}
>
{alert && <Alert>Algo deu errado, tente novamente mais tarde.</Alert>}
<PhotoCrop callback={setCroppedImage} />
</Modal>
);
};

View File

@@ -0,0 +1,49 @@
import React, { FC, useState } from "react";
import PhotoCropper from "react-avatar-edit";
type Props = {
callback: (value: any) => void
}
const borderStyle: React.CSSProperties = {
textAlign: 'center',
margin: 'auto',
borderStyle: 'dotted',
borderWidth: '0.3rem',
borderRadius: '0.3rem',
}
export const PhotoCrop: FC<Props> = ({ callback }) => {
const [result, setResult] = useState<any>();
const onCrop = (cropped: any) => {
setResult(cropped);
callback(result);
};
const onClose = () => {
setResult(null);
};
const onBeforeFileLoad = (elem: any) => {
if (elem.target.files[0].size > 1000000) {
elem.target.value = "";
alert("A imagem selecionada é grande de mais!")
}
};
const dimention = 300;
return (
<PhotoCropper
borderStyle={borderStyle}
label="Escolha uma imagem"
width={dimention}
height={dimention}
imageWidth={dimention}
imageHeight={dimention}
onCrop={(e) => onCrop(e)}
onClose={() => onClose()}
onBeforeFileLoad={onBeforeFileLoad}
/>
);
};

View File

@@ -0,0 +1 @@
export { AvatarEditor } from "./AvatarEditor";

View File

@@ -0,0 +1,63 @@
import React from 'react'
const styleClasses = {
primary: "bg-primary-normal hover:bg-primary-dark text-white",
secondary: "bg-gray-200 hover:bg-gray-400 text-gray-800",
disabled: "bg-gray-200 text-gray-600 cursor-not-allowed shadow-none hover:shadow-none",
tertiary: "shadow-none hover:shadow-none drop-shadow-sm text-gray-900 hover:text-gray-600",
}
export type ButtonProps = {
type?: 'default' | 'primary' | 'tertiary';
className?: string;
children?: string | JSX.Element;
htmlType?: 'submit' | 'button' | 'reset';
onClick?: React.MouseEventHandler<HTMLElement>;
disabled?: boolean
}
const ButtonBase: React.ForwardRefRenderFunction<unknown, ButtonProps> = (props, ref) => {
const {
type = 'default',
className = '',
htmlType = 'button',
onClick,
disabled,
children,
...rest
} = props
const buttonRef = (ref as any) || React.createRef<HTMLElement>()
const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
if (htmlType !== 'submit') {
e.preventDefault()
}
if (disabled || !onClick) return
onClick(e)
}
const extraClasses = () => {
if (disabled) return styleClasses.disabled
if (type === 'primary') return styleClasses.primary
if (type === 'tertiary') return styleClasses.tertiary
return styleClasses.secondary
}
return <button
{...rest}
type={htmlType}
disabled={disabled}
className={`transition duration-300 ease-in-out block text-center cursor-pointer p-2 px-8 rounded shadow-lg hover:shadow-lg ${extraClasses()} ${className}`}
onClick={handleClick}
ref={buttonRef}
>
{children}
</button>
}
export const Button = React.forwardRef<unknown, ButtonProps>(ButtonBase)

View File

@@ -0,0 +1 @@
export { Button } from "./Button";

View File

@@ -0,0 +1,23 @@
import React, { FC } from "react";
type Props = {
title: string
action?: () => void
children: any
className?: string
}
export const Card: FC<Props> = ({
title, action, children, className = '',
}) => (
<div className={`bg-white md:rounded shadow-sm border border-gray-300 w-full ${className}`}>
<div className="border-b border-gray-300 bg-gray-100 md:rounded-t p-2 shadow-sm flex items-center">
<span className="text-lg text-gray-800 flex-grow">{title}</span>
{
action ? action() : null
}
</div>
<div className="p-4 h-full">
{children}
</div>
</div>
);

View File

@@ -0,0 +1 @@
export { Card } from "./Card";

View File

@@ -0,0 +1,19 @@
import React, { FC } from "react";
import styled from "styled-components";
const Grid = styled.div`
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
`;
type Props = {
className?: string
children: any
}
export const CardGrid: FC<Props> = ({ children, className }) => (
<Grid className={className}>
{children}
</Grid>
);

View File

@@ -0,0 +1 @@
export { CardGrid } from "./CardGrid";

View File

@@ -0,0 +1,14 @@
import React, {FC} from 'react'
import {useCurrentUser} from "../../contexts";
import {UserAvatar} from "../UserAvatar";
export const CurrentUserAvatar: FC = () => {
const {user} = useCurrentUser()
if (!user) return null
return (
<UserAvatar user={user}/>
)
}

View File

@@ -0,0 +1 @@
export * from './CurrentUserAvatar'

View File

@@ -0,0 +1,46 @@
import React, { FC } from "react"
import { Button } from "../Button"
import { Modal } from '../Modal'
type DialogType = 'confirmation' | 'notice'
type Props = {
type?: DialogType
title: string
isOpen?: boolean
text?: any
setIsOpen: (state: boolean) => void
onConfirmation: () => void
};
export const Dialog: FC<Props> = ({
type = 'confirmation',
title,
isOpen: open = false,
setIsOpen,
onConfirmation,
text,
}) => {
return (
<Modal
title={title}
isOpen={open}
setIsOpen={setIsOpen}
buttons={
<>
{type === 'confirmation' &&
<Button onClick={() => setIsOpen(false)}>
Cancelar
</Button>
}
<Button type="primary" onClick={onConfirmation}>
Confirmar
</Button>
</>
}
>
{text}
</Modal>
)
};

View File

@@ -0,0 +1 @@
export * from "./Dialog";

View File

@@ -0,0 +1,62 @@
import React, { FC, Fragment } from 'react'
import { Disclosure, Transition } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/outline'
type Item = {
title?: string | JSX.Element
body?: string | JSX.Element
icon?: JSX.Element
}
type Props = {
items: Item[]
}
export const Disclosures: FC<Props> = ({
items
}) => {
return (
<div className="w-full grid grid-cols-1 gap-3">
<div className="w-full max-w-md p-2 mx-auto bg-white rounded-2xl">
{items.map((item) => (
<Disclosure as="div" className="my-2 rounded border">
{({ open }) => (
<>
<Disclosure.Button as={Fragment}>
<button className="flex p-2 bg-gray-200 w-full justify-between">
<div className="grid place-items-center">
{item.icon}
</div>
<span className="pl-2">
{item.title}
</span>
<ChevronDownIcon
className={`${open ? 'transform rotate-180' : ''} w-5 h-5 text-gray-800`}
/>
</button>
</Disclosure.Button>
<Transition
show={open}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Disclosure.Panel
className="p-2 bg-gray-100"
>
{item.body ?? 'Nenhum comentário.'}
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
)
)}
</div>
</div>
)
}

View File

@@ -0,0 +1 @@
export * from "./Disclosures";

View File

@@ -0,0 +1,39 @@
import React, {ChangeEvent} from 'react'
import {v4 as uuid} from 'uuid'
export type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> & {
label?: string
}
const ButtonBase: React.ForwardRefRenderFunction<unknown, Props> = (props, ref) => {
const {
className = '',
onChange,
label,
...rest
} = props
const inputRef = (ref as any) || React.createRef<HTMLElement>()
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
if (!onChange) return
onChange(e)
}
const id = uuid()
return (
<span>
{label ?? <label htmlFor={id}>{label}</label>}
<input
{...rest}
id={id}
className={`block rounded p-1 w-full border-gray-400 border shadow-sm ${className}`}
onChange={handleChange}
ref={inputRef}
/>
</span>
)
}
export const Input = React.forwardRef<unknown, Props>(ButtonBase)

View File

@@ -0,0 +1 @@
export * from './Input'

View File

@@ -0,0 +1,19 @@
import React, { FC } from "react";
import styled from "styled-components";
const StyledInputGroup = styled.div`
&:first-of-type {
margin-top: 0
}
&:last-of-type {
margin-bottom: 0
}
`;
type Props = {
children?: any
}
export const InputGroup: FC<Props> = ({ children }) => (
<StyledInputGroup className="mt-4 mb-2">{children}</StyledInputGroup>
);

View File

@@ -0,0 +1 @@
export { InputGroup } from "./InputGroup";

View File

@@ -0,0 +1,57 @@
import React, { FC } from 'react'
type ListItemIconProps = {
icon: JSX.Element
}
const ListItemIcon: FC<ListItemIconProps> = ({ icon }) => {
return (
<div className="grid place-items-center pr-3">
{icon}
</div>
)
}
type ListItemTextProps = {
text?: string
}
const ListItemText: FC<ListItemTextProps> = ({ text }) => {
return (
<div className="pl-2">
<p>
{text ?? ''}
</p>
</div>
)
}
type ListItemProps = {
icon?: JSX.Element
text?: string
}
export const ListItem: FC<ListItemProps> = ({ icon, text, children }) => {
return (
<li className="flex py-2 border-t border-b border-gray-200">
{icon && <ListItemIcon icon={icon} />}
{text && <ListItemText text={text} />}
{children}
</li>
)
}
type ListProps = {
className?: string
}
export const List: FC<ListProps> = ({
className = '',
children,
}) => {
return (
<ul className={`list-none p-0 m-0 ${className}`}>
{children}
</ul>
)
}

View File

@@ -0,0 +1 @@
export * from "./List";

View File

@@ -0,0 +1,82 @@
import React, { FC, Fragment } from 'react'
import { Dialog, Transition } from '@headlessui/react'
type Props = {
isOpen: boolean
setIsOpen: (state: boolean) => void
buttons?: any,
title: string,
className?: string,
}
export const Modal: FC<Props> = ({
isOpen,
setIsOpen,
children,
buttons,
title,
className = '',
}) => {
const closeModal = () => {
setIsOpen(false)
}
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog
open={isOpen}
as="div"
className={`fixed inset-0 z-10 overflow-y-auto ${className}`}
onClose={closeModal}
>
<div className="min-h-screen px-4 text-center">
<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"
>
<Dialog.Overlay className="fixed inset-0 bg-gray-900 bg-opacity-50" />
</Transition.Child>
<span
className="inline-block h-screen align-middle"
aria-hidden="true"
>
&#8203;
</span>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div className="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900 mt-2 mb-4"
>
{title}
</Dialog.Title>
<div className="mt-2">
{children}
</div>
{buttons &&
<div className="mt-4 grid grid-flow-col gap-3">
{buttons}
</div>
}
</div>
</Transition.Child>
</div>
</Dialog>
</Transition>
)
}

View File

@@ -0,0 +1 @@
export * from "./Modal";

View File

@@ -0,0 +1,106 @@
import React, { FC, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link, useHistory } from "react-router-dom";
import { FaHome, FaPlus } from "react-icons/fa";
import styled from "styled-components";
import { turnOff } from "../../services/store/unsavedChanges";
import { RootState } from "../../services/store";
import { Dialog } from "../Dialog";
import {QuestionRoutePaths} from "../../routes";
const HorizontalMenu = styled.ul`
margin: 0;
padding: 0;
list-style: none;
width: 100%;
display: flex;
& > li {
display: inline;
cursor: pointer;
}
& > li {
display: inline;
}
& > li > div {
cursor: pointer;
display: inline-flex;
flex-direction: row;
margin-right: 2rem;
}
`;
type ItemProps = {
className?: string
children?: any
}
const Item: FC<ItemProps> = ({ children, className }) => (
<div className={`hover:text-white ${className || ""}`}>
{children}
</div>
);
type Props = {
home?: boolean
newQuestion?: boolean
children?: any
}
export const Navigator: FC<Props> = ({
home = false, newQuestion = false, children,
}) => {
const [confirmLeaveDialog, setConfirmLeaveDialog] = useState(false);
const unsavedChanges = useSelector((state: RootState) => state.unsavedChanges)
const dispatch = useDispatch()
const history = useHistory();
const confirmLeave = () => {
dispatch(turnOff());
history.push(QuestionRoutePaths.index);
};
const goHome = () => {
if (unsavedChanges) {
setConfirmLeaveDialog(true);
} else {
confirmLeave();
}
};
return (
<>
<Dialog
isOpen={confirmLeaveDialog}
setIsOpen={(value) => setConfirmLeaveDialog(value)}
onConfirmation={confirmLeave}
title="Modificações não Salvas"
text="Todas as alterações serão descartadas. Deseja continuar?"
/>
<div className="flex p-1 text-md px-2 sm:px-8 text-gray-400 bg-primary-dark shadow-md" style={{ maxHeight: "34.4px" }}>
<HorizontalMenu className="list-none">
{home
&& (
<Item>
<button onClick={() => goHome()} className="flex">
<FaHome className="my-auto" />
<span className="pl-3">Início</span>
</button>
</Item>
)}
{
(newQuestion) ? (
<Item>
<Link to="/questions/new" className="flex">
<FaPlus className="my-auto" />
<span className="pl-3">Nova Questão</span>
</Link>
</Item>
) : null
}
{children}
</HorizontalMenu>
</div>
</>
);
};

View File

@@ -0,0 +1 @@
export { Navigator } from "./Navegator";

View File

@@ -0,0 +1,27 @@
import React, {FC} from "react";
import {User} from "../../__generated__/graphql-schema";
import BoringAvatar from "boring-avatars";
type Props = {
user: User
className?: string
}
export const UserAvatar: FC<Props> = ({user, className}) => {
return (
<div className={`rounded-full border-2 border-primary-light shadow ${className || ''}`}>
{user.avatarUrl ?
<img
src={`${process.env.REACT_APP_BACKEND_URL}/${user.avatarUrl}`}
alt={`Avatar do usuário ${user.name}`}
/>
: <BoringAvatar
size={"100%"}
name={user.name}
variant="pixel"
colors={["#595F72", "#575D90", "#84A07C", "#C3D350", "#E6F14A"]}
/>
}
</div>
)
};

View File

@@ -0,0 +1 @@
export { UserAvatar } from "./UserAvatar";

View File

@@ -0,0 +1,16 @@
export * from "./Alert";
export * from "./AlertV2";
export * from "./Button";
export * from "./Card";
export * from "./CardGrid";
export * from "./InputGroup";
export * from "./AvatarEditor";
export * from "./Navegator";
export * from "./UserAvatar";
export * from "./Dialog";
export * from "./Appbar";
export * from "./Modal";
export * from "./List";
export * from "./Disclosures";
export * from './CurrentUserAvatar'
export * from './Input'