ui: add components

This commit is contained in:
Yangshun Tay
2022-10-04 09:30:15 +08:00
parent db672a2beb
commit e93cc73d51
22 changed files with 1146 additions and 8 deletions

View File

@ -0,0 +1,64 @@
import clsx from 'clsx';
import React, { Fragment } from 'react';
import { Menu, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/24/solid';
import DropdownMenuItem from './DropdownMenuItem';
export type DropdownMenuAlignment = 'end' | 'start';
export type DropdownMenuSize = 'inherit' | 'regular';
type Props = Readonly<{
align?: DropdownMenuAlignment;
children: React.ReactNode; // TODO: Change to strict children.
label: React.ReactNode;
size?: DropdownMenuSize;
}>;
DropdownMenu.Item = DropdownMenuItem;
const alignmentClasses: Record<DropdownMenuAlignment, string> = {
end: 'origin-top-right right-0',
start: 'origin-top-left left-0',
};
export default function DropdownMenu({
align = 'start',
children,
label,
size = 'regular',
}: Props) {
return (
<Menu as="div" className="relative inline-block">
<div className="flex">
<Menu.Button
className={clsx(
'group inline-flex justify-center font-medium text-slate-700 hover:text-slate-900',
size === 'regular' && 'text-sm',
)}>
<div>{label}</div>
<ChevronDownIcon
aria-hidden="true"
className="-mr-1 ml-1 h-5 w-5 flex-shrink-0 text-slate-400 group-hover:text-slate-500"
/>
</Menu.Button>
</div>
<Transition
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
className={clsx(
alignmentClasses[align],
'ring-primary-500 absolute z-10 mt-2 w-48 rounded-md bg-white shadow-lg ring-1 ring-opacity-5 focus:outline-none',
)}>
<div className="py-1">{children}</div>
</Menu.Items>
</Transition>
</Menu>
);
}

View File

@ -0,0 +1,41 @@
import clsx from 'clsx';
import React from 'react';
import { Menu } from '@headlessui/react';
type Props = Readonly<{
href?: string;
isSelected?: boolean;
label: React.ReactNode;
onClick: () => void;
}>;
export default function DropdownMenuItem({
href,
isSelected = false,
label,
onClick,
}: Props) {
return (
<Menu.Item>
{({ active }) => {
const props = {
children: label,
className: clsx(
isSelected ? 'font-medium text-slate-900' : 'text-slate-500',
active && 'bg-slate-100',
'block px-4 py-2 text-sm w-full text-left',
),
onClick,
};
if (href == null) {
return <button type="button" {...props} />;
}
// TODO: Change to <Link> when there's a need for client-side navigation.
return <a href={href} {...props} />;
}}
</Menu.Item>
);
}