@jjlmoya/utils-motor 1.10.0 → 1.12.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.
Files changed (37) hide show
  1. package/package.json +2 -2
  2. package/scripts/validate-icons.mjs +57 -0
  3. package/src/category/index.ts +2 -1
  4. package/src/entries.ts +4 -1
  5. package/src/index.ts +5 -0
  6. package/src/tests/locale_completeness.test.ts +1 -1
  7. package/src/tests/tool_validation.test.ts +1 -1
  8. package/src/tool/trailerTongueWeightCalculator/bibliography.astro +6 -0
  9. package/src/tool/trailerTongueWeightCalculator/bibliography.ts +16 -0
  10. package/src/tool/trailerTongueWeightCalculator/component.astro +116 -0
  11. package/src/tool/trailerTongueWeightCalculator/controller.ts +143 -0
  12. package/src/tool/trailerTongueWeightCalculator/dom-views.ts +79 -0
  13. package/src/tool/trailerTongueWeightCalculator/entry.ts +30 -0
  14. package/src/tool/trailerTongueWeightCalculator/evaluator.ts +10 -0
  15. package/src/tool/trailerTongueWeightCalculator/i18n/de.ts +39 -0
  16. package/src/tool/trailerTongueWeightCalculator/i18n/en.ts +63 -0
  17. package/src/tool/trailerTongueWeightCalculator/i18n/es.ts +38 -0
  18. package/src/tool/trailerTongueWeightCalculator/i18n/fr.ts +38 -0
  19. package/src/tool/trailerTongueWeightCalculator/i18n/id.ts +38 -0
  20. package/src/tool/trailerTongueWeightCalculator/i18n/it.ts +38 -0
  21. package/src/tool/trailerTongueWeightCalculator/i18n/ja.ts +38 -0
  22. package/src/tool/trailerTongueWeightCalculator/i18n/ko.ts +38 -0
  23. package/src/tool/trailerTongueWeightCalculator/i18n/nl.ts +38 -0
  24. package/src/tool/trailerTongueWeightCalculator/i18n/pl.ts +38 -0
  25. package/src/tool/trailerTongueWeightCalculator/i18n/pt.ts +38 -0
  26. package/src/tool/trailerTongueWeightCalculator/i18n/ru.ts +38 -0
  27. package/src/tool/trailerTongueWeightCalculator/i18n/sv.ts +38 -0
  28. package/src/tool/trailerTongueWeightCalculator/i18n/tr.ts +38 -0
  29. package/src/tool/trailerTongueWeightCalculator/i18n/zh.ts +38 -0
  30. package/src/tool/trailerTongueWeightCalculator/index.ts +10 -0
  31. package/src/tool/trailerTongueWeightCalculator/logic.test.ts +44 -0
  32. package/src/tool/trailerTongueWeightCalculator/logic.ts +91 -0
  33. package/src/tool/trailerTongueWeightCalculator/seo.astro +15 -0
  34. package/src/tool/trailerTongueWeightCalculator/storage.ts +32 -0
  35. package/src/tool/trailerTongueWeightCalculator/trailer-tongue-weight-calculator.css +429 -0
  36. package/src/tool/trailerTongueWeightCalculator/ui.ts +49 -0
  37. package/src/tools.ts +4 -1
