@jjlmoya/utils-home 1.31.0 → 1.32.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 (31) hide show
  1. package/package.json +2 -1
  2. package/src/entries.ts +4 -1
  3. package/src/tests/locale_completeness.test.ts +2 -2
  4. package/src/tests/tool_validation.test.ts +2 -2
  5. package/src/tool/lightingCalculator/bibliography.astro +36 -0
  6. package/src/tool/lightingCalculator/bibliography.ts +10 -0
  7. package/src/tool/lightingCalculator/component.astro +308 -0
  8. package/src/tool/lightingCalculator/draw.ts +247 -0
  9. package/src/tool/lightingCalculator/entry.ts +29 -0
  10. package/src/tool/lightingCalculator/how-many-lights-per-room.css +493 -0
  11. package/src/tool/lightingCalculator/i18n/de.ts +213 -0
  12. package/src/tool/lightingCalculator/i18n/en.ts +213 -0
  13. package/src/tool/lightingCalculator/i18n/es.ts +213 -0
  14. package/src/tool/lightingCalculator/i18n/fr.ts +213 -0
  15. package/src/tool/lightingCalculator/i18n/id.ts +213 -0
  16. package/src/tool/lightingCalculator/i18n/it.ts +213 -0
  17. package/src/tool/lightingCalculator/i18n/ja.ts +213 -0
  18. package/src/tool/lightingCalculator/i18n/ko.ts +213 -0
  19. package/src/tool/lightingCalculator/i18n/nl.ts +213 -0
  20. package/src/tool/lightingCalculator/i18n/pl.ts +213 -0
  21. package/src/tool/lightingCalculator/i18n/pt.ts +213 -0
  22. package/src/tool/lightingCalculator/i18n/ru.ts +213 -0
  23. package/src/tool/lightingCalculator/i18n/sv.ts +213 -0
  24. package/src/tool/lightingCalculator/i18n/tr.ts +213 -0
  25. package/src/tool/lightingCalculator/i18n/zh.ts +213 -0
  26. package/src/tool/lightingCalculator/index.ts +9 -0
  27. package/src/tool/lightingCalculator/logic.ts +119 -0
  28. package/src/tool/lightingCalculator/seo.astro +15 -0
  29. package/src/tool/lightingCalculator/state.ts +113 -0
  30. package/src/tool/lightingCalculator/ui.ts +48 -0
  31. package/src/tools.ts +2 -0
