@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
|
@@ -4,26 +4,34 @@ vitepress/components/Layout/SidebarItems.vue
|
|
|
4
4
|
|
|
5
5
|
<template>
|
|
6
6
|
<template v-for="item in items" :key="getItemKey(item)">
|
|
7
|
-
<!-- 只有在不超过最大层级时才渲染组 -->
|
|
8
7
|
<div
|
|
9
|
-
v-if="
|
|
8
|
+
v-if="item.isDragPlaceholder"
|
|
9
|
+
class="sidebar-row is-drag-placeholder"
|
|
10
|
+
:style="getNodeStyle(depth)"
|
|
11
|
+
aria-hidden="true"
|
|
12
|
+
/>
|
|
13
|
+
|
|
14
|
+
<!-- 纯目录行(无 link) -->
|
|
15
|
+
<div
|
|
16
|
+
v-else-if="isPureFolder(item)"
|
|
10
17
|
class="group"
|
|
11
|
-
:class="
|
|
18
|
+
:class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
|
|
12
19
|
>
|
|
13
20
|
<div
|
|
14
|
-
class="sidebar-row group-row"
|
|
15
|
-
:class="[
|
|
21
|
+
class="sidebar-row group-row folder-row"
|
|
22
|
+
:class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
|
|
16
23
|
:style="getNodeStyle(depth)"
|
|
17
|
-
:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@
|
|
21
|
-
@drop.stop="onGroupDrop($event, item)"
|
|
24
|
+
:data-folder-path="serializeFolderPath(item)"
|
|
25
|
+
:data-toc-line-index="item.tocLineIndex"
|
|
26
|
+
:data-node-id="getNodeId(item)"
|
|
27
|
+
@pointerdown="onFolderRowPointerDown($event, item)"
|
|
22
28
|
>
|
|
23
29
|
<button
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
v-if="hasChildren(item)"
|
|
31
|
+
class="group-arrow-btn"
|
|
32
|
+
type="button"
|
|
33
|
+
:title="item.collapsed ? '展开' : '折叠'"
|
|
34
|
+
@click.stop="toggleItem(item)"
|
|
27
35
|
>
|
|
28
36
|
<span class="arrow" :class="{ collapsed: item.collapsed }">
|
|
29
37
|
<img
|
|
@@ -33,44 +41,55 @@ vitepress/components/Layout/SidebarItems.vue
|
|
|
33
41
|
alt=""
|
|
34
42
|
/>
|
|
35
43
|
</span>
|
|
44
|
+
</button>
|
|
45
|
+
<span v-else class="group-arrow-spacer" aria-hidden="true" />
|
|
36
46
|
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
class="group-title row-main"
|
|
50
|
+
@click.stop="onGroupTitleClick(item)"
|
|
51
|
+
>
|
|
37
52
|
<span class="group-title-text">{{ item.text }}</span>
|
|
38
53
|
</button>
|
|
39
54
|
|
|
40
55
|
<div
|
|
41
56
|
v-if="isDev"
|
|
42
57
|
class="row-actions"
|
|
43
|
-
:class="{ 'menu-open': isGroupMenuOpen(item) }"
|
|
58
|
+
:class="{ 'menu-open': isGroupMenuOpen(item) || isFolderMenuOpen(item) }"
|
|
44
59
|
@click.stop
|
|
45
60
|
>
|
|
46
61
|
<button
|
|
47
62
|
class="action-btn menu-trigger"
|
|
48
63
|
title="更多操作"
|
|
49
64
|
type="button"
|
|
50
|
-
@click="toggleMenu(
|
|
65
|
+
@click="toggleMenu(getFolderMenuKey(item, 'more'))"
|
|
51
66
|
>
|
|
52
67
|
<span class="ellipsis">...</span>
|
|
53
68
|
</button>
|
|
54
69
|
|
|
55
70
|
<div
|
|
56
|
-
v-if="isMenuOpen(
|
|
71
|
+
v-if="isMenuOpen(getFolderMenuKey(item, 'more'))"
|
|
57
72
|
class="action-menu"
|
|
58
73
|
>
|
|
59
|
-
<button
|
|
60
|
-
|
|
74
|
+
<button
|
|
75
|
+
class="menu-item"
|
|
76
|
+
type="button"
|
|
77
|
+
@click="renameFolder(item)"
|
|
78
|
+
>
|
|
79
|
+
重命名
|
|
61
80
|
</button>
|
|
62
81
|
<button
|
|
63
82
|
class="menu-item danger"
|
|
64
83
|
type="button"
|
|
65
|
-
@click="
|
|
84
|
+
@click="deleteFolder(item)"
|
|
66
85
|
>
|
|
67
|
-
|
|
86
|
+
删除分组
|
|
68
87
|
</button>
|
|
69
88
|
</div>
|
|
70
89
|
|
|
71
90
|
<button
|
|
72
91
|
class="action-btn menu-trigger add-trigger"
|
|
73
|
-
title="
|
|
92
|
+
title="新增子笔记"
|
|
74
93
|
type="button"
|
|
75
94
|
@click="toggleMenu(getGroupMenuKey(item, 'add'))"
|
|
76
95
|
>
|
|
@@ -95,6 +114,159 @@ vitepress/components/Layout/SidebarItems.vue
|
|
|
95
114
|
>
|
|
96
115
|
新增多篇笔记
|
|
97
116
|
</button>
|
|
117
|
+
<button
|
|
118
|
+
class="menu-item"
|
|
119
|
+
type="button"
|
|
120
|
+
@click="createFolderInGroup(item)"
|
|
121
|
+
>
|
|
122
|
+
新建子目录
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div v-show="!item.collapsed" class="group-items">
|
|
129
|
+
<SidebarItems
|
|
130
|
+
:items="getChildItems(item)"
|
|
131
|
+
:depth="depth + 1"
|
|
132
|
+
:max-depth="maxDepth"
|
|
133
|
+
:show-note-id="showNoteId"
|
|
134
|
+
:base="base"
|
|
135
|
+
:current-path="currentPath"
|
|
136
|
+
:item-depth="depth + 1"
|
|
137
|
+
:group-path="getFolderPath(item)"
|
|
138
|
+
:is-dev="isDev"
|
|
139
|
+
:done-prefix="donePrefix"
|
|
140
|
+
:undone-prefix="undonePrefix"
|
|
141
|
+
:drag-context="getSidebarDrag()"
|
|
142
|
+
@create-note-in-group="$emit('create-note-in-group', $event)"
|
|
143
|
+
@create-notes-in-group="$emit('create-notes-in-group', $event)"
|
|
144
|
+
@create-folder-in-group="$emit('create-folder-in-group', $event)"
|
|
145
|
+
@rename-folder="$emit('rename-folder', $event)"
|
|
146
|
+
@delete-entry="$emit('delete-entry', $event)"
|
|
147
|
+
@rename-note="$emit('rename-note', $event)"
|
|
148
|
+
@delete-note="$emit('delete-note', $event)"
|
|
149
|
+
@create-note-around="$emit('create-note-around', $event)"
|
|
150
|
+
@open-note-about="$emit('open-note-about', $event)"
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- 父笔记行(legacy:有 link 且有子项) -->
|
|
156
|
+
<div
|
|
157
|
+
v-else-if="hasChildren(item)"
|
|
158
|
+
class="group"
|
|
159
|
+
:class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
|
|
160
|
+
>
|
|
161
|
+
<div
|
|
162
|
+
class="sidebar-row group-row parent-note-row"
|
|
163
|
+
:class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
|
|
164
|
+
:style="getNodeStyle(depth)"
|
|
165
|
+
:data-note-id="extractNoteIdFromLink(item.link) || undefined"
|
|
166
|
+
:data-toc-line-index="item.tocLineIndex"
|
|
167
|
+
:data-node-id="getNodeId(item)"
|
|
168
|
+
@pointerdown="onRowPointerDown($event, item)"
|
|
169
|
+
>
|
|
170
|
+
<button
|
|
171
|
+
class="group-arrow-btn"
|
|
172
|
+
type="button"
|
|
173
|
+
:title="item.collapsed ? '展开' : '折叠'"
|
|
174
|
+
@click.stop="toggleItem(item)"
|
|
175
|
+
>
|
|
176
|
+
<span class="arrow" :class="{ collapsed: item.collapsed }">
|
|
177
|
+
<img
|
|
178
|
+
:src="
|
|
179
|
+
item.collapsed ? icon__sidebar_collapsed : icon__sidebar_opened
|
|
180
|
+
"
|
|
181
|
+
alt=""
|
|
182
|
+
/>
|
|
183
|
+
</span>
|
|
184
|
+
</button>
|
|
185
|
+
|
|
186
|
+
<a
|
|
187
|
+
:href="getFullLink(item.link)"
|
|
188
|
+
:class="[
|
|
189
|
+
'nav-item',
|
|
190
|
+
'row-main',
|
|
191
|
+
'parent-note-link',
|
|
192
|
+
{ active: isActive(item.link) },
|
|
193
|
+
`nav-item-${extractNoteIdFromLink(item.link)}`,
|
|
194
|
+
`nav-item-level-${depth + 1}`,
|
|
195
|
+
]"
|
|
196
|
+
:data-note-id="extractNoteIdFromLink(item.link)"
|
|
197
|
+
@click="onParentNoteTitleClick($event, item)"
|
|
198
|
+
>
|
|
199
|
+
{{ getNoteDisplayText(item.text, item.link) }}
|
|
200
|
+
</a>
|
|
201
|
+
|
|
202
|
+
<div
|
|
203
|
+
v-if="isDev"
|
|
204
|
+
class="row-actions"
|
|
205
|
+
:class="{ 'menu-open': isGroupMenuOpen(item) || isNoteMenuOpen(item) }"
|
|
206
|
+
@click.stop
|
|
207
|
+
>
|
|
208
|
+
<button
|
|
209
|
+
class="action-btn menu-trigger"
|
|
210
|
+
title="更多操作"
|
|
211
|
+
type="button"
|
|
212
|
+
@click="toggleMenu(getNoteMenuKey(item, 'more'))"
|
|
213
|
+
>
|
|
214
|
+
<span class="ellipsis">...</span>
|
|
215
|
+
</button>
|
|
216
|
+
|
|
217
|
+
<div
|
|
218
|
+
v-if="isMenuOpen(getNoteMenuKey(item, 'more'))"
|
|
219
|
+
class="action-menu"
|
|
220
|
+
>
|
|
221
|
+
<button class="menu-item" type="button" @click="renameNote(item)">
|
|
222
|
+
重命名
|
|
223
|
+
</button>
|
|
224
|
+
<button
|
|
225
|
+
class="menu-item danger"
|
|
226
|
+
type="button"
|
|
227
|
+
@click="deleteNote(item)"
|
|
228
|
+
>
|
|
229
|
+
删除笔记
|
|
230
|
+
</button>
|
|
231
|
+
<button class="menu-item" type="button" @click="openNoteAbout(item)">
|
|
232
|
+
关于
|
|
233
|
+
</button>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<button
|
|
237
|
+
class="action-btn menu-trigger add-trigger"
|
|
238
|
+
title="新增子笔记"
|
|
239
|
+
type="button"
|
|
240
|
+
@click="toggleMenu(getGroupMenuKey(item, 'add'))"
|
|
241
|
+
>
|
|
242
|
+
+
|
|
243
|
+
</button>
|
|
244
|
+
|
|
245
|
+
<div
|
|
246
|
+
v-if="isMenuOpen(getGroupMenuKey(item, 'add'))"
|
|
247
|
+
class="action-menu"
|
|
248
|
+
>
|
|
249
|
+
<button
|
|
250
|
+
class="menu-item"
|
|
251
|
+
type="button"
|
|
252
|
+
@click="createNoteInGroup(item)"
|
|
253
|
+
>
|
|
254
|
+
新增笔记
|
|
255
|
+
</button>
|
|
256
|
+
<button
|
|
257
|
+
class="menu-item"
|
|
258
|
+
type="button"
|
|
259
|
+
@click="createNotesInGroup(item)"
|
|
260
|
+
>
|
|
261
|
+
新增多篇笔记
|
|
262
|
+
</button>
|
|
263
|
+
<button
|
|
264
|
+
class="menu-item"
|
|
265
|
+
type="button"
|
|
266
|
+
@click="createFolderInGroup(item)"
|
|
267
|
+
>
|
|
268
|
+
新建子目录
|
|
269
|
+
</button>
|
|
98
270
|
</div>
|
|
99
271
|
</div>
|
|
100
272
|
</div>
|
|
@@ -110,18 +282,18 @@ vitepress/components/Layout/SidebarItems.vue
|
|
|
110
282
|
:item-depth="depth + 1"
|
|
111
283
|
:group-path="getGroupPath(item)"
|
|
112
284
|
:is-dev="isDev"
|
|
113
|
-
:sidebar-density="sidebarDensity"
|
|
114
285
|
:done-prefix="donePrefix"
|
|
115
286
|
:undone-prefix="undonePrefix"
|
|
116
|
-
|
|
117
|
-
@delete-group="$emit('delete-group', $event)"
|
|
287
|
+
:drag-context="getSidebarDrag()"
|
|
118
288
|
@create-note-in-group="$emit('create-note-in-group', $event)"
|
|
119
289
|
@create-notes-in-group="$emit('create-notes-in-group', $event)"
|
|
290
|
+
@create-folder-in-group="$emit('create-folder-in-group', $event)"
|
|
291
|
+
@rename-folder="$emit('rename-folder', $event)"
|
|
292
|
+
@delete-entry="$emit('delete-entry', $event)"
|
|
120
293
|
@rename-note="$emit('rename-note', $event)"
|
|
121
294
|
@delete-note="$emit('delete-note', $event)"
|
|
122
295
|
@create-note-around="$emit('create-note-around', $event)"
|
|
123
296
|
@open-note-about="$emit('open-note-about', $event)"
|
|
124
|
-
@reorder-sidebar="$emit('reorder-sidebar', $event)"
|
|
125
297
|
/>
|
|
126
298
|
</div>
|
|
127
299
|
</div>
|
|
@@ -130,14 +302,15 @@ vitepress/components/Layout/SidebarItems.vue
|
|
|
130
302
|
<div
|
|
131
303
|
v-else-if="!hasChildren(item)"
|
|
132
304
|
class="sidebar-row note-row"
|
|
133
|
-
:class="[
|
|
305
|
+
:class="[{ 'is-drag-source-slot': item.isDragSourceSlot }]"
|
|
134
306
|
:style="getNodeStyle(actualItemDepth)"
|
|
135
|
-
:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
@
|
|
139
|
-
@drop.stop="onNoteDrop($event, item)"
|
|
307
|
+
:data-note-id="extractNoteIdFromLink(item.link) || undefined"
|
|
308
|
+
:data-toc-line-index="item.tocLineIndex"
|
|
309
|
+
:data-node-id="getNodeId(item)"
|
|
310
|
+
@pointerdown="onRowPointerDown($event, item)"
|
|
140
311
|
>
|
|
312
|
+
<span class="group-arrow-spacer" aria-hidden="true" />
|
|
313
|
+
|
|
141
314
|
<a
|
|
142
315
|
:href="getFullLink(item.link)"
|
|
143
316
|
:class="[
|
|
@@ -220,18 +393,29 @@ vitepress/components/Layout/SidebarItems.vue
|
|
|
220
393
|
</template>
|
|
221
394
|
|
|
222
395
|
<script setup lang="ts">
|
|
396
|
+
import { useRouter } from 'vitepress'
|
|
223
397
|
import { computed, onBeforeUnmount, ref } from 'vue'
|
|
224
398
|
|
|
399
|
+
import { useSidebarDragContext } from './composables/useSidebarDrag'
|
|
400
|
+
import { SIDEBAR_INDENT_SIZE } from './sidebarDragLogic'
|
|
401
|
+
import { computeSidebarNodeId } from '../../../utils/tocNodeId'
|
|
225
402
|
import {
|
|
226
403
|
icon__sidebar_opened,
|
|
227
404
|
icon__sidebar_collapsed,
|
|
228
405
|
} from '../../assets/icons'
|
|
229
406
|
|
|
407
|
+
import type { SidebarDragContext } from './composables/useSidebarDrag'
|
|
408
|
+
|
|
230
409
|
interface SidebarItem {
|
|
231
410
|
text: string
|
|
232
411
|
link?: string
|
|
233
412
|
items?: SidebarItem[]
|
|
234
413
|
collapsed?: boolean
|
|
414
|
+
folderPath?: string[]
|
|
415
|
+
tocLineIndex?: number
|
|
416
|
+
nodeId?: string
|
|
417
|
+
isDragSourceSlot?: boolean
|
|
418
|
+
isDragPlaceholder?: boolean
|
|
235
419
|
}
|
|
236
420
|
|
|
237
421
|
interface Props {
|
|
@@ -244,97 +428,85 @@ interface Props {
|
|
|
244
428
|
itemDepth?: number // 用于计算链接项的缩进,默认等于 depth
|
|
245
429
|
groupPath?: string[]
|
|
246
430
|
isDev?: boolean
|
|
247
|
-
sidebarDensity?: SidebarDensity
|
|
248
431
|
donePrefix?: string
|
|
249
432
|
undonePrefix?: string
|
|
433
|
+
dragContext?: SidebarDragContext | null
|
|
250
434
|
}
|
|
251
435
|
|
|
436
|
+
const router = useRouter()
|
|
437
|
+
|
|
252
438
|
const props = withDefaults(defineProps<Props>(), {
|
|
253
439
|
depth: 0,
|
|
254
|
-
maxDepth:
|
|
440
|
+
maxDepth: 0,
|
|
255
441
|
showNoteId: false,
|
|
256
442
|
base: '/',
|
|
257
443
|
currentPath: '',
|
|
258
444
|
itemDepth: undefined,
|
|
259
445
|
groupPath: () => [],
|
|
260
446
|
isDev: false,
|
|
261
|
-
sidebarDensity: 'default',
|
|
262
447
|
donePrefix: '✅',
|
|
263
448
|
undonePrefix: '⏰',
|
|
449
|
+
dragContext: null,
|
|
264
450
|
})
|
|
265
451
|
|
|
266
452
|
const emit = defineEmits<{
|
|
267
|
-
'
|
|
268
|
-
'
|
|
269
|
-
'create-
|
|
270
|
-
'
|
|
453
|
+
'create-note-in-group': [payload: ParentNotePayload]
|
|
454
|
+
'create-notes-in-group': [payload: ParentNotePayload]
|
|
455
|
+
'create-folder-in-group': [payload: ParentNotePayload]
|
|
456
|
+
'rename-folder': [payload: FolderPayload]
|
|
457
|
+
'delete-entry': [payload: EntryPayload]
|
|
271
458
|
'rename-note': [payload: NotePayload]
|
|
272
459
|
'delete-note': [payload: NotePayload]
|
|
273
460
|
'create-note-around': [
|
|
274
461
|
payload: NotePayload & { placement: 'before' | 'after' },
|
|
275
462
|
]
|
|
276
463
|
'open-note-about': [payload: NotePayload]
|
|
277
|
-
'reorder-sidebar': [payload: ReorderPayload]
|
|
278
464
|
}>()
|
|
279
465
|
|
|
280
|
-
interface
|
|
281
|
-
|
|
466
|
+
interface ParentNotePayload {
|
|
467
|
+
parentNoteIndex?: string
|
|
468
|
+
parentFolderPath?: string[]
|
|
469
|
+
parentTocLineIndex?: number
|
|
282
470
|
text: string
|
|
283
471
|
}
|
|
284
472
|
|
|
285
|
-
interface
|
|
286
|
-
|
|
473
|
+
interface FolderPayload {
|
|
474
|
+
tocLineIndex: number
|
|
287
475
|
text: string
|
|
288
|
-
link?: string
|
|
289
476
|
}
|
|
290
477
|
|
|
291
|
-
interface
|
|
292
|
-
|
|
293
|
-
|
|
478
|
+
interface EntryPayload {
|
|
479
|
+
tocLineIndex: number
|
|
480
|
+
text: string
|
|
294
481
|
noteIndex?: string
|
|
295
482
|
}
|
|
296
483
|
|
|
297
|
-
interface
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
interface ReorderPayload {
|
|
304
|
-
dragType: 'group' | 'note'
|
|
305
|
-
groupPath?: string[]
|
|
306
|
-
noteIndex?: string
|
|
307
|
-
targetType: 'group' | 'note'
|
|
308
|
-
targetGroupPath?: string[]
|
|
309
|
-
targetNoteIndex?: string
|
|
310
|
-
placement?: 'before' | 'after'
|
|
484
|
+
interface NotePayload {
|
|
485
|
+
noteIndex: string
|
|
486
|
+
text: string
|
|
487
|
+
link?: string
|
|
311
488
|
}
|
|
312
489
|
|
|
313
490
|
type MenuType = 'more' | 'add'
|
|
314
|
-
type SidebarDensity = 'compact' | 'default' | 'loose'
|
|
315
491
|
type NoteStatus = 'done' | 'undone' | null
|
|
316
492
|
|
|
317
493
|
// 获取实际的 item depth(用于链接项的缩进)
|
|
318
494
|
const actualItemDepth = computed(() => props.itemDepth ?? props.depth)
|
|
319
495
|
const openMenuKey = ref<string | null>(null)
|
|
320
|
-
const
|
|
496
|
+
const injectedDrag = useSidebarDragContext()
|
|
321
497
|
let ignoredToggleKey: string | null = null
|
|
322
498
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
499
|
+
function getSidebarDrag(): SidebarDragContext | null {
|
|
500
|
+
return props.dragContext ?? injectedDrag
|
|
501
|
+
}
|
|
326
502
|
|
|
327
503
|
if (typeof document !== 'undefined') {
|
|
328
504
|
document.addEventListener('click', closeMenuOnDocumentClick, true)
|
|
329
|
-
document.addEventListener('dragend', clearDropTarget, true)
|
|
330
|
-
document.addEventListener('drop', clearDropTarget, true)
|
|
331
505
|
}
|
|
332
506
|
|
|
333
507
|
onBeforeUnmount(() => {
|
|
334
508
|
if (typeof document !== 'undefined') {
|
|
335
509
|
document.removeEventListener('click', closeMenuOnDocumentClick, true)
|
|
336
|
-
document.removeEventListener('dragend', clearDropTarget, true)
|
|
337
|
-
document.removeEventListener('drop', clearDropTarget, true)
|
|
338
510
|
}
|
|
339
511
|
})
|
|
340
512
|
|
|
@@ -349,39 +521,98 @@ function getChildItems(item: SidebarItem): SidebarItem[] {
|
|
|
349
521
|
|
|
350
522
|
// 获取项的唯一 key
|
|
351
523
|
function getItemKey(item: SidebarItem): string {
|
|
524
|
+
if (item.isDragPlaceholder) {
|
|
525
|
+
return `drag-placeholder-${item.tocLineIndex ?? 'slot'}`
|
|
526
|
+
}
|
|
352
527
|
return item.link || item.text
|
|
353
528
|
}
|
|
354
529
|
|
|
355
530
|
// 切换项的展开/折叠状态
|
|
356
531
|
function toggleItem(item: SidebarItem) {
|
|
532
|
+
if (!hasChildren(item)) return
|
|
357
533
|
item.collapsed = !item.collapsed
|
|
358
534
|
}
|
|
359
535
|
|
|
536
|
+
function onGroupTitleClick(item: SidebarItem) {
|
|
537
|
+
toggleItem(item)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function navigateToSidebarLink(link?: string) {
|
|
541
|
+
if (!link || isActive(link)) return
|
|
542
|
+
router.go(getFullLink(link))
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function activateParentNoteLink(item: SidebarItem) {
|
|
546
|
+
if (!hasChildren(item) || !item.link) return
|
|
547
|
+
toggleItem(item)
|
|
548
|
+
navigateToSidebarLink(item.link)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function suppressNextParentNoteClick(row: HTMLElement) {
|
|
552
|
+
row.addEventListener(
|
|
553
|
+
'click',
|
|
554
|
+
(event) => {
|
|
555
|
+
if (!(event.target instanceof Element)) return
|
|
556
|
+
if (!event.target.closest('.parent-note-link')) return
|
|
557
|
+
event.preventDefault()
|
|
558
|
+
event.stopImmediatePropagation()
|
|
559
|
+
},
|
|
560
|
+
{ capture: true, once: true },
|
|
561
|
+
)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function onParentNoteTitleClick(event: MouseEvent, item: SidebarItem) {
|
|
565
|
+
if (!hasChildren(item) || !item.link) return
|
|
566
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
567
|
+
return
|
|
568
|
+
}
|
|
569
|
+
event.preventDefault()
|
|
570
|
+
activateParentNoteLink(item)
|
|
571
|
+
}
|
|
572
|
+
|
|
360
573
|
function getNodeStyle(depth: number) {
|
|
361
574
|
return {
|
|
362
575
|
paddingLeft: `${depth * getIndentSize()}px`,
|
|
363
576
|
}
|
|
364
577
|
}
|
|
365
578
|
|
|
366
|
-
function getDensityClass(): string {
|
|
367
|
-
return `density-${props.sidebarDensity}`
|
|
368
|
-
}
|
|
369
|
-
|
|
370
579
|
function getIndentSize(): number {
|
|
371
|
-
|
|
372
|
-
if (props.sidebarDensity === 'loose') return 28
|
|
373
|
-
return 24
|
|
580
|
+
return SIDEBAR_INDENT_SIZE
|
|
374
581
|
}
|
|
375
582
|
|
|
376
583
|
function getGroupPath(item: SidebarItem): string[] {
|
|
377
584
|
return [...props.groupPath, item.text]
|
|
378
585
|
}
|
|
379
586
|
|
|
380
|
-
function
|
|
381
|
-
return
|
|
382
|
-
|
|
587
|
+
function isPureFolder(item: SidebarItem): boolean {
|
|
588
|
+
return !item.link && (!!(item.folderPath && item.folderPath.length > 0) || hasChildren(item))
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function getFolderPath(item: SidebarItem): string[] {
|
|
592
|
+
return item.folderPath ?? getGroupPath(item)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function serializeFolderPath(item: SidebarItem): string {
|
|
596
|
+
return JSON.stringify(getFolderPath(item))
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function getParentNotePayload(item: SidebarItem): ParentNotePayload {
|
|
600
|
+
const base = {
|
|
601
|
+
parentTocLineIndex: item.tocLineIndex,
|
|
383
602
|
text: item.text,
|
|
384
603
|
}
|
|
604
|
+
|
|
605
|
+
if (isPureFolder(item)) {
|
|
606
|
+
return {
|
|
607
|
+
...base,
|
|
608
|
+
parentFolderPath: getFolderPath(item),
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return {
|
|
613
|
+
...base,
|
|
614
|
+
parentNoteIndex: extractNoteIdFromLink(item.link) || '',
|
|
615
|
+
}
|
|
385
616
|
}
|
|
386
617
|
|
|
387
618
|
function getNotePayload(item: SidebarItem): NotePayload {
|
|
@@ -393,21 +624,20 @@ function getNotePayload(item: SidebarItem): NotePayload {
|
|
|
393
624
|
}
|
|
394
625
|
|
|
395
626
|
function getGroupMenuKey(item: SidebarItem, menuType: MenuType): string {
|
|
627
|
+
if (item.tocLineIndex !== undefined) {
|
|
628
|
+
return `group:${menuType}:${item.tocLineIndex}`
|
|
629
|
+
}
|
|
396
630
|
return `group:${menuType}:${getGroupPath(item).join('/')}`
|
|
397
631
|
}
|
|
398
632
|
|
|
399
|
-
function
|
|
400
|
-
return `
|
|
633
|
+
function getFolderMenuKey(item: SidebarItem, menuType: MenuType): string {
|
|
634
|
+
return `folder:${menuType}:${item.tocLineIndex ?? item.text}`
|
|
401
635
|
}
|
|
402
636
|
|
|
403
637
|
function getNoteMenuKey(item: SidebarItem, menuType: MenuType): string {
|
|
404
638
|
return `note:${menuType}:${extractNoteIdFromLink(item.link) || item.link || item.text}`
|
|
405
639
|
}
|
|
406
640
|
|
|
407
|
-
function getNoteDropKey(item: SidebarItem): string {
|
|
408
|
-
return `note:${extractNoteIdFromLink(item.link) || item.link || item.text}`
|
|
409
|
-
}
|
|
410
|
-
|
|
411
641
|
function toggleMenu(menuKey: string) {
|
|
412
642
|
if (ignoredToggleKey === menuKey) {
|
|
413
643
|
ignoredToggleKey = null
|
|
@@ -440,10 +670,11 @@ function closeMenuOnDocumentClick() {
|
|
|
440
670
|
}
|
|
441
671
|
|
|
442
672
|
function isGroupMenuOpen(item: SidebarItem): boolean {
|
|
443
|
-
return (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
673
|
+
return isMenuOpen(getGroupMenuKey(item, 'add'))
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function isFolderMenuOpen(item: SidebarItem): boolean {
|
|
677
|
+
return isMenuOpen(getFolderMenuKey(item, 'more'))
|
|
447
678
|
}
|
|
448
679
|
|
|
449
680
|
function isNoteMenuOpen(item: SidebarItem): boolean {
|
|
@@ -453,23 +684,43 @@ function isNoteMenuOpen(item: SidebarItem): boolean {
|
|
|
453
684
|
)
|
|
454
685
|
}
|
|
455
686
|
|
|
456
|
-
function
|
|
457
|
-
emit('
|
|
687
|
+
function createNoteInGroup(item: SidebarItem) {
|
|
688
|
+
emit('create-note-in-group', getParentNotePayload(item))
|
|
458
689
|
closeMenu()
|
|
459
690
|
}
|
|
460
691
|
|
|
461
|
-
function
|
|
462
|
-
emit('
|
|
692
|
+
function createNotesInGroup(item: SidebarItem) {
|
|
693
|
+
emit('create-notes-in-group', getParentNotePayload(item))
|
|
463
694
|
closeMenu()
|
|
464
695
|
}
|
|
465
696
|
|
|
466
|
-
function
|
|
467
|
-
emit('create-
|
|
697
|
+
function createFolderInGroup(item: SidebarItem) {
|
|
698
|
+
emit('create-folder-in-group', getParentNotePayload(item))
|
|
468
699
|
closeMenu()
|
|
469
700
|
}
|
|
470
701
|
|
|
471
|
-
function
|
|
472
|
-
|
|
702
|
+
function getFolderPayload(item: SidebarItem): FolderPayload {
|
|
703
|
+
return {
|
|
704
|
+
tocLineIndex: item.tocLineIndex ?? -1,
|
|
705
|
+
text: item.text,
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function getEntryPayload(item: SidebarItem): EntryPayload {
|
|
710
|
+
return {
|
|
711
|
+
tocLineIndex: item.tocLineIndex ?? -1,
|
|
712
|
+
text: item.text,
|
|
713
|
+
noteIndex: extractNoteIdFromLink(item.link) || undefined,
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function renameFolder(item: SidebarItem) {
|
|
718
|
+
emit('rename-folder', getFolderPayload(item))
|
|
719
|
+
closeMenu()
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function deleteFolder(item: SidebarItem) {
|
|
723
|
+
emit('delete-entry', getEntryPayload(item))
|
|
473
724
|
closeMenu()
|
|
474
725
|
}
|
|
475
726
|
|
|
@@ -479,7 +730,11 @@ function renameNote(item: SidebarItem) {
|
|
|
479
730
|
}
|
|
480
731
|
|
|
481
732
|
function deleteNote(item: SidebarItem) {
|
|
482
|
-
|
|
733
|
+
if (hasChildren(item) && item.tocLineIndex !== undefined) {
|
|
734
|
+
emit('delete-entry', getEntryPayload(item))
|
|
735
|
+
} else {
|
|
736
|
+
emit('delete-note', getNotePayload(item))
|
|
737
|
+
}
|
|
483
738
|
closeMenu()
|
|
484
739
|
}
|
|
485
740
|
|
|
@@ -496,228 +751,147 @@ function createNoteAround(item: SidebarItem, placement: 'before' | 'after') {
|
|
|
496
751
|
closeMenu()
|
|
497
752
|
}
|
|
498
753
|
|
|
499
|
-
function
|
|
500
|
-
if (!
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
event.dataTransfer.setData(dragDataType, JSON.stringify(payload))
|
|
504
|
-
event.dataTransfer.setData(
|
|
505
|
-
payload.type === 'note' ? dragNoteType : dragGroupType,
|
|
506
|
-
'1',
|
|
754
|
+
function isInteractiveDragTarget(target: EventTarget | null): boolean {
|
|
755
|
+
if (!(target instanceof HTMLElement)) return false
|
|
756
|
+
return !!target.closest(
|
|
757
|
+
'.group-arrow-btn, .row-actions, .action-menu, .action-btn',
|
|
507
758
|
)
|
|
508
759
|
}
|
|
509
760
|
|
|
510
|
-
|
|
511
|
-
const raw = event.dataTransfer?.getData(dragDataType)
|
|
512
|
-
if (!raw) return null
|
|
513
|
-
|
|
514
|
-
try {
|
|
515
|
-
return JSON.parse(raw) as DragPayload
|
|
516
|
-
} catch {
|
|
517
|
-
return null
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function getDragTypes(event: DragEvent): string[] {
|
|
522
|
-
return Array.from(event.dataTransfer?.types ?? [])
|
|
523
|
-
}
|
|
761
|
+
const DRAG_START_THRESHOLD_PX = 4
|
|
524
762
|
|
|
525
|
-
function
|
|
526
|
-
const
|
|
763
|
+
function onRowPointerDown(event: PointerEvent, item: SidebarItem) {
|
|
764
|
+
const sidebarDrag = getSidebarDrag()
|
|
765
|
+
if (!props.isDev || !sidebarDrag || event.button !== 0) return
|
|
766
|
+
if (isInteractiveDragTarget(event.target)) return
|
|
527
767
|
|
|
528
|
-
|
|
529
|
-
if (
|
|
768
|
+
const noteIndex = extractNoteIdFromLink(item.link)
|
|
769
|
+
if (!noteIndex || item.tocLineIndex === undefined) return
|
|
530
770
|
|
|
531
|
-
|
|
532
|
-
|
|
771
|
+
const row = event.currentTarget as HTMLElement
|
|
772
|
+
const fromLink =
|
|
773
|
+
event.target instanceof Element && !!event.target.closest('.row-main')
|
|
533
774
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
event.dataTransfer.dropEffect = 'move'
|
|
775
|
+
if (fromLink) {
|
|
776
|
+
event.preventDefault()
|
|
537
777
|
}
|
|
538
|
-
}
|
|
539
778
|
|
|
540
|
-
|
|
541
|
-
dropTarget.value = null
|
|
542
|
-
}
|
|
779
|
+
row.setPointerCapture(event.pointerId)
|
|
543
780
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
)
|
|
549
|
-
}
|
|
781
|
+
const startX = event.clientX
|
|
782
|
+
const startY = event.clientY
|
|
783
|
+
const pointerId = event.pointerId
|
|
784
|
+
let dragged = false
|
|
550
785
|
|
|
551
|
-
function
|
|
552
|
-
|
|
553
|
-
if (!row) return 'before'
|
|
786
|
+
function onPendingMove(moveEvent: PointerEvent) {
|
|
787
|
+
if (moveEvent.pointerId !== pointerId) return
|
|
554
788
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
const key = getGroupDropKey(item)
|
|
561
|
-
return {
|
|
562
|
-
'drop-before':
|
|
563
|
-
dropTarget.value?.type === 'group' &&
|
|
564
|
-
dropTarget.value.key === key &&
|
|
565
|
-
dropTarget.value.placement === 'before',
|
|
566
|
-
'drop-inside':
|
|
567
|
-
dropTarget.value?.type === 'group' &&
|
|
568
|
-
dropTarget.value.key === key &&
|
|
569
|
-
dropTarget.value.placement === 'inside',
|
|
570
|
-
}
|
|
571
|
-
}
|
|
789
|
+
const distance = Math.hypot(
|
|
790
|
+
moveEvent.clientX - startX,
|
|
791
|
+
moveEvent.clientY - startY,
|
|
792
|
+
)
|
|
793
|
+
if (distance < DRAG_START_THRESHOLD_PX) return
|
|
572
794
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
dropTarget.value.key === key &&
|
|
579
|
-
dropTarget.value.placement === 'before',
|
|
580
|
-
'drop-after':
|
|
581
|
-
dropTarget.value?.type === 'note' &&
|
|
582
|
-
dropTarget.value.key === key &&
|
|
583
|
-
dropTarget.value.placement === 'after',
|
|
795
|
+
dragged = true
|
|
796
|
+
moveEvent.preventDefault()
|
|
797
|
+
cleanupPending()
|
|
798
|
+
closeMenu()
|
|
799
|
+
sidebarDrag.startDrag(noteIndex, item.tocLineIndex!, row, moveEvent)
|
|
584
800
|
}
|
|
585
|
-
}
|
|
586
801
|
|
|
587
|
-
function
|
|
588
|
-
|
|
589
|
-
closeMenu()
|
|
590
|
-
setDragData(event, {
|
|
591
|
-
type: 'group',
|
|
592
|
-
groupPath: getGroupPath(item),
|
|
593
|
-
})
|
|
594
|
-
}
|
|
802
|
+
function onPendingUp(upEvent: PointerEvent) {
|
|
803
|
+
if (upEvent.pointerId !== pointerId) return
|
|
595
804
|
|
|
596
|
-
|
|
597
|
-
if (!props.isDev) return
|
|
598
|
-
const noteIndex = extractNoteIdFromLink(item.link)
|
|
599
|
-
if (!noteIndex) return
|
|
805
|
+
cleanupPending()
|
|
600
806
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const draggedType = getDraggedType(event)
|
|
612
|
-
if (!draggedType) return
|
|
613
|
-
|
|
614
|
-
setDropEffect(event)
|
|
615
|
-
|
|
616
|
-
const targetGroupPath = getGroupPath(item)
|
|
617
|
-
const payload = getDragData(event)
|
|
618
|
-
if (
|
|
619
|
-
draggedType === 'group' &&
|
|
620
|
-
payload?.groupPath &&
|
|
621
|
-
isSamePath(payload.groupPath, targetGroupPath)
|
|
622
|
-
) {
|
|
623
|
-
clearDropTarget()
|
|
624
|
-
return
|
|
807
|
+
if (!dragged && fromLink) {
|
|
808
|
+
if (hasChildren(item) && item.link) {
|
|
809
|
+
suppressNextParentNoteClick(row)
|
|
810
|
+
activateParentNoteLink(item)
|
|
811
|
+
} else if (hasChildren(item)) {
|
|
812
|
+
toggleItem(item)
|
|
813
|
+
} else {
|
|
814
|
+
row.querySelector<HTMLAnchorElement>('.row-main')?.click()
|
|
815
|
+
}
|
|
816
|
+
}
|
|
625
817
|
}
|
|
626
818
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
819
|
+
function cleanupPending() {
|
|
820
|
+
if (row.hasPointerCapture(pointerId)) {
|
|
821
|
+
row.releasePointerCapture(pointerId)
|
|
822
|
+
}
|
|
823
|
+
window.removeEventListener('pointermove', onPendingMove)
|
|
824
|
+
window.removeEventListener('pointerup', onPendingUp)
|
|
825
|
+
window.removeEventListener('pointercancel', onPendingUp)
|
|
631
826
|
}
|
|
827
|
+
|
|
828
|
+
window.addEventListener('pointermove', onPendingMove)
|
|
829
|
+
window.addEventListener('pointerup', onPendingUp)
|
|
830
|
+
window.addEventListener('pointercancel', onPendingUp)
|
|
632
831
|
}
|
|
633
832
|
|
|
634
|
-
function
|
|
635
|
-
|
|
833
|
+
function onFolderRowPointerDown(event: PointerEvent, item: SidebarItem) {
|
|
834
|
+
const sidebarDrag = getSidebarDrag()
|
|
835
|
+
if (!props.isDev || !sidebarDrag || event.button !== 0) return
|
|
836
|
+
if (isInteractiveDragTarget(event.target)) return
|
|
837
|
+
if (item.tocLineIndex === undefined) return
|
|
636
838
|
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
const targetGroupPath = getGroupPath(item)
|
|
839
|
+
const row = event.currentTarget as HTMLElement
|
|
840
|
+
const fromTitle =
|
|
841
|
+
event.target instanceof Element && !!event.target.closest('.group-title')
|
|
644
842
|
|
|
645
|
-
if (
|
|
646
|
-
|
|
647
|
-
dragType: 'note',
|
|
648
|
-
noteIndex: payload.noteIndex,
|
|
649
|
-
targetType: 'group',
|
|
650
|
-
targetGroupPath,
|
|
651
|
-
})
|
|
652
|
-
clearDropTarget()
|
|
653
|
-
return
|
|
843
|
+
if (fromTitle) {
|
|
844
|
+
event.preventDefault()
|
|
654
845
|
}
|
|
655
846
|
|
|
656
|
-
|
|
657
|
-
if (isSamePath(payload.groupPath, targetGroupPath)) {
|
|
658
|
-
clearDropTarget()
|
|
659
|
-
return
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
emit('reorder-sidebar', {
|
|
663
|
-
dragType: 'group',
|
|
664
|
-
groupPath: payload.groupPath,
|
|
665
|
-
targetType: 'group',
|
|
666
|
-
targetGroupPath,
|
|
667
|
-
placement: 'before',
|
|
668
|
-
})
|
|
669
|
-
}
|
|
847
|
+
row.setPointerCapture(event.pointerId)
|
|
670
848
|
|
|
671
|
-
|
|
672
|
-
|
|
849
|
+
const startX = event.clientX
|
|
850
|
+
const startY = event.clientY
|
|
851
|
+
const pointerId = event.pointerId
|
|
852
|
+
let dragged = false
|
|
673
853
|
|
|
674
|
-
function
|
|
675
|
-
|
|
854
|
+
function onPendingMove(moveEvent: PointerEvent) {
|
|
855
|
+
if (moveEvent.pointerId !== pointerId) return
|
|
676
856
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return
|
|
682
|
-
}
|
|
857
|
+
const distance = Math.hypot(
|
|
858
|
+
moveEvent.clientX - startX,
|
|
859
|
+
moveEvent.clientY - startY,
|
|
860
|
+
)
|
|
861
|
+
if (distance < DRAG_START_THRESHOLD_PX) return
|
|
683
862
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
863
|
+
dragged = true
|
|
864
|
+
moveEvent.preventDefault()
|
|
865
|
+
cleanupPending()
|
|
866
|
+
closeMenu()
|
|
867
|
+
sidebarDrag!.startFolderDrag(item.tocLineIndex!, row, moveEvent)
|
|
689
868
|
}
|
|
690
|
-
}
|
|
691
869
|
|
|
692
|
-
function
|
|
693
|
-
|
|
870
|
+
function onPendingUp(upEvent: PointerEvent) {
|
|
871
|
+
if (upEvent.pointerId !== pointerId) return
|
|
872
|
+
cleanupPending()
|
|
694
873
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
!payload ||
|
|
699
|
-
payload.type !== 'note' ||
|
|
700
|
-
!payload.noteIndex ||
|
|
701
|
-
!targetNoteIndex
|
|
702
|
-
) {
|
|
703
|
-
clearDropTarget()
|
|
704
|
-
return
|
|
874
|
+
if (!dragged && fromTitle) {
|
|
875
|
+
toggleItem(item)
|
|
876
|
+
}
|
|
705
877
|
}
|
|
706
878
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
879
|
+
function cleanupPending() {
|
|
880
|
+
if (row.hasPointerCapture(pointerId)) {
|
|
881
|
+
row.releasePointerCapture(pointerId)
|
|
882
|
+
}
|
|
883
|
+
window.removeEventListener('pointermove', onPendingMove)
|
|
884
|
+
window.removeEventListener('pointerup', onPendingUp)
|
|
885
|
+
window.removeEventListener('pointercancel', onPendingUp)
|
|
710
886
|
}
|
|
711
887
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
})
|
|
720
|
-
clearDropTarget()
|
|
888
|
+
window.addEventListener('pointermove', onPendingMove)
|
|
889
|
+
window.addEventListener('pointerup', onPendingUp)
|
|
890
|
+
window.addEventListener('pointercancel', onPendingUp)
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function getNodeId(item: SidebarItem): string {
|
|
894
|
+
return item.nodeId ?? computeSidebarNodeId(item)
|
|
721
895
|
}
|
|
722
896
|
|
|
723
897
|
// 获取完整链接(包含 base)
|
|
@@ -817,14 +991,8 @@ function getNoteDisplayText(text: string, link?: string): string {
|
|
|
817
991
|
<style scoped>
|
|
818
992
|
.group {
|
|
819
993
|
margin-bottom: 2px;
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
.group.density-compact {
|
|
823
|
-
margin-bottom: 0;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
.group.density-loose {
|
|
827
|
-
margin-bottom: 6px;
|
|
994
|
+
width: 100%;
|
|
995
|
+
min-width: 0;
|
|
828
996
|
}
|
|
829
997
|
|
|
830
998
|
.sidebar-row {
|
|
@@ -832,50 +1000,33 @@ function getNoteDisplayText(text: string, link?: string): string {
|
|
|
832
1000
|
display: flex;
|
|
833
1001
|
align-items: center;
|
|
834
1002
|
width: 100%;
|
|
835
|
-
min-height: 30px;
|
|
836
|
-
border-radius: 6px;
|
|
837
|
-
transition: background-color 0.18s ease;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
.sidebar-row.density-compact {
|
|
841
|
-
min-height: 26px;
|
|
842
|
-
border-radius: 4px;
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
.sidebar-row.density-loose {
|
|
846
1003
|
min-height: 36px;
|
|
847
1004
|
border-radius: 8px;
|
|
1005
|
+
transition: background-color 0.18s ease;
|
|
848
1006
|
}
|
|
849
1007
|
|
|
850
1008
|
.sidebar-row:hover {
|
|
851
1009
|
background: var(--vp-c-default-soft);
|
|
852
1010
|
}
|
|
853
1011
|
|
|
854
|
-
.sidebar-row.
|
|
855
|
-
|
|
856
|
-
position: absolute;
|
|
857
|
-
right: 8px;
|
|
858
|
-
left: 8px;
|
|
859
|
-
z-index: 4;
|
|
860
|
-
height: 2px;
|
|
861
|
-
background: var(--vp-c-brand-1);
|
|
862
|
-
border-radius: 999px;
|
|
863
|
-
content: '';
|
|
864
|
-
pointer-events: none;
|
|
1012
|
+
body.sidebar-drag-active .sidebar-row:not(.is-dragging-source) {
|
|
1013
|
+
cursor: grabbing;
|
|
865
1014
|
}
|
|
866
1015
|
|
|
867
|
-
.sidebar-row.
|
|
868
|
-
|
|
1016
|
+
.sidebar-row.is-dragging-source {
|
|
1017
|
+
opacity: 0.35;
|
|
869
1018
|
}
|
|
870
1019
|
|
|
871
|
-
|
|
872
|
-
|
|
1020
|
+
/* 语雀式:拖拽中源项原位保留布局高度,内容不可见 */
|
|
1021
|
+
.group.is-drag-source-slot,
|
|
1022
|
+
.sidebar-row.is-drag-source-slot {
|
|
1023
|
+
visibility: hidden;
|
|
1024
|
+
pointer-events: none;
|
|
873
1025
|
}
|
|
874
1026
|
|
|
875
|
-
.sidebar-row.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
outline-offset: -1px;
|
|
1027
|
+
.sidebar-row.is-drag-placeholder {
|
|
1028
|
+
visibility: hidden;
|
|
1029
|
+
pointer-events: none;
|
|
879
1030
|
}
|
|
880
1031
|
|
|
881
1032
|
.row-main {
|
|
@@ -883,35 +1034,57 @@ function getNoteDisplayText(text: string, link?: string): string {
|
|
|
883
1034
|
flex: 1;
|
|
884
1035
|
}
|
|
885
1036
|
|
|
886
|
-
.
|
|
1037
|
+
.parent-note-row {
|
|
887
1038
|
display: flex;
|
|
888
1039
|
align-items: center;
|
|
889
|
-
gap:
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1040
|
+
gap: 4px;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.note-row {
|
|
1044
|
+
display: flex;
|
|
1045
|
+
align-items: center;
|
|
1046
|
+
gap: 4px;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.group-arrow-btn {
|
|
1050
|
+
display: inline-flex;
|
|
1051
|
+
align-items: center;
|
|
1052
|
+
justify-content: center;
|
|
1053
|
+
flex: 0 0 20px;
|
|
1054
|
+
width: 20px;
|
|
1055
|
+
height: 20px;
|
|
1056
|
+
padding: 0;
|
|
895
1057
|
border: none;
|
|
1058
|
+
background: none;
|
|
896
1059
|
cursor: pointer;
|
|
897
|
-
text-align: left;
|
|
898
|
-
transition: color 0.25s;
|
|
899
1060
|
}
|
|
900
1061
|
|
|
901
|
-
.
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
font-size: 13px;
|
|
906
|
-
line-height: 18px;
|
|
1062
|
+
.group-arrow-spacer {
|
|
1063
|
+
flex: 0 0 20px;
|
|
1064
|
+
width: 20px;
|
|
1065
|
+
height: 20px;
|
|
907
1066
|
}
|
|
908
1067
|
|
|
909
|
-
.
|
|
910
|
-
|
|
1068
|
+
.parent-note-link {
|
|
1069
|
+
font-weight: 600;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.group-title {
|
|
1073
|
+
display: flex;
|
|
1074
|
+
align-items: center;
|
|
1075
|
+
gap: 6px;
|
|
1076
|
+
width: 100%;
|
|
911
1077
|
min-height: 36px;
|
|
912
1078
|
padding: 6px 8px;
|
|
1079
|
+
font-weight: 600;
|
|
913
1080
|
font-size: 14px;
|
|
914
1081
|
line-height: 22px;
|
|
1082
|
+
color: var(--vp-c-text-1);
|
|
1083
|
+
background: none;
|
|
1084
|
+
border: none;
|
|
1085
|
+
cursor: pointer;
|
|
1086
|
+
text-align: left;
|
|
1087
|
+
transition: color 0.25s;
|
|
915
1088
|
}
|
|
916
1089
|
|
|
917
1090
|
.group-title:hover {
|
|
@@ -944,17 +1117,19 @@ function getNoteDisplayText(text: string, link?: string): string {
|
|
|
944
1117
|
.nav-item {
|
|
945
1118
|
display: flex;
|
|
946
1119
|
align-items: center;
|
|
947
|
-
min-height:
|
|
948
|
-
padding:
|
|
1120
|
+
min-height: 36px;
|
|
1121
|
+
padding: 6px 8px;
|
|
949
1122
|
color: var(--vp-c-text-2);
|
|
950
1123
|
text-decoration: none;
|
|
951
1124
|
border-radius: 4px;
|
|
952
1125
|
font-size: 14px;
|
|
953
|
-
line-height:
|
|
1126
|
+
line-height: 22px;
|
|
954
1127
|
transition: all 0.25s;
|
|
955
1128
|
overflow: hidden;
|
|
956
1129
|
text-overflow: ellipsis;
|
|
957
1130
|
white-space: nowrap;
|
|
1131
|
+
-webkit-user-drag: none;
|
|
1132
|
+
user-select: none;
|
|
958
1133
|
}
|
|
959
1134
|
|
|
960
1135
|
.nav-item:hover {
|