@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.
Files changed (43) hide show
  1. package/.github/workflows/acceptance.yml +3 -2
  2. package/.github/workflows/changelog.yml +1 -1
  3. package/.github/workflows/code.yml +3 -2
  4. package/.github/workflows/deploy.yml +1 -1
  5. package/.github/workflows/unit.yml +3 -2
  6. package/CHANGELOG.md +56 -0
  7. package/Makefile +2 -2
  8. package/README.md +12 -1
  9. package/acceptance/cypress/tests/basic.cy.js +1 -1
  10. package/acceptance/cypress/tests/nav.cy.js +60 -0
  11. package/locales/de/LC_MESSAGES/volto.po +43 -1
  12. package/locales/en/LC_MESSAGES/volto.po +48 -1
  13. package/locales/pt_BR/volto.po +317 -0
  14. package/locales/volto.pot +49 -2
  15. package/package.json +3 -3
  16. package/src/components/Blocks/Listing/ImageGallery.jsx +126 -0
  17. package/src/components/Header/Header.jsx +3 -1
  18. package/src/components/MobileNavigation/MobileNavigation.jsx +334 -0
  19. package/src/components/MobileNavigation/MobileToolsFooter.jsx +10 -0
  20. package/src/components/Navigation/Navigation.jsx +198 -182
  21. package/src/customizations/volto/components/manage/Blocks/Listing/ImageGallery.jsx +8 -0
  22. package/src/icons/back-down.svg +8 -0
  23. package/src/icons/fullscreen.svg +7 -0
  24. package/src/icons/left-key.svg +4 -0
  25. package/src/icons/pause.svg +6 -0
  26. package/src/icons/play.svg +4 -0
  27. package/src/icons/right-key.svg +4 -0
  28. package/src/index.js +7 -1
  29. package/src/theme/_content.scss +1 -1
  30. package/src/theme/_fonts.scss +317 -0
  31. package/src/theme/_header.scss +610 -8
  32. package/src/theme/_layout.scss +7 -24
  33. package/src/theme/_sitemap.scss +25 -0
  34. package/src/theme/_typo-custom.scss +0 -73
  35. package/src/theme/_typography.scss +1 -245
  36. package/src/theme/_variables.scss +14 -0
  37. package/src/theme/blocks/_button.scss +6 -0
  38. package/src/theme/blocks/_grid.scss +8 -0
  39. package/src/theme/blocks/_image.scss +1 -1
  40. package/src/theme/blocks/_listing.scss +111 -5
  41. package/src/theme/blocks/_maps.scss +4 -0
  42. package/src/theme/blocks/_teaser.scss +1 -4
  43. package/src/theme/main.scss +5 -1
@@ -1,199 +1,215 @@
1
1
  // SemanticUI-free pre-@plone/components
2
- import React, { Component } from 'react';
2
+
3
+ import React, { useState, useEffect, useRef } from 'react';
3
4
  import PropTypes from 'prop-types';
