@cu-mkp/editioncrafter 1.3.0-beta.2 → 1.3.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editioncrafter.js +14757 -14573
- package/dist/es/src/RecordList/component/PhraseListTable.jsx +108 -0
- package/dist/es/src/RecordList/component/Record.jsx +37 -1
- package/dist/es/src/RecordList/index.jsx +9 -4
- package/dist/es/src/RecordList/styles/base.css +2 -1
- package/dist/es/src/RecordList/styles/record.css +92 -4
- package/dist/es/src/TagExplore/index.jsx +2 -1
- package/package.json +2 -2
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useContext, useMemo, useState } from 'react'
|
|
2
|
+
import Pill from '../../common/components/Pill'
|
|
3
|
+
import FilterContext from '../context/FilterContext'
|
|
4
|
+
|
|
5
|
+
const MAX_PHRASE_LENGTH = 20
|
|
6
|
+
|
|
7
|
+
function PhraseList(props) {
|
|
8
|
+
const phraseStr = useMemo(() => {
|
|
9
|
+
const arr = []
|
|
10
|
+
|
|
11
|
+
props.phrases.forEach((phrase) => {
|
|
12
|
+
if (phrase.layer !== props.layer) {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (arr.includes(phrase.name)) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (phrase.name.length > MAX_PHRASE_LENGTH) {
|
|
21
|
+
arr.push(`${phrase.name.slice(0, MAX_PHRASE_LENGTH)}...`)
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
arr.push(phrase.name)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return arr.join('; ')
|
|
29
|
+
}, [props.layer, props.phrases])
|
|
30
|
+
|
|
31
|
+
return <p>{phraseStr}</p>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function PhraseListTable(props) {
|
|
35
|
+
const ctx = useContext(FilterContext)
|
|
36
|
+
|
|
37
|
+
const [layer, setLayer] = useState(Object.keys(ctx.layers)[0])
|
|
38
|
+
|
|
39
|
+
const tagsToShow = useMemo(
|
|
40
|
+
() => Object.keys(props.tagCounts).filter(tagName => props.tagCounts[tagName].phrases.some(phrase => phrase.layer === layer)),
|
|
41
|
+
[layer, props.tagCounts],
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
className="phrase-list-table-container"
|
|
47
|
+
style={{ display: props.visible ? 'initial' : 'none' }}
|
|
48
|
+
>
|
|
49
|
+
<div className="layer-select-container">
|
|
50
|
+
<span>Layer</span>
|
|
51
|
+
<select
|
|
52
|
+
className="layer-select"
|
|
53
|
+
onChange={e => setLayer(e.target.value)}
|
|
54
|
+
>
|
|
55
|
+
{Object.keys(ctx.layers).map(xmlId => (
|
|
56
|
+
<option
|
|
57
|
+
key={xmlId}
|
|
58
|
+
value={xmlId}
|
|
59
|
+
>
|
|
60
|
+
{ctx.layers[xmlId]}
|
|
61
|
+
</option>
|
|
62
|
+
))}
|
|
63
|
+
</select>
|
|
64
|
+
</div>
|
|
65
|
+
<table className="phrase-list-table">
|
|
66
|
+
<thead>
|
|
67
|
+
<tr>
|
|
68
|
+
<th>Tags for phrases</th>
|
|
69
|
+
<th>
|
|
70
|
+
<span>References in this layer</span>
|
|
71
|
+
</th>
|
|
72
|
+
</tr>
|
|
73
|
+
</thead>
|
|
74
|
+
<tbody>
|
|
75
|
+
{tagsToShow.length === 0 && (
|
|
76
|
+
<tr>
|
|
77
|
+
<td colSpan={2}>
|
|
78
|
+
<div className="empty-table-container">
|
|
79
|
+
No tags in this layer.
|
|
80
|
+
</div>
|
|
81
|
+
</td>
|
|
82
|
+
</tr>
|
|
83
|
+
)}
|
|
84
|
+
{tagsToShow.map(tag => (
|
|
85
|
+
<tr key={tag}>
|
|
86
|
+
<td>
|
|
87
|
+
<Pill
|
|
88
|
+
label={tag}
|
|
89
|
+
isActive={ctx.tags.includes(props.tagCounts[tag].id)}
|
|
90
|
+
>
|
|
91
|
+
<span className="tag-count">{props.tagCounts[tag].count}</span>
|
|
92
|
+
</Pill>
|
|
93
|
+
</td>
|
|
94
|
+
<td>
|
|
95
|
+
<PhraseList
|
|
96
|
+
layer={layer}
|
|
97
|
+
phrases={props.tagCounts[tag].phrases}
|
|
98
|
+
/>
|
|
99
|
+
</td>
|
|
100
|
+
</tr>
|
|
101
|
+
))}
|
|
102
|
+
</tbody>
|
|
103
|
+
</table>
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default PhraseListTable
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { useContext, useMemo } from 'react'
|
|
1
|
+
import { useContext, useMemo, useState } from 'react'
|
|
2
|
+
import { IoChevronDownOutline, IoChevronUpOutline } from 'react-icons/io5'
|
|
2
3
|
import Pill from '../../common/components/Pill'
|
|
3
4
|
import FilterContext from '../context/FilterContext'
|
|
5
|
+
import PhraseList from './PhraseListTable'
|
|
4
6
|
|
|
5
7
|
function getRecordName(div) {
|
|
6
8
|
if (div.element_name) {
|
|
@@ -22,7 +24,24 @@ function getSurfaceLink(baseUrl, div, cats, tags) {
|
|
|
22
24
|
)
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
function PhraseToggle(props) {
|
|
28
|
+
return (
|
|
29
|
+
<button
|
|
30
|
+
aria-label="Toggle phrases"
|
|
31
|
+
className="phrase-toggle"
|
|
32
|
+
onClick={props.onClick}
|
|
33
|
+
type="button"
|
|
34
|
+
>
|
|
35
|
+
{props.toggled
|
|
36
|
+
? <IoChevronUpOutline />
|
|
37
|
+
: <IoChevronDownOutline />}
|
|
38
|
+
</button>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
function Record(props) {
|
|
43
|
+
const [showPhrases, setShowPhrases] = useState(false)
|
|
44
|
+
|
|
26
45
|
const ctx = useContext(FilterContext)
|
|
27
46
|
|
|
28
47
|
const categories = useMemo(
|
|
@@ -38,11 +57,19 @@ function Record(props) {
|
|
|
38
57
|
tags.forEach((tag) => {
|
|
39
58
|
if (result[tag.name]) {
|
|
40
59
|
result[tag.name].count++
|
|
60
|
+
result[tag.name].phrases.push({
|
|
61
|
+
name: el.element_name,
|
|
62
|
+
layer: el.layer_xml_id,
|
|
63
|
+
})
|
|
41
64
|
}
|
|
42
65
|
else {
|
|
43
66
|
result[tag.name] = {
|
|
44
67
|
id: tag.xml_id,
|
|
45
68
|
count: 1,
|
|
69
|
+
phrases: [{
|
|
70
|
+
name: el.element_name,
|
|
71
|
+
layer: el.layer_xml_id,
|
|
72
|
+
}],
|
|
46
73
|
}
|
|
47
74
|
}
|
|
48
75
|
})
|
|
@@ -74,7 +101,16 @@ function Record(props) {
|
|
|
74
101
|
<span className="tag-count">{tagCounts[tagName].count}</span>
|
|
75
102
|
</Pill>
|
|
76
103
|
))}
|
|
104
|
+
<PhraseToggle
|
|
105
|
+
toggled={showPhrases}
|
|
106
|
+
onClick={() => setShowPhrases(!showPhrases)}
|
|
107
|
+
/>
|
|
77
108
|
</div>
|
|
109
|
+
<PhraseList
|
|
110
|
+
tagCounts={tagCounts}
|
|
111
|
+
elements={props.childElements}
|
|
112
|
+
visible={showPhrases}
|
|
113
|
+
/>
|
|
78
114
|
</div>
|
|
79
115
|
)
|
|
80
116
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import initSqlJs from 'sql.js'
|
|
3
|
+
import sqlJsInfo from 'sql.js/package.json'
|
|
3
4
|
import Loading from '../common/components/Loading'
|
|
4
5
|
import RecordListView from './component/RecordListView'
|
|
5
6
|
import Sidebar from './component/Sidebar'
|
|
6
|
-
|
|
7
7
|
import FilterContext from './context/FilterContext'
|
|
8
8
|
|
|
9
9
|
import './styles/base.css'
|
|
@@ -26,7 +26,7 @@ async function initDb(url) {
|
|
|
26
26
|
const arr = new Uint8Array(buf)
|
|
27
27
|
|
|
28
28
|
const SQL = await initSqlJs({
|
|
29
|
-
locateFile: file => `https://sql.js.
|
|
29
|
+
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/${sqlJsInfo.version}/${file}`,
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
const db = new SQL.Database(arr)
|
|
@@ -56,7 +56,8 @@ function RecordList(props) {
|
|
|
56
56
|
...filters,
|
|
57
57
|
toggleCategoryFilter,
|
|
58
58
|
toggleTagFilter,
|
|
59
|
-
|
|
59
|
+
layers: props.layers,
|
|
60
|
+
}), [filters, toggleCategoryFilter, toggleTagFilter, props.layers])
|
|
60
61
|
|
|
61
62
|
useEffect(() => {
|
|
62
63
|
const loadDb = async () => {
|
|
@@ -83,7 +84,11 @@ function RecordList(props) {
|
|
|
83
84
|
<FilterContext.Provider value={initialContext}>
|
|
84
85
|
<div className="editioncrafter-record-list">
|
|
85
86
|
<Sidebar db={db} />
|
|
86
|
-
<RecordListView
|
|
87
|
+
<RecordListView
|
|
88
|
+
db={db}
|
|
89
|
+
recordLabel={props.recordLabel}
|
|
90
|
+
viewerUrl={props.viewerUrl}
|
|
91
|
+
/>
|
|
87
92
|
</div>
|
|
88
93
|
</FilterContext.Provider>
|
|
89
94
|
)
|
|
@@ -36,13 +36,34 @@
|
|
|
36
36
|
align-items: center;
|
|
37
37
|
font-size: 14px;
|
|
38
38
|
font-weight: 500;
|
|
39
|
+
position: relative;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
.editioncrafter-record-list .record-list-view .record-box .tag-list .tag-list-label {
|
|
42
43
|
font-weight: 600;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
.editioncrafter-record-list .record-list-view .record-box .
|
|
46
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-toggle {
|
|
47
|
+
position: absolute;
|
|
48
|
+
right: 0;
|
|
49
|
+
bottom: 2px;
|
|
50
|
+
background: #07529A;
|
|
51
|
+
border: none;
|
|
52
|
+
border-radius: 5px;
|
|
53
|
+
color: white;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
padding: 6px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-toggle svg {
|
|
61
|
+
height: 18px;
|
|
62
|
+
width: 18px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.editioncrafter-record-list .record-list-view .record-box .tag-list .pill,
|
|
66
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table .pill {
|
|
46
67
|
background: #ffffff;
|
|
47
68
|
display: flex;
|
|
48
69
|
height: 32px;
|
|
@@ -59,7 +80,7 @@
|
|
|
59
80
|
color: #ffffff
|
|
60
81
|
}
|
|
61
82
|
|
|
62
|
-
.editioncrafter-record-list .record-list-view .record-box .
|
|
83
|
+
.editioncrafter-record-list .record-list-view .record-box .pill .tag-count {
|
|
63
84
|
display: inline-flex;
|
|
64
85
|
height: 24px;
|
|
65
86
|
min-width: 24px;
|
|
@@ -73,12 +94,79 @@
|
|
|
73
94
|
background: #B5C8DC99;
|
|
74
95
|
}
|
|
75
96
|
|
|
76
|
-
.editioncrafter-record-list .record-list-view .record-box .
|
|
97
|
+
.editioncrafter-record-list .record-list-view .record-box .pill.active {
|
|
77
98
|
border: 1px solid #07529A;
|
|
78
99
|
color: #07529A;
|
|
79
100
|
}
|
|
80
101
|
|
|
81
|
-
.editioncrafter-record-list .record-list-view .record-box .
|
|
102
|
+
.editioncrafter-record-list .record-list-view .record-box .pill.active .tag-count {
|
|
82
103
|
background: #07529A;
|
|
83
104
|
color: #ffffff;
|
|
84
105
|
}
|
|
106
|
+
|
|
107
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table-container {
|
|
108
|
+
position: relative;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table-container .layer-select-container {
|
|
112
|
+
position: absolute;
|
|
113
|
+
right: 10px;
|
|
114
|
+
top: 0;
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
gap: 12px;
|
|
119
|
+
font-size: 13px;
|
|
120
|
+
font-style: normal;
|
|
121
|
+
font-weight: 400;
|
|
122
|
+
line-height: 140%;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table-container button:hover {
|
|
126
|
+
cursor: default;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table-container .layer-select {
|
|
130
|
+
display: flex;
|
|
131
|
+
height: 32px;
|
|
132
|
+
padding: 0px 12px;
|
|
133
|
+
justify-content: space-between;
|
|
134
|
+
align-items: center;
|
|
135
|
+
font-size: 14px;
|
|
136
|
+
border-radius: 4px;
|
|
137
|
+
border: 1px solid#E1E3E6;
|
|
138
|
+
background:#F3F4F7;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table-container .layer-select:hover {
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table th {
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table {
|
|
150
|
+
text-align: left;
|
|
151
|
+
border-collapse: collapse;
|
|
152
|
+
font-size: 14px;
|
|
153
|
+
width: 100%;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table tr {
|
|
157
|
+
border-bottom: 1px solid #E1E3E6;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table td,
|
|
161
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table th {
|
|
162
|
+
padding: 12px;
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.editioncrafter-record-list .record-list-view .record-box .phrase-list-table .empty-table-container {
|
|
167
|
+
height: 80px;
|
|
168
|
+
width: 100%;
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
justify-content: center;
|
|
172
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import { HashRouter } from 'react-router-dom'
|
|
3
3
|
import initSqlJs from 'sql.js'
|
|
4
|
+
import sqlJsInfo from 'sql.js/package.json'
|
|
4
5
|
import Loading from '../common/components/Loading'
|
|
5
6
|
import { getObjs } from '../common/lib/sql'
|
|
6
7
|
import EditionCrafter from '../EditionCrafter'
|
|
@@ -24,7 +25,7 @@ async function initDb(url) {
|
|
|
24
25
|
const arr = new Uint8Array(buf)
|
|
25
26
|
|
|
26
27
|
const db = await initSqlJs({
|
|
27
|
-
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js
|
|
28
|
+
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/${sqlJsInfo.version}/${file}`,
|
|
28
29
|
}).then((SQL) => {
|
|
29
30
|
const db = new SQL.Database(arr)
|
|
30
31
|
return db
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cu-mkp/editioncrafter",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.3.0-beta.
|
|
4
|
+
"version": "1.3.0-beta.3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "A simple digital critical edition publication tool",
|
|
7
7
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"redux": "^4.2.1",
|
|
48
48
|
"redux-saga": "^1.2.2",
|
|
49
49
|
"remark-gfm": "^3.0.1",
|
|
50
|
-
"sql.js": "^1.
|
|
50
|
+
"sql.js": "^1.13.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@antfu/eslint-config": "^3.8.0",
|