@eeacms/volto-slate-footnote 3.0.0 → 4.0.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.
@@ -8,7 +8,7 @@ import briefcaseSVG from '@plone/volto/icons/briefcase.svg';
8
8
  import checkSVG from '@plone/volto/icons/check.svg';
9
9
  import clearSVG from '@plone/volto/icons/clear.svg';
10
10
  import { Node } from 'slate';
11
- import { getAllBlocks } from 'volto-slate/utils';
11
+ import { getAllBlocksAndSlateFields } from '@eeacms/volto-slate-footnote/editor/utils';
12
12
 
13
13
  const FootnoteEditor = (props) => {
14
14
  const {
@@ -22,10 +22,9 @@ const FootnoteEditor = (props) => {
22
22
  hasValue,
23
23
  onChangeValues,
24
24
  } = props;
25
-
26
25
  const dispatch = useDispatch();
26
+ const pid = `${editor.uid}-${pluginId}`;
27
27
  const [formData, setFormData] = React.useState({});
28
-
29
28
  const active = getActiveElement(editor);
30
29
 
31
30
  if (!active) {
@@ -35,12 +34,15 @@ const FootnoteEditor = (props) => {
35
34
  const [elementNode] = active;
36
35
  const isElement = isActiveElement(editor);
37
36
 
38
- const blockProps = editor.getBlockProps();
39
- const metadata = blockProps.metadata || blockProps.properties;
40
- const blocks = getAllBlocks(metadata, []);
37
+ const blockProps = editor?.getBlockProps ? editor.getBlockProps() : {};
38
+ const metadata = blockProps.metadata || blockProps.properties || {};
39
+ const blocks = getAllBlocksAndSlateFields(metadata);
41
40
  const filteredBlocks = [];
42
41
 
43
42
  // make a list of filtered footnotes that have unique title
43
+ // to be used as choices for the multi search widget
44
+ // add label and value for the multi search widget
45
+ // flatten blocks to add all extra in the list
44
46
  blocks
45
47
  .filter((b) => b['@type'] === 'slate')
46
48
  .forEach(({ value }) => {
@@ -48,7 +50,30 @@ const FootnoteEditor = (props) => {
48
50
 
49
51
  Array.from(Node.elements(value[0])).forEach(([block]) => {
50
52
  block.children.forEach((node) => {
51
- if (
53
+ if (node.data && node.type === 'footnote' && node.data.extra) {
54
+ if (
55
+ !filteredBlocks.find((item) => item.title === node.data.footnote)
56
+ ) {
57
+ filteredBlocks.push({
58
+ ...node.data,
59
+ title: node.data.footnote || node.data.value,
60
+ label: node.data.footnote || node.data.value,
61
+ value: node.data.footnote || node.data.value,
62
+ });
63
+ }
64
+ node.data.extra.forEach((ftitem) => {
65
+ if (
66
+ !filteredBlocks.find((item) => item.title === ftitem.footnote)
67
+ ) {
68
+ filteredBlocks.push({
69
+ ...ftitem,
70
+ title: ftitem.footnote || ftitem.value,
71
+ label: ftitem.footnote || ftitem.value,
72
+ value: ftitem.footnote || ftitem.value,
73
+ });
74
+ }
75
+ });
76
+ } else if (
52
77
  node.data &&
53
78
  node.type === 'footnote' &&
54
79
  !filteredBlocks.find((item) => item.title === node.data.footnote)
@@ -56,6 +81,8 @@ const FootnoteEditor = (props) => {
56
81
  filteredBlocks.push({
57
82
  ...node.data,
58
83
  title: node.data.footnote,
84
+ label: node.data.footnote,
85
+ value: node.data.footnote,
59
86
  });
60
87
  }
61
88
  });
@@ -65,17 +92,24 @@ const FootnoteEditor = (props) => {
65
92
  // Update the form data based on the current element
66
93
  const elRef = React.useRef(null);
67
94
 
95
+ // add label and value for the multi search widget to be able to show/filter current data
68
96
  if (isElement && !isEqual(elementNode, elRef.current)) {
69
97
  elRef.current = elementNode;
70
- setFormData(elementNode.data || {});
98
+ setFormData({
99
+ footnote: {
100
+ ...elementNode.data,
101
+ label: elementNode.data.footnote,
102
+ value: elementNode.data.footnote,
103
+ },
104
+ });
71
105
  } else if (!isElement) {
72
106
  elRef.current = null;
73
107
  }
74
108
 
75
109
  const saveDataToEditor = React.useCallback(
76
110
  (formData) => {
77
- if (hasValue(formData)) {
78
- insertElement(editor, formData);
111
+ if (hasValue(formData.footnote)) {
112
+ insertElement(editor, formData.footnote);
79
113
  } else {
80
114
  unwrapElement(editor);
81
115
  }
@@ -111,14 +145,12 @@ const FootnoteEditor = (props) => {
111
145
  icon={<VoltoIcon size="24px" name={briefcaseSVG} />}
112
146
  onChangeField={(value) => {
113
147
  if (!onChangeValues) {
114
- return setFormData({
115
- ...formData,
116
- ...value,
117
- });
148
+ return setFormData(value);
118
149
  }
119
150
  return onChangeValues('footnote', value, formData, setFormData);
120
151
  }}
121
152
  formData={formData}
153
+ dataBoss={formData}
122
154
  source={filteredBlocks}
123
155
  headerActions={
124
156
  <>
@@ -126,7 +158,7 @@ const FootnoteEditor = (props) => {
126
158
  onClick={() => {
127
159
  saveDataToEditor(formData);
128
160
  dispatch(
129
- setPluginOptions(pluginId, {
161
+ setPluginOptions(pid, {
130
162
  show_sidebar_editor: false,
131
163
  }),
132
164
  );
@@ -139,7 +171,7 @@ const FootnoteEditor = (props) => {
139
171
  onClick={() => {
140
172
  checkForCancel();
141
173
  dispatch(
142
- setPluginOptions(pluginId, {
174
+ setPluginOptions(pid, {
143
175
  show_sidebar_editor: false,
144
176
  }),
145
177
  );
@@ -0,0 +1,150 @@
1
+ /**
2
+ * ArrayWidget component.
3
+ * @module components/manage/Widgets/ArrayWidget
4
+ */
5
+
6
+ import React, { useState } from 'react';
7
+ import { defineMessages } from 'react-intl';
8
+ import {
9
+ Option,
10
+ DropdownIndicator,
11
+ selectTheme,
12
+ customSelectStyles,
13
+ } from '@plone/volto/components/manage/Widgets/SelectStyling';
14
+ import { escapeRegExp, filter } from 'lodash';
15
+ import { nanoid } from 'volto-slate/utils';
16
+
17
+ import { FormFieldWrapper } from '@plone/volto/components';
18
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
19
+
20
+ const messages = defineMessages({
21
+ select: {
22
+ id: 'Select…',
23
+ defaultMessage: 'Select…',
24
+ },
25
+ no_options: {
26
+ id: 'No options',
27
+ defaultMessage: 'No options',
28
+ },
29
+ });
30
+
31
+ const MultiSelectSearchWidget = injectLazyLibs('reactSelectAsyncCreateable')(
32
+ (props) => {
33
+ const parentFootnote = props.value;
34
+ const extraValues = props.value.extra ? props.value.extra : [];
35
+ const [selectedOption, setSelectedOption] = useState(
36
+ parentFootnote.value ? [...[parentFootnote], ...extraValues] : [],
37
+ );
38
+
39
+ /**
40
+ * evaluate on Regex to filter results
41
+ * @param {Object} e - event
42
+ * @param {Object} data
43
+ */
44
+ const loadOptions = (search) => {
45
+ const re = new RegExp(escapeRegExp(search), 'i');
46
+ const isMatch = (result) => re.test(result.value);
47
+ const resultsFiltered = filter(props.choices, isMatch);
48
+
49
+ return new Promise((resolve, reject) => {
50
+ resolve(resultsFiltered);
51
+ });
52
+ };
53
+
54
+ /**
55
+ * If the list is empty or the first is not parent, return true
56
+ * @param {Object[]} selectedOption list of objects - footnotes
57
+ * @returns {boolean}
58
+ */
59
+ const isParetFootnoteRemoved = (selectedOption) =>
60
+ !selectedOption[0] || selectedOption[0].value !== parentFootnote.value;
61
+
62
+ /**
63
+ * replace all parentFootnote data except uid, with the first from the list
64
+ * @param {Object[]} selectedOption list of objects - footnotes
65
+ * @returns {Object}
66
+ */
67
+ const setParentFootnoteFromExtra = (selectedOption) => {
68
+ const { footnote, label, value } = selectedOption[0] || [];
69
+
70
+ return {
71
+ ...parentFootnote,
72
+ footnote: footnote || selectedOption[0]?.value,
73
+ label,
74
+ value,
75
+ extra: selectedOption.slice(1),
76
+ };
77
+ };
78
+
79
+ /**
80
+ * Will make the footnotes object, that will be saved as first from selectedOption
81
+ * the rest will be added to extra
82
+ * @param {*} selectedOption
83
+ * @returns
84
+ */
85
+ const setFootnoteFromSelection = (selectedOption) => {
86
+ const extra = selectedOption.slice(1).map((item) => {
87
+ const obj = {
88
+ uid: nanoid(5),
89
+ ...item,
90
+ footnote: item.value,
91
+ };
92
+
93
+ const { __isNew__: remove, ...rest } = obj;
94
+ return rest;
95
+ });
96
+
97
+ return { ...parentFootnote, extra };
98
+ };
99
+
100
+ /**
101
+ * Handle the field change, will remake the result based on the new selected list
102
+ * @method handleChange
103
+ * @param {array} selectedOption The selected options (already aggregated).
104
+ * @returns {undefined}
105
+ */
106
+ const handleChange = (selectedOption) => {
107
+ setSelectedOption(selectedOption);
108
+
109
+ // manage case if parent footnotes (first from the options) was removed
110
+ const resultSelected = isParetFootnoteRemoved(selectedOption)
111
+ ? setParentFootnoteFromExtra(selectedOption)
112
+ : setFootnoteFromSelection(selectedOption);
113
+
114
+ props.onChange({
115
+ footnote: resultSelected,
116
+ });
117
+ };
118
+
119
+ // from choices (list of all footnotes available including current in value) get all not found in current in value
120
+ // consider that new footnotes have value and footnote undefined
121
+ const defaultOptions = (props.choices || []).filter(
122
+ (item) =>
123
+ !selectedOption.find(({ label }) => label === item.label) && item.value,
124
+ );
125
+ const AsyncCreatableSelect = props.reactSelectAsyncCreateable.default;
126
+
127
+ return (
128
+ <FormFieldWrapper {...props}>
129
+ <AsyncCreatableSelect
130
+ isDisabled={props.isDisabled}
131
+ className="react-select-container"
132
+ classNamePrefix="react-select"
133
+ defaultOptions={defaultOptions}
134
+ styles={customSelectStyles}
135
+ theme={selectTheme}
136
+ components={{ DropdownIndicator, Option }}
137
+ isMulti
138
+ options={defaultOptions}
139
+ value={selectedOption || []}
140
+ loadOptions={loadOptions}
141
+ onChange={handleChange}
142
+ placeholder={props.intl.formatMessage(messages.select)}
143
+ noOptionsMessage={() => props.intl.formatMessage(messages.no_options)}
144
+ />
145
+ </FormFieldWrapper>
146
+ );
147
+ },
148
+ );
149
+
150
+ export default MultiSelectSearchWidget;
@@ -11,6 +11,7 @@ export const withFootnote = (editor) => {
11
11
 
12
12
  editor.normalizeNode = (entry) => {
13
13
  const [node, path] = entry;
14
+
14
15
  if (node.type === FOOTNOTE && !node.data?.uid) {
15
16
  Transforms.setNodes(
16
17
  editor,
@@ -34,12 +35,10 @@ export const withFootnote = (editor) => {
34
35
  // this will be usefull when copy/pase items have the same uid
35
36
  export const withBeforeInsertFragment = (editor) => {
36
37
  const { beforeInsertFragment } = editor;
37
-
38
38
  editor.beforeInsertFragment = (parsed) => {
39
39
  if (parsed?.[0]?.children?.[0]?.data?.uid) {
40
40
  parsed[0].children[0].data.uid = nanoid(5);
41
41
  }
42
-
43
42
  return beforeInsertFragment ? beforeInsertFragment(parsed) : parsed;
44
43
  };
45
44
 
@@ -1,48 +1,149 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import { Popup } from 'semantic-ui-react';
2
+ import { Popup, List } from 'semantic-ui-react';
3
3
  import { useEditorContext } from 'volto-slate/hooks';
4
- import { getAllBlocks } from 'volto-slate/utils';
4
+ import { getAllBlocksAndSlateFields } from '@eeacms/volto-slate-footnote/editor/utils';
5
5
  import { makeFootnoteListOfUniqueItems } from './utils';
6
6
 
7
+ /**
8
+ * Removes '<?xml version="1.0"?>' from footnote
9
+ * @param {string} footnote
10
+ * @returns {string} formatted footnote
11
+ */
7
12
  const makeFootnote = (footnote) => {
8
13
  const free = footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
9
14
 
10
15
  return free;
11
16
  };
12
17
 
18
+ /**
19
+ * Will open accordion if contains footnote reference
20
+ * @param {string} footnoteId
21
+ */
22
+ const openAccordionIfContainsFootnoteReference = (footnoteId) => {
23
+ if (typeof window !== 'undefined') {
24
+ const footnote = document.querySelector(footnoteId);
25
+ if (footnote !== null && footnote.closest('.accordion') !== null) {
26
+ const comp = footnote.closest('.accordion').querySelector('.title');
27
+ if (!comp.className.includes('active')) {
28
+ comp.click();
29
+ }
30
+ }
31
+ }
32
+
33
+ return true;
34
+ };
35
+
13
36
  export const FootnoteElement = (props) => {
14
37
  const { attributes, children, element, mode, extras } = props;
15
38
  const { data = {} } = element;
16
39
  const { uid, zoteroId } = data;
17
40
  const editor = useEditorContext();
18
- const [citationIndice, setCitationIndice] = useState(null);
19
- const [citationRefId, setCitationRefId] = useState(null);
41
+ const [citationIndice, setCitationIndice] = useState(null); // list of indices to reference
42
+ const [citationRefId, setCitationRefId] = useState(null); // indice of element to be referenced
20
43
 
21
44
  useEffect(() => {
22
- const blockProps = editor ? editor.getBlockProps() : null;
45
+ const blockProps = editor?.getBlockProps ? editor.getBlockProps() : null;
23
46
  const metadata = blockProps
24
47
  ? blockProps.metadata || blockProps.properties
25
- : extras.metadata;
26
- const blocks = getAllBlocks(metadata, []);
48
+ : extras?.metadata || {};
49
+ const blocks = getAllBlocksAndSlateFields(metadata);
27
50
  const notesObjResult = makeFootnoteListOfUniqueItems(blocks);
28
51
 
29
- const indice = zoteroId
30
- ? Object.keys(notesObjResult).indexOf(zoteroId) + 1
31
- : notesObjResult[data.uid]
32
- ? Object.keys(notesObjResult).indexOf(data.uid) + 1
33
- : Object.keys(notesObjResult).indexOf(
34
- Object.keys(notesObjResult).find(
35
- (noteKey) =>
36
- notesObjResult[noteKey].refs &&
37
- notesObjResult[noteKey].refs[data.uid],
38
- ),
39
- ) + 1;
40
-
41
- const findReferenceId = Object.keys(notesObjResult).find(
42
- (noteKey) =>
43
- notesObjResult[noteKey].uid === uid ||
44
- (notesObjResult[noteKey].refs && notesObjResult[noteKey].refs[uid]),
45
- );
52
+ // will cosider zotero citations and footnote
53
+ // notesObjResult contains all zotero/footnote as unique, and contain refs for other zotero/footnote
54
+ const indice = zoteroId // ZOTERO
55
+ ? data.extra
56
+ ? [
57
+ `[${Object.keys(notesObjResult).indexOf(zoteroId) + 1}]`, // parent footnote
58
+ ...data.extra.map(
59
+ // citations from extra
60
+ (zoteroObj, index) =>
61
+ // all zotero citation are indexed by zoteroId in notesObjResult
62
+ `[${
63
+ Object.keys(notesObjResult).indexOf(zoteroObj.zoteroId) + 1
64
+ }]`,
65
+ ),
66
+ ].join('')
67
+ : // no extra citations (no multiples)
68
+ `[${Object.keys(notesObjResult).indexOf(zoteroId) + 1}]`
69
+ : // FOOTNOTES
70
+ // not all footnotes will be found in notesObjResult because they might have different uid
71
+ notesObjResult[data.uid]
72
+ ? // footnotes from extra
73
+ data.extra
74
+ ? [
75
+ // parent footnote
76
+ `[${Object.keys(notesObjResult).indexOf(data.uid) + 1}]`,
77
+ ...data.extra.map((footnoteObj, index) => {
78
+ return notesObjResult[footnoteObj.uid]
79
+ ? // take footnote if uid is found
80
+ `[${
81
+ Object.keys(notesObjResult).indexOf(footnoteObj.uid) + 1
82
+ }]`
83
+ : // if uid is not found look for it in other footnotes refs
84
+ `[${
85
+ Object.keys(notesObjResult).indexOf(
86
+ Object.keys(notesObjResult).find(
87
+ (noteKey) =>
88
+ notesObjResult[noteKey].refs &&
89
+ notesObjResult[noteKey].refs[data.uid],
90
+ ),
91
+ ) + 1
92
+ }]`;
93
+ }),
94
+ ].join('')
95
+ : // no extra footnotes (no multiples)
96
+ `[${Object.keys(notesObjResult).indexOf(data.uid) + 1}]`
97
+ : // footnotes not found in notesObjResult
98
+ data.extra
99
+ ? [
100
+ // look for it in other footnotes refs - parent
101
+ `[${
102
+ Object.keys(notesObjResult).indexOf(
103
+ Object.keys(notesObjResult).find(
104
+ (noteKey) =>
105
+ notesObjResult[noteKey].refs &&
106
+ notesObjResult[noteKey].refs[data.uid],
107
+ ),
108
+ ) + 1
109
+ }]`,
110
+ ...data.extra.map((footnoteObj, index) => {
111
+ return notesObjResult[footnoteObj.uid]
112
+ ? // footnotes from extra might be found in notesObjResult
113
+ `[${Object.keys(notesObjResult).indexOf(footnoteObj.uid) + 1}]`
114
+ : // if uid is not found look for it in other footnotes refs
115
+ `[${
116
+ Object.keys(notesObjResult).indexOf(
117
+ Object.keys(notesObjResult).find(
118
+ (noteKey) =>
119
+ notesObjResult[noteKey].refs &&
120
+ notesObjResult[noteKey].refs[data.uid],
121
+ ),
122
+ ) + 1
123
+ }]`;
124
+ }),
125
+ ].join('')
126
+ : // no extra footnotes
127
+ `[${
128
+ Object.keys(notesObjResult).indexOf(
129
+ Object.keys(notesObjResult).find(
130
+ (noteKey) =>
131
+ notesObjResult[noteKey].refs &&
132
+ notesObjResult[noteKey].refs[data.uid],
133
+ ),
134
+ ) + 1
135
+ }]`;
136
+ const findReferenceId =
137
+ // search within parent citations first, otherwise the uid might be inside a refs obj that comes before
138
+ Object.keys(notesObjResult).find(
139
+ (noteKey) => notesObjResult[noteKey].uid === uid,
140
+ ) ||
141
+ // if not found in parent, search in refs, it might be a footnote references multiple times
142
+ Object.keys(notesObjResult).find(
143
+ (noteKey) =>
144
+ notesObjResult[noteKey].uid === uid ||
145
+ (notesObjResult[noteKey].refs && notesObjResult[noteKey].refs[uid]),
146
+ );
46
147
 
47
148
  setCitationIndice(indice);
48
149
  setCitationRefId(findReferenceId);
@@ -51,11 +152,7 @@ export const FootnoteElement = (props) => {
51
152
  return (
52
153
  <>
53
154
  {mode === 'view' ? (
54
- <a
55
- href={`#footnote-${citationRefId}`}
56
- id={`ref-${uid}`}
57
- aria-describedby="footnote-label"
58
- >
155
+ <span id={`ref-${uid}`} aria-describedby="footnote-label">
59
156
  <Popup
60
157
  position="bottom left"
61
158
  trigger={
@@ -68,16 +165,57 @@ export const FootnoteElement = (props) => {
68
165
  {children}
69
166
  </span>
70
167
  }
168
+ hoverable
71
169
  >
72
170
  <Popup.Content>
73
- <div
74
- dangerouslySetInnerHTML={{
75
- __html: makeFootnote(data.footnote),
76
- }}
77
- />{' '}
171
+ <List divided relaxed selection>
172
+ <List.Item
173
+ as="a"
174
+ href={`#footnote-${citationRefId}`}
175
+ onClick={() =>
176
+ openAccordionIfContainsFootnoteReference(
177
+ `#footnote-${citationRefId}`,
178
+ )
179
+ }
180
+ key={`#footnote-${citationRefId}`}
181
+ >
182
+ <List.Content>
183
+ <List.Description>
184
+ <div
185
+ dangerouslySetInnerHTML={{
186
+ __html: makeFootnote(data.footnote),
187
+ }}
188
+ />{' '}
189
+ </List.Description>
190
+ </List.Content>
191
+ </List.Item>
192
+ {data.extra &&
193
+ data.extra.map((item) => (
194
+ <List.Item
195
+ as="a"
196
+ href={`#footnote-${item.zoteroId || item.uid}`}
197
+ onClick={() =>
198
+ openAccordionIfContainsFootnoteReference(
199
+ `#footnote-${item.zoteroId || item.uid}`,
200
+ )
201
+ }
202
+ key={`#footnote-${item.zoteroId || item.uid}`}
203
+ >
204
+ <List.Content>
205
+ <List.Description>
206
+ <div
207
+ dangerouslySetInnerHTML={{
208
+ __html: makeFootnote(item.footnote),
209
+ }}
210
+ />{' '}
211
+ </List.Description>
212
+ </List.Content>
213
+ </List.Item>
214
+ ))}
215
+ </List>
78
216
  </Popup.Content>
79
217
  </Popup>
80
- </a>
218
+ </span>
81
219
  ) : (
82
220
  <Popup
83
221
  position="bottom left"
@@ -91,13 +229,54 @@ export const FootnoteElement = (props) => {
91
229
  {children}
92
230
  </span>
93
231
  }
232
+ hoverable
94
233
  >
95
234
  <Popup.Content>
96
- <div
97
- dangerouslySetInnerHTML={{
98
- __html: makeFootnote(data.footnote),
99
- }}
100
- />{' '}
235
+ <List divided relaxed selection>
236
+ <List.Item
237
+ as="a"
238
+ href={`#footnote-${citationRefId}`}
239
+ onClick={() =>
240
+ openAccordionIfContainsFootnoteReference(
241
+ `#footnote-${citationRefId}`,
242
+ )
243
+ }
244
+ key={`#footnote-${citationRefId}`}
245
+ >
246
+ <List.Content>
247
+ <List.Description>
248
+ <div
249
+ dangerouslySetInnerHTML={{
250
+ __html: makeFootnote(data.footnote),
251
+ }}
252
+ />{' '}
253
+ </List.Description>
254
+ </List.Content>
255
+ </List.Item>
256
+ {data.extra &&
257
+ data.extra.map((item) => (
258
+ <List.Item
259
+ as="a"
260
+ href={`#footnote-${item.zoteroId || item.uid}`}
261
+ onClick={() =>
262
+ openAccordionIfContainsFootnoteReference(
263
+ `#footnote-${item.zoteroId || item.uid}`,
264
+ )
265
+ }
266
+ key={`#footnote-${item.zoteroId || item.uid}`}
267
+ >
268
+ <List.Content>
269
+ <List.Description>
270
+ <div
271
+ dangerouslySetInnerHTML={{
272
+ __html: makeFootnote(item.footnote),
273
+ }}
274
+ />{' '}
275
+ </List.Description>
276
+ </List.Content>
277
+ </List.Item>
278
+ ))}
279
+ </List>
101
280
  </Popup.Content>
102
281
  </Popup>
103
282
  )}
@@ -4,8 +4,8 @@ body {
4
4
  counter-reset: footnotes;
5
5
  }
6
6
 
7
- a[aria-describedby='footnote-label'] {
8
- color: inherit;
7
+ span[aria-describedby='footnote-label'] {
8
+ color: #004b87;
9
9
  cursor: default;
10
10
  outline: none;
11
11
  text-decoration: none;
@@ -18,30 +18,6 @@ a[aria-describedby='footnote-label'] {
18
18
  }
19
19
  }
20
20
 
21
- .citation-indice {
22
- color: inherit;
23
- counter-increment: footnotes;
24
- cursor: default;
25
- outline: none;
26
- text-decoration: none;
27
-
28
- &::after {
29
- margin-left: 2px;
30
- color: blue;
31
- content: '[' counter(footnotes) ']';
32
- cursor: pointer;
33
- font-size: 0.8em;
34
- text-decoration: underline;
35
- }
36
-
37
- &:focus {
38
- &::after {
39
- outline: thin dotted;
40
- outline-offset: 2px;
41
- }
42
- }
43
- }
44
-
45
21
  .footnote-edit-node {
46
22
  padding: 0px 4px;
47
23
  background-color: #e6f3ff;
@@ -51,7 +27,7 @@ a[aria-describedby='footnote-label'] {
51
27
  .footnote-edit-node::after,
52
28
  .citation-item::after {
53
29
  color: #0645ad;
54
- content: ' [' attr(data-footnote-indice) ']';
30
+ content: attr(data-footnote-indice);
55
31
  font-size: 75%;
56
32
  vertical-align: super;
57
33
  }