diff options
author | Marcin Aman <marcin.aman@gmail.com> | 2021-09-08 10:41:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-08 10:41:50 +0200 |
commit | ab9040a94d5635e18194469cd217282b4d819b9d (patch) | |
tree | 523641d0d14b205be1070d65ea8669cedbd04d70 /plugins | |
parent | 539cfb503d48f26c39237b12293f6f72c9ac50c1 (diff) | |
download | dokka-ab9040a94d5635e18194469cd217282b4d819b9d.tar.gz dokka-ab9040a94d5635e18194469cd217282b4d819b9d.tar.bz2 dokka-ab9040a94d5635e18194469cd217282b4d819b9d.zip |
Dark mode on samples (#2116)
* Dark mode on samples
* Fix samples script firing even when samples were not defined on current page
Diffstat (limited to 'plugins')
4 files changed, 104 insertions, 54 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 31753332..a6c7e326 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -685,7 +685,12 @@ open class HtmlRenderer( code.children.forEach { buildContentNode(it, pageContext) } } } - copyButton() + /* + Disable copy button on samples as: + - it is useless + - it overflows with playground's run button + */ + if(!code.style.contains(ContentStyle.RunnableSample)) copyButton() } } diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt index ca239d83..88b88cf8 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt @@ -29,7 +29,7 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { final override fun invoke(input: RootPageNode): RootPageNode { val analysis = setUpAnalysis(context) val kotlinPlaygroundScript = - "<script src=\"https://unpkg.com/kotlin-playground@1\" data-selector=\"code.runnablesample\"></script>" + "<script src=\"https://unpkg.com/kotlin-playground@1\"></script>" return input.transformContentPagesTree { page -> page.documentable?.documentation?.entries?.fold(page) { acc, entry -> diff --git a/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js b/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js index ae838d6f..ff1bf3d9 100644 --- a/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js +++ b/plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js @@ -5,10 +5,14 @@ filteringContext = { } let highlightedAnchor; let topNavbarOffset; +let instances = []; + +const samplesDarkThemeName = 'darcula' +const samplesLightThemeName = 'idea' window.addEventListener('load', () => { document.querySelectorAll("div[data-platform-hinted]") - .forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event,elem))) + .forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event, elem))) document.querySelectorAll("div[tabs-section]") .forEach(elem => elem.addEventListener('click', (event) => toggleSectionsEventHandler(event))) const filterSection = document.getElementById('filter-section') @@ -28,15 +32,56 @@ const darkModeSwitch = () => { const storage = localStorage.getItem(localStorageKey) const savedDarkMode = storage ? JSON.parse(storage) : false const element = document.getElementById("theme-toggle-button") + initPlayground(savedDarkMode ? samplesDarkThemeName : samplesLightThemeName) element.addEventListener('click', () => { - document.getElementsByTagName("html")[0].classList.toggle("theme-dark") - localStorage.setItem(localStorageKey, JSON.stringify(!savedDarkMode)) + const enabledClasses = document.getElementsByTagName("html")[0].classList + enabledClasses.toggle("theme-dark") + + //if previously we had saved dark theme then we set it to light as this is what we save in local storage + const darkModeEnabled = enabledClasses.contains("theme-dark") + if (darkModeEnabled) { + initPlayground(samplesDarkThemeName) + } + else { + initPlayground(samplesLightThemeName) + } + localStorage.setItem(localStorageKey, JSON.stringify(darkModeEnabled)) }) } +const initPlayground = (theme) => { + if(!samplesAreEnabled()) return + instances.forEach(instance => instance.destroy()) + instances = [] + + // Manually tag code fragments as not processed by playground since we also manually destroy all of its instances + document.querySelectorAll('code.runnablesample').forEach(node => { + node.removeAttribute("data-kotlin-playground-initialized"); + }) + + KotlinPlayground('code.runnablesample', { + getInstance: playgroundInstance => { + instances.push(playgroundInstance) + }, + theme: theme + }); +} + +// We check if type is accessible from the current scope to determine if samples script is present +// As an alternative we could extract this samples-specific script to new js file but then we would handle dark mode in 2 separate files which is not ideal +const samplesAreEnabled = () => { + try { + KotlinPlayground + return true + } catch (e) { + return false + } +} + + const initHidingLeftNavigation = () => { - document.getElementById("leftToggler").onclick = function(event) { + document.getElementById("leftToggler").onclick = function (event) { //Events need to be prevented from bubbling since they will trigger next handler event.preventDefault(); event.stopPropagation(); @@ -53,12 +98,15 @@ const initHidingLeftNavigation = () => { // If this is not present user is forced to refresh the site in order to use an anchor window.onhashchange = handleAnchor -function scrollToElementInContent(element){ - const scrollToElement = () => document.getElementById('main').scrollTo({ top: element.offsetTop - topNavbarOffset.offsetHeight, behavior: "smooth"}) +function scrollToElementInContent(element) { + const scrollToElement = () => document.getElementById('main').scrollTo({ + top: element.offsetTop - topNavbarOffset.offsetHeight, + behavior: "smooth" + }) const waitAndScroll = () => { setTimeout(() => { - if(topNavbarOffset){ + if (topNavbarOffset) { scrollToElement() } else { waitForScroll() @@ -66,7 +114,7 @@ function scrollToElementInContent(element){ }, 50) } - if(topNavbarOffset){ + if (topNavbarOffset) { scrollToElement() } else { waitAndScroll() @@ -75,28 +123,28 @@ function scrollToElementInContent(element){ function handleAnchor() { - if(highlightedAnchor){ + if (highlightedAnchor) { highlightedAnchor.classList.remove('anchor-highlight') highlightedAnchor = null; } - let searchForTab = function(element) { - if(element && element.hasAttribute) { - if(element.hasAttribute("data-togglable")) return element; + let searchForTab = function (element) { + if (element && element.hasAttribute) { + if (element.hasAttribute("data-togglable")) return element; else return searchForTab(element.parentNode) } else return null } let anchor = window.location.hash if (anchor != "") { anchor = anchor.substring(1) - let element = document.querySelector('a[data-name="' + anchor+'"]') + let element = document.querySelector('a[data-name="' + anchor + '"]') if (element) { let tab = searchForTab(element) if (tab) { toggleSections(tab) } const content = element.nextElementSibling - if(content){ + if (content) { content.classList.add('anchor-highlight') highlightedAnchor = content } @@ -106,7 +154,7 @@ function handleAnchor() { } } -function initTabs(){ +function initTabs() { document.querySelectorAll("div[tabs-section]") .forEach(element => { showCorrespondingTabBody(element) @@ -116,13 +164,13 @@ function initTabs(){ if (cached) { let parsed = JSON.parse(cached) let tab = document.querySelector('div[tabs-section] > button[data-togglable="' + parsed + '"]') - if(tab) { + if (tab) { toggleSections(tab) } } } -function showCorrespondingTabBody(element){ +function showCorrespondingTabBody(element) { const key = element.querySelector("button[data-active]").getAttribute("data-togglable") document.querySelector(".tabs-section-body") .querySelector("div[data-togglable='" + key + "']") @@ -130,14 +178,14 @@ function showCorrespondingTabBody(element){ } function filterButtonHandler(event) { - if(event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) { - let sourceset = event.target.getAttribute("data-filter") - if(filteringContext.activeFilters.indexOf(sourceset) != -1) { - filterSourceset(sourceset) - } else { - unfilterSourceset(sourceset) - } + if (event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) { + let sourceset = event.target.getAttribute("data-filter") + if (filteringContext.activeFilters.indexOf(sourceset) != -1) { + filterSourceset(sourceset) + } else { + unfilterSourceset(sourceset) } + } } function initializeFiltering() { @@ -152,11 +200,11 @@ function initializeFiltering() { if (cached) { let parsed = JSON.parse(cached) //Events are used by react to get values in 'on this page' - const event = new CustomEvent('sourceset-filter-change', { detail: parsed }); + const event = new CustomEvent('sourceset-filter-change', {detail: parsed}); window.dispatchEvent(event); filteringContext.activeFilters = filteringContext.restrictedDependencies - .filter(q => parsed.indexOf(q) == -1 ) + .filter(q => parsed.indexOf(q) == -1) } else { filteringContext.activeFilters = filteringContext.restrictedDependencies } @@ -170,7 +218,7 @@ function filterSourceset(sourceset) { } function unfilterSourceset(sourceset) { - if(filteringContext.activeFilters.length == 0) { + if (filteringContext.activeFilters.length == 0) { filteringContext.activeFilters = filteringContext.dependencies[sourceset].concat([sourceset]) refreshFiltering() filteringContext.dependencies[sourceset].concat([sourceset]).forEach(p => removeSourcesetFilterFromCache(p)) @@ -203,9 +251,9 @@ function removeSourcesetFilterFromCache(sourceset) { function toggleSections(target) { localStorage.setItem('active-tab', JSON.stringify(target.getAttribute("data-togglable"))) const activateTabs = (containerClass) => { - for(const element of document.getElementsByClassName(containerClass)){ - for(const child of element.children){ - if(child.getAttribute("data-togglable") === target.getAttribute("data-togglable")){ + for (const element of document.getElementsByClassName(containerClass)) { + for (const child of element.children) { + if (child.getAttribute("data-togglable") === target.getAttribute("data-togglable")) { child.setAttribute("data-active", "") } else { child.removeAttribute("data-active") @@ -218,8 +266,8 @@ function toggleSections(target) { activateTabs("tabs-section-body") } -function toggleSectionsEventHandler(evt){ - if(!evt.target.getAttribute("data-togglable")) return +function toggleSectionsEventHandler(evt) { + if (!evt.target.getAttribute("data-togglable")) return toggleSections(evt.target) } @@ -228,20 +276,18 @@ function togglePlatformDependent(e, container) { if (target.tagName != 'BUTTON') return; let index = target.getAttribute('data-toggle') - for(let child of container.children){ - if(child.hasAttribute('data-toggle-list')){ - for(let bm of child.children){ - if(bm == target){ - bm.setAttribute('data-active',"") - } else if(bm != target) { + for (let child of container.children) { + if (child.hasAttribute('data-toggle-list')) { + for (let bm of child.children) { + if (bm == target) { + bm.setAttribute('data-active', "") + } else if (bm != target) { bm.removeAttribute('data-active') } } - } - else if(child.getAttribute('data-togglable') == index) { - child.setAttribute('data-active',"") - } - else { + } else if (child.getAttribute('data-togglable') == index) { + child.setAttribute('data-active', "") + } else { child.removeAttribute('data-active') } } @@ -256,9 +302,9 @@ function refreshFiltering() { elem.setAttribute("data-filterable-current", platformList.join(' ')) } ) - const event = new CustomEvent('sourceset-filter-change', { detail: sourcesetList }); + const event = new CustomEvent('sourceset-filter-change', {detail: sourcesetList}); window.dispatchEvent(event); - + refreshFilterButtons() refreshPlatformTabs() } @@ -270,17 +316,17 @@ function refreshPlatformTabs() { let firstAvailable = null p.childNodes.forEach( element => { - if(element.getAttribute("data-filterable-current") != ''){ - if( firstAvailable == null) { + if (element.getAttribute("data-filterable-current") != '') { + if (firstAvailable == null) { firstAvailable = element } - if(element.hasAttribute("data-active")) { + if (element.hasAttribute("data-active")) { active = true; } } } ) - if( active == false && firstAvailable) { + if (active == false && firstAvailable) { firstAvailable.click() } } @@ -290,8 +336,8 @@ function refreshPlatformTabs() { function refreshFilterButtons() { document.querySelectorAll("#filter-section > button") .forEach(f => { - if(filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1){ - f.setAttribute("data-active","") + if (filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1) { + f.setAttribute("data-active", "") } else { f.removeAttribute("data-active") } diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css index 85a9c2f9..5008d0ba 100644 --- a/plugins/base/src/main/resources/dokka/styles/style.css +++ b/plugins/base/src/main/resources/dokka/styles/style.css @@ -600,7 +600,6 @@ blockquote { pre { display: block; - overflow-x: auto; } th, td { |