diff --git a/assets/css/plugin-check-admin.css b/assets/css/plugin-check-admin.css index 115a348f7..2e0e3ec09 100644 --- a/assets/css/plugin-check-admin.css +++ b/assets/css/plugin-check-admin.css @@ -130,14 +130,125 @@ padding: 0 14px 14px; } -#plugin-check__results .plugin-check__false-positive-results h4:first-child { - margin-top: 12px; -} - .plugin-check__false-positive-results table.plugin-check__results-table { margin-bottom: 1em; } +/* Expand/collapse: animation + layout */ +#plugin-check__results, +.plugin-check__false-positive-results { + interpolate-size: allow-keywords; +} + +.plugin-check__results-toolbar { + margin-top: 8px; + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.plugin-check__results-toolbar.is-hidden { + display: none; +} + +.plugin-check__toolbar-button { + display: inline-flex; + align-items: center; + gap: 6px; + border-color: #2271b1; + color: #2271b1; + background: #fff; +} + +.wp-core-ui .button.plugin-check__toolbar-button .dashicons { + width: 18px; + height: 18px; + font-size: 18px; + line-height: 0.9; + vertical-align: middle; +} + +.plugin-check__file-results { + margin-bottom: 12px; + background: #fff; + border: 1px solid #c3c4c7; +} + +.plugin-check__file-results-header { + border-left: 4px solid #2271b1; + border-bottom: 1px solid #c3c4c7; +} + +.plugin-check__file-results-toggle { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + width: 100%; + margin: 0; + padding: 12px 14px; + border: 0; + background: transparent; + color: #1d2327; + font-size: 14px; + font-weight: 600; + text-align: left; + cursor: pointer; +} + +.plugin-check__file-results-leading { + display: inline-flex; + align-items: center; + gap: 8px; + min-width: 0; +} + +.plugin-check__file-results-toggle .dashicons { + flex-shrink: 0; + width: 20px; + height: 20px; + font-size: 20px; +} + +.plugin-check__file-results-file-icon, +.plugin-check__file-results-toggle-icon { + color: #2271b1; +} + +.plugin-check__file-results-toggle-icon { + transition: transform 300ms ease; +} + +.plugin-check__file-results:not(:has(.plugin-check__file-results-panel.open)) + .plugin-check__file-results-toggle-icon { + transform: rotate(-90deg); +} + +.plugin-check__file-results-label { + overflow-wrap: anywhere; +} + +.plugin-check__file-results-panel { + height: 0; + overflow: hidden; + transition: height 300ms ease; +} + +.plugin-check__file-results-panel.open { + height: auto; +} + +.plugin-check__file-results-panel .plugin-check__results-table { + margin: 0; + border: 0; +} + +@supports not (interpolate-size: allow-keywords) { + .plugin-check__file-results-panel { + transition: none; + } +} + .plugin-check__options { display: flex; } diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js index 22227cc4e..f9f1c7948 100644 --- a/assets/js/plugin-check-admin.js +++ b/assets/js/plugin-check-admin.js @@ -4,6 +4,15 @@ const exportContainer = document.getElementById( 'plugin-check__export-controls' ); + const resultsToolbar = document.getElementById( + 'plugin-check__results-toolbar' + ); + const expandAllButton = document.getElementById( + 'plugin-check__expand-all' + ); + const collapseAllButton = document.getElementById( + 'plugin-check__collapse-all' + ); const spinner = document.getElementById( 'plugin-check__spinner' ); const pluginsList = document.getElementById( 'plugin-check__plugins-dropdown' @@ -32,8 +41,76 @@ let falsePositiveResults = createEmptyAggregatedResults(); let checksCompleted = false; exportContainer.classList.add( 'is-hidden' ); + if ( resultsToolbar ) { + resultsToolbar.classList.add( 'is-hidden' ); + } exportContainer.addEventListener( 'click', onExportContainerClick ); + if ( expandAllButton && collapseAllButton ) { + expandAllButton.addEventListener( 'click', () => { + setAllFileResultsExpanded( true ); + } ); + collapseAllButton.addEventListener( 'click', () => { + setAllFileResultsExpanded( false ); + } ); + resultsContainer.addEventListener( 'click', onFileResultsToggleClick ); + } + + /** + * Sets expanded/collapsed state for one file results section. + * + * @param {HTMLElement} section File results wrapper. + * @param {boolean} expanded Whether the section is expanded. + */ + function setFileResultsExpanded( section, expanded ) { + const toggle = section.querySelector( + '.plugin-check__file-results-toggle' + ); + const panel = section.querySelector( '.plugin-check__file-results-panel' ); + + if ( toggle ) { + toggle.setAttribute( 'aria-expanded', expanded ? 'true' : 'false' ); + } + if ( panel ) { + panel.classList.toggle( 'open', expanded ); + } + } + + /** + * Expands or collapses every file results section. + * + * @param {boolean} expanded Whether sections should be expanded. + */ + function setAllFileResultsExpanded( expanded ) { + resultsContainer + .querySelectorAll( '.plugin-check__file-results' ) + .forEach( ( section ) => { + setFileResultsExpanded( section, expanded ); + } ); + } + + /** + * Toggles one file results section. + * + * @param {Event} event Click event. + */ + function onFileResultsToggleClick( event ) { + const toggle = event.target.closest( + '.plugin-check__file-results-toggle' + ); + if ( ! toggle ) { + return; + } + + const section = toggle.closest( '.plugin-check__file-results' ); + if ( ! section ) { + return; + } + + const isExpanded = toggle.getAttribute( 'aria-expanded' ) === 'true'; + setFileResultsExpanded( section, ! isExpanded ); + } + const includeExperimental = document.getElementById( 'plugin-check__include-experimental' ); @@ -122,6 +199,9 @@ resultsContainer.innerText = ''; exportContainer.innerHTML = ''; exportContainer.classList.add( 'is-hidden' ); + if ( resultsToolbar ) { + resultsToolbar.classList.add( 'is-hidden' ); + } resetAggregatedResults(); checksCompleted = false; } @@ -420,14 +500,29 @@ return ''; } + function updateResultsToolbarVisibility() { + if ( ! resultsToolbar ) { + return; + } + + const hasFileResults = resultsContainer.querySelector( + '.plugin-check__file-results' + ); + resultsToolbar.classList.toggle( 'is-hidden', ! hasFileResults ); + } + function renderExportButtons() { exportContainer.innerHTML = ''; if ( ! checksCompleted ) { exportContainer.classList.add( 'is-hidden' ); + if ( resultsToolbar ) { + resultsToolbar.classList.add( 'is-hidden' ); + } return; } exportContainer.classList.remove( 'is-hidden' ); + updateResultsToolbarVisibility(); const exportButtonConfigs = [ { diff --git a/includes/Admin/Admin_Page.php b/includes/Admin/Admin_Page.php index 5dd4881e8..1bdbc6497 100644 --- a/includes/Admin/Admin_Page.php +++ b/includes/Admin/Admin_Page.php @@ -413,17 +413,11 @@ public function admin_footer() { ); ?>