@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.
Files changed (50) hide show
  1. package/README.md +11 -0
  2. package/dist/editioncrafter.js +27385 -22786
  3. package/dist/es/src/EditionCrafter/action/NotesActions.js +11 -0
  4. package/dist/es/src/EditionCrafter/action/initialState/documentInitialState.js +15 -1
  5. package/dist/es/src/EditionCrafter/action/initialState/notesInitialState.js +8 -0
  6. package/dist/es/src/EditionCrafter/action/rootReducer.js +4 -0
  7. package/dist/es/src/EditionCrafter/component/DiploMatic.js +19 -17
  8. package/dist/es/src/EditionCrafter/component/DocumentView.js +23 -5
  9. package/dist/es/src/EditionCrafter/component/EmptyPaneView.js +22 -0
  10. package/dist/es/src/EditionCrafter/component/ImageView.js +61 -40
  11. package/dist/es/src/EditionCrafter/component/Navigation.js +7 -0
  12. package/dist/es/src/EditionCrafter/component/NotesView.js +50 -0
  13. package/dist/es/src/EditionCrafter/component/SplitPaneView.js +10 -9
  14. package/dist/es/src/EditionCrafter/component/TagToolbar.jsx +14 -4
  15. package/dist/es/src/EditionCrafter/context/TagFilter.jsx +22 -11
  16. package/dist/es/src/EditionCrafter/context/TagFilterContext.js +2 -1
  17. package/dist/es/src/EditionCrafter/index.jsx +31 -0
  18. package/dist/es/src/EditionCrafter/model/DocumentHelper.js +1 -0
  19. package/dist/es/src/EditionCrafter/model/Folio.js +27 -1
  20. package/dist/es/src/EditionCrafter/saga/RouteListenerSaga.js +33 -7
  21. package/dist/es/src/EditionCrafter/scss/_imageView.scss +1 -1
  22. package/dist/es/src/EditionCrafter/scss/_notes.scss +49 -0
  23. package/dist/es/src/EditionCrafter/scss/editioncrafter.scss +1 -0
  24. package/dist/es/src/RecordList/component/Record.jsx +1 -1
  25. package/dist/es/src/RecordList/component/RecordListView.jsx +1 -1
  26. package/dist/es/src/RecordList/component/Sidebar.jsx +1 -1
  27. package/dist/es/src/RecordList/component/SidebarTagList.jsx +1 -1
  28. package/dist/es/src/RecordList/index.jsx +1 -1
  29. package/dist/es/src/RecordList/styles/base.css +0 -16
  30. package/dist/es/src/TagExplore/assets/InsertLeft.jsx +18 -0
  31. package/dist/es/src/TagExplore/assets/InsertRight.jsx +18 -0
  32. package/dist/es/src/TagExplore/assets/Left.jsx +17 -0
  33. package/dist/es/src/TagExplore/assets/Right.jsx +17 -0
  34. package/dist/es/src/TagExplore/assets/insert_left.svg +12 -0
  35. package/dist/es/src/TagExplore/assets/insert_right.svg +12 -0
  36. package/dist/es/src/TagExplore/assets/left.svg +11 -0
  37. package/dist/es/src/TagExplore/assets/right.svg +11 -0
  38. package/dist/es/src/TagExplore/components/DocumentDetail.jsx +224 -0
  39. package/dist/es/src/TagExplore/components/NarrowSidebar.jsx +35 -0
  40. package/dist/es/src/TagExplore/components/SurfaceBrowser.jsx +176 -0
  41. package/dist/es/src/TagExplore/components/TagExploreSidebar.jsx +55 -0
  42. package/dist/es/src/TagExplore/components/TagFilters.jsx +106 -0
  43. package/dist/es/src/TagExplore/index.jsx +108 -0
  44. package/dist/es/src/TagExplore/styles/base.css +192 -0
  45. package/dist/es/src/{RecordList/component → common/components}/Pill.jsx +2 -0
  46. package/dist/es/src/common/components/Pill.style.css +16 -0
  47. package/dist/es/src/index.jsx +3 -27
  48. package/package.json +2 -27
  49. /package/dist/es/src/{RecordList/component → common/components}/Loading.jsx +0 -0
  50. /package/dist/es/src/{RecordList → common}/lib/sql.js +0 -0
@@ -0,0 +1,11 @@
1
+ const NotesActions = {}
2
+
3
+ NotesActions.loadNotes = function loadNotes(state, noteData) {
4
+ return {
5
+ ...state,
6
+ loaded: true,
7
+ notes: noteData,
8
+ }
9
+ }
10
+
11
+ export default NotesActions
@@ -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: getHeaderUrlFromManifestUrl(iiifManifest),
25
+ headerUrl: getHeaderUrlsFromManifestUrls(iiifManifest),
12
26
  transcriptionTypes,
