@manuscripts/body-editor 3.19.0 → 3.19.2
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/cross-ref-check-modal/CrossRefWarningModal.js +202 -52
- package/dist/cjs/components/cross-ref-check-modal/openModal.js +8 -2
- package/dist/cjs/components/references/ReferenceForm/ReferenceForm.js +22 -40
- package/dist/cjs/components/references/ReferenceForm/styled-components.js +1 -13
- package/dist/cjs/components/references/ReferencesEditor.js +26 -1
- package/dist/cjs/components/references/ReferencesModal.js +71 -7
- package/dist/cjs/lib/utils.js +12 -1
- package/dist/cjs/menus.js +12 -0
- package/dist/cjs/plugins/cross-references.js +37 -14
- package/dist/cjs/versions.js +1 -1
- package/dist/cjs/views/ReactSubView.js +1 -0
- package/dist/cjs/views/cross_reference.js +13 -2
- package/dist/cjs/views/cross_reference_editable.js +1 -4
- package/dist/es/components/cross-ref-check-modal/CrossRefWarningModal.js +202 -52
- package/dist/es/components/cross-ref-check-modal/openModal.js +9 -3
- package/dist/es/components/references/ReferenceForm/ReferenceForm.js +21 -40
- package/dist/es/components/references/ReferenceForm/styled-components.js +0 -12
- package/dist/es/components/references/ReferencesEditor.js +21 -0
- package/dist/es/components/references/ReferencesModal.js +73 -9
- package/dist/es/lib/utils.js +11 -1
- package/dist/es/menus.js +12 -0
- package/dist/es/plugins/cross-references.js +37 -15
- package/dist/es/versions.js +1 -1
- package/dist/es/views/ReactSubView.js +1 -0
- package/dist/es/views/cross_reference.js +13 -2
- package/dist/es/views/cross_reference_editable.js +1 -4
- package/dist/types/components/authors-affiliations/AuthorsAndAffiliationsModals.d.ts +1 -1
- package/dist/types/components/cross-ref-check-modal/CrossRefWarningModal.d.ts +4 -2
- package/dist/types/components/cross-ref-check-modal/openModal.d.ts +2 -2
- package/dist/types/components/references/ReferenceForm/ReferenceForm.d.ts +3 -3
- package/dist/types/components/references/ReferenceForm/styled-components.d.ts +0 -1
- package/dist/types/components/references/ReferencesEditor.d.ts +3 -0
- package/dist/types/lib/utils.d.ts +4 -0
- package/dist/types/plugins/cross-references.d.ts +2 -0
- package/dist/types/versions.d.ts +1 -1
- package/dist/types/views/ReactSubView.d.ts +5 -2
- package/dist/types/views/cross_reference.d.ts +1 -0
- package/package.json +2 -2
- package/src/components/cross-ref-check-modal/CrossRefWarningModal.tsx +315 -83
- package/src/components/cross-ref-check-modal/openModal.ts +14 -4
- package/src/components/references/ReferenceForm/ReferenceForm.tsx +21 -82
- package/src/components/references/ReferenceForm/styled-components.ts +0 -13
- package/src/components/references/ReferencesEditor.tsx +38 -0
- package/src/components/references/ReferencesModal.tsx +116 -14
- package/src/lib/utils.ts +24 -0
- package/src/lib/view.ts +1 -0
- package/src/menus.tsx +12 -0
- package/src/plugins/cross-references.ts +41 -13
- package/src/versions.ts +1 -1
- package/src/views/ReactSubView.tsx +6 -3
- package/src/views/cross_reference.ts +14 -2
- package/src/views/cross_reference_editable.ts +1 -4
- package/styles/Editor.css +12 -0
|
@@ -14,71 +14,148 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { ManuscriptNode } from '@manuscripts/transform'
|
|
18
|
-
import React, { useState } from 'react'
|
|
19
|
-
|
|
20
17
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
ArrowUpIcon,
|
|
19
|
+
AttentionRedIcon,
|
|
20
|
+
ButtonGroup,
|
|
21
|
+
IconButton,
|
|
23
22
|
ModalContainer,
|
|
24
|
-
ModalHeader,
|
|
25
23
|
PrimaryButton,
|
|
24
|
+
RadioButton,
|
|
26
25
|
StyledModal,
|
|
27
26
|
TertiaryButton,
|
|
28
|
-
|
|
27
|
+
WebLinkIcon,
|
|
28
|
+
withFocusTrap,
|
|
29
29
|
} from '@manuscripts/style-guide'
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
30
|
+
import { ManuscriptNode, schema } from '@manuscripts/transform'
|
|
31
|
+
import { NodeType, ResolvedPos } from 'prosemirror-model'
|
|
32
|
+
import React, { useState } from 'react'
|
|
33
|
+
|
|
34
|
+
import { getSurroundingText } from '../../lib/utils'
|
|
35
|
+
import { nodeTypeIcon } from '../../node-type-icons'
|
|
32
36
|
import styled from 'styled-components'
|
|
33
37
|
|
|
34
38
|
export type XrefGroup = {
|
|
35
39
|
referenced: ManuscriptNode
|
|
36
40
|
label: string
|
|
41
|
+
caption: string
|
|
37
42
|
xrefs: [ManuscriptNode, ResolvedPos][]
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
export type DeleteOption = 'delete-without-ref' | 'delete-with-ref'
|
|
46
|
+
|
|
40
47
|
export const CrossRefWarningModal: React.FC<{
|
|
41
48
|
onClose: () => void
|
|
42
49
|
xrefs: XrefGroup[]
|
|
43
|
-
onConfirm: () => void
|
|
50
|
+
onConfirm: (deleteOption: DeleteOption) => void
|
|
44
51
|
selectAndScrollTo: ($pos: ResolvedPos) => void
|
|
45
52
|
}> = ({ onClose, xrefs, onConfirm, selectAndScrollTo }) => {
|
|
46
53
|
const [isOpen, setIsOpen] = useState(true)
|
|
54
|
+
const [deleteOption, setDeleteOption] =
|
|
55
|
+
useState<DeleteOption>('delete-without-ref')
|
|
56
|
+
|
|
47
57
|
const handleClose = () => {
|
|
48
58
|
setIsOpen(false)
|
|
49
59
|
onClose()
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
const [showRef, setShowRef] = useState(false)
|
|
63
|
+
const [isScrolling, setIsScrolling] = useState(false)
|
|
64
|
+
|
|
65
|
+
const handleSelectAndScrollTo = ($pos: ResolvedPos) => {
|
|
66
|
+
setIsScrolling(true)
|
|
67
|
+
selectAndScrollTo($pos)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const toggleReferenceList = () => setShowRef(!showRef)
|
|
71
|
+
|
|
72
|
+
const onChangeDeleteOption = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
73
|
+
const { value } = e.target
|
|
74
|
+
setDeleteOption(value as DeleteOption)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const references = xrefs.flatMap((xrefGroup) =>
|
|
78
|
+
xrefGroup.xrefs.map((xref) => [...xref])
|
|
79
|
+
) as [ManuscriptNode, ResolvedPos][]
|
|
80
|
+
|
|
81
|
+
const elementLabel =
|
|
82
|
+
xrefs.length === 1 ? xrefs[0].label : `${xrefs.length} elements`
|
|
83
|
+
const locationWord = references.length === 1 ? 'location' : 'locations'
|
|
84
|
+
const pronoun = xrefs.length === 1 ? 'it' : 'them'
|
|
85
|
+
|
|
52
86
|
return (
|
|
53
87
|
<Modal
|
|
54
88
|
isOpen={isOpen}
|
|
89
|
+
$isScrolling={isScrolling}
|
|
55
90
|
onRequestClose={() => handleClose()}
|
|
56
91
|
shouldCloseOnOverlayClick={false}
|
|
57
92
|
hideOverlay={true}
|
|
58
93
|
>
|
|
59
|
-
<Container
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
data-cy="modal-close-button"
|
|
64
|
-
/>
|
|
65
|
-
</ModalHeader>
|
|
94
|
+
<Container
|
|
95
|
+
$isScrolling={isScrolling}
|
|
96
|
+
data-cy="cross-reference-warning-modal"
|
|
97
|
+
>
|
|
66
98
|
<Body>
|
|
67
99
|
<Title>
|
|
68
|
-
<
|
|
69
|
-
|
|
100
|
+
<AttentionRedIcon width={24} height={24} />
|
|
101
|
+
Confirm Deletion
|
|
70
102
|
</Title>
|
|
71
|
-
<p>
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
103
|
+
<p>
|
|
104
|
+
<b>{elementLabel}</b> {xrefs.length === 1 ? 'is' : 'are'} actively
|
|
105
|
+
referenced in{' '}
|
|
106
|
+
<b>
|
|
107
|
+
{references.length} {locationWord}
|
|
108
|
+
</b>{' '}
|
|
109
|
+
in your document. Deleting {pronoun} will break the following
|
|
110
|
+
cross-references:
|
|
111
|
+
</p>
|
|
112
|
+
<div>
|
|
113
|
+
<ToggleHeader>
|
|
114
|
+
<SecondaryBoldHeading>
|
|
115
|
+
{showRef
|
|
116
|
+
? 'Hide locations'
|
|
117
|
+
: `Show ${references.length} location`}
|
|
118
|
+
</SecondaryBoldHeading>
|
|
119
|
+
<ToggleButton onClick={toggleReferenceList}>
|
|
120
|
+
{showRef ? <ArrowUpIcon /> : <ArrowDownIcon />}
|
|
121
|
+
</ToggleButton>
|
|
122
|
+
</ToggleHeader>
|
|
123
|
+
{showRef && (
|
|
124
|
+
<ListWrapper>
|
|
125
|
+
{xrefs.length === 1 ? (
|
|
126
|
+
<XrefGroupDisplay
|
|
127
|
+
label={xrefs[0].label}
|
|
128
|
+
xrefs={xrefs[0].xrefs}
|
|
129
|
+
selectAndScrollTo={handleSelectAndScrollTo}
|
|
130
|
+
/>
|
|
131
|
+
) : (
|
|
132
|
+
<XrefGroupsDisplay
|
|
133
|
+
xrefsGroup={xrefs}
|
|
134
|
+
selectAndScrollTo={handleSelectAndScrollTo}
|
|
135
|
+
/>
|
|
136
|
+
)}
|
|
137
|
+
</ListWrapper>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
<SelectorContainer>
|
|
141
|
+
<RadioButton
|
|
142
|
+
name={'delete-option'}
|
|
143
|
+
id={'delete-without-ref-option'}
|
|
144
|
+
value={'delete-without-ref'}
|
|
145
|
+
label={'Delete but keep referenced text'}
|
|
146
|
+
checked={deleteOption === 'delete-without-ref'}
|
|
147
|
+
onChange={onChangeDeleteOption}
|
|
148
|
+
/>
|
|
149
|
+
<RadioButton
|
|
150
|
+
name={'delete-option'}
|
|
151
|
+
id={'delete-with-ref-option'}
|
|
152
|
+
value={'delete-with-ref'}
|
|
153
|
+
label={'Delete & remove references'}
|
|
154
|
+
checked={deleteOption === 'delete-with-ref'}
|
|
155
|
+
onChange={onChangeDeleteOption}
|
|
156
|
+
/>
|
|
157
|
+
</SelectorContainer>
|
|
158
|
+
<ButtonGroup>
|
|
82
159
|
<TertiaryButton type="button" onClick={() => handleClose()}>
|
|
83
160
|
Cancel
|
|
84
161
|
</TertiaryButton>
|
|
@@ -86,13 +163,13 @@ export const CrossRefWarningModal: React.FC<{
|
|
|
86
163
|
$danger={true}
|
|
87
164
|
type="button"
|
|
88
165
|
onClick={() => {
|
|
89
|
-
onConfirm()
|
|
166
|
+
onConfirm(deleteOption)
|
|
90
167
|
setIsOpen(false)
|
|
91
168
|
}}
|
|
92
169
|
>
|
|
93
|
-
Delete
|
|
170
|
+
Delete
|
|
94
171
|
</PrimaryButton>
|
|
95
|
-
</
|
|
172
|
+
</ButtonGroup>
|
|
96
173
|
</Body>
|
|
97
174
|
</Container>
|
|
98
175
|
</Modal>
|
|
@@ -100,45 +177,95 @@ export const CrossRefWarningModal: React.FC<{
|
|
|
100
177
|
}
|
|
101
178
|
|
|
102
179
|
const XrefGroupDisplay: React.FC<{
|
|
103
|
-
|
|
180
|
+
label: string
|
|
181
|
+
xrefs: XrefGroup['xrefs']
|
|
182
|
+
selectAndScrollTo: ($pos: ResolvedPos) => void
|
|
183
|
+
}> = ({ label, xrefs, selectAndScrollTo }) => {
|
|
184
|
+
return (
|
|
185
|
+
<ReferencesList>
|
|
186
|
+
{xrefs.map(([xrefNode, pos], i) => {
|
|
187
|
+
const { leftHandText, rightHandText } = getSurroundingText(
|
|
188
|
+
pos,
|
|
189
|
+
xrefNode
|
|
190
|
+
)
|
|
191
|
+
const derivedLabel = xrefNode.attrs.label || label || '[cross-ref]'
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<ListItem key={i}>
|
|
195
|
+
<XRefTextContainer>
|
|
196
|
+
<XRefAdjacentText $direction={'rtl'}>
|
|
197
|
+
{leftHandText}
|
|
198
|
+
</XRefAdjacentText>
|
|
199
|
+
<XRefLabel>{derivedLabel}</XRefLabel>
|
|
200
|
+
<XRefAdjacentText $direction={'ltr'}>
|
|
201
|
+
{rightHandText}
|
|
202
|
+
</XRefAdjacentText>
|
|
203
|
+
</XRefTextContainer>
|
|
204
|
+
<ScrollButton $mini={true} onClick={() => selectAndScrollTo(pos)}>
|
|
205
|
+
Show
|
|
206
|
+
</ScrollButton>
|
|
207
|
+
</ListItem>
|
|
208
|
+
)
|
|
209
|
+
})}
|
|
210
|
+
</ReferencesList>
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const XrefGroupsDisplay: React.FC<{
|
|
215
|
+
xrefsGroup: XrefGroup[]
|
|
104
216
|
selectAndScrollTo: ($pos: ResolvedPos) => void
|
|
105
|
-
}> = ({
|
|
217
|
+
}> = ({ xrefsGroup, selectAndScrollTo }) => {
|
|
106
218
|
return (
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
219
|
+
<ReferencesList>
|
|
220
|
+
{xrefsGroup.map(({ referenced, label, caption, xrefs }, i) => {
|
|
221
|
+
const icon = getReferencedIcon(referenced.type)
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div key={i}>
|
|
225
|
+
<DarkSecondaryBoldHeading>
|
|
226
|
+
{icon ? <ItemIcon>{icon}</ItemIcon> : null}
|
|
227
|
+
{`${label}:`}
|
|
228
|
+
<CaptionTitle $direction={'ltr'}>{caption}</CaptionTitle>
|
|
229
|
+
</DarkSecondaryBoldHeading>
|
|
230
|
+
<XrefGroupDisplay
|
|
231
|
+
label={label}
|
|
232
|
+
xrefs={xrefs}
|
|
233
|
+
selectAndScrollTo={selectAndScrollTo}
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
)
|
|
237
|
+
})}
|
|
238
|
+
</ReferencesList>
|
|
121
239
|
)
|
|
122
240
|
}
|
|
123
241
|
|
|
124
|
-
const
|
|
242
|
+
const getReferencedIcon = (type: NodeType): React.ReactNode => {
|
|
243
|
+
if (type === schema.nodes.supplement) {
|
|
244
|
+
return <WebLinkIcon className="file-icon" />
|
|
245
|
+
}
|
|
246
|
+
return nodeTypeIcon(schema.nodes[type.name])
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const Container = styled(ModalContainer)<{ $isScrolling: boolean }>`
|
|
125
250
|
position: absolute;
|
|
126
|
-
top: 1rem;
|
|
127
|
-
left:
|
|
128
|
-
|
|
129
|
-
max-height: calc(
|
|
251
|
+
top: ${({ $isScrolling }) => ($isScrolling ? '1rem' : '30%')};
|
|
252
|
+
left: ${({ $isScrolling }) =>
|
|
253
|
+
$isScrolling ? 'calc(100% - 556px - 0rem)' : '50%'};
|
|
254
|
+
max-height: calc(60vh - 2rem);
|
|
130
255
|
min-height: 280px;
|
|
131
|
-
transform:
|
|
132
|
-
|
|
256
|
+
transform: ${({ $isScrolling }) =>
|
|
257
|
+
$isScrolling ? 'translate(0, 0)' : 'translate(-50%, 0)'};
|
|
258
|
+
max-width: 556px;
|
|
133
259
|
transition:
|
|
134
260
|
top 0.2s,
|
|
261
|
+
left 0.2s,
|
|
135
262
|
transform 0.2s;
|
|
136
263
|
`
|
|
137
264
|
|
|
138
265
|
// since we need to scroll inside the editor when this dialog is active, we can't use dialog.showModal()
|
|
139
266
|
// so we recreate the appearance using classic position:fixed/after approach.
|
|
140
267
|
// While showModal doesn't block scrolling - it doesn't allow to focus on the editor and that kills the scrollIntoView
|
|
141
|
-
const Modal = styled(StyledModal)
|
|
268
|
+
const Modal = styled(StyledModal)<{ $isScrolling: boolean }>`
|
|
142
269
|
position: fixed;
|
|
143
270
|
top: 0;
|
|
144
271
|
left: 0;
|
|
@@ -163,6 +290,8 @@ const Modal = styled(StyledModal)`
|
|
|
163
290
|
right: 0;
|
|
164
291
|
bottom: 0;
|
|
165
292
|
background: rgba(0, 0, 0, 0.2);
|
|
293
|
+
opacity: ${({ $isScrolling }) => ($isScrolling ? 0.15 : 1)};
|
|
294
|
+
transform: opacity 0.2s;
|
|
166
295
|
}
|
|
167
296
|
h3 {
|
|
168
297
|
font-size: 16px;
|
|
@@ -173,11 +302,11 @@ const Modal = styled(StyledModal)`
|
|
|
173
302
|
}
|
|
174
303
|
`
|
|
175
304
|
|
|
176
|
-
const Body = styled.div`
|
|
305
|
+
const Body = withFocusTrap(styled.div`
|
|
177
306
|
margin: 1.5rem;
|
|
178
307
|
display: flex;
|
|
179
308
|
flex-flow: column;
|
|
180
|
-
`
|
|
309
|
+
`)
|
|
181
310
|
|
|
182
311
|
const Title = styled.h2`
|
|
183
312
|
font-size: 18px;
|
|
@@ -187,37 +316,140 @@ const Title = styled.h2`
|
|
|
187
316
|
color: #353535;
|
|
188
317
|
svg {
|
|
189
318
|
vertical-align: text-top;
|
|
319
|
+
margin-right: 8px;
|
|
190
320
|
}
|
|
191
321
|
`
|
|
192
322
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
323
|
+
const ToggleHeader = styled.div`
|
|
324
|
+
display: flex;
|
|
325
|
+
justify-content: space-between;
|
|
326
|
+
align-items: center;
|
|
327
|
+
padding: 6px 3px 6px 8px;
|
|
328
|
+
border-radius: 4px 4px 0 0;
|
|
329
|
+
border: 1px solid #e2e2e2;
|
|
197
330
|
background: #f2f2f2;
|
|
331
|
+
`
|
|
332
|
+
|
|
333
|
+
const ListWrapper = styled.div`
|
|
198
334
|
border: 1px solid #e2e2e2;
|
|
199
|
-
border-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
335
|
+
border-top: none;
|
|
336
|
+
max-height: 21vh;
|
|
337
|
+
min-height: 100px;
|
|
338
|
+
overflow-y: auto;
|
|
339
|
+
padding: 12px;
|
|
340
|
+
`
|
|
341
|
+
|
|
342
|
+
const ToggleButton = styled(IconButton)`
|
|
343
|
+
height: 24px;
|
|
344
|
+
&&:not([disabled]):focus-visible {
|
|
345
|
+
outline: 2px solid ${(props) => props.theme.colors.outline.focus};
|
|
346
|
+
outline-offset: 2px;
|
|
347
|
+
}
|
|
348
|
+
svg {
|
|
349
|
+
width: 12px;
|
|
350
|
+
height: 7px;
|
|
212
351
|
}
|
|
213
352
|
`
|
|
214
353
|
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
354
|
+
const SecondaryHeading = styled.div`
|
|
355
|
+
font-size: 14px;
|
|
356
|
+
font-style: normal;
|
|
357
|
+
font-weight: 700;
|
|
358
|
+
line-height: 24px;
|
|
359
|
+
color: #6e6e6e;
|
|
360
|
+
user-select: none;
|
|
218
361
|
`
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
362
|
+
|
|
363
|
+
const SecondaryBoldHeading = styled(SecondaryHeading)`
|
|
364
|
+
font-weight: 700;
|
|
365
|
+
color: #6e6e6e;
|
|
366
|
+
`
|
|
367
|
+
|
|
368
|
+
const DarkSecondaryBoldHeading = styled(SecondaryBoldHeading)`
|
|
369
|
+
display: flex;
|
|
370
|
+
padding: 0 4px;
|
|
371
|
+
margin-bottom: 10px;
|
|
372
|
+
border-radius: 4px;
|
|
373
|
+
color: #353535;
|
|
374
|
+
background: #f2f2f2;
|
|
375
|
+
`
|
|
376
|
+
|
|
377
|
+
const ItemIcon = styled.span`
|
|
378
|
+
display: flex;
|
|
379
|
+
padding-right: 8px;
|
|
380
|
+
svg {
|
|
381
|
+
align-self: center;
|
|
382
|
+
}
|
|
383
|
+
`
|
|
384
|
+
|
|
385
|
+
const ReferencesList = styled.ul`
|
|
386
|
+
display: flex;
|
|
387
|
+
gap: 10px;
|
|
388
|
+
flex-direction: column;
|
|
389
|
+
padding: 0;
|
|
390
|
+
margin: 0;
|
|
391
|
+
list-style: none;
|
|
392
|
+
`
|
|
393
|
+
|
|
394
|
+
const ListItem = styled.li`
|
|
395
|
+
display: flex;
|
|
396
|
+
justify-content: space-between;
|
|
397
|
+
align-items: center;
|
|
398
|
+
border-bottom: solid 1px #e2e2e2;
|
|
399
|
+
&:last-child {
|
|
400
|
+
border-bottom: none;
|
|
401
|
+
padding: 0;
|
|
402
|
+
}
|
|
403
|
+
`
|
|
404
|
+
|
|
405
|
+
const ScrollButton = styled(TertiaryButton)`
|
|
406
|
+
margin: 0;
|
|
407
|
+
padding: 4px;
|
|
408
|
+
font-size: 14px;
|
|
409
|
+
line-height: 24px;
|
|
410
|
+
text-decoration-line: underline;
|
|
411
|
+
`
|
|
412
|
+
|
|
413
|
+
const XRefTextContainer = styled.div`
|
|
414
|
+
display: flex;
|
|
415
|
+
color: #6e6e6e;
|
|
416
|
+
font-size: 12px;
|
|
417
|
+
font-style: normal;
|
|
418
|
+
font-weight: 400;
|
|
419
|
+
line-height: 16px;
|
|
420
|
+
`
|
|
421
|
+
|
|
422
|
+
const XRefLabel = styled.span`
|
|
423
|
+
border-radius: 4px;
|
|
424
|
+
border: 1px solid #fe8f1f;
|
|
425
|
+
background: #fff9e5;
|
|
426
|
+
mix-blend-mode: darken;
|
|
427
|
+
`
|
|
428
|
+
|
|
429
|
+
const XRefAdjacentText = styled.span<{ $direction: 'rtl' | 'ltr' }>`
|
|
430
|
+
display: block;
|
|
431
|
+
max-width: 150px;
|
|
432
|
+
white-space: nowrap;
|
|
433
|
+
overflow: hidden;
|
|
434
|
+
text-overflow: ellipsis;
|
|
435
|
+
direction: ${(props) => props.$direction};
|
|
436
|
+
`
|
|
437
|
+
|
|
438
|
+
const CaptionTitle = styled(XRefAdjacentText)`
|
|
439
|
+
max-width: 120px;
|
|
440
|
+
margin-left: 4px;
|
|
441
|
+
`
|
|
442
|
+
|
|
443
|
+
const SelectorContainer = styled.div`
|
|
444
|
+
display: flex;
|
|
445
|
+
gap: 24px;
|
|
446
|
+
padding: 8px 12px 4px 12px;
|
|
447
|
+
margin: 24px 0;
|
|
448
|
+
border-radius: 4px;
|
|
449
|
+
border: 1px solid #fe8f1f;
|
|
450
|
+
background: #fff9e5;
|
|
451
|
+
`
|
|
452
|
+
|
|
453
|
+
const ArrowDownIcon = styled(ArrowUpIcon)`
|
|
454
|
+
transform: rotate(180deg);
|
|
223
455
|
`
|
|
@@ -19,12 +19,16 @@ import { ResolvedPos } from 'prosemirror-model'
|
|
|
19
19
|
|
|
20
20
|
import { getEditorProps } from '../../plugins/editor-props'
|
|
21
21
|
import ReactSubView from '../../views/ReactSubView'
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
CrossRefWarningModal,
|
|
24
|
+
DeleteOption,
|
|
25
|
+
XrefGroup,
|
|
26
|
+
} from './CrossRefWarningModal'
|
|
23
27
|
|
|
24
28
|
export const openCrossRefWarningModal = (
|
|
25
29
|
view: ManuscriptEditorView,
|
|
26
30
|
xrefGroups: XrefGroup[],
|
|
27
|
-
onConfirm: () => void,
|
|
31
|
+
onConfirm: (deleteOption: DeleteOption) => void,
|
|
28
32
|
onClose: () => void,
|
|
29
33
|
selectAndScrollTo: ($pos: ResolvedPos) => void
|
|
30
34
|
): HTMLDivElement => {
|
|
@@ -32,8 +36,14 @@ export const openCrossRefWarningModal = (
|
|
|
32
36
|
const props = getEditorProps(state)
|
|
33
37
|
const componentProps = {
|
|
34
38
|
xrefs: xrefGroups,
|
|
35
|
-
onConfirm
|
|
36
|
-
|
|
39
|
+
onConfirm: (deleteOption: DeleteOption) => {
|
|
40
|
+
dialog.unmount()
|
|
41
|
+
onConfirm(deleteOption)
|
|
42
|
+
},
|
|
43
|
+
onClose: () => {
|
|
44
|
+
dialog.unmount()
|
|
45
|
+
onClose()
|
|
46
|
+
},
|
|
37
47
|
selectAndScrollTo,
|
|
38
48
|
}
|
|
39
49
|
|
|
@@ -16,15 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
AddAuthorIcon,
|
|
19
|
-
ButtonGroup,
|
|
20
|
-
Category,
|
|
21
|
-
DeleteIcon,
|
|
22
|
-
Dialog,
|
|
23
|
-
IconButton,
|
|
24
|
-
LinkIcon,
|
|
25
19
|
OptionType,
|
|
26
|
-
PrimaryButton,
|
|
27
|
-
SecondaryButton,
|
|
28
20
|
SelectField,
|
|
29
21
|
Label,
|
|
30
22
|
FormRow,
|
|
@@ -42,9 +34,7 @@ import { shouldRenderField } from '../../../lib/utils'
|
|
|
42
34
|
import { ChangeHandlingForm } from '../../ChangeHandlingForm'
|
|
43
35
|
import { PersonDropDown } from './PersonDropDown'
|
|
44
36
|
import {
|
|
45
|
-
Actions,
|
|
46
37
|
Button,
|
|
47
|
-
DeleteButton,
|
|
48
38
|
FormFields,
|
|
49
39
|
ReferenceTextArea,
|
|
50
40
|
ReferenceTextField,
|
|
@@ -58,22 +48,33 @@ const bibliographyItemTypeOptions: OptionType[] = bibliographyItemTypes.map(
|
|
|
58
48
|
)
|
|
59
49
|
export interface ReferenceFormActions {
|
|
60
50
|
reset: () => void
|
|
51
|
+
submit: () => void
|
|
52
|
+
isDirty: () => boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const validateReference = (values: BibliographyItemAttrs) => {
|
|
56
|
+
const errors: Partial<BibliographyItemAttrs> = {}
|
|
57
|
+
|
|
58
|
+
if (values.type === 'literal') {
|
|
59
|
+
if (!values.literal?.trim()) {
|
|
60
|
+
errors.literal = 'Literal is required for unstructured references'
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (!values.title?.trim()) {
|
|
64
|
+
errors.title = 'Title is required'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return errors
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
export const ReferenceForm: React.FC<{
|
|
64
71
|
values: BibliographyItemAttrs
|
|
65
|
-
showDelete: boolean
|
|
66
72
|
onChange: (values: BibliographyItemAttrs) => void
|
|
67
|
-
onCancel: () => void
|
|
68
|
-
onDelete: () => void
|
|
69
73
|
onSave: (values: BibliographyItemAttrs) => void
|
|
70
74
|
actionsRef?: MutableRefObject<ReferenceFormActions | undefined>
|
|
71
75
|
}> = ({
|
|
72
76
|
values,
|
|
73
|
-
showDelete,
|
|
74
77
|
onChange,
|
|
75
|
-
onDelete,
|
|
76
|
-
onCancel,
|
|
77
78
|
onSave,
|
|
78
79
|
actionsRef,
|
|
79
80
|
}) => {
|
|
@@ -88,28 +89,15 @@ export const ReferenceForm: React.FC<{
|
|
|
88
89
|
}
|
|
89
90
|
}, [values])
|
|
90
91
|
|
|
91
|
-
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
|
92
|
-
|
|
93
|
-
const validateReference = (values: BibliographyItemAttrs) => {
|
|
94
|
-
const errors: Partial<BibliographyItemAttrs> = {}
|
|
95
|
-
|
|
96
|
-
if (values.type === 'literal') {
|
|
97
|
-
if (!values.literal?.trim()) {
|
|
98
|
-
errors.literal = 'Literal is required for unstructured references'
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
if (!values.title?.trim()) {
|
|
102
|
-
errors.title = 'Title is required'
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return errors
|
|
106
|
-
}
|
|
107
|
-
|
|
108
92
|
if (actionsRef && !actionsRef.current) {
|
|
109
93
|
actionsRef.current = {
|
|
110
94
|
reset: () => {
|
|
111
95
|
formRef.current?.resetForm()
|
|
112
96
|
},
|
|
97
|
+
submit: () => {
|
|
98
|
+
formRef.current?.submitForm()
|
|
99
|
+
},
|
|
100
|
+
isDirty: () => formRef.current?.dirty ?? false,
|
|
113
101
|
}
|
|
114
102
|
}
|
|
115
103
|
|
|
@@ -124,55 +112,6 @@ export const ReferenceForm: React.FC<{
|
|
|
124
112
|
{(formik) => {
|
|
125
113
|
return (
|
|
126
114
|
<ChangeHandlingForm onChange={onChange}>
|
|
127
|
-
<Dialog
|
|
128
|
-
isOpen={showDeleteDialog}
|
|
129
|
-
category={Category.confirmation}
|
|
130
|
-
header="Delete Reference"
|
|
131
|
-
message="Are you sure you want to delete this reference from the list?"
|
|
132
|
-
actions={{
|
|
133
|
-
secondary: {
|
|
134
|
-
action: () => {
|
|
135
|
-
onDelete()
|
|
136
|
-
setShowDeleteDialog(false)
|
|
137
|
-
},
|
|
138
|
-
title: 'Delete',
|
|
139
|
-
},
|
|
140
|
-
primary: {
|
|
141
|
-
action: () => setShowDeleteDialog(false),
|
|
142
|
-
title: 'Cancel',
|
|
143
|
-
},
|
|
144
|
-
}}
|
|
145
|
-
/>
|
|
146
|
-
<Actions>
|
|
147
|
-
<ButtonGroup>
|
|
148
|
-
<IconButton
|
|
149
|
-
as="a"
|
|
150
|
-
href={`https://doi.org/${formik.values.DOI}`}
|
|
151
|
-
target={'_blank'}
|
|
152
|
-
>
|
|
153
|
-
<LinkIcon />
|
|
154
|
-
</IconButton>
|
|
155
|
-
<DeleteButton
|
|
156
|
-
$defaultColor
|
|
157
|
-
disabled={!showDelete}
|
|
158
|
-
data-tooltip-content="Unable to delete because the item is used in the document"
|
|
159
|
-
data-tooltip-hidden={showDelete}
|
|
160
|
-
onClick={() => setShowDeleteDialog(true)}
|
|
161
|
-
>
|
|
162
|
-
<DeleteIcon />
|
|
163
|
-
</DeleteButton>
|
|
164
|
-
</ButtonGroup>
|
|
165
|
-
<ButtonGroup>
|
|
166
|
-
<SecondaryButton onClick={onCancel}>Cancel</SecondaryButton>
|
|
167
|
-
<PrimaryButton
|
|
168
|
-
type="submit"
|
|
169
|
-
disabled={!formik.isValid || !formik.dirty}
|
|
170
|
-
>
|
|
171
|
-
Save
|
|
172
|
-
</PrimaryButton>
|
|
173
|
-
</ButtonGroup>
|
|
174
|
-
</Actions>
|
|
175
|
-
|
|
176
115
|
<FormFields ref={fieldsRef}>
|
|
177
116
|
<FormRow>
|
|
178
117
|
<Label htmlFor={'citation-item-type'}>Type</Label>
|