@teipublisher/pb-components 1.26.0 → 1.28.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/CHANGELOG.md +41 -0
- package/css/gridjs/mermaid.min.css +1 -0
- package/dist/demo/demos.json +4 -1
- package/dist/demo/pb-table-grid.html +17 -0
- package/dist/demo/people.json +35 -0
- package/dist/pb-components-bundle.js +136 -117
- package/dist/pb-elements.json +231 -0
- package/dist/pb-odd-editor.js +23 -14
- package/i18n/common/de.json +8 -1
- package/i18n/common/en.json +11 -3
- package/package.json +2 -1
- package/pb-elements.json +231 -0
- package/src/authority/connectors.js +4 -0
- package/src/authority/reconciliation.js +124 -0
- package/src/pb-components.js +1 -0
- package/src/pb-odd-editor.js +6 -2
- package/src/pb-odd-elementspec-editor.js +2 -0
- package/src/pb-odd-model-editor.js +17 -0
- package/src/pb-odd-parameter-editor.js +12 -5
- package/src/pb-table-column.js +66 -0
- package/src/pb-table-grid.js +212 -0
- package/src/pb-view-annotate.js +21 -6
- package/src/pb-view.js +10 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit-element';
|
|
2
|
+
import { Grid } from "gridjs";
|
|
3
|
+
import { pbMixin } from './pb-mixin.js';
|
|
4
|
+
import { resolveURL } from './utils.js';
|
|
5
|
+
import '@polymer/paper-input/paper-input';
|
|
6
|
+
import '@polymer/iron-icons';
|
|
7
|
+
import '@polymer/iron-form';
|
|
8
|
+
import '@polymer/paper-icon-button';
|
|
9
|
+
import './pb-table-column.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A table grid based on [gridjs](https://gridjs.io/), which loads its data from a server endpoint
|
|
13
|
+
* specified in `source`. If `source` is a relative URI, it will be resolved relative to the
|
|
14
|
+
* TEI Publisher endpoint.
|
|
15
|
+
*
|
|
16
|
+
* The JSON data returned by the endpoint should be an object with two properties:
|
|
17
|
+
*
|
|
18
|
+
* * `count`: the overall number of rows available on the server
|
|
19
|
+
* * `results`: an array containing each record as an object
|
|
20
|
+
*
|
|
21
|
+
* The parameters send to the server are as follows:
|
|
22
|
+
*
|
|
23
|
+
*
|
|
24
|
+
* Parameter | Description
|
|
25
|
+
* ---------|----------
|
|
26
|
+
* limit | number of records to return for each page
|
|
27
|
+
* start | start offset from which to return records
|
|
28
|
+
* order | the id of the column to sort by
|
|
29
|
+
* dir | sort direction: either 'asc' or 'desc'
|
|
30
|
+
* search | an optional search string entered by the user
|
|
31
|
+
*
|
|
32
|
+
* Table columns are configured via nested `<pb-table-column>` elements:
|
|
33
|
+
*
|
|
34
|
+
* ```html
|
|
35
|
+
* <pb-table-column label="Name" property="name" sort width="33%"></pb-table-column>
|
|
36
|
+
* <pb-table-column label="Born" property="birth"></pb-table-column>
|
|
37
|
+
* <pb-table-column label="Died" property="death"></pb-table-column>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export class PbTableGrid extends pbMixin(LitElement) {
|
|
41
|
+
static get properties() {
|
|
42
|
+
return {
|
|
43
|
+
/**
|
|
44
|
+
* URI of the server-side endpoint to retrieve data from.
|
|
45
|
+
* Relative URIs are resolved relative to the configured TEI Publisher endpoint.
|
|
46
|
+
*/
|
|
47
|
+
source: {
|
|
48
|
+
type: String
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* Path to the gridjs theme CSS files.
|
|
52
|
+
*/
|
|
53
|
+
cssPath: {
|
|
54
|
+
type: String,
|
|
55
|
+
attribute: 'css-path'
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* If specified, columns (without a fixed width) will be resizable.
|
|
59
|
+
*/
|
|
60
|
+
resizable: {
|
|
61
|
+
type: Boolean
|
|
62
|
+
},
|
|
63
|
+
perPage: {
|
|
64
|
+
type: Number,
|
|
65
|
+
attribute: 'per-page'
|
|
66
|
+
},
|
|
67
|
+
height: {
|
|
68
|
+
type: String
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* If specified, enable server-side search.
|
|
72
|
+
*/
|
|
73
|
+
search: {
|
|
74
|
+
type: Boolean
|
|
75
|
+
},
|
|
76
|
+
_params: {
|
|
77
|
+
type: Object
|
|
78
|
+
},
|
|
79
|
+
...super.properties
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
constructor() {
|
|
84
|
+
super();
|
|
85
|
+
this.cssPath = '../css/gridjs';
|
|
86
|
+
this._params = {};
|
|
87
|
+
this.resizable = false;
|
|
88
|
+
this.search = false;
|
|
89
|
+
this.perPage = 10;
|
|
90
|
+
this.height = null;
|
|
91
|
+
this.fixedHeader = false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
connectedCallback() {
|
|
95
|
+
super.connectedCallback();
|
|
96
|
+
|
|
97
|
+
this.subscribeTo('pb-search-resubmit', (ev) => {
|
|
98
|
+
this._params = Object.assign({}, ev.detail.params);
|
|
99
|
+
this._submit();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
window.addEventListener('popstate', (ev) => {
|
|
103
|
+
this._params = ev.state;
|
|
104
|
+
this._submit();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (!this.height) {
|
|
108
|
+
const property = getComputedStyle(this).getPropertyValue('--pb-table-grid-height');
|
|
109
|
+
if (property) {
|
|
110
|
+
this.height = property;
|
|
111
|
+
} else {
|
|
112
|
+
this.height = 'auto';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
firstUpdated() {
|
|
118
|
+
const table = this.shadowRoot.getElementById('table');
|
|
119
|
+
|
|
120
|
+
const pbColumns = this.querySelectorAll('pb-table-column');
|
|
121
|
+
const columns = [];
|
|
122
|
+
pbColumns.forEach((column) => columns.push(column.data()));
|
|
123
|
+
PbTableGrid.waitOnce('pb-page-ready', () => {
|
|
124
|
+
this._params = this.getParameters();
|
|
125
|
+
const url = this.toAbsoluteURL(this.source);
|
|
126
|
+
const config = {
|
|
127
|
+
height: this.height,
|
|
128
|
+
fixedHeader: true,
|
|
129
|
+
columns,
|
|
130
|
+
resizable: this.resizable,
|
|
131
|
+
server: {
|
|
132
|
+
url,
|
|
133
|
+
then: data => data.results,
|
|
134
|
+
total: data => data.count
|
|
135
|
+
},
|
|
136
|
+
sort: {
|
|
137
|
+
multiColumn: false,
|
|
138
|
+
enabled: true,
|
|
139
|
+
server: {
|
|
140
|
+
url: (prev, cols) => {
|
|
141
|
+
if (!cols.length) return prev;
|
|
142
|
+
const col = cols[0];
|
|
143
|
+
return `${prev}${prev.indexOf('?') > -1 ? '&' : '?'}order=${columns[col.index].id}&dir=${col.direction === 1 ? 'asc' : 'desc'}`;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
pagination: {
|
|
148
|
+
enabled: true,
|
|
149
|
+
limit: this.perPage,
|
|
150
|
+
server: {
|
|
151
|
+
url: (prev, page, limit) => {
|
|
152
|
+
const form = this.shadowRoot.getElementById('form');
|
|
153
|
+
if (form) {
|
|
154
|
+
Object.assign(this._params, form.serializeForm());
|
|
155
|
+
}
|
|
156
|
+
this._params.limit = limit;
|
|
157
|
+
this._params.start = page * limit;
|
|
158
|
+
this.setParameters(this._params);
|
|
159
|
+
this.pushHistory('grid', this._params);
|
|
160
|
+
|
|
161
|
+
return `${prev}${prev.indexOf('?') > -1 ? '&' : '?'}${new URLSearchParams(this._params).toString()}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
this.grid = new Grid(config);
|
|
168
|
+
this.grid.on('load', () => {
|
|
169
|
+
this.emitTo('pb-results-received', {
|
|
170
|
+
"params": this._params
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
this.grid.render(table);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_submit() {
|
|
179
|
+
this.grid.forceRender();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
render() {
|
|
183
|
+
const themes = resolveURL(this.cssPath);
|
|
184
|
+
return html`
|
|
185
|
+
<link href="${themes}/mermaid.min.css" rel="stylesheet">
|
|
186
|
+
${
|
|
187
|
+
this.search ? html`
|
|
188
|
+
<iron-form id="form">
|
|
189
|
+
<form action="">
|
|
190
|
+
<paper-input id="search" name="search" label="Search" @keyup="${(e) => e.keyCode == 13 ? this._submit() : null}">
|
|
191
|
+
<paper-icon-button icon="search" @click="${this._submit}" slot="suffix"></paper-icon-button>
|
|
192
|
+
</paper-input>
|
|
193
|
+
</form>
|
|
194
|
+
</iron-form>
|
|
195
|
+
` : null
|
|
196
|
+
}
|
|
197
|
+
<div id="table"></div>
|
|
198
|
+
`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static get styles() {
|
|
202
|
+
return css`
|
|
203
|
+
:host {
|
|
204
|
+
display: block;
|
|
205
|
+
}
|
|
206
|
+
button {
|
|
207
|
+
border: 0;
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
customElements.define('pb-table-grid', PbTableGrid);
|
package/src/pb-view-annotate.js
CHANGED
|
@@ -454,7 +454,8 @@ class PbViewAnnotate extends PbView {
|
|
|
454
454
|
|
|
455
455
|
console.log('<pb-view-annotate> Range: %o', range);
|
|
456
456
|
const span = document.createElement('span');
|
|
457
|
-
|
|
457
|
+
const addClass = teiRange.properties[this.key] === '' ? 'incomplete' : '';
|
|
458
|
+
span.className = `annotation annotation-${teiRange.type} ${teiRange.type} ${addClass}`;
|
|
458
459
|
span.dataset.type = teiRange.type;
|
|
459
460
|
span.dataset.annotation = JSON.stringify(teiRange.properties);
|
|
460
461
|
|
|
@@ -657,9 +658,12 @@ class PbViewAnnotate extends PbView {
|
|
|
657
658
|
range = clearProperties(range);
|
|
658
659
|
this.emitTo('pb-annotations-changed', { ranges: this._ranges });
|
|
659
660
|
}
|
|
660
|
-
|
|
661
|
-
const json = Object.assign(
|
|
661
|
+
const jsonOld = JSON.parse(span.dataset.annotation);
|
|
662
|
+
const json = Object.assign(jsonOld || {}, properties);
|
|
662
663
|
span.dataset.annotation = JSON.stringify(json);
|
|
664
|
+
if (json[this.key] !== '') {
|
|
665
|
+
span.classList.remove('incomplete');
|
|
666
|
+
}
|
|
663
667
|
}
|
|
664
668
|
|
|
665
669
|
_editAnnotation(ev) {
|
|
@@ -753,7 +757,7 @@ class PbViewAnnotate extends PbView {
|
|
|
753
757
|
ev.preventDefault();
|
|
754
758
|
ev.stopPropagation();
|
|
755
759
|
const type = span.dataset.type;
|
|
756
|
-
const data = JSON.parse(span.dataset.annotation);
|
|
760
|
+
const data = JSON.parse(span.dataset.annotation) || {};
|
|
757
761
|
const color = this._annotationColors.get(type);
|
|
758
762
|
typeInd.innerHTML = type;
|
|
759
763
|
typeInd.style.backgroundColor = `var(--pb-annotation-${type})`;
|
|
@@ -896,7 +900,7 @@ class PbViewAnnotate extends PbView {
|
|
|
896
900
|
const annoData = node.parentNode.dataset.annotation;
|
|
897
901
|
const annoType = node.parentNode.dataset.type;
|
|
898
902
|
if (annoData && annoType) {
|
|
899
|
-
const parsed = JSON.parse(annoData);
|
|
903
|
+
const parsed = JSON.parse(annoData) || {};
|
|
900
904
|
isAnnotated = annoType === type;
|
|
901
905
|
ref = parsed[this.key];
|
|
902
906
|
}
|
|
@@ -1004,6 +1008,17 @@ class PbViewAnnotate extends PbView {
|
|
|
1004
1008
|
classes.push(`
|
|
1005
1009
|
.annotation-${type}::after {
|
|
1006
1010
|
background-color: var(--pb-annotation-${type});
|
|
1011
|
+
border-color: var(--pb-annotation-${type});
|
|
1012
|
+
color: var(${color.isLight ? '--pb-color-primary' : '--pb-color-inverse'});
|
|
1013
|
+
}
|
|
1014
|
+
.annotation-${type}.incomplete::after {
|
|
1015
|
+
background: repeating-linear-gradient(
|
|
1016
|
+
315deg,
|
|
1017
|
+
var(--pb-annotation-${type}),
|
|
1018
|
+
var(--pb-annotation-${type}) 5px,
|
|
1019
|
+
var(${color.isLight ? '--pb-annotation-stripes-light' : '--pb-annotation-stripes-dark'}) 5px,
|
|
1020
|
+
var(${color.isLight ? '--pb-annotation-stripes-light' : '--pb-annotation-stripes-dark'}) 10px
|
|
1021
|
+
);
|
|
1007
1022
|
color: var(${color.isLight ? '--pb-color-primary' : '--pb-color-inverse'});
|
|
1008
1023
|
}
|
|
1009
1024
|
`);
|
|
@@ -1064,7 +1079,7 @@ class PbViewAnnotate extends PbView {
|
|
|
1064
1079
|
font-variant: normal;
|
|
1065
1080
|
padding: 2px;
|
|
1066
1081
|
}
|
|
1067
|
-
|
|
1082
|
+
|
|
1068
1083
|
[part=highlight] {
|
|
1069
1084
|
border: 3px solid rgb(255, 174, 0);
|
|
1070
1085
|
border-radius: 8px;
|
package/src/pb-view.js
CHANGED
|
@@ -496,6 +496,15 @@ export class PbView extends pbMixin(LitElement) {
|
|
|
496
496
|
|
|
497
497
|
_refresh(ev) {
|
|
498
498
|
if (ev && ev.detail) {
|
|
499
|
+
if (ev.detail.hash && !(ev.detail.id || ev.detail.path || ev.detail.odd || ev.detail.view || ev.detail.position)) {
|
|
500
|
+
// if only the scroll target has changed: scroll to the element without reloading
|
|
501
|
+
this._scrollTarget = ev.detail.hash;
|
|
502
|
+
const target = this.shadowRoot.getElementById(this._scrollTarget);
|
|
503
|
+
if (target) {
|
|
504
|
+
setTimeout(() => target.scrollIntoView());
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
499
508
|
if (ev.detail.path) {
|
|
500
509
|
const doc = this.getDocument();
|
|
501
510
|
doc.path = ev.detail.path;
|
|
@@ -641,7 +650,7 @@ export class PbView extends pbMixin(LitElement) {
|
|
|
641
650
|
if (target) {
|
|
642
651
|
setTimeout(() => {
|
|
643
652
|
target.scrollIntoView();
|
|
644
|
-
});
|
|
653
|
+
}, 100);
|
|
645
654
|
}
|
|
646
655
|
this._scrollTarget = null;
|
|
647
656
|
});
|