@@ -0,0 +1,429 @@
1
+ .ttw-tool {
2
+ --n-paper: #f8f4ec;
3
+ --n-surface: #fffdf8;
4
+ --n-surface-muted: #eee8dc;
5
+ --n-ink: #28241f;
6
+ --n-muted: #6e665c;
7
+ --n-line: #d8cfc1;
8
+ --n-accent: #b24a2f;
9
+ --n-accent-soft: #f2d4c8;
10
+ --n-load: #be6d2f;
11
+ --n-axle: #376b68;
12
+ --n-safe: #35735d;
13
+ --n-warn: #a96722;
14
+ --n-danger: #a23d36;
15
+ --n-shadow: #382619;
16
+
17
+ color: var(--n-ink);
18
+ }
19
+
20
+ :global(.theme-dark) .ttw-tool {
21
+ --n-paper: #1c1916;
22
+ --n-surface: #292521;
23
+ --n-surface-muted: #37312b;
24
+ --n-ink: #f6eee3;
25
+ --n-muted: #c7b9a9;
26
+ --n-line: #65594d;
27
+ --n-accent: #e18467;
28
+ --n-accent-soft: #60382e;
29
+ --n-load: #e3a060;
30
+ --n-axle: #7bb6ae;
31
+ --n-safe: #83c8a8;
32
+ --n-warn: #e4ad64;
33
+ --n-danger: #ed8d84;
34
+ --n-shadow: #000;
35
+ }
36
+
37
+ .ttw-surface {
38
+ overflow: hidden;
39
+ border: 1px solid var(--n-line);
40
+ border-radius: 1.5rem;
41
+ background: var(--n-paper);
42
+ box-shadow: 0 1.5rem 3rem var(--n-shadow);
43
+ }
44
+
45
+ .ttw-toolbar,
46
+ .ttw-presets,
47
+ .ttw-layout,
48
+ .ttw-formula { padding: 1.2rem 1.4rem; }
49
+
50
+ .ttw-toolbar {
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: space-between;
54
+ border-bottom: 1px solid var(--n-line);
55
+ background: var(--n-surface);
56
+ }
57
+
58
+ .ttw-unit-switch {
59
+ display: inline-flex;
60
+ overflow: hidden;
61
+ border: 1px solid var(--n-line);
62
+ border-radius: 999px;
63
+ }
64
+
65
+ .ttw-unit-switch button,
66
+ .ttw-reset {
67
+ border: 0;
68
+ background: transparent;
69
+ color: var(--n-muted);
70
+ cursor: pointer;
71
+ font: inherit;
72
+ font-size: 0.8rem;
73
+ font-weight: 800;
74
+ letter-spacing: 0.04em;
75
+ padding: 0.55rem 0.8rem;
76
+ }
77
+
78
+ .ttw-unit-switch button[aria-pressed="true"] {
79
+ background: var(--n-accent);
80
+ color: var(--n-surface);
81
+ }
82
+ .ttw-reset { color: var(--n-accent); }
83
+
84
+ .ttw-reset:hover,
85
+ .ttw-reset:focus-visible,
86
+ .ttw-unit-switch button:focus-visible,
87
+ .ttw-presets button:focus-visible {
88
+ outline: 3px solid var(--n-accent-soft);
89
+ outline-offset: 2px;
90
+ }
91
+
92
+ .ttw-presets {
93
+ display: grid;
94
+ grid-template-columns: 1fr repeat(3, minmax(0, 1fr));
95
+ gap: 0.6rem;
96
+ border-bottom: 1px solid var(--n-line);
97
+ background: var(--n-surface);
98
+ }
99
+
100
+ .ttw-presets-label,
101
+ .ttw-overline {
102
+ color: var(--n-accent);
103
+ font-size: 0.72rem;
104
+ font-weight: 900;
105
+ letter-spacing: 0.12em;
106
+ text-transform: uppercase;
107
+ }
108
+
109
+ .ttw-presets-label { align-self: center; }
110
+
111
+ .ttw-presets button {
112
+ min-width: 0;
113
+ border: 1px solid var(--n-line);
114
+ border-radius: 0.8rem;
115
+ padding: 0.7rem;
116
+ background: var(--n-paper);
117
+ color: var(--n-ink);
118
+ cursor: pointer;
119
+ text-align: left;
120
+ }
121
+
122
+ .ttw-presets button.is-active {
123
+ border-color: var(--n-accent);
124
+ background: var(--n-accent-soft);
125
+ }
126
+ .ttw-presets strong,
127
+ .ttw-presets small { display: block; }
128
+ .ttw-presets small {
129
+ margin-top: 0.2rem;
130
+ color: var(--n-muted);
131
+ font-size: 0.75rem;
132
+ }
133
+
134
+ .ttw-layout {
135
+ display: grid;
136
+ grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
137
+ gap: 1.4rem;
138
+ }
139
+
140
+ .ttw-controls,
141
+ .ttw-result { min-width: 0; }
142
+ .ttw-controls {
143
+ padding-right: 1.4rem;
144
+ border-right: 1px solid var(--n-line);
145
+ }
146
+ .ttw-section-heading { margin-bottom: 1rem; }
147
+ .ttw-overline { margin: 0 0 0.35rem; }
148
+ .ttw-section-heading h2,
149
+ .ttw-result-heading h2 {
150
+ margin: 0;
151
+ font-size: 1.25rem;
152
+ line-height: 1.15;
153
+ }
154
+ .ttw-field-stack,
155
+ .ttw-limits {
156
+ display: grid;
157
+ gap: 0.9rem;
158
+ }
159
+ .ttw-limits {
160
+ margin-top: 1.5rem;
161
+ padding-top: 1.4rem;
162
+ border-top: 1px solid var(--n-line);
163
+ }
164
+ .ttw-field {
165
+ display: grid;
166
+ gap: 0.35rem;
167
+ }
168
+ .ttw-field-title { font-weight: 800; }
169
+ .ttw-field small {
170
+ color: var(--n-muted);
171
+ font-size: 0.78rem;
172
+ line-height: 1.35;
173
+ }
174
+
175
+ .ttw-input-row {
176
+ display: grid;
177
+ grid-template-columns: 1fr auto;
178
+ align-items: center;
179
+ border: 1px solid var(--n-line);
180
+ border-radius: 0.65rem;
181
+ background: var(--n-surface);
182
+ }
183
+
184
+ .ttw-input-row input {
185
+ width: 100%;
186
+ min-width: 0;
187
+ border: 0;
188
+ padding: 0.65rem 0.75rem;
189
+ background: transparent;
190
+ color: var(--n-ink);
191
+ font: inherit;
192
+ font-weight: 800;
193
+ }
194
+
195
+ .ttw-input-row input:focus-visible {
196
+ outline: 2px solid var(--n-accent);
197
+ outline-offset: -2px;
198
+ }
199
+ .ttw-input-row em {
200
+ padding: 0 0.75rem;
201
+ color: var(--n-muted);
202
+ font-size: 0.8rem;
203
+ font-style: normal;
204
+ font-weight: 800;
205
+ }
206
+ .ttw-field input[type="range"] {
207
+ width: 100%;
208
+ accent-color: var(--n-accent);
209
+ }
210
+ .ttw-result { padding: 0.2rem 0; }
211
+
212
+ .ttw-result-heading {
213
+ display: flex;
214
+ align-items: flex-start;
215
+ justify-content: space-between;
216
+ gap: 0.8rem;
217
+ }
218
+
219
+ .ttw-status {
220
+ border: 1px solid var(--n-safe);
221
+ border-radius: 999px;
222
+ padding: 0.4rem 0.65rem;
223
+ color: var(--n-safe);
224
+ font-size: 0.75rem;
225
+ font-weight: 900;
226
+ white-space: nowrap;
227
+ }
228
+
229
+ .ttw-tool[data-status="light"] .ttw-status {
230
+ border-color: var(--n-warn);
231
+ color: var(--n-warn);
232
+ }
233
+ .ttw-tool[data-status="heavy"] .ttw-status,
234
+ .ttw-tool[data-status="invalid"] .ttw-status {
235
+ border-color: var(--n-danger);
236
+ color: var(--n-danger);
237
+ }
238
+
239
+ .ttw-scene-wrap {
240
+ margin-top: 1rem;
241
+ padding: 0.7rem 0.3rem 0.4rem;
242
+ border-top: 1px solid var(--n-line);
243
+ border-bottom: 1px solid var(--n-line);
244
+ }
245
+ .ttw-scene {
246
+ display: block;
247
+ width: 100%;
248
+ height: auto;
249
+ }
250
+
251
+ .ttw-force-line,
252
+ .ttw-deck,
253
+ .ttw-drawbar,
254
+ .ttw-axle,
255
+ .ttw-ground,
256
+ .ttw-load-line {
257
+ fill: none;
258
+ stroke: var(--n-ink);
259
+ stroke-linecap: round;
260
+ stroke-linejoin: round;
261
+ }
262
+
263
+ .ttw-force-line {
264
+ stroke: var(--n-accent);
265
+ stroke-width: 7;
266
+ }
267
+ .ttw-deck { stroke-width: 12; }
268
+ .ttw-drawbar { stroke-width: 8; }
269
+ .ttw-axle {
270
+ stroke: var(--n-axle);
271
+ stroke-width: 6;
272
+ }
273
+ .ttw-ground {
274
+ stroke: var(--n-line);
275
+ stroke-width: 5;
276
+ }
277
+ .ttw-load-line {
278
+ stroke: var(--n-load);
279
+ stroke-dasharray: 5 8;
280
+ stroke-width: 4;
281
+ }
282
+ .ttw-load-dot {
283
+ fill: var(--n-load);
284
+ stroke: var(--n-surface);
285
+ stroke-width: 5;
286
+ }
287
+ .ttw-wheel {
288
+ fill: var(--n-surface-muted);
289
+ stroke: var(--n-axle);
290
+ stroke-width: 6;
291
+ }
292
+ .ttw-scene-label {
293
+ fill: var(--n-muted);
294
+ font-size: 13px;
295
+ font-weight: 700;
296
+ }
297
+ .ttw-scene-value {
298
+ fill: var(--n-ink);
299
+ font-size: 16px;
300
+ font-weight: 900;
301
+ text-anchor: middle;
302
+ }
303
+
304
+ .ttw-scene-legend {
305
+ display: flex;
306
+ flex-wrap: wrap;
307
+ gap: 0.8rem 1rem;
308
+ color: var(--n-muted);
309
+ font-size: 0.75rem;
310
+ }
311
+ .ttw-scene-legend span {
312
+ display: inline-flex;
313
+ align-items: center;
314
+ gap: 0.35rem;
315
+ }
316
+ .ttw-dot {
317
+ width: 0.65rem;
318
+ height: 0.65rem;
319
+ border-radius: 50%;
320
+ }
321
+ .ttw-dot-load { background: var(--n-load); }
322
+ .ttw-dot-tongue { background: var(--n-accent); }
323
+ .ttw-dot-axle { background: var(--n-axle); }
324
+
325
+ .ttw-lead {
326
+ display: grid;
327
+ grid-template-columns: 1fr auto;
328
+ align-items: end;
329
+ gap: 0.25rem 0.8rem;
330
+ margin-top: 1rem;
331
+ }
332
+ .ttw-lead span {
333
+ color: var(--n-muted);
334
+ font-size: 0.8rem;
335
+ font-weight: 800;
336
+ }
337
+ .ttw-lead strong {
338
+ grid-row: span 2;
339
+ color: var(--n-accent);
340
+ font-size: clamp(2rem, 5vw, 3.3rem);
341
+ line-height: 0.95;
342
+ }
343
+ .ttw-lead small {
344
+ color: var(--n-muted);
345
+ font-weight: 800;
346
+ }
347
+
348
+ .ttw-metrics {
349
+ display: grid;
350
+ grid-template-columns: repeat(3, 1fr);
351
+ gap: 0.6rem;
352
+ margin-top: 1rem;
353
+ }
354
+ .ttw-metrics div {
355
+ min-width: 0;
356
+ padding: 0.75rem;
357
+ border-left: 3px solid var(--n-line);
358
+ background: var(--n-surface);
359
+ }
360
+ .ttw-metrics span,
361
+ .ttw-metrics strong { display: block; }
362
+ .ttw-metrics span {
363
+ color: var(--n-muted);
364
+ font-size: 0.72rem;
365
+ }
366
+ .ttw-metrics strong {
367
+ margin-top: 0.35rem;
368
+ font-size: 0.95rem;
369
+ overflow-wrap: anywhere;
370
+ }
371
+
372
+ .ttw-shift {
373
+ margin-top: 1rem;
374
+ padding: 0.8rem 0.9rem;
375
+ border-left: 3px solid var(--n-accent);
376
+ background: var(--n-surface);
377
+ }
378
+ .ttw-shift strong {
379
+ display: block;
380
+ font-size: 0.85rem;
381
+ }
382
+ .ttw-shift p {
383
+ margin: 0.25rem 0 0;
384
+ color: var(--n-muted);
385
+ line-height: 1.45;
386
+ }
387
+ .ttw-summary {
388
+ margin: 1rem 0 0;
389
+ color: var(--n-muted);
390
+ line-height: 1.55;
391
+ }
392
+
393
+ .ttw-formula {
394
+ display: flex;
395
+ flex-wrap: wrap;
396
+ gap: 0.35rem 0.8rem;
397
+ border-top: 1px solid var(--n-line);
398
+ background: var(--n-surface);
399
+ color: var(--n-muted);
400
+ font-size: 0.8rem;
401
+ line-height: 1.5;
402
+ }
403
+ .ttw-formula strong { color: var(--n-ink); }
404
+
405
+ @media (max-width: 760px) {
406
+ .ttw-presets { grid-template-columns: 1fr; }
407
+ .ttw-presets-label { margin-bottom: -0.2rem; }
408
+ .ttw-layout { grid-template-columns: 1fr; }
409
+ .ttw-controls {
410
+ padding-right: 0;
411
+ border-right: 0;
412
+ }
413
+ .ttw-result {
414
+ padding-top: 1.2rem;
415
+ border-top: 1px solid var(--n-line);
416
+ }
417
+ }
418
+
419
+ @media (max-width: 460px) {
420
+ .ttw-toolbar,
421
+ .ttw-presets,
422
+ .ttw-layout,
423
+ .ttw-formula {
424
+ padding-left: 1rem;
425
+ padding-right: 1rem;
426
+ }
427
+ .ttw-metrics { grid-template-columns: 1fr; }
428
+ .ttw-scene-legend { gap: 0.5rem; }
429
+ }
@@ -0,0 +1,49 @@
1
+ export interface TrailerTongueWeightCalculatorUI extends Record<string, string> {
2
+ metricLabel: string;
3
+ imperialLabel: string;
4
+ resetLabel: string;
5
+ presetLabel: string;
6
+ balancedPreset: string;
7
+ frontHeavyPreset: string;
8
+ rearHeavyPreset: string;
9
+ presetMetaTemplate: string;
10
+ geometryLabel: string;
11
+ geometryHint: string;
12
+ totalMassLabel: string;
13
+ totalMassHint: string;
14
+ hitchDistanceLabel: string;
15
+ hitchDistanceHint: string;
16
+ centerPositionLabel: string;
17
+ centerPositionHint: string;
18
+ tongueLimitsLabel: string;
19
+ tongueLimitsHint: string;
20
+ minimumTongueLabel: string;
21
+ minimumTongueHint: string;
22
+ maximumTongueLabel: string;
23
+ maximumTongueHint: string;
24
+ resultKicker: string;
25
+ resultTitle: string;
26
+ sceneLabel: string;
27
+ hitchLabel: string;
28
+ axleLabel: string;
29
+ centerOfMassLabel: string;
30
+ loadedMassLabel: string;
31
+ tongueWeightLabel: string;
32
+ tonguePercentLabel: string;
33
+ axleLoadLabel: string;
34
+ allowedRangeLabel: string;
35
+ statusSafe: string;
36
+ statusLight: string;
37
+ statusHeavy: string;
38
+ statusInvalid: string;
39
+ shiftTitle: string;
40
+ shiftForwardTemplate: string;
41
+ shiftRearwardTemplate: string;
42
+ shiftNone: string;
43
+ summaryTemplate: string;
44
+ formulaLabel: string;
45
+ formulaText: string;
46
+ inputUnitMass: string;
47
+ inputUnitDistance: string;
48
+ outputUnitMass: string;
49
+ }
package/src/tools.ts CHANGED
@@ -5,7 +5,10 @@ import { MOTORCYCLE_SKID_MARK_SPEED_ESTIMATOR_TOOL } from './tool/motorcycleSkid
5
5
  import { REAL_FUEL_CONSUMPTION_CALCULATOR_TOOL } from './tool/realFuelConsumptionCalculator';
