@jjlmoya/utils-aquarium 1.0.0 → 1.52.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 +13 -4
- package/src/tests/bibliography_wellformed_export.test.ts +46 -0
- package/src/tests/i18n_coverage.test.ts +1 -1
- package/src/tests/seo_wellformed_export.test.ts +65 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/aquarium-tank-volume-water-change-calculator.css +107 -56
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/bibliography.ts +6 -6
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/component.astro +24 -39
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/controller.ts +90 -39
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/dom-views.ts +19 -11
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/entry.ts +14 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/de.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/en.ts +8 -8
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/es.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/fr.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/id.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/it.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/ja.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/ko.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/nl.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/pl.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/pt.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/ru.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/sv.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/tr.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/zh.ts +34 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/localized-content.ts +60 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/logic.test.ts +10 -0
- package/src/tool/aquariumTankVolumeWaterChangeCalculator/ui.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jjlmoya/utils-aquarium",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -11,8 +11,13 @@
|
|
|
11
11
|
"./runtime/*": "./src/tool/*/index.ts",
|
|
12
12
|
"./category-seo": "./src/category/seo.astro"
|
|
13
13
|
},
|
|
14
|
-
"files": [
|
|
15
|
-
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"scripts"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
16
21
|
"scripts": {
|
|
17
22
|
"dev": "astro dev",
|
|
18
23
|
"start": "astro dev",
|
|
@@ -29,7 +34,11 @@
|
|
|
29
34
|
"major": "npm version major",
|
|
30
35
|
"postinstall": "node scripts/postinstall.mjs"
|
|
31
36
|
},
|
|
32
|
-
"lint-staged": {
|
|
37
|
+
"lint-staged": {
|
|
38
|
+
"*.{ts,tsx,astro}": [
|
|
39
|
+
"eslint --fix"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
33
42
|
"dependencies": {
|
|
34
43
|
"@iconify-json/mdi": "^1.2.3",
|
|
35
44
|
"@jjlmoya/prompagate": "^1.1.0",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { readdirSync, readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join, relative } from 'path';
|
|
4
|
+
|
|
5
|
+
function findBibliographyAstroFiles(dir: string): string[] {
|
|
6
|
+
const files: string[] = [];
|
|
7
|
+
if (!existsSync(dir)) return files;
|
|
8
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
9
|
+
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const fullPath = join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
files.push(...findBibliographyAstroFiles(fullPath));
|
|
14
|
+
} else if (entry.isFile() && entry.name === 'bibliography.astro') {
|
|
15
|
+
files.push(fullPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const toolDir = join(process.cwd(), 'src', 'tool');
|
|
23
|
+
const bibliographyFiles = findBibliographyAstroFiles(toolDir);
|
|
24
|
+
|
|
25
|
+
describe('Bibliography Component Wellformed Export', () => {
|
|
26
|
+
it('found tool bibliography.astro components', () => {
|
|
27
|
+
expect(bibliographyFiles.length).toBeGreaterThan(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
bibliographyFiles.forEach((file) => {
|
|
31
|
+
const relativePath = relative(process.cwd(), file);
|
|
32
|
+
|
|
33
|
+
it(`${relativePath} should use SharedBibliography from @jjlmoya/utils-shared`, () => {
|
|
34
|
+
const content = readFileSync(file, 'utf-8');
|
|
35
|
+
|
|
36
|
+
const usesSharedComponent =
|
|
37
|
+
content.includes('@jjlmoya/utils-shared') &&
|
|
38
|
+
(content.includes('Bibliography') || content.includes('SharedBibliography'));
|
|
39
|
+
|
|
40
|
+
expect(
|
|
41
|
+
usesSharedComponent,
|
|
42
|
+
`File "${relativePath}" does not use the standard Bibliography component from @jjlmoya/utils-shared, resulting in unstyled or raw bibliography output.`,
|
|
43
|
+
).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { ALL_TOOLS } from '../tools';
|
|
3
3
|
|
|
4
|
-
const EXPECTED_LOCALES = ['en'];
|
|
4
|
+
const EXPECTED_LOCALES = ['de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'];
|
|
5
5
|
|
|
6
6
|
describe('I18n Coverage Validation', () => {
|
|
7
7
|
ALL_TOOLS.forEach(({ entry }) => {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { readdirSync, readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join, relative } from 'path';
|
|
4
|
+
|
|
5
|
+
function findSeoAstroFiles(dir: string): string[] {
|
|
6
|
+
const files: string[] = [];
|
|
7
|
+
if (!existsSync(dir)) return files;
|
|
8
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
9
|
+
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const fullPath = join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
files.push(...findSeoAstroFiles(fullPath));
|
|
14
|
+
} else if (entry.isFile() && entry.name === 'seo.astro') {
|
|
15
|
+
files.push(fullPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const toolDir = join(process.cwd(), 'src', 'tool');
|
|
23
|
+
const seoFiles = findSeoAstroFiles(toolDir);
|
|
24
|
+
|
|
25
|
+
describe('SEO Component Wellformed Export', () => {
|
|
26
|
+
it('found tool seo.astro components', () => {
|
|
27
|
+
expect(seoFiles.length).toBeGreaterThan(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
seoFiles.forEach((file) => {
|
|
31
|
+
const relativePath = relative(process.cwd(), file);
|
|
32
|
+
|
|
33
|
+
it(`${relativePath} should dynamically load SEO sections and use SEORenderer`, () => {
|
|
34
|
+
const content = readFileSync(file, 'utf-8');
|
|
35
|
+
|
|
36
|
+
const usesSeoRenderer =
|
|
37
|
+
content.includes('@jjlmoya/utils-shared') &&
|
|
38
|
+
(content.includes('SEORenderer') || content.includes('SEOArticle'));
|
|
39
|
+
|
|
40
|
+
const acquiresDynamicContent =
|
|
41
|
+
content.includes('.i18n') ||
|
|
42
|
+
content.includes('loader') ||
|
|
43
|
+
content.includes('await loader') ||
|
|
44
|
+
content.includes('.seo');
|
|
45
|
+
|
|
46
|
+
const isBrokenPattern =
|
|
47
|
+
/sections\s*=\s*\[\s*\]/.test(content) && !acquiresDynamicContent;
|
|
48
|
+
|
|
49
|
+
expect(
|
|
50
|
+
usesSeoRenderer,
|
|
51
|
+
`File "${relativePath}" does not import or use SEORenderer from @jjlmoya/utils-shared.`,
|
|
52
|
+
).toBe(true);
|
|
53
|
+
|
|
54
|
+
expect(
|
|
55
|
+
isBrokenPattern,
|
|
56
|
+
`File "${relativePath}" relies on a static sections=[] prop default without fetching content from entry.i18n, resulting in empty SEO text on consumers.`,
|
|
57
|
+
).toBe(false);
|
|
58
|
+
|
|
59
|
+
expect(
|
|
60
|
+
acquiresDynamicContent,
|
|
61
|
+
`File "${relativePath}" does not fetch dynamic i18n content for SEO rendering.`,
|
|
62
|
+
).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
--n-accent: #d96f4f;
|
|
11
11
|
--n-accent-ink: #fff;
|
|
12
12
|
--n-error: #a33632;
|
|
13
|
-
--n-shadow:
|
|
13
|
+
--n-shadow: #dbe6e2;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
.theme-dark {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
--n-accent: #ef9474;
|
|
26
26
|
--n-accent-ink: #251512;
|
|
27
27
|
--n-error: #ff9b91;
|
|
28
|
-
--n-shadow:
|
|
28
|
+
--n-shadow: #101a1c;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.atvwc-root {
|
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
padding: clamp(1rem, 3vw, 2rem);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
.atvwc-heading,
|
|
45
44
|
.atvwc-panel-topline,
|
|
46
45
|
.atvwc-result-heading,
|
|
47
46
|
.atvwc-footnotes {
|
|
@@ -51,6 +50,7 @@
|
|
|
51
50
|
justify-content: space-between;
|
|
52
51
|
}
|
|
53
52
|
|
|
53
|
+
.atvwc-step,
|
|
54
54
|
.atvwc-eyebrow,
|
|
55
55
|
.atvwc-subtitle,
|
|
56
56
|
.atvwc-help,
|
|
@@ -62,12 +62,20 @@
|
|
|
62
62
|
margin: 0;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
.atvwc-step,
|
|
65
66
|
.atvwc-eyebrow {
|
|
66
67
|
font-size: 1.15rem;
|
|
67
68
|
font-weight: 800;
|
|
68
69
|
letter-spacing: .02em;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
.atvwc-step {
|
|
73
|
+
color: var(--n-water);
|
|
74
|
+
font-size: .75rem;
|
|
75
|
+
letter-spacing: .12em;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
.atvwc-subtitle,
|
|
72
80
|
.atvwc-help,
|
|
73
81
|
.atvwc-result-hint,
|
|
@@ -92,23 +100,17 @@
|
|
|
92
100
|
|
|
93
101
|
.atvwc-unit-control button,
|
|
94
102
|
.atvwc-shape,
|
|
95
|
-
.atvwc-reset
|
|
96
|
-
|
|
103
|
+
.atvwc-reset {
|
|
104
|
+
background: var(--n-card);
|
|
97
105
|
border: 1px solid var(--n-line);
|
|
98
106
|
border-radius: .7rem;
|
|
107
|
+
color: var(--n-ink);
|
|
99
108
|
cursor: pointer;
|
|
100
109
|
font: inherit;
|
|
101
110
|
min-height: 2.6rem;
|
|
102
111
|
padding: .55rem .8rem;
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
.atvwc-unit-control button,
|
|
106
|
-
.atvwc-shape,
|
|
107
|
-
.atvwc-reset {
|
|
108
|
-
background: var(--n-card);
|
|
109
|
-
color: var(--n-ink);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
114
|
.atvwc-unit-control button[aria-pressed="true"],
|
|
113
115
|
.atvwc-shape.is-active {
|
|
114
116
|
background: var(--n-water-soft);
|
|
@@ -119,16 +121,13 @@
|
|
|
119
121
|
|
|
120
122
|
.atvwc-layout {
|
|
121
123
|
display: grid;
|
|
122
|
-
gap:
|
|
124
|
+
gap: 0;
|
|
123
125
|
grid-template-columns: minmax(0, 1.1fr) minmax(18rem, .9fr);
|
|
124
|
-
margin-top:
|
|
126
|
+
margin-top: 0;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
.atvwc-panel {
|
|
128
|
-
|
|
129
|
-
border: 1px solid var(--n-line);
|
|
130
|
-
border-radius: 1.1rem;
|
|
131
|
-
padding: clamp(1rem, 2vw, 1.5rem);
|
|
130
|
+
padding: .25rem 0;
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
.atvwc-panel h2 {
|
|
@@ -136,6 +135,16 @@
|
|
|
136
135
|
margin: 0;
|
|
137
136
|
}
|
|
138
137
|
|
|
138
|
+
.atvwc-form-panel {
|
|
139
|
+
border-right: 1px solid var(--n-line);
|
|
140
|
+
padding-right: clamp(1rem, 3vw, 2rem);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.atvwc-visual-panel {
|
|
144
|
+
min-height: 0;
|
|
145
|
+
padding-left: clamp(1rem, 3vw, 2rem);
|
|
146
|
+
}
|
|
147
|
+
|
|
139
148
|
.atvwc-reset {
|
|
140
149
|
min-height: 2.25rem;
|
|
141
150
|
padding: .35rem .65rem;
|
|
@@ -186,29 +195,31 @@
|
|
|
186
195
|
.atvwc-field input:focus-visible,
|
|
187
196
|
.atvwc-unit-control button:focus-visible,
|
|
188
197
|
.atvwc-shape:focus-visible,
|
|
189
|
-
.atvwc-reset:focus-visible
|
|
190
|
-
.atvwc-calculate:focus-visible {
|
|
198
|
+
.atvwc-reset:focus-visible {
|
|
191
199
|
outline: 3px solid var(--n-focus);
|
|
192
200
|
outline-offset: 2px;
|
|
193
201
|
}
|
|
194
202
|
|
|
195
|
-
.atvwc-calculate {
|
|
196
|
-
background: var(--n-accent);
|
|
197
|
-
border-color: var(--n-accent);
|
|
198
|
-
color: var(--n-accent-ink);
|
|
199
|
-
font-weight: 800;
|
|
200
|
-
margin-top: 1.25rem;
|
|
201
|
-
width: 100%;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
203
|
.atvwc-bow-note {
|
|
205
204
|
display: none;
|
|
206
205
|
}
|
|
207
206
|
|
|
208
|
-
.atvwc-
|
|
207
|
+
.atvwc-root[data-shape="bow"] .atvwc-bow-note {
|
|
208
|
+
display: block;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.atvwc-scene-header {
|
|
212
|
+
align-items: center;
|
|
209
213
|
display: flex;
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
justify-content: space-between;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.atvwc-scene-kicker {
|
|
218
|
+
color: var(--n-muted);
|
|
219
|
+
font-size: .82rem;
|
|
220
|
+
font-weight: 800;
|
|
221
|
+
letter-spacing: .08em;
|
|
222
|
+
text-transform: uppercase;
|
|
212
223
|
}
|
|
213
224
|
|
|
214
225
|
.atvwc-tank-stage {
|
|
@@ -234,6 +245,17 @@
|
|
|
234
245
|
width: 10rem;
|
|
235
246
|
}
|
|
236
247
|
|
|
248
|
+
.atvwc-root[data-shape="cylindrical"] .atvwc-tank {
|
|
249
|
+
border-radius: 50%;
|
|
250
|
+
height: 7rem;
|
|
251
|
+
width: 7rem;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.atvwc-root[data-shape="bow"] .atvwc-tank {
|
|
255
|
+
border-radius: .8rem 2.8rem 2.8rem .8rem;
|
|
256
|
+
transform: perspective(14rem) rotateY(-8deg);
|
|
257
|
+
}
|
|
258
|
+
|
|
237
259
|
.atvwc-water,
|
|
238
260
|
.atvwc-gravel {
|
|
239
261
|
bottom: 0;
|
|
@@ -244,7 +266,7 @@
|
|
|
244
266
|
|
|
245
267
|
.atvwc-water {
|
|
246
268
|
background: var(--n-water);
|
|
247
|
-
height: 70
|
|
269
|
+
height: var(--water-level, 70%);
|
|
248
270
|
opacity: .82;
|
|
249
271
|
transition: height .25s ease;
|
|
250
272
|
}
|
|
@@ -266,23 +288,6 @@
|
|
|
266
288
|
width: 84%;
|
|
267
289
|
}
|
|
268
290
|
|
|
269
|
-
.atvwc-fish {
|
|
270
|
-
color: var(--n-accent);
|
|
271
|
-
font-size: 1.5rem;
|
|
272
|
-
position: absolute;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.atvwc-fish-one {
|
|
276
|
-
left: 20%;
|
|
277
|
-
top: 32%;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.atvwc-fish-two {
|
|
281
|
-
right: 19%;
|
|
282
|
-
top: 54%;
|
|
283
|
-
transform: scaleX(-1);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
291
|
.atvwc-result-heading {
|
|
287
292
|
align-items: center;
|
|
288
293
|
margin-top: 1.25rem;
|
|
@@ -294,6 +299,13 @@
|
|
|
294
299
|
margin: .25rem 0 0;
|
|
295
300
|
}
|
|
296
301
|
|
|
302
|
+
.atvwc-result-heading p {
|
|
303
|
+
color: var(--n-accent);
|
|
304
|
+
font-size: .95rem;
|
|
305
|
+
font-weight: 800;
|
|
306
|
+
margin: .4rem 0 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
297
309
|
.atvwc-scale {
|
|
298
310
|
background: var(--n-water-soft);
|
|
299
311
|
border-radius: 999px;
|
|
@@ -337,28 +349,59 @@
|
|
|
337
349
|
.atvwc-footnotes {
|
|
338
350
|
flex-wrap: wrap;
|
|
339
351
|
gap: .6rem 1rem;
|
|
340
|
-
margin-top: auto;
|
|
341
352
|
padding-top: 1rem;
|
|
342
353
|
}
|
|
343
354
|
|
|
355
|
+
.atvwc-stage-note {
|
|
356
|
+
align-items: flex-start;
|
|
357
|
+
bottom: 1rem;
|
|
358
|
+
display: grid;
|
|
359
|
+
gap: .1rem;
|
|
360
|
+
left: 1rem;
|
|
361
|
+
position: absolute;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.atvwc-stage-note strong {
|
|
365
|
+
color: var(--n-ink);
|
|
366
|
+
font-size: .95rem;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.atvwc-stage-note span,
|
|
370
|
+
.atvwc-level-mark {
|
|
371
|
+
color: var(--n-muted);
|
|
372
|
+
font-size: .72rem;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.atvwc-level-mark {
|
|
376
|
+
position: absolute;
|
|
377
|
+
right: 1rem;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.atvwc-level-mark-top {
|
|
381
|
+
top: 1rem;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.atvwc-level-mark-bottom {
|
|
385
|
+
bottom: 1rem;
|
|
386
|
+
}
|
|
387
|
+
|
|
344
388
|
.atvwc-error {
|
|
345
389
|
color: var(--n-error);
|
|
346
390
|
font-weight: 700;
|
|
347
391
|
margin-top: .75rem;
|
|
348
392
|
}
|
|
349
393
|
|
|
350
|
-
.atvwc-root
|
|
351
|
-
.atvwc-root
|
|
394
|
+
.atvwc-root.has-results .atvwc-empty,
|
|
395
|
+
.atvwc-root.has-error .atvwc-empty {
|
|
352
396
|
display: none;
|
|
353
397
|
}
|
|
354
398
|
|
|
355
|
-
.atvwc-root
|
|
356
|
-
.atvwc-root
|
|
399
|
+
.atvwc-root.has-error .atvwc-result-card strong,
|
|
400
|
+
.atvwc-root.has-error .atvwc-footnotes strong {
|
|
357
401
|
color: var(--n-muted);
|
|
358
402
|
}
|
|
359
403
|
|
|
360
404
|
@media (max-width: 760px) {
|
|
361
|
-
.atvwc-heading,
|
|
362
405
|
.atvwc-layout {
|
|
363
406
|
display: block;
|
|
364
407
|
}
|
|
@@ -369,7 +412,15 @@
|
|
|
369
412
|
}
|
|
370
413
|
|
|
371
414
|
.atvwc-visual-panel {
|
|
415
|
+
border-top: 1px solid var(--n-line);
|
|
372
416
|
margin-top: 1.25rem;
|
|
417
|
+
padding-left: 0;
|
|
418
|
+
padding-top: 1.25rem;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.atvwc-form-panel {
|
|
422
|
+
border-right: 0;
|
|
423
|
+
padding-right: 0;
|
|
373
424
|
}
|
|
374
425
|
|
|
375
426
|
.atvwc-input-grid {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export const bibliography = [
|
|
2
2
|
{
|
|
3
|
-
name: '
|
|
4
|
-
url: 'https://www.
|
|
3
|
+
name: 'Merck Veterinary Manual, Formulae to Calculate Tank Volumes',
|
|
4
|
+
url: 'https://www.merckvetmanual.com/multimedia/table/formulae-to-calculate-tank-volumes',
|
|
5
5
|
},
|
|
6
6
|
{
|
|
7
|
-
name: '
|
|
8
|
-
url: 'https://www.
|
|
7
|
+
name: 'FAO, Determinación de los volúmenes de agua',
|
|
8
|
+
url: 'https://www.fao.org/fishery/static/FAO_Training/FAO_Training/General/x6709s/x6709s15.htm',
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
|
-
name: '
|
|
12
|
-
url: 'https://
|
|
11
|
+
name: 'U.S. Geological Survey, Water Density',
|
|
12
|
+
url: 'https://www.usgs.gov/water-science-school/science/water-density',
|
|
13
13
|
},
|
|
14
14
|
];
|
|
@@ -6,23 +6,18 @@ const t = (ui ?? {}) as AquariumTankVolumeWaterChangeCalculatorUI;
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
<div id="aquarium-tank-volume-water-change-calculator" class="atvwc-root" data-ui={JSON.stringify(t)}>
|
|
9
|
-
<div class="atvwc-heading">
|
|
10
|
-
<div>
|
|
11
|
-
<p class="atvwc-eyebrow">{t.toolTitle}</p>
|
|
12
|
-
<p class="atvwc-subtitle">{t.toolSubtitle}</p>
|
|
13
|
-
</div>
|
|
14
|
-
<div class="atvwc-unit-control" role="group" aria-label={t.unitLabel}>
|
|
15
|
-
<span class="atvwc-control-label">{t.unitLabel}</span>
|
|
16
|
-
<button type="button" data-unit-choice="metric" aria-pressed="true">{t.metricLabel}</button>
|
|
17
|
-
<button type="button" data-unit-choice="imperial" aria-pressed="false">{t.imperialLabel}</button>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
9
|
<div class="atvwc-layout">
|
|
22
10
|
<section class="atvwc-panel atvwc-form-panel" aria-labelledby="atvwc-dimensions-title">
|
|
23
11
|
<div class="atvwc-panel-topline">
|
|
24
|
-
<
|
|
25
|
-
|
|
12
|
+
<div>
|
|
13
|
+
<p class="atvwc-step">{t.configureLabel}</p>
|
|
14
|
+
<h2 id="atvwc-dimensions-title">{t.dimensionsTitle}</h2>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="atvwc-unit-control" role="group" aria-label={t.unitLabel}>
|
|
17
|
+
<span class="atvwc-control-label">{t.unitLabel}</span>
|
|
18
|
+
<button type="button" data-unit-choice="metric" aria-pressed="true">{t.metricLabel}</button>
|
|
19
|
+
<button type="button" data-unit-choice="imperial" aria-pressed="false">{t.imperialLabel}</button>
|
|
20
|
+
</div>
|
|
26
21
|
</div>
|
|
27
22
|
|
|
28
23
|
<fieldset class="atvwc-fieldset">
|
|
@@ -35,43 +30,33 @@ const t = (ui ?? {}) as AquariumTankVolumeWaterChangeCalculatorUI;
|
|
|
35
30
|
<p class="atvwc-help">{t.shapeHint}</p>
|
|
36
31
|
</fieldset>
|
|
37
32
|
|
|
38
|
-
<div class="atvwc-input-grid" data-
|
|
39
|
-
<label class="atvwc-field"><span>{t.lengthLabel} <span data-length-unit
|
|
40
|
-
<label class="atvwc-field"><span>{t.widthLabel} <span data-length-unit
|
|
41
|
-
<label class="atvwc-field"><span>{t.heightLabel} <span data-length-unit
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
<div class="atvwc-input-grid" data-shape-fields="cylindrical" hidden>
|
|
45
|
-
<label class="atvwc-field"><span>{t.diameterLabel} <span data-length-unit></span></span><input data-input="diameterCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
46
|
-
<label class="atvwc-field"><span>{t.heightLabel} <span data-length-unit></span></span><input data-input="heightCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
33
|
+
<div class="atvwc-input-grid" data-dynamic-fields>
|
|
34
|
+
<label class="atvwc-field" data-field="lengthCm"><span>{t.lengthLabel} <span data-length-unit>{t.lengthUnitMetric}</span></span><input data-input="lengthCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
35
|
+
<label class="atvwc-field" data-field="widthCm"><span>{t.widthLabel} <span data-length-unit>{t.lengthUnitMetric}</span></span><input data-input="widthCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
36
|
+
<label class="atvwc-field" data-field="heightCm"><span>{t.heightLabel} <span data-length-unit>{t.lengthUnitMetric}</span></span><input data-input="heightCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
47
37
|
</div>
|
|
48
38
|
|
|
49
|
-
<div class="atvwc-input-grid
|
|
50
|
-
<label class="atvwc-field"><span>{t.
|
|
51
|
-
<label class="atvwc-field"><span>{t.widthLabel} <span data-length-unit></span></span><input data-input="widthCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
52
|
-
<label class="atvwc-field"><span>{t.heightLabel} <span data-length-unit></span></span><input data-input="heightCm" type="number" min="0.1" max="500" step="0.1" inputmode="decimal" /></label>
|
|
53
|
-
<label class="atvwc-field"><span>{t.bowDepthLabel} <span data-length-unit></span></span><input data-input="bowDepthCm" type="number" min="0" max="500" step="0.1" inputmode="decimal" /></label>
|
|
54
|
-
</div>
|
|
55
|
-
|
|
56
|
-
<div class="atvwc-input-grid">
|
|
57
|
-
<label class="atvwc-field"><span>{t.substrateLabel} <span data-length-unit></span></span><input data-input="substrateDepthCm" type="number" min="0" max="500" step="0.1" inputmode="decimal" /></label>
|
|
39
|
+
<div class="atvwc-input-grid atvwc-common-fields">
|
|
40
|
+
<label class="atvwc-field"><span>{t.substrateLabel} <span data-length-unit>{t.lengthUnitMetric}</span></span><input data-input="substrateDepthCm" type="number" min="0" max="500" step="0.1" inputmode="decimal" /></label>
|
|
58
41
|
<label class="atvwc-field"><span>{t.changeLabel} %</span><input data-input="changePercent" type="number" min="0" max="100" step="1" inputmode="decimal" /></label>
|
|
59
42
|
</div>
|
|
60
43
|
|
|
61
44
|
<p class="atvwc-help">{t.substrateHint}</p>
|
|
62
45
|
<p class="atvwc-help atvwc-bow-note">{t.bowHint}</p>
|
|
63
|
-
<button type="button" class="atvwc-calculate" data-action="calculate">{t.calculateLabel}</button>
|
|
64
46
|
</section>
|
|
65
47
|
|
|
66
48
|
<section class="atvwc-panel atvwc-visual-panel" aria-labelledby="atvwc-results-title">
|
|
67
|
-
<div class="atvwc-
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
|
|
49
|
+
<div class="atvwc-scene-header">
|
|
50
|
+
<span class="atvwc-scene-kicker">{t.netVolumeLabel}</span>
|
|
51
|
+
<span class="atvwc-scale" data-result="scale"></span>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="atvwc-tank-stage" aria-label={t.resultHint}>
|
|
54
|
+
<div class="atvwc-tank" aria-hidden="true"><div class="atvwc-water" data-water></div><div class="atvwc-gravel"></div><div class="atvwc-glass-line"></div><span class="atvwc-level-mark atvwc-level-mark-top">100%</span><span class="atvwc-level-mark atvwc-level-mark-bottom">0%</span></div>
|
|
55
|
+
<div class="atvwc-stage-note"><strong data-result="stage-volume">-</strong><span>{t.netVolumeLabel}</span></div>
|
|
71
56
|
</div>
|
|
72
57
|
<div class="atvwc-result-heading">
|
|
73
|
-
<
|
|
74
|
-
<
|
|
58
|
+
<h2 id="atvwc-results-title" data-result="net">{t.emptyState}</h2>
|
|
59
|
+
<p data-result="change-summary"></p>
|
|
75
60
|
</div>
|
|
76
61
|
<p class="atvwc-result-hint">{t.resultHint}</p>
|
|
77
62
|
<p class="atvwc-error" data-error role="alert"></p>
|