@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.
Files changed (62) hide show
  1. package/dist/cjs/components/affiliations/AffiliationItem.js +7 -11
  2. package/dist/cjs/components/affiliations/AffiliationList.js +3 -5
  3. package/dist/cjs/components/affiliations/AffiliationsModal.js +13 -4
  4. package/dist/cjs/components/authors/AuthorList.js +4 -2
  5. package/dist/cjs/components/authors/AuthorsModal.js +10 -3
  6. package/dist/cjs/components/authors/DraggableAuthor.js +7 -11
  7. package/dist/cjs/components/awards/AwardForm.js +108 -100
  8. package/dist/cjs/components/hooks/use-debounce.js +15 -0
  9. package/dist/cjs/components/modal-drawer/GenericDrawerGroup.js +1 -0
  10. package/dist/cjs/components/references/CitationEditor.js +6 -7
  11. package/dist/cjs/components/references/ImportBibliographyForm.js +12 -3
  12. package/dist/cjs/components/references/ReferenceForm/PersonDropDown.js +1 -1
  13. package/dist/cjs/components/references/ReferenceForm/styled-components.js +12 -3
  14. package/dist/cjs/components/references/ReferenceSearch.js +2 -2
  15. package/dist/cjs/components/references/ReferenceSearchResults.js +58 -11
  16. package/dist/cjs/components/references/ReferenceSearchSection.js +3 -11
  17. package/dist/cjs/components/references/ReferencesModal.js +5 -4
  18. package/dist/cjs/components/toolbar/InsertTableDialog.js +11 -3
  19. package/dist/cjs/components/views/CrossReferenceItems.js +8 -14
  20. package/dist/cjs/components/views/FootnotesSelector.js +6 -6
  21. package/dist/cjs/components/views/InsertSpecialCharacter.js +24 -10
  22. package/dist/cjs/components/views/LinkForm.js +8 -1
  23. package/dist/cjs/lib/popper.js +4 -2
  24. package/dist/cjs/versions.js +1 -1
  25. package/dist/cjs/views/cross_reference_editable.js +1 -0
  26. package/dist/cjs/views/equation_editable.js +6 -0
  27. package/dist/cjs/views/inline_equation_editable.js +6 -0
  28. package/dist/cjs/views/inline_footnote.js +1 -0
  29. package/dist/es/components/affiliations/AffiliationItem.js +8 -12
  30. package/dist/es/components/affiliations/AffiliationList.js +3 -5
  31. package/dist/es/components/affiliations/AffiliationsModal.js +14 -5
  32. package/dist/es/components/authors/AuthorList.js +4 -2
  33. package/dist/es/components/authors/AuthorsModal.js +11 -4
  34. package/dist/es/components/authors/DraggableAuthor.js +8 -12
  35. package/dist/es/components/awards/AwardForm.js +110 -102
  36. package/dist/es/components/hooks/use-debounce.js +15 -1
  37. package/dist/es/components/modal-drawer/GenericDrawerGroup.js +2 -1
  38. package/dist/es/components/references/CitationEditor.js +7 -8
  39. package/dist/es/components/references/ImportBibliographyForm.js +14 -5
  40. package/dist/es/components/references/ReferenceForm/PersonDropDown.js +1 -1
  41. package/dist/es/components/references/ReferenceForm/styled-components.js +13 -4
  42. package/dist/es/components/references/ReferenceSearch.js +3 -3
  43. package/dist/es/components/references/ReferenceSearchResults.js +26 -12
  44. package/dist/es/components/references/ReferenceSearchSection.js +4 -12
  45. package/dist/es/components/references/ReferencesModal.js +6 -5
  46. package/dist/es/components/toolbar/InsertTableDialog.js +11 -3
  47. package/dist/es/components/views/CrossReferenceItems.js +9 -15
  48. package/dist/es/components/views/FootnotesSelector.js +7 -7
  49. package/dist/es/components/views/InsertSpecialCharacter.js +25 -11
  50. package/dist/es/components/views/LinkForm.js +9 -2
  51. package/dist/es/lib/popper.js +4 -2
  52. package/dist/es/versions.js +1 -1
  53. package/dist/es/views/cross_reference_editable.js +1 -0
  54. package/dist/es/views/equation_editable.js +6 -0
  55. package/dist/es/views/inline_equation_editable.js +6 -0
  56. package/dist/es/views/inline_footnote.js +1 -0
  57. package/dist/types/components/hooks/use-debounce.d.ts +1 -0
  58. package/dist/types/components/references/ReferenceForm/styled-components.d.ts +11 -1
  59. package/dist/types/components/references/ReferenceSearchResults.d.ts +20 -2
  60. package/dist/types/versions.d.ts +1 -1
  61. package/package.json +2 -2
  62. 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.div `
90
- display: flex;
91
- align-items: center;
92
- margin-right: 8px;
89
+ const RemoveButton = styled(SecondaryIconButton) `
93
90
  svg {
94
- cursor: pointer;
95
- }
96
- .icon_element {
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, FormActionsBar, } from '@manuscripts/style-guide';
16
+ import { MultiValueInput, PrimaryButton, SearchIcon, SecondaryButton, TextField, FormRow, Label, ButtonGroup, } from '@manuscripts/style-guide';
17
17
  import { Field, Formik } from 'formik';
