chore: use select/dropdown instead of popover

pull/4833/head
Steven 4 months ago
parent d32924ec48
commit c1708df7a2

@ -40,7 +40,7 @@ const HomeSidebar = observer((props: Props) => {
return ( return (
<aside <aside
className={cn( className={cn(
"relative w-full h-full overflow-auto flex flex-col justify-start items-start bg-sidebar text-sidebar-foreground", "relative w-full h-full overflow-auto flex flex-col justify-start items-start bg-background text-sidebar-foreground",
props.className, props.className,
)} )}
> >

@ -1,7 +1,7 @@
import { CheckSquareIcon, Code2Icon, SquareSlashIcon } from "lucide-react"; import { CheckSquareIcon, Code2Icon, SquareSlashIcon } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../ui/dropdown-menu";
import { EditorRefActions } from "../Editor"; import { EditorRefActions } from "../Editor";
interface Props { interface Props {
@ -60,41 +60,33 @@ const MarkdownMenu = (props: Props) => {
}; };
return ( return (
<Popover> <DropdownMenu>
<PopoverTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon"> <Button variant="ghost" size="icon">
<SquareSlashIcon className="size-5" /> <SquareSlashIcon className="size-5" />
</Button> </Button>
</PopoverTrigger> </DropdownMenuTrigger>
<PopoverContent align="start" className="text-sm p-1"> <DropdownMenuContent align="start">
<div className="flex flex-col text-sm gap-0.5"> <DropdownMenuItem onClick={handleCodeBlockClick}>
<button <Code2Icon className="w-4 h-auto text-muted-foreground" />
onClick={handleCodeBlockClick} {t("markdown.code-block")}
className="flex items-center gap-2 px-2 py-1 text-left text-foreground hover:bg-background outline-none rounded" </DropdownMenuItem>
<DropdownMenuItem onClick={handleCheckboxClick}>
<CheckSquareIcon className="w-4 h-auto text-muted-foreground" />
{t("markdown.checkbox")}
</DropdownMenuItem>
<div className="px-2 -mt-1">
<a
className="text-xs text-primary hover:underline"
href="https://www.usememos.com/docs/getting-started/content-syntax"
target="_blank"
rel="noopener noreferrer"
> >
<Code2Icon className="w-4 h-auto" /> {t("markdown.content-syntax")}
<span>{t("markdown.code-block")}</span> </a>
</button>
<button
onClick={handleCheckboxClick}
className="flex items-center gap-2 px-2 py-1 text-left text-foreground hover:bg-background outline-none rounded"
>
<CheckSquareIcon className="w-4 h-auto" />
<span>{t("markdown.checkbox")}</span>
</button>
<div className="pl-2">
<a
className="text-xs text-primary hover:underline"
href="https://www.usememos.com/docs/getting-started/content-syntax"
target="_blank"
rel="noopener noreferrer"
>
{t("markdown.content-syntax")}
</a>
</div>
</div> </div>
</PopoverContent> </DropdownMenuContent>
</Popover> </DropdownMenu>
); );
}; };

@ -1,8 +1,5 @@
import { ChevronDownIcon } from "lucide-react";
import { useState } from "react";
import VisibilityIcon from "@/components/VisibilityIcon"; import VisibilityIcon from "@/components/VisibilityIcon";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { Visibility } from "@/types/proto/api/v1/memo_service"; import { Visibility } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
@ -10,13 +7,11 @@ interface Props {
value: Visibility; value: Visibility;
onChange: (visibility: Visibility) => void; onChange: (visibility: Visibility) => void;
onOpenChange?: (open: boolean) => void; onOpenChange?: (open: boolean) => void;
className?: string;
} }
const VisibilitySelector = (props: Props) => { const VisibilitySelector = (props: Props) => {
const { value, onChange } = props; const { value, onChange } = props;
const t = useTranslate(); const t = useTranslate();
const [open, setOpen] = useState(false);
const visibilityOptions = [ const visibilityOptions = [
{ value: Visibility.PRIVATE, label: t("memo.visibility.private") }, { value: Visibility.PRIVATE, label: t("memo.visibility.private") },
@ -24,53 +19,26 @@ const VisibilitySelector = (props: Props) => {
{ value: Visibility.PUBLIC, label: t("memo.visibility.public") }, { value: Visibility.PUBLIC, label: t("memo.visibility.public") },
]; ];
const currentOption = visibilityOptions.find((option) => option.value === value);
const handleSelect = (visibility: Visibility) => {
onChange(visibility);
handleOpenChange(false);
};
const handleOpenChange = (open: boolean) => { const handleOpenChange = (open: boolean) => {
setOpen(open);
if (props.onOpenChange) { if (props.onOpenChange) {
props.onOpenChange(open); props.onOpenChange(open);
} }
}; };
return ( return (
<Popover open={open} onOpenChange={handleOpenChange}> <Select value={value.toString()} onValueChange={onChange} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild> <SelectTrigger size="xs" className="!bg-background">
<button <SelectValue />
className={cn( </SelectTrigger>
`flex items-center justify-center gap-1 px-0.5 text-xs rounded hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 transition-colors`, <SelectContent align="end">
props.className, {visibilityOptions.map((option) => (
)} <SelectItem key={option.value} value={option.value.toString()}>
type="button" <VisibilityIcon className="size-3.5" visibility={option.value} />
> {option.label}
<VisibilityIcon className="w-3 h-3" visibility={value} /> </SelectItem>
<span>{currentOption?.label}</span> ))}
<ChevronDownIcon className="w-3 h-3 opacity-60" /> </SelectContent>
</button> </Select>
</PopoverTrigger>
<PopoverContent className="p-1!" align="end" sideOffset={2} alignOffset={-4}>
<div className="flex flex-col gap-0.5">
{visibilityOptions.map((option) => (
<button
key={option.value}
onClick={() => handleSelect(option.value)}
className={cn(
`flex items-center gap-1 px-1 py-1 text-xs text-left hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 rounded transition-colors`,
option.value === value ? "bg-muted" : "",
)}
>
<VisibilityIcon className="w-3 h-3" visibility={option.value} />
<span>{option.label}</span>
</button>
))}
</div>
</PopoverContent>
</Popover>
); );
}; };

@ -530,7 +530,7 @@ const MemoEditor = observer((props: Props) => {
</div> </div>
<div <div
className={cn( className={cn(
"absolute right-1 top-1 opacity-60", "absolute right-1 top-1",
"flex flex-row justify-end items-center gap-1", "flex flex-row justify-end items-center gap-1",
"invisible group-focus-within:visible group-hover:visible hover:visible focus-within:visible", "invisible group-focus-within:visible group-hover:visible hover:visible focus-within:visible",
(isVisibilitySelectorOpen || memoName) && "visible", (isVisibilitySelectorOpen || memoName) && "visible",

@ -33,15 +33,15 @@ const SearchBar = observer(() => {
return ( return (
<div className="relative w-full h-auto flex flex-row justify-start items-center"> <div className="relative w-full h-auto flex flex-row justify-start items-center">
<SearchIcon className="absolute left-2 w-4 h-auto opacity-40 text-muted-foreground" /> <SearchIcon className="absolute left-2 w-4 h-auto opacity-40 text-sidebar-foreground" />
<input <input
className={cn("w-full text-muted-foreground leading-6 bg-muted border border-border text-sm rounded-lg p-1 pl-8 outline-0")} className={cn("w-full text-sidebar-foreground leading-6 bg-sidebar border border-border text-sm rounded-lg p-1 pl-8 outline-0")}
placeholder={t("memo.search-placeholder")} placeholder={t("memo.search-placeholder")}
value={queryText} value={queryText}
onChange={onTextChange} onChange={onTextChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<MemoDisplaySettingMenu className="absolute right-2 top-2 text-muted-foreground" /> <MemoDisplaySettingMenu className="absolute right-2 top-2 text-sidebar-foreground" />
</div> </div>
); );
}); });

@ -41,24 +41,24 @@ const UserBanner = (props: Props) => {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start"> <DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => navigateTo(`/u/${encodeURIComponent(currentUser.username)}`)}> <DropdownMenuItem onClick={() => navigateTo(`/u/${encodeURIComponent(currentUser.username)}`)}>
<SquareUserIcon className="w-4 h-auto text-muted-foreground" /> <SquareUserIcon className="size-4 text-muted-foreground" />
<span className="truncate">{t("common.profile")}</span> {t("common.profile")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => navigateTo(Routes.ARCHIVED)}> <DropdownMenuItem onClick={() => navigateTo(Routes.ARCHIVED)}>
<ArchiveIcon className="w-4 h-auto text-muted-foreground" /> <ArchiveIcon className="size-4 text-muted-foreground" />
<span className="truncate">{t("common.archived")}</span> {t("common.archived")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => navigateTo(Routes.INBOX)}> <DropdownMenuItem onClick={() => navigateTo(Routes.INBOX)}>
<BellIcon className="w-4 h-auto text-muted-foreground" /> <BellIcon className="size-4 text-muted-foreground" />
<span className="truncate">{t("common.inbox")}</span> {t("common.inbox")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => navigateTo(Routes.SETTING)}> <DropdownMenuItem onClick={() => navigateTo(Routes.SETTING)}>
<SettingsIcon className="w-4 h-auto text-muted-foreground" /> <SettingsIcon className="size-4 text-muted-foreground" />
<span className="truncate">{t("common.settings")}</span> {t("common.settings")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={handleSignOut}> <DropdownMenuItem onClick={handleSignOut}>
<LogOutIcon className="w-4 h-auto text-muted-foreground" /> <LogOutIcon className="size-4 text-muted-foreground" />
<span className="truncate">{t("common.sign-out")}</span> {t("common.sign-out")}
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

@ -21,14 +21,14 @@ function SelectTrigger({
children, children,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & { }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"; size?: "xs" | "sm" | "default";
}) { }) {
return ( return (
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
data-slot="select-trigger" data-slot="select-trigger"
data-size={size} data-size={size}
className={cn( className={cn(
"border-border data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-2 py-1 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=sm]:h-7 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "border-border data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between rounded-md border bg-transparent text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=default]:gap-2 data-[size=default]:px-2 data-[size=default]:py-1 data-[size=sm]:h-7 data-[size=sm]:gap-2 data-[size=sm]:px-2 data-[size=sm]:py-1 data-[size=xs]:h-6 data-[size=xs]:gap-1 data-[size=xs]:px-1 data-[size=xs]:py-0.5 data-[size=xs]:text-xs *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
)} )}
{...props} {...props}

@ -162,7 +162,7 @@
"visibility": { "visibility": {
"disabled": "Public memos are disabled", "disabled": "Public memos are disabled",
"private": "Private", "private": "Private",
"protected": "Workspace", "protected": "Protected",
"public": "Public" "public": "Public"
}, },
"list": "List", "list": "List",

@ -122,7 +122,7 @@
"visibility": { "visibility": {
"disabled": "Memo publik dinonaktifkan", "disabled": "Memo publik dinonaktifkan",
"private": "Pribadi", "private": "Pribadi",
"protected": "Workspace", "protected": "Protected",
"public": "Publik" "public": "Publik"
} }
}, },

Loading…
Cancel
Save