@cu-mkp/editioncrafter 1.3.1-beta.7 → 1.3.1-beta.9

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.
@@ -216,6 +216,7 @@ function parseSingleManifest(manifest, transcriptionTypes, document) {
216
216
  annotations: canvas.annotations
217
217
  ? canvas.annotations.filter(a => a.motivation === 'tagging')
218
218
  : [],
219
+ metadata: canvas.metadata,
219
220
  }
220
221
 
221
222
  folios.push(folio)
@@ -4,12 +4,11 @@ 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'
8
-
9
7
  import diplomaticInitialState from './initialState/diplomaticInitialState'
10
8
  import documentInitialState from './initialState/documentInitialState'
11
9
  import glossaryInitialState from './initialState/glossaryInitialState'
12
10
  import notesInitialState from './initialState/notesInitialState'
11
+ import NotesActions from './NotesActions'
13
12
 
14
13
  export default function rootReducer(config) {
15
14
  const {
@@ -30,8 +29,14 @@ export default function rootReducer(config) {
30
29
  derivativesInfo[key] = config.documentInfo[key].documentName
31
30
  })
32
31
  }
33
- const transcriptionTypes = variorum ? transcriptionTypesInfo : config.transcriptionTypes
34
- const iiifManifest = variorum ? manifestInfo : config.iiifManifest
32
+
33
+ // handle the case that there's exactly one document in documentInfo
34
+ let docConfig = config
35
+ if (documentInfo && Object.keys(documentInfo).length === 1) {
36
+ docConfig = Object.values(documentInfo)[0]
37
+ }
38
+ const transcriptionTypes = variorum ? transcriptionTypesInfo : docConfig.transcriptionTypes
39
+ const iiifManifest = variorum ? manifestInfo : docConfig.iiifManifest
35
40
  const derivativeNames = variorum && derivativesInfo
