aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Aman <marcin.aman@gmail.com>2021-09-08 10:41:50 +0200
committerGitHub <noreply@github.com>2021-09-08 10:41:50 +0200
commitab9040a94d5635e18194469cd217282b4d819b9d (patch)
tree523641d0d14b205be1070d65ea8669cedbd04d70
parent539cfb503d48f26c39237b12293f6f72c9ac50c1 (diff)
downloaddokka-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
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt7
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt2
-rw-r--r--plugins/base/src/main/resources/dokka/scripts/platform-content-handler.js148
-rw-r--r--plugins/base/src/main/resources/dokka/styles/style.css1
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 {