├── .github ├── README.md ├── images │ └── demo.jpg └── workflows │ └── publish.yml ├── .gitignore ├── .nvmrc ├── .yarn ├── releases │ └── yarn-4.8.1.cjs └── versions │ ├── 21f98b06.yml │ ├── 4e0236a4.yml │ ├── 738a0150.yml │ ├── 7c8e8bbb.yml │ ├── 8890918f.yml │ ├── 9706cd5b.yml │ └── aa836340.yml ├── .yarnrc.yml ├── LICENSE ├── package.json ├── packages ├── example │ ├── package.json │ ├── src │ │ ├── main.ts │ │ └── user │ │ │ ├── Admin.ts │ │ │ └── User.ts │ ├── tsconfig.json │ └── typedoc.json └── plugin │ ├── .eslintignore │ ├── .eslintrc.cjs │ ├── LICENSE │ ├── README.md │ ├── assets │ ├── css │ │ └── custom.css │ ├── images │ │ ├── folder-open.svg │ │ ├── folder.svg │ │ └── ts.svg │ └── js │ │ ├── HierarchyManager.ts │ │ ├── StateManager.ts │ │ └── custom.ts │ ├── package.json │ ├── src │ ├── index.tsx │ ├── partials │ │ └── navigation.tsx │ └── themes │ │ ├── OverrideTheme.tsx │ │ └── OverrideThemeContext.tsx │ ├── tsconfig.json │ ├── tsup.config.ts │ └── webpack.config.cjs └── yarn.lock /.github/README.md: -------------------------------------------------------------------------------- 1 | ../packages/plugin/README.md -------------------------------------------------------------------------------- /.github/images/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.github/images/demo.jpg -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and Publish 2 | 3 | on: push 4 | 5 | jobs: 6 | build-and-publish: 7 | runs-on: ubuntu-latest 8 | defaults: 9 | run: 10 | working-directory: ./packages/plugin 11 | env: 12 | NPM_DIFUKS_TOKEN: ${{ secrets.NPM_DIFUKS_TOKEN }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | token: ${{ secrets.PERSONAL_TOKEN }} 17 | 18 | # for yarn version check 19 | - if: github.ref != 'refs/heads/master' 20 | run: | 21 | git fetch --unshallow origin master 22 | 23 | - uses: actions/setup-node@v4 24 | with: 25 | node-version-file: .nvmrc 26 | cache: yarn 27 | cache-dependency-path: yarn.lock 28 | 29 | - name: Install modules 30 | run: yarn 31 | 32 | - name: Build plugin 33 | run: yarn build 34 | 35 | - name: Lint plugin 36 | run: yarn lint 37 | 38 | - name: Check packages versions 39 | if: github.ref != 'refs/heads/master' 40 | run: yarn version check 41 | 42 | - name: Apply versions 43 | if: github.ref == 'refs/heads/master' 44 | run: yarn version apply 45 | 46 | - name: Publish package 47 | if: github.ref == 'refs/heads/master' 48 | run: yarn npm publish --tolerate-republish 49 | 50 | - uses: stefanzweifel/git-auto-commit-action@v5 51 | if: github.ref == 'refs/heads/master' 52 | with: 53 | commit_message: | 54 | ci: Release packages 55 | 56 | [skip ci] 57 | 58 | - name: Get package version 59 | if: github.ref == 'refs/heads/master' 60 | id: package-version 61 | uses: martinbeentjes/npm-get-version-action@v1.3.1 62 | with: 63 | path: packages/plugin 64 | 65 | - uses: stefanzweifel/git-auto-commit-action@v5 66 | if: github.ref == 'refs/heads/master' 67 | with: 68 | tagging_message: v${{ steps.package-version.outputs.current-version }} 69 | commit_message: | 70 | ci: Release packages 71 | 72 | [skip ci] 73 | 74 | - name: Get pull request info 75 | if: github.ref == 'refs/heads/master' 76 | id: pull-request-info 77 | uses: actions-ecosystem/action-get-merged-pull-request@v1.0.1 78 | with: 79 | github_token: ${{ secrets.GITHUB_TOKEN }} 80 | 81 | - uses: ncipollo/release-action@v1 82 | id: create-release 83 | if: github.ref == 'refs/heads/master' 84 | with: 85 | skipIfReleaseExists: true 86 | makeLatest: true 87 | name: Release ${{ steps.package-version.outputs.current-version }} 88 | body: ${{ steps.pull-request-info.outputs.body || github.event.head_commit.message }} 89 | tag: v${{ steps.package-version.outputs.current-version }} 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | tsconfig.*tsbuildinfo 3 | docs 4 | .idea/ 5 | .pnp.* 6 | .yarn/* 7 | node_modules 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/sdks 12 | !.yarn/versions 13 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v22 2 | -------------------------------------------------------------------------------- /.yarn/versions/21f98b06.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/21f98b06.yml -------------------------------------------------------------------------------- /.yarn/versions/4e0236a4.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/4e0236a4.yml -------------------------------------------------------------------------------- /.yarn/versions/738a0150.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/738a0150.yml -------------------------------------------------------------------------------- /.yarn/versions/7c8e8bbb.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/7c8e8bbb.yml -------------------------------------------------------------------------------- /.yarn/versions/8890918f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/8890918f.yml -------------------------------------------------------------------------------- /.yarn/versions/9706cd5b.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/9706cd5b.yml -------------------------------------------------------------------------------- /.yarn/versions/aa836340.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/1c1236e5ed4e126f8fdcaab394bf106f1a4c06c8/.yarn/versions/aa836340.yml -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | enableGlobalCache: true 2 | 3 | nodeLinker: pnp 4 | 5 | npmAuthToken: "${NPM_DIFUKS_TOKEN:-}" 6 | 7 | npmPublishAccess: public 8 | 9 | npmRegistryServer: "https://registry.npmjs.com/" 10 | 11 | packageExtensions: 12 | eslint-module-utils@*: 13 | dependencies: 14 | eslint-import-resolver-typescript: 3.6.1 15 | typedoc@*: 16 | peerDependencies: 17 | typedoc-theme-hierarchy: "*" 18 | debug@*: 19 | dependencies: 20 | "supports-color": "*" 21 | 22 | yarnPath: .yarn/releases/yarn-4.8.1.cjs 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dmitriy Fuks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "packageManager": "yarn@4.8.1" 7 | } 8 | -------------------------------------------------------------------------------- /packages/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "scripts": { 4 | "build": "tsc", 5 | "doc": "typedoc && http-server public/docs" 6 | }, 7 | "devDependencies": { 8 | "http-server": "14.1.1", 9 | "typedoc": "0.28.1", 10 | "typedoc-theme-hierarchy": "workspace:*", 11 | "typescript": "5.8.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/example/src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Main interface 3 | */ 4 | export interface Main { 5 | getDate(date: any): string; 6 | } 7 | -------------------------------------------------------------------------------- /packages/example/src/user/Admin.ts: -------------------------------------------------------------------------------- 1 | import { User } from './User'; 2 | 3 | export interface Admin extends User { 4 | role: 'admin'; 5 | } 6 | -------------------------------------------------------------------------------- /packages/example/src/user/User.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | name: string; 3 | age: number; 4 | } 5 | -------------------------------------------------------------------------------- /packages/example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "outDir": "./dist", 5 | "strict": false, 6 | "noEmit": false, 7 | "skipLibCheck": true, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /packages/example/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["src"], 3 | "entryPointStrategy": "expand", 4 | "out": "public/docs", 5 | "plugin": ["typedoc-theme-hierarchy"], 6 | "theme": "hierarchy", 7 | "tsconfig": "./tsconfig.json", 8 | "name": "Project name" 9 | } 10 | -------------------------------------------------------------------------------- /packages/plugin/.eslintignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /packages/plugin/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | require(`@rushstack/eslint-patch/modern-module-resolution`); 2 | 3 | module.exports = { 4 | extends: [`eslint-config-fuks`], 5 | parserOptions: { 6 | project: `./tsconfig.json`, 7 | sourceType: `module`, 8 | }, 9 | rules: { 10 | 'no-relative-imports/no-relative-imports': `off`, 11 | 'react/no-unknown-property': `off`, 12 | 'react/jsx-key': `off`, 13 | 'react/display-name': `off`, 14 | 'unicorn/prefer-node-protocol': `off`, 15 | 'react/function-component-definition': `off`, 16 | 'react/destructuring-assignment': `off`, 17 | 'react/button-has-type': `off`, 18 | 'react/jsx-props-no-spreading': `off`, 19 | 'no-underscore-dangle': `off`, 20 | '@typescript-eslint/no-use-before-define': `off`, 21 | 'jsx-a11y/control-has-associated-label': `off`, 22 | }, 23 | overrides: [ 24 | { 25 | files: [`*.json`], 26 | parserOptions: { 27 | project: false, 28 | }, 29 | }, 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /packages/plugin/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dmitriy Fuks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/plugin/README.md: -------------------------------------------------------------------------------- 1 | # Typedoc Theme Hierarchy 2 | 3 | Hierarchy theme for [typedoc](https://typedoc.org/) 4 | 5 | [![typedoc-theme-hierarchy (latest)](https://img.shields.io/npm/v/typedoc-theme-hierarchy)](https://www.npmjs.com/package/typedoc-theme-hierarchy) 6 | [![typedoc-theme-hierarchy (downloads)](https://img.shields.io/npm/dw/typedoc-theme-hierarchy)](https://www.npmjs.com/package/typedoc-theme-hierarchy) 7 | [![typedoc-theme-hierarchy (stars)](https://img.shields.io/github/stars/difuks/typedoc-theme-hierarchy?style=social)](https://github.com/DiFuks/typedoc-theme-hierarchy) 8 | 9 | See [example here](https://github.com/DiFuks/typedoc-theme-hierarchy/tree/master/packages/example) 10 | 11 | The plugin supports only `expand` and `resolve` values for the `entryPointStrategy` option. Support for `packages` is planned for the future. Please create an issue if you need it. 12 | 13 | ![example](https://raw.githubusercontent.com/DiFuks/typedoc-theme-hierarchy/master/.github/images/demo.jpg) 14 | 15 | ## Installing 16 | 17 | ```bash 18 | # For typedoc ^0.28.0 19 | npm i typedoc-theme-hierarchy@^6.0.0 -D 20 | 21 | # For typedoc ^0.26.0 || ^0.27.0 22 | npm i typedoc-theme-hierarchy@^5.0.0 -D 23 | 24 | # For typedoc ^0.24.0 || ^0.25.0 25 | npm i typedoc-theme-hierarchy@^4.0.0 -D 26 | 27 | # For typedoc ^0.23.6 28 | npm i typedoc-theme-hierarchy@^3.0.0 -D 29 | 30 | # For typedoc 0.23.5 31 | npm i typedoc-theme-hierarchy@^2.0.0 -D 32 | 33 | # For typedoc ^0.22.0 || <=0.23.4 34 | npm i typedoc-theme-hierarchy@^1.3.5 -D 35 | ``` 36 | 37 | ## Usage 38 | 39 | From terminal: 40 | 41 | ```bash 42 | typedoc --entryPoints src --entryPointStrategy expand --out docs --plugin typedoc-theme-hierarchy --theme hierarchy 43 | ``` 44 | 45 | From `typedoc.json`: 46 | 47 | ```json 48 | { 49 | "entryPoints": ["src"], 50 | "entryPointStrategy": "Expand", 51 | "out": "public/docs", 52 | "plugin": ["typedoc-theme-hierarchy"], 53 | "theme": "hierarchy", 54 | "tsconfig": "./tsconfig.json", 55 | "name": "Project name" 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /packages/plugin/assets/css/custom.css: -------------------------------------------------------------------------------- 1 | .tree { 2 | margin-top: 20px; 3 | background: var(--color-panel); 4 | } 5 | 6 | .tree-config { 7 | display: flex; 8 | gap: 8px; 9 | justify-content: end; 10 | padding: 8px; 11 | } 12 | 13 | .tree-config__button { 14 | border: 0; 15 | cursor: pointer; 16 | height: 20px; 17 | padding: 0; 18 | width: 20px; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | background-color: transparent; 23 | color: var(--color-toolbar-text); 24 | opacity: 0.8; 25 | } 26 | 27 | .tree-config__button:hover { 28 | opacity: 0.9; 29 | } 30 | 31 | .tree-content { 32 | position: relative; 33 | padding: 0 20px 20px; 34 | font-size: 0.85rem; 35 | font-weight: 400; 36 | line-height: 1.5; 37 | color: var(--color-text); 38 | } 39 | 40 | .tree-content span { 41 | font-size: 13px; 42 | letter-spacing: 0.4px; 43 | } 44 | 45 | .tree-content ul { 46 | padding-left: 5px; 47 | list-style: none; 48 | margin: 0; 49 | } 50 | 51 | .tree-content ul li { 52 | position: relative; 53 | padding-top: 5px; 54 | padding-bottom: 5px; 55 | padding-left: 15px; 56 | -webkit-box-sizing: border-box; 57 | -moz-box-sizing: border-box; 58 | box-sizing: border-box; 59 | } 60 | 61 | .tree-content ul li:before { 62 | position: absolute; 63 | top: 15px; 64 | left: 0; 65 | width: 10px; 66 | height: 1px; 67 | margin: auto; 68 | content: ""; 69 | background-color: #666; 70 | } 71 | 72 | .tree-content ul li:after { 73 | position: absolute; 74 | top: 0; 75 | bottom: 0; 76 | left: 0; 77 | width: 1px; 78 | height: 100%; 79 | content: ""; 80 | background-color: #666; 81 | } 82 | 83 | .tree-content ul li:last-child:after { 84 | height: 15px; 85 | } 86 | 87 | .tree-content ul a { 88 | cursor: pointer; 89 | } 90 | 91 | .category:not([data-id="root"]) { 92 | display: none; 93 | 94 | } 95 | 96 | .category:not([data-id="root"])._open { 97 | display: block; 98 | } 99 | 100 | .category__title { 101 | cursor: pointer; 102 | color: var(--color-text-aside); 103 | } 104 | 105 | .category__title, .category__link { 106 | text-decoration: none; 107 | display: flex; 108 | align-items: center; 109 | flex-shrink: 0; 110 | } 111 | 112 | a.category__title:hover, a.category__link:hover { 113 | text-decoration: underline; 114 | } 115 | 116 | .category__title._open .category__folder { 117 | background: url("../images/folder-open.svg"); 118 | } 119 | 120 | .category__folder { 121 | display: inline-block; 122 | height: 15px; 123 | width: 15px; 124 | background: url("../images/folder.svg"); 125 | margin-right: 6px; 126 | flex-shrink: 0; 127 | } 128 | 129 | .category__link._active { 130 | color: inherit; 131 | } 132 | 133 | .category__link--ts:before { 134 | content: ""; 135 | display: inline-block; 136 | width: 15px; 137 | height: 15px; 138 | margin: 0 7px 2px 0; 139 | background-image: url(../images/ts.svg); 140 | vertical-align: middle; 141 | flex-shrink: 0; 142 | } 143 | -------------------------------------------------------------------------------- /packages/plugin/assets/images/folder-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/plugin/assets/images/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/plugin/assets/images/ts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | TypeScript logo 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/plugin/assets/js/HierarchyManager.ts: -------------------------------------------------------------------------------- 1 | import { StateManager } from './StateManager.js'; 2 | 3 | export class HierarchyManager { 4 | private readonly stateManager = new StateManager(); 5 | 6 | private readonly titleSelector = `.js-category-title`; 7 | 8 | private readonly listSelector = `.js-category-list`; 9 | 10 | public init(): void { 11 | this.addListeners(); 12 | this.initSaved(); 13 | this.openCurrentPath(); 14 | } 15 | 16 | private openPathAndSave(id: string): void { 17 | this.openPath(id); 18 | 19 | this.stateManager.addOpenedPath(id); 20 | } 21 | 22 | private openPath(id: string): void { 23 | const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); 24 | 25 | if (!list) { 26 | return; 27 | } 28 | 29 | list.classList.add(`_open`); 30 | list.parentNode?.querySelector(this.titleSelector)?.classList.add(`_open`); 31 | } 32 | 33 | private closePath(id: string): void { 34 | const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); 35 | 36 | if (!list) { 37 | return; 38 | } 39 | 40 | list.classList.remove(`_open`); 41 | list.parentNode?.querySelector(this.titleSelector)?.classList.remove(`_open`); 42 | 43 | this.stateManager.removeOpenedPath(id); 44 | } 45 | 46 | private closePathWithChildren(id: string): void { 47 | this.closePath(id); 48 | 49 | const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); 50 | 51 | if (!list) { 52 | return; 53 | } 54 | 55 | const childLists = list.querySelectorAll(this.listSelector); 56 | 57 | for (const item of childLists) { 58 | this.closePath(item.dataset.id || ``); 59 | } 60 | } 61 | 62 | private togglePath(id: string): void { 63 | const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); 64 | 65 | if (!list) { 66 | return; 67 | } 68 | 69 | if (list.classList.contains(`_open`)) { 70 | this.closePathWithChildren(id); 71 | 72 | return; 73 | } 74 | 75 | this.openPathAndSave(id); 76 | } 77 | 78 | private addListeners(): void { 79 | const items = document.querySelectorAll(`.js-category-title:not([data-id="root"])`); 80 | 81 | for (const item of items) { 82 | item.addEventListener(`click`, () => { 83 | const id = item.dataset.id || ``; 84 | 85 | this.togglePath(id); 86 | }); 87 | } 88 | 89 | this.addExpandListener(); 90 | this.addCollapseListener(); 91 | this.addTargetListener(); 92 | } 93 | 94 | private addExpandListener(): void { 95 | const expandButton = document.querySelector(`.js-tree-expand`); 96 | 97 | expandButton?.addEventListener(`click`, () => { 98 | const items = document.querySelectorAll(this.listSelector); 99 | 100 | for (const item of items) { 101 | const id = item.dataset.id || ``; 102 | 103 | this.openPathAndSave(id); 104 | } 105 | }); 106 | } 107 | 108 | private addCollapseListener(): void { 109 | const collapseButton = document.querySelector(`.js-tree-collapse`); 110 | 111 | collapseButton?.addEventListener(`click`, () => { 112 | const items = document.querySelectorAll(this.listSelector); 113 | 114 | for (const item of items) { 115 | const id = item.dataset.id || ``; 116 | 117 | this.closePath(id); 118 | } 119 | }); 120 | } 121 | 122 | private addTargetListener(): void { 123 | const targetButton = document.querySelector(`.js-tree-target`); 124 | 125 | targetButton?.addEventListener(`click`, () => { 126 | const targetElement = this.openCurrentPath(); 127 | 128 | targetElement?.scrollIntoView(); 129 | }); 130 | } 131 | 132 | private initSaved(): void { 133 | const savedPaths = this.stateManager.getOpenedPaths(); 134 | 135 | for (const id of savedPaths) { 136 | this.openPath(id); 137 | } 138 | } 139 | 140 | private openCurrentPath(): Element | null { 141 | const pathnameSplit = window.location.pathname.split(`/`); 142 | const pathname = `/${pathnameSplit[pathnameSplit.length - 2] || ``}/${ 143 | pathnameSplit[pathnameSplit.length - 1] || `` 144 | }`; 145 | 146 | const activeElement = document.querySelector(`.js-category-link[data-id="${pathname}"]`); 147 | 148 | if (!activeElement) { 149 | return null; 150 | } 151 | 152 | activeElement.classList.add(`_active`); 153 | 154 | let parent = activeElement.closest(this.listSelector); 155 | 156 | // eslint-disable-next-line no-constant-condition,@typescript-eslint/no-unnecessary-condition 157 | while (true) { 158 | if (!parent) { 159 | break; 160 | } 161 | 162 | const id = parent.dataset.id || ``; 163 | 164 | this.openPath(id); 165 | 166 | parent = (parent.parentNode as HTMLElement).closest(this.listSelector); 167 | } 168 | 169 | return activeElement; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /packages/plugin/assets/js/StateManager.ts: -------------------------------------------------------------------------------- 1 | export class StateManager { 2 | private readonly openedPathLsKey = `opened-path-state`; 3 | 4 | private openedPaths: string[] = []; 5 | 6 | public constructor() { 7 | const lsOpenedPathState = localStorage.getItem(`opened-path-state`); 8 | 9 | this.openedPaths = lsOpenedPathState ? (JSON.parse(lsOpenedPathState) as string[]) : []; 10 | } 11 | 12 | /** 13 | * Добавляет path в стейт. 14 | */ 15 | public addOpenedPath(path: string): void { 16 | this.openedPaths.push(path); 17 | this.updateState(); 18 | } 19 | 20 | /** 21 | * Удаляет path из стейта. 22 | */ 23 | public removeOpenedPath(path: string): void { 24 | this.openedPaths = this.openedPaths.filter(savedPath => savedPath !== path); 25 | this.updateState(); 26 | } 27 | 28 | /** 29 | * Получает все открытые paths. 30 | */ 31 | public getOpenedPaths(): string[] { 32 | return this.openedPaths; 33 | } 34 | 35 | private updateState(): void { 36 | localStorage.setItem(this.openedPathLsKey, JSON.stringify(this.openedPaths)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/plugin/assets/js/custom.ts: -------------------------------------------------------------------------------- 1 | import { HierarchyManager } from './HierarchyManager.js'; 2 | 3 | import '../css/custom.css'; 4 | 5 | const hierarchyManager = new HierarchyManager(); 6 | 7 | hierarchyManager.init(); 8 | -------------------------------------------------------------------------------- /packages/plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typedoc-theme-hierarchy", 3 | "version": "6.0.0", 4 | "license": "MIT", 5 | "type": "module", 6 | "main": "dist/index.cjs", 7 | "module": "dist/index.js", 8 | "exports": { 9 | ".": { 10 | "import": "./dist/index.js", 11 | "require": "./dist/index.cjs" 12 | } 13 | }, 14 | "repository": "git@github.com:DiFuks/typedoc-theme-hierarchy.git", 15 | "homepage": "https://github.com/DiFuks/typedoc-theme-hierarchy", 16 | "description": "Hierarchy theme for typedoc", 17 | "keywords": [ 18 | "typedoc-theme", 19 | "typedocplugin" 20 | ], 21 | "files": [ 22 | "dist", 23 | "LICENSE", 24 | "README.md" 25 | ], 26 | "author": { 27 | "name": "Dmitry Fuks", 28 | "email": "difuks@gmail.com", 29 | "url": "https://github.com/DiFuks" 30 | }, 31 | "devDependencies": { 32 | "@rushstack/eslint-patch": "^1.1.4", 33 | "@types/fs-extra": "^9.0.13", 34 | "@types/mini-css-extract-plugin": "^2.4.0", 35 | "@types/webpack": "^5.28.0", 36 | "clean-webpack-plugin": "^4.0.0", 37 | "css-loader": "^6.5.1", 38 | "css-minimizer-webpack-plugin": "^3.3.1", 39 | "eslint": "^8.18.0", 40 | "eslint-config-fuks": "2.0.0", 41 | "mini-css-extract-plugin": "^2.4.5", 42 | "terser-webpack-plugin": "^5.3.3", 43 | "ts-loader": "^9.4.4", 44 | "tsup": "^8.4.0", 45 | "typedoc": "0.28.1", 46 | "typescript": "5.8.2", 47 | "webpack": "5.92.1", 48 | "webpack-cli": "5.1.4" 49 | }, 50 | "peerDependencies": { 51 | "typedoc": "^0.28.0" 52 | }, 53 | "scripts": { 54 | "build": "webpack && tsup", 55 | "lint": "eslint \"./**/*.{ts,tsx,js}\"" 56 | }, 57 | "dependencies": { 58 | "fs-extra": "11.1.1" 59 | }, 60 | "packageManager": "yarn@3.6.1" 61 | } 62 | -------------------------------------------------------------------------------- /packages/plugin/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { type Application, JSX } from 'typedoc'; 2 | 3 | import { OverrideTheme } from './themes/OverrideTheme.js'; 4 | 5 | /** 6 | * Инициализирует плагин с темой. 7 | */ 8 | export const load = (app: Application): void => { 9 | app.renderer.hooks.on( 10 | `head.end`, 11 | (context): JSX.Element => , 12 | ); 13 | 14 | app.renderer.hooks.on( 15 | `body.end`, 16 | (context): JSX.Element =>