@jjlmoya/utils-drones 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 (34) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/en.ts +1 -1
  3. package/src/category/i18n/fr.ts +1 -1
  4. package/src/components/PreviewNavSidebar.astro +116 -116
  5. package/src/components/PreviewToolbar.astro +143 -143
  6. package/src/data.ts +1 -1
  7. package/src/env.d.ts +1 -1
  8. package/src/layouts/PreviewLayout.astro +118 -118
  9. package/src/pages/[locale].astro +251 -251
  10. package/src/pages/index.astro +4 -4
  11. package/src/tests/faq_count.test.ts +1 -1
  12. package/src/tests/mocks/astro_mock.js +1 -1
  13. package/src/tests/no_h1_in_components.test.ts +1 -1
  14. package/src/tests/tool_validation.test.ts +15 -15
  15. package/src/tool/antenna-length-calculator/bibliography.astro +6 -6
  16. package/src/tool/antenna-length-calculator/component.astro +329 -329
  17. package/src/tool/antenna-length-calculator/seo.astro +15 -15
  18. package/src/tool/drone-battery-c-rating-calculator/i18n/de.ts +192 -192
  19. package/src/tool/drone-battery-c-rating-calculator/i18n/en.ts +192 -192
  20. package/src/tool/drone-battery-c-rating-calculator/i18n/es.ts +192 -192
  21. package/src/tool/drone-battery-c-rating-calculator/i18n/id.ts +192 -192
  22. package/src/tool/drone-battery-c-rating-calculator/i18n/ja.ts +192 -192
  23. package/src/tool/drone-battery-c-rating-calculator/i18n/ko.ts +192 -192
  24. package/src/tool/drone-battery-c-rating-calculator/i18n/nl.ts +192 -192
  25. package/src/tool/drone-battery-c-rating-calculator/i18n/pl.ts +192 -192
  26. package/src/tool/drone-battery-c-rating-calculator/i18n/ru.ts +192 -192
  27. package/src/tool/drone-battery-c-rating-calculator/i18n/sv.ts +192 -192
  28. package/src/tool/drone-battery-c-rating-calculator/i18n/tr.ts +192 -192
  29. package/src/tool/drone-battery-c-rating-calculator/i18n/zh.ts +192 -192
  30. package/src/tool/drone-flight-time/component.astro +187 -187
  31. package/src/tool/drone-flight-time/seo.astro +15 -15
  32. package/src/tool/drone-motor-propeller-calculator/seo.astro +7 -4
  33. package/src/tool/gps-coordinates-converter/component.astro +275 -275
  34. package/src/tool/gps-coordinates-converter/seo.astro +15 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-drones",
3
- "version": "1.31.0",
3
+ "version": "1.32.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -104,4 +104,4 @@ export const content: CategoryLocaleContent = {
104
104
  },
105
105
  ],
106
106
  };
107
-
107
+
@@ -104,4 +104,4 @@ export const content: CategoryLocaleContent = {
104
104
  },
105
105
  ],
106
106
  };