13
27
  variorum,
14
28
  threePanel,
@@ -0,0 +1,8 @@
1
+ export default function notesInitialState(notesURL) {
2
+ return {
3
+ notes: '',
4
+ loaded: false,
5
+ URL: notesURL,
6
+ }
7
+ }
8
+
@@ -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
- <HashRouter>
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
- <ImageGridView
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
- <GlossaryView
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
- useEffect(() => {
30
- if (anno && searchParams.get('zone')) {
31
- // TODO: Figure out why annotations are an empty list
32
- // unless I wait for > 20 ms.
33
- setTimeout(() => anno.selectAnnotation(searchParams.get('zone')), 50)
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
- }, [anno])
59
+ }, [folio, tags, imageViewRef, searchParams])
60
+
61
+ useEffect(() => {
62
+ setTimeout(() => updateHighlightedZones(), 50)
63
+ }, [updateHighlightedZones])
36
64
 
37
- const onZoomGrid = (e) => {
65
+ const onZoomGrid = () => {
38
66
  props.documentViewActions.changeTranscriptionType(props.side, 'g')
39
67
  }
40
68
 
41
- const onZoomFixed_1 = (e) => {
69
+ const onZoomFixed_1 = () => {
42
70
  viewer.viewport.zoomTo(viewer.viewport.getMaxZoom())
43
71
  }
44
72
 
45
- const onZoomFixed_2 = (e) => {
73
+ const onZoomFixed_2 = () => {
46
74
  viewer.viewport.zoomTo((viewer.viewport.getMaxZoom() / 2))
47
75
  }
48
76
 
49
- const onZoomFixed_3 = (e) => {
77
+ const onZoomFixed_3 = () => {
50
78
  viewer.viewport.fitVertically()
51
79
  }
52
80
 
53
- const onZoomIn = (e) => {
81
+ const onZoomIn = () => {
54
82
  viewer.viewport.zoomBy(2)
55
83
  }
56
84
 
57
- const onZoomOut = (e) => {
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
- navigate(location.pathname)
110
+ searchParams.delete('zone')
111
+ navigate(`${location.pathname}?${createSearchParams(searchParams.toString())}`)
82
112
  })
83
113
  }
84
- }, [location.pathname, anno])
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
- const whole = window.innerWidth
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 = window.innerWidth - 2 * this.dividerWidth
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(window.innerWidth * this.splitFraction))
102
- const third_px = Math.floor(Math.abs(window.innerWidth * this.splitFractionRight))
103
- const right_px = Math.floor(window.innerWidth * (1.0 - this.splitFraction - this.splitFractionRight))
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(window.innerWidth * this.splitFraction))
117
- const right_px = Math.floor(window.innerWidth * (1.0 - this.splitFraction))
118
- const third_px = Math.floor(window.innerWidth * (1.0 - this.splitFraction))
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 { tags, toggleTag } = useContext(TagFilterContext)
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
- const name = props.document.tags[xmlId]
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 [tags, setTags] = useState(getTagsFromParams(searchParams.get('tags')))
23
+ const [tagsLeft, setTagsLeft] = useState(getTagsFromParams(searchParams.get('tagsLeft')))
24
+ const [tagsRight, setTagsRight] = useState(getTagsFromParams(searchParams.get('tagsRight')))
24
25
 
25
- const tagParams = searchParams.get('tags')
26
+ const tagParamsLeft = searchParams.get('tagsLeft')
27
+ const tagParamsRight = searchParams.get('tagsRight')
26
28
 
27
29
  useEffect(() => {
28
- const newTags = getTagsFromParams(tagParams)
29
- setTags(newTags)
30
- }, [tagParams])
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('tags', newTags.join(','))
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('tags', `${tagParams},${xmlId}`)
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
- tags,
59
+ tagsLeft,
60
+ tagsRight,
50
61
  toggleTag,
51
62
  }
52
- }, [location.pathname, navigate, searchParams, tagParams, tags])
63
+ }, [location.pathname, navigate, searchParams, tagParamsLeft, tagParamsRight, tagsLeft, tagsRight])
53
64
 
54
65
  return (
55
66
  <TagFilterContext.Provider value={ctxValue}>
@@ -1,7 +1,8 @@
1
1
  import { createContext } from 'react'
2
2
 
3
3
  const TagFilterContext = createContext({
4
- tags: [],
4
+ tagsLeft: [],
5
+ tagsRight: [],
5
6
  toggleTag: () => null,
6
7
  })
7
8
 
@@ -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
@@ -3,6 +3,7 @@ const DocumentHelper = {}
3
3
  DocumentHelper.transcriptionTypeLabels = {
4
4
  f: 'Facsimile', // keep
5
5
  glossary: 'Glossary', // keep
6
+ notes: 'Notes',
6
7
  }
7
8
 
8
9
  export default DocumentHelper