@eeacms/volto-n2k 0.1.14 → 1.0.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/.coverage.babel.config.js +1 -1
- package/.i18n.babel.config.js +1 -0
- package/.project.eslintrc.js +46 -0
- package/CHANGELOG.md +16 -2
- package/README.md +1 -72
- package/RELEASE.md +74 -0
- package/babel.config.js +17 -0
- package/cypress.config.js +26 -0
- package/jest-addon.config.js +4 -4
- package/locales/volto.pot +69 -0
- package/package.json +21 -14
- package/src/components/index.js +3 -3
- package/src/components/manage/Blocks/ExploreSites/View.jsx +10 -5
- package/src/components/manage/Blocks/Landing/Edit.jsx +1 -0
- package/src/components/manage/Blocks/Landing/View.jsx +5 -17
- package/src/components/manage/Blocks/NavigationAnchors/View.jsx +1 -1
- package/src/components/theme/AppExtras/MultilingualRedirector/MultilingualRedirector.jsx +3 -3
- package/src/components/theme/Header/Header.jsx +56 -25
- package/src/components/theme/LanguageSelector/LanguageSelector.jsx +61 -13
- package/src/components/theme/Navigation/Navigation.jsx +34 -20
- package/src/components/theme/Sitemap/Sitemap.jsx +2 -2
- package/src/index.js +31 -29
- package/src/less/styles.less +10 -12
- package/src/static/global-line.svg +1 -0
- package/Jenkinsfile +0 -242
- package/cypress/fixtures/example.json +0 -5
- package/cypress/integration/block-basics.js +0 -32
- package/cypress/plugins/index.js +0 -26
- package/cypress/support/commands.js +0 -315
- package/cypress/support/index.js +0 -53
- package/cypress.json +0 -17
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module components/theme/Header/Header
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, { useEffect, useContext } from 'react';
|
|
6
|
+
import React, { useEffect, useContext, useMemo } from 'react';
|
|
7
7
|
import { matchPath, withRouter } from 'react-router';
|
|
8
8
|
import { Container, Sticky } from 'semantic-ui-react';
|
|
9
9
|
import { connect } from 'react-redux';
|
|
@@ -12,7 +12,7 @@ import config from '@plone/volto/registry';
|
|
|
12
12
|
import { Anontools } from '@plone/volto/components';
|
|
13
13
|
import { withLocalStorage } from '@eeacms/volto-n2k/hocs';
|
|
14
14
|
import Navigation from '../Navigation/Navigation';
|
|
15
|
-
import { StickyContext } from '
|
|
15
|
+
import { StickyContext } from '@eeacms/volto-bise/components';
|
|
16
16
|
|
|
17
17
|
const Navbar = (props) => {
|
|
18
18
|
const currentLang = props.localStorage.get('N2K_LANGUAGE');
|
|
@@ -26,6 +26,8 @@ const Navbar = (props) => {
|
|
|
26
26
|
{currentLang ? (
|
|
27
27
|
<Navigation
|
|
28
28
|
isSticky={props.isSticky}
|
|
29
|
+
isRoot={props.isRoot}
|
|
30
|
+
isExplorer={props.isExplorer}
|
|
29
31
|
pathname={props.pathname}
|
|
30
32
|
/>
|
|
31
33
|
) : (
|
|
@@ -47,37 +49,66 @@ const Navbar = (props) => {
|
|
|
47
49
|
const Header = (props) => {
|
|
48
50
|
const [isSticky, setIsSticky] = React.useState(false);
|
|
49
51
|
const { stickyRef } = useContext(StickyContext);
|
|
50
|
-
const isRoot =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const isRoot = useMemo(
|
|
53
|
+
() =>
|
|
54
|
+
matchPath(props.pathname, {
|
|
55
|
+
path: config.settings.n2k.multilingualRoot,
|
|
56
|
+
exact: true,
|
|
57
|
+
strict: false,
|
|
58
|
+
}),
|
|
59
|
+
[props.pathname],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const isExplorer = useMemo(
|
|
63
|
+
() =>
|
|
64
|
+
(isRoot &&
|
|
65
|
+
!config.settings.n2k.supportedLanguages.includes(isRoot.params.lang)) ||
|
|
66
|
+
matchPath(props.pathname, {
|
|
67
|
+
path: '/natura2000/explore-natura2000/*',
|
|
68
|
+
exact: true,
|
|
69
|
+
strict: false,
|
|
70
|
+
}),
|
|
71
|
+
[props.pathname, isRoot],
|
|
72
|
+
);
|
|
55
73
|
|
|
56
74
|
useEffect(() => {
|
|
57
75
|
if (!props.localStorage.get('N2K_LANGUAGE')) {
|
|
58
|
-
props.localStorage.set(
|
|
76
|
+
props.localStorage.set(
|
|
77
|
+
'N2K_LANGUAGE',
|
|
78
|
+
config.settings.n2k.defaultLanguage,
|
|
79
|
+
);
|
|
59
80
|
}
|
|
60
81
|
/* eslint-disable-next-line */
|
|
61
82
|
}, []);
|
|
62
83
|
|
|
63
|
-
return isRoot ? (
|
|
64
|
-
|
|
84
|
+
return isRoot || isExplorer ? (
|
|
85
|
+
<div className="ui basic segment sticky-header-wrapper" role="banner">
|
|
86
|
+
<Navbar
|
|
87
|
+
{...props}
|
|
88
|
+
isSticky={false}
|
|
89
|
+
isRoot={isRoot}
|
|
90
|
+
isExplorer={isExplorer}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
65
93
|
) : (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
94
|
+
<Sticky
|
|
95
|
+
context={stickyRef}
|
|
96
|
+
className="ui basic segment sticky-header-wrapper"
|
|
97
|
+
role="banner"
|
|
98
|
+
onStick={() => {
|
|
99
|
+
setIsSticky(true);
|
|
100
|
+
}}
|
|
101
|
+
onUnstick={() => {
|
|
102
|
+
setIsSticky(false);
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<Navbar
|
|
106
|
+
{...props}
|
|
107
|
+
isSticky={isSticky}
|
|
108
|
+
isRoot={isRoot}
|
|
109
|
+
isExplorer={isExplorer}
|
|
110
|
+
/>
|
|
111
|
+
</Sticky>
|
|
81
112
|
);
|
|
82
113
|
};
|
|
83
114
|
|
|
@@ -9,12 +9,14 @@ import { useSelector } from 'react-redux';
|
|
|
9
9
|
import cx from 'classnames';
|
|
10
10
|
import { langmap } from '@plone/volto/helpers';
|
|
11
11
|
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
12
|
-
import { Dropdown } from 'semantic-ui-react';
|
|
12
|
+
import { Dropdown, Image } from 'semantic-ui-react';
|
|
13
13
|
import config from '@plone/volto/registry';
|
|
14
14
|
import { withLocalStorage } from '@eeacms/volto-n2k/hocs';
|
|
15
15
|
import { getN2kItems, pathExists } from '@eeacms/volto-n2k/helpers';
|
|
16
16
|
import './styles.less';
|
|
17
17
|
|
|
18
|
+
import globeIcon from '@eeacms/volto-n2k/static/global-line.svg';
|
|
19
|
+
|
|
18
20
|
const LanguageSelector = (props) => {
|
|
19
21
|
const { settings } = config;
|
|
20
22
|
const content = useSelector((state) => state.content);
|
|
@@ -23,24 +25,24 @@ const LanguageSelector = (props) => {
|
|
|
23
25
|
const pathname = props.location.pathname;
|
|
24
26
|
const currentLang = localStorage.get('N2K_LANGUAGE');
|
|
25
27
|
const matchRoot = matchPath(pathname, {
|
|
26
|
-
path: settings.multilingualRoot,
|
|
28
|
+
path: settings.n2k.multilingualRoot,
|
|
27
29
|
exact: true,
|
|
28
30
|
strict: false,
|
|
29
31
|
});
|
|
30
32
|
const matchChild = matchPath(pathname, {
|
|
31
|
-
path: settings.multilingualPath,
|
|
33
|
+
path: settings.n2k.multilingualPath,
|
|
32
34
|
exact: true,
|
|
33
35
|
strict: false,
|
|
34
36
|
});
|
|
35
37
|
const match = matchRoot || matchChild;
|
|
36
38
|
const hasMultilingualSupport =
|
|
37
|
-
match && settings.supportedLanguages.includes(match.params.lang);
|
|
39
|
+
match && settings.n2k.supportedLanguages.includes(match.params.lang);
|
|
38
40
|
const translations = hasMultilingualSupport
|
|
39
|
-
? settings.supportedLanguages.map((lang) => {
|
|
41
|
+
? settings.n2k.supportedLanguages.map((lang) => {
|
|
40
42
|
return {
|
|
41
43
|
path: matchRoot
|
|
42
44
|
? `/natura2000/${lang}`
|
|
43
|
-
: generatePath(settings.multilingualPath, {
|
|
45
|
+
: generatePath(settings.n2k.multilingualPath, {
|
|
44
46
|
...match.params,
|
|
45
47
|
lang,
|
|
46
48
|
}),
|
|
@@ -48,20 +50,66 @@ const LanguageSelector = (props) => {
|
|
|
48
50
|
};
|
|
49
51
|
})
|
|
50
52
|
: [];
|
|
51
|
-
const supportedLanguagesOptions = settings.supportedLanguages.map(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const supportedLanguagesOptions = settings.n2k.supportedLanguages.map(
|
|
54
|
+
(lang) => ({
|
|
55
|
+
key: lang,
|
|
56
|
+
value: lang,
|
|
57
|
+
text: langmap[lang].nativeName,
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
56
60
|
|
|
57
61
|
return (
|
|
58
62
|
<div className={cx('language-selector', props.className)}>
|
|
63
|
+
{/* <Header.TopDropdownMenu
|
|
64
|
+
id="language-switcher"
|
|
65
|
+
className="item"
|
|
66
|
+
text={`${language.toUpperCase()}`}
|
|
67
|
+
mobileText={`${language.toUpperCase()}`}
|
|
68
|
+
icon={
|
|
69
|
+
<Image src={globeIcon} alt="language dropdown globe icon"></Image>
|
|
70
|
+
}
|
|
71
|
+
viewportWidth={width}
|
|
72
|
+
>
|
|
73
|
+
<ul
|
|
74
|
+
className="wrapper language-list"
|
|
75
|
+
role="listbox"
|
|
76
|
+
aria-label="language switcher"
|
|
77
|
+
>
|
|
78
|
+
{eea.languages.map((item, index) => (
|
|
79
|
+
<Dropdown.Item
|
|
80
|
+
as="li"
|
|
81
|
+
key={index}
|
|
82
|
+
text={
|
|
83
|
+
<span>
|
|
84
|
+
{item.name}
|
|
85
|
+
<span className="country-code">
|
|
86
|
+
{item.code.toUpperCase()}
|
|
87
|
+
</span>
|
|
88
|
+
</span>
|
|
89
|
+
}
|
|
90
|
+
onClick={() => {
|
|
91
|
+
const translation = find(translations, {
|
|
92
|
+
language: item.code,
|
|
93
|
+
});
|
|
94
|
+
const to = translation
|
|
95
|
+
? flattenToAppURL(translation['@id'])
|
|
96
|
+
: `/${item.code}`;
|
|
97
|
+
setLanguage(item.code);
|
|
98
|
+
history.push(to);
|
|
99
|
+
}}
|
|
100
|
+
></Dropdown.Item>
|
|
101
|
+
))}
|
|
102
|
+
</ul>
|
|
103
|
+
</Header.TopDropdownMenu> */}
|
|
59
104
|
<Dropdown
|
|
60
105
|
aria-label="Language selector"
|
|
61
106
|
disabled={content.get.loading}
|
|
62
107
|
placeholder="Select a language"
|
|
63
|
-
|
|
108
|
+
text={currentLang}
|
|
64
109
|
scrolling
|
|
110
|
+
icon={
|
|
111
|
+
<Image src={globeIcon} alt="language dropdown globe icon"></Image>
|
|
112
|
+
}
|
|
65
113
|
options={supportedLanguagesOptions}
|
|
66
114
|
onChange={(e, data) => {
|
|
67
115
|
const lang = data.value;
|
|
@@ -78,7 +126,7 @@ const LanguageSelector = (props) => {
|
|
|
78
126
|
}
|
|
79
127
|
}
|
|
80
128
|
|
|
81
|
-
if (config.settings.supportedLanguages.includes(lang)) {
|
|
129
|
+
if (config.settings.n2k.supportedLanguages.includes(lang)) {
|
|
82
130
|
localStorage.set('N2K_LANGUAGE', lang);
|
|
83
131
|
}
|
|
84
132
|
}}
|
|
@@ -12,7 +12,11 @@ import { Link } from 'react-router-dom';
|
|
|
12
12
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
13
13
|
import { Menu, Dropdown } from 'semantic-ui-react';
|
|
14
14
|
import cx from 'classnames';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
getBaseUrl,
|
|
17
|
+
flattenToAppURL,
|
|
18
|
+
hasApiExpander,
|
|
19
|
+
} from '@plone/volto/helpers';
|
|
16
20
|
import { UniversalLink, Icon } from '@plone/volto/components';
|
|
17
21
|
import qs from 'querystring';
|
|
18
22
|
import { getNavigation } from '@plone/volto/actions';
|
|
@@ -89,6 +93,10 @@ class Navigation extends Component {
|
|
|
89
93
|
).isRequired,
|
|
90
94
|
};
|
|
91
95
|
|
|
96
|
+
static defaultProps = {
|
|
97
|
+
token: null,
|
|
98
|
+
};
|
|
99
|
+
|
|
92
100
|
/**
|
|
93
101
|
* Constructor
|
|
94
102
|
* @method constructor
|
|
@@ -113,10 +121,13 @@ class Navigation extends Component {
|
|
|
113
121
|
* @returns {undefined}
|
|
114
122
|
*/
|
|
115
123
|
componentDidMount() {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
124
|
+
const { settings } = config;
|
|
125
|
+
if (!hasApiExpander('navigation', getBaseUrl(this.props.pathname))) {
|
|
126
|
+
this.props.getNavigation(
|
|
127
|
+
getBaseUrl(this.props.pathname),
|
|
128
|
+
settings.navDepth,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
120
131
|
this.setState({
|
|
121
132
|
isSdf: this.isSdf(),
|
|
122
133
|
});
|
|
@@ -134,20 +145,23 @@ class Navigation extends Component {
|
|
|
134
145
|
};
|
|
135
146
|
|
|
136
147
|
/**
|
|
137
|
-
* Component
|
|
138
|
-
* @method
|
|
139
|
-
* @param {Object}
|
|
148
|
+
* Component will receive props
|
|
149
|
+
* @method componentWillReceiveProps
|
|
150
|
+
* @param {Object} nextProps Next properties
|
|
140
151
|
* @returns {undefined}
|
|
141
152
|
*/
|
|
142
|
-
|
|
153
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
154
|
+
const { settings } = config;
|
|
143
155
|
if (
|
|
144
|
-
|
|
145
|
-
|
|
156
|
+
nextProps.pathname !== this.props.pathname ||
|
|
157
|
+
nextProps.token !== this.props.token
|
|
146
158
|
) {
|
|
147
|
-
this.props.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
if (!hasApiExpander('navigation', getBaseUrl(this.props.pathname))) {
|
|
160
|
+
this.props.getNavigation(
|
|
161
|
+
getBaseUrl(nextProps.pathname),
|
|
162
|
+
settings.navDepth,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
151
165
|
this.closeMobileMenu();
|
|
152
166
|
}
|
|
153
167
|
|
|
@@ -298,7 +312,7 @@ class Navigation extends Component {
|
|
|
298
312
|
''
|
|
299
313
|
)}
|
|
300
314
|
|
|
301
|
-
{!this.state.isSdf
|
|
315
|
+
{!this.state.isSdf && !this.props.isRoot && !this.props.isExplorer
|
|
302
316
|
? this.props.items.map((item) => {
|
|
303
317
|
const flatUrl = flattenToAppURL(item.url);
|
|
304
318
|
const itemID = item.title.split(' ').join('-').toLowerCase();
|
|
@@ -391,7 +405,7 @@ class Navigation extends Component {
|
|
|
391
405
|
);
|
|
392
406
|
})
|
|
393
407
|
: ''}
|
|
394
|
-
{!this.state.isSdf ? (
|
|
408
|
+
{!this.state.isSdf && !this.props.isExplorer ? (
|
|
395
409
|
<Menu.Item className="firstLevel language-selector-wrapper">
|
|
396
410
|
<LanguageSelector navigation={this.props.navigation} />
|
|
397
411
|
</Menu.Item>
|
|
@@ -412,7 +426,7 @@ const getN2kItems = (items, localStorage) => {
|
|
|
412
426
|
items.filter((item) => item.url === '/natura2000')?.[0]?.items || [];
|
|
413
427
|
natura2000.forEach((item) => {
|
|
414
428
|
const languageFolder = matchPath(item.url, {
|
|
415
|
-
path: config.settings.multilingualRoot,
|
|
429
|
+
path: config.settings.n2k.multilingualRoot,
|
|
416
430
|
exact: true,
|
|
417
431
|
strict: false,
|
|
418
432
|
});
|
|
@@ -421,7 +435,7 @@ const getN2kItems = (items, localStorage) => {
|
|
|
421
435
|
}
|
|
422
436
|
if (
|
|
423
437
|
languageFolder &&
|
|
424
|
-
!config.settings.supportedLanguages.includes(
|
|
438
|
+
!config.settings.n2k.supportedLanguages.includes(
|
|
425
439
|
languageFolder.params.lang,
|
|
426
440
|
) &&
|
|
427
441
|
item.url !== '/natura2000/sites' &&
|
|
@@ -443,10 +457,10 @@ export default compose(
|
|
|
443
457
|
connect(
|
|
444
458
|
(state, props) => {
|
|
445
459
|
return {
|
|
460
|
+
token: state.userSession.token,
|
|
446
461
|
route_parameters: state.route_parameters,
|
|
447
462
|
navigation: state.navigation,
|
|
448
463
|
items: getN2kItems(state.navigation.items, props.localStorage),
|
|
449
|
-
userToken: state?.userSession?.token,
|
|
450
464
|
};
|
|
451
465
|
},
|
|
452
466
|
{ getNavigation },
|
|
@@ -91,7 +91,7 @@ const getN2kItems = (items, localStorage) => {
|
|
|
91
91
|
items.filter((item) => item.url === '/natura2000')?.[0]?.items || [];
|
|
92
92
|
natura2000.forEach((item) => {
|
|
93
93
|
const languageFolder = matchPath(item.url, {
|
|
94
|
-
path: config.settings.multilingualRoot,
|
|
94
|
+
path: config.settings.n2k.multilingualRoot,
|
|
95
95
|
exact: true,
|
|
96
96
|
strict: false,
|
|
97
97
|
});
|
|
@@ -100,7 +100,7 @@ const getN2kItems = (items, localStorage) => {
|
|
|
100
100
|
}
|
|
101
101
|
if (
|
|
102
102
|
languageFolder &&
|
|
103
|
-
!config.settings.supportedLanguages.includes(
|
|
103
|
+
!config.settings.n2k.supportedLanguages.includes(
|
|
104
104
|
languageFolder.params.lang,
|
|
105
105
|
) &&
|
|
106
106
|
item.url !== '/natura2000/sites' &&
|
package/src/index.js
CHANGED
|
@@ -70,35 +70,37 @@ const applyConfig = (config) => {
|
|
|
70
70
|
|
|
71
71
|
config.settings = {
|
|
72
72
|
...config.settings,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
73
|
+
n2k: {
|
|
74
|
+
multilingualRoot: '/natura2000/:lang',
|
|
75
|
+
multilingualPath: '/natura2000/:lang/*',
|
|
76
|
+
defaultLanguage: 'en',
|
|
77
|
+
supportedLanguages: [
|
|
78
|
+
'bg',
|
|
79
|
+
'hr',
|
|
80
|
+
'cs',
|
|
81
|
+
'da',
|
|
82
|
+
'nl',
|
|
83
|
+
'en',
|
|
84
|
+
'et',
|
|
85
|
+
'fi',
|
|
86
|
+
'fr',
|
|
87
|
+
'de',
|
|
88
|
+
'el',
|
|
89
|
+
'hu',
|
|
90
|
+
'ga',
|
|
91
|
+
'it',
|
|
92
|
+
'lv',
|
|
93
|
+
'lt',
|
|
94
|
+
'mt',
|
|
95
|
+
'pl',
|
|
96
|
+
'pt',
|
|
97
|
+
'ro',
|
|
98
|
+
'sk',
|
|
99
|
+
'sl',
|
|
100
|
+
'es',
|
|
101
|
+
'sv',
|
|
102
|
+
],
|
|
103
|
+
},
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
config.settings.themes = {
|
package/src/less/styles.less
CHANGED
|
@@ -347,24 +347,22 @@ body.grey-bg {
|
|
|
347
347
|
position: absolute;
|
|
348
348
|
top: 0;
|
|
349
349
|
right: 0;
|
|
350
|
-
margin:
|
|
350
|
+
margin: 0;
|
|
351
|
+
margin-bottom: 0;
|
|
351
352
|
transform: translateX(100%) translateY(0);
|
|
352
353
|
|
|
353
354
|
&:hover {
|
|
354
355
|
background-color: transparent !important;
|
|
355
356
|
}
|
|
356
357
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
border-radius: 100%;
|
|
366
|
-
content: '';
|
|
367
|
-
transform: translateX(-50%);
|
|
358
|
+
.language-selector .ui.dropdown {
|
|
359
|
+
display: flex;
|
|
360
|
+
flex-direction: row-reverse;
|
|
361
|
+
align-items: center;
|
|
362
|
+
|
|
363
|
+
img {
|
|
364
|
+
margin-right: 0.25rem;
|
|
365
|
+
}
|
|
368
366
|
}
|
|
369
367
|
}
|
|
370
368
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z" fill="rgba(46,62,76,1)"/></svg>
|