@jjlmoya/utils-tools 1.1.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 +63 -0
- package/src/category/i18n/en.ts +172 -0
- package/src/category/i18n/es.ts +172 -0
- package/src/category/i18n/fr.ts +172 -0
- package/src/category/index.ts +23 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +11 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +90 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +146 -0
- package/src/pages/[locale].astro +251 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/locale_completeness.test.ts +42 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/seo_length.test.ts +23 -0
- package/src/tests/title_quality.test.ts +56 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/date-diff-calculator/bibliography.astro +14 -0
- package/src/tool/date-diff-calculator/component.astro +370 -0
- package/src/tool/date-diff-calculator/i18n/en.ts +132 -0
- package/src/tool/date-diff-calculator/i18n/es.ts +132 -0
- package/src/tool/date-diff-calculator/i18n/fr.ts +132 -0
- package/src/tool/date-diff-calculator/index.ts +22 -0
- package/src/tool/date-diff-calculator/seo.astro +14 -0
- package/src/tool/date-diff-calculator/ui.ts +17 -0
- package/src/tool/drive-direct-link/bibliography.astro +14 -0
- package/src/tool/drive-direct-link/component.astro +280 -0
- package/src/tool/drive-direct-link/i18n/en.ts +118 -0
- package/src/tool/drive-direct-link/i18n/es.ts +118 -0
- package/src/tool/drive-direct-link/i18n/fr.ts +118 -0
- package/src/tool/drive-direct-link/index.ts +22 -0
- package/src/tool/drive-direct-link/seo.astro +14 -0
- package/src/tool/drive-direct-link/ui.ts +10 -0
- package/src/tool/email-list-cleaner/bibliography.astro +14 -0
- package/src/tool/email-list-cleaner/component.astro +375 -0
- package/src/tool/email-list-cleaner/i18n/en.ts +140 -0
- package/src/tool/email-list-cleaner/i18n/es.ts +140 -0
- package/src/tool/email-list-cleaner/i18n/fr.ts +140 -0
- package/src/tool/email-list-cleaner/index.ts +22 -0
- package/src/tool/email-list-cleaner/seo.astro +14 -0
- package/src/tool/email-list-cleaner/ui.ts +15 -0
- package/src/tool/env-badge-spain/bibliography.astro +14 -0
- package/src/tool/env-badge-spain/component.astro +303 -0
- package/src/tool/env-badge-spain/components/BadgeForm.astro +243 -0
- package/src/tool/env-badge-spain/components/BadgeResult.astro +151 -0
- package/src/tool/env-badge-spain/i18n/en.ts +153 -0
- package/src/tool/env-badge-spain/i18n/es.ts +153 -0
- package/src/tool/env-badge-spain/i18n/fr.ts +153 -0
- package/src/tool/env-badge-spain/index.ts +22 -0
- package/src/tool/env-badge-spain/seo.astro +14 -0
- package/src/tool/env-badge-spain/ui.ts +53 -0
- package/src/tool/morse-beacon/bibliography.astro +14 -0
- package/src/tool/morse-beacon/component.astro +534 -0
- package/src/tool/morse-beacon/i18n/en.ts +157 -0
- package/src/tool/morse-beacon/i18n/es.ts +157 -0
- package/src/tool/morse-beacon/i18n/fr.ts +157 -0
- package/src/tool/morse-beacon/index.ts +22 -0
- package/src/tool/morse-beacon/logic/MorseEngine.ts +124 -0
- package/src/tool/morse-beacon/seo.astro +14 -0
- package/src/tool/morse-beacon/ui.ts +18 -0
- package/src/tool/password-generator/bibliography.astro +14 -0
- package/src/tool/password-generator/component.astro +259 -0
- package/src/tool/password-generator/components/Config.astro +227 -0
- package/src/tool/password-generator/components/Display.astro +147 -0
- package/src/tool/password-generator/components/Strength.astro +70 -0
- package/src/tool/password-generator/i18n/en.ts +166 -0
- package/src/tool/password-generator/i18n/es.ts +166 -0
- package/src/tool/password-generator/i18n/fr.ts +166 -0
- package/src/tool/password-generator/index.ts +22 -0
- package/src/tool/password-generator/seo.astro +14 -0
- package/src/tool/password-generator/ui.ts +16 -0
- package/src/tool/routes/bibliography.astro +14 -0
- package/src/tool/routes/component.astro +543 -0
- package/src/tool/routes/i18n/en.ts +157 -0
- package/src/tool/routes/i18n/es.ts +157 -0
- package/src/tool/routes/i18n/fr.ts +157 -0
- package/src/tool/routes/index.ts +22 -0
- package/src/tool/routes/logic/GeocodingService.ts +60 -0
- package/src/tool/routes/logic/RouteManager.ts +192 -0
- package/src/tool/routes/logic/RouteService.ts +66 -0
- package/src/tool/routes/seo.astro +14 -0
- package/src/tool/routes/ui.ts +16 -0
- package/src/tool/rule-of-three/bibliography.astro +14 -0
- package/src/tool/rule-of-three/component.astro +369 -0
- package/src/tool/rule-of-three/i18n/en.ts +171 -0
- package/src/tool/rule-of-three/i18n/es.ts +171 -0
- package/src/tool/rule-of-three/i18n/fr.ts +171 -0
- package/src/tool/rule-of-three/index.ts +22 -0
- package/src/tool/rule-of-three/seo.astro +14 -0
- package/src/tool/rule-of-three/ui.ts +13 -0
- package/src/tool/seo-content-optimizer/bibliography.astro +14 -0
- package/src/tool/seo-content-optimizer/component.astro +552 -0
- package/src/tool/seo-content-optimizer/i18n/en.ts +136 -0
- package/src/tool/seo-content-optimizer/i18n/es.ts +136 -0
- package/src/tool/seo-content-optimizer/i18n/fr.ts +136 -0
- package/src/tool/seo-content-optimizer/index.ts +22 -0
- package/src/tool/seo-content-optimizer/seo.astro +14 -0
- package/src/tool/seo-content-optimizer/ui.ts +29 -0
- package/src/tool/speed-reader/bibliography.astro +14 -0
- package/src/tool/speed-reader/component.astro +586 -0
- package/src/tool/speed-reader/i18n/en.ts +152 -0
- package/src/tool/speed-reader/i18n/es.ts +152 -0
- package/src/tool/speed-reader/i18n/fr.ts +152 -0
- package/src/tool/speed-reader/index.ts +22 -0
- package/src/tool/speed-reader/logic/RSVPEngine.ts +106 -0
- package/src/tool/speed-reader/seo.astro +14 -0
- package/src/tool/speed-reader/ui.ts +14 -0
- package/src/tool/text-pixel-calculator/bibliography.astro +14 -0
- package/src/tool/text-pixel-calculator/component.astro +315 -0
- package/src/tool/text-pixel-calculator/components/Editor.astro +240 -0
- package/src/tool/text-pixel-calculator/components/Preview.astro +155 -0
- package/src/tool/text-pixel-calculator/components/Stats.astro +87 -0
- package/src/tool/text-pixel-calculator/i18n/en.ts +133 -0
- package/src/tool/text-pixel-calculator/i18n/es.ts +133 -0
- package/src/tool/text-pixel-calculator/i18n/fr.ts +133 -0
- package/src/tool/text-pixel-calculator/index.ts +22 -0
- package/src/tool/text-pixel-calculator/seo.astro +14 -0
- package/src/tool/text-pixel-calculator/ui.ts +15 -0
- package/src/tool/whatsapp-link/bibliography.astro +14 -0
- package/src/tool/whatsapp-link/component.astro +455 -0
- package/src/tool/whatsapp-link/i18n/en.ts +128 -0
- package/src/tool/whatsapp-link/i18n/es.ts +128 -0
- package/src/tool/whatsapp-link/i18n/fr.ts +128 -0
- package/src/tool/whatsapp-link/index.ts +22 -0
- package/src/tool/whatsapp-link/seo.astro +14 -0
- package/src/tool/whatsapp-link/ui.ts +15 -0
- package/src/tools.ts +15 -0
- package/src/types.ts +72 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface RoutesUI extends Record<string, string> {
|
|
2
|
+
titleSidebar: string;
|
|
3
|
+
descriptionSidebar: string;
|
|
4
|
+
emptyState: string;
|
|
5
|
+
btnOptimize: string;
|
|
6
|
+
btnCalculating: string;
|
|
7
|
+
btnClear: string;
|
|
8
|
+
labelDistance: string;
|
|
9
|
+
labelDeleteAria: string;
|
|
10
|
+
labelLoadingAddress: string;
|
|
11
|
+
labelPoint: string;
|
|
12
|
+
errorMinPoints: string;
|
|
13
|
+
errorCalculate: string;
|
|
14
|
+
errorAddress: string;
|
|
15
|
+
errorAddressName: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { ruleOfThree } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'es' } = Astro.props;
|
|
11
|
+
const content = await ruleOfThree.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SharedBibliography links={content.bibliography} />}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { KnownLocale } from '../../types';
|
|
3
|
+
import type { RuleOfThreeUI } from './ui';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: KnownLocale;
|
|
7
|
+
ui?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { ui } = Astro.props;
|
|
11
|
+
const t = (ui ?? {}) as RuleOfThreeUI;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<div class="rot-root">
|
|
15
|
+
<div class="rot-card">
|
|
16
|
+
<div class="rot-grid">
|
|
17
|
+
<div class="rot-row">
|
|
18
|
+
<div class="rot-field">
|
|
19
|
+
<label class="rot-label" for="rot-val-a">{t.labelA}</label>
|
|
20
|
+
<input type="number" id="rot-val-a" placeholder="A" class="rot-input" />
|
|
21
|
+
<div class="rot-hint">{t.hintA}</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="rot-separator">
|
|
25
|
+
<div class="rot-sep-line"></div>
|
|
26
|
+
<span class="rot-sep-text">{t.separatorIsTo}</span>
|
|
27
|
+
<div class="rot-sep-line"></div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="rot-field">
|
|
31
|
+
<label class="rot-label" for="rot-val-b">{t.labelB}</label>
|
|
32
|
+
<input type="number" id="rot-val-b" placeholder="B" class="rot-input" />
|
|
33
|
+
<div class="rot-hint">{t.hintB}</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div class="rot-row">
|
|
38
|
+
<div class="rot-field">
|
|
39
|
+
<label class="rot-label" for="rot-val-c">{t.labelC}</label>
|
|
40
|
+
<input type="number" id="rot-val-c" placeholder="C" class="rot-input rot-input-purple" />
|
|
41
|
+
<div class="rot-hint">{t.hintC}</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="rot-separator">
|
|
45
|
+
<div class="rot-sep-line"></div>
|
|
46
|
+
<span class="rot-sep-text">{t.separatorWillBe}</span>
|
|
47
|
+
<div class="rot-sep-line"></div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="rot-field rot-field-result">
|
|
51
|
+
<label class="rot-label rot-label-result" for="rot-val-x">{t.labelX}</label>
|
|
52
|
+
<div class="rot-result-row">
|
|
53
|
+
<input type="text" id="rot-val-x" readonly placeholder="X" class="rot-input rot-input-result" />
|
|
54
|
+
<button id="rot-copy-btn" class="rot-copy-btn" title={t.copyTitle} aria-label={t.copyTitle}>
|
|
55
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
56
|
+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect>
|
|
57
|
+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path>
|
|
58
|
+
</svg>
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="rot-hint rot-hint-result">{t.hintX}</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="rot-formula">
|
|
67
|
+
(<span id="rot-formula-b" class="rot-formula-b">B</span>
|
|
68
|
+
×
|
|
69
|
+
<span id="rot-formula-c" class="rot-formula-c">C</span>)
|
|
70
|
+
÷
|
|
71
|
+
<span id="rot-formula-a" class="rot-formula-a">A</span>
|
|
72
|
+
= <span class="rot-formula-x">X</span>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
.rot-root {
|
|
79
|
+
--rot-accent: #7c3aed;
|
|
80
|
+
--rot-accent-light: #8b5cf6;
|
|
81
|
+
--rot-purple: #9333ea;
|
|
82
|
+
--rot-result: #2563eb;
|
|
83
|
+
--rot-result-bg: #eff6ff;
|
|
84
|
+
--rot-result-border: #bfdbfe;
|
|
85
|
+
--rot-result-label: #2563eb;
|
|
86
|
+
--rot-field-bg: #f8fafc;
|
|
87
|
+
--rot-field-border: #e2e8f0;
|
|
88
|
+
--rot-field-hover: #bfdbfe;
|
|
89
|
+
--rot-field-hover-purple: #e9d5ff;
|
|
90
|
+
--rot-card-bg: rgba(255, 255, 255, 0.8);
|
|
91
|
+
--rot-card-border: rgba(255, 255, 255, 0.2);
|
|
92
|
+
--rot-text-main: #334155;
|
|
93
|
+
--rot-text-label: #64748b;
|
|
94
|
+
--rot-text-hint: #94a3b8;
|
|
95
|
+
--rot-formula-bg: #f1f5f9;
|
|
96
|
+
--rot-formula-text: #64748b;
|
|
97
|
+
--rot-sep-color: #cbd5e1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:global(.theme-dark) .rot-root {
|
|
101
|
+
--rot-result-bg: rgba(37, 99, 235, 0.2);
|
|
102
|
+
--rot-result-border: rgba(37, 99, 235, 0.3);
|
|
103
|
+
--rot-result-label: #60a5fa;
|
|
104
|
+
--rot-result: #60a5fa;
|
|
105
|
+
--rot-field-bg: rgba(30, 41, 59, 0.5);
|
|
106
|
+
--rot-field-border: #334155;
|
|
107
|
+
--rot-field-hover: rgba(37, 99, 235, 0.3);
|
|
108
|
+
--rot-field-hover-purple: rgba(147, 51, 234, 0.3);
|
|
109
|
+
--rot-card-bg: rgba(15, 23, 42, 0.8);
|
|
110
|
+
--rot-card-border: rgba(51, 65, 85, 0.3);
|
|
111
|
+
--rot-text-main: #e2e8f0;
|
|
112
|
+
--rot-text-label: #94a3b8;
|
|
113
|
+
--rot-text-hint: #64748b;
|
|
114
|
+
--rot-formula-bg: #1e293b;
|
|
115
|
+
--rot-formula-text: #94a3b8;
|
|
116
|
+
--rot-sep-color: #475569;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.rot-card {
|
|
120
|
+
width: 100%;
|
|
121
|
+
max-width: 56rem;
|
|
122
|
+
margin: 0 auto;
|
|
123
|
+
background: var(--rot-card-bg);
|
|
124
|
+
border: 1px solid var(--rot-card-border);
|
|
125
|
+
border-radius: 1.5rem;
|
|
126
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.08);
|
|
127
|
+
padding: 2rem;
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
gap: 3rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@media (min-width: 768px) {
|
|
134
|
+
.rot-card { padding: 3rem; }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.rot-grid {
|
|
138
|
+
display: flex;
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
gap: 1.5rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.rot-row {
|
|
144
|
+
display: grid;
|
|
145
|
+
grid-template-columns: 1fr;
|
|
146
|
+
gap: 1.5rem;
|
|
147
|
+
align-items: center;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@media (min-width: 768px) {
|
|
151
|
+
.rot-row { grid-template-columns: 1fr auto 1fr; }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.rot-field {
|
|
155
|
+
background: var(--rot-field-bg);
|
|
156
|
+
border: 2px solid var(--rot-field-border);
|
|
157
|
+
border-radius: 1rem;
|
|
158
|
+
padding: 1.5rem;
|
|
159
|
+
transition: border-color 0.2s;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.rot-field:focus-within {
|
|
163
|
+
border-color: var(--rot-field-hover);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.rot-field-result {
|
|
167
|
+
background: var(--rot-result-bg);
|
|
168
|
+
border-color: var(--rot-result-border);
|
|
169
|
+
box-shadow: 0 10px 15px -3px rgba(37, 99, 235, 0.05);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.rot-label {
|
|
173
|
+
display: block;
|
|
174
|
+
font-size: 0.75rem;
|
|
175
|
+
font-weight: 700;
|
|
176
|
+
color: var(--rot-text-label);
|
|
177
|
+
text-transform: uppercase;
|
|
178
|
+
letter-spacing: 0.05em;
|
|
179
|
+
margin-bottom: 0.5rem;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.rot-label-result {
|
|
183
|
+
color: var(--rot-result-label);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.rot-input {
|
|
187
|
+
width: 100%;
|
|
188
|
+
text-align: center;
|
|
189
|
+
font-size: 1.875rem;
|
|
190
|
+
font-weight: 700;
|
|
191
|
+
background: transparent;
|
|
192
|
+
border: none;
|
|
193
|
+
outline: none;
|
|
194
|
+
color: var(--rot-text-main);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.rot-input::placeholder {
|
|
198
|
+
color: var(--rot-text-hint);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.rot-input-purple:focus {
|
|
202
|
+
color: var(--rot-purple);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.rot-input-result {
|
|
206
|
+
font-size: 2.25rem;
|
|
207
|
+
font-weight: 900;
|
|
208
|
+
color: var(--rot-result);
|
|
209
|
+
cursor: default;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.rot-input-result::placeholder {
|
|
213
|
+
color: var(--rot-result-border);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
input[type="number"]::-webkit-inner-spin-button,
|
|
217
|
+
input[type="number"]::-webkit-outer-spin-button {
|
|
218
|
+
-webkit-appearance: none;
|
|
219
|
+
margin: 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
input[type="number"] {
|
|
223
|
+
-moz-appearance: textfield;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.rot-hint {
|
|
227
|
+
font-size: 0.75rem;
|
|
228
|
+
text-align: center;
|
|
229
|
+
color: var(--rot-text-hint);
|
|
230
|
+
margin-top: 0.5rem;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.rot-hint-result {
|
|
234
|
+
color: rgba(37, 99, 235, 0.5);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.rot-result-row {
|
|
238
|
+
position: relative;
|
|
239
|
+
display: flex;
|
|
240
|
+
align-items: center;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.rot-copy-btn {
|
|
244
|
+
position: absolute;
|
|
245
|
+
right: 0;
|
|
246
|
+
top: 50%;
|
|
247
|
+
transform: translateY(-50%);
|
|
248
|
+
padding: 0.5rem;
|
|
249
|
+
background: none;
|
|
250
|
+
border: none;
|
|
251
|
+
cursor: pointer;
|
|
252
|
+
color: var(--rot-result);
|
|
253
|
+
opacity: 0.5;
|
|
254
|
+
transition: opacity 0.15s;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.rot-copy-btn:hover {
|
|
258
|
+
opacity: 1;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.rot-separator {
|
|
262
|
+
display: flex;
|
|
263
|
+
align-items: center;
|
|
264
|
+
justify-content: center;
|
|
265
|
+
gap: 0.5rem;
|
|
266
|
+
color: var(--rot-sep-color);
|
|
267
|
+
padding: 0.5rem 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.rot-sep-line {
|
|
271
|
+
height: 1px;
|
|
272
|
+
width: 1rem;
|
|
273
|
+
background: currentcolor;
|
|
274
|
+
display: none;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@media (min-width: 768px) {
|
|
278
|
+
.rot-sep-line { display: block; }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.rot-sep-text {
|
|
282
|
+
font-size: 0.875rem;
|
|
283
|
+
font-weight: 500;
|
|
284
|
+
text-transform: uppercase;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.rot-formula {
|
|
288
|
+
display: inline-block;
|
|
289
|
+
padding: 0.75rem 1.5rem;
|
|
290
|
+
background: var(--rot-formula-bg);
|
|
291
|
+
border-radius: 0.75rem;
|
|
292
|
+
font-size: 0.9rem;
|
|
293
|
+
color: var(--rot-formula-text);
|
|
294
|
+
text-align: center;
|
|
295
|
+
width: 100%;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.rot-formula-a {
|
|
299
|
+
font-weight: 700;
|
|
300
|
+
color: var(--rot-text-main);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.rot-formula-b {
|
|
304
|
+
font-weight: 700;
|
|
305
|
+
color: var(--rot-text-main);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.rot-formula-c {
|
|
309
|
+
font-weight: 700;
|
|
310
|
+
color: var(--rot-purple);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.rot-formula-x {
|
|
314
|
+
font-weight: 700;
|
|
315
|
+
color: var(--rot-result);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@keyframes rot-pop {
|
|
319
|
+
0% { transform: scale(1); }
|
|
320
|
+
50% { transform: scale(1.05); }
|
|
321
|
+
100% { transform: scale(1); }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
:global(.rot-pop) { animation: rot-pop 0.2s ease; }
|
|
325
|
+
</style>
|
|
326
|
+
|
|
327
|
+
<script>
|
|
328
|
+
const valA = document.getElementById('rot-val-a') as HTMLInputElement;
|
|
329
|
+
const valB = document.getElementById('rot-val-b') as HTMLInputElement;
|
|
330
|
+
const valC = document.getElementById('rot-val-c') as HTMLInputElement;
|
|
331
|
+
const valX = document.getElementById('rot-val-x') as HTMLInputElement;
|
|
332
|
+
const formulaA = document.getElementById('rot-formula-a');
|
|
333
|
+
const formulaB = document.getElementById('rot-formula-b');
|
|
334
|
+
const formulaC = document.getElementById('rot-formula-c');
|
|
335
|
+
|
|
336
|
+
function calculate() {
|
|
337
|
+
const a = parseFloat(valA.value);
|
|
338
|
+
const b = parseFloat(valB.value);
|
|
339
|
+
const c = parseFloat(valC.value);
|
|
340
|
+
|
|
341
|
+
if (formulaA) formulaA.textContent = valA.value || 'A';
|
|
342
|
+
if (formulaB) formulaB.textContent = valB.value || 'B';
|
|
343
|
+
if (formulaC) formulaC.textContent = valC.value || 'C';
|
|
344
|
+
|
|
345
|
+
if (!isNaN(a) && !isNaN(b) && !isNaN(c) && a !== 0) {
|
|
346
|
+
valX.value = parseFloat(((b * c) / a).toFixed(4)).toString();
|
|
347
|
+
valX.parentElement?.classList.remove('rot-pop');
|
|
348
|
+
requestAnimationFrame(() => { valX.parentElement?.classList.add('rot-pop'); });
|
|
349
|
+
} else {
|
|
350
|
+
valX.value = '';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
[valA, valB, valC].forEach((input) => { input.addEventListener('input', calculate); });
|
|
355
|
+
|
|
356
|
+
const copyBtn = document.getElementById('rot-copy-btn');
|
|
357
|
+
copyBtn?.addEventListener('click', () => {
|
|
358
|
+
if (!valX.value) return;
|
|
359
|
+
navigator.clipboard.writeText(valX.value);
|
|
360
|
+
const svg = copyBtn.querySelector('svg');
|
|
361
|
+
if (!svg) return;
|
|
362
|
+
svg.innerHTML = '<path d="M20 6L9 17l-5-5"/>';
|
|
363
|
+
svg.style.color = '#22c55e';
|
|
364
|
+
setTimeout(() => {
|
|
365
|
+
svg.innerHTML = '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path>';
|
|
366
|
+
svg.style.color = '';
|
|
367
|
+
}, 1000);
|
|
368
|
+
});
|
|
369
|
+
</script>
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
2
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
3
|
+
import type { RuleOfThreeUI } from '../ui';
|
|
4
|
+
|
|
5
|
+
const faqData = [
|
|
6
|
+
{
|
|
7
|
+
question: 'What is the simple direct rule of three?',
|
|
8
|
+
answer: "It is a mathematical method for solving proportionality problems between three known values and one unknown. It is called 'direct' because when one quantity increases, the other increases in the same proportion.",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
question: 'When should I use the inverse rule of three?',
|
|
12
|
+
answer: 'It is used when the quantities are inversely proportional (if one increases, the other decreases). For example: if 2 painters take 5 hours, 4 painters will take less time. Our current calculator focuses on direct proportionality.',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
question: 'Is it accurate for professional calculations?',
|
|
16
|
+
answer: 'Yes. Our calculator uses high-fidelity floating point precision, ideal for dosage adjustments, graphic design scales, commercial budgets and recipe conversions.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
question: 'How is it calculated manually?',
|
|
20
|
+
answer: 'The formula is: (Value B * Value C) / Value A = X. That is, you multiply the diagonal values and divide the result by the remaining value.',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const howToData = [
|
|
25
|
+
{
|
|
26
|
+
name: 'Identify the base relationship',
|
|
27
|
+
text: 'Enter the two known values that form the initial proportion (e.g. 100km equals 8 litres).',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Enter the third value',
|
|
31
|
+
text: 'Write the new value whose equivalent you want to find (e.g. if I am going to travel 250km...).',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Get the result',
|
|
35
|
+
text: 'The system will automatically calculate the unknown value (X) in real time without pressing any buttons.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Copy or reset',
|
|
39
|
+
text: 'You can copy the result directly to the clipboard or clear the fields to perform a new calculation.',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
44
|
+
'@context': 'https://schema.org',
|
|
45
|
+
'@type': 'FAQPage',
|
|
46
|
+
mainEntity: faqData.map((item) => ({
|
|
47
|
+
'@type': 'Question',
|
|
48
|
+
name: item.question,
|
|
49
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
50
|
+
})),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const howToSchema: WithContext<HowTo> = {
|
|
54
|
+
'@context': 'https://schema.org',
|
|
55
|
+
'@type': 'HowTo',
|
|
56
|
+
name: 'How to use the rule of three calculator',
|
|
57
|
+
step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
61
|
+
'@context': 'https://schema.org',
|
|
62
|
+
'@type': 'SoftwareApplication',
|
|
63
|
+
name: 'Simple Rule of Three Calculator',
|
|
64
|
+
applicationCategory: 'UtilitiesApplication',
|
|
65
|
+
operatingSystem: 'Web',
|
|
66
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
67
|
+
description: 'Solve mathematical proportions instantly. The definitive tool for calculating quantities, recipes, discounts and conversions.',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ui: RuleOfThreeUI = {
|
|
71
|
+
labelA: 'If I have...',
|
|
72
|
+
labelB: 'It equals...',
|
|
73
|
+
labelC: 'And now I have...',
|
|
74
|
+
labelX: 'Result (X)',
|
|
75
|
+
hintA: 'Initial quantity',
|
|
76
|
+
hintB: 'Known value',
|
|
77
|
+
hintC: 'New quantity',
|
|
78
|
+
hintX: 'Calculated value',
|
|
79
|
+
separatorIsTo: 'Is to',
|
|
80
|
+
separatorWillBe: 'Will be',
|
|
81
|
+
copyTitle: 'Copy result',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const content: ToolLocaleContent<RuleOfThreeUI> = {
|
|
85
|
+
slug: 'rule-of-three',
|
|
86
|
+
title: 'Simple Rule of Three Calculator',
|
|
87
|
+
description: 'Solve mathematical proportions instantly. The definitive tool for calculating quantities, recipes, discounts and conversions.',
|
|
88
|
+
ui,
|
|
89
|
+
faqTitle: 'Frequently Asked Questions',
|
|
90
|
+
faq: faqData,
|
|
91
|
+
howTo: howToData,
|
|
92
|
+
bibliographyTitle: 'References',
|
|
93
|
+
bibliography: [
|
|
94
|
+
{ name: 'Proportionality (mathematics) — Wikipedia', url: 'https://en.wikipedia.org/wiki/Proportionality_(mathematics)' },
|
|
95
|
+
{ name: 'Ratios and proportions — Khan Academy', url: 'https://www.khanacademy.org/math/pre-algebra/pre-algebra-ratios-rates/pre-algebra-ratios-intro/a/ratio-review' },
|
|
96
|
+
],
|
|
97
|
+
schemas: [faqSchema, howToSchema, appSchema],
|
|
98
|
+
seo: [
|
|
99
|
+
{
|
|
100
|
+
type: 'title',
|
|
101
|
+
text: 'What is the rule of three used for?',
|
|
102
|
+
level: 2,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'paragraph',
|
|
106
|
+
html: 'The simple direct rule of three is the "Swiss army knife" of mathematics. It solves problems where three data points are known and we seek the fourth. If you know the relationship between <strong>A and B</strong>, and you have a new value <strong>C</strong>, the calculator finds <strong>X</strong> instantly.',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: 'comparative',
|
|
110
|
+
columns: 2,
|
|
111
|
+
items: [
|
|
112
|
+
{
|
|
113
|
+
icon: 'mdi:chef-hat',
|
|
114
|
+
title: 'Cooking and Recipes',
|
|
115
|
+
description: 'If the recipe calls for 500g of flour for 4 people, how much do I need for 7?',
|
|
116
|
+
points: ['(500 × 7) ÷ 4 = 875g'],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
icon: 'mdi:shopping',
|
|
120
|
+
title: 'Shopping and Prices',
|
|
121
|
+
description: 'If a pack of 3 cans costs €2.50, how much would 10 cans cost me?',
|
|
122
|
+
points: ['(2.50 × 10) ÷ 3 = €8.33'],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
icon: 'mdi:car',
|
|
126
|
+
title: 'Travel and Distance',
|
|
127
|
+
description: 'If I take 45 min to do 60km, how long will it take me to do 140km?',
|
|
128
|
+
points: ['(45 × 140) ÷ 60 = 105 min'],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
icon: 'mdi:monitor-screenshot',
|
|
132
|
+
title: 'Design and Screens',
|
|
133
|
+
description: 'If the image is 1920px wide and I want to reduce it to 800px maintaining the proportion...',
|
|
134
|
+
points: ['(OriginalHeight × 800) ÷ 1920 = NewHeight'],
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'title',
|
|
140
|
+
text: 'How does the formula work?',
|
|
141
|
+
level: 2,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
type: 'paragraph',
|
|
145
|
+
html: 'The formula is always the same: <strong>multiply the diagonal values and divide by the remaining one</strong>.',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
type: 'code',
|
|
149
|
+
code: '(B × C) ÷ A = X',
|
|
150
|
+
ariaLabel: 'Simple direct rule of three formula',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: 'paragraph',
|
|
154
|
+
html: 'In the calculator: we multiply value <strong>B</strong> by value <strong>C</strong> and divide by <strong>A</strong>. The result is <strong>X</strong>.',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: 'title',
|
|
158
|
+
text: 'Professional and everyday uses',
|
|
159
|
+
level: 2,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
type: 'paragraph',
|
|
163
|
+
html: 'Beyond school mathematics, the rule of three is everywhere: in pharmacy for calculating dosages by body weight, in photography for calculating exposure times, in finance for calculating percentage variations, or in printing for scaling format dimensions.',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: 'tip',
|
|
167
|
+
title: 'Precision tip',
|
|
168
|
+
html: 'The result is displayed with up to 4 significant decimal places. If you work with money or exact measurements, remember to round according to context: euros to 2 decimal places, pixels to integers.',
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|