@jjlmoya/utils-nature 1.25.0 → 1.26.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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +2 -1
  4. package/src/index.ts +2 -0
  5. package/src/pages/[locale]/[slug].astro +5 -4
  6. package/src/tests/consumer_loader_contract.test.ts +24 -0
  7. package/src/tests/tool_validation.test.ts +2 -2
  8. package/src/tool/wildlifeCameraTrapEffortPlanner/bibliography.astro +9 -0
  9. package/src/tool/wildlifeCameraTrapEffortPlanner/bibliography.ts +16 -0
  10. package/src/tool/wildlifeCameraTrapEffortPlanner/component.astro +115 -0
  11. package/src/tool/wildlifeCameraTrapEffortPlanner/controller.ts +81 -0
  12. package/src/tool/wildlifeCameraTrapEffortPlanner/dom-views.ts +98 -0
  13. package/src/tool/wildlifeCameraTrapEffortPlanner/entry.ts +29 -0
  14. package/src/tool/wildlifeCameraTrapEffortPlanner/evaluator.test.ts +22 -0
  15. package/src/tool/wildlifeCameraTrapEffortPlanner/evaluator.ts +11 -0
  16. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/de.ts +49 -0
  17. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/en.ts +189 -0
  18. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/es.ts +46 -0
  19. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/fr.ts +44 -0
  20. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/id.ts +44 -0
  21. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/it.ts +43 -0
  22. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/ja.ts +43 -0
  23. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/ko.ts +43 -0
  24. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/nl.ts +43 -0
  25. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/pl.ts +43 -0
  26. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/pt.ts +43 -0
  27. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/ru.ts +43 -0
  28. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/sv.ts +43 -0
  29. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/tr.ts +43 -0
  30. package/src/tool/wildlifeCameraTrapEffortPlanner/i18n/zh.ts +43 -0
  31. package/src/tool/wildlifeCameraTrapEffortPlanner/index.ts +11 -0
  32. package/src/tool/wildlifeCameraTrapEffortPlanner/logic.test.ts +48 -0
  33. package/src/tool/wildlifeCameraTrapEffortPlanner/logic.ts +99 -0
  34. package/src/tool/wildlifeCameraTrapEffortPlanner/seo.astro +14 -0
  35. package/src/tool/wildlifeCameraTrapEffortPlanner/storage.ts +18 -0
  36. package/src/tool/wildlifeCameraTrapEffortPlanner/ui.ts +48 -0
  37. package/src/tool/wildlifeCameraTrapEffortPlanner/wildlife-camera-trap-effort-planner.css +442 -0
  38. package/src/tools.ts +3 -1
  39. package/src/types.ts +6 -5
