diff --git a/src/components/compress/custom-els/MultiPanel/index.ts b/src/components/compress/custom-els/MultiPanel/index.ts index d38aabcd..39bdecc1 100644 --- a/src/components/compress/custom-els/MultiPanel/index.ts +++ b/src/components/compress/custom-els/MultiPanel/index.ts @@ -16,10 +16,15 @@ function getClosestHeading(el: Element): HTMLElement | undefined { return undefined; } -async function close(content: HTMLElement) { +async function close(heading: HTMLElement) { + const content = heading.nextElementSibling as HTMLElement; + + // if there is no content, nothing to expand + if (!content) return; + const from = content.getBoundingClientRect().height; - content.removeAttribute('expanded'); + heading.removeAttribute('content-expanded'); content.setAttribute('aria-expanded', 'false'); // Wait a microtask so other calls to open/close can get the final sizes. @@ -34,10 +39,15 @@ async function close(content: HTMLElement) { content.style.height = ''; } -async function open(content: HTMLElement) { +async function open(heading: HTMLElement) { + const content = heading.nextElementSibling as HTMLElement; + + // if there is no content, nothing to expand + if (!content) return; + const from = content.getBoundingClientRect().height; - content.setAttribute('expanded', ''); + heading.setAttribute('content-expanded', ''); content.setAttribute('aria-expanded', 'true'); const to = content.getBoundingClientRect().height; @@ -148,23 +158,19 @@ export default class MultiPanel extends HTMLElement { private _toggle(heading: HTMLElement) { if (!heading) return; - const content = heading.nextElementSibling as HTMLElement; - - // if there is no content, nothing to expand - if (!content) return; // toggle expanded and aria-expanded attributes - if (content.hasAttribute('expanded')) { - close(content); + if (heading.hasAttribute('content-expanded')) { + close(heading); } else { if (this.openOneOnly) this._closeAll(); - open(content); + open(heading); } } private _closeAll(options: CloseAllOptions = {}): void { const { exceptFirst = false } = options; - let els = [...this.children].filter(el => el.matches('[expanded]')) as HTMLElement[]; + let els = [...this.children].filter(el => el.matches('[content-expanded]')) as HTMLElement[]; if (exceptFirst) { els = els.slice(1); @@ -194,8 +200,8 @@ export default class MultiPanel extends HTMLElement { // Remove classes and attributes to prepare for this change. heading.classList.remove(style.panelContent); content.classList.remove(style.panelHeading); - heading.removeAttribute('expanded'); heading.removeAttribute('aria-expanded'); + heading.removeAttribute('content-expanded'); // If appreciable, remove tabindex from content which used to be header. content.removeAttribute('tabindex'); @@ -218,6 +224,13 @@ export default class MultiPanel extends HTMLElement { heading.setAttribute('tabindex', '-1'); } + // It's possible that the heading & content expanded attributes are now out of sync. Resync + // them using the heading as the source of truth. + content.setAttribute( + 'aria-expanded', + heading.hasAttribute('content-expanded') ? 'true' : 'false', + ); + // next sibling of content = next heading heading = content.nextElementSibling; } diff --git a/src/components/compress/custom-els/MultiPanel/styles.css b/src/components/compress/custom-els/MultiPanel/styles.css index 546a2a05..07ce0f6f 100644 --- a/src/components/compress/custom-els/MultiPanel/styles.css +++ b/src/components/compress/custom-els/MultiPanel/styles.css @@ -5,6 +5,6 @@ height: 0px; overflow: auto; } -.panel-content[expanded] { +.panel-content[aria-expanded=true] { height: auto; } diff --git a/src/components/compress/index.tsx b/src/components/compress/index.tsx index 66ed86d0..2fc9c553 100644 --- a/src/components/compress/index.tsx +++ b/src/components/compress/index.tsx @@ -34,6 +34,7 @@ import Processor from '../../codecs/processor'; import { VectorResizeOptions, BitmapResizeOptions } from '../../codecs/resize/processor-meta'; import './custom-els/MultiPanel'; import Results from '../results'; +import { ExpandIcon } from '../../lib/icons'; export interface SourceImage { file: File | Fileish; @@ -421,7 +422,10 @@ export default class Compress extends Component { source={source} loading={loading || image.loading} > - {mobileView ? `${resultTitles[i]} (${encoderMap[image.encoderState.type].label})` : null} + {!mobileView ? null : [ + , + `${resultTitles[i]} (${encoderMap[image.encoderState.type].label})`, + ]} )); diff --git a/src/components/compress/style.scss b/src/components/compress/style.scss index f4a5c4c1..f896f408 100644 --- a/src/components/compress/style.scss +++ b/src/components/compress/style.scss @@ -59,3 +59,16 @@ order: 3; } } + +.expand-icon { + transform: rotate(180deg); + margin-left: -12px; +} + +[content-expanded] .expand-icon { + transform: none; +} + +:focus .expand-icon { + fill: #34B9EB; +} diff --git a/src/components/results/style.scss b/src/components/results/style.scss index 9d3f6ef2..420a8da4 100644 --- a/src/components/results/style.scss +++ b/src/components/results/style.scss @@ -19,6 +19,10 @@ grid-template-columns: 1fr auto; background: rgba(0, 0, 0, 0.9); font-size: 1.4rem; + + &:focus { + outline: none; + } } .result-data { @@ -30,6 +34,8 @@ } .result-title { + display: flex; + align-items: center; margin-right: 0.4em; } diff --git a/src/lib/icons.tsx b/src/lib/icons.tsx index 06cb7dac..e114c472 100644 --- a/src/lib/icons.tsx +++ b/src/lib/icons.tsx @@ -41,3 +41,9 @@ export const CheckedIcon = (props: JSX.HTMLAttributes) => ( ); + +export const ExpandIcon = (props: JSX.HTMLAttributes) => ( + + + +);