@tnotesjs/ui 0.1.5 → 0.2.1
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 +30 -97
- package/dist/code/highlight.js +6 -15
- package/dist/components/Footprints/parse.js +1 -3
- package/dist/components/Mindmap/markdown.d.ts +1 -7
- package/dist/components/Mindmap/markdown.js +1 -15
- package/package.json +26 -9
- package/src/browser/clipboard.ts +16 -19
- package/src/browser/theme.ts +10 -15
- package/src/code/highlight.ts +89 -105
- package/src/components/Badge/Badge.vue +4 -4
- package/src/components/CodeBlock/CodeBlock.vue +43 -53
- package/src/components/CodeGroup/CodeGroup.vue +32 -35
- package/src/components/Footprints/Footprints.vue +2 -14
- package/src/components/Footprints/parse.ts +1 -3
- package/src/components/ImagePreview/ImagePreview.vue +101 -126
- package/src/components/Mermaid/Mermaid.vue +9 -12
- package/src/components/Mindmap/FocusBreadcrumbs.vue +31 -28
- package/src/components/Mindmap/InlineRuns.ts +25 -16
- package/src/components/Mindmap/Mindmap.vue +147 -81
- package/src/components/Mindmap/MindmapOutlineNode.vue +4 -1
- package/src/components/Mindmap/editor/AppIcon.vue +32 -11
- package/src/components/Mindmap/editor/CanvasContextMenu.vue +126 -49
- package/src/components/Mindmap/editor/LinkPopover.vue +101 -23
- package/src/components/Mindmap/editor/MarkdownView.vue +7 -4
- package/src/components/Mindmap/editor/MindmapView.vue +11 -3
- package/src/components/Mindmap/editor/OutlineView.vue +144 -58
- package/src/components/Mindmap/editor/RichInlineEditor.vue +93 -44
- package/src/components/Mindmap/editor/SelectionToolbar.vue +133 -41
- package/src/components/Mindmap/editor/canvasClipboard.ts +1 -5
- package/src/components/Mindmap/editor/imagePaste.ts +1 -1
- package/src/components/Mindmap/editor/mindmapClipboard.ts +17 -5
- package/src/components/Mindmap/editor/outlineDrag.ts +1 -1
- package/src/components/Mindmap/markdown.ts +10 -29
- package/src/components/WordList/RightClickMenu.vue +10 -35
- package/src/components/WordList/WordList.vue +19 -42
- package/src/entries/bilibili-video.ts +1 -1
- package/src/entries/excalidraw-editor.ts +20 -0
- package/src/entries/excalidraw-view.ts +15 -0
- package/src/entries/footprints.ts +1 -1
- package/src/entries/mermaid.ts +1 -1
- package/src/entries/mindmap.ts +1 -1
- package/src/entries/notes-table.ts +2 -2
- package/src/entries/word-list.ts +1 -1
- package/src/excalidraw/ExcalidrawSvg.vue +125 -0
- package/src/excalidraw/editorHost.ts +110 -0
- package/src/excalidraw/editorSession.ts +224 -0
- package/src/excalidraw/exporter.ts +50 -0
- package/src/excalidraw/focus.ts +33 -0
- package/src/excalidraw/fonts.ts +92 -0
- package/src/excalidraw/renderer.ts +105 -0
- package/src/excalidraw/scene.ts +93 -0
- package/src/index.ts +25 -33
- package/src/markdown/image.ts +4 -13
- package/src/styles/code.css +5 -5
- package/src/styles/prose.css +1 -1
- package/src/styles/swiper.css +1 -1
- package/src/styles/tokens.css +5 -6
- package/src/swiper/hydrate.ts +91 -102
- package/src/code/highlight.test.ts +0 -55
- package/src/components/Mermaid/Mermaid.test.ts +0 -54
- package/src/components/Mermaid/adoptLiveSvg.test.ts +0 -16
- package/src/components/Mindmap/FocusBreadcrumbs.test.ts +0 -265
- package/src/components/sharedMedia.test.ts +0 -139
- package/src/markdown/image.test.ts +0 -88
- package/src/swiper/hydrate.test.ts +0 -65
|
@@ -6,9 +6,15 @@ import {
|
|
|
6
6
|
parseMarkdown,
|
|
7
7
|
richSelectionRect,
|
|
8
8
|
serializeSubtree,
|
|
9
|
-
wrapTextLines
|
|
9
|
+
wrapTextLines
|
|
10
|
+
} from '@tnotesjs/mindmap-core'
|
|
11
|
+
import type {
|
|
12
|
+
InlineFormat,
|
|
13
|
+
InlineLink,
|
|
14
|
+
MindmapNode,
|
|
15
|
+
MindmapSession,
|
|
16
|
+
RichInlineEditorElement
|
|
10
17
|
} from '@tnotesjs/mindmap-core'
|
|
11
|
-
import type { InlineFormat, InlineLink, MindmapNode, MindmapSession, RichInlineEditorElement } from '@tnotesjs/mindmap-core'
|
|
12
18
|
import LinkPopover from './LinkPopover.vue'
|
|
13
19
|
import RichInlineEditor from './RichInlineEditor.vue'
|
|
14
20
|
import SelectionToolbar from './SelectionToolbar.vue'
|
|
@@ -104,7 +110,7 @@ function measureTextBlockHeight(
|
|
|
104
110
|
maxWidth: number,
|
|
105
111
|
fontSize: number,
|
|
106
112
|
lineHeight: number,
|
|
107
|
-
minHeight: number
|
|
113
|
+
minHeight: number
|
|
108
114
|
): number {
|
|
109
115
|
const lines = wrapTextLines(text || ' ', maxWidth, (s) => measureOutlineText(s, fontSize))
|
|
110
116
|
return Math.max(minHeight, lines.length * lineHeight + 8)
|
|
@@ -161,8 +167,7 @@ const titleHeight = computed(() => {
|
|
|
161
167
|
const root = props.session.focusRootNode
|
|
162
168
|
const draft = draftTextHeights.value.get(root.id)
|
|
163
169
|
const textH =
|
|
164
|
-
draft ??
|
|
165
|
-
measureTextBlockHeight(layoutDisplayText(root), contentAreaWidth(), 28, TITLE_LINE, 56)
|
|
170
|
+
draft ?? measureTextBlockHeight(layoutDisplayText(root), contentAreaWidth(), 28, TITLE_LINE, 56)
|
|
166
171
|
return textH + imageBlockHeight(root)
|
|
167
172
|
})
|
|
168
173
|
|
|
@@ -349,7 +354,7 @@ function selectionPosition(input: RichInlineEditorElement): { left: number; top:
|
|
|
349
354
|
const rect = richSelectionRect(input)
|
|
350
355
|
return {
|
|
351
356
|
left: Math.max(190, Math.min(window.innerWidth - 190, rect.left + rect.width / 2)),
|
|
352
|
-
top: Math.max(58, rect.top - 8)
|
|
357
|
+
top: Math.max(58, rect.top - 8)
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
|
|
@@ -372,17 +377,21 @@ const selectedTextFormats = computed<Partial<Record<InlineFormat, boolean>>>(()
|
|
|
372
377
|
const selection = textSelection.value
|
|
373
378
|
if (!selection) return {}
|
|
374
379
|
const formats: InlineFormat[] = ['bold', 'italic', 'underline', 'strike', 'highlight', 'code']
|
|
375
|
-
return Object.fromEntries(
|
|
376
|
-
format
|
|
377
|
-
|
|
378
|
-
|
|
380
|
+
return Object.fromEntries(
|
|
381
|
+
formats.map((format) => [
|
|
382
|
+
format,
|
|
383
|
+
props.session.inlineFormatActive(selection.nodeId, selection.start, selection.end, format)
|
|
384
|
+
])
|
|
385
|
+
)
|
|
379
386
|
})
|
|
380
387
|
|
|
381
388
|
const multiSelectionPosition = computed(() => {
|
|
382
389
|
if (props.version < 0 || props.session.selectionIds.size <= 1 || textSelection.value) return null
|
|
383
390
|
const id = props.session.selectedNode?.id
|
|
384
391
|
if (!id) return null
|
|
385
|
-
const row = containerRef.value?.querySelector(
|
|
392
|
+
const row = containerRef.value?.querySelector(
|
|
393
|
+
`.outline-row[data-node-id="${id}"]`
|
|
394
|
+
) as HTMLElement | null
|
|
386
395
|
if (!row) return null
|
|
387
396
|
const rect = row.getBoundingClientRect()
|
|
388
397
|
if (rect.width === 0 && rect.height === 0) {
|
|
@@ -391,7 +400,7 @@ const multiSelectionPosition = computed(() => {
|
|
|
391
400
|
if (rect.bottom < 48 || rect.top > window.innerHeight) return null
|
|
392
401
|
return {
|
|
393
402
|
left: Math.max(180, Math.min(window.innerWidth - 180, rect.left + rect.width / 2)),
|
|
394
|
-
top: Math.max(58, rect.top - 8)
|
|
403
|
+
top: Math.max(58, rect.top - 8)
|
|
395
404
|
}
|
|
396
405
|
})
|
|
397
406
|
|
|
@@ -474,7 +483,7 @@ function openSelectionLinkEditor() {
|
|
|
474
483
|
start: selection.start,
|
|
475
484
|
end: selection.end,
|
|
476
485
|
url: 'https://',
|
|
477
|
-
position: { left: selection.position.left, top: selection.position.top + 12 }
|
|
486
|
+
position: { left: selection.position.left, top: selection.position.top + 12 }
|
|
478
487
|
}
|
|
479
488
|
nextTick(() => linkPopoverRef.value?.focusInput())
|
|
480
489
|
}
|
|
@@ -603,7 +612,7 @@ function syncDraftText(node: MindmapNode, depth: number | null, text: string) {
|
|
|
603
612
|
maxW,
|
|
604
613
|
isTitle ? 28 : 15,
|
|
605
614
|
isTitle ? TITLE_LINE : TEXT_LINE,
|
|
606
|
-
isTitle ? 56 : ROW_HEIGHT
|
|
615
|
+
isTitle ? 56 : ROW_HEIGHT
|
|
607
616
|
)
|
|
608
617
|
if (draftTextHeights.value.get(node.id) !== h) {
|
|
609
618
|
const next = new Map(draftTextHeights.value)
|
|
@@ -651,10 +660,13 @@ function caretVisualBoundary(input: RichInlineEditorElement): { first: boolean;
|
|
|
651
660
|
'tabSize',
|
|
652
661
|
'whiteSpace',
|
|
653
662
|
'wordBreak',
|
|
654
|
-
'overflowWrap'
|
|
663
|
+
'overflowWrap'
|
|
655
664
|
] as const
|
|
656
665
|
for (const property of copied) {
|
|
657
|
-
mirror.style.setProperty(
|
|
666
|
+
mirror.style.setProperty(
|
|
667
|
+
property.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`),
|
|
668
|
+
style[property] as string
|
|
669
|
+
)
|
|
658
670
|
}
|
|
659
671
|
mirror.style.position = 'fixed'
|
|
660
672
|
mirror.style.left = '-10000px'
|
|
@@ -696,7 +708,11 @@ function inlineFormatShortcut(event: KeyboardEvent, hasSelection = true): Inline
|
|
|
696
708
|
return null
|
|
697
709
|
}
|
|
698
710
|
|
|
699
|
-
function applyInputFormatShortcut(
|
|
711
|
+
function applyInputFormatShortcut(
|
|
712
|
+
node: MindmapNode,
|
|
713
|
+
input: RichInlineEditorElement,
|
|
714
|
+
format: InlineFormat
|
|
715
|
+
) {
|
|
700
716
|
const caretStart = input.selectionStart ?? 0
|
|
701
717
|
const caretEnd = input.selectionEnd ?? 0
|
|
702
718
|
if (input.value.length === 0) return
|
|
@@ -713,7 +729,12 @@ function applyInputFormatShortcut(node: MindmapNode, input: RichInlineEditorElem
|
|
|
713
729
|
editor?.setSelectionRange(caretStart, caretStart)
|
|
714
730
|
})
|
|
715
731
|
} else {
|
|
716
|
-
const selection: TextSelectionState = {
|
|
732
|
+
const selection: TextSelectionState = {
|
|
733
|
+
nodeId: node.id,
|
|
734
|
+
start,
|
|
735
|
+
end,
|
|
736
|
+
position: selectionPosition(input)
|
|
737
|
+
}
|
|
717
738
|
textSelection.value = selection
|
|
718
739
|
restoreTextSelection(selection)
|
|
719
740
|
}
|
|
@@ -736,7 +757,12 @@ function applyInputClearFormats(node: MindmapNode, input: RichInlineEditorElemen
|
|
|
736
757
|
editor?.setSelectionRange(caretStart, caretStart)
|
|
737
758
|
})
|
|
738
759
|
} else {
|
|
739
|
-
const selection: TextSelectionState = {
|
|
760
|
+
const selection: TextSelectionState = {
|
|
761
|
+
nodeId: node.id,
|
|
762
|
+
start,
|
|
763
|
+
end,
|
|
764
|
+
position: selectionPosition(input)
|
|
765
|
+
}
|
|
740
766
|
textSelection.value = selection
|
|
741
767
|
restoreTextSelection(selection)
|
|
742
768
|
}
|
|
@@ -1017,7 +1043,8 @@ function onEditKeydown(node: MindmapNode, e: KeyboardEvent) {
|
|
|
1017
1043
|
const prevLen = prevRow.node.content.text.length
|
|
1018
1044
|
session.transact((doc) => {
|
|
1019
1045
|
doc.updateDisplayText(prevRow.node, prevRow.node.content.text + value)
|
|
1020
|
-
for (const c of [...node.children])
|
|
1046
|
+
for (const c of [...node.children])
|
|
1047
|
+
doc.move(c, prevRow.node, prevRow.node.children.length)
|
|
1021
1048
|
doc.remove(node)
|
|
1022
1049
|
})
|
|
1023
1050
|
session.select(prevId)
|
|
@@ -1121,7 +1148,9 @@ function selectedRoots(): MindmapNode[] {
|
|
|
1121
1148
|
}
|
|
1122
1149
|
|
|
1123
1150
|
function serializeSelection(): string {
|
|
1124
|
-
return selectedRoots()
|
|
1151
|
+
return selectedRoots()
|
|
1152
|
+
.map((node) => serializeSubtree(node))
|
|
1153
|
+
.join('\n')
|
|
1125
1154
|
}
|
|
1126
1155
|
|
|
1127
1156
|
function selectAllRows() {
|
|
@@ -1130,7 +1159,7 @@ function selectAllRows() {
|
|
|
1130
1159
|
props.session.selectMany(
|
|
1131
1160
|
list.map((row) => row.node.id),
|
|
1132
1161
|
list[list.length - 1].node.id,
|
|
1133
|
-
list[0].node.id
|
|
1162
|
+
list[0].node.id
|
|
1134
1163
|
)
|
|
1135
1164
|
containerRef.value?.focus()
|
|
1136
1165
|
}
|
|
@@ -1178,7 +1207,8 @@ function extendSelectionTo(index: number) {
|
|
|
1178
1207
|
const list = rows.value
|
|
1179
1208
|
const target = list[index]
|
|
1180
1209
|
if (!target) return
|
|
1181
|
-
const anchorId =
|
|
1210
|
+
const anchorId =
|
|
1211
|
+
props.session.selectionAnchor?.id ?? props.session.selectedNode?.id ?? target.node.id
|
|
1182
1212
|
const anchorIndex = list.findIndex((row) => row.node.id === anchorId)
|
|
1183
1213
|
const normalizedAnchor = anchorIndex >= 0 ? anchorIndex : index
|
|
1184
1214
|
const from = Math.min(normalizedAnchor, index)
|
|
@@ -1186,7 +1216,7 @@ function extendSelectionTo(index: number) {
|
|
|
1186
1216
|
props.session.selectMany(
|
|
1187
1217
|
list.slice(from, to + 1).map((row) => row.node.id),
|
|
1188
1218
|
target.node.id,
|
|
1189
|
-
list[normalizedAnchor].node.id
|
|
1219
|
+
list[normalizedAnchor].node.id
|
|
1190
1220
|
)
|
|
1191
1221
|
scrollRowIntoView(index)
|
|
1192
1222
|
}
|
|
@@ -1196,7 +1226,12 @@ function extendSelection(direction: -1 | 1) {
|
|
|
1196
1226
|
if (list.length === 0) return
|
|
1197
1227
|
const selected = props.session.selectedNode
|
|
1198
1228
|
const current = selected ? list.findIndex((row) => row.node.id === selected.id) : -1
|
|
1199
|
-
const next =
|
|
1229
|
+
const next =
|
|
1230
|
+
current < 0
|
|
1231
|
+
? direction > 0
|
|
1232
|
+
? 0
|
|
1233
|
+
: list.length - 1
|
|
1234
|
+
: Math.max(0, Math.min(list.length - 1, current + direction))
|
|
1200
1235
|
extendSelectionTo(next)
|
|
1201
1236
|
}
|
|
1202
1237
|
|
|
@@ -1264,9 +1299,10 @@ function onCopy(node: MindmapNode, e: ClipboardEvent) {
|
|
|
1264
1299
|
const input = e.currentTarget as RichInlineEditorElement
|
|
1265
1300
|
if (input.selectionStart !== input.selectionEnd) return
|
|
1266
1301
|
e.preventDefault()
|
|
1267
|
-
const text =
|
|
1268
|
-
|
|
1269
|
-
|
|
1302
|
+
const text =
|
|
1303
|
+
props.session.selectionIds.size > 1 && props.session.selectionIds.has(node.id)
|
|
1304
|
+
? serializeSelection()
|
|
1305
|
+
: serializeSubtree(node)
|
|
1270
1306
|
e.clipboardData?.setData('text/plain', text)
|
|
1271
1307
|
}
|
|
1272
1308
|
|
|
@@ -1369,8 +1405,8 @@ function onInlineLinkEnter(node: MindmapNode, link: InlineLink, event: MouseEven
|
|
|
1369
1405
|
url: link.url,
|
|
1370
1406
|
position: {
|
|
1371
1407
|
left: Math.max(220, Math.min(window.innerWidth - 220, rect.left + rect.width / 2)),
|
|
1372
|
-
top: Math.min(window.innerHeight - 54, rect.bottom + 6)
|
|
1373
|
-
}
|
|
1408
|
+
top: Math.min(window.innerHeight - 54, rect.bottom + 6)
|
|
1409
|
+
}
|
|
1374
1410
|
}
|
|
1375
1411
|
}
|
|
1376
1412
|
|
|
@@ -1433,7 +1469,7 @@ function onImageResizePointerDown(node: MindmapNode, e: PointerEvent) {
|
|
|
1433
1469
|
imgResize = {
|
|
1434
1470
|
id: node.id,
|
|
1435
1471
|
startX: e.clientX,
|
|
1436
|
-
startW: node.content.image.width ?? DEFAULT_OUTLINE_IMG_W
|
|
1472
|
+
startW: node.content.image.width ?? DEFAULT_OUTLINE_IMG_W
|
|
1437
1473
|
}
|
|
1438
1474
|
suppressRowClick = true
|
|
1439
1475
|
window.addEventListener('pointermove', onImageResizeMove)
|
|
@@ -1442,7 +1478,10 @@ function onImageResizePointerDown(node: MindmapNode, e: PointerEvent) {
|
|
|
1442
1478
|
|
|
1443
1479
|
function onImageResizeMove(e: PointerEvent) {
|
|
1444
1480
|
if (!imgResize) return
|
|
1445
|
-
const next = Math.max(
|
|
1481
|
+
const next = Math.max(
|
|
1482
|
+
MIN_IMG_W,
|
|
1483
|
+
Math.min(MAX_IMG_W, Math.round(imgResize.startW + (e.clientX - imgResize.startX)))
|
|
1484
|
+
)
|
|
1446
1485
|
const map = new Map(liveImageWidth.value)
|
|
1447
1486
|
map.set(imgResize.id, next)
|
|
1448
1487
|
liveImageWidth.value = map
|
|
@@ -1626,7 +1665,8 @@ let stopRangeSelect: (() => void) | null = null
|
|
|
1626
1665
|
function onRowPointerDown(row: Row, e: PointerEvent) {
|
|
1627
1666
|
if (e.button !== 0) return
|
|
1628
1667
|
const target = e.target as HTMLElement | null
|
|
1629
|
-
if (target?.closest('.row-gutter, .row-checkbox, .row-badge, .row-image, .row-image-handle'))
|
|
1668
|
+
if (target?.closest('.row-gutter, .row-checkbox, .row-badge, .row-image, .row-image-handle'))
|
|
1669
|
+
return
|
|
1630
1670
|
|
|
1631
1671
|
if (e.shiftKey) {
|
|
1632
1672
|
e.preventDefault()
|
|
@@ -1675,7 +1715,7 @@ function onRowPointerDown(row: Row, e: PointerEvent) {
|
|
|
1675
1715
|
props.session.selectMany(
|
|
1676
1716
|
selected.map((item) => item.node.id),
|
|
1677
1717
|
rows.value[index]?.node.id ?? null,
|
|
1678
|
-
rows.value[startIndex]?.node.id ?? null
|
|
1718
|
+
rows.value[startIndex]?.node.id ?? null
|
|
1679
1719
|
)
|
|
1680
1720
|
}
|
|
1681
1721
|
const onUp = (ev: PointerEvent) => {
|
|
@@ -1754,7 +1794,7 @@ function onGripPointerDown(node: MindmapNode, e: PointerEvent) {
|
|
|
1754
1794
|
dragging: false,
|
|
1755
1795
|
pointerX: e.clientX,
|
|
1756
1796
|
pointerY: e.clientY,
|
|
1757
|
-
indicator: null
|
|
1797
|
+
indicator: null
|
|
1758
1798
|
}
|
|
1759
1799
|
let finished = false
|
|
1760
1800
|
const startX = e.clientX
|
|
@@ -1826,7 +1866,9 @@ function calcDropIndicator(clientX: number, clientY: number, dragId: string): Dr
|
|
|
1826
1866
|
const idx = rowIndexAt(clientY)
|
|
1827
1867
|
if (idx < 0) return null
|
|
1828
1868
|
const hit = list[idx]
|
|
1829
|
-
const hitEl = el.querySelector(
|
|
1869
|
+
const hitEl = el.querySelector(
|
|
1870
|
+
`.outline-row[data-node-id="${hit.node.id}"]`
|
|
1871
|
+
) as HTMLElement | null
|
|
1830
1872
|
if (!hitEl) return null
|
|
1831
1873
|
const hitRect = hitEl.getBoundingClientRect()
|
|
1832
1874
|
const midY = (hitRect.top + hitRect.bottom) / 2
|
|
@@ -1856,7 +1898,9 @@ function calcDropIndicator(clientX: number, clientY: number, dragId: string): Dr
|
|
|
1856
1898
|
}
|
|
1857
1899
|
const anchor = list[anchorIdx]
|
|
1858
1900
|
|
|
1859
|
-
const anchorEl = el.querySelector(
|
|
1901
|
+
const anchorEl = el.querySelector(
|
|
1902
|
+
`.outline-row[data-node-id="${anchor.node.id}"]`
|
|
1903
|
+
) as HTMLElement | null
|
|
1860
1904
|
const anchorRect = (anchorEl ?? hitEl).getBoundingClientRect()
|
|
1861
1905
|
const indentEdge = anchorRect.left + anchor.depth * INDENT
|
|
1862
1906
|
const hasVisibleKids = anchor.node.children.some((c) => c.id !== dragId) && !anchor.node.collapsed
|
|
@@ -1873,7 +1917,7 @@ function calcDropIndicator(clientX: number, clientY: number, dragId: string): Dr
|
|
|
1873
1917
|
clientX,
|
|
1874
1918
|
indentEdge,
|
|
1875
1919
|
anchor.depth,
|
|
1876
|
-
INDENT
|
|
1920
|
+
INDENT
|
|
1877
1921
|
)
|
|
1878
1922
|
const place = resolved.node
|
|
1879
1923
|
const depth = resolved.depth
|
|
@@ -1890,7 +1934,7 @@ function lineIndicator(
|
|
|
1890
1934
|
type: 'before' | 'after' | 'child',
|
|
1891
1935
|
row: Row,
|
|
1892
1936
|
lineDepth: number,
|
|
1893
|
-
spacer: HTMLElement
|
|
1937
|
+
spacer: HTMLElement
|
|
1894
1938
|
): DropIndicator {
|
|
1895
1939
|
const top =
|
|
1896
1940
|
type === 'before'
|
|
@@ -1906,7 +1950,7 @@ function lineIndicator(
|
|
|
1906
1950
|
top,
|
|
1907
1951
|
left: lineLeft,
|
|
1908
1952
|
width,
|
|
1909
|
-
guideDepth: type === 'child' ? row.depth : null
|
|
1953
|
+
guideDepth: type === 'child' ? row.depth : null
|
|
1910
1954
|
}
|
|
1911
1955
|
}
|
|
1912
1956
|
|
|
@@ -1942,7 +1986,15 @@ function onDragUp() {
|
|
|
1942
1986
|
@copy="onContainerCopy"
|
|
1943
1987
|
@cut="onContainerCut"
|
|
1944
1988
|
>
|
|
1945
|
-
<div
|
|
1989
|
+
<div
|
|
1990
|
+
v-if="focusRoot"
|
|
1991
|
+
class="outline-title"
|
|
1992
|
+
:class="{
|
|
1993
|
+
'is-selected': focusRoot.id === selectedId,
|
|
1994
|
+
'has-image': !!focusRoot.content.image
|
|
1995
|
+
}"
|
|
1996
|
+
:style="{ minHeight: `${titleHeight}px` }"
|
|
1997
|
+
>
|
|
1946
1998
|
<RichInlineEditor
|
|
1947
1999
|
class="title-input"
|
|
1948
2000
|
:class="{ 'is-view-mode': focusedId !== focusRoot.id }"
|
|
@@ -1999,7 +2051,7 @@ function onDragUp() {
|
|
|
1999
2051
|
'is-matched': matches.has(row.node.id),
|
|
2000
2052
|
'is-hovered': hoveredId === row.node.id,
|
|
2001
2053
|
'has-image': !!row.node.content.image,
|
|
2002
|
-
'is-drag-source': drag?.dragging && drag.id === row.node.id
|
|
2054
|
+
'is-drag-source': drag?.dragging && drag.id === row.node.id
|
|
2003
2055
|
}"
|
|
2004
2056
|
:style="{ top: `${row.top}px`, height: `${row.height}px` }"
|
|
2005
2057
|
@mouseenter="hoveredId = row.node.id"
|
|
@@ -2035,25 +2087,32 @@ function onDragUp() {
|
|
|
2035
2087
|
class="row-collapse"
|
|
2036
2088
|
:class="{
|
|
2037
2089
|
collapsed: row.node.collapsed,
|
|
2038
|
-
visible: hoveredId === row.node.id || row.node.collapsed
|
|
2090
|
+
visible: hoveredId === row.node.id || row.node.collapsed
|
|
2091
|
+
}"
|
|
2092
|
+
:style="{
|
|
2093
|
+
left: `${row.depth > 0 ? (row.depth - 1) * INDENT + BULLET_CENTER - COLLAPSE_LEAD / 2 : 0}px`
|
|
2039
2094
|
}"
|
|
2040
|
-
:style="{ left: `${row.depth > 0 ? (row.depth - 1) * INDENT + BULLET_CENTER - COLLAPSE_LEAD / 2 : 0}px` }"
|
|
2041
2095
|
title="折叠/展开"
|
|
2042
2096
|
@click="onArrowClick(row.node, $event)"
|
|
2043
2097
|
@pointerdown.stop="onGripPointerDown(row.node, $event)"
|
|
2044
2098
|
/>
|
|
2045
2099
|
<span
|
|
2046
2100
|
class="row-bullet"
|
|
2047
|
-
:class="{
|
|
2101
|
+
:class="{
|
|
2102
|
+
'has-children': row.node.children.length > 0,
|
|
2103
|
+
collapsed: row.node.collapsed
|
|
2104
|
+
}"
|
|
2048
2105
|
:style="{ left: `${COLLAPSE_LEAD + row.depth * INDENT}px` }"
|
|
2049
2106
|
title="点击进入主题"
|
|
2050
2107
|
@click="onBulletClick(row.node, $event)"
|
|
2051
2108
|
@pointerdown.stop="onGripPointerDown(row.node, $event)"
|
|
2052
2109
|
>
|
|
2053
2110
|
<i class="bullet-dot" />
|
|
2054
|
-
<span
|
|
2055
|
-
|
|
2056
|
-
|
|
2111
|
+
<span
|
|
2112
|
+
v-if="row.node.collapsed && row.node.children.length > 0"
|
|
2113
|
+
class="bullet-count"
|
|
2114
|
+
>{{ descendantCount(row.node) }}</span
|
|
2115
|
+
>
|
|
2057
2116
|
</span>
|
|
2058
2117
|
</span>
|
|
2059
2118
|
|
|
@@ -2067,14 +2126,16 @@ function onDragUp() {
|
|
|
2067
2126
|
:title="row.node.content.checked ? '标记为未完成' : '标记为已完成'"
|
|
2068
2127
|
@click="onCheckboxClick(row.node, $event)"
|
|
2069
2128
|
>
|
|
2070
|
-
<svg v-if="row.node.content.checked" viewBox="0 0 16 16" aria-hidden="true"
|
|
2129
|
+
<svg v-if="row.node.content.checked" viewBox="0 0 16 16" aria-hidden="true">
|
|
2130
|
+
<path d="m3.5 8 3 3 6-6" />
|
|
2131
|
+
</svg>
|
|
2071
2132
|
</button>
|
|
2072
2133
|
|
|
2073
2134
|
<RichInlineEditor
|
|
2074
2135
|
class="row-input"
|
|
2075
2136
|
:class="{
|
|
2076
2137
|
'is-task-done': row.node.content.checked === true,
|
|
2077
|
-
'is-view-mode': focusedId !== row.node.id
|
|
2138
|
+
'is-view-mode': focusedId !== row.node.id
|
|
2078
2139
|
}"
|
|
2079
2140
|
:editor-id="row.node.id"
|
|
2080
2141
|
:raw="row.node.content.image ? row.node.content.text : row.node.content.raw"
|
|
@@ -2134,7 +2195,7 @@ function onDragUp() {
|
|
|
2134
2195
|
:style="{
|
|
2135
2196
|
top: `${drag.indicator.top}px`,
|
|
2136
2197
|
left: `${drag.indicator.left}px`,
|
|
2137
|
-
width: `${drag.indicator.width}px
|
|
2198
|
+
width: `${drag.indicator.width}px`
|
|
2138
2199
|
}"
|
|
2139
2200
|
/>
|
|
2140
2201
|
</div>
|
|
@@ -2147,7 +2208,13 @@ function onDragUp() {
|
|
|
2147
2208
|
<i class="bullet-dot" />
|
|
2148
2209
|
</div>
|
|
2149
2210
|
|
|
2150
|
-
<input
|
|
2211
|
+
<input
|
|
2212
|
+
ref="imagePickerRef"
|
|
2213
|
+
type="file"
|
|
2214
|
+
accept="image/*"
|
|
2215
|
+
hidden
|
|
2216
|
+
@change="onSelectionImagePicked"
|
|
2217
|
+
/>
|
|
2151
2218
|
|
|
2152
2219
|
<SelectionToolbar
|
|
2153
2220
|
v-if="textSelection && !linkEditor"
|
|
@@ -2236,7 +2303,9 @@ function onDragUp() {
|
|
|
2236
2303
|
color: var(--mm-text-dim);
|
|
2237
2304
|
font-weight: 500;
|
|
2238
2305
|
}
|
|
2239
|
-
.title-input.is-view-mode {
|
|
2306
|
+
.title-input.is-view-mode {
|
|
2307
|
+
cursor: text;
|
|
2308
|
+
}
|
|
2240
2309
|
.title-image {
|
|
2241
2310
|
flex: none;
|
|
2242
2311
|
padding: 0 0 8px;
|
|
@@ -2454,16 +2523,31 @@ function onDragUp() {
|
|
|
2454
2523
|
background: transparent;
|
|
2455
2524
|
cursor: pointer;
|
|
2456
2525
|
color: var(--mm-text-dim);
|
|
2457
|
-
transition:
|
|
2526
|
+
transition:
|
|
2527
|
+
border-color 0.12s,
|
|
2528
|
+
background 0.12s,
|
|
2529
|
+
box-shadow 0.12s;
|
|
2458
2530
|
}
|
|
2459
2531
|
.row-checkbox.checked {
|
|
2460
2532
|
border-color: var(--mm-accent);
|
|
2461
2533
|
background: var(--mm-accent);
|
|
2462
2534
|
color: white;
|
|
2463
2535
|
}
|
|
2464
|
-
.row-checkbox:hover {
|
|
2465
|
-
|
|
2466
|
-
|
|
2536
|
+
.row-checkbox:hover {
|
|
2537
|
+
border-color: var(--mm-accent);
|
|
2538
|
+
}
|
|
2539
|
+
.row-checkbox:focus-visible {
|
|
2540
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--mm-accent) 28%, transparent);
|
|
2541
|
+
}
|
|
2542
|
+
.row-checkbox svg {
|
|
2543
|
+
width: 14px;
|
|
2544
|
+
height: 14px;
|
|
2545
|
+
fill: none;
|
|
2546
|
+
stroke: currentColor;
|
|
2547
|
+
stroke-width: 2;
|
|
2548
|
+
stroke-linecap: round;
|
|
2549
|
+
stroke-linejoin: round;
|
|
2550
|
+
}
|
|
2467
2551
|
.row-input {
|
|
2468
2552
|
flex: 1;
|
|
2469
2553
|
min-width: 60px;
|
|
@@ -2486,7 +2570,9 @@ function onDragUp() {
|
|
|
2486
2570
|
background: transparent;
|
|
2487
2571
|
box-shadow: none;
|
|
2488
2572
|
}
|
|
2489
|
-
.row-input.is-view-mode {
|
|
2573
|
+
.row-input.is-view-mode {
|
|
2574
|
+
cursor: text;
|
|
2575
|
+
}
|
|
2490
2576
|
.row-input.is-task-done {
|
|
2491
2577
|
text-decoration: line-through;
|
|
2492
2578
|
color: var(--mm-text-dim);
|