18
- import React, { useEffect, useRef, useState } from '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 { useDebounce } from '../hooks/use-debounce';
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 debouncedSearchQuery = useDebounce(searchQuery, 300);
29
- useEffect(() => {
30
- const searchFunders = async () => {
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(SearchContainer, null,
84
- React.createElement(SearchIconContainer, null,
85
- React.createElement(SearchIcon, null)),
86
- React.createElement(Field, { name: "source" }, (props) => (React.createElement(StyledTextField, { id: "source", placeholder: "Search for funder...", onChange: (e) => {
87
- props.field.onChange(e);
88
- handleFunderSearch(e);
89
- }, value: props.field.value || '' }))),
90
- isLoading && React.createElement(LoadingText, null, "Loading..."),
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(FormActionsBar, null,
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
- left: ${(props) => props.theme.grid.unit * 4}px;
117
- position: absolute;
118
- top: 50%;
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.theme.colors.text.primary};
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
- ${SearchContainer}:hover &,
127
- ${SearchContainer}:focus-within & {
128
- path {
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
- const StyledTextField = styled(TextField) `
134
- padding-left: ${(props) => props.theme.grid.unit * 11}px;
135
- &:hover,
136
- &:focus {
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 LoadingText = styled.div `
161
- position: absolute;
162
- right: 12px;
163
- top: 50%;
164
- transform: translateY(-50%);
165
- color: ${(props) => props.theme.colors.text.secondary};
166
- font-size: ${(props) => props.theme.font.size.small};
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, IconTextButton, PrimaryButton, SecondaryButton, } from '@manuscripts/style-guide';
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.div `
45
+ const Actions = styled(ButtonGroup) `
45
46
  margin: ${(props) => props.theme.grid.unit * 4}px;
46
47
  display: flex;
47
- justify-content: space-between;
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(React.Fragment, null,
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(IconTextButton, null),
160
- React.createElement(ButtonGroup, null,
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
- padding: 0.6em 0.5em;
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.button `
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: 0 ${(props) => props.theme.grid.unit * 4}px;
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
- export const ReferenceSearchResult = styled.div `
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 }) => (React.createElement(ReferenceSearchResultsContainer, null,
54
- items.map((item) => (React.createElement(ReferenceSearchResult, { onClick: () => onSelect(item), key: item.id, "data-cy": `${isSelected(item) ? 'selected-reference' : 'reference'}` },
55
- React.createElement(StatusIcon, null, isSelected(item) ? (React.createElement(AddedIcon, { width: 24, height: 24 })) : (React.createElement(AddIcon, { width: 24, height: 24 }))),
56
- React.createElement(ReferenceLine, { item: item })))),
57
- items.length < 25 && total > items.length ? (React.createElement(MoreButton, { onClick: onShowMore, "data-cy": 'more-button' }, "Show more")) : undefined));
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.div `
22
- margin: 0 ${(props) => props.theme.grid.unit * 4}px
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
- const ReferenceButton = styled.div `
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;