@eeacms/volto-slate-footnote 7.0.1 → 7.1.1
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 +11 -0
- package/package.json +1 -1
- package/src/Blocks/Footnote/FootnotesBlockView.jsx +39 -6
- package/src/editor/render.jsx +63 -45
- package/src/editor/utils.js +0 -9
- package/src/editor/utils.test.js +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [7.1.1](https://github.com/eea/volto-slate-footnote/compare/7.1.0...7.1.1) - 16 September 2024
|
|
8
|
+
|
|
9
|
+
### [7.1.0](https://github.com/eea/volto-slate-footnote/compare/7.0.1...7.1.0) - 13 September 2024
|
|
10
|
+
|
|
11
|
+
#### :rocket: New Features
|
|
12
|
+
|
|
13
|
+
- feat: autodetect links and use universal link - refs #274052 [dobri1408 - [`9c13101`](https://github.com/eea/volto-slate-footnote/commit/9c13101b62b1df150e1b3dbb93807df60a074ad5)]
|
|
14
|
+
|
|
15
|
+
#### :hammer_and_wrench: Others
|
|
16
|
+
|
|
17
|
+
- Release 7.1.0 [alin - [`c15afb0`](https://github.com/eea/volto-slate-footnote/commit/c15afb0be79ba9c4e3a46e3c190a41db468e88cd)]
|
|
7
18
|
### [7.0.1](https://github.com/eea/volto-slate-footnote/compare/7.0.0...7.0.1) - 23 August 2024
|
|
8
19
|
|
|
9
20
|
#### :hammer_and_wrench: Others
|
package/package.json
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
openAccordionOrTabIfContainsFootnoteReference,
|
|
4
4
|
getAllBlocksAndSlateFields,
|
|
5
5
|
makeFootnoteListOfUniqueItems,
|
|
6
|
-
makeFootnote,
|
|
7
6
|
} from '@eeacms/volto-slate-footnote/editor/utils';
|
|
8
7
|
import './less/public.less';
|
|
9
8
|
|
|
10
9
|
import { UniversalLink } from '@plone/volto/components';
|
|
11
10
|
|
|
12
11
|
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
|
|
12
|
+
const urlRegex = /https?:\/\/[^\s]+/g;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @summary The React component that displays the list of footnotes inserted
|
|
@@ -19,6 +19,7 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
|
|
|
19
19
|
* @param {Object} props Contains the properties `data` and `properties` as
|
|
20
20
|
* received from the Volto form.
|
|
21
21
|
*/
|
|
22
|
+
|
|
22
23
|
const FootnotesBlockView = (props) => {
|
|
23
24
|
const { data, properties, tabData, content } = props;
|
|
24
25
|
const { title, global, placeholder = 'Footnotes' } = data;
|
|
@@ -75,6 +76,35 @@ const FootnotesBlockView = (props) => {
|
|
|
75
76
|
startList = citationIndice;
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
const renderTextWithLinks = (text) => {
|
|
80
|
+
if (!text) return null;
|
|
81
|
+
const parts = text.split(urlRegex);
|
|
82
|
+
const links = text.match(urlRegex);
|
|
83
|
+
let result = [];
|
|
84
|
+
|
|
85
|
+
parts.forEach((part, index) => {
|
|
86
|
+
result.push(
|
|
87
|
+
<div
|
|
88
|
+
dangerouslySetInnerHTML={{
|
|
89
|
+
__html: part,
|
|
90
|
+
}}
|
|
91
|
+
/>,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (links && links[index]) {
|
|
95
|
+
result.push(
|
|
96
|
+
<UniversalLink
|
|
97
|
+
key={`link-${index}`}
|
|
98
|
+
href={links[index]}
|
|
99
|
+
openLinkInNewTab={false}
|
|
100
|
+
>
|
|
101
|
+
{links[index]}
|
|
102
|
+
</UniversalLink>,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return result;
|
|
107
|
+
};
|
|
78
108
|
return (
|
|
79
109
|
<div className="footnotes-listing-block">
|
|
80
110
|
<h3 title={placeholder}>{title}</h3>
|
|
@@ -85,16 +115,19 @@ const FootnotesBlockView = (props) => {
|
|
|
85
115
|
const { uid, footnote, zoteroId, parentUid } = note;
|
|
86
116
|
const { refs } = note;
|
|
87
117
|
const refsList = refs ? Object.keys(refs) : null;
|
|
118
|
+
|
|
119
|
+
// const history = createBrowserHistory();
|
|
120
|
+
// const api = new Api();
|
|
121
|
+
// const store = configureStore(window.__data, history, api);
|
|
122
|
+
const footnoteText = !footnote
|
|
123
|
+
? ''
|
|
124
|
+
: footnote.replace('<?xml version="1.0"?>', '');
|
|
88
125
|
return (
|
|
89
126
|
<li
|
|
90
127
|
key={`footnote-${zoteroId || uid}`}
|
|
91
128
|
id={`footnote-${zoteroId || uid}`}
|
|
92
129
|
>
|
|
93
|
-
<div
|
|
94
|
-
dangerouslySetInnerHTML={{
|
|
95
|
-
__html: makeFootnote(footnote),
|
|
96
|
-
}}
|
|
97
|
-
/>
|
|
130
|
+
<div>{renderTextWithLinks(footnoteText)}</div>
|
|
98
131
|
{refsList ? (
|
|
99
132
|
<>
|
|
100
133
|
{/** some footnotes are never parent so we need the parent to reference */}
|
package/src/editor/render.jsx
CHANGED
|
@@ -15,9 +15,8 @@ import { UniversalLink } from '@plone/volto/components';
|
|
|
15
15
|
* @param {string} footnote
|
|
16
16
|
* @returns {string} formatted footnote
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
18
|
+
|
|
19
|
+
const urlRegex = /https?:\/\/[^\s]+/g;
|
|
21
20
|
|
|
22
21
|
export const FootnoteElement = (props) => {
|
|
23
22
|
const { attributes, children, element, mode, extras } = props;
|
|
@@ -37,7 +36,7 @@ export const FootnoteElement = (props) => {
|
|
|
37
36
|
const notesObjResult = isEmpty(metadata)
|
|
38
37
|
? makeFootnoteListOfUniqueItems(storeBlocks)
|
|
39
38
|
: makeFootnoteListOfUniqueItems(blocks);
|
|
40
|
-
// will
|
|
39
|
+
// will consider zotero citations and footnote
|
|
41
40
|
// notesObjResult contains all zotero/footnote as unique, and contain refs for other zotero/footnote
|
|
42
41
|
const indiceIfZoteroId = data.extra
|
|
43
42
|
? [
|
|
@@ -52,6 +51,35 @@ export const FootnoteElement = (props) => {
|
|
|
52
51
|
: // no extra citations (no multiples)
|
|
53
52
|
`[${Object.keys(notesObjResult).indexOf(zoteroId) + 1}]`;
|
|
54
53
|
|
|
54
|
+
const renderTextWithLinks = (text) => {
|
|
55
|
+
if (!text) return null;
|
|
56
|
+
const parts = text.split(urlRegex);
|
|
57
|
+
const links = text.match(urlRegex);
|
|
58
|
+
let result = [];
|
|
59
|
+
|
|
60
|
+
parts.forEach((part, index) => {
|
|
61
|
+
result.push(
|
|
62
|
+
<div
|
|
63
|
+
dangerouslySetInnerHTML={{
|
|
64
|
+
__html: part,
|
|
65
|
+
}}
|
|
66
|
+
/>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (links && links[index]) {
|
|
70
|
+
result.push(
|
|
71
|
+
<UniversalLink
|
|
72
|
+
key={`link-${index}`}
|
|
73
|
+
href={links[index]}
|
|
74
|
+
openLinkInNewTab={false}
|
|
75
|
+
>
|
|
76
|
+
{links[index]}
|
|
77
|
+
</UniversalLink>,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return result;
|
|
82
|
+
};
|
|
55
83
|
const citationIndice = zoteroId // ZOTERO
|
|
56
84
|
? indiceIfZoteroId
|
|
57
85
|
: // FOOTNOTES
|
|
@@ -76,13 +104,17 @@ export const FootnoteElement = (props) => {
|
|
|
76
104
|
Object.keys(notesObjResult).find(
|
|
77
105
|
(noteKey) => notesObjResult[noteKey].uid === uid,
|
|
78
106
|
) ||
|
|
79
|
-
// if not found in parent, search in refs, it might be a footnote
|
|
107
|
+
// if not found in parent, search in refs, it might be a footnote referenced multiple times
|
|
80
108
|
Object.keys(notesObjResult).find(
|
|
81
109
|
(noteKey) =>
|
|
82
110
|
notesObjResult[noteKey].uid === uid ||
|
|
83
111
|
(notesObjResult[noteKey].refs && notesObjResult[noteKey].refs[uid]),
|
|
84
112
|
);
|
|
85
113
|
|
|
114
|
+
const footnoteText = !data.footnote
|
|
115
|
+
? ''
|
|
116
|
+
: data.footnote.replace('<?xml version="1.0"?>', '');
|
|
117
|
+
|
|
86
118
|
return (
|
|
87
119
|
<>
|
|
88
120
|
{mode === 'view' ? (
|
|
@@ -109,7 +141,6 @@ export const FootnoteElement = (props) => {
|
|
|
109
141
|
<Popup.Content>
|
|
110
142
|
<List divided relaxed selection>
|
|
111
143
|
<List.Item
|
|
112
|
-
as={UniversalLink}
|
|
113
144
|
href={`#footnote-${citationRefId}`}
|
|
114
145
|
onClick={() =>
|
|
115
146
|
openAccordionOrTabIfContainsFootnoteReference(
|
|
@@ -120,37 +151,34 @@ export const FootnoteElement = (props) => {
|
|
|
120
151
|
>
|
|
121
152
|
<List.Content>
|
|
122
153
|
<List.Description>
|
|
123
|
-
|
|
124
|
-
dangerouslySetInnerHTML={{
|
|
125
|
-
__html: makeFootnote(data.footnote),
|
|
126
|
-
}}
|
|
127
|
-
/>{' '}
|
|
154
|
+
{renderTextWithLinks(footnoteText)}
|
|
128
155
|
</List.Description>
|
|
129
156
|
</List.Content>
|
|
130
157
|
</List.Item>
|
|
131
158
|
{data.extra &&
|
|
132
|
-
data.extra.map((item) =>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
data.extra.map((item) => {
|
|
160
|
+
const footnoteText = !item.footnote
|
|
161
|
+
? ''
|
|
162
|
+
: item.footnote.replace('<?xml version="1.0"?>', '');
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<List.Item
|
|
166
|
+
href={`#footnote-${item.zoteroId || item.uid}`}
|
|
167
|
+
onClick={() =>
|
|
168
|
+
openAccordionOrTabIfContainsFootnoteReference(
|
|
169
|
+
`#footnote-${item.zoteroId || item.uid}`,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
key={`#footnote-${item.zoteroId || item.uid}`}
|
|
173
|
+
>
|
|
174
|
+
<List.Content>
|
|
175
|
+
<List.Description>
|
|
176
|
+
{renderTextWithLinks(footnoteText)}
|
|
177
|
+
</List.Description>
|
|
178
|
+
</List.Content>
|
|
179
|
+
</List.Item>
|
|
180
|
+
);
|
|
181
|
+
})}
|
|
154
182
|
</List>
|
|
155
183
|
</Popup.Content>
|
|
156
184
|
</Popup>
|
|
@@ -173,7 +201,6 @@ export const FootnoteElement = (props) => {
|
|
|
173
201
|
<Popup.Content>
|
|
174
202
|
<List divided relaxed selection>
|
|
175
203
|
<List.Item
|
|
176
|
-
as={UniversalLink}
|
|
177
204
|
href={`#footnote-${citationRefId}`}
|
|
178
205
|
onClick={() =>
|
|
179
206
|
openAccordionOrTabIfContainsFootnoteReference(
|
|
@@ -184,18 +211,13 @@ export const FootnoteElement = (props) => {
|
|
|
184
211
|
>
|
|
185
212
|
<List.Content>
|
|
186
213
|
<List.Description>
|
|
187
|
-
|
|
188
|
-
dangerouslySetInnerHTML={{
|
|
189
|
-
__html: makeFootnote(data.footnote),
|
|
190
|
-
}}
|
|
191
|
-
/>{' '}
|
|
214
|
+
{renderTextWithLinks(footnoteText)}
|
|
192
215
|
</List.Description>
|
|
193
216
|
</List.Content>
|
|
194
217
|
</List.Item>
|
|
195
218
|
{data.extra &&
|
|
196
219
|
data.extra.map((item) => (
|
|
197
220
|
<List.Item
|
|
198
|
-
as={UniversalLink}
|
|
199
221
|
href={`#footnote-${item.zoteroId || item.uid}`}
|
|
200
222
|
onClick={() =>
|
|
201
223
|
openAccordionOrTabIfContainsFootnoteReference(
|
|
@@ -206,11 +228,7 @@ export const FootnoteElement = (props) => {
|
|
|
206
228
|
>
|
|
207
229
|
<List.Content>
|
|
208
230
|
<List.Description>
|
|
209
|
-
|
|
210
|
-
dangerouslySetInnerHTML={{
|
|
211
|
-
__html: makeFootnote(item.footnote),
|
|
212
|
-
}}
|
|
213
|
-
/>{' '}
|
|
231
|
+
{renderTextWithLinks(item.footnote)}
|
|
214
232
|
</List.Description>
|
|
215
233
|
</List.Content>
|
|
216
234
|
</List.Item>
|
package/src/editor/utils.js
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import config from '@plone/volto/registry';
|
|
2
2
|
import { Node } from 'slate';
|
|
3
3
|
import { getAllBlocks } from '@plone/volto-slate/utils';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* remove <?xml version="1.0"?> from the string
|
|
7
|
-
* @param {*} footnote - xml format
|
|
8
|
-
* @returns string
|
|
9
|
-
*/
|
|
10
|
-
export const makeFootnote = (footnote) => {
|
|
11
|
-
return footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
|
|
12
|
-
};
|
|
13
4
|
/**
|
|
14
5
|
* retrive all slate children of nested objects
|
|
15
6
|
* @param {object} path - the keys that we want to extract the slate children from
|
package/src/editor/utils.test.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
makeFootnote,
|
|
3
2
|
openAccordionOrTabIfContainsFootnoteReference,
|
|
4
3
|
getAllBlocksAndSlateFields,
|
|
5
4
|
} from './utils';
|
|
@@ -9,19 +8,6 @@ jest.mock('@plone/volto-slate/utils', () => ({
|
|
|
9
8
|
getAllBlocks: jest.fn(),
|
|
10
9
|
}));
|
|
11
10
|
|
|
12
|
-
describe('makeFootnote', () => {
|
|
13
|
-
it('should remove xml version string from footnote', () => {
|
|
14
|
-
const xmlString = '<?xml version="1.0"?>Test text';
|
|
15
|
-
const expectedResult = 'Test text';
|
|
16
|
-
expect(makeFootnote(xmlString)).toEqual(expectedResult);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should return empty string when footnote is null or undefined', () => {
|
|
20
|
-
expect(makeFootnote(null)).toEqual('');
|
|
21
|
-
expect(makeFootnote(undefined)).toEqual('');
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
11
|
describe('openAccordionOrTabIfContainsFootnoteReference', () => {
|
|
26
12
|
it('should open accordion if it contains footnote reference', () => {
|
|
27
13
|
document.body.innerHTML = `
|