mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 23:56:36 +08:00
fix(frontend): prevent multiselect overflow and normalize card spacing (#152)
## Summary - fix multi-select trigger overflow in shared select bridge by making trigger/value flex children shrink correctly - harden grouped assignment summaries against long selected-value text wrapping overflow - normalize card header/body spacing and node card IP row height so card layouts stay visually consistent across modules ## Verification - ran `npm run build` in `vite-frontend` successfully - checked diagnostics on changed frontend files (no diagnostics)
This commit is contained in:
@@ -1260,9 +1260,9 @@ export default function ForwardPage() {
|
||||
return (
|
||||
<Card
|
||||
key={forward.id}
|
||||
className="group shadow-sm border border-divider hover:shadow-md transition-shadow duration-200"
|
||||
className="group shadow-sm border border-divider hover:shadow-md transition-shadow duration-200 overflow-hidden"
|
||||
>
|
||||
<CardHeader className="pb-2">
|
||||
<CardHeader className="pb-2 md:pb-2">
|
||||
<div className="flex justify-between items-start w-full">
|
||||
{selectMode && (
|
||||
<Checkbox
|
||||
@@ -1319,7 +1319,7 @@ export default function ForwardPage() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody className="pt-0 pb-3">
|
||||
<CardBody className="pt-0 pb-3 md:pt-0 md:pb-3">
|
||||
<div className="space-y-2">
|
||||
{/* 地址信息 */}
|
||||
<div className="space-y-1">
|
||||
@@ -1667,7 +1667,7 @@ export default function ForwardPage() {
|
||||
key={userGroup.userId || "unknown"}
|
||||
className="shadow-sm border border-divider w-full overflow-hidden"
|
||||
>
|
||||
<CardHeader className="pb-3">
|
||||
<CardHeader className="pb-2 md:pb-2">
|
||||
<div className="flex items-center justify-between w-full min-w-0">
|
||||
<div className="flex items-center gap-3 min-w-0 flex-1">
|
||||
<div className="w-10 h-10 bg-primary-100 dark:bg-primary-900/30 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
@@ -1709,7 +1709,7 @@ export default function ForwardPage() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody className="pt-0">
|
||||
<CardBody className="pt-0 md:pt-0">
|
||||
<Accordion className="px-0" variant="splitted">
|
||||
{userGroup.tunnelGroups.map((tunnelGroup) => (
|
||||
<AccordionItem
|
||||
|
||||
@@ -779,7 +779,7 @@ export default function GroupPage() {
|
||||
>
|
||||
{(item) => <SelectItem key={item.id}>{item.name}</SelectItem>}
|
||||
</Select>
|
||||
<p className="text-xs text-default-500">
|
||||
<p className="text-xs text-default-500 break-all whitespace-normal">
|
||||
当前已选:
|
||||
{Array.from(selectedTunnelKeys)
|
||||
.map((id) => tunnelNameMap.get(Number(id)) || id)
|
||||
@@ -824,7 +824,7 @@ export default function GroupPage() {
|
||||
>
|
||||
{(item) => <SelectItem key={item.id}>{item.user}</SelectItem>}
|
||||
</Select>
|
||||
<p className="text-xs text-default-500">
|
||||
<p className="text-xs text-default-500 break-all whitespace-normal">
|
||||
当前已选:
|
||||
{Array.from(selectedUserKeys)
|
||||
.map((id) => userNameMap.get(Number(id)) || id)
|
||||
|
||||
@@ -235,9 +235,9 @@ export default function LimitPage() {
|
||||
{rules.map((rule) => (
|
||||
<Card
|
||||
key={rule.id}
|
||||
className="shadow-sm border border-gray-200 dark:border-gray-700"
|
||||
className="shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"
|
||||
>
|
||||
<CardHeader className="pb-3">
|
||||
<CardHeader className="pb-2 md:pb-2">
|
||||
<div className="flex justify-between items-start w-full">
|
||||
<div>
|
||||
<h3 className="font-semibold text-foreground">
|
||||
@@ -253,7 +253,7 @@ export default function LimitPage() {
|
||||
</Chip>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody className="pt-0">
|
||||
<CardBody className="pt-0 pb-3 md:pt-0 md:pb-3">
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-small text-default-600">
|
||||
|
||||
@@ -1155,7 +1155,7 @@ export default function NodePage() {
|
||||
<span className="text-default-600 flex-shrink-0">
|
||||
IP
|
||||
</span>
|
||||
<div className="text-right text-xs min-w-0 flex-1 ml-2">
|
||||
<div className="text-right text-xs min-w-0 flex-1 ml-2 min-h-[2.125rem]">
|
||||
{node.serverIpV4?.trim() ||
|
||||
node.serverIpV6?.trim() ? (
|
||||
<div className="space-y-0.5">
|
||||
|
||||
@@ -420,7 +420,7 @@ export default function PanelSharingPage() {
|
||||
key={share.id}
|
||||
className="border border-divider shadow-sm"
|
||||
>
|
||||
<CardHeader className="flex justify-between">
|
||||
<CardHeader className="flex justify-between pb-2 md:pb-2">
|
||||
<h3 className="font-bold">{share.name}</h3>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
@@ -447,7 +447,7 @@ export default function PanelSharingPage() {
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody className="text-sm space-y-2">
|
||||
<CardBody className="text-sm space-y-2 pt-0 md:pt-0">
|
||||
<p>
|
||||
端口范围: {share.portRangeStart} -{" "}
|
||||
{share.portRangeEnd}
|
||||
@@ -535,13 +535,13 @@ export default function PanelSharingPage() {
|
||||
key={node.nodeId}
|
||||
className="border border-divider shadow-sm"
|
||||
>
|
||||
<CardHeader className="flex justify-between">
|
||||
<CardHeader className="flex justify-between pb-2 md:pb-2">
|
||||
<h3 className="font-bold">{node.nodeName}</h3>
|
||||
<span className="text-xs text-default-500">
|
||||
绑定 {node.activeBindingNum || 0}
|
||||
</span>
|
||||
</CardHeader>
|
||||
<CardBody className="text-sm space-y-2">
|
||||
<CardBody className="text-sm space-y-2 pt-0 md:pt-0">
|
||||
{node.syncError && (
|
||||
<div className="px-2 py-1.5 rounded-md bg-warning-50 dark:bg-warning-100/10 text-warning-700 dark:text-warning-400 text-xs">
|
||||
{node.syncError === "provider_share_deleted"
|
||||
|
||||
@@ -740,9 +740,9 @@ export default function TunnelPage() {
|
||||
{(listeners) => (
|
||||
<Card
|
||||
key={tunnel.id}
|
||||
className="group shadow-sm border border-divider hover:shadow-md transition-shadow duration-200"
|
||||
className="group shadow-sm border border-divider hover:shadow-md transition-shadow duration-200 overflow-hidden"
|
||||
>
|
||||
<CardHeader className="pb-2">
|
||||
<CardHeader className="pb-2 md:pb-2">
|
||||
<div className="flex justify-between items-start w-full">
|
||||
{selectMode && (
|
||||
<Checkbox
|
||||
@@ -784,7 +784,7 @@ export default function TunnelPage() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody className="pt-0 pb-3">
|
||||
<CardBody className="pt-0 pb-3 md:pt-0 md:pb-3">
|
||||
<div className="space-y-3">
|
||||
{/* 拓扑结构 */}
|
||||
<div className="pt-2 border-t border-divider">
|
||||
|
||||
@@ -688,9 +688,9 @@ export default function UserPage() {
|
||||
return (
|
||||
<Card
|
||||
key={user.id}
|
||||
className="shadow-sm border border-divider hover:shadow-md transition-shadow duration-200"
|
||||
className="shadow-sm border border-divider hover:shadow-md transition-shadow duration-200 overflow-hidden"
|
||||
>
|
||||
<CardHeader className="pb-2">
|
||||
<CardHeader className="pb-2 md:pb-2">
|
||||
<div className="flex justify-between items-start w-full">
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-foreground truncate text-sm">
|
||||
@@ -713,7 +713,7 @@ export default function UserPage() {
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody className="pt-0 pb-3">
|
||||
<CardBody className="pt-0 pb-3 md:pt-0 md:pb-3">
|
||||
<div className="space-y-2">
|
||||
{/* 流量信息 */}
|
||||
<div className="space-y-1.5">
|
||||
|
||||
@@ -16,5 +16,5 @@ export function CardHeader({ className, ...props }: React.ComponentProps<"div">)
|
||||
}
|
||||
|
||||
export function CardBody({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return <CardContent className={cn("p-4 md:p-6", className)} {...props} />;
|
||||
return <CardContent className={cn("p-4 pt-0 md:p-6 md:pt-0", className)} {...props} />;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ export function Select<T>({
|
||||
{selectionMode === "multiple" ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full rounded-md border border-input bg-background shadow-sm",
|
||||
"w-full overflow-hidden rounded-md border border-input bg-background shadow-sm",
|
||||
isDisabled ? "cursor-not-allowed opacity-60" : "",
|
||||
className,
|
||||
)}
|
||||
@@ -250,7 +250,7 @@ export function Select<T>({
|
||||
aria-controls={`${generatedId}-listbox`}
|
||||
aria-expanded={isExpanded}
|
||||
className={cn(
|
||||
"flex w-full items-center gap-2 px-3 py-2 text-left",
|
||||
"flex w-full min-w-0 items-center gap-2 overflow-hidden px-3 py-2 text-left",
|
||||
classNames?.trigger,
|
||||
)}
|
||||
disabled={isDisabled}
|
||||
@@ -259,7 +259,7 @@ export function Select<T>({
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"block flex-1 truncate",
|
||||
"block min-w-0 flex-1 truncate",
|
||||
textSizeClass(size),
|
||||
selectedArray.length > 0 ? "text-foreground" : "text-default-500",
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user