Skip to content

Commit

Permalink
feat: Auto-scroll (#112)
Browse files Browse the repository at this point in the history
* add misc store & auto-scrol

* fix showNewTips

* update translation
  • Loading branch information
kyun authored Jan 2, 2024
1 parent 276ab85 commit fa71e8e
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
"run": "Run",
"placeholder": "Executable code",
"newContent": "New content",
"auto-scroll-on": "Auto Scroll On",
"auto-scroll-off": "Auto Scroll Off",
"error-trace": {
"title": "Error detail",
"hints": "HINTS",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
"run": "実行",
"placeholder": "実行可能なコード",
"newContent": "新しいメッセージ",
"auto-scroll-on": "自動スクロールオン",
"auto-scroll-off": "自動スクロールオフ",
"error-trace": {
"title": "エラーの詳細",
"hints": "使用のヒント",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
"run": "실행",
"placeholder": "실행 가능한 코드",
"newContent": "새로운 메시지",
"auto-scroll-on": "자동 스크롤링 켜기",
"auto-scroll-off": "자동 스크롤링 끄기",
"error-trace": {
"title": "오류 세부 정보",
"hints": "사용 팁",
Expand Down
2 changes: 2 additions & 0 deletions src/assets/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
"run": "执行",
"placeholder": "可执行代码",
"newContent": "新消息",
"auto-scroll-on": "自动滚动",
"auto-scroll-off": "自动滚动关闭",
"error-trace": {
"title": "错误详情",
"hints": "使用须知",
Expand Down
28 changes: 25 additions & 3 deletions src/pages/Devtools/ConsolePanel/components/FooterInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { RightOutlined } from '@ant-design/icons';
import { Input, Button } from 'antd';
import {
CaretRightOutlined,
PauseOutlined,
RightOutlined,
} from '@ant-design/icons';
import { Input, Button, Tooltip } from 'antd';
import { Shortcuts } from '../Shortcuts';
import { useSocketMessageStore } from '@/store/socket-message';
import { TextAreaRef } from 'antd/es/input/TextArea';
import { useRef, useState, useEffect, useCallback, memo } from 'react';
import { useTranslation } from 'react-i18next';
import type { KeyboardEvent } from 'react';
import { useMiscStore } from '@/store/misc';

const EXECUTE_HISTORY_ID = 'page_spy_execute_history';
const EXECUTE_HISTORY_MAX_SIZE = 100;
Expand All @@ -22,6 +27,10 @@ export const FooterInput = memo(() => {
const executeHistory = useRef<string[]>(
JSON.parse(localStorage.getItem(EXECUTE_HISTORY_ID) || '[]'),
);
const [isAutoScroll, setIsAutoScroll] = useMiscStore((state) => [
state.isAutoScroll,
state.setIsAutoScroll,
]);

useEffect(() => {
inputRef.current?.focus();
Expand Down Expand Up @@ -167,11 +176,24 @@ export const FooterInput = memo(() => {
<Button
type="primary"
size="small"
style={{ marginTop: 4 }}
style={{ marginTop: 4, marginRight: 8 }}
onClick={handleDebugCode}
>
{t('run')}
</Button>
<Tooltip
title={!isAutoScroll ? t('auto-scroll-on') : t('auto-scroll-off')}
>
<Button
onClick={() => {
setIsAutoScroll(!isAutoScroll);
}}
size="small"
style={{ marginTop: 4 }}
>
{!isAutoScroll ? <CaretRightOutlined /> : <PauseOutlined />}
</Button>
</Tooltip>
<Shortcuts />
</div>
);
Expand Down
46 changes: 41 additions & 5 deletions src/pages/Devtools/ConsolePanel/components/MainContent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { useSocketMessageStore } from '@/store/socket-message';
import { DoubleRightOutlined } from '@ant-design/icons';
import { Row, Col, Button } from 'antd';
import { useCallback, useEffect, useRef, useState } from 'react';
import {
UIEventHandler,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import ConsoleNode from '../ConsoleNode';
import {
isPlaceholderNode,
Expand All @@ -11,11 +17,15 @@ import { isErrorTraceNode, ErrorTraceNode } from '../ConsoleNode/ErrorTrace';
import LogType from '../LogType';
import Timestamp from '../Timestamp';
import { useTranslation } from 'react-i18next';
import { useMiscStore } from '@/store/misc';

export const MainContent = () => {
const { t } = useTranslation('translation', { keyPrefix: 'console' });

const containerEl = useRef<HTMLDivElement | null>(null);
const currentScrollTop = useRef<number>(0);
const messageLength = useRef<number>(0);

const scrollToBottom = useCallback(() => {
const container = containerEl.current;
if (!container) return;
Expand All @@ -30,6 +40,10 @@ export const MainContent = () => {
state.consoleMsg,
state.consoleMsgTypeFilter,
]);
const [isAutoScroll, setIsAutoScroll] = useMiscStore((state) => [
state.isAutoScroll,
state.setIsAutoScroll,
]);
const [newTips, setNewTips] = useState<boolean>(false);
useEffect(() => {
if (data.length === 0) {
Expand All @@ -41,18 +55,21 @@ export const MainContent = () => {
const isDebug = ['debug-origin', 'debug-eval'].includes(logType);
const evalError =
data.length > 1 && data[data.length - 2].logType === 'debug-origin';
if (isDebug || evalError) {
if (isDebug || evalError || isAutoScroll) {
scrollToBottom();
} else {
const container = containerEl.current;
if (!container) return;

const { offsetHeight, scrollHeight } = container;
if (scrollHeight > offsetHeight) {
if (
scrollHeight > offsetHeight &&
messageLength.current !== data.length
) {
setNewTips(true);
}
}
}, [data, scrollToBottom]);
}, [data, scrollToBottom, isAutoScroll]);

useEffect(() => {
const container = containerEl.current;
Expand All @@ -73,12 +90,31 @@ export const MainContent = () => {
};
}, []);

const handleScroll: UIEventHandler<HTMLDivElement> = useCallback(
(e) => {
const direction =
e.currentTarget.scrollTop > currentScrollTop.current ? 'down' : 'up';
currentScrollTop.current = e.currentTarget.scrollTop;
if (direction === 'down' && isAutoScroll) return;
const isBottom =
Math.ceil(e.currentTarget.scrollTop) ===
e.currentTarget.scrollHeight - e.currentTarget.offsetHeight;
if (isBottom) {
messageLength.current = e.currentTarget.childNodes.length;
setIsAutoScroll(true);
} else {
setIsAutoScroll(false);
}
},
[setIsAutoScroll, isAutoScroll],
);

const consoleDataList = dataFilter.length
? data.filter((item) => dataFilter.includes(item.logType))
: data;

return (
<div className="console-list" ref={containerEl}>
<div className="console-list" ref={containerEl} onScroll={handleScroll}>
{consoleDataList.map((item) => (
<div className={`console-item ${item.logType}`} key={item.id}>
<div className="console-item__title">
Expand Down
18 changes: 18 additions & 0 deletions src/store/misc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { produce } from 'immer';
import { create } from 'zustand';

interface MiscInfo {
isAutoScroll: boolean;
setIsAutoScroll: (data: boolean) => void;
}

export const useMiscStore = create<MiscInfo>((set) => ({
isAutoScroll: false,
setIsAutoScroll: (data: boolean) => {
set(
produce<MiscInfo>((state) => {
state.isAutoScroll = data;
}),
);
},
}));

0 comments on commit fa71e8e

Please sign in to comment.