@tscircuit/fake-snippets 0.0.74 → 0.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,113 @@
1
+ import { useState } from "react"
2
+ import { Button } from "../ui/button"
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ DialogFooter,
9
+ } from "../ui/dialog"
10
+ import { Label } from "../ui/label"
11
+ import {
12
+ Select,
13
+ SelectContent,
14
+ SelectItem,
15
+ SelectTrigger,
16
+ SelectValue,
17
+ } from "../ui/select"
18
+ import { Checkbox } from "../ui/checkbox"
19
+ import { AnyCircuitElement } from "circuit-json"
20
+ import { createUseDialog } from "./create-use-dialog"
21
+ import {
22
+ downloadPcbSvg,
23
+ DownloadPcbSvgOptions,
24
+ } from "@/lib/download-fns/download-pcb-svg"
25
+
26
+ interface PcbDownloadDialogProps {
27
+ open: boolean
28
+ onOpenChange: (open: boolean) => void
29
+ circuitJson: AnyCircuitElement[]
30
+ fileName: string
31
+ }
32
+
33
+ export const PcbDownloadDialog = ({
34
+ open,
35
+ onOpenChange,
36
+ circuitJson,
37
+ fileName,
38
+ }: PcbDownloadDialogProps) => {
39
+ const [layer, setLayer] = useState<"all" | "top" | "bottom">("all")
40
+ const [drawPadding, setDrawPadding] = useState(true)
41
+ const [transparentBg, setTransparentBg] = useState(false)
42
+ const [matchAspectRatio, setMatchAspectRatio] = useState(false)
43
+
44
+ const handleDownload = () => {
45
+ const options: DownloadPcbSvgOptions = {
46
+ layer,
47
+ drawPaddingOutsideBoard: drawPadding,
48
+ backgroundColor: transparentBg ? "transparent" : "#000",
49
+ matchAspectRatio,
50
+ }
51
+ downloadPcbSvg(circuitJson, fileName, options)
52
+ onOpenChange(false)
53
+ }
54
+
55
+ return (
56
+ <Dialog open={open} onOpenChange={onOpenChange}>
57
+ <DialogContent>
58
+ <DialogHeader>
59
+ <DialogTitle>Download PCB SVG</DialogTitle>
60
+ </DialogHeader>
61
+ <div className="space-y-4 py-2">
62
+ <div className="grid grid-cols-4 items-center gap-4">
63
+ <Label htmlFor="layer" className="text-right">
64
+ Layer
65
+ </Label>
66
+ <Select value={layer} onValueChange={(v) => setLayer(v as any)}>
67
+ <SelectTrigger id="layer" className="col-span-3">
68
+ <SelectValue placeholder="Layer" />
69
+ </SelectTrigger>
70
+ <SelectContent className="!z-[999]">
71
+ <SelectItem value="all">All</SelectItem>
72
+ <SelectItem value="top">Top</SelectItem>
73
+ <SelectItem value="bottom">Bottom</SelectItem>
74
+ </SelectContent>
75
+ </Select>
76
+ </div>
77
+ <div className="flex items-center space-x-2">
78
+ <Checkbox
79
+ id="padding"
80
+ checked={drawPadding}
81
+ onCheckedChange={(v) => setDrawPadding(Boolean(v))}
82
+ />
83
+ <Label htmlFor="padding">Draw Padding and Board Outline</Label>
84
+ </div>
85
+ <div className="flex items-center space-x-2">
86
+ <Checkbox
87
+ id="transparentBg"
88
+ checked={transparentBg}
89
+ onCheckedChange={(v) => setTransparentBg(Boolean(v))}
90
+ />
91
+ <Label htmlFor="transparentBg">Transparent Background</Label>
92
+ </div>
93
+ <div className="flex items-center space-x-2">
94
+ <Checkbox
95
+ id="matchAspectRatio"
96
+ checked={matchAspectRatio}
97
+ onCheckedChange={(v) => setMatchAspectRatio(Boolean(v))}
98
+ />
99
+ <Label htmlFor="matchAspectRatio">Match Aspect Ratio</Label>
100
+ </div>
101
+ </div>
102
+ <DialogFooter>
103
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
104
+ Cancel
105
+ </Button>
106
+ <Button onClick={handleDownload}>Download</Button>
107
+ </DialogFooter>
108
+ </DialogContent>
109
+ </Dialog>
110
+ )
111
+ }
112
+
113
+ export const usePcbDownloadDialog = createUseDialog(PcbDownloadDialog)
@@ -0,0 +1,35 @@
1
+ import { AnyCircuitElement } from "circuit-json"
2
+ import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
3
+ import { saveAs } from "file-saver"
4
+
5
+ export interface DownloadPcbSvgOptions {
6
+ layer?: "all" | "top" | "bottom"
7
+ drawPaddingOutsideBoard?: boolean
8
+ backgroundColor?: string
9
+ matchAspectRatio?: boolean
10
+ }
11
+
12
+ export const downloadPcbSvg = (
13
+ circuitJson: AnyCircuitElement[],
14
+ fileName: string,
15
+ options: DownloadPcbSvgOptions = {},
16
+ ) => {
17
+ const convertOptions: any = {}
18
+ if (options.layer && options.layer !== "all") {
19
+ convertOptions.layer = options.layer
20
+ }
21
+ if (options.matchAspectRatio) {
22
+ convertOptions.matchBoardAspectRatio = true
23
+ }
24
+ if (typeof options.drawPaddingOutsideBoard === "boolean") {
25
+ convertOptions.drawPaddingOutsideBoard = options.drawPaddingOutsideBoard
26
+ }
27
+ if (options.backgroundColor) {
28
+ convertOptions.backgroundColor = options.backgroundColor
29
+ }
30
+
31
+ const svg = convertCircuitJsonToPcbSvg(circuitJson, convertOptions)
32
+
33
+ const blob = new Blob([svg], { type: "image/svg+xml" })
34
+ saveAs(blob, fileName + ".svg")
35
+ }
@@ -3,7 +3,18 @@ import en from "javascript-time-ago/locale/en"
3
3
 
4
4
  TimeAgo.addDefaultLocale(en)
5
5
 
6
- export const timeAgo = (date: Date) => {
7
- const timeAgo = new TimeAgo("en-US")
8
- return timeAgo.format(date)
6
+ export const timeAgo = (
7
+ date: Date | string | null | undefined,
8
+ fallback = "???",
9
+ ) => {
10
+ if (!date) return fallback
11
+ if (typeof date === "string") {
12
+ date = new Date(date)
13
+ }
14
+ try {
15
+ const timeAgo = new TimeAgo("en-US")
16
+ return timeAgo.format(date)
17
+ } catch (error) {
18
+ return fallback
19
+ }
9
20
  }