Для компонента кнопки Material-UI я хотел бы, чтобы стиль «фокус» выглядел так же, как стиль «focusVisible». Это означает, что я хочу, чтобы он имел такой же видимый волновой эффект, если кнопка была сфокусирована программно или с помощью мыши, как если бы кнопка была сфокусирована с помощью клавиши табуляции.
Обходной путь, который я нашел, заключается в вызове dispatchEvent(new window.Event("keydown")) для элемента до того, как он будет сфокусирован, в результате чего клавиатура будет последним используемым типом ввода. Это приведет к тому, что кнопка будет выглядеть так, как я хочу, ДО ТОГО, как событие onMouseLeave (из MUI <ButtonBase/>) или другое событие мыши не будет запущено, что приведет к исчезновению видимого фокуса.
Я понял, как изменить стиль фокуса компонента следующим образом:
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?
ButtonBase (который Button делегирует ) имеет свойство действия , которое предоставляет возможность установить видимость фокуса для кнопки.
ButtonBase использует для этого хук useImperativeHandle. Чтобы использовать его, вы передаете ссылку в реквизит action, а затем можете позже вызвать actionRef.current.focusVisible().
Однако этого самого по себе недостаточно, потому что есть несколько событий мыши и касания, которые ButtonBase прослушивает , чтобы запустить/остановить пульсацию. Если вы используете реквизит disableTouchRipple, он предотвращает попытки ButtonBase запустить/остановить пульсацию на основе этих событий.
К сожалению, disableTouchRipple предотвращает анимацию нажатия и касания кнопки. Их можно восстановить, явно добавив еще один элемент TouchRipple, которым вы управляете. В моем примере ниже показана обработка onMouseDown и onMouseUp в качестве доказательства концепции, но идеальное решение будет иметь дело со всеми различными событиями, которые обрабатывает 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.info("Some extra onFocus functionality")}
>
Button 2
</FocusRippleButton>
</div>
);
}
Привет Райан, это отлично и делает именно то, что я просил. Но есть одна проблема с использованием этого решения с применением свойства «disableTouchRipple»: теперь у кнопки больше нет анимации кликов. Знаете ли вы, как мне получить доступ к «rippleRef» из ButtonBase, чтобы я мог сам вызвать пульсацию? Или есть обходной путь, чтобы заставить анимацию кликов снова работать?
Вы получаете анимацию щелчка в том смысле, что рябь фокуса сразу же начинается, когда вы щелкаете; хотя я понимаю, что это не то же самое - нажатие на кнопку, которая уже находится в фокусе, не дает никакой обратной связи. Я не смог найти способ заставить пульсацию фокуса работать на клики без disableTouchRipple (перепробовал несколько способов).
Material-UI не предоставляет никакого доступа к rippleRef. Если вы попытаетесь передать его через TouchRippleProps, он сломается ButtonBase, так как потеряет доступ к rippleRef. Я не вижу способа сделать это без изменений в Material-UI.
Кажется, ты прав. Я пытался найти способ получить доступ к rippleRef, но я не думаю, что он есть... К сожалению. Теперь мне интересно, можно ли использовать отдельный элемент <TouchRipple/> для самостоятельного создания анимации?
@user3044553 user3044553 Да, можно добавить отдельный элемент <TouchRipple> для анимации щелчка. Я обновил свой ответ, включив в него чрезмерно упрощенную (не учитывающую все события, которые обрабатывает ButtonBase) версию этого.
Мы можем создать ссылку на action компонента material-ui Button и использовать ссылку на действие внутри 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>
Вы можете найти демо по этой ссылке
Я столкнулся с той же проблемой, жаль, что все еще не работает