From 83f8f9c8125a7b9658330744f574e66d8b1b670c Mon Sep 17 00:00:00 2001 From: Wesley Luyten Date: Thu, 27 Jul 2023 14:59:41 -0500 Subject: [PATCH] fix: add indicator CSS parts --- src/js/experimental/media-captions-listbox.js | 76 +++++++++---------- .../experimental/media-captions-selectmenu.js | 2 +- src/js/experimental/media-chrome-listbox.js | 12 +-- .../media-playback-rate-listbox.js | 8 +- .../media-playback-rate-selectmenu.js | 2 +- .../experimental/media-rendition-listbox.js | 5 +- .../media-rendition-selectmenu.js | 2 +- src/js/media-captions-button.js | 62 +++++++-------- 8 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/js/experimental/media-captions-listbox.js b/src/js/experimental/media-captions-listbox.js index 996633f9f..852411e60 100644 --- a/src/js/experimental/media-captions-listbox.js +++ b/src/js/experimental/media-captions-listbox.js @@ -14,37 +14,6 @@ slotTemplate.innerHTML = /*html*/` `; -/** - * @param {any} el Should be HTMLElement but issues with globalThis shim - * @param {string} attrName - * @returns {Array} An array of TextTrack-like objects. - */ -const getSubtitlesListAttr = (el, attrName) => { - const attrVal = el.getAttribute(attrName); - return attrVal ? parseTextTracksStr(attrVal) : []; -}; - -/** - * - * @param {any} el Should be HTMLElement but issues with globalThis shim - * @param {string} attrName - * @param {Array} list An array of TextTrack-like objects - */ -const setSubtitlesListAttr = (el, attrName, list) => { - // null, undefined, and empty arrays are treated as "no value" here - if (!list?.length) { - el.removeAttribute(attrName); - return; - } - - // don't set if the new value is the same as existing - const newValStr = stringifyTextTrackList(list); - const oldVal = el.getAttribute(attrName); - if (oldVal === newValStr) return; - - el.setAttribute(attrName, newValStr); -}; - /** * @attr {string} mediasubtitleslist - (read-only) A list of all subtitles and captions. * @attr {boolean} mediasubtitlesshowing - (read-only) A list of the showing subtitles and captions. @@ -75,11 +44,13 @@ class MediaCaptionsListbox extends MediaChromeListbox { constructor() { super({ slotTemplate }); - this.#selectedIndicator = this.getSlottedIndicator('selected-indicator'); - this.#captionsIndicator = this.getSlottedIndicator('captions-indicator'); + this.#selectedIndicator = this.getSlottedIndicator('selected'); + this.#captionsIndicator = this.getSlottedIndicator('captions'); } attributeChangedCallback(attrName, oldValue, newValue) { + super.attributeChangedCallback(attrName, oldValue, newValue); + if (attrName === MediaUIAttributes.MEDIA_SUBTITLES_LIST && oldValue !== newValue) { this.#render(); @@ -91,8 +62,6 @@ class MediaCaptionsListbox extends MediaChromeListbox { this.removeAttribute('aria-multiselectable'); console.warn("Captions List doesn't currently support multiple selections. You can enable multiple items via the media.textTrack API."); } - - super.attributeChangedCallback(attrName, oldValue, newValue); } connectedCallback() { @@ -165,25 +134,54 @@ class MediaCaptionsListbox extends MediaChromeListbox { } #onChange() { - const selectedOption = this.selectedOptions[0]?.value; - // turn off currently selected tracks toggleSubsCaps(this, false); - if (!selectedOption) return; + if (!this.value) return; const event = new globalThis.CustomEvent( MediaUIEvents.MEDIA_SHOW_SUBTITLES_REQUEST, { composed: true, bubbles: true, - detail: selectedOption, + detail: this.value, } ); this.dispatchEvent(event); } } +/** + * @param {any} el Should be HTMLElement but issues with globalThis shim + * @param {string} attrName + * @returns {Array} An array of TextTrack-like objects. + */ +const getSubtitlesListAttr = (el, attrName) => { + const attrVal = el.getAttribute(attrName); + return attrVal ? parseTextTracksStr(attrVal) : []; +}; + +/** + * + * @param {any} el Should be HTMLElement but issues with globalThis shim + * @param {string} attrName + * @param {Array} list An array of TextTrack-like objects + */ +const setSubtitlesListAttr = (el, attrName, list) => { + // null, undefined, and empty arrays are treated as "no value" here + if (!list?.length) { + el.removeAttribute(attrName); + return; + } + + // don't set if the new value is the same as existing + const newValStr = stringifyTextTrackList(list); + const oldVal = el.getAttribute(attrName); + if (oldVal === newValStr) return; + + el.setAttribute(attrName, newValStr); +}; + if (!globalThis.customElements.get('media-captions-listbox')) { globalThis.customElements.define('media-captions-listbox', MediaCaptionsListbox); } diff --git a/src/js/experimental/media-captions-selectmenu.js b/src/js/experimental/media-captions-selectmenu.js index c6ec404ef..4175349cc 100644 --- a/src/js/experimental/media-captions-selectmenu.js +++ b/src/js/experimental/media-captions-selectmenu.js @@ -16,7 +16,7 @@ class MediaCaptionsSelectMenu extends MediaChromeSelectMenu { const captionsListbox = document.createElement('media-captions-listbox'); captionsListbox.part.add('listbox'); - captionsListbox.setAttribute('exportparts', 'option'); + captionsListbox.setAttribute('exportparts', 'option, indicator'); const buttonSlot = this.shadowRoot.querySelector('slot[name=button]'); const listboxSlot = this.shadowRoot.querySelector('slot[name=listbox]'); diff --git a/src/js/experimental/media-chrome-listbox.js b/src/js/experimental/media-chrome-listbox.js index 63305bb12..c37670ac9 100644 --- a/src/js/experimental/media-chrome-listbox.js +++ b/src/js/experimental/media-chrome-listbox.js @@ -48,17 +48,17 @@ template.innerHTML = /*html*/` margin-inline: .5ch; } - media-chrome-option > .indicator { + media-chrome-option > [part~="indicator"] { fill: var(--media-icon-color, var(--media-primary-color, rgb(238 238 238))); height: var(--media-option-indicator-height, 1.25em); vertical-align: var(--media-option-indicator-vertical-align, text-top); } - media-chrome-option > .selected-indicator { + media-chrome-option > [part~="selected"] { margin-top: -.06em; } - media-chrome-option[aria-selected="false"] > .selected-indicator { + media-chrome-option[aria-selected="false"] > [part~="selected"] { visibility: hidden; } @@ -152,7 +152,7 @@ class MediaChromeListbox extends globalThis.HTMLElement { } getSlottedIndicator(name) { - let indicator = this.querySelector(`:scope > [slot="${name}"]`); + let indicator = this.querySelector(`:scope > [slot="${name}-indicator"]`); // Chaining slots if (indicator?.nodeName == 'SLOT') @@ -160,10 +160,10 @@ class MediaChromeListbox extends globalThis.HTMLElement { indicator = indicator.assignedElements({ flatten: true })[0]; if (!indicator) - indicator = this.shadowRoot.querySelector(`[name="${name}"] > svg`); + indicator = this.shadowRoot.querySelector(`[name="${name}-indicator"] > svg`); indicator.removeAttribute('slot'); - indicator.classList.add('indicator', name); + indicator.part.add(name, 'indicator'); return indicator; } diff --git a/src/js/experimental/media-playback-rate-listbox.js b/src/js/experimental/media-playback-rate-listbox.js index cd5384d11..8ae7abe9c 100644 --- a/src/js/experimental/media-playback-rate-listbox.js +++ b/src/js/experimental/media-playback-rate-listbox.js @@ -31,7 +31,7 @@ class MediaPlaybackRateListbox extends MediaChromeListbox { constructor() { super(); - this.#selectedIndicator = this.getSlottedIndicator('selected-indicator'); + this.#selectedIndicator = this.getSlottedIndicator('selected'); this.#render(); } @@ -104,16 +104,14 @@ class MediaPlaybackRateListbox extends MediaChromeListbox { } #onChange() { - const selectedOption = this.selectedOptions[0]?.value; - - if (!selectedOption) return; + if (!this.value) return; const event = new globalThis.CustomEvent( MediaUIEvents.MEDIA_PLAYBACK_RATE_REQUEST, { composed: true, bubbles: true, - detail: selectedOption, + detail: this.value, } ); this.dispatchEvent(event); diff --git a/src/js/experimental/media-playback-rate-selectmenu.js b/src/js/experimental/media-playback-rate-selectmenu.js index 4414fa7e5..fc0cbd774 100644 --- a/src/js/experimental/media-playback-rate-selectmenu.js +++ b/src/js/experimental/media-playback-rate-selectmenu.js @@ -16,7 +16,7 @@ class MediaPlaybackRateSelectMenu extends MediaChromeSelectMenu { const playbackRateListbox = document.createElement('media-playback-rate-listbox'); playbackRateListbox.part.add('listbox'); - playbackRateListbox.setAttribute('exportparts', 'option'); + playbackRateListbox.setAttribute('exportparts', 'option, indicator'); const buttonSlot = this.shadowRoot.querySelector('slot[name=button]'); const listboxSlot = this.shadowRoot.querySelector('slot[name=listbox]'); diff --git a/src/js/experimental/media-rendition-listbox.js b/src/js/experimental/media-rendition-listbox.js index d8c10e880..6b97f68fd 100644 --- a/src/js/experimental/media-rendition-listbox.js +++ b/src/js/experimental/media-rendition-listbox.js @@ -28,10 +28,11 @@ class MediaRenditionListbox extends MediaChromeListbox { constructor() { super(); - this.#selectedIndicator = this.getSlottedIndicator('selected-indicator'); + this.#selectedIndicator = this.getSlottedIndicator('selected'); } attributeChangedCallback(attrName, oldValue, newValue) { + super.attributeChangedCallback(attrName, oldValue, newValue); if (attrName === MediaUIAttributes.MEDIA_RENDITION_SELECTED && oldValue !== newValue) { this.value = newValue ?? 'auto'; @@ -41,8 +42,6 @@ class MediaRenditionListbox extends MediaChromeListbox { this.#renditionList = parseRenditionList(newValue); this.#render(); } - - super.attributeChangedCallback(attrName, oldValue, newValue); } connectedCallback() { diff --git a/src/js/experimental/media-rendition-selectmenu.js b/src/js/experimental/media-rendition-selectmenu.js index 9aa5526b6..4ba368461 100644 --- a/src/js/experimental/media-rendition-selectmenu.js +++ b/src/js/experimental/media-rendition-selectmenu.js @@ -16,7 +16,7 @@ class MediaRenditionSelectMenu extends MediaChromeSelectMenu { const renditionListbox = document.createElement('media-rendition-listbox'); renditionListbox.part.add('listbox'); - renditionListbox.setAttribute('exportparts', 'option'); + renditionListbox.setAttribute('exportparts', 'option, indicator'); const buttonSlot = this.shadowRoot.querySelector('slot[name=button]'); const listboxSlot = this.shadowRoot.querySelector('slot[name=listbox]'); diff --git a/src/js/media-captions-button.js b/src/js/media-captions-button.js index 54e2bc901..6781e9e2a 100644 --- a/src/js/media-captions-button.js +++ b/src/js/media-captions-button.js @@ -40,37 +40,6 @@ const updateAriaChecked = (el) => { el.setAttribute('aria-checked', areSubsOn(el)); }; -/** - * @param {any} el Should be HTMLElement but issues with globalThis shim - * @param {string} attrName - * @returns {Array} An array of TextTrack-like objects. - */ -const getSubtitlesListAttr = (el, attrName) => { - const attrVal = el.getAttribute(attrName); - return attrVal ? parseTextTracksStr(attrVal) : []; -}; - -/** - * - * @param {any} el Should be HTMLElement but issues with globalThis shim - * @param {string} attrName - * @param {Array} list An array of TextTrack-like objects - */ -const setSubtitlesListAttr = (el, attrName, list) => { - // null, undefined, and empty arrays are treated as "no value" here - if (!list?.length) { - el.removeAttribute(attrName); - return; - } - - // don't set if the new value is the same as existing - const newValStr = stringifyTextTrackList(list); - const oldVal = el.getAttribute(attrName); - if (oldVal === newValStr) return; - - el.setAttribute(attrName, newValStr); -}; - /** * @slot on - An element that will be shown while closed captions or subtitles are on. * @slot off - An element that will be shown while closed captions or subtitles are off. @@ -144,6 +113,37 @@ class MediaCaptionsButton extends MediaChromeButton { } } +/** + * @param {any} el Should be HTMLElement but issues with globalThis shim + * @param {string} attrName + * @returns {Array} An array of TextTrack-like objects. + */ +const getSubtitlesListAttr = (el, attrName) => { + const attrVal = el.getAttribute(attrName); + return attrVal ? parseTextTracksStr(attrVal) : []; +}; + +/** + * + * @param {any} el Should be HTMLElement but issues with globalThis shim + * @param {string} attrName + * @param {Array} list An array of TextTrack-like objects + */ +const setSubtitlesListAttr = (el, attrName, list) => { + // null, undefined, and empty arrays are treated as "no value" here + if (!list?.length) { + el.removeAttribute(attrName); + return; + } + + // don't set if the new value is the same as existing + const newValStr = stringifyTextTrackList(list); + const oldVal = el.getAttribute(attrName); + if (oldVal === newValStr) return; + + el.setAttribute(attrName, newValStr); +}; + if (!globalThis.customElements.get('media-captions-button')) { globalThis.customElements.define('media-captions-button', MediaCaptionsButton); }