| 1 | <!-- |
| 2 | Table Of content |
| 3 | |
| 4 | `mode` can be either 'all', 'onlyCurrentTree' or 'onlySiblings' |
| 5 | |
| 6 | Usage: |
| 7 | |
| 8 | <Toc columns='2' maxDepth='3' mode='onlySiblings'/> |
| 9 | --> |
| 10 | <script setup lang='ts'> |
| 11 | import type { TocItem } from '@slidev/types' |
| 12 | import { computed } from 'vue' |
| 13 | import { useSlideContext } from '../context' |
| 14 | import TocList from './TocList.vue' |
| 15 | |
| 16 | const props = withDefaults( |
| 17 | defineProps<{ |
| 18 | columns?: string | number |
| 19 | listClass?: string | string[] |
| 20 | start?: string | number |
| 21 | listStyle?: string | string[] |
| 22 | maxDepth?: string | number |
| 23 | minDepth?: string | number |
| 24 | mode?: 'all' | 'onlyCurrentTree' | 'onlySiblings' |
| 25 | }>(), |
| 26 | { |
| 27 | columns: 1, |
| 28 | listClass: '', |
| 29 | start: 1, |
| 30 | listStyle: '', |
| 31 | maxDepth: Number.POSITIVE_INFINITY, |
| 32 | minDepth: 1, |
| 33 | mode: 'all', |
| 34 | }, |
| 35 | ) |
| 36 | |
| 37 | const { $slidev } = useSlideContext() |
| 38 | |
| 39 | function filterTreeDepth(tree: TocItem[], level = 1): TocItem[] { |
| 40 | if (level > Number(props.maxDepth)) { |
| 41 | return [] |
| 42 | } |
| 43 | else if (level < Number(props.minDepth)) { |
| 44 | const activeItem = tree.find((item: TocItem) => item.active || item.activeParent) |
| 45 | return activeItem ? filterTreeDepth(activeItem.children, level + 1) : [] |
| 46 | } |
| 47 | return tree |
| 48 | .map((item: TocItem) => ({ |
| 49 | ...item, |
| 50 | children: filterTreeDepth(item.children, level + 1), |
| 51 | })) |
| 52 | } |
| 53 | |
| 54 | function filterOnlyCurrentTree(tree: TocItem[]): TocItem[] { |
| 55 | return tree |
| 56 | .filter( |
| 57 | (item: TocItem) => item.active || item.activeParent || item.hasActiveParent, |
| 58 | ) |
| 59 | .map((item: TocItem) => ({ |
| 60 | ...item, |
| 61 | children: filterOnlyCurrentTree(item.children), |
| 62 | })) |
| 63 | } |
| 64 | |
| 65 | function filterOnlySiblings(tree: TocItem[]): TocItem[] { |
| 66 | const treehasActiveItem = tree.some( |
| 67 | (item: TocItem) => item.active || item.activeParent || item.hasActiveParent, |
| 68 | ) |
| 69 | return tree |
| 70 | .filter(() => treehasActiveItem) |
| 71 | .map((item: TocItem) => ({ |
| 72 | ...item, |
| 73 | children: filterOnlySiblings(item.children), |
| 74 | })) |
| 75 | } |
| 76 | |
| 77 | const toc = computed(() => { |
| 78 | const tree = $slidev?.nav.tocTree |
| 79 | if (!tree) |
| 80 | return [] |
| 81 | let tocTree = filterTreeDepth(tree) |
| 82 | if (props.mode === 'onlyCurrentTree') |
| 83 | tocTree = filterOnlyCurrentTree(tocTree) |
| 84 | else if (props.mode === 'onlySiblings') |
| 85 | tocTree = filterOnlySiblings(tocTree) |
| 86 | return tocTree |
| 87 | }) |
| 88 | </script> |
| 89 | |
| 90 | <template> |
| 91 | <div class="slidev-toc" :style="`column-count:${columns}`"> |
| 92 | <TocList |
| 93 | :level="1" |
| 94 | :start="start" |
| 95 | :list-style="listStyle" |
| 96 | :list="toc" |
| 97 | :list-class="listClass" |
| 98 | /> |
| 99 | </div> |
| 100 | </template> |
| 101 |