style: tight table design

This commit is contained in:
zhom
2025-11-26 01:42:33 +04:00
parent 35ba7e2d96
commit c7c910d1ca
3 changed files with 61 additions and 38 deletions
+40 -22
View File
@@ -100,7 +100,7 @@ type TableMeta = {
profileId: string,
proxyId: string | null,
) => void | Promise<void>;
checkingProxyId: string | null;
checkingProfileId: string | null;
proxyCheckResults: Record<string, ProxyCheckResult>;
// Selection helpers
@@ -209,6 +209,7 @@ const TagsCell = React.memo<{
const [visibleCount, setVisibleCount] = React.useState<number>(
effectiveTags.length,
);
const [isFocused, setIsFocused] = React.useState(false);
React.useLayoutEffect(() => {
// Only measure when not editing this profile's tags
@@ -298,7 +299,7 @@ const TagsCell = React.memo<{
type="button"
ref={containerRef as unknown as React.RefObject<HTMLButtonElement>}
className={cn(
"flex overflow-hidden gap-1 items-center p-2.5 w-full bg-transparent rounded border-none cursor-pointer",
"flex overflow-hidden gap-1 items-center px-2 py-1 h-6 w-full bg-transparent rounded border-none cursor-pointer",
isDisabled ? "opacity-60" : "cursor-pointer hover:bg-accent/50",
)}
onClick={() => {
@@ -322,7 +323,7 @@ const TagsCell = React.memo<{
);
return (
<div className="w-48 h-full cursor-pointer">
<div className="w-48 h-6 cursor-pointer">
<Tooltip>
<TooltipTrigger asChild>{ButtonContent}</TooltipTrigger>
{hiddenCount > 0 && (
@@ -348,11 +349,14 @@ const TagsCell = React.memo<{
return (
<div
className={cn(
"w-48 h-8",
"w-48 h-6 relative",
isDisabled && "opacity-60 pointer-events-none",
)}
>
<div ref={editorRef}>
<div
ref={editorRef}
className="absolute top-0 left-0 z-50 w-48 min-h-6 bg-popover rounded-md shadow-md"
>
<MultipleSelector
value={valueOptions}
options={allOptions}
@@ -360,13 +364,22 @@ const TagsCell = React.memo<{
creatable
selectFirstItem={false}
placeholder={effectiveTags.length === 0 ? "Add tags" : ""}
className="overflow-x-hidden overflow-y-scroll h-7 bg-transparent"
badgeClassName=""
className={cn(
"bg-transparent !border-0 focus-within:!ring-0",
"[&_div:first-child]:!border-0 [&_div:first-child]:!ring-0 [&_div:first-child]:focus-within:!ring-0",
"[&_div:first-child]:!min-h-6 [&_div:first-child]:!px-2 [&_div:first-child]:!py-1",
"[&_div:first-child>div]:items-center [&_div:first-child>div]:!h-6",
"[&_input]:!ml-0 [&_input]:!mt-0 [&_input]:!px-0",
!isFocused && "[&_div:first-child>div]:justify-center",
)}
badgeClassName="shrink-0"
inputProps={{
className: "py-1",
className: "!py-0 text-sm caret-current !ml-0 !mt-0 !px-0",
onKeyDown: (e) => {
if (e.key === "Escape") setOpenTagsEditorFor(null);
},
onFocus: () => setIsFocused(true),
onBlur: () => setIsFocused(false),
}}
/>
</div>
@@ -440,9 +453,9 @@ export function ProfilesDataTable({
const [openProxySelectorFor, setOpenProxySelectorFor] = React.useState<
string | null
>(null);
const [checkingProxyId, setCheckingProxyId] = React.useState<string | null>(
null,
);
const [checkingProfileId, setCheckingProfileId] = React.useState<
string | null
>(null);
const [proxyCheckResults, setProxyCheckResults] = React.useState<
Record<string, ProxyCheckResult>
>({});
@@ -818,7 +831,7 @@ export function ProfilesDataTable({
proxyOverrides,
storedProxies,
handleProxySelection,
checkingProxyId,
checkingProfileId,
proxyCheckResults,
// Selection helpers
@@ -864,7 +877,7 @@ export function ProfilesDataTable({
proxyOverrides,
storedProxies,
handleProxySelection,
checkingProxyId,
checkingProfileId,
proxyCheckResults,
handleToggleAll,
handleCheckboxChange,
@@ -1033,7 +1046,7 @@ export function ProfilesDataTable({
size="sm"
disabled={!canLaunch || isLaunching || isStopping}
className={cn(
"cursor-pointer min-w-[70px]",
"cursor-pointer min-w-[70px] h-7",
!canLaunch && "opacity-50",
)}
onClick={() =>
@@ -1118,7 +1131,7 @@ export function ProfilesDataTable({
meta.setRenameError(null);
}
}}
className="inline-block w-30"
className="w-30 h-6 px-2 py-1 text-sm font-medium leading-none border-0 shadow-none focus-visible:ring-0"
/>
<div className="flex absolute right-0 top-full z-50 gap-1 translate-y-[30%] opacity-100 bg-black rounded-md">
<LoadingButton
@@ -1138,11 +1151,11 @@ export function ProfilesDataTable({
const display =
name.length < 14 ? (
<div className="font-medium text-left">{name}</div>
<div className="font-medium text-left leading-none">{name}</div>
) : (
<Tooltip>
<TooltipTrigger asChild>
<span>{trimName(name, 14)}</span>
<span className="leading-none">{trimName(name, 14)}</span>
</TooltipTrigger>
<TooltipContent>{name}</TooltipContent>
</Tooltip>
@@ -1160,7 +1173,7 @@ export function ProfilesDataTable({
<button
type="button"
className={cn(
"p-2 mr-auto text-left bg-transparent rounded border-none w-30",
"px-2 py-1 mr-auto text-left bg-transparent rounded border-none w-30 h-6",
isDisabled
? "opacity-60 cursor-not-allowed"
: "cursor-pointer hover:bg-accent/50",
@@ -1330,7 +1343,7 @@ export function ProfilesDataTable({
<PopoverTrigger asChild>
<span
className={cn(
"flex gap-2 items-center p-2 rounded",
"flex gap-2 items-center px-2 py-1 rounded",
isDisabled
? "opacity-60 cursor-not-allowed pointer-events-none"
: "cursor-pointer hover:bg-accent/50",
@@ -1355,7 +1368,11 @@ export function ProfilesDataTable({
</Tooltip>
{!isDisabled && (
<PopoverContent className="w-[240px] p-0" align="start">
<PopoverContent
className="w-[240px] p-0"
align="end"
sideOffset={8}
>
<Command>
<CommandInput placeholder="Search proxies..." />
<CommandList>
@@ -1408,9 +1425,10 @@ export function ProfilesDataTable({
{profileHasProxy && effectiveProxy && !isDisabled && (
<ProxyCheckButton
proxy={effectiveProxy}
checkingProxyId={meta.checkingProxyId}
profileId={profile.id}
checkingProfileId={meta.checkingProfileId}
cachedResult={meta.proxyCheckResults[effectiveProxy.id]}
setCheckingProxyId={setCheckingProxyId}
setCheckingProfileId={setCheckingProfileId}
onCheckComplete={(result) => {
setProxyCheckResults((prev) => ({
...prev,
+19 -14
View File
@@ -16,22 +16,24 @@ import type { ProxyCheckResult, StoredProxy } from "@/types";
interface ProxyCheckButtonProps {
proxy: StoredProxy;
checkingProxyId: string | null;
profileId: string;
checkingProfileId: string | null;
cachedResult?: ProxyCheckResult;
onCheckComplete?: (result: ProxyCheckResult) => void;
onCheckFailed?: (result: ProxyCheckResult) => void;
disabled?: boolean;
setCheckingProxyId?: (id: string | null) => void;
setCheckingProfileId?: (id: string | null) => void;
}
export function ProxyCheckButton({
proxy,
checkingProxyId,
profileId,
checkingProfileId,
cachedResult,
onCheckComplete,
onCheckFailed,
disabled = false,
setCheckingProxyId,
setCheckingProfileId,
}: ProxyCheckButtonProps) {
const [localResult, setLocalResult] = React.useState<
ProxyCheckResult | undefined
@@ -42,9 +44,9 @@ export function ProxyCheckButton({
}, [cachedResult]);
const handleCheck = React.useCallback(async () => {
if (checkingProxyId === proxy.id) return;
if (checkingProfileId === profileId) return;
setCheckingProxyId?.(proxy.id);
setCheckingProfileId?.(profileId);
try {
const result = await invoke<ProxyCheckResult>("check_proxy_validity", {
proxyId: proxy.id,
@@ -86,17 +88,18 @@ export function ProxyCheckButton({
setLocalResult(failedResult);
onCheckFailed?.(failedResult);
} finally {
setCheckingProxyId?.(null);
setCheckingProfileId?.(null);
}
}, [
proxy,
checkingProxyId,
profileId,
checkingProfileId,
onCheckComplete,
onCheckFailed,
setCheckingProxyId,
setCheckingProfileId,
]);
const isCurrentlyChecking = checkingProxyId === proxy.id;
const isCurrentlyChecking = checkingProfileId === profileId;
const result = localResult;
return (
@@ -117,7 +120,7 @@ export function ProxyCheckButton({
<FiCheck className="absolute bottom-[-6px] right-[-4px]" />
</span>
) : result && !result.is_valid ? (
<span className="text-red-600 text-sm"></span>
<span className="text-destructive text-sm"></span>
) : (
<FiCheck className="w-3 h-3" />
)}
@@ -135,15 +138,17 @@ export function ProxyCheckButton({
{[result.city, result.country].filter(Boolean).join(", ") ||
"Unknown"}
</p>
<p className="text-xs text-muted-foreground">IP: {result.ip}</p>
<p className="text-xs text-muted-foreground">
<p className="text-xs text-primary-foreground/70">
IP: {result.ip}
</p>
<p className="text-xs text-primary-foreground/70">
Checked {formatRelativeTime(result.timestamp)}
</p>
</div>
) : result && !result.is_valid ? (
<div>
<p>Proxy check failed</p>
<p className="text-xs text-muted-foreground">
<p className="text-xs text-primary-foreground/70">
Failed {formatRelativeTime(result.timestamp)}
</p>
</div>
+2 -2
View File
@@ -67,7 +67,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
<th
data-slot="table-head"
className={cn(
"px-2 h-10 font-medium text-left align-middle whitespace-nowrap text-foreground",
"px-2 h-8 font-medium text-left align-middle whitespace-nowrap text-foreground",
className,
)}
{...props}
@@ -79,7 +79,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
return (
<td
data-slot="table-cell"
className={cn("p-2 align-middle whitespace-nowrap", className)}
className={cn("px-2 py-1 align-middle whitespace-nowrap", className)}
{...props}
/>
);