107
-
107
+
@@ -1,116 +1,116 @@
1
- ---
2
- interface NavItem {
3
- id: string;
4
- title: string;
5
- href: string;
6
- isActive?: boolean;
7
- }
8
-
9
- interface Props {
10
- categoryTitle: string;
11
- tools?: NavItem[];
12
- }
13
-
14
- const { categoryTitle, tools = [] } = Astro.props;
15
- ---
16
-
17
- <nav class="preview-nav-sidebar">
18
- <div class="sidebar-header">
19
- <h3>{categoryTitle}</h3>
20
- </div>
21
-
22
- <ul class="tools-list">
23
- {tools.map((tool) => (
24
- <li>
25
- <a
26
- href={tool.href}
27
- class:list={['tool-link', { active: tool.isActive }]}
28
- >
29
- <span class="tool-title">{tool.title}</span>
30
- </a>
31
- </li>
32
- ))}
33
- </ul>
34
- </nav>
35
-
36
- <style>
37
- .preview-nav-sidebar {
38
- display: flex;
39
- flex-direction: column;
40
- height: 100%;
41
- background: var(--bg-surface, #0f172a);
42
- }
43
-
44
- .sidebar-header {
45
- padding: 2rem 1.5rem 1.5rem;
46
- border-bottom: 1px solid var(--border-color, #1e293b);
47
- }
48
-
49
- .sidebar-header h3 {
50
- margin: 0;
51
- font-size: 0.75rem;
52
- font-weight: 900;
53
- text-transform: uppercase;
54
- letter-spacing: 0.1em;
55
- color: var(--text-muted, #94a3b8);
56
- }
57
-
58
- .tools-list {
59
- list-style: none;
60
- margin: 0;
61
- padding: 0.5rem 0;
62
- display: flex;
63
- flex-direction: column;
64
- gap: 0.25rem;
65
- }
66
-
67
- .tools-list li {
68
- margin: 0;
69
- }
70
-
71
- .tool-link {
72
- display: flex;
73
- align-items: center;
74
- padding: 0.75rem 1.5rem;
75
- color: var(--text-muted, #94a3b8);
76
- text-decoration: none;
77
- font-size: 0.9375rem;
78
- font-weight: 500;
79
- transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
80
- border-left: 3px solid transparent;
81
- position: relative;
82
- }
83
-
84
- .tool-link:hover {
85
- color: var(--text-base, #f1f5f9);
86
- background: rgba(255, 255, 255, 0.08);
87
- padding-left: 1.75rem;
88
- }
89
-
90
- .tool-link.active {
91
- color: var(--accent, #f43f5e);
92
- background: rgba(244, 63, 94, 0.15);
93
- border-left-color: var(--accent, #f43f5e);
94
- font-weight: 600;
95
- }
96
-
97
- .tool-link.active::before {
98
- content: '';
99
- position: absolute;
100
- left: 0;
101
- top: 50%;
102
- transform: translateY(-50%);
103
- width: 3px;
104
- height: 24px;
105
- background: var(--accent, #f43f5e);
106
- border-radius: 0 2px 2px 0;
107
- }
108
-
109
- .tool-title {
110
- display: block;
111
- overflow: hidden;
112
- text-overflow: ellipsis;
113
- white-space: nowrap;
114
- }
115
- </style>
116
-
1
+ ---
2
+ interface NavItem {
3
+ id: string;
4
+ title: string;
5
+ href: string;
6
+ isActive?: boolean;
7
+ }
8
+
9
+ interface Props {
10
+ categoryTitle: string;
11
+ tools?: NavItem[];
12
+ }
13
+
14
+ const { categoryTitle, tools = [] } = Astro.props;
15
+ ---
16
+
17
+ <nav class="preview-nav-sidebar">
18
+ <div class="sidebar-header">
19
+ <h3>{categoryTitle}</h3>
20
+ </div>
21
+
22
+ <ul class="tools-list">
23
+ {tools.map((tool) => (
24
+ <li>
25
+ <a
26
+ href={tool.href}
27
+ class:list={['tool-link', { active: tool.isActive }]}
28
+ >
29
+ <span class="tool-title">{tool.title}</span>
30
+ </a>
31
+ </li>
32
+ ))}
33
+ </ul>
34
+ </nav>
35
+
36
+ <style>
37
+ .preview-nav-sidebar {
38
+ display: flex;
39
+ flex-direction: column;
40
+ height: 100%;
41
+ background: var(--bg-surface, #0f172a);
42
+ }
43
+
44
+ .sidebar-header {
45
+ padding: 2rem 1.5rem 1.5rem;
46
+ border-bottom: 1px solid var(--border-color, #1e293b);
47
+ }
48
+
49
+ .sidebar-header h3 {
50
+ margin: 0;
51
+ font-size: 0.75rem;
52
+ font-weight: 900;
53
+ text-transform: uppercase;
54
+ letter-spacing: 0.1em;
55
+ color: var(--text-muted, #94a3b8);
56
+ }
57
+
58
+ .tools-list {
59
+ list-style: none;
60
+ margin: 0;
61
+ padding: 0.5rem 0;
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 0.25rem;
65
+ }
66
+
67
+ .tools-list li {
68
+ margin: 0;
69
+ }
70
+
71
+ .tool-link {
72
+ display: flex;
73
+ align-items: center;
74
+ padding: 0.75rem 1.5rem;
75
+ color: var(--text-muted, #94a3b8);
76
+ text-decoration: none;
77
+ font-size: 0.9375rem;
78
+ font-weight: 500;
79
+ transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
80
+ border-left: 3px solid transparent;
81
+ position: relative;
82
+ }
83
+
84
+ .tool-link:hover {
85
+ color: var(--text-base, #f1f5f9);
86
+ background: rgba(255, 255, 255, 0.08);
87
+ padding-left: 1.75rem;
88
+ }
89
+
90
+ .tool-link.active {
91
+ color: var(--accent, #f43f5e);
92
+ background: rgba(244, 63, 94, 0.15);
93
+ border-left-color: var(--accent, #f43f5e);
94
+ font-weight: 600;
95
+ }
96
+
97
+ .tool-link.active::before {
98
+ content: '';
99
+ position: absolute;
100
+ left: 0;
101
+ top: 50%;
102
+ transform: translateY(-50%);
103
+ width: 3px;
104
+ height: 24px;
105
+ background: var(--accent, #f43f5e);
106
+ border-radius: 0 2px 2px 0;
107
+ }
108
+
109
+ .tool-title {
110
+ display: block;
111
+ overflow: hidden;
112
+ text-overflow: ellipsis;
113
+ white-space: nowrap;
114
+ }
115
+ </style>
116
+
@@ -1,143 +1,143 @@
1
- ---
2
- import type { KnownLocale } from '../types';
3
-
4
- interface Props {
5
- currentLocale: KnownLocale;
6
- localeUrls?: Partial<Record<KnownLocale, string>>;
7
- }
8
-
9
- const { currentLocale, localeUrls = {} } = Astro.props;
10
-
11
- const otherLocales = Object.entries(localeUrls).filter(([l]) => l !== currentLocale) as [
12
- KnownLocale,
13
- string,
14
- ][];
15
- ---
16
-
17
- <nav class="preview-toolbar">
18
- {
19
- otherLocales.length > 0 && (
20
- <div class="locale-group">
21
- <span class="locale-current">{currentLocale.toUpperCase()}</span>
22
- {otherLocales.map(([locale, url]) => (
23
- <a href={url} class="btn-locale">
24
- {locale.toUpperCase()}
25
- </a>
26
- ))}
27
- </div>
28
- )
29
- }
30
-
31
- <button id="theme-toggle" class="btn-theme" aria-label="Toggle theme" title="Toggle theme">
32
- <span class="theme-icon"></span>
33
- </button>
34
- </nav>
35
-
36
- <script>
37
- function initToolbar() {
38
- const btn = document.getElementById('theme-toggle');
39
- if (!btn) return;
40
-
41
- const applyTheme = (theme: string) => {
42
- document.documentElement.classList.remove('theme-light', 'theme-dark');
43
- document.documentElement.classList.add(theme);
44
- localStorage.setItem('theme', theme);
45
- };
46
-
47
- btn.addEventListener('click', () => {
48
- const next = document.documentElement.classList.contains('theme-dark')
49
- ? 'theme-light'
50
- : 'theme-dark';
51
- applyTheme(next);
52
- });
53
- }
54
-
55
- initToolbar();
56
- document.addEventListener('astro:after-swap', initToolbar);
57
- </script>
58
-
59
- <style>
60
- .preview-toolbar {
61
- position: fixed;
62
- top: 1rem;
63
- right: 1.5rem;
64
- display: flex;
65
- align-items: center;
66
- gap: 0.5rem;
67
- z-index: 1000;
68
- background: var(--bg-surface);
69
- padding: 0.4rem;
70
- border-radius: 1rem;
71
- border: 1px solid var(--border-color);
72
- backdrop-filter: blur(12px);
73
- }
74
-
75
- .locale-group {
76
- display: flex;
77
- align-items: center;
78
- gap: 0.25rem;
79
- padding-right: 0.5rem;
80
- border-right: 1px solid var(--border-color);
81
- }
82
-
83
- .locale-current {
84
- font-size: 0.7rem;
85
- font-weight: 900;
86
- color: var(--text-muted);
87
- padding: 0.4rem 0.6rem;
88
- letter-spacing: 0.08em;
89
- }
90
-
91
- .btn-locale {
92
- background: transparent;
93
- border: 1px solid var(--border-color);
94
- color: var(--text-main);
95
- cursor: pointer;
96
- padding: 0.4rem 0.7rem;
97
- border-radius: 0.5rem;
98
- font-weight: 700;
99
- font-size: 0.7rem;
100
- text-decoration: none;
101
- letter-spacing: 0.08em;
102
- transition: all 0.2s ease;
103
- }
104
-
105
- .btn-locale:hover {
106
- border-color: var(--accent);
107
- color: var(--accent);
108
- }
109
-
110
- .btn-theme {
111
- background: transparent;
112
- border: none;
113
- cursor: pointer;
114
- padding: 0.5rem;
115
- border-radius: 0.6rem;
116
- display: flex;
117
- align-items: center;
118
- justify-content: center;
119
- transition: background 0.2s ease;
120
- }
121
-
122
- .btn-theme:hover {
123
- background: rgba(255, 255, 255, 0.08);
124
- }
125
-
126
- .theme-icon {
127
- display: block;
128
- width: 16px;
129
- height: 16px;
130
- border-radius: 50%;
131
- }
132
-
133
- :global(.theme-light) .theme-icon {
134
- background: #f59e0b;
135
- box-shadow: 0 0 8px #fbbf24;
136
- }
137
-
138
- :global(.theme-dark) .theme-icon {
139
- background: #94a3b8;
140
- box-shadow: inset -4px -2px 0 #1e293b;
141
- }
142
- </style>
143
-
1
+ ---
2
+ import type { KnownLocale } from '../types';
3
+
4
+ interface Props {
5
+ currentLocale: KnownLocale;
6
+ localeUrls?: Partial<Record<KnownLocale, string>>;
7
+ }
8
+
9
+ const { currentLocale, localeUrls = {} } = Astro.props;
10
+
11
+ const otherLocales = Object.entries(localeUrls).filter(([l]) => l !== currentLocale) as [
12
+ KnownLocale,
13
+ string,
14
+ ][];
15
+ ---
16
+
17
+ <nav class="preview-toolbar">
18
+ {
19
+ otherLocales.length > 0 && (
20
+ <div class="locale-group">
21
+ <span class="locale-current">{currentLocale.toUpperCase()}</span>
22
+ {otherLocales.map(([locale, url]) => (
23
+ <a href={url} class="btn-locale">
24
+ {locale.toUpperCase()}
25
+ </a>
26
+ ))}
27
+ </div>
28
+ )
29
+ }
30
+
31
+ <button id="theme-toggle" class="btn-theme" aria-label="Toggle theme" title="Toggle theme">
32
+ <span class="theme-icon"></span>
33
+ </button>
34
+ </nav>
35
+
36
+ <script>
37
+ function initToolbar() {
38
+ const btn = document.getElementById('theme-toggle');
39
+ if (!btn) return;
40
+
41
+ const applyTheme = (theme: string) => {
42
+ document.documentElement.classList.remove('theme-light', 'theme-dark');
43
+ document.documentElement.classList.add(theme);
44
+ localStorage.setItem('theme', theme);
45
+ };
46
+
47
+ btn.addEventListener('click', () => {
48
+ const next = document.documentElement.classList.contains('theme-dark')
49
+ ? 'theme-light'
50
+ : 'theme-dark';
51
+ applyTheme(next);
52
+ });
53
+ }
54
+
55
+ initToolbar();
56
+ document.addEventListener('astro:after-swap', initToolbar);
57
+ </script>
58
+
59
+ <style>
60
+ .preview-toolbar {
61
+ position: fixed;
62
+ top: 1rem;
63
+ right: 1.5rem;
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 0.5rem;
67
+ z-index: 1000;
68
+ background: var(--bg-surface);
69
+ padding: 0.4rem;
70
+ border-radius: 1rem;
71
+ border: 1px solid var(--border-color);
72
+ backdrop-filter: blur(12px);
73
+ }
74
+
75
+ .locale-group {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 0.25rem;
79
+ padding-right: 0.5rem;
80
+ border-right: 1px solid var(--border-color);
81
+ }
82
+
83
+ .locale-current {
84
+ font-size: 0.7rem;
85
+ font-weight: 900;
86
+ color: var(--text-muted);
87
+ padding: 0.4rem 0.6rem;
88
+ letter-spacing: 0.08em;
89
+ }
90
+
91
+ .btn-locale {
92
+ background: transparent;
93
+ border: 1px solid var(--border-color);
94
+ color: var(--text-main);
95
+ cursor: pointer;
96
+ padding: 0.4rem 0.7rem;
97
+ border-radius: 0.5rem;
98
+ font-weight: 700;
99
+ font-size: 0.7rem;
100
+ text-decoration: none;
101
+ letter-spacing: 0.08em;
102
+ transition: all 0.2s ease;
103
+ }
104
+
105
+ .btn-locale:hover {
106
+ border-color: var(--accent);
107
+ color: var(--accent);
108
+ }
109
+
110
+ .btn-theme {
111
+ background: transparent;
112
+ border: none;
113
+ cursor: pointer;
114
+ padding: 0.5rem;
115
+ border-radius: 0.6rem;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ transition: background 0.2s ease;
120
+ }
121
+
122
+ .btn-theme:hover {
123
+ background: rgba(255, 255, 255, 0.08);
124
+ }
125
+
126
+ .theme-icon {
127
+ display: block;
128
+ width: 16px;
129
+ height: 16px;
130
+ border-radius: 50%;
131
+ }
132
+
133
+ :global(.theme-light) .theme-icon {
134
+ background: #f59e0b;
135
+ box-shadow: 0 0 8px #fbbf24;
136
+ }
137
+
138
+ :global(.theme-dark) .theme-icon {
139
+ background: #94a3b8;
140
+ box-shadow: inset -4px -2px 0 #1e293b;
141
+ }
142
+ </style>
143
+
package/src/data.ts CHANGED
@@ -8,4 +8,4 @@ export type {
8
8
  DronesToolEntry,
9
9
  DronesCategoryEntry,
10
10
  } from './types';
11
-
11
+
package/src/env.d.ts CHANGED
@@ -2,4 +2,4 @@ declare module '*.astro' {
2
2
  const Component: (_props: Record<string, unknown>) => unknown;
3
3
  export default Component;
4
4
  }
5
-
5
+