@cu-mkp/editioncrafter 1.3.0-beta.1 → 1.3.0-beta.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/README.md +7 -0
- package/dist/editioncrafter.js +34747 -31554
- package/dist/es/src/EditionCrafter/component/DiploMatic.js +19 -17
- package/dist/es/src/EditionCrafter/component/DocumentView.js +13 -9
- 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 +1 -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/Folio.js +27 -1
- package/dist/es/src/EditionCrafter/saga/RouteListenerSaga.js +22 -7
- package/dist/es/src/EditionCrafter/scss/_imageView.scss +1 -1
- package/dist/es/src/RecordList/component/PhraseListTable.jsx +108 -0
- package/dist/es/src/RecordList/component/Record.jsx +38 -2
- 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 +10 -5
- package/dist/es/src/RecordList/styles/base.css +2 -17
- package/dist/es/src/RecordList/styles/record.css +92 -4
- 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 +109 -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 -28
- /package/dist/es/src/{RecordList/component → common/components}/Loading.jsx +0 -0
- /package/dist/es/src/{RecordList → common}/lib/sql.js +0 -0
|
@@ -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,10 +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
|
-
import NotesView from './NotesView'
|
|
12
12
|
import ImageGridView from './ImageGridView'
|
|
13
13
|
import ImageView from './ImageView'
|
|
14
|
+
import NotesView from './NotesView'
|
|
14
15
|
import SinglePaneView from './SinglePaneView'
|
|
15
16
|
import SplitPaneView from './SplitPaneView'
|
|
16
17
|
import TranscriptionView from './TranscriptionView'
|
|
@@ -462,14 +463,17 @@ function DocumentView(props) {
|
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
if (viewType === 'ImageGridView') {
|
|
465
|
-
return (
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
+
)
|
|
473
477
|
)
|
|
474
478
|
}
|
|
475
479
|
|
|
@@ -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
|
)
|
|
@@ -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
|
|
@@ -24,6 +24,27 @@ function getTagIds(html) {
|
|
|
24
24
|
return tagIds
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function getZoneTagData(annotations) {
|
|
28
|
+
const tagIds = new Set()
|
|
29
|
+
const zoneTagIndex = {}
|
|
30
|
+
|
|
31
|
+
annotations.forEach((anno) => {
|
|
32
|
+
zoneTagIndex[anno.id] = []
|
|
33
|
+
anno.body.forEach((item) => {
|
|
34
|
+
if (item.purpose === 'classifying') {
|
|
35
|
+
const value = item.value.slice(1)
|
|
36
|
+
tagIds.add(value)
|
|
37
|
+
zoneTagIndex[anno.id].push(value)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
tagIds: Array.from(tagIds),
|
|
44
|
+
zoneTagIndex,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
export async function loadFolio(folioData) {
|
|
28
49
|
if (folioData.loading) {
|
|
29
50
|
return folioData
|
|
@@ -49,6 +70,11 @@ export async function loadFolio(folioData) {
|
|
|
49
70
|
})
|
|
50
71
|
}
|
|
51
72
|
|
|
73
|
+
const { tagIds, zoneTagIndex } = getZoneTagData(folio.annotations)
|
|
74
|
+
|
|
75
|
+
folio.tagIds = [...tagIds]
|
|
76
|
+
folio.zoneTagIndex = { ...zoneTagIndex }
|
|
77
|
+
|
|
52
78
|
if (transcriptionTypes.length > 0) {
|
|
53
79
|
for await (const transcriptionType of transcriptionTypes) {
|
|
54
80
|
const { htmlURL, xmlURL } = folio.annotationURLs[transcriptionType]
|
|
@@ -68,7 +94,7 @@ export async function loadFolio(folioData) {
|
|
|
68
94
|
}
|
|
69
95
|
else {
|
|
70
96
|
folio.transcription[transcriptionType] = transcription
|
|
71
|
-
folio.tagIds = tagIds
|
|
97
|
+
folio.tagIds = [...folio.tagIds, ...tagIds]
|
|
72
98
|
folio.loading = false
|
|
73
99
|
transcriptionTypeTracker[transcriptionType] = true
|
|
74
100
|
}
|
|
@@ -7,13 +7,10 @@ const justDocument = state => state.document
|
|
|
7
7
|
const justGlossary = state => state.glossary
|
|
8
8
|
const justNotes = state => state.notes
|
|
9
9
|
|
|
10
|
-
function*
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const res = yield fetch(headerUrl)
|
|
10
|
+
function* parseTagUrl(url) {
|
|
11
|
+
const res = yield fetch(url)
|
|
14
12
|
|
|
15
13
|
if (!res.ok) {
|
|
16
|
-
yield putResolveAction('DocumentActions.loadTags', tags)
|
|
17
14
|
return null
|
|
18
15
|
}
|
|
19
16
|
|
|
@@ -23,6 +20,8 @@ function* parseTags(headerUrl) {
|
|
|
23
20
|
|
|
24
21
|
const categoryEls = headerDoc.querySelectorAll('tei-category')
|
|
25
22
|
|
|
23
|
+
const documentTags = {}
|
|
24
|
+
|
|
26
25
|
for (const categoryEl of categoryEls) {
|
|
27
26
|
const xmlId = categoryEl.getAttribute('id')
|
|
28
27
|
|
|
@@ -30,12 +29,28 @@ function* parseTags(headerUrl) {
|
|
|
30
29
|
const desc = categoryEl.querySelector('tei-catdesc')
|
|
31
30
|
if (desc) {
|
|
32
31
|
const name = desc.textContent
|
|
33
|
-
|
|
32
|
+
documentTags[xmlId] = name
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
return documentTags
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function* parseTags(headerUrl) {
|
|
41
|
+
if (typeof headerUrl === 'string') {
|
|
42
|
+
const result = yield parseTagUrl(headerUrl)
|
|
43
|
+
yield putResolveAction('DocumentActions.loadTags', result)
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const tags = {}
|
|
47
|
+
|
|
48
|
+
for (const docId of Object.keys(headerUrl)) {
|
|
49
|
+
tags[docId] = yield parseTagUrl(headerUrl[docId])
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
yield putResolveAction('DocumentActions.loadTags', tags)
|
|
53
|
+
}
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
function* userNavigation(action) {
|