36
41
  return combineReducers({
37
42
  diplomatic: createReducer('DiplomaticActions', DiplomaticActions, diplomaticInitialState),
@@ -0,0 +1,13 @@
1
+ export default function ImageMetadata(props) {
2
+ const { data } = props
3
+
4
+ return (
5
+ <div className="image-metadata">
6
+ { Object.keys(data)?.map(key => (
7
+ <div key={key}>
8
+ { `${key}: ${data[key]}` }
9
+ </div>
10
+ ))}
11
+ </div>
12
+ )
13
+ }
@@ -10,6 +10,7 @@ import {
10
10
  useSearchParams,
11
11
  } from 'react-router-dom'
12
12
  import TagFilterContext from '../context/TagFilterContext'
13
+ import ImageMetadata from './ImageMetadata'
13
14
  import ImageZoomControl from './ImageZoomControl'
14
15
  import Navigation from './Navigation'
15
16
 
@@ -187,6 +188,11 @@ function ImageView(props) {
187
188
  initViewer={initViewer}
188
189
  loading={folio.loading}
189
190
  />
191
+ {
192
+ folio.metadata && (
193
+ <ImageMetadata data={folio.metadata} />
194
+ )
195
+ }
190
196
  </div>
191
197
  )
192
198
  : (
@@ -0,0 +1,16 @@
1
+ .editioncrafter {
2
+
3
+ .image-metadata {
4
+ position: absolute;
5
+ bottom: 8px;
6
+ left: 8px;
7
+ background-color: rgba(128, 128, 128, 0.5);
8
+ color: white;
9
+ display: flex;
10
+ flex-direction: row;
11
+ gap: 16px;
12
+ flex-wrap: wrap;
13
+ padding: 4px 6px;
14
+ }
15
+
16
+ }
@@ -99,6 +99,7 @@ animation: #{$str};
99
99
 
100
100
  @import "diplomatic";
101
101
  @import "imageGridView";
102
+ @import "imageMetadata";
102
103
  @import "splitPaneView";
103
104
  @import "imageView";
104
105
  @import "imageZoomControl";
@@ -1,5 +1,6 @@
1
- import { Checkbox, FormControlLabel, FormGroup, Typography } from '@material-ui/core'
2
- import { useContext, useEffect, useMemo, useState } from 'react'
1
+ import { Accordion, AccordionDetails, AccordionSummary, Checkbox, FormControlLabel, FormGroup, Typography } from '@material-ui/core'
2
+ import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
3
+ import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
3
4
  import { getObjs } from '../../common/lib/sql'
4
5
  import TagFilterContext from '../../EditionCrafter/context/TagFilterContext'
5
6
 
@@ -9,6 +10,17 @@ function getData(db) {
9
10
  *
10
11
  FROM
11
12
  taxonomies;
13
+ ORDER BY
14
+ name ASC;
15
+ `)
16
+
17
+ const categoriesStmt = db.prepare(`
18
+ SELECT
19
+ *
20
+ FROM
21
+ categories
22
+ ORDER BY
23
+ name ASC;
12
24
  `)
13
25
 
14
26
  const tagsStmt = db.prepare(`
@@ -16,6 +28,7 @@ function getData(db) {
16
28
  tags.id AS id,
17
29
  tags.name AS name,
18
30
  tags.xml_id AS xml_id,
31
+ tags.parent_category_id AS parent_category_id,
19
32
  taxonomies.name as taxonomy,
20
33
  taxonomies.id as taxonomy_id
21
34
  FROM
@@ -23,18 +36,119 @@ function getData(db) {
23
36
  LEFT JOIN taxonomies
24
37
  ON tags.taxonomy_id = taxonomies.id
25
38
  GROUP BY
26
- tags.xml_id`)
39
+ tags.xml_id
40
+ ORDER BY
41
+ tags.name ASC`)
27
42
 
28
43
  return {
29
44
  tags: getObjs(tagsStmt),
45
+ categories: getObjs(categoriesStmt),
30
46
  taxonomies: getObjs(taxonomiesStmt),
31
47
  }
32
48
  }
33
49
 
50
+ function CategoryFilter(props) {
51
+ const {
52
+ name,
53
+ categoryId,
54
+ tags,
55
+ categories,
56
+ toggleTag,
57
+ onToggleSelected,
58
+ isSurface,
59
+ filters,
60
+ } = props
61
+
62
+ const hasDescendentTags = useCallback((catId) => {
63
+ if (tags?.filter(tag => (tag.parent_category_id === catId))?.length) {
64
+ return true
65
+ }
66
+
67
+ const subs = categories?.filter(cat => (cat.parent_category_id === catId))
68
+
69
+ for (const sub of subs) {
70
+ if (hasDescendentTags(sub.id)) {
71
+ return true
72
+ }
73
+ }
74
+
75
+ return false
76
+ }, [tags, categories])
77
+
78
+ const categoryTags = useMemo(() => {
79
+ return tags?.filter(tag => (tag.parent_category_id === categoryId))
80
+ }, [tags, categoryId])
81
+
82
+ const subcategories = useMemo(() => {
83
+ return categories?.filter(cat => (cat.parent_category_id === categoryId))
84
+ }, [categories, categoryId])
85
+
86
+ return hasDescendentTags(categoryId) && (
87
+ <Accordion>
88
+ <AccordionSummary
89
+ expandIcon={<ExpandMoreIcon />}
90
+ aria-controls={`category-tags-${name}-content`}
91
+ id={`category-tags-${name}`}
92
+ className="accordion-summary"
93
+ >
94
+ <Typography>{name}</Typography>
95
+ <Typography>{categoryTags?.length || ''}</Typography>
96
+ </AccordionSummary>
97
+ <AccordionDetails
98
+ className="accordion-detail"
99
+ >
100
+ { !!categoryTags?.length && (
101
+ <ul>
102
+ { categoryTags?.map(tag => (
103
+ <FormControlLabel
104
+ as="li"
105
+ control={(
106
+ <Checkbox
107
+ checked={filters.includes(tag.id)}
108
+ onChange={() => {
109
+ onToggleSelected(tag.id)
110
+ if (isSurface) {
111
+ toggleTag(tag.xml_id, 'left')
112
+ toggleTag(tag.xml_id, 'right')
113
+ }
114
+ }}
115
+ />
116
+ )}
117
+ key={tag.id}
118
+ label={tag.name}
119
+ />
120
+ ))}
121
+ </ul>
122
+ )}
123
+ {
124
+ !!subcategories?.length && (
125
+ <>
126
+ {
127
+ subcategories.map(cat => (
128
+ <CategoryFilter
129
+ key={cat.id}
130
+ name={cat.name}
131
+ categoryId={cat.id}
132
+ tags={tags}
133
+ categories={categories}
134
+ toggleTag={toggleTag}
135
+ onToggleSelected={onToggleSelected}
136
+ isSurface={isSurface}
137
+ filters={filters}
138
+ />
139
+ ))
140
+ }
141
+ </>
142
+ )
143
+ }
144
+ </AccordionDetails>
145
+ </Accordion>
146
+ )
147
+ }
148
+
34
149
  function TagFilters(props) {
35
150
  const { onToggleSelected, filters, query } = props
36
151
  const data = useMemo(() => getData(props.db), [props.db])
37
- const [expanded, setExpanded] = useState(data.taxonomies?.map(() => (false)))
38
152
  const [displayedTags, setDisplayedTags] = useState({})
39
153
 
40
154
  const { toggleTag } = useContext(TagFilterContext)
@@ -44,13 +158,13 @@ function TagFilters(props) {
44
158
  const filteredTags = data.tags.filter(tag => (!query || !query.length || tag.name.toLowerCase().includes(query.toLowerCase())))
45
159
  for (let i = 0; i < data.taxonomies.length; i++) {
46
160
  const tax = data.taxonomies[i]
47
- const tagList = expanded[i] ? filteredTags.filter(t => (t.taxonomy_id === tax.id)) : filteredTags.filter(t => (t.taxonomy_id === tax.id))?.slice(0, 5)
161
+ const tagList = filteredTags.filter(t => t.taxonomy_id === tax.id)
48
162
  if (tagList?.length) {
49
163
  tags[tax.id] = tagList
50
164
  }
51
165
  }
52
166
  setDisplayedTags(tags)
53
- }, [expanded, data, query])
167
+ }, [data, query])
54
168
 
55
169
  return (
56
170
  <>
@@ -59,49 +173,52 @@ function TagFilters(props) {
59
173
  ? (
60
174
  <div className="tag-list">
61
175
  <FormGroup>
62
- { data.taxonomies.map((tax, idx) => {
176
+ { data.taxonomies.map((tax) => {
63
177
  const tagList = displayedTags[tax.id]
178
+ const topLevelTags = tagList?.filter(tag => (!tag.parent_category_id))
64
179
  return (
65
180
  tagList?.length
66
181
  ? (
67
182
  <div key={tax.id}>
68
183
  <Typography>{`${tax.name.slice(0, 1).toUpperCase()}${tax.name.slice(1)}`}</Typography>
69
- <ul>
70
- { tagList?.map(tag => (
71
- <FormControlLabel
72
- as="li"
73
- control={(
74
- <Checkbox
75
- checked={filters.includes(tag.id)}
76
- onChange={() => {
77
- onToggleSelected(tag.id)
78
- if (tax.is_surface) {
79
- toggleTag(tag.xml_id, 'left')
80
- toggleTag(tag.xml_id, 'right')
81
- }
82
- }}
83
- />
84
- )}
85
- key={tag.id}
86
- label={tag.name}
184
+ { !!topLevelTags?.length && (
185
+ <ul>
186
+ { topLevelTags?.map(tag => (
187
+ <FormControlLabel
188
+ as="li"
189
+ control={(
190
+ <Checkbox
191
+ checked={filters.includes(tag.id)}
192
+ onChange={() => {
193
+ onToggleSelected(tag.id)
194
+ if (tax.is_surface) {
195
+ toggleTag(tag.xml_id, 'left')
196
+ toggleTag(tag.xml_id, 'right')
197
+ }
198
+ }}
199
+ />
200
+ )}
201
+ key={tag.id}
202
+ label={tag.name}
203
+ />
204
+ ))}
205
+ </ul>
206
+ )}
207
+ {
208
+ data.categories?.filter(cat => (cat.taxonomy_id === tax.id && !cat.parent_category_id))?.map(cat => (
209
+ <CategoryFilter
210
+ key={cat.id}
211
+ name={cat.name}
212
+ categoryId={cat.id}
213
+ tags={tagList}
214
+ categories={data.categories}
215
+ toggleTag={toggleTag}
216
+ onToggleSelected={onToggleSelected}
217
+ isSurface={tax.is_surface}
218
+ filters={filters}
87
219
  />
88
- ))}
89
- </ul>
90
- { data.tags.filter(t => (t.taxonomy_id === tax.id))?.length && data.tags.filter(t => (t.taxonomy_id === tax.id)).length >= 6
91
- ? (
92
- <button
93
- className="tag-filter-button"
94
- type="button"
95
- onClick={() => {
96
- const newState = [...expanded]
97
- newState[idx] = !expanded[idx]
98
- setExpanded(newState)
99
- }}
100
- >
101
- { expanded[idx] ? 'Show less' : 'Show more'}
102
- </button>
103
- )
104
- : null }
220
+ ))
221
+ }
105
222
  </div>
106
223
  )
107
224
  : null
@@ -30,7 +30,7 @@
30
30
  background-color: #242629;
31
31
  z-index: 10000;
32
32
  padding: 12px 0 12px 12px;
33
- width: 300px;
33
+ width: 430px;
34
34
  height: 100%;
35
35
  max-height: calc(97dvh - 48px);
36
36
  border-left: solid white 1px;
@@ -194,4 +194,6 @@
194
194
 
195
195
  .tag-explore .accordion-detail {
196
196
  background-color: #242629;
197
+ display: flex;
198
+ flex-direction: column;
197
199
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cu-mkp/editioncrafter",
3
3
  "type": "module",
4
- "version": "1.3.1-beta.7",
4
+ "version": "1.3.1-beta.9",
5
5
  "private": false,
6
6
  "description": "A simple digital critical edition publication tool",
7
7
  "license": "MIT",