From 5632e968513beebcddd9bfadf16362a54789709a Mon Sep 17 00:00:00 2001 From: John Hildenbiddle Date: Thu, 29 Feb 2024 17:07:31 -0600 Subject: [PATCH] Add prettier and fix lint issues --- .eslintrc.cjs | 81 +++-- .markdownlint.json | 16 +- .prettierrc.json | 5 + .vscode/settings.json | 8 +- CHANGELOG.md | 38 +-- docs/assets/css/main.css | 71 +++-- docs/index.html | 173 ++++++----- docs/index.md | 63 ++-- docs/sidebar.md | 3 +- package-lock.json | 29 ++ package.json | 5 +- postcss.config.cjs | 14 +- prettierignore | 1 + rollup.config.js | 179 ++++++------ server.js | 89 +++--- src/css/vars.css | 34 +-- src/js/index.js | 618 ++++++++++++++++++++++----------------- src/scss/style.scss | 229 ++++++++------- 18 files changed, 887 insertions(+), 769 deletions(-) create mode 100644 .prettierrc.json create mode 100644 prettierignore diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ac3fc3a..9cef792 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,45 +1,40 @@ module.exports = { - 'env': { - 'browser' : true, - 'commonjs': true, - 'es6' : true, - 'node' : true - }, - 'extends': [ - 'eslint:recommended' - ], - 'ignorePatterns': [ - 'dist' - ], - 'parserOptions': { - 'ecmaVersion': 2022, - 'sourceType' : 'module' - }, - 'plugins': [ - ], - 'rules': { - 'array-bracket-spacing' : ['error', 'never'], - 'array-callback-return' : ['error'], - 'block-scoped-var' : ['error'], - 'block-spacing' : ['error', 'always'], - 'curly' : ['error'], - 'dot-notation' : ['error'], - 'eqeqeq' : ['error'], - 'indent' : ['error', 4], - 'no-console' : ['warn'], - 'no-floating-decimal' : ['error'], - 'no-implicit-coercion' : ['error'], - 'no-implicit-globals' : ['error'], - 'no-loop-func' : ['error'], - 'no-return-assign' : ['error'], - 'no-template-curly-in-string': ['error'], - 'no-unneeded-ternary' : ['error'], - 'no-unused-vars' : ['error', { 'args': 'none' }], - 'no-useless-computed-key' : ['error'], - 'no-useless-return' : ['error'], - 'no-var' : ['error'], - 'prefer-const' : ['error'], - 'quotes' : ['error', 'single'], - 'semi' : ['error', 'always'] - } + env: { + browser: true, + commonjs: true, + es6: true, + node: true + }, + extends: ['eslint:recommended', 'prettier'], + ignorePatterns: ['dist'], + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module' + }, + plugins: [], + rules: { + 'array-bracket-spacing': ['error', 'never'], + 'array-callback-return': ['error'], + 'block-scoped-var': ['error'], + 'block-spacing': ['error', 'always'], + curly: ['error'], + 'dot-notation': ['error'], + eqeqeq: ['error'], + // 'indent': ['error', 4], + 'no-console': ['warn'], + 'no-floating-decimal': ['error'], + 'no-implicit-coercion': ['error'], + 'no-implicit-globals': ['error'], + 'no-loop-func': ['error'], + 'no-return-assign': ['error'], + 'no-template-curly-in-string': ['error'], + 'no-unneeded-ternary': ['error'], + 'no-unused-vars': ['error', { args: 'none' }], + 'no-useless-computed-key': ['error'], + 'no-useless-return': ['error'], + 'no-var': ['error'], + 'prefer-const': ['error'], + quotes: ['error', 'single'], + semi: ['error', 'always'] + } }; diff --git a/.markdownlint.json b/.markdownlint.json index 0f06446..e277d7d 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1,10 +1,10 @@ { - "default": true, - "MD001": false, - "MD004": { "style": "consistent" }, - "MD013": false, - "MD023": false, - "MD024": false, - "MD033": false, - "MD036": false + "default": true, + "MD001": false, + "MD004": { "style": "consistent" }, + "MD013": false, + "MD023": false, + "MD024": false, + "MD033": false, + "MD036": false } diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..17e152d --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "arrowParens": "avoid", + "singleQuote": true, + "trailingComma": "none" +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 0d5c3c5..cf99d51 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,3 @@ { - "cSpell.words": [ - "Codacy", - "jhildenbiddle", - "themeable" - ] -} \ No newline at end of file + "cSpell.words": ["Codacy", "jhildenbiddle", "themeable"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a4c4d..e372d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ ## 1.6.1 -*2024-02-06* +_2024-02-06_ - Fix GitHub workflow badge ## 1.6.0 -*2022-09-11* +_2022-09-11_ - Add support for nested tabs (#5) - Fix tab content margin of first & last element @@ -16,79 +16,79 @@ ## 1.5.4 -*2022-09-10* +_2022-09-10_ - Fix inactive tab content flicker on tab change (#27) - Fix tab parsing with compressed HTML (#45) ## 1.5.3 -*2022-07-29* +_2022-07-29_ - Fix plugin insertion point (fix for docsify-mustache) (#44) - Update dependencies ## 1.5.2 -*2021-09-02* +_2021-09-02_ - Fix code quality badges - Add GitHub CI ## 1.5.1 -*2021-09-02* +_2021-09-02_ - Fix setting active tab from anchor with unicode (#41) ## 1.5.0 -*2021-04-27* +_2021-04-27_ - Add support for markdown and HTML in tab labels (#38) - Update custom style examples in documentation ## 1.4.4 -*2020-11-05* +_2020-11-05_ - Fix tab comments with code block rendering (#29) ## 1.4.3 -*2020-06-25* +_2020-06-25_ - Fix handling of regex replacement patterns in markdown (#26) ## 1.4.2 -*2020-05-11* +_2020-05-11_ - Fix error when no active tab set in URL (#23) ## 1.4.1 -*2020-05-09* +_2020-05-09_ - Fix handling of URL encoded anchor IDs (#22) ## 1.4.0 -*2020-04-12* +_2020-04-12_ - Add tab selection on hash change - Fix tab selection based on anchor ID in IE ## 1.3.0 -*2020-04-11* +_2020-04-11_ - Add tab selection based on anchor ID in URL (#20) - Fix tab content container padding and first/last child margins ## 1.2.0 -*2020-02-11* +_2020-02-11_ - Update sync behavior to allow synced tab selections across pages (#17) - Fix rendering of tabset when using tab comments (#16) @@ -96,7 +96,7 @@ ## 1.1.2 -*2019-01-08* +_2019-01-08_ - Add Sentry.io - Update dependencies @@ -106,25 +106,25 @@ ## 1.1.0 -*2018-11-10* +_2018-11-10_ - Add support for tabsets nested within lists ## 1.0.6 -*2018-11-01* +_2018-11-01_ - Fix rendering issue caused by marked package upgrade in docsify 4.8.0 ## 1.0.5 -*2018-10-11* +_2018-10-11_ - Fix bug that prevented rendering of tabs from markdown with Windows-style line terminators ## 1.0.0 - 1.0.4 -*2018-10-09* +_2018-10-09_ - Initial release diff --git a/docs/assets/css/main.css b/docs/assets/css/main.css index 5d47f74..9427af9 100644 --- a/docs/assets/css/main.css +++ b/docs/assets/css/main.css @@ -1,62 +1,61 @@ /* Required for browsers w/o shadow DOM support */ -iframe[src*="buttons.github.io"] { - margin: 0; +iframe[src*='buttons.github.io'] { + margin: 0; } .markdown-section strong code { - font-weight: normal; + font-weight: normal; } /* Theme Toggles */ -label[data-class-target="label + .docsify-tabs"] { - margin-right: 0.8em; +label[data-class-target='label + .docsify-tabs'] { + margin-right: 0.8em; } - /* Custom Styles */ /* ========================================================================== */ /* Badges */ .tab-badge, -[data-tab="badge"]:after { - position: absolute; - top: 0; - right: 0; - transform: translate(35%, -45%); - padding: 0.25em 0.35em; - border-radius: 3px; - background: red; - color: white; - font-family: sans-serif; - font-size: 11px; - font-weight: bold; +[data-tab='badge']:after { + position: absolute; + top: 0; + right: 0; + transform: translate(35%, -45%); + padding: 0.25em 0.35em; + border-radius: 3px; + background: red; + color: white; + font-family: sans-serif; + font-size: 11px; + font-weight: bold; } -[data-tab="badge"]:after { - content: 'New!'; +[data-tab='badge']:after { + content: 'New!'; } /* Active State */ -.docsify-tabs__tab--active[data-tab="active state"] { - box-shadow: none; - background: #13547a; - color: white; +.docsify-tabs__tab--active[data-tab='active state'] { + box-shadow: none; + background: #13547a; + color: white; } -.docsify-tabs__content[data-tab-content="active state"] { - background-image: linear-gradient(0deg, #80d0c7 0%, #13547a 100%); +.docsify-tabs__content[data-tab-content='active state'] { + background-image: linear-gradient(0deg, #80d0c7 0%, #13547a 100%); } -.docsify-tabs__content[data-tab-content="active state"] p strong { - color: white; +.docsify-tabs__content[data-tab-content='active state'] p strong { + color: white; } /* CodePen */ -[data-tab-content="codepen"] .cp_embed_wrapper { - position: relative; - top: calc(0px - var(--docsifytabs-content-padding)); - left: calc(0px - var(--docsifytabs-content-padding)); - width: calc(100% + calc(var(--docsifytabs-content-padding) * 2)); - margin-bottom: calc(0px - var(--docsifytabs-content-padding)); +[data-tab-content='codepen'] .cp_embed_wrapper { + position: relative; + top: calc(0px - var(--docsifytabs-content-padding)); + left: calc(0px - var(--docsifytabs-content-padding)); + width: calc(100% + calc(var(--docsifytabs-content-padding) * 2)); + margin-bottom: calc(0px - var(--docsifytabs-content-padding)); } -[data-tab-content="codepen"] .cp_embed_wrapper > * { - margin: 0; +[data-tab-content='codepen'] .cp_embed_wrapper > * { + margin: 0; } diff --git a/docs/index.html b/docs/index.html index 05801a2..b32b2b1 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,16 +1,35 @@ - - - - - - - - docsify-tabs - A docsify.js plugin for rendering tabbed content from markdown - - - + + + + + + + + docsify-tabs - A docsify.js plugin for rendering tabbed content from + markdown + + + + @@ -19,74 +38,84 @@ - + - + - +
@@ -97,8 +126,8 @@ - + diff --git a/docs/index.md b/docs/index.md index 04db026..8850ad0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -39,13 +39,13 @@ function add(a, b) { Life is what happens when you're busy making other plans. -\- *John Lennon* +\- _John Lennon_ #### **Nested Tab 2** The greatest glory in living lies not in never falling, but in rising every time we fall. -\- *Nelson Mandela* +\- _Nelson Mandela_ @@ -117,11 +117,11 @@ This is some text. window.$docsify = { // ... tabs: { - persist : true, // default - sync : true, // default - theme : 'classic', // default - tabComments: true, // default - tabHeadings: true // default + persist: true, // default + sync: true, // default + theme: 'classic', // default + tabComments: true, // default + tabHeadings: true // default } }; ``` @@ -202,11 +202,11 @@ Options are set within the [`window.$docsify`](https://docsify.js.org/#/configur window.$docsify = { // ... tabs: { - persist : true, // default - sync : true, // default - theme : 'classic', // default - tabComments: true, // default - tabHeadings: true // default + persist: true, // default + sync: true, // default + theme: 'classic', // default + tabComments: true, // default + tabHeadings: true // default } }; @@ -558,15 +558,15 @@ More advanced styling can be applied by leveraging the CSS class names and data ```html -
- ... -
+
...
``` When the tab is active, note the addition of the `docsify-tabs__tab--active` class: ```html - + ``` **Examples** @@ -590,24 +590,27 @@ When the tab is active, note the addition of the `docsify-tabs__tab--active` cla **HTML Output** ```html - -
- ... -
+ +
...
``` **Custom CSS** ```css -.docsify-tabs__tab--active[data-tab="active state"] { +.docsify-tabs__tab--active[data-tab='active state'] { box-shadow: none; background: #13547a; color: white; } -.docsify-tabs__content[data-tab-content="active state"] { +.docsify-tabs__content[data-tab-content='active state'] { background-image: linear-gradient(0deg, #80d0c7 0%, #13547a 100%); } -.docsify-tabs__content[data-tab-content="active state"] p strong { +.docsify-tabs__content[data-tab-content='active state'] p strong { color: white; } ``` @@ -637,16 +640,16 @@ CodePen Embed Code... **HTML Output** ```html - -
- ... -
+ +
...
``` **Custom CSS** ```css -[data-tab-content="codepen"] .cp_embed_wrapper { +[data-tab-content='codepen'] .cp_embed_wrapper { position: relative; top: calc(0px - var(--docsifytabs-content-padding)); left: calc(0px - var(--docsifytabs-content-padding)); @@ -654,7 +657,7 @@ CodePen Embed Code... margin-bottom: calc(0px - var(--docsifytabs-content-padding)); } -[data-tab-content="codepen"] .cp_embed_wrapper > * { +[data-tab-content='codepen'] .cp_embed_wrapper > * { margin: 0; } ``` @@ -676,7 +679,7 @@ CodePen Embed Code... **Custom CSS** ```css -[data-tab="badge"]:after { +[data-tab='badge']:after { content: 'New!'; position: absolute; top: 0; diff --git a/docs/sidebar.md b/docs/sidebar.md index e7cc36b..c0f202b 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -1,5 +1,4 @@ - -- [Documentation](/) +- [Documentation](/) - [Changelog](changelog) - **Links** - [![GitHub](assets/img/github.svg)GitHub](https://github.com/jhildenbiddle/docsify-tabs) diff --git a/package-lock.json b/package-lock.json index 86ff251..f9f8bb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "compression": "^1.7.4", "es-check": "^7.0.0", "eslint": "^8.12.0", + "eslint-config-prettier": "^9.1.0", "markdownlint-cli": "^0.39.0", "mergician": "^2.0.0", "npm-run-all": "^4.1.5", @@ -29,6 +30,7 @@ "postcss-custom-properties": "^13.3.5", "postcss-flexbugs-fixes": "^5.0.2", "postcss-import": "^16.0.1", + "prettier": "^3.2.5", "rimraf": "^5.0.5", "rollup": "^4.12.0", "rollup-plugin-postcss": "^4.0.1", @@ -4173,6 +4175,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -7205,6 +7219,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/promise.series": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz", diff --git a/package.json b/package.json index e4b2776..a1e1318 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "build": "rollup -c", "clean": "rimraf dist/*", "escheck": "es-check es5 'dist/**/*.js'", - "lint": "eslint . && markdownlint . --ignore node_modules", + "lint": "prettier . --check && eslint . && markdownlint *.md docs/*.md --ignore node_modules", + "lint:fix": "prettier . --write && eslint . --fix", "prepare": "run-s clean build", "serve": "node server.js", "start": "run-p watch serve", @@ -61,6 +62,7 @@ "compression": "^1.7.4", "es-check": "^7.0.0", "eslint": "^8.12.0", + "eslint-config-prettier": "^9.1.0", "markdownlint-cli": "^0.39.0", "mergician": "^2.0.0", "npm-run-all": "^4.1.5", @@ -68,6 +70,7 @@ "postcss-custom-properties": "^13.3.5", "postcss-flexbugs-fixes": "^5.0.2", "postcss-import": "^16.0.1", + "prettier": "^3.2.5", "rimraf": "^5.0.5", "rollup": "^4.12.0", "rollup-plugin-postcss": "^4.0.1", diff --git a/postcss.config.cjs b/postcss.config.cjs index 01621d7..629b015 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -1,9 +1,9 @@ module.exports = { - map : false, - plugins: [ - require('postcss-import')(), - require('autoprefixer')(), - require('postcss-custom-properties')(), - require('postcss-flexbugs-fixes')() - ] + map: false, + plugins: [ + require('postcss-import')(), + require('autoprefixer')(), + require('postcss-custom-properties')(), + require('postcss-flexbugs-fixes')() + ] }; diff --git a/prettierignore b/prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/prettierignore @@ -0,0 +1 @@ +dist diff --git a/rollup.config.js b/rollup.config.js index 18e3344..5b739c9 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,134 +1,127 @@ -import { babel } from '@rollup/plugin-babel'; -import commonjs from '@rollup/plugin-commonjs'; -import eslint from '@rollup/plugin-eslint'; -import fs from 'node:fs'; -import json from '@rollup/plugin-json'; +import { babel } from '@rollup/plugin-babel'; +import commonjs from '@rollup/plugin-commonjs'; +import eslint from '@rollup/plugin-eslint'; +import fs from 'node:fs'; +import json from '@rollup/plugin-json'; import { mergician } from 'mergician'; -import path from 'node:path'; -import postcss from 'rollup-plugin-postcss'; -import nodeResolve from '@rollup/plugin-node-resolve'; -import terser from '@rollup/plugin-terser'; +import path from 'node:path'; +import postcss from 'rollup-plugin-postcss'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; const pkg = JSON.parse( - fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8') // prettier-ignore + fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8') // prettier-ignore ); - // Settings // ============================================================================= // Copyright -const currentYear = (new Date()).getFullYear(); -const releaseYear = 2018; +const currentYear = new Date().getFullYear(); +const releaseYear = 2018; // Output -const entryFile = path.resolve('.', 'src', 'js', 'index.js'); +const entryFile = path.resolve('.', 'src', 'js', 'index.js'); const outputFile = path.resolve('.', 'dist', `${pkg.name}.js`); // Banner const bannerData = [ - `${pkg.name}`, - `v${pkg.version}`, - `${pkg.homepage}`, - `(c) ${releaseYear}${currentYear === releaseYear ? '' : '-' + currentYear} ${pkg.author}`, - `${pkg.license} license` + `${pkg.name}`, + `v${pkg.version}`, + `${pkg.homepage}`, + `(c) ${releaseYear}${currentYear === releaseYear ? '' : '-' + currentYear} ${pkg.author}`, + `${pkg.license} license` ]; // Plugins const pluginSettings = { - eslint: { - exclude : ['node_modules/**', './package.json', './src/**/*.{css,scss}'], - throwOnWarning: false, - throwOnError : true - }, - babel: { - babelrc: false, - exclude: ['node_modules/**'], - babelHelpers: 'bundled', - presets: [ - ['@babel/preset-env', { - modules: false, - targets: { - browsers: ['ie >= 11'] - } - }] - ], + eslint: { + exclude: ['node_modules/**', './package.json', './src/**/*.{css,scss}'], + throwOnWarning: false, + throwOnError: true + }, + babel: { + babelrc: false, + exclude: ['node_modules/**'], + babelHelpers: 'bundled', + presets: [ + [ + '@babel/preset-env', + { + modules: false, + targets: { + browsers: ['ie >= 11'] + } + } + ] + ] + }, + postcss: { + inject: { + insertAt: 'top' }, - postcss: { - inject: { - insertAt: 'top' - }, - minimize: true, + minimize: true + }, + terser: { + beautify: { + compress: false, + mangle: false, + output: { + beautify: true, + comments: /(?:^!|@(?:license|preserve))/ + } }, - terser: { - beautify: { - compress: false, - mangle : false, - output: { - beautify: true, - comments: /(?:^!|@(?:license|preserve))/ - } - }, - minify: { - compress: true, - mangle : true, - output : { - comments: new RegExp(pkg.name) - } - } + minify: { + compress: true, + mangle: true, + output: { + comments: new RegExp(pkg.name) + } } + } }; - // Config // ============================================================================= // Base const config = { - input : entryFile, - output: { - banner : `/*!\n * ${ bannerData.join('\n * ') }\n */`, - file : outputFile, - sourcemap: true - }, - plugins: [ - nodeResolve(), - commonjs(), - json(), - postcss(pluginSettings.postcss), - eslint(pluginSettings.eslint), - babel(pluginSettings.babel) - ], - watch: { - clearScreen: false - } + input: entryFile, + output: { + banner: `/*!\n * ${bannerData.join('\n * ')}\n */`, + file: outputFile, + sourcemap: true + }, + plugins: [ + nodeResolve(), + commonjs(), + json(), + postcss(pluginSettings.postcss), + eslint(pluginSettings.eslint), + babel(pluginSettings.babel) + ], + watch: { + clearScreen: false + } }; // Formats // ----------------------------------------------------------------------------- // IIFE const iife = mergician({}, config, { - output: { - format: 'iife' - }, - plugins: config.plugins.concat([ - terser(pluginSettings.terser.beautify) - ]) + output: { + format: 'iife' + }, + plugins: config.plugins.concat([terser(pluginSettings.terser.beautify)]) }); // IIFE (Minified) const iifeMinified = mergician({}, config, { - output: { - file : iife.output.file.replace(/\.js$/, '.min.js'), - format: iife.output.format - }, - plugins: config.plugins.concat([ - terser(pluginSettings.terser.minify) - ]) + output: { + file: iife.output.file.replace(/\.js$/, '.min.js'), + format: iife.output.format + }, + plugins: config.plugins.concat([terser(pluginSettings.terser.minify)]) }); - // Exports // ============================================================================= -export default [ - iife, - iifeMinified -]; +export default [iife, iifeMinified]; diff --git a/server.js b/server.js index 5b7c951..a5d8a1e 100644 --- a/server.js +++ b/server.js @@ -4,55 +4,46 @@ import compression from 'compression'; const bsServer = create(); bsServer.init({ - files: [ - './dist/**/*.*', - './docs/**/*.*' - ], - ghostMode: { - clicks: false, - forms : false, - scroll: false + files: ['./dist/**/*.*', './docs/**/*.*'], + ghostMode: { + clicks: false, + forms: false, + scroll: false + }, + open: false, + notify: false, + cors: true, + reloadDebounce: 1000, + reloadOnRestart: true, + server: { + baseDir: ['./docs/'], + middleware: [compression()], + routes: { + '/CHANGELOG.md': './CHANGELOG.md' + } + }, + serveStatic: ['./dist/'], + rewriteRules: [ + // Replace CDN URLs with local paths + { + match: /https?.*\/CHANGELOG.md/g, + replace: '/CHANGELOG.md' }, - open: false, - notify: false, - cors: true, - reloadDebounce: 1000, - reloadOnRestart: true, - server: { - baseDir: [ - './docs/' - ], - middleware: [ - compression() - ], - routes: { - '/CHANGELOG.md': './CHANGELOG.md' - } + { + // CDN versioned default + // Ex1: //cdn.com/package-name + // Ex2: http://cdn.com/package-name@1.0.0 + // Ex3: https://cdn.com/package-name@latest + match: /(?:https?:)*\/\/.*cdn.*docsify-tabs[@\d.latest]*(?=["'])/g, + replace: '/docsify-tabs.js' }, - serveStatic: [ - './dist/' - ], - rewriteRules: [ - // Replace CDN URLs with local paths - { - match : /https?.*\/CHANGELOG.md/g, - replace: '/CHANGELOG.md' - }, - { - // CDN versioned default - // Ex1: //cdn.com/package-name - // Ex2: http://cdn.com/package-name@1.0.0 - // Ex3: https://cdn.com/package-name@latest - match : /(?:https?:)*\/\/.*cdn.*docsify-tabs[@\d.latest]*(?=["'])/g, - replace: '/docsify-tabs.js' - }, - { - // CDN paths to local paths - // Ex1: //cdn.com/package-name/path/file.js => /path/file.js - // Ex2: http://cdn.com/package-name@1.0.0/dist/file.js => /dist/file.js - // Ex3: https://cdn.com/package-name@latest/dist/file.js => /dist/file.js - match : /(?:https?:)*\/\/.*cdn.*docsify-tabs[@\d.latest]*\/(?:dist\/)/g, - replace: '/' - } - ] + { + // CDN paths to local paths + // Ex1: //cdn.com/package-name/path/file.js => /path/file.js + // Ex2: http://cdn.com/package-name@1.0.0/dist/file.js => /dist/file.js + // Ex3: https://cdn.com/package-name@latest/dist/file.js => /dist/file.js + match: /(?:https?:)*\/\/.*cdn.*docsify-tabs[@\d.latest]*\/(?:dist\/)/g, + replace: '/' + } + ] }); diff --git a/src/css/vars.css b/src/css/vars.css index 3f74f42..ea3e57e 100644 --- a/src/css/vars.css +++ b/src/css/vars.css @@ -1,20 +1,20 @@ :root { - /* Tab blocks */ - --docsifytabs-border-color : #ededed; - --docsifytabs-border-px : 1px; - --docsifytabs-border-radius-px : ; - --docsifytabs-margin : 1.5em 0; + /* Tab blocks */ + --docsifytabs-border-color: #ededed; + --docsifytabs-border-px: 1px; + --docsifytabs-border-radius-px: ; + --docsifytabs-margin: 1.5em 0; - /* Tabs */ - --docsifytabs-tab-background : #f8f8f8; - --docsifytabs-tab-background--active: var(--docsifytabs-content-background); - --docsifytabs-tab-color : #999; - --docsifytabs-tab-color--active : inherit; - --docsifytabs-tab-highlight-px : 3px; - --docsifytabs-tab-highlight-color : var(--theme-color, currentColor); - --docsifytabs-tab-padding : 0.6em 1em; + /* Tabs */ + --docsifytabs-tab-background: #f8f8f8; + --docsifytabs-tab-background--active: var(--docsifytabs-content-background); + --docsifytabs-tab-color: #999; + --docsifytabs-tab-color--active: inherit; + --docsifytabs-tab-highlight-px: 3px; + --docsifytabs-tab-highlight-color: var(--theme-color, currentColor); + --docsifytabs-tab-padding: 0.6em 1em; - /* Tab content */ - --docsifytabs-content-background : inherit; - --docsifytabs-content-padding : 1.5rem; -} \ No newline at end of file + /* Tab content */ + --docsifytabs-content-background: inherit; + --docsifytabs-content-padding: 1.5rem; +} diff --git a/src/js/index.js b/src/js/index.js index 2d8e9be..5ac1bd4 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -3,64 +3,65 @@ import { version as pkgVersion } from '../../package.json'; import '../scss/style.scss'; - // Constants and variables // ============================================================================= const commentReplaceMark = 'tabs:replace'; const classNames = { - tabsContainer : 'content', - tabBlock : 'docsify-tabs', - tabButton : 'docsify-tabs__tab', - tabButtonActive: 'docsify-tabs__tab--active', - tabContent : 'docsify-tabs__content' + tabsContainer: 'content', + tabBlock: 'docsify-tabs', + tabButton: 'docsify-tabs__tab', + tabButtonActive: 'docsify-tabs__tab--active', + tabContent: 'docsify-tabs__content' }; const regex = { - // Matches markdown code blocks (inline and multi-line) - // Example: ```text``` - codeMarkup: /(```[\s\S]*?```)/gm, - - // Matches tab replacement comment - // 0: Match - // 1: Replacement HTML - commentReplaceMarkup: new RegExp(``), - - // Matches inner-most tab set by start/end comment - // Ex: () - // 0: Match - // 1: Indent - // 2: Start comment: - // 3: undefined - // 4: End comment: - tabBlockMarkup: /( *)()(?:(?!())[\s\S])*()/, - - // Matches tab label and content - // 0: Match - // 1: Label: - // 2: Content - tabCommentMarkup: /[\r\n]*(\s*)[\r\n]+([\s\S]*?)[\r\n]*\s*(?=)/m + // Matches markdown code blocks (inline and multi-line) + // Example: ```text``` + codeMarkup: /(```[\s\S]*?```)/gm, + + // Matches tab replacement comment + // 0: Match + // 1: Replacement HTML + commentReplaceMarkup: new RegExp(``), + + // Matches inner-most tab set by start/end comment + // Ex: () + // 0: Match + // 1: Indent + // 2: Start comment: + // 3: undefined + // 4: End comment: + tabBlockMarkup: + /( *)()(?:(?!())[\s\S])*()/, + + // Matches tab label and content + // 0: Match + // 1: Label: + // 2: Content + tabCommentMarkup: + /[\r\n]*(\s*)[\r\n]+([\s\S]*?)[\r\n]*\s*(?=)/m }; const settings = { - persist : true, - sync : true, - theme : 'classic', - tabComments: true, - tabHeadings: true + persist: true, + sync: true, + theme: 'classic', + tabComments: true, + tabHeadings: true }; const storageKeys = { - get persist() { - return `docsify-tabs.persist.${window.location.pathname}`; - }, - sync: 'docsify-tabs.sync' + get persist() { + return `docsify-tabs.persist.${window.location.pathname}`; + }, + sync: 'docsify-tabs.sync' }; - // Functions // ============================================================================= /** @@ -72,21 +73,21 @@ const storageKeys = { * @return {(object|null)} */ function getClosest(elm, closestSelectorString) { - if (Element.prototype.closest) { - return elm.closest(closestSelectorString); - } + if (Element.prototype.closest) { + return elm.closest(closestSelectorString); + } - while (elm) { - const isMatch = matchSelector(elm, closestSelectorString); + while (elm) { + const isMatch = matchSelector(elm, closestSelectorString); - if (isMatch) { - return elm; - } - - elm = elm.parentNode || null; + if (isMatch) { + return elm; } - return elm; + elm = elm.parentNode || null; + } + + return elm; } /** @@ -97,11 +98,12 @@ function getClosest(elm, closestSelectorString) { * @return {boolean} */ function matchSelector(elm, selectorString) { - const matches = Element.prototype.matches || - Element.prototype.msMatchesSelector || - Element.prototype.webkitMatchesSelector; + const matches = + Element.prototype.matches || + Element.prototype.msMatchesSelector || + Element.prototype.webkitMatchesSelector; - return matches.call(elm, selectorString); + return matches.call(elm, selectorString); } /** @@ -113,80 +115,99 @@ function matchSelector(elm, selectorString) { * @returns {string} */ function renderTabsStage1(content, vm) { - const codeBlockMatch = content.match(regex.codeMarkup) || []; - const codeBlockMarkers = codeBlockMatch.map((item, i) => { - const codeMarker = ``; - - // Replace code block with marker to ensure tab markup within code - // blocks is not processed. These markers are replaced with their - // associated code blocs after tabs have been processed. - content = content.replace(item, () => codeMarker); - - return codeMarker; - }); - const tabTheme = settings.theme ? `${classNames.tabBlock}--${settings.theme}` : ''; - const tempElm = document.createElement('div'); - - let tabBlockMatch = content.match(regex.tabBlockMarkup); - let tabIndex = 1; - - // Process each tab set - while (tabBlockMatch) { - let tabBlockOut = tabBlockMatch[0]; - - const tabBlockIndent = tabBlockMatch[1]; - const tabBlockStart = tabBlockMatch[2]; - const tabBlockEnd = tabBlockMatch[4]; - const hasTabComments = settings.tabComments && regex.tabCommentMarkup.test(tabBlockOut); - const hasTabHeadings = settings.tabHeadings && regex.tabHeadingMarkup.test(tabBlockOut); - - let tabMatch; - let tabStartReplacement = ''; - let tabEndReplacement = ''; - - if (hasTabComments || hasTabHeadings) { - tabStartReplacement = ``; - tabEndReplacement = `\n${tabBlockIndent}`; - - // Process each tab panel - while ((tabMatch = (settings.tabComments ? regex.tabCommentMarkup.exec(tabBlockOut) : null) || (settings.tabHeadings ? regex.tabHeadingMarkup.exec(tabBlockOut) : null)) !== null) { - // Process tab title as markdown - // Ex: - tempElm.innerHTML = tabMatch[2].trim() ? vm.compiler.compile(tabMatch[2]).replace(/<\/?p>/g, '') : `Tab ${tabIndex}`; - - const tabTitle = tempElm.innerHTML; - const tabContent = (tabMatch[3] || '').trim(); - const tabData = ( - tempElm.textContent || - (tempElm.firstChild.getAttribute('alt') || tempElm.firstChild.getAttribute('src')) - ).trim().toLowerCase(); - - // Use replace function to avoid regex special replacement - // strings being processed ($$, $&, $`, $', $n) - tabBlockOut = tabBlockOut.replace(tabMatch[0], () => [ - `\n${tabBlockIndent}`, - `\n${tabBlockIndent}`, - `\n\n${tabBlockIndent}${tabContent}`, - `\n\n${tabBlockIndent}`, - ].join('')); - - tabIndex++; - } - } - - tabBlockOut = tabBlockOut.replace(tabBlockStart, () => tabStartReplacement); - tabBlockOut = tabBlockOut.replace(tabBlockEnd, () => tabEndReplacement); - content = content.replace(tabBlockMatch[0], () => tabBlockOut); + const codeBlockMatch = content.match(regex.codeMarkup) || []; + const codeBlockMarkers = codeBlockMatch.map((item, i) => { + const codeMarker = ``; + + // Replace code block with marker to ensure tab markup within code + // blocks is not processed. These markers are replaced with their + // associated code blocs after tabs have been processed. + content = content.replace(item, () => codeMarker); + + return codeMarker; + }); + const tabTheme = settings.theme + ? `${classNames.tabBlock}--${settings.theme}` + : ''; + const tempElm = document.createElement('div'); + + let tabBlockMatch = content.match(regex.tabBlockMarkup); + let tabIndex = 1; + + // Process each tab set + while (tabBlockMatch) { + let tabBlockOut = tabBlockMatch[0]; + + const tabBlockIndent = tabBlockMatch[1]; + const tabBlockStart = tabBlockMatch[2]; + const tabBlockEnd = tabBlockMatch[4]; + const hasTabComments = + settings.tabComments && regex.tabCommentMarkup.test(tabBlockOut); + const hasTabHeadings = + settings.tabHeadings && regex.tabHeadingMarkup.test(tabBlockOut); + + let tabMatch; + let tabStartReplacement = ''; + let tabEndReplacement = ''; + + if (hasTabComments || hasTabHeadings) { + tabStartReplacement = ``; + tabEndReplacement = `\n${tabBlockIndent}`; + + // Process each tab panel + while ( + (tabMatch = + (settings.tabComments + ? regex.tabCommentMarkup.exec(tabBlockOut) + : null) || + (settings.tabHeadings + ? regex.tabHeadingMarkup.exec(tabBlockOut) + : null)) !== null + ) { + // Process tab title as markdown + // Ex: + tempElm.innerHTML = tabMatch[2].trim() + ? vm.compiler.compile(tabMatch[2]).replace(/<\/?p>/g, '') + : `Tab ${tabIndex}`; + + const tabTitle = tempElm.innerHTML; + const tabContent = (tabMatch[3] || '').trim(); + const tabData = ( + tempElm.textContent || + tempElm.firstChild.getAttribute('alt') || + tempElm.firstChild.getAttribute('src') + ) + .trim() + .toLowerCase(); + + // Use replace function to avoid regex special replacement + // strings being processed ($$, $&, $`, $', $n) + tabBlockOut = tabBlockOut.replace(tabMatch[0], () => + [ + `\n${tabBlockIndent}`, + `\n${tabBlockIndent}`, + `\n\n${tabBlockIndent}${tabContent}`, + `\n\n${tabBlockIndent}` + ].join('') + ); - tabBlockMatch = content.match(regex.tabBlockMarkup); + tabIndex++; + } } - // Restore code blocks - codeBlockMarkers.forEach((item, i) => { - content = content.replace(item, () => codeBlockMatch[i]); - }); + tabBlockOut = tabBlockOut.replace(tabBlockStart, () => tabStartReplacement); + tabBlockOut = tabBlockOut.replace(tabBlockEnd, () => tabEndReplacement); + content = content.replace(tabBlockMatch[0], () => tabBlockOut); - return content; + tabBlockMatch = content.match(regex.tabBlockMarkup); + } + + // Restore code blocks + codeBlockMarkers.forEach((item, i) => { + content = content.replace(item, () => codeBlockMatch[i]); + }); + + return content; } /** @@ -197,16 +218,16 @@ function renderTabsStage1(content, vm) { * @returns {string} */ function renderTabsStage2(html) { - let tabReplaceMatch; // eslint-disable-line no-unused-vars + let tabReplaceMatch; // eslint-disable-line no-unused-vars - while ((tabReplaceMatch = regex.commentReplaceMarkup.exec(html)) !== null) { - const tabComment = tabReplaceMatch[0]; - const tabReplacement = tabReplaceMatch[1] || ''; + while ((tabReplaceMatch = regex.commentReplaceMarkup.exec(html)) !== null) { + const tabComment = tabReplaceMatch[0]; + const tabReplacement = tabReplaceMatch[1] || ''; - html = html.replace(tabComment, () => tabReplacement); - } + html = html.replace(tabComment, () => tabReplacement); + } - return html; + return html; } /** @@ -215,31 +236,54 @@ function renderTabsStage2(html) { * clicked (if persist option is enabled). */ function setDefaultTabs() { - const tabsContainer = document.querySelector(`.${classNames.tabsContainer}`); - const tabBlocks = tabsContainer ? Array.apply(null, tabsContainer.querySelectorAll(`.${classNames.tabBlock}`)) : []; - const tabStoragePersist = JSON.parse(sessionStorage.getItem(storageKeys.persist)) || {}; - const tabStorageSync = JSON.parse(sessionStorage.getItem(storageKeys.sync)) || []; - - setActiveTabFromAnchor(); - - tabBlocks.forEach((tabBlock, index) => { - let activeButton = Array.apply(null, tabBlock.children).filter(elm => matchSelector(elm, `.${classNames.tabButtonActive}`))[0]; - - if (!activeButton) { - if (settings.sync && tabStorageSync.length) { - activeButton = tabStorageSync - .map(label => Array.apply(null, tabBlock.children).filter(elm => matchSelector(elm, `.${classNames.tabButton}[data-tab="${label}"]`))[0]) - .filter(elm => elm)[0]; - } - - if (!activeButton && settings.persist) { - activeButton = Array.apply(null, tabBlock.children).filter(elm => matchSelector(elm, `.${classNames.tabButton}[data-tab="${tabStoragePersist[index]}"]`))[0]; - } - - activeButton = activeButton || tabBlock.querySelector(`.${classNames.tabButton}`); - activeButton && activeButton.classList.add(classNames.tabButtonActive); - } - }); + const tabsContainer = document.querySelector(`.${classNames.tabsContainer}`); + const tabBlocks = tabsContainer + ? Array.apply( + null, + tabsContainer.querySelectorAll(`.${classNames.tabBlock}`) + ) + : []; + const tabStoragePersist = + JSON.parse(sessionStorage.getItem(storageKeys.persist)) || {}; + const tabStorageSync = + JSON.parse(sessionStorage.getItem(storageKeys.sync)) || []; + + setActiveTabFromAnchor(); + + tabBlocks.forEach((tabBlock, index) => { + let activeButton = Array.apply(null, tabBlock.children).filter(elm => + matchSelector(elm, `.${classNames.tabButtonActive}`) + )[0]; + + if (!activeButton) { + if (settings.sync && tabStorageSync.length) { + activeButton = tabStorageSync + .map( + label => + Array.apply(null, tabBlock.children).filter(elm => + matchSelector( + elm, + `.${classNames.tabButton}[data-tab="${label}"]` + ) + )[0] + ) + .filter(elm => elm)[0]; + } + + if (!activeButton && settings.persist) { + activeButton = Array.apply(null, tabBlock.children).filter(elm => + matchSelector( + elm, + `.${classNames.tabButton}[data-tab="${tabStoragePersist[index]}"]` + ) + )[0]; + } + + activeButton = + activeButton || tabBlock.querySelector(`.${classNames.tabButton}`); + activeButton && activeButton.classList.add(classNames.tabButtonActive); + } + }); } /** @@ -249,145 +293,171 @@ function setDefaultTabs() { * @param {object} elm Tab toggle element to mark as active */ function setActiveTab(elm, _isMatchingTabSync = false) { - const activeButton = getClosest(elm, `.${classNames.tabButton}`); - - if (activeButton) { - const activeButtonLabel = activeButton.getAttribute('data-tab'); - const tabsContainer = document.querySelector(`.${classNames.tabsContainer}`); - const tabBlock = activeButton.parentNode; - const tabButtons = Array.apply(null, tabBlock.children).filter(elm => matchSelector(elm, 'button')); - const tabBlockOffset = tabBlock.offsetTop; - - tabButtons.forEach(buttonElm => buttonElm.classList.remove(classNames.tabButtonActive)); - activeButton.classList.add(classNames.tabButtonActive); - - if (!_isMatchingTabSync) { - if (settings.persist) { - const tabBlocks = tabsContainer ? Array.apply(null, tabsContainer.querySelectorAll(`.${classNames.tabBlock}`)) : []; - const tabBlockIndex = tabBlocks.indexOf(tabBlock); - const tabStorage = JSON.parse(sessionStorage.getItem(storageKeys.persist)) || {}; - - tabStorage[tabBlockIndex] = activeButtonLabel; - sessionStorage.setItem(storageKeys.persist, JSON.stringify(tabStorage)); - } - - if (settings.sync) { - const tabButtonMatches = tabsContainer ? Array.apply(null, tabsContainer.querySelectorAll(`.${classNames.tabButton}[data-tab="${activeButtonLabel}"]`)) : []; - const tabStorage = JSON.parse(sessionStorage.getItem(storageKeys.sync)) || []; - - tabButtonMatches.forEach(tabButtonMatch => { - setActiveTab(tabButtonMatch, true); - }); - - // Maintain position in viewport when tab group's offset changes - window.scrollBy(0, 0 - (tabBlockOffset - tabBlock.offsetTop)); - - // Remove existing label if not first in array - if (tabStorage.indexOf(activeButtonLabel) > 0) { - tabStorage.splice(tabStorage.indexOf(activeButtonLabel), 1); - } - - // Add label if not already in first position - if (tabStorage.indexOf(activeButtonLabel) !== 0) { - tabStorage.unshift(activeButtonLabel); - sessionStorage.setItem(storageKeys.sync, JSON.stringify(tabStorage)); - } - } + const activeButton = getClosest(elm, `.${classNames.tabButton}`); + + if (activeButton) { + const activeButtonLabel = activeButton.getAttribute('data-tab'); + const tabsContainer = document.querySelector( + `.${classNames.tabsContainer}` + ); + const tabBlock = activeButton.parentNode; + const tabButtons = Array.apply(null, tabBlock.children).filter(elm => + matchSelector(elm, 'button') + ); + const tabBlockOffset = tabBlock.offsetTop; + + tabButtons.forEach(buttonElm => + buttonElm.classList.remove(classNames.tabButtonActive) + ); + activeButton.classList.add(classNames.tabButtonActive); + + if (!_isMatchingTabSync) { + if (settings.persist) { + const tabBlocks = tabsContainer + ? Array.apply( + null, + tabsContainer.querySelectorAll(`.${classNames.tabBlock}`) + ) + : []; + const tabBlockIndex = tabBlocks.indexOf(tabBlock); + const tabStorage = + JSON.parse(sessionStorage.getItem(storageKeys.persist)) || {}; + + tabStorage[tabBlockIndex] = activeButtonLabel; + sessionStorage.setItem(storageKeys.persist, JSON.stringify(tabStorage)); + } + + if (settings.sync) { + const tabButtonMatches = tabsContainer + ? Array.apply( + null, + tabsContainer.querySelectorAll( + `.${classNames.tabButton}[data-tab="${activeButtonLabel}"]` + ) + ) + : []; + const tabStorage = + JSON.parse(sessionStorage.getItem(storageKeys.sync)) || []; + + tabButtonMatches.forEach(tabButtonMatch => { + setActiveTab(tabButtonMatch, true); + }); + + // Maintain position in viewport when tab group's offset changes + window.scrollBy(0, 0 - (tabBlockOffset - tabBlock.offsetTop)); + + // Remove existing label if not first in array + if (tabStorage.indexOf(activeButtonLabel) > 0) { + tabStorage.splice(tabStorage.indexOf(activeButtonLabel), 1); } + + // Add label if not already in first position + if (tabStorage.indexOf(activeButtonLabel) !== 0) { + tabStorage.unshift(activeButtonLabel); + sessionStorage.setItem(storageKeys.sync, JSON.stringify(tabStorage)); + } + } } + } } /** * Sets the active tab based on the anchor ID in the URL */ function setActiveTabFromAnchor() { - const anchorID = decodeURIComponent((window.location.hash.match(/(?:id=)([^&]+)/) || [])[1]); - const anchorSelector = anchorID && `.${classNames.tabBlock} #${anchorID}`; - const isAnchorElmInTabBlock = anchorID && document.querySelector(anchorSelector); - - if (isAnchorElmInTabBlock) { - const anchorElm = document.querySelector(`#${anchorID}`); - - let tabContent; - - if (anchorElm.closest) { - tabContent = anchorElm.closest(`.${classNames.tabContent}`); - } - else { - tabContent = anchorElm.parentNode; - - while (tabContent !== document.body && !tabContent.classList.contains(`${classNames.tabContent}`)) { - tabContent = tabContent.parentNode; - } - } - - setActiveTab(tabContent.previousElementSibling); + const anchorID = decodeURIComponent( + (window.location.hash.match(/(?:id=)([^&]+)/) || [])[1] + ); + const anchorSelector = anchorID && `.${classNames.tabBlock} #${anchorID}`; + const isAnchorElmInTabBlock = + anchorID && document.querySelector(anchorSelector); + + if (isAnchorElmInTabBlock) { + const anchorElm = document.querySelector(`#${anchorID}`); + + let tabContent; + + if (anchorElm.closest) { + tabContent = anchorElm.closest(`.${classNames.tabContent}`); + } else { + tabContent = anchorElm.parentNode; + + while ( + tabContent !== document.body && + !tabContent.classList.contains(`${classNames.tabContent}`) + ) { + tabContent = tabContent.parentNode; + } } -} + setActiveTab(tabContent.previousElementSibling); + } +} // Plugin // ============================================================================= function docsifyTabs(hook, vm) { - let hasTabs = false; + let hasTabs = false; - hook.beforeEach(function(content) { - hasTabs = regex.tabBlockMarkup.test(content); + hook.beforeEach(function (content) { + hasTabs = regex.tabBlockMarkup.test(content); - if (hasTabs) { - content = renderTabsStage1(content, vm); - } + if (hasTabs) { + content = renderTabsStage1(content, vm); + } - return content; - }); + return content; + }); - hook.afterEach(function(html, next) { - if (hasTabs) { - html = renderTabsStage2(html); - } + hook.afterEach(function (html, next) { + if (hasTabs) { + html = renderTabsStage2(html); + } - next(html); - }); + next(html); + }); - hook.doneEach(function() { - if (hasTabs) { - setDefaultTabs(); - } - }); + hook.doneEach(function () { + if (hasTabs) { + setDefaultTabs(); + } + }); - hook.mounted(function() { - const tabsContainer = document.querySelector(`.${classNames.tabsContainer}`); + hook.mounted(function () { + const tabsContainer = document.querySelector( + `.${classNames.tabsContainer}` + ); - tabsContainer && tabsContainer.addEventListener('click', function handleTabClick(evt) { - setActiveTab(evt.target); - }); + tabsContainer && + tabsContainer.addEventListener('click', function handleTabClick(evt) { + setActiveTab(evt.target); + }); - window.addEventListener('hashchange', setActiveTabFromAnchor, false); - }); + window.addEventListener('hashchange', setActiveTabFromAnchor, false); + }); } - if (window) { - window.$docsify = window.$docsify || {}; - - // Add config object - window.$docsify.tabs = window.$docsify.tabs || {}; + window.$docsify = window.$docsify || {}; - // Update settings based on $docsify config - Object.keys(window.$docsify.tabs).forEach(key => { - if (Object.prototype.hasOwnProperty.call(settings, key)) { - settings[key] = window.$docsify.tabs[key]; - } - }); + // Add config object + window.$docsify.tabs = window.$docsify.tabs || {}; - // Add plugin data - window.$docsify.tabs.version = pkgVersion; - - // Init plugin - if (settings.tabComments || settings.tabHeadings) { - window.$docsify.plugins = [].concat( - (window.$docsify.plugins || []), - docsifyTabs - ); + // Update settings based on $docsify config + Object.keys(window.$docsify.tabs).forEach(key => { + if (Object.prototype.hasOwnProperty.call(settings, key)) { + settings[key] = window.$docsify.tabs[key]; } + }); + + // Add plugin data + window.$docsify.tabs.version = pkgVersion; + + // Init plugin + if (settings.tabComments || settings.tabHeadings) { + window.$docsify.plugins = [].concat( + window.$docsify.plugins || [], + docsifyTabs + ); + } } diff --git a/src/scss/style.scss b/src/scss/style.scss index d219a4b..2c4769d 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -1,147 +1,152 @@ -@use "./../css/vars.css"; +@use './../css/vars.css'; // Base // ============================================================================= .docsify-tabs:before, .docsify-tabs__tab { - z-index: 1; + z-index: 1; } .docsify-tabs__tab:focus, .docsify-tabs__tab--active { - z-index: 2; + z-index: 2; } .docsify-tabs { - display: flex; - flex-wrap: wrap; - position: relative; - - &:before { - content: ''; - order: 0; - flex: 1; - } + display: flex; + flex-wrap: wrap; + position: relative; + + &:before { + content: ''; + order: 0; + flex: 1; + } } .docsify-tabs__tab { - order: -1; - position: relative; - margin: 0; - font-size: inherit; - appearance: none; + order: -1; + position: relative; + margin: 0; + font-size: inherit; + appearance: none; } -.docsify-tabs__content[class] { // Add weight instead of !important - visibility: hidden; - position: absolute; - overflow: hidden; - height: 0; - width: 100%; +.docsify-tabs__content[class] { + // Add weight instead of !important + visibility: hidden; + position: absolute; + overflow: hidden; + height: 0; + width: 100%; - > :first-child { - margin-top: 0; - } + > :first-child { + margin-top: 0; + } - > :last-child { - margin-bottom: 0; - } + > :last-child { + margin-bottom: 0; + } - .docsify-tabs__tab--active + & { - visibility: visible; - position: relative; - overflow: auto; - height: auto; - } + .docsify-tabs__tab--active + & { + visibility: visible; + position: relative; + overflow: auto; + height: auto; + } } - // Themes // ============================================================================= -[class*="docsify-tabs--"] { - margin: var(--docsifytabs-margin); - - > .docsify-tabs__tab { - padding: var(--docsifytabs-tab-padding); - background: var(--docsifytabs-tab-background); - color: var(--docsifytabs-tab-color); - } - - > .docsify-tabs__tab--active { - background: var(--docsifytabs-tab-background--active); - color: var(--docsifytabs-tab-color--active); - } - - > .docsify-tabs__content { - background: var(--docsifytabs-content-background); - } - - > .docsify-tabs__tab--active + .docsify-tabs__content { - padding: var(--docsifytabs-content-padding); - } +[class*='docsify-tabs--'] { + margin: var(--docsifytabs-margin); + + > .docsify-tabs__tab { + padding: var(--docsifytabs-tab-padding); + background: var(--docsifytabs-tab-background); + color: var(--docsifytabs-tab-color); + } + + > .docsify-tabs__tab--active { + background: var(--docsifytabs-tab-background--active); + color: var(--docsifytabs-tab-color--active); + } + + > .docsify-tabs__content { + background: var(--docsifytabs-content-background); + } + + > .docsify-tabs__tab--active + .docsify-tabs__content { + padding: var(--docsifytabs-content-padding); + } } // Classic // ----------------------------------------------------------------------------- .docsify-tabs--classic { - &:before, - > .docsify-tabs__tab, - > .docsify-tabs__content { - border-width: var(--docsifytabs-border-px); - border-style: solid; - border-color: var(--docsifytabs-border-color); - } - - &:before { - margin-right: var(--docsifytabs-border-px); - border-top-width: 0; - border-left-width: 0; - border-right-width: 0; - } - - > .docsify-tabs__tab { - &:first-of-type { - border-top-left-radius: var(--docsifytabs-border-radius-px); - } - - &:last-of-type { - border-top-right-radius: var(--docsifytabs-border-radius-px); - } - } - - > .docsify-tabs__tab ~ .docsify-tabs__tab { - margin-left: calc(0px - var(--docsifytabs-border-px)); - } - - > .docsify-tabs__tab--active { - border-bottom-width: 0; - box-shadow: inset 0 var(--docsifytabs-tab-highlight-px) 0 0 var(--docsifytabs-tab-highlight-color); - } - - > .docsify-tabs__content { - margin-top: calc(0px - var(--docsifytabs-border-px)); - border-top: 0; - border-radius: 0 var(--docsifytabs-border-radius-px) var(--docsifytabs-border-radius-px) var(--docsifytabs-border-radius-px); - } + &:before, + > .docsify-tabs__tab, + > .docsify-tabs__content { + border-width: var(--docsifytabs-border-px); + border-style: solid; + border-color: var(--docsifytabs-border-color); + } + + &:before { + margin-right: var(--docsifytabs-border-px); + border-top-width: 0; + border-left-width: 0; + border-right-width: 0; + } + + > .docsify-tabs__tab { + &:first-of-type { + border-top-left-radius: var(--docsifytabs-border-radius-px); + } + + &:last-of-type { + border-top-right-radius: var(--docsifytabs-border-radius-px); + } + } + + > .docsify-tabs__tab ~ .docsify-tabs__tab { + margin-left: calc(0px - var(--docsifytabs-border-px)); + } + + > .docsify-tabs__tab--active { + border-bottom-width: 0; + box-shadow: inset 0 var(--docsifytabs-tab-highlight-px) 0 0 + var(--docsifytabs-tab-highlight-color); + } + + > .docsify-tabs__content { + margin-top: calc(0px - var(--docsifytabs-border-px)); + border-top: 0; + border-radius: 0 var(--docsifytabs-border-radius-px) + var(--docsifytabs-border-radius-px) var(--docsifytabs-border-radius-px); + } } // Material // ----------------------------------------------------------------------------- .docsify-tabs--material { - > .docsify-tabs__tab { - margin-bottom: calc(var(--docsifytabs-tab-highlight-px) - var(--docsifytabs-border-px)); - background: transparent; - border: 0; - } - - > .docsify-tabs__tab--active { - box-shadow: 0 var(--docsifytabs-tab-highlight-px) 0 0 var(--docsifytabs-tab-highlight-color); - background: transparent; - } - - > .docsify-tabs__content { - border-width: var(--docsifytabs-border-px) 0; - border-style: solid; - border-color: var(--docsifytabs-border-color); - } + > .docsify-tabs__tab { + margin-bottom: calc( + var(--docsifytabs-tab-highlight-px) - var(--docsifytabs-border-px) + ); + background: transparent; + border: 0; + } + + > .docsify-tabs__tab--active { + box-shadow: 0 var(--docsifytabs-tab-highlight-px) 0 0 + var(--docsifytabs-tab-highlight-color); + background: transparent; + } + + > .docsify-tabs__content { + border-width: var(--docsifytabs-border-px) 0; + border-style: solid; + border-color: var(--docsifytabs-border-color); + } }