@teipublisher/pb-components 3.3.1 → 3.4.0
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/.github/dependabot.yml +105 -5
- package/CHANGELOG.md +19 -0
- package/dist/pb-components-bundle.js +279 -258
- package/dist/pb-elements.json +1 -1
- package/dist/pb-odd-editor.js +37 -37
- package/dist/{vaadin-element-mixin-e6a38937.js → vaadin-element-mixin-5b332881.js} +37 -23
- package/package.json +1 -1
- package/pb-elements.json +1 -1
- package/src/pb-navigation.js +59 -10
- package/src/pb-view-annotate.js +106 -23
- package/src/pb-view.js +49 -6
package/src/pb-view-annotate.js
CHANGED
|
@@ -342,6 +342,10 @@ class PbViewAnnotate extends PbView {
|
|
|
342
342
|
|
|
343
343
|
this.subscribeTo('pb-add-annotation', ev => this.addAnnotation(ev.detail));
|
|
344
344
|
this.subscribeTo('pb-edit-annotation', this._editAnnotation.bind(this));
|
|
345
|
+
this.subscribeTo('pb-start-update', () => {
|
|
346
|
+
this._cancelMarkerRefresh();
|
|
347
|
+
this._clearMarkers();
|
|
348
|
+
});
|
|
345
349
|
this.subscribeTo('pb-refresh', () => {
|
|
346
350
|
this._ranges = [];
|
|
347
351
|
this._rangesMap.clear();
|
|
@@ -373,6 +377,7 @@ class PbViewAnnotate extends PbView {
|
|
|
373
377
|
disconnectedCallback() {
|
|
374
378
|
super.disconnectedCallback();
|
|
375
379
|
|
|
380
|
+
this._cancelMarkerRefresh();
|
|
376
381
|
// Remove any events that we had earlier.
|
|
377
382
|
this._disconnectedSignal.abort();
|
|
378
383
|
}
|
|
@@ -383,10 +388,11 @@ class PbViewAnnotate extends PbView {
|
|
|
383
388
|
|
|
384
389
|
set annotations(annoData) {
|
|
385
390
|
this._ranges = annoData;
|
|
386
|
-
this.updateAnnotations(true);
|
|
391
|
+
this.updateAnnotations(true, false);
|
|
387
392
|
this._markIncompleteAnnotations();
|
|
388
393
|
this._initAnnotationColors();
|
|
389
394
|
this._annotationStyles();
|
|
395
|
+
this._scheduleMarkerRefresh();
|
|
390
396
|
}
|
|
391
397
|
|
|
392
398
|
saveHistory() {
|
|
@@ -431,7 +437,7 @@ class PbViewAnnotate extends PbView {
|
|
|
431
437
|
|
|
432
438
|
zoom(direction) {
|
|
433
439
|
super.zoom(direction);
|
|
434
|
-
|
|
440
|
+
this._scheduleMarkerRefresh();
|
|
435
441
|
}
|
|
436
442
|
|
|
437
443
|
getKey(type) {
|
|
@@ -444,7 +450,7 @@ class PbViewAnnotate extends PbView {
|
|
|
444
450
|
const scheduleCallback = () => {
|
|
445
451
|
_pendingCallback = setTimeout(() => {
|
|
446
452
|
_pendingCallback = null;
|
|
447
|
-
this.
|
|
453
|
+
this._scheduleMarkerRefresh();
|
|
448
454
|
}, 200);
|
|
449
455
|
};
|
|
450
456
|
window.addEventListener('resize', () => {
|
|
@@ -467,19 +473,76 @@ class PbViewAnnotate extends PbView {
|
|
|
467
473
|
|
|
468
474
|
_handleContent() {
|
|
469
475
|
super._handleContent();
|
|
470
|
-
this.updateComplete.then(() =>
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
);
|
|
476
|
+
this.updateComplete.then(() => {
|
|
477
|
+
this._initAnnotationColors();
|
|
478
|
+
this._annotationStyles();
|
|
479
|
+
this.updateAnnotations(true, false);
|
|
480
|
+
this._markIncompleteAnnotations();
|
|
481
|
+
if (this._scrollTop) {
|
|
482
|
+
this.scrollTop = this._scrollTop;
|
|
483
|
+
this._scrollTop = undefined;
|
|
484
|
+
}
|
|
485
|
+
this.emitTo('pb-annotations-loaded');
|
|
486
|
+
// Marker positions depend on the ODD stylesheet and final text layout.
|
|
487
|
+
this._scheduleMarkerRefresh();
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
_cancelMarkerRefresh() {
|
|
492
|
+
if (this._markerRefreshController) {
|
|
493
|
+
this._markerRefreshController.abort();
|
|
494
|
+
this._markerRefreshController = null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Refresh annotation markers after layout has settled. Measuring too early
|
|
500
|
+
* (e.g. while paginated content is still being replaced) leaves markers at
|
|
501
|
+
* stale coordinates.
|
|
502
|
+
*/
|
|
503
|
+
async _scheduleMarkerRefresh() {
|
|
504
|
+
this._cancelMarkerRefresh();
|
|
505
|
+
const controller = new AbortController();
|
|
506
|
+
this._markerRefreshController = controller;
|
|
507
|
+
const { signal } = controller;
|
|
508
|
+
|
|
509
|
+
try {
|
|
510
|
+
await this.updateComplete;
|
|
511
|
+
if (signal.aborted) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const styleLink = this.shadowRoot.querySelector('#view link[rel="stylesheet"]');
|
|
516
|
+
if (styleLink && !styleLink.sheet) {
|
|
517
|
+
await new Promise(resolve => {
|
|
518
|
+
const done = () => resolve();
|
|
519
|
+
styleLink.addEventListener('load', done, { once: true });
|
|
520
|
+
styleLink.addEventListener('error', done, { once: true });
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
if (signal.aborted) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (document.fonts?.ready) {
|
|
528
|
+
await document.fonts.ready;
|
|
529
|
+
}
|
|
530
|
+
if (signal.aborted) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
535
|
+
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
536
|
+
if (signal.aborted) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
this._paintMarkers();
|
|
541
|
+
} finally {
|
|
542
|
+
if (this._markerRefreshController === controller) {
|
|
543
|
+
this._markerRefreshController = null;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
483
546
|
}
|
|
484
547
|
|
|
485
548
|
_updateAnnotation(teiRange, silent = false, batch = false) {
|
|
@@ -548,13 +611,13 @@ class PbViewAnnotate extends PbView {
|
|
|
548
611
|
this._rangesMap.set(span, teiRange);
|
|
549
612
|
|
|
550
613
|
if (!batch) {
|
|
551
|
-
this.
|
|
614
|
+
this._scheduleMarkerRefresh();
|
|
552
615
|
}
|
|
553
616
|
|
|
554
617
|
return span;
|
|
555
618
|
}
|
|
556
619
|
|
|
557
|
-
updateAnnotations(silent = false) {
|
|
620
|
+
updateAnnotations(silent = false, refreshMarkers = true) {
|
|
558
621
|
this._ranges.forEach(teiRange => {
|
|
559
622
|
let span;
|
|
560
623
|
switch (teiRange.type) {
|
|
@@ -579,7 +642,9 @@ class PbViewAnnotate extends PbView {
|
|
|
579
642
|
break;
|
|
580
643
|
}
|
|
581
644
|
});
|
|
582
|
-
|
|
645
|
+
if (refreshMarkers) {
|
|
646
|
+
this._scheduleMarkerRefresh();
|
|
647
|
+
}
|
|
583
648
|
}
|
|
584
649
|
|
|
585
650
|
_getSelection() {
|
|
@@ -750,7 +815,7 @@ class PbViewAnnotate extends PbView {
|
|
|
750
815
|
|
|
751
816
|
this.emitTo('pb-annotations-changed', { ranges: this._ranges });
|
|
752
817
|
|
|
753
|
-
|
|
818
|
+
this._scheduleMarkerRefresh();
|
|
754
819
|
|
|
755
820
|
this._inHandler = true;
|
|
756
821
|
try {
|
|
@@ -976,11 +1041,16 @@ class PbViewAnnotate extends PbView {
|
|
|
976
1041
|
|
|
977
1042
|
/**
|
|
978
1043
|
* For all annotations currently shown, create a marker element and position
|
|
979
|
-
* it absolute next to the annotation
|
|
980
|
-
*
|
|
981
|
-
* @param {HTMLElement} root element containing the markers
|
|
1044
|
+
* it absolute next to the annotation. Waits for layout to settle before measuring.
|
|
982
1045
|
*/
|
|
983
1046
|
refreshMarkers() {
|
|
1047
|
+
this._scheduleMarkerRefresh();
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Measure annotation spans and paint underline markers into the marker layer.
|
|
1052
|
+
*/
|
|
1053
|
+
_paintMarkers() {
|
|
984
1054
|
const root = this.shadowRoot.getElementById('view');
|
|
985
1055
|
const rootRect = root.getBoundingClientRect();
|
|
986
1056
|
const markerLayer = this.shadowRoot.getElementById('marker-layer');
|
|
@@ -1212,6 +1282,19 @@ class PbViewAnnotate extends PbView {
|
|
|
1212
1282
|
return [
|
|
1213
1283
|
super.styles,
|
|
1214
1284
|
css`
|
|
1285
|
+
:host {
|
|
1286
|
+
position: relative;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
#marker-layer {
|
|
1290
|
+
position: absolute;
|
|
1291
|
+
top: 0;
|
|
1292
|
+
left: 0;
|
|
1293
|
+
width: 100%;
|
|
1294
|
+
height: 100%;
|
|
1295
|
+
pointer-events: none;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1215
1298
|
.annotation-type {
|
|
1216
1299
|
display: inline-block;
|
|
1217
1300
|
text-align: right;
|
package/src/pb-view.js
CHANGED
|
@@ -650,6 +650,18 @@ export class PbView extends themableMixin(pbMixin(LitElement)) {
|
|
|
650
650
|
_doLoad(params) {
|
|
651
651
|
this.emitTo('pb-start-update', params);
|
|
652
652
|
|
|
653
|
+
// On the first load, check whether the server already rendered this
|
|
654
|
+
// fragment into our light DOM. If so, request only
|
|
655
|
+
// the navigation metadata (content=none) and adopt the existing markup
|
|
656
|
+
// instead of rendering the same content a second time.
|
|
657
|
+
if (this._ssrContent === undefined) {
|
|
658
|
+
this._ssrContent = this.querySelector(':scope > [data-pb-ssr]') || null;
|
|
659
|
+
this._ssrFootnotes = this.querySelector(':scope > [data-pb-ssr-footnotes]') || null;
|
|
660
|
+
}
|
|
661
|
+
if (this._ssrContent) {
|
|
662
|
+
params.content = 'none';
|
|
663
|
+
}
|
|
664
|
+
|
|
653
665
|
console.log('<pb-view> Loading view with params %o', params);
|
|
654
666
|
if (!this.infiniteScroll) {
|
|
655
667
|
this._clear();
|
|
@@ -709,9 +721,7 @@ export class PbView extends themableMixin(pbMixin(LitElement)) {
|
|
|
709
721
|
|
|
710
722
|
const index = await fetch(`index.json`).then(response => response.json());
|
|
711
723
|
const paramNames = ['odd', 'view', 'xpath', 'map'];
|
|
712
|
-
this.
|
|
713
|
-
paramNames.push(`user.${param.getAttribute('name')}`),
|
|
714
|
-
);
|
|
724
|
+
this._params().forEach(param => paramNames.push(`user.${param.getAttribute('name')}`));
|
|
715
725
|
let url = params.id ? createKey([...paramNames, 'id']) : createKey([...paramNames, 'root']);
|
|
716
726
|
let file = index[url];
|
|
717
727
|
if (!file) {
|
|
@@ -827,7 +837,17 @@ export class PbView extends themableMixin(pbMixin(LitElement)) {
|
|
|
827
837
|
const elem = document.createElement('div');
|
|
828
838
|
// elem.style.opacity = 0; //hide it - animation has to make sure to blend it in
|
|
829
839
|
fragment.appendChild(elem);
|
|
830
|
-
|
|
840
|
+
if (this._ssrContent && (!resp.content || resp.content.length === 0)) {
|
|
841
|
+
// Adopt the server-rendered content from light DOM (SSR) rather than
|
|
842
|
+
// re-rendering it: move its children into the shadow content container.
|
|
843
|
+
while (this._ssrContent.firstChild) {
|
|
844
|
+
elem.appendChild(this._ssrContent.firstChild);
|
|
845
|
+
}
|
|
846
|
+
this._ssrContent.remove();
|
|
847
|
+
this._ssrContent = null;
|
|
848
|
+
} else {
|
|
849
|
+
elem.innerHTML = resp.content;
|
|
850
|
+
}
|
|
831
851
|
|
|
832
852
|
// if before-update-event is set, we do not replace the content immediately,
|
|
833
853
|
// but emit an event
|
|
@@ -889,11 +909,23 @@ export class PbView extends themableMixin(pbMixin(LitElement)) {
|
|
|
889
909
|
|
|
890
910
|
if (this.appendFootnotes) {
|
|
891
911
|
const footnotes = document.createElement('div');
|
|
892
|
-
if (
|
|
912
|
+
if (this._ssrFootnotes) {
|
|
913
|
+
// Adopt the server-rendered footnotes from light DOM (SSR).
|
|
914
|
+
while (this._ssrFootnotes.firstChild) {
|
|
915
|
+
footnotes.appendChild(this._ssrFootnotes.firstChild);
|
|
916
|
+
}
|
|
917
|
+
} else if (resp.footnotes) {
|
|
893
918
|
footnotes.innerHTML = resp.footnotes;
|
|
894
919
|
}
|
|
895
920
|
this._footnotes = footnotes;
|
|
896
921
|
}
|
|
922
|
+
// Drop the SSR footnotes marker even when not appending, so it does not
|
|
923
|
+
// linger in the light DOM (matches the dynamic path, which ignores
|
|
924
|
+
// footnotes unless append-footnotes is set).
|
|
925
|
+
if (this._ssrFootnotes) {
|
|
926
|
+
this._ssrFootnotes.remove();
|
|
927
|
+
this._ssrFootnotes = null;
|
|
928
|
+
}
|
|
897
929
|
|
|
898
930
|
this._initFootnotes(this._footnotes);
|
|
899
931
|
|
|
@@ -1013,9 +1045,20 @@ export class PbView extends themableMixin(pbMixin(LitElement)) {
|
|
|
1013
1045
|
}
|
|
1014
1046
|
}
|
|
1015
1047
|
|
|
1048
|
+
/**
|
|
1049
|
+
* Collect the configuration `pb-param` children of this view. Excludes any
|
|
1050
|
+
* `pb-param` that may occur inside server-rendered (SSR) content adopted into
|
|
1051
|
+
* the light DOM, so injected content can never be mistaken for a parameter.
|
|
1052
|
+
*/
|
|
1053
|
+
_params() {
|
|
1054
|
+
return Array.from(this.querySelectorAll('pb-param')).filter(
|
|
1055
|
+
param => !param.closest('[data-pb-ssr], [data-pb-ssr-footnotes]'),
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1016
1059
|
_getParameters() {
|
|
1017
1060
|
const params = [];
|
|
1018
|
-
this.
|
|
1061
|
+
this._params().forEach(param => {
|
|
1019
1062
|
params[`user.${param.getAttribute('name')}`] = param.getAttribute('value');
|
|
1020
1063
|
});
|
|
1021
1064
|
// add parameters for features set with pb-toggle-feature
|