@tnotesjs/core 0.1.27 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/package.json +3 -1
- package/templates/sub-repo/.gitattributes +30 -0
- package/templates/sub-repo/.github/workflows/deploy.yml +87 -0
- package/templates/sub-repo/.vitepress/config.mts +12 -0
- package/templates/sub-repo/.vitepress/env.d.ts +10 -0
- package/templates/sub-repo/.vitepress/theme/index.ts +12 -0
- package/templates/sub-repo/.vscode/settings.json +330 -0
- package/templates/sub-repo/.vscode/tnotes.code-snippets +110 -0
- package/templates/sub-repo/public/favicon.ico +0 -0
- package/templates/sub-repo/public/logo.png +0 -0
- package/templates/sub-repo/tsconfig.json +45 -0
- package/vitepress/assets/icons/icon__setting_dark.svg +4 -0
- package/vitepress/assets/icons/icon__setting_light.svg +4 -0
- package/vitepress/assets/icons/index.ts +2 -2
- package/vitepress/client/localSearchIndexBridge.ts +24 -0
- package/vitepress/components/Discussions/Discussions.vue +1 -1
- package/vitepress/components/Footprints/Footprints.vue +90 -25
- package/vitepress/components/Layout/CustomSidebar.vue +489 -117
- package/vitepress/components/Layout/Layout.vue +5 -0
- package/vitepress/components/Layout/NavBarSettingsTrigger.vue +10 -2
- package/vitepress/components/Layout/SidebarItems.vue +517 -342
- package/vitepress/components/Layout/SidebarNavBefore.vue +1 -50
- package/vitepress/components/Layout/SidebarResizeHandle.vue +6 -23
- package/vitepress/components/Layout/composables/docLayoutLogic.ts +59 -0
- package/vitepress/components/Layout/composables/useDocLayout.test.ts +69 -0
- package/vitepress/components/Layout/composables/useDocLayout.ts +128 -0
- package/vitepress/components/Layout/composables/useSidebarDrag.ts +514 -0
- package/vitepress/components/Layout/composables/useSidebarLayout.ts +25 -8
- package/vitepress/components/Layout/composables/useVSCodeIntegration.ts +8 -3
- package/vitepress/components/Layout/sidebarCollapsedState.test.ts +183 -0
- package/vitepress/components/Layout/sidebarCollapsedState.ts +241 -0
- package/vitepress/components/Layout/sidebarDragLogic.test.ts +1146 -0
- package/vitepress/components/Layout/sidebarDragLogic.ts +1307 -0
- package/vitepress/components/Layout/sidebarHitTest.test.ts +572 -0
- package/vitepress/components/Layout/sidebarHitTest.ts +801 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.test.ts +53 -0
- package/vitepress/components/Layout/sidebarItemRenderLogic.ts +50 -0
- package/vitepress/components/MarkMap/MarkMap.vue +67 -10
- package/vitepress/components/Settings/Settings.vue +78 -74
- package/vitepress/components/SidebarCard/FolderTreeItems.vue +268 -0
- package/vitepress/components/SidebarCard/SidebarCard.vue +125 -274
- package/vitepress/components/constants.ts +18 -6
- package/vitepress/components/sidebar.data.ts +13 -11
- package/vitepress/components/sidebarTreeHelpers.test.ts +142 -0
- package/vitepress/components/sidebarTreeHelpers.ts +168 -0
- package/vitepress/components/utils/vscodePaths.test.ts +41 -0
- package/vitepress/components/utils/vscodePaths.ts +52 -0
- package/vitepress/config/index.ts +2 -0
- package/vitepress/plugins/localSearchIndexBuilder.ts +209 -0
- package/vitepress/plugins/localSearchReindex.test.ts +46 -0
- package/vitepress/plugins/localSearchReindexLogic.ts +50 -0
- package/vitepress/plugins/localSearchReindexPatch.test.ts +30 -0
- package/vitepress/plugins/localSearchReindexPatch.ts +37 -0
- package/vitepress/plugins/localSearchReindexPlugin.ts +318 -0
- package/vitepress/plugins/sidebarStructurePlugin.ts +209 -63
- package/vitepress/theme/index.ts +8 -0
- package/vitepress/theme/styles/base.scss +0 -8
- package/vitepress/theme/styles/components/markmap.scss +23 -2
- package/vitepress/theme/styles/components/swiper.scss +1 -1
- package/vitepress/theme/styles/doc-layout.scss +212 -0
- package/vitepress/theme/styles/index.scss +3 -0
- package/vitepress/theme/styles/layout.scss +60 -10
- package/dist/chunk-4KG6RXFS.js +0 -4637
- package/dist/chunk-AGSL3VZE.js +0 -193
- package/dist/cli/index.js +0 -683
- package/dist/index.js +0 -8
- package/dist/vitepress/config/index.js +0 -1178
- package/vitepress/assets/icons/icon__mindmap.svg +0 -1
- package/vitepress/assets/icons/icon__setting.svg +0 -1
- package/vitepress/components/SidebarCard/MindMapView.vue +0 -488
|
@@ -14,16 +14,6 @@ vitepress/components/Layout/SidebarNavBefore.vue
|
|
|
14
14
|
>
|
|
15
15
|
<img :src="icon__fold" class="toggle-icon" alt="切换展开折叠" />
|
|
16
16
|
</button>
|
|
17
|
-
<a
|
|
18
|
-
:class="[
|
|
19
|
-
'group-label',
|
|
20
|
-
'group-label-link',
|
|
21
|
-
{ 'is-active': isCatalogActive },
|
|
22
|
-
]"
|
|
23
|
-
:href="catalogLink"
|
|
24
|
-
>
|
|
25
|
-
目录
|
|
26
|
-
</a>
|
|
27
17
|
</div>
|
|
28
18
|
|
|
29
19
|
<div class="group group-right">
|
|
@@ -54,9 +44,6 @@ vitepress/components/Layout/SidebarNavBefore.vue
|
|
|
54
44
|
</template>
|
|
55
45
|
|
|
56
46
|
<script setup lang="ts">
|
|
57
|
-
import { useData, useRoute } from 'vitepress'
|
|
58
|
-
import { computed } from 'vue'
|
|
59
|
-
|
|
60
47
|
import {
|
|
61
48
|
icon__fold,
|
|
62
49
|
icon__number_purple,
|
|
@@ -74,26 +61,6 @@ defineEmits<{
|
|
|
74
61
|
'toggle-note-id': []
|
|
75
62
|
'focus-current': []
|
|
76
63
|
}>()
|
|
77
|
-
|
|
78
|
-
const { site } = useData()
|
|
79
|
-
const route = useRoute()
|
|
80
|
-
|
|
81
|
-
const base = computed(() => site.value.base || '/')
|
|
82
|
-
const catalogLink = computed(() => `${base.value}README`)
|
|
83
|
-
const catalogPath = computed(() => normalizeRoutePath(catalogLink.value))
|
|
84
|
-
const isCatalogActive = computed(() => {
|
|
85
|
-
const currentPath = normalizeRoutePath(route.path)
|
|
86
|
-
|
|
87
|
-
return currentPath === catalogPath.value || currentPath === '/README'
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
function normalizeRoutePath(path: string): string {
|
|
91
|
-
const normalizedPath = path.split(/[?#]/)[0].replace(/\.html$/, '')
|
|
92
|
-
|
|
93
|
-
return normalizedPath.length > 1
|
|
94
|
-
? normalizedPath.replace(/\/$/, '')
|
|
95
|
-
: normalizedPath
|
|
96
|
-
}
|
|
97
64
|
</script>
|
|
98
65
|
|
|
99
66
|
<style scoped>
|
|
@@ -104,7 +71,7 @@ function normalizeRoutePath(path: string): string {
|
|
|
104
71
|
display: flex;
|
|
105
72
|
flex-direction: column;
|
|
106
73
|
gap: 8px;
|
|
107
|
-
padding: 10px
|
|
74
|
+
padding: 10px 0 12px;
|
|
108
75
|
/* border-bottom: 1px solid var(--vp-c-divider); */
|
|
109
76
|
margin-bottom: 10px;
|
|
110
77
|
background: var(--vp-sidebar-bg-color, var(--vp-c-bg));
|
|
@@ -123,22 +90,6 @@ function normalizeRoutePath(path: string): string {
|
|
|
123
90
|
gap: 4px;
|
|
124
91
|
}
|
|
125
92
|
|
|
126
|
-
.group-label {
|
|
127
|
-
font-size: 13px;
|
|
128
|
-
color: var(--vp-c-text-2);
|
|
129
|
-
text-decoration: none;
|
|
130
|
-
user-select: none;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.group-label-link:hover {
|
|
134
|
-
color: var(--vp-c-brand-1);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.group-label-link.is-active {
|
|
138
|
-
color: var(--vp-c-brand-1);
|
|
139
|
-
font-weight: 600;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
93
|
.toggle-btn {
|
|
143
94
|
display: inline-flex;
|
|
144
95
|
align-items: center;
|
|
@@ -38,6 +38,7 @@ vitepress/components/Layout/SidebarResizeHandle.vue
|
|
|
38
38
|
import { useRoute } from 'vitepress'
|
|
39
39
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
40
40
|
|
|
41
|
+
import { updateResponsiveLayout } from './composables/useDocLayout'
|
|
41
42
|
import { useSidebarLayout } from './composables/useSidebarLayout'
|
|
42
43
|
import { icon__next, icon__prev } from '../../assets/icons'
|
|
43
44
|
|
|
@@ -57,13 +58,11 @@ const isDragging = ref(false)
|
|
|
57
58
|
const isContentFullscreen = ref(false)
|
|
58
59
|
const isSidebarAvailable = ref(false)
|
|
59
60
|
const shortcutText = ref('Ctrl + Alt + ,')
|
|
60
|
-
const WIDE_LAYOUT_MIN_WIDTH = 1440
|
|
61
61
|
let fullscreenObserver: MutationObserver | null = null
|
|
62
|
-
|
|
63
|
-
const toggleIcon = computed(() => (hidden.value ? icon__next : icon__prev))
|
|
64
62
|
const toggleActionText = computed(() =>
|
|
65
63
|
hidden.value ? '展开侧边栏' : '收起侧边栏',
|
|
66
64
|
)
|
|
65
|
+
const toggleIcon = computed(() => (hidden.value ? icon__next : icon__prev))
|
|
67
66
|
const toggleTitle = computed(
|
|
68
67
|
() => `${toggleActionText.value}\n${shortcutText.value}`,
|
|
69
68
|
)
|
|
@@ -102,6 +101,7 @@ function resize(event: MouseEvent) {
|
|
|
102
101
|
if (!isDragging.value) return
|
|
103
102
|
|
|
104
103
|
setSidebarWidth(getSidebarWidthFromClientX(event.clientX))
|
|
104
|
+
updateResponsiveLayout()
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function stopResize() {
|
|
@@ -112,6 +112,7 @@ function stopResize() {
|
|
|
112
112
|
window.removeEventListener('mousemove', resize)
|
|
113
113
|
window.removeEventListener('mouseup', stopResize)
|
|
114
114
|
saveSidebarWidth()
|
|
115
|
+
updateResponsiveLayout()
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
function handleShortcut(event: KeyboardEvent) {
|
|
@@ -125,6 +126,7 @@ function handleShortcut(event: KeyboardEvent) {
|
|
|
125
126
|
|
|
126
127
|
event.preventDefault()
|
|
127
128
|
toggleSidebar()
|
|
129
|
+
updateResponsiveLayout()
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
watch(
|
|
@@ -155,18 +157,7 @@ function getSidebarWidthFromClientX(clientX: number): number {
|
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
function getSidebarLayoutOffset(): number {
|
|
158
|
-
|
|
159
|
-
if (window.innerWidth < WIDE_LAYOUT_MIN_WIDTH) return 0
|
|
160
|
-
|
|
161
|
-
const layoutMaxWidth = Number.parseFloat(
|
|
162
|
-
window
|
|
163
|
-
.getComputedStyle(document.documentElement)
|
|
164
|
-
.getPropertyValue('--vp-layout-max-width'),
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
if (!Number.isFinite(layoutMaxWidth)) return 0
|
|
168
|
-
|
|
169
|
-
return Math.max(0, (window.innerWidth - layoutMaxWidth) / 2)
|
|
160
|
+
return 0
|
|
170
161
|
}
|
|
171
162
|
|
|
172
163
|
function isToggleShortcut(event: KeyboardEvent): boolean {
|
|
@@ -361,12 +352,4 @@ function isEditableTarget(target: EventTarget | null): boolean {
|
|
|
361
352
|
display: none;
|
|
362
353
|
}
|
|
363
354
|
}
|
|
364
|
-
|
|
365
|
-
@media (min-width: 1440px) {
|
|
366
|
-
.sidebar-resize-handle {
|
|
367
|
-
left: calc(
|
|
368
|
-
(100vw - var(--vp-layout-max-width)) / 2 + var(--tn-sidebar-width, 260px) - 5px
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
355
|
</style>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docLayoutLogic.ts
|
|
3
|
+
*
|
|
4
|
+
* 语雀式三栏布局的纯函数(可单测,不依赖 VitePress define)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type ContentWidthMode = 'wide' | 'standard'
|
|
8
|
+
|
|
9
|
+
const ASIDE_WIDTH = 300
|
|
10
|
+
const STANDARD_CONTENT_WIDTH = 750
|
|
11
|
+
const WIDE_CONTENT_MIN_WIDTH = 640
|
|
12
|
+
const LAYOUT_PADDING = 16
|
|
13
|
+
/** VitePress 桌面侧栏断点;以下由 VP 自带移动顶栏/抽屉负责 */
|
|
14
|
+
export const DESKTOP_SIDEBAR_BREAKPOINT = 960
|
|
15
|
+
|
|
16
|
+
function getContentTargetWidth(mode: ContentWidthMode): number {
|
|
17
|
+
return mode === 'standard' ? STANDARD_CONTENT_WIDTH : WIDE_CONTENT_MIN_WIDTH
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resolveResponsiveLayoutState(input: {
|
|
21
|
+
viewportWidth: number
|
|
22
|
+
sidebarWidth: number
|
|
23
|
+
userSidebarHidden: boolean
|
|
24
|
+
contentWidthMode: ContentWidthMode
|
|
25
|
+
hasAside: boolean
|
|
26
|
+
}): { asideAutoHidden: boolean; sidebarAutoHidden: boolean } {
|
|
27
|
+
// 移动端不使用桌面三栏降级,避免误隐藏顶栏站点标题
|
|
28
|
+
if (input.viewportWidth < DESKTOP_SIDEBAR_BREAKPOINT) {
|
|
29
|
+
return { asideAutoHidden: false, sidebarAutoHidden: false }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sidebarWidth = input.userSidebarHidden ? 0 : input.sidebarWidth
|
|
33
|
+
const contentTarget = getContentTargetWidth(input.contentWidthMode)
|
|
34
|
+
|
|
35
|
+
if (!input.hasAside) {
|
|
36
|
+
const needSidebarAndContent =
|
|
37
|
+
sidebarWidth + contentTarget + LAYOUT_PADDING * 2
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
asideAutoHidden: false,
|
|
41
|
+
sidebarAutoHidden: input.viewportWidth < needSidebarAndContent,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const needThreeColumns =
|
|
46
|
+
sidebarWidth + contentTarget + ASIDE_WIDTH + LAYOUT_PADDING * 2
|
|
47
|
+
const needSidebarAndContent =
|
|
48
|
+
sidebarWidth + contentTarget + LAYOUT_PADDING * 2
|
|
49
|
+
|
|
50
|
+
if (input.viewportWidth < needSidebarAndContent) {
|
|
51
|
+
return { asideAutoHidden: true, sidebarAutoHidden: true }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (input.viewportWidth < needThreeColumns) {
|
|
55
|
+
return { asideAutoHidden: true, sidebarAutoHidden: false }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { asideAutoHidden: false, sidebarAutoHidden: false }
|
|
59
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useDocLayout.test.ts
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { resolveResponsiveLayoutState } from './docLayoutLogic'
|
|
8
|
+
|
|
9
|
+
describe('resolveResponsiveLayoutState', () => {
|
|
10
|
+
it('keeps three columns on wide viewport', () => {
|
|
11
|
+
expect(
|
|
12
|
+
resolveResponsiveLayoutState({
|
|
13
|
+
viewportWidth: 1600,
|
|
14
|
+
sidebarWidth: 260,
|
|
15
|
+
userSidebarHidden: false,
|
|
16
|
+
contentWidthMode: 'wide',
|
|
17
|
+
hasAside: true,
|
|
18
|
+
}),
|
|
19
|
+
).toEqual({ asideAutoHidden: false, sidebarAutoHidden: false })
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('hides aside first when viewport is medium', () => {
|
|
23
|
+
expect(
|
|
24
|
+
resolveResponsiveLayoutState({
|
|
25
|
+
viewportWidth: 1300,
|
|
26
|
+
sidebarWidth: 260,
|
|
27
|
+
userSidebarHidden: false,
|
|
28
|
+
contentWidthMode: 'standard',
|
|
29
|
+
hasAside: true,
|
|
30
|
+
}),
|
|
31
|
+
).toEqual({ asideAutoHidden: true, sidebarAutoHidden: false })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('hides aside and sidebar when viewport is narrow', () => {
|
|
35
|
+
expect(
|
|
36
|
+
resolveResponsiveLayoutState({
|
|
37
|
+
viewportWidth: 1000,
|
|
38
|
+
sidebarWidth: 260,
|
|
39
|
+
userSidebarHidden: false,
|
|
40
|
+
contentWidthMode: 'standard',
|
|
41
|
+
hasAside: true,
|
|
42
|
+
}),
|
|
43
|
+
).toEqual({ asideAutoHidden: true, sidebarAutoHidden: true })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('does not apply auto-collapse on mobile viewport', () => {
|
|
47
|
+
expect(
|
|
48
|
+
resolveResponsiveLayoutState({
|
|
49
|
+
viewportWidth: 375,
|
|
50
|
+
sidebarWidth: 260,
|
|
51
|
+
userSidebarHidden: false,
|
|
52
|
+
contentWidthMode: 'wide',
|
|
53
|
+
hasAside: true,
|
|
54
|
+
}),
|
|
55
|
+
).toEqual({ asideAutoHidden: false, sidebarAutoHidden: false })
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('does not reserve aside space when page has no outline on desktop', () => {
|
|
59
|
+
expect(
|
|
60
|
+
resolveResponsiveLayoutState({
|
|
61
|
+
viewportWidth: 1000,
|
|
62
|
+
sidebarWidth: 260,
|
|
63
|
+
userSidebarHidden: false,
|
|
64
|
+
contentWidthMode: 'standard',
|
|
65
|
+
hasAside: false,
|
|
66
|
+
}),
|
|
67
|
+
).toEqual({ asideAutoHidden: false, sidebarAutoHidden: true })
|
|
68
|
+
})
|
|
69
|
+
})
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vitepress/components/Layout/composables/useDocLayout.ts
|
|
3
|
+
*
|
|
4
|
+
* 语雀式三栏布局:正文页宽模式 + 视口不足时自动隐藏右栏/左栏
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ref } from 'vue'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
resolveResponsiveLayoutState,
|
|
12
|
+
type ContentWidthMode,
|
|
13
|
+
} from './docLayoutLogic'
|
|
14
|
+
import { useSidebarLayout } from './useSidebarLayout'
|
|
15
|
+
import { CONTENT_WIDTH_MODE_KEY } from '../../constants'
|
|
16
|
+
|
|
17
|
+
export type { ContentWidthMode } from './docLayoutLogic'
|
|
18
|
+
|
|
19
|
+
const contentWidthMode = ref<ContentWidthMode>('wide')
|
|
20
|
+
const asideAutoHidden = ref(false)
|
|
21
|
+
let initialized = false
|
|
22
|
+
let resizeListenerAttached = false
|
|
23
|
+
|
|
24
|
+
function canUseDOM(): boolean {
|
|
25
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readContentWidthMode(): ContentWidthMode {
|
|
29
|
+
if (!canUseDOM()) return 'wide'
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const saved = window.localStorage.getItem(CONTENT_WIDTH_MODE_KEY)
|
|
33
|
+
return saved === 'standard' ? 'standard' : 'wide'
|
|
34
|
+
} catch {
|
|
35
|
+
return 'wide'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function applyContentWidthMode(mode: ContentWidthMode) {
|
|
40
|
+
if (!canUseDOM()) return
|
|
41
|
+
|
|
42
|
+
const root = document.documentElement
|
|
43
|
+
root.classList.toggle('content-width-wide', mode === 'wide')
|
|
44
|
+
root.classList.toggle('content-width-standard', mode === 'standard')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function pageHasDocAside(): boolean {
|
|
48
|
+
if (!canUseDOM()) return false
|
|
49
|
+
return Boolean(document.querySelector('.VPDoc.has-aside'))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function applyAsideAutoHidden(nextHidden: boolean) {
|
|
53
|
+
if (!canUseDOM()) return
|
|
54
|
+
|
|
55
|
+
asideAutoHidden.value = nextHidden
|
|
56
|
+
document.documentElement.classList.toggle('aside-auto-hidden', nextHidden)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function applySidebarAutoHidden(nextHidden: boolean) {
|
|
60
|
+
if (!canUseDOM()) return
|
|
61
|
+
|
|
62
|
+
document.documentElement.classList.toggle('sidebar-auto-hidden', nextHidden)
|
|
63
|
+
const { setSidebarAutoHidden } = useSidebarLayout()
|
|
64
|
+
setSidebarAutoHidden(nextHidden)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function updateResponsiveLayout() {
|
|
68
|
+
if (!canUseDOM()) return
|
|
69
|
+
|
|
70
|
+
const { hidden, width } = useSidebarLayout()
|
|
71
|
+
const next = resolveResponsiveLayoutState({
|
|
72
|
+
viewportWidth: window.innerWidth,
|
|
73
|
+
sidebarWidth: width.value,
|
|
74
|
+
userSidebarHidden: hidden.value,
|
|
75
|
+
contentWidthMode: contentWidthMode.value,
|
|
76
|
+
hasAside: pageHasDocAside(),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
applyAsideAutoHidden(next.asideAutoHidden)
|
|
80
|
+
applySidebarAutoHidden(next.sidebarAutoHidden)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleLayoutResize() {
|
|
84
|
+
updateResponsiveLayout()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function attachResizeListener() {
|
|
88
|
+
if (!canUseDOM() || resizeListenerAttached) return
|
|
89
|
+
|
|
90
|
+
window.addEventListener('resize', handleLayoutResize, { passive: true })
|
|
91
|
+
resizeListenerAttached = true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function initDocLayout() {
|
|
95
|
+
if (!canUseDOM()) return
|
|
96
|
+
|
|
97
|
+
useSidebarLayout().initSidebarLayout()
|
|
98
|
+
contentWidthMode.value = readContentWidthMode()
|
|
99
|
+
applyContentWidthMode(contentWidthMode.value)
|
|
100
|
+
|
|
101
|
+
if (!initialized) {
|
|
102
|
+
initialized = true
|
|
103
|
+
attachResizeListener()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
updateResponsiveLayout()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function useDocLayout() {
|
|
110
|
+
return {
|
|
111
|
+
contentWidthMode,
|
|
112
|
+
asideAutoHidden,
|
|
113
|
+
initDocLayout,
|
|
114
|
+
updateResponsiveLayout,
|
|
115
|
+
readContentWidthMode,
|
|
116
|
+
applyContentWidthMode,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function persistContentWidthMode(mode: ContentWidthMode) {
|
|
121
|
+
if (!canUseDOM()) return
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
window.localStorage.setItem(CONTENT_WIDTH_MODE_KEY, mode)
|
|
125
|
+
} catch {
|
|
126
|
+
// ignore quota / privacy mode
|
|
127
|
+
}
|
|
128
|
+
}
|