Skip to content

Commit

Permalink
feat: redesign select dropdown (#4727)
Browse files Browse the repository at this point in the history
  • Loading branch information
bigint authored Mar 10, 2024
2 parents 267a5df + 56296bd commit 20e8bf2
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ const AmountConfig: FC<AmountConfigProps> = ({
<div className="w-5/6">
<div className="label">Select currency</div>
<Select
onChange={(e) => {
onChange={(value) => {
setCollectType({
amount: {
currency: e.target.value,
currency: value,
value: collectModule.amount?.value
}
});
Expand Down
18 changes: 10 additions & 8 deletions apps/web/src/components/Composer/LicensePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { MetadataLicenseType } from '@lens-protocol/metadata';
import type { FC } from 'react';

import { ExclamationCircleIcon } from '@heroicons/react/24/outline';
Expand All @@ -10,7 +9,7 @@ import isFeatureAvailable from '@lib/isFeatureAvailable';
import { usePublicationLicenseStore } from 'src/store/non-persisted/publication/usePublicationLicenseStore';

const LicensePicker: FC = () => {
const { setLicense } = usePublicationLicenseStore();
const { license, setLicense } = usePublicationLicenseStore();

if (!isFeatureAvailable(FeatureFlag.Staff)) {
return null;
Expand All @@ -24,14 +23,17 @@ const LicensePicker: FC = () => {
<div className="ld-text-gray-500 text-sm">What's this?</div>
</div>
<Select
onChange={(e) => setLicense(e.target.value as MetadataLicenseType)}
onChange={(value) =>
setLicense(value as PublicationMetadataLicenseType)
}
options={
Object.values(PublicationMetadataLicenseType)
.filter((license) => getAssetLicense(license))
.map((license) => ({
label: getAssetLicense(license) as string,
selected: true,
value: license
.filter((type) => getAssetLicense(type))
.map((type) => ({
helper: getAssetLicense(type)?.helper as string,
label: getAssetLicense(type)?.label as string,
selected: license === type,
value: type
})) as any
}
/>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/Publication/Metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Metadata: FC<MetadataProps> = ({ metadata }) => {
<div className="flex items-center space-x-2">
<ScaleIcon className="size-4 min-w-max" />
<div>
Licence: <b>{license}</b>
Licence: <b>{license.label}</b>
</div>
</div>
</Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,17 @@ const TipOpenActionModule: FC<TipOpenActionModuleProps> = ({
<div className="flex w-5/12 flex-col items-end space-y-1">
<Select
defaultValue={DEFAULT_COLLECT_TOKEN}
onChange={(e) => {
setTip({ ...tip, currency: e.target.value });
onChange={(value) => {
setTip({ ...tip, currency: value });
setSelectedCurrency(
allowedTokens?.find(
(token) => token.contractAddress === e.target.value
(token) => token.contractAddress === value
) as AllowedToken
);
}}
options={allowedTokens?.map((token) => ({
label: token.name,
selected: token.contractAddress === tip.currency,
value: token.contractAddress
}))}
/>
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/Settings/Account/SuperFollow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,10 @@ const SuperFollow: FC = () => {
? currentProfile?.followModule?.amount.asset.contract.address
: undefined
}
onChange={(e) => setSelectedCurrency(e.target.value)}
onChange={(value) => setSelectedCurrency(value)}
options={allowedTokens?.map((token) => ({
label: token.name,
selected: token.contractAddress === selectedCurrency,
value: token.contractAddress
}))}
/>
Expand Down
9 changes: 7 additions & 2 deletions apps/web/src/components/Settings/Allowance/CollectModules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const getAllowancePayload = (currency: string) => {

const CollectModules: FC = () => {
const { currentProfile } = useProfileStore();
const [selectedCurrency, setSelectedCurrency] = useState(
DEFAULT_COLLECT_TOKEN
);
const [currencyLoading, setCurrencyLoading] = useState(false);

const {
Expand Down Expand Up @@ -68,15 +71,17 @@ const CollectModules: FC = () => {
<div className="divider my-5" />
<div className="label mt-6">Select currency</div>
<Select
onChange={(e) => {
onChange={(value) => {
setCurrencyLoading(true);
setSelectedCurrency(value);
refetch({
request: getAllowancePayload(e.target.value)
request: getAllowancePayload(value)
}).finally(() => setCurrencyLoading(false));
}}
options={
allowedTokens?.map((token) => ({
label: token.name,
selected: token.contractAddress === selectedCurrency,
value: token.contractAddress
})) || [{ label: 'Loading...', value: 'Loading...' }]
}
Expand Down
9 changes: 7 additions & 2 deletions apps/web/src/components/Settings/Allowance/OpenActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const getAllowancePayload = (currency: string) => {

const OpenActions: FC = () => {
const { currentProfile } = useProfileStore();
const [selectedCurrency, setSelectedCurrency] = useState(
DEFAULT_COLLECT_TOKEN
);
const [currencyLoading, setCurrencyLoading] = useState(false);

const {
Expand Down Expand Up @@ -62,15 +65,17 @@ const OpenActions: FC = () => {
<div className="divider my-5" />
<div className="label mt-6">Select currency</div>
<Select
onChange={(e) => {
onChange={(value) => {
setCurrencyLoading(true);
setSelectedCurrency(value);
refetch({
request: getAllowancePayload(e.target.value)
request: getAllowancePayload(value)
}).finally(() => setCurrencyLoading(false));
}}
options={
allowedTokens?.map((token) => ({
label: token.name,
selected: token.contractAddress === selectedCurrency,
value: token.contractAddress
})) || [{ label: 'Loading...', value: 'Loading...' }]
}
Expand Down
6 changes: 2 additions & 4 deletions apps/web/src/components/Shared/Modal/ReportProfile/Reason.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ const Reason: FC<ReasonProps> = ({
<div>
<div className="label">Type</div>
<Select
onChange={(e) => setType(e.target.value)}
onChange={(value) => setType(value)}
options={[
{
disabled: true,
label: 'Select type',
selected: true,
value: 'Select type'
},
{
Expand All @@ -49,12 +48,11 @@ const Reason: FC<ReasonProps> = ({
<div>
<div className="label">Reason</div>
<Select
onChange={(e) => setSubReason(e.target.value)}
onChange={(value) => setSubReason(value)}
options={[
{
disabled: true,
label: 'Select reason',
selected: true,
value: 'Select reason'
},
...(type === 'fraudReason'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ const Reason: FC<ReasonProps> = ({
<div>
<div className="label">Type</div>
<Select
onChange={(e) => setType(e.target.value)}
onChange={(value) => setType(value)}
options={[
{
disabled: true,
label: 'Select type',
selected: true,
value: 'Select type'
},
{
Expand Down Expand Up @@ -61,12 +60,11 @@ const Reason: FC<ReasonProps> = ({
<div>
<div className="label">Reason</div>
<Select
onChange={(e) => setSubReason(e.target.value)}
onChange={(value) => setSubReason(value)}
options={[
{
disabled: true,
label: 'Select reason',
selected: true,
value: 'Select reason'
},
...(type === 'illegalReason'
Expand Down
13 changes: 6 additions & 7 deletions apps/web/src/components/Staff/Users/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,13 @@ const List: FC = () => {
value={value}
/>
<Select
className="w-auto"
className="w-72"
defaultValue={orderBy}
onChange={(e) =>
setOrderBy(e.target.value as ExploreProfilesOrderByType)
}
options={Object.values(ExploreProfilesOrderByType).map((orderBy) => ({
label: orderBy,
value: orderBy
onChange={(value) => setOrderBy(value as ExploreProfilesOrderByType)}
options={Object.values(ExploreProfilesOrderByType).map((type) => ({
label: type,
selected: orderBy === type,
value: type
}))}
/>
<button onClick={onRefetch} type="button">
Expand Down
24 changes: 18 additions & 6 deletions apps/web/src/lib/getAssetLicense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,30 @@ import { PublicationMetadataLicenseType } from '@hey/lens';

const getAssetLicense = (
licenseId: Maybe<PublicationMetadataLicenseType> | undefined
): null | string => {
): {
helper: string;
label: string;
} | null => {
if (!licenseId) {
return null;
}

switch (licenseId) {
case PublicationMetadataLicenseType.Cco:
return 'Anyone can use it, zero restrictions';
case PublicationMetadataLicenseType.TbnlNcNdNplLedger:
return 'NFT holder granted commercial rights';
case PublicationMetadataLicenseType.TbnlNcNdNplLegal:
return 'NFT holder granted personal rights';
return {
helper: 'CC0',
label: 'Public domain - zero restrictions'
};
case PublicationMetadataLicenseType.TbnlCDNplLegal:
return {
helper: 'TBNL-C-D-NPL-Legal',
label: 'NFT owner has full commercial rights'
};
case PublicationMetadataLicenseType.TbnlNcDNplLegal:
return {
helper: 'TBNL-NC-D-NPL-Legal',
label: 'NFT owner has personal use rights'
};
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { MetadataLicenseType } from '@lens-protocol/metadata';
import type { PublicationMetadataLicenseType } from '@hey/lens';

import { createTrackedSelector } from 'react-tracked';
import { create } from 'zustand';

interface PublicationLicenseState {
license: MetadataLicenseType | null;
setLicense: (license: MetadataLicenseType | null) => void;
license: null | PublicationMetadataLicenseType;
setLicense: (license: null | PublicationMetadataLicenseType) => void;
}

const store = create<PublicationLicenseState>((set) => ({
Expand Down
87 changes: 62 additions & 25 deletions packages/ui/src/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,80 @@
import type { ComponentProps } from 'react';

import { forwardRef, useId } from 'react';
import { Listbox, Transition } from '@headlessui/react';
import { CheckCircleIcon, ChevronDownIcon } from '@heroicons/react/24/solid';
import { forwardRef, Fragment } from 'react';

import cn from '../cn';

interface SelectProps extends ComponentProps<'select'> {
interface SelectProps {
className?: string;
label?: string;
defaultValue?: string;
onChange: (value: any) => any;
options?: {
disabled?: boolean;
helper?: string;
label: string;
selected?: boolean;
value: number | string;
}[];
}

export const Select = forwardRef<HTMLSelectElement, SelectProps>(
function Select({ className = '', label, options, ...rest }, ref) {
const id = useId();
function Select({ className, defaultValue, onChange, options }) {
const selected = options?.find((option) => option.selected) || options?.[0];

return (
<label htmlFor={id}>
{label ? <div className="label">{label}</div> : null}
<select
className={cn(
'w-full rounded-xl border border-gray-300 bg-white outline-none focus:border-gray-500 focus:ring-gray-400 dark:border-gray-700 dark:bg-gray-800',
className
)}
id={id}
ref={ref}
{...rest}
>
{options?.map(({ disabled, label, selected, value }) => (
<option disabled={disabled} selected={selected} value={value}>
{label}
</option>
))}
</select>
</label>
<Listbox onChange={onChange} value={defaultValue || selected?.value}>
<div className="relative">
<Listbox.Button
className={cn(
'flex w-full items-center justify-between space-x-3 rounded-xl border border-gray-300 bg-white px-3 py-2 text-left outline-none focus:border-gray-500 focus:ring-gray-400 dark:border-gray-700 dark:bg-gray-800',
className
)}
>
<span>{selected?.label}</span>
<ChevronDownIcon className="mr-1 size-5 text-gray-400" />
</Listbox.Button>
<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"
>
<Listbox.Options className="absolute z-[5] mt-2 max-h-60 w-full overflow-y-scroll rounded-xl border bg-white shadow-sm focus:outline-none dark:border-gray-700 dark:bg-gray-900">
{options?.map((option, id) => (
<Listbox.Option
className={({ active }: { active: boolean }) =>
cn(
{ 'dropdown-active': active },
'm-2 cursor-pointer rounded-lg'
)
}
key={id}
value={option.value}
>
{({ selected }) => (
<div className="flex flex-col space-y-0 px-2 py-1.5">
<span className="flex w-full items-center justify-between space-x-3 text-gray-700 dark:text-gray-200">
<span className="block truncate">{option.label}</span>
{selected ? (
<CheckCircleIcon className="size-5" />
) : null}
</span>
{option.helper ? (
<span className="ld-text-gray-500 text-xs">
{option.helper}
</span>
) : null}
</div>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
);
}
);

1 comment on commit 20e8bf2

@vercel
Copy link

@vercel vercel bot commented on 20e8bf2 Mar 10, 2024

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

web – ./apps/web

web-git-main-heyxyz.vercel.app
heyxyz.vercel.app
web-heyxyz.vercel.app
hey.xyz

Please sign in to comment.