@cu-mkp/editioncrafter 1.2.1 → 1.3.0-beta.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/README.md +11 -0
- package/dist/editioncrafter.js +27385 -22786
- package/dist/es/src/EditionCrafter/action/NotesActions.js +11 -0
- package/dist/es/src/EditionCrafter/action/initialState/documentInitialState.js +15 -1
- package/dist/es/src/EditionCrafter/action/initialState/notesInitialState.js +8 -0
- package/dist/es/src/EditionCrafter/action/rootReducer.js +4 -0
- package/dist/es/src/EditionCrafter/component/DiploMatic.js +19 -17
- package/dist/es/src/EditionCrafter/component/DocumentView.js +23 -5
- package/dist/es/src/EditionCrafter/component/EmptyPaneView.js +22 -0
- package/dist/es/src/EditionCrafter/component/ImageView.js +61 -40
- package/dist/es/src/EditionCrafter/component/Navigation.js +7 -0
- package/dist/es/src/EditionCrafter/component/NotesView.js +50 -0
- package/dist/es/src/EditionCrafter/component/SplitPaneView.js +10 -9
- package/dist/es/src/EditionCrafter/component/TagToolbar.jsx +14 -4
- package/dist/es/src/EditionCrafter/context/TagFilter.jsx +22 -11
- package/dist/es/src/EditionCrafter/context/TagFilterContext.js +2 -1
- package/dist/es/src/EditionCrafter/index.jsx +31 -0
- package/dist/es/src/EditionCrafter/model/DocumentHelper.js +1 -0
- package/dist/es/src/EditionCrafter/model/Folio.js +27 -1
- package/dist/es/src/EditionCrafter/saga/RouteListenerSaga.js +33 -7
- package/dist/es/src/EditionCrafter/scss/_imageView.scss +1 -1
- package/dist/es/src/EditionCrafter/scss/_notes.scss +49 -0
- package/dist/es/src/EditionCrafter/scss/editioncrafter.scss +1 -0
- package/dist/es/src/RecordList/component/Record.jsx +1 -1
- package/dist/es/src/RecordList/component/RecordListView.jsx +1 -1
- package/dist/es/src/RecordList/component/Sidebar.jsx +1 -1
- package/dist/es/src/RecordList/component/SidebarTagList.jsx +1 -1
- package/dist/es/src/RecordList/index.jsx +1 -1
- package/dist/es/src/RecordList/styles/base.css +0 -16
- package/dist/es/src/TagExplore/assets/InsertLeft.jsx +18 -0
- package/dist/es/src/TagExplore/assets/InsertRight.jsx +18 -0
- package/dist/es/src/TagExplore/assets/Left.jsx +17 -0
- package/dist/es/src/TagExplore/assets/Right.jsx +17 -0
- package/dist/es/src/TagExplore/assets/insert_left.svg +12 -0
- package/dist/es/src/TagExplore/assets/insert_right.svg +12 -0
- package/dist/es/src/TagExplore/assets/left.svg +11 -0
- package/dist/es/src/TagExplore/assets/right.svg +11 -0
- package/dist/es/src/TagExplore/components/DocumentDetail.jsx +224 -0
- package/dist/es/src/TagExplore/components/NarrowSidebar.jsx +35 -0
- package/dist/es/src/TagExplore/components/SurfaceBrowser.jsx +176 -0
- package/dist/es/src/TagExplore/components/TagExploreSidebar.jsx +55 -0
- package/dist/es/src/TagExplore/components/TagFilters.jsx +106 -0
- package/dist/es/src/TagExplore/index.jsx +108 -0
- package/dist/es/src/TagExplore/styles/base.css +192 -0
- package/dist/es/src/{RecordList/component → common/components}/Pill.jsx +2 -0
- package/dist/es/src/common/components/Pill.style.css +16 -0
- package/dist/es/src/index.jsx +3 -27
- package/package.json +2 -27
- /package/dist/es/src/{RecordList/component → common/components}/Loading.jsx +0 -0
- /package/dist/es/src/{RecordList → common}/lib/sql.js +0 -0
|
@@ -3,12 +3,26 @@ function getHeaderUrlFromManifestUrl(manifestUrl) {
|
|
|
3
3
|
return `${truncated}/html/index.html`
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
function getHeaderUrlsFromManifestUrls(data) {
|
|
7
|
+
if (typeof data === 'string') {
|
|
8
|
+
return getHeaderUrlFromManifestUrl(data)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const result = {}
|
|
12
|
+
|
|
13
|
+
Object.entries(data).forEach((ent) => {
|
|
14
|
+
result[ent[0]] = getHeaderUrlFromManifestUrl(ent[1])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
export default function documentInitalState(iiifManifest, documentName, transcriptionTypes, variorum = false, derivativeNames = null, threePanel = false) {
|
|
7
21
|
return {
|
|
8
22
|
documentName,
|
|
9
23
|
derivativeNames,
|
|
10
24
|
manifestURL: iiifManifest,
|
|
11
|
-
headerUrl:
|
|
25
|
+
headerUrl: getHeaderUrlsFromManifestUrls(iiifManifest),
|
|
12
26
|
transcriptionTypes,
|
|
13
27
|
variorum,
|
|
14
28
|
threePanel,
|
|
@@ -4,16 +4,19 @@ import { createReducer } from '../model/ReduxStore'
|
|
|
4
4
|
import DiplomaticActions from './DiplomaticActions'
|
|
5
5
|
import DocumentActions from './DocumentActions'
|
|
6
6
|
import GlossaryActions from './GlossaryActions'
|
|
7
|
+
import NotesActions from './NotesActions'
|
|
7
8
|
|
|
8
9
|
import diplomaticInitialState from './initialState/diplomaticInitialState'
|
|
9
10
|
import documentInitialState from './initialState/documentInitialState'
|
|
10
11
|
import glossaryInitialState from './initialState/glossaryInitialState'
|
|
12
|
+
import notesInitialState from './initialState/notesInitialState'
|
|
11
13
|
|
|
12
14
|
export default function rootReducer(config) {
|
|
13
15
|
const {
|
|
14
16
|
documentName,
|
|
15
17
|
documentInfo,
|
|
16
18
|
glossaryURL,
|
|
19
|
+
notesURL,
|
|
17
20
|
threePanel = false,
|
|
18
21
|
} = config
|
|
19
22
|
const variorum = documentInfo && Object.keys(documentInfo).length > 1
|
|
@@ -34,5 +37,6 @@ export default function rootReducer(config) {
|
|
|
34
37
|
diplomatic: createReducer('DiplomaticActions', DiplomaticActions, diplomaticInitialState),
|
|
35
38
|
document: createReducer('DocumentActions', DocumentActions, documentInitialState(iiifManifest, documentName, transcriptionTypes, variorum, derivativeNames, threePanel)),
|
|
36
39
|
glossary: createReducer('GlossaryActions', GlossaryActions, glossaryInitialState(glossaryURL)),
|
|
40
|
+
notes: createReducer('NotesActions', NotesActions, notesInitialState(notesURL)),
|
|
37
41
|
})
|
|
38
42
|
}
|
|
@@ -36,25 +36,27 @@ function DiploMatic(props) {
|
|
|
36
36
|
const { fixedFrameMode } = props.diplomatic
|
|
37
37
|
const fixedFrameModeClass = fixedFrameMode ? 'editioncrafter' : 'editioncrafter sticky'
|
|
38
38
|
|
|
39
|
+
const mainBody = (
|
|
40
|
+
<div id="diplomatic" className={fixedFrameModeClass} ref={containerRef} style={{ height: containerHeight }}>
|
|
41
|
+
<RouteListener />
|
|
42
|
+
<div id="content" style={{ height: '100%' }}>
|
|
43
|
+
<Routes>
|
|
44
|
+
<Route path="/ec/:folioID/:transcriptionType/:folioID2/:transcriptionType2/:folioID3/:transcriptionType3" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
45
|
+
<Route path="/ec/:folioID/:transcriptionType/:folioID2/:transcriptionType2" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
46
|
+
<Route path="/ec/:folioID/:transcriptionType" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
47
|
+
<Route path="/ec/:folioID" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
48
|
+
<Route path="/ec" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
49
|
+
<Route path="/" element={<Navigate to="/ec" />} exact />
|
|
50
|
+
</Routes>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const topLevel = !(props.tagExplorerMode === true) ? <HashRouter><TagFilterProvider>{mainBody}</TagFilterProvider></HashRouter> : mainBody
|
|
56
|
+
|
|
39
57
|
return (
|
|
40
58
|
<Provider store={props.store}>
|
|
41
|
-
|
|
42
|
-
<TagFilterProvider>
|
|
43
|
-
<div id="diplomatic" className={fixedFrameModeClass} ref={containerRef} style={{ height: containerHeight }}>
|
|
44
|
-
<RouteListener />
|
|
45
|
-
<div id="content" style={{ height: '100%' }}>
|
|
46
|
-
<Routes>
|
|
47
|
-
<Route path="/ec/:folioID/:transcriptionType/:folioID2/:transcriptionType2/:folioID3/:transcriptionType3" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
48
|
-
<Route path="/ec/:folioID/:transcriptionType/:folioID2/:transcriptionType2" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
49
|
-
<Route path="/ec/:folioID/:transcriptionType" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
50
|
-
<Route path="/ec/:folioID" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
51
|
-
<Route path="/ec" element={<DocumentView {...props} containerWidth={containerWidth} />} exact />
|
|
52
|
-
<Route path="/" element={<Navigate to="/ec" />} exact />
|
|
53
|
-
</Routes>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
</TagFilterProvider>
|
|
57
|
-
</HashRouter>
|
|
59
|
+
{ topLevel }
|
|
58
60
|
</Provider>
|
|
59
61
|
)
|
|
60
62
|
}
|
|
@@ -7,9 +7,11 @@ import {
|
|
|
7
7
|
useParams,
|
|
8
8
|
} from 'react-router-dom'
|
|
9
9
|
import { dispatchAction } from '../model/ReduxStore'
|
|
10
|
+
import EmptyPaneView from './EmptyPaneView'
|
|
10
11
|
import GlossaryView from './GlossaryView'
|
|
11
12
|
import ImageGridView from './ImageGridView'
|
|
12
13
|
import ImageView from './ImageView'
|
|
14
|
+
import NotesView from './NotesView'
|
|
13
15
|
import SinglePaneView from './SinglePaneView'
|
|
14
16
|
import SplitPaneView from './SplitPaneView'
|
|
15
17
|
import TranscriptionView from './TranscriptionView'
|
|
@@ -345,6 +347,9 @@ function DocumentView(props) {
|
|
|
345
347
|
if (transcriptionType === 'glossary') {
|
|
346
348
|
return 'GlossaryView'
|
|
347
349
|
}
|
|
350
|
+
if (transcriptionType === 'notes') {
|
|
351
|
+
return 'NotesView'
|
|
352
|
+
}
|
|
348
353
|
return xmlMode ? 'XMLView' : 'TranscriptionView'
|
|
349
354
|
}
|
|
350
355
|
|
|
@@ -458,20 +463,33 @@ function DocumentView(props) {
|
|
|
458
463
|
}
|
|
459
464
|
|
|
460
465
|
if (viewType === 'ImageGridView') {
|
|
466
|
+
return (props.tagExplorerMode
|
|
467
|
+
? <EmptyPaneView side={side} documentView={docView} />
|
|
468
|
+
: (
|
|
469
|
+
<ImageGridView
|
|
470
|
+
key={key}
|
|
471
|
+
documentView={docView}
|
|
472
|
+
documentViewActions={documentViewActions}
|
|
473
|
+
side={side}
|
|
474
|
+
selectedDoc={document || props.document.variorum && Object.keys(props.document.derivativeNames)[side === 'left' ? 0 : side === 'right' ? 1 : Object.keys(props.document.derivativeNames).length > 2 ? 2 : 1]}
|
|
475
|
+
/>
|
|
476
|
+
)
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (viewType === 'GlossaryView') {
|
|
461
481
|
return (
|
|
462
|
-
<
|
|
482
|
+
<GlossaryView
|
|
463
483
|
key={key}
|
|
464
484
|
documentView={docView}
|
|
465
485
|
documentViewActions={documentViewActions}
|
|
466
486
|
side={side}
|
|
467
|
-
selectedDoc={document || props.document.variorum && Object.keys(props.document.derivativeNames)[side === 'left' ? 0 : side === 'right' ? 1 : Object.keys(props.document.derivativeNames).length > 2 ? 2 : 1]}
|
|
468
487
|
/>
|
|
469
488
|
)
|
|
470
489
|
}
|
|
471
|
-
|
|
472
|
-
if (viewType === 'GlossaryView') {
|
|
490
|
+
if (viewType === 'NotesView') {
|
|
473
491
|
return (
|
|
474
|
-
<
|
|
492
|
+
<NotesView
|
|
475
493
|
key={key}
|
|
476
494
|
documentView={docView}
|
|
477
495
|
documentViewActions={documentViewActions}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Typography } from '@material-ui/core'
|
|
2
|
+
import Left from '../../TagExplore/assets/Left'
|
|
3
|
+
import Right from '../../TagExplore/assets/Right'
|
|
4
|
+
|
|
5
|
+
function EmptyPaneView(props) {
|
|
6
|
+
return (
|
|
7
|
+
<div style={{ backgroundColor: 'black', display: 'flex', justifyContent: 'center', alignItems: 'center', color: 'white' }}>
|
|
8
|
+
<div style={{ display: 'flex', flexDirection: 'row', gap: '12px', alignItems: 'center' }}>
|
|
9
|
+
{ props.side === 'left' ? <Left /> : <Right /> }
|
|
10
|
+
<Typography>
|
|
11
|
+
Select
|
|
12
|
+
{' '}
|
|
13
|
+
{props.side}
|
|
14
|
+
{' '}
|
|
15
|
+
page
|
|
16
|
+
</Typography>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default EmptyPaneView
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Annotorious from '@recogito/annotorious-openseadragon'
|
|
2
2
|
import OpenSeadragon from 'openseadragon'
|
|
3
|
-
import React, { useEffect, useState } from 'react'
|
|
3
|
+
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
import { connect } from 'react-redux'
|
|
5
5
|
|
|
6
6
|
import {
|
|
@@ -9,10 +9,11 @@ import {
|
|
|
9
9
|
useNavigate,
|
|
10
10
|
useSearchParams,
|
|
11
11
|
} from 'react-router-dom'
|
|
12
|
+
import TagFilterContext from '../context/TagFilterContext'
|
|
12
13
|
import ImageZoomControl from './ImageZoomControl'
|
|
13
14
|
import Navigation from './Navigation'
|
|
14
|
-
import { BigRingSpinner } from './RingSpinner'
|
|
15
15
|
|
|
16
|
+
import { BigRingSpinner } from './RingSpinner'
|
|
16
17
|
import SeaDragonComponent from './SeaDragonComponent'
|
|
17
18
|
import '@recogito/annotorious-openseadragon/dist/annotorious.min.css'
|
|
18
19
|
|
|
@@ -20,41 +21,68 @@ function ImageView(props) {
|
|
|
20
21
|
const [viewer, setViewer] = useState(null)
|
|
21
22
|
const [anno, setAnno] = useState(null)
|
|
22
23
|
|
|
24
|
+
const { tagsLeft, tagsRight } = useContext(TagFilterContext)
|
|
25
|
+
|
|
26
|
+
const tags = useMemo(() => (props.side === 'right' ? tagsRight : tagsLeft), [props.side, tagsLeft, tagsRight])
|
|
27
|
+
|
|
23
28
|
const location = useLocation()
|
|
24
29
|
const navigate = useNavigate()
|
|
25
30
|
|
|
26
31
|
const [searchParams] = useSearchParams()
|
|
27
|
-
const [loading, setLoading] = useState(false)
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const imageViewRef = useRef(null)
|
|
34
|
+
|
|
35
|
+
const folio = props.document.folioIndex[props.folioID]
|
|
36
|
+
|
|
37
|
+
const updateHighlightedZones = useCallback(() => {
|
|
38
|
+
if (folio.zoneTagIndex && imageViewRef.current) {
|
|
39
|
+
const annotationEls = imageViewRef.current.querySelectorAll('.a9s-annotation')
|
|
40
|
+
const zonesToHighlight = Object.keys(folio.zoneTagIndex)
|
|
41
|
+
.filter(zoneId => folio.zoneTagIndex[zoneId].some(tag => tags.includes(tag)))
|
|
42
|
+
|
|
43
|
+
const manualSelection = searchParams.get('zone')
|
|
44
|
+
|
|
45
|
+
if (manualSelection && !zonesToHighlight.includes(manualSelection)) {
|
|
46
|
+
zonesToHighlight.push(manualSelection)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
annotationEls.forEach((annoEl) => {
|
|
50
|
+
const annoId = annoEl.getAttribute('data-id')
|
|
51
|
+
if (zonesToHighlight.includes(annoId)) {
|
|
52
|
+
annoEl.classList.add('selected')
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
annoEl.classList.remove('selected')
|
|
56
|
+
}
|
|
57
|
+
})
|
|
34
58
|
}
|
|
35
|
-
}, [
|
|
59
|
+
}, [folio, tags, imageViewRef, searchParams])
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
setTimeout(() => updateHighlightedZones(), 50)
|
|
63
|
+
}, [updateHighlightedZones])
|
|
36
64
|
|
|
37
|
-
const onZoomGrid = (
|
|
65
|
+
const onZoomGrid = () => {
|
|
38
66
|
props.documentViewActions.changeTranscriptionType(props.side, 'g')
|
|
39
67
|
}
|
|
40
68
|
|
|
41
|
-
const onZoomFixed_1 = (
|
|
69
|
+
const onZoomFixed_1 = () => {
|
|
42
70
|
viewer.viewport.zoomTo(viewer.viewport.getMaxZoom())
|
|
43
71
|
}
|
|
44
72
|
|
|
45
|
-
const onZoomFixed_2 = (
|
|
73
|
+
const onZoomFixed_2 = () => {
|
|
46
74
|
viewer.viewport.zoomTo((viewer.viewport.getMaxZoom() / 2))
|
|
47
75
|
}
|
|
48
76
|
|
|
49
|
-
const onZoomFixed_3 = (
|
|
77
|
+
const onZoomFixed_3 = () => {
|
|
50
78
|
viewer.viewport.fitVertically()
|
|
51
79
|
}
|
|
52
80
|
|
|
53
|
-
const onZoomIn = (
|
|
81
|
+
const onZoomIn = () => {
|
|
54
82
|
viewer.viewport.zoomBy(2)
|
|
55
83
|
}
|
|
56
84
|
|
|
57
|
-
const onZoomOut = (
|
|
85
|
+
const onZoomOut = () => {
|
|
58
86
|
viewer.viewport.zoomBy(0.5)
|
|
59
87
|
}
|
|
60
88
|
|
|
@@ -74,14 +102,25 @@ function ImageView(props) {
|
|
|
74
102
|
// every time it changes!
|
|
75
103
|
anno.on('selectAnnotation', (annotation) => {
|
|
76
104
|
searchParams.set('zone', annotation.id)
|
|
105
|
+
updateHighlightedZones()
|
|
77
106
|
navigate(`${location.pathname}?${createSearchParams(searchParams.toString())}`)
|
|
78
107
|
})
|
|
79
108
|
|
|
80
109
|
anno.on('cancelSelected', () => {
|
|
81
|
-
|
|
110
|
+
searchParams.delete('zone')
|
|
111
|
+
navigate(`${location.pathname}?${createSearchParams(searchParams.toString())}`)
|
|
82
112
|
})
|
|
83
113
|
}
|
|
84
|
-
}, [location.pathname
|
|
114
|
+
}, [searchParams, anno, updateHighlightedZones, folio, navigate, location.pathname])
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (folio.tileSource && viewer) {
|
|
118
|
+
viewer.open(folio.tileSource)
|
|
119
|
+
if (folio.annotations && anno) {
|
|
120
|
+
anno.setAnnotations(folio.annotations)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, [anno, viewer, folio, props.document.folioIndex])
|
|
85
124
|
|
|
86
125
|
const initViewer = async (el, tileSource) => {
|
|
87
126
|
if (!el) {
|
|
@@ -117,29 +156,11 @@ function ImageView(props) {
|
|
|
117
156
|
if (viewer) {
|
|
118
157
|
viewer.destroy()
|
|
119
158
|
}
|
|
120
|
-
}, [])
|
|
121
|
-
|
|
122
|
-
const { tileSource } = props.document.folioIndex[props.folioID]
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
const folio = props.document.folioIndex[props.folioID]
|
|
126
|
-
if (folio.loading) {
|
|
127
|
-
setLoading(true)
|
|
128
|
-
}
|
|
129
|
-
if (folio.tileSource && viewer) {
|
|
130
|
-
viewer.open(folio.tileSource)
|
|
131
|
-
if (folio.annotations && anno) {
|
|
132
|
-
anno.setAnnotations(folio.annotations)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (!folio.loading) {
|
|
136
|
-
setLoading(false)
|
|
137
|
-
}
|
|
138
|
-
}, [anno, viewer, props.folioID, props.document.folioIndex])
|
|
159
|
+
}, [viewer])
|
|
139
160
|
|
|
140
161
|
return (
|
|
141
|
-
<div>
|
|
142
|
-
{ tileSource
|
|
162
|
+
<div ref={imageViewRef}>
|
|
163
|
+
{ folio.tileSource
|
|
143
164
|
? (
|
|
144
165
|
<div className={`image-view imageViewComponent ${props.side}`} style={{ position: 'relative' }}>
|
|
145
166
|
<Navigation
|
|
@@ -162,9 +183,9 @@ function ImageView(props) {
|
|
|
162
183
|
<SeaDragonComponent
|
|
163
184
|
key={props.folioID}
|
|
164
185
|
side={props.side}
|
|
165
|
-
tileSource={tileSource}
|
|
186
|
+
tileSource={folio.tileSource}
|
|
166
187
|
initViewer={initViewer}
|
|
167
|
-
loading={loading}
|
|
188
|
+
loading={folio.loading}
|
|
168
189
|
/>
|
|
169
190
|
</div>
|
|
170
191
|
)
|
|
@@ -312,6 +312,11 @@ function Navigation(props) {
|
|
|
312
312
|
{DocumentHelper.transcriptionTypeLabels.glossary}
|
|
313
313
|
</MenuItem>
|
|
314
314
|
) }
|
|
315
|
+
{ props.notes && (
|
|
316
|
+
<MenuItem value="notes" key="notes">
|
|
317
|
+
{DocumentHelper.transcriptionTypeLabels.notes}
|
|
318
|
+
</MenuItem>
|
|
319
|
+
)}
|
|
315
320
|
</Select>
|
|
316
321
|
{!imageViewActive && (
|
|
317
322
|
<ToggleButton
|
|
@@ -362,6 +367,7 @@ function Navigation(props) {
|
|
|
362
367
|
document={props.document}
|
|
363
368
|
folio={folio}
|
|
364
369
|
toggleTags={toggleTags}
|
|
370
|
+
side={side}
|
|
365
371
|
/>
|
|
366
372
|
)}
|
|
367
373
|
<div className="navigationComponentNarrow">
|
|
@@ -440,6 +446,7 @@ function mapStateToProps(state) {
|
|
|
440
446
|
return {
|
|
441
447
|
document: state.document,
|
|
442
448
|
glossary: !!state.glossary.URL,
|
|
449
|
+
notes: !!state.notes.URL,
|
|
443
450
|
}
|
|
444
451
|
}
|
|
445
452
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Typography } from '@material-ui/core'
|
|
2
|
+
import React, { Component } from 'react'
|
|
3
|
+
import ReactMarkdown from 'react-markdown'
|
|
4
|
+
import remarkGfm from 'remark-gfm'
|
|
5
|
+
import { connect } from 'react-redux'
|
|
6
|
+
import Navigation from './Navigation'
|
|
7
|
+
|
|
8
|
+
class NotesView extends Component {
|
|
9
|
+
constructor() {
|
|
10
|
+
super()
|
|
11
|
+
this.state = { filterTerm: '' }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onFilterChange = (event) => {
|
|
15
|
+
const filterTerm = event.target.value
|
|
16
|
+
this.setState({ ...this.state, filterTerm })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
if (!this.props.notes.loaded)
|
|
21
|
+
return null
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div id="notesView" style={{ position: 'relative', overflow: 'auto' }}>
|
|
25
|
+
<Navigation
|
|
26
|
+
side={this.props.side}
|
|
27
|
+
onFilterChange={this.onFilterChange}
|
|
28
|
+
value={this.state.filterTerm}
|
|
29
|
+
documentView={this.props.documentView}
|
|
30
|
+
documentViewActions={this.props.documentViewActions}
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
<div id="notesViewInner">
|
|
34
|
+
<div id="notesContent">
|
|
35
|
+
<ReactMarkdown children={this.props.notes.notes} remarkPlugins={[remarkGfm]}/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function mapStateToProps(state) {
|
|
45
|
+
return {
|
|
46
|
+
notes: state.notes,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default connect(mapStateToProps)(NotesView)
|
|
@@ -12,7 +12,8 @@ class SplitPaneView extends Component {
|
|
|
12
12
|
this.splitFraction = props.threePanel ? 0.49 : 0.5
|
|
13
13
|
this.splitFractionRight = props.threePanel ? 0.01 : 0
|
|
14
14
|
this.dividerWidth = 16
|
|
15
|
-
|
|
15
|
+
this.ecComponent = document.querySelector('#diplomatic')
|
|
16
|
+
const whole = this.ecComponent.clientWidth
|
|
16
17
|
const leftW = whole / 3
|
|
17
18
|
|
|
18
19
|
const split_left = (leftW / whole)
|
|
@@ -37,12 +38,12 @@ class SplitPaneView extends Component {
|
|
|
37
38
|
// On drag, update the UI continuously
|
|
38
39
|
onDrag = (e) => {
|
|
39
40
|
if (this.dragging) {
|
|
40
|
-
const whole =
|
|
41
|
+
const whole = this.ecComponent.clientWidth - 2 * this.dividerWidth
|
|
41
42
|
let left_viewWidth
|
|
42
43
|
let right_viewWidth
|
|
43
44
|
let third_viewWidth
|
|
44
45
|
if (this.activeDivider === 1) {
|
|
45
|
-
left_viewWidth = e.clientX - this.dividerWidth / 2
|
|
46
|
+
left_viewWidth = (e.clientX - this.ecComponent.offsetLeft) - this.dividerWidth / 2
|
|
46
47
|
third_viewWidth = whole * this.splitFractionRight
|
|
47
48
|
right_viewWidth = whole - left_viewWidth - third_viewWidth
|
|
48
49
|
}
|
|
@@ -98,9 +99,9 @@ class SplitPaneView extends Component {
|
|
|
98
99
|
// Update the sizes of the panes
|
|
99
100
|
updatePaneSize() {
|
|
100
101
|
// Record state change
|
|
101
|
-
const left_px = Math.floor(Math.abs(
|
|
102
|
-
const third_px = Math.floor(Math.abs(
|
|
103
|
-
const right_px = Math.floor(
|
|
102
|
+
const left_px = Math.floor(Math.abs(this.ecComponent.clientWidth * this.splitFraction))
|
|
103
|
+
const third_px = Math.floor(Math.abs(this.ecComponent.clientWidth * this.splitFractionRight))
|
|
104
|
+
const right_px = Math.floor(this.ecComponent.clientWidth * (1.0 - this.splitFraction - this.splitFractionRight))
|
|
104
105
|
if (this.props.onWidth && left_px >= this.leftPaneMinWidth && right_px >= this.rightPaneMinWidth && third_px >= this.thirdPaneMinWidth) {
|
|
105
106
|
this.props.onWidth(left_px, right_px, third_px)
|
|
106
107
|
}
|
|
@@ -113,9 +114,9 @@ class SplitPaneView extends Component {
|
|
|
113
114
|
window.addEventListener('resize', this.onResize)
|
|
114
115
|
// Set the default width on mount
|
|
115
116
|
if (this.props.onWidth) {
|
|
116
|
-
const left_px = Math.floor(Math.abs(
|
|
117
|
-
const right_px = Math.floor(
|
|
118
|
-
const third_px = Math.floor(
|
|
117
|
+
const left_px = Math.floor(Math.abs(this.ecComponent.clientWidth * this.splitFraction))
|
|
118
|
+
const right_px = Math.floor(this.ecComponent.clientWidth * (1.0 - this.splitFraction))
|
|
119
|
+
const third_px = Math.floor(this.ecComponent.clientWidth * (1.0 - this.splitFraction))
|
|
119
120
|
this.props.onWidth(left_px, right_px, third_px)
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext } from 'react'
|
|
1
|
+
import { useContext, useMemo } from 'react'
|
|
2
2
|
import { BsCheck, BsX } from 'react-icons/bs'
|
|
3
3
|
import { GoTag } from 'react-icons/go'
|
|
4
4
|
import TagFilterContext from '../context/TagFilterContext'
|
|
@@ -17,7 +17,8 @@ function TagPill(props) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function TagToolbar(props) {
|
|
20
|
-
const {
|
|
20
|
+
const { tagsLeft, tagsRight, toggleTag } = useContext(TagFilterContext)
|
|
21
|
+
const tags = useMemo(() => (props.side === 'right' ? tagsRight : tagsLeft), [props.side, tagsRight, tagsLeft])
|
|
21
22
|
|
|
22
23
|
return (
|
|
23
24
|
<div className="tag-bar">
|
|
@@ -27,7 +28,16 @@ function TagToolbar(props) {
|
|
|
27
28
|
Tags
|
|
28
29
|
</span>
|
|
29
30
|
{props.folio.tagIds.map((xmlId) => {
|
|
30
|
-
|
|
31
|
+
let name
|
|
32
|
+
|
|
33
|
+
if (props.document.variorum) {
|
|
34
|
+
name = props.document.tags[props.folio.doc_id]
|
|
35
|
+
? props.document.tags[props.folio.doc_id][xmlId]
|
|
36
|
+
: undefined
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
name = props.document.tags[xmlId]
|
|
40
|
+
}
|
|
31
41
|
|
|
32
42
|
if (name) {
|
|
33
43
|
return (
|
|
@@ -35,7 +45,7 @@ function TagToolbar(props) {
|
|
|
35
45
|
isActive={tags.includes(xmlId)}
|
|
36
46
|
key={xmlId}
|
|
37
47
|
name={name}
|
|
38
|
-
onClick={() => toggleTag(xmlId)}
|
|
48
|
+
onClick={() => toggleTag(xmlId, props.side)}
|
|
39
49
|
/>
|
|
40
50
|
)
|
|
41
51
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import TagFilterContext from './TagFilterContext'
|
|
9
9
|
|
|
10
10
|
function getTagsFromParams(val) {
|
|
11
|
-
if (val) {
|
|
11
|
+
if (val && val !== 'null') {
|
|
12
12
|
return val.split(',')
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -20,36 +20,47 @@ function TagFilterProvider(props) {
|
|
|
20
20
|
const location = useLocation()
|
|
21
21
|
const navigate = useNavigate()
|
|
22
22
|
|
|
23
|
-
const [
|
|
23
|
+
const [tagsLeft, setTagsLeft] = useState(getTagsFromParams(searchParams.get('tagsLeft')))
|
|
24
|
+
const [tagsRight, setTagsRight] = useState(getTagsFromParams(searchParams.get('tagsRight')))
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
+
const tagParamsLeft = searchParams.get('tagsLeft')
|
|
27
|
+
const tagParamsRight = searchParams.get('tagsRight')
|
|
26
28
|
|
|
27
29
|
useEffect(() => {
|
|
28
|
-
const newTags = getTagsFromParams(
|
|
29
|
-
|
|
30
|
-
}, [
|
|
30
|
+
const newTags = getTagsFromParams(tagParamsLeft)
|
|
31
|
+
setTagsLeft(newTags)
|
|
32
|
+
}, [tagParamsLeft])
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const newTags = getTagsFromParams(tagParamsRight)
|
|
36
|
+
setTagsRight(newTags)
|
|
37
|
+
}, [tagParamsRight])
|
|
31
38
|
|
|
32
39
|
const ctxValue = useMemo(() => {
|
|
33
|
-
const toggleTag = (xmlId) => {
|
|
40
|
+
const toggleTag = (xmlId, side) => {
|
|
41
|
+
const tags = side === 'right' ? tagsRight : tagsLeft
|
|
42
|
+
const tagParams = side === 'right' ? tagParamsRight : tagParamsLeft
|
|
43
|
+
const setTags = side === 'right' ? setTagsRight : setTagsLeft
|
|
34
44
|
if (tags.includes(xmlId)) {
|
|
35
45
|
const oldTags = getTagsFromParams(tagParams)
|
|
36
46
|
const newTags = oldTags.filter(t => t !== xmlId)
|
|
37
|
-
searchParams.set(
|
|
47
|
+
searchParams.set(`tags${side === 'right' ? 'Right' : 'Left'}`, newTags.join(','))
|
|
38
48
|
setTags(newTags)
|
|
39
49
|
navigate(`${location.pathname}?${createSearchParams(searchParams.toString())}`)
|
|
40
50
|
}
|
|
41
51
|
else {
|
|
42
|
-
searchParams.set(
|
|
52
|
+
searchParams.set(`tags${side === 'right' ? 'Right' : 'Left'}`, `${tagParams},${xmlId}`)
|
|
43
53
|
setTags([...tags, xmlId])
|
|
44
54
|
navigate(`${location.pathname}?${createSearchParams(searchParams.toString())}`)
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
return {
|
|
49
|
-
|
|
59
|
+
tagsLeft,
|
|
60
|
+
tagsRight,
|
|
50
61
|
toggleTag,
|
|
51
62
|
}
|
|
52
|
-
}, [location.pathname, navigate, searchParams,
|
|
63
|
+
}, [location.pathname, navigate, searchParams, tagParamsLeft, tagParamsRight, tagsLeft, tagsRight])
|
|
53
64
|
|
|
54
65
|
return (
|
|
55
66
|
<TagFilterContext.Provider value={ctxValue}>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createTheme, ThemeProvider } from '@material-ui/core/styles'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import DiploMatic from './component/DiploMatic'
|
|
4
|
+
import { createReduxStore } from './model/ReduxStore'
|
|
5
|
+
import './scss/editioncrafter.scss'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default instantiation
|
|
9
|
+
*/
|
|
10
|
+
function EditionCrafter(props) {
|
|
11
|
+
const theme = createTheme({
|
|
12
|
+
palette: {
|
|
13
|
+
primary: {
|
|
14
|
+
main: '#792421',
|
|
15
|
+
},
|
|
16
|
+
secondary: {
|
|
17
|
+
main: '#EBE3DD',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const tagExplorerMode = props.tagExplorerMode === true
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ThemeProvider theme={theme}>
|
|
26
|
+
<DiploMatic config={props} store={createReduxStore(props)} tagExplorerMode={tagExplorerMode} />
|
|
27
|
+
</ThemeProvider>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default EditionCrafter
|