提问人:Paul 提问时间:10/28/2023 最后编辑:Paul 更新时间:11/4/2023 访问量:271
如何在子菜单打开时保持悬停处于启用状态
how keep the hover enabled while the submenu is open
问:
我的网站上有一个简单的表格,列出了设备及其特性(在下面链接的示例中,将有表格的缩短版本)。
import "./styles.css";
import { SubMenu } from "./SubMenu";
const subMenuSlice = <SubMenu />;
const nodes = [
{
id: "0",
name: "Samsung Galaxy",
subMenu: subMenuSlice
},
{
id: "0",
name: "Iphone",
subMenu: subMenuSlice
}
];
export default function App() {
return (
<table>
<tbody>
{nodes.map((val, key) => (
<tr key={key}>
<td>{val.name}</td>
<td>{val.subMenu}</td>
</tr>
))}
</tbody>
</table>
);
}
子菜单.tsx
import { useState } from "react";
import AppsIcon from "@mui/icons-material/Apps";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import "./styles.css";
export const SubMenu = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
<DropdownMenu.Trigger>
<AppsIcon className="sss" />
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
side="bottom"
sideOffset={-30}
align="start"
alignOffset={80}
>
<button className="style-button">Edit </button>
<button className="style-button">Make </button>
<button className="style-button">Delete </button>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
};
样式.css
.sss {
visibility: hidden;
}
tr:hover .sss {
background: gray;
visibility: visible;
}
tr:hover {
background: gray;
visibility: visible;
pointer-events: initial !important;
}
.style-button:hover {
background-color: aqua;
}
https://codesandbox.io/s/romantic-rgb-5t7xkq
如您所见,当您将鼠标悬停在任何一条线上时,整条线都会变成灰色,并出现一个附加按钮。通过单击此按钮,用户将获得一个子菜单。
问题描述:问题是当用户将光标移动到子菜单时,悬停(灰色)从表格行中消失。请告诉我如何在子菜单处于活动状态(打开)时保持悬停处于启用状态
答:
我会跟踪某些状态,例如正在“悬停”的项的电流。然后,对于该项目,添加一个额外的类,并根据该类设置其样式,在您的示例中:id
应用程序.tsx
import "./styles.css";
import { SubMenu } from "./SubMenu";
import { useState } from "react";
const subMenuSlice = <SubMenu />;
const nodes = [
{
id: "0",
name: "Samsung Galaxy",
subMenu: subMenuSlice
},
{
id: "1",
name: "Iphone",
subMenu: subMenuSlice
}
];
export default function App() {
const [isHovered, setIsHovered] = useState(null);
const handleMouseEnter = (id) => {
setIsHovered(id);
};
const handleMouseLeave = () => {
setIsHovered(null);
};
return (
<table>
<tbody>
{nodes.map((val, key) => (
<tr
key={key}
onMouseEnter={() => handleMouseEnter(val.id)}
onMouseLeave={handleMouseLeave}
className={val.id === isHovered ? "hovered" : ""} // here you set the class if the id matches.
>
<td>{val.name}</td>
<td>{val.subMenu}</td>
</tr>
))}
</tbody>
</table>
);
}
样式.css
.sss {
visibility: hidden;
}
tr.hovered .sss {
background: gray;
visibility: visible;
}
tr.hovered {
background: gray;
visibility: visible;
pointer-events: initial !important;
}
.style-button:hover {
background-color: aqua;
}
在此示例中,您必须确保所有 ID 都是唯一的。如果无法做到这一点,请使用另一个唯一值。
更新
如果您不仅希望在组件悬停时保持这种状态,而且通常在处于活动状态时保持这种状态,我会执行以下操作:
将状态重命名为“isActive”,并将子组件中的值传递给父组件,以便可以在 className 上使用此值。此外,在悬停组件时重新添加原始样式,在这种情况下,您将在悬停组件和悬停时都具有样式。这是你如何做到的:
应用程序.tsx
import "./styles.css";
import { SubMenu } from "./SubMenu";
import { useState } from "react";
const subMenuSlice = <SubMenu />;
const nodes = [
{
id: "0",
name: "Samsung Galaxy",
subMenu: subMenuSlice
},
{
id: "1",
name: "Iphone",
subMenu: subMenuSlice
}
];
export default function App() {
const [isActive, setIsActive] = useState<string | null>(null);
return (
<table>
<tbody>
{nodes.map((val, key) => (
<tr key={key} className={isActive === val.id ? "active" : ""}>
<td>{val.name}</td>
<td>
{/* Check if the callback returns true, if so set isActive to the id */}
<SubMenu
openCallback={(boolValue) =>
boolValue ? setIsActive(val.id) : setIsActive(null)
}
/>
</td>
</tr>
))}
</tbody>
</table>
);
}
样式.css
.sss {
visibility: hidden;
}
tr.active .sss, tr:hover .sss {
background: gray;
visibility: visible;
}
tr.active, tr:hover {
background: gray;
visibility: visible;
pointer-events: initial !important;
}
.style-button:hover {
background-color: aqua;
}
子菜单.tsx
import { useState } from "react";
import AppsIcon from "@mui/icons-material/Apps";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import "./styles.css";
export const SubMenu = ({ openCallback }: { openCallback?: (arg: boolean) => void }) => {
const [isOpen, setIsOpen] = useState(false);
const handleOpen = () => {
setIsOpen((prevState) => !prevState);
if (openCallback) {
openCallback(!isOpen); // call the function with the boolean value !isOpen
}
};
return (
<DropdownMenu.Root open={isOpen} onOpenChange={handleOpen}>
<DropdownMenu.Trigger>
<AppsIcon className="sss" />
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
side="bottom"
sideOffset={-30}
align="start"
alignOffset={80}
>
<button className="style-button">Edit </button>
<button className="style-button">Make </button>
<button className="style-button">Delete </button>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
};
评论
notes
如果你不急于支持旧浏览器,你可以使用 :has() CSS 关系伪类来设置它包含 having .需要注意的是,Firefox 尚不支持此功能。然而,在撰写本文时,它确实有~87.53%的全球支持。tr
button
data-state="open"
因此,如果这适合您,方法如下:
为打开按钮的后代提供灰色 bg:
.sss
tr td button[data-state="open"] .sss {...}
给包含 (/'has') 打开按钮的灰色 bg:
tr
tr:has(button[data-state="open"]) {...}
您的完整样式 .css 如下所示:
.sss {
visibility: hidden;
}
tr:hover .sss,
tr td button[data-state="open"] .sss {
background: gray;
visibility: visible;
}
tr:hover,
tr:has(button[data-state="open"]) {
background: gray;
visibility: visible;
pointer-events: initial !important;
}
.style-button:hover {
background-color: aqua;
}
演示:https://codesandbox.io/s/sweet-tess-zcf23k
评论
.sss
button
.sss
删除,以便内容不会粘贴到门户
中,我还要补充一点,单元格之间没有空格。
下面是一个示例:codesandbox<DropdownMenu.Portal>
body
table { border-spacing: 0; }
有很多方法,我介绍一种。
-App.tsx
import "./styles.css";
import { SubMenu } from "./SubMenu";
import { useState } from "react";
const nodes = [
{
id: "0",
name: "Samsung Galaxy"
},
{
id: "1",
name: "Iphone"
}
];
export default function App() {
const [isActive, setIsActive] = useState<string | null>(null);
const subMenuSlice = (id) => (
<SubMenu openCallback={(boolValue) => boolValue ? setIsActive(id) : setIsActive(null)} />
);
const addSubMenuNodes = nodes.map((node) => ({
...node,
subMenu: subMenuSlice(node.id)
}));
//you can modify submenu for each node in nodes.
return (
<table>
<tbody>
{addSubMenuNodes.map((val, key) => (
<tr key={key} className={isActive === val.id ? "active" : ""}>
<td>{val.name}</td>
<td>{val.subMenu}</td>
</tr>
))}
</tbody>
</table>
);
}
-SubMenu.tsx
import { useState } from "react";
import AppsIcon from "@mui/icons-material/Apps";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import "./styles.css";
export const SubMenu = ({ openCallback }) => {
const [isOpen, setIsOpen] = useState(false);
const handleOpen = () => {
setIsOpen((prevState) => !prevState);
if (openCallback) {
openCallback(!isOpen); // call the function with the boolean value !isOpen
}
};
return (
<DropdownMenu.Root open={isOpen} onOpenChange={handleOpen}>
<DropdownMenu.Trigger>
<AppsIcon className="sss" />
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
side="bottom"
sideOffset={-30}
align="start"
alignOffset={80}
>
<button className="style-button">Edit </button>
<button className="style-button">Make </button>
<button className="style-button">Delete </button>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
};
-样式.css
table {
border-spacing: 0;
}
.sss {
visibility: hidden;
}
tr.active .sss,
tr:hover .sss {
background: gray;
visibility: visible;
}
tr.active,
tr:hover {
background: gray;
visibility: visible;
pointer-events: initial !important;
}
.style-button:hover {
background-color: aqua;
}
它将正常工作。
我希望我的回答能帮到你。
祝你好运。
哦,如果这没问题,请不要忘记投票给我的答案
评论