如何在聚焦的Material-UI按钮组件上始终应用focusVisible样式?

3
对于 Material-UI Button 组件,我希望 "focus" 样式看起来与 "focusVisible" 样式相同。也就是说,如果按钮通过程序或鼠标获取焦点,我希望它具有相同的涟漪效果。
我找到了一种解决方法,即在元素被聚焦之前调用 dispatchEvent(new window.Event("keydown")),使键盘成为最后一个输入类型。这将产生所需的外观效果,直到 MUI <ButtonBase/> 的 onMouseLeave 事件或其他鼠标事件被触发,从而导致可见焦点消失。
我已经找到了如何更改组件的焦点样式:
import React from "react"
import { withStyles } from "@material-ui/core/styles"
import Button from "@material-ui/core/Button"

const styles = {
  root: {
    '&:focus': {
      border: "3px solid #000000"
    }
  }
}

const CustomButtonRaw = React.forwardRef((props, ref) => {
  const { classes, ...rest } = props
  return <Button classes={{root: classes.root}} {...rest} ref={ref}/>
}

const CustomButton = withStyles(styles, { name: "CustomButton" })(CustomButtonRaw)

export default CustomButton

因此,当按钮处于“焦点”状态时,我可以为其应用一些样式(例如,我应用了一个边框)。但是我不知道如何获取要应用的样式。我尝试在按钮上放置className 'Mui-visibleFocus',但似乎没有效果。是否有某种方法可以获取如果按钮处于visibleFocus状态时将应用的样式?


我遇到了同样的问题,很遗憾它还是不起作用。 - undefined
Think Blew 发布了一个回答,说:“尝试了这个方法,并使用 Buttonbase 的 action ref 得到了一个解决方案 https://codesandbox.io/s/modal-dialog-button-autofocus-y9hfz” - undefined
2个回答

4

ButtonBaseButton的委托对象)具有一个动作属性,该属性提供了设置按钮的焦点可见状态的功能。

ButtonBase利用useImperativeHandle hook实现此功能。要使用它,您需要将引用传递到action属性中,然后稍后可以调用actionRef.current.focusVisible()

然而,仅此还不足够,因为有几个鼠标和触摸事件 ButtonBase listens to 以便开始/停止涟漪效果。如果您使用 disableTouchRipple 属性,则会 防止 ButtonBase 基于这些事件尝试启动/停止涟漪效果

不幸的是,disableTouchRipple 会阻止按钮上的点击和触摸动画。可以通过显式添加另一个 TouchRipple 元素并对其进行控制来恢复这些动画。下面的示例展示了处理 onMouseDownonMouseUp 的概念验证,但理想的解决方案应处理 ButtonBase 处理的所有不同事件。

以下是一个可运行的示例:

import React from "react";
import Button from "@material-ui/core/Button";
import TouchRipple from "@material-ui/core/ButtonBase/TouchRipple";

const FocusRippleButton = React.forwardRef(function FocusRippleButton(
  { onFocus, onMouseDown, onMouseUp, children, ...other },
  ref
) {
  const actionRef = React.useRef();
  const rippleRef = React.useRef();
  const handleFocus = (event) => {
    actionRef.current.focusVisible();
    if (onFocus) {
      onFocus(event);
    }
  };
  const handleMouseUp = (event) => {
    rippleRef.current.stop(event);
    if (onMouseUp) {
      onMouseUp(event);
    }
  };
  const handleMouseDown = (event) => {
    rippleRef.current.start(event);
    if (onMouseDown) {
      onMouseDown(event);
    }
  };
  return (
    <Button
      ref={ref}
      action={actionRef}
      disableTouchRipple
      onFocus={handleFocus}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      {...other}
    >
      {children}
      <TouchRipple ref={rippleRef} />
    </Button>
  );
});
export default function App() {
  return (
    <div className="App">
      <FocusRippleButton variant="contained" color="primary">
        Button 1
      </FocusRippleButton>
      <br />
      <br />
      <FocusRippleButton
        variant="contained"
        color="primary"
        onFocus={() => console.log("Some extra onFocus functionality")}
      >
        Button 2
      </FocusRippleButton>
    </div>
  );
}

Edit Unconditional focus ripple


嗨Ryan,这太棒了,完全符合我的要求。但是使用“disableTouchRipple”属性的解决方案有一个问题:现在按钮不再有点击动画了。你知道我如何访问ButtonBase中的“rippleRef”,以便我可以自己触发涟漪效果吗?或者是否有其他方法可以让点击动画重新生效? - undefined
点击动画是指当你点击时,焦点涟漪立即开始;尽管我意识到这并不完全相同——点击已经具有焦点的按钮不会提供任何反馈。我找不到在没有使用disableTouchRipple的情况下使焦点涟漪适用于点击的方法(我尝试了几种方式)。 - undefined
Material-UI没有提供任何访问rippleRef的方式。如果你尝试通过TouchRippleProps来传递它,那么会导致ButtonBase失去对rippleRef的访问,从而破坏其功能。我没有找到在不修改Material-UI的情况下实现此功能的方法。 - undefined
看起来你是对的。我一直在尝试找到访问rippleRef的方法,但我觉得没有这样的方法...真是不幸。现在我在想,是否可以使用单独的<TouchRipple/>元素来自己创建动画呢? - undefined
@user3044553 是的,可以为点击动画添加一个单独的<TouchRipple>元素。我已经更新了我的答案,包括一个过于简化的版本(不处理ButtonBase处理的所有事件)。 - undefined

0
我们可以创建一个对 material-ui Button 组件的 action 的引用,并在 useLayoutEffect 中使用该引用来实现涟漪效果。
import React, { createRef, useLayoutEffect } from "react";
import Button from "@material-ui/core/Button";

function FocusButton(props) {
  const { handleClose } = props;

  const actionRef = createRef();

  useLayoutEffect(() => {
    if (actionRef.current) {
      actionRef.current.focusVisible();
    }
  }, []);

  return (
    <Button action={actionRef} onClick={handleClose}>
      Ok
    </Button>
  );
}

以上的FocusButton可以用作Button的替代品,或者您可以添加引用并在触发方法中调用focusVisible()
例如:
const buttonRef = createRef();

const handleButton2Click = () => {
    buttonRef.current.focusVisible();
};

.
.
.
.

<Button action={buttonRef} variant="outlined">
        Button 1
</Button>

<Button variant="outlined" color="primary" onClick={handleButton2Click}>
        Button 2
</Button>

你可以在这个链接中找到演示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接