@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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FaQuestionCircle
|
|
3
|
+
} from 'react-icons/fa'
|
|
4
|
+
|
|
5
|
+
import { BsFillGrid3X3GapFill } from 'react-icons/bs'
|
|
6
|
+
import { List, ListItem, ListItemIcon, Paper } from '@material-ui/core'
|
|
7
|
+
|
|
8
|
+
function NarrowSidebar(props) {
|
|
9
|
+
const { helpRef, toggleDrawer, toggleHelp } = props
|
|
10
|
+
return (
|
|
11
|
+
<Paper className="narrow-sidebar">
|
|
12
|
+
<List>
|
|
13
|
+
<ListItem
|
|
14
|
+
onClick={toggleDrawer}
|
|
15
|
+
button
|
|
16
|
+
>
|
|
17
|
+
<ListItemIcon>
|
|
18
|
+
<BsFillGrid3X3GapFill />
|
|
19
|
+
</ListItemIcon>
|
|
20
|
+
</ListItem>
|
|
21
|
+
<ListItem
|
|
22
|
+
onClick={toggleHelp}
|
|
23
|
+
ref={helpRef}
|
|
24
|
+
button
|
|
25
|
+
>
|
|
26
|
+
<ListItemIcon>
|
|
27
|
+
<FaQuestionCircle />
|
|
28
|
+
</ListItemIcon>
|
|
29
|
+
</ListItem>
|
|
30
|
+
</List>
|
|
31
|
+
</Paper>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default NarrowSidebar
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Box, Button, ButtonGroup, Collapse, Divider, IconButton, Typography } from '@material-ui/core'
|
|
2
|
+
import { red } from '@material-ui/core/colors'
|
|
3
|
+
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
|
|
4
|
+
import GridOnIcon from '@material-ui/icons/GridOn'
|
|
5
|
+
import ListIcon from '@material-ui/icons/List'
|
|
6
|
+
import TuneIcon from '@material-ui/icons/Tune'
|
|
7
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
8
|
+
|
|
9
|
+
import { useLocation, useNavigate } from 'react-router-dom'
|
|
10
|
+
import { getObjs } from '../../common/lib/sql'
|
|
11
|
+
import DocumentDetail from './DocumentDetail'
|
|
12
|
+
import TagFilters from './TagFilters'
|
|
13
|
+
|
|
14
|
+
function getData(db) {
|
|
15
|
+
const docStmt = db.prepare(`
|
|
16
|
+
SELECT
|
|
17
|
+
documents.id AS id,
|
|
18
|
+
documents.name AS name,
|
|
19
|
+
documents.local_id AS local_id
|
|
20
|
+
FROM
|
|
21
|
+
documents
|
|
22
|
+
`)
|
|
23
|
+
|
|
24
|
+
return getObjs(docStmt)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseFolioID(folioID) {
|
|
28
|
+
if (!folioID) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
const parts = folioID.split('_')
|
|
32
|
+
const localID = parts.slice(0, parts.length - 1).join('_')
|
|
33
|
+
const surfaceID = parts[parts.length - 1]
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
localID,
|
|
37
|
+
surfaceID,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getSelection(path) {
|
|
42
|
+
const parts = path.split('/')
|
|
43
|
+
const folioID = parts[2]
|
|
44
|
+
const folioID2 = parts[4]
|
|
45
|
+
const left = parseFolioID(folioID)
|
|
46
|
+
const right = parseFolioID(folioID2)
|
|
47
|
+
return { left, right }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function SurfaceBrowser(props) {
|
|
51
|
+
const { db, open, toggleOpen } = props
|
|
52
|
+
const documents = useMemo(() => getData(db), [db])
|
|
53
|
+
const [pageCount, setPageCount] = useState({})
|
|
54
|
+
const [totalPages, setTotalPages] = useState(0)
|
|
55
|
+
const [tags, setTags] = useState([])
|
|
56
|
+
const [showFilters, setShowFilters] = useState(false)
|
|
57
|
+
|
|
58
|
+
const navigate = useNavigate()
|
|
59
|
+
const location = useLocation()
|
|
60
|
+
const selection = useMemo(() => getSelection(location.pathname), [location])
|
|
61
|
+
|
|
62
|
+
const navigateToSelection = (nextSelection) => {
|
|
63
|
+
const folioID = nextSelection?.left ? `${nextSelection.left.localID}_${nextSelection.left.surfaceID}` : null
|
|
64
|
+
const folioID2 = nextSelection?.right ? `${nextSelection.right.localID}_${nextSelection.right.surfaceID}` : null
|
|
65
|
+
const navParams = `/ec/${folioID || '-1'}/${folioID ? 'f' : 'g'}/${folioID2 || '-1'}/${folioID2 ? 'f' : 'g'}`
|
|
66
|
+
navigate(navParams + location.search)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const updatePageCount = (documentID, numPages) => {
|
|
70
|
+
const newCount = pageCount
|
|
71
|
+
newCount[documentID] = numPages
|
|
72
|
+
setPageCount(newCount)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
let p = 0
|
|
77
|
+
for (const key of Object.keys(pageCount)) {
|
|
78
|
+
p += pageCount[key]
|
|
79
|
+
}
|
|
80
|
+
setTotalPages(p)
|
|
81
|
+
}, [pageCount, tags])
|
|
82
|
+
|
|
83
|
+
const documentDetails = documents.map((doc) => {
|
|
84
|
+
return (
|
|
85
|
+
<DocumentDetail
|
|
86
|
+
key={`document-detail-${doc.id}`}
|
|
87
|
+
db={db}
|
|
88
|
+
documentName={doc.name}
|
|
89
|
+
documentLocalID={doc.local_id}
|
|
90
|
+
documentID={doc.id}
|
|
91
|
+
selection={selection}
|
|
92
|
+
navigateToSelection={navigateToSelection}
|
|
93
|
+
updatePageCount={count => updatePageCount(doc.id, count)}
|
|
94
|
+
tags={tags}
|
|
95
|
+
>
|
|
96
|
+
</DocumentDetail>
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Collapse in={open} horizontal>
|
|
102
|
+
<Box
|
|
103
|
+
className="surface-browser"
|
|
104
|
+
>
|
|
105
|
+
<IconButton onClick={toggleOpen} className="surface-browser-close">
|
|
106
|
+
<ChevronLeftIcon />
|
|
107
|
+
</IconButton>
|
|
108
|
+
<Divider></Divider>
|
|
109
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
110
|
+
<Typography>Contents</Typography>
|
|
111
|
+
<Button
|
|
112
|
+
startIcon={<TuneIcon />}
|
|
113
|
+
onClick={() => setShowFilters(current => (!current))}
|
|
114
|
+
>
|
|
115
|
+
Filter
|
|
116
|
+
{ tags && tags.length
|
|
117
|
+
? (
|
|
118
|
+
<div style={{
|
|
119
|
+
fontSize: 'small',
|
|
120
|
+
backgroundColor: 'red',
|
|
121
|
+
borderRadius: '999px',
|
|
122
|
+
display: 'flex',
|
|
123
|
+
justifyContent: 'center',
|
|
124
|
+
alignItems: 'center',
|
|
125
|
+
padding: '3px',
|
|
126
|
+
color: 'white',
|
|
127
|
+
height: '16px',
|
|
128
|
+
width: '16px',
|
|
129
|
+
position: 'absolute',
|
|
130
|
+
top: '0',
|
|
131
|
+
left: '-12px',
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{tags.length}
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
: null}
|
|
138
|
+
</Button>
|
|
139
|
+
</div>
|
|
140
|
+
<Typography>
|
|
141
|
+
{totalPages}
|
|
142
|
+
{' '}
|
|
143
|
+
Page
|
|
144
|
+
{ totalPages !== 1 ? 's' : ''}
|
|
145
|
+
</Typography>
|
|
146
|
+
{/* <ButtonGroup color="primary" aria-label="outlined primary button group">
|
|
147
|
+
<IconButton aria-label="grid">
|
|
148
|
+
<GridOnIcon></GridOnIcon>
|
|
149
|
+
</IconButton>
|
|
150
|
+
<IconButton aria-label="list">
|
|
151
|
+
<ListIcon></ListIcon>
|
|
152
|
+
</IconButton>
|
|
153
|
+
</ButtonGroup> */}
|
|
154
|
+
{ showFilters && (
|
|
155
|
+
<TagFilters
|
|
156
|
+
db={db}
|
|
157
|
+
filters={tags}
|
|
158
|
+
onToggleSelected={(tagId) => {
|
|
159
|
+
if (tags.includes(tagId)) {
|
|
160
|
+
setTags(current => (current.filter(t => (t !== tagId))))
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
setTags(current => ([...current, tagId]))
|
|
164
|
+
}
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
) }
|
|
168
|
+
<Box className="surface-browser-document-details">
|
|
169
|
+
{ documentDetails }
|
|
170
|
+
</Box>
|
|
171
|
+
</Box>
|
|
172
|
+
</Collapse>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export default SurfaceBrowser
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createTheme, ThemeProvider } from '@material-ui/core/styles'
|
|
2
|
+
import React, { useRef, useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import HelpPopper from '../../EditionCrafter/component/HelpPopper'
|
|
5
|
+
import NarrowSidebar from './NarrowSidebar'
|
|
6
|
+
import SurfaceBrowser from './SurfaceBrowser'
|
|
7
|
+
|
|
8
|
+
function TagExploreSidebar(props) {
|
|
9
|
+
const { db } = props
|
|
10
|
+
const [openHelp, setOpenHelp] = useState(false)
|
|
11
|
+
const [openDrawer, setOpenDrawer] = useState(false)
|
|
12
|
+
|
|
13
|
+
const toggleHelp = () => {
|
|
14
|
+
setOpenHelp(!openHelp)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const toggleDrawer = () => {
|
|
18
|
+
setOpenDrawer(!openDrawer)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const helpRef = useRef(null)
|
|
22
|
+
const helpMarginStyle = { marginLeft: '60px', backgroundColor: '#242629', zIndex: '9999' }
|
|
23
|
+
const theme = createTheme({
|
|
24
|
+
palette: {
|
|
25
|
+
type: 'dark',
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="tag-explore-sidebar">
|
|
31
|
+
<ThemeProvider theme={theme}>
|
|
32
|
+
<NarrowSidebar
|
|
33
|
+
toggleDrawer={toggleDrawer}
|
|
34
|
+
toggleHelp={toggleHelp}
|
|
35
|
+
helpRef={helpRef}
|
|
36
|
+
>
|
|
37
|
+
</NarrowSidebar>
|
|
38
|
+
<SurfaceBrowser
|
|
39
|
+
db={db}
|
|
40
|
+
open={openDrawer}
|
|
41
|
+
toggleOpen={toggleDrawer}
|
|
42
|
+
>
|
|
43
|
+
</SurfaceBrowser>
|
|
44
|
+
<HelpPopper
|
|
45
|
+
marginStyle={helpMarginStyle}
|
|
46
|
+
anchorEl={helpRef.current}
|
|
47
|
+
open={openHelp}
|
|
48
|
+
onClose={toggleHelp}
|
|
49
|
+
/>
|
|
50
|
+
</ThemeProvider>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default TagExploreSidebar
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Checkbox, FormControlLabel, FormGroup, Typography } from '@material-ui/core'
|
|
2
|
+
import { useContext, useEffect, useMemo, useState } from 'react'
|
|
3
|
+
import { getObjs } from '../../common/lib/sql'
|
|
4
|
+
import TagFilterContext from '../../EditionCrafter/context/TagFilterContext'
|
|
5
|
+
|
|
6
|
+
function getData(db) {
|
|
7
|
+
const taxonomiesStmt = db.prepare(`
|
|
8
|
+
SELECT
|
|
9
|
+
*
|
|
10
|
+
FROM
|
|
11
|
+
taxonomies;
|
|
12
|
+
`)
|
|
13
|
+
|
|
14
|
+
const tagsStmt = db.prepare(`
|
|
15
|
+
SELECT
|
|
16
|
+
tags.id AS id,
|
|
17
|
+
tags.name AS name,
|
|
18
|
+
tags.xml_id AS xml_id,
|
|
19
|
+
taxonomies.name as taxonomy,
|
|
20
|
+
taxonomies.id as taxonomy_id
|
|
21
|
+
FROM
|
|
22
|
+
tags
|
|
23
|
+
LEFT JOIN taxonomies
|
|
24
|
+
ON tags.taxonomy_id = taxonomies.id
|
|
25
|
+
INNER JOIN taggings
|
|
26
|
+
ON taggings.tag_id = tags.id
|
|
27
|
+
INNER JOIN elements
|
|
28
|
+
ON elements.id = taggings.element_id
|
|
29
|
+
WHERE
|
|
30
|
+
elements.type = 'zone'
|
|
31
|
+
GROUP BY
|
|
32
|
+
tags.xml_id`)
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
tags: getObjs(tagsStmt),
|
|
36
|
+
taxonomies: getObjs(taxonomiesStmt),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function TagFilters(props) {
|
|
41
|
+
const { onToggleSelected, filters } = props
|
|
42
|
+
const data = useMemo(() => getData(props.db), [props.db])
|
|
43
|
+
const [expanded, setExpanded] = useState(data.taxonomies?.map(() => (false)))
|
|
44
|
+
const [displayedTags, setDisplayedTags] = useState({})
|
|
45
|
+
|
|
46
|
+
const { toggleTag } = useContext(TagFilterContext)
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const tags = {}
|
|
50
|
+
for (let i = 0; i < data.taxonomies.length; i++) {
|
|
51
|
+
const tax = data.taxonomies[i]
|
|
52
|
+
const tagList = expanded[i] ? data.tags.filter(t => (t.taxonomy_id === tax.id)) : data.tags.filter(t => (t.taxonomy_id === tax.id))?.slice(0, 5)
|
|
53
|
+
tags[tax.id] = tagList
|
|
54
|
+
}
|
|
55
|
+
setDisplayedTags(tags)
|
|
56
|
+
}, [expanded, data])
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="tag-filters">
|
|
60
|
+
<div className="tag-list">
|
|
61
|
+
<FormGroup>
|
|
62
|
+
{ data.taxonomies.map((tax, idx) => {
|
|
63
|
+
const tagList = displayedTags[tax.id]
|
|
64
|
+
return (
|
|
65
|
+
<div key={tax.id}>
|
|
66
|
+
<Typography>{tax.name}</Typography>
|
|
67
|
+
<ul>
|
|
68
|
+
{ tagList?.map(tag => (
|
|
69
|
+
<FormControlLabel
|
|
70
|
+
as="li"
|
|
71
|
+
control={(
|
|
72
|
+
<Checkbox
|
|
73
|
+
checked={filters.includes(tag.id)}
|
|
74
|
+
onChange={() => {
|
|
75
|
+
onToggleSelected(tag.id)
|
|
76
|
+
toggleTag(tag.xml_id, 'left')
|
|
77
|
+
toggleTag(tag.xml_id, 'right')
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
)}
|
|
81
|
+
key={tag.id}
|
|
82
|
+
label={tag.name}
|
|
83
|
+
/>
|
|
84
|
+
))}
|
|
85
|
+
</ul>
|
|
86
|
+
<button
|
|
87
|
+
className="tag-filter-button"
|
|
88
|
+
type="button"
|
|
89
|
+
onClick={() => {
|
|
90
|
+
const newState = [...expanded]
|
|
91
|
+
newState[idx] = !expanded[idx]
|
|
92
|
+
setExpanded(newState)
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{ !data.tags.filter(t => (t.taxonomy_id === tax.id))?.length || data.tags.filter(t => (t.taxonomy_id === tax.id)).length < 6 ? null : expanded[idx] ? 'Show less' : 'Show more'}
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
})}
|
|
100
|
+
</FormGroup>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default TagFilters
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { HashRouter } from 'react-router-dom'
|
|
3
|
+
import initSqlJs from 'sql.js'
|
|
4
|
+
import Loading from '../common/components/Loading'
|
|
5
|
+
import { getObjs } from '../common/lib/sql'
|
|
6
|
+
import EditionCrafter from '../EditionCrafter'
|
|
7
|
+
import TagFilterProvider from '../EditionCrafter/context/TagFilter'
|
|
8
|
+
import TagExploreSidebar from './components/TagExploreSidebar'
|
|
9
|
+
import './styles/base.css'
|
|
10
|
+
|
|
11
|
+
const initialFilters = {
|
|
12
|
+
categories: [],
|
|
13
|
+
tags: [],
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function initDb(url) {
|
|
17
|
+
const file = await fetch(url)
|
|
18
|
+
|
|
19
|
+
if (!file.ok) {
|
|
20
|
+
throw new Error('Failed fetching SQLite file.')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const buf = await file.arrayBuffer()
|
|
24
|
+
const arr = new Uint8Array(buf)
|
|
25
|
+
|
|
26
|
+
const db = await initSqlJs({
|
|
27
|
+
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.12.0/${file}`,
|
|
28
|
+
}).then((SQL) => {
|
|
29
|
+
const db = new SQL.Database(arr)
|
|
30
|
+
return db
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return db
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getData(db) {
|
|
37
|
+
const documentStmt = db.prepare(`
|
|
38
|
+
SELECT
|
|
39
|
+
documents.name AS name,
|
|
40
|
+
documents.local_id AS local_id
|
|
41
|
+
FROM
|
|
42
|
+
documents
|
|
43
|
+
`)
|
|
44
|
+
|
|
45
|
+
return getObjs(documentStmt)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function generateECProps(props, db) {
|
|
49
|
+
const documents = getData(db)
|
|
50
|
+
const { documentName, baseURL, transcriptionTypes } = props
|
|
51
|
+
const documentInfo = {}
|
|
52
|
+
|
|
53
|
+
for (const document of documents) {
|
|
54
|
+
documentInfo[document.local_id] = {
|
|
55
|
+
documentName: document.name,
|
|
56
|
+
transcriptionTypes,
|
|
57
|
+
iiifManifest: `${baseURL}/${document.local_id}/iiif/manifest.json`,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
documentName,
|
|
63
|
+
documentInfo,
|
|
64
|
+
tagExplorerMode: true,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function TagExplore(props) {
|
|
69
|
+
const [db, setDb] = useState(null)
|
|
70
|
+
const [ecProps, setECProps] = useState(null)
|
|
71
|
+
const [filters, setFilters] = useState(initialFilters)
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const loadDb = async () => {
|
|
75
|
+
const db = await initDb(props.dbUrl)
|
|
76
|
+
const ecProps = generateECProps(props, db)
|
|
77
|
+
setDb(db)
|
|
78
|
+
setECProps(ecProps)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!db) {
|
|
82
|
+
loadDb()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
if (db) {
|
|
87
|
+
db.close()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}, [props.dbUrl, db])
|
|
91
|
+
|
|
92
|
+
if (!db || !ecProps) {
|
|
93
|
+
return <Loading />
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="tag-explore">
|
|
98
|
+
<HashRouter>
|
|
99
|
+
<TagFilterProvider>
|
|
100
|
+
<TagExploreSidebar db={db} />
|
|
101
|
+
<EditionCrafter {...ecProps} />
|
|
102
|
+
</TagFilterProvider>
|
|
103
|
+
</HashRouter>
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default TagExplore
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
.tag-explore {
|
|
2
|
+
display: grid;
|
|
3
|
+
max-height: 100vh;
|
|
4
|
+
grid-template-columns: 50px 1fr;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.tag-explore-sidebar {
|
|
8
|
+
position: relative;
|
|
9
|
+
z-index: 999;
|
|
10
|
+
height: 100%;
|
|
11
|
+
max-height: 100dvh;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.tag-explore .narrow-sidebar {
|
|
15
|
+
height: 100vh;
|
|
16
|
+
width: 48px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.tag-explore-sidebar {
|
|
20
|
+
overflow-y: none;
|
|
21
|
+
height: 100%;
|
|
22
|
+
background-color: black;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.tag-explore .tag-filters {
|
|
26
|
+
overflow-y: auto;
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: 48px;
|
|
29
|
+
left: 0;
|
|
30
|
+
background-color: #242629;
|
|
31
|
+
z-index: 10000;
|
|
32
|
+
padding: 12px 0 12px 12px;
|
|
33
|
+
width: 100%;
|
|
34
|
+
height: 100%;
|
|
35
|
+
max-height: 100dvh;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.tag-explore .tag-filter-button {
|
|
39
|
+
background-color: #242629;
|
|
40
|
+
padding: 12px;
|
|
41
|
+
color: white;
|
|
42
|
+
border: 1px solid white;
|
|
43
|
+
border-radius: 6px;
|
|
44
|
+
margin: 6px auto 12px auto;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.tag-explore .tag-filter-button:hover {
|
|
49
|
+
background-color: white;
|
|
50
|
+
color: black;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.tag-explore .tag-filters ul {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.tag-explore .surface-browser-close {
|
|
59
|
+
position: absolute;
|
|
60
|
+
top: 0;
|
|
61
|
+
right: -24px;
|
|
62
|
+
background-color: #242629;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.tag-explore .tag-filters .tag-list, .tag-explore-sidebar {
|
|
66
|
+
display: flex;
|
|
67
|
+
gap: 4px;
|
|
68
|
+
flex-wrap: wrap;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.surface-thumbnail-caption {
|
|
72
|
+
text-align: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.surface-thumbnail-overlay {
|
|
76
|
+
position: absolute;
|
|
77
|
+
width: 100%;
|
|
78
|
+
height: 100%;
|
|
79
|
+
inset: 0;
|
|
80
|
+
background-color: #2426297d;
|
|
81
|
+
opacity: 0%;
|
|
82
|
+
display: flex;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 12px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.surface-thumbnail-overlay-selected {
|
|
89
|
+
position: absolute;
|
|
90
|
+
width: 100%;
|
|
91
|
+
height: 100%;
|
|
92
|
+
inset: 0;
|
|
93
|
+
display: flex;
|
|
94
|
+
justify-content: flex-end;
|
|
95
|
+
align-items: flex-end;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.surface-thumbnail-overlay-selected.left {
|
|
99
|
+
border: 2px solid #008F81;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.surface-thumbnail-overlay-selected.right {
|
|
103
|
+
border: 2px solid #A31621;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.surface-thumbnail-overlay-selected > div {
|
|
107
|
+
color: white;
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: row;
|
|
110
|
+
justify-content: space-around;
|
|
111
|
+
gap: 6px;
|
|
112
|
+
align-items: center;
|
|
113
|
+
padding: 6px;
|
|
114
|
+
border-top-left-radius: 6px;
|
|
115
|
+
font-size: small;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.surface-thumbnail-overlay-selected.left > div {
|
|
119
|
+
background-color: #008F81;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.surface-thumbnail-overlay-selected.right > div {
|
|
123
|
+
background-color: #A31621;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.surface-thumbnail-overlay .surface-thumbnail-overlay-content {
|
|
127
|
+
opacity: 100%;
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: row;
|
|
130
|
+
gap: 12px;
|
|
131
|
+
justify-content: center;
|
|
132
|
+
align-items: center;
|
|
133
|
+
border-radius: 12px;
|
|
134
|
+
padding: 8px;
|
|
135
|
+
background-color: #242629;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.surface-thumbnail-overlay:hover {
|
|
139
|
+
opacity: 100%;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.surface-thumbnail-overlay a {
|
|
143
|
+
cursor: pointer;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.surface-thumbnail-figure {
|
|
147
|
+
padding: 0;
|
|
148
|
+
margin: 16px;
|
|
149
|
+
position: relative;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.tag-explore .surface-browser {
|
|
153
|
+
position: absolute;
|
|
154
|
+
top: 0;
|
|
155
|
+
left: 48px;
|
|
156
|
+
height: 100vh;
|
|
157
|
+
min-width: 300px;
|
|
158
|
+
display: flex;
|
|
159
|
+
flex-direction: column;
|
|
160
|
+
background-color: #242629;
|
|
161
|
+
color: white;
|
|
162
|
+
padding: 12px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.tag-explore .surface-browser-document-details {
|
|
166
|
+
overflow-y: auto;
|
|
167
|
+
position: relative;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.tag-explore .accordion-summary {
|
|
171
|
+
flex-direction: row-reverse;
|
|
172
|
+
background-color: #242629
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.tag-explore .accordion-summary div {
|
|
176
|
+
display: flex;
|
|
177
|
+
flex-direction: row;
|
|
178
|
+
justify-content: space-between;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* .tag-explore .accordion-summary .MuiAccordionSummary-expandIcon {
|
|
182
|
+
padding-left: 0;
|
|
183
|
+
} */
|
|
184
|
+
|
|
185
|
+
.tag-explore .accordion-summary .MuiAccordionSummary-content {
|
|
186
|
+
padding-left: 12px;
|
|
187
|
+
gap: 12px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.tag-explore .accordion-detail {
|
|
191
|
+
background-color: #242629;
|
|
192
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.pill {
|
|
2
|
+
appearance: none;
|
|
3
|
+
display: flex;
|
|
4
|
+
height: 32px;
|
|
5
|
+
padding: 4px 12px;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: 4px;
|
|
9
|
+
border-radius: 50px;
|
|
10
|
+
border: 1px solid #CEDAE7;
|
|
11
|
+
background: #CEDAE7;
|
|
12
|
+
color: #0A0A0A;
|
|
13
|
+
font-family: Inter;
|
|
14
|
+
font-size: 14px;
|
|
15
|
+
font-weight: 500;
|
|
16
|
+
}
|