Skip to content

Commit

Permalink
feat: mirror address supports drop-down selection and input ([#51](#51))
Browse files Browse the repository at this point in the history
Signed-off-by: The1111mp <[email protected]>
  • Loading branch information
1111mp committed Feb 5, 2024
1 parent 14b97a8 commit e057d62
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 37 deletions.
20 changes: 20 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -438,5 +438,25 @@
"No-results": {
"message": "No results.",
"description": "The text of the No-results"
},
"Asc": {
"message": "Asc",
"description": "The text of the Asc"
},
"Desc": {
"message": "Desc",
"description": "The text of the Desc"
},
"Mirror-Tip": {
"message": "Please select or input the appropriate mirror url according to your region.",
"description": "The text of the Mirror-Tip"
},
"Default": {
"message": "Default",
"description": "The text of the Default"
},
"Custom": {
"message": "Custom",
"description": "The text of the Custom"
}
}
20 changes: 20 additions & 0 deletions _locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -438,5 +438,25 @@
"No-results": {
"message": "无记录。",
"description": "The text of the No-results"
},
"Asc": {
"message": "升序",
"description": "The text of the Asc"
},
"Desc": {
"message": "降序",
"description": "The text of the Desc"
},
"Mirror-Tip": {
"message": "请根据您所在的地区选择或者输入适合的镜像地址。",
"description": "The text of the Mirror-Tip"
},
"Default": {
"message": "默认",
"description": "The text of the Default"
},
"Custom": {
"message": "自定义",
"description": "The text of the Custom"
}
}
134 changes: 134 additions & 0 deletions src/renderer/src/components/ui/auto-complete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { memo, useCallback, useRef, useState } from "react";
import {
Command,
CommandInput,
CommandGroup,
CommandList,
CommandItem,
CommandEmpty
} from "./command";
import { motion, AnimatePresence } from "framer-motion";
import { CheckIcon } from "@radix-ui/react-icons";
import { useI18n } from "@renderer/app-context";

type AutoCompleteProps = {
value?: string;
options?: string[];
placeholder?: string;
shouldFilter?: boolean;
onChange?: (value: string) => void;
};

const DefMirrors = ["https://nodejs.org/dist", "https://npmmirror.com/mirrors/node"];

const AutoComplete: React.FC<AutoCompleteProps> = memo(
({ value: valueProp = "", options = [], placeholder = "", shouldFilter = true, onChange }) => {
const [open, setOpen] = useState<boolean>(false);
const [value, setValue] = useState<string>(valueProp);

const input = useRef<HTMLInputElement>(null);
const i18n = useI18n();

const onKeyDown = useCallback(
(evt: React.KeyboardEvent<HTMLDivElement>) => {
evt.stopPropagation();

if (!input.current) return;

switch (evt.key) {
case "Enter": {
if (![...DefMirrors, ...options].includes(value)) {
evt.preventDefault();
}
}
case "Escape": {
input.current?.blur();
}
}
},
[value, options]
);

const onValueChange = useCallback(
(value: string) => {
setValue(value);
onChange?.(value);
},
[onChange]
);

const onSelect = useCallback(
(value: string) => {
setValue(value);
onChange?.(value);

setTimeout(() => {
input.current?.blur();
});
},
[onChange]
);

return (
<Command shouldFilter={shouldFilter} className="overflow-visible" onKeyDown={onKeyDown}>
<CommandInput
ref={input}
value={value}
placeholder={placeholder}
onFocus={() => setOpen(true)}
onBlur={() => setOpen(false)}
onValueChange={onValueChange}
/>
<div className="relative mt-1">
<AnimatePresence>
{open && (
<motion.div
className="absolute top-0 w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md"
initial={{ opacity: 0, translateY: "-0.5rem" }}
animate={{ opacity: 1, translateY: "0" }}
exit={{ opacity: 0, translateY: "-0.5rem" }}
transition={{ duration: 0.3 }}
>
<CommandList className="max-h-56 [overflow:overlay]">
<CommandEmpty>No results found.</CommandEmpty>
{options.length ? (
<CommandGroup heading={i18n("Custom")}>
{options.map((optValue) => (
<CommandItem
key={optValue}
value={optValue}
title={optValue}
className="gap-1"
onSelect={onSelect}
>
<span className="flex-1 truncate">{optValue}</span>
{optValue === value ? <CheckIcon /> : null}
</CommandItem>
))}
</CommandGroup>
) : null}
<CommandGroup heading={i18n("Default")}>
{DefMirrors.map((optValue) => (
<CommandItem
key={optValue}
value={optValue}
title={optValue}
className="gap-1"
onSelect={onSelect}
>
<span className="flex-1 truncate">{optValue}</span>
{optValue === value ? <CheckIcon /> : null}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</motion.div>
)}
</AnimatePresence>
</div>
</Command>
);
}
);

export { DefMirrors, AutoComplete, type AutoCompleteProps };
56 changes: 30 additions & 26 deletions src/renderer/src/components/ui/command.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import * as React from "react";
import { type DialogProps } from "@radix-ui/react-dialog";
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
import { forwardRef } from "react";
import { Command as CommandPrimitive } from "cmdk";

import { cn } from "@renderer/lib/utils";
import { Dialog, DialogContent } from "./dialog";
// import { Dialog, DialogContent } from "src/renderer/src/components/ui/dialog";

const Command = React.forwardRef<
import { type DialogProps } from "@radix-ui/react-dialog";

const Command = forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
Expand Down Expand Up @@ -36,26 +35,31 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
);
};

const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
/>
</div>
));
type CommandInputProps = React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> & {
icon?: React.ReactNode;
};

