From b26c79cf8722cfd758b49e0740de20573fadf374 Mon Sep 17 00:00:00 2001 From: Konstantin Chernenko Date: Tue, 5 Sep 2023 12:33:26 +0200 Subject: fix(KTL-1177): Resize Observer has indefinite loop (#3116) * fix(KTL-1177): Resize Observer has indefinite loop --- .../scripts/symbol-parameters-wrapper_deferred.js | 115 +++++++++------------ 1 file changed, 46 insertions(+), 69 deletions(-) (limited to 'plugins/base/src/main/resources/dokka') diff --git a/plugins/base/src/main/resources/dokka/scripts/symbol-parameters-wrapper_deferred.js b/plugins/base/src/main/resources/dokka/scripts/symbol-parameters-wrapper_deferred.js index d363488d..7ecae7a6 100644 --- a/plugins/base/src/main/resources/dokka/scripts/symbol-parameters-wrapper_deferred.js +++ b/plugins/base/src/main/resources/dokka/scripts/symbol-parameters-wrapper_deferred.js @@ -4,84 +4,61 @@ // helps with some corner cases where starts working already, // but the signature is not yet long enough to be wrapped -const leftPaddingPx = 60 +(function() { + const leftPaddingPx = 60; -const symbolResizeObserver = new ResizeObserver(entries => { - entries.forEach(entry => { - const symbolElement = entry.target - symbolResizeObserver.unobserve(symbolElement) // only need it once, otherwise will be executed multiple times - wrapSymbolParameters(symbolElement); - }) -}); - -const wrapAllSymbolParameters = () => { - document.querySelectorAll("div.symbol").forEach(symbol => wrapSymbolParameters(symbol)) -} - -const wrapSymbolParameters = (symbol) => { - let parametersBlock = symbol.querySelector("span.parameters") - if (parametersBlock == null) { - return // nothing to wrap + function createNbspIndent() { + let indent = document.createElement("span"); + indent.append(document.createTextNode("\u00A0\u00A0\u00A0\u00A0")); + indent.classList.add("nbsp-indent"); + return indent; } - let symbolBlockWidth = symbol.clientWidth + function wrapSymbolParameters(entry) { + const symbol = entry.target; + const symbolBlockWidth = entry.borderBoxSize && entry.borderBoxSize[0] && entry.borderBoxSize[0].inlineSize; - // Even though the script is marked as `defer` and we wait for `DOMContentLoaded` event, - // it can happen that `symbolBlockWidth` is 0, indicating that something hasn't been loaded. - // In this case, just retry once all styles have been applied and it has been resized correctly. - if (symbolBlockWidth === 0) { - symbolResizeObserver.observe(symbol) - return - } + // Even though the script is marked as `defer` and we wait for `DOMContentLoaded` event, + // or if this block is a part of hidden tab, it can happen that `symbolBlockWidth` is 0, + // indicating that something hasn't been loaded. + // In this case, observer will be triggered onсe again when it will be ready. + if (symbolBlockWidth > 0) { + const node = symbol.querySelector(".parameters"); - let innerTextWidth = Array.from(symbol.children) - .filter(it => !it.classList.contains("block")) // blocks are usually on their own (like annotations), so ignore it - .map(it => it.getBoundingClientRect().width).reduce((a, b) => a + b, 0) + if (node) { + // if window resize happened and observer was triggered, reset previously wrapped + // parameters as they might not need wrapping anymore, and check again + node.classList.remove("wrapped"); + node.querySelectorAll(".parameter .nbsp-indent") + .forEach(indent => indent.remove()); - // if signature text takes up more than a single line, wrap params for readability - let shouldWrapParams = innerTextWidth > (symbolBlockWidth - leftPaddingPx) - if (shouldWrapParams) { - parametersBlock.classList.add("wrapped") - parametersBlock.querySelectorAll("span.parameter").forEach(param => { - // has to be a physical indent so that it can be copied. styles like - // paddings and `::before { content: " " }` do not work for that - param.prepend(createNbspIndent()) - }) - } -} + const innerTextWidth = Array.from(symbol.children) + .filter(it => !it.classList.contains("block")) // blocks are usually on their own (like annotations), so ignore it + .map(it => it.getBoundingClientRect().width) + .reduce((a, b) => a + b, 0); -const createNbspIndent = () => { - let indent = document.createElement("span") - indent.append(document.createTextNode("\u00A0\u00A0\u00A0\u00A0")) - indent.classList.add("nbsp-indent") - return indent -} + // if signature text takes up more than a single line, wrap params for readability + if (innerTextWidth > (symbolBlockWidth - leftPaddingPx)) { + node.classList.add("wrapped"); + node.querySelectorAll(".parameter").forEach(param => { + // has to be a physical indent so that it can be copied. styles like + // paddings and `::before { content: " " }` do not work for that + param.prepend(createNbspIndent()); + }); + } + } + } + } -const resetAllSymbolParametersWrapping = () => { - document.querySelectorAll("div.symbol").forEach(symbol => resetSymbolParametersWrapping(symbol)) -} + const symbolsObserver = new ResizeObserver(entries => entries.forEach(wrapSymbolParameters)); -const resetSymbolParametersWrapping = (symbol) => { - let parameters = symbol.querySelector("span.parameters") - if (parameters != null) { - parameters.classList.remove("wrapped") - parameters.querySelectorAll("span.parameter").forEach(param => { - let indent = param.querySelector("span.nbsp-indent") - if (indent != null) indent.remove() - }) + function initHandlers() { + document.querySelectorAll("div.symbol").forEach(symbol => symbolsObserver.observe(symbol)); } -} -if (document.readyState === 'loading') { - window.addEventListener('DOMContentLoaded', () => { - wrapAllSymbolParameters() - }) -} else { - wrapAllSymbolParameters() -} + if (document.readyState === 'loading') window.addEventListener('DOMContentLoaded', initHandlers); + else initHandlers(); -window.onresize = event => { - // need to re-calculate if params need to be wrapped after resize - resetAllSymbolParametersWrapping() - wrapAllSymbolParameters() -} + // ToDo: Add `unobserve` if dokka will be SPA-like: + // https://github.com/w3c/csswg-drafts/issues/5155 +})(); -- cgit