4
- import { connect } from 'react-redux';
5
- import { compose } from 'redux';
6
- import { defineMessages, injectIntl } from 'react-intl';
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 { BodyClass, getBaseUrl, hasApiExpander } from '@plone/volto/helpers';
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 { CSSTransition } from 'react-transition-group';
12
- import NavItems from '@plone/volto/components/theme/Navigation/NavItems';
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
- closeMobileMenu: {
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
- * Navigation container class.
27
- * @class Navigation
28
- * @extends Component
29
- */
30
- class Navigation extends Component {
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
- static defaultProps = {
49
- token: null,
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
- * Constructor
54
- * @method constructor
55
- * @param {Object} props Component properties
56
- * @constructs Navigation
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
- componentDidMount() {
68
- const { settings } = config;
69
- if (!hasApiExpander('navigation', getBaseUrl(this.props.pathname))) {
70
- this.props.getNavigation(
71
- getBaseUrl(this.props.pathname),
72
- settings.navDepth,
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
- if (e.key && e.key !== 'Enter') {
117
- return;
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
- this.setState({ isMobileMenuOpen: false });
120
- }
121
-
122
- /**
123
- * Render method.
124
- * @method render
125
- * @returns {string} Markup for the component.
126
- */
127
- render() {
128
- return (
129
- <nav className="navigation" id="navigation" aria-label="navigation">
130
- <div className="hamburger-wrapper mobile tablet only">
131
- <button
132
- className={cx('hamburger hamburger--spin', {
133
- 'is-active': this.state.isMobileMenuOpen,
134
- })}
135
- aria-label={
136
- this.state.isMobileMenuOpen
137
- ? this.props.intl.formatMessage(messages.closeMobileMenu, {
138
- type: this.props.type,
139
- })
140
- : this.props.intl.formatMessage(messages.openMobileMenu, {
141
- type: this.props.type,
142
- })
143
- }
144
- title={
145
- this.state.isMobileMenuOpen
146
- ? this.props.intl.formatMessage(messages.closeMobileMenu, {
147
- type: this.props.type,
148
- })
149
- : this.props.intl.formatMessage(messages.openMobileMenu, {
150
- type: this.props.type,
151
- })
152
- }
153
- type="button"
154
- onClick={this.toggleMobileMenu}
155
- >
156
- <span className="hamburger-box">
157
- <span className="hamburger-inner" />
158
- </span>
159
- </button>
160
- </div>
161
- <div className="desktop menu computer large screen widescreen only">
162
- <NavItems items={this.props.items} lang={this.props.lang} />
163
- </div>
164
- <CSSTransition
165
- in={this.state.isMobileMenuOpen}
166
- timeout={500}
167
- classNames="mobile-menu"
168
- unmountOnExit
169
- >
170
- <div key="mobile-menu-key" className="mobile-menu">
171
- <BodyClass className="has-mobile-menu-open" />
172
- <div className="mobile-menu-nav">
173
- <div
174
- role="button"
175
- tabIndex={0}
176
- onClick={this.closeMobileMenu}
177
- onKeyUp={this.closeMobileMenu}
178
- >
179
- <NavItems items={this.props.items} lang={this.props.lang} />
180
- </div>
181
- </div>
182
- </div>
183
- </CSSTransition>
184
- </nav>
185
- );
186
- }
187
- }
188
-
189
- export default compose(
190
- injectIntl,
191
- connect(
192
- (state) => ({
193
- token: state.userSession.token,
194
- items: state.navigation.items,
195
- lang: state.intl.locale,
196
- }),
197
- { getNavigation },
198
- ),
199
- )(Navigation);
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">&#8212;</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
+ &#8212;
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
+ /**
2
+ * OVERRIDE ImageGallery.jsx
3
+ * REASON: To implement Title, Description and Copyright info.
4
+ */
5
+
6
+ import ImageGallery from '../../../../../../components/Blocks/Listing/ImageGallery';
7
+
8
+ export default ImageGallery;
@@ -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,6 @@
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 fill-rule="evenodd">
4
+ <path d="m 19,27 h 6 V 9 h -6 z m -8,0 h 6 V 9 h -6 z"/>
5
+ </g>
6
+ </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 24.711864,18.593219 12.521032,9.1928375 V 27.993602 Z"/>
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.separator.id) {
300
+ if (config.blocks.blocksConfig?.separator?.id) {
295
301
  config.blocks.blocksConfig.separator = {
296
302
  ...config.blocks.blocksConfig.separator,
297
303
  schemaEnhancer: composeSchema(
@@ -173,7 +173,7 @@
173
173
  .content-area {
174
174
  figure {
175
175
  img {
176
- aspect-ratio: $aspect-ratio;
176
+ object-fit: contain;
177
177
  }
178
178
  figcaption {
179
179
  margin-top: $spacing-small;