manifold/web/components/filter-select-users.tsx
Sinclair Chen 8d06e4b4d2
refactor text input into one component (#1016)
* Add responsive text input component

* Add styled expanding textarea component
2022-10-09 19:37:24 -07:00

165 lines
5.6 KiB
TypeScript

import { UserIcon, XIcon } from '@heroicons/react/outline'
import { useUsers } from 'web/hooks/use-users'
import { User } from 'common/user'
import { Fragment, useMemo, useState } from 'react'
import clsx from 'clsx'
import { Menu, Transition } from '@headlessui/react'
import { Avatar } from 'web/components/avatar'
import { Row } from 'web/components/layout/row'
import { searchInAny } from 'common/util/parse'
import { UserLink } from 'web/components/user-link'
import { Input } from './input'
export function FilterSelectUsers(props: {
setSelectedUsers: (users: User[]) => void
selectedUsers: User[]
ignoreUserIds: string[]
showSelectedUsersTitle?: boolean
selectedUsersClassName?: string
maxUsers?: number
}) {
const {
ignoreUserIds,
selectedUsers,
setSelectedUsers,
showSelectedUsersTitle,
selectedUsersClassName,
maxUsers,
} = props
const users = useUsers()
const [query, setQuery] = useState('')
const [filteredUsers, setFilteredUsers] = useState<User[]>([])
const beginQuerying = query.length > 2
useMemo(() => {
if (beginQuerying)
setFilteredUsers(
users.filter((user: User) => {
return (
!selectedUsers.map((user) => user.name).includes(user.name) &&
!ignoreUserIds.includes(user.id) &&
searchInAny(query, user.name, user.username)
)
})
)
}, [beginQuerying, users, selectedUsers, ignoreUserIds, query])
const shouldShow = maxUsers ? selectedUsers.length < maxUsers : true
return (
<div>
{shouldShow && (
<>
<div className="relative mt-1 rounded-md">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<UserIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</div>
<Input
type="text"
name="user name"
id="user name"
value={query}
onChange={(e) => setQuery(e.target.value)}
className="block w-full pl-10"
placeholder="Austin Chen"
/>
</div>
<Menu
as="div"
className={clsx(
'relative inline-block w-full overflow-y-scroll text-right',
beginQuerying && 'h-36'
)}
>
{({}) => (
<Transition
show={beginQuerying}
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={true}
className="absolute right-0 mt-2 w-full origin-top-right cursor-pointer divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<div className="py-1">
{filteredUsers.map((user: User) => (
<Menu.Item key={user.id}>
{({ active }) => (
<span
className={clsx(
active
? 'bg-gray-100 text-gray-900'
: 'text-gray-700',
'group flex items-center px-4 py-2 text-sm'
)}
onClick={() => {
setQuery('')
setSelectedUsers([...selectedUsers, user])
}}
>
<Avatar
username={user.username}
avatarUrl={user.avatarUrl}
size={'xs'}
className={'mr-2'}
/>
{user.name}
</span>
)}
</Menu.Item>
))}
</div>
</Menu.Items>
</Transition>
)}
</Menu>
</>
)}
{selectedUsers.length > 0 && (
<>
<div className={'mb-2'}>
{showSelectedUsersTitle && 'Added members:'}
</div>
<Row
className={clsx(
'mt-0 grid grid-cols-6 gap-2',
selectedUsersClassName
)}
>
{selectedUsers.map((user: User) => (
<div
key={user.id}
className="col-span-2 flex flex-row items-center justify-between"
>
<Row className={'items-center'}>
<Avatar
username={user.username}
avatarUrl={user.avatarUrl}
size={'sm'}
/>
<UserLink
username={user.username}
className="ml-2"
name={user.name}
/>
</Row>
<XIcon
onClick={() =>
setSelectedUsers([
...selectedUsers.filter((u) => u.id != user.id),
])
}
className=" h-5 w-5 cursor-pointer text-gray-400"
aria-hidden="true"
/>
</div>
))}
</Row>
</>
)}
</div>
)
}