@kitconcept/volto-light-theme 1.0.1 → 2.1.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/.github/workflows/acceptance.yml +3 -2
- package/.github/workflows/changelog.yml +1 -1
- package/.github/workflows/code.yml +3 -2
- package/.github/workflows/deploy.yml +1 -1
- package/.github/workflows/unit.yml +3 -2
- package/CHANGELOG.md +56 -0
- package/Makefile +2 -2
- package/README.md +12 -1
- package/acceptance/cypress/tests/basic.cy.js +1 -1
- package/acceptance/cypress/tests/nav.cy.js +60 -0
- package/locales/de/LC_MESSAGES/volto.po +43 -1
- package/locales/en/LC_MESSAGES/volto.po +48 -1
- package/locales/pt_BR/volto.po +317 -0
- package/locales/volto.pot +49 -2
- package/package.json +3 -3
- package/src/components/Blocks/Listing/ImageGallery.jsx +126 -0
- package/src/components/Header/Header.jsx +3 -1
- package/src/components/MobileNavigation/MobileNavigation.jsx +334 -0
- package/src/components/MobileNavigation/MobileToolsFooter.jsx +10 -0
- package/src/components/Navigation/Navigation.jsx +198 -182
- package/src/customizations/volto/components/manage/Blocks/Listing/ImageGallery.jsx +8 -0
- package/src/icons/back-down.svg +8 -0
- package/src/icons/fullscreen.svg +7 -0
- package/src/icons/left-key.svg +4 -0
- package/src/icons/pause.svg +6 -0
- package/src/icons/play.svg +4 -0
- package/src/icons/right-key.svg +4 -0
- package/src/index.js +7 -1
- package/src/theme/_content.scss +1 -1
- package/src/theme/_fonts.scss +317 -0
- package/src/theme/_header.scss +610 -8
- package/src/theme/_layout.scss +7 -24
- package/src/theme/_sitemap.scss +25 -0
- package/src/theme/_typo-custom.scss +0 -73
- package/src/theme/_typography.scss +1 -245
- package/src/theme/_variables.scss +14 -0
- package/src/theme/blocks/_button.scss +6 -0
- package/src/theme/blocks/_grid.scss +8 -0
- package/src/theme/blocks/_image.scss +1 -1
- package/src/theme/blocks/_listing.scss +111 -5
- package/src/theme/blocks/_maps.scss +4 -0
- package/src/theme/blocks/_teaser.scss +1 -4
- package/src/theme/main.scss +5 -1
|
@@ -1,199 +1,215 @@
|
|
|
1
1
|
// SemanticUI-free pre-@plone/components
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
3
4
|
import PropTypes from 'prop-types';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
6
|
+
import { NavLink } from 'react-router-dom';
|
|
7
|
+
import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib';
|
|
8
|
+
import { useIntl, defineMessages, injectIntl } from 'react-intl';
|
|
7
9
|
import cx from 'classnames';
|
|
8
|
-
import {
|
|
10
|
+
import { getBaseUrl, hasApiExpander } from '@plone/volto/helpers';
|
|
9
11
|
import config from '@plone/volto/registry';
|
|
12
|
+
|
|
10
13
|
import { getNavigation } from '@plone/volto/actions';
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
14
|
+
import { Icon } from '@plone/volto/components';
|
|
15
|
+
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
16
|
+
import NavItem from '@plone/volto/components/theme/Navigation/NavItem';
|
|
13
17
|
|
|
14
18
|
const messages = defineMessages({
|
|
15
|
-
|
|
19
|
+
closeMenu: {
|
|
16
20
|
id: 'Close menu',
|
|
17
21
|
defaultMessage: 'Close menu',
|
|
18
22
|
},
|
|
19
|
-
openMobileMenu: {
|
|
20
|
-
id: 'Open menu',
|
|
21
|
-
defaultMessage: 'Open menu',
|
|
22
|
-
},
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* Property types.
|
|
33
|
-
* @property {Object} propTypes Property types.
|
|
34
|
-
* @static
|
|
35
|
-
*/
|
|
36
|
-
static propTypes = {
|
|
37
|
-
getNavigation: PropTypes.func.isRequired,
|
|
38
|
-
pathname: PropTypes.string.isRequired,
|
|
39
|
-
items: PropTypes.arrayOf(
|
|
40
|
-
PropTypes.shape({
|
|
41
|
-
title: PropTypes.string,
|
|
42
|
-
url: PropTypes.string,
|
|
43
|
-
}),
|
|
44
|
-
).isRequired,
|
|
45
|
-
lang: PropTypes.string.isRequired,
|
|
46
|
-
};
|
|
25
|
+
const Navigation = ({ pathname }) => {
|
|
26
|
+
const [desktopMenuOpen, setDesktopMenuOpen] = useState(null);
|
|
27
|
+
const [currentOpenIndex, setCurrentOpenIndex] = useState(null);
|
|
28
|
+
const navigation = useRef(null);
|
|
29
|
+
const dispatch = useDispatch();
|
|
30
|
+
const intl = useIntl();
|
|
31
|
+
const enableFatMenu = config.settings.enableFatMenu;
|
|
47
32
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
33
|
+
const lang = useSelector((state) => state.intl.locale);
|
|
34
|
+
const token = useSelector((state) => state.userSession.token, shallowEqual);
|
|
35
|
+
const items = useSelector((state) => state.navigation.items, shallowEqual);
|
|
51
36
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*/
|
|
58
|
-
constructor(props) {
|
|
59
|
-
super(props);
|
|
60
|
-
this.toggleMobileMenu = this.toggleMobileMenu.bind(this);
|
|
61
|
-
this.closeMobileMenu = this.closeMobileMenu.bind(this);
|
|
62
|
-
this.state = {
|
|
63
|
-
isMobileMenuOpen: false,
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const handleClickOutside = (e) => {
|
|
39
|
+
if (navigation.current && doesNodeContainClick(navigation.current, e))
|
|
40
|
+
return;
|
|
41
|
+
closeMenu();
|
|
64
42
|
};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Component will receive props
|
|
79
|
-
* @method componentWillReceiveProps
|
|
80
|
-
* @param {Object} nextProps Next properties
|
|
81
|
-
* @returns {undefined}
|
|
82
|
-
*/
|
|
83
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
84
|
-
const { settings } = config;
|
|
85
|
-
if (
|
|
86
|
-
nextProps.pathname !== this.props.pathname ||
|
|
87
|
-
nextProps.token !== this.props.token
|
|
88
|
-
) {
|
|
89
|
-
if (!hasApiExpander('navigation', getBaseUrl(this.props.pathname))) {
|
|
90
|
-
this.props.getNavigation(
|
|
91
|
-
getBaseUrl(nextProps.pathname),
|
|
92
|
-
settings.navDepth,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Toggle mobile menu's open state
|
|
100
|
-
* @method toggleMobileMenu
|
|
101
|
-
* @returns {undefined}
|
|
102
|
-
*/
|
|
103
|
-
toggleMobileMenu() {
|
|
104
|
-
this.setState({ isMobileMenuOpen: !this.state.isMobileMenuOpen });
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Close mobile menu
|
|
109
|
-
* @method closeMobileMenu
|
|
110
|
-
* @returns {undefined}
|
|
111
|
-
*/
|
|
112
|
-
closeMobileMenu(e) {
|
|
113
|
-
if (!this.state.isMobileMenuOpen) {
|
|
114
|
-
return;
|
|
43
|
+
|
|
44
|
+
document.addEventListener('mousedown', handleClickOutside, false);
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
document.removeEventListener('mousedown', handleClickOutside, false);
|
|
48
|
+
};
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!hasApiExpander('navigation', getBaseUrl(pathname))) {
|
|
53
|
+
dispatch(getNavigation(getBaseUrl(pathname), config.settings.navDepth));
|
|
115
54
|
}
|
|
116
|
-
|
|
117
|
-
|
|
55
|
+
}, [pathname, token, dispatch]);
|
|
56
|
+
|
|
57
|
+
const isActive = (url) => {
|
|
58
|
+
return (url === '' && pathname === '/') || (url !== '' && pathname === url);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const openMenu = (index) => {
|
|
62
|
+
if (index === currentOpenIndex) {
|
|
63
|
+
setDesktopMenuOpen(null);
|
|
64
|
+
setCurrentOpenIndex(null);
|
|
65
|
+
} else {
|
|
66
|
+
setDesktopMenuOpen(index);
|
|
67
|
+
setCurrentOpenIndex(index);
|
|
118
68
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const closeMenu = (index) => {
|
|
72
|
+
setDesktopMenuOpen(null);
|
|
73
|
+
setCurrentOpenIndex(null);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const handleEsc = (event) => {
|
|
78
|
+
if (event.keyCode === 27) {
|
|
79
|
+
closeMenu();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
window.addEventListener('keydown', handleEsc);
|
|
83
|
+
|
|
84
|
+
return () => {
|
|
85
|
+
window.removeEventListener('keydown', handleEsc);
|
|
86
|
+
};
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<nav
|
|
91
|
+
id="navigation"
|
|
92
|
+
aria-label="navigation"
|
|
93
|
+
className="navigation"
|
|
94
|
+
ref={navigation}
|
|
95
|
+
>
|
|
96
|
+
<div className={'computer large screen widescreen only'}>
|
|
97
|
+
<ul className="desktop-menu">
|
|
98
|
+
{items.map((item, index) => (
|
|
99
|
+
<li key={item.url}>
|
|
100
|
+
{enableFatMenu ? (
|
|
101
|
+
<>
|
|
102
|
+
<button
|
|
103
|
+
onClick={() => openMenu(index)}
|
|
104
|
+
className={cx('item', {
|
|
105
|
+
active:
|
|
106
|
+
desktopMenuOpen === index ||
|
|
107
|
+
(!desktopMenuOpen && pathname === item.url),
|
|
108
|
+
})}
|
|
109
|
+
>
|
|
110
|
+
{item.title}
|
|
111
|
+
</button>
|
|
112
|
+
|
|
113
|
+
<div className="submenu-wrapper">
|
|
114
|
+
<div
|
|
115
|
+
className={cx('submenu', {
|
|
116
|
+
active: desktopMenuOpen === index,
|
|
117
|
+
})}
|
|
118
|
+
>
|
|
119
|
+
<div className="submenu-inner">
|
|
120
|
+
<NavLink
|
|
121
|
+
to={item.url === '' ? '/' : item.url}
|
|
122
|
+
onClick={() => closeMenu()}
|
|
123
|
+
className="submenu-header"
|
|
124
|
+
>
|
|
125
|
+
<h2>{item.nav_title ?? item.title}</h2>
|
|
126
|
+
</NavLink>
|
|
127
|
+
<button
|
|
128
|
+
className="close"
|
|
129
|
+
onClick={closeMenu}
|
|
130
|
+
aria-label={intl.formatMessage(messages.closeMenu)}
|
|
131
|
+
>
|
|
132
|
+
<Icon name={clearSVG} size="48px" />
|
|
133
|
+
</button>
|
|
134
|
+
<ul>
|
|
135
|
+
{item.items &&
|
|
136
|
+
item.items.length > 0 &&
|
|
137
|
+
item.items.map((subitem) => (
|
|
138
|
+
<div
|
|
139
|
+
className="subitem-wrapper"
|
|
140
|
+
key={subitem.url}
|
|
141
|
+
>
|
|
142
|
+
<li key={subitem.url}>
|
|
143
|
+
<NavLink
|
|
144
|
+
to={subitem.url}
|
|
145
|
+
onClick={() => closeMenu()}
|
|
146
|
+
className={cx({
|
|
147
|
+
current: isActive(subitem.url),
|
|
148
|
+
})}
|
|
149
|
+
>
|
|
150
|
+
<span className="left-arrow">—</span>
|
|
151
|
+
<span>
|
|
152
|
+
{subitem.nav_title || subitem.title}
|
|
153
|
+
</span>
|
|
154
|
+
</NavLink>
|
|
155
|
+
</li>
|
|
156
|
+
<div className="sub-submenu">
|
|
157
|
+
<ul>
|
|
158
|
+
{subitem.items &&
|
|
159
|
+
subitem.items.length > 0 &&
|
|
160
|
+
subitem.items.map((subsubitem) => (
|
|
161
|
+
<div
|
|
162
|
+
className="subsubitem-wrapper"
|
|
163
|
+
key={subsubitem.url}
|
|
164
|
+
>
|
|
165
|
+
<li key={subsubitem.url}>
|
|
166
|
+
<NavLink
|
|
167
|
+
to={subsubitem.url}
|
|
168
|
+
onClick={() => closeMenu()}
|
|
169
|
+
className={cx({
|
|
170
|
+
current: isActive(
|
|
171
|
+
subsubitem.url,
|
|
172
|
+
),
|
|
173
|
+
})}
|
|
174
|
+
>
|
|
175
|
+
<span className="left-arrow">
|
|
176
|
+
—
|
|
177
|
+
</span>
|
|
178
|
+
|
|
179
|
+
<span>
|
|
180
|
+
{subsubitem.nav_title ||
|
|
181
|
+
subsubitem.title}
|
|
182
|
+
</span>
|
|
183
|
+
</NavLink>
|
|
184
|
+
</li>
|
|
185
|
+
</div>
|
|
186
|
+
))}
|
|
187
|
+
</ul>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
))}
|
|
191
|
+
</ul>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</>
|
|
196
|
+
) : (
|
|
197
|
+
<NavItem item={item} lang={lang} key={item.url} />
|
|
198
|
+
)}
|
|
199
|
+
</li>
|
|
200
|
+
))}
|
|
201
|
+
</ul>
|
|
202
|
+
</div>
|
|
203
|
+
</nav>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
Navigation.propTypes = {
|
|
208
|
+
pathname: PropTypes.string.isRequired,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
Navigation.defaultProps = {
|
|
212
|
+
token: null,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export default injectIntl(Navigation);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36">
|
|
2
|
+
<g fill-rule="evenodd">
|
|
3
|
+
<polygon points="29 23 29 21 21 21 21 29 23 29 23 24.414 29.293 30.707 30.707 29.293 24.414 23"/>
|
|
4
|
+
<polygon points="13 11.586 6.707 5.293 5.293 6.707 11.586 13 7 13 7 15 15 15 15 7 13 7"/>
|
|
5
|
+
<polygon points="29 13 24.414 13 30.707 6.707 29.293 5.293 23 11.586 23 7 21 7 21 15 29 15"/>
|
|
6
|
+
<polygon points="7 23 11.586 23 5.293 29.293 6.707 30.707 13 24.414 13 29 15 29 15 21 7 21"/>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g clip-path="url(#clip0_5704_67)" transform="matrix(0.72424773,0,0,0.72424773,-0.006815,0.49090202)">
|
|
4
|
+
<path d="m 11.5,11 2.9886,11.1536 8.1649,-8.165 z M 25,24.5 25.7071,25.2071 26.4142,24.5 25.7071,23.7929 Z M 11.5,38 22.6535,35.0114 14.4886,26.8464 Z m 5.6569,-19.9289 7.136,7.136 1.4142,-1.4142 -7.136,-7.136 z m 7.136,5.7218 -7.136,7.136 1.4142,1.4142 7.136,-7.136 z"/>
|
|
5
|
+
<path d="m 38.391,11 -2.9886,11.1536 -8.165,-8.165 z m -13.5,13.5 -0.7071,0.7071 -0.7071,-0.7071 0.7071,-0.7071 z m 13.5,13.5 -11.1536,-2.9886 8.165,-8.165 z m -5.6569,-19.9289 -7.136,7.136 -1.4142,-1.4142 7.136,-7.136 z m -7.136,5.7218 7.136,7.136 -1.4142,1.4142 -7.136,-7.136 z"/>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M 29.100048,0.43270825 7.8737003,18.004379 29.100048,35.576057" fill="none" stroke="#000000" stroke-width="1.2"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path d="M 7.2996057,0.43270834 28.525973,18.004385 7.2996057,35.576069" fill="none" stroke="#000000" stroke-width="1.2"/>
|
|
4
|
+
</svg>
|
package/src/index.js
CHANGED
|
@@ -51,7 +51,12 @@ defineMessages({
|
|
|
51
51
|
const applyConfig = (config) => {
|
|
52
52
|
config.settings.enableAutoBlockGroupingByBackgroundColor = true;
|
|
53
53
|
config.settings.navDepth = 3;
|
|
54
|
+
config.settings.enableFatMenu = true;
|
|
54
55
|
config.settings.slate.useLinkedHeadings = false;
|
|
56
|
+
config.settings.contentMetadataTagsImageField = 'preview_image';
|
|
57
|
+
|
|
58
|
+
// Remove Hero Block
|
|
59
|
+
config.blocks.blocksConfig.hero.restricted = true;
|
|
55
60
|
|
|
56
61
|
// No required blocks (eg. Title)
|
|
57
62
|
config.blocks.requiredBlocks = [];
|
|
@@ -126,6 +131,7 @@ const applyConfig = (config) => {
|
|
|
126
131
|
];
|
|
127
132
|
|
|
128
133
|
config.settings.slidingSearchAnimation = true;
|
|
134
|
+
config.settings.openExternalLinkInNewTab = true;
|
|
129
135
|
|
|
130
136
|
config.blocks.blocksConfig.accordion = {
|
|
131
137
|
...config.blocks.blocksConfig.accordion,
|
|
@@ -291,7 +297,7 @@ const applyConfig = (config) => {
|
|
|
291
297
|
};
|
|
292
298
|
|
|
293
299
|
// Check if the separator is present before enhancing it
|
|
294
|
-
if (config.blocks.blocksConfig
|
|
300
|
+
if (config.blocks.blocksConfig?.separator?.id) {
|
|
295
301
|
config.blocks.blocksConfig.separator = {
|
|
296
302
|
...config.blocks.blocksConfig.separator,
|
|
297
303
|
schemaEnhancer: composeSchema(
|