如何在反应material中有两个次要动作,一个在左角,一个在右边,而不影响次要动作的行为
How to have two Secondary Actions in react material, One on left corner and one on right without affecting the behaviour of secondary action
我需要在列表项的左端(眼睛图标)和右端(删除图标)显示两个辅助操作。我可以通过覆盖 css 元素 right & left
.
来调整位置
问题是辅助操作 delete
也触发了 ListItem's onClick
事件。因为在代码中顺序如下,先删除,最后写眼睛组件。
代码沙箱linkhttps://codesandbox.io/s/problem-video-forked-oh65hb?file=/demo.js:2568-3179
<ListItemSecondaryAction style={{ right: "11%", left: "auto" }}>
<IconButton
onClick={(event) => {}}
edge="end"
aria-label="delete"
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
<ListItemSecondaryAction style={{ right: "auto", left: "11%" }}>
<IconButton edge="end" aria-label="delete">
{<VisibilityIcon id="entity" />}
</IconButton>
</ListItemSecondaryAction>
因此,点击眼睛不会触发任何额外的 onclick 事件,但点击删除会触发。因为删除没有写成最后一个元素。
如何避免这种情况,如何在不影响行为的情况下在列表项的左端和右端设置两个辅助操作按钮。
我终于用 e.stopPropagation()
修复了它
<IconButton
onClick={(e) => {
e.stopPropagation();
}}
edge="end"
aria-label="delete"
>
{<VisibilityIcon id="entity" />}
</IconButton>
次要动作在v5中的处理方式不同,所以这个答案只适用于v4(而且问题中的沙箱使用的是v4)。
当 MUI 使用辅助操作呈现 ListItem 时,它使用以下结构呈现它(我添加了“data-desc”属性以包含我对该结构的评论):
<li data-desc="This is the Container component">
<div data-desc="This is the element that behaves like a button and has the corresponding hover effect.">
{listItemChildrenExceptForSecondaryAction}
</div>
{secondaryAction}
</li>
MUI 获取辅助操作以避免触发列表项主按钮部分的悬停和单击事件的方法是 plucking it out of the list of children 并将其呈现在按钮外部。该定位在视觉上将其置于按钮内,但就 DOM 结构而言,它位于按钮之外。
MUI 仅对作为 ListItem
的最后一个子项的辅助操作执行此操作,因此如果您想要另一个辅助操作,则需要通过不同的机制将其呈现在按钮之外。实现此目的的一种方法是覆盖“Container”组件并将附加操作作为道具传递给它,以便它可以在按钮外部呈现它(这将是容器 children
的一部分)。
这是使用此方法的沙箱的修改版本:
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import Collapse from "@material-ui/core/Collapse";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import Visibility from "@material-ui/icons/Visibility";
import VisibilityOff from "@material-ui/icons/VisibilityOff";
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
maxWidth: 360
}
}));
const VisibilityIcon = ({ id }) => {
const [entVisibility, setEntVisibility] = React.useState(true);
return entVisibility ? (
<Visibility
onClick={() => {
setEntVisibility(false);
}}
/>
) : (
<VisibilityOff
onClick={() => {
setEntVisibility(true);
}}
/>
);
};
const CustomContainerComponent = React.forwardRef(
function CustomContainerComponent(
{ children, extraSecondaryAction, ...other },
ref
) {
return (
<li ref={ref} {...other}>
{children}
{extraSecondaryAction}
</li>
);
}
);
export default function SelectedListItem() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(null);
const [open, setOpen] = React.useState(false);
const items = [];
for (let i = 0; i < 5; i++) {
items.push(`Entity Name ${i}`);
}
const subItems = [];
for (let i = 0; i < 3; i++) {
subItems.push(`subitem ${i}`);
}
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(!open);
};
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
{items.map((item, index) => {
return (
<React.Fragment key={index}>
<ListItem
ContainerComponent={CustomContainerComponent}
disableRipple
button
ContainerProps={{
extraSecondaryAction: (
<ListItemSecondaryAction
style={{ right: "11%", left: "auto" }}
>
<IconButton
onClick={(event) => {}}
edge="end"
aria-label="delete"
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
)
}}
selected={selectedIndex === index}
onClick={(event) => handleListItemClick(event, index)}
>
<ListItemIcon style={{ flex: "1" }}>
<IconButton edge="start" aria-label="delete">
{open && selectedIndex === index ? (
<ExpandLess />
) : (
<ExpandMore />
)}
</IconButton>
</ListItemIcon>
<ListItemText primary={item} style={{ flex: "3" }} />
<ListItemSecondaryAction style={{ right: "auto", left: "11%" }}>
<IconButton edge="end" aria-label="delete">
{<VisibilityIcon id="entity" />}
</IconButton>
</ListItemSecondaryAction>
</ListItem>
<Collapse
in={open && selectedIndex === index}
timeout="auto"
unmountOnExit
>
<List component="div" disablePadding>
<ListItem button>
<ListItemText primary="subItem" style={{ flex: "4" }} />
</ListItem>
</List>
<Divider />
</Collapse>
</React.Fragment>
);
})}
</List>
</div>
);
}
我需要在列表项的左端(眼睛图标)和右端(删除图标)显示两个辅助操作。我可以通过覆盖 css 元素 right & left
.
问题是辅助操作 delete
也触发了 ListItem's onClick
事件。因为在代码中顺序如下,先删除,最后写眼睛组件。
代码沙箱linkhttps://codesandbox.io/s/problem-video-forked-oh65hb?file=/demo.js:2568-3179
<ListItemSecondaryAction style={{ right: "11%", left: "auto" }}>
<IconButton
onClick={(event) => {}}
edge="end"
aria-label="delete"
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
<ListItemSecondaryAction style={{ right: "auto", left: "11%" }}>
<IconButton edge="end" aria-label="delete">
{<VisibilityIcon id="entity" />}
</IconButton>
</ListItemSecondaryAction>
因此,点击眼睛不会触发任何额外的 onclick 事件,但点击删除会触发。因为删除没有写成最后一个元素。
如何避免这种情况,如何在不影响行为的情况下在列表项的左端和右端设置两个辅助操作按钮。
我终于用 e.stopPropagation()
<IconButton
onClick={(e) => {
e.stopPropagation();
}}
edge="end"
aria-label="delete"
>
{<VisibilityIcon id="entity" />}
</IconButton>
次要动作在v5中的处理方式不同,所以这个答案只适用于v4(而且问题中的沙箱使用的是v4)。
当 MUI 使用辅助操作呈现 ListItem 时,它使用以下结构呈现它(我添加了“data-desc”属性以包含我对该结构的评论):
<li data-desc="This is the Container component">
<div data-desc="This is the element that behaves like a button and has the corresponding hover effect.">
{listItemChildrenExceptForSecondaryAction}
</div>
{secondaryAction}
</li>
MUI 获取辅助操作以避免触发列表项主按钮部分的悬停和单击事件的方法是 plucking it out of the list of children 并将其呈现在按钮外部。该定位在视觉上将其置于按钮内,但就 DOM 结构而言,它位于按钮之外。
MUI 仅对作为 ListItem
的最后一个子项的辅助操作执行此操作,因此如果您想要另一个辅助操作,则需要通过不同的机制将其呈现在按钮之外。实现此目的的一种方法是覆盖“Container”组件并将附加操作作为道具传递给它,以便它可以在按钮外部呈现它(这将是容器 children
的一部分)。
这是使用此方法的沙箱的修改版本:
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import Collapse from "@material-ui/core/Collapse";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import Visibility from "@material-ui/icons/Visibility";
import VisibilityOff from "@material-ui/icons/VisibilityOff";
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
maxWidth: 360
}
}));
const VisibilityIcon = ({ id }) => {
const [entVisibility, setEntVisibility] = React.useState(true);
return entVisibility ? (
<Visibility
onClick={() => {
setEntVisibility(false);
}}
/>
) : (
<VisibilityOff
onClick={() => {
setEntVisibility(true);
}}
/>
);
};
const CustomContainerComponent = React.forwardRef(
function CustomContainerComponent(
{ children, extraSecondaryAction, ...other },
ref
) {
return (
<li ref={ref} {...other}>
{children}
{extraSecondaryAction}
</li>
);
}
);
export default function SelectedListItem() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(null);
const [open, setOpen] = React.useState(false);
const items = [];
for (let i = 0; i < 5; i++) {
items.push(`Entity Name ${i}`);
}
const subItems = [];
for (let i = 0; i < 3; i++) {
subItems.push(`subitem ${i}`);
}
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(!open);
};
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
{items.map((item, index) => {
return (
<React.Fragment key={index}>
<ListItem
ContainerComponent={CustomContainerComponent}
disableRipple
button
ContainerProps={{
extraSecondaryAction: (
<ListItemSecondaryAction
style={{ right: "11%", left: "auto" }}
>
<IconButton
onClick={(event) => {}}
edge="end"
aria-label="delete"
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
)
}}
selected={selectedIndex === index}
onClick={(event) => handleListItemClick(event, index)}
>
<ListItemIcon style={{ flex: "1" }}>
<IconButton edge="start" aria-label="delete">
{open && selectedIndex === index ? (
<ExpandLess />
) : (
<ExpandMore />
)}
</IconButton>
</ListItemIcon>
<ListItemText primary={item} style={{ flex: "3" }} />
<ListItemSecondaryAction style={{ right: "auto", left: "11%" }}>
<IconButton edge="end" aria-label="delete">
{<VisibilityIcon id="entity" />}
</IconButton>
</ListItemSecondaryAction>
</ListItem>
<Collapse
in={open && selectedIndex === index}
timeout="auto"
unmountOnExit
>
<List component="div" disablePadding>
<ListItem button>
<ListItemText primary="subItem" style={{ flex: "4" }} />
</ListItem>
</List>
<Divider />
</Collapse>
</React.Fragment>
);
})}
</List>
</div>
);
}