Skip to content

Commit

Permalink
feat: 允许动态添加inline button
Browse files Browse the repository at this point in the history
  • Loading branch information
SSmJaE committed Mar 19, 2023
1 parent b60e57e commit 54771e6
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 60 deletions.
5 changes: 5 additions & 0 deletions src/types/theme.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ declare module "@emotion/react" {
activeSecondary: string;
error: string;
};
answerTypeColorMapping: {
GPT: string;
标答: string;
无答案: string;
};
}
}
28 changes: 20 additions & 8 deletions src/utils/logger/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { store } from "@store";
import Button, { IButtonProps } from "@components/Button";

function scrollDown() {
//等待message渲染完成,不然不会拉到最底
Expand All @@ -11,12 +12,23 @@ export const RECORD_TYPES = ["info", "error", "question", "hr"] as const;

export type RecordType = typeof RECORD_TYPES[number];

type IDynamicButton = Pick<IButtonProps, "children" | "onClick" | "disabled">;

export interface IRecord<T = RecordType, C = any> {
id: string;
timestamp: string;
type: T;
content: C;
extra?: string;
/**
* 动态挂载额外的可交互按钮
*
* 因为valtio会snapshot(frozen),如果直接传入component,会触发Cannot assign to read only property 'validated' of object '#<Object>'
* 所以这里只传入props,
*
* 理论上来说,在类redux中存函数的引用,也是不规范的,但是无所谓了
* */
action?: IDynamicButton[];
}

export interface IQuestionContent {
Expand All @@ -27,7 +39,7 @@ export interface IQuestionContent {
};
answerText: string;
raw: {
element: HTMLElement;
element?: HTMLElement;
};
solve: {
couldSolve: boolean;
Expand Down Expand Up @@ -71,22 +83,22 @@ export class Logger {
}
}

log(option: Pick<IRecord, "type" | "content" | "extra">) {
log(option: Pick<IRecord, "type" | "content" | "extra" | "action">) {
this.addLog({
...option,
timestamp: new Date().toISOString(),
id: `${Math.random()}`,
});
}

info(content: string, extra?: string) {
return this.log({ type: "info", content, extra });
info(content: string, extra?: string, action?: IDynamicButton[]) {
return this.log({ type: "info", content, extra, action });
}
question(content: IQuestionContent, extra?: string) {
return this.log({ type: "question", content, extra });
question(content: IQuestionContent, extra?: string, action?: IDynamicButton[]) {
return this.log({ type: "question", content, extra, action });
}
error(content: IErrorContent, extra?: string) {
return this.log({ type: "error", content, extra });
error(content: IErrorContent, extra?: string, action?: IDynamicButton[]) {
return this.log({ type: "error", content, extra, action });
}
hr() {
return this.log({ type: "hr", content: "" });
Expand Down
7 changes: 6 additions & 1 deletion src/views/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import { ConfigPanel } from "./Config";
import { FloatingBall } from "./Floating";
import { LogPanel } from "./Log";

const theme: Theme = {
export const theme: Theme = {
colors: {
primary: "rgb(255, 255, 255)", // 60%
secondary: "rgb(230, 230, 230)", // 30%
active: "#2196f3", // 10%
activeSecondary: "rgb(231, 243, 255)",
error: "rgb(231, 71, 93)",
},
answerTypeColorMapping: {
GPT: "orange",
标答: "limegreen",
无答案: "rgb(231, 71, 93)",
},
};

export default function App() {
Expand Down
2 changes: 1 addition & 1 deletion src/views/Config/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const ConfigPanelContainer = styled(animated.div)<{}>(
flexDirection: "column",

width: 600,
position: "absolute",
position: "fixed",
top: 100,
right: 100,
zIndex: 101,
Expand Down
22 changes: 12 additions & 10 deletions src/views/Log/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,17 @@ export function LogPanel() {

const theme = useTheme();

useEffect(() => {
if (status) return;
// useEffect(() => {
// if (status) return;

for (const [index, record] of buffer.entries()) {
setTimeout(() => {
store.logs.push(record);
}, index * 200);
}
// for (const [index, record] of buffer.entries()) {
// setTimeout(() => {
// store.logs.push(record);
// }, index * 200);
// }

status = true;
}, []);
// status = true;
// }, []);

// logger.debug({
// floating,
Expand Down Expand Up @@ -240,7 +240,8 @@ export function LogPanel() {
>
<animated.div
style={{
position: "absolute",
// 页面可能很长,所以这里使用 fixed 定位
position: "fixed",
top: 100,
left: 100,
// minWidth: 300,
Expand Down Expand Up @@ -337,6 +338,7 @@ export function LogPanel() {
content={record.extra}
placement={"right"}
disabled={record.extra === undefined}
delay={record.type === "error"}
>
<RecordContainer
key={record.timestamp}
Expand Down
25 changes: 25 additions & 0 deletions src/views/Log/records/Info.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { useState } from "react";

import { IInfoRecord } from "@/src/utils/logger";
import { useTheme } from "@emotion/react";
import styled from "@emotion/styled";
import { animated } from "@react-spring/web";

import { InlineTag, useSlideIn } from "../../components/InlineTag";
import Button from "../../components/Button";

const InfoRecordContainer = styled(animated.span)(
{
Expand All @@ -29,12 +33,16 @@ export function InfoRecord({ record }: { record: IInfoRecord }) {
const spring = useSlideIn();
const theme = useTheme();

const [isHover, setHover] = useState(false);

return (
<div
style={{
position: "relative",
lineHeight: "normal",
}}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<InlineTag
style={{
Expand All @@ -53,6 +61,23 @@ export function InfoRecord({ record }: { record: IInfoRecord }) {
__html: `${record.content}`,
}}
/>

<div
style={{
position: "absolute",
bottom: 0,
right: 0,
display: isHover ? "flex" : "none",
}}
>
{/* hover时,显示在最后一行的最右边 */}
{record.action &&
record.action.map(({ children, onClick }, index) => (
<Button key={index} onClick={onClick}>
{children}
</Button>
))}
</div>
</div>
);
}
64 changes: 34 additions & 30 deletions src/views/Log/records/Question.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,43 @@
import { createRef, useState } from "react";
import { useHover, useMount } from "ahooks";
// import Typed from "typed.js";
import { useRef } from "react";
// import TypeIt from "typeit-react";
import TypeIt from "../../components/TypeIt";

import Button from "../../components/Button";
import { createRef, useRef, useState } from "react";
import { ErrorBoundary } from "react-error-boundary";

import { animated, config, useSpring, useSprings, useTrail } from "@react-spring/web";
import { useTheme } from "@emotion/react";
import { store } from "@/src/store";
import logger, { IQuestionContent, IQuestionRecord } from "@/src/utils/logger";
import { ErrorBoundary } from "react-error-boundary";
import { InlineTag, useSlideIn } from "../../components/InlineTag";
import { useTheme } from "@emotion/react";
import { animated, config, useSpring, useSprings, useTrail } from "@react-spring/web";

const defaultColorMapping = {
GPT: "orange",
标答: "limegreen",
无答案: "red",
};
import { theme } from "../../App";
import Button from "../../components/Button";
import { InlineTag, useSlideIn } from "../../components/InlineTag";
// import TypeIt from "typeit-react";
import TypeIt from "../../components/TypeIt";

function SolveButton({ content: { solve, answerText } }: { content: IQuestionContent }) {
const [isHovering, setIsHovering] = useState(false);

let buttonText: string = "解答该题";
// let buttonText: string = "解答该题";

// if (solve.couldSolve) {
// if (solve.hasSolved) {
// if (isHovering) {
// buttonText = "再次解答";
// } else {
// buttonText = "已解答👌";
// }
// }
// } else {
// buttonText = "无法解答";
// }

if (solve.couldSolve) {
if (solve.hasSolved) {
if (isHovering) {
buttonText = "再次解答";
} else {
buttonText = "已解答👌";
}
}
} else {
buttonText = "无法解答";
}
// TODO 完成这个功能
const buttonText = "无法解答";

return (
<Button
disabled={!solve.couldSolve}
// TODO
disabled
// disabled={!solve.couldSolve}
onClick={() => {
solve.solveThis(answerText);
}}
Expand Down Expand Up @@ -128,7 +126,7 @@ export function QuestionRecord({ record }: { record: IQuestionRecord }) {
// backgroundColor: "limegreen", // "red" "yellow"
backgroundColor: record.content.info.color
? record.content.info.color
: defaultColorMapping[record.content.info.content] ?? "gray",
: theme.answerTypeColorMapping[record.content.info.content] ?? "gray",
color: "white",
fontFamily: "华文新魏",
}}
Expand Down Expand Up @@ -181,6 +179,12 @@ export function QuestionRecord({ record }: { record: IQuestionRecord }) {
}}
>
{/* hover时,显示在最后一行的最右边 */}
{record.action &&
record.action.map(({ children, onClick }, index) => (
<Button key={index} onClick={onClick}>
{children}
</Button>
))}
<SolveButton content={record.content} />
<CopyButton answerText={record.content.answerText} />
</div>
Expand Down
17 changes: 9 additions & 8 deletions src/views/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@ const ButtonContainer = styled.span<{ disabled: boolean }>(
}),
);

export interface IButtonProps {
onClick?: () => void;
children: React.ReactNode;
disabled?: boolean;
style?: React.CSSProperties;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
}
export default function Button({
onClick,
children,
disabled,
style,
onMouseEnter,
onMouseLeave,
}: {
onClick?: () => void;
children: React.ReactNode;
disabled?: boolean;
style?: React.CSSProperties;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
}) {
}: IButtonProps) {
return (
<ButtonContainer
style={{
Expand Down
7 changes: 5 additions & 2 deletions src/views/components/PopOver.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ export default function PopOver({
disabled = false,
offsetPixel = 8,
backgroundColor = "rgba(104, 101, 101, 0.89)",
delay = false,
}: {
children: React.ReactNode;
content: React.ReactNode;
placement?: Placement;
disabled?: boolean;
offsetPixel?: number;
backgroundColor?: string;
delay?: boolean;
}) {
const theme = useTheme();

Expand All @@ -50,7 +52,7 @@ export default function PopOver({
const hover = useHover(context, {
delay: {
// open: 200,
close: 250,
close: delay ? 250 : undefined,
},
});

Expand All @@ -77,7 +79,8 @@ export default function PopOver({
left: x ?? 0,
backgroundColor,
// backgroundColor: theme.colors.secondary,
width: "max-content",
// width: "max-content",
maxWidth: "400px",
color: "white",
borderRadius: 4,
fontSize: 20,
Expand Down

0 comments on commit 54771e6

Please sign in to comment.