react-native-maplibre-lite 0.2.0 → 0.2.1
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/README.md +92 -6
- package/lib/module/components/MapView.js +116 -14
- package/lib/module/components/MapView.js.map +1 -1
- package/lib/module/components/NavigatorHud.js +152 -0
- package/lib/module/components/NavigatorHud.js.map +1 -0
- package/lib/module/components/NavigatorRecenterButton.js +48 -0
- package/lib/module/components/NavigatorRecenterButton.js.map +1 -0
- package/lib/module/components/NavigatorVoiceControl.js +173 -0
- package/lib/module/components/NavigatorVoiceControl.js.map +1 -0
- package/lib/module/components/navigatorChromeTheme.js +98 -0
- package/lib/module/components/navigatorChromeTheme.js.map +1 -0
- package/lib/module/components/navigatorManeuverIcon.js +210 -0
- package/lib/module/components/navigatorManeuverIcon.js.map +1 -0
- package/lib/module/components/navigatorVoiceCatalog.js +225 -0
- package/lib/module/components/navigatorVoiceCatalog.js.map +1 -0
- package/lib/module/components/navigatorVoiceKeys.js +14 -0
- package/lib/module/components/navigatorVoiceKeys.js.map +1 -0
- package/lib/module/components/navigatorVoicePlayer.js +100 -0
- package/lib/module/components/navigatorVoicePlayer.js.map +1 -0
- package/lib/module/components/navigatorVoiceStrings.js +31 -0
- package/lib/module/components/navigatorVoiceStrings.js.map +1 -0
- package/lib/module/components/types.js +22 -0
- package/lib/module/components/types.js.map +1 -1
- package/lib/module/components/useNavigatorVoice.js +78 -0
- package/lib/module/components/useNavigatorVoice.js.map +1 -0
- package/lib/module/components/utils.js +26 -0
- package/lib/module/components/utils.js.map +1 -1
- package/lib/module/components/webMapBuild.js +1 -1
- package/lib/module/components/webMapBuild.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/components/MapView.d.ts +14 -1
- package/lib/typescript/src/components/MapView.d.ts.map +1 -1
- package/lib/typescript/src/components/NavigatorHud.d.ts +13 -0
- package/lib/typescript/src/components/NavigatorHud.d.ts.map +1 -0
- package/lib/typescript/src/components/NavigatorRecenterButton.d.ts +11 -0
- package/lib/typescript/src/components/NavigatorRecenterButton.d.ts.map +1 -0
- package/lib/typescript/src/components/NavigatorVoiceControl.d.ts +20 -0
- package/lib/typescript/src/components/NavigatorVoiceControl.d.ts.map +1 -0
- package/lib/typescript/src/components/navigatorChromeTheme.d.ts +19 -0
- package/lib/typescript/src/components/navigatorChromeTheme.d.ts.map +1 -0
- package/lib/typescript/src/components/navigatorManeuverIcon.d.ts +20 -0
- package/lib/typescript/src/components/navigatorManeuverIcon.d.ts.map +1 -0
- package/lib/typescript/src/components/navigatorVoiceCatalog.d.ts +50 -0
- package/lib/typescript/src/components/navigatorVoiceCatalog.d.ts.map +1 -0
- package/lib/typescript/src/components/navigatorVoiceKeys.d.ts +10 -0
- package/lib/typescript/src/components/navigatorVoiceKeys.d.ts.map +1 -0
- package/lib/typescript/src/components/navigatorVoicePlayer.d.ts +15 -0
- package/lib/typescript/src/components/navigatorVoicePlayer.d.ts.map +1 -0
- package/lib/typescript/src/components/navigatorVoiceStrings.d.ts +19 -0
- package/lib/typescript/src/components/navigatorVoiceStrings.d.ts.map +1 -0
- package/lib/typescript/src/components/types.d.ts +83 -17
- package/lib/typescript/src/components/types.d.ts.map +1 -1
- package/lib/typescript/src/components/useNavigatorVoice.d.ts +20 -0
- package/lib/typescript/src/components/useNavigatorVoice.d.ts.map +1 -0
- package/lib/typescript/src/components/utils.d.ts +9 -0
- package/lib/typescript/src/components/utils.d.ts.map +1 -1
- package/lib/typescript/src/components/webMapBuild.d.ts +1 -1
- package/lib/typescript/src/components/webMapBuild.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +16 -7
- package/resources/README.md +62 -0
- package/resources/map.html +797 -0
- package/src/components/MapView.tsx +154 -8
- package/src/components/NavigatorHud.tsx +166 -0
- package/src/components/NavigatorRecenterButton.tsx +45 -0
- package/src/components/NavigatorVoiceControl.tsx +198 -0
- package/src/components/navigatorChromeTheme.ts +118 -0
- package/src/components/navigatorManeuverIcon.tsx +177 -0
- package/src/components/navigatorVoiceCatalog.ts +275 -0
- package/src/components/navigatorVoiceKeys.ts +132 -0
- package/src/components/navigatorVoicePlayer.tsx +126 -0
- package/src/components/navigatorVoiceStrings.ts +42 -0
- package/src/components/types.ts +87 -18
- package/src/components/useNavigatorVoice.ts +96 -0
- package/src/components/utils.ts +28 -0
- package/src/components/webMapBuild.ts +1 -1
- package/src/index.tsx +8 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Pressable, ScrollView, Text, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { NavigatorVoiceIcon } from './navigatorManeuverIcon';
|
|
5
|
+
import type { NavigatorChromeTheme } from './navigatorChromeTheme';
|
|
6
|
+
import type { VoiceCatalogEntry, VoiceVolumeLevel } from './navigatorVoiceCatalog';
|
|
7
|
+
import type { NavigatorUiStrings } from './navigatorVoiceStrings';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* FAB выбора голоса + меню (список голосов, громкость 1–5, «отключить»).
|
|
11
|
+
* Порт DOM-меню из веб-навигатора. Каталог/манифест и персист — в
|
|
12
|
+
* `useNavigatorVoice`; компонент только презентационный.
|
|
13
|
+
*/
|
|
14
|
+
export function NavigatorVoiceControl({
|
|
15
|
+
theme,
|
|
16
|
+
strings,
|
|
17
|
+
catalog,
|
|
18
|
+
selectedDir,
|
|
19
|
+
voiceEnabled,
|
|
20
|
+
volumeLevel,
|
|
21
|
+
onSelectVoice,
|
|
22
|
+
onSelectVolume,
|
|
23
|
+
onDisable,
|
|
24
|
+
}: {
|
|
25
|
+
theme: NavigatorChromeTheme;
|
|
26
|
+
strings: NavigatorUiStrings;
|
|
27
|
+
catalog: VoiceCatalogEntry[];
|
|
28
|
+
selectedDir: string | null;
|
|
29
|
+
voiceEnabled: boolean;
|
|
30
|
+
volumeLevel: VoiceVolumeLevel;
|
|
31
|
+
onSelectVoice: (dir: string) => void;
|
|
32
|
+
onSelectVolume: (level: VoiceVolumeLevel) => void;
|
|
33
|
+
onDisable: () => void;
|
|
34
|
+
}) {
|
|
35
|
+
const [open, setOpen] = useState(false);
|
|
36
|
+
|
|
37
|
+
if (catalog.length === 0) return null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<Pressable
|
|
42
|
+
accessibilityRole="button"
|
|
43
|
+
accessibilityLabel={strings.voicePickerAria}
|
|
44
|
+
onPress={() => setOpen((v) => !v)}
|
|
45
|
+
style={({ pressed }) => ({
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
right: 16,
|
|
48
|
+
bottom: 16 + 56 + 8,
|
|
49
|
+
width: 56,
|
|
50
|
+
height: 56,
|
|
51
|
+
borderRadius: 28,
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
backgroundColor: theme.background,
|
|
55
|
+
shadowColor: '#0f172a',
|
|
56
|
+
shadowOpacity: 0.32,
|
|
57
|
+
shadowRadius: 24,
|
|
58
|
+
shadowOffset: { width: 0, height: 10 },
|
|
59
|
+
elevation: 6,
|
|
60
|
+
opacity: pressed ? 0.9 : 1,
|
|
61
|
+
})}
|
|
62
|
+
>
|
|
63
|
+
<NavigatorVoiceIcon
|
|
64
|
+
color={voiceEnabled ? theme.iconForeground : theme.muted}
|
|
65
|
+
/>
|
|
66
|
+
</Pressable>
|
|
67
|
+
|
|
68
|
+
{open ? (
|
|
69
|
+
<View
|
|
70
|
+
style={{
|
|
71
|
+
position: 'absolute',
|
|
72
|
+
right: 16,
|
|
73
|
+
bottom: 16 + 56 + 8 + 56 + 8,
|
|
74
|
+
minWidth: 200,
|
|
75
|
+
maxWidth: 280,
|
|
76
|
+
paddingVertical: 6,
|
|
77
|
+
borderRadius: 12,
|
|
78
|
+
backgroundColor: theme.background,
|
|
79
|
+
shadowColor: '#0f172a',
|
|
80
|
+
shadowOpacity: 0.32,
|
|
81
|
+
shadowRadius: 24,
|
|
82
|
+
shadowOffset: { width: 0, height: 10 },
|
|
83
|
+
elevation: 8,
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<ScrollView style={{ maxHeight: 280 }}>
|
|
87
|
+
{catalog.map((entry) => {
|
|
88
|
+
const active = voiceEnabled && entry.dir === selectedDir;
|
|
89
|
+
return (
|
|
90
|
+
<Pressable
|
|
91
|
+
key={entry.dir}
|
|
92
|
+
accessibilityRole="menuitem"
|
|
93
|
+
onPress={() => {
|
|
94
|
+
setOpen(false);
|
|
95
|
+
onSelectVoice(entry.dir);
|
|
96
|
+
}}
|
|
97
|
+
style={{ paddingVertical: 10, paddingHorizontal: 16 }}
|
|
98
|
+
>
|
|
99
|
+
<Text
|
|
100
|
+
style={{
|
|
101
|
+
fontSize: 15,
|
|
102
|
+
color: active ? theme.iconForeground : theme.foreground,
|
|
103
|
+
fontWeight: active ? '600' : '400',
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{entry.name}
|
|
107
|
+
</Text>
|
|
108
|
+
</Pressable>
|
|
109
|
+
);
|
|
110
|
+
})}
|
|
111
|
+
|
|
112
|
+
<View
|
|
113
|
+
style={{
|
|
114
|
+
marginTop: 4,
|
|
115
|
+
paddingTop: 12,
|
|
116
|
+
paddingHorizontal: 16,
|
|
117
|
+
borderTopWidth: 1,
|
|
118
|
+
borderTopColor: 'rgba(255, 255, 255, 0.12)',
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<Text style={{ marginBottom: 10, fontSize: 13, color: theme.muted }}>
|
|
122
|
+
{strings.voiceVolumeLabel}
|
|
123
|
+
</Text>
|
|
124
|
+
<View
|
|
125
|
+
style={{
|
|
126
|
+
flexDirection: 'row',
|
|
127
|
+
alignItems: 'flex-end',
|
|
128
|
+
gap: 6,
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
{([1, 2, 3, 4, 5] as const).map((step) => {
|
|
132
|
+
const stepActive = step <= volumeLevel;
|
|
133
|
+
return (
|
|
134
|
+
<Pressable
|
|
135
|
+
key={step}
|
|
136
|
+
accessibilityRole="button"
|
|
137
|
+
accessibilityLabel={strings.voiceVolumeStepAria(step)}
|
|
138
|
+
onPress={() => onSelectVolume(step as VoiceVolumeLevel)}
|
|
139
|
+
style={{
|
|
140
|
+
flex: 1,
|
|
141
|
+
height: 36,
|
|
142
|
+
justifyContent: 'flex-end',
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
paddingBottom: 4,
|
|
145
|
+
borderRadius: 6,
|
|
146
|
+
backgroundColor:
|
|
147
|
+
step === volumeLevel
|
|
148
|
+
? 'rgba(59, 130, 246, 0.18)'
|
|
149
|
+
: 'transparent',
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
<View
|
|
153
|
+
style={{
|
|
154
|
+
width: 14,
|
|
155
|
+
height: Math.max(6, step * 6),
|
|
156
|
+
borderRadius: 2,
|
|
157
|
+
backgroundColor: stepActive
|
|
158
|
+
? theme.iconForeground
|
|
159
|
+
: theme.muted,
|
|
160
|
+
}}
|
|
161
|
+
/>
|
|
162
|
+
</Pressable>
|
|
163
|
+
);
|
|
164
|
+
})}
|
|
165
|
+
</View>
|
|
166
|
+
</View>
|
|
167
|
+
|
|
168
|
+
<Pressable
|
|
169
|
+
accessibilityRole="menuitem"
|
|
170
|
+
onPress={() => {
|
|
171
|
+
setOpen(false);
|
|
172
|
+
onDisable();
|
|
173
|
+
}}
|
|
174
|
+
style={{
|
|
175
|
+
marginTop: 4,
|
|
176
|
+
paddingTop: 12,
|
|
177
|
+
paddingBottom: 10,
|
|
178
|
+
paddingHorizontal: 16,
|
|
179
|
+
borderTopWidth: 1,
|
|
180
|
+
borderTopColor: 'rgba(255, 255, 255, 0.12)',
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
<Text
|
|
184
|
+
style={{
|
|
185
|
+
fontSize: 15,
|
|
186
|
+
color: !voiceEnabled ? theme.iconForeground : theme.muted,
|
|
187
|
+
fontWeight: !voiceEnabled ? '600' : '400',
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
{strings.voiceDisable}
|
|
191
|
+
</Text>
|
|
192
|
+
</Pressable>
|
|
193
|
+
</ScrollView>
|
|
194
|
+
</View>
|
|
195
|
+
) : null}
|
|
196
|
+
</>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { NavigatorChromeParams } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Цвета HUD/FAB навигатора, выведенные из `navigatorChrome`. Порт
|
|
5
|
+
* HUD-части `resolveNavigatorChrome` из веб-навигатора: раньше эти
|
|
6
|
+
* CSS-переменные применялись к DOM, теперь панель рисует нативная часть.
|
|
7
|
+
*/
|
|
8
|
+
export type NavigatorChromeTheme = {
|
|
9
|
+
background: string;
|
|
10
|
+
foreground: string;
|
|
11
|
+
subtle: string;
|
|
12
|
+
muted: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
divider: string;
|
|
15
|
+
iconBackground: string;
|
|
16
|
+
iconForeground: string;
|
|
17
|
+
shadow: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type Rgb = { r: number; g: number; b: number };
|
|
21
|
+
|
|
22
|
+
const DEF_ACCENT = '#3b82f6';
|
|
23
|
+
const DEF_HUD_BG = 'rgba(15, 23, 42, 0.92)';
|
|
24
|
+
const DEF_HUD_FG = '#f8fafc';
|
|
25
|
+
const DEF_HUD_MUTED = '#94a3b8';
|
|
26
|
+
const DEF_HUD_SUBTLE = '#e2e8f0';
|
|
27
|
+
const DEF_HUD_SUMMARY = '#cbd5e1';
|
|
28
|
+
const RGB_WHITE: Rgb = { r: 255, g: 255, b: 255 };
|
|
29
|
+
|
|
30
|
+
function clamp255(n: number): number {
|
|
31
|
+
return Math.max(0, Math.min(255, Math.round(n)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function mixRgb(a: Rgb, b: Rgb, t: number): Rgb {
|
|
35
|
+
return {
|
|
36
|
+
r: clamp255(a.r + (b.r - a.r) * t),
|
|
37
|
+
g: clamp255(a.g + (b.g - a.g) * t),
|
|
38
|
+
b: clamp255(a.b + (b.b - a.b) * t),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toHex({ r, g, b }: Rgb): string {
|
|
43
|
+
const h = (n: number) => n.toString(16).padStart(2, '0');
|
|
44
|
+
return `#${h(r)}${h(g)}${h(b)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseHexColor(input: string | undefined): Rgb | null {
|
|
48
|
+
if (typeof input !== 'string') return null;
|
|
49
|
+
const s = input.trim();
|
|
50
|
+
if (!/^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$/.test(s)) return null;
|
|
51
|
+
if (s.length === 4) {
|
|
52
|
+
const r = parseInt(s[1]! + s[1]!, 16);
|
|
53
|
+
const g = parseInt(s[2]! + s[2]!, 16);
|
|
54
|
+
const b = parseInt(s[3]! + s[3]!, 16);
|
|
55
|
+
return { r, g, b };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
r: parseInt(s.slice(1, 3), 16),
|
|
59
|
+
g: parseInt(s.slice(3, 5), 16),
|
|
60
|
+
b: parseInt(s.slice(5, 7), 16),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function rgba(rgb: Rgb, a: number): string {
|
|
65
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${a})`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function mixCssHexOr(a: string, b: string, t: number, fallback: string): string {
|
|
69
|
+
const ra = parseHexColor(a);
|
|
70
|
+
const rb = parseHexColor(b);
|
|
71
|
+
if (!ra || !rb) return fallback;
|
|
72
|
+
return toHex(mixRgb(ra, rb, t));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function dividerFromMutedCss(muted: string): string {
|
|
76
|
+
const m = parseHexColor(muted);
|
|
77
|
+
if (m) return rgba(m, 0.28);
|
|
78
|
+
return 'rgba(148, 163, 184, 0.28)';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function resolveNavigatorChromeTheme(
|
|
82
|
+
input?: NavigatorChromeParams | null
|
|
83
|
+
): NavigatorChromeTheme {
|
|
84
|
+
const accentRgb =
|
|
85
|
+
parseHexColor(input?.accent) ?? parseHexColor(DEF_ACCENT) ?? { r: 59, g: 130, b: 246 };
|
|
86
|
+
|
|
87
|
+
const background =
|
|
88
|
+
typeof input?.hudBackground === 'string' && input.hudBackground.trim()
|
|
89
|
+
? input.hudBackground.trim()
|
|
90
|
+
: DEF_HUD_BG;
|
|
91
|
+
const foreground =
|
|
92
|
+
typeof input?.hudForeground === 'string' && input.hudForeground.trim()
|
|
93
|
+
? input.hudForeground.trim()
|
|
94
|
+
: DEF_HUD_FG;
|
|
95
|
+
const muted =
|
|
96
|
+
typeof input?.hudMuted === 'string' && input.hudMuted.trim()
|
|
97
|
+
? input.hudMuted.trim()
|
|
98
|
+
: DEF_HUD_MUTED;
|
|
99
|
+
|
|
100
|
+
const subtle = mixCssHexOr(foreground, muted, 0.52, DEF_HUD_SUBTLE);
|
|
101
|
+
const summary = mixCssHexOr(foreground, muted, 0.3, DEF_HUD_SUMMARY);
|
|
102
|
+
const divider = dividerFromMutedCss(muted);
|
|
103
|
+
|
|
104
|
+
const iconForeground = toHex(mixRgb(accentRgb, RGB_WHITE, 0.38));
|
|
105
|
+
const iconBackground = rgba(accentRgb, 0.18);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
background,
|
|
109
|
+
foreground,
|
|
110
|
+
subtle,
|
|
111
|
+
muted,
|
|
112
|
+
summary,
|
|
113
|
+
divider,
|
|
114
|
+
iconBackground,
|
|
115
|
+
iconForeground,
|
|
116
|
+
shadow: 'rgba(15, 23, 42, 0.32)',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Svg, { Circle, G, Path } from 'react-native-svg';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SVG-иконка манёвра навигатора. Порт `getSignIcon` из веб-части
|
|
5
|
+
* (`webProject/src/map/navigator.ts`) на `react-native-svg`.
|
|
6
|
+
*/
|
|
7
|
+
export function NavigatorManeuverIcon({
|
|
8
|
+
sign,
|
|
9
|
+
color,
|
|
10
|
+
size = 32,
|
|
11
|
+
}: {
|
|
12
|
+
sign: number;
|
|
13
|
+
color: string;
|
|
14
|
+
size?: number;
|
|
15
|
+
}) {
|
|
16
|
+
const stroke = color;
|
|
17
|
+
|
|
18
|
+
const arrow = (rotation: number) => (
|
|
19
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
20
|
+
<G rotation={rotation} origin="12, 12">
|
|
21
|
+
<Path
|
|
22
|
+
d="M12 21 V5"
|
|
23
|
+
stroke={stroke}
|
|
24
|
+
strokeWidth={2.4}
|
|
25
|
+
strokeLinecap="round"
|
|
26
|
+
strokeLinejoin="round"
|
|
27
|
+
/>
|
|
28
|
+
<Path
|
|
29
|
+
d="M5 12 L12 5 L19 12"
|
|
30
|
+
stroke={stroke}
|
|
31
|
+
strokeWidth={2.4}
|
|
32
|
+
strokeLinecap="round"
|
|
33
|
+
strokeLinejoin="round"
|
|
34
|
+
/>
|
|
35
|
+
</G>
|
|
36
|
+
</Svg>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
switch (sign) {
|
|
40
|
+
case -3:
|
|
41
|
+
return arrow(-135);
|
|
42
|
+
case -2:
|
|
43
|
+
return arrow(-90);
|
|
44
|
+
case -1:
|
|
45
|
+
return arrow(-45);
|
|
46
|
+
case 0:
|
|
47
|
+
return arrow(0);
|
|
48
|
+
case 1:
|
|
49
|
+
return arrow(45);
|
|
50
|
+
case 2:
|
|
51
|
+
return arrow(90);
|
|
52
|
+
case 3:
|
|
53
|
+
return arrow(135);
|
|
54
|
+
case 4:
|
|
55
|
+
return (
|
|
56
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
57
|
+
<Circle cx={12} cy={12} r={9} stroke={stroke} strokeWidth={2.2} />
|
|
58
|
+
<Circle cx={12} cy={12} r={3} fill={stroke} />
|
|
59
|
+
</Svg>
|
|
60
|
+
);
|
|
61
|
+
case 5:
|
|
62
|
+
return (
|
|
63
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
64
|
+
<Circle cx={12} cy={12} r={4} stroke={stroke} strokeWidth={2.2} />
|
|
65
|
+
<Circle cx={12} cy={12} r={9} stroke={stroke} strokeWidth={2.2} />
|
|
66
|
+
</Svg>
|
|
67
|
+
);
|
|
68
|
+
case 6:
|
|
69
|
+
case -6:
|
|
70
|
+
return (
|
|
71
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
72
|
+
<Circle cx={12} cy={12} r={6} stroke={stroke} strokeWidth={2.2} />
|
|
73
|
+
<Path
|
|
74
|
+
d="M12 18 V21"
|
|
75
|
+
stroke={stroke}
|
|
76
|
+
strokeWidth={2.2}
|
|
77
|
+
strokeLinecap="round"
|
|
78
|
+
strokeLinejoin="round"
|
|
79
|
+
/>
|
|
80
|
+
<Path
|
|
81
|
+
d="M9 21 H15"
|
|
82
|
+
stroke={stroke}
|
|
83
|
+
strokeWidth={2.2}
|
|
84
|
+
strokeLinecap="round"
|
|
85
|
+
strokeLinejoin="round"
|
|
86
|
+
/>
|
|
87
|
+
</Svg>
|
|
88
|
+
);
|
|
89
|
+
case -8:
|
|
90
|
+
case 8:
|
|
91
|
+
case -98:
|
|
92
|
+
return (
|
|
93
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
94
|
+
<Path
|
|
95
|
+
d="M8 20 V11 a5 5 0 0 1 10 0 V14"
|
|
96
|
+
stroke={stroke}
|
|
97
|
+
strokeWidth={2.4}
|
|
98
|
+
strokeLinecap="round"
|
|
99
|
+
strokeLinejoin="round"
|
|
100
|
+
/>
|
|
101
|
+
<Path
|
|
102
|
+
d="M14 18 L18 14 L22 18"
|
|
103
|
+
stroke={stroke}
|
|
104
|
+
strokeWidth={2.4}
|
|
105
|
+
strokeLinecap="round"
|
|
106
|
+
strokeLinejoin="round"
|
|
107
|
+
/>
|
|
108
|
+
</Svg>
|
|
109
|
+
);
|
|
110
|
+
default:
|
|
111
|
+
return arrow(0);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Иконка кнопки «вернуть камеру на положение» (порт из веб recenter FAB). */
|
|
116
|
+
export function NavigatorRecenterIcon({
|
|
117
|
+
color,
|
|
118
|
+
size = 28,
|
|
119
|
+
}: {
|
|
120
|
+
color: string;
|
|
121
|
+
size?: number;
|
|
122
|
+
}) {
|
|
123
|
+
return (
|
|
124
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
125
|
+
<Circle cx={12} cy={12} r={8.5} stroke={color} strokeWidth={2.2} />
|
|
126
|
+
<Path
|
|
127
|
+
d="M12 15.5 V8.5"
|
|
128
|
+
stroke={color}
|
|
129
|
+
strokeWidth={2.2}
|
|
130
|
+
strokeLinecap="round"
|
|
131
|
+
strokeLinejoin="round"
|
|
132
|
+
/>
|
|
133
|
+
<Path
|
|
134
|
+
d="M9.25 11.25 L12 8.5 L14.75 11.25"
|
|
135
|
+
stroke={color}
|
|
136
|
+
strokeWidth={2.2}
|
|
137
|
+
strokeLinecap="round"
|
|
138
|
+
strokeLinejoin="round"
|
|
139
|
+
/>
|
|
140
|
+
</Svg>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Иконка кнопки выбора голоса озвучки (порт из веб voice FAB). */
|
|
145
|
+
export function NavigatorVoiceIcon({
|
|
146
|
+
color,
|
|
147
|
+
size = 26,
|
|
148
|
+
}: {
|
|
149
|
+
color: string;
|
|
150
|
+
size?: number;
|
|
151
|
+
}) {
|
|
152
|
+
return (
|
|
153
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
154
|
+
<Path
|
|
155
|
+
d="M11 5 L6 9 L2 9 L2 15 L6 15 L11 19 Z"
|
|
156
|
+
stroke={color}
|
|
157
|
+
strokeWidth={2}
|
|
158
|
+
strokeLinecap="round"
|
|
159
|
+
strokeLinejoin="round"
|
|
160
|
+
/>
|
|
161
|
+
<Path
|
|
162
|
+
d="M15.54 8.46a5 5 0 0 1 0 7.07"
|
|
163
|
+
stroke={color}
|
|
164
|
+
strokeWidth={2}
|
|
165
|
+
strokeLinecap="round"
|
|
166
|
+
strokeLinejoin="round"
|
|
167
|
+
/>
|
|
168
|
+
<Path
|
|
169
|
+
d="M19.07 4.93a10 10 0 0 1 0 14.14"
|
|
170
|
+
stroke={color}
|
|
171
|
+
strokeWidth={2}
|
|
172
|
+
strokeLinecap="round"
|
|
173
|
+
strokeLinejoin="round"
|
|
174
|
+
/>
|
|
175
|
+
</Svg>
|
|
176
|
+
);
|
|
177
|
+
}
|