Formatting menu: Show popup guide faster, fade in/out
This commit is contained in:
parent
076da53815
commit
f597f15faf
3 changed files with 66 additions and 34 deletions
|
@ -194,6 +194,9 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 120ms ease-out;
|
||||||
|
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
background-color: $color-black;
|
background-color: $color-black;
|
||||||
color: $color-gray-05;
|
color: $color-gray-05;
|
||||||
|
|
|
@ -15,10 +15,10 @@ import * as log from '../../logging/log';
|
||||||
import * as Errors from '../../types/errors';
|
import * as Errors from '../../types/errors';
|
||||||
import type { LocalizerType } from '../../types/Util';
|
import type { LocalizerType } from '../../types/Util';
|
||||||
import { handleOutsideClick } from '../../util/handleOutsideClick';
|
import { handleOutsideClick } from '../../util/handleOutsideClick';
|
||||||
import { SECOND } from '../../util/durations/constants';
|
|
||||||
|
|
||||||
const FADE_OUT_MS = 200;
|
const MENU_FADE_OUT_MS = 200;
|
||||||
const BUTTON_HOVER_TIMEOUT = 2 * SECOND;
|
const POPUP_GUIDE_FADE_MS = 120;
|
||||||
|
const BUTTON_HOVER_TIMEOUT_MS = 900;
|
||||||
const MENU_TEXT_BUFFER = 12; // pixels
|
const MENU_TEXT_BUFFER = 12; // pixels
|
||||||
|
|
||||||
// Note: Keyboard shortcuts are defined in the constructor below, and when using
|
// Note: Keyboard shortcuts are defined in the constructor below, and when using
|
||||||
|
@ -160,7 +160,7 @@ export class FormattingMenu {
|
||||||
this.lastRect = undefined;
|
this.lastRect = undefined;
|
||||||
this.fadingOutTimeout = undefined;
|
this.fadingOutTimeout = undefined;
|
||||||
this.render();
|
this.render();
|
||||||
}, FADE_OUT_MS);
|
}, MENU_FADE_OUT_MS);
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
@ -452,34 +452,47 @@ function FormattingButton({
|
||||||
toggleForStyle: (style: QuillFormattingStyle) => unknown;
|
toggleForStyle: (style: QuillFormattingStyle) => unknown;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const buttonRef = React.useRef<HTMLButtonElement | null>(null);
|
const buttonRef = React.useRef<HTMLButtonElement | null>(null);
|
||||||
const timerRef = React.useRef<NodeJS.Timeout | undefined>();
|
|
||||||
const [isHovered, setIsHovered] = React.useState<boolean>(false);
|
const [isHovered, setIsHovered] = React.useState<boolean>(false);
|
||||||
|
const hoverTimerRef = React.useRef<NodeJS.Timeout | undefined>();
|
||||||
|
|
||||||
|
const [isFadingOut, setIsFadingOut] = React.useState<boolean>(false);
|
||||||
|
const fadeOutTimerRef = React.useRef<NodeJS.Timeout | undefined>();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (timerRef.current) {
|
if (hoverTimerRef.current) {
|
||||||
clearTimeout(timerRef.current);
|
clearTimeout(hoverTimerRef.current);
|
||||||
timerRef.current = undefined;
|
hoverTimerRef.current = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fadeOutTimerRef.current) {
|
||||||
|
clearTimeout(fadeOutTimerRef.current);
|
||||||
|
fadeOutTimerRef.current = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasLongHovered && isHovered && buttonRef.current ? (
|
{(isFadingOut || (hasLongHovered && isHovered)) && buttonRef.current ? (
|
||||||
<Popper placement="top" referenceElement={buttonRef.current}>
|
<Popper placement="top" referenceElement={buttonRef.current}>
|
||||||
{({ ref, style: popperStyles }) => (
|
{({ ref, style: popperStyles }) => {
|
||||||
|
const opacity = !popperStyles.transform || isFadingOut ? 0 : 1;
|
||||||
|
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
className="module-composition-input__format-menu__item__popover"
|
className="module-composition-input__format-menu__item__popover"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
style={popperStyles}
|
style={{ ...popperStyles, opacity }}
|
||||||
>
|
>
|
||||||
{popupGuideText}
|
{popupGuideText}
|
||||||
<div className="module-composition-input__format-menu__item__popover__shortcut">
|
<div className="module-composition-input__format-menu__item__popover__shortcut">
|
||||||
{popupGuideShortcut}
|
{popupGuideShortcut}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
);
|
||||||
|
}}
|
||||||
</Popper>
|
</Popper>
|
||||||
) : null}
|
) : null}
|
||||||
<button
|
<button
|
||||||
|
@ -499,21 +512,29 @@ function FormattingButton({
|
||||||
toggleForStyle(style);
|
toggleForStyle(style);
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => {
|
onMouseEnter={() => {
|
||||||
if (timerRef.current) {
|
if (hoverTimerRef.current) {
|
||||||
clearTimeout(timerRef.current);
|
clearTimeout(hoverTimerRef.current);
|
||||||
timerRef.current = undefined;
|
hoverTimerRef.current = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
timerRef.current = setTimeout(() => {
|
hoverTimerRef.current = setTimeout(() => {
|
||||||
onLongHover(true);
|
onLongHover(true);
|
||||||
}, BUTTON_HOVER_TIMEOUT);
|
}, BUTTON_HOVER_TIMEOUT_MS);
|
||||||
|
|
||||||
setIsHovered(true);
|
setIsHovered(true);
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
if (timerRef.current) {
|
if (hoverTimerRef.current) {
|
||||||
clearTimeout(timerRef.current);
|
clearTimeout(hoverTimerRef.current);
|
||||||
timerRef.current = undefined;
|
hoverTimerRef.current = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLongHovered && isHovered) {
|
||||||
|
fadeOutTimerRef.current = setTimeout(() => {
|
||||||
|
setIsFadingOut(false);
|
||||||
|
fadeOutTimerRef.current = undefined;
|
||||||
|
}, POPUP_GUIDE_FADE_MS);
|
||||||
|
setIsFadingOut(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsHovered(false);
|
setIsHovered(false);
|
||||||
|
|
|
@ -2861,18 +2861,26 @@
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/quill/formatting/menu.tsx",
|
"path": "ts/quill/formatting/menu.tsx",
|
||||||
"line": " const buttonRef = React.useRef<HTMLButtonElement | null>(null);",
|
"line": " const fadeOutTimerRef = React.useRef<NodeJS.Timeout | undefined>();",
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||||
"updated": "2023-04-22T00:07:56.294Z",
|
"updated": "2023-08-02T19:01:24.771Z",
|
||||||
"reasonDetail": "Popper needs to reference the button"
|
"reasonDetail": "We need a persistent timer to know when to remove after fade-out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/quill/formatting/menu.tsx",
|
"path": "ts/quill/formatting/menu.tsx",
|
||||||
"line": " const timerRef = React.useRef<NodeJS.Timeout | undefined>();",
|
"line": " const hoverTimerRef = React.useRef<NodeJS.Timeout | undefined>();",
|
||||||
|
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||||
|
"updated": "2023-08-02T19:01:24.771Z",
|
||||||
|
"reasonDetail": "We need a persistent timer to track long-hovers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/quill/formatting/menu.tsx",
|
||||||
|
"line": " const buttonRef = React.useRef<HTMLButtonElement | null>(null);",
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2023-04-22T00:07:56.294Z",
|
"updated": "2023-04-22T00:07:56.294Z",
|
||||||
"reasonDetail": "We need a persistent timer to track long-hovers"
|
"reasonDetail": "Popper needs to reference the button"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"rule": "DOM-innerHTML",
|
"rule": "DOM-innerHTML",
|
||||||
|
|
Loading…
Reference in a new issue