@@ -0,0 +1,442 @@
1
+ .wctep-root {
2
+ --n-ink: #24312d;
3
+ --n-muted: #64716a;
4
+ --n-paper: #f5f0e6;
5
+ --n-surface: #fffdf8;
6
+ --n-surface-alt: #e9efe7;
7
+ --n-line: #c9d1c5;
8
+ --n-accent: #bd5c3d;
9
+ --n-accent-soft: #f2d9c9;
10
+ --n-green: #456b55;
11
+ --n-map: #dce8da;
12
+ --n-map-line: #a4b9a3;
13
+ --n-focus: #315e83;
14
+
15
+ color: var(--n-ink);
16
+ font: 1rem Georgia, 'Times New Roman', serif;
17
+ }
18
+
19
+ .theme-dark .wctep-root {
20
+ --n-ink: #edf1e9;
21
+ --n-muted: #b4c0b5;
22
+ --n-paper: #1b2421;
23
+ --n-surface: #25302b;
24
+ --n-surface-alt: #303c35;
25
+ --n-line: #526158;
26
+ --n-accent: #e48761;
27
+ --n-accent-soft: #553a30;
28
+ --n-green: #9dc39f;
29
+ --n-map: #2d4135;
30
+ --n-map-line: #5c8067;
31
+ --n-focus: #99c8ed;
32
+ }
33
+
34
+ .wctep-shell {
35
+ overflow: hidden;
36
+ border: 1px solid var(--n-line);
37
+ border-radius: 22px;
38
+ background: var(--n-paper);
39
+ box-shadow: 8px 10px 0 var(--n-accent-soft);
40
+ }
41
+
42
+ .wctep-controls,
43
+ .wctep-reading,
44
+ .wctep-review,
45
+ .wctep-notes {
46
+ padding: clamp(1.1rem, 3vw, 2rem);
47
+ }
48
+
49
+ .wctep-controls,
50
+ .wctep-reading,
51
+ .wctep-review {
52
+ border-bottom: 1px solid var(--n-line);
53
+ }
54
+
55
+ .wctep-control-topline,
56
+ .wctep-reading-head,
57
+ .wctep-map-key {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: space-between;
61
+ gap: 1rem;
62
+ }
63
+
64
+ .wctep-mark,
65
+ .wctep-section-label,
66
+ .wctep-note-label {
67
+ color: var(--n-green);
68
+ font-size: 0.72rem;
69
+ font-weight: 800;
70
+ letter-spacing: 0.12em;
71
+ text-transform: uppercase;
72
+ }
73
+
74
+ .wctep-unit-switch {
75
+ display: inline-flex;
76
+ padding: 3px;
77
+ border: 1px solid var(--n-line);
78
+ border-radius: 999px;
79
+ background: var(--n-surface);
80
+ }
81
+
82
+ .wctep-unit-switch button,
83
+ .wctep-preset-row button {
84
+ border: 0;
85
+ color: var(--n-muted);
86
+ background: transparent;
87
+ cursor: pointer;
88
+ font: inherit;
89
+ }
90
+
91
+ .wctep-unit-switch button {
92
+ padding: 0.4rem 0.7rem;
93
+ border-radius: 999px;
94
+ font-size: 0.72rem;
95
+ font-weight: 800;
96
+ }
97
+
98
+ .wctep-unit-switch button[aria-pressed='true'] {
99
+ color: var(--n-surface);
100
+ background: var(--n-green);
101
+ }
102
+
103
+ .wctep-fields {
104
+ display: grid;
105
+ grid-template-columns: repeat(4, minmax(0, 1fr));
106
+ gap: 1rem;
107
+ margin-top: 1.5rem;
108
+ }
109
+
110
+ .wctep-field-wide {
111
+ grid-column: span 2;
112
+ }
113
+
114
+ .wctep-field {
115
+ display: grid;
116
+ gap: 0.45rem;
117
+ }
118
+
119
+ .wctep-label {
120
+ min-height: 2.4em;
121
+ font-size: 0.83rem;
122
+ font-weight: 750;
123
+ }
124
+
125
+ .wctep-input-line {
126
+ display: flex;
127
+ align-items: center;
128
+ min-height: 3rem;
129
+ border-bottom: 2px solid var(--n-green);
130
+ background: var(--n-surface);
131
+ }
132
+
133
+ .wctep-input-line input {
134
+ width: 100%;
135
+ min-width: 0;
136
+ padding: 0.7rem 0.75rem;
137
+ border: 0;
138
+ outline: 0;
139
+ color: var(--n-ink);
140
+ background: transparent;
141
+ font: 1.05rem Georgia, 'Times New Roman', serif;
142
+ }
143
+
144
+ .wctep-input-line input:focus-visible,
145
+ .wctep-unit-switch button:focus-visible,
146
+ .wctep-preset-row button:focus-visible {
147
+ outline: 3px solid var(--n-focus);
148
+ outline-offset: 2px;
149
+ }
150
+
151
+ .wctep-input-line > span {
152
+ padding-right: 0.75rem;
153
+ color: var(--n-muted);
154
+ font-size: 0.76rem;
155
+ white-space: nowrap;
156
+ }
157
+
158
+ .wctep-help,
159
+ .wctep-caption,
160
+ .wctep-review-copy p,
161
+ .wctep-notes p,
162
+ .wctep-status-note {
163
+ color: var(--n-muted);
164
+ font-size: 0.78rem;
165
+ line-height: 1.5;
166
+ }
167
+
168
+ .wctep-presets {
169
+ display: grid;
170
+ gap: 0.65rem;
171
+ margin-top: 1.5rem;
172
+ }
173
+
174
+ .wctep-preset-row {
175
+ display: flex;
176
+ flex-wrap: wrap;
177
+ gap: 0.55rem;
178
+ }
179
+
180
+ .wctep-preset-row button {
181
+ padding: 0.55rem 0.85rem;
182
+ border: 1px solid var(--n-line);
183
+ border-radius: 7px;
184
+ color: var(--n-ink);
185
+ background: var(--n-surface);
186
+ font-size: 0.78rem;
187
+ }
188
+
189
+ .wctep-preset-row button:hover {
190
+ border-color: var(--n-accent);
191
+ color: var(--n-accent);
192
+ }
193
+
194
+ .wctep-reading {
195
+ display: grid;
196
+ grid-template-columns: minmax(0, 1.25fr) minmax(15rem, 0.75fr);
197
+ gap: clamp(1.2rem, 4vw, 2.5rem);
198
+ background: var(--n-surface);
199
+ }
200
+
201
+ .wctep-map-column {
202
+ min-width: 0;
203
+ }
204
+
205
+ .wctep-status {
206
+ padding: 0.45rem 0.7rem;
207
+ border-radius: 99px;
208
+ color: var(--n-surface);
209
+ background: var(--n-accent);
210
+ font-size: 0.72rem;
211
+ font-weight: 800;
212
+ text-align: right;
213
+ }
214
+
215
+ .wctep-map {
216
+ margin-top: 1rem;
217
+ border: 1px solid var(--n-map-line);
218
+ background: var(--n-map);
219
+ }
220
+
221
+ .wctep-map svg {
222
+ display: block;
223
+ width: 100%;
224
+ aspect-ratio: 1.45;
225
+ }
226
+
227
+ .wctep-contour,
228
+ .wctep-route {
229
+ fill: none;
230
+ stroke: var(--n-map-line);
231
+ stroke-width: 0.7;
232
+ }
233
+
234
+ .wctep-route {
235
+ stroke: var(--n-accent);
236
+ stroke-dasharray: 2 2;
237
+ stroke-width: 1.1;
238
+ }
239
+
240
+ .wctep-cell {
241
+ fill: var(--n-surface);
242
+ fill-opacity: 0.5;
243
+ stroke: var(--n-green);
244
+ stroke-width: 0.6;
245
+ stroke-dasharray: 1 1;
246
+ }
247
+
248
+ .wctep-station-dot {
249
+ fill: var(--n-accent);
250
+ }
251
+
252
+ .wctep-station text,
253
+ .wctep-overflow,
254
+ .wctep-north-label {
255
+ fill: var(--n-ink);
256
+ font-size: 3px;
257
+ font-weight: 800;
258
+ }
259
+
260
+ .wctep-north {
261
+ fill: none;
262
+ stroke: var(--n-ink);
263
+ stroke-width: 0.7;
264
+ }
265
+
266
+ .wctep-map-key {
267
+ justify-content: flex-start;
268
+ flex-wrap: wrap;
269
+ margin-top: 0.7rem;
270
+ color: var(--n-muted);
271
+ font-size: 0.72rem;
272
+ }
273
+
274
+ .wctep-map-key strong {
275
+ margin-left: auto;
276
+ color: var(--n-accent);
277
+ font-size: 1rem;
278
+ }
279
+
280
+ .wctep-key-station,
281
+ .wctep-key-cell {
282
+ display: inline-block;
283
+ width: 0.7rem;
284
+ height: 0.7rem;
285
+ }
286
+
287
+ .wctep-key-station {
288
+ border-radius: 50%;
289
+ background: var(--n-accent);
290
+ }
291
+
292
+ .wctep-key-cell {
293
+ border: 1px dashed var(--n-green);
294
+ }
295
+
296
+ .wctep-caption {
297
+ margin: 0.7rem 0 0;
298
+ }
299
+
300
+ .wctep-metrics {
301
+ display: grid;
302
+ grid-template-columns: repeat(2, minmax(0, 1fr));
303
+ align-content: start;
304
+ border-left: 1px solid var(--n-line);
305
+ }
306
+
307
+ .wctep-metric {
308
+ display: grid;
309
+ gap: 0.35rem;
310
+ padding: 0.85rem 1rem;
311
+ border-bottom: 1px solid var(--n-line);
312
+ }
313
+
314
+ .wctep-metric:nth-child(odd) {
315
+ border-right: 1px solid var(--n-line);
316
+ }
317
+
318
+ .wctep-metric span {
319
+ color: var(--n-muted);
320
+ font-size: 0.7rem;
321
+ line-height: 1.3;
322
+ }
323
+
324
+ .wctep-metric strong {
325
+ overflow-wrap: anywhere;
326
+ color: var(--n-ink);
327
+ font-size: clamp(1.25rem, 3vw, 1.8rem);
328
+ }
329
+
330
+ .wctep-metric-major {
331
+ grid-column: span 2;
332
+ background: var(--n-accent-soft);
333
+ }
334
+
335
+ .wctep-metric-wide {
336
+ grid-column: span 2;
337
+ }
338
+
339
+ .wctep-review {
340
+ display: grid;
341
+ grid-template-columns: minmax(14rem, 0.75fr) minmax(0, 1.25fr);
342
+ gap: 2rem;
343
+ background: var(--n-surface-alt);
344
+ }
345
+
346
+ .wctep-review-copy p {
347
+ max-width: 30rem;
348
+ margin: 0.7rem 0 0;
349
+ }
350
+
351
+ .wctep-review ol {
352
+ display: grid;
353
+ grid-template-columns: repeat(4, minmax(0, 1fr));
354
+ gap: 0.5rem;
355
+ margin: 0;
356
+ padding: 0;
357
+ list-style: none;
358
+ }
359
+
360
+ .wctep-review li {
361
+ display: grid;
362
+ align-content: space-between;
363
+ min-height: 4.5rem;
364
+ padding: 0.7rem;
365
+ border-left: 3px solid var(--n-accent);
366
+ background: var(--n-surface);
367
+ }
368
+
369
+ .wctep-review li strong {
370
+ color: var(--n-ink);
371
+ font-size: 0.8rem;
372
+ }
373
+
374
+ .wctep-review li span {
375
+ color: var(--n-muted);
376
+ font-size: 0.7rem;
377
+ line-height: 1.35;
378
+ }
379
+
380
+ .wctep-notes {
381
+ display: grid;
382
+ grid-template-columns: repeat(2, minmax(0, 1fr));
383
+ gap: 1rem 2rem;
384
+ }
385
+
386
+ .wctep-notes p {
387
+ margin: 0.5rem 0 0;
388
+ }
389
+
390
+ .wctep-status-note {
391
+ grid-column: 1 / -1;
392
+ margin: 0;
393
+ padding-top: 1rem;
394
+ border-top: 1px solid var(--n-line);
395
+ color: var(--n-ink);
396
+ }
397
+
398
+ @media (max-width: 850px) {
399
+ .wctep-fields {
400
+ grid-template-columns: repeat(2, minmax(0, 1fr));
401
+ }
402
+
403
+ .wctep-field-wide {
404
+ grid-column: span 2;
405
+ }
406
+
407
+ .wctep-reading,
408
+ .wctep-review {
409
+ grid-template-columns: 1fr;
410
+ }
411
+
412
+ .wctep-metrics {
413
+ border-top: 1px solid var(--n-line);
414
+ border-left: 0;
415
+ }
416
+ }
417
+
418
+ @media (max-width: 520px) {
419
+ .wctep-control-topline,
420
+ .wctep-reading-head {
421
+ align-items: flex-start;
422
+ flex-direction: column;
423
+ }
424
+
425
+ .wctep-fields,
426
+ .wctep-notes {
427
+ grid-template-columns: 1fr;
428
+ }
429
+
430
+ .wctep-field-wide,
431
+ .wctep-status-note {
432
+ grid-column: auto;
433
+ }
434
+
435
+ .wctep-status {
436
+ text-align: left;
437
+ }
438
+
439
+ .wctep-review ol {
440
+ grid-template-columns: repeat(2, minmax(0, 1fr));
441
+ }
442
+ }
package/src/tools.ts CHANGED
@@ -6,6 +6,7 @@ import { RAIN_HARVESTER_TOOL } from './tool/rainHarvester/index';
6
6
  import { DIGITAL_CARBON_TOOL } from './tool/digitalCarbon/index';
