@jjlmoya/utils-diy 1.11.0 → 1.13.0
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.
- package/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +3 -1
- package/src/index.ts +2 -0
- package/src/pages/[locale]/[slug].astro +28 -12
- package/src/tests/locale_completeness.test.ts +7 -9
- package/src/tests/shared-test-helpers.ts +56 -0
- package/src/tests/tool_exports.test.ts +34 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/balusterCalculator/bibliography.astro +2 -10
- package/src/tool/balusterCalculator/bibliography.ts +16 -0
- package/src/tool/balusterCalculator/i18n/de.ts +2 -15
- package/src/tool/balusterCalculator/i18n/en.ts +2 -15
- package/src/tool/balusterCalculator/i18n/es.ts +2 -15
- package/src/tool/balusterCalculator/i18n/fr.ts +2 -15
- package/src/tool/balusterCalculator/i18n/id.ts +2 -15
- package/src/tool/balusterCalculator/i18n/it.ts +2 -15
- package/src/tool/balusterCalculator/i18n/ja.ts +2 -15
- package/src/tool/balusterCalculator/i18n/ko.ts +2 -15
- package/src/tool/balusterCalculator/i18n/nl.ts +2 -15
- package/src/tool/balusterCalculator/i18n/pl.ts +2 -15
- package/src/tool/balusterCalculator/i18n/pt.ts +2 -15
- package/src/tool/balusterCalculator/i18n/ru.ts +2 -15
- package/src/tool/balusterCalculator/i18n/sv.ts +2 -15
- package/src/tool/balusterCalculator/i18n/tr.ts +2 -15
- package/src/tool/balusterCalculator/i18n/zh.ts +2 -15
- package/src/tool/balusterCalculator/seo.astro +1 -1
- package/src/tool/clayCalculator/bibliography.astro +2 -10
- package/src/tool/clayCalculator/bibliography.ts +12 -0
- package/src/tool/clayCalculator/i18n/de.ts +2 -11
- package/src/tool/clayCalculator/i18n/en.ts +2 -11
- package/src/tool/clayCalculator/i18n/es.ts +2 -11
- package/src/tool/clayCalculator/i18n/fr.ts +2 -11
- package/src/tool/clayCalculator/i18n/id.ts +2 -11
- package/src/tool/clayCalculator/i18n/it.ts +2 -11
- package/src/tool/clayCalculator/i18n/ja.ts +2 -11
- package/src/tool/clayCalculator/i18n/ko.ts +2 -11
- package/src/tool/clayCalculator/i18n/nl.ts +2 -11
- package/src/tool/clayCalculator/i18n/pl.ts +2 -11
- package/src/tool/clayCalculator/i18n/pt.ts +2 -11
- package/src/tool/clayCalculator/i18n/ru.ts +2 -11
- package/src/tool/clayCalculator/i18n/sv.ts +2 -11
- package/src/tool/clayCalculator/i18n/tr.ts +2 -11
- package/src/tool/clayCalculator/i18n/zh.ts +2 -11
- package/src/tool/clayCalculator/seo.astro +1 -1
- package/src/tool/concreteCalculator/bibliography.astro +2 -9
- package/src/tool/concreteCalculator/bibliography.ts +7 -0
- package/src/tool/concreteCalculator/i18n/de.ts +2 -15
- package/src/tool/concreteCalculator/i18n/en.ts +2 -15
- package/src/tool/concreteCalculator/i18n/es.ts +2 -15
- package/src/tool/concreteCalculator/i18n/fr.ts +2 -15
- package/src/tool/concreteCalculator/i18n/id.ts +2 -15
- package/src/tool/concreteCalculator/i18n/it.ts +2 -15
- package/src/tool/concreteCalculator/i18n/ja.ts +2 -15
- package/src/tool/concreteCalculator/i18n/ko.ts +2 -15
- package/src/tool/concreteCalculator/i18n/nl.ts +2 -15
- package/src/tool/concreteCalculator/i18n/pl.ts +2 -15
- package/src/tool/concreteCalculator/i18n/pt.ts +2 -15
- package/src/tool/concreteCalculator/i18n/ru.ts +2 -15
- package/src/tool/concreteCalculator/i18n/sv.ts +2 -15
- package/src/tool/concreteCalculator/i18n/tr.ts +2 -15
- package/src/tool/concreteCalculator/i18n/zh.ts +2 -15
- package/src/tool/concreteCalculator/seo.astro +2 -1
- package/src/tool/cutOptimizer/bibliography.astro +2 -9
- package/src/tool/cutOptimizer/bibliography.ts +12 -0
- package/src/tool/cutOptimizer/i18n/de.ts +2 -11
- package/src/tool/cutOptimizer/i18n/en.ts +2 -11
- package/src/tool/cutOptimizer/i18n/es.ts +2 -11
- package/src/tool/cutOptimizer/i18n/fr.ts +2 -11
- package/src/tool/cutOptimizer/i18n/id.ts +2 -11
- package/src/tool/cutOptimizer/i18n/it.ts +2 -11
- package/src/tool/cutOptimizer/i18n/ja.ts +2 -11
- package/src/tool/cutOptimizer/i18n/ko.ts +2 -11
- package/src/tool/cutOptimizer/i18n/nl.ts +2 -11
- package/src/tool/cutOptimizer/i18n/pl.ts +2 -11
- package/src/tool/cutOptimizer/i18n/pt.ts +2 -11
- package/src/tool/cutOptimizer/i18n/ru.ts +2 -11
- package/src/tool/cutOptimizer/i18n/sv.ts +2 -11
- package/src/tool/cutOptimizer/i18n/tr.ts +2 -11
- package/src/tool/cutOptimizer/i18n/zh.ts +2 -11
- package/src/tool/cutOptimizer/seo.astro +2 -1
- package/src/tool/drillCalculator/bibliography.astro +2 -9
- package/src/tool/drillCalculator/bibliography.ts +12 -0
- package/src/tool/drillCalculator/i18n/de.ts +2 -5
- package/src/tool/drillCalculator/i18n/en.ts +2 -5
- package/src/tool/drillCalculator/i18n/es.ts +2 -5
- package/src/tool/drillCalculator/i18n/fr.ts +2 -5
- package/src/tool/drillCalculator/i18n/id.ts +2 -5
- package/src/tool/drillCalculator/i18n/it.ts +2 -5
- package/src/tool/drillCalculator/i18n/ja.ts +2 -5
- package/src/tool/drillCalculator/i18n/ko.ts +2 -5
- package/src/tool/drillCalculator/i18n/nl.ts +2 -5
- package/src/tool/drillCalculator/i18n/pl.ts +2 -5
- package/src/tool/drillCalculator/i18n/pt.ts +2 -5
- package/src/tool/drillCalculator/i18n/ru.ts +2 -5
- package/src/tool/drillCalculator/i18n/sv.ts +2 -5
- package/src/tool/drillCalculator/i18n/tr.ts +2 -5
- package/src/tool/drillCalculator/i18n/zh.ts +2 -5
- package/src/tool/drillCalculator/seo.astro +2 -1
- package/src/tool/drillSharpener/bibliography.astro +2 -9
- package/src/tool/drillSharpener/bibliography.ts +16 -0
- package/src/tool/drillSharpener/i18n/de.ts +2 -5
- package/src/tool/drillSharpener/i18n/en.ts +2 -6
- package/src/tool/drillSharpener/i18n/es.ts +2 -6
- package/src/tool/drillSharpener/i18n/fr.ts +2 -6
- package/src/tool/drillSharpener/i18n/id.ts +2 -5
- package/src/tool/drillSharpener/i18n/it.ts +2 -5
- package/src/tool/drillSharpener/i18n/ja.ts +2 -5
- package/src/tool/drillSharpener/i18n/ko.ts +2 -5
- package/src/tool/drillSharpener/i18n/nl.ts +2 -5
- package/src/tool/drillSharpener/i18n/pl.ts +2 -5
- package/src/tool/drillSharpener/i18n/pt.ts +2 -5
- package/src/tool/drillSharpener/i18n/ru.ts +2 -5
- package/src/tool/drillSharpener/i18n/sv.ts +2 -5
- package/src/tool/drillSharpener/i18n/tr.ts +2 -5
- package/src/tool/drillSharpener/i18n/zh.ts +2 -5
- package/src/tool/drillSharpener/seo.astro +2 -1
- package/src/tool/epoxyCalculator/bibliography.astro +2 -10
- package/src/tool/epoxyCalculator/bibliography.ts +20 -0
- package/src/tool/epoxyCalculator/i18n/de.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/en.ts +2 -19
- package/src/tool/epoxyCalculator/i18n/es.ts +2 -19
- package/src/tool/epoxyCalculator/i18n/fr.ts +2 -19
- package/src/tool/epoxyCalculator/i18n/id.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/it.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/ja.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/ko.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/nl.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/pl.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/pt.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/ru.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/sv.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/tr.ts +2 -5
- package/src/tool/epoxyCalculator/i18n/zh.ts +2 -5
- package/src/tool/epoxyCalculator/seo.astro +1 -1
- package/src/tool/furnitureFit/bibliography.astro +2 -9
- package/src/tool/furnitureFit/bibliography.ts +12 -0
- package/src/tool/furnitureFit/i18n/de.ts +2 -5
- package/src/tool/furnitureFit/i18n/en.ts +2 -5
- package/src/tool/furnitureFit/i18n/es.ts +2 -5
- package/src/tool/furnitureFit/i18n/fr.ts +2 -5
- package/src/tool/furnitureFit/i18n/id.ts +2 -5
- package/src/tool/furnitureFit/i18n/it.ts +2 -5
- package/src/tool/furnitureFit/i18n/ja.ts +2 -5
- package/src/tool/furnitureFit/i18n/ko.ts +2 -5
- package/src/tool/furnitureFit/i18n/nl.ts +2 -5
- package/src/tool/furnitureFit/i18n/pl.ts +2 -5
- package/src/tool/furnitureFit/i18n/pt.ts +2 -5
- package/src/tool/furnitureFit/i18n/ru.ts +2 -5
- package/src/tool/furnitureFit/i18n/sv.ts +2 -5
- package/src/tool/furnitureFit/i18n/tr.ts +2 -5
- package/src/tool/furnitureFit/i18n/zh.ts +2 -5
- package/src/tool/furnitureFit/seo.astro +2 -1
- package/src/tool/mortarCalculator/bibliography.astro +2 -9
- package/src/tool/mortarCalculator/bibliography.ts +20 -0
- package/src/tool/mortarCalculator/i18n/de.ts +2 -5
- package/src/tool/mortarCalculator/i18n/en.ts +2 -19
- package/src/tool/mortarCalculator/i18n/es.ts +2 -19
- package/src/tool/mortarCalculator/i18n/fr.ts +2 -19
- package/src/tool/mortarCalculator/i18n/id.ts +2 -5
- package/src/tool/mortarCalculator/i18n/it.ts +2 -5
- package/src/tool/mortarCalculator/i18n/ja.ts +2 -5
- package/src/tool/mortarCalculator/i18n/ko.ts +2 -5
- package/src/tool/mortarCalculator/i18n/nl.ts +2 -5
- package/src/tool/mortarCalculator/i18n/pl.ts +2 -5
- package/src/tool/mortarCalculator/i18n/pt.ts +2 -5
- package/src/tool/mortarCalculator/i18n/ru.ts +2 -5
- package/src/tool/mortarCalculator/i18n/sv.ts +2 -5
- package/src/tool/mortarCalculator/i18n/tr.ts +2 -5
- package/src/tool/mortarCalculator/i18n/zh.ts +2 -5
- package/src/tool/mortarCalculator/seo.astro +2 -1
- package/src/tool/passepartoutCalculator/bibliography.astro +2 -9
- package/src/tool/passepartoutCalculator/bibliography.ts +16 -0
- package/src/tool/passepartoutCalculator/i18n/de.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/en.ts +2 -15
- package/src/tool/passepartoutCalculator/i18n/es.ts +2 -15
- package/src/tool/passepartoutCalculator/i18n/fr.ts +2 -15
- package/src/tool/passepartoutCalculator/i18n/id.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/it.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/ja.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/ko.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/nl.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/pl.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/pt.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/ru.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/sv.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/tr.ts +2 -6
- package/src/tool/passepartoutCalculator/i18n/zh.ts +2 -6
- package/src/tool/passepartoutCalculator/seo.astro +2 -1
- package/src/tool/stairCalculator/bibliography.astro +2 -9
- package/src/tool/stairCalculator/bibliography.ts +16 -0
- package/src/tool/stairCalculator/i18n/de.ts +2 -5
- package/src/tool/stairCalculator/i18n/en.ts +2 -6
- package/src/tool/stairCalculator/i18n/es.ts +2 -6
- package/src/tool/stairCalculator/i18n/fr.ts +2 -6
- package/src/tool/stairCalculator/i18n/id.ts +2 -5
- package/src/tool/stairCalculator/i18n/it.ts +2 -5
- package/src/tool/stairCalculator/i18n/ja.ts +2 -5
- package/src/tool/stairCalculator/i18n/ko.ts +2 -5
- package/src/tool/stairCalculator/i18n/nl.ts +2 -5
- package/src/tool/stairCalculator/i18n/pl.ts +2 -5
- package/src/tool/stairCalculator/i18n/pt.ts +2 -5
- package/src/tool/stairCalculator/i18n/ru.ts +2 -5
- package/src/tool/stairCalculator/i18n/sv.ts +2 -5
- package/src/tool/stairCalculator/i18n/tr.ts +2 -5
- package/src/tool/stairCalculator/i18n/zh.ts +2 -5
- package/src/tool/stairCalculator/seo.astro +2 -1
- package/src/tool/thermalExpansionCalculator/bibliography.astro +2 -9
- package/src/tool/thermalExpansionCalculator/bibliography.ts +16 -0
- package/src/tool/thermalExpansionCalculator/i18n/de.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/en.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/es.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/fr.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/id.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/it.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/ja.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/ko.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/nl.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/pl.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/pt.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/ru.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/sv.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/tr.ts +2 -6
- package/src/tool/thermalExpansionCalculator/i18n/zh.ts +2 -6
- package/src/tool/thermalExpansionCalculator/seo.astro +2 -1
- package/src/tool/voltageDropCalculator/bibliography.astro +2 -9
- package/src/tool/voltageDropCalculator/bibliography.ts +16 -0
- package/src/tool/voltageDropCalculator/i18n/de.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/en.ts +2 -6
- package/src/tool/voltageDropCalculator/i18n/es.ts +2 -6
- package/src/tool/voltageDropCalculator/i18n/fr.ts +2 -6
- package/src/tool/voltageDropCalculator/i18n/id.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/it.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/ja.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/ko.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/nl.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/pl.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/pt.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/ru.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/sv.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/tr.ts +2 -5
- package/src/tool/voltageDropCalculator/i18n/zh.ts +2 -5
- package/src/tool/voltageDropCalculator/seo.astro +2 -1
- package/src/tool/workshopFractionConverter/bibliography.astro +6 -0
- package/src/tool/workshopFractionConverter/bibliography.ts +12 -0
- package/src/tool/workshopFractionConverter/client.ts +183 -0
- package/src/tool/workshopFractionConverter/component.astro +226 -0
- package/src/tool/workshopFractionConverter/converter.ts +110 -0
- package/src/tool/workshopFractionConverter/engine.ts +223 -0
- package/src/tool/workshopFractionConverter/entry.ts +24 -0
- package/src/tool/workshopFractionConverter/i18n/de.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/en.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/es.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/fr.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/id.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/it.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/ja.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/ko.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/nl.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/pl.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/pt.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/ru.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/sv.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/tr.ts +147 -0
- package/src/tool/workshopFractionConverter/i18n/zh.ts +147 -0
- package/src/tool/workshopFractionConverter/index.ts +11 -0
- package/src/tool/workshopFractionConverter/seo.astro +15 -0
- package/src/tool/workshopFractionConverter/types-client.ts +89 -0
- package/src/tool/workshopFractionConverter/ui-updater.ts +94 -0
- package/src/tool/workshopFractionConverter/ui.ts +38 -0
- package/src/tool/workshopFractionConverter/workshop-fraction-converter.css +563 -0
- package/src/tools.ts +2 -1
- package/src/types.ts +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { Fraction, HistoryEntry } from './types-client';
|
|
2
|
+
import { convertFraction, reverseSearch } from './converter';
|
|
3
|
+
import { displayResult, displayReverseResult } from './ui-updater';
|
|
4
|
+
|
|
5
|
+
export class WorkshopFractionConverter {
|
|
6
|
+
history: HistoryEntry[] = [];
|
|
7
|
+
currentMode = 'forward';
|
|
8
|
+
currentPrecision = 'carpentry';
|
|
9
|
+
ui: Record<string, string> = {};
|
|
10
|
+
constructor(ui?: Record<string, string>) {
|
|
11
|
+
if (ui) this.ui = ui;
|
|
12
|
+
this.setupEventListeners();
|
|
13
|
+
this.loadHistory();
|
|
14
|
+
}
|
|
15
|
+
setupEventListeners() {
|
|
16
|
+
document.querySelectorAll('#wfc-mode-toggle .wfc-mode-btn').forEach((btn) => {
|
|
17
|
+
btn.addEventListener('click', (e: Event) => this.handleModeToggle(e));
|
|
18
|
+
});
|
|
19
|
+
document.querySelectorAll('#wfc-precision-toggle .wfc-mode-btn').forEach((btn) => {
|
|
20
|
+
btn.addEventListener('click', (e: Event) => this.handlePrecisionToggle(e));
|
|
21
|
+
});
|
|
22
|
+
document.querySelectorAll('.wfc-stepper-btn').forEach((btn) => {
|
|
23
|
+
btn.addEventListener('click', (e: Event) => this.handleStepper(e));
|
|
24
|
+
});
|
|
25
|
+
document.querySelectorAll('.wfc-preset-btn').forEach((btn) => {
|
|
26
|
+
btn.addEventListener('click', (e: Event) => this.handlePreset(e));
|
|
27
|
+
});
|
|
28
|
+
this.setupActionButtons();
|
|
29
|
+
this.setupInputListeners();
|
|
30
|
+
}
|
|
31
|
+
setupActionButtons() {
|
|
32
|
+
document.getElementById('wfc-convert-btn')?.addEventListener('click', () => this.convert());
|
|
33
|
+
document.getElementById('wfc-clear-btn')?.addEventListener('click', () => this.clear());
|
|
34
|
+
document.getElementById('wfc-copy-btn')?.addEventListener('click', () => this.copyResult());
|
|
35
|
+
document.getElementById('wfc-save-btn')?.addEventListener('click', () => this.saveToHistory());
|
|
36
|
+
}
|
|
37
|
+
setupInputListeners() {
|
|
38
|
+
document.getElementById('wfc-whole')?.addEventListener('change', () => this.convert());
|
|
39
|
+
document.getElementById('wfc-numerator')?.addEventListener('change', () => this.convert());
|
|
40
|
+
document.getElementById('wfc-denominator')?.addEventListener('change', () => this.convert());
|
|
41
|
+
document.getElementById('wfc-reverse-mm')?.addEventListener('change', () => this.convert());
|
|
42
|
+
}
|
|
43
|
+
handleModeToggle(e: Event) {
|
|
44
|
+
const target = e.target as HTMLElement;
|
|
45
|
+
document.querySelectorAll('#wfc-mode-toggle .wfc-mode-btn').forEach((b) => b.classList.remove('active'));
|
|
46
|
+
target.classList.add('active');
|
|
47
|
+
this.currentMode = target.dataset.mode || 'forward';
|
|
48
|
+
this.toggleMode(this.currentMode);
|
|
49
|
+
}
|
|
50
|
+
handlePrecisionToggle(e: Event) {
|
|
51
|
+
const target = e.target as HTMLElement;
|
|
52
|
+
document.querySelectorAll('#wfc-precision-toggle .wfc-mode-btn').forEach((b) => b.classList.remove('active'));
|
|
53
|
+
target.classList.add('active');
|
|
54
|
+
this.currentPrecision = target.dataset.precision || 'carpentry';
|
|
55
|
+
}
|
|
56
|
+
handleStepper(e: Event) {
|
|
57
|
+
const target = e.target as HTMLElement;
|
|
58
|
+
const stepper = target.dataset.stepper || '';
|
|
59
|
+
const [field, direction] = stepper.split('-');
|
|
60
|
+
let inputId = '';
|
|
61
|
+
if (field === 'whole') inputId = 'wfc-whole';
|
|
62
|
+
else if (field === 'num') inputId = 'wfc-numerator';
|
|
63
|
+
else if (field === 'denom') inputId = 'wfc-denominator';
|
|
64
|
+
const input = document.getElementById(inputId) as HTMLInputElement | null;
|
|
65
|
+
if (!input) return;
|
|
66
|
+
const value = parseInt(input.value, 10) || 0;
|
|
67
|
+
const newValue = direction === 'up' ? value + 1 : Math.max(0, value - 1);
|
|
68
|
+
input.value = String(newValue);
|
|
69
|
+
this.convert();
|
|
70
|
+
}
|
|
71
|
+
handlePreset(e: Event) {
|
|
72
|
+
const target = e.target as HTMLElement;
|
|
73
|
+
const preset = target.dataset.preset || '';
|
|
74
|
+
const [num, denom] = preset.split('/').map((x) => parseInt(x, 10));
|
|
75
|
+
const wholeInput = document.getElementById('wfc-whole') as HTMLInputElement | null;
|
|
76
|
+
const numInput = document.getElementById('wfc-numerator') as HTMLInputElement | null;
|
|
77
|
+
const denomInput = document.getElementById('wfc-denominator') as HTMLSelectElement | null;
|
|
78
|
+
if (wholeInput) wholeInput.value = '0';
|
|
79
|
+
if (numInput) numInput.value = String(num);
|
|
80
|
+
if (denomInput) denomInput.value = String(denom);
|
|
81
|
+
document.querySelectorAll('.wfc-preset-btn').forEach((btn) => btn.classList.remove('active'));
|
|
82
|
+
target.classList.add('active');
|
|
83
|
+
this.convert();
|
|
84
|
+
}
|
|
85
|
+
toggleMode(mode: string) {
|
|
86
|
+
const forwardInput = document.getElementById('wfc-forward-input');
|
|
87
|
+
const reverseInput = document.getElementById('wfc-reverse-input');
|
|
88
|
+
if (mode === 'forward') {
|
|
89
|
+
forwardInput?.classList.remove('wfc-hidden');
|
|
90
|
+
reverseInput?.classList.add('wfc-hidden');
|
|
91
|
+
} else {
|
|
92
|
+
forwardInput?.classList.add('wfc-hidden');
|
|
93
|
+
reverseInput?.classList.remove('wfc-hidden');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
convert() {
|
|
97
|
+
if (this.currentMode === 'forward') {
|
|
98
|
+
this.convertForward();
|
|
99
|
+
} else {
|
|
100
|
+
this.convertReverse();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
convertForward() {
|
|
104
|
+
const whole = parseInt((document.getElementById('wfc-whole') as HTMLInputElement).value, 10) || 0;
|
|
105
|
+
const numerator = parseInt((document.getElementById('wfc-numerator') as HTMLInputElement).value, 10) || 0;
|
|
106
|
+
const denominator = parseInt((document.getElementById('wfc-denominator') as HTMLSelectElement).value, 10) || 1;
|
|
107
|
+
const input: Fraction = { whole, numerator, denominator };
|
|
108
|
+
displayResult(convertFraction(input), this.currentPrecision, this.ui);
|
|
109
|
+
}
|
|
110
|
+
convertReverse() {
|
|
111
|
+
const mm = parseFloat((document.getElementById('wfc-reverse-mm') as HTMLInputElement).value) || 0;
|
|
112
|
+
if (mm === 0) {
|
|
113
|
+
document.getElementById('wfc-no-result')?.classList.remove('wfc-hidden');
|
|
114
|
+
document.getElementById('wfc-result-content')?.classList.add('wfc-hidden');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
displayReverseResult(reverseSearch(mm), this.currentPrecision, this.ui);
|
|
118
|
+
}
|
|
119
|
+
copyResult() {
|
|
120
|
+
const fractionEl = document.getElementById('wfc-result-fraction');
|
|
121
|
+
const mmEl = document.getElementById('wfc-result-mm');
|
|
122
|
+
const fraction = fractionEl?.textContent || '';
|
|
123
|
+
const mm = mmEl?.textContent || '';
|
|
124
|
+
const text = `${fraction} = ${mm}`;
|
|
125
|
+
navigator.clipboard.writeText(text);
|
|
126
|
+
}
|
|
127
|
+
saveToHistory() {
|
|
128
|
+
const fractionEl = document.getElementById('wfc-result-fraction');
|
|
129
|
+
const mmEl = document.getElementById('wfc-result-mm');
|
|
130
|
+
const fraction = fractionEl?.textContent || '';
|
|
131
|
+
const mm = mmEl?.textContent || '';
|
|
132
|
+
const entry = { fraction, mm, time: Date.now() };
|
|
133
|
+
this.history.unshift(entry);
|
|
134
|
+
if (this.history.length > 20) this.history.pop();
|
|
135
|
+
this.saveHistoryLocal();
|
|
136
|
+
this.renderHistory();
|
|
137
|
+
}
|
|
138
|
+
loadHistory() {
|
|
139
|
+
const stored = localStorage.getItem('wfc-history');
|
|
140
|
+
if (stored) {
|
|
141
|
+
this.history = JSON.parse(stored);
|
|
142
|
+
this.renderHistory();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
saveHistoryLocal() {
|
|
146
|
+
localStorage.setItem('wfc-history', JSON.stringify(this.history));
|
|
147
|
+
}
|
|
148
|
+
renderHistory() {
|
|
149
|
+
const list = document.getElementById('wfc-history-list');
|
|
150
|
+
if (!list) return;
|
|
151
|
+
if (this.history.length === 0) {
|
|
152
|
+
list.innerHTML = `<div style="text-align: center; color: #9ca3af; font-size: 0.85rem;">${this.ui.historyEmpty || 'No measurements yet'}</div>`;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
list.innerHTML = this.buildHistoryHTML();
|
|
156
|
+
this.attachHistoryHandlers(list);
|
|
157
|
+
}
|
|
158
|
+
buildHistoryHTML() {
|
|
159
|
+
return this.history
|
|
160
|
+
.map((item, idx) => `<div class="wfc-history-item"><span><span class="wfc-history-item-fraction">${item.fraction}</span> = ${item.mm}</span><button class="wfc-history-remove" data-idx="${idx}">${this.ui.historyRemove || 'Remove'}</button></div>`)
|
|
161
|
+
.join('');
|
|
162
|
+
}
|
|
163
|
+
attachHistoryHandlers(list: HTMLElement) {
|
|
164
|
+
list.querySelectorAll('.wfc-history-remove').forEach((btn) => {
|
|
165
|
+
btn.addEventListener('click', (e: Event) => {
|
|
166
|
+
const target = e.target as HTMLElement;
|
|
167
|
+
const idx = parseInt(target.dataset.idx || '0', 10);
|
|
168
|
+
this.history.splice(idx, 1);
|
|
169
|
+
this.saveHistoryLocal();
|
|
170
|
+
this.renderHistory();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
clear() {
|
|
175
|
+
(document.getElementById('wfc-whole') as HTMLInputElement).value = '0';
|
|
176
|
+
(document.getElementById('wfc-numerator') as HTMLInputElement).value = '0';
|
|
177
|
+
(document.getElementById('wfc-denominator') as HTMLSelectElement).value = '1';
|
|
178
|
+
(document.getElementById('wfc-reverse-mm') as HTMLInputElement).value = '';
|
|
179
|
+
document.getElementById('wfc-no-result')?.classList.remove('wfc-hidden');
|
|
180
|
+
document.getElementById('wfc-result-content')?.classList.add('wfc-hidden');
|
|
181
|
+
document.querySelectorAll('.wfc-preset-btn').forEach((btn) => btn.classList.remove('active'));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { WorkshopFractionConverterUI } from './ui';
|
|
3
|
+
|
|
4
|
+
interface Props { ui?: WorkshopFractionConverterUI; }
|
|
5
|
+
const { ui } = Astro.props;
|
|
6
|
+
const t = (ui ?? {}) as WorkshopFractionConverterUI;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<div class="wfc-root" data-wfc-root data-ui={JSON.stringify(t)}>
|
|
10
|
+
<div class="wfc-card">
|
|
11
|
+
<div class="wfc-grid">
|
|
12
|
+
<div class="wfc-sidebar">
|
|
13
|
+
<div class="wfc-field">
|
|
14
|
+
<label class="wfc-field-label">{t.labelFraction}</label>
|
|
15
|
+
<div class="wfc-mode-toggle" id="wfc-mode-toggle">
|
|
16
|
+
<button class="wfc-mode-btn active" data-mode="forward">
|
|
17
|
+
{t.modeForward}
|
|
18
|
+
</button>
|
|
19
|
+
<button class="wfc-mode-btn" data-mode="reverse">
|
|
20
|
+
{t.modeReverse}
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="wfc-field" id="wfc-forward-input" data-forward-input>
|
|
26
|
+
<label class="wfc-field-label">{t.labelFraction}</label>
|
|
27
|
+
<div class="wfc-fraction-input">
|
|
28
|
+
<div class="wfc-fraction-column">
|
|
29
|
+
<span class="wfc-fraction-label">{t.labelWhole}</span>
|
|
30
|
+
<div class="wfc-stepper-group">
|
|
31
|
+
<button class="wfc-stepper-btn" data-stepper="whole-down">−</button>
|
|
32
|
+
<button class="wfc-stepper-btn" data-stepper="whole-up">+</button>
|
|
33
|
+
</div>
|
|
34
|
+
<input
|
|
35
|
+
type="number"
|
|
36
|
+
class="wfc-input-number"
|
|
37
|
+
id="wfc-whole"
|
|
38
|
+
value="0"
|
|
39
|
+
min="0"
|
|
40
|
+
max="999"
|
|
41
|
+
inputmode="numeric"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="wfc-fraction-column">
|
|
45
|
+
<span class="wfc-fraction-label">{t.labelNumerator}</span>
|
|
46
|
+
<div class="wfc-stepper-group">
|
|
47
|
+
<button class="wfc-stepper-btn" data-stepper="num-down">−</button>
|
|
48
|
+
<button class="wfc-stepper-btn" data-stepper="num-up">+</button>
|
|
49
|
+
</div>
|
|
50
|
+
<input
|
|
51
|
+
type="number"
|
|
52
|
+
class="wfc-input-number"
|
|
53
|
+
id="wfc-numerator"
|
|
54
|
+
value="0"
|
|
55
|
+
min="0"
|
|
56
|
+
max="64"
|
|
57
|
+
inputmode="numeric"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="wfc-fraction-column">
|
|
61
|
+
<span class="wfc-fraction-label">{t.labelDenominator}</span>
|
|
62
|
+
<div class="wfc-stepper-group">
|
|
63
|
+
<button class="wfc-stepper-btn" data-stepper="denom-down">−</button>
|
|
64
|
+
<button class="wfc-stepper-btn" data-stepper="denom-up">+</button>
|
|
65
|
+
</div>
|
|
66
|
+
<select class="wfc-input-number" id="wfc-denominator" style="appearance: none;">
|
|
67
|
+
<option value="1">1</option>
|
|
68
|
+
<option value="2">2</option>
|
|
69
|
+
<option value="4">4</option>
|
|
70
|
+
<option value="8">8</option>
|
|
71
|
+
<option value="16">16</option>
|
|
72
|
+
<option value="32">32</option>
|
|
73
|
+
<option value="64">64</option>
|
|
74
|
+
</select>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="wfc-field">
|
|
79
|
+
<label class="wfc-field-label">{t.labelFraction}</label>
|
|
80
|
+
<div class="wfc-presets" id="wfc-presets">
|
|
81
|
+
<button class="wfc-preset-btn" data-preset="1/4">1/4"</button>
|
|
82
|
+
<button class="wfc-preset-btn" data-preset="1/2">1/2"</button>
|
|
83
|
+
<button class="wfc-preset-btn" data-preset="3/8">3/8"</button>
|
|
84
|
+
<button class="wfc-preset-btn" data-preset="5/16">5/16"</button>
|
|
85
|
+
<button class="wfc-preset-btn" data-preset="3/16">3/16"</button>
|
|
86
|
+
<button class="wfc-preset-btn" data-preset="1/8">1/8"</button>
|
|
87
|
+
<button class="wfc-preset-btn" data-preset="1/16">1/16"</button>
|
|
88
|
+
<button class="wfc-preset-btn" data-preset="5/8">5/8"</button>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="wfc-field wfc-hidden" id="wfc-reverse-input" data-reverse-input>
|
|
94
|
+
<label class="wfc-field-label">{t.reverseSearchLabel}</label>
|
|
95
|
+
<div style="display: flex; gap: 0.5rem;">
|
|
96
|
+
<input
|
|
97
|
+
type="number"
|
|
98
|
+
class="wfc-input-number"
|
|
99
|
+
id="wfc-reverse-mm"
|
|
100
|
+
placeholder="12.7"
|
|
101
|
+
min="0.1"
|
|
102
|
+
max="999"
|
|
103
|
+
step="0.1"
|
|
104
|
+
inputmode="decimal"
|
|
105
|
+
style="flex: 1;"
|
|
106
|
+
/>
|
|
107
|
+
<span style="align-self: flex-end; padding-bottom: 0.75rem; color: #6b7280;">mm</span>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div class="wfc-field">
|
|
112
|
+
<label class="wfc-field-label">{t.precisionLabel}</label>
|
|
113
|
+
<div class="wfc-mode-toggle" id="wfc-precision-toggle">
|
|
114
|
+
<button class="wfc-mode-btn active" data-precision="carpentry">
|
|
115
|
+
{t.precisionCarpentry}
|
|
116
|
+
</button>
|
|
117
|
+
<button class="wfc-mode-btn" data-precision="metal">
|
|
118
|
+
{t.precisionMetal}
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="wfc-button-group">
|
|
124
|
+
<button class="wfc-button wfc-button-primary" id="wfc-convert-btn">
|
|
125
|
+
{t.buttonConvert}
|
|
126
|
+
</button>
|
|
127
|
+
<button class="wfc-button wfc-button-secondary" id="wfc-clear-btn">
|
|
128
|
+
{t.buttonClear}
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="wfc-history" id="wfc-history">
|
|
133
|
+
<div class="wfc-history-title">{t.historyTitle}</div>
|
|
134
|
+
<div class="wfc-history-list" id="wfc-history-list">
|
|
135
|
+
<div style="text-align: center; color: #9ca3af; font-size: 0.85rem;">
|
|
136
|
+
{t.historyEmpty}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div class="wfc-main" id="wfc-main-content">
|
|
143
|
+
<div id="wfc-no-result" style="text-align: center; padding: 3rem 0; color: #9ca3af;">
|
|
144
|
+
<p>{t.helpText}</p>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div class="wfc-hidden" id="wfc-result-content">
|
|
148
|
+
<div class="wfc-result-box">
|
|
149
|
+
<div class="wfc-result-title">{t.resultTitle}</div>
|
|
150
|
+
<div class="wfc-result-row">
|
|
151
|
+
<span class="wfc-result-label">{t.resultFraction}</span>
|
|
152
|
+
<span class="wfc-result-value wfc-text-mono" id="wfc-result-fraction">
|
|
153
|
+
0"
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
<div class="wfc-result-row">
|
|
157
|
+
<span class="wfc-result-label">{t.resultInches}</span>
|
|
158
|
+
<span class="wfc-result-value wfc-text-mono" id="wfc-result-inches">
|
|
159
|
+
0.00"
|
|
160
|
+
</span>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="wfc-result-row">
|
|
163
|
+
<span class="wfc-result-label">{t.resultMillimeters}</span>
|
|
164
|
+
<span class="wfc-result-value wfc-text-mono" id="wfc-result-mm">
|
|
165
|
+
0.00 mm
|
|
166
|
+
</span>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div class="wfc-visual-scale">
|
|
171
|
+
<div style="font-size: 0.75rem; font-weight: 600; color: #6b7280; margin-bottom: 0.75rem;">
|
|
172
|
+
{t.labelVisualReference}
|
|
173
|
+
</div>
|
|
174
|
+
<div class="wfc-scale-ruler" id="wfc-scale-ruler">
|
|
175
|
+
<div class="wfc-scale-indicator" id="wfc-scale-indicator" style="width: 0;"></div>
|
|
176
|
+
</div>
|
|
177
|
+
<div style="font-size: 0.75rem; color: #6b7280;">
|
|
178
|
+
<span id="wfc-scale-text">0 mm</span>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div>
|
|
183
|
+
<div class="wfc-result-title">{t.matchTitle}</div>
|
|
184
|
+
<div class="wfc-match-cards">
|
|
185
|
+
<div class="wfc-match-card" id="wfc-fastener-match">
|
|
186
|
+
<div class="wfc-match-header">
|
|
187
|
+
<span class="wfc-match-name" id="wfc-fastener-name">−</span>
|
|
188
|
+
<span class="wfc-match-badge" id="wfc-fastener-badge">−</span>
|
|
189
|
+
</div>
|
|
190
|
+
<div class="wfc-match-details" id="wfc-fastener-details">
|
|
191
|
+
{t.matchClosestFastener}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="wfc-match-card" id="wfc-socket-match">
|
|
196
|
+
<div class="wfc-match-header">
|
|
197
|
+
<span class="wfc-match-name" id="wfc-socket-name">−</span>
|
|
198
|
+
<span class="wfc-match-badge" id="wfc-socket-badge">−</span>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="wfc-match-details" id="wfc-socket-details">
|
|
201
|
+
{t.matchClosestSocket}
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div class="wfc-button-group">
|
|
208
|
+
<button class="wfc-button wfc-button-primary" id="wfc-copy-btn">
|
|
209
|
+
{t.buttonCopyResult}
|
|
210
|
+
</button>
|
|
211
|
+
<button class="wfc-button wfc-button-secondary" id="wfc-save-btn">
|
|
212
|
+
{t.buttonSaveToHistory}
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<script>
|
|
222
|
+
import { WorkshopFractionConverter } from './client';
|
|
223
|
+
const root = document.querySelector('[data-wfc-root]');
|
|
224
|
+
const ui = JSON.parse(root?.getAttribute('data-ui') || '{}');
|
|
225
|
+
new WorkshopFractionConverter(ui);
|
|
226
|
+
</script>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Fraction, ConversionResult, ReverseSearchResult } from './types-client';
|
|
2
|
+
import { STANDARD_FASTENERS, STANDARD_SOCKETS } from './types-client';
|
|
3
|
+
|
|
4
|
+
export function fractionToInches(input: Fraction) {
|
|
5
|
+
if (input.denominator === 0) return 0;
|
|
6
|
+
return input.whole + input.numerator / input.denominator;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function inchesToMillimeters(inches: number) {
|
|
10
|
+
return inches * 25.4;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function formatFraction(input: Fraction) {
|
|
14
|
+
if (input.denominator === 0 || (input.numerator === 0 && input.denominator === 1)) {
|
|
15
|
+
return `${input.whole}"`;
|
|
16
|
+
}
|
|
17
|
+
if (input.whole === 0) {
|
|
18
|
+
return `${input.numerator}/${input.denominator}"`;
|
|
19
|
+
}
|
|
20
|
+
return `${input.whole} ${input.numerator}/${input.denominator}"`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function findClosestFastener(mm: number) {
|
|
24
|
+
if (STANDARD_FASTENERS.length === 0) return null;
|
|
25
|
+
let closest = STANDARD_FASTENERS[0];
|
|
26
|
+
let minDiff = Math.abs(closest.mm - mm);
|
|
27
|
+
for (const fastener of STANDARD_FASTENERS) {
|
|
28
|
+
const diff = Math.abs(fastener.mm - mm);
|
|
29
|
+
if (diff < minDiff) {
|
|
30
|
+
minDiff = diff;
|
|
31
|
+
closest = fastener;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const pct = (minDiff / mm) * 100;
|
|
35
|
+
return {
|
|
36
|
+
name: closest.name,
|
|
37
|
+
size_mm: closest.mm,
|
|
38
|
+
size_inch: closest.inch,
|
|
39
|
+
difference_mm: closest.mm - mm,
|
|
40
|
+
difference_pct: pct,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function findClosestSocket(mm: number) {
|
|
45
|
+
if (STANDARD_SOCKETS.length === 0) return null;
|
|
46
|
+
let closest = STANDARD_SOCKETS[0];
|
|
47
|
+
let minDiff = Math.abs(closest.mm - mm);
|
|
48
|
+
for (const socket of STANDARD_SOCKETS) {
|
|
49
|
+
const diff = Math.abs(socket.mm - mm);
|
|
50
|
+
if (diff < minDiff) {
|
|
51
|
+
minDiff = diff;
|
|
52
|
+
closest = socket;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const pct = (minDiff / mm) * 100;
|
|
56
|
+
return {
|
|
57
|
+
name: closest.name,
|
|
58
|
+
size_mm: closest.mm,
|
|
59
|
+
size_inch: closest.inch,
|
|
60
|
+
difference_mm: closest.mm - mm,
|
|
61
|
+
difference_pct: pct,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function mmToFraction(mm: number): Fraction {
|
|
66
|
+
const inches = mm / 25.4;
|
|
67
|
+
const whole = Math.floor(inches);
|
|
68
|
+
const fractional = inches - whole;
|
|
69
|
+
const denominators = [2, 4, 8, 16, 32, 64];
|
|
70
|
+
let bestNum = 0;
|
|
71
|
+
let bestDenom = 1;
|
|
72
|
+
let bestError = Math.abs(fractional);
|
|
73
|
+
for (const denom of denominators) {
|
|
74
|
+
const num = Math.round(fractional * denom);
|
|
75
|
+
const error = Math.abs(fractional - num / denom);
|
|
76
|
+
if (error < bestError) {
|
|
77
|
+
bestError = error;
|
|
78
|
+
bestNum = num;
|
|
79
|
+
bestDenom = denom;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (bestNum === bestDenom) {
|
|
83
|
+
return { whole: whole + 1, numerator: 0, denominator: 1 };
|
|
84
|
+
}
|
|
85
|
+
return { whole, numerator: bestNum, denominator: bestDenom };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function convertFraction(input: Fraction): ConversionResult {
|
|
89
|
+
const inches = fractionToInches(input);
|
|
90
|
+
const mm = inchesToMillimeters(inches);
|
|
91
|
+
return {
|
|
92
|
+
fraction: input,
|
|
93
|
+
inches,
|
|
94
|
+
millimeters: mm,
|
|
95
|
+
closestFastener: findClosestFastener(mm),
|
|
96
|
+
closestSocket: findClosestSocket(mm),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function reverseSearch(millimeters: number): ReverseSearchResult {
|
|
101
|
+
const inches = millimeters / 25.4;
|
|
102
|
+
const fraction = mmToFraction(millimeters);
|
|
103
|
+
return {
|
|
104
|
+
input_mm: millimeters,
|
|
105
|
+
closest_fraction: fraction,
|
|
106
|
+
inches,
|
|
107
|
+
closestFastener: findClosestFastener(millimeters),
|
|
108
|
+
closestSocket: findClosestSocket(millimeters),
|
|
109
|
+
};
|
|
110
|
+
}
|