6
6
  import { TIRE_PRESSURE_CONVERTER_TOOL } from './tool/tirePressureConverter';
7
7
  import { EV_CHARGING_TIME_COST_PLANNER_TOOL } from './tool/evChargingTimeCostPlanner';
8
+ import { TRAILER_TONGUE_WEIGHT_CALCULATOR_TOOL } from './tool/trailerTongueWeightCalculator';
8
9
 
9
- export const ALL_TOOLS = [BRAKING_DISTANCE_CALCULATOR_TOOL, REAL_FUEL_CONSUMPTION_CALCULATOR_TOOL, CAR_MOTORCYCLE_GEAR_RATIO_CALCULATOR_TOOL, TIRE_PRESSURE_CONVERTER_TOOL, MOTORCYCLE_SKID_MARK_SPEED_ESTIMATOR_TOOL, EV_CHARGING_TIME_COST_PLANNER_TOOL];
10
+ export const ALL_TOOLS = [BRAKING_DISTANCE_CALCULATOR_TOOL, REAL_FUEL_CONSUMPTION_CALCULATOR_TOOL, CAR_MOTORCYCLE_GEAR_RATIO_CALCULATOR_TOOL, TIRE_PRESSURE_CONVERTER_TOOL, MOTORCYCLE_SKID_MARK_SPEED_ESTIMATOR_TOOL, EV_CHARGING_TIME_COST_PLANNER_TOOL, TRAILER_TONGUE_WEIGHT_CALCULATOR_TOOL];
10
11
  export { evChargingTimeCostPlanner } from './tool/evChargingTimeCostPlanner/entry';
11
12
  export type { EVChargingTimeCostPlannerLocaleContent } from './tool/evChargingTimeCostPlanner/entry';
13
+ export { trailerTongueWeightCalculator } from './tool/trailerTongueWeightCalculator/entry';
14
+ export type { TrailerTongueWeightCalculatorLocaleContent } from './tool/trailerTongueWeightCalculator/entry';