'use strict'; (function exposeSidebarView(root, factory) { const api = factory(); if (typeof module !== 'undefined' && module.exports) module.exports = api; if (root) root.AptSidebarView = api; }(typeof window !== 'undefined' ? window : undefined, function sidebarViewFactory() { const ROW_HEIGHT = 30; function stableDomId(key) { return `apt-tree-row-${Array.from(String(key || '')).map((character) => character.codePointAt(0).toString(16)).join('-')}`; } function describeVirtualRows(snapshot, options = {}) { const activeIds = options.activeDeviceIds instanceof Set ? options.activeDeviceIds : new Set(); const model = options.model; const isOnline = typeof options.isOnline === 'function' ? options.isOnline : () => false; const windowRows = snapshot && snapshot.window && Array.isArray(snapshot.window.rows) ? snapshot.window.rows : []; return windowRows.map((row, windowIndex) => { const node = model && model.rowByKey ? model.rowByKey.get(row.key) : null; const device = node && node.kind === 'camera' ? node.source : null; const deviceId = device ? device.id : row.id; const classes = ['tree-row', `tree-${row.kind}`]; if (row.ariaSelected) classes.push('selected'); if (snapshot.state && snapshot.state.activeKey === row.key) classes.push('active'); if (row.pendingDeletion) classes.push('pending-deletion'); if (row.hierarchyStatus && !['assigned', 'inferred-site'].includes(row.hierarchyStatus)) classes.push('hierarchy-warning'); if (row.kind === 'camera') { classes.push(isOnline(device || row) ? 'online' : 'offline'); if (activeIds.has(String(deviceId || '').toLowerCase())) classes.push('proxy-active'); } return Object.freeze({ key: row.key, kind: row.kind, name: row.name, device, deviceId, role: 'treeitem', domId: stableDomId(row.key), top: windowIndex * ROW_HEIGHT, classes: classes.join(' '), pendingDeletion: row.pendingDeletion === true, hierarchyStatus: row.hierarchyStatus, address: device && device.address ? device.address : '', aria: { level: row.ariaLevel, expanded: row.ariaExpanded, selected: row.ariaSelected, posinset: row.ariaPosInSet, setsize: row.ariaSetSize, }, }); }); } function setAria(element, name, value) { if (value === undefined || value === null) element.removeAttribute(`aria-${name}`); else element.setAttribute(`aria-${name}`, String(value)); } function createSidebarView(options = {}) { const documentRef = options.document; const tree = options.tree; const spacer = options.spacer; const windowElement = options.windowElement; if (!documentRef || !tree || !spacer || !windowElement) { throw new TypeError('Sidebar view requires document, tree, spacer, and windowElement.'); } function createRow(descriptor) { const element = documentRef.createElement('div'); element.id = descriptor.domId; element.className = descriptor.classes; element.dataset.rowKey = descriptor.key; element.setAttribute('role', descriptor.role); element.style.top = `${descriptor.top}px`; setAria(element, 'level', descriptor.aria.level); setAria(element, 'expanded', descriptor.aria.expanded); setAria(element, 'selected', descriptor.aria.selected); setAria(element, 'posinset', descriptor.aria.posinset); setAria(element, 'setsize', descriptor.aria.setsize); const disclosure = documentRef.createElement('span'); disclosure.className = 'tree-disclosure'; disclosure.setAttribute('aria-hidden', 'true'); disclosure.textContent = descriptor.kind === 'camera' ? '' : (descriptor.aria.expanded ? '\u25bc' : '\u25b6'); const label = documentRef.createElement('span'); label.className = 'tree-label'; label.textContent = descriptor.name; element.append(disclosure, label); if (descriptor.address) { const address = documentRef.createElement('span'); address.className = 'tree-address'; address.textContent = descriptor.address; element.appendChild(address); } if (descriptor.pendingDeletion) { const pending = documentRef.createElement('span'); pending.className = 'tree-badge pending'; pending.textContent = 'Pending deletion'; element.appendChild(pending); } if (descriptor.hierarchyStatus && !['assigned', 'inferred-site'].includes(descriptor.hierarchyStatus)) { const warning = documentRef.createElement('span'); warning.className = 'tree-warning'; warning.title = `Hierarchy: ${descriptor.hierarchyStatus}`; warning.setAttribute('aria-label', `Hierarchy warning: ${descriptor.hierarchyStatus}`); warning.textContent = '!'; element.appendChild(warning); } return element; } function commit(snapshot) { const descriptors = describeVirtualRows(snapshot, { model: options.model, activeDeviceIds: typeof options.getActiveDeviceIds === 'function' ? options.getActiveDeviceIds() : new Set(), isOnline: options.isOnline, }); spacer.style.height = `${snapshot.window.totalHeight}px`; windowElement.style.transform = `translateY(${snapshot.window.offsetTop}px)`; windowElement.style.height = `${descriptors.length * ROW_HEIGHT}px`; const fragment = documentRef.createDocumentFragment(); for (const descriptor of descriptors) fragment.appendChild(createRow(descriptor)); windowElement.replaceChildren(fragment); const active = descriptors.find((row) => snapshot.state.activeKey === row.key); if (active) tree.setAttribute('aria-activedescendant', active.domId); else tree.removeAttribute('aria-activedescendant'); tree.classList.toggle('has-hidden-selection', snapshot.rows.hiddenSelected === true); if (typeof options.onCommit === 'function') options.onCommit(snapshot, descriptors); return descriptors; } return Object.freeze({ commit }); } return Object.freeze({ ROW_HEIGHT, stableDomId, describeVirtualRows, createSidebarView }); }));