@kitconcept/volto-light-theme 2.0.0 → 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.
@@ -1,16 +1,15 @@
1
- import React from 'react';
2
- import { defineMessages, useIntl } from 'react-intl';
1
+ import React, { useCallback } from 'react';
2
+ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
3
3
  import { useSelector } from 'react-redux';
4
4
  import { Link, useHistory } from 'react-router-dom';
5
5
  import cx from 'classnames';
6
6
 
7
7
  import { CSSTransition } from 'react-transition-group';
8
- import { FormattedMessage } from 'react-intl';
9
8
 
10
- import { BodyClass } from '@plone/volto/helpers';
11
- import { Icon, LanguageSelector } from '@plone/volto/components';
9
+ import { Icon } from '@plone/volto/components';
12
10
  import arrowRightSVG from '@plone/volto/icons/right-key.svg';
13
11
  import arrowLeftSVG from '@plone/volto/icons/left-key.svg';
12
+ import { MobileToolsFooter } from './MobileToolsFooter';
14
13
 
15
14
  const messages = defineMessages({
16
15
  closeMobileMenu: {
@@ -24,79 +23,111 @@ const messages = defineMessages({
24
23
  });
25
24
 
26
25
  const MobileNavigation = (props) => {
27
- const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
28
- const [secondaryMenuOpened, setSecondaryMenuOpened] = React.useState(null);
29
- const [isSecondaryMobileMenuOpen, setIsSecondaryMobileMenuOpen] =
30
- React.useState(false);
31
- const [tertiaryMenuOpened, setTertiaryMenuOpened] = React.useState(null);
32
- const [isTertiaryMobileMenuOpen, setIsTertiaryMobileMenuOpen] =
33
- React.useState(false);
26
+ const [menuState, setMenuState] = React.useState({
27
+ isMobileMenuOpen: false,
28
+ secondaryMenuOpened: null,
29
+ isSecondaryMobileMenuOpen: false,
30
+ tertiaryMenuOpened: null,
31
+ isTertiaryMobileMenuOpen: false,
32
+ });
33
+
34
+ const {
35
+ isMobileMenuOpen,
36
+ secondaryMenuOpened,
37
+ isSecondaryMobileMenuOpen,
38
+ tertiaryMenuOpened,
39
+ isTertiaryMobileMenuOpen,
40
+ } = menuState;
34
41
  const intl = useIntl();
35
42
  const menus = React.useRef(null);
36
43
  const currentLang = useSelector((state) => state.intl.locale);
37
44
  const items = useSelector((state) => state.navigation.items || []);
38
45
  const history = useHistory();
39
46
 
40
- const Footer = () => (
41
- <ul className="mobile-tools">
42
- <li>
43
- <LanguageSelector fullLabel={true} />
44
- </li>
45
- </ul>
46
- );
47
+ const Footer = props.MobileToolsFooter || MobileToolsFooter;
47
48
 
48
- function toggleMobileMenu() {
49
+ const toggleMobileMenu = useCallback(() => {
49
50
  const body = document.getElementsByTagName('body')[0];
50
- if (!isMobileMenuOpen) {
51
- body.style.position = 'fixed';
52
- body.style.width = '100%';
53
- body.style.top = '0px';
54
- body.style.left = '0px';
55
- } else {
56
- body.style = '';
57
- }
58
- setIsMobileMenuOpen(!isMobileMenuOpen);
51
+ body.classList.toggle('has-menu-open');
59
52
 
60
- if (isMobileMenuOpen) {
61
- setIsSecondaryMobileMenuOpen(false);
62
- setSecondaryMenuOpened(null);
63
- setIsTertiaryMobileMenuOpen(false);
64
- setTertiaryMenuOpened(null);
65
- }
66
- }
67
-
68
- function openSecondaryMenu(index) {
69
- setSecondaryMenuOpened(index);
70
- setIsSecondaryMobileMenuOpen(true);
71
- }
53
+ setMenuState((prevState) => ({
54
+ ...prevState,
55
+ isMobileMenuOpen: !prevState.isMobileMenuOpen,
56
+ }));
57
+ }, []);
72
58
 
73
- function closeSecondaryMenu(e) {
59
+ const openSecondaryMenu = useCallback((e, index) => {
74
60
  e.stopPropagation();
75
- setIsSecondaryMobileMenuOpen(false);
76
- setSecondaryMenuOpened(null);
77
- }
78
61
 
79
- function openTertiaryMenu(index) {
80
- setTertiaryMenuOpened(index);
81
- setIsTertiaryMobileMenuOpen(true);
82
- }
62
+ setMenuState((prevState) => ({
63
+ ...prevState,
64
+ secondaryMenuOpened: index,
65
+ isSecondaryMobileMenuOpen: true,
66
+ }));
67
+ }, []);
83
68
 
84
- function closeTertiaryMenu(e) {
69
+ const closeSecondaryMenu = useCallback((e) => {
85
70
  e.stopPropagation();
86
- setIsTertiaryMobileMenuOpen(false);
87
- setTertiaryMenuOpened(null);
88
- }
71
+ setMenuState((prevState) => ({
72
+ ...prevState,
73
+ isSecondaryMobileMenuOpen: false,
74
+ secondaryMenuOpened: null,
75
+ }));
76
+ }, []);
89
77
 
90
- function closeMenus(e) {
78
+ const openTertiaryMenu = useCallback((e, index) => {
91
79
  e.stopPropagation();
92
- setIsSecondaryMobileMenuOpen(false);
93
- setIsTertiaryMobileMenuOpen(false);
94
- setSecondaryMenuOpened(null);
95
- setTertiaryMenuOpened(null);
96
- setIsMobileMenuOpen(false);
97
- const body = document.getElementsByTagName('body')[0];
98
- body.style = '';
99
- }
80
+
81
+ setMenuState((prevState) => ({
82
+ ...prevState,
83
+ tertiaryMenuOpened: index,
84
+ isTertiaryMobileMenuOpen: true,
85
+ }));
86
+ }, []);
87
+
88
+ const closeTertiaryMenu = useCallback((e) => {
89
+ e.stopPropagation();
90
+
91
+ setMenuState((prevState) => ({
92
+ ...prevState,
93
+ isTertiaryMobileMenuOpen: false,
94
+ tertiaryMenuOpened: null,
95
+ }));
96
+ }, []);
97
+
98
+ const closeMenus = useCallback((e) => {
99
+ if (e && e.stopPropagation) {
100
+ e.stopPropagation();
101
+ }
102
+
103
+ setMenuState(() => ({
104
+ isSecondaryMobileMenuOpen: false,
105
+ isTertiaryMobileMenuOpen: false,
106
+ secondaryMenuOpened: null,
107
+ tertiaryMenuOpened: null,
108
+ isMobileMenuOpen: false,
109
+ }));
110
+ }, []);
111
+
112
+ const handleLinkClicked = useCallback(
113
+ (e, section, callback, index) => {
114
+ e.preventDefault();
115
+ if (section.items.length > 0) {
116
+ callback(e, index);
117
+ } else {
118
+ history.push(section.url);
119
+ return closeMenus(e);
120
+ }
121
+ },
122
+ [history, closeMenus],
123
+ );
124
+
125
+ React.useEffect(() => {
126
+ const closeMenuOnHistoryChange = history.listen(() => closeMenus({}));
127
+ return () => {
128
+ closeMenuOnHistoryChange();
129
+ };
130
+ }, [history, closeMenus]);
100
131
 
101
132
  return (
102
133
  <div className="mobile-nav mobile only tablet only" ref={menus}>
@@ -128,10 +159,8 @@ const MobileNavigation = (props) => {
128
159
  in={isMobileMenuOpen}
129
160
  timeout={500}
130
161
  classNames="menu-drawer"
131
- unmountOnExit
132
162
  >
133
163
  <div className="menu-drawer">
134
- <BodyClass className="has-menu-open" />
135
164
  <ul className="sections">
136
165
  <li className="header">
137
166
  <Link to={`/${currentLang}`} onClick={closeMenus}>
@@ -143,18 +172,17 @@ const MobileNavigation = (props) => {
143
172
  <li
144
173
  key={section.url}
145
174
  className={section.url === props.pathname ? 'current' : ''}
146
- onClick={(e) => {
147
- if (section.items.length > 0) {
148
- openSecondaryMenu(index);
149
- } else {
150
- history.push(section.url);
151
- return closeMenus(e);
152
- }
153
- }}
154
175
  role="presentation"
155
176
  >
156
- {section.nav_title || section.title}
157
- {section.items.length > 0 && <Icon name={arrowRightSVG} />}
177
+ <Link
178
+ to={section.url === '' ? '/' : section.url}
179
+ onClick={(e) =>
180
+ handleLinkClicked(e, section, openSecondaryMenu, index)
181
+ }
182
+ >
183
+ {section.nav_title || section.title}
184
+ {section.items.length > 0 && <Icon name={arrowRightSVG} />}
185
+ </Link>
158
186
  <CSSTransition
159
187
  in={
160
188
  isSecondaryMobileMenuOpen && secondaryMenuOpened === index
@@ -179,7 +207,7 @@ const MobileNavigation = (props) => {
179
207
  <li>
180
208
  <Link
181
209
  to={section.url === '' ? '/' : section.url}
182
- onClick={(e) => closeMenus(e)}
210
+ onClick={closeMenus}
183
211
  >
184
212
  <FormattedMessage
185
213
  id="Overview"
@@ -197,20 +225,26 @@ const MobileNavigation = (props) => {
197
225
  ? 'current'
198
226
  : ''
199
227
  }
200
- onClick={(e) => {
201
- if (subsection.items.length > 0) {
202
- openTertiaryMenu(index);
203
- } else {
204
- history.push(subsection.url);
205
- return closeMenus(e);
206
- }
207
- }}
208
228
  role="presentation"
209
229
  >
210
- {subsection.nav_title || subsection.title}
211
- {subsection.items.length > 0 && (
212
- <Icon name={arrowRightSVG} />
213
- )}
230
+ <Link
231
+ to={
232
+ subsection.url === '' ? '/' : subsection.url
233
+ }
234
+ onClick={(e) =>
235
+ handleLinkClicked(
236
+ e,
237
+ subsection,
238
+ openTertiaryMenu,
239
+ index,
240
+ )
241
+ }
242
+ >
243
+ {subsection.nav_title || subsection.title}
244
+ {subsection.items.length > 0 && (
245
+ <Icon name={arrowRightSVG} />
246
+ )}
247
+ </Link>
214
248
  <CSSTransition
215
249
  in={
216
250
  isTertiaryMobileMenuOpen &&
@@ -245,7 +279,7 @@ const MobileNavigation = (props) => {
245
279
  ? '/'
246
280
  : subsection.url
247
281
  }
248
- onClick={(e) => closeMenus(e)}
282
+ onClick={closeMenus}
249
283
  >
250
284
  <FormattedMessage
251
285
  id="Overview"
@@ -269,7 +303,7 @@ const MobileNavigation = (props) => {
269
303
  >
270
304
  <Link
271
305
  to={subsubsection.url}
272
- onClick={(e) => closeMenus(e)}
306
+ onClick={closeMenus}
273
307
  >
274
308
  {subsubsection.nav_title ||
275
309
  subsubsection.title}
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { LanguageSelector } from '@plone/volto/components';
3
+
4
+ export const MobileToolsFooter = () => (
5
+ <ul className="mobile-tools">
6
+ <li>
7
+ <LanguageSelector fullLabel={true} />
8
+ </li>
9
+ </ul>
10
+ );
@@ -2,9 +2,8 @@
2
2
 
3
3
  import React, { useState, useEffect, useRef } from 'react';
4
4
  import PropTypes from 'prop-types';
5
- import { connect } from 'react-redux';
6
- import { compose } from 'redux';
7
- import { NavLink, withRouter } from 'react-router-dom';
5
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
6
+ import { NavLink } from 'react-router-dom';
8
7
  import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib';
9
8
  import { useIntl, defineMessages, injectIntl } from 'react-intl';
10
9
  import cx from 'classnames';
@@ -17,19 +16,24 @@ import clearSVG from '@plone/volto/icons/clear.svg';
17
16
  import NavItem from '@plone/volto/components/theme/Navigation/NavItem';
18
17
 
19
18
  const messages = defineMessages({
20
- overview: {
21
- id: 'Overview',
22
- defaultMessage: 'Overview',
19
+ closeMenu: {
20
+ id: 'Close menu',
21
+ defaultMessage: 'Close menu',
23
22
  },
24
23
  });
25
24
 
26
- const Navigation = ({ getNavigation, pathname, items, lang }) => {
25
+ const Navigation = ({ pathname }) => {
27
26
  const [desktopMenuOpen, setDesktopMenuOpen] = useState(null);
28
27
  const [currentOpenIndex, setCurrentOpenIndex] = useState(null);
29
28
  const navigation = useRef(null);
29
+ const dispatch = useDispatch();
30
30
  const intl = useIntl();
31
31
  const enableFatMenu = config.settings.enableFatMenu;
32
32
 
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);
36
+
33
37
  useEffect(() => {
34
38
  const handleClickOutside = (e) => {
35
39
  if (navigation.current && doesNodeContainClick(navigation.current, e))
@@ -45,11 +49,10 @@ const Navigation = ({ getNavigation, pathname, items, lang }) => {
45
49
  }, []);
46
50
 
47
51
  useEffect(() => {
48
- const { settings } = config;
49
52
  if (!hasApiExpander('navigation', getBaseUrl(pathname))) {
50
- getNavigation(getBaseUrl(pathname), settings.navDepth);
53
+ dispatch(getNavigation(getBaseUrl(pathname), config.settings.navDepth));
51
54
  }
52
- }, [getNavigation, pathname]);
55
+ }, [pathname, token, dispatch]);
53
56
 
54
57
  const isActive = (url) => {
55
58
  return (url === '' && pathname === '/') || (url !== '' && pathname === url);
@@ -70,6 +73,19 @@ const Navigation = ({ getNavigation, pathname, items, lang }) => {
70
73
  setCurrentOpenIndex(null);
71
74
  };
72
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
+
73
89
  return (
74
90
  <nav
75
91
  id="navigation"
@@ -100,24 +116,21 @@ const Navigation = ({ getNavigation, pathname, items, lang }) => {
100
116
  active: desktopMenuOpen === index,
101
117
  })}
102
118
  >
103
- <div
104
- role="presentation"
105
- className="close"
106
- onClick={closeMenu}
107
- >
108
- <Icon name={clearSVG} size="48px" />
109
- </div>
110
119
  <div className="submenu-inner">
111
120
  <NavLink
112
121
  to={item.url === '' ? '/' : item.url}
113
122
  onClick={() => closeMenu()}
114
123
  className="submenu-header"
115
124
  >
116
- <h2>
117
- {item.nav_title ?? item.title} (
118
- {intl.formatMessage(messages.overview)})
119
- </h2>
125
+ <h2>{item.nav_title ?? item.title}</h2>
120
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>
121
134
  <ul>
122
135
  {item.items &&
123
136
  item.items.length > 0 &&
@@ -192,30 +205,11 @@ const Navigation = ({ getNavigation, pathname, items, lang }) => {
192
205
  };
193
206
 
194
207
  Navigation.propTypes = {
195
- getNavigation: PropTypes.func.isRequired,
196
208
  pathname: PropTypes.string.isRequired,
197
- items: PropTypes.arrayOf(
198
- PropTypes.shape({
199
- title: PropTypes.string,
200
- url: PropTypes.string,
201
- }),
202
- ).isRequired,
203
- lang: PropTypes.string.isRequired,
204
209
  };
205
210
 
206
211
  Navigation.defaultProps = {
207
212
  token: null,
208
213
  };
209
214
 
210
- export default compose(
211
- injectIntl,
212
- withRouter,
213
- connect(
214
- (state) => ({
215
- token: state.userSession.token,
216
- items: state.navigation.items,
217
- lang: state.intl.locale,
218
- }),
219
- { getNavigation },
220
- ),
221
- )(Navigation);
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
@@ -53,6 +53,10 @@ const applyConfig = (config) => {
53
53
  config.settings.navDepth = 3;
54
54
  config.settings.enableFatMenu = true;
55
55
  config.settings.slate.useLinkedHeadings = false;
56
+ config.settings.contentMetadataTagsImageField = 'preview_image';
57
+
58
+ // Remove Hero Block
59
+ config.blocks.blocksConfig.hero.restricted = true;
56
60
 
57
61
  // No required blocks (eg. Title)
58
62
  config.blocks.requiredBlocks = [];
@@ -127,6 +131,7 @@ const applyConfig = (config) => {
127
131
  ];
128
132
 
129
133
  config.settings.slidingSearchAnimation = true;
134
+ config.settings.openExternalLinkInNewTab = true;
130
135
 
131
136
  config.blocks.blocksConfig.accordion = {
132
137
  ...config.blocks.blocksConfig.accordion,
@@ -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;