@typix-editor/extension-floating-link 1.0.0
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/LICENSE +21 -0
- package/README.md +0 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +2 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Diyorbek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
File without changes
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
"use strict";var F=Object.defineProperty;var st=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var pt=Object.prototype.hasOwnProperty;var dt=(t,e)=>{for(var g in e)F(t,g,{get:e[g],enumerable:!0})},ft=(t,e,g,E)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of ct(e))!pt.call(t,a)&&a!==g&&F(t,a,{get:()=>e[a],enumerable:!(E=st(e,a))||E.enumerable});return t};var ut=t=>ft(F({},"__esModule",{value:!0}),t);var mt={};dt(mt,{FloatingLinkExtension:()=>et});module.exports=ut(mt);var Q=require("@lexical/react/LexicalComposerContext"),j=require("react-dom"),tt=require("@typix-editor/react");var v=require("@lexical/link"),C=require("@lexical/utils"),m=require("lexical"),D=require("react"),T=require("@typix-editor/react");function J(t){let[e,g]=(0,D.useState)(t),[E,a]=(0,D.useState)(!1);return(0,D.useEffect)(()=>{function d(){let o=(0,m.$getSelection)();if((0,m.$isRangeSelection)(o)){let s=(0,T.getSelectedNode)(o),k=(0,C.$findMatchingParent)(s,v.$isLinkNode),f=(0,C.$findMatchingParent)(s,v.$isAutoLinkNode);if(!(k||f)){a(!1);return}let R=o.getNodes().filter(n=>!(0,m.$isLineBreakNode)(n)).find(n=>{let h=(0,C.$findMatchingParent)(n,v.$isLinkNode),M=(0,C.$findMatchingParent)(n,v.$isAutoLinkNode);return k&&!k.is(h)||h&&!h.is(k)||f&&!f.is(M)||M&&(!M.is(f)||M.getIsUnlinked())});a(!R)}else if((0,m.$isNodeSelection)(o)){let s=o.getNodes();if(s.length===0){a(!1);return}let k=s[0],f=k.getParent();a((0,v.$isLinkNode)(f)||(0,v.$isLinkNode)(k))}}return(0,C.mergeRegister)(t.registerUpdateListener(({editorState:o})=>{o.read(()=>{d()})}),t.registerCommand(m.SELECTION_CHANGE_COMMAND,(o,s)=>(d(),g(s),!1),m.COMMAND_PRIORITY_CRITICAL),t.registerCommand(m.CLICK_COMMAND,o=>{let s=(0,m.$getSelection)();if((0,m.$isRangeSelection)(s)){let k=(0,T.getSelectedNode)(s),f=(0,C.$findMatchingParent)(k,v.$isLinkNode);if((0,v.$isLinkNode)(f)&&(o.metaKey||o.ctrlKey))return window.open(f.getURL(),"_blank"),!0}return!1},m.COMMAND_PRIORITY_LOW))},[t]),{activeEditor:e,isLink:E,setIsLink:a}}var _=require("@lexical/link"),P=require("@lexical/utils"),u=require("lexical"),l=require("react"),L=require("@typix-editor/react");function H({editor:t,isLink:e,setIsLink:g,verticalOffset:E}){let a=(0,l.useRef)(null),{floatingAnchorElem:d}=(0,L.useRootContext)(),o=(0,l.useRef)(null),[s,k]=(0,l.useState)(""),[f,R]=(0,l.useState)("https://"),[n,h]=(0,l.useState)(!1),[M,w]=(0,l.useState)(null),$=(0,l.useRef)(e);(0,l.useEffect)(()=>{$.current=e},[e]);let S=(0,l.useCallback)(()=>{let c=a.current;if(!$.current&&c&&d){(0,L.setFloatingElemPositionForLinkEditor)(null,c,d);return}let i=(0,u.$getSelection)(),N="";if((0,u.$isRangeSelection)(i)){let y=(0,L.getSelectedNode)(i),x=(0,P.$findMatchingParent)(y,_.$isLinkNode);x?N=x.getURL():(0,_.$isLinkNode)(y)&&(N=y.getURL())}else if((0,u.$isNodeSelection)(i)){let y=i.getNodes();if(y.length>0){let x=y[0],I=x.getParent();(0,_.$isLinkNode)(I)?N=I.getURL():(0,_.$isLinkNode)(x)&&(N=x.getURL())}}k(N);let A=(0,u.getDOMSelection)(t._window),X=document.activeElement;if(c===null)return;let O=t.getRootElement();if(i!==null&&O!==null&&t.isEditable()&&d){let y;if((0,u.$isNodeSelection)(i)){let x=i.getNodes();if(x.length>0){let I=t.getElementByKey(x[0].getKey());I&&(y=I.getBoundingClientRect())}}else A!==null&&O.contains(A.anchorNode)&&(y=A.focusNode?.parentElement?.getBoundingClientRect());y&&(y.y+=E,(0,L.setFloatingElemPositionForLinkEditor)(y,c,d)),w(i)}else(!X||!c.contains(X))&&(O!==null&&d&&(0,L.setFloatingElemPositionForLinkEditor)(null,c,d),w(null),h(!1),k(""));return!0},[d,t,E]);(0,l.useEffect)(()=>{let c=d?.parentElement,i=()=>{t.getEditorState().read(()=>{S()})};return window.addEventListener("resize",i),c&&c.addEventListener("scroll",i),()=>{window.removeEventListener("resize",i),c&&c.removeEventListener("scroll",i)}},[d?.parentElement,t,S]),(0,l.useEffect)(()=>(0,P.mergeRegister)(t.registerUpdateListener(({editorState:c})=>{c.read(()=>{S()})}),t.registerCommand(u.SELECTION_CHANGE_COMMAND,()=>(S(),!1),u.COMMAND_PRIORITY_LOW),t.registerCommand(u.KEY_ESCAPE_COMMAND,()=>e?(g(!1),!0):!1,u.COMMAND_PRIORITY_HIGH)),[t,S,g,e]),(0,l.useEffect)(()=>{t.getEditorState().read(()=>{S()})},[t,S]),(0,l.useEffect)(()=>{n&&o.current&&o.current.focus()},[n,e]),(0,l.useEffect)(()=>{let c=a.current;if(c===null)return;let i=N=>{!c.contains(N.relatedTarget)&&e&&(g(!1),h(!1))};return c.addEventListener("focusout",i),()=>{c.removeEventListener("focusout",i)}},[g,e]);let ot=(0,l.useCallback)(()=>{M!==null&&(f.trim()===""||f==="https://"||(t.update(()=>{t.dispatchCommand(_.TOGGLE_LINK_COMMAND,(0,L.sanitizeUrl)(f));let c=(0,u.$getSelection)();if((0,u.$isRangeSelection)(c)){let i=(0,L.getSelectedNode)(c).getParent();if((0,_.$isAutoLinkNode)(i)){let N=(0,_.$createLinkNode)(i.getURL(),{rel:i.__rel,target:i.__target,title:i.__title});i.replace(N,!0)}}}),R("https://"),h(!1)))},[t,f,M]),it=(0,l.useCallback)(()=>{h(!1),R(s||"https://")},[s]),rt=(0,l.useCallback)(()=>{R(s),h(!0)},[s]),lt=(0,l.useCallback)(()=>{t.dispatchCommand(_.TOGGLE_LINK_COMMAND,null)},[t]),at=f==="https://"||(0,L.validateUrl)(f);return{editorRef:a,renderProps:{isEditing:n,linkUrl:s,editedUrl:f,isValidUrl:at,setEditedUrl:R,submitLink:ot,cancelEdit:it,startEdit:rt,deleteLink:lt,inputRef:o}}}var W=require("@typix-editor/react");var r=require("react/jsx-runtime"),b={width:16,height:16,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};function K(){return(0,r.jsxs)("svg",{...b,"aria-hidden":"true",children:[(0,r.jsx)("path",{d:"M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"}),(0,r.jsx)("path",{d:"m15 5 4 4"})]})}function B(){return(0,r.jsxs)("svg",{...b,"aria-hidden":"true",children:[(0,r.jsx)("path",{d:"M3 6h18"}),(0,r.jsx)("path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"}),(0,r.jsx)("path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"}),(0,r.jsx)("line",{x1:"10",x2:"10",y1:"11",y2:"17"}),(0,r.jsx)("line",{x1:"14",x2:"14",y1:"11",y2:"17"})]})}function G(){return(0,r.jsx)("svg",{...b,"aria-hidden":"true",children:(0,r.jsx)("polyline",{points:"20 6 9 17 4 12"})})}function Y(){return(0,r.jsxs)("svg",{...b,"aria-hidden":"true",children:[(0,r.jsx)("path",{d:"M18 6 6 18"}),(0,r.jsx)("path",{d:"m6 6 12 12"})]})}function z(){return(0,r.jsxs)("svg",{...b,width:12,height:12,"aria-hidden":"true",children:[(0,r.jsx)("path",{d:"M15 3h6v6"}),(0,r.jsx)("path",{d:"M10 14 21 3"}),(0,r.jsx)("path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"})]})}function V(){return(0,r.jsxs)("svg",{...b,"aria-hidden":"true",children:[(0,r.jsx)("path",{d:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"}),(0,r.jsx)("path",{d:"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"})]})}var p=require("react/jsx-runtime");function Z({isEditing:t,linkUrl:e,editedUrl:g,isValidUrl:E,setEditedUrl:a,submitLink:d,cancelEdit:o,startEdit:s,deleteLink:k,inputRef:f}){let R=n=>{n.key==="Enter"?(n.preventDefault(),d()):n.key==="Escape"&&(n.preventDefault(),o())};return t?(0,p.jsxs)("div",{className:"typix-floating-link__edit",children:[(0,p.jsxs)("div",{className:"typix-floating-link__input-wrapper",children:[(0,p.jsx)("span",{className:"typix-floating-link__input-icon",children:(0,p.jsx)(V,{})}),(0,p.jsx)("input",{ref:f,className:"typix-floating-link__input",value:g,onChange:n=>a(n.target.value),onKeyDown:R,placeholder:"Paste or type a link...","aria-label":"Link URL",spellCheck:!1,autoComplete:"off"})]}),(0,p.jsxs)("div",{className:"typix-floating-link__actions",children:[(0,p.jsx)("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--cancel","aria-label":"Cancel",title:"Cancel",onClick:o,onMouseDown:n=>n.preventDefault(),children:(0,p.jsx)(Y,{})}),(0,p.jsx)("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--confirm","aria-label":"Apply link",title:"Apply",disabled:!E,onClick:n=>{n.preventDefault(),d()},onMouseDown:n=>n.preventDefault(),children:(0,p.jsx)(G,{})})]})]}):(0,p.jsxs)("div",{className:"typix-floating-link__view",children:[(0,p.jsxs)("a",{className:"typix-floating-link__url",href:(0,W.sanitizeUrl)(e),target:"_blank",rel:"noopener noreferrer",title:e,children:[(0,p.jsx)("span",{className:"typix-floating-link__url-text",children:e}),(0,p.jsx)(z,{})]}),(0,p.jsx)("div",{className:"typix-floating-link__divider"}),(0,p.jsx)("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--edit","aria-label":"Edit link",title:"Edit",onClick:n=>{n.preventDefault(),s()},onMouseDown:n=>n.preventDefault(),children:(0,p.jsx)(K,{})}),(0,p.jsx)("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--delete","aria-label":"Remove link",title:"Remove",onClick:k,onMouseDown:n=>n.preventDefault(),children:(0,p.jsx)(B,{})})]})}var U=require("react/jsx-runtime");function q({editor:t,isLink:e,setIsLink:g,children:E,verticalOffset:a}){let{editorRef:d,renderProps:o}=H({editor:t,isLink:e,setIsLink:g,verticalOffset:a});return(0,U.jsx)("div",{ref:d,className:"typix-floating-link",onMouseDown:s=>{s.target!==d.current&&s.preventDefault()},children:e?E?E(o):(0,U.jsx)(Z,{...o}):null})}var nt=require("react/jsx-runtime");function et({children:t,verticalOffset:e=40}={}){let[g]=(0,Q.useLexicalComposerContext)(),{floatingAnchorElem:E}=(0,tt.useRootContext)(),{activeEditor:a,isLink:d,setIsLink:o}=J(g);return E?(0,j.createPortal)((0,nt.jsx)(q,{editor:a,isLink:d,setIsLink:o,verticalOffset:e,children:t}),E):null}0&&(module.exports={FloatingLinkExtension});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode, JSX } from 'react';
|
|
3
|
+
|
|
4
|
+
type FloatingLinkRenderProps = {
|
|
5
|
+
/** Whether the editor is in editing mode (input visible) */
|
|
6
|
+
isEditing: boolean;
|
|
7
|
+
/** The current link URL (from the link node) */
|
|
8
|
+
linkUrl: string;
|
|
9
|
+
/** The URL being edited in the input */
|
|
10
|
+
editedUrl: string;
|
|
11
|
+
/** Whether the edited URL is valid */
|
|
12
|
+
isValidUrl: boolean;
|
|
13
|
+
/** Update the URL in the input */
|
|
14
|
+
setEditedUrl: (url: string) => void;
|
|
15
|
+
/** Submit the edited URL */
|
|
16
|
+
submitLink: () => void;
|
|
17
|
+
/** Cancel editing and return to view mode */
|
|
18
|
+
cancelEdit: () => void;
|
|
19
|
+
/** Enter edit mode */
|
|
20
|
+
startEdit: () => void;
|
|
21
|
+
/** Remove the link entirely */
|
|
22
|
+
deleteLink: () => void;
|
|
23
|
+
/** Ref for the URL input element (auto-focused when editing) */
|
|
24
|
+
inputRef: React.RefObject<HTMLInputElement | null>;
|
|
25
|
+
};
|
|
26
|
+
type FloatingLinkExtensionProps = {
|
|
27
|
+
/** Custom render function. Receives link state and actions, replaces the default UI entirely. */
|
|
28
|
+
children?: (props: FloatingLinkRenderProps) => ReactNode;
|
|
29
|
+
/** Vertical offset in px for positioning the floating element below the link (default: 40) */
|
|
30
|
+
verticalOffset?: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
declare function FloatingLinkExtension({ children, verticalOffset, }?: FloatingLinkExtensionProps): JSX.Element | null;
|
|
34
|
+
|
|
35
|
+
export { FloatingLinkExtension, type FloatingLinkExtensionProps, type FloatingLinkRenderProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode, JSX } from 'react';
|
|
3
|
+
|
|
4
|
+
type FloatingLinkRenderProps = {
|
|
5
|
+
/** Whether the editor is in editing mode (input visible) */
|
|
6
|
+
isEditing: boolean;
|
|
7
|
+
/** The current link URL (from the link node) */
|
|
8
|
+
linkUrl: string;
|
|
9
|
+
/** The URL being edited in the input */
|
|
10
|
+
editedUrl: string;
|
|
11
|
+
/** Whether the edited URL is valid */
|
|
12
|
+
isValidUrl: boolean;
|
|
13
|
+
/** Update the URL in the input */
|
|
14
|
+
setEditedUrl: (url: string) => void;
|
|
15
|
+
/** Submit the edited URL */
|
|
16
|
+
submitLink: () => void;
|
|
17
|
+
/** Cancel editing and return to view mode */
|
|
18
|
+
cancelEdit: () => void;
|
|
19
|
+
/** Enter edit mode */
|
|
20
|
+
startEdit: () => void;
|
|
21
|
+
/** Remove the link entirely */
|
|
22
|
+
deleteLink: () => void;
|
|
23
|
+
/** Ref for the URL input element (auto-focused when editing) */
|
|
24
|
+
inputRef: React.RefObject<HTMLInputElement | null>;
|
|
25
|
+
};
|
|
26
|
+
type FloatingLinkExtensionProps = {
|
|
27
|
+
/** Custom render function. Receives link state and actions, replaces the default UI entirely. */
|
|
28
|
+
children?: (props: FloatingLinkRenderProps) => ReactNode;
|
|
29
|
+
/** Vertical offset in px for positioning the floating element below the link (default: 40) */
|
|
30
|
+
verticalOffset?: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
declare function FloatingLinkExtension({ children, verticalOffset, }?: FloatingLinkExtensionProps): JSX.Element | null;
|
|
34
|
+
|
|
35
|
+
export { FloatingLinkExtension, type FloatingLinkExtensionProps, type FloatingLinkRenderProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import{useLexicalComposerContext as Pt}from"@lexical/react/LexicalComposerContext";import{createPortal as At}from"react-dom";import{useRootContext as Ot}from"@typix-editor/react";import{$isAutoLinkNode as $,$isLinkNode as h}from"@lexical/link";import{$findMatchingParent as C,mergeRegister as dt}from"@lexical/utils";import{$getSelection as X,$isLineBreakNode as ft,$isNodeSelection as ut,$isRangeSelection as J,CLICK_COMMAND as mt,COMMAND_PRIORITY_CRITICAL as gt,COMMAND_PRIORITY_LOW as Et,SELECTION_CHANGE_COMMAND as kt}from"lexical";import{useEffect as Lt,useState as H}from"react";import{getSelectedNode as K}from"@typix-editor/react";function B(t){let[a,u]=H(t),[m,p]=H(!1);return Lt(()=>{function l(){let n=X();if(J(n)){let i=K(n),d=C(i,h),s=C(i,$);if(!(d||s)){p(!1);return}let y=n.getNodes().filter(e=>!ft(e)).find(e=>{let E=C(e,h),_=C(e,$);return d&&!d.is(E)||E&&!E.is(d)||s&&!s.is(_)||_&&(!_.is(s)||_.getIsUnlinked())});p(!y)}else if(ut(n)){let i=n.getNodes();if(i.length===0){p(!1);return}let d=i[0],s=d.getParent();p(h(s)||h(d))}}return dt(t.registerUpdateListener(({editorState:n})=>{n.read(()=>{l()})}),t.registerCommand(kt,(n,i)=>(l(),u(i),!1),gt),t.registerCommand(mt,n=>{let i=X();if(J(i)){let d=K(i),s=C(d,h);if(h(s)&&(n.metaKey||n.ctrlKey))return window.open(s.getURL(),"_blank"),!0}return!1},Et))},[t]),{activeEditor:a,isLink:m,setIsLink:p}}import{$createLinkNode as yt,$isAutoLinkNode as _t,$isLinkNode as I,TOGGLE_LINK_COMMAND as G}from"@lexical/link";import{$findMatchingParent as vt,mergeRegister as ht}from"@lexical/utils";import{$getSelection as Y,$isNodeSelection as z,$isRangeSelection as V,COMMAND_PRIORITY_HIGH as Nt,COMMAND_PRIORITY_LOW as xt,getDOMSelection as Rt,KEY_ESCAPE_COMMAND as Ct,SELECTION_CHANGE_COMMAND as Mt}from"lexical";import{useCallback as M,useEffect as N,useRef as O,useState as D}from"react";import{getSelectedNode as W,sanitizeUrl as St,setFloatingElemPositionForLinkEditor as F,useRootContext as bt,validateUrl as It}from"@typix-editor/react";function Z({editor:t,isLink:a,setIsLink:u,verticalOffset:m}){let p=O(null),{floatingAnchorElem:l}=bt(),n=O(null),[i,d]=D(""),[s,y]=D("https://"),[e,E]=D(!1),[_,T]=D(null),U=O(a);N(()=>{U.current=a},[a]);let v=M(()=>{let r=p.current;if(!U.current&&r&&l){F(null,r,l);return}let o=Y(),k="";if(V(o)){let g=W(o),L=vt(g,I);L?k=L.getURL():I(g)&&(k=g.getURL())}else if(z(o)){let g=o.getNodes();if(g.length>0){let L=g[0],R=L.getParent();I(R)?k=R.getURL():I(L)&&(k=L.getURL())}}d(k);let P=Rt(t._window),w=document.activeElement;if(r===null)return;let A=t.getRootElement();if(o!==null&&A!==null&&t.isEditable()&&l){let g;if(z(o)){let L=o.getNodes();if(L.length>0){let R=t.getElementByKey(L[0].getKey());R&&(g=R.getBoundingClientRect())}}else P!==null&&A.contains(P.anchorNode)&&(g=P.focusNode?.parentElement?.getBoundingClientRect());g&&(g.y+=m,F(g,r,l)),T(o)}else(!w||!r.contains(w))&&(A!==null&&l&&F(null,r,l),T(null),E(!1),d(""));return!0},[l,t,m]);N(()=>{let r=l?.parentElement,o=()=>{t.getEditorState().read(()=>{v()})};return window.addEventListener("resize",o),r&&r.addEventListener("scroll",o),()=>{window.removeEventListener("resize",o),r&&r.removeEventListener("scroll",o)}},[l?.parentElement,t,v]),N(()=>ht(t.registerUpdateListener(({editorState:r})=>{r.read(()=>{v()})}),t.registerCommand(Mt,()=>(v(),!1),xt),t.registerCommand(Ct,()=>a?(u(!1),!0):!1,Nt)),[t,v,u,a]),N(()=>{t.getEditorState().read(()=>{v()})},[t,v]),N(()=>{e&&n.current&&n.current.focus()},[e,a]),N(()=>{let r=p.current;if(r===null)return;let o=k=>{!r.contains(k.relatedTarget)&&a&&(u(!1),E(!1))};return r.addEventListener("focusout",o),()=>{r.removeEventListener("focusout",o)}},[u,a]);let lt=M(()=>{_!==null&&(s.trim()===""||s==="https://"||(t.update(()=>{t.dispatchCommand(G,St(s));let r=Y();if(V(r)){let o=W(r).getParent();if(_t(o)){let k=yt(o.getURL(),{rel:o.__rel,target:o.__target,title:o.__title});o.replace(k,!0)}}}),y("https://"),E(!1)))},[t,s,_]),at=M(()=>{E(!1),y(i||"https://")},[i]),st=M(()=>{y(i),E(!0)},[i]),ct=M(()=>{t.dispatchCommand(G,null)},[t]),pt=s==="https://"||It(s);return{editorRef:p,renderProps:{isEditing:e,linkUrl:i,editedUrl:s,isValidUrl:pt,setEditedUrl:y,submitLink:lt,cancelEdit:at,startEdit:st,deleteLink:ct,inputRef:n}}}import{sanitizeUrl as Dt}from"@typix-editor/react";import{jsx as c,jsxs as S}from"react/jsx-runtime";var x={width:16,height:16,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};function q(){return S("svg",{...x,"aria-hidden":"true",children:[c("path",{d:"M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"}),c("path",{d:"m15 5 4 4"})]})}function Q(){return S("svg",{...x,"aria-hidden":"true",children:[c("path",{d:"M3 6h18"}),c("path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"}),c("path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"}),c("line",{x1:"10",x2:"10",y1:"11",y2:"17"}),c("line",{x1:"14",x2:"14",y1:"11",y2:"17"})]})}function j(){return c("svg",{...x,"aria-hidden":"true",children:c("polyline",{points:"20 6 9 17 4 12"})})}function tt(){return S("svg",{...x,"aria-hidden":"true",children:[c("path",{d:"M18 6 6 18"}),c("path",{d:"m6 6 12 12"})]})}function et(){return S("svg",{...x,width:12,height:12,"aria-hidden":"true",children:[c("path",{d:"M15 3h6v6"}),c("path",{d:"M10 14 21 3"}),c("path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"})]})}function nt(){return S("svg",{...x,"aria-hidden":"true",children:[c("path",{d:"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"}),c("path",{d:"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"})]})}import{jsx as f,jsxs as b}from"react/jsx-runtime";function ot({isEditing:t,linkUrl:a,editedUrl:u,isValidUrl:m,setEditedUrl:p,submitLink:l,cancelEdit:n,startEdit:i,deleteLink:d,inputRef:s}){let y=e=>{e.key==="Enter"?(e.preventDefault(),l()):e.key==="Escape"&&(e.preventDefault(),n())};return t?b("div",{className:"typix-floating-link__edit",children:[b("div",{className:"typix-floating-link__input-wrapper",children:[f("span",{className:"typix-floating-link__input-icon",children:f(nt,{})}),f("input",{ref:s,className:"typix-floating-link__input",value:u,onChange:e=>p(e.target.value),onKeyDown:y,placeholder:"Paste or type a link...","aria-label":"Link URL",spellCheck:!1,autoComplete:"off"})]}),b("div",{className:"typix-floating-link__actions",children:[f("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--cancel","aria-label":"Cancel",title:"Cancel",onClick:n,onMouseDown:e=>e.preventDefault(),children:f(tt,{})}),f("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--confirm","aria-label":"Apply link",title:"Apply",disabled:!m,onClick:e=>{e.preventDefault(),l()},onMouseDown:e=>e.preventDefault(),children:f(j,{})})]})]}):b("div",{className:"typix-floating-link__view",children:[b("a",{className:"typix-floating-link__url",href:Dt(a),target:"_blank",rel:"noopener noreferrer",title:a,children:[f("span",{className:"typix-floating-link__url-text",children:a}),f(et,{})]}),f("div",{className:"typix-floating-link__divider"}),f("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--edit","aria-label":"Edit link",title:"Edit",onClick:e=>{e.preventDefault(),i()},onMouseDown:e=>e.preventDefault(),children:f(q,{})}),f("button",{type:"button",className:"typix-floating-link__btn typix-floating-link__btn--delete","aria-label":"Remove link",title:"Remove",onClick:d,onMouseDown:e=>e.preventDefault(),children:f(Q,{})})]})}import{jsx as it}from"react/jsx-runtime";function rt({editor:t,isLink:a,setIsLink:u,children:m,verticalOffset:p}){let{editorRef:l,renderProps:n}=Z({editor:t,isLink:a,setIsLink:u,verticalOffset:p});return it("div",{ref:l,className:"typix-floating-link",onMouseDown:i=>{i.target!==l.current&&i.preventDefault()},children:a?m?m(n):it(ot,{...n}):null})}import{jsx as Tt}from"react/jsx-runtime";function Ft({children:t,verticalOffset:a=40}={}){let[u]=Pt(),{floatingAnchorElem:m}=Ot(),{activeEditor:p,isLink:l,setIsLink:n}=B(u);return m?At(Tt(rt,{editor:p,isLink:l,setIsLink:n,verticalOffset:a,children:t}),m):null}export{Ft as FloatingLinkExtension};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@typix-editor/extension-floating-link",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Floating link editor extension for Typix",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"author": "Diyorbek Juraev <mrdiyorbekjuraev@gmail.com>",
|
|
7
|
+
"homepage": "https://typix.com",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.cjs",
|
|
11
|
+
"module": "dist/index.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/mrdiyorbek-juraev/typix.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/mrdiyorbek-juraev/typix/issues"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": "^18.2.0 || ^19.0.0-0",
|
|
29
|
+
"react-dom": "^18.2.0 || ^19.0.0-0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@lexical/react": "^0.39.0",
|
|
33
|
+
"@types/react": "^19.2.7",
|
|
34
|
+
"@lexical/utils": "^0.39.0",
|
|
35
|
+
"@lexical/link": "^0.39.0",
|
|
36
|
+
"@types/react-dom": "^19.2.1",
|
|
37
|
+
"lexical": "^0.39.0",
|
|
38
|
+
"react": "^18.2.0 || ^19.0.0-0",
|
|
39
|
+
"react-dom": "^18.2.0 || ^19.0.0-0",
|
|
40
|
+
"@typix-editor/react": "4.0.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"build:fast": "tsup",
|
|
45
|
+
"dev": "pnpm build:fast --watch",
|
|
46
|
+
"clean": "git clean -xdf .cache .turbo dist node_modules rm -rf dist",
|
|
47
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
|
|
48
|
+
}
|
|
49
|
+
}
|