Skip to content

Commit

Permalink
feat(new tools): Units Converter
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sharevb committed Oct 6, 2024
1 parent 80e46c9 commit d1aca6a
Show file tree
Hide file tree
Showing 26 changed files with 686 additions and 10 deletions.
11 changes: 11 additions & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ declare module '@vue/runtime-core' {
export interface GlobalComponents {
'404.page': typeof import('./src/pages/404.page.vue')['default']
About: typeof import('./src/pages/About.vue')['default']
AngleConverter: typeof import('./src/tools/angle-converter/angle-converter.vue')['default']
App: typeof import('./src/App.vue')['default']
AreaConverter: typeof import('./src/tools/area-converter/area-converter.vue')['default']
'Base.layout': typeof import('./src/layouts/base.layout.vue')['default']
Base64FileConverter: typeof import('./src/tools/base64-file-converter/base64-file-converter.vue')['default']
Base64StringConverter: typeof import('./src/tools/base64-string-converter/base64-string-converter.vue')['default']
Expand Down Expand Up @@ -75,8 +77,10 @@ declare module '@vue/runtime-core' {
EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default']
EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default']
Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
EnergyConverter: typeof import('./src/tools/energy-converter/energy-converter.vue')['default']
EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
ForceConverter: typeof import('./src/tools/force-converter/force-converter.vue')['default']
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
Expand Down Expand Up @@ -125,11 +129,14 @@ declare module '@vue/runtime-core' {
JsonViewer: typeof import('./src/tools/json-viewer/json-viewer.vue')['default']
JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
LengthConverter: typeof import('./src/tools/length-converter/length-converter.vue')['default']
ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
ManyUnitsConverter: typeof import('./src/tools/many-units-converter/many-units-converter.vue')['default']
MassConverter: typeof import('./src/tools/mass-converter/mass-converter.vue')['default']
MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
MenuBarItem: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue')['default']
Expand Down Expand Up @@ -179,6 +186,8 @@ declare module '@vue/runtime-core' {
PdfSignatureDetails: typeof import('./src/tools/pdf-signature-checker/components/pdf-signature-details.vue')['default']
PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default']
PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
PowerConverter: typeof import('./src/tools/power-converter/power-converter.vue')['default']
PressureConverter: typeof import('./src/tools/pressure-converter/pressure-converter.vue')['default']
QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
Expand All @@ -204,11 +213,13 @@ declare module '@vue/runtime-core' {
'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
ToolCard: typeof import('./src/components/ToolCard.vue')['default']
UlidGenerator: typeof import('./src/tools/ulid-generator/ulid-generator.vue')['default']
UnitsConverter: typeof import('./src/components/UnitsConverter.vue')['default']
UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']
UserAgentResultCards: typeof import('./src/tools/user-agent-parser/user-agent-result-cards.vue')['default']
UuidGenerator: typeof import('./src/tools/uuid-generator/uuid-generator.vue')['default']
VolumeConverter: typeof import('./src/tools/volume-converter/volume-converter.vue')['default']
WifiQrCodeGenerator: typeof import('./src/tools/wifi-qr-code-generator/wifi-qr-code-generator.vue')['default']
XmlFormatter: typeof import('./src/tools/xml-formatter/xml-formatter.vue')['default']
YamlToJson: typeof import('./src/tools/yaml-to-json-converter/yaml-to-json.vue')['default']
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"change-case": "^4.1.2",
"colord": "^2.9.3",
"composerize-ts": "^0.6.2",
"convert": "^5.4.1",
"country-code-lookup": "^0.1.0",
"cron-validator": "^1.3.1",
"cronstrue": "^2.26.0",
Expand Down
24 changes: 16 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions src/components/UnitsConverter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script setup lang="ts">
import _ from 'lodash';
import convert, { type Unit } from 'convert';
const props = withDefaults(defineProps<{
supportedUnits: { [key: string]: string }
defaultUnit: string
labelWidth?: string
unitMinWidth?: string
}>(), {
labelWidth: '150px',
unitMinWidth: '50px',
});
const { supportedUnits, defaultUnit, labelWidth, unitMinWidth } = toRefs(props);
const units = reactive<
Record<
string,
{ title: string; unit: string; ref: number }
>
>(Object.entries(supportedUnits.value).map(([key, label]) => ({
title: label,
unit: key,
ref: 1,
})).reduce((prev, current) => ({
...prev,
[current.unit]: current,
}), {}));
function update(key: string) {
if (!units[key]) {
return;
}
const { ref: value } = units[key];
const converter = convert(value, key as Unit);
_.chain(units)
.omit(key)
.forEach(({ unit }) => {
try {
units[unit].ref = converter.to(unit as Unit);
}
catch (e: any) {
units[unit].ref = 0;
}
})
.value();
}
update(defaultUnit.value);
</script>

<template>
<div>
<n-input-group v-for="[key, { title, unit }] in Object.entries(units)" :key="key" mb-3 w-full>
<n-input-group-label :style="{ width: labelWidth }">
{{ title }}
</n-input-group-label>

<n-input-number
v-model:value="units[key].ref"
style="flex: 1"
@update:value="() => update(key)"
/>

<n-input-group-label :style="{ minWidth: unitMinWidth }">
{{ unit }}
</n-input-group-label>
</n-input-group>
</div>
</template>
16 changes: 16 additions & 0 deletions src/tools/angle-converter/angle-converter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import UnitsConverter from '@/components/UnitsConverter.vue';
const supportedUnits = {
deg: 'degree (°)',
rad: 'radian',
turn: 'turn',
gradian: 'gradian',
grad: 'grad',
gon: 'gon',
};
</script>

<template>
<UnitsConverter default-unit="deg" :supported-units="supportedUnits" label-width="150px" />
</template>
12 changes: 12 additions & 0 deletions src/tools/angle-converter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Angle } from '@vicons/tabler';
import { defineTool } from '../tool';

export const tool = defineTool({
name: 'Angle Units Converter',
path: '/angle-converter',
description: 'Convert values between angle units',
keywords: ['angle', 'converter'],
component: () => import('./angle-converter.vue'),
icon: Angle,
createdAt: new Date('2024-08-15'),
});
35 changes: 35 additions & 0 deletions src/tools/area-converter/area-converter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import UnitsConverter from '@/components/UnitsConverter.vue';
const supportedUnits = {
'': 'square meter',
'Pm²': 'square petameter',
'Tm²': 'square terameter',
'Gm²': 'square gigameter',
'Mm²': 'square megameter',
'km²': 'square kilometer',
'hm²': 'square hectometer',
'dam²': 'square decameter',
'dm²': 'square decimeter',
'cm²': 'square centimeter',
'mm²': 'square millimeter',
'μm²': 'square micrometer',
'nm²': 'square nanometer',
'pm²': 'square picometer',
'fm²': 'square femtometer',
'ac': 'acre',
'ca': 'centiare',
'da': 'deciare',
'are': 'are',
'daa': 'decare',
'ha': 'hectare',
'ft²': 'square foot (/ft2/sq ft)',
'in²': 'square inch (/in2/sq in)',
'yd²': 'square yard (yd2/sq yd)',
'mi²': 'square mile (mi2/sq mi)',
};
</script>

<template>
<UnitsConverter default-unit="" :supported-units="supportedUnits" label-width="150px" />
</template>
12 changes: 12 additions & 0 deletions src/tools/area-converter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SquaresDiagonal } from '@vicons/tabler';
import { defineTool } from '../tool';

export const tool = defineTool({
name: 'Area Units Converter',
path: '/area-converter',
description: 'Convert values between area units',
keywords: ['area', 'converter'],
component: () => import('./area-converter.vue'),
icon: SquaresDiagonal,
createdAt: new Date('2024-08-15'),
});
40 changes: 40 additions & 0 deletions src/tools/energy-converter/energy-converter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script setup lang="ts">
import UnitsConverter from '@/components/UnitsConverter.vue';
const supportedUnits = {
J: 'joule',
PJ: 'petajoule',
TJ: 'terajoule',
GJ: 'gigajoule',
MJ: 'megajoule',
kJ: 'kilojoule',
hJ: 'hectojoule',
daJ: 'decajoule',
dJ: 'decijoule',
cJ: 'centijoule',
mJ: 'millijoule',
µJ: 'microjoule',
nJ: 'nanojoule',
pJ: 'picojoule',
fJ: 'femtojoule',
Wh: 'watt-hour',
PWh: 'petawatt-hour',
TWh: 'terawatt-hour',
GWh: 'gigawatt-hour',
MWh: 'megawatt-hour',
kWh: 'kilowatt-hour',
hWh: 'hectowatt-hour',
daWh: 'decawatt-hour',
dWh: 'deciwatt-hour',
cWh: 'centiwatt-hour',
mWh: 'milliwatt-hour',
µWh: 'microwatt-hour',
nWh: 'nanowatt-hour',
pWh: 'picowatt-hour',
fWh: 'femtowatt-hour',
};
</script>

<template>
<UnitsConverter default-unit="J" :supported-units="supportedUnits" label-width="150px" />
</template>
12 changes: 12 additions & 0 deletions src/tools/energy-converter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Power } from '@vicons/tabler';
import { defineTool } from '../tool';

export const tool = defineTool({
name: 'Energy Units Converter',
path: '/energy-converter',
description: 'Convert values between energy units',
keywords: ['energy', 'converter'],
component: () => import('./energy-converter.vue'),
icon: Power,
createdAt: new Date('2024-08-15'),
});
38 changes: 38 additions & 0 deletions src/tools/force-converter/force-converter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup lang="ts">
import UnitsConverter from '@/components/UnitsConverter.vue';
const supportedUnits = {
'N': 'newton',
'PN': 'petanewton',
'TN': 'teranewton',
'GN': 'giganewton',
'MN': 'meganewton',
'kN': 'kilonewton',
'hN': 'hectonewton',
'daN': 'decanewton',
'dN': 'decinewton',
'cN': 'centinewton',
'mN': 'millinewton',
'µN': 'micronewton',
'nN': 'nanonewton',
'pN': 'piconewton',
'fN': 'femtonewton',
'dyn': 'dyne',
'lbf': 'pound of force',
'kip': 'kip',
'klb': 'klb',
'kipf': 'kipf',
'klbf': 'klbf',
'pdl': 'poundal',
'kgf': 'kilogram-force',
'kp': 'kilopond',
'Mp': 'megapond',
'tf': 'tonne-force',
'metric tf': 'metric ton-force',
'megagram-force': 'megagram-force',
};
</script>

<template>
<UnitsConverter default-unit="N" :supported-units="supportedUnits" label-width="150px" />
</template>
12 changes: 12 additions & 0 deletions src/tools/force-converter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Power } from '@vicons/tabler';
import { defineTool } from '../tool';

export const tool = defineTool({
name: 'Force Units Converter',
path: '/force-converter',
description: 'Convert values between force units',
keywords: ['force', 'converter'],
component: () => import('./force-converter.vue'),
icon: Power,
createdAt: new Date('2024-08-15'),
});
Loading

0 comments on commit d1aca6a

Please sign in to comment.