@jjlmoya/utils-motor 1.2.0 → 1.3.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 +3 -1
- package/src/entries.ts +7 -1
- package/src/index.ts +10 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/seo_translation_completeness.test.ts +69 -0
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/carMotorcycleGearRatioCalculator/bibliography.astro +6 -0
- package/src/tool/carMotorcycleGearRatioCalculator/bibliography.ts +12 -0
- package/src/tool/carMotorcycleGearRatioCalculator/car-motorcycle-gear-ratio-rpm-speed-calculator.css +668 -0
- package/src/tool/carMotorcycleGearRatioCalculator/component.astro +137 -0
- package/src/tool/carMotorcycleGearRatioCalculator/controller.ts +209 -0
- package/src/tool/carMotorcycleGearRatioCalculator/dom-views.ts +121 -0
- package/src/tool/carMotorcycleGearRatioCalculator/entry.ts +30 -0
- package/src/tool/carMotorcycleGearRatioCalculator/evaluator.ts +28 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/de.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/en.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/es.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/fr.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/id.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/it.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/ja.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/ko.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/nl.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/pl.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/pt.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/ru.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/sv.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/tr.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/i18n/zh.ts +162 -0
- package/src/tool/carMotorcycleGearRatioCalculator/index.ts +10 -0
- package/src/tool/carMotorcycleGearRatioCalculator/logic.test.ts +51 -0
- package/src/tool/carMotorcycleGearRatioCalculator/logic.ts +91 -0
- package/src/tool/carMotorcycleGearRatioCalculator/seo.astro +15 -0
- package/src/tool/carMotorcycleGearRatioCalculator/storage.ts +46 -0
- package/src/tool/carMotorcycleGearRatioCalculator/ui.ts +50 -0
- package/src/tool/tirePressureConverter/bibliography.astro +6 -0
- package/src/tool/tirePressureConverter/bibliography.ts +12 -0
- package/src/tool/tirePressureConverter/component.astro +82 -0
- package/src/tool/tirePressureConverter/controller.ts +120 -0
- package/src/tool/tirePressureConverter/dom-views.ts +31 -0
- package/src/tool/tirePressureConverter/entry.ts +30 -0
- package/src/tool/tirePressureConverter/evaluator.ts +8 -0
- package/src/tool/tirePressureConverter/i18n/de.ts +96 -0
- package/src/tool/tirePressureConverter/i18n/en.ts +96 -0
- package/src/tool/tirePressureConverter/i18n/es.ts +96 -0
- package/src/tool/tirePressureConverter/i18n/fr.ts +96 -0
- package/src/tool/tirePressureConverter/i18n/id.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/it.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/ja.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/ko.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/nl.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/pl.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/pt.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/ru.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/sv.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/tr.ts +95 -0
- package/src/tool/tirePressureConverter/i18n/zh.ts +95 -0
- package/src/tool/tirePressureConverter/index.ts +10 -0
- package/src/tool/tirePressureConverter/logic.test.ts +37 -0
- package/src/tool/tirePressureConverter/logic.ts +47 -0
- package/src/tool/tirePressureConverter/seo.astro +15 -0
- package/src/tool/tirePressureConverter/storage.ts +27 -0
- package/src/tool/tirePressureConverter/tire-pressure-converter-psi-bar-kpa-kgf-cm2.css +293 -0
- package/src/tool/tirePressureConverter/ui.ts +20 -0
- package/src/tools.ts +3 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
|
+
import type { SEOSection } from '@jjlmoya/utils-shared';
|
|
3
|
+
import type { TirePressureConverterLocaleContent } from '../entry';
|
|
4
|
+
|
|
5
|
+
const schemas: WithContext<SoftwareApplication | FAQPage | HowTo>[] = [
|
|
6
|
+
{
|
|
7
|
+
'@context': 'https://schema.org',
|
|
8
|
+
'@type': 'SoftwareApplication',
|
|
9
|
+
name: '胎压单位转换计算器',
|
|
10
|
+
applicationCategory: 'UtilitiesApplication',
|
|
11
|
+
operatingSystem: 'Web',
|
|
12
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
13
|
+
description: '精准转换 PSI、bar、kPa 和 kgf/cm² 胎压单位。',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
'@context': 'https://schema.org',
|
|
17
|
+
'@type': 'FAQPage',
|
|
18
|
+
mainEntity: [
|
|
19
|
+
{
|
|
20
|
+
'@type': 'Question',
|
|
21
|
+
name: '我应该使用多少胎压?',
|
|
22
|
+
acceptedAnswer: { '@type': 'Answer', text: '请参阅驾驶员车门柱标牌或用户手册上的冷态推荐胎压。本转换器仅进行单位换算。' },
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
'@type': 'Question',
|
|
26
|
+
name: '为什么前后轮的推荐胎压可能不同?',
|
|
27
|
+
acceptedAnswer: { '@type': 'Answer', text: '车辆轴荷分布与前后轴承重不同,因此厂商可能会为前后轮指定不同的冷态胎压。' },
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
'@context': 'https://schema.org',
|
|
33
|
+
'@type': 'HowTo',
|
|
34
|
+
name: '转换胎压读数',
|
|
35
|
+
step: [
|
|
36
|
+
{ '@type': 'HowToStep', name: '输入读数', text: '输入胎压表显示的数字。' },
|
|
37
|
+
{ '@type': 'HowToStep', name: '选择源单位', text: '选择 PSI、bar、kPa 或 kgf/cm²。' },
|
|
38
|
+
{ '@type': 'HowToStep', name: '查看转换结果', text: '在结果栏中查看对应的其他单位数值。' },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const seo: SEOSection[] = [
|
|
44
|
+
{ type: 'title', level: 2, text: '在不改变实际物理数值的前提下转换胎压单位' },
|
|
45
|
+
{ type: 'paragraph', html: '当打气泵、胎压表或车辆标牌使用不同的胎压单位时,使用本胎压单位转换器。只需输入一个读数,即可在一屏中同时查看 PSI、bar、kPa 和 kgf/cm² 的等效数值。' },
|
|
46
|
+
{ type: 'title', text: '如何使用胎压转换器', level: 3 },
|
|
47
|
+
{ type: 'list', items: ['输入胎压表上显示的数值。', '选择该数值旁标注的单位。', '选择前轮、后轮或全车车轴以便于记录。', '在转换结果栏中读取所需的对应单位数值。'] },
|
|
48
|
+
{ type: 'tip', title: '转换器不会帮您决定推荐胎压', html: '请在车辆 B 柱贴纸或用户手册中查找推荐的冷态胎压。轮胎侧壁上模印的数字是该轮胎的最高极限承受压力,并非特定车辆的推荐胎压。' },
|
|
49
|
+
{ type: 'title', text: '常见胎压单位一览', level: 3 },
|
|
50
|
+
{ type: 'table', headers: ['单位', '含义', '精确换算关系'], rows: [['PSI', '磅/平方英寸', '1 psi'], ['bar', '巴 (气压单位)', '1 bar = 14.5037738 psi'], ['kPa', '千帕 (国内标准)', '1 kPa = 0.145037738 psi'], ['kgf/cm²', '公斤力/平方厘米', '1 kgf/cm² = 14.2233433 psi']] },
|
|
51
|
+
{ type: 'diagnostic', variant: 'warning', title: '注意测量时的轮胎温度条件', badge: '冷态测量', html: '胎压会随温度变化。为了获得可靠的测量结果,请务必在轮胎处于冷态时进行测量。' },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export const content: TirePressureConverterLocaleContent = {
|
|
55
|
+
slug: 'tire-pressure-converter-psi-bar-kpa-kgf-cm2',
|
|
56
|
+
title: '胎压单位转换计算器',
|
|
57
|
+
description: '轻松转换汽车与摩托车轮胎的 PSI、bar、kPa 和 kgf/cm² 胎压单位,支持前后轴标记。',
|
|
58
|
+
ui: {
|
|
59
|
+
flowLabel: '胎压单位转换桥',
|
|
60
|
+
flowHint: '输入您拥有的读数,选择其单位,即可直接获取其他单位的对应数值。',
|
|
61
|
+
readingLabel: '胎压表读数',
|
|
62
|
+
readingHint: '输入您胎压表或标牌上显示的数字。',
|
|
63
|
+
sourceUnitLabel: '读数单位',
|
|
64
|
+
axleLabel: '车轴标记',
|
|
65
|
+
frontAxle: '前轮',
|
|
66
|
+
rearAxle: '后轮',
|
|
67
|
+
allAxles: '全车车轴',
|
|
68
|
+
conversionLabel: '对应等效胎压',
|
|
69
|
+
psiLabel: 'PSI',
|
|
70
|
+
barLabel: 'bar',
|
|
71
|
+
kpaLabel: 'kPa',
|
|
72
|
+
kgfcm2Label: 'kgf/cm²',
|
|
73
|
+
readyMessage: '已转换:',
|
|
74
|
+
emptyMessage: '请输入胎压读数以查看转换结果。',
|
|
75
|
+
safetyNote: '本工具用于单位换算。特定车辆的冷态推荐胎压请参阅用户手册。',
|
|
76
|
+
inputPlaceholder: '例如: 2.5',
|
|
77
|
+
},
|
|
78
|
+
seo,
|
|
79
|
+
faqTitle: '胎压单位转换常见问题',
|
|
80
|
+
faq: [
|
|
81
|
+
{ question: '我应该打多少胎压?', answer: '请查阅驾驶员门框上的标牌或用户手册中的冷态推荐胎压。本工具仅用于单位换算。' },
|
|
82
|
+
{ question: '为什么前轮和后轮的胎压会不一样?', answer: '前后轴的载荷分布不同,因此厂商会指定不同的前轮与后轮冷态胎压。' },
|
|
83
|
+
{ question: '轮胎侧面印的数字是推荐胎压吗?', answer: '不是。轮胎侧壁上的数字是该轮胎的最大极限承压值,而非车辆推荐值。' },
|
|
84
|
+
],
|
|
85
|
+
bibliographyTitle: '参考资料',
|
|
86
|
+
bibliography: [
|
|
87
|
+
{ name: '米其林轮胎胎压保养指南', url: 'https://www.michelin.com.cn' },
|
|
88
|
+
],
|
|
89
|
+
howTo: [
|
|
90
|
+
{ name: '输入读数', text: '输入胎压表显示的数字。' },
|
|
91
|
+
{ name: '选择源单位', text: '选择 PSI、bar、kPa 或 kgf/cm²。' },
|
|
92
|
+
{ name: '查看转换结果', text: '在结果栏中查看对应的其他单位数值。' },
|
|
93
|
+
],
|
|
94
|
+
schemas,
|
|
95
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { tirePressureConverter } from './entry';
|
|
2
|
+
|
|
3
|
+
export * from './entry';
|
|
4
|
+
|
|
5
|
+
export const TIRE_PRESSURE_CONVERTER_TOOL = {
|
|
6
|
+
entry: tirePressureConverter,
|
|
7
|
+
Component: () => import('./component.astro'),
|
|
8
|
+
SEOComponent: () => import('./seo.astro'),
|
|
9
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
10
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { convertPressure, formatPressure, isPressureUnit, parsePressureInput, pressureNeedleAngle } from './logic';
|
|
3
|
+
|
|
4
|
+
describe('tire pressure conversion', () => {
|
|
5
|
+
it('converts PSI to all supported units', () => {
|
|
6
|
+
const result = convertPressure(32, 'psi');
|
|
7
|
+
expect(result?.values.bar).toBeCloseTo(2.2063, 3);
|
|
8
|
+
expect(result?.values.kpa).toBeCloseTo(220.632, 2);
|
|
9
|
+
expect(result?.values.kgfcm2).toBeCloseTo(2.2498, 3);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('converts from metric units', () => {
|
|
13
|
+
expect(convertPressure(2.2, 'bar')?.psi).toBeCloseTo(31.9083, 3);
|
|
14
|
+
expect(convertPressure(220, 'kpa')?.values.psi).toBeCloseTo(31.9083, 3);
|
|
15
|
+
expect(convertPressure(2.25, 'kgfcm2')?.values.psi).toBeCloseTo(32.0025, 3);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('rejects invalid values and units', () => {
|
|
19
|
+
expect(convertPressure(-1, 'psi')).toBeNull();
|
|
20
|
+
expect(convertPressure(Number.NaN, 'psi')).toBeNull();
|
|
21
|
+
expect(convertPressure(32, 'unknown' as never)).toBeNull();
|
|
22
|
+
expect(isPressureUnit('bar')).toBe(true);
|
|
23
|
+
expect(isPressureUnit('pascal')).toBe(false);
|
|
24
|
+
expect(parsePressureInput('')).toBeNull();
|
|
25
|
+
expect(parsePressureInput(' ')).toBeNull();
|
|
26
|
+
expect(parsePressureInput('-2')).toBeNull();
|
|
27
|
+
expect(parsePressureInput('32')).toBe(32);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('formats values and clamps the scene needle', () => {
|
|
31
|
+
expect(formatPressure(2.20626, 'bar')).toBe('2.21');
|
|
32
|
+
expect(formatPressure(220.632, 'kpa')).toBe('220.6');
|
|
33
|
+
expect(pressureNeedleAngle(0)).toBe(-132);
|
|
34
|
+
expect(pressureNeedleAngle(30)).toBe(0);
|
|
35
|
+
expect(pressureNeedleAngle(90)).toBe(132);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type PressureUnit = 'psi' | 'bar' | 'kpa' | 'kgfcm2';
|
|
2
|
+
|
|
3
|
+
export interface PressureConversion {
|
|
4
|
+
input: number;
|
|
5
|
+
sourceUnit: PressureUnit;
|
|
6
|
+
psi: number;
|
|
7
|
+
values: Record<PressureUnit, number>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const PSI_PER_UNIT: Record<PressureUnit, number> = {
|
|
11
|
+
psi: 1,
|
|
12
|
+
bar: 14.5037738,
|
|
13
|
+
kpa: 0.145037738,
|
|
14
|
+
kgfcm2: 14.2233433,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const PRESSURE_UNITS: PressureUnit[] = ['psi', 'bar', 'kpa', 'kgfcm2'];
|
|
18
|
+
|
|
19
|
+
export function isPressureUnit(value: string): value is PressureUnit {
|
|
20
|
+
return PRESSURE_UNITS.includes(value as PressureUnit);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function convertPressure(input: number, sourceUnit: PressureUnit): PressureConversion | null {
|
|
24
|
+
if (!Number.isFinite(input) || input < 0 || !isPressureUnit(sourceUnit)) return null;
|
|
25
|
+
const psi = input * PSI_PER_UNIT[sourceUnit];
|
|
26
|
+
const values = PRESSURE_UNITS.reduce((result, unit) => {
|
|
27
|
+
result[unit] = psi / PSI_PER_UNIT[unit];
|
|
28
|
+
return result;
|
|
29
|
+
}, {} as Record<PressureUnit, number>);
|
|
30
|
+
return { input, sourceUnit, psi, values };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parsePressureInput(input: string): number | null {
|
|
34
|
+
if (input.trim() === '') return null;
|
|
35
|
+
const value = Number(input);
|
|
36
|
+
return Number.isFinite(value) && value >= 0 ? value : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function formatPressure(value: number, unit: PressureUnit): string {
|
|
40
|
+
const decimals = unit === 'kpa' ? 1 : 2;
|
|
41
|
+
return value.toFixed(decimals);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function pressureNeedleAngle(psi: number): number {
|
|
45
|
+
const clamped = Math.min(Math.max(psi, 0), 60);
|
|
46
|
+
return -132 + (clamped / 60) * 264;
|
|
47
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { tirePressureConverter } from './entry';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const loader = tirePressureConverter.i18n[locale] ?? tirePressureConverter.i18n.en;
|
|
12
|
+
const content = await loader?.();
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PressureUnit } from './logic';
|
|
2
|
+
|
|
3
|
+
export interface SavedPressureState {
|
|
4
|
+
input: string;
|
|
5
|
+
sourceUnit: PressureUnit;
|
|
6
|
+
axle: 'front' | 'rear' | 'all';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const STORAGE_KEY = 'jjlmoya:tire-pressure-converter';
|
|
10
|
+
|
|
11
|
+
export function loadPressureState(): SavedPressureState | null {
|
|
12
|
+
try {
|
|
13
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
14
|
+
if (!raw) return null;
|
|
15
|
+
const state = JSON.parse(raw) as SavedPressureState;
|
|
16
|
+
if (!state || typeof state.input !== 'string') return null;
|
|
17
|
+
return state;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function savePressureState(state: SavedPressureState): void {
|
|
24
|
+
try {
|
|
25
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
.tire-pressure-tool {
|
|
2
|
+
--n-ink: #16241f;
|
|
3
|
+
--n-muted: #5e7168;
|
|
4
|
+
--n-paper: #f4f1e8;
|
|
5
|
+
--n-surface: #fffdf7;
|
|
6
|
+
--n-line: #c6d2c5;
|
|
7
|
+
--n-accent: #d36b3d;
|
|
8
|
+
--n-accent-deep: #873d2b;
|
|
9
|
+
--n-blue: #2d7f89;
|
|
10
|
+
--n-dark: #0d1917;
|
|
11
|
+
--n-white: #fff;
|
|
12
|
+
--n-result-ink: #f8f6ee;
|
|
13
|
+
--n-result-muted: #9fb1a7;
|
|
14
|
+
--n-result-arc: #33433d;
|
|
15
|
+
--n-result-card: #172823;
|
|
16
|
+
--n-result-line: #2c4139;
|
|
17
|
+
|
|
18
|
+
color: var(--n-ink);
|
|
19
|
+
background: var(--n-paper);
|
|
20
|
+
border: 1px solid var(--n-line);
|
|
21
|
+
border-radius: 24px;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.tire-pressure-tool button,
|
|
26
|
+
.tire-pressure-tool input {
|
|
27
|
+
font: inherit;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.pressure-workbench {
|
|
31
|
+
display: grid;
|
|
32
|
+
grid-template-columns: minmax(260px, .9fr) minmax(360px, 1.1fr);
|
|
33
|
+
min-height: 460px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.pressure-input-panel {
|
|
37
|
+
padding: 2rem;
|
|
38
|
+
border-right: 1px solid var(--n-line);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.pressure-workbench-intro {
|
|
42
|
+
margin-bottom: 2rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.pressure-kicker {
|
|
46
|
+
margin: 0 0 .5rem;
|
|
47
|
+
color: var(--n-accent-deep);
|
|
48
|
+
font-size: .72rem;
|
|
49
|
+
font-weight: 800;
|
|
50
|
+
letter-spacing: .14em;
|
|
51
|
+
text-transform: uppercase;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.pressure-workbench-intro p:last-child,
|
|
55
|
+
.pressure-field-hint,
|
|
56
|
+
.pressure-safety {
|
|
57
|
+
color: var(--n-muted);
|
|
58
|
+
line-height: 1.55;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.pressure-field {
|
|
62
|
+
margin-bottom: 1.5rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.pressure-field label,
|
|
66
|
+
.pressure-field legend {
|
|
67
|
+
display: block;
|
|
68
|
+
margin-bottom: .65rem;
|
|
69
|
+
color: var(--n-ink);
|
|
70
|
+
font-size: .82rem;
|
|
71
|
+
font-weight: 800;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.pressure-field-hint {
|
|
75
|
+
margin: .55rem 0 0;
|
|
76
|
+
font-size: .78rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.pressure-input {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: .65rem;
|
|
83
|
+
padding: .85rem 1rem;
|
|
84
|
+
background: var(--n-surface);
|
|
85
|
+
border: 1px solid var(--n-line);
|
|
86
|
+
border-radius: 14px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.pressure-input input {
|
|
90
|
+
width: 100%;
|
|
91
|
+
min-width: 0;
|
|
92
|
+
padding: 0;
|
|
93
|
+
color: var(--n-ink);
|
|
94
|
+
background: transparent;
|
|
95
|
+
border: 0;
|
|
96
|
+
outline: 0;
|
|
97
|
+
font-size: 1.7rem;
|
|
98
|
+
font-weight: 800;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.pressure-input span {
|
|
102
|
+
color: var(--n-muted);
|
|
103
|
+
font-weight: 800;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.pressure-unit-row,
|
|
107
|
+
.pressure-axle-row {
|
|
108
|
+
display: grid;
|
|
109
|
+
grid-template-columns: repeat(4, 1fr);
|
|
110
|
+
gap: .4rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.pressure-axle-row {
|
|
114
|
+
grid-template-columns: repeat(3, 1fr);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.pressure-unit-row button,
|
|
118
|
+
.pressure-axle-row button {
|
|
119
|
+
min-height: 2.7rem;
|
|
120
|
+
padding: .4rem .3rem;
|
|
121
|
+
color: var(--n-muted);
|
|
122
|
+
background: transparent;
|
|
123
|
+
border: 1px solid var(--n-line);
|
|
124
|
+
border-radius: 10px;
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
font-size: .76rem;
|
|
127
|
+
font-weight: 800;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.pressure-unit-row button.is-active,
|
|
131
|
+
.pressure-axle-row button.is-active {
|
|
132
|
+
color: var(--n-white);
|
|
133
|
+
background: var(--n-accent-deep);
|
|
134
|
+
border-color: var(--n-accent-deep);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.pressure-unit-row button:focus-visible,
|
|
138
|
+
.pressure-axle-row button:focus-visible,
|
|
139
|
+
.pressure-input input:focus-visible {
|
|
140
|
+
outline: 3px solid var(--n-accent);
|
|
141
|
+
outline-offset: 3px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.pressure-safety {
|
|
145
|
+
margin: 1.8rem 0 0;
|
|
146
|
+
padding-top: 1rem;
|
|
147
|
+
border-top: 1px solid var(--n-line);
|
|
148
|
+
font-size: .8rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.pressure-result-panel {
|
|
152
|
+
display: grid;
|
|
153
|
+
grid-template-rows: 1fr auto auto;
|
|
154
|
+
padding: 1.5rem 2rem 2rem;
|
|
155
|
+
background: var(--n-dark);
|
|
156
|
+
color: var(--n-result-ink);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.pressure-scene {
|
|
160
|
+
display: grid;
|
|
161
|
+
place-items: center;
|
|
162
|
+
min-height: 250px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.pressure-scene-svg {
|
|
166
|
+
width: min(100%, 390px);
|
|
167
|
+
overflow: visible;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.pressure-scene-arc {
|
|
171
|
+
fill: none;
|
|
172
|
+
stroke: var(--n-result-arc);
|
|
173
|
+
stroke-linecap: round;
|
|
174
|
+
stroke-width: 18;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.pressure-scene-arc-active {
|
|
178
|
+
stroke: var(--n-accent);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.pressure-scene-hub {
|
|
182
|
+
fill: var(--n-accent);
|
|
183
|
+
stroke: var(--n-result-ink);
|
|
184
|
+
stroke-width: 5;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.pressure-scene-needle {
|
|
188
|
+
stroke: var(--n-result-ink);
|
|
189
|
+
stroke-linecap: round;
|
|
190
|
+
stroke-width: 5;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.pressure-scene-value {
|
|
194
|
+
fill: var(--n-result-ink);
|
|
195
|
+
font-size: 28px;
|
|
196
|
+
font-weight: 800;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.pressure-scene-unit,
|
|
200
|
+
.pressure-scene-mark {
|
|
201
|
+
fill: var(--n-result-muted);
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
font-weight: 800;
|
|
204
|
+
letter-spacing: .08em;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.pressure-scene-empty {
|
|
208
|
+
color: var(--n-result-muted);
|
|
209
|
+
font-size: 1rem;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.pressure-conversion-label {
|
|
213
|
+
margin: 0 0 .75rem;
|
|
214
|
+
color: var(--n-result-muted);
|
|
215
|
+
font-size: .72rem;
|
|
216
|
+
font-weight: 800;
|
|
217
|
+
letter-spacing: .14em;
|
|
218
|
+
text-transform: uppercase;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.pressure-values {
|
|
222
|
+
display: grid;
|
|
223
|
+
grid-template-columns: repeat(4, 1fr);
|
|
224
|
+
gap: .5rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.pressure-value {
|
|
228
|
+
padding: .8rem .7rem;
|
|
229
|
+
background: var(--n-result-card);
|
|
230
|
+
border: 1px solid var(--n-result-line);
|
|
231
|
+
border-radius: 12px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.pressure-value span {
|
|
235
|
+
display: block;
|
|
236
|
+
margin-bottom: .3rem;
|
|
237
|
+
color: var(--n-result-muted);
|
|
238
|
+
font-size: .7rem;
|
|
239
|
+
font-weight: 800;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.pressure-value strong {
|
|
243
|
+
display: block;
|
|
244
|
+
color: var(--n-result-ink);
|
|
245
|
+
font-size: 1.1rem;
|
|
246
|
+
white-space: nowrap;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.pressure-status {
|
|
250
|
+
margin: 1rem 0 0;
|
|
251
|
+
color: var(--n-accent);
|
|
252
|
+
font-size: .8rem;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@media (max-width: 760px) {
|
|
256
|
+
.pressure-workbench {
|
|
257
|
+
display: block;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.pressure-input-panel {
|
|
261
|
+
border-right: 0;
|
|
262
|
+
border-bottom: 1px solid var(--n-line);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.pressure-result-panel {
|
|
266
|
+
padding: 1rem 1rem 1.5rem;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@media (max-width: 420px) {
|
|
271
|
+
.pressure-input-panel {
|
|
272
|
+
padding: 1.25rem;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.pressure-unit-row {
|
|
276
|
+
grid-template-columns: repeat(2, 1fr);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.pressure-values {
|
|
280
|
+
grid-template-columns: repeat(2, 1fr);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.theme-dark .tire-pressure-tool {
|
|
285
|
+
--n-ink: #edf4ee;
|
|
286
|
+
--n-muted: #b3c2b8;
|
|
287
|
+
--n-paper: #111b18;
|
|
288
|
+
--n-surface: #182620;
|
|
289
|
+
--n-line: #3d5348;
|
|
290
|
+
--n-accent: #ef9165;
|
|
291
|
+
--n-accent-deep: #bd5e3f;
|
|
292
|
+
--n-dark: #07100e;
|
|
293
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface TirePressureConverterUI extends Record<string, string> {
|
|
2
|
+
flowLabel: string;
|
|
3
|
+
flowHint: string;
|
|
4
|
+
readingLabel: string;
|
|
5
|
+
readingHint: string;
|
|
6
|
+
sourceUnitLabel: string;
|
|
7
|
+
axleLabel: string;
|
|
8
|
+
frontAxle: string;
|
|
9
|
+
rearAxle: string;
|
|
10
|
+
allAxles: string;
|
|
11
|
+
conversionLabel: string;
|
|
12
|
+
psiLabel: string;
|
|
13
|
+
barLabel: string;
|
|
14
|
+
kpaLabel: string;
|
|
15
|
+
kgfcm2Label: string;
|
|
16
|
+
readyMessage: string;
|
|
17
|
+
emptyMessage: string;
|
|
18
|
+
safetyNote: string;
|
|
19
|
+
inputPlaceholder: string;
|
|
20
|
+
}
|
package/src/tools.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { ALL_ENTRIES } from './entries';
|
|
2
2
|
import { BRAKING_DISTANCE_CALCULATOR_TOOL } from './tool/brakingDistanceCalculator';
|
|
3
|
+
import { CAR_MOTORCYCLE_GEAR_RATIO_CALCULATOR_TOOL } from './tool/carMotorcycleGearRatioCalculator';
|
|
3
4
|
import { REAL_FUEL_CONSUMPTION_CALCULATOR_TOOL } from './tool/realFuelConsumptionCalculator';
|
|
5
|
+
import { TIRE_PRESSURE_CONVERTER_TOOL } from './tool/tirePressureConverter';
|
|
4
6
|
|
|
5
|
-
export const ALL_TOOLS = [BRAKING_DISTANCE_CALCULATOR_TOOL, REAL_FUEL_CONSUMPTION_CALCULATOR_TOOL];
|
|
7
|
+
export const ALL_TOOLS = [BRAKING_DISTANCE_CALCULATOR_TOOL, REAL_FUEL_CONSUMPTION_CALCULATOR_TOOL, CAR_MOTORCYCLE_GEAR_RATIO_CALCULATOR_TOOL, TIRE_PRESSURE_CONVERTER_TOOL];
|