Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: extracted UI components to ui-kit in plugin-base-frontend #3470

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -38,3 +38,6 @@ android.local.properties

core/out/
runners/**/out/

**/node_modules
**/build
16,920 changes: 4,769 additions & 12,151 deletions dokka-subprojects/plugin-base-frontend/package-lock.json

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions dokka-subprojects/plugin-base-frontend/package.json
Expand Up @@ -7,6 +7,7 @@
"dist": "./dist"
},
"scripts": {
"build:ui-kit": "webpack --config ./webpack.config-ui-kit.js",
"build": "webpack --mode=production --devtool source-map",
"lint": "eslint . && npm run stylelint",
"stylelint": "stylelint --ignore-path .gitignore ./src/main/**/*.scss",
Expand All @@ -24,33 +25,41 @@
},
"dependencies": {
"@babel/core": "^7.16.0",
"@babel/preset-env": "^7.24.5",
"@jetbrains/babel-preset-jetbrains": "^2.3.1",
"@jetbrains/logos": "1.4.27",
"@jetbrains/ring-ui": "^4.1.3",
"@types/node": "^16.11.10",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"babel-loader": "^8.2.3",
"core-js": "^2.6.12",
"lodash": "^4.17.21",
"postcss-import": "^14.0.2",
"postcss-preset-env": "^7.0.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"ts-loader": "^9.2.6",
"typescript": "^4.5.2",
"webpack": "^5.76.0",
"webpack-cli": "^4.9.1",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.6.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.16.3",
"@jetbrains/stylelint-config": "^3.0.2",
"@types/lodash": "^4.14.177",
"css-loader": "^7.1.1",
"css-minimizer-webpack-plugin": "^7.0.0",
"cssnano": "^7.0.1",
"eslint": "^8.3.0",
"mini-css-extract-plugin": "^2.4.5",
"node-sass": "^9.0.0",
"postcss-loader": "^8.1.1",
"react-svg-loader": "^3.0.3",
"sass": "^1.43.5",
"sass-loader": "^12.3.0",
"sass-loader": "^14.0.0",
"style-loader": "^4.0.0",
"stylelint": "^14.1.0",
"svg-inline-loader": "^0.8.2",
"terser-webpack-plugin": "^5.2.5"
Expand Down
@@ -0,0 +1,7 @@
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
import * as tabs from './tabs/index.js';

export { tabs };

@@ -0,0 +1,5 @@
/*!
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

@import 'tabs/styles.scss';
@@ -0,0 +1,95 @@
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
import './styles.scss';

/** When Dokka is viewed via iframe, local storage could be inaccessible (see https://github.com/Kotlin/dokka/issues/3323)
* This is a wrapper around local storage to prevent errors in such cases
* */
const safeLocalStorage = (() => {
let isLocalStorageAvailable = false;
try {
const testKey = '__testLocalStorageKey__';
localStorage.setItem(testKey, testKey);
localStorage.removeItem(testKey);
isLocalStorageAvailable = true;
} catch (e) {
console.error('Local storage is not available', e);
}

return {
getItem: (key) => {
if (!isLocalStorageAvailable) {
return null;
}
return localStorage.getItem(key);
},
setItem: (key, value) => {
if (!isLocalStorageAvailable) {
return;
}
localStorage.setItem(key, value);
}
};
})();

export function initTabs() {
// we could have only a single type of data - classlike or package
const mainContent = document.querySelector('.main-content');
const type = mainContent ? mainContent.getAttribute("data-page-type") : null;
const localStorageKey = "active-tab-" + type;
document.querySelectorAll('div[tabs-section]').forEach(element => {
showCorrespondingTabBody(element);
element.addEventListener('click', ({ target }) => {
const togglable = target ? target.getAttribute("data-togglable") : null;
if (!togglable) return;

safeLocalStorage.setItem(localStorageKey, JSON.stringify(togglable));
toggleSections(target);
});
});

const cached = safeLocalStorage.getItem(localStorageKey);
if (!cached) return;

const tab = document.querySelector(
'div[tabs-section] > button[data-togglable="' + JSON.parse(cached) + '"]'
);
if (!tab) return;

toggleSections(tab);
}

function showCorrespondingTabBody(element) {
const buttonWithKey = element.querySelector("button[data-active]")
if (buttonWithKey) {
toggleSections(buttonWithKey)
}
}

function toggleSections(target) {
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")) {
child.setAttribute("data-active", "")
} else {
child.removeAttribute("data-active")
}
}
}
}
const toggleTargets = target.getAttribute("data-togglable").split(",")
const activateTabsBody = (containerClass) => {
document.querySelectorAll("." + containerClass + " *[data-togglable]")
.forEach(child => {
if (toggleTargets.includes(child.getAttribute("data-togglable"))) {
child.setAttribute("data-active", "")
} else if (!child.classList.contains("sourceset-dependent-content")) { // data-togglable is used to switch source set as well, ignore it
child.removeAttribute("data-active")
}
})
}
activateTabs("tabs-section")
activateTabsBody("tabs-section-body")
}
@@ -0,0 +1,48 @@
/*!
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

.section-tab,
.platform-hinted > .platform-bookmarks-row > .platform-bookmark {
border: 0;
padding: 11px 3px;
margin: 0 8px;
cursor: pointer;
outline: none;
font-size: var(--default-font-size);
background-color: transparent;
color: var(--inactive-section-color);
border-bottom: 1px solid var(--inactive-tab-border-color);
}

.section-tab:hover {
color: var(--default-font-color);
border-bottom: 2px solid var(--default-font-color);
}

.section-tab[data-active=''] {
color: var(--active-section-color);
border-bottom: 2px solid var(--active-tab-border-color);
}

.tabs-section-body > div {
margin-top: 12px;
}

.tabs-section-body .with-platform-tabs {
padding-top: 12px;
padding-bottom: 12px;
}

.js .platform-hinted > .content:not([data-active]),
.js .tabs-section-body *[data-togglable]:not([data-active]) {
display: none;
}

/*
the hack to hide the headers inside tabs for a package page because each tab
has only one header, and the header text is the same as the tab name, so no point in showing it
*/
.main-content[data-page-type="package"] .tabs-section-body h2 {
display: none;
}
Empty file.
58 changes: 58 additions & 0 deletions dokka-subprojects/plugin-base-frontend/webpack.config-ui-kit.js
@@ -0,0 +1,58 @@
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
entry: {
entry: ['./src/main/ui-kit/index.js'],
},
mode: 'production',
output: {
path: path.resolve(__dirname, '../plugin-base/src/main/resources/dokka/ui-kit/'),
filename: 'ui-kit.js',
},
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'cssnano',
{
preset: ['default', { discardComments: { removeAll: true } }],
},
],
],
},
},
},
'sass-loader',
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'ui-kit.css',
}),
],
};
Expand Up @@ -61,6 +61,7 @@ public class ScriptsInstaller(private val dokkaContext: DokkaContext) : PageTran
"scripts/platform-content-handler.js",
"scripts/main.js",
"scripts/prism.js",
"ui-kit/ui-kit.js",

