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:
JimmFly
2024-08-05 09:27:18 +00:00
parent 6d253c0600
commit 9cc976ce2e

View File

@@ -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 ? (