diff --git a/ui/src/css/base.css b/ui/src/css/base.css index 95f01d184..3154b1eaf 100644 --- a/ui/src/css/base.css +++ b/ui/src/css/base.css @@ -98,7 +98,7 @@ summary::marker { opacity: 0; transition-duration: 0.15s; transition-property: opacity; - z-index: 1; + z-index: 5; } .background-dimmer.has-open-modal { opacity: 100; diff --git a/ui/src/css/components/annotation.css b/ui/src/css/components/annotation.css index 84e80836e..f48d9811d 100644 --- a/ui/src/css/components/annotation.css +++ b/ui/src/css/components/annotation.css @@ -165,7 +165,7 @@ save-dialog { transform: scale(0.95); transition-duration: 0.25s; transition-property: transform, opacity; - z-index: 4; + z-index: 100; } save-dialog.is-open { opacity: 1; @@ -192,7 +192,7 @@ modify-track-dialog { transform: scale(0.95); transition-duration: 0.25s; transition-property: transform, opacity; - z-index: 4; + z-index: 100; } modify-track-dialog.is-open { opacity: 1; @@ -324,7 +324,6 @@ favorite-button { .annotation__version-list { overflow-y: auto; - max-height: 240px; } .annotation__announcement-list { diff --git a/ui/src/css/components/nav.css b/ui/src/css/components/nav.css index 5d9670fe6..877afd114 100644 --- a/ui/src/css/components/nav.css +++ b/ui/src/css/components/nav.css @@ -9,7 +9,7 @@ transition-duration: 0.25s; transition-property: transform; width: 420px; - z-index: 3; + z-index: 100; } .nav.is-open { transform: translateX(100%); diff --git a/ui/src/css/components/table.css b/ui/src/css/components/table.css index 00576c14a..02691d222 100644 --- a/ui/src/css/components/table.css +++ b/ui/src/css/components/table.css @@ -276,3 +276,29 @@ border: 1px solid #a2afcd; overflow-wrap: break-word; } + +.version-table { + table-layout: fixed; + background-color: var(--color-charcoal--medium-dark); +} +.version-table th { + padding-top: 12px; + padding-bottom: 12px; + background-color: var(--color-charcoal--light70); +} +.version-table thead { + border: 1px solid var(--color-charcoal--light70); +} +.version-table tbody tr { + border: 1px solid var(--color-charcoal--light70); +} +.version-table tbody tr.selected { + background-color: var(--color-charcoal--light70); +} +.version-table tbody tr:hover { + background-color: var(--color-charcoal--light70); +} +.version-table td { + height: 70px; + vertical-align: middle; +} diff --git a/ui/src/css/variables.css b/ui/src/css/variables.css index f1a2cf429..94bed6563 100644 --- a/ui/src/css/variables.css +++ b/ui/src/css/variables.css @@ -3,11 +3,11 @@ **/ :host, :root { - --color-purple: #696cff; - --color-purple50: rgba(var(--color-purple), 0.5); + --color-purple: #696cff; /* rgb(105, 108, 255) */ + --color-purple50: rgba(105, 108, 255, 0.5); - --color-blue: #1b9ffb; - --color-blue50: rgba(var(--color-blue), 0.5); + --color-blue: #1b9ffb; /* rgb(27, 159, 251) */ + --color-blue50: rgba(27, 159, 251, 0.5); --color-green: #85d81d; --color-parakeet-green: #03c04a; @@ -22,30 +22,27 @@ --color-teal: rgb(64, 224, 208); --color-teal--50: rgba(var(--color-teal), 0.5); - --color-blue-iris: #4a4eae; - --color-blue-iris50: rgba(var(--color-blue-iris), 0.5); + --color-blue-iris: #4a4eae; /* rgb(74, 78, 174) */ + --color-blue-iris50: rgba(74, 78, 174, 0.5); - --color-white: #ffffff; - --color-white--75: rgba(var(--color-white), 0.75); - --color-white--50: rgba(var(--color-white), 0.5); - --color-white--25: rgba(var(--color-white), 0.25); + --color-white: #ffffff; /* rgb(255, 255, 255) */ + --color-white--75: rgba(255, 255, 255, 0.75); + --color-white--50: rgba(255, 255, 255, 0.5); + --color-white--25: rgba(255, 255, 255, 0.25); --color-gray--light: #a2afcd; --color-gray--medium: #72809d; - --color-gray--dark: #6d7a96; - --color-gray--dark50: rgba(var(--color-gray--dark), 0.5); - - --color-charcoal--light: #262e3d; - --color-charcoal--light70: rgba(var(--color-charcoal--light), 0.7); - --color-charcoal--medium: #151b28; - --color-charcoal--medium70: rgba(var(--color-charcoal--medium), 0.7); - --color-charcoal--medium-dark: #0d1320; - --color-charcoal--medium-dark70: rgba( - var(--color-charcoal--medium-dark), - 0.7 - ); - --color-charcoal--dark: #00070d; - --color-charcoal--dark70: rgba(var(--color-charcoal--dark), 0.7); + --color-gray--dark: #6d7a96; /* rgb(109, 122, 150) */ + --color-gray--dark50: rgba(109, 122, 150, 0.5); + + --color-charcoal--light: #262e3d; /* rgb(38, 46, 61) */ + --color-charcoal--light70: rgba(38, 46, 61, 0.7); + --color-charcoal--medium: #151b28; /* rgb(21, 27, 40) */ + --color-charcoal--medium70: rgba(21, 27, 40, 0.7); + --color-charcoal--medium-dark: #0d1320; /* rgb(13, 19, 32) */ + --color-charcoal--medium-dark70: rgba(13, 19, 32, 0.7); + --color-charcoal--dark: #00070d; /* rgb(0, 7, 13) */ + --color-charcoal--dark70: rgba(0, 7, 13, 0.7); --color-charcoal--brown: #2e2314; --color-charcoal--red: #281d26; --color-steel--dark: #3d4147; diff --git a/ui/src/js/analytics/export/export-page.js b/ui/src/js/analytics/export/export-page.js index 6214e2b87..dc3d39a03 100644 --- a/ui/src/js/analytics/export/export-page.js +++ b/ui/src/js/analytics/export/export-page.js @@ -1719,13 +1719,14 @@ class MainPage extends TatorPage { this._selectedMediaIds = []; var urlParams = new URLSearchParams(window.location.search); - var sectionIds = urlParams.get("section"); - if (sectionIds) { - this._selectedSectionIds = sectionIds.split(",").map(Number); + + var mediaIds = urlParams.get("media_id"); + if (mediaIds) { + this._selectedMediaIds = mediaIds.split(",").map(Number); } else { - var mediaIds = urlParams.get("media_id"); - if (mediaIds) { - this._selectedMediaIds = mediaIds.split(",").map(Number); + var sectionIds = urlParams.get("section"); + if (sectionIds) { + this._selectedSectionIds = sectionIds.split(",").map(Number); } } diff --git a/ui/src/js/annotation/annotation-page.js b/ui/src/js/annotation/annotation-page.js index 66166bfda..17aaa3248 100644 --- a/ui/src/js/annotation/annotation-page.js +++ b/ui/src/js/annotation/annotation-page.js @@ -19,11 +19,6 @@ export class AnnotationPage extends TatorPage { this._shadow.appendChild(this._loading); this._versionLookup = {}; - this._modalDimmer = document.createElement("div"); - this._modalDimmer.setAttribute("class", "background-dimmer"); - this._modalDimmer.style.zIndex = 101; - this._shadow.appendChild(this._modalDimmer); - document.body.setAttribute("class", "no-padding-bottom"); const header = document.createElement("div"); @@ -822,14 +817,12 @@ export class AnnotationPage extends TatorPage { this._versionDialog.addEventListener("versionSelect", (evt) => { this._loading.style.display = "block"; - this._modalDimmer.classList.add("has-open-modal"); this._data .setVersion(evt.detail.version, evt.detail.viewables) .then(() => { this._settings.setAttribute("version", evt.detail.version.id); this._canvas.refresh(); this._loading.style.display = "none"; - this._modalDimmer.classList.remove("has-open-modal"); }); this._browser.version = evt.detail.version; this._versionButton.text = evt.detail.version.name; @@ -1028,17 +1021,17 @@ export class AnnotationPage extends TatorPage { } } - // Finde the index of the default version. - let selected_version_idx = 0; + // Find the index of the default version. + let selected_version_id = 0; for (const [idx, version] of versions.entries()) { if (version.id == default_version) { this._version = this._versionLookup[default_version]; - selected_version_idx = idx; + selected_version_id = idx; } } // Initialize version dialog. - this._versionDialog.init(versions, selected_version_idx); + this._versionDialog.init(versions, selected_version_id); if (versions.length == 0) { this._versionButton.style.display = "none"; } else { diff --git a/ui/src/js/annotation/version-dialog.js b/ui/src/js/annotation/version-dialog.js index 237baac0b..15e5dbc2b 100644 --- a/ui/src/js/annotation/version-dialog.js +++ b/ui/src/js/annotation/version-dialog.js @@ -7,18 +7,34 @@ export class VersionDialog extends ModalDialog { // Rework the styles this._div.setAttribute("class", "modal-wrap modal-wide d-flex"); this._modal.setAttribute("class", "modal py-6 px-6 rounded-2"); - this._header.setAttribute("class", "px-3 py-3"); - this._titleDiv.setAttribute("class", "h2"); - this._title.nodeValue = "Versions"; + this._header.setAttribute("class", ""); this._main.remove(); this._footer.remove(); + const searchDiv = document.createElement("div"); + searchDiv.setAttribute( + "class", + "d-flex flex-row flex-grow flex-justify-between py-3 px-2 rounded-2 mt-3" + ); + this._header.appendChild(searchDiv); + + const dialogHeader = document.createElement("div"); + dialogHeader.setAttribute("class", "h1 d-flex flex-items-center"); + dialogHeader.textContent = "Select Version"; + searchDiv.appendChild(dialogHeader); + + this._searchInput = document.createElement("input"); + this._searchInput.setAttribute("class", "form-control input-sm col-6 f2"); + this._searchInput.setAttribute("type", "text"); + this._searchInput.setAttribute("placeholder", "Search version by name..."); + searchDiv.appendChild(this._searchInput); + const tableDiv = document.createElement("div"); tableDiv.setAttribute("class", "py-4 annotation__version-list"); this._header.appendChild(tableDiv); this._table = document.createElement("table"); - this._table.setAttribute("class", "table col-12"); + this._table.setAttribute("class", "version-table col-12 rounded-2"); tableDiv.appendChild(this._table); const thead = document.createElement("thead"); @@ -32,15 +48,18 @@ export class VersionDialog extends ModalDialog { thead.appendChild(tr); const thName = document.createElement("th"); - thName.setAttribute("class", "py-3 col-9"); + thName.setAttribute("class", "px-3"); thName.textContent = "Version"; tr.appendChild(thName); const thView = document.createElement("th"); + thView.style.width = "80px"; thView.textContent = "Viewable"; tr.appendChild(thView); const thSelect = document.createElement("th"); + thSelect.setAttribute("class", "text-center"); + thSelect.style.width = "120px"; thSelect.textContent = "Editable"; tr.appendChild(thSelect); @@ -49,28 +68,62 @@ export class VersionDialog extends ModalDialog { spanView.textContent = "View version"; thView.appendChild(spanView); + this._tableBody = document.createElement("tbody"); + this._table.appendChild(this._tableBody); + + this._searchInput.addEventListener("input", () => { + this.filterVersionTable(); + }); + } + + init(versions, selectedId) { + console.log(`Selecting version ID: ${selectedId}`); + + this._tableRows = []; this._buttons = []; this._viewables = []; - } + this._tableOrder = []; // Order of version IDs in the table - init(versions, selected_idx) { + this._tableBody.innerHTML = ""; this._versions = versions; + this._versionMap = {}; + for (const version of versions) { + this._versionMap[version.id] = version; + } + + // Organize the version list by name + // Put the Baseline version first + this._versions.sort((a, b) => { + if (a.name == "Baseline") { + return -1; + } + if (b.name == "Baseline") { + return 1; + } + return a.name.localeCompare(b.name); + }); + // Initializes the dialog. // versions: returned object from Version endpoint. for (const version of versions) { - const tbody = document.createElement("tbody"); - this._table.appendChild(tbody); - const tr = document.createElement("tr"); - tbody.appendChild(tr); + this._tableBody.appendChild(tr); const tdName = document.createElement("td"); - tdName.setAttribute("title", version.description); - tdName.textContent = version.name; + tdName.setAttribute("class", "px-3"); + + var versionHTML = ``; + versionHTML += `
${version.name}
`; + if (version.description != "" && version.description != null) { + versionHTML += `
${version.description}
`; + } + versionHTML += `
(ID: ${version.id})
`; + tdName.innerHTML = versionHTML; tr.appendChild(tdName); const tdViewable = document.createElement("td"); tdViewable.setAttribute("class", "px-2"); + tdViewable.style.width = "80px"; tr.appendChild(tdViewable); let viewable = document.createElement("bool-input"); viewable.setValue(false); @@ -78,10 +131,12 @@ export class VersionDialog extends ModalDialog { "change", this._handleViewableChange.bind(this) ); + viewable._legend.style.display = "none"; tdViewable.appendChild(viewable); const tdSelect = document.createElement("td"); - tdSelect.setAttribute("class", "px-2"); + tdSelect.setAttribute("class", "px-3"); + tdSelect.style.width = "160px"; tr.appendChild(tdSelect); const select = document.createElement("version-select"); @@ -90,18 +145,69 @@ export class VersionDialog extends ModalDialog { tdSelect.appendChild(select); this._buttons.push(select); this._viewables.push(viewable); + this._tableOrder.push(version.id); + this._tableRows.push(tr); } - this._selected_idx = selected_idx; - this._buttons[selected_idx].select(true); - this._viewables[selected_idx].setValue(true); - this._viewables[selected_idx].setDisable(true); - this._updatedDependentLayers(selected_idx); + this._selected_idx = null; + for (let idx = 0; idx < this._tableOrder.length; idx++) { + if (this._tableOrder[idx] == selectedId) { + this._selected_idx = idx; + break; + } + } + + // Auto-select the baseline version if none is selected + if (this._selected_idx == null) { + for (let idx = 0; idx < this._tableOrder.length; idx++) { + let version = this._versionMap[this._tableOrder[idx]]; + if (version.name == "Baseline") { + this._selected_idx = idx; + this._tableRows[idx].classList.add("selected"); + this._tableRows[idx].scrollIntoView(); + break; + } + } + } + + this._buttons[this._selected_idx].select(true); + this._viewables[this._selected_idx].setValue(true); + this._viewables[this._selected_idx].setDisable(true); + this._updatedDependentLayers(this._selected_idx); + + this._searchInput.value = ""; + this.filterVersionTable(); + } + + filterVersionTable() { + const search = this._searchInput.value.toLowerCase(); + + for (let idx = 0; idx < this._tableOrder.length; idx++) { + var displayThisVersion = false; + if (this._selected_idx == idx) { + displayThisVersion = true; + } + if (search.trim().length == 0 || search == null) { + displayThisVersion = true; + } + + const version = this._versionMap[this._tableOrder[idx]]; + const tr = this._tableRows[idx]; + if ( + version.name.toLowerCase().indexOf(search) >= 0 || + displayThisVersion + ) { + tr.style.display = ""; + } else { + tr.style.display = "none"; + } + } } // A selected layer might have dependent layers that come for the ride. _updatedDependentLayers(selected_idx) { - const selected_version = this._versions[selected_idx]; + const selected_version = this._versionMap[this._tableOrder[selected_idx]]; + if (typeof selected_version === "undefined") { return; } @@ -131,10 +237,14 @@ export class VersionDialog extends ModalDialog { viewables.push(version.id); } } + + const selected_version = + this._versionMap[this._tableOrder[this._selected_idx]]; + this.dispatchEvent( new CustomEvent("versionSelect", { detail: { - version: this._versions[this._selected_idx], + version: selected_version, viewables: viewables, }, }) @@ -146,15 +256,18 @@ export class VersionDialog extends ModalDialog { for (let idx = 0; idx < this._buttons.length; idx++) { const button = this._buttons[idx]; const viewable = this._viewables[idx]; - const sameVersion = button._version.id == id; + const sameVersion = this._tableOrder[idx] == id; + const tableRow = this._tableRows[idx]; if (!sameVersion) { button.deselect(); viewable.setValue(false); viewable.setDisable(false); + tableRow.classList.remove("selected"); } else { button.select(true); viewable.setValue(true); viewable.setDisable(true); + tableRow.classList.add("selected"); selected_idx = idx; } }