diff --git a/static/css/views/photos.css b/static/css/views/photos.css index 33221c9a..43ab6418 100644 --- a/static/css/views/photos.css +++ b/static/css/views/photos.css @@ -113,6 +113,11 @@ border-color: var(--color-border-medium); } +.photo-tile:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + .photo-tile:hover img { transform: scale(1.03); } diff --git a/static/js/features/library/photos.js b/static/js/features/library/photos.js index 437de6ff..8a116a6d 100644 --- a/static/js/features/library/photos.js +++ b/static/js/features/library/photos.js @@ -56,6 +56,8 @@ const photosView = { _resizeHandler: null, /** @type {number} */ _resizeTimer: 0, + /** @type {string|null} Anchor id for shift-range selection */ + _selectAnchorId: null, PAGE_SIZE: 200, @@ -202,6 +204,7 @@ const photosView = { // real elements so we keep references for the observer. this._container.innerHTML = this._renderToolbar(); this._container.onclick = (e) => this._handleClick(e); + this._container.onkeydown = (e) => this._handleKeydown(e); const groups = this._groupItems(this.items); for (const [label, files] of groups) { @@ -436,7 +439,7 @@ const photosView = { const selected = this.selected.has(file.id) ? ' selected' : ''; const cachedThumb = isVideo && this._videoThumbCache.has(file.id) ? this._videoThumbCache.get(file.id) : null; const thumbUrl = cachedThumb || `/api/files/${file.id}/thumbnail/preview`; - let h = `