volto-dropdownmenu 2.2.8 → 2.4.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.
@@ -32,7 +32,7 @@ const DropdownMenu = ({ menu, open = false, closeMenu }) => {
32
32
  const location = useLocation();
33
33
  const blocksFieldname = getBlocksFieldname(menu);
34
34
  const blocksLayoutFieldname = getBlocksLayoutFieldname(menu);
35
-
35
+ const { clickableNavigationRoots = true } = menu;
36
36
  const navItemWidth = menu.navigationRoot?.length > 1 ? 3 : 4;
37
37
  const blocksWidth =
38
38
  menu.navigationRoot?.length === 1
@@ -40,6 +40,13 @@ const DropdownMenu = ({ menu, open = false, closeMenu }) => {
40
40
  : menu.navigationRoot?.length > 2 || menu.navigationRoot?.length === 0
41
41
  ? 12
42
42
  : 6;
43
+ let hasBlocks = hasBlocksData(menu);
44
+ if (menu?.blocks && Object.keys(menu.blocks).length === 1) {
45
+ let b = menu.blocks[Object.keys(menu.blocks)[0]];
46
+ if (b['@type'] === 'text' && (!b.text || b.text?.length === 0)) {
47
+ hasBlocks = false;
48
+ }
49
+ }
43
50
 
44
51
  return (
45
52
  <div
@@ -69,7 +76,10 @@ const DropdownMenu = ({ menu, open = false, closeMenu }) => {
69
76
  <h2>
70
77
  <ConditionalLink
71
78
  to={flattenToAppURL(navRoot['@id'])}
72
- condition={menu.navigationRoot?.length > 1}
79
+ condition={
80
+ menu.navigationRoot?.length > 1 &&
81
+ clickableNavigationRoots
82
+ }
73
83
  >
74
84
  <span>{navRoot.title}</span>
75
85
  </ConditionalLink>
@@ -87,7 +97,7 @@ const DropdownMenu = ({ menu, open = false, closeMenu }) => {
87
97
  )}
88
98
  </Grid.Column>
89
99
  ))}