@@ -0,0 +1,29 @@
1
+ import type { HomeToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { LightingCalculatorUI } from './ui';
3
+
4
+ export type LightingCalculatorLocaleContent = ToolLocaleContent<LightingCalculatorUI>;
5
+
6
+ export const lightingCalculator: HomeToolEntry<LightingCalculatorUI> = {
7
+ id: 'how-many-lights-per-room',
8
+ icons: {
9
+ bg: 'mdi:lightbulb',
10
+ fg: 'mdi:flash',
11
+ },
12
+ i18n: {
13
+ de: async () => (await import('./i18n/de')).content,
14
+ en: async () => (await import('./i18n/en')).content,
15
+ es: async () => (await import('./i18n/es')).content,
16
+ fr: async () => (await import('./i18n/fr')).content,
17
+ id: async () => (await import('./i18n/id')).content,
18
+ it: async () => (await import('./i18n/it')).content,
19
+ ja: async () => (await import('./i18n/ja')).content,
20
+ ko: async () => (await import('./i18n/ko')).content,
21
+ nl: async () => (await import('./i18n/nl')).content,
22
+ pl: async () => (await import('./i18n/pl')).content,
23
+ pt: async () => (await import('./i18n/pt')).content,
24
+ ru: async () => (await import('./i18n/ru')).content,
25
+ sv: async () => (await import('./i18n/sv')).content,
26
+ tr: async () => (await import('./i18n/tr')).content,
27
+ zh: async () => (await import('./i18n/zh')).content,
28
+ },
29
+ };
@@ -0,0 +1,493 @@
1
+ .lc-wrap {
2
+ width: 100%;
3
+ padding: 2rem 1rem;
4
+ display: flex;
5
+ justify-content: center;
6
+ }
7
+
8
+ .lc-card {
9
+ width: 100%;
10
+ max-width: 720px;
11
+ background: var(--bg-surface);
12
+ border: 1px solid var(--border-color);
13
+ border-radius: 20px;
14
+ padding: 1.5rem;
15
+ box-shadow: 0 20px 60px rgba(0,0,0,0.15);
16
+ }
17
+
18
+ .lc-head {
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: space-between;
22
+ margin-bottom: 1rem;
23
+ }
24
+
25
+ .lc-title {
26
+ font-size: 1rem;
27
+ font-weight: 700;
28
+ color: var(--text-main);
29
+ letter-spacing: 0.02em;
30
+ }
31
+
32
+ .lc-unit-toggle {
33
+ display: flex;
34
+ gap: 0.25rem;
35
+ background: var(--bg-page);
36
+ border-radius: 8px;
37
+ padding: 0.25rem;
38
+ }
39
+
40
+ .lc-unit-btn {
41
+ border: none;
42
+ background: transparent;
43
+ color: var(--text-muted);
44
+ font-size: 0.75rem;
45
+ font-weight: 600;
46
+ padding: 0.35rem 0.75rem;
47
+ border-radius: 6px;
48
+ cursor: pointer;
49
+ transition: all 0.2s;
50
+ }
51
+
52
+ .lc-unit-btn:hover {
53
+ color: var(--text-main);
54
+ }
55
+
56
+ .lc-unit-active {
57
+ background: var(--bg-surface);
58
+ color: var(--text-main);
59
+ border: 1px solid var(--border-color);
60
+ }
61
+
62
+ .lc-row {
63
+ display: flex;
64
+ gap: 0.75rem;
65
+ margin-bottom: 0.75rem;
66
+ flex-wrap: wrap;
67
+ }
68
+
69
+ .lc-chips {
70
+ display: flex;
71
+ gap: 0.5rem;
72
+ flex-wrap: wrap;
73
+ width: 100%;
74
+ }
75
+
76
+ .lc-chip {
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 0.4rem;
80
+ padding: 0.5rem 0.75rem;
81
+ border-radius: 10px;
82
+ border: none;
83
+ background: transparent;
84
+ color: var(--text-muted);
85
+ font-size: 0.8rem;
86
+ font-weight: 500;
87
+ cursor: pointer;
88
+ transition: all 0.2s;
89
+ line-height: 1;
90
+ }
91
+
92
+ .lc-chip:hover {
93
+ color: var(--text-main);
94
+ }
95
+
96
+ .lc-chip-active {
97
+ background: var(--text-muted);
98
+ color: var(--bg-surface);
99
+ }
100
+
101
+ .lc-chip-icon {
102
+ width: 14px;
103
+ height: 14px;
104
+ fill: currentcolor;
105
+ opacity: 0.7;
106
+ }
107
+
108
+ .lc-chip-active .lc-chip-icon {
109
+ opacity: 1;
110
+ }
111
+
112
+ .lc-input-group {
113
+ flex: 1;
114
+ min-width: 100px;
115
+ }
116
+
117
+ .lc-grow {
118
+ flex: 2;
119
+ }
120
+
121
+ .lc-input-label {
122
+ display: block;
123
+ font-size: 0.65rem;
124
+ font-weight: 600;
125
+ text-transform: uppercase;
126
+ letter-spacing: 0.08em;
127
+ color: var(--text-muted);
128
+ margin-bottom: 0.35rem;
129
+ }
130
+
131
+ .lc-input-wrap {
132
+ display: flex;
133
+ align-items: center;
134
+ gap: 0.5rem;
135
+ background: var(--bg-page);
136
+ border: 1px solid var(--border-color);
137
+ border-radius: 10px;
138
+ padding: 0.5rem 0.75rem;
139
+ transition: border-color 0.2s;
140
+ }
141
+
142
+ .lc-input-wrap:focus-within {
143
+ border-color: var(--text-main);
144
+ }
145
+
146
+ .lc-input {
147
+ background: transparent;
148
+ border: none;
149
+ color: var(--text-main);
150
+ font-size: 0.9rem;
151
+ font-weight: 600;
152
+ width: 60px;
153
+ outline: none;
154
+ }
155
+
156
+ .lc-input-unit {
157
+ font-size: 0.7rem;
158
+ color: var(--text-muted);
159
+ font-weight: 500;
160
+ margin-left: auto;
161
+ transition: opacity 0.2s;
162
+ }
163
+
164
+ .lc-bulb-bar {
165
+ display: flex;
166
+ gap: 0.5rem;
167
+ }
168
+
169
+ .lc-bulb {
170
+ display: flex;
171
+ align-items: center;
172
+ gap: 0.4rem;
173
+ padding: 0.5rem 0.6rem;
174
+ border-radius: 8px;
175
+ border: none;
176
+ background: transparent;
177
+ color: var(--text-muted);
178
+ font-size: 0.75rem;
179
+ font-weight: 500;
180
+ cursor: pointer;
181
+ transition: all 0.2s;
182
+ line-height: 1;
183
+ }
184
+
185
+ .lc-bulb:hover {
186
+ color: var(--text-main);
187
+ }
188
+
189
+ .lc-bulb-active {
190
+ background: var(--text-muted);
191
+ color: var(--bg-surface);
192
+ }
193
+
194
+ .lc-bulb-dot {
195
+ width: 8px;
196
+ height: 8px;
197
+ border-radius: 50%;
198
+ transition: transform 0.2s;
199
+ }
200
+
201
+ .lc-bulb:hover .lc-bulb-dot {
202
+ transform: scale(1.3);
203
+ }
204
+
205
+ .lc-ambient-btn {
206
+ display: flex;
207
+ align-items: center;
208
+ gap: 0.4rem;
209
+ padding: 0.5rem 0.75rem;
210
+ border-radius: 8px;
211
+ border: none;
212
+ background: transparent;
213
+ color: var(--text-muted);
214
+ font-size: 0.75rem;
215
+ font-weight: 500;
216
+ cursor: pointer;
217
+ transition: all 0.2s;
218
+ line-height: 1;
219
+ }
220
+
221
+ .lc-ambient-btn:hover {
222
+ color: var(--text-main);
223
+ }
224
+
225
+ .lc-ambient-active {
226
+ background: var(--text-muted);
227
+ color: var(--bg-surface);
228
+ }
229
+
230
+ .lc-plan-wrap {
231
+ width: 100%;
232
+ aspect-ratio: 3 / 2;
233
+ border-radius: 16px;
234
+ overflow: hidden;
235
+ background: var(--bg-page);
236
+ border: 2px solid var(--border-color);
237
+ margin: 1rem 0;
238
+ transition: border-color 0.3s;
239
+ }
240
+
241
+ .lc-plan-wrap.optimal {
242
+ border-color: rgba(34, 197, 94, 0.5);
243
+ }
244
+
245
+ .lc-plan-wrap.insufficient {
246
+ border-color: rgba(245, 158, 11, 0.5);
247
+ }
248
+
249
+ .lc-plan-wrap.excess {
250
+ border-color: rgba(239, 68, 68, 0.5);
251
+ }
252
+
253
+ .lc-plan {
254
+ width: 100%;
255
+ height: 100%;
256
+ display: block;
257
+ }
258
+
259
+ .lc-dial-row {
260
+ display: grid;
261
+ grid-template-columns: 1fr;
262
+ gap: 1rem;
263
+ margin-bottom: 1rem;
264
+ justify-items: center;
265
+ }
266
+
267
+ .lc-dial-wrap {
268
+ display: flex;
269
+ flex-direction: column;
270
+ align-items: center;
271
+ gap: 0.5rem;
272
+ }
273
+
274
+ .lc-dial {
275
+ width: 100%;
276
+ max-width: 120px;
277
+ height: auto;
278
+ }
279
+
280
+ .lc-dial-label {
281
+ font-size: 0.65rem;
282
+ font-weight: 600;
283
+ text-transform: uppercase;
284
+ letter-spacing: 0.08em;
285
+ color: var(--text-muted);
286
+ }
287
+
288
+ .lc-bulb-icons {
289
+ display: flex;
290
+ flex-wrap: wrap;
291
+ gap: 0.25rem;
292
+ justify-content: center;
293
+ max-width: 140px;
294
+ min-height: 50px;
295
+ align-items: center;
296
+ }
297
+
298
+ .lc-status {
299
+ display: flex;
300
+ align-items: flex-start;
301
+ gap: 0.6rem;
302
+ padding: 0.75rem 1rem;
303
+ border-radius: 12px;
304
+ background: var(--bg-page);
305
+ border: 1px solid var(--border-color);
306
+ transition: all 0.3s;
307
+ }
308
+
309
+ .lc-status-dot {
310
+ width: 8px;
311
+ height: 8px;
312
+ border-radius: 50%;
313
+ margin-top: 0.35rem;
314
+ flex-shrink: 0;
315
+ transition: all 0.3s;
316
+ }
317
+
318
+ .lc-status-body {
319
+ display: flex;
320
+ flex-direction: column;
321
+ gap: 0.15rem;
322
+ min-width: 0;
323
+ }
324
+
325
+ .lc-status-text {
326
+ font-size: 0.85rem;
327
+ font-weight: 700;
328
+ color: var(--text-main);
329
+ line-height: 1.2;
330
+ }
331
+
332
+ .lc-status-desc {
333
+ font-size: 0.75rem;
334
+ font-weight: 500;
335
+ color: var(--text-muted);
336
+ line-height: 1.4;
337
+ }
338
+
339
+ .lc-status.optimal {
340
+ border-color: rgba(34, 197, 94, 0.3);
341
+ background: rgba(34, 197, 94, 0.06);
342
+ }
343
+
344
+ .lc-status.optimal .lc-status-dot {
345
+ background: #22c55e;
346
+ box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
347
+ }
348
+
349
+ .lc-status.optimal .lc-status-text {
350
+ color: #22c55e;
351
+ }
352
+
353
+ .lc-status.insufficient {
354
+ border-color: rgba(245, 158, 11, 0.3);
355
+ background: rgba(245, 158, 11, 0.06);
356
+ }
357
+
358
+ .lc-status.insufficient .lc-status-dot {
359
+ background: #f59e0b;
360
+ box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
361
+ }
362
+
363
+ .lc-status.insufficient .lc-status-text {
364
+ color: #f59e0b;
365
+ }
366
+
367
+ .lc-status.excess {
368
+ border-color: rgba(239, 68, 68, 0.3);
369
+ background: rgba(239, 68, 68, 0.06);
370
+ }
371
+
372
+ .lc-status.excess .lc-status-dot {
373
+ background: #ef4444;
374
+ box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
375
+ }
376
+
377
+ .lc-status.excess .lc-status-text {
378
+ color: #ef4444;
379
+ }
380
+
381
+ .lc-victory {
382
+ margin-top: 1rem;
383
+ padding: 1rem;
384
+ border-radius: 12px;
385
+ background: var(--bg-page);
386
+ border: 1px solid var(--border-color);
387
+ }
388
+
389
+ .lc-victory-title {
390
+ font-size: 0.85rem;
391
+ font-weight: 700;
392
+ color: var(--text-main);
393
+ margin-bottom: 0.75rem;
394
+ }
395
+
396
+ .lc-victory-row {
397
+ display: flex;
398
+ justify-content: space-between;
399
+ align-items: center;
400
+ margin-bottom: 0.6rem;
401
+ font-size: 0.85rem;
402
+ line-height: 1.4;
403
+ }
404
+
405
+ .lc-victory-label {
406
+ color: var(--text-muted);
407
+ font-weight: 500;
408
+ letter-spacing: 0.02em;
409
+ }
410
+
411
+ .lc-victory-value {
412
+ color: var(--text-main);
413
+ font-weight: 700;
414
+ font-size: 0.95rem;
415
+ letter-spacing: -0.01em;
416
+ }
417
+
418
+ .lc-export-btn {
419
+ width: 100%;
420
+ margin-top: 0.75rem;
421
+ padding: 0.6rem;
422
+ border-radius: 10px;
423
+ border: 1px solid var(--border-color);
424
+ background: var(--bg-surface);
425
+ color: var(--text-main);
426
+ font-size: 0.8rem;
427
+ font-weight: 600;
428
+ cursor: pointer;
429
+ transition: all 0.2s;
430
+ }
431
+
432
+ .lc-export-btn:hover {
433
+ border-color: var(--text-main);
434
+ background: var(--bg-page);
435
+ }
436
+
437
+ .lc-manual-toggle {
438
+ margin-top: 1rem;
439
+ padding: 0.6rem 1rem;
440
+ text-align: center;
441
+ font-size: 0.75rem;
442
+ font-weight: 600;
443
+ color: var(--text-muted);
444
+ cursor: pointer;
445
+ transition: all 0.2s;
446
+ border: 1px solid var(--border-color);
447
+ border-radius: 10px;
448
+ background: var(--bg-page);
449
+ display: flex;
450
+ align-items: center;
451
+ justify-content: center;
452
+ gap: 0.4rem;
453
+ }
454
+
455
+ .lc-manual-toggle:hover {
456
+ color: var(--text-main);
457
+ border-color: var(--text-muted);
458
+ }
459
+
460
+ .lc-manual-toggle svg {
461
+ width: 14px;
462
+ height: 14px;
463
+ fill: currentcolor;
464
+ }
465
+
466
+ .lc-manual {
467
+ max-height: 0;
468
+ overflow: hidden;
469
+ transition: all 0.3s ease;
470
+ opacity: 0;
471
+ transform: translateY(-4px);
472
+ }
473
+
474
+ .lc-manual-open {
475
+ max-height: 200px;
476
+ padding-top: 0.75rem;
477
+ opacity: 1;
478
+ transform: translateY(0);
479
+ }
480
+
481
+ @media (max-width: 600px) {
482
+ .lc-row {
483
+ flex-direction: column;
484
+ }
485
+
486
+ .lc-dial-row {
487
+ grid-template-columns: 1fr;
488
+ }
489
+
490
+ .lc-dial {
491
+ max-width: 160px;
492
+ }
493
+ }