Skip to content

Commit d1aca6a

Browse files
committed
feat(new tools): Units Converter
All units, pressure, power, angle, area, energy, force, length, mass, volume units converter Many Units Converter: convert any unit string (ie 1d 3m) into best unit and selected target unit Fix CorentinTh#571
1 parent 80e46c9 commit d1aca6a

File tree

26 files changed

+686
-10
lines changed

26 files changed

+686
-10
lines changed

components.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ declare module '@vue/runtime-core' {
1111
export interface GlobalComponents {
1212
'404.page': typeof import('./src/pages/404.page.vue')['default']
1313
About: typeof import('./src/pages/About.vue')['default']
14+
AngleConverter: typeof import('./src/tools/angle-converter/angle-converter.vue')['default']
1415
App: typeof import('./src/App.vue')['default']
16+
AreaConverter: typeof import('./src/tools/area-converter/area-converter.vue')['default']
1517
'Base.layout': typeof import('./src/layouts/base.layout.vue')['default']
1618
Base64FileConverter: typeof import('./src/tools/base64-file-converter/base64-file-converter.vue')['default']
1719
Base64StringConverter: typeof import('./src/tools/base64-string-converter/base64-string-converter.vue')['default']
@@ -75,8 +77,10 @@ declare module '@vue/runtime-core' {
7577
EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default']
7678
EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default']
7779
Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
80+
EnergyConverter: typeof import('./src/tools/energy-converter/energy-converter.vue')['default']
7881
EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
7982
FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
83+
ForceConverter: typeof import('./src/tools/force-converter/force-converter.vue')['default']
8084
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
8185
GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
8286
'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
@@ -125,11 +129,14 @@ declare module '@vue/runtime-core' {
125129
JsonViewer: typeof import('./src/tools/json-viewer/json-viewer.vue')['default']
126130
JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
127131
KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
132+
LengthConverter: typeof import('./src/tools/length-converter/length-converter.vue')['default']
128133
ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
129134
LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
130135
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
131136
MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
132137
MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
138+
ManyUnitsConverter: typeof import('./src/tools/many-units-converter/many-units-converter.vue')['default']
139+
MassConverter: typeof import('./src/tools/mass-converter/mass-converter.vue')['default']
133140
MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
134141
MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
135142
MenuBarItem: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue')['default']
@@ -179,6 +186,8 @@ declare module '@vue/runtime-core' {
179186
PdfSignatureDetails: typeof import('./src/tools/pdf-signature-checker/components/pdf-signature-details.vue')['default']
180187
PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default']
181188
PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
189+
PowerConverter: typeof import('./src/tools/power-converter/power-converter.vue')['default']
190+
PressureConverter: typeof import('./src/tools/pressure-converter/pressure-converter.vue')['default']
182191
QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
183192
RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
184193
ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
@@ -204,11 +213,13 @@ declare module '@vue/runtime-core' {
204213
'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
205214
ToolCard: typeof import('./src/components/ToolCard.vue')['default']
206215
UlidGenerator: typeof import('./src/tools/ulid-generator/ulid-generator.vue')['default']
216+
UnitsConverter: typeof import('./src/components/UnitsConverter.vue')['default']
207217
UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
208218
UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
209219
UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']
210220
UserAgentResultCards: typeof import('./src/tools/user-agent-parser/user-agent-result-cards.vue')['default']
211221
UuidGenerator: typeof import('./src/tools/uuid-generator/uuid-generator.vue')['default']
222+
VolumeConverter: typeof import('./src/tools/volume-converter/volume-converter.vue')['default']
212223
WifiQrCodeGenerator: typeof import('./src/tools/wifi-qr-code-generator/wifi-qr-code-generator.vue')['default']
213224
XmlFormatter: typeof import('./src/tools/xml-formatter/xml-formatter.vue')['default']
214225
YamlToJson: typeof import('./src/tools/yaml-to-json-converter/yaml-to-json.vue')['default']

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"change-case": "^4.1.2",
5151
"colord": "^2.9.3",
5252
"composerize-ts": "^0.6.2",
53+
"convert": "^5.4.1",
5354
"country-code-lookup": "^0.1.0",
5455
"cron-validator": "^1.3.1",
5556
"cronstrue": "^2.26.0",

pnpm-lock.yaml

Lines changed: 16 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/UnitsConverter.vue

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script setup lang="ts">
2+
import _ from 'lodash';
3+
import convert, { type Unit } from 'convert';
4+
5+
const props = withDefaults(defineProps<{
6+
supportedUnits: { [key: string]: string }
7+
defaultUnit: string
8+
labelWidth?: string
9+
unitMinWidth?: string
10+
}>(), {
11+
labelWidth: '150px',
12+
unitMinWidth: '50px',
13+
});
14+
const { supportedUnits, defaultUnit, labelWidth, unitMinWidth } = toRefs(props);
15+
const units = reactive<
16+
Record<
17+
string,
18+
{ title: string; unit: string; ref: number }
19+
>
20+
>(Object.entries(supportedUnits.value).map(([key, label]) => ({
21+
title: label,
22+
unit: key,
23+
ref: 1,
24+
})).reduce((prev, current) => ({
25+
...prev,
26+
[current.unit]: current,
27+
}), {}));
28+
29+
function update(key: string) {
30+
if (!units[key]) {
31+
return;
32+
}
33+
const { ref: value } = units[key];
34+
35+
const converter = convert(value, key as Unit);
36+
37+
_.chain(units)
38+
.omit(key)
39+
.forEach(({ unit }) => {
40+
try {
41+
units[unit].ref = converter.to(unit as Unit);
42+
}
43+
catch (e: any) {
44+
units[unit].ref = 0;
45+
}
46+
})
47+
.value();
48+
}
49+
50+
update(defaultUnit.value);
51+
</script>
52+
53+
<template>
54+
<div>
55+
<n-input-group v-for="[key, { title, unit }] in Object.entries(units)" :key="key" mb-3 w-full>
56+
<n-input-group-label :style="{ width: labelWidth }">
57+
{{ title }}
58+
</n-input-group-label>
59+
60+
<n-input-number
61+
v-model:value="units[key].ref"
62+
style="flex: 1"
63+
@update:value="() => update(key)"
64+
/>
65+
66+
<n-input-group-label :style="{ minWidth: unitMinWidth }">
67+
{{ unit }}
68+
</n-input-group-label>
69+
</n-input-group>
70+
</div>
71+
</template>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
import UnitsConverter from '@/components/UnitsConverter.vue';
3+
4+
const supportedUnits = {
5+
deg: 'degree (°)',
6+
rad: 'radian',
7+
turn: 'turn',
8+
gradian: 'gradian',
9+
grad: 'grad',
10+
gon: 'gon',
11+
};
12+
</script>
13+
14+
<template>
15+
<UnitsConverter default-unit="deg" :supported-units="supportedUnits" label-width="150px" />
16+
</template>

src/tools/angle-converter/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Angle } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Angle Units Converter',
6+
path: '/angle-converter',
7+
description: 'Convert values between angle units',
8+
keywords: ['angle', 'converter'],
9+
component: () => import('./angle-converter.vue'),
10+
icon: Angle,
11+
createdAt: new Date('2024-08-15'),
12+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script setup lang="ts">
2+
import UnitsConverter from '@/components/UnitsConverter.vue';
3+
4+
const supportedUnits = {
5+
'': 'square meter',
6+
'Pm²': 'square petameter',
7+
'Tm²': 'square terameter',
8+
'Gm²': 'square gigameter',
9+
'Mm²': 'square megameter',
10+
'km²': 'square kilometer',
11+
'hm²': 'square hectometer',
12+
'dam²': 'square decameter',
13+
'dm²': 'square decimeter',
14+
'cm²': 'square centimeter',
15+
'mm²': 'square millimeter',
16+
'μm²': 'square micrometer',
17+
'nm²': 'square nanometer',
18+
'pm²': 'square picometer',
19+
'fm²': 'square femtometer',
20+
'ac': 'acre',
21+
'ca': 'centiare',
22+
'da': 'deciare',
23+
'are': 'are',
24+
'daa': 'decare',
25+
'ha': 'hectare',
26+
'ft²': 'square foot (/ft2/sq ft)',
27+
'in²': 'square inch (/in2/sq in)',
28+
'yd²': 'square yard (yd2/sq yd)',
29+
'mi²': 'square mile (mi2/sq mi)',
30+
};
31+
</script>
32+
33+
<template>
34+
<UnitsConverter default-unit="" :supported-units="supportedUnits" label-width="150px" />
35+
</template>

src/tools/area-converter/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { SquaresDiagonal } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Area Units Converter',
6+
path: '/area-converter',
7+
description: 'Convert values between area units',
8+
keywords: ['area', 'converter'],
9+
component: () => import('./area-converter.vue'),
10+
icon: SquaresDiagonal,
11+
createdAt: new Date('2024-08-15'),
12+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script setup lang="ts">
2+
import UnitsConverter from '@/components/UnitsConverter.vue';
3+
4+
const supportedUnits = {
5+
J: 'joule',
6+
PJ: 'petajoule',
7+
TJ: 'terajoule',
8+
GJ: 'gigajoule',
9+
MJ: 'megajoule',
10+
kJ: 'kilojoule',
11+
hJ: 'hectojoule',
12+
daJ: 'decajoule',
13+
dJ: 'decijoule',
14+
cJ: 'centijoule',
15+
mJ: 'millijoule',
16+
µJ: 'microjoule',
17+
nJ: 'nanojoule',
18+
pJ: 'picojoule',
19+
fJ: 'femtojoule',
20+
Wh: 'watt-hour',
21+
PWh: 'petawatt-hour',
22+
TWh: 'terawatt-hour',
23+
GWh: 'gigawatt-hour',
24+
MWh: 'megawatt-hour',
25+
kWh: 'kilowatt-hour',
26+
hWh: 'hectowatt-hour',
27+
daWh: 'decawatt-hour',
28+
dWh: 'deciwatt-hour',
29+
cWh: 'centiwatt-hour',
30+
mWh: 'milliwatt-hour',
31+
µWh: 'microwatt-hour',
32+
nWh: 'nanowatt-hour',
33+
pWh: 'picowatt-hour',
34+
fWh: 'femtowatt-hour',
35+
};
36+
</script>
37+
38+
<template>
39+
<UnitsConverter default-unit="J" :supported-units="supportedUnits" label-width="150px" />
40+
</template>

src/tools/energy-converter/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Power } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Energy Units Converter',
6+
path: '/energy-converter',
7+
description: 'Convert values between energy units',
8+
keywords: ['energy', 'converter'],
9+
component: () => import('./energy-converter.vue'),
10+
icon: Power,
11+
createdAt: new Date('2024-08-15'),
12+
});

0 commit comments

Comments
 (0)