Skip to content

Commit

Permalink
feat: keyboard shortcut for adding links (#1497)
Browse files Browse the repository at this point in the history
Co-authored-by: taiga-family-bot <taiga-family-bot@users.noreply.github.com>
  • Loading branch information
hakimio and taiga-family-bot authored Oct 2, 2024
1 parent edf18d1 commit 681b1ec
Showing 1 changed file with 78 additions and 9 deletions.
87 changes: 78 additions & 9 deletions projects/editor/extensions/link/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,59 @@
import {tuiParseNodeAttributes} from '@taiga-ui/editor/utils';
import type {Editor, KeyboardShortcutCommand, Range} from '@tiptap/core';
import {getHTMLFromFragment, markPasteRule} from '@tiptap/core';
import {Link} from '@tiptap/extension-link';
import {find} from 'linkifyjs';

function getCurrentWordBounds(editor: Editor): Range {
const {state} = editor;
const {selection} = state;
const {$anchor, empty} = selection;

if (!empty) {
return {
from: selection.from,
to: selection.to,
};
}

if ($anchor) {
const {pos} = $anchor;
const start = $anchor.start();
const parent = $anchor.parent;
const textBefore = parent.textBetween(0, pos - start, undefined, '\uFFFC');
const textAfter = parent.textBetween(
pos - start,
parent.content.size,
undefined,
'\uFFFC',
);

const wordBefore = textBefore
// eslint-disable-next-line unicorn/prefer-string-replace-all
.replaceAll(/\uFFFC/g, '')
.split(/\b/)
.pop();
const wordAfter = textAfter
// eslint-disable-next-line unicorn/prefer-string-replace-all
.replaceAll(/\uFFFC/g, '')
.split(/\b/)
.shift();

const from = pos - (wordBefore?.length ?? 0);
const to = pos + (wordAfter?.length ?? 0);

return {
from,
to,
};
}

return {
from: selection.to,
to: selection.to + 1,
};
}

export const TuiLink = Link.extend({
addAttributes() {
return {
Expand All @@ -16,37 +67,55 @@ export const TuiLink = Link.extend({
...this.parent?.(),
toggleLink:
(attributes) =>
({chain, state}) => {
({chain, state, editor}) => {
// eslint-disable-next-line no-lone-blocks
{
const {selection, doc, schema} = state;
const selected = doc.cut(selection.to, selection.to + 1);
const {doc, schema} = state;
const {from, to} = getCurrentWordBounds(editor);
const selected = doc.cut(to, to + 1);
const sliced = getHTMLFromFragment(
selected.content,
schema,
).replaceAll(/<\/?[^>]+(>|$)/g, '');

const forwardSymbolIsWhitespace = sliced === ' ';

const toggleMark = chain().toggleMark(this.name, attributes, {
extendEmptyMarkRange: true,
});
const toggleMark = chain()
.setTextSelection({from, to})
.toggleMark(this.name, attributes, {
extendEmptyMarkRange: true,
});

return (
forwardSymbolIsWhitespace
? toggleMark.setTextSelection(selection.to - 1)
? toggleMark.setTextSelection(to - 1)
: toggleMark
.setTextSelection(selection.to)
.setTextSelection(to)
.insertContent(
// require: `@tiptap/extension-text-style`
'<span style="font-size: 15px"> </span>',
)
.setTextSelection(selection.to - 1)
.setTextSelection(to - 1)
).run();
}
},
};
},

addKeyboardShortcuts(): Record<string, KeyboardShortcutCommand> {
return {
'Mod-k': ({editor}) => {
const isLink = this.editor.isActive('link');
const editorChain = editor.chain().focus();
const command = isLink
? editorChain.unsetLink()
: editorChain.toggleLink({href: ''});

return command.run();
},
};
},

addPasteRules() {
return [
markPasteRule({
Expand Down

0 comments on commit 681b1ec

Please sign in to comment.