Skip to content

Commit

Permalink
fix: regression for anchor links (#1614)
Browse files Browse the repository at this point in the history
  • Loading branch information
splincode authored Nov 27, 2024
1 parent ae5a8c2 commit b313ea1
Show file tree
Hide file tree
Showing 12 changed files with 3,008 additions and 5,701 deletions.
8,544 changes: 2,876 additions & 5,668 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@
"@maskito/angular": "1.9.0",
"@maskito/core": "1.9.0",
"@maskito/kit": "1.9.0",
"@ng-web-apis/common": "3.0.6",
"@ng-web-apis/intersection-observer": "3.2.0",
"@ng-web-apis/mutation-observer": "3.1.0",
"@ng-web-apis/resize-observer": "3.0.6",
"@ng-web-apis/universal": "3.0.7",
"@ng-web-apis/common": "3.2.3",
"@ng-web-apis/intersection-observer": "3.2.3",
"@ng-web-apis/mutation-observer": "3.2.3",
"@ng-web-apis/resize-observer": "3.2.3",
"@ng-web-apis/universal": "3.2.3",
"@nx/jest": "17.2.5",
"@nx/node": "17.2.5",
"@nx/workspace": "17.2.5",
Expand Down
14 changes: 11 additions & 3 deletions projects/tui-editor/components/edit-link/edit-link.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,11 @@ export class TuiEditLinkComponent {
setAnchor(anchor: string): void {
this.url = anchor;
this.changePrefix(true);
this.addLink.emit(this.href);
}

changePrefix(isPrefix: boolean): void {
this.prefix = isPrefix ? TUI_EDITOR_LINK_HASH_PREFIX : this.defaultProtocol;
changePrefix(useHash: boolean): void {
this.prefix = useHash ? TUI_EDITOR_LINK_HASH_PREFIX : this.defaultProtocol;
}

onSave(): void {
Expand Down Expand Up @@ -164,6 +165,10 @@ export class TuiEditLinkComponent {
.prefix as TuiEditorLinkPrefix) || this.defaultProtocol;

if (a) {
if (this.isOnlyAnchorMode) {
return TUI_EDITOR_LINK_HASH_PREFIX;
}

return (!a.getAttribute('href') && a.getAttribute('id')) ||
a.getAttribute('href')?.startsWith(TUI_EDITOR_LINK_HASH_PREFIX)
? TUI_EDITOR_LINK_HASH_PREFIX
Expand All @@ -176,7 +181,10 @@ export class TuiEditLinkComponent {
private detectAnchorMode(): boolean {
const a = this.getAnchorElement();

return !a?.href && !!a?.getAttribute('id');
return (
!a?.href &&
(!!a?.getAttribute('id') || a?.getAttribute('data-type') === 'jump-anchor')
);
}

private getFocusedParentElement(): HTMLElement | null {
Expand Down
11 changes: 10 additions & 1 deletion projects/tui-editor/components/toolbar/toolbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
TUI_IMAGE_LOADER,
TuiEditorOptions,
} from '@tinkoff/tui-editor/tokens';
import {tuiGetCurrentWordBounds} from '@tinkoff/tui-editor/utils';
import {Observable} from 'rxjs';
import {take, takeUntil} from 'rxjs/operators';

Expand Down Expand Up @@ -250,7 +251,15 @@ export class TuiToolbarComponent {
}

onLink(url?: string): void {
this.editor?.toggleLink(url ?? '');
if (url === '#') {
const editor = this.editor?.getOriginTiptapEditor();
const {from = 0} = editor ? tuiGetCurrentWordBounds(editor) : {};

this.editor?.setAnchor('');
this.editor?.getOriginTiptapEditor()?.commands.focus((from ?? 0) + 1);
} else {
this.editor?.toggleLink(url ?? '');
}
}

enabled(tool: TuiEditorTool): boolean {
Expand Down
1 change: 1 addition & 0 deletions projects/tui-editor/constants/hack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TUI_TIPTAP_WHITESPACE_HACK = `<span style="font-size: 15px"> </span>`; // require: `@tiptap/extension-text-style`
1 change: 1 addition & 0 deletions projects/tui-editor/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './default-events';
export * from './default-font-options-handler';
export * from './default-html5-media-attributes';
export * from './default-link-options-handler';
export * from './hack';
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {TUI_TIPTAP_WHITESPACE_HACK} from '@tinkoff/tui-editor/constants';
import {tuiGetCurrentWordBounds, tuiGetSlicedFragment} from '@tinkoff/tui-editor/utils';
import {Mark, mergeAttributes} from '@tiptap/core';

declare module '@tiptap/core' {
Expand Down Expand Up @@ -42,11 +44,24 @@ export const TuiJumpAnchor = Mark.create({
return {
setAnchor:
id =>
({chain}) =>
chain()
({chain, state, editor}) => {
const {from, to} = tuiGetCurrentWordBounds(editor);
const sliced = tuiGetSlicedFragment(state, to);
const forwardSymbolIsWhitespace = sliced === ` `;
const jumpAnchorMark = chain()
.setTextSelection({from, to})
.extendMarkRange(`jumpAnchor`)
.setMark(`jumpAnchor`, {id})
.run(),
.setMark(`jumpAnchor`, {id});

return (
forwardSymbolIsWhitespace
? jumpAnchorMark.setTextSelection(to - 1)
: jumpAnchorMark
.setTextSelection(to)
.insertContent(TUI_TIPTAP_WHITESPACE_HACK)
.setTextSelection(to - 1)
).run();
},

removeAnchor:
() =>
Expand Down
38 changes: 19 additions & 19 deletions projects/tui-editor/extensions/link/link.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import {tuiParseNodeAttributes} from '@tinkoff/tui-editor/utils';
import {getHTMLFromFragment, markPasteRule} from '@tiptap/core';
import {TUI_TIPTAP_WHITESPACE_HACK} from '@tinkoff/tui-editor/constants';
import {
tuiGetCurrentWordBounds,
tuiGetSlicedFragment,
tuiParseNodeAttributes,
} from '@tinkoff/tui-editor/utils';
import {markPasteRule} from '@tiptap/core';
import {Link} from '@tiptap/extension-link';
import {find} from 'linkifyjs';

Expand All @@ -16,31 +21,26 @@ export const TuiLink = Link.extend({
...this.parent?.(),
toggleLink:
attributes =>
({chain, state}) => {
({chain, state, editor}) => {
// eslint-disable-next-line no-lone-blocks
{
const {selection, doc} = state;
const selected = doc.cut(selection.to, selection.to + 1);
const sliced = getHTMLFromFragment(
selected.content,
state.schema,
).replace(/<\/?[^>]+(>|$)/g, ``);
const {from, to} = tuiGetCurrentWordBounds(editor);
const sliced = tuiGetSlicedFragment(state, to);
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)
.insertContent(
// require: `@tiptap/extension-text-style`
`<span style="font-size: 15px"> </span>`,
)
.setTextSelection(selection.to - 1)
.setTextSelection(to)
.insertContent(TUI_TIPTAP_WHITESPACE_HACK)
.setTextSelection(to - 1)
).run();
}
},
Expand Down
51 changes: 51 additions & 0 deletions projects/tui-editor/utils/get-current-word-bounds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type {Editor, Range} from '@tiptap/core';

export function tuiGetCurrentWordBounds(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,
};
}
12 changes: 12 additions & 0 deletions projects/tui-editor/utils/get-sliced-fragment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {getHTMLFromFragment} from '@tiptap/core';
import type {EditorState} from '@tiptap/pm/state';

export function tuiGetSlicedFragment(state: EditorState, to: number): string {
const {doc, schema} = state;
const selected = doc.cut(to, to + 1);

return getHTMLFromFragment(selected.content, schema).replaceAll(
/<\/?[^>]+(>|$)/g,
``,
);
}
2 changes: 2 additions & 0 deletions projects/tui-editor/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
export * from './delete-nodes';
export * from './get-current-word-bounds';
export * from './get-element-point';
export * from './get-gradient-data';
export * from './get-mark-range';
export * from './get-nested-nodes';
export * from './get-selected-content';
export * from './get-selection-state';
export * from './get-sliced-fragment';
export * from './insert-html';
export * from './insert-text';
export * from './is-selection-in';
Expand Down
2 changes: 1 addition & 1 deletion taiga-ui
Submodule taiga-ui updated 130 files

0 comments on commit b313ea1

Please sign in to comment.