Move forms to sidepanel

表单组件迁移到 SidePanel
原 Popup 引导打开 SidePanel
优化表单组件和 Hooks,获取 Form 组件 Ref
This commit is contained in:
2024-04-12 14:09:38 +08:00
parent 288de31580
commit 0240b13123
10 changed files with 152 additions and 54 deletions
+112
View File
@@ -0,0 +1,112 @@
import { useEffect, useRef } from "react";
import { sendToBackground } from "@plasmohq/messaging";
import useForm from "~hooks/useForm";
import Tab from "~components/ui/tab";
import Form from "~components/ui/form";
import { add } from "~components/ui/message/utils";
interface FormValue {
title: string;
link: string;
desc: string;
tips: string;
author: string;
image: string;
sitename: string;
tags: string;
}
const getInfo = async () => {
const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
if (tab) {
return await chrome.tabs.sendMessage(tab.id, { type: 'toolbox:getInfo' });
}
}
const submitForm = (body: FormValue) => {
sendToBackground({
name: "read",
body: {
action: "submitAddForm",
values: body,
},
}).then((res) => {
if (res.status === "Success") {
add({
content: "提交成功",
});
}
else {
add({
content: res.msg,
});
}
}).catch((e) => {
if (e instanceof Error) {
add({
content: e.message,
});
}
});
}
function Read() {
const formRef = useRef<HTMLFormElement>();
const { bindInput, setValues, onSubmit } = useForm<FormValue>({
initialValues: {
tags: "",
},
});
useEffect(() => {
getInfo().then((res) => {
if (!res) {
return;
}
setValues(res);
})
}, []);
return (
<Tab>
<Tab.Body>
<Form ref={formRef} onSubmit={onSubmit(submitForm)}>
<input {...bindInput("title")} required placeholder="标题" />
<input {...bindInput("link")} required placeholder="链接" />
<div>
<label htmlFor="desc">概括</label>
<textarea {...bindInput("desc")} rows={5} required placeholder="概括"></textarea>
</div>
<div>
<label htmlFor="tips">评论 / 推荐理由</label>
<textarea {...bindInput("tips")} rows={5} placeholder="推荐理由"></textarea>
</div>
<div>
<label htmlFor="author">作者</label>
<input {...bindInput("author")} placeholder="作者" />
</div>
<div>
<label htmlFor="image">插图</label>
<input {...bindInput("image")} placeholder="插图" />
</div>
<div>
<label htmlFor="sitename">站点名称</label>
<input {...bindInput("sitename")} placeholder="站点名称" />
</div>
<div>
<label htmlFor="tags">标签</label>
<input {...bindInput("tags")} placeholder="标签" />
</div>
</Form>
</Tab.Body>
<Tab.Footer>
<button onClick={() => formRef.current?.requestSubmit()}>提交</button>
</Tab.Footer>
</Tab>
);
}
export default Read;
+144
View File
@@ -0,0 +1,144 @@
import { useState, useEffect, useRef } from "react";
import { sendToBackground } from "@plasmohq/messaging";
import useForm from "~hooks/useForm";
import Tab from "~components/ui/tab";
import Form from "~components/ui/form";
import { add } from "~components/ui/message/utils";
import Placeholder from "~components/ui/placeholder";
import styles from "./toy.module.less";
interface FormValue {
name: string;
ename: string;
project: string;
made: string;
sale: string;
type: number;
desc: string;
}
const getInfo = async () => {
let tab: chrome.tabs.Tab;
[tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
if (tab && !tab.url.includes("chrome")) {
const a = await chrome.tabs.sendMessage(tab.id, { type: 'toolbox:getBiliToy' });
console.log('get toy:', a);
return a;
}
}
function Toy() {
const formRef = useRef<HTMLFormElement>();
const { bindInput, setValues, onSubmit } = useForm<FormValue>({
initialValues: {
type: 3,
},
});
const [imgs, setImgs] = useState([]);
const [currentImg, setCurrentImg] = useState(0);
const submitForm = (body: FormValue) => {
sendToBackground({
name: "bili",
body: {
action: "submitAddForm",
values: {
...body,
imageUrl: imgs[0],
},
},
}).then((res) => {
if (res.status === "Success") {
add({
content: "提交成功",
});
}
else {
add({
content: res.msg,
});
}
});
}
useEffect(() => {
getInfo().then((res) => {
if (!res) {
return;
}
const { images, ...others } = res;
setImgs(images);
setCurrentImg(0);
// ! 必须等组件渲染出来才能设置值,这里是个问题
window.requestAnimationFrame(() => {
setValues(others);
});
})
}, []);
return (
<Placeholder className={styles.bili} show={imgs.length === 0} value="非 B 站会员购页面">
<Tab>
<Tab.Body>
<div className={styles.image}>
<img src={imgs[currentImg]} alt="" />
<div className={styles.selector}>
{imgs.map((item, index) => (
<img key={index} src={item} alt="" onClick={() => setCurrentImg(index)} />
))}
</div>
</div>
<Form className={styles.form} onSubmit={onSubmit(submitForm)}>
<div>
<label htmlFor="name">中文名</label>
<input {...bindInput("name")} required placeholder="中文名" />
</div>
<div>
<label htmlFor="ename">英文名</label>
<input {...bindInput("ename")} placeholder="英文名" />
</div>
<div>
<label htmlFor="project">作品</label>
<input {...bindInput("project")} placeholder="作品" />
</div>
<div>
<label htmlFor="made">制作</label>
<input {...bindInput("made")} placeholder="制作" />
</div>
<div>
<label htmlFor="sale">发售日期</label>
<input {...bindInput("sale")} placeholder="发售日期" />
</div>
<div>
<label htmlFor="type">状态</label>
<select {...bindInput("type")} required>
<option value={1}>已到货</option>
<option value={2}>已预订</option>
<option value={3}>想要但没钱</option>
</select>
</div>
<div>
<label htmlFor="desc">描述</label>
<textarea {...bindInput("desc")} rows={5} placeholder="描述"></textarea>
</div>
</Form>
</Tab.Body>
<Tab.Footer>
<button onClick={() => formRef.current?.requestSubmit()}>提交</button>
</Tab.Footer>
</Tab>
</Placeholder>
);
}
export default Toy;
+24
View File
@@ -0,0 +1,24 @@
.bili {
.image {
margin-bottom: 2em;
img {
border-radius: .5em;
}
}
.selector {
gap: 1em;
display: flex;
flex-wrap: wrap;
margin-top: 1em;
img {
width: 3em;
height: 3em;
cursor: pointer;
border-radius: .5em;
border: 1px solid #eee;
}
}
}
+10 -4
View File
@@ -1,4 +1,4 @@
import type { FormHTMLAttributes } from "react";
import { useImperativeHandle, type FormHTMLAttributes, useRef, forwardRef, type Ref } from "react";
import { clsn } from "~utils";
import styles from "./form.module.less";
@@ -6,12 +6,18 @@ interface FormProps extends FormHTMLAttributes<HTMLFormElement> {
}
function Form({ children, className, ...props }: FormProps) {
function Form({ children, className, ...props }: FormProps, ref: Ref<HTMLFormElement>) {
const formRef = useRef();
useImperativeHandle(ref, () => {
return formRef.current;
}, []);
return (
<form className={clsn(styles.form, className)} {...props}>
<form ref={formRef} className={clsn(styles.form, className)} {...props}>
{children}
</form>
);
}
export default Form;
export default forwardRef(Form);
+8
View File
@@ -35,4 +35,12 @@ Tab.Body = function Body({ children }: PropsWithChildren) {
);
}
Tab.Footer = function Footer({ children }: PropsWithChildren) {
return (
<footer className={styles.footer}>
{children}
</footer>
);
}
export default Tab;
+17 -2
View File
@@ -1,7 +1,14 @@
.tab {
.header, .footer {
left: 0;
right: 0;
padding: .75em 1em;
position: sticky;
background-color: #fff;
}
.header {
padding: .75em;
position: relative;
top: 0;
text-align: center;
border-bottom: 1px solid #eee;
@@ -27,4 +34,12 @@
.body {
padding: 1em;
}
.footer {
bottom: 0;
border-top: 1px solid #eee;
display: flex;
align-items: flex-end;
}
}