Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds space-finder autocomplete combobox #268

Merged
merged 10 commits into from
Jan 19, 2023
79 changes: 44 additions & 35 deletions examples/react/w3console/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent, useEffect, useState } from 'react'
import type { Space } from '@w3ui/keyring-core'

import { Authenticator, Uploader, UploadsList, W3APIProvider } from '@w3ui/react'
import { Authenticator, Uploader, UploadsList, W3APIProvider, SpaceFinder } from '@w3ui/react'
import { useKeyring } from '@w3ui/react-keyring'
import { useUploadsList } from '@w3ui/react-uploads-list'
import md5 from 'blueimp-md5'
Expand Down Expand Up @@ -64,24 +64,31 @@ function SpaceSection (): JSX.Element {
useEffect(() => { void reload() }, [space])
const registered = Boolean(space?.registered())
return (
<div className='container mx-auto'>
{registered
? (
<div>
<header className='py-3'>
{(space !== undefined) && (
<div className='flex flex-row items-start gap-2'>
<img title={space.did()} src={`https://www.gravatar.com/avatar/${md5(space.did())}?d=identicon`} className='w-10 hover:saturate-200 saturate-0 invert border-solid border-gray-500 border' />
<div>
<h1 className='text-xl font-semibold leading-5'>{space.name() || 'Untitled'}</h1>
<label className='font-mono text-xs text-gray-500'>{space.did()}</label>
</div>
</div>
)}

</header>
<div className='container mx-auto'>
{registered ? (
<>
<div className='flex flex-row space-x-4 mb-4 justify-between'>
<div className='shrink-0'>
{(space !== undefined) && (
<img src={`https://www.gravatar.com/avatar/${md5(space.did())}?d=identicon`} className='w-20' />
)}
</div>
<Uploader onUploadComplete={() => { void reload() }} />
<Uploader onUploadComplete={() => { void reload() }} />
<div className='mt-8'>
<UploadsList />
</div>
<UploadsList />
</>
)
: (
) : (
<SpaceRegistrar />
)}
)}
</div>
</div>
)
}
Expand Down Expand Up @@ -112,11 +119,13 @@ function SpaceCreator (props: any): JSX.Element {
? (
<form onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}>
<input
className="text-black"
type='email' placeholder='Email' autofocus
value={email}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setEmail(e.target.value) }}
/>
<input
className="text-black"
placeholder='Name'
value={name}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setName(e.target.value) }}
Expand All @@ -143,39 +152,39 @@ function SpaceSelector (props: any): JSX.Element {
await setCurrentSpace(space.did())
}
return (
<div {...props}>
<h3 className='text-lg uppercase font-bold my-4'>Spaces</h3>
<ul className='space-y-2'>
{spaces.map((space, i) => (
<li key={space.did()} className={`hover:font-bold ${space.sameAs(currentSpace) ? 'font-bold' : ''}`}>
<button onClick={() => { void selectSpace(space) }}>
{space.name() ?? `Space ${i + 1}`}
</button>
</li>
))}
</ul>
<SpaceCreator className='mt-12' />
<div>
<h3 className='text-xs tracking-wider uppercase font-bold my-2 text-gray-400 font-mono'>Spaces</h3>
<SpaceFinder spaces={spaces} selected={currentSpace} setSelected={selectSpace} />
</div>
)
}

export function Logo (): JSX.Element {
return (
<h1 className='font-bold flex flex-row justify-center items-center gap-2'>
<svg className='site-logo-image text-white' width='30' viewBox='0 0 27.2 27.18' xmlns='http://www.w3.org/2000/svg'><path d='M13.6 27.18A13.59 13.59 0 1127.2 13.6a13.61 13.61 0 01-13.6 13.58zM13.6 2a11.59 11.59 0 1011.6 11.6A11.62 11.62 0 0013.6 2z' fill='currentColor' /><path d='M12.82 9.9v2.53h1.6V9.9l2.09 1.21.77-1.21-2.16-1.32 2.16-1.32-.77-1.21-2.09 1.21V4.73h-1.6v2.53l-2-1.21L10 7.26l2.2 1.32L10 9.9l.78 1.21zM18 17.79v2.52h1.56v-2.52L21.63 19l.78-1.2-2.16-1.33 2.16-1.28-.78-1.19-2.08 1.2v-2.58H18v2.56L15.9 14l-.77 1.2 2.16 1.32-2.16 1.33.77 1.15zM8.13 17.79v2.52h1.56v-2.52L11.82 19l.77-1.2-2.16-1.33 2.12-1.28-.73-1.24-2.13 1.23v-2.56H8.13v2.56L6.05 14l-.78 1.2 2.16 1.3-2.16 1.33.78 1.17z' fill='currentColor' /></svg>
console
</h1>
)
}