90
- {hasBlocksData(menu) && (
100
+ {hasBlocks && (
91
101
  <Grid.Column
92
102
  width={blocksWidth}
93
103
  className="dropdownmenu-blocks-column"
@@ -50,6 +50,11 @@ const Navigation = ({ pathname, type }) => {
50
50
  dispatch(getDropdownMenuNavitems());
51
51
  }, [dispatch, token]);
52
52
 
53
+ useEffect(() => {
54
+ setMobileMenuOpen(false);
55
+ setOpenDropodownIndex(-1);
56
+ }, [pathname]);
57
+
53
58
  const getAnchorTarget = (nodeElement) => {
54
59
  if (nodeElement.nodeName === 'A') {
55
60
  return nodeElement;
package/src/index.js CHANGED
@@ -40,5 +40,11 @@ export default (config) => {
40
40
 
41
41
  config.settings.controlPanelsIcons['dropdown-menu-settings'] = menuSVG;
42
42
 
43
+ config.settings['volto-dropdownmenu'] = {
44
+ options: {
45
+ clickableNavigationRoots: false, //if true, a checkbox option in dropdown menu appears
46
+ },
47
+ };
48
+
43
49
  return config;
44
50
  };
@@ -76,6 +76,10 @@ const messages = defineMessages({
76
76
  id: 'dropdownmenu-deletemenuitem-button',
77
77
  defaultMessage: 'Delete menu item',
78
78
  },
79
+ clickableNavigationRoots: {
80
+ id: 'dropdownmenu-clickableNavigationRoots',
81
+ defaultMessage: 'Clickable navigation roots',
82
+ },
79
83
  });
80
84
 
81
85
  const MenuConfigurationForm = ({ id, menuItem, onChange, deleteMenuItem }) => {
@@ -117,11 +121,12 @@ const MenuConfigurationForm = ({ id, menuItem, onChange, deleteMenuItem }) => {
117
121
  return () => {
118
122
  document
119
123
  .querySelector('form.ui.form')
120
- .removeEventListener('click', preventClick);
124
+ ?.removeEventListener('click', preventClick);
121
125
  document.querySelectorAll('form.ui.form input').forEach((item) => {
122
- item.removeEventListener('keypress', preventEnter);
126
+ item?.removeEventListener('keypress', preventEnter);
123
127
  });
124
128
  };
129
+ // eslint-disable-next-line react-hooks/exhaustive-deps
125
130
  }, []);
126
131
 
127
132
  const onChangeFormData = (id, value) => {
@@ -201,6 +206,22 @@ const MenuConfigurationForm = ({ id, menuItem, onChange, deleteMenuItem }) => {
201
206
  }
202
207
  />
203
208
  </div>
209
+
210
+ {config.settings?.['volto-dropdownmenu']?.options
211
+ ?.clickableNavigationRoots && (
212
+ <div className="menu-item-field-clickableNavigationRoots">
213
+ <CheckboxWidget
214
+ id={`${id}-clickableNavigationRoots`}
215
+ title={intl.formatMessage(messages.clickableNavigationRoots)}
216
+ description=""
217
+ defaultValue={true}
218
+ value={!!menuItem.clickableNavigationRoots}
219
+ onChange={(id, value) =>
220
+ onChangeFormData('clickableNavigationRoots', value)
221
+ }
222
+ />
223
+ </div>
224
+ )}
204
225
  <div className="menu-item-field-showMoreLink">
205
226
  <ObjectBrowserWidget
206
227
  id={`${id}-showMoreLink`}
@@ -233,7 +254,6 @@ const MenuConfigurationForm = ({ id, menuItem, onChange, deleteMenuItem }) => {
233
254
  }
234
255
  />
235
256
  </div>
236
-
237
257
  <UIForm.Field inline className="help wide" id="menu-blocks">
238
258
  <Grid>
239
259
  <Grid.Row stretched>
package/src/i18n.js DELETED
@@ -1,183 +0,0 @@
1
- /* eslint no-console: 0 */
2
- /**
3
- * i18n script.
4
- * @module scripts/i18n
5
- */
6
-
7
- const { find, keys, map, concat, reduce } = require('lodash');
8
- const glob = require('glob').sync;
9
- const fs = require('fs');
10
- const Pofile = require('pofile');
11
- const babel = require('@babel/core');
12
-
13
- /**
14
- * Extract messages into separate JSON files
15
- * @function extractMessages
16
- * @return {undefined}
17
- */
18
- function extractMessages() {
19
- map(glob('src/**/*.js?(x)'), (filename) => {
20
- babel.transformFileSync(filename, {}, (err) => {
21
- if (err) {
22
- console.log(err);
23
- }
24
- });
25
- });
26
- }
27
-
28
- /**
29
- * Get messages from separate JSON files
30
- * @function getMessages
31
- * @return {Object} Object with messages
32
- */
33
- function getMessages() {
34
- return reduce(
35
- concat(
36
- {},
37
- ...map(
38
- // We ignore the existing customized shadowed components ones, since most
39
- // probably we won't be overriding them
40
- // If so, we should do it in the config object or somewhere else
41
- glob(
42
- 'build/messages/src/**/*.json',
43
- // , {
44
- // ignore: 'build/messages/src/customizations/**',
45
- // }
46
- ),
47
- (filename) =>
48
- map(JSON.parse(fs.readFileSync(filename, 'utf8')), (message) => ({
49
- ...message,
50
- filename: filename.match(/build\/messages\/src\/(.*).json$/)[1],
51
- })),
52
- ),
53
- ),
54
- (current, value) => {
55
- let result = current;
56
- if (current.id) {
57
- result = {
58
- [current.id]: {
59
- defaultMessage: current.defaultMessage,
60
- filenames: [current.filename],
61
- },
62
- };
63
- }
64
-
65
- if (result[value.id]) {
66
- result[value.id].filenames.push(value.filename);
67
- } else {
68
- result[value.id] = {
69
- defaultMessage: value.defaultMessage,
70
- filenames: [value.filename],
71
- };
72
- }
73
- return result;
74
- },
75
- );
76
- }
77
-
78
- /**
79
- * Convert messages to pot format
80
- * @function messagesToPot
81
- * @param {Object} messages Messages
82
- * @return {string} Formatted pot string
83
- */
84
- function messagesToPot(messages) {
85
- return map(keys(messages).sort(), (key) =>
86
- [
87
- ...map(messages[key].filenames, (filename) => `#: ${filename}`),
88
- `# defaultMessage: ${messages[key].defaultMessage}`,
89
- `msgid "${key}"`,
90
- 'msgstr ""',
91
- ].join('\n'),
92
- ).join('\n\n');
93
- }
94
-
95
- /**
96
- * Pot header
97
- * @function potHeader
98
- * @return {string} Formatted pot header
99
- */
100
- function potHeader() {
101
- return `msgid ""
102
- msgstr ""
103
- "Project-Id-Version: Plone\\n"
104
- "POT-Creation-Date: ${new Date().toISOString()}\\n"
105
- "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\\n"
106
- "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\\n"
107
- "MIME-Version: 1.0\\n"
108
- "Content-Type: text/plain; charset=utf-8\\n"
109
- "Content-Transfer-Encoding: 8bit\\n"
110
- "Plural-Forms: nplurals=1; plural=0;\\n"
111
- "Language-Code: en\\n"
112
- "Language-Name: English\\n"
113
- "Preferred-Encodings: utf-8\\n"
114
- "Domain: volto\\n"
115
-
116
- `;
117
- }
118
-
119
- /**
120
- * Format header
121
- * @function formatHeader
122
- * @param {Array} comments Array of comments
123
- * @param {Object} headers Object of header items
124
- * @return {string} Formatted header
125
- */
126
- function formatHeader(comments, headers) {
127
- return [
128
- ...map(comments, (comment) => `# ${comment}`),
129
- 'msgid ""',
130
- 'msgstr ""',
131
- ...map(keys(headers), (key) => `"${key}: ${headers[key]}\\n"`),
132
- '',
133
- ].join('\n');
134
- }
135
-
136
- /**
137
- * Sync po by the pot file
138
- * @function syncPoByPot
139
- * @return {undefined}
140
- */
141
- function syncPoByPot() {
142
- const pot = Pofile.parse(fs.readFileSync('locales/volto.pot', 'utf8'));
143
-
144
- map(glob('locales/**/*.po'), (filename) => {
145
- const po = Pofile.parse(fs.readFileSync(filename, 'utf8'));
146
-
147
- fs.writeFileSync(
148
- filename,
149
- `${formatHeader(po.comments, po.headers)}
150
- ${map(pot.items, (item) => {
151
- const poItem = find(po.items, { msgid: item.msgid });
152
- return [
153
- `${map(item.references, (ref) => `#: ${ref}`).join('\n')}`,
154
- `msgid "${item.msgid}"`,
155
- `msgstr "${poItem ? poItem.msgstr : ''}"`,
156
- ].join('\n');
157
- }).join('\n\n')}\n`,
158
- );
159
- });
160
- }
161
-
162
- // Main tasks
163
- console.log('Extracting messages from source files...');
164
- extractMessages();
165
- console.log('Synchronizing messages to pot file...');
166
- // We only write the pot file if it's really different
167
- const newPot = `${potHeader()}${messagesToPot(getMessages())}\n`.replace(
168
- /"POT-Creation-Date:(.*)\\n"/,
169
- '',
170
- );
171
- const oldPot = fs
172
- .readFileSync('locales/volto.pot', 'utf8')
173
- .replace(/"POT-Creation-Date:(.*)\\n"/, '');
174
-
175
- if (newPot !== oldPot) {
176
- fs.writeFileSync(
177
- 'locales/volto.pot',
178
- `${potHeader()}${messagesToPot(getMessages())}\n`,
179
- );
180
- }
181
- console.log('Synchronizing messages to po files...');
182
- syncPoByPot();
183
- console.log('done!');