diff --git a/src/helpers/links.js b/src/helpers/links.js index 007c9f791d1..b4223f9c59c 100644 --- a/src/helpers/links.js +++ b/src/helpers/links.js @@ -56,6 +56,9 @@ const domHref = function(node) { if (ref.match(/^[a-zA-Z]*:/)) { return ref } + if (ref.startsWith('#')) { + return ref + } const match = ref.match(/^([^?]*)\?fileId=(\d+)/) if (match) { const [, relPath, id] = match @@ -82,9 +85,10 @@ const openLink = function(event, _attrs) { const linkElement = event.target.closest('a') const htmlHref = linkElement.href const query = OC.parseQueryString(htmlHref) - const fragment = OC.parseQueryString(htmlHref.split('#').pop()) - if (query.dir && fragment.relPath) { - const filename = fragment.relPath.split('/').pop() + const fragment = htmlHref.split('#').pop() + const fragmentQuery = OC.parseQueryString(fragment) + if (query?.dir && fragmentQuery?.relPath) { + const filename = fragmentQuery.relPath.split('/').pop() const path = `${query.dir}/${filename}` document.title = `${filename} - ${OC.theme.title}` if (window.location.pathname.match(/apps\/files\/$/)) { @@ -95,7 +99,7 @@ const openLink = function(event, _attrs) { OCA.Viewer.open({ path }) return } - if (query.fileId) { + if (query?.fileId) { // open the direct file link window.open(generateUrl(`/f/${query.fileId}`)) return @@ -104,6 +108,13 @@ const openLink = function(event, _attrs) { console.error('Invalid link', htmlHref) return false } + if (fragment) { + const el = document.getElementById(fragment) + if (el) { + el.scrollIntoView() + return + } + } window.open(htmlHref) return true } diff --git a/src/nodes/Heading/HeadingView.vue b/src/nodes/Heading/HeadingView.vue new file mode 100644 index 00000000000..c2a728a3d1d --- /dev/null +++ b/src/nodes/Heading/HeadingView.vue @@ -0,0 +1,112 @@ + + + + + + + diff --git a/src/nodes/Heading/index.js b/src/nodes/Heading/index.js index ce7e73e7862..63614da7ef1 100644 --- a/src/nodes/Heading/index.js +++ b/src/nodes/Heading/index.js @@ -1,8 +1,18 @@ import TipTapHeading from '@tiptap/extension-heading' +import { VueNodeViewRenderer } from '@tiptap/vue-2' import debounce from 'debounce' + +import HeadingView from './HeadingView.vue' import { setHeadings, extractHeadings } from './extractor.js' const Heading = TipTapHeading.extend({ + addOptions() { + return { + ...this.parent?.(), + linkSymbol: '#', + } + }, + addAttributes() { return { ...this.parent(), @@ -16,6 +26,11 @@ const Heading = TipTapHeading.extend({ }, } }, + + addNodeView() { + return VueNodeViewRenderer(HeadingView) + }, + addKeyboardShortcuts() { return this.options.levels.reduce((items, level) => ({ ...items, diff --git a/src/plugins/link.js b/src/plugins/link.js index 4002dc0dea5..0349c50d524 100644 --- a/src/plugins/link.js +++ b/src/plugins/link.js @@ -15,7 +15,6 @@ const clickHandler = ({ editor, type, onClick }) => { console.debug(link) return false } - // We use custom onClick handler only for left clicks if (event.button === 0 && !event.ctrlKey) { event.stopPropagation()