export function App (): JSX.Element {
return (
<W3APIProvider>
<Authenticator>
<div className='flex min-h-full w-full'>
<nav className='flex-none w-72 bg-white p-4 border-r border-gray-200'>
<div className='flex flex-col justify-start min-h-full'>
<nav className='flex-none w-64 bg-gray-900 text-white px-4 pb-4 border-r border-gray-800'>
<div className='flex flex-col justify-between min-h-full'>
<div class="flex-none">
<SpaceSelector />
</div>
<div>
<h1 className='font-bold pb-4 flex flex-row justify-start items-center gap-2'>
<svg className='site-logo-image text-black' width='30' viewBox='0 0 27.2 27.18' xmlns='http://www.w3.org/2000/svg'><path d='M13.6 27.18A13.59 13.59 0 1127.2 13.6a13.61 13.61 0 01-13.6 13.58zM13.6 2a11.59 11.59 0 1011.6 11.6A11.62 11.62 0 0013.6 2z' fill='currentColor' /><path d='M12.82 9.9v2.53h1.6V9.9l2.09 1.21.77-1.21-2.16-1.32 2.16-1.32-.77-1.21-2.09 1.21V4.73h-1.6v2.53l-2-1.21L10 7.26l2.2 1.32L10 9.9l.78 1.21zM18 17.79v2.52h1.56v-2.52L21.63 19l.78-1.2-2.16-1.33 2.16-1.28-.78-1.19-2.08 1.2v-2.58H18v2.56L15.9 14l-.77 1.2 2.16 1.32-2.16 1.33.77 1.15zM8.13 17.79v2.52h1.56v-2.52L11.82 19l.77-1.2-2.16-1.33 2.12-1.28-.73-1.24-2.13 1.23v-2.56H8.13v2.56L6.05 14l-.78 1.2 2.16 1.3-2.16 1.33.78 1.17z' fill='currentColor' /></svg>
console
</h1>
<SpaceCreator className='mb-4' />
<Logo />
</div>
<SpaceSelector className='flex-none grow' />
</div>
</nav>
<main className='grow bg-gray-100 dark:bg-dark-gray p-4'>
<main className='grow bg-dark-gray text-white p-4'>
<SpaceSection />
</main>
</div>
Expand Down
8 changes: 4 additions & 4 deletions examples/react/w3console/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

:root {
--w3ui-uploader-height: theme(spacing.36);
--w3ui-uploader-primary: theme(colors.orange.400);
--w3ui-uploader-primary-hover: theme(colors.orange.500);
--w3ui-uploader-primary: theme(colors.slate.800);
--w3ui-uploader-primary-hover: theme(colors.blue.900);
}

.w3-uploads-list {
Expand All @@ -21,15 +21,15 @@
}

.w3-uploads-list thead {
@apply text-left bg-gray-400 bg-opacity-50 text-sm;
@apply text-left bg-opacity-50 text-xs tracking-wide text-zinc-300;
}

.w3-uploads-list th {
@apply p-3;
}

.w3-uploads-list td {
@apply block w-64 p-3;
@apply block w-64 p-2 pl-3 font-mono text-xs;
}

.w3-uploads-list nav {
Expand Down
3 changes: 2 additions & 1 deletion examples/react/w3console/tailwind.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
module.exports = {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}'
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@w3ui/react/src/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
Expand Down
2 changes: 2 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
},
"homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/react-ui",
"dependencies": {
"@headlessui/react": "^1.7.7",
"@heroicons/react": "^2.0.13",
"@w3ui/react-keyring": "workspace:^",
"@w3ui/react-uploader": "workspace:^",
"@w3ui/react-uploads-list": "workspace:^",
Expand Down
89 changes: 89 additions & 0 deletions packages/react/src/SpaceFinder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { Fragment, useState } from 'react'
import { Combobox, Transition } from '@headlessui/react'
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid'

export function SpaceFinder ({ spaces, selected, setSelected, className }) {
const [query, setQuery] = useState('')

console.log('space', query, selected)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.log('space', query, selected)


const filtered =
query === ''
? spaces
: spaces.filter((space) =>
(space.name() || space.did())
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.toLowerCase().replace(/\s+/g, ''))
)

return (
<div className={className}>
<Combobox value={selected} onChange={setSelected} by={(a, b) => a.sameAs(b)}>
<div className="relative mt-1">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm down to merge this as-is, but just a note that we'll need to translate this Tailwind styling to CSS-variable-based styling before we ship this package for real

<div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm">
<Combobox.Input
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0 text-black"
displayValue={(space) => space.name() || space.did()}
onChange={(event) => setQuery(event.target.value)}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
afterLeave={() => setQuery('')}
>
<Combobox.Options className="absolute mt-1 max-h-44 w-full bg-white overflow-auto rounded-md py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" static>
{filtered.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 font-mono text-xs text-red-500">
(╯°□°)╯︵ ┻━┻
</div>
) : (
filtered.map((space) => (
<Combobox.Option
key={space.did()}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active ? 'bg-teal-600 text-white' : 'text-gray-900'
}`
}
value={space}
>
{({ selected, active }) => (
<>
<span
className={`block truncate ${
selected ? 'font-medium' : 'font-normal'
}`}
>
{space.name() || space.did()}
</span>
{selected ? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? 'text-white' : 'text-teal-600'
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</div>
)
}
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './UploadsList'
export * from './Authenticator'
export * from './W3Upload'
export * from './SpacePicker'
export * from './SpaceFinder'
export * from './providers/W3API'
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.