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.

1,075 lines (1,066 loc) 104 kB
/*! * * Copyright 2024-2026 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 * * https://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 { r as registerInstance, h, H as Host, c as getElement, d as createEvent } from './index-BeCqCMz1.js'; import { r as renderers, c as clearCache } from './PID-CeLI8GZT.js'; import './json-viewer.entry.js'; const CopyButton = class { constructor(hostRef) { registerInstance(this, hostRef); this.copied = false; this.darkMode = 'light'; this.copyValue = (event) => { event.stopPropagation(); event.preventDefault(); const textArea = document.createElement('textarea'); textArea.value = this.value; textArea.setAttribute('aria-hidden', 'true'); textArea.setAttribute('readonly', 'readonly'); textArea.style.cssText = 'position:fixed;top:0;left:0;width:2em;height:2em;padding:0;border:none;outline:none;box-shadow:none;opacity:0;'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); textArea.setSelectionRange(0, textArea.value.length); let copied = false; try { copied = document.execCommand('copy'); } catch (_) { } document.body.removeChild(textArea); if (copied) { this.showSuccess(); return; } if ('clipboard' in navigator) { navigator.clipboard.writeText(this.value).then(() => this.showSuccess(), () => { }); } }; } getIsDarkMode() { if (this.darkMode === 'dark') { return true; } if (this.darkMode === 'light') { return false; } const parentComponent = this.el.closest('pid-component'); if (parentComponent === null || parentComponent === void 0 ? void 0 : parentComponent.classList.contains('bg-gray-800')) { return true; } if (this.darkMode === 'system') { return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; } return false; } render() { const buttonText = this.copied ? '✓ Copied!' : 'Copy'; const ariaLabel = this.getAriaLabel(); const isDarkMode = this.getIsDarkMode(); return (h(Host, { key: 'c5c3499428ec7ab4b306458dc25af78c26e1b91d', class: 'inline-block align-baseline text-xs' }, this.copied && (h("span", { key: 'eab38ff718680f01488fb1a3fe0cf36547a09daa', class: "sr-only", "aria-live": "assertive" }, "Content copied to clipboard")), h("button", { key: 'ac8d35bc7e2cb5038846adc8134b20ca31ac09e0', class: `${this.copied ? (isDarkMode ? 'bg-green-700' : 'bg-green-200') : isDarkMode ? 'bg-gray-700 hover:bg-gray-600' : 'bg-white hover:bg-blue-200'} relative z-30 max-h-min flex-none items-center rounded-md border ${isDarkMode ? 'border-gray-600 text-gray-200 hover:text-white' : 'border-slate-500 text-slate-800 hover:text-slate-900'} px-2 py-0.5 font-mono font-medium transition-colors duration-200 focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:outline-hidden`, onClick: e => this.copyValue(e), "aria-label": ariaLabel, title: ariaLabel, type: "button" }, buttonText))); } showSuccess() { this.copied = true; setTimeout(() => { this.copied = false; }, 1500); } getAriaLabel() { const baseLabel = this.label || 'content'; return this.copied ? `${baseLabel} copied to clipboard` : `Copy ${baseLabel} to clipboard`; } get el() { return getElement(this); } }; const PidActions = class { constructor(hostRef) { registerInstance(this, hostRef); this.actions = []; this.darkMode = 'system'; } render() { if (this.actions.length === 0) { return null; } const isDarkMode = this.darkMode === 'dark' || (this.darkMode === 'system' && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches); const containerId = this.actionsId || `actions-${Math.random().toString(36).substring(2, 11)}`; return (h("div", { id: containerId, class: `actions-container sticky right-0 bottom-0 left-0 z-20 w-full ${isDarkMode ? 'bg-gray-800' : 'bg-white'}`, role: "toolbar", "aria-label": "Available actions" }, h("span", { id: `${containerId}-desc`, class: "sr-only" }, "The following links open related resources in new tabs"), h("div", { class: "flex flex-wrap justify-between gap-1", "aria-describedby": `${containerId}-desc` }, this.actions.map((action, index) => { const baseClasses = 'p-1 font-semibold text-sm rounded-sm border transition-colors duration-200'; const focusClasses = 'focus:outline-hidden focus:ring-2 focus:ring-offset-1 focus:ring-blue-500'; let styleClasses; if (isDarkMode) { switch (action.style) { case 'primary': styleClasses = 'bg-blue-700 text-white hover:bg-blue-600 border-blue-600'; break; case 'secondary': styleClasses = 'bg-slate-700 text-blue-300 hover:bg-slate-600 border-slate-600'; break; case 'danger': styleClasses = 'bg-red-700 text-white hover:bg-red-600 border-red-600'; break; default: styleClasses = 'bg-gray-700 text-gray-200 hover:bg-gray-600 border-gray-600'; } } else { switch (action.style) { case 'primary': styleClasses = 'bg-blue-500 text-white hover:bg-blue-600 border-blue-400'; break; case 'secondary': styleClasses = 'bg-slate-200 text-blue-500 hover:bg-slate-300 border-slate-300'; break; case 'danger': styleClasses = 'bg-red-500 text-white hover:bg-red-600 border-red-400'; break; default: styleClasses = 'bg-gray-200 text-gray-700 hover:bg-gray-300 border-gray-300'; } } return (h("a", { key: `action-${action.title}-${index}`, href: action.link, class: `${baseClasses} ${styleClasses} ${focusClasses}`, rel: "noopener noreferrer", target: "_blank", "aria-label": `${action.title} (opens in new tab)`, title: `${action.title} - Opens in a new tab` }, h("span", null, action.title), h("span", { class: "sr-only" }, "(opens in new tab)"))); })))); } }; const collapsibleCss = () => `details summary::-webkit-details-marker{display:none}pid-collapsible.resize-both{resize:both!important;overflow:auto!important;max-width:100%!important;will-change:width,height;transition:none!important}pid-collapsible[expanded]{position:absolute;z-index:50;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1)}@media not all and (min-resolution:.001dpcm){@supports (-webkit-appearance:none){pid-collapsible details{display:flex!important;flex-direction:column!important}pid-collapsible details summary{display:block!important}pid-collapsible.resize-both{-webkit-resize:both!important;resize:both!important;overflow:auto!important;position:relative!important}pid-collapsible.resize-both:after{content:"";position:absolute;bottom:0;right:0;width:15px;height:15px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-width='2' d='M22 2 2 22M22 8 8 22M22 14l-8 8'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:100% 100%;cursor:nwse-resize;z-index:10;pointer-events:none}}}pid-collapsible.resize-both:after{content:"";position:absolute;bottom:0;right:0;width:15px;height:15px;cursor:nwse-resize;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-width='2' d='M22 2 2 22M22 8 8 22M22 14l-8 8'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:100% 100%;z-index:10;pointer-events:none}@supports (-webkit-appearance:none) and (not (display:-webkit-box)){pid-collapsible.resize-both{resize:both!important;overflow:auto!important;display:block!important;position:relative!important}pid-collapsible.resize-both:before{content:"";display:block;width:100%;height:100%;position:absolute;top:0;left:0;pointer-events:none;z-index:-1}}@supports (-webkit-appearance:none){pid-collapsible details{display:-webkit-box!important;display:-webkit-flex!important;display:flex!important;-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-webkit-flex-direction:column!important;flex-direction:column!important;min-height:100%}}@media (-webkit-min-device-pixel-ratio:0) and (min-resolution:.001dpcm){pid-collapsible{-webkit-transform:translateZ(0);transform:translateZ(0)}pid-collapsible.resizing{pointer-events:none;contain:layout size}pid-collapsible.resize-both{-webkit-resize:both!important}pid-collapsible.resize-both:after{content:"";position:absolute;bottom:0;right:0;width:15px;height:15px;cursor:nwse-resize;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-width='2' d='M22 2 2 22M22 8 8 22M22 14l-8 8'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:100% 100%;z-index:10;pointer-events:none}}:root{--z-back:-1;--z-resize:10;--z-content:20;--z-footer:30;--z-header:50}:host{display:block;--initial-width:500px;--initial-height:300px;--min-width:300px;--min-height:200px}@media (max-width:768px){:host{--initial-width:400px;--min-width:250px}}@media (max-width:480px){:host{--initial-width:300px;--min-width:200px}}@media (prefers-contrast:more){pid-collapsible summary{border:1px solid}pid-collapsible.resize-both:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='currentColor' stroke-linecap='round' stroke-width='3' d='M22 2 2 22M22 8 8 22M22 14l-8 8'/%3E%3C/svg%3E");opacity:.9}}@media print{pid-collapsible.resize-both{resize:none!important}pid-collapsible.resize-both:after{display:none!important}}pid-collapsible summary:focus-visible{outline:2px solid #0ea5e9;outline-offset:2px}pid-collapsible .overflow-visible,pid-pagination .overflow-visible{overflow:visible!important}`; const CONSTANTS = { DEFAULT_WIDTH: '500px', DEFAULT_HEIGHT: '300px', MIN_WIDTH: 300, MIN_HEIGHT: 200, PADDING_WIDTH: 40, PADDING_HEIGHT: 60, FOOTER_HEIGHT: 60, }; const Z_INDICES = { RESIZE_HANDLE: 10, FOOTER_CONTENT: 30, STICKY_ELEMENTS: 50, }; const RESIZE_INDICATOR_SVG = ` <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M22 2L2 22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> <path d="M22 8L8 22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> <path d="M22 14L14 22" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> </svg> `; const PidCollapsible = class { constructor(hostRef) { registerInstance(this, hostRef); this.collapsibleToggle = createEvent(this, "collapsibleToggle"); this.contentHeightChange = createEvent(this, "contentHeightChange"); this.open = false; this.emphasize = false; this.darkMode = 'system'; this.lineHeight = 24; this.showFooter = false; this.expanded = false; this.previewScrollable = false; this.isDarkMode = false; this.isToggling = false; this.lastClickTime = 0; this.pendingClickTimer = null; this.resizeDebounceTimer = null; this.lastResizeDimensions = { width: 0, height: 0 }; this.handleDarkModeChange = () => { this.updateDarkMode(); }; this.handlePageChange = (event) => { console.debug('Page changed to:', event.detail); this.recalculateContentDimensions(); }; this.handleSafariCompatibility = (e) => { if (!this.isSafari() || this.isToggling) return; const target = e.target; if ((target === null || target === void 0 ? void 0 : target.closest('copy-button')) || (target === null || target === void 0 ? void 0 : target.closest('[slot="summary-actions"]')) || (target === null || target === void 0 ? void 0 : target.closest('button')) || (target === null || target === void 0 ? void 0 : target.closest('a'))) { return; } e.preventDefault(); e.stopPropagation(); this.performToggle(!this.open); }; this.handleToggle = (event) => { event.preventDefault(); event.stopPropagation(); const details = this.el.querySelector('details'); if (details) { details.open = this.open; } }; this.handleSummaryClick = (event) => { const path = event.composedPath(); const clickedInteractive = path.some(el => { var _a; return el instanceof HTMLElement && (el.tagName === 'BUTTON' || el.tagName === 'A' || el.tagName === 'COPY-BUTTON' || el.tagName === 'PID-ACTIONS' || ((_a = el.getAttribute) === null || _a === void 0 ? void 0 : _a.call(el, 'role')) === 'button'); }); if (clickedInteractive) { return; } event.preventDefault(); event.stopPropagation(); if (this.isToggling) return; const now = Date.now(); const elapsed = now - this.lastClickTime; this.lastClickTime = now; if (this.pendingClickTimer !== null) { clearTimeout(this.pendingClickTimer); this.pendingClickTimer = null; } if (elapsed < 300 && this.open) { this.performToggle(false); } else { this.pendingClickTimer = setTimeout(() => { this.pendingClickTimer = null; this.performToggle(!this.open); }, 200); } }; } watchOpen() { this.updateAppearance(); if (this.open) this.recalculateContentDimensions(); } watchDarkMode() { this.updateDarkMode(); } componentWillLoad() { this.currentWidth = this.initialWidth || CONSTANTS.DEFAULT_WIDTH; this.currentHeight = this.initialHeight || CONSTANTS.DEFAULT_HEIGHT; this.initializeDarkMode(); } componentDidLoad() { this.setupResizeObserver(); this.updateAppearance(); this.addBrowserCompatibilityListeners(); this.addComponentEventListeners(); if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { this.el.style.verticalAlign = 'top'; } } disconnectedCallback() { this.cleanupResources(); this.cleanupDarkModeListener(); } async recalculateContentDimensions() { if (this.open) { if (this.resizeDebounceTimer !== null) { window.cancelAnimationFrame(this.resizeDebounceTimer); } return new Promise(resolve => { this.resizeDebounceTimer = window.requestAnimationFrame(() => { if (this.lastExpandedWidth) { this.currentWidth = this.lastExpandedWidth; } else { this.currentWidth = this.initialWidth || this.getResponsiveDefaultWidth(); } this.el.style.width = this.currentWidth; this.el.style.height = 'auto'; this.el.style.maxHeight = 'max-content'; requestAnimationFrame(() => { if (this.open) { const actualHeight = this.el.scrollHeight; this.el.style.height = `${actualHeight}px`; this.el.style.maxHeight = `${actualHeight}px`; } this.lastExpandedWidth = this.currentWidth; const dimensions = this.calculateContentDimensions(); this.contentHeightChange.emit({ maxHeight: dimensions.maxHeight }); this.resizeDebounceTimer = null; resolve(dimensions); }); }); }); } return null; } render() { const hostClasses = this.getHostClasses(); const detailsClasses = this.getDetailsClasses(); const summaryClasses = this.getSummaryClasses(); const contentClasses = this.getContentClasses(); const footerClasses = this.getFooterClasses(); const footerActionsClasses = this.getFooterActionsClasses(); return (h(Host, { key: 'cbcf55c166323c79e344ead5bd5811d19088c6e4', class: hostClasses }, h("details", { key: '6b2e144d57340c7fc0637c62a1e618429ff423b4', class: detailsClasses, open: this.open, onToggle: this.handleToggle }, h("summary", { key: '4c053bf0b5a0609e7e5f37abf0892dc459dc08a5', class: summaryClasses, onClick: this.handleSummaryClick }, this.emphasize && (h("span", { key: '8a264bf2bd3223734bf3df20b244c987a02bb1dd' }, h("svg", { key: '2ea1c2df5db8316f4bdc14f3d1e1f2c8b3bb9403', class: `${this.isDarkMode ? 'text-gray-300' : 'text-gray-600'} transition-transform duration-200 group-open:rotate-180 mr-2 ml-1`, fill: "none", height: "12", width: "12", stroke: "currentColor", "stroke-linecap": "round", "stroke-linejoin": "round", "stroke-width": "1.5", viewBox: "0 0 12 12", "aria-hidden": "true" }, h("path", { key: '3c4e317acb12ea1ac020f5ea6fe654c0b154ce0e', d: "M 2 3 l 4 6 l 4 -6" })))), h("span", { key: '3cbead450cf92831327ff381033ebe4c499af442', class: `block ${this.previewScrollable ? 'shrink-0' : 'min-w-0 whitespace-nowrap overflow-hidden text-ellipsis'}` }, h("slot", { key: '5706002a1de3ed07ddf9f5fc21e9c223c36b3eb4', name: "summary" })), h("div", { key: 'ca8c041962942bb4eec5d713aa6ad34d4dbd021a', class: `ml-auto shrink-0 ${this.previewScrollable ? 'sticky right-0' : ''}` }, h("slot", { key: '6fc238f5b0b681da07e37ae57e3aa1d3146e1895', name: "summary-actions" }))), h("div", { key: '41d4b51f1e6fd78ed9ff8e1fa7f7bb3dcfc45a25', class: `${contentClasses}` }, h("slot", { key: 'ef9f60ae2c92cbae6d16b68b68aa9def3b9c806b' })), this.showFooter && this.open && (h("div", { key: '2dade649916ee68dd3fe367e485ad5bbacd644d9', class: footerClasses }, h("div", { key: 'db9ac1fe6b58291e6ccd4f214b2123832962ccd4', class: `z-50 overflow-visible border-b ${this.isDarkMode ? 'border-gray-700 bg-gray-800' : 'border-gray-100 bg-white'}` }, h("slot", { key: '36ee7ecacee44fdf2a1f2e8d92a83f7e980f1ea7', name: "footer" })), h("div", { key: '38969d3f4c65671dc51e66cfbc076094115cb38c', class: footerActionsClasses }, h("div", { key: '9666f30918d3c437211c33d351e0d3cdc9a9ffd6', class: "grow overflow-visible" }, h("slot", { key: 'fb9431b73a3d54bf2d85db2ce51565a161b67715', name: "footer-left" })), h("div", { key: 'a599293e40865c70bf9936a4e3c377cf833986cc', class: "flex shrink-0 items-center gap-2 overflow-visible" }, h("slot", { key: '57c7cfe3db67cc7706d32281b1b580bd705f5432', name: "footer-actions" })))))))); } initializeDarkMode() { if (window.matchMedia) { this.darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); this.updateDarkMode(); if (this.darkModeMediaQuery.addEventListener) { this.darkModeMediaQuery.addEventListener('change', this.handleDarkModeChange); } else if (this.darkModeMediaQuery.addListener) { this.darkModeMediaQuery.addListener(this.handleDarkModeChange); } } else { this.isDarkMode = this.darkMode === 'dark'; } } updateDarkMode() { if (this.darkMode === 'dark') { this.isDarkMode = true; } else if (this.darkMode === 'light') { this.isDarkMode = false; } else if (this.darkMode === 'system' && this.darkModeMediaQuery) { this.isDarkMode = this.darkModeMediaQuery.matches; } } cleanupDarkModeListener() { if (this.darkModeMediaQuery) { if (this.darkModeMediaQuery.removeEventListener) { this.darkModeMediaQuery.removeEventListener('change', this.handleDarkModeChange); } else if (this.darkModeMediaQuery.removeListener) { this.darkModeMediaQuery.removeListener(this.handleDarkModeChange); } } } setupResizeObserver() { if (!window.ResizeObserver) { console.warn('ResizeObserver not supported in this browser'); return; } if (this.resizeObserver) { this.resizeObserver.disconnect(); } this.resizeObserver = new ResizeObserver(entries => { if (!this.open) return; const entry = entries[0]; if (!entry) return; const width = entry.contentRect.width; const height = entry.contentRect.height; if (Math.abs(width - this.lastResizeDimensions.width) < 2 && Math.abs(height - this.lastResizeDimensions.height) < 2) { return; } this.lastResizeDimensions = { width, height }; if (this.resizeDebounceTimer !== null) { window.cancelAnimationFrame(this.resizeDebounceTimer); } this.resizeDebounceTimer = window.requestAnimationFrame(() => { this.currentWidth = `${width}px`; this.currentHeight = `${height}px`; this.resizeDebounceTimer = null; }); }); if (this.open) { this.resizeObserver.observe(this.el); } } addBrowserCompatibilityListeners() { const details = this.el.querySelector('details'); if (!details) return; const summary = details.querySelector('summary'); if (!summary) return; summary.addEventListener('click', this.handleSafariCompatibility, { capture: true }); } isSafari() { return /^((?!chrome|android).)*safari/i.test(navigator.userAgent) && !/CriOS|FxiOS|EdgiOS/i.test(navigator.userAgent); } addComponentEventListeners() { const dataTables = this.el.querySelectorAll('pid-data-table'); dataTables.forEach(dataTable => { dataTable.addEventListener('pageChange', this.handlePageChange); }); } removeComponentEventListeners() { const dataTables = this.el.querySelectorAll('pid-data-table'); dataTables.forEach(dataTable => { dataTable.removeEventListener('pageChange', this.handlePageChange); }); } cleanupResources() { if (this.resizeDebounceTimer !== null) { window.cancelAnimationFrame(this.resizeDebounceTimer); this.resizeDebounceTimer = null; } if (this.resizeObserver) { this.resizeObserver.disconnect(); this.resizeObserver = null; } this.removeComponentEventListeners(); const details = this.el.querySelector('details'); if (details) { const summary = details.querySelector('summary'); if (summary) { summary.removeEventListener('click', this.handleSafariCompatibility, { capture: true }); } } } updateAppearance() { this.resetStyles(); if (this.open) { this.applyExpandedStyles(); } else { this.applyCollapsedStyles(); } } resetStyles() { const classesToRemove = ['resize-both', 'overflow-auto', 'w-auto', 'inline-block', 'align-middle', 'align-top', 'overflow-hidden', 'py-0', 'my-0', 'float-left', 'bg-white', 'block']; classesToRemove.forEach(cls => { if (this.el.classList.contains(cls)) { this.el.classList.remove(cls); } }); this.el.style.width = ''; this.el.style.height = ''; this.el.style.maxWidth = ''; this.el.style.maxHeight = ''; this.el.style.resize = ''; this.el.style.lineHeight = ''; this.el.style.overflow = ''; } applyExpandedStyles() { try { this.el.classList.add('resize-both', 'relative', 'block'); if (this.emphasize) { this.el.classList.add('bg-white'); } if (this.lastExpandedWidth) { this.currentWidth = this.lastExpandedWidth; } else if (this.initialWidth) { this.currentWidth = this.initialWidth; } else { this.currentWidth = this.getResponsiveDefaultWidth(); } this.el.style.width = this.currentWidth; this.el.style.height = 'auto'; this.el.style.maxHeight = 'max-content'; const summary = this.el.querySelector('summary'); if (summary) { summary.style.height = `${this.lineHeight}px`; summary.style.minHeight = `${this.lineHeight}px`; summary.style.maxHeight = `${this.lineHeight}px`; } this.el.style.resize = 'both'; this.addResizeIndicator(); requestAnimationFrame(() => { if (this.open) { const actualHeight = this.el.scrollHeight; this.el.style.height = `${actualHeight}px`; this.el.style.maxHeight = `${actualHeight}px`; } }); if (this.resizeObserver) { this.resizeObserver.observe(this.el); } } catch (error) { console.error('Failed to apply expanded styles:', error); } } getResponsiveDefaultWidth() { var _a; let node = this.el; while (node) { const root = node.getRootNode(); if (root instanceof ShadowRoot) { node = root.host; continue; } if (node instanceof HTMLElement) { const tag = node.tagName.toLowerCase(); if (tag === 'pid-component' || tag === 'pid-collapsible') { node = node.parentElement; continue; } } break; } const container = node instanceof HTMLElement ? node : null; const availableWidth = (_a = container === null || container === void 0 ? void 0 : container.clientWidth) !== null && _a !== void 0 ? _a : window.innerWidth; if (availableWidth < 600) { return '100%'; } if (availableWidth <= 1024) { return '70%'; } return '50%'; } calculateContentDimensions() { const contentElement = this.el.querySelector('.grow'); const contentWidth = (contentElement === null || contentElement === void 0 ? void 0 : contentElement.scrollWidth) || CONSTANTS.MIN_WIDTH; const contentHeight = (contentElement === null || contentElement === void 0 ? void 0 : contentElement.scrollHeight) || CONSTANTS.MIN_HEIGHT; const footerHeight = this.showFooter ? CONSTANTS.FOOTER_HEIGHT : 0; const maxWidth = contentWidth + CONSTANTS.PADDING_WIDTH; const maxHeight = contentHeight + CONSTANTS.PADDING_HEIGHT + footerHeight; return { contentWidth, contentHeight, maxWidth, maxHeight }; } applyCollapsedStyles() { if (this.el.style.width && this.el.style.width !== 'auto') { this.lastExpandedWidth = this.el.style.width; this.currentWidth = this.el.style.width; } if (this.el.style.height && this.el.style.height !== `${this.lineHeight}px`) { this.lastExpandedHeight = this.el.style.height; this.currentHeight = this.el.style.height; } if (this.lastExpandedWidth || this.lastExpandedHeight) { console.debug('Storing dimensions for later restoration:', { width: this.lastExpandedWidth, height: this.lastExpandedHeight, }); } this.el.style.maxWidth = ''; this.el.style.maxHeight = ''; this.el.style.width = 'auto'; this.el.classList.add('w-auto', 'inline-block', 'py-0', 'my-0'); this.el.style.overflow = 'clip'; this.el.style.height = `${this.lineHeight}px`; this.el.style.lineHeight = `${this.lineHeight}px`; this.el.style.minHeight = `${this.lineHeight}px`; this.el.style.maxHeight = `${this.lineHeight}px`; if (this.isSafari()) { this.el.style.marginBottom = '1px'; this.el.style.verticalAlign = 'top'; } this.el.style.resize = 'none'; this.removeResizeIndicator(); if (this.resizeObserver) { this.resizeObserver.unobserve(this.el); } } addResizeIndicator() { try { this.removeResizeIndicator(); const resizeIndicator = document.createElement('div'); resizeIndicator.className = `absolute bottom-0 right-0 w-4 h-4 opacity-60 pointer-events-none resize-indicator cursor-nwse-resize text-slate-400 z-${Z_INDICES.RESIZE_HANDLE}`; resizeIndicator.innerHTML = RESIZE_INDICATOR_SVG; resizeIndicator.setAttribute('aria-hidden', 'true'); this.el.appendChild(resizeIndicator); } catch (error) { console.error('Failed to add resize indicator:', error); } } removeResizeIndicator() { const resizeIndicators = this.el.querySelectorAll('.resize-indicator'); resizeIndicators.forEach(indicator => indicator.remove()); } performToggle(newOpen) { this.isToggling = true; const details = this.el.querySelector('details'); this.open = newOpen; if (details) { details.open = newOpen; } this.collapsibleToggle.emit(this.open); this.updateAppearance(); if (this.open && this.isSafari()) { setTimeout(() => this.recalculateContentDimensions(), 50); } setTimeout(() => { if (details) details.open = this.open; this.isToggling = false; }, 50); } getHostClasses() { const baseClasses = ['relative', 'font-sans', 'leading-normal']; if (this.emphasize) { baseClasses.push('box-border', 'border', 'rounded-md', 'shadow-xs'); if (this.isDarkMode) { baseClasses.push('border-gray-600'); } else { baseClasses.push('border-gray-300'); } } if (this.open) { baseClasses.push('mb-2', 'max-w-full', 'text-xs', 'block'); } else { baseClasses.push(this.initialWidth === '100%' ? 'max-w-full' : 'max-w-md'); baseClasses.push('my-0', 'text-sm', 'inline-block'); } if (this.isDarkMode) { baseClasses.push('text-white'); } return baseClasses.join(' '); } getDetailsClasses() { const baseClasses = ['group', 'w-full', 'font-sans', 'transition-all', 'duration-200', 'ease-in-out', 'flex', 'flex-col']; if (this.open) ; else { baseClasses.push('text-clip', 'overflow-hidden'); } if (this.isDarkMode) { baseClasses.push('bg-gray-800', 'text-white'); } return baseClasses.join(' '); } getSummaryClasses() { const baseClasses = [ 'font-bold', 'font-mono', 'cursor-pointer', 'list-none', 'flex', 'items-center', 'focus:outline-hidden', 'focus-visible:ring-2', 'focus-visible:ring-blue-400', 'focus-visible:ring-offset-1', 'rounded-lg', 'marker:hidden', '[&::-webkit-details-marker]:hidden', 'select-none', 'py-0', 'min-w-1/10', ]; if (this.open) { baseClasses.push('sticky', 'top-0', `z-${Z_INDICES.STICKY_ELEMENTS}`, 'overflow-x-auto', 'backdrop-blur-xs'); if (this.isDarkMode) { baseClasses.push('bg-gray-800'); if (this.emphasize) { baseClasses.push('border-b', 'box-border', 'border-gray-700'); } } else { baseClasses.push('bg-white', 'text-ellipsis'); if (this.emphasize) { baseClasses.push('border-b', 'box-border', 'border-gray-100'); } } } else { baseClasses.push('whitespace-nowrap', 'overflow-hidden', 'text-ellipsis', 'truncate', 'max-w-full'); } baseClasses.push(`h-[${this.lineHeight}px]`); return baseClasses.join(' '); } getContentClasses() { const baseClasses = ['grow', 'flex', 'flex-col', 'min-h-0']; if (this.open) ; else { baseClasses.push('overflow-hidden', 'p-0'); } if (this.isDarkMode) { baseClasses.push('bg-gray-800', 'text-white'); } return baseClasses.join(' '); } getFooterClasses() { const baseClasses = ['flex', 'flex-col', 'w-full', 'mt-auto', 'sticky', 'bottom-0', 'left-0', 'right-0', 'border-t', `z-${Z_INDICES.FOOTER_CONTENT}`, 'backdrop-blur-xs']; if (this.isDarkMode) { baseClasses.push('bg-gray-800', 'border-gray-700'); } else { baseClasses.push('bg-white', 'border-gray-200'); } return baseClasses.join(' '); } getFooterActionsClasses() { const baseClasses = ['flex', 'items-center', 'justify-between', 'gap-2', 'p-1', 'min-h-12', 'shrink-0', 'overflow-x-auto']; if (this.isDarkMode) { baseClasses.push('bg-gray-800'); } else { baseClasses.push('bg-white'); } return baseClasses.join(' '); } get el() { return getElement(this); } static get watchers() { return { "open": [{ "watchOpen": 0 }], "darkMode": [{ "watchDarkMode": 0 }] }; } }; PidCollapsible.style = collapsibleCss(); function getEffectiveRenderers(orderedRendererKeys) { if (!orderedRendererKeys || orderedRendererKeys.length === 0) { return renderers; } const result = []; for (const key of orderedRendererKeys) { const found = renderers.find(r => r.key === key); if (found) { result.push(found); } else { console.warn(`Parser: Unknown renderer key "${key}" in ordered renderer list, skipping.`); } } return result; } class Parser { static async getEstimatedPriority(value) { for (let i = 0; i < renderers.length; i++) { const obj = new renderers[i].constructor(value); const quickResult = obj.quickCheck(); if (quickResult === true) { return i; } if (quickResult === undefined) { const hasMeaningful = await obj.hasMeaningfulInformation(); if (hasMeaningful) { return i; } } } return 0; } static getBestFitQuick(value, orderedRendererKeys) { const effective = getEffectiveRenderers(orderedRendererKeys); if (orderedRendererKeys && orderedRendererKeys.length > 0) { for (const entry of effective) { const obj = new entry.constructor(value); if (obj.quickCheck()) { return entry.key; } } return null; } let bestKey = null; for (let i = effective.length - 1; i >= 0; i--) { const entry = effective[i]; const obj = new entry.constructor(value); if (obj.quickCheck() && entry.key !== 'FallbackType') { bestKey = entry.key; } } return bestKey; } static async getBestFit(value, settings, orderedRendererKeys, fallbackToAll = true) { const effective = getEffectiveRenderers(orderedRendererKeys); const hasOrderedList = orderedRendererKeys && orderedRendererKeys.length > 0; if (hasOrderedList) { for (const entry of effective) { const obj = new entry.constructor(value); const quickResult = obj.quickCheck(); if (quickResult === true) { Parser.applySettings(obj, settings); await obj.init(); return obj; } if (quickResult === undefined || !quickResult) { if (await obj.hasMeaningfulInformation()) { Parser.applySettings(obj, settings); await obj.init(); return obj; } } } if (fallbackToAll) { return Parser.getBestFit(value, settings, undefined, false); } return null; } const candidates = []; for (let i = 0; i < effective.length; i++) { const obj = new effective[i].constructor(value); const quickResult = obj.quickCheck(); if (quickResult === true || quickResult === undefined) { candidates.push({ index: i, obj, uncertain: quickResult === undefined }); } } if (candidates.length === 0) { return null; } const results = await Promise.all(candidates.map(async ({ index, obj }) => { const meaningful = await obj.hasMeaningfulInformation(); const relevance = this.calculateRelevance(obj, index); return { obj, meaningful, relevance, index }; })); const validResults = results.filter(r => r.meaningful); if (validResults.length === 0) { return null; } validResults.sort((a, b) => { if (b.relevance !== a.relevance) { return b.relevance - a.relevance; } return a.index - b.index; }); const best = validResults[0].obj; Parser.applySettings(best, settings); await best.init(); return best; } static calculateRelevance(obj, index) { const priorityWeight = 1000; const itemWeight = 1; const actionWeight = 1; const priorityScore = (renderers.length - index) * priorityWeight; const itemScore = obj.items.length * itemWeight; const actionScore = obj.actions.length * actionWeight; return priorityScore + itemScore + actionScore; } static applySettings(obj, settings) { var _a; try { const settingsKey = obj.getSettingsKey(); const settingsValues = (_a = settings.find(v => v.type === settingsKey)) === null || _a === void 0 ? void 0 : _a.values; if (settingsValues) obj.settings = settingsValues; } catch (e) { console.warn('Error while adding settings to object:', e); } } } const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c); let idbProxyableTypes; let cursorAdvanceMethods; // This is a function to prevent it throwing up in node environments. function getIdbProxyableTypes() { return (idbProxyableTypes || (idbProxyableTypes = [ IDBDatabase, IDBObjectStore, IDBIndex, IDBCursor, IDBTransaction, ])); } // This is a function to prevent it throwing up in node environments. function getCursorAdvanceMethods() { return (cursorAdvanceMethods || (cursorAdvanceMethods = [ IDBCursor.prototype.advance, IDBCursor.prototype.continue, IDBCursor.prototype.continuePrimaryKey, ])); } const transactionDoneMap = new WeakMap(); const transformCache = new WeakMap(); const reverseTransformCache = new WeakMap(); function promisifyRequest(request) { const promise = new Promise((resolve, reject) => { const unlisten = () => { request.removeEventListener('success', success); request.removeEventListener('error', error); }; const success = () => { resolve(wrap(request.result)); unlisten(); }; const error = () => { reject(request.error); unlisten(); }; request.addEventListener('success', success); request.addEventListener('error', error); }); // This mapping exists in reverseTransformCache but doesn't doesn't exist in transformCache. This // is because we create many promises from a single IDBRequest. reverseTransformCache.set(promise, request); return promise; } function cacheDonePromiseForTransaction(tx) { // Early bail if we've already created a done promise for this transaction. if (transactionDoneMap.has(tx)) return; const done = new Promise((resolve, reject) => { const unlisten = () => { tx.removeEventListener('complete', complete); tx.removeEventListener('error', error); tx.removeEventListener('abort', error); }; const complete = () => { resolve(); unlisten(); }; const error = () => { reject(tx.error || new DOMException('AbortError', 'AbortError')); unlisten(); }; tx.addEventListener('complete', complete); tx.addEventListener('error', error); tx.addEventListener('abort', error); }); // Cache it for later retrieval. transactionDoneMap.set(tx, done); } let idbProxyTraps = { get(target, prop, receiver) { if (target instanceof IDBTransaction) { // Special handling for transaction.done. if (prop === 'done') return transactionDoneMap.get(target); // Make tx.store return the only store in the transaction, or undefined if there are many. if (prop === 'store') { return receiver.objectStoreNames[1] ? undefined : receiver.objectStore(receiver.objectStoreNames[0]); } } // Else transform whatever we get back. return wrap(target[prop]); }, set(target, prop, value) { target[prop] = value; return true; }, has(target, prop) { if (target instanceof IDBTransaction && (prop === 'done' || prop === 'store')) { return true; } return prop in target; }, }; function replaceTraps(callback) { idbProxyTraps = callback(idbProxyTraps); } function wrapFunction(func) { // Due to expected object equality (which is enforced by the caching in `wrap`), we // only create one new func per func. // Cursor methods are special, as the behaviour is a little more different to standard IDB. In // IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense // with real promises, so each advance methods returns a new promise for the cursor object, or // undefined if the end of the cursor has been reached. if (getCursorAdvanceMethods().includes(func)) { return function (...args) { // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use // the original object. func.apply(unwrap(this), args); return wrap(this.request); }; } return function (...args) { // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use // the original object. return wrap(func.apply(unwrap(this), args)); }; } function transformCachableValue(value) { if (typeof value === 'function') return wrapFunction(value); // This doesn't return, it just creates a 'done' promise for the transaction, // which is later returned for transaction.done (see idbObjectHandler). if (value instanceof IDBTransaction) cacheDonePromiseForTransaction(value); if (instanceOfAny(value, getIdbProxyableTypes())) return new Proxy(value, idbProxyTraps); // Return the same value back if we're not going to transform it. return value; } function wrap(value) { // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached. if (value instanceof IDBRequest) return promisifyRequest(value); // If we've already transformed this value before, reuse the transformed value. // This is faster, but it also provides object equality. if (transformCache.has(value)) return transformCache.get(value); const newValue = transformCachableValue(value); // Not all types are transformed. // These may be primitive types, so they can't be WeakMap keys. if (newValue !== value) { transformCache.set(value, newValue); reverseTransformCache.set(newValue, value); } return newValue; } const unwrap = (value) => reverseTransformCache.get(value); /** * Open a database. * * @param name Name of the database. * @param version Schema version. * @param callbacks Additional callbacks. */ function openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) { const request = indexedDB.open(name, version); const openPromise = wrap(request); if (upgrade) { request.addEventListener('upgradeneeded', (event) => { upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event); }); } if (blocked) { request.addEventListener('blocked', (event) => blocked( // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405 event.oldVersion, event.newVersion, event)); } openPromise .then((db) => { if (terminated) db.addEventListener('close', () => terminated()); if (blocking) { db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event)); } }) .catch(() => { }); return openPromise; } const readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count']; const writeMethods = ['put', 'add', 'delete', 'clear']; const cachedMethods = new Map(); function getMethod(target, prop) { if (!(target instanceof IDBDatabase && !(prop in target) && typeof prop === 'string')) { return; } if (cachedMethods.get(prop)) return cachedMethods.get(prop); const targetFuncName = prop.replace(/FromIndex$/, ''); const useIndex = prop !== targetFuncName; const isWrite = writeMethods.includes(targetFuncName); if ( // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge. !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) || !(isWrite || readMethods.includes(targetFuncName))) { return; } const method = async function (storeName, ...args) { // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :( const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly'); let target = tx.store; if (useIndex) target = target.index(args.shift()); // Must reject if op rejects. // If it's a write operation, must reject if tx.done rejects. // Must reject with op rejection first. // Must resolve with op value. // Must handle both promises (no unhandled rejections) return (await Promise.all([ target[targetFuncName](...args), isWrite && tx.done, ]))[0]; }; cachedMethods.set(prop, method); return method; } replaceTraps((old