feat(web): connect quick share flow
Signed-off-by: JustMe <mr.toing@outlook.com>
This commit is contained in:
1 parent
53768a195d
commit
e02e024605
138 files changed
+10614
No files matched your search
@@ -0,0 +1,67 @@
|
||||
import { useRef } from 'react';
|
||||
import { readClipboard } from '@/lib/clipboard';
|
||||
import { useMediaQuery } from '@/lib/useMediaQuery';
|
||||
import { Icon, type IconName } from '@/ui/Icon';
|
||||
import styles from './ActionBar.module.css';
|
||||
|
||||
interface Props {
|
||||
/** dock:手机底部动作栏;toolbar:电脑列表上方的工具条 */
|
||||
layout: 'dock' | 'toolbar';
|
||||
/** 添加文件、文本:频道发到频道里,分享放进草稿 */
|
||||
onFiles: (files: File[]) => void;
|
||||
onText: (text: string) => void;
|
||||
onCompose: () => void;
|
||||
/** 不传则不显示扫码(分享草稿里不需要) */
|
||||
onScan?: () => void;
|
||||
}
|
||||
|
||||
interface Action {
|
||||
key: string;
|
||||
label: string;
|
||||
icon: IconName;
|
||||
/** 触屏专有(相册、拍照) */
|
||||
touchOnly?: boolean;
|
||||
run: () => void;
|
||||
}
|
||||
|
||||
export function ActionBar({ layout, onFiles, onText, onCompose, onScan }: Props) {
|
||||
const touch = useMediaQuery('(pointer: coarse)');
|
||||
const fileRef = useRef<HTMLInputElement>(null);
|
||||
const galleryRef = useRef<HTMLInputElement>(null);
|
||||
const cameraRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
async function paste() {
|
||||
const clip = await readClipboard();
|
||||
if (!clip) return onCompose(); // 无权限时退回到手动输入
|
||||
if (clip.files.length > 0) onFiles(clip.files);
|
||||
else if (clip.text.trim()) onText(clip.text.trim());
|
||||
}
|
||||
|
||||
const actions: Action[] = [
|
||||
{ key: 'paste', label: '粘贴', icon: 'paste', run: () => void paste() },
|
||||
{ key: 'file', label: '文件', icon: 'file', run: () => fileRef.current?.click() },
|
||||
{ key: 'gallery', label: '相册', icon: 'image', touchOnly: true, run: () => galleryRef.current?.click() },
|
||||
{ key: 'camera', label: '拍照', icon: 'camera', touchOnly: true, run: () => cameraRef.current?.click() },
|
||||
...(onScan ? [{ key: 'scan', label: '扫码', icon: 'scan' as const, run: onScan }] : []),
|
||||
{ key: 'write', label: '写', icon: 'edit', run: onCompose },
|
||||
];
|
||||
const shown = actions.filter((a) => layout === 'dock' || touch || !a.touchOnly);
|
||||
|
||||
function onPick(files: FileList | null) {
|
||||
if (files?.length) onFiles([...files]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${styles.bar} ${styles[layout]}`} role="toolbar" aria-label="添加内容">
|
||||
<input ref={fileRef} type="file" multiple hidden onChange={(e) => { onPick(e.target.files); e.target.value = ''; }} />
|
||||
<input ref={galleryRef} type="file" accept="image/*,video/*" multiple hidden onChange={(e) => { onPick(e.target.files); e.target.value = ''; }} />
|
||||
<input ref={cameraRef} type="file" accept="image/*" capture="environment" hidden onChange={(e) => { onPick(e.target.files); e.target.value = ''; }} />
|
||||
{shown.map((a) => (
|
||||
<button key={a.key} type="button" className={styles.action} onClick={a.run}>
|
||||
<Icon name={a.icon} size={layout === 'dock' ? 22 : 18} />
|
||||
<span>{a.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user