// It's important for this script to be deferred because it has logic that makes decisions based on
// rendered elements (for instance taking their clientWidth), and if not all styles are loaded/applied
Expand All @@ -86,7 +87,8 @@ public class StylesInstaller(private val dokkaContext: DokkaContext) : PageTrans
"styles/main.css",
"styles/prism.css",
"styles/logo-styles.css",
"styles/font-jb-sans-auto.css"
"styles/font-jb-sans-auto.css",
"ui-kit/ui-kit.css"
)

override fun invoke(input: RootPageNode): RootPageNode =
Expand Down
@@ -0,0 +1,34 @@
.platform-hinted > .platform-bookmarks-row > .platform-bookmark, .section-tab {
background-color: transparent;
border: 0;
border-bottom: 1px solid var(--inactive-tab-border-color);
color: var(--inactive-section-color);
cursor: pointer;
font-size: var(--default-font-size);
margin: 0 8px;
outline: none;
padding: 11px 3px
}

.section-tab:hover {
border-bottom: 2px solid var(--default-font-color);
color: red;
}

.section-tab[data-active=""] {
border-bottom: 2px solid var(--active-tab-border-color);
color: var(--active-section-color)
}

.tabs-section-body > div {
margin-top: 12px
}

.tabs-section-body .with-platform-tabs {
padding-bottom: 12px;
padding-top: 12px
}

.js .platform-hinted > .content:not([data-active]), .js .tabs-section-body [data-togglable]:not([data-active]), .main-content[data-page-type=package] .tabs-section-body h2 {
display: none
}

Large diffs are not rendered by default.