feat(md-deck): Add fallback copy dialog for clipboard failure

This commit is contained in:
hyper
2026-08-07 12:13:40 +08:00
parent e3daac3080
commit 9e081891df
2 changed files with 94 additions and 31 deletions
+60 -1
View File
@@ -1,3 +1,5 @@
import { Show, createSignal, onMount } from "solid-js";
import { Portal } from "solid-js/web";
import type { DeckStore } from "./hooks/deckStore";
export interface DeckHeaderProps {
@@ -9,8 +11,28 @@ export interface DeckHeaderProps {
*/
export function DeckHeader(props: DeckHeaderProps) {
const { store } = props;
const [showCopyFallback, setShowCopyFallback] = createSignal(false);
const [fallbackCode, setFallbackCode] = createSignal("");
let textareaRef: HTMLTextAreaElement | undefined;
onMount(() => {
if (showCopyFallback() && textareaRef) {
textareaRef.focus();
textareaRef.select();
}
});
const handleCopy = () => {
store.actions.copyCode((code) => {
setFallbackCode(code);
setShowCopyFallback(true);
});
};
const closeFallback = () => setShowCopyFallback(false);
return (
<>
<div class="flex items-center gap-2 border-b border-gray-200 pb-2 mb-4">
{/* 编辑按钮 */}
<button
@@ -34,11 +56,48 @@ export function DeckHeader(props: DeckHeaderProps) {
{/* 复制代码按钮 */}
<button
onClick={() => store.actions.copyCode()}
onClick={handleCopy}
class="px-2 py-1 rounded text-xs font-medium transition-colors cursor-pointer bg-purple-100 text-purple-600 hover:bg-purple-200"
>
📋
</button>
</div>
{/* 剪贴板不可用时的手动复制弹窗 */}
<Show when={showCopyFallback()}>
<Portal>
<div
class="fixed inset-0 bg-black/50 z-60 flex items-center justify-center p-4"
onClick={closeFallback}
>
<div
class="bg-white rounded-lg shadow-xl w-full max-w-lg p-4"
onClick={(e) => e.stopPropagation()}
>
<h3 class="font-bold mb-2"></h3>
<p class="text-sm text-gray-600 mb-3">
HTTPS
</p>
<textarea
ref={textareaRef}
readonly
value={fallbackCode()}
class="w-full h-40 border border-gray-300 rounded px-3 py-2 text-xs font-mono bg-gray-50 resize-none"
onFocus={(e) => e.currentTarget.select()}
onClick={(e) => e.currentTarget.select()}
/>
<div class="flex justify-end gap-2 mt-3">
<button
onClick={closeFallback}
class="px-3 py-1.5 rounded text-sm font-medium cursor-pointer bg-gray-100 text-gray-700 hover:bg-gray-200"
>
</button>
</div>
</div>
</div>
</Portal>
</Show>
</>
);
}
+7 -3
View File
@@ -145,7 +145,7 @@ export interface DeckActions {
clearError: () => void;
generateCode: (backLayersStr?: string) => string;
copyCode: (backLayersStr?: string) => Promise<void>;
copyCode: (fallback?: (code: string) => void) => Promise<void>;
setExporting: (exporting: boolean) => void;
exportDeck: () => void;
@@ -514,15 +514,19 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
return parts.join("");
};
const copyCode = async (backLayersStr?: string) => {
const code = generateCode(backLayersStr);
const copyCode = async (fallback?: (code: string) => void) => {
const code = generateCode();
try {
await navigator.clipboard.writeText(code);
alert("已复制到剪贴板!");
} catch (err) {
console.error("复制失败:", err);
if (fallback) {
fallback(code);
} else {
alert("复制失败,请手动复制");
}
}
};
const setExporting = (exporting: boolean) =>