fix(core): canvas text adapts to input scrolling (#7733)
close PD-1539 Fixed the problem that the input text in find in page cannot be scrolled correctly.
This commit is contained in:
@@ -10,7 +10,10 @@ import { useLiveData, useService } from '@toeverything/infra';
|
|||||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {
|
import {
|
||||||
|
type ChangeEventHandler,
|
||||||
|
type CompositionEventHandler,
|
||||||
type KeyboardEventHandler,
|
type KeyboardEventHandler,
|
||||||
|
type SetStateAction,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
@@ -23,7 +26,11 @@ import * as styles from './find-in-page-modal.css';
|
|||||||
|
|
||||||
const animationTimeout = 120;
|
const animationTimeout = 120;
|
||||||
|
|
||||||
const drawText = (canvas: HTMLCanvasElement, text: string) => {
|
const drawText = (
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
text: string,
|
||||||
|
scrollLeft: number
|
||||||
|
) => {
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
return;
|
return;
|
||||||
@@ -43,16 +50,21 @@ const drawText = (canvas: HTMLCanvasElement, text: string) => {
|
|||||||
ctx.fillStyle = textColor;
|
ctx.fillStyle = textColor;
|
||||||
ctx.font = '15px Inter';
|
ctx.font = '15px Inter';
|
||||||
|
|
||||||
ctx.fillText(text, 0, 22);
|
const offsetX = -scrollLeft; // Offset based on scrollLeft
|
||||||
|
|
||||||
|
ctx.fillText(text, offsetX, 22);
|
||||||
|
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
};
|
};
|
||||||
|
|
||||||
const CanvasText = ({
|
const CanvasText = ({
|
||||||
text,
|
text,
|
||||||
|
scrollLeft,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
|
scrollLeft: number;
|
||||||
className: string;
|
className: string;
|
||||||
}) => {
|
}) => {
|
||||||
const ref = useRef<HTMLCanvasElement>(null);
|
const ref = useRef<HTMLCanvasElement>(null);
|
||||||
@@ -62,9 +74,9 @@ const CanvasText = ({
|
|||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
drawText(canvas, text);
|
drawText(canvas, text, scrollLeft);
|
||||||
return observeResize(canvas, () => drawText(canvas, text));
|
return observeResize(canvas, () => drawText(canvas, text, scrollLeft));
|
||||||
}, [text]);
|
}, [text, scrollLeft]);
|
||||||
|
|
||||||
return <canvas className={className} ref={ref} />;
|
return <canvas className={className} ref={ref} />;
|
||||||
};
|
};
|
||||||
@@ -79,6 +91,8 @@ export const FindInPageModal = () => {
|
|||||||
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [active, setActive] = useState(false);
|
const [active, setActive] = useState(false);
|
||||||
|
const [scrollLeft, setScrollLeft] = useState(0);
|
||||||
|
const [composing, setComposing] = useState(false);
|
||||||
|
|
||||||
const [{ status }, toggle] = useTransition({
|
const [{ status }, toggle] = useTransition({
|
||||||
timeout: animationTimeout,
|
timeout: animationTimeout,
|
||||||
@@ -88,16 +102,19 @@ export const FindInPageModal = () => {
|
|||||||
toggle(visible);
|
toggle(visible);
|
||||||
}, [visible]);
|
}, [visible]);
|
||||||
|
|
||||||
const handleValueChange = useCallback(
|
const handleValueChange: ChangeEventHandler<HTMLInputElement> = useCallback(
|
||||||
(v: string) => {
|
e => {
|
||||||
setValue(v);
|
const value = e.target.value;
|
||||||
findInPage.findInPage(v);
|
setValue(value);
|
||||||
if (v.length === 0) {
|
if (!composing) {
|
||||||
|
findInPage.findInPage(value);
|
||||||
|
}
|
||||||
|
if (value.length === 0) {
|
||||||
findInPage.clear();
|
findInPage.clear();
|
||||||
}
|
}
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
},
|
},
|
||||||
[findInPage]
|
[composing, findInPage]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFocus = useCallback(() => {
|
const handleFocus = useCallback(() => {
|
||||||
@@ -170,8 +187,29 @@ export const FindInPageModal = () => {
|
|||||||
[handleBackWard, handleForward]
|
[handleBackWard, handleForward]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleScroll = useCallback(
|
||||||
|
(e: { currentTarget: { scrollLeft: SetStateAction<number> } }) => {
|
||||||
|
setScrollLeft(e.currentTarget.scrollLeft);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCompositionStart: CompositionEventHandler<HTMLInputElement> =
|
||||||
|
useCallback(() => {
|
||||||
|
setComposing(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCompositionEnd: CompositionEventHandler<HTMLInputElement> =
|
||||||
|
useCallback(
|
||||||
|
e => {
|
||||||
|
setComposing(false);
|
||||||
|
findInPage.findInPage(e.currentTarget.value);
|
||||||
|
},
|
||||||
|
[findInPage]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root modal open={status !== 'exited'}>
|
<Dialog.Root open={status !== 'exited'}>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Overlay className={styles.modalOverlay} />
|
<Dialog.Overlay className={styles.modalOverlay} />
|
||||||
<div className={styles.modalContentWrapper}>
|
<div className={styles.modalContentWrapper}>
|
||||||
@@ -201,9 +239,16 @@ export const FindInPageModal = () => {
|
|||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
className={styles.input}
|
className={styles.input}
|
||||||
onKeyDown={handleKeydown}
|
onKeyDown={handleKeydown}
|
||||||
onChange={e => handleValueChange(e.target.value)}
|
onChange={handleValueChange}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
onCompositionStart={handleCompositionStart}
|
||||||
|
onCompositionEnd={handleCompositionEnd}
|
||||||
|
/>
|
||||||
|
<CanvasText
|
||||||
|
className={styles.inputHack}
|
||||||
|
text={value}
|
||||||
|
scrollLeft={scrollLeft}
|
||||||
/>
|
/>
|
||||||
<CanvasText className={styles.inputHack} text={value} />
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.count}>
|
<div className={styles.count}>
|
||||||
{value.length > 0 && result && result.matches !== 0 ? (
|
{value.length > 0 && result && result.matches !== 0 ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user