const CommandInput = forwardRef<React.ElementRef<typeof CommandPrimitive.Input>, CommandInputProps>(
({ className, icon = null, ...props }, ref) => {
return (
<div className="flex items-center gap-2 border rounded-md px-3" cmdk-input-wrapper="">
{icon}
<CommandPrimitive.Input
ref={ref}
className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
/>
</div>
);
}
);

CommandInput.displayName = CommandPrimitive.Input.displayName;

const CommandList = React.forwardRef<
const CommandList = forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
Expand All @@ -68,7 +72,7 @@ const CommandList = React.forwardRef<

CommandList.displayName = CommandPrimitive.List.displayName;

const CommandEmpty = React.forwardRef<
const CommandEmpty = forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
Expand All @@ -77,7 +81,7 @@ const CommandEmpty = React.forwardRef<

CommandEmpty.displayName = CommandPrimitive.Empty.displayName;

const CommandGroup = React.forwardRef<
const CommandGroup = forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
Expand All @@ -93,7 +97,7 @@ const CommandGroup = React.forwardRef<

CommandGroup.displayName = CommandPrimitive.Group.displayName;

const CommandSeparator = React.forwardRef<
const CommandSeparator = forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
Expand All @@ -105,7 +109,7 @@ const CommandSeparator = React.forwardRef<
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;

const CommandItem = React.forwardRef<
const CommandItem = forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger
} from "./dropdown-menu";
import { useI18n } from "@renderer/app-context";

interface DataTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>;
Expand All @@ -25,6 +26,8 @@ export function DataTableColumnSortHeader<TData, TValue>({
return <div className={cn(className)}>{title}</div>;
}

const i18n = useI18n();

return (
<div className={cn("flex items-center space-x-2", className)}>
<DropdownMenu>
Expand All @@ -48,16 +51,16 @@ export function DataTableColumnSortHeader<TData, TValue>({
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUpIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Asc
{i18n("Asc")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDownIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Desc
{i18n("Desc")}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.clearSorting()}>
<EyeNoneIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Cancel
{i18n("Cancel")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/components/ui/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./alert-dialog";
export * from "./auto-complete";
export * from "./badge";
export * from "./button";
export * from "./command";
Expand Down
Loading

0 comments on commit e057d62

Please sign in to comment.