UNPKG

@kit-data-manager/pid-component

Version:

The PID-Component is a web component that can be used to evaluate and display FAIR Digital Objects, PIDs, ORCiDs, and possibly other identifiers in a user-friendly way. It is easily extensible to support other identifier types.

409 lines (408 loc) 23.8 kB
/*! * * Copyright 2024 Karlsruhe Institute of Technology. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { h, Host } from "@stencil/core"; import { Database } from "../../utils/IndexedDBUtil"; import { clearCache } from "../../utils/DataCache"; export class PidComponent { constructor() { this.settings = '[]'; this.amountOfItems = 10; this.levelOfSubcomponents = 1; this.currentLevelOfSubcomponents = 0; this.emphasizeComponent = true; this.showTopLevelCopy = true; this.defaultTTL = 24 * 60 * 60 * 1000; this.items = []; this.actions = []; this.loadSubcomponents = false; this.displayStatus = 'loading'; this.tablePage = 0; this.temporarilyEmphasized = this.emphasizeComponent; this.toggleSubcomponents = () => { if (!this.hideSubcomponents && this.levelOfSubcomponents - this.currentLevelOfSubcomponents > 0) this.loadSubcomponents = !this.loadSubcomponents; }; this.showTooltip = (event) => { let target = event.target; do { target = target.parentElement; } while (target !== null && target.tagName !== 'A'); if (target !== null) target.children[1].classList.remove('hidden'); }; this.hideTooltip = (event) => { let target = event.target; do { target = target.parentElement; } while (target !== null && target.tagName !== 'A'); if (target !== null) target.children[1].classList.add('hidden'); }; } async watchValue() { this.displayStatus = 'loading'; await this.componentWillLoad(); } async watchLoadSubcomponents() { this.temporarilyEmphasized = this.loadSubcomponents; } async componentWillLoad() { let settings = []; try { settings = JSON.parse(this.settings); } catch (e) { console.error('Failed to parse settings.', e); } settings.forEach(value => { if (!value.values.some(v => v.name === 'ttl')) { value.values.push({ name: 'ttl', value: this.defaultTTL }); } }); try { const db = new Database(); this.identifierObject = await db.getEntity(this.value, settings); } catch (e) { console.error('Failed to get entity from db', e); return; } if (!this.hideSubcomponents) { this.items = this.identifierObject.items; this.items.sort((a, b) => { if (a.priority > b.priority) return 1; if (a.priority < b.priority) return -1; if (a.estimatedTypePriority > b.estimatedTypePriority) return 1; if (a.estimatedTypePriority < b.estimatedTypePriority) return -1; }); this.actions = this.identifierObject.actions; this.actions.sort((a, b) => a.priority - b.priority); } this.displayStatus = 'loaded'; console.log('Finished loading for ', this.value, this.identifierObject); await clearCache(); } render() { return (h(Host, { key: 'dc894b8d28790b44f836cc5330e68ccd4e4fe430', class: "inline flex-grow max-w-full font-sans flex-wrap align-baseline items-center text-xs" }, (this.items.length === 0 && this.actions.length === 0) || this.hideSubcomponents ? (this.identifierObject !== undefined && this.displayStatus === 'loaded' ? (h("span", { class: this.currentLevelOfSubcomponents === 0 ? 'group ' + (this.emphasizeComponent || this.temporarilyEmphasized ? 'rounded-md shadow-md border px-1 bg-white ' : 'bg-white/40') + 'text-xs text-clip inline-flex flex-grow open:align-top open:w-full ease-in-out transition-all duration-200 overflow-y-hidden font-bold font-mono cursor-pointer list-none overflow-x-hidden space-x-3 flex-nowrap flex-shrink-0 items-center' : '' }, h("span", { class: 'font-medium font-mono inline-flex flex-nowrap overflow-x-auto text-xs select-all' }, this.identifierObject.renderPreview()), this.currentLevelOfSubcomponents === 0 && this.showTopLevelCopy && this.emphasizeComponent ? h("copy-button", { value: this.identifierObject.value }) : '')) : (h("span", { class: 'inline-flex items-center transition ease-in-out' }, h("svg", { class: "animate-spin ml-1 mr-3 h-5 w-5 text-black", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24" }, h("circle", { class: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", "stroke-width": "4" }), h("path", { class: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })), "Loading... ", this.value))) : (h("details", { class: 'group ' + (this.emphasizeComponent || this.temporarilyEmphasized ? 'rounded-md border pl-0.5 py-0 bg-white bg-opacity-0' : 'bg-white/60') + 'text-clip inline flex-grow font-sans open:align-top open:w-full ease-in-out transition-all duration-200', open: this.openByDefault, onToggle: this.toggleSubcomponents }, h("summary", { class: "overflow-y-hidden font-bold font-mono cursor-pointer list-none overflow-x-hidden inline-flex flex-nowrap flex-shrink-0 items-center" }, h("span", { class: 'inline-flex flex-nowrap overflow-x-auto pr-1 items-center' }, this.emphasizeComponent || this.temporarilyEmphasized ? (h("span", { class: 'flex-shrink-0 pr-1' }, h("svg", { class: "transition group-open:-rotate-180", fill: "none", height: "12", "shape-rendering": "geometricPrecision", stroke: "currentColor", "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", viewBox: "0 0 12 12", width: "10" }, h("path", { d: "M 2 3 l 4 6 l 4 -6" })))) : (''), h("span", { class: 'font-medium font-mono inline-flex flex-nowrap overflow-x-auto text-sm select-all' }, this.identifierObject.renderPreview())), this.currentLevelOfSubcomponents === 0 && this.showTopLevelCopy && (this.emphasizeComponent || this.temporarilyEmphasized) ? (h("copy-button", { value: this.identifierObject.value })) : ('')), this.items.length > 0 ? (h("div", null, h("div", { class: "resize-y divide-y text-sm leading-6 bg-gray-100 m-1 p-0.5 h-64 max-h-fit overflow-y-scroll border rounded min-h-[4rem]" }, h("table", { class: "text-left w-full text-sm font-sans select-text" }, h("thead", { class: "bg-slate-600 flex text-slate-200 w-full rounded-t" }, h("tr", { class: "flex w-full rounded font-semibold" }, h("th", { class: "px-1 w-1/4" }, "Key"), h("th", { class: "px-1 w-3/4" }, "Value"))), h("tbody", { class: "bg-grey-100 flex flex-col items-center justify-between overflow-y-scroll w-full rounded-b" }, this.items .filter((_, index) => { return index >= this.tablePage * this.amountOfItems && index < this.tablePage * this.amountOfItems + this.amountOfItems; }) .map(value => (h("tr", { class: 'odd:bg-slate-200 flex w-full' }, h("td", { class: 'overflow-x-auto p-1 w-1/4 font-mono' }, h("a", { role: "link", class: "right-0 focus:outline-none focus:ring-gray-300 rounded-md focus:ring-offset-2 focus:ring-2 focus:bg-gray-200 relative md:mt-0 inline flex-nowrap", onMouseOver: this.showTooltip, onFocus: this.showTooltip, onMouseOut: this.hideTooltip }, h("div", { class: "cursor-pointer align-top justify-between flex-nowrap" }, h("a", { href: value.keyLink, target: '_blank', rel: 'noopener noreferrer', class: 'mr-2 text-blue-400 justify-start float-left' }, value.keyTitle), h("svg", { "aria-haspopup": "true", xmlns: "http://www.w3.org/2000/svg", class: "icon icon-tabler icon-tabler-info-circle justify-end min-w-[1rem] min-h-[1rem] flex-none float-right", width: "25", height: "25", viewBox: "0 0 24 24", "stroke-width": "1.5", stroke: "#A0AEC0", fill: "none", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { stroke: "none", d: "M0 0h24v24H0z" }), h("circle", { cx: "12", cy: "12", r: "9" }), h("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" }), h("polyline", { points: "11 12 12 12 12 16 13 16" }))), h("p", { role: "tooltip", class: "hidden z-20 mt-1 transition duration-100 ease-in-out shadow-md bg-white rounded text-xs text-gray-600 p-1 flex-wrap overflow-clip" }, value.keyTooltip))), h("td", { class: 'align-top overflow-x-auto text-sm p-1 w-3/4 select-text flex ' }, h("span", { class: 'flex-grow' }, this.loadSubcomponents && !this.hideSubcomponents && !value.renderDynamically ? (h("pid-component", { value: value.value, levelOfSubcomponents: this.levelOfSubcomponents, currentLevelOfSubcomponents: this.currentLevelOfSubcomponents + 1, amountOfItems: this.amountOfItems, settings: this.settings })) : !this.hideSubcomponents && this.currentLevelOfSubcomponents === this.levelOfSubcomponents && !value.renderDynamically ? (h("pid-component", { value: value.value, levelOfSubcomponents: this.currentLevelOfSubcomponents, currentLevelOfSubcomponents: this.currentLevelOfSubcomponents, amountOfItems: this.amountOfItems, settings: this.settings, hideSubcomponents: true })) : (h("span", { class: 'font-mono text-sm' }, value.value))), h("copy-button", { value: value.value })))))))), h("div", { class: "flex items-center justify-between border-t border-gray-200 bg-white px-1 py-1 sm:px-1 max-h-12" }, h("div", { class: "hidden sm:flex sm:flex-1 sm:flex-nowrap sm:items-center sm:justify-between text-sm" }, h("div", { class: '' }, h("p", { class: "text-sm text-gray-700" }, "Showing", h("span", { class: "font-medium" }, " ", 1 + this.tablePage * this.amountOfItems, " "), "to", h("span", { class: "font-medium" }, " ", Math.min(this.tablePage * this.amountOfItems + this.amountOfItems, this.items.length), " "), "of", h("span", { class: "font-medium" }, " ", this.items.length, " "), "entries")), h("div", null, this.items.length > this.amountOfItems ? (h("nav", { class: "isolate inline-flex -space-x-px rounded-md shadow-sm", "aria-label": "Pagination" }, h("button", { onClick: () => { this.tablePage = Math.max(this.tablePage - 1, 0); }, class: "relative inline-flex items-center rounded-l-md px-1 py-1 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" }, h("span", { class: "sr-only" }, "Previous"), h("svg", { class: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, h("path", { "fill-rule": "evenodd", d: "M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z", "clip-rule": "evenodd" }))), Array(Math.ceil(this.items.length / this.amountOfItems)) .fill(0) .map((_, index) => { return (h("button", { onClick: () => (this.tablePage = index), class: index === this.tablePage ? 'relative z-10 inline-flex items-center bg-blue-600 px-2 py-1 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600' : 'relative hidden items-center px-2 py-1 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 md:inline-flex' }, index + 1)); }), h("button", { onClick: () => { this.tablePage = Math.min(this.tablePage + 1, Math.floor(this.items.length / this.amountOfItems)); }, class: "relative inline-flex items-center rounded-r-md px-1 py-1 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0" }, h("span", { class: "sr-only" }, "Next"), h("svg", { class: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, h("path", { "fill-rule": "evenodd", d: "M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z", "clip-rule": "evenodd" }))))) : ('')))))) : (''), this.identifierObject.renderBody(), this.actions.length > 0 ? (h("span", { class: 'm-0.5 flex justify-between gap-1' }, this.actions.map(action => { let style = 'p-1 font-semibold text-sm rounded border '; switch (action.style) { case 'primary': style += 'bg-blue-500 text-white'; break; case 'secondary': style += 'bg-slate-200 text-blue-500'; break; case 'danger': style += 'bg-red-500 text-white'; break; } return (h("a", { href: action.link, class: style, rel: 'noopener noreferrer', target: '_blank' }, action.title)); }))) : (''))))); } static get is() { return "pid-component"; } static get originalStyleUrls() { return { "$": ["pid-component.css"] }; } static get styleUrls() { return { "$": ["pid-component.css"] }; } static get properties() { return { "value": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{string}" }], "text": "The value to parse, evaluate and render." }, "getter": false, "setter": false, "attribute": "value", "reflect": false }, "settings": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{string}" }], "text": "A stringified JSON object containing settings for this component.\nThe resulting object is passed to every subcomponent, so that every component has the same settings.\nValues and the according type are defined by the components themselves.\n(optional)\n\nSchema:\n```typescript\n{\n type: string,\n values: {\n name: string,\n value: any\n }[]\n}[]\n```" }, "getter": false, "setter": false, "attribute": "settings", "reflect": false, "defaultValue": "'[]'" }, "openByDefault": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{boolean}" }], "text": "Determines whether the component is open or not by default.\n(optional)" }, "getter": false, "setter": false, "attribute": "open-by-default", "reflect": false }, "amountOfItems": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{number}" }], "text": "The number of items to show in the table per page.\nDefaults to 10.\n(optional)" }, "getter": false, "setter": false, "attribute": "amount-of-items", "reflect": false, "defaultValue": "10" }, "levelOfSubcomponents": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{number}" }], "text": "The total number of levels of subcomponents to show.\nDefaults to 1.\n(optional)" }, "getter": false, "setter": false, "attribute": "level-of-subcomponents", "reflect": false, "defaultValue": "1" }, "currentLevelOfSubcomponents": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{number}" }], "text": "The current level of subcomponents.\nDefaults to 0.\n(optional)" }, "getter": false, "setter": false, "attribute": "current-level-of-subcomponents", "reflect": false, "defaultValue": "0" }, "hideSubcomponents": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{boolean}" }], "text": "Determines whether subcomponents should generally be shown or not.\nIf set to true, the component won't show any subcomponents.\nIf not set, the component will show subcomponents\nif the current level of subcomponents is not the total level of subcomponents or greater.\n(optional)" }, "getter": false, "setter": false, "attribute": "hide-subcomponents", "reflect": false }, "emphasizeComponent": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{boolean}" }], "text": "Determines whether components should be emphasized towards their surrounding by border and shadow.\nIf set to true, border and shadows will be shown around the component.\nIt not set, the component won't be surrounded by border and shadow.\n(optional)" }, "getter": false, "setter": false, "attribute": "emphasize-component", "reflect": false, "defaultValue": "true" }, "showTopLevelCopy": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{boolean}" }], "text": "Determines whether on the top level the copy button is shown.\nIf set to true, the copy button is shown also on the top level.\nIt not set, the copy button is only shown for sub-components.\n(optional)" }, "getter": false, "setter": false, "attribute": "show-top-level-copy", "reflect": false, "defaultValue": "true" }, "defaultTTL": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "type", "text": "{number}" }, { "name": "default", "text": "24 * 60 * 60 * 1000" }], "text": "Determines the default time to live (TTL) for entries in the IndexedDB.\nDefaults to 24 hours.\nUnits are in milliseconds.\n(optional)" }, "getter": false, "setter": false, "attribute": "default-t-t-l", "reflect": false, "defaultValue": "24 * 60 * 60 * 1000" } }; } static get states() { return { "identifierObject": {}, "items": {}, "actions": {}, "loadSubcomponents": {}, "displayStatus": {}, "tablePage": {}, "temporarilyEmphasized": {} }; } static get watchers() { return [{ "propName": "value", "methodName": "watchValue" }, { "propName": "loadSubcomponents", "methodName": "watchLoadSubcomponents" }]; } } //# sourceMappingURL=pid-component.js.map