typography overhaul, custom scrollbars, import/export, settings UI
This commit is contained in:
@@ -1,60 +1,23 @@
|
||||
import { useRef } from "react";
|
||||
import { Download, Upload } from "lucide-react";
|
||||
import { Upload } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useAppStore } from "@/stores/app-store";
|
||||
import { useBoardStore } from "@/stores/board-store";
|
||||
import { useToastStore } from "@/stores/toast-store";
|
||||
import { saveBoard } from "@/lib/storage";
|
||||
import {
|
||||
exportBoardAsJson,
|
||||
exportBoardAsCsv,
|
||||
importBoardFromJson,
|
||||
importFromTrelloJson,
|
||||
} from "@/lib/import-export";
|
||||
|
||||
function downloadBlob(content: string, filename: string, mimeType: string) {
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export function ImportExportButtons() {
|
||||
export function ImportButton() {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const addToast = useToastStore((s) => s.addToast);
|
||||
const board = useBoardStore((s) => s.board);
|
||||
const refreshBoards = useAppStore((s) => s.refreshBoards);
|
||||
const setView = useAppStore((s) => s.setView);
|
||||
const addRecentBoard = useAppStore((s) => s.addRecentBoard);
|
||||
const openBoard = useBoardStore((s) => s.openBoard);
|
||||
|
||||
function handleExportJson() {
|
||||
if (!board) return;
|
||||
const json = exportBoardAsJson(board);
|
||||
const safeName = board.title.replace(/[^a-zA-Z0-9-_]/g, "_");
|
||||
downloadBlob(json, `${safeName}.json`, "application/json");
|
||||
addToast("Board exported as JSON", "success");
|
||||
}
|
||||
|
||||
function handleExportCsv() {
|
||||
if (!board) return;
|
||||
const csv = exportBoardAsCsv(board);
|
||||
const safeName = board.title.replace(/[^a-zA-Z0-9-_]/g, "_");
|
||||
downloadBlob(csv, `${safeName}.csv`, "text/csv");
|
||||
addToast("Board exported as CSV", "success");
|
||||
}
|
||||
|
||||
function handleImportClick() {
|
||||
fileInputRef.current?.click();
|
||||
}
|
||||
@@ -94,8 +57,7 @@ export function ImportExportButtons() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
{/* Import button */}
|
||||
<>
|
||||
<Button variant="outline" size="sm" onClick={handleImportClick}>
|
||||
<Upload className="size-4" />
|
||||
Import
|
||||
@@ -107,24 +69,6 @@ export function ImportExportButtons() {
|
||||
onChange={handleFileSelected}
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
{/* Export dropdown */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm" disabled={!board}>
|
||||
<Download className="size-4" />
|
||||
Export
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={handleExportJson}>
|
||||
Export as JSON
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleExportCsv}>
|
||||
Export as CSV
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user