design-comuni-plone-theme 10.0.0 → 10.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
 
2
2
 
3
+ ## [10.1.0](https://github.com/redturtle/design-comuni-plone-theme/compare/v10.0.0...v10.1.0) (2023-11-06)
4
+
5
+
6
+ ### Features
7
+
8
+ * Sidemenu customizable ([#388](https://github.com/redturtle/design-comuni-plone-theme/issues/388)) ([94fa0c1](https://github.com/redturtle/design-comuni-plone-theme/commit/94fa0c107f73acb9b7e89638f613206391cefc46))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * GalleryPreview ([29f0299](https://github.com/redturtle/design-comuni-plone-theme/commit/29f02990f1e7467e536f151c9e86821c1b0c9e98))
14
+ * locales ([14d268b](https://github.com/redturtle/design-comuni-plone-theme/commit/14d268be970d6fccce06c0c24f99c67745735fef))
15
+ * useSideMenu observer ([bd2e4ab](https://github.com/redturtle/design-comuni-plone-theme/commit/bd2e4ab451d4bc5a05b2673cdbf4a2b05038f4b4))
16
+
3
17
  ## [10.0.0](https://github.com/redturtle/design-comuni-plone-theme/compare/v8.9.0...v10.0.0) (2023-11-03)
4
18
 
5
19
 
@@ -164,6 +164,7 @@ msgid "Content in evidence"
164
164
  msgstr ""
165
165
 
166
166
  #: components/ItaliaTheme/View/Commons/SideMenu
167
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
167
168
  # defaultMessage: Contenuto
168
169
  msgid "Contenuto"
169
170
  msgstr ""
@@ -2214,6 +2215,7 @@ msgid "incarico"
2214
2215
  msgstr ""
2215
2216
 
2216
2217
  #: components/ItaliaTheme/View/Commons/SideMenu
2218
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
2217
2219
  # defaultMessage: Indice della pagina
2218
2220
  msgid "index"
2219
2221
  msgstr ""
@@ -149,6 +149,7 @@ msgid "Content in evidence"
149
149
  msgstr ""
150
150
 
151
151
  #: components/ItaliaTheme/View/Commons/SideMenu
152
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
152
153
  # defaultMessage: Contenuto
153
154
  msgid "Contenuto"
154
155
  msgstr "Content"
@@ -2199,6 +2200,7 @@ msgid "incarico"
2199
2200
  msgstr "assignment"
2200
2201
 
2201
2202
  #: components/ItaliaTheme/View/Commons/SideMenu
2203
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
2202
2204
  # defaultMessage: Indice della pagina
2203
2205
  msgid "index"
2204
2206
  msgstr "Page index"
@@ -158,6 +158,7 @@ msgid "Content in evidence"
158
158
  msgstr "Contenido destacado"
159
159
 
160
160
  #: components/ItaliaTheme/View/Commons/SideMenu
161
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
161
162
  # defaultMessage: Contenuto
162
163
  msgid "Contenuto"
163
164
  msgstr "Contenido"
@@ -2208,6 +2209,7 @@ msgid "incarico"
2208
2209
  msgstr "Compromiso"
2209
2210
 
2210
2211
  #: components/ItaliaTheme/View/Commons/SideMenu
2212
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
2211
2213
  # defaultMessage: Indice della pagina
2212
2214
  msgid "index"
2213
2215
  msgstr "Índice de página"
@@ -166,6 +166,7 @@ msgid "Content in evidence"
166
166
  msgstr ""
167
167
 
168
168
  #: components/ItaliaTheme/View/Commons/SideMenu
169
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
169
170
  # defaultMessage: Contenuto
170
171
  msgid "Contenuto"
171
172
  msgstr "Contenu"
@@ -2216,6 +2217,7 @@ msgid "incarico"
2216
2217
  msgstr "Engagement"
2217
2218
 
2218
2219
  #: components/ItaliaTheme/View/Commons/SideMenu
2220
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
2219
2221
  # defaultMessage: Indice della pagina
2220
2222
  msgid "index"
2221
2223
  msgstr "Index des pages"
@@ -149,6 +149,7 @@ msgid "Content in evidence"
149
149
  msgstr "Contenuto in primo piano"
150
150
 
151
151
  #: components/ItaliaTheme/View/Commons/SideMenu
152
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
152
153
  # defaultMessage: Contenuto
153
154
  msgid "Contenuto"
154
155
  msgstr "Contenuto"
@@ -2199,6 +2200,7 @@ msgid "incarico"
2199
2200
  msgstr "incarico"
2200
2201
 
2201
2202
  #: components/ItaliaTheme/View/Commons/SideMenu
2203
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
2202
2204
  # defaultMessage: Indice della pagina
2203
2205
  msgid "index"
2204
2206
  msgstr "Indice della pagina"
package/locales/volto.pot CHANGED
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: Plone\n"
4
- "POT-Creation-Date: 2023-11-03T10:53:24.029Z\n"
4
+ "POT-Creation-Date: 2023-11-06T09:06:10.531Z\n"
5
5
  "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
6
6
  "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
7
7
  "MIME-Version: 1.0\n"
@@ -151,6 +151,7 @@ msgid "Content in evidence"
151
151
  msgstr ""
152
152
 
153
153
  #: components/ItaliaTheme/View/Commons/SideMenu
154
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
154
155
  # defaultMessage: Contenuto
155
156
  msgid "Contenuto"
156
157
  msgstr ""
@@ -2201,6 +2202,7 @@ msgid "incarico"
2201
2202
  msgstr ""
2202
2203
 
2203
2204
  #: components/ItaliaTheme/View/Commons/SideMenu
2205
+ #: components/ItaliaTheme/View/Commons/SideMenuByTitles
2204
2206
  # defaultMessage: Indice della pagina
2205
2207
  msgid "index"
2206
2208
  msgstr ""
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "design-comuni-plone-theme",
3
3
  "description": "Volto Theme for Italia design guidelines",
4
4
  "license": "GPL-v3",
5
- "version": "10.0.0",
5
+ "version": "10.1.0",
6
6
  "main": "src/index.js",
7
7
  "keywords": [
8
8
  "volto-addon",
@@ -34,8 +34,8 @@ const GalleryPreview = ({ id, viewIndex, setViewIndex, items }) => {
34
34
  const [modalIsOpen, setModalIsOpen] = useState(false);
35
35
  const image = items[viewIndex];
36
36
 
37
- let checkUrlImage = image.image_field
38
- ? image.image_scales?.[image.image_field]?.[0]?.scales?.larger?.download
37
+ let checkUrlImage = image?.image_field
38
+ ? image?.image_scales?.[image?.image_field]?.[0]?.scales?.larger?.download
39
39
  : image?.image?.scales?.larger?.download ||
40
40
  image?.image_scales?.image[0]?.scales?.larger?.download;
41
41
 
@@ -3,11 +3,10 @@
3
3
  * @module components/theme/View/BandoView
4
4
  */
5
5
 
6
- import React, { useState, createRef, useEffect } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
 
9
9
  import {
10
- SideMenu,
11
10
  PageHeader,
12
11
  RelatedItems,
13
12
  BandoPlaceholderAfterContent,
@@ -22,6 +21,7 @@ import {
22
21
  RelatedItemInEvidence,
23
22
  SkipToMainContent,
24
23
  ContentTypeViewSections,
24
+ useSideMenu,
25
25
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
26
26
 
27
27
  export const BandoViewSectionsOrder = [
@@ -44,15 +44,8 @@ export const BandoViewSectionsOrder = [
44
44
  */
45
45
  const BandoView = ({ content, location }) => {
46
46
  let documentBody = createRef();
47
- const [sideMenuElements, setSideMenuElements] = useState(null);
47
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
48
48
 
49
- useEffect(() => {
50
- if (documentBody.current) {
51
- if (__CLIENT__) {
52
- setSideMenuElements(documentBody.current);
53
- }
54
- }
55
- }, [documentBody]);
56
49
  return (
57
50
  <>
58
51
  <div className="container px-4 my-4 bando-view">
@@ -1,3 +1,6 @@
1
+ /* ************
2
+ Costruisce il sidemenu in base alle sections strutturate nella parte destra del contenuto
3
+ **************/
1
4
  /* eslint-disable react-hooks/exhaustive-deps */
2
5
  /* eslint-disable jsx-a11y/anchor-is-valid */
3
6
  /* eslint-disable no-unused-expressions */
@@ -0,0 +1,220 @@
1
+ /* ****************
2
+ Costuisce il SideMenu utilizzando tutti gli h2 che trova nella parte di destra del contenuto.
3
+ ******************/
4
+ /* eslint-disable react-hooks/exhaustive-deps */
5
+ /* eslint-disable jsx-a11y/anchor-is-valid */
6
+ /* eslint-disable no-unused-expressions */
7
+ import { defineMessages, useIntl } from 'react-intl';
8
+ import React, { useEffect, useState, useCallback, useMemo } from 'react';
9
+ import { throttle } from 'lodash';
10
+ import {
11
+ Progress,
12
+ Accordion,
13
+ AccordionBody,
14
+ AccordionHeader,
15
+ } from 'design-react-kit';
16
+ import cx from 'classnames';
17
+
18
+ const messages = defineMessages({
19
+ index: {
20
+ id: 'index',
21
+ defaultMessage: 'Indice',
22
+ },
23
+ contenuto: {
24
+ id: 'Contenuto',
25
+ defaultMessage: 'Contenuto',
26
+ },
27
+ });
28
+
29
+ const extractHeaders = (elements, intl) => {
30
+ let item;
31
+ let headers = [];
32
+ for (var index = 0; index < elements.length; index++) {
33
+ item = elements[index];
34
+
35
+ if (item.id === 'text-body') {
36
+ headers.push({
37
+ id: item.id,
38
+ title:
39
+ item.getAttribute('menu_title') ||
40
+ intl.formatMessage(messages.contenuto),
41
+ item: item,
42
+ });
43
+
44
+ const h = item.getElementsByTagName('h2');
45
+
46
+ for (var hi = 0; hi < h.length; hi++) {
47
+ headers.push({
48
+ id: h[hi].id,
49
+ title: h[hi].innerText,
50
+ item: h[hi],
51
+ });
52
+ }
53
+ } else {
54
+ let item_header = item.querySelector('#header-' + item.id);
55
+ if (item_header) {
56
+ headers.push({
57
+ id: item.id,
58
+ title: item_header.textContent,
59
+ item: item,
60
+ });
61
+ }
62
+ }
63
+ }
64
+ return headers;
65
+ };
66
+
67
+ /**
68
+ * SideMenuByTitles view component class.
69
+ * @function SideMenuByTitles
70
+ * @params {object} content: Content object.
71
+ * @returns {string} Markup of the component.
72
+ */
73
+ const SideMenuByTitles = ({ data, content_uid }) => {
74
+ const intl = useIntl();
75
+
76
+ const [headers, setHeaders] = useState([]);
77
+ const [activeSection, setActiveSection] = useState(null);
78
+ const [scrollY, setScrollY] = useState(0);
79
+ const [isClient, setIsClient] = useState(false);
80
+
81
+ const [isNavOpen, setIsNavOpen] = useState(
82
+ __CLIENT__ ? window.innerWidth >= 992 : false,
83
+ );
84
+
85
+ useEffect(() => {
86
+ setIsClient(true);
87
+ }, []);
88
+
89
+ const handleScroll = useCallback(() => {
90
+ const scrollOffset = 0.1 * window.innerHeight;
91
+ setScrollY(window.scrollY);
92
+ const headersHeights = headers
93
+ .map((section) => {
94
+ const element = document.getElementById(section.id);
95
+ return {
96
+ id: section.id,
97
+ top: element?.getBoundingClientRect()?.top,
98
+ };
99
+ })
100
+ .filter((section) => section.top <= scrollOffset);
101
+ if (headersHeights.length > 0) {
102
+ const section = headersHeights.reduce(
103
+ (prev, curr) => (prev.top > curr.top ? prev : curr),
104
+ headers[0],
105
+ );
106
+ setActiveSection(section.id);
107
+ } else {
108
+ setActiveSection(null); //di default nessun header è selezionato.
109
+ }
110
+ }, [headers, activeSection]);
111
+
112
+ useEffect(() => {
113
+ if (data?.children) {
114
+ const extractedHeaders = extractHeaders(data.children, intl);
115
+
116
+ if (extractedHeaders.length > 0) {
117
+ setHeaders(extractedHeaders);
118
+ // setActiveSection(extractedHeaders[0].id); //di default nessun header è selezionato.
119
+ }
120
+ }
121
+ }, [data, content_uid]);
122
+
123
+ useEffect(() => {
124
+ if (headers.length > 0)
125
+ window.addEventListener('scroll', throttledHandleScroll, {
126
+ passive: true,
127
+ });
128
+
129
+ return () => {
130
+ window.removeEventListener('scroll', throttledHandleScroll);
131
+ };
132
+ }, [headers]);
133
+
134
+ const throttledHandleScroll = throttle(handleScroll, 100);
135
+
136
+ const handleClickAnchor = (id) => (e) => {
137
+ e.preventDefault();
138
+ if (window.innerWidth < 992) {
139
+ setIsNavOpen(false);
140
+ }
141
+ // Blur a link
142
+ document.getElementById(`item-${id}`).blur();
143
+ // Focus on section
144
+ document.getElementById(id).focus({ preventScroll: true });
145
+ // Scroll to section
146
+ // setTimeout hack should wait for rerender after setIsNavOpen
147
+ setTimeout(() => {
148
+ document.getElementById(id)?.scrollIntoView?.({
149
+ behavior: 'smooth',
150
+ block: 'start',
151
+ });
152
+ }, 0);
153
+ };
154
+
155
+ const yCountEnd = isClient
156
+ ? document.querySelector('#main-content-section')
157
+ : null;
158
+
159
+ const progressValue = useMemo(() => {
160
+ if (!isClient) return 0;
161
+ return (scrollY - yCountEnd.offsetTop) / yCountEnd.offsetHeight || 0;
162
+ }, [scrollY, isClient]);
163
+
164
+ return headers?.length > 0 ? (
165
+ <div className="sticky-wrapper navbar-wrapper page-side-menu">
166
+ <nav className="navbar it-navscroll-wrapper navbar-expand-lg">
167
+ <div className="menu-wrapper">
168
+ <div className="link-list-wrapper menu-link-list">
169
+ <div className="accordion-wrapper">
170
+ <Accordion>
171
+ <AccordionHeader
172
+ active={isNavOpen}
173
+ onToggle={() => {
174
+ setIsNavOpen(!isNavOpen);
175
+ }}
176
+ >
177
+ <h3>{intl.formatMessage(messages.index)}</h3>
178
+ </AccordionHeader>
179
+ <div className="mb-3">
180
+ <Progress
181
+ value={progressValue > 0 ? 100 * progressValue : 0}
182
+ role="progressbar"
183
+ />
184
+ </div>
185
+ <AccordionBody
186
+ active={isNavOpen}
187
+ className={
188
+ isNavOpen
189
+ ? 'accordion-collapse show'
190
+ : 'accordion-collapse collapse'
191
+ }
192
+ >
193
+ <ul className="link-list" data-element="page-index">
194
+ {headers.map((item, i) => {
195
+ return (
196
+ <li className="nav-item" key={item.id}>
197
+ <a
198
+ className={cx('nav-link', {
199
+ active: item.id === activeSection,
200
+ })}
201
+ href={`#${item.id}`}
202
+ onClick={handleClickAnchor(item.id)}
203
+ id={`item-${item.id}`}
204
+ >
205
+ <span>{item.title}</span>
206
+ </a>
207
+ </li>
208
+ );
209
+ })}
210
+ </ul>
211
+ </AccordionBody>
212
+ </Accordion>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </nav>
217
+ </div>
218
+ ) : null;
219
+ };
220
+ export default SideMenuByTitles;
@@ -3,7 +3,7 @@
3
3
  * @module components/theme/View/DocumentoView
4
4
  */
5
5
 
6
- import React, { useState, createRef, useEffect } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
 
9
9
  import {
@@ -17,12 +17,12 @@ import {
17
17
  DocumentoDocAllegati,
18
18
  DocumentoUlterioriInformazioni,
19
19
  ContentImage,
20
- SideMenu,
21
20
  PageHeader,
22
21
  RelatedItems,
23
22
  RelatedItemInEvidence,
24
23
  SkipToMainContent,
25
24
  ContentTypeViewSections,
25
+ useSideMenu,
26
26
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
27
27
 
28
28
  export const DocumentoViewSectionsOrder = [
@@ -48,16 +48,9 @@ export const DocumentoViewSectionsOrder = [
48
48
  */
49
49
  const DocumentoView = ({ content, location }) => {
50
50
  let documentBody = createRef();
51
- const [sideMenuElements, setSideMenuElements] = useState(null);
52
- //const userLogged = useSelector((state) => state.userSession);
51
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
53
52
 
54
- useEffect(() => {
55
- if (documentBody.current) {
56
- if (__CLIENT__) {
57
- setSideMenuElements(documentBody.current);
58
- }
59
- }
60
- }, [documentBody]);
53
+ //const userLogged = useSelector((state) => state.userSession);
61
54
 
62
55
  return (
63
56
  <>
@@ -3,12 +3,11 @@
3
3
  * @module components/theme/View/EventoView
4
4
  */
5
5
 
6
- import React, { useState, createRef, useEffect } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
 
9
9
  import {
10
10
  ContentImage,
11
- SideMenu,
12
11
  PageHeader,
13
12
  RelatedItems,
14
13
  EventoPlaceholderAfterContent,
@@ -25,6 +24,7 @@ import {
25
24
  SkipToMainContent,
26
25
  ContentTypeViewSections,
27
26
  EventoSponsors,
27
+ useSideMenu,
28
28
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
29
29
 
30
30
  export const EventoViewSectionsOrder = [
@@ -53,15 +53,7 @@ export const EventoViewSectionsOrder = [
53
53
  */
54
54
  const EventoView = ({ content, location }) => {
55
55
  let documentBody = createRef();
56
- const [sideMenuElements, setSideMenuElements] = useState(null);
57
-
58
- useEffect(() => {
59
- if (documentBody.current) {
60
- if (__CLIENT__) {
61
- setSideMenuElements(documentBody.current);
62
- }
63
- }
64
- }, [documentBody]);
56
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
65
57
 
66
58
  return (
67
59
  <>
@@ -3,12 +3,11 @@
3
3
  * @module components/theme/View/NewsItemView
4
4
  */
5
5
 
6
- import React, { createRef, useEffect, useState } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { readingTime } from '../ViewUtils';
8
+
9
9
  import {
10
10
  PageHeader,
11
- SideMenu,
12
11
  ContentImage,
13
12
  RelatedItems,
14
13
  NewsItemPlaceholderAfterContent,
@@ -23,6 +22,8 @@ import {
23
22
  NewsItemDataset,
24
23
  NewsItemMetadata,
25
24
  ContentTypeViewSections,
25
+ useSideMenu,
26
+ useReadingTime,
26
27
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
27
28
 
28
29
  export const NewsItemViewSectionsOrder = [
@@ -47,9 +48,9 @@ export const NewsItemViewSectionsOrder = [
47
48
  * @returns {string} Markup of the component.
48
49
  */
49
50
  const NewsItemView = ({ content, location }) => {
50
- const [readingtime, setReadingtime] = useState(0);
51
51
  let documentBody = createRef();
52
- const [sideMenuElements, setSideMenuElements] = useState(null);
52
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
53
+ const { readingtime } = useReadingTime(content, documentBody);
53
54
 
54
55
  let related_items = [];
55
56
  if (content?.notizie_correlate?.length > 0) {
@@ -59,17 +60,6 @@ const NewsItemView = ({ content, location }) => {
59
60
  related_items = [...related_items, ...content.relatedItems];
60
61
  }
61
62
 
62
- useEffect(() => {
63
- if (documentBody.current) {
64
- if (__CLIENT__) {
65
- setReadingtime(
66
- readingTime(content.title, content.description, documentBody),
67
- );
68
- setSideMenuElements(documentBody.current);
69
- }
70
- }
71
- }, [content.description, content.title, documentBody]);
72
-
73
63
  return (
74
64
  <>
75
65
  <div className="container px-4 my-4 newsitem-view">
@@ -3,13 +3,12 @@
3
3
  * @module components/theme/View/PaginaArgomentoViewNoBlocks
4
4
  */
5
5
 
6
- import React, { createRef, useState, useEffect } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { defineMessages, useIntl } from 'react-intl';
9
9
  import {
10
10
  GenericCard,
11
11
  ContentImage,
12
- SideMenu,
13
12
  PageHeader,
14
13
  OfficeCard,
15
14
  RichTextSection,
@@ -20,6 +19,7 @@ import {
20
19
  SkipToMainContent,
21
20
  RelatedItems,
22
21
  RelatedItemInEvidence,
22
+ useSideMenu,
23
23
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
24
24
 
25
25
  // import { getBaseUrl } from '@plone/volto/helpers';
@@ -68,15 +68,7 @@ const messages = defineMessages({
68
68
  const PaginaArgomentoViewNoBlocks = ({ content }) => {
69
69
  const intl = useIntl();
70
70
  let documentBody = createRef();
71
- const [sideMenuElements, setSideMenuElements] = useState(null);
72
-
73
- useEffect(() => {
74
- if (documentBody.current) {
75
- if (__CLIENT__) {
76
- setSideMenuElements(documentBody.current);
77
- }
78
- }
79
- }, [documentBody]);
71
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
80
72
 
81
73
  return (
82
74
  <>
@@ -3,11 +3,10 @@
3
3
  * @module components/theme/View/PersonaView
4
4
  */
5
5
 
6
- import React, { createRef, useEffect, useState } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
 
9
9
  import {
10
- SideMenu,
11
10
  PageHeader,
12
11
  RelatedItems,
13
12
  PersonaRuolo,
@@ -19,6 +18,7 @@ import {
19
18
  RelatedItemInEvidence,
20
19
  SkipToMainContent,
21
20
  ContentTypeViewSections,
21
+ useSideMenu,
22
22
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
23
23
 
24
24
  export const PersonaViewSectionsOrder = [
@@ -36,14 +36,7 @@ export const PersonaViewSectionsOrder = [
36
36
  */
37
37
  const PersonaView = ({ content }) => {
38
38
  let documentBody = createRef();
39
- const [sideMenuElements, setSideMenuElements] = useState(null);
40
- useEffect(() => {
41
- if (documentBody.current) {
42
- if (__CLIENT__) {
43
- setSideMenuElements(documentBody.current);
44
- }
45
- }
46
- }, [documentBody]);
39
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
47
40
 
48
41
  return (
49
42
  <>
@@ -3,12 +3,11 @@
3
3
  * @module components/theme/View/ServizioView
4
4
  */
5
5
 
6
- import React, { createRef, useEffect, useState } from 'react';
6
+ import React, { createRef } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { useIntl } from 'react-intl';
9
9
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
10
10
  import {
11
- SideMenu,
12
11
  PageHeader,
13
12
  ContentImage,
14
13
  ServizioPlaceholderAfterContent,
@@ -38,6 +37,7 @@ import {
38
37
  ServizioArgomenti,
39
38
  ServizioMetatag,
40
39
  ContentTypeViewSections,
40
+ useSideMenu,
41
41
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
42
42
 
43
43
  export const ServizioViewSectionsOrder = (props) => [
@@ -109,14 +109,7 @@ const ServizioView = ({ content, moment }) => {
109
109
  Moment.locale(intl.locale);
110
110
 
111
111
  const documentBody = createRef();
112
- const [sideMenuElements, setSideMenuElements] = useState(null);
113
- useEffect(() => {
114
- if (documentBody.current) {
115
- if (__CLIENT__) {
116
- setSideMenuElements(documentBody.current);
117
- }
118
- }
119
- }, [documentBody]);
112
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
120
113
 
121
114
  return (
122
115
  <>
@@ -3,14 +3,13 @@
3
3
  * @module components/theme/View/UOView
4
4
  */
5
5
 
6
- import React, { createRef, useEffect, useState } from 'react';
6
+ import React, { createRef } from 'react';
7
7
 
8
8
  import PropTypes from 'prop-types';
9
9
  import {
10
10
  PageHeader,
11
11
  UOServices,
12
12
  RelatedItems,
13
- SideMenu,
14
13
  ContentImage,
15
14
  UOPlaceholderAfterContent,
16
15
  UOPlaceholderAfterRelatedItems,
@@ -23,6 +22,7 @@ import {
23
22
  RelatedItemInEvidence,
24
23
  SkipToMainContent,
25
24
  ContentTypeViewSections,
25
+ useSideMenu,
26
26
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
27
27
 
28
28
  export const UOViewSectionsOrder = [
@@ -56,13 +56,7 @@ export const UOViewSectionsOrder = [
56
56
  */
57
57
  const UOView = ({ content }) => {
58
58
  let documentBody = createRef();
59
- const [sideMenuElements, setSideMenuElements] = useState(null);
60
-
61
- useEffect(() => {
62
- if (documentBody.current && __CLIENT__) {
63
- setSideMenuElements(documentBody.current);
64
- }
65
- }, [documentBody]);
59
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
66
60
 
67
61
  return (
68
62
  <>
@@ -3,11 +3,10 @@
3
3
  * @module components/theme/View/VenueView
4
4
  */
5
5
 
6
- import React, { createRef, useEffect, useState } from 'react';
6
+ import React, { createRef, useEffect } from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
 
9
9
  import {
10
- SideMenu,
11
10
  PageHeader,
12
11
  ContentImage,
13
12
  RelatedItems,
@@ -23,6 +22,7 @@ import {
23
22
  VenueContacts,
24
23
  VenueMoreInfos,
25
24
  ContentTypeViewSections,
25
+ useSideMenu,
26
26
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
27
27
 
28
28
  export const VenueViewSectionsOrder = [
@@ -56,15 +56,7 @@ export const VenueViewSectionsOrder = [
56
56
  */
57
57
  const VenueView = ({ content }) => {
58
58
  let documentBody = createRef();
59
- const [sideMenuElements, setSideMenuElements] = useState(null);
60
-
61
- useEffect(() => {
62
- if (documentBody.current) {
63
- if (__CLIENT__) {
64
- setSideMenuElements(documentBody.current);
65
- }
66
- }
67
- }, [documentBody]);
59
+ const { sideMenuElements, SideMenu } = useSideMenu(content, documentBody);
68
60
 
69
61
  useEffect(() => {
70
62
  if (
@@ -1,6 +1,8 @@
1
- import React from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import { IntlProvider } from 'react-intl';
3
3
  import { renderToString } from 'react-dom/server';
4
+ import { SideMenu } from 'design-comuni-plone-theme/components/ItaliaTheme/View';
5
+ import config from '@plone/volto/registry';
4
6
 
5
7
  export const readingTime = (title, description, htmlBody) => {
6
8
  const body = htmlBody.current.textContent;
@@ -13,3 +15,69 @@ export const readingTime = (title, description, htmlBody) => {
13
15
 
14
16
  export const getHTMLString = (content, locale) =>
15
17
  renderToString(<IntlProvider locale={locale}>{content}</IntlProvider>);
18
+
19
+ //Hook per avere il reading time di una pagina
20
+ export const useReadingTime = (content, documentBody) => {
21
+ const [readingtime, setReadingtime] = useState(0);
22
+
23
+ useEffect(() => {
24
+ if (documentBody.current) {
25
+ if (__CLIENT__) {
26
+ setReadingtime(
27
+ readingTime(content.title, content.description, documentBody),
28
+ );
29
+ }
30
+ }
31
+ }, [content.description, content.title, documentBody]);
32
+
33
+ return { readingtime, setReadingtime };
34
+ };
35
+
36
+ //Hook per avere i SideMenu elements di una pagina
37
+ export const useSideMenu = (content, documentBody) => {
38
+ const [sideMenuElements, setSideMenuElements] = useState(null);
39
+ const [observer, setObserver] = useState(null);
40
+ const updateSideMenuOnLoadingBlocks =
41
+ config.settings?.italiaThemeViewsConfig?.[content['@type']]
42
+ ?.updateSideMenuOnLoadingBlocks ?? false;
43
+ const SideMenuComponent =
44
+ config.settings?.italiaThemeViewsConfig?.[content['@type']]?.sideMenu ??
45
+ SideMenu;
46
+
47
+ useEffect(() => {
48
+ if (documentBody.current) {
49
+ if (__CLIENT__) {
50
+ setSideMenuElements(documentBody.current);
51
+ }
52
+ }
53
+ }, [content.description, content.title, documentBody]);
54
+
55
+ //observer is needed for loadable blocks like listing and rss, if you want to use their title's for SideMenu
56
+
57
+ useEffect(() => {
58
+ if (!updateSideMenuOnLoadingBlocks) return;
59
+
60
+ if (!observer) {
61
+ const obs = updateSideMenuOnLoadingBlocks
62
+ ? new MutationObserver((mutationList) => {
63
+ setSideMenuElements(documentBody.current);
64
+ })
65
+ : null;
66
+ setObserver(obs);
67
+ }
68
+ if (observer) {
69
+ observer.observe(documentBody.current, {
70
+ //attributes: true,
71
+ childList: true,
72
+ subtree: true,
73
+ });
74
+ }
75
+ return () => {
76
+ if (observer) {
77
+ observer.disconnect();
78
+ }
79
+ };
80
+ }, [observer, setObserver, documentBody, updateSideMenuOnLoadingBlocks]);
81
+
82
+ return { sideMenuElements, setSideMenuElements, SideMenu: SideMenuComponent };
83
+ };
@@ -5,6 +5,12 @@ export {
5
5
  RichTextRender,
6
6
  richTextHasContent,
7
7
  } from 'design-comuni-plone-theme/components/ItaliaTheme/View/Commons/RichTextRender';
8
+ export {
9
+ useSideMenu,
10
+ useReadingTime,
11
+ readingTime,
12
+ getHTMLString,
13
+ } from 'design-comuni-plone-theme/components/ItaliaTheme/View/ViewUtils';
8
14
 
9
15
  export RenderBlocks from 'design-comuni-plone-theme/components/ItaliaTheme/View/Commons/RenderBlocks';
10
16
  export RelatedNewsArticles from 'design-comuni-plone-theme/components/ItaliaTheme/View/Commons/RelatedNewsArticles';
@@ -191,6 +191,8 @@ export default function applyConfig(voltoConfig) {
191
191
  // sections: [
192
192
  // //sections order for Venue content-type view. See components/ItaliaTheme/View/VenueView/VenueView.jsx for default VenueViewSectionsOrder
193
193
  // ],
194
+ // updateSideMenuOnLoadingBlocks:false, //serve se si vuole fare in modo che il sideMenu si aggiorni con i titli dei blocchi presenti nel testo
195
+ // sideMenu:null // qui va messo il component da usare per fare il SideMenu, se non è impostato viene usato quello di default
194
196
  // },
195
197
  },
196
198
  siteProperties: {