@manuscripts/body-editor 3.12.2 → 3.12.3
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/dist/cjs/components/affiliations/AffiliationItem.js +7 -11
- package/dist/cjs/components/affiliations/AffiliationList.js +3 -5
- package/dist/cjs/components/affiliations/AffiliationsModal.js +13 -4
- package/dist/cjs/components/authors/AuthorList.js +4 -2
- package/dist/cjs/components/authors/AuthorsModal.js +10 -3
- package/dist/cjs/components/authors/DraggableAuthor.js +7 -11
- package/dist/cjs/components/awards/AwardForm.js +108 -100
- package/dist/cjs/components/hooks/use-debounce.js +15 -0
- package/dist/cjs/components/modal-drawer/GenericDrawerGroup.js +1 -0
- package/dist/cjs/components/references/CitationEditor.js +6 -7
- package/dist/cjs/components/references/ImportBibliographyForm.js +12 -3
- package/dist/cjs/components/references/ReferenceForm/PersonDropDown.js +1 -1
- package/dist/cjs/components/references/ReferenceForm/styled-components.js +12 -3
- package/dist/cjs/components/references/ReferenceSearch.js +2 -2
- package/dist/cjs/components/references/ReferenceSearchResults.js +58 -11
- package/dist/cjs/components/references/ReferenceSearchSection.js +3 -11
- package/dist/cjs/components/references/ReferencesModal.js +5 -4
- package/dist/cjs/components/toolbar/InsertTableDialog.js +11 -3
- package/dist/cjs/components/views/CrossReferenceItems.js +8 -14
- package/dist/cjs/components/views/FootnotesSelector.js +6 -6
- package/dist/cjs/components/views/InsertSpecialCharacter.js +24 -10
- package/dist/cjs/components/views/LinkForm.js +8 -1
- package/dist/cjs/lib/popper.js +4 -2
- package/dist/cjs/versions.js +1 -1
- package/dist/cjs/views/cross_reference_editable.js +1 -0
- package/dist/cjs/views/equation_editable.js +6 -0
- package/dist/cjs/views/inline_equation_editable.js +6 -0
- package/dist/cjs/views/inline_footnote.js +1 -0
- package/dist/es/components/affiliations/AffiliationItem.js +8 -12
- package/dist/es/components/affiliations/AffiliationList.js +3 -5
- package/dist/es/components/affiliations/AffiliationsModal.js +14 -5
- package/dist/es/components/authors/AuthorList.js +4 -2
- package/dist/es/components/authors/AuthorsModal.js +11 -4
- package/dist/es/components/authors/DraggableAuthor.js +8 -12
- package/dist/es/components/awards/AwardForm.js +110 -102
- package/dist/es/components/hooks/use-debounce.js +15 -1
- package/dist/es/components/modal-drawer/GenericDrawerGroup.js +2 -1
- package/dist/es/components/references/CitationEditor.js +7 -8
- package/dist/es/components/references/ImportBibliographyForm.js +14 -5
- package/dist/es/components/references/ReferenceForm/PersonDropDown.js +1 -1
- package/dist/es/components/references/ReferenceForm/styled-components.js +13 -4
- package/dist/es/components/references/ReferenceSearch.js +3 -3
- package/dist/es/components/references/ReferenceSearchResults.js +26 -12
- package/dist/es/components/references/ReferenceSearchSection.js +4 -12
- package/dist/es/components/references/ReferencesModal.js +6 -5
- package/dist/es/components/toolbar/InsertTableDialog.js +11 -3
- package/dist/es/components/views/CrossReferenceItems.js +9 -15
- package/dist/es/components/views/FootnotesSelector.js +7 -7
- package/dist/es/components/views/InsertSpecialCharacter.js +25 -11
- package/dist/es/components/views/LinkForm.js +9 -2
- package/dist/es/lib/popper.js +4 -2
- package/dist/es/versions.js +1 -1
- package/dist/es/views/cross_reference_editable.js +1 -0
- package/dist/es/views/equation_editable.js +6 -0
- package/dist/es/views/inline_equation_editable.js +6 -0
- package/dist/es/views/inline_footnote.js +1 -0
- package/dist/types/components/hooks/use-debounce.d.ts +1 -0
- package/dist/types/components/references/ReferenceForm/styled-components.d.ts +11 -1
- package/dist/types/components/references/ReferenceSearchResults.d.ts +20 -2
- package/dist/types/versions.d.ts +1 -1
- package/package.json +2 -2
- package/styles/popper.css +2 -0
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { Avatar, CorrespondingAuthorIcon, CrclTickAnimation, DeleteIcon, DraggableIcon, } from '@manuscripts/style-guide';
|
|
16
|
+
import { Avatar, CorrespondingAuthorIcon, CrclTickAnimation, DeleteIcon, DraggableIcon, SecondaryIconButton, withNavigableListItem, } from '@manuscripts/style-guide';
|
|
17
17
|
import React, { useRef, useState } from 'react';
|
|
18
18
|
import { useDrag, useDrop } from 'react-dnd';
|
|
19
19
|
import styled from 'styled-components';
|
|
20
20
|
import { authorLabel } from '../../lib/authors';
|
|
21
21
|
import { getDropSide } from '../../lib/dnd';
|
|
22
|
-
const AuthorContainer = styled.div `
|
|
22
|
+
const AuthorContainer = withNavigableListItem(styled.div `
|
|
23
23
|
padding: ${(props) => props.theme.grid.unit * 2}px 0
|
|
24
24
|
${(props) => props.theme.grid.unit * 2}px;
|
|
25
25
|
display: flex;
|
|
@@ -53,7 +53,7 @@ const AuthorContainer = styled.div `
|
|
|
53
53
|
cursor: grabbing;
|
|
54
54
|
border-bottom-color: ${(props) => props.theme.colors.brand.dark};
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
`);
|
|
57
57
|
const AvatarContainer = styled.div `
|
|
58
58
|
display: inline-flex;
|
|
59
59
|
position: relative;
|
|
@@ -86,15 +86,11 @@ const DragHandle = styled(DraggableIcon) `
|
|
|
86
86
|
margin-left: -4px;
|
|
87
87
|
margin-right: -12px;
|
|
88
88
|
`;
|
|
89
|
-
const RemoveButton = styled
|
|
90
|
-
display: flex;
|
|
91
|
-
align-items: center;
|
|
92
|
-
margin-right: 8px;
|
|
89
|
+
const RemoveButton = styled(SecondaryIconButton) `
|
|
93
90
|
svg {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
fill: #6e6e6e;
|
|
91
|
+
path[fill='white'] {
|
|
92
|
+
fill: white;
|
|
93
|
+
}
|
|
98
94
|
}
|
|
99
95
|
`;
|
|
100
96
|
const StyledCrclTickAnimation = styled(CrclTickAnimation) `
|
|
@@ -162,6 +158,6 @@ export const DraggableAuthor = React.memo(({ author, isSelected, onClick, onDele
|
|
|
162
158
|
React.createElement(AuthorNotes, { "data-cy": "author-notes" }, author.isCorresponding && (React.createElement(AuthorBadge, null,
|
|
163
159
|
React.createElement(CorrespondingAuthorIcon, null))))),
|
|
164
160
|
React.createElement(AuthorName, { "data-cy": "author-name" }, authorLabel(author)),
|
|
165
|
-
isSelected && (React.createElement(RemoveButton, { onClick: () => onDelete(), "data-tooltip-content": "Delete" },
|
|
161
|
+
isSelected && (React.createElement(RemoveButton, { size: 13, onClick: () => onDelete(), "data-tooltip-content": "Delete" },
|
|
166
162
|
React.createElement(DeleteIcon, { fill: '#6E6E6E' })))));
|
|
167
163
|
});
|
|
@@ -13,58 +13,49 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { MultiValueInput, PrimaryButton, SearchIcon, SecondaryButton, TextField, FormRow, Label,
|
|
16
|
+
import { MultiValueInput, PrimaryButton, SearchIcon, SecondaryButton, TextField, FormRow, Label, ButtonGroup, } from '@manuscripts/style-guide';
|
|
17
17
|
import { Field, Formik } from 'formik';
|
|
18
|
-
import React, {
|
|
19
|
-
import styled from 'styled-components';
|
|
18
|
+
import React, { useMemo, useRef } from 'react';
|
|
19
|
+
import styled, { useTheme } from 'styled-components';
|
|
20
20
|
import { ChangeHandlingForm } from '../ChangeHandlingForm';
|
|
21
|
-
import {
|
|
21
|
+
import { components, } from 'react-select';
|
|
22
|
+
import AsyncCreatableSelect from 'react-select/async-creatable';
|
|
23
|
+
import useDebounced from '../hooks/use-debounce';
|
|
24
|
+
const loadOptions = async (inputValue) => {
|
|
25
|
+
try {
|
|
26
|
+
const formattedQuery = inputValue.replace(/\s+/g, '+');
|
|
27
|
+
const response = await fetch(`https://api.crossref.org/funders?query=${encodeURIComponent(formattedQuery)}`);
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
console.log(`HTTP error! status: ${response.status}`);
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
const funderOptions = data.message.items.map((funder) => ({
|
|
34
|
+
value: funder.name,
|
|
35
|
+
label: funder.name,
|
|
36
|
+
}));
|
|
37
|
+
return funderOptions.sort((a, b) => a.label.localeCompare(b.label));
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
console.error('Error fetching funders:', e);
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
};
|
|
22
44
|
export const AwardForm = ({ values, onSave, onCancel, onChange, }) => {
|
|
23
|
-
const [funders, setFunders] = useState([]);
|
|
24
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
25
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
26
45
|
const formRef = useRef(null);
|
|
27
46
|
const primaryButtonText = values.source ? 'Update funder' : 'Add funder';
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const query = debouncedSearchQuery;
|
|
32
|
-
if (!query) {
|
|
33
|
-
setFunders([]);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
setIsLoading(true);
|
|
37
|
-
try {
|
|
38
|
-
const formattedQuery = query.replace(/\s+/g, '+');
|
|
39
|
-
const response = await fetch(`https://api.crossref.org/funders?query=${encodeURIComponent(formattedQuery)}`);
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
42
|
-
}
|
|
43
|
-
const data = await response.json();
|
|
44
|
-
const funderOptions = data.message.items.map((funder) => ({
|
|
45
|
-
value: funder.name,
|
|
46
|
-
label: funder.name,
|
|
47
|
-
}));
|
|
48
|
-
funderOptions.sort((a, b) => a.label.localeCompare(b.label));
|
|
49
|
-
setFunders(funderOptions);
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
console.error('Error fetching funders:', error);
|
|
53
|
-
setFunders([]);
|
|
54
|
-
}
|
|
55
|
-
finally {
|
|
56
|
-
setIsLoading(false);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
searchFunders();
|
|
60
|
-
}, [debouncedSearchQuery]);
|
|
61
|
-
const handleFunderSearch = (event) => {
|
|
62
|
-
setSearchQuery(event.target.value);
|
|
63
|
-
};
|
|
47
|
+
const themes = useTheme();
|
|
48
|
+
const customStyles = useMemo(() => getCustomStyles(themes), [themes]);
|
|
49
|
+
const handleOnLoadFunders = useDebounced(async (inputValue) => loadOptions(inputValue), 300);
|
|
64
50
|
const handleCancel = () => {
|
|
65
51
|
formRef.current?.resetForm();
|
|
66
52
|
onCancel();
|
|
67
53
|
};
|
|
54
|
+
const handleInputChange = (value, action) => {
|
|
55
|
+
if (action.action === 'input-change') {
|
|
56
|
+
formRef.current?.setFieldValue('source', value);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
68
59
|
const validate = (values) => {
|
|
69
60
|
const errors = {};
|
|
70
61
|
if (!values.source) {
|
|
@@ -80,18 +71,14 @@ export const AwardForm = ({ values, onSave, onCancel, onChange, }) => {
|
|
|
80
71
|
React.createElement(Field, { type: "hidden", name: "id" }),
|
|
81
72
|
React.createElement(FormRow, null,
|
|
82
73
|
React.createElement(Label, { htmlFor: 'source' }, "Funder name"),
|
|
83
|
-
React.createElement(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
funders.length > 0 && (React.createElement(SearchResults, null, funders.map((funder) => (React.createElement(SearchResultItem, { key: funder.value, onClick: () => {
|
|
92
|
-
formik.setFieldValue('source', funder.value);
|
|
93
|
-
setFunders([]);
|
|
94
|
-
} }, funder.label)))))),
|
|
74
|
+
React.createElement(Field, { name: "source" }, (props) => (React.createElement(AsyncCreatableSelect, { inputId: 'source', loadOptions: handleOnLoadFunders, onInputChange: handleInputChange, autoFocus: true, onChange: (option) => option &&
|
|
75
|
+
formRef.current?.setFieldValue('source', option.value), inputValue: props.field.value, value: null, isValidNewOption: () => false, components: {
|
|
76
|
+
Input,
|
|
77
|
+
Control,
|
|
78
|
+
Option,
|
|
79
|
+
DropdownIndicator: null,
|
|
80
|
+
IndicatorSeparator: null,
|
|
81
|
+
}, styles: customStyles, placeholder: "Search for funder..." }))),
|
|
95
82
|
formik.errors.source && formik.touched.source && (React.createElement(ErrorText, null, formik.errors.source))),
|
|
96
83
|
React.createElement(FormRow, null,
|
|
97
84
|
React.createElement(Label, { htmlFor: 'code' }, "Grant number"),
|
|
@@ -102,68 +89,89 @@ export const AwardForm = ({ values, onSave, onCancel, onChange, }) => {
|
|
|
102
89
|
React.createElement(Label, { htmlFor: 'recipient' }, "Recipient name"),
|
|
103
90
|
React.createElement(Field, { name: "recipient" }, (props) => (React.createElement(TextField, { id: "recipient", placeholder: "Enter full name", ...props.field })))),
|
|
104
91
|
React.createElement(FormRow, null,
|
|
105
|
-
React.createElement(
|
|
92
|
+
React.createElement(FormActions, null,
|
|
106
93
|
React.createElement(SecondaryButton, { onClick: handleCancel }, "Cancel"),
|
|
107
94
|
React.createElement(PrimaryButton, { type: "submit", disabled: !formik.dirty || formik.isSubmitting }, primaryButtonText)))));
|
|
108
95
|
}));
|
|
109
96
|
};
|
|
110
|
-
const SearchContainer = styled.div `
|
|
111
|
-
position: relative;
|
|
112
|
-
width: 100%;
|
|
113
|
-
`;
|
|
114
97
|
const SearchIconContainer = styled.span `
|
|
115
98
|
display: flex;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
transform: translateY(-50%);
|
|
120
|
-
z-index: 2;
|
|
99
|
+
align-items: center;
|
|
100
|
+
padding-left: ${(props) => props.theme.grid.unit * 4}px;
|
|
101
|
+
padding-right: ${(props) => props.theme.grid.unit * 2}px;
|
|
121
102
|
|
|
122
103
|
path {
|
|
123
|
-
stroke: ${(props) => props.
|
|
104
|
+
stroke: ${(props) => props.isFocused
|
|
105
|
+
? props.theme.colors.brand.medium
|
|
106
|
+
: props.theme.colors.text.primary};
|
|
124
107
|
}
|
|
108
|
+
`;
|
|
109
|
+
const OptionWrapper = styled.div `
|
|
110
|
+
padding-left: ${(props) => props.theme.grid.unit * 4}px;
|
|
111
|
+
padding-top: ${(props) => props.theme.grid.unit * 2}px;
|
|
112
|
+
padding-bottom: ${(props) => props.theme.grid.unit * 2}px;
|
|
125
113
|
|
|
126
|
-
${
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
stroke: ${(props) => props.theme.colors.brand.medium};
|
|
114
|
+
background-color: ${(props) => {
|
|
115
|
+
if (props.selected) {
|
|
116
|
+
return props.theme.colors.background.selected;
|
|
130
117
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
background-color: ${(props) => props.theme.colors.background.fifth};
|
|
138
|
-
}
|
|
139
|
-
`;
|
|
140
|
-
const SearchResults = styled.div `
|
|
141
|
-
position: absolute;
|
|
142
|
-
top: 100%;
|
|
143
|
-
left: 0;
|
|
144
|
-
right: 0;
|
|
145
|
-
max-height: 200px;
|
|
146
|
-
overflow-y: auto;
|
|
147
|
-
background: white;
|
|
148
|
-
border: 1px solid ${(props) => props.theme.colors.border.secondary};
|
|
149
|
-
border-radius: 4px;
|
|
150
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
151
|
-
z-index: 1000;
|
|
152
|
-
`;
|
|
153
|
-
const SearchResultItem = styled.div `
|
|
154
|
-
padding: 8px 12px;
|
|
155
|
-
cursor: pointer;
|
|
118
|
+
if (props.focused) {
|
|
119
|
+
return props.theme.colors.background.fifth;
|
|
120
|
+
}
|
|
121
|
+
return 'transparent';
|
|
122
|
+
}};
|
|
123
|
+
|
|
156
124
|
&:hover {
|
|
157
125
|
background-color: ${(props) => props.theme.colors.background.fifth};
|
|
158
126
|
}
|
|
159
127
|
`;
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
128
|
+
const Option = ({ innerProps, innerRef, data, isFocused, isSelected, }) => {
|
|
129
|
+
return (React.createElement(OptionWrapper, { ...innerProps, ref: innerRef, focused: isFocused, selected: isSelected }, data.label));
|
|
130
|
+
};
|
|
131
|
+
const Control = ({ children, ...props }) => {
|
|
132
|
+
return (React.createElement(components.Control, { ...props },
|
|
133
|
+
React.createElement(SearchIconContainer, { isFocused: props.isFocused },
|
|
134
|
+
React.createElement(SearchIcon, null)),
|
|
135
|
+
children));
|
|
136
|
+
};
|
|
137
|
+
const Input = (props) => (React.createElement(components.Input, { ...props, isHidden: false }));
|
|
138
|
+
const getCustomStyles = (theme) => ({
|
|
139
|
+
control: (provided, state) => ({
|
|
140
|
+
...provided,
|
|
141
|
+
minHeight: '40px',
|
|
142
|
+
borderRadius: '4px',
|
|
143
|
+
border: `${state.isFocused ? '2px' : '1px'} solid ${state.isFocused ? theme.colors.brand.default : theme.colors.border.secondary}`,
|
|
144
|
+
backgroundColor: state.isFocused || state.menuIsOpen
|
|
145
|
+
? theme.colors.background.fifth
|
|
146
|
+
: theme.colors.background.primary,
|
|
147
|
+
boxShadow: 'none',
|
|
148
|
+
cursor: 'text',
|
|
149
|
+
transition: 'background-color 0.2s',
|
|
150
|
+
'&:hover': {
|
|
151
|
+
backgroundColor: theme.colors.background.fifth,
|
|
152
|
+
},
|
|
153
|
+
}),
|
|
154
|
+
input: (provided) => ({
|
|
155
|
+
...provided,
|
|
156
|
+
margin: 0,
|
|
157
|
+
padding: 0,
|
|
158
|
+
color: theme.colors.text.primary,
|
|
159
|
+
fontSize: theme.font.size.normal,
|
|
160
|
+
fontFamily: theme.font.family.sans,
|
|
161
|
+
}),
|
|
162
|
+
placeholder: (provided, state) => ({
|
|
163
|
+
...provided,
|
|
164
|
+
margin: 0,
|
|
165
|
+
fontStyle: 'italic',
|
|
166
|
+
color: state.isFocused ? '#c9c9c9' : theme.colors.text.secondary,
|
|
167
|
+
fontSize: theme.font.size.medium,
|
|
168
|
+
fontFamily: theme.font.family.sans,
|
|
169
|
+
fontWeight: theme.font.weight.normal,
|
|
170
|
+
lineHeight: theme.font.lineHeight.large,
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
const FormActions = styled(ButtonGroup) `
|
|
174
|
+
margin: ${(props) => props.theme.grid.unit * 2}px;
|
|
167
175
|
`;
|
|
168
176
|
const ErrorText = styled.div `
|
|
169
177
|
color: ${(props) => props.theme.colors.text.error};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { useEffect, useState } from 'react';
|
|
16
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
17
17
|
export const useDebounce = (value, delay) => {
|
|
18
18
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
19
19
|
useEffect(() => {
|
|
@@ -26,3 +26,17 @@ export const useDebounce = (value, delay) => {
|
|
|
26
26
|
}, [value, delay]);
|
|
27
27
|
return debouncedValue;
|
|
28
28
|
};
|
|
29
|
+
export default function useDebounced(fn, delay) {
|
|
30
|
+
const timeoutRef = useRef(null);
|
|
31
|
+
return useCallback((arg) => {
|
|
32
|
+
const timeout = timeoutRef.current;
|
|
33
|
+
if (timeout) {
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
}
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
timeoutRef.current = setTimeout(async () => {
|
|
38
|
+
resolve(await fn(arg));
|
|
39
|
+
}, delay);
|
|
40
|
+
});
|
|
41
|
+
}, [fn, delay]);
|
|
42
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { SelectedItemsBox } from '@manuscripts/style-guide';
|
|
16
|
+
import { outlineStyle, SelectedItemsBox, } from '@manuscripts/style-guide';
|
|
17
17
|
import React from 'react';
|
|
18
18
|
import styled from 'styled-components';
|
|
19
19
|
export function DrawerGroup({ removeItem, selectedItems, onSelect, items, showDrawer, setShowDrawer, title, labelField, Drawer, cy, Icon, buttonText, }) {
|
|
@@ -43,4 +43,5 @@ export const AssignButton = styled.button `
|
|
|
43
43
|
&:hover {
|
|
44
44
|
opacity: 0.8;
|
|
45
45
|
}
|
|
46
|
+
${outlineStyle}
|
|
46
47
|
`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ButtonGroup, Category, DeleteSolidIcon, Dialog, EditIcon, IconButton,
|
|
1
|
+
import { ButtonGroup, Category, DeleteSolidIcon, Dialog, EditIcon, IconButton, PrimaryButton, SecondaryButton, withFocusTrap, } from '@manuscripts/style-guide';
|
|
2
2
|
import { generateNodeID, schema, } from '@manuscripts/transform';
|
|
3
3
|
import React, { useMemo, useReducer, useState } from 'react';
|
|
4
4
|
import styled from 'styled-components';
|
|
@@ -9,6 +9,7 @@ import { ImportBibliographyModal } from './ImportBibliographyModal';
|
|
|
9
9
|
import { ReferenceLine } from './ReferenceLine';
|
|
10
10
|
import { ReferenceSearch } from './ReferenceSearch';
|
|
11
11
|
import { ReferencesModal } from './ReferencesModal';
|
|
12
|
+
const Container = withFocusTrap(styled.div ``);
|
|
12
13
|
const CitedItemActions = styled.div `
|
|
13
14
|
display: flex;
|
|
14
15
|
align-items: center;
|
|
@@ -41,10 +42,10 @@ const ActionButton = styled(IconButton).attrs({
|
|
|
41
42
|
const EditReferenceButton = styled(ActionButton) `
|
|
42
43
|
margin-right: ${(props) => props.theme.grid.unit * 3}px;
|
|
43
44
|
`;
|
|
44
|
-
const Actions = styled
|
|
45
|
+
const Actions = styled(ButtonGroup) `
|
|
45
46
|
margin: ${(props) => props.theme.grid.unit * 4}px;
|
|
46
47
|
display: flex;
|
|
47
|
-
justify-content:
|
|
48
|
+
justify-content: flex-end;
|
|
48
49
|
align-items: center;
|
|
49
50
|
`;
|
|
50
51
|
const itemsReducer = attrsReducer();
|
|
@@ -132,7 +133,7 @@ export const CitationEditor = ({ query, rids: $rids, items: $items, citationCoun
|
|
|
132
133
|
if (!rids.length) {
|
|
133
134
|
return (React.createElement(ReferenceSearch, { query: query, sources: sources, items: items, onAdd: handleAdd, onImport: handleImport, onCite: handleCite, onCancel: onCancel }));
|
|
134
135
|
}
|
|
135
|
-
return (React.createElement(
|
|
136
|
+
return (React.createElement(Container, null,
|
|
136
137
|
React.createElement(Dialog, { isOpen: deleteDialog.show, category: Category.confirmation, header: "Remove cited item", message: "Are you sure you want to remove this cited item? It will still exist in the reference list.", actions: {
|
|
137
138
|
secondary: {
|
|
138
139
|
action: () => {
|
|
@@ -156,8 +157,6 @@ export const CitationEditor = ({ query, rids: $rids, items: $items, citationCoun
|
|
|
156
157
|
React.createElement(ActionButton, { disabled: !canEdit, onClick: () => setDeleteDialog({ show: true, id: item.id }) },
|
|
157
158
|
React.createElement(DeleteSolidIcon, { className: 'remove-icon' }))))))),
|
|
158
159
|
React.createElement(Actions, null,
|
|
159
|
-
React.createElement(
|
|
160
|
-
React.createElement(
|
|
161
|
-
React.createElement(SecondaryButton, { onClick: onCancel }, "Done"),
|
|
162
|
-
React.createElement(PrimaryButton, { disabled: !canEdit, onClick: () => setSearching(true) }, "Add Citation")))));
|
|
160
|
+
React.createElement(SecondaryButton, { onClick: onCancel }, "Done"),
|
|
161
|
+
React.createElement(PrimaryButton, { disabled: !canEdit, onClick: () => setSearching(true) }, "Add Citation"))));
|
|
163
162
|
};
|
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { FormActionsBar, FormRow, Label, PrimaryButton, SecondaryButton, TextArea, } from '@manuscripts/style-guide';
|
|
16
|
+
import { FormActionsBar, FormRow, Label, outlineStyle, PrimaryButton, SecondaryButton, TextArea, } from '@manuscripts/style-guide';
|
|
17
17
|
import { useFormik } from 'formik';
|
|
18
18
|
import { debounce } from 'lodash';
|
|
19
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
19
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
20
20
|
import styled, { css } from 'styled-components';
|
|
21
21
|
import { importBibliographyItems } from '../../lib/references';
|
|
22
22
|
import { ReferenceLine } from './ReferenceLine';
|
|
23
23
|
export const ImportBibliographyForm = ({ onCancel, onSave, }) => {
|
|
24
24
|
const [dragging, setDragging] = useState(false);
|
|
25
|
+
const fileInputRef = useRef(null);
|
|
25
26
|
const formik = useFormik({
|
|
26
27
|
initialValues: {
|
|
27
28
|
content: '',
|
|
@@ -80,13 +81,19 @@ export const ImportBibliographyForm = ({ onCancel, onSave, }) => {
|
|
|
80
81
|
};
|
|
81
82
|
reader.readAsText(file);
|
|
82
83
|
};
|
|
84
|
+
const handleOnKeyDown = (e) => {
|
|
85
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
fileInputRef?.current?.click();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
83
90
|
return (React.createElement("form", { onSubmit: formik.handleSubmit, onReset: formik.handleReset },
|
|
84
91
|
React.createElement(FormRow, null,
|
|
85
|
-
React.createElement(DropContainer, { onDrop: handleDrop, onDragOver: (e) => {
|
|
92
|
+
React.createElement(DropContainer, { tabIndex: 0, role: "button", onDrop: handleDrop, onDragOver: (e) => {
|
|
86
93
|
e.preventDefault();
|
|
87
94
|
setDragging(true);
|
|
88
|
-
}, onDragLeave: () => setDragging(false), active: dragging },
|
|
89
|
-
React.createElement("input", { id: "file", name: "file", type: "file", onChange: handleFileChange, style: { display: 'none' } }),
|
|
95
|
+
}, onDragLeave: () => setDragging(false), onKeyDown: handleOnKeyDown, active: dragging },
|
|
96
|
+
React.createElement("input", { id: "file", name: "file", type: "file", ref: fileInputRef, onChange: handleFileChange, style: { display: 'none' } }),
|
|
90
97
|
React.createElement(Label, { htmlFor: "file" }, "Drag & Drop or Click here to upload a file."))),
|
|
91
98
|
React.createElement(FormRow, null,
|
|
92
99
|
React.createElement(Label, { htmlFor: "content" }, "Alternatively, you can directly Copy&Paste below, the text of the bibliography items."),
|
|
@@ -134,4 +141,6 @@ const DropContainer = styled.div `
|
|
|
134
141
|
font-family: ${(props) => props.theme.font.family.Lato};
|
|
135
142
|
color: ${(props) => props.theme.colors.text.onLight};
|
|
136
143
|
}
|
|
144
|
+
|
|
145
|
+
${outlineStyle}
|
|
137
146
|
`;
|
|
@@ -13,7 +13,7 @@ export const PersonDropDown = (props) => {
|
|
|
13
13
|
React.createElement(ToggleButton, { type: "button", onClick: () => setIsOpen(!isOpen), isOpen: isOpen },
|
|
14
14
|
React.createElement(DropdownIndicator, null),
|
|
15
15
|
title),
|
|
16
|
-
React.createElement(RemoveButton, { type: "button", "aria-label": "Delete this editor", onClick: () => remove(index) },
|
|
16
|
+
React.createElement(RemoveButton, { size: 13, type: "button", "aria-label": "Delete this editor", onClick: () => remove(index) },
|
|
17
17
|
React.createElement(DeleteIcon, null))),
|
|
18
18
|
isOpen && (React.createElement(PersonForm, null,
|
|
19
19
|
React.createElement(FormRow, null,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArrowDownIcon, IconButton, TextArea, TextField, } from '@manuscripts/style-guide';
|
|
1
|
+
import { ArrowDownIcon, IconButton, outlineStyle, SecondaryIconButton, TextArea, TextField, } from '@manuscripts/style-guide';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
export const Button = styled(IconButton).attrs({
|
|
4
4
|
defaultColor: true,
|
|
@@ -86,7 +86,7 @@ export const ToggleButton = styled.button `
|
|
|
86
86
|
text-align: left;
|
|
87
87
|
font-family: ${(props) => props.theme.font.family.sans};
|
|
88
88
|
font-size: 1rem;
|
|
89
|
-
|
|
89
|
+
margin: 0.6em;
|
|
90
90
|
|
|
91
91
|
outline: none;
|
|
92
92
|
|
|
@@ -97,11 +97,20 @@ export const ToggleButton = styled.button `
|
|
|
97
97
|
svg {
|
|
98
98
|
transform: ${(props) => (props.isOpen ? 'rotateX(180deg)' : 'initial')};
|
|
99
99
|
}
|
|
100
|
+
${outlineStyle}
|
|
100
101
|
`;
|
|
101
|
-
export const RemoveButton = styled
|
|
102
|
+
export const RemoveButton = styled(SecondaryIconButton) `
|
|
102
103
|
border: none;
|
|
103
104
|
background: transparent;
|
|
104
105
|
padding: 0;
|
|
105
|
-
|
|
106
106
|
outline: none;
|
|
107
|
+
svg {
|
|
108
|
+
path[fill='white'] {
|
|
109
|
+
fill: white;
|
|
110
|
+
}
|
|
111
|
+
rect[fill],
|
|
112
|
+
path:not([fill='white']) {
|
|
113
|
+
fill: #f35143;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
107
116
|
`;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { AddNewIcon, ButtonGroup, IconTextButton, PrimaryButton, SecondaryButton, UploadIcon, } from '@manuscripts/style-guide';
|
|
16
|
+
import { AddNewIcon, ButtonGroup, IconTextButton, PrimaryButton, SecondaryButton, UploadIcon, withFocusTrap, } from '@manuscripts/style-guide';
|
|
17
17
|
import { debounce } from 'lodash';
|
|
18
18
|
import React, { useState } from 'react';
|
|
19
19
|
import styled from 'styled-components';
|
|
@@ -31,10 +31,10 @@ const Actions = styled(ButtonGroup) `
|
|
|
31
31
|
justify-content: space-between;
|
|
32
32
|
padding: ${(props) => props.theme.grid.unit * 4}px;
|
|
33
33
|
`;
|
|
34
|
-
const Container = styled.div `
|
|
34
|
+
const Container = withFocusTrap(styled.div `
|
|
35
35
|
flex: 1;
|
|
36
36
|
font-family: ${(props) => props.theme.font.family.sans};
|
|
37
|
-
|
|
37
|
+
`);
|
|
38
38
|
const AddReferenceActions = styled(ButtonGroup) `
|
|
39
39
|
button {
|
|
40
40
|
margin-right: ${(props) => props.theme.grid.unit * 8}px;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { AddedIcon, AddIcon, SecondaryButton } from '@manuscripts/style-guide';
|
|
17
|
-
import React from 'react';
|
|
16
|
+
import { AddedIcon, AddIcon, SecondaryButton, withListNavigation, withNavigableListItem, } from '@manuscripts/style-guide';
|
|
17
|
+
import React, { useRef } from 'react';
|
|
18
18
|
import styled from 'styled-components';
|
|
19
19
|
import { ReferenceLine } from './ReferenceLine';
|
|
20
20
|
const StatusIcon = styled.div `
|
|
@@ -36,12 +36,14 @@ const MoreButton = styled(SecondaryButton) `
|
|
|
36
36
|
margin-left: ${(props) => props.theme.grid.unit * 4}px;
|
|
37
37
|
color: ${(props) => props.theme.colors.button.default.color.default};
|
|
38
38
|
`;
|
|
39
|
-
export const ReferenceSearchResultsContainer = styled.div `
|
|
40
|
-
padding:
|
|
39
|
+
export const ReferenceSearchResultsContainer = withListNavigation(styled.div `
|
|
40
|
+
padding: ${(props) => props.theme.grid.unit * 2}px
|
|
41
|
+
${(props) => props.theme.grid.unit * 4}px;
|
|
41
42
|
flex: 1;
|
|
42
43
|
overflow-y: auto;
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
outline: none;
|
|
45
|
+
`);
|
|
46
|
+
export const ReferenceSearchResult = withNavigableListItem(styled.div `
|
|
45
47
|
cursor: pointer;
|
|
46
48
|
padding: ${(props) => props.theme.grid.unit * 2}px 0;
|
|
47
49
|
display: flex;
|
|
@@ -49,9 +51,21 @@ export const ReferenceSearchResult = styled.div `
|
|
|
49
51
|
&:not(:last-of-type) {
|
|
50
52
|
border-bottom: 1px solid #f6f6f6;
|
|
51
53
|
}
|
|
52
|
-
|
|
53
|
-
export const ReferenceSearchResults = ({ items, total, isSelected, onSelect, onShowMore }) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
`);
|
|
55
|
+
export const ReferenceSearchResults = ({ items, total, isSelected, onSelect, onShowMore }) => {
|
|
56
|
+
const list = useRef(null);
|
|
57
|
+
const onExpandReferenceList = () => {
|
|
58
|
+
const element = list.current;
|
|
59
|
+
if (element) {
|
|
60
|
+
element.setAttribute('tabindex', '0');
|
|
61
|
+
element.focus();
|
|
62
|
+
element.setAttribute('tabindex', '-1');
|
|
63
|
+
}
|
|
64
|
+
onShowMore();
|
|
65
|
+
};
|
|
66
|
+
return (React.createElement(ReferenceSearchResultsContainer, { ref: list },
|
|
67
|
+
items.map((item) => (React.createElement(ReferenceSearchResult, { onClick: () => onSelect(item), key: item.id, "data-cy": `${isSelected(item) ? 'selected-reference' : 'reference'}` },
|
|
68
|
+
React.createElement(StatusIcon, null, isSelected(item) ? (React.createElement(AddedIcon, { width: 24, height: 24 })) : (React.createElement(AddIcon, { width: 24, height: 24 }))),
|
|
69
|
+
React.createElement(ReferenceLine, { item: item })))),
|
|
70
|
+
items.length < 25 && total > items.length ? (React.createElement(MoreButton, { onClick: onExpandReferenceList, "data-cy": 'more-button' }, "Show more")) : undefined));
|
|
71
|
+
};
|
|
@@ -13,24 +13,16 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { TriangleCollapsedIcon, TriangleExpandedIcon, } from '@manuscripts/style-guide';
|
|
16
|
+
import { IconTextButton, TriangleCollapsedIcon, TriangleExpandedIcon, } from '@manuscripts/style-guide';
|
|
17
17
|
import React, { useEffect, useState } from 'react';
|
|
18
18
|
import styled from 'styled-components';
|
|
19
19
|
import { ReferenceSearchResults } from './ReferenceSearchResults';
|
|
20
20
|
import { ReferenceSearchResultsPlaceholder } from './ReferenceSearchResultsPlaceholder';
|
|
21
|
-
const SearchSourceLabel = styled
|
|
22
|
-
margin:
|
|
21
|
+
const SearchSourceLabel = styled(IconTextButton) `
|
|
22
|
+
margin: ${(props) => props.theme.grid.unit * 2}px
|
|
23
|
+
${(props) => props.theme.grid.unit * 4}px
|
|
23
24
|
${(props) => props.theme.grid.unit * 2}px;
|
|
24
25
|
color: ${(props) => props.theme.colors.text.secondary};
|
|
25
|
-
cursor: pointer;
|
|
26
|
-
|
|
27
|
-
display: flex;
|
|
28
|
-
align-items: center;
|
|
29
|
-
|
|
30
|
-
.icon {
|
|
31
|
-
margin-left: 8px;
|
|
32
|
-
margin-right: 13px;
|
|
33
|
-
}
|
|
34
26
|
|
|
35
27
|
&:hover {
|
|
36
28
|
color: ${(props) => props.theme.colors.text.muted};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Category, CitationCountIcon, CloseButton, Dialog, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, useScrollDetection, } from '@manuscripts/style-guide';
|
|
1
|
+
import { Category, CitationCountIcon, CloseButton, Dialog, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, useScrollDetection, withListNavigation, withNavigableListItem, } from '@manuscripts/style-guide';
|
|
2
2
|
import { isEqual } from 'lodash';
|
|
3
3
|
import React, { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import styled from 'styled-components';
|
|
@@ -13,10 +13,11 @@ const ReferencesSidebar = styled(ModalSidebar) `
|
|
|
13
13
|
const ReferencesSidebarContent = styled(SidebarContent) `
|
|
14
14
|
overflow-y: auto;
|
|
15
15
|
`;
|
|
16
|
-
const ReferencesInnerWrapper = styled.div `
|
|
16
|
+
const ReferencesInnerWrapper = withListNavigation(styled.div `
|
|
17
17
|
width: 100%;
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
padding: 12px 0;
|
|
19
|
+
`);
|
|
20
|
+
const ReferenceButton = withNavigableListItem(styled.div `
|
|
20
21
|
cursor: pointer;
|
|
21
22
|
display: flex;
|
|
22
23
|
justify-content: flex-start;
|
|
@@ -43,7 +44,7 @@ const ReferenceButton = styled.div `
|
|
|
43
44
|
padding: ${(props) => props.theme.grid.unit * 2}px;
|
|
44
45
|
border-radius: 6px;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
+
`);
|
|
47
48
|
const IconContainer = styled.div `
|
|
48
49
|
padding-right: ${(props) => props.theme.grid.unit * 5}px;
|
|
49
50
|
position: relative;
|