UNPKG

@teipublisher/pb-components

Version:
375 lines (347 loc) 10.6 kB
import { LitElement, html, css } from 'lit-element'; import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'; import { pbMixin, waitOnce } from './pb-mixin.js'; import { translate } from "./pb-i18n.js"; import { createConnectors } from "./authority/connectors.js"; import "./pb-restricted.js"; import '@polymer/paper-input/paper-input'; import '@polymer/paper-icon-button'; /** * Performs authority lookups via configurable connectors. * * @fires pb-authority-select - Fired when user selects an entry from the list * @fires pb-authority-edit-entity - Fired when user clicks the edit button next to an entry * @fires pb-authority-new-entity - Fired when user clicks the add new entity button * @fires pb-authority-lookup - When received, starts a lookup using the passed in query string and * authority type */ export class PbAuthorityLookup extends pbMixin(LitElement) { static get properties() { return { /** * The query string to be sent to the authority */ query: { type: String, reflect: true, }, /** * Enable to alphabetically reorder authority search results by label. * Otherwise results are shown as returned by the authority. */ sortByLabel: { type: Boolean, attribute: 'sort-by-label' }, /** * A list of comma-separated stopwords which should be excluded * when searching for other occurrences of an authority in the * HTML text */ stopwords: { type: String }, /** * A list of space- or comma-separated group names, whose members will be * allowed to add or edit entries in the local register (if enabled). * * @default "tei" */ group: { type: String }, /** * The authority type to use. Should correspond to a name defined for one of the connectors. */ type: { type: String, reflect: true, }, /** * Do not show occurrences count, which would be fetched from the server. */ noOccurrences: { type: Boolean, attribute: 'no-occurrences', }, /** * Automatically start a lookup when the query parameter is set on initialization. */ autoLookup: { type: Boolean, attribute: 'auto', }, _results: { type: Array, }, ...super.properties, }; } constructor() { super(); this.query = ''; this.type = null; this.sortByLabel = false; this._results = []; this._authorities = {}; this.noOccurrences = false; this.group = 'tei'; } connectedCallback() { super.connectedCallback(); this._stopwordSet = new Set(); if (this.stopwords) { this.stopwords.split(/\s*,\s*/).forEach((sw) => this._stopwordSet.add(sw.toLowerCase())); } this.subscribeTo('pb-authority-lookup', ev => { this.query = ev.detail.query; this.type = ev.detail.type; this._results = []; this._query(); }); waitOnce('pb-page-ready', () => { const connectors = createConnectors(this.getEndpoint(), this); connectors.forEach(connector => { this._authorities[connector.register] = connector }); if (this.autoLookup) { this._query(); } }); console.log('<pb-authority-lookup> Registered authorities: %o', this._authorities); } render() { return html` <paper-input id="query" label="${translate('annotations.lookup')}" always-float-label value="${this.query}" @change="${this._queryChanged}" > <iron-icon icon="icons:search" slot="prefix"></iron-icon> ${ this._authorities[this.type] && this._authorities[this.type].editable ? html` <pb-restricted group="${this.group}" slot="suffix"> <paper-icon-button icon="icons:add" @click="${this._addEntity}" title="${translate('annotations.add-entity')}"></paper-icon-button> </pb-restricted> ` : null } </paper-input> <slot name="authform"></slot> <div id="output"> <ul part="output"> ${this._results.map(item => this._formatItem(item))} </ul> </div> `; } async lookup(register, id, container) { if (!id || id === '') { console.log('<pb-authority-lookup> Key is empty'); container.innerHTML = ''; return Promise.resolve(); } const authority = this._authorities[register]; console.log('<pb-authority-lookup> Retrieving info for %s from %s using %s', id, register, authority.constructor.name); let info = await authority.info(id, container); if (info.strings) { info = Object.assign(info, { strings: info.strings.filter((s) => s && !this._stopwordSet.has(s.toLowerCase())) }); } return info; } _formatItem(item) { return html` <li> <div class="icons"> <paper-icon-button icon="icons:link" @click="${() => this._select(item)}" title="link to" ></paper-icon-button> </div> <div class="link"> ${item.link ? html`<a target="_blank" href="${item.link}">${unsafeHTML(item.label)}</a>` : html`${unsafeHTML(item.label)}` } </div> ${item.occurrences > 0 ? html`<div><span class="occurrences" part="occurrences">${item.occurrences}</span></div>` : null} ${item.provider ? html`<div><span class="source" part="source">${item.provider}</span></div>` :null} <div><span class="register" part="register">${item.register}</span></div> ${ this._authorities[this.type] && this._authorities[this.type].editable ? html` <pb-restricted group="${this.group}"> <div class="icons"> <paper-icon-button icon="editor:mode-edit" @click="${() => this._editEntity(item)}" title="${translate('annotations.edit-entity')}" ></paper-icon-button> </div> </pb-restricted>` : null } ${item.details ? html`<div class="details" part="details">${item.details}</div>` : null} </li> `; } _select(item) { const connector = this._authorities[item.register]; const options = { strings: item.strings, type: item.register, properties: { ref: item.id, } }; if (connector) { connector .select(item) .then(() => this.emitTo('pb-authority-select', options)) .catch((e) => this.emitTo('pb-authority-error', {status: e.message})); } else { this.emitTo('pb-authority-select', options); } } _editEntity(item) { const connector = this._authorities[item.register]; if (connector) { connector .select(item) .then(() => this.emitTo('pb-authority-edit-entity', {id: item.id, type: item.register})); } else { this.emitTo('pb-authority-edit-entity', {id: item.id, type: item.register}); } } _queryChanged() { this._results = []; this.query = this.shadowRoot.getElementById('query').value; this._query(); } _query() { this.emitTo('pb-start-update'); this._authorities[this.type].query(this.query).then(results => { this._occurrences(results.items) .then((merged) => { this._results = merged; }); this.emitTo('pb-end-update'); // this.shadowRoot.getElementById('query').focus(); }); } _addEntity() { this.emitTo('pb-authority-new-entity', {query: this.query, type: this.type}); } _occurrences(items) { if (this.noOccurrences) { return Promise.resolve(items); } const params = new FormData(); params.append('register', this.type); items.forEach((item) => { params.append('id', item.id); }); return new Promise((resolve) => { fetch(`${this.getEndpoint()}/api/annotations/occurrences`, { method: 'POST', body: params }) .then(response => { if (response.ok) { return response.json(); } }) .then(json => { items.forEach((item) => { if (json[item.id]) { item.occurrences = json[item.id]; } else { item.occurrences = 0; } }); items.sort((i1, i2) => { const d = i2.occurrences - i1.occurrences; if (d === 0) { if (i1.provider === 'local' && i2.provider !== 'local') { return -1; } if (i2.provider === 'local' && i1.provider !== 'local') { return 1; } return this.sortByLabel ? i1.label.localeCompare(i2.label) : 0; } return d; }); resolve(items); }); }); } static get styles() { return css` :host { display: flex; flex-direction: column; } header { display: flex; align-items: center; } .link { flex-grow: 2; } #output { overflow: auto; /*FireFox*/ scrollbar-width: none; } /* #output .icons{ visibility: hidden; } #output .icons:hover{ visibility: visible; } */ #output ul { width: 100%; padding: 0; list-style: none; } #output li { display: flex; justify-content: space-between; flex-wrap: wrap; align-items: center; border-bottom: 1px solid #efefef; } #output li:hover{ background:#efefef; } #output td:nth-child(3), #output td:nth-child(4), #output td:nth-child(5) { text-align: right; vertical-align: middle; } .details, .source, .register, .occurrences { font-size: .85rem; width: 100%; } .source, .register, .occurrences { border-radius: 4px; padding: 4px; color: var(--pb-color-inverse); } .source { background-color: #637b8c; } .register { background-color: var(--pb-color-lighter); } .occurrences { background-color: var(--pb-color-focus); } `; } } customElements.define('pb-authority-lookup', PbAuthorityLookup);