forked from wesbos/bracket.engineer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.ts
More file actions
115 lines (97 loc) · 3.54 KB
/
app.ts
File metadata and controls
115 lines (97 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { exportTo3MF } from './export';
import { setupPreview } from "./preview";
import { createBracket, defaultParams } from "./psu-bracket";
interface BracketParams {
width: number;
depth: number;
height: number;
bracketThickness: number;
ribbingCount: number;
ribbingThickness: number;
holeDiameter: number;
earWidth: number;
hasBottom: boolean;
}
// Initialize the preview
const canvas = document.getElementById("preview") as HTMLCanvasElement;
const updateBracket = setupPreview(canvas);
const controls = document.querySelector<HTMLFormElement>("#controls");
// Get all range inputs
const inputs = Array.from(controls?.querySelectorAll<HTMLInputElement>("input") ?? []).filter(input => !input.classList.contains('value-display'));
// todo - I have a tip somewhere on an easy way to split this into two arrays
const displayInputs = Array.from(controls?.querySelectorAll<HTMLInputElement>("input") ?? []).filter(input => input.classList.contains('value-display'));
function parseFormData(data: FormData) {
const params: Record<string, any> = {};
for(const [key, value] of data.entries()) {
// First see if it's a checkbox
if(value === "on") {
params[key] = true;
} else {
const maybeNumber = parseFloat(value);
params[key] = isNaN(maybeNumber) ? value : maybeNumber;
}
}
return params as BracketParams;
}
function displayValues(params: BracketParams) {
for(const input of inputs) {
const label = input.nextElementSibling as HTMLDivElement;
const unit = input.getAttribute("data-unit") ?? 'mm';
if(label && label.classList.contains('value-display')) {
label.value = `${input.value}`;
}
}
// Also pop the color on the root so we can use in css
document.documentElement.style.setProperty('--color', params.color);
}
function handleInput(e: Event) {
// If someone types into a valueDisplay, update the input
if(e.target.classList.contains('value-display')) {
const input = e.target.previousElementSibling as HTMLInputElement;
input.value = e.target.value;
}
const data = new FormData(controls);
const params = parseFormData(data);
displayValues(params);
updateBracket(params);
}
function updateUrl() {
const data = new FormData(controls);
const url = new URLSearchParams(data);
history.pushState({}, '', `?${url.toString()}`);
}
controls.addEventListener("input", handleInput);
controls.addEventListener("change", updateUrl);
// On page load, check if there is a url param and parse it
function restoreState() {
const url = new URLSearchParams(window.location.search);
const params = {
defaultParams,
...parseFormData(url)
}
// Merge in any defaults
// Restore any params from the URL
for(const [key, value] of Object.entries(params)) {
const input = document.getElementById(key) as HTMLInputElement;
if(input) {
input.value = value.toString();
}
}
// trigger an input event to update the values
const event = new Event('input', { bubbles: true });
controls.dispatchEvent(event);
}
restoreState();
const exportButton = document.getElementById("export-button") as HTMLButtonElement;
exportButton.addEventListener("click", async () => {
const params = parseFormData(new FormData(controls));
const model = createBracket(params);
const dimensions = `${params.width}x${params.depth}x${params.height}`;
const blob = await exportTo3MF(model, dimensions);
const url = URL.createObjectURL(blob);
// download the blob
const a = document.createElement("a");
a.href = url;
a.download = `bracket-${dimensions}.3mf`;
a.click();
});