7
7
  import { URBAN_GARDEN_PLANNER_TOOL } from './tool/urbanGardenPlanner/index';
8
8
  import { COMPOST_BIN_VOLUME_RATIO_CALCULATOR_TOOL } from './tool/compostBinVolumeRatioCalculator/index';
9
+ import { WILDLIFE_CAMERA_TRAP_EFFORT_PLANNER_TOOL } from './tool/wildlifeCameraTrapEffortPlanner/index';
9
10
 
10
11
  export const ALL_TOOLS: ToolDefinition[] = [
11
12
  CRICKET_THERMOMETER_TOOL,
@@ -13,5 +14,6 @@ export const ALL_TOOLS: ToolDefinition[] = [
13
14
  RAIN_HARVESTER_TOOL,
14
15
  DIGITAL_CARBON_TOOL,
15
16
  URBAN_GARDEN_PLANNER_TOOL,
16
- COMPOST_BIN_VOLUME_RATIO_CALCULATOR_TOOL
17
+ COMPOST_BIN_VOLUME_RATIO_CALCULATOR_TOOL,
18
+ WILDLIFE_CAMERA_TRAP_EFFORT_PLANNER_TOOL
17
19
  ];
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { SEOSection } from '@jjlmoya/utils-shared';
2
2
  import type { WithContext, Thing } from 'schema-dts';
3
+ import type { AstroComponentFactory } from 'astro/runtime/server/index.js';
3
4
 
4
5
  export type { SEOSection };
5
6
 
@@ -46,7 +47,7 @@ export type LocaleLoader<T> = () => Promise<T>;
46
47
 
47
48
  export type LocaleMap<T> = Partial<Record<KnownLocale, LocaleLoader<T>>>;
48
49
 
49
- export interface NatureToolEntry<TUI = Record<string, string>> {
50
+ export interface NatureToolEntry<TUI = unknown> {
50
51
  id: string;
51
52
  icons: {
52
53
  bg: string;
@@ -57,14 +58,14 @@ export interface NatureToolEntry<TUI = Record<string, string>> {
57
58
 
58
59
  export interface NatureCategoryEntry {
59
60
  icon: string;
60
- tools: NatureToolEntry<Record<string, string>>[];
61
+ tools: NatureToolEntry[];
61
62
  i18n: LocaleMap<CategoryLocaleContent>;
62
63
  }
63
64
 
64
65
  export interface ToolDefinition {
65
66
  entry: NatureToolEntry;
66
- Component: unknown;
67
- SEOComponent: unknown;
68
- BibliographyComponent: unknown;
67
+ Component: () => Promise<{ default: AstroComponentFactory }>;
68
+ SEOComponent: () => Promise<{ default: AstroComponentFactory }>;
69
+ BibliographyComponent: () => Promise<{ default: AstroComponentFactory }>;
69
70
  }
70
71