Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit and improves the link component. #1109

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Document from "@tiptap/extension-document";
import { Plugin, PluginKey } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";

export default Document.extend({
name: "selectionDecorator",
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey("selectionDecorator"),
state: {
init() {
return {
decorations: DecorationSet.empty,
selectedTextDecoration: null,
};
},
apply(tr, oldState) {
const { doc, selection } = tr;
let { decorations, selectedTextDecoration } = oldState;

if (selectedTextDecoration) {
decorations = decorations.remove([selectedTextDecoration]);
}

if (!selection.empty) {
selectedTextDecoration = Decoration.inline(
selection.from,
selection.to,
{ class: "selected-text" }
);
decorations = decorations.add(doc, [selectedTextDecoration]);
} else {
selectedTextDecoration = null;
}

return { decorations, selectedTextDecoration };
},
},
props: {
decorations(state) {
return this.getState(state).decorations;
},
},
}),
];
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import KeyboardShortcuts from "../KeyboardShortcuts/ExtensionConfig";
import Link from "../Link/ExtensionConfig";
import Mention, { createMentionSuggestions } from "../Mention/ExtensionConfig";
import Placeholder from "../Placeholder/ExtensionConfig";
import SelectionDecoration from "../SelectionDecoration/ExtensionConfig";
import SpecialMentions from "../SpecialMentions/ExtensionConfig";
import Table from "../Table/ExtensionConfig";
import Variable from "../Variable/ExtensionConfig";
Expand Down Expand Up @@ -52,6 +53,7 @@ const useCustomExtensions = ({
EmojiPicker,
FigCaption,
HighlightInternal,
SelectionDecoration,
Focus.configure({ mode: "shallowest" }),
Highlight,
VideoExtension,
Expand Down
61 changes: 31 additions & 30 deletions src/components/Editor/LinkPopOver.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,41 @@ import { createPortal } from "react-dom";
import { useTranslation } from "react-i18next";

import { LINK_VALIDATION_SCHEMA } from "./constants";
import { getLinkPopoverPosition } from "./Menu/Fixed/utils";
import { validateAndFormatUrl } from "./utils";

const LinkPopOver = ({ editor }) => {
const { view } = editor || {};
const { from } = editor.state.selection;
const initialTextContent = view?.state?.doc?.nodeAt(from)?.text || "";

const [arrowPosition, setArrowPosition] = useState({ top: 0, left: 0 });
const [popoverPosition, setPopoverPosition] = useState({ top: 0, left: 0 });
const [isEditing, setIsEditing] = useState(false);
const [isLinkActive, setIsLinkActive] = useState(editor?.isActive("link"));

const popOverRef = useRef(null);
const popoverRef = useRef(null);

const { t } = useTranslation();

const linkAttributes = editor?.getAttributes("link");

const updatePopoverPosition = () => {
if (!(view && popOverRef.current)) return;
const newPos = view.coordsAtPos(view.state.selection.$to.pos);

const popoverRect = popOverRef.current?.getBoundingClientRect();

const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;

const maxLeft = screenWidth - popoverRect.width;
const maxTop = screenHeight - popoverRect.height - 50;

const adjustedLeft = newPos?.left ? Math.min(newPos.left - 50, maxLeft) : 0;
const adjustedTop = newPos?.top ? Math.min(newPos.top - 22, maxTop) : 0;

setPopoverPosition({
top: `${adjustedTop}px`,
left: `${adjustedLeft}px`,
});
if (!view) return;

const { arrowPosition, popoverPosition } = getLinkPopoverPosition(
editor,
popoverRef
);
setPopoverPosition(popoverPosition);
setArrowPosition(arrowPosition);
};

const handleUnlink = () =>
editor.chain().focus().extendMarkRange("link").unsetLink().run();
const handleUnlink = editor
.chain()
.focus()
.extendMarkRange("link")
.unsetLink().run;

const removePopover = () => {
setIsEditing(false);
Expand Down Expand Up @@ -101,7 +96,7 @@ const LinkPopOver = ({ editor }) => {
const handleKeyDown = event =>
equals(event.key, "Escape") && setIsEditing(false);

useOnClickOutside(popOverRef, removePopover);
useOnClickOutside(popoverRef, removePopover);

useEffect(() => {
window.addEventListener("resize", removePopover);
Expand Down Expand Up @@ -213,14 +208,20 @@ const LinkPopOver = ({ editor }) => {

return createPortal(
isLinkActive ? (
<div
className="ne-link-popover"
id="ne-link-view-popover"
ref={popOverRef}
style={popoverStyle}
>
{isEditing ? renderEditingMode() : renderViewMode()}
</div>
<>
<div
className="ne-link-arrow fade-in"
style={{ top: arrowPosition.top, left: arrowPosition.left }}
/>
<div
className="ne-link-popover fade-in"
id="ne-link-view-popover"
ref={popoverRef}
style={popoverStyle}
>
{isEditing ? renderEditingMode() : renderViewMode()}
</div>
</>
) : null,
document.body
);
Expand Down
41 changes: 12 additions & 29 deletions src/components/Editor/Menu/Fixed/LinkAddPopOver.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next";
import { validateAndFormatUrl } from "components/Editor/utils";
import { URL_REGEXP } from "src/common/constants";

import { getCursorPos } from "./utils";
import { getLinkPopoverPosition, getCursorPos } from "./utils";

const LinkAddPopOver = ({
isAddLinkActive,
Expand All @@ -30,7 +30,7 @@ const LinkAddPopOver = ({
);
const [arrowPosition, setArrowPosition] = useState({ top: 0, left: 0 });

const popOverRef = useRef(null);
const popoverRef = useRef(null);

const { t } = useTranslation();

Expand Down Expand Up @@ -86,32 +86,15 @@ const LinkAddPopOver = ({
};

const updatePopoverPosition = () => {
if (!popOverRef.current) return;
const newPos = editor.view.coordsAtPos(editor.view.state.selection.$to.pos);

setArrowPosition({
top: `${newPos.top + 20}px`,
left: `${newPos.left - 10}px`,
});

const popoverRect = popOverRef.current?.getBoundingClientRect();

const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;

const maxLeft = screenWidth - popoverRect.width;
const maxTop = screenHeight - popoverRect.height - 50;

const adjustedLeft = newPos?.left ? Math.min(newPos.left - 50, maxLeft) : 0;
const adjustedTop = newPos?.top ? Math.min(newPos.top - 22, maxTop) : 0;

setPopoverPosition({
top: `${adjustedTop}px`,
left: `${adjustedLeft}px`,
});
const { arrowPosition, popoverPosition } = getLinkPopoverPosition(
editor,
popoverRef
);
setPopoverPosition(popoverPosition);
setArrowPosition(arrowPosition);
};

useOnClickOutside(popOverRef, removePopover);
useOnClickOutside(popoverRef, removePopover);

useEffect(() => {
if (editor && isAddLinkActive) {
Expand All @@ -130,13 +113,13 @@ const LinkAddPopOver = ({
? createPortal(
<>
<div
className="ne-link-arrow"
className="ne-link-arrow fade-in"
style={{ top: arrowPosition.top, left: arrowPosition.left }}
/>
<div
className="ne-link-popover"
className="ne-link-popover fade-in"
id="ne-link-add-popover"
ref={popOverRef}
ref={popoverRef}
style={popoverStyle}
>
<Input
Expand Down
53 changes: 53 additions & 0 deletions src/components/Editor/Menu/Fixed/utils.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,56 @@ export const buildOptionsFromAddonCommands = ({ editor, commands }) => {
};

export const getCursorPos = (editor, to) => editor?.view.coordsAtPos(to);

export const getLinkPopoverPosition = (editor, popoverRef) => {
const {
selection: {
$to: { pos: selectionEnd },
$from: { pos: selectionStart },
},
} = editor.view.state;
const selectionLength = selectionEnd - selectionStart;
let offset = 0;
if (selectionLength > 1) offset = Math.round(selectionLength / 2);

if (selectionLength === 1) offset = 1;

// Calculate the arrow position
const arrowCoords = editor.view.coordsAtPos(selectionStart + offset);
const arrowPosition = {
top: `${arrowCoords.top + 21}px`,
left: `${arrowCoords.left - 8.5}px`,
};

// Calculate the popover position
const popoverCoords = editor.view.coordsAtPos(selectionStart);
let adjustedLeft = popoverCoords?.left,
adjustedTop = popoverCoords?.top;
if (popoverRef?.current) {
const popoverRect = popoverRef.current?.getBoundingClientRect();

const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;

const maxLeft = screenWidth - popoverRect.width;
const maxTop = screenHeight - popoverRect.height - 50;

adjustedLeft = popoverCoords?.left
? Math.min(popoverCoords.left - 50, maxLeft)
: 0;

adjustedTop = popoverCoords?.top
? Math.min(popoverCoords.top - 22, maxTop)
: 0;
} else {
adjustedLeft = popoverCoords.left - 50;
adjustedTop = popoverCoords.top - 22;
}

const popoverPosition = {
top: `${adjustedTop}px`,
left: `${adjustedLeft}px`,
};

return { arrowPosition, popoverPosition };
};
5 changes: 5 additions & 0 deletions src/styles/abstracts/_animations.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@
transform: rotate(360deg);
}
}

.fade-in {
opacity: 0;
animation: fade-in 100ms ease-in-out forwards;
}
5 changes: 5 additions & 0 deletions src/styles/editor/_editor-content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,8 @@
.neeto-editor__image--right {
justify-content: flex-end;
}

.selected-text {
padding: 2px 0px;
background-color: rgb(var(--neeto-ui-gray-300));
}
8 changes: 4 additions & 4 deletions src/styles/editor/_link-popover.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
background-color: #fff;
color: #333;
padding: 10px 16px;
border-radius: 4px;
border: 1px solid #ccc;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
border-radius: 5px;
border: 1px solid var(--neeto-ui-popover-light-theme-border-color);
box-shadow: var(--neeto-ui-tooltip-light-theme-box-shadow);
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0s 0.3s;

&__url-input{
Expand Down Expand Up @@ -41,7 +41,7 @@
.ne-link-arrow {
z-index: 99999;
content: "";
position: absolute;
position: fixed;
top: 0;
left: 0;
border-style: solid;
Expand Down