@cu-mkp/editioncrafter 1.1.0-beta.1 → 1.2.0-beta.1
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/dist/editioncrafter.js +20709 -18672
- package/dist/es/src/{action → EditionCrafter/action}/DocumentActions.js +8 -0
- package/dist/es/src/{action → EditionCrafter/action}/initialState/documentInitialState.js +7 -0
- package/dist/es/src/{component → EditionCrafter/component}/DiploMatic.js +17 -14
- package/dist/es/src/{component → EditionCrafter/component}/DocumentView.js +16 -6
- package/dist/es/src/EditionCrafter/component/Navigation.js +446 -0
- package/dist/es/src/{component → EditionCrafter/component}/RouteListener.js +3 -3
- package/dist/es/src/EditionCrafter/component/TagToolbar.jsx +57 -0
- package/dist/es/src/{component → EditionCrafter/component}/TranscriptionView.js +27 -7
- package/dist/es/src/EditionCrafter/context/TagFilter.jsx +61 -0
- package/dist/es/src/EditionCrafter/context/TagFilterContext.js +8 -0
- package/dist/es/src/EditionCrafter/icons/DocumentPagesIcon.jsx +18 -0
- package/dist/es/src/{model → EditionCrafter/model}/Folio.js +27 -1
- package/dist/es/src/{model → EditionCrafter/model}/folioLayout.js +6 -5
- package/dist/es/src/{saga → EditionCrafter/saga}/RouteListenerSaga.js +44 -3
- package/dist/es/src/{scss → EditionCrafter/scss}/_base.scss +11 -1
- package/dist/es/src/{scss → EditionCrafter/scss}/_diplomatic.scss +15 -24
- package/dist/es/src/EditionCrafter/scss/_navigation.scss +468 -0
- package/dist/es/src/{scss → EditionCrafter/scss}/_transcriptView.scss +50 -41
- package/dist/es/src/{scss → EditionCrafter/scss}/editioncrafter.scss +2 -1
- package/dist/es/src/RecordList/component/CollapsibleMenu.jsx +22 -0
- package/dist/es/src/RecordList/component/Loading.jsx +12 -0
- package/dist/es/src/RecordList/component/Pill.jsx +10 -0
- package/dist/es/src/RecordList/component/Record.jsx +82 -0
- package/dist/es/src/RecordList/component/RecordListView.jsx +109 -0
- package/dist/es/src/RecordList/component/Sidebar.jsx +83 -0
- package/dist/es/src/RecordList/component/SidebarTagList.jsx +38 -0
- package/dist/es/src/RecordList/context/FilterContext.jsx +8 -0
- package/dist/es/src/RecordList/index.jsx +92 -0
- package/dist/es/src/RecordList/lib/sql.js +9 -0
- package/dist/es/src/RecordList/styles/base.css +78 -0
- package/dist/es/src/RecordList/styles/record.css +84 -0
- package/dist/es/src/RecordList/styles/sidebar.css +39 -0
- package/dist/es/src/{index.js → index.jsx} +6 -3
- package/package.json +6 -3
- package/dist/es/src/component/Navigation.js +0 -402
- package/dist/es/src/hooks/useIsWidthUp.js +0 -9
- package/dist/es/src/lib/registerServiceWorker.js +0 -111
- package/dist/es/src/scss/_navigation.scss +0 -209
- /package/dist/es/src/{action → EditionCrafter/action}/DiplomaticActions.js +0 -0
- /package/dist/es/src/{action → EditionCrafter/action}/GlossaryActions.js +0 -0
- /package/dist/es/src/{action → EditionCrafter/action}/initialState/diplomaticInitialState.js +0 -0
- /package/dist/es/src/{action → EditionCrafter/action}/initialState/glossaryInitialState.js +0 -0
- /package/dist/es/src/{action → EditionCrafter/action}/rootReducer.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/AlphabetLinks.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/CustomizedTooltops.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/EditorComment.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/ErrorBoundary.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/FigureImage.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/GlossaryView.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/HelpPopper.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/ImageGridView.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/ImageView.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/ImageZoomControl.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/JumpToFolio.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/Pagination.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/Parser.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/RingSpinner.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/SeaDragonComponent.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/SinglePaneView.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/SplitPaneView.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/Watermark.js +0 -0
- /package/dist/es/src/{component → EditionCrafter/component}/XMLView.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/ByIcon.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/CcIcon.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/NcIcon.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/SaIcon.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/SideMenuIconLeft.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/SideMenuIconRight.js +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/howtouse-asterisk.png +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/howtouse-curly.png +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/howtouse-square.png +0 -0
- /package/dist/es/src/{icons → EditionCrafter/icons}/howtouse-ups.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/banner-about.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/banner-essays.jpg +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/banner-essays.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/banner-how-to.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/banner-resources.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/book-open-cropped.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/book-open.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/book-spine.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/bookcover-cropped.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/cropped-MKLizardFilled-32x32.jpg +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/editioncrafterlogo.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/folio48r-drawing.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/howtouse-asterisk.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/howtouse-beaker.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/howtouse-curly.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/howtouse-square.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/howtouse-ups.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/lizard-no-bg.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/logo_center_multi_line.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/logo_center_single_line.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/logo_columbia.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/mk-banner-logo.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/mk-homepage-logo.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/spinner.gif +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/text-bg.png +0 -0
- /package/dist/es/src/{img → EditionCrafter/img}/watermark.png +0 -0
- /package/dist/es/src/{lib → EditionCrafter/lib}/copyObject.js +0 -0
- /package/dist/es/src/{model → EditionCrafter/model}/DocumentHelper.js +0 -0
- /package/dist/es/src/{model → EditionCrafter/model}/ReduxStore.js +0 -0
- /package/dist/es/src/{saga → EditionCrafter/saga}/rootSaga.js +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_CETEIcean.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_globalNavigation.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_glossary.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_imageGridView.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_imageView.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_imageZoomControl.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_jumpbox.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_pagination.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_ringSpinner.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_singlePaneView.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_spinner.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_splitPaneView.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_thumbnails.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_watermark.scss +0 -0
- /package/dist/es/src/{scss → EditionCrafter/scss}/_xmlView.scss +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { FaChevronDown, FaChevronUp } from 'react-icons/fa'
|
|
3
|
+
|
|
4
|
+
function CollapsibleMenu(props) {
|
|
5
|
+
const [open, setOpen] = useState(true)
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<>
|
|
9
|
+
<div className="collapsible-menu">
|
|
10
|
+
<button className="collapsible-menu-toggle" onClick={() => setOpen(!open)} type="button">
|
|
11
|
+
<p className="collapsible-menu-title">{props.title}</p>
|
|
12
|
+
{open
|
|
13
|
+
? <FaChevronUp />
|
|
14
|
+
: <FaChevronDown />}
|
|
15
|
+
</button>
|
|
16
|
+
</div>
|
|
17
|
+
{open && props.children}
|
|
18
|
+
</>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default CollapsibleMenu
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useContext, useMemo } from 'react'
|
|
2
|
+
import FilterContext from '../context/FilterContext'
|
|
3
|
+
import Pill from './Pill'
|
|
4
|
+
|
|
5
|
+
function getRecordName(div) {
|
|
6
|
+
if (div.element_name) {
|
|
7
|
+
return `${div.element_name} - ${div.surface_name}`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return div.surface_name
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getTagObjects(ids, allTags) {
|
|
14
|
+
return ids
|
|
15
|
+
.map(xmlId => allTags.find(t => t.xml_id === xmlId))
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getSurfaceLink(baseUrl, div, cats, tags) {
|
|
20
|
+
return (
|
|
21
|
+
`${baseUrl}&viewMode=story#/ec/${div.surface_xml_id}/f/${div.surface_xml_id}/${div.layer_xml_id}?tags=${[...cats, ...tags].join(',')}`
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function Record(props) {
|
|
26
|
+
const ctx = useContext(FilterContext)
|
|
27
|
+
|
|
28
|
+
const categories = useMemo(
|
|
29
|
+
() => getTagObjects(props.div.tagging_ids, props.tags),
|
|
30
|
+
[props.div.tagging_ids, props.tags],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const tagCounts = useMemo(() => {
|
|
34
|
+
const result = {}
|
|
35
|
+
|
|
36
|
+
props.childElements.forEach((el) => {
|
|
37
|
+
const tags = getTagObjects(el.tagging_ids, props.tags)
|
|
38
|
+
tags.forEach((tag) => {
|
|
39
|
+
if (result[tag.name]) {
|
|
40
|
+
result[tag.name].count++
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
result[tag.name] = {
|
|
44
|
+
id: tag.xml_id,
|
|
45
|
+
count: 1,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return result
|
|
52
|
+
}, [props.childElements, props.tags])
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="record-box">
|
|
56
|
+
<p>
|
|
57
|
+
<a
|
|
58
|
+
href={getSurfaceLink(props.viewerUrl, props.div, ctx.categories, ctx.tags)}
|
|
59
|
+
target="_blank"
|
|
60
|
+
rel="noopener noreferrer"
|
|
61
|
+
>
|
|
62
|
+
{getRecordName(props.div)}
|
|
63
|
+
</a>
|
|
64
|
+
</p>
|
|
65
|
+
<div className="category-list">
|
|
66
|
+
{categories.map(cat => (
|
|
67
|
+
<Pill key={cat.id} label={cat.name} isActive={ctx.categories.includes(cat.xml_id)} />
|
|
68
|
+
))}
|
|
69
|
+
</div>
|
|
70
|
+
<div className="tag-list">
|
|
71
|
+
<span className="tag-list-label">Tags</span>
|
|
72
|
+
{Object.keys(tagCounts).map(tagName => (
|
|
73
|
+
<Pill key={tagName} label={tagName} isActive={ctx.tags.includes(tagCounts[tagName].id)}>
|
|
74
|
+
<span className="tag-count">{tagCounts[tagName].count}</span>
|
|
75
|
+
</Pill>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default Record
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useContext, useMemo } from 'react'
|
|
2
|
+
import FilterContext from '../context/FilterContext'
|
|
3
|
+
import { getObjs } from '../lib/sql'
|
|
4
|
+
import Record from './Record'
|
|
5
|
+
|
|
6
|
+
function cleanUpTagIds(obj) {
|
|
7
|
+
return {
|
|
8
|
+
...obj,
|
|
9
|
+
tagging_ids: obj.tagging_ids.split(','),
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getData(db) {
|
|
14
|
+
const elementsStmt = db.prepare(`
|
|
15
|
+
SELECT
|
|
16
|
+
elements.id AS id,
|
|
17
|
+
elements.name AS element_name,
|
|
18
|
+
elements.type AS element_type,
|
|
19
|
+
elements.parent_id AS parent_id,
|
|
20
|
+
surfaces.name AS surface_name,
|
|
21
|
+
surfaces.xml_id AS surface_xml_id,
|
|
22
|
+
layers.xml_id AS layer_xml_id,
|
|
23
|
+
GROUP_CONCAT(tags.xml_id) as tagging_ids
|
|
24
|
+
FROM
|
|
25
|
+
taggings
|
|
26
|
+
INNER JOIN elements
|
|
27
|
+
ON elements.id = taggings.element_id
|
|
28
|
+
INNER JOIN tags
|
|
29
|
+
ON tags.id = taggings.tag_id
|
|
30
|
+
INNER JOIN surfaces
|
|
31
|
+
ON surfaces.id = elements.surface_id
|
|
32
|
+
INNER JOIN layers
|
|
33
|
+
ON layers.id = elements.layer_id
|
|
34
|
+
GROUP BY elements.id`)
|
|
35
|
+
|
|
36
|
+
const tagsStmt = db.prepare('SELECT * from tags')
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
elements: getObjs(elementsStmt).map(cleanUpTagIds),
|
|
40
|
+
tags: getObjs(tagsStmt),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isFilterMatch(ctx, divData) {
|
|
45
|
+
const categoryMatch = (ctx.categories.length === 0
|
|
46
|
+
|| divData.element.tagging_ids.some(id => ctx.categories.includes(id)))
|
|
47
|
+
|
|
48
|
+
const tagMatch = (ctx.tags.length === 0
|
|
49
|
+
|| divData.childTags.some(id => ctx.tags.includes(id)))
|
|
50
|
+
|
|
51
|
+
return categoryMatch && tagMatch
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function RecordListView(props) {
|
|
55
|
+
const ctx = useContext(FilterContext)
|
|
56
|
+
|
|
57
|
+
const { elements, tags } = useMemo(() => getData(props.db), [props.db])
|
|
58
|
+
|
|
59
|
+
const divs = useMemo(() => {
|
|
60
|
+
const arr = []
|
|
61
|
+
|
|
62
|
+
for (const element of elements) {
|
|
63
|
+
if (element.element_type !== 'div') {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const children = elements.filter(childEl => childEl.parent_id === element.id)
|
|
68
|
+
const childTags = children.flatMap(childEl => childEl.tagging_ids)
|
|
69
|
+
|
|
70
|
+
arr.push({
|
|
71
|
+
element,
|
|
72
|
+
children,
|
|
73
|
+
childTags,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return arr
|
|
78
|
+
}, [elements])
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className="record-list-view">
|
|
82
|
+
<h1 className="entries-header">
|
|
83
|
+
{props.recordLabel || 'Records'}
|
|
84
|
+
{' '}
|
|
85
|
+
(
|
|
86
|
+
{divs.length}
|
|
87
|
+
)
|
|
88
|
+
</h1>
|
|
89
|
+
{divs.map((divData) => {
|
|
90
|
+
if (isFilterMatch(ctx, divData)) {
|
|
91
|
+
return (
|
|
92
|
+
<Record
|
|
93
|
+
childElements={elements.filter(el => el.parent_id === divData.element.id)}
|
|
94
|
+
div={divData.element}
|
|
95
|
+
key={divData.element.id}
|
|
96
|
+
tags={tags}
|
|
97
|
+
viewerUrl={props.viewerUrl}
|
|
98
|
+
/>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return null
|
|
103
|
+
},
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default RecordListView
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { getObjs } from '../lib/sql'
|
|
3
|
+
import CollapsibleMenu from './CollapsibleMenu'
|
|
4
|
+
import SidebarTagList from './SidebarTagList'
|
|
5
|
+
|
|
6
|
+
function getData(db) {
|
|
7
|
+
const tagsStmt = db.prepare(`
|
|
8
|
+
SELECT
|
|
9
|
+
tags.id AS id,
|
|
10
|
+
tags.name AS name,
|
|
11
|
+
tags.xml_id AS xml_id
|
|
12
|
+
FROM
|
|
13
|
+
tags
|
|
14
|
+
INNER JOIN taggings
|
|
15
|
+
ON taggings.tag_id = tags.id
|
|
16
|
+
INNER JOIN elements
|
|
17
|
+
ON elements.id = taggings.element_id
|
|
18
|
+
WHERE
|
|
19
|
+
elements.type = 'seg'
|
|
20
|
+
GROUP BY
|
|
21
|
+
tags.xml_id`)
|
|
22
|
+
|
|
23
|
+
const tags = getObjs(tagsStmt)
|
|
24
|
+
|
|
25
|
+
const categoriesStmt = db.prepare(`
|
|
26
|
+
SELECT
|
|
27
|
+
tags.id AS id,
|
|
28
|
+
tags.name AS name,
|
|
29
|
+
tags.xml_id AS xml_id
|
|
30
|
+
FROM
|
|
31
|
+
tags
|
|
32
|
+
INNER JOIN taggings
|
|
33
|
+
ON taggings.tag_id = tags.id
|
|
34
|
+
INNER JOIN elements
|
|
35
|
+
ON elements.id = taggings.element_id
|
|
36
|
+
WHERE
|
|
37
|
+
elements.type = 'div'
|
|
38
|
+
GROUP BY
|
|
39
|
+
tags.xml_id`)
|
|
40
|
+
|
|
41
|
+
const categories = getObjs(categoriesStmt)
|
|
42
|
+
|
|
43
|
+
const taggingsStmt = db.prepare('SELECT * FROM taggings')
|
|
44
|
+
const taggings = getObjs(taggingsStmt)
|
|
45
|
+
|
|
46
|
+
const labeledTags = tags.map((tag) => {
|
|
47
|
+
if (tag.parent_id) {
|
|
48
|
+
const parent = tags.find(pt => pt.id === tag.parent_id)
|
|
49
|
+
return {
|
|
50
|
+
...tag,
|
|
51
|
+
name: `${parent.name} - ${tag.name}`,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return tag
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
tags: labeledTags,
|
|
60
|
+
categories,
|
|
61
|
+
taggings,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function RecordListSidebar(props) {
|
|
66
|
+
const data = useMemo(() => getData(props.db), [props.db])
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="ec-sidebar">
|
|
70
|
+
<h2>Filters</h2>
|
|
71
|
+
<div className="h-separator" />
|
|
72
|
+
<CollapsibleMenu title="Categories">
|
|
73
|
+
<SidebarTagList tags={data.categories} type="categories" />
|
|
74
|
+
</CollapsibleMenu>
|
|
75
|
+
<div className="h-separator" />
|
|
76
|
+
<CollapsibleMenu title="Tags">
|
|
77
|
+
<SidebarTagList tags={data.tags} type="tags" />
|
|
78
|
+
</CollapsibleMenu>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default RecordListSidebar
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useContext, useMemo } from 'react'
|
|
2
|
+
import { IoCheckmarkSharp } from 'react-icons/io5'
|
|
3
|
+
import FilterContext from '../context/FilterContext'
|
|
4
|
+
import Pill from './Pill'
|
|
5
|
+
|
|
6
|
+
function TagPill(props) {
|
|
7
|
+
const { categories, toggleCategoryFilter, tags, toggleTagFilter } = useContext(FilterContext)
|
|
8
|
+
|
|
9
|
+
const toggleFilter = props.type === 'categories'
|
|
10
|
+
? toggleCategoryFilter
|
|
11
|
+
: toggleTagFilter
|
|
12
|
+
|
|
13
|
+
const isActive = useMemo(() => props.type === 'categories'
|
|
14
|
+
? categories.includes(props.tag.xml_id)
|
|
15
|
+
: tags.includes(props.tag.xml_id), [props.type, props.tag.xml_id, categories, tags])
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Pill
|
|
19
|
+
isActive={isActive}
|
|
20
|
+
onClick={() => toggleFilter(props.tag.xml_id)}
|
|
21
|
+
label={props.tag.name}
|
|
22
|
+
>
|
|
23
|
+
{isActive && <IoCheckmarkSharp />}
|
|
24
|
+
</Pill>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function SidebarTagList(props) {
|
|
29
|
+
return (
|
|
30
|
+
<div className="sidebar-tag-list">
|
|
31
|
+
{props.tags.map(tag => (
|
|
32
|
+
<TagPill key={tag.id} type={props.type} tag={tag} />
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default SidebarTagList
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import initSqlJs from 'sql.js'
|
|
3
|
+
import Loading from './component/Loading'
|
|
4
|
+
import RecordListView from './component/RecordListView'
|
|
5
|
+
import Sidebar from './component/Sidebar'
|
|
6
|
+
|
|
7
|
+
import FilterContext from './context/FilterContext'
|
|
8
|
+
|
|
9
|
+
import './styles/base.css'
|
|
10
|
+
import './styles/record.css'
|
|
11
|
+
import './styles/sidebar.css'
|
|
12
|
+
|
|
13
|
+
const initialFilters = {
|
|
14
|
+
categories: [],
|
|
15
|
+
tags: [],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function initDb(url) {
|
|
19
|
+
const file = await fetch(url)
|
|
20
|
+
|
|
21
|
+
if (!file.ok) {
|
|
22
|
+
throw new Error('Failed fetching SQLite file.')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const buf = await file.arrayBuffer()
|
|
26
|
+
const arr = new Uint8Array(buf)
|
|
27
|
+
|
|
28
|
+
const SQL = await initSqlJs({
|
|
29
|
+
locateFile: file => `https://sql.js.org/dist/${file}`,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const db = new SQL.Database(arr)
|
|
33
|
+
|
|
34
|
+
return db
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function RecordList(props) {
|
|
38
|
+
const [db, setDb] = useState(null)
|
|
39
|
+
const [filters, setFilters] = useState(initialFilters)
|
|
40
|
+
|
|
41
|
+
const toggleCategoryFilter = useCallback(id => setFilters({
|
|
42
|
+
...filters,
|
|
43
|
+
categories: filters.categories.includes(id)
|
|
44
|
+
? filters.categories.filter(existing => existing !== id)
|
|
45
|
+
: [...filters.categories, id],
|
|
46
|
+
}), [filters])
|
|
47
|
+
|
|
48
|
+
const toggleTagFilter = useCallback(id => setFilters({
|
|
49
|
+
...filters,
|
|
50
|
+
tags: filters.tags.includes(id)
|
|
51
|
+
? filters.tags.filter(existing => existing !== id)
|
|
52
|
+
: [...filters.tags, id],
|
|
53
|
+
}), [filters])
|
|
54
|
+
|
|
55
|
+
const initialContext = useMemo(() => ({
|
|
56
|
+
...filters,
|
|
57
|
+
toggleCategoryFilter,
|
|
58
|
+
toggleTagFilter,
|
|
59
|
+
}), [filters, toggleCategoryFilter, toggleTagFilter])
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const loadDb = async () => {
|
|
63
|
+
const db = await initDb(props.dbUrl)
|
|
64
|
+
setDb(db)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!db) {
|
|
68
|
+
loadDb()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return () => {
|
|
72
|
+
if (db) {
|
|
73
|
+
db.close()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}, [props.dbUrl, db])
|
|
77
|
+
|
|
78
|
+
if (!db) {
|
|
79
|
+
return <Loading />
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<FilterContext.Provider value={initialContext}>
|
|
84
|
+
<div className="editioncrafter-record-list">
|
|
85
|
+
<Sidebar db={db} />
|
|
86
|
+
<RecordListView db={db} recordLabel={props.recordLabel} viewerUrl={props.viewerUrl} />
|
|
87
|
+
</div>
|
|
88
|
+
</FilterContext.Provider>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default RecordList
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
|
|
2
|
+
|
|
3
|
+
.editioncrafter-record-list {
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: 20px;
|
|
8
|
+
font-family: 'Inter', sans-serif;
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.editioncrafter-record-list h1, h2, h3, h4, p {
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.editioncrafter-record-list h1 {
|
|
19
|
+
font-size: 32px;
|
|
20
|
+
font-style: normal;
|
|
21
|
+
font-weight: 600;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.editioncrafter-record-list h2 {
|
|
25
|
+
font-size: 24px;
|
|
26
|
+
font-style: normal;
|
|
27
|
+
font-weight: 600;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.editioncrafter-record-list div.h-separator {
|
|
31
|
+
border-top: 1px solid #E1E3E6;
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.editioncrafter-record-list * {
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.editioncrafter-record-list a {
|
|
40
|
+
color: #07529A;
|
|
41
|
+
font-family: Inter;
|
|
42
|
+
font-size: 18px;
|
|
43
|
+
font-style: normal;
|
|
44
|
+
font-weight: 700;
|
|
45
|
+
text-decoration: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.editioncrafter-record-list button:hover {
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.editioncrafter-record-list .loading {
|
|
53
|
+
height: 100%;
|
|
54
|
+
width: 100%;
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-flow: column;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
gap: 20px;
|
|
60
|
+
background-color: #ffffff;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.editioncrafter-record-list .pill {
|
|
64
|
+
appearance: none;
|
|
65
|
+
display: flex;
|
|
66
|
+
height: 32px;
|
|
67
|
+
padding: 4px 12px;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: 4px;
|
|
71
|
+
border-radius: 50px;
|
|
72
|
+
border: 1px solid #CEDAE7;
|
|
73
|
+
background: #CEDAE7;
|
|
74
|
+
color: #0A0A0A;
|
|
75
|
+
font-family: Inter;
|
|
76
|
+
font-size: 14px;
|
|
77
|
+
font-weight: 500;
|
|
78
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.editioncrafter-record-list .record-list-view {
|
|
2
|
+
flex-grow: 1;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-flow: column;
|
|
5
|
+
gap: 16px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.editioncrafter-record-list .record-list-view .entries-header {
|
|
9
|
+
padding: 10px 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.editioncrafter-record-list .record-list-view .record-box {
|
|
13
|
+
width: 100%;
|
|
14
|
+
border-radius: 5px;
|
|
15
|
+
border: 1px solid #E1E3E6;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-flow: column;
|
|
18
|
+
gap: 8px;
|
|
19
|
+
padding: 16px;
|
|
20
|
+
transition: 0.3s;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.editioncrafter-record-list .record-list-view .record-box:hover {
|
|
24
|
+
box-shadow: 1px 1px 6px 0px rgba(0, 0, 0, 0.25);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.editioncrafter-record-list .record-list-view .record-box .category-list {
|
|
28
|
+
display: flex;
|
|
29
|
+
gap: 6px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-wrap: wrap;
|
|
35
|
+
gap: 8px;
|
|
36
|
+
align-items: center;
|
|
37
|
+
font-size: 14px;
|
|
38
|
+
font-weight: 500;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list .tag-list-label {
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list .pill {
|
|
46
|
+
background: #ffffff;
|
|
47
|
+
display: flex;
|
|
48
|
+
height: 32px;
|
|
49
|
+
padding: 4px 12px 4px 4px;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: 8px;
|
|
53
|
+
border-radius: 50px;
|
|
54
|
+
border: 1px solid #E1E3E6;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.editioncrafter-record-list .record-list-view .record-box .category-list .pill.active {
|
|
58
|
+
background: #07529A;
|
|
59
|
+
color: #ffffff
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list .pill .tag-count {
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
height: 24px;
|
|
65
|
+
min-width: 24px;
|
|
66
|
+
padding: 4px;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: 10px;
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
border-radius: 50px;
|
|
73
|
+
background: #B5C8DC99;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list .pill.active {
|
|
77
|
+
border: 1px solid #07529A;
|
|
78
|
+
color: #07529A;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list .pill.active .tag-count {
|
|
82
|
+
background: #07529A;
|
|
83
|
+
color: #ffffff;
|
|
84
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.editioncrafter-record-list .ec-sidebar {
|
|
2
|
+
background-color: #F3F4F7;
|
|
3
|
+
padding: 32px 24px;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: 20px;
|
|
7
|
+
width: 380px;
|
|
8
|
+
flex-grow: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.editioncrafter-record-list .collapsible-menu .collapsible-menu-toggle {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
appearance: none;
|
|
16
|
+
background: none;
|
|
17
|
+
border: none;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.editioncrafter-record-list .collapsible-menu .collapsible-menu-title {
|
|
22
|
+
font-size: 16px;
|
|
23
|
+
font-weight: 700;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.editioncrafter-record-list .sidebar-tag-list {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
gap: 6px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.editioncrafter-record-list .sidebar-tag-list .pill.active {
|
|
33
|
+
background: #07529A;
|
|
34
|
+
color: #ffffff;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.editioncrafter-record-list .sidebar-tag-list .hidden {
|
|
38
|
+
visibility: hidden;
|
|
39
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createTheme, ThemeProvider } from '@material-ui/core/styles'
|
|
2
2
|
import React from 'react'
|
|
3
|
-
import DiploMatic from './component/DiploMatic'
|
|
4
|
-
import { createReduxStore } from './model/ReduxStore'
|
|
5
|
-
import './
|
|
3
|
+
import DiploMatic from './EditionCrafter/component/DiploMatic'
|
|
4
|
+
import { createReduxStore } from './EditionCrafter/model/ReduxStore'
|
|
5
|
+
import _RecordList from './RecordList'
|
|
6
|
+
import './EditionCrafter/scss/editioncrafter.scss'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Default instantiation
|
|
@@ -26,4 +27,6 @@ function EditionCrafter(props) {
|
|
|
26
27
|
)
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
export { _RecordList as RecordList }
|
|
31
|
+
|
|
29
32
|
export default EditionCrafter
|