plug-and-play-editor 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class E{container;textArea;editorArea;toolbar;plugins=new Map;constructor(t,a=[]){const n=typeof t=="string"?document.querySelector(t):t;if(!n||n.tagName!=="TEXTAREA")throw new Error("Editor must be initialized on a <textarea> element.");this.textArea=n,this.container=document.createElement("div"),this.container.className="play-editor-container",this.toolbar=document.createElement("div"),this.toolbar.className="play-editor-toolbar",this.editorArea=document.createElement("div"),this.editorArea.className="play-editor-content",this.editorArea.contentEditable="true",this.editorArea.innerHTML=this.textArea.value,this.init(a)}init(t){this.textArea.style.display="none",this.textArea.parentNode?.insertBefore(this.container,this.textArea),this.container.appendChild(this.toolbar),this.container.appendChild(this.editorArea),this.editorArea.addEventListener("input",()=>{this.textArea.value=this.editorArea.innerHTML}),this.editorArea.addEventListener("keydown",a=>{a.key==="Tab"&&(a.preventDefault(),document.execCommand("insertHTML",!1,"&emsp;"),this.textArea.value=this.editorArea.innerHTML)}),t.forEach(a=>{this.plugins.set(a.name,a),a.init(this)})}exec(t,a=void 0){this.editorArea.focus(),document.execCommand(t,!1,a),this.textArea.value=this.editorArea.innerHTML}addToolbarButton(t,a,n){const o=document.createElement("button");return o.type="button",o.className="play-editor-btn",o.title=a,o.innerHTML=t,o.addEventListener("click",i=>{i.preventDefault(),n()}),this.toolbar.appendChild(o),o}addToolbarDivider(){const t=document.createElement("div");t.className="play-editor-divider",this.toolbar.appendChild(t)}getContent(){return this.editorArea.innerHTML}setContent(t){this.editorArea.innerHTML=t,this.textArea.value=t}}const c=e=>`<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${e}</svg>`,d={bold:c('<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>'),italic:c('<line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/>'),underline:c('<path d="M6 4v6a6 6 0 0 0 12 0V4"/><line x1="4" y1="20" x2="20" y2="20"/>'),strikethrough:c('<path d="M16 4H9a3 3 0 0 0-2.83 4"/><path d="M14 12a4 4 0 0 1 0 8H6"/><line x1="4" y1="12" x2="20" y2="12"/>'),listUnordered:c('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>'),listOrdered:c('<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'),indent:c('<polyline points="3 8 7 12 3 16"/><line x1="21" y1="12" x2="11" y2="12"/><line x1="21" y1="6" x2="11" y2="6"/><line x1="21" y1="18" x2="11" y2="18"/>'),outdent:c('<polyline points="7 8 3 12 7 16"/><line x1="21" y1="12" x2="11" y2="12"/><line x1="21" y1="6" x2="11" y2="6"/><line x1="21" y1="18" x2="11" y2="18"/>'),textColor:c('<path d="M4 20h16"/><path d="m6 16 6-12 6 12"/><path d="M8 12h8"/>'),highlighter:c('<path d="m9 11-6 6v3h9l3-3"/><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"/>'),alignLeft:c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="15" y1="12" x2="3" y2="12"/><line x1="17" y1="18" x2="3" y2="18"/>'),alignCenter:c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="17" y1="12" x2="7" y2="12"/><line x1="19" y1="18" x2="5" y2="18"/>'),alignRight:c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="12" x2="9" y2="12"/><line x1="21" y1="18" x2="7" y2="18"/>'),alignJustify:c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="12" x2="3" y2="12"/><line x1="21" y1="18" x2="3" y2="18"/>'),ltr:c('<path d="M11 4h6"/><path d="M13 4v16"/><path d="M17 4v16"/><path d="M7 12 3 8l4-4"/>'),rtl:c('<path d="M10 4h6"/><path d="M12 4v16"/><path d="M16 4v16"/><path d="M6 8l4 4-4 4"/>'),link: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"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),unlink:c('<path d="m18.84 12.25 1.72-1.71a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="m5.17 11.75-1.71 1.71a5 5 0 0 0 7.07 7.07l1.71-1.71"/><line x1="2" y1="2" x2="22" y2="22"/>'),image:c('<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>'),video:c('<polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>'),table:c('<path d="M12 3v18"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/>'),tableRowAdd:c('<path d="M3 3h18v6H3z"/><path d="M3 15h18v6H3z"/><path d="M12 15v6"/><path d="M12 3v6"/>'),tableColAdd:c('<path d="M3 3v18h6V3z"/><path d="M15 3v18h6V3z"/><path d="M15 12h6"/><path d="M3 12h6"/>'),chevronDown:c('<path d="M6 9l6 6 6-6"/>'),minus:c('<line x1="5" y1="12" x2="19" y2="12"/>'),fileBreak:c('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M3 12h18"/>'),listTree:c('<path d="M21 12h-8"/><path d="M21 6H11"/><path d="M21 18h-8"/><path d="M3 6v4c0 1.1.9 2 2 2h3"/><path d="M3 10v6c0 1.1.9 2 2 2h3"/>'),imageUpload:c('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>'),atSign:c('<circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/>'),code:c('<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>'),braces:c('<path d="M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5a2 2 0 0 0 2 2h1"/><path d="M16 3h1a2 2 0 0 1 2 2v5a2 2 0 0 1 2 2 2 2 0 0 1-2 2v5a2 2 0 0 1-2 2h-1"/>'),calendar:c('<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>'),clock:c('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>'),smile:c('<circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/>')},k={name:"formatting",init(e){e.addToolbarButton(d.bold,"Bold",()=>{e.exec("bold")}),e.addToolbarButton(d.italic,"Italic",()=>{e.exec("italic")}),e.addToolbarButton(d.underline,"Underline",()=>{e.exec("underline")}),e.addToolbarButton(d.strikethrough,"Strikethrough",()=>{e.exec("strikeThrough")})}},R={name:"lists",init(e){e.addToolbarDivider(),e.addToolbarButton(d.listUnordered,"Unordered List",()=>{e.exec("insertUnorderedList")}),e.addToolbarButton(d.listOrdered,"Ordered List",()=>{e.exec("insertOrderedList")}),e.addToolbarButton(d.indent,"Indent",()=>{e.exec("indent")}),e.addToolbarButton(d.outdent,"Outdent",()=>{e.exec("outdent")})}},B={name:"color",init(e){e.addToolbarDivider();const t=(a,n,o)=>{const i=document.createElement("div");i.className="play-editor-color-picker",i.title=o;const s=document.createElement("input");s.type="color",s.value="#000000";const p=document.createElement("span");return p.innerHTML=n,i.appendChild(p),i.appendChild(s),s.addEventListener("input",h=>{const g=h.target;e.exec(a,g.value)}),e.toolbar.appendChild(i),i};t("foreColor",d.textColor,"Text Color"),t("hiliteColor",d.highlighter,"Background Color")}},H={name:"links",init(e){e.addToolbarDivider(),e.addToolbarButton(d.link,"Insert Link",()=>{const t=prompt("Enter the link URL:","https://");t&&(e.exec("createLink",t),e.editorArea.querySelectorAll("a").forEach(n=>{n.target||(n.target="_blank")}))}),e.addToolbarButton(d.unlink,"Unlink",()=>{e.exec("unlink")})}},N={name:"media",init(e){e.addToolbarDivider(),e.addToolbarButton(d.image,"Insert Image (URL)",()=>{const a=prompt("Enter image URL:","https://");a&&(e.exec("insertImage",a),e.editorArea.querySelectorAll("img").forEach(o=>{o.style.maxWidth||(o.style.maxWidth="100%",o.style.height="auto")}))});const t=document.createElement("input");t.type="file",t.accept="image/*",t.style.display="none",document.body.appendChild(t),e.addToolbarButton(d.imageUpload,"Upload Image",()=>{t.click()}),t.addEventListener("change",()=>{const a=t.files?.[0];if(!a)return;const n=new FileReader;n.onload=o=>{const i=o.target?.result;if(i){const s=document.createElement("img");s.src=i,s.style.maxWidth="100%",s.style.height="auto",s.style.borderRadius="6px",s.style.margin="0.5em 0",e.editorArea.focus();const p=window.getSelection();if(p&&p.rangeCount>0){const h=p.getRangeAt(0);h.deleteContents(),h.insertNode(s),h.setStartAfter(s),h.collapse(!0),p.removeAllRanges(),p.addRange(h)}else e.editorArea.appendChild(s);e.textArea.value=e.editorArea.innerHTML}},n.readAsDataURL(a),t.value=""}),e.addToolbarButton(d.video,"Insert Video/Media",()=>{const a=prompt("Enter iframe embed code:",'<iframe src="..."></iframe>');a&&e.exec("insertHTML",a)})}},S={name:"directionality",init(e){e.addToolbarDivider(),e.addToolbarButton(d.ltr,"Left to Right",()=>{const t=window.getSelection();if(!t||t.rangeCount===0)return;let n=t.getRangeAt(0).commonAncestorContainer;n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);const o=n;o&&o!==e.editorArea?o.setAttribute("dir","ltr"):e.editorArea.setAttribute("dir","ltr")}),e.addToolbarButton(d.rtl,"Right to Left",()=>{const t=window.getSelection();if(!t||t.rangeCount===0)return;let n=t.getRangeAt(0).commonAncestorContainer;n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);const o=n;o&&o!==e.editorArea?o.setAttribute("dir","rtl"):e.editorArea.setAttribute("dir","rtl")})}},D={name:"alignment",init(e){e.addToolbarDivider(),e.addToolbarButton(d.alignLeft,"Align Left",()=>{e.exec("justifyLeft")}),e.addToolbarButton(d.alignCenter,"Align Center",()=>{e.exec("justifyCenter")}),e.addToolbarButton(d.alignRight,"Align Right",()=>{e.exec("justifyRight")}),e.addToolbarButton(d.alignJustify,"Justify",()=>{e.exec("justifyFull")})}},P={name:"tables",init(e){e.addToolbarDivider(),e.addToolbarButton(d.table,"Insert 3x3 Table",()=>{let n='<table class="play-editor-table"><tbody>';for(let o=0;o<3;o++){n+="<tr>";for(let i=0;i<3;i++)n+="<td><br></td>";n+="</tr>"}n+="</tbody></table><p><br></p>",e.exec("insertHTML",n)}),e.addToolbarButton(d.tableRowAdd,"Add Row Below",()=>{const t=window.getSelection();if(!t||!t.rangeCount)return;let a=t.anchorNode;for(;a&&a!==e.editorArea&&a.nodeName!=="TR";)a=a.parentNode;if(a&&a.nodeName==="TR"){const n=a,o=n.children.length,i=document.createElement("tr");for(let s=0;s<o;s++){const p=document.createElement("td");p.innerHTML="<br>",i.appendChild(p)}n.parentNode?.insertBefore(i,n.nextSibling),e.textArea.value=e.editorArea.innerHTML}}),e.addToolbarButton(d.tableColAdd,"Add Col Right",()=>{const t=window.getSelection();if(!t||!t.rangeCount)return;let a=t.anchorNode;for(;a&&a!==e.editorArea&&a.nodeName!=="TD"&&a.nodeName!=="TH";)a=a.parentNode;if(a&&(a.nodeName==="TD"||a.nodeName==="TH")){const n=a,o=Array.from(n.parentNode.children).indexOf(n),i=n.closest("table");i&&(i.querySelectorAll("tr").forEach(p=>{const h=document.createElement(a.nodeName);h.innerHTML="<br>";const g=p.children[o];p.insertBefore(h,g.nextSibling)}),e.textArea.value=e.editorArea.innerHTML)}})}},I={name:"accordion",init(e){e.addToolbarButton(d.chevronDown,"Insert Accordion",()=>{e.exec("insertHTML",`
2
+ <details class="play-editor-accordion" open>
3
+ <summary class="play-editor-accordion-title">Click to expand</summary>
4
+ <div class="play-editor-accordion-content">
5
+ <p>Accordion content goes here...</p>
6
+ </div>
7
+ </details>
8
+ <p><br></p>
9
+ `)})}},O={name:"page-break",init(e){e.addToolbarDivider(),e.addToolbarButton(d.minus,"Horizontal Rule",()=>{e.exec("insertHorizontalRule")}),e.addToolbarButton(d.fileBreak,"Page Break",()=>{e.exec("insertHTML",`
10
+ <div class="play-editor-page-break" contenteditable="false">
11
+ <span>Page Break</span>
12
+ </div>
13
+ <p><br></p>
14
+ `)})}},U={name:"toc",init(e){e.addToolbarButton(d.listTree,"Insert Table of Contents",()=>{const t=e.editorArea.querySelectorAll("h1, h2, h3, h4, h5, h6");if(t.length===0){alert("No headings found to generate a Table of Contents.");return}let a='<div class="play-editor-toc" contenteditable="false"><h3>Table of Contents</h3><ul>';t.forEach((n,o)=>{const i=`toc-heading-${o}`;n.id=i;const p=(parseInt(n.tagName.charAt(1))-1)*20;a+=`<li style="margin-left: ${p}px"><a href="#${i}">${n.textContent}</a></li>`}),a+="</ul></div><p><br></p>",e.exec("insertHTML",a)})}},j={name:"paste-image",init(e){e.editorArea.addEventListener("paste",t=>{const a=t.clipboardData?.items;if(a)for(let n=0;n<a.length;n++){const o=a[n];if(o.type.startsWith("image/")){t.preventDefault();const i=o.getAsFile();if(!i)return;const s=new FileReader;s.onload=p=>{const h=p.target?.result;if(h){const g=document.createElement("img");g.src=h,g.style.maxWidth="100%",g.style.height="auto",g.style.borderRadius="6px",g.style.margin="0.5em 0";const l=window.getSelection();if(l&&l.rangeCount>0){const r=l.getRangeAt(0);r.deleteContents(),r.insertNode(g),r.setStartAfter(g),r.collapse(!0),l.removeAllRanges(),l.addRange(r)}else e.editorArea.appendChild(g);e.textArea.value=e.editorArea.innerHTML}},s.readAsDataURL(i);break}}})}},$=[];function L(e={}){const t=e.trigger||"@",a=e.users||$;return{name:"mentions",init(n){n.addToolbarDivider(),n.addToolbarButton(d.atSign,"Mention (@)",()=>{n.editorArea.focus(),n.exec("insertHTML",t),g()});let o=null;function i(){o&&(o.remove(),o=null)}function s(){const l=window.getSelection();if(!l||l.rangeCount===0)return null;const r=l.getRangeAt(0).cloneRange();r.collapse(!1);const m=document.createElement("span");m.textContent="โ€‹",r.insertNode(m);const u=m.getBoundingClientRect(),x=n.editorArea.getBoundingClientRect(),y={top:u.bottom-x.top+n.editorArea.scrollTop,left:u.left-x.left};return m.remove(),l.getRangeAt(0).commonAncestorContainer.parentElement?.normalize(),y}function p(l){const r=window.getSelection();if(!r||r.rangeCount===0)return;const m=r.getRangeAt(0),u=m.startContainer;if(u.nodeType===Node.TEXT_NODE){const x=u.textContent||"",y=m.startOffset,v=x.lastIndexOf(t,y);if(v!==-1){const f=x.substring(0,v),w=x.substring(y);u.textContent=f;const b=document.createElement("span");b.className="play-editor-mention",b.contentEditable="false",b.dataset.userId=l.id,b.textContent=`@${l.name}`;const A=document.createTextNode("ย "+w),M=u.parentNode;M.insertBefore(b,u.nextSibling),M.insertBefore(A,b.nextSibling);const T=document.createRange();T.setStart(A,1),T.collapse(!0),r.removeAllRanges(),r.addRange(T)}}i(),n.textArea.value=n.editorArea.innerHTML}async function h(l){i();let r;if(typeof a=="function"){const u=a(l);r=u instanceof Promise?await u:u}else r=a.filter(u=>u.name.toLowerCase().includes(l.toLowerCase()));if(r.length===0)return;const m=s();m&&(o=document.createElement("div"),o.className="play-editor-mention-dropdown",o.style.top=`${m.top+4}px`,o.style.left=`${m.left}px`,r.slice(0,8).forEach((u,x)=>{const y=document.createElement("div");y.className="play-editor-mention-item",x===0&&y.classList.add("active");const v=u.name.split(" ").map(f=>f[0]).join("").toUpperCase();y.innerHTML=`
15
+ <div class="play-editor-mention-avatar">${u.avatar?`<img src="${u.avatar}" />`:v}</div>
16
+ <span class="play-editor-mention-name">${u.name}</span>
17
+ `,y.addEventListener("mousedown",f=>{f.preventDefault(),p(u)}),o.appendChild(y)}),n.editorArea.style.position="relative",n.editorArea.appendChild(o))}async function g(){const l=window.getSelection();if(!l||l.rangeCount===0){i();return}const r=l.getRangeAt(0),m=r.startContainer;if(m.nodeType!==Node.TEXT_NODE){i();return}const u=m.textContent||"",x=r.startOffset,y=u.substring(0,x),v=y.lastIndexOf(t);if(v===-1){i();return}const f=y.substring(v+t.length);if(f.includes(" ")&&f.trim().length>0){i();return}await h(f)}n.editorArea.addEventListener("input",g),n.editorArea.addEventListener("keydown",l=>{if(!o)return;const r=o.querySelectorAll(".play-editor-mention-item"),m=o.querySelector(".play-editor-mention-item.active");let u=Array.from(r).indexOf(m);l.key==="ArrowDown"?(l.preventDefault(),m?.classList.remove("active"),u=(u+1)%r.length,r[u].classList.add("active")):l.key==="ArrowUp"?(l.preventDefault(),m?.classList.remove("active"),u=(u-1+r.length)%r.length,r[u].classList.add("active")):l.key==="Enter"||l.key==="Tab"?m&&(l.preventDefault(),m.dispatchEvent(new MouseEvent("mousedown"))):l.key==="Escape"&&(l.preventDefault(),i())}),document.addEventListener("click",l=>{o&&!o.contains(l.target)&&i()})}}}const z=L(),q={name:"code-block",init(e){e.addToolbarDivider(),e.addToolbarButton(d.code,"Inline Code",()=>{const n=window.getSelection();if(!n||n.rangeCount===0)return;const o=n.getRangeAt(0),i=o.toString();if(i){const s=document.createElement("code");s.className="play-editor-inline-code",s.textContent=i,o.deleteContents(),o.insertNode(s),o.setStartAfter(s),o.collapse(!0),n.removeAllRanges(),n.addRange(o)}else{const s=document.createElement("code");s.className="play-editor-inline-code",s.innerHTML="&nbsp;",o.insertNode(s),o.selectNodeContents(s),n.removeAllRanges(),n.addRange(o)}e.textArea.value=e.editorArea.innerHTML}),e.addToolbarButton(d.braces,"Code Block",()=>{const o=window.getSelection()?.toString()||"// your code here",i=`
18
+ <pre class="play-editor-code-block"><code>${F(o)}</code></pre>
19
+ <p><br></p>
20
+ `;e.exec("insertHTML",i)});let t=!1;const a=e.addToolbarButton(d.code,"Toggle HTML Source",()=>{if(t=!t,t){const n=e.editorArea.innerHTML;e.editorArea.innerText=n,e.editorArea.classList.add("play-editor-source-view"),a.classList.add("play-editor-btn-active")}else{const n=e.editorArea.innerText;e.editorArea.innerHTML=n,e.editorArea.classList.remove("play-editor-source-view"),a.classList.remove("play-editor-btn-active"),e.textArea.value=e.editorArea.innerHTML}})}};function F(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}const _={name:"datetime",init(e){e.addToolbarDivider(),e.addToolbarButton(d.calendar,"Insert Date",()=>{const t=new Date,a=t.toLocaleDateString(void 0,{year:"numeric",month:"long",day:"numeric"});e.exec("insertHTML",`<time class="play-editor-datetime" datetime="${t.toISOString()}">${a}</time>`)}),e.addToolbarButton(d.clock,"Insert Time",()=>{const t=new Date,a=t.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"});e.exec("insertHTML",`<time class="play-editor-datetime" datetime="${t.toISOString()}">${a}</time>`)})}},C={Smileys:["๐Ÿ˜€","๐Ÿ˜ƒ","๐Ÿ˜„","๐Ÿ˜","๐Ÿ˜†","๐Ÿ˜…","๐Ÿคฃ","๐Ÿ˜‚","๐Ÿ™‚","๐Ÿ˜‰","๐Ÿ˜Š","๐Ÿ˜‡","๐Ÿฅฐ","๐Ÿ˜","๐Ÿคฉ","๐Ÿ˜˜","๐Ÿ˜—","๐Ÿ˜š","๐Ÿ˜‹","๐Ÿ˜›","๐Ÿ˜œ","๐Ÿคช","๐Ÿ˜","๐Ÿค‘","๐Ÿค—","๐Ÿคญ","๐Ÿคซ","๐Ÿค”","๐Ÿซก"],Gestures:["๐Ÿ‘","๐Ÿ‘Ž","๐Ÿ‘","๐Ÿ™Œ","๐Ÿค","๐Ÿ™","โœŒ๏ธ","๐Ÿคž","๐ŸคŸ","๐Ÿค˜","๐Ÿ‘Œ","๐ŸคŒ","๐Ÿ‘‹","โœ‹","๐Ÿ–๏ธ","๐Ÿ––","๐Ÿ’ช","๐Ÿฆพ"],Hearts:["โค๏ธ","๐Ÿงก","๐Ÿ’›","๐Ÿ’š","๐Ÿ’™","๐Ÿ’œ","๐Ÿ–ค","๐Ÿค","๐ŸคŽ","๐Ÿ’”","โฃ๏ธ","๐Ÿ’•","๐Ÿ’ž","๐Ÿ’“","๐Ÿ’—","๐Ÿ’–","๐Ÿ’˜","๐Ÿ’"],Objects:["๐ŸŽ‰","๐ŸŽŠ","๐ŸŽˆ","๐ŸŽ","๐Ÿ†","๐Ÿฅ‡","โญ","๐ŸŒŸ","๐Ÿ’ก","๐Ÿ”ฅ","โœจ","๐Ÿ’ฏ","โœ…","โŒ","โš ๏ธ","๐Ÿ“Œ","๐Ÿ“Ž","๐Ÿ”—","๐Ÿ“","๐Ÿ“…","๐Ÿ“Š","๐Ÿ“ˆ"],Arrows:["โžก๏ธ","โฌ…๏ธ","โฌ†๏ธ","โฌ‡๏ธ","โ†—๏ธ","โ†˜๏ธ","โ†™๏ธ","โ†–๏ธ","โ†”๏ธ","โ†•๏ธ","๐Ÿ”„","๐Ÿ”ƒ"]},V={name:"emoji",init(e){let t=null;e.addToolbarButton(d.smile,"Insert Emoji",()=>{if(t){t.remove(),t=null;return}const a=window.getSelection();let n=null;a&&a.rangeCount>0&&(n=a.getRangeAt(0).cloneRange()),t=document.createElement("div"),t.className="play-editor-emoji-picker";const o=document.createElement("div");o.className="play-editor-emoji-tabs";const i=document.createElement("div");i.className="play-editor-emoji-grid";const s=Object.keys(C);function p(g){i.innerHTML="",C[g].forEach(l=>{const r=document.createElement("button");r.type="button",r.className="play-editor-emoji-item",r.textContent=l,r.addEventListener("mousedown",m=>{m.preventDefault(),e.editorArea.focus(),n&&(a?.removeAllRanges(),a?.addRange(n)),e.exec("insertHTML",l),a&&a.rangeCount>0&&(n=a.getRangeAt(0).cloneRange())}),i.appendChild(r)})}s.forEach((g,l)=>{const r=document.createElement("button");r.type="button",r.className="play-editor-emoji-tab",l===0&&r.classList.add("active"),r.textContent=g,r.addEventListener("mousedown",m=>{m.preventDefault(),o.querySelectorAll(".play-editor-emoji-tab").forEach(u=>u.classList.remove("active")),r.classList.add("active"),p(g)}),o.appendChild(r)}),t.appendChild(o),t.appendChild(i),e.container.style.position="relative",e.container.appendChild(t),p(s[0]);const h=g=>{t&&!t.contains(g.target)&&(t.remove(),t=null,document.removeEventListener("mousedown",h))};setTimeout(()=>document.addEventListener("mousedown",h),0)})}};exports.AccordionPlugin=I;exports.AlignmentPlugin=D;exports.CodeBlockPlugin=q;exports.ColorPlugin=B;exports.DateTimePlugin=_;exports.DirectionalityPlugin=S;exports.Editor=E;exports.EmojiPlugin=V;exports.FormattingPlugin=k;exports.LinksPlugin=H;exports.ListsPlugin=R;exports.MediaPlugin=N;exports.MentionsPlugin=z;exports.PageBreakPlugin=O;exports.PasteImagePlugin=j;exports.TablesPlugin=P;exports.TocPlugin=U;exports.createMentionsPlugin=L;
@@ -0,0 +1,69 @@
1
+ export declare const AccordionPlugin: Plugin_2;
2
+
3
+ export declare const AlignmentPlugin: Plugin_2;
4
+
5
+ export declare const CodeBlockPlugin: Plugin_2;
6
+
7
+ export declare const ColorPlugin: Plugin_2;
8
+
9
+ export declare function createMentionsPlugin(options?: MentionsPluginOptions): Plugin_2;
10
+
11
+ export declare const DateTimePlugin: Plugin_2;
12
+
13
+ export declare const DirectionalityPlugin: Plugin_2;
14
+
15
+ export declare class Editor {
16
+ container: HTMLElement;
17
+ textArea: HTMLTextAreaElement;
18
+ editorArea: HTMLDivElement;
19
+ toolbar: HTMLDivElement;
20
+ private plugins;
21
+ constructor(selector: string | HTMLTextAreaElement, plugins?: Plugin_2[]);
22
+ private init;
23
+ exec(command: string, value?: string | undefined): void;
24
+ addToolbarButton(iconHtml: string, tooltip: string, onClick: () => void): HTMLButtonElement;
25
+ addToolbarDivider(): void;
26
+ getContent(): string;
27
+ setContent(html: string): void;
28
+ }
29
+
30
+ export declare const EmojiPlugin: Plugin_2;
31
+
32
+ export declare const FormattingPlugin: Plugin_2;
33
+
34
+ export declare const LinksPlugin: Plugin_2;
35
+
36
+ export declare const ListsPlugin: Plugin_2;
37
+
38
+ export declare const MediaPlugin: Plugin_2;
39
+
40
+ export declare const MentionsPlugin: Plugin_2;
41
+
42
+ export declare interface MentionsPluginOptions {
43
+ /** Provide a list of users or a function that returns users filtered by query */
44
+ users?: MentionUser[] | ((query: string) => MentionUser[] | Promise<MentionUser[]>);
45
+ /** Character that triggers the mention dropdown (default: '@') */
46
+ trigger?: string;
47
+ }
48
+
49
+ declare interface MentionUser {
50
+ id: string;
51
+ name: string;
52
+ avatar?: string;
53
+ }
54
+
55
+ export declare const PageBreakPlugin: Plugin_2;
56
+
57
+ export declare const PasteImagePlugin: Plugin_2;
58
+
59
+ declare interface Plugin_2 {
60
+ name: string;
61
+ init(editor: Editor): void;
62
+ }
63
+ export { Plugin_2 as Plugin }
64
+
65
+ export declare const TablesPlugin: Plugin_2;
66
+
67
+ export declare const TocPlugin: Plugin_2;
68
+
69
+ export { }
package/dist/index.mjs ADDED
@@ -0,0 +1,561 @@
1
+ class k {
2
+ container;
3
+ textArea;
4
+ editorArea;
5
+ toolbar;
6
+ plugins = /* @__PURE__ */ new Map();
7
+ constructor(t, a = []) {
8
+ const n = typeof t == "string" ? document.querySelector(t) : t;
9
+ if (!n || n.tagName !== "TEXTAREA")
10
+ throw new Error(
11
+ "Editor must be initialized on a <textarea> element."
12
+ );
13
+ this.textArea = n, this.container = document.createElement("div"), this.container.className = "play-editor-container", this.toolbar = document.createElement("div"), this.toolbar.className = "play-editor-toolbar", this.editorArea = document.createElement("div"), this.editorArea.className = "play-editor-content", this.editorArea.contentEditable = "true", this.editorArea.innerHTML = this.textArea.value, this.init(a);
14
+ }
15
+ init(t) {
16
+ this.textArea.style.display = "none", this.textArea.parentNode?.insertBefore(this.container, this.textArea), this.container.appendChild(this.toolbar), this.container.appendChild(this.editorArea), this.editorArea.addEventListener("input", () => {
17
+ this.textArea.value = this.editorArea.innerHTML;
18
+ }), this.editorArea.addEventListener("keydown", (a) => {
19
+ a.key === "Tab" && (a.preventDefault(), document.execCommand("insertHTML", !1, "&emsp;"), this.textArea.value = this.editorArea.innerHTML);
20
+ }), t.forEach((a) => {
21
+ this.plugins.set(a.name, a), a.init(this);
22
+ });
23
+ }
24
+ exec(t, a = void 0) {
25
+ this.editorArea.focus(), document.execCommand(t, !1, a), this.textArea.value = this.editorArea.innerHTML;
26
+ }
27
+ addToolbarButton(t, a, n) {
28
+ const o = document.createElement("button");
29
+ return o.type = "button", o.className = "play-editor-btn", o.title = a, o.innerHTML = t, o.addEventListener("click", (i) => {
30
+ i.preventDefault(), n();
31
+ }), this.toolbar.appendChild(o), o;
32
+ }
33
+ addToolbarDivider() {
34
+ const t = document.createElement("div");
35
+ t.className = "play-editor-divider", this.toolbar.appendChild(t);
36
+ }
37
+ getContent() {
38
+ return this.editorArea.innerHTML;
39
+ }
40
+ setContent(t) {
41
+ this.editorArea.innerHTML = t, this.textArea.value = t;
42
+ }
43
+ }
44
+ const c = (e) => `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${e}</svg>`, d = {
45
+ // Formatting
46
+ bold: c('<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>'),
47
+ italic: c('<line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/>'),
48
+ underline: c('<path d="M6 4v6a6 6 0 0 0 12 0V4"/><line x1="4" y1="20" x2="20" y2="20"/>'),
49
+ strikethrough: c('<path d="M16 4H9a3 3 0 0 0-2.83 4"/><path d="M14 12a4 4 0 0 1 0 8H6"/><line x1="4" y1="12" x2="20" y2="12"/>'),
50
+ // Lists
51
+ listUnordered: c('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>'),
52
+ listOrdered: c('<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'),
53
+ indent: c('<polyline points="3 8 7 12 3 16"/><line x1="21" y1="12" x2="11" y2="12"/><line x1="21" y1="6" x2="11" y2="6"/><line x1="21" y1="18" x2="11" y2="18"/>'),
54
+ outdent: c('<polyline points="7 8 3 12 7 16"/><line x1="21" y1="12" x2="11" y2="12"/><line x1="21" y1="6" x2="11" y2="6"/><line x1="21" y1="18" x2="11" y2="18"/>'),
55
+ // Color
56
+ textColor: c('<path d="M4 20h16"/><path d="m6 16 6-12 6 12"/><path d="M8 12h8"/>'),
57
+ highlighter: c('<path d="m9 11-6 6v3h9l3-3"/><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"/>'),
58
+ // Alignment
59
+ alignLeft: c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="15" y1="12" x2="3" y2="12"/><line x1="17" y1="18" x2="3" y2="18"/>'),
60
+ alignCenter: c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="17" y1="12" x2="7" y2="12"/><line x1="19" y1="18" x2="5" y2="18"/>'),
61
+ alignRight: c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="12" x2="9" y2="12"/><line x1="21" y1="18" x2="7" y2="18"/>'),
62
+ alignJustify: c('<line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="12" x2="3" y2="12"/><line x1="21" y1="18" x2="3" y2="18"/>'),
63
+ // Directionality
64
+ ltr: c('<path d="M11 4h6"/><path d="M13 4v16"/><path d="M17 4v16"/><path d="M7 12 3 8l4-4"/>'),
65
+ rtl: c('<path d="M10 4h6"/><path d="M12 4v16"/><path d="M16 4v16"/><path d="M6 8l4 4-4 4"/>'),
66
+ // Links
67
+ link: 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"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),
68
+ unlink: c('<path d="m18.84 12.25 1.72-1.71a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="m5.17 11.75-1.71 1.71a5 5 0 0 0 7.07 7.07l1.71-1.71"/><line x1="2" y1="2" x2="22" y2="22"/>'),
69
+ // Media
70
+ image: c('<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>'),
71
+ video: c('<polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>'),
72
+ // Tables
73
+ table: c('<path d="M12 3v18"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/>'),
74
+ tableRowAdd: c('<path d="M3 3h18v6H3z"/><path d="M3 15h18v6H3z"/><path d="M12 15v6"/><path d="M12 3v6"/>'),
75
+ tableColAdd: c('<path d="M3 3v18h6V3z"/><path d="M15 3v18h6V3z"/><path d="M15 12h6"/><path d="M3 12h6"/>'),
76
+ // Accordion
77
+ chevronDown: c('<path d="M6 9l6 6 6-6"/>'),
78
+ // Page Break / HR
79
+ minus: c('<line x1="5" y1="12" x2="19" y2="12"/>'),
80
+ fileBreak: c('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M3 12h18"/>'),
81
+ // TOC
82
+ listTree: c('<path d="M21 12h-8"/><path d="M21 6H11"/><path d="M21 18h-8"/><path d="M3 6v4c0 1.1.9 2 2 2h3"/><path d="M3 10v6c0 1.1.9 2 2 2h3"/>'),
83
+ // Upload / File select
84
+ imageUpload: c('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>'),
85
+ // Mentions
86
+ atSign: c('<circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/>'),
87
+ // Code
88
+ code: c('<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>'),
89
+ braces: c('<path d="M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5a2 2 0 0 0 2 2h1"/><path d="M16 3h1a2 2 0 0 1 2 2v5a2 2 0 0 1 2 2 2 2 0 0 1-2 2v5a2 2 0 0 1-2 2h-1"/>'),
90
+ // Date/Time
91
+ calendar: c('<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>'),
92
+ clock: c('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>'),
93
+ // Emoji
94
+ smile: c('<circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/>')
95
+ }, B = {
96
+ name: "formatting",
97
+ init(e) {
98
+ e.addToolbarButton(d.bold, "Bold", () => {
99
+ e.exec("bold");
100
+ }), e.addToolbarButton(d.italic, "Italic", () => {
101
+ e.exec("italic");
102
+ }), e.addToolbarButton(d.underline, "Underline", () => {
103
+ e.exec("underline");
104
+ }), e.addToolbarButton(d.strikethrough, "Strikethrough", () => {
105
+ e.exec("strikeThrough");
106
+ });
107
+ }
108
+ }, H = {
109
+ name: "lists",
110
+ init(e) {
111
+ e.addToolbarDivider(), e.addToolbarButton(d.listUnordered, "Unordered List", () => {
112
+ e.exec("insertUnorderedList");
113
+ }), e.addToolbarButton(d.listOrdered, "Ordered List", () => {
114
+ e.exec("insertOrderedList");
115
+ }), e.addToolbarButton(d.indent, "Indent", () => {
116
+ e.exec("indent");
117
+ }), e.addToolbarButton(d.outdent, "Outdent", () => {
118
+ e.exec("outdent");
119
+ });
120
+ }
121
+ }, N = {
122
+ name: "color",
123
+ init(e) {
124
+ e.addToolbarDivider();
125
+ const t = (a, n, o) => {
126
+ const i = document.createElement("div");
127
+ i.className = "play-editor-color-picker", i.title = o;
128
+ const s = document.createElement("input");
129
+ s.type = "color", s.value = "#000000";
130
+ const p = document.createElement("span");
131
+ return p.innerHTML = n, i.appendChild(p), i.appendChild(s), s.addEventListener("input", (g) => {
132
+ const h = g.target;
133
+ e.exec(a, h.value);
134
+ }), e.toolbar.appendChild(i), i;
135
+ };
136
+ t("foreColor", d.textColor, "Text Color"), t("hiliteColor", d.highlighter, "Background Color");
137
+ }
138
+ }, S = {
139
+ name: "links",
140
+ init(e) {
141
+ e.addToolbarDivider(), e.addToolbarButton(d.link, "Insert Link", () => {
142
+ const t = prompt("Enter the link URL:", "https://");
143
+ t && (e.exec("createLink", t), e.editorArea.querySelectorAll("a").forEach((n) => {
144
+ n.target || (n.target = "_blank");
145
+ }));
146
+ }), e.addToolbarButton(d.unlink, "Unlink", () => {
147
+ e.exec("unlink");
148
+ });
149
+ }
150
+ }, D = {
151
+ name: "media",
152
+ init(e) {
153
+ e.addToolbarDivider(), e.addToolbarButton(d.image, "Insert Image (URL)", () => {
154
+ const a = prompt("Enter image URL:", "https://");
155
+ a && (e.exec("insertImage", a), e.editorArea.querySelectorAll("img").forEach((o) => {
156
+ o.style.maxWidth || (o.style.maxWidth = "100%", o.style.height = "auto");
157
+ }));
158
+ });
159
+ const t = document.createElement("input");
160
+ t.type = "file", t.accept = "image/*", t.style.display = "none", document.body.appendChild(t), e.addToolbarButton(d.imageUpload, "Upload Image", () => {
161
+ t.click();
162
+ }), t.addEventListener("change", () => {
163
+ const a = t.files?.[0];
164
+ if (!a) return;
165
+ const n = new FileReader();
166
+ n.onload = (o) => {
167
+ const i = o.target?.result;
168
+ if (i) {
169
+ const s = document.createElement("img");
170
+ s.src = i, s.style.maxWidth = "100%", s.style.height = "auto", s.style.borderRadius = "6px", s.style.margin = "0.5em 0", e.editorArea.focus();
171
+ const p = window.getSelection();
172
+ if (p && p.rangeCount > 0) {
173
+ const g = p.getRangeAt(0);
174
+ g.deleteContents(), g.insertNode(s), g.setStartAfter(s), g.collapse(!0), p.removeAllRanges(), p.addRange(g);
175
+ } else
176
+ e.editorArea.appendChild(s);
177
+ e.textArea.value = e.editorArea.innerHTML;
178
+ }
179
+ }, n.readAsDataURL(a), t.value = "";
180
+ }), e.addToolbarButton(d.video, "Insert Video/Media", () => {
181
+ const a = prompt("Enter iframe embed code:", '<iframe src="..."></iframe>');
182
+ a && e.exec("insertHTML", a);
183
+ });
184
+ }
185
+ }, I = {
186
+ name: "directionality",
187
+ init(e) {
188
+ e.addToolbarDivider(), e.addToolbarButton(d.ltr, "Left to Right", () => {
189
+ const t = window.getSelection();
190
+ if (!t || t.rangeCount === 0) return;
191
+ let n = t.getRangeAt(0).commonAncestorContainer;
192
+ n.nodeType === Node.TEXT_NODE && (n = n.parentNode);
193
+ const o = n;
194
+ o && o !== e.editorArea ? o.setAttribute("dir", "ltr") : e.editorArea.setAttribute("dir", "ltr");
195
+ }), e.addToolbarButton(d.rtl, "Right to Left", () => {
196
+ const t = window.getSelection();
197
+ if (!t || t.rangeCount === 0) return;
198
+ let n = t.getRangeAt(0).commonAncestorContainer;
199
+ n.nodeType === Node.TEXT_NODE && (n = n.parentNode);
200
+ const o = n;
201
+ o && o !== e.editorArea ? o.setAttribute("dir", "rtl") : e.editorArea.setAttribute("dir", "rtl");
202
+ });
203
+ }
204
+ }, P = {
205
+ name: "alignment",
206
+ init(e) {
207
+ e.addToolbarDivider(), e.addToolbarButton(d.alignLeft, "Align Left", () => {
208
+ e.exec("justifyLeft");
209
+ }), e.addToolbarButton(d.alignCenter, "Align Center", () => {
210
+ e.exec("justifyCenter");
211
+ }), e.addToolbarButton(d.alignRight, "Align Right", () => {
212
+ e.exec("justifyRight");
213
+ }), e.addToolbarButton(d.alignJustify, "Justify", () => {
214
+ e.exec("justifyFull");
215
+ });
216
+ }
217
+ }, O = {
218
+ name: "tables",
219
+ init(e) {
220
+ e.addToolbarDivider(), e.addToolbarButton(d.table, "Insert 3x3 Table", () => {
221
+ let n = '<table class="play-editor-table"><tbody>';
222
+ for (let o = 0; o < 3; o++) {
223
+ n += "<tr>";
224
+ for (let i = 0; i < 3; i++)
225
+ n += "<td><br></td>";
226
+ n += "</tr>";
227
+ }
228
+ n += "</tbody></table><p><br></p>", e.exec("insertHTML", n);
229
+ }), e.addToolbarButton(d.tableRowAdd, "Add Row Below", () => {
230
+ const t = window.getSelection();
231
+ if (!t || !t.rangeCount) return;
232
+ let a = t.anchorNode;
233
+ for (; a && a !== e.editorArea && a.nodeName !== "TR"; )
234
+ a = a.parentNode;
235
+ if (a && a.nodeName === "TR") {
236
+ const n = a, o = n.children.length, i = document.createElement("tr");
237
+ for (let s = 0; s < o; s++) {
238
+ const p = document.createElement("td");
239
+ p.innerHTML = "<br>", i.appendChild(p);
240
+ }
241
+ n.parentNode?.insertBefore(i, n.nextSibling), e.textArea.value = e.editorArea.innerHTML;
242
+ }
243
+ }), e.addToolbarButton(d.tableColAdd, "Add Col Right", () => {
244
+ const t = window.getSelection();
245
+ if (!t || !t.rangeCount) return;
246
+ let a = t.anchorNode;
247
+ for (; a && a !== e.editorArea && a.nodeName !== "TD" && a.nodeName !== "TH"; )
248
+ a = a.parentNode;
249
+ if (a && (a.nodeName === "TD" || a.nodeName === "TH")) {
250
+ const n = a, o = Array.from(n.parentNode.children).indexOf(n), i = n.closest("table");
251
+ i && (i.querySelectorAll("tr").forEach((p) => {
252
+ const g = document.createElement(a.nodeName);
253
+ g.innerHTML = "<br>";
254
+ const h = p.children[o];
255
+ p.insertBefore(g, h.nextSibling);
256
+ }), e.textArea.value = e.editorArea.innerHTML);
257
+ }
258
+ });
259
+ }
260
+ }, U = {
261
+ name: "accordion",
262
+ init(e) {
263
+ e.addToolbarButton(d.chevronDown, "Insert Accordion", () => {
264
+ e.exec("insertHTML", `
265
+ <details class="play-editor-accordion" open>
266
+ <summary class="play-editor-accordion-title">Click to expand</summary>
267
+ <div class="play-editor-accordion-content">
268
+ <p>Accordion content goes here...</p>
269
+ </div>
270
+ </details>
271
+ <p><br></p>
272
+ `);
273
+ });
274
+ }
275
+ }, j = {
276
+ name: "page-break",
277
+ init(e) {
278
+ e.addToolbarDivider(), e.addToolbarButton(d.minus, "Horizontal Rule", () => {
279
+ e.exec("insertHorizontalRule");
280
+ }), e.addToolbarButton(d.fileBreak, "Page Break", () => {
281
+ e.exec("insertHTML", `
282
+ <div class="play-editor-page-break" contenteditable="false">
283
+ <span>Page Break</span>
284
+ </div>
285
+ <p><br></p>
286
+ `);
287
+ });
288
+ }
289
+ }, $ = {
290
+ name: "toc",
291
+ init(e) {
292
+ e.addToolbarButton(d.listTree, "Insert Table of Contents", () => {
293
+ const t = e.editorArea.querySelectorAll("h1, h2, h3, h4, h5, h6");
294
+ if (t.length === 0) {
295
+ alert("No headings found to generate a Table of Contents.");
296
+ return;
297
+ }
298
+ let a = '<div class="play-editor-toc" contenteditable="false"><h3>Table of Contents</h3><ul>';
299
+ t.forEach((n, o) => {
300
+ const i = `toc-heading-${o}`;
301
+ n.id = i;
302
+ const p = (parseInt(n.tagName.charAt(1)) - 1) * 20;
303
+ a += `<li style="margin-left: ${p}px"><a href="#${i}">${n.textContent}</a></li>`;
304
+ }), a += "</ul></div><p><br></p>", e.exec("insertHTML", a);
305
+ });
306
+ }
307
+ }, z = {
308
+ name: "paste-image",
309
+ init(e) {
310
+ e.editorArea.addEventListener("paste", (t) => {
311
+ const a = t.clipboardData?.items;
312
+ if (a)
313
+ for (let n = 0; n < a.length; n++) {
314
+ const o = a[n];
315
+ if (o.type.startsWith("image/")) {
316
+ t.preventDefault();
317
+ const i = o.getAsFile();
318
+ if (!i) return;
319
+ const s = new FileReader();
320
+ s.onload = (p) => {
321
+ const g = p.target?.result;
322
+ if (g) {
323
+ const h = document.createElement("img");
324
+ h.src = g, h.style.maxWidth = "100%", h.style.height = "auto", h.style.borderRadius = "6px", h.style.margin = "0.5em 0";
325
+ const l = window.getSelection();
326
+ if (l && l.rangeCount > 0) {
327
+ const r = l.getRangeAt(0);
328
+ r.deleteContents(), r.insertNode(h), r.setStartAfter(h), r.collapse(!0), l.removeAllRanges(), l.addRange(r);
329
+ } else
330
+ e.editorArea.appendChild(h);
331
+ e.textArea.value = e.editorArea.innerHTML;
332
+ }
333
+ }, s.readAsDataURL(i);
334
+ break;
335
+ }
336
+ }
337
+ });
338
+ }
339
+ }, w = [];
340
+ function E(e = {}) {
341
+ const t = e.trigger || "@", a = e.users || w;
342
+ return {
343
+ name: "mentions",
344
+ init(n) {
345
+ n.addToolbarDivider(), n.addToolbarButton(d.atSign, "Mention (@)", () => {
346
+ n.editorArea.focus(), n.exec("insertHTML", t), h();
347
+ });
348
+ let o = null;
349
+ function i() {
350
+ o && (o.remove(), o = null);
351
+ }
352
+ function s() {
353
+ const l = window.getSelection();
354
+ if (!l || l.rangeCount === 0) return null;
355
+ const r = l.getRangeAt(0).cloneRange();
356
+ r.collapse(!1);
357
+ const m = document.createElement("span");
358
+ m.textContent = "โ€‹", r.insertNode(m);
359
+ const u = m.getBoundingClientRect(), x = n.editorArea.getBoundingClientRect(), y = {
360
+ top: u.bottom - x.top + n.editorArea.scrollTop,
361
+ left: u.left - x.left
362
+ };
363
+ return m.remove(), l.getRangeAt(0).commonAncestorContainer.parentElement?.normalize(), y;
364
+ }
365
+ function p(l) {
366
+ const r = window.getSelection();
367
+ if (!r || r.rangeCount === 0) return;
368
+ const m = r.getRangeAt(0), u = m.startContainer;
369
+ if (u.nodeType === Node.TEXT_NODE) {
370
+ const x = u.textContent || "", y = m.startOffset, v = x.lastIndexOf(t, y);
371
+ if (v !== -1) {
372
+ const f = x.substring(0, v), L = x.substring(y);
373
+ u.textContent = f;
374
+ const b = document.createElement("span");
375
+ b.className = "play-editor-mention", b.contentEditable = "false", b.dataset.userId = l.id, b.textContent = `@${l.name}`;
376
+ const A = document.createTextNode("ย " + L), M = u.parentNode;
377
+ M.insertBefore(b, u.nextSibling), M.insertBefore(A, b.nextSibling);
378
+ const T = document.createRange();
379
+ T.setStart(A, 1), T.collapse(!0), r.removeAllRanges(), r.addRange(T);
380
+ }
381
+ }
382
+ i(), n.textArea.value = n.editorArea.innerHTML;
383
+ }
384
+ async function g(l) {
385
+ i();
386
+ let r;
387
+ if (typeof a == "function") {
388
+ const u = a(l);
389
+ r = u instanceof Promise ? await u : u;
390
+ } else
391
+ r = a.filter(
392
+ (u) => u.name.toLowerCase().includes(l.toLowerCase())
393
+ );
394
+ if (r.length === 0) return;
395
+ const m = s();
396
+ m && (o = document.createElement("div"), o.className = "play-editor-mention-dropdown", o.style.top = `${m.top + 4}px`, o.style.left = `${m.left}px`, r.slice(0, 8).forEach((u, x) => {
397
+ const y = document.createElement("div");
398
+ y.className = "play-editor-mention-item", x === 0 && y.classList.add("active");
399
+ const v = u.name.split(" ").map((f) => f[0]).join("").toUpperCase();
400
+ y.innerHTML = `
401
+ <div class="play-editor-mention-avatar">${u.avatar ? `<img src="${u.avatar}" />` : v}</div>
402
+ <span class="play-editor-mention-name">${u.name}</span>
403
+ `, y.addEventListener("mousedown", (f) => {
404
+ f.preventDefault(), p(u);
405
+ }), o.appendChild(y);
406
+ }), n.editorArea.style.position = "relative", n.editorArea.appendChild(o));
407
+ }
408
+ async function h() {
409
+ const l = window.getSelection();
410
+ if (!l || l.rangeCount === 0) {
411
+ i();
412
+ return;
413
+ }
414
+ const r = l.getRangeAt(0), m = r.startContainer;
415
+ if (m.nodeType !== Node.TEXT_NODE) {
416
+ i();
417
+ return;
418
+ }
419
+ const u = m.textContent || "", x = r.startOffset, y = u.substring(0, x), v = y.lastIndexOf(t);
420
+ if (v === -1) {
421
+ i();
422
+ return;
423
+ }
424
+ const f = y.substring(v + t.length);
425
+ if (f.includes(" ") && f.trim().length > 0) {
426
+ i();
427
+ return;
428
+ }
429
+ await g(f);
430
+ }
431
+ n.editorArea.addEventListener("input", h), n.editorArea.addEventListener("keydown", (l) => {
432
+ if (!o) return;
433
+ const r = o.querySelectorAll(".play-editor-mention-item"), m = o.querySelector(".play-editor-mention-item.active");
434
+ let u = Array.from(r).indexOf(m);
435
+ l.key === "ArrowDown" ? (l.preventDefault(), m?.classList.remove("active"), u = (u + 1) % r.length, r[u].classList.add("active")) : l.key === "ArrowUp" ? (l.preventDefault(), m?.classList.remove("active"), u = (u - 1 + r.length) % r.length, r[u].classList.add("active")) : l.key === "Enter" || l.key === "Tab" ? m && (l.preventDefault(), m.dispatchEvent(new MouseEvent("mousedown"))) : l.key === "Escape" && (l.preventDefault(), i());
436
+ }), document.addEventListener("click", (l) => {
437
+ o && !o.contains(l.target) && i();
438
+ });
439
+ }
440
+ };
441
+ }
442
+ const q = E(), _ = {
443
+ name: "code-block",
444
+ init(e) {
445
+ e.addToolbarDivider(), e.addToolbarButton(d.code, "Inline Code", () => {
446
+ const n = window.getSelection();
447
+ if (!n || n.rangeCount === 0) return;
448
+ const o = n.getRangeAt(0), i = o.toString();
449
+ if (i) {
450
+ const s = document.createElement("code");
451
+ s.className = "play-editor-inline-code", s.textContent = i, o.deleteContents(), o.insertNode(s), o.setStartAfter(s), o.collapse(!0), n.removeAllRanges(), n.addRange(o);
452
+ } else {
453
+ const s = document.createElement("code");
454
+ s.className = "play-editor-inline-code", s.innerHTML = "&nbsp;", o.insertNode(s), o.selectNodeContents(s), n.removeAllRanges(), n.addRange(o);
455
+ }
456
+ e.textArea.value = e.editorArea.innerHTML;
457
+ }), e.addToolbarButton(d.braces, "Code Block", () => {
458
+ const o = window.getSelection()?.toString() || "// your code here", i = `
459
+ <pre class="play-editor-code-block"><code>${R(o)}</code></pre>
460
+ <p><br></p>
461
+ `;
462
+ e.exec("insertHTML", i);
463
+ });
464
+ let t = !1;
465
+ const a = e.addToolbarButton(d.code, "Toggle HTML Source", () => {
466
+ if (t = !t, t) {
467
+ const n = e.editorArea.innerHTML;
468
+ e.editorArea.innerText = n, e.editorArea.classList.add("play-editor-source-view"), a.classList.add("play-editor-btn-active");
469
+ } else {
470
+ const n = e.editorArea.innerText;
471
+ e.editorArea.innerHTML = n, e.editorArea.classList.remove("play-editor-source-view"), a.classList.remove("play-editor-btn-active"), e.textArea.value = e.editorArea.innerHTML;
472
+ }
473
+ });
474
+ }
475
+ };
476
+ function R(e) {
477
+ const t = document.createElement("div");
478
+ return t.textContent = e, t.innerHTML;
479
+ }
480
+ const F = {
481
+ name: "datetime",
482
+ init(e) {
483
+ e.addToolbarDivider(), e.addToolbarButton(d.calendar, "Insert Date", () => {
484
+ const t = /* @__PURE__ */ new Date(), a = t.toLocaleDateString(void 0, {
485
+ year: "numeric",
486
+ month: "long",
487
+ day: "numeric"
488
+ });
489
+ e.exec("insertHTML", `<time class="play-editor-datetime" datetime="${t.toISOString()}">${a}</time>`);
490
+ }), e.addToolbarButton(d.clock, "Insert Time", () => {
491
+ const t = /* @__PURE__ */ new Date(), a = t.toLocaleTimeString(void 0, {
492
+ hour: "2-digit",
493
+ minute: "2-digit"
494
+ });
495
+ e.exec("insertHTML", `<time class="play-editor-datetime" datetime="${t.toISOString()}">${a}</time>`);
496
+ });
497
+ }
498
+ }, C = {
499
+ Smileys: ["๐Ÿ˜€", "๐Ÿ˜ƒ", "๐Ÿ˜„", "๐Ÿ˜", "๐Ÿ˜†", "๐Ÿ˜…", "๐Ÿคฃ", "๐Ÿ˜‚", "๐Ÿ™‚", "๐Ÿ˜‰", "๐Ÿ˜Š", "๐Ÿ˜‡", "๐Ÿฅฐ", "๐Ÿ˜", "๐Ÿคฉ", "๐Ÿ˜˜", "๐Ÿ˜—", "๐Ÿ˜š", "๐Ÿ˜‹", "๐Ÿ˜›", "๐Ÿ˜œ", "๐Ÿคช", "๐Ÿ˜", "๐Ÿค‘", "๐Ÿค—", "๐Ÿคญ", "๐Ÿคซ", "๐Ÿค”", "๐Ÿซก"],
500
+ Gestures: ["๐Ÿ‘", "๐Ÿ‘Ž", "๐Ÿ‘", "๐Ÿ™Œ", "๐Ÿค", "๐Ÿ™", "โœŒ๏ธ", "๐Ÿคž", "๐ŸคŸ", "๐Ÿค˜", "๐Ÿ‘Œ", "๐ŸคŒ", "๐Ÿ‘‹", "โœ‹", "๐Ÿ–๏ธ", "๐Ÿ––", "๐Ÿ’ช", "๐Ÿฆพ"],
501
+ Hearts: ["โค๏ธ", "๐Ÿงก", "๐Ÿ’›", "๐Ÿ’š", "๐Ÿ’™", "๐Ÿ’œ", "๐Ÿ–ค", "๐Ÿค", "๐ŸคŽ", "๐Ÿ’”", "โฃ๏ธ", "๐Ÿ’•", "๐Ÿ’ž", "๐Ÿ’“", "๐Ÿ’—", "๐Ÿ’–", "๐Ÿ’˜", "๐Ÿ’"],
502
+ Objects: ["๐ŸŽ‰", "๐ŸŽŠ", "๐ŸŽˆ", "๐ŸŽ", "๐Ÿ†", "๐Ÿฅ‡", "โญ", "๐ŸŒŸ", "๐Ÿ’ก", "๐Ÿ”ฅ", "โœจ", "๐Ÿ’ฏ", "โœ…", "โŒ", "โš ๏ธ", "๐Ÿ“Œ", "๐Ÿ“Ž", "๐Ÿ”—", "๐Ÿ“", "๐Ÿ“…", "๐Ÿ“Š", "๐Ÿ“ˆ"],
503
+ Arrows: ["โžก๏ธ", "โฌ…๏ธ", "โฌ†๏ธ", "โฌ‡๏ธ", "โ†—๏ธ", "โ†˜๏ธ", "โ†™๏ธ", "โ†–๏ธ", "โ†”๏ธ", "โ†•๏ธ", "๐Ÿ”„", "๐Ÿ”ƒ"]
504
+ }, V = {
505
+ name: "emoji",
506
+ init(e) {
507
+ let t = null;
508
+ e.addToolbarButton(d.smile, "Insert Emoji", () => {
509
+ if (t) {
510
+ t.remove(), t = null;
511
+ return;
512
+ }
513
+ const a = window.getSelection();
514
+ let n = null;
515
+ a && a.rangeCount > 0 && (n = a.getRangeAt(0).cloneRange()), t = document.createElement("div"), t.className = "play-editor-emoji-picker";
516
+ const o = document.createElement("div");
517
+ o.className = "play-editor-emoji-tabs";
518
+ const i = document.createElement("div");
519
+ i.className = "play-editor-emoji-grid";
520
+ const s = Object.keys(C);
521
+ function p(h) {
522
+ i.innerHTML = "", C[h].forEach((l) => {
523
+ const r = document.createElement("button");
524
+ r.type = "button", r.className = "play-editor-emoji-item", r.textContent = l, r.addEventListener("mousedown", (m) => {
525
+ m.preventDefault(), e.editorArea.focus(), n && (a?.removeAllRanges(), a?.addRange(n)), e.exec("insertHTML", l), a && a.rangeCount > 0 && (n = a.getRangeAt(0).cloneRange());
526
+ }), i.appendChild(r);
527
+ });
528
+ }
529
+ s.forEach((h, l) => {
530
+ const r = document.createElement("button");
531
+ r.type = "button", r.className = "play-editor-emoji-tab", l === 0 && r.classList.add("active"), r.textContent = h, r.addEventListener("mousedown", (m) => {
532
+ m.preventDefault(), o.querySelectorAll(".play-editor-emoji-tab").forEach((u) => u.classList.remove("active")), r.classList.add("active"), p(h);
533
+ }), o.appendChild(r);
534
+ }), t.appendChild(o), t.appendChild(i), e.container.style.position = "relative", e.container.appendChild(t), p(s[0]);
535
+ const g = (h) => {
536
+ t && !t.contains(h.target) && (t.remove(), t = null, document.removeEventListener("mousedown", g));
537
+ };
538
+ setTimeout(() => document.addEventListener("mousedown", g), 0);
539
+ });
540
+ }
541
+ };
542
+ export {
543
+ U as AccordionPlugin,
544
+ P as AlignmentPlugin,
545
+ _ as CodeBlockPlugin,
546
+ N as ColorPlugin,
547
+ F as DateTimePlugin,
548
+ I as DirectionalityPlugin,
549
+ k as Editor,
550
+ V as EmojiPlugin,
551
+ B as FormattingPlugin,
552
+ S as LinksPlugin,
553
+ H as ListsPlugin,
554
+ D as MediaPlugin,
555
+ q as MentionsPlugin,
556
+ j as PageBreakPlugin,
557
+ z as PasteImagePlugin,
558
+ O as TablesPlugin,
559
+ $ as TocPlugin,
560
+ E as createMentionsPlugin
561
+ };
@@ -0,0 +1 @@
1
+ @import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap";:root{--pe-bg: #ffffff;--pe-text: #1e293b;--pe-text-muted: #64748b;--pe-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--pe-border: #e2e8f0;--pe-radius: 10px;--pe-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--pe-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -4px rgba(0, 0, 0, .1);--pe-focus: rgba(59, 130, 246, .45);--pe-toolbar-bg: #f8fafc;--pe-btn-hover: rgba(0, 0, 0, .06);--pe-btn-active: rgba(0, 0, 0, .1);--pe-accent: #3b82f6}.play-editor-container{border:1px solid var(--pe-border);border-radius:var(--pe-radius);background:var(--pe-bg);font-family:var(--pe-font);color:var(--pe-text);overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--pe-shadow);transition:border-color .2s,box-shadow .2s}.play-editor-container:focus-within{border-color:var(--pe-accent);box-shadow:0 0 0 3px var(--pe-focus)}.play-editor-toolbar{padding:6px 10px;background:var(--pe-toolbar-bg);border-bottom:1px solid var(--pe-border);display:flex;flex-wrap:wrap;gap:2px;align-items:center}.play-editor-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--pe-text-muted);cursor:pointer;position:relative;transition:background .15s,color .15s,transform .1s}.play-editor-btn svg{width:18px;height:18px;pointer-events:none}.play-editor-btn:hover{background:var(--pe-btn-hover);color:var(--pe-text)}.play-editor-btn:active{background:var(--pe-btn-active);transform:scale(.94)}.play-editor-btn:after{content:attr(title);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%) scale(.92);padding:4px 8px;border-radius:6px;background:#1e293b;color:#f8fafc;font-size:11px;font-weight:500;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .15s,transform .15s;z-index:10}.play-editor-btn:hover:after{opacity:1;transform:translate(-50%) scale(1)}.play-editor-divider{width:1px;height:22px;background:var(--pe-border);margin:0 4px;flex-shrink:0}.play-editor-content{min-height:300px;padding:1.25rem 1.5rem;outline:none;overflow-y:auto;font-size:15px;line-height:1.7;color:var(--pe-text)}.play-editor-content:empty:before{content:"Start typingโ€ฆ";color:var(--pe-text-muted);pointer-events:none;display:block}.play-editor-content p{margin:0 0 .75em}.play-editor-content h1{font-size:1.75em;font-weight:700;margin:1.2em 0 .4em;color:#0f172a}.play-editor-content h2{font-size:1.35em;font-weight:600;margin:1em 0 .35em;color:#0f172a}.play-editor-content h3{font-size:1.15em;font-weight:600;margin:.8em 0 .3em;color:#0f172a}.play-editor-content a{color:var(--pe-accent);text-decoration:underline}.play-editor-content img{max-width:100%;height:auto;border-radius:6px;margin:.5em 0}.play-editor-content hr{border:none;border-top:2px solid var(--pe-border);margin:1.5em 0}.play-editor-content blockquote{border-left:3px solid var(--pe-accent);padding-left:1em;margin-left:0;color:var(--pe-text-muted)}.play-editor-color-picker{position:relative;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:6px;cursor:pointer;border:none;transition:background .15s;color:var(--pe-text-muted)}.play-editor-color-picker:hover{background:var(--pe-btn-hover);color:var(--pe-text)}.play-editor-color-picker input[type=color]{position:absolute;inset:0;opacity:0;width:100%;height:100%;cursor:pointer}.play-editor-color-picker span{pointer-events:none;display:flex;align-items:center}.play-editor-color-picker span svg{width:18px;height:18px}.play-editor-table{border-collapse:collapse;width:100%;margin:1em 0}.play-editor-table td,.play-editor-table th{border:1px solid var(--pe-border);padding:10px 14px;min-width:60px;font-size:14px}.play-editor-table th{background:var(--pe-toolbar-bg);font-weight:600}.play-editor-accordion{border:1px solid var(--pe-border);border-radius:8px;margin:1em 0;overflow:hidden}.play-editor-accordion summary{padding:12px 16px;background:var(--pe-toolbar-bg);cursor:pointer;font-weight:600;font-size:14px;-webkit-user-select:none;user-select:none;list-style:none;display:flex;align-items:center;gap:8px}.play-editor-accordion summary:before{content:"โ–ธ";transition:transform .2s}.play-editor-accordion[open] summary:before{transform:rotate(90deg)}.play-editor-accordion-content{padding:14px 16px}.play-editor-page-break{page-break-after:always;display:flex;align-items:center;gap:12px;margin:1.5em 0}.play-editor-page-break:before,.play-editor-page-break:after{content:"";flex:1;border-top:2px dashed var(--pe-border)}.play-editor-page-break span{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--pe-text-muted);white-space:nowrap}.play-editor-toc{background:var(--pe-toolbar-bg);border:1px solid var(--pe-border);border-radius:8px;padding:16px 20px;margin:1em 0}.play-editor-toc h3{font-size:14px;font-weight:700;margin:0 0 10px;text-transform:uppercase;letter-spacing:.04em;color:var(--pe-text-muted)}.play-editor-toc ul{list-style:none;padding:0;margin:0}.play-editor-toc li{padding:4px 0}.play-editor-toc a{color:var(--pe-accent);text-decoration:none;font-size:14px;font-weight:500}.play-editor-toc a:hover{text-decoration:underline}.play-editor-mention{display:inline-flex;align-items:center;background:linear-gradient(135deg,#eff6ff,#e0e7ff);color:var(--pe-accent);font-weight:600;font-size:.875em;padding:2px 8px;border-radius:12px;margin:0 2px;-webkit-user-select:all;user-select:all;cursor:default;white-space:nowrap}.play-editor-mention-dropdown{position:absolute;z-index:100;min-width:220px;max-height:260px;overflow-y:auto;background:var(--pe-bg);border:1px solid var(--pe-border);border-radius:10px;box-shadow:0 10px 25px -5px #00000026,0 8px 10px -6px #0000001a;padding:4px}.play-editor-mention-item{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;cursor:pointer;transition:background .12s}.play-editor-mention-item:hover,.play-editor-mention-item.active{background:var(--pe-btn-hover)}.play-editor-mention-avatar{width:30px;height:30px;border-radius:50%;background:linear-gradient(135deg,#3b82f6,#8b5cf6);color:#fff;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}.play-editor-mention-avatar img{width:100%;height:100%;border-radius:50%;object-fit:cover}.play-editor-mention-name{font-size:13px;font-weight:500;color:var(--pe-text)}.play-editor-inline-code{background:#f1f5f9;color:#e11d48;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.875em;padding:2px 6px;border-radius:4px;border:1px solid var(--pe-border)}.play-editor-code-block{background:#1e293b;color:#e2e8f0;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:13px;line-height:1.6;padding:16px 20px;border-radius:8px;overflow-x:auto;margin:1em 0;tab-size:2}.play-editor-code-block code{background:transparent;color:inherit;padding:0;border:none;font-size:inherit}.play-editor-source-view{font-family:SF Mono,Fira Code,Cascadia Code,monospace!important;font-size:13px!important;white-space:pre-wrap;background:#1e293b!important;color:#e2e8f0!important}.play-editor-btn-active{background:var(--pe-accent)!important;color:#fff!important}.play-editor-datetime{display:inline-flex;align-items:center;background:#f0fdf4;color:#16a34a;font-weight:500;font-size:.875em;padding:2px 8px;border-radius:4px;border:1px solid #bbf7d0;white-space:nowrap}.play-editor-emoji-picker{position:absolute;top:0;left:0;z-index:200;width:340px;background:var(--pe-bg);border:1px solid var(--pe-border);border-radius:12px;box-shadow:0 20px 25px -5px #00000026,0 8px 10px -6px #0000001a;padding:8px;animation:pe-fade-in .15s ease}@keyframes pe-fade-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.play-editor-emoji-tabs{display:flex;gap:2px;padding-bottom:6px;border-bottom:1px solid var(--pe-border);margin-bottom:6px;overflow-x:auto}.play-editor-emoji-tab{background:transparent;border:none;padding:4px 10px;border-radius:6px;font-size:11px;font-weight:600;font-family:inherit;color:var(--pe-text-muted);cursor:pointer;white-space:nowrap;transition:background .12s,color .12s}.play-editor-emoji-tab:hover{background:var(--pe-btn-hover)}.play-editor-emoji-tab.active{background:var(--pe-accent);color:#fff}.play-editor-emoji-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:2px;max-height:200px;overflow-y:auto;padding:2px}.play-editor-emoji-item{width:36px;height:36px;display:flex;align-items:center;justify-content:center;font-size:20px;border:none;background:transparent;border-radius:6px;cursor:pointer;transition:background .1s,transform .1s}.play-editor-emoji-item:hover{background:var(--pe-btn-hover);transform:scale(1.15)}
package/dist/react.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("react/jsx-runtime"),i=require("react"),e=require("./index.cjs"),P=[e.FormattingPlugin,e.ListsPlugin,e.ColorPlugin,e.DirectionalityPlugin,e.AlignmentPlugin,e.LinksPlugin,e.MediaPlugin,e.TablesPlugin,e.AccordionPlugin,e.PageBreakPlugin,e.TocPlugin,e.PasteImagePlugin,e.MentionsPlugin,e.CodeBlockPlugin,e.DateTimePlugin,e.EmojiPlugin],s=i.forwardRef(({defaultValue:a="",onChange:u,plugins:c,className:g,minHeight:o},d)=>{const r=i.useRef(null),n=i.useRef(null);return i.useImperativeHandle(d,()=>({getContent:()=>n.current?.getContent()??"",setContent:t=>n.current?.setContent(t),editor:n.current})),i.useEffect(()=>{if(!r.current||n.current)return;const t=new e.Editor(r.current,c??P);n.current=t,o&&(t.editorArea.style.minHeight=`${o}px`),u&&t.editorArea.addEventListener("input",()=>{u(t.getContent())})},[]),l.jsx("div",{className:g,children:l.jsx("textarea",{ref:r,defaultValue:a,style:{display:"none"}})})});s.displayName="PlayEditor";exports.PlayEditor=s;
@@ -0,0 +1,48 @@
1
+ import { ForwardRefExoticComponent } from 'react';
2
+ import { RefAttributes } from 'react';
3
+
4
+ declare class Editor {
5
+ container: HTMLElement;
6
+ textArea: HTMLTextAreaElement;
7
+ editorArea: HTMLDivElement;
8
+ toolbar: HTMLDivElement;
9
+ private plugins;
10
+ constructor(selector: string | HTMLTextAreaElement, plugins?: Plugin_2[]);
11
+ private init;
12
+ exec(command: string, value?: string | undefined): void;
13
+ addToolbarButton(iconHtml: string, tooltip: string, onClick: () => void): HTMLButtonElement;
14
+ addToolbarDivider(): void;
15
+ getContent(): string;
16
+ setContent(html: string): void;
17
+ }
18
+
19
+ export declare const PlayEditor: ForwardRefExoticComponent<PlayEditorProps & RefAttributes<PlayEditorRef>>;
20
+
21
+ export declare interface PlayEditorProps {
22
+ /** Initial HTML content */
23
+ defaultValue?: string;
24
+ /** Called whenever the editor content changes */
25
+ onChange?: (html: string) => void;
26
+ /** Custom plugins array. If not provided, all built-in plugins are loaded. */
27
+ plugins?: Plugin_2[];
28
+ /** Additional CSS class for the container wrapper */
29
+ className?: string;
30
+ /** Minimum height of the editor content area in px */
31
+ minHeight?: number;
32
+ }
33
+
34
+ export declare interface PlayEditorRef {
35
+ /** Get the current HTML content */
36
+ getContent: () => string;
37
+ /** Set the HTML content programmatically */
38
+ setContent: (html: string) => void;
39
+ /** Access the underlying Editor instance */
40
+ editor: Editor | null;
41
+ }
42
+
43
+ declare interface Plugin_2 {
44
+ name: string;
45
+ init(editor: Editor): void;
46
+ }
47
+
48
+ export { }
package/dist/react.mjs ADDED
@@ -0,0 +1,50 @@
1
+ import { jsx as o } from "react/jsx-runtime";
2
+ import { forwardRef as P, useRef as l, useImperativeHandle as c, useEffect as d } from "react";
3
+ import { Editor as f, FormattingPlugin as m, ListsPlugin as p, ColorPlugin as y, DirectionalityPlugin as C, AlignmentPlugin as E, LinksPlugin as L, MediaPlugin as x, TablesPlugin as A, AccordionPlugin as R, PageBreakPlugin as k, TocPlugin as v, PasteImagePlugin as I, MentionsPlugin as T, CodeBlockPlugin as j, DateTimePlugin as w, EmojiPlugin as B } from "./index.mjs";
4
+ const D = [
5
+ m,
6
+ p,
7
+ y,
8
+ C,
9
+ E,
10
+ L,
11
+ x,
12
+ A,
13
+ R,
14
+ k,
15
+ v,
16
+ I,
17
+ T,
18
+ j,
19
+ w,
20
+ B
21
+ ], M = P(
22
+ ({ defaultValue: u = "", onChange: i, plugins: a, className: g, minHeight: r }, s) => {
23
+ const n = l(null), t = l(null);
24
+ return c(s, () => ({
25
+ getContent: () => t.current?.getContent() ?? "",
26
+ setContent: (e) => t.current?.setContent(e),
27
+ editor: t.current
28
+ })), d(() => {
29
+ if (!n.current || t.current) return;
30
+ const e = new f(
31
+ n.current,
32
+ a ?? D
33
+ );
34
+ t.current = e, r && (e.editorArea.style.minHeight = `${r}px`), i && e.editorArea.addEventListener("input", () => {
35
+ i(e.getContent());
36
+ });
37
+ }, []), /* @__PURE__ */ o("div", { className: g, children: /* @__PURE__ */ o(
38
+ "textarea",
39
+ {
40
+ ref: n,
41
+ defaultValue: u,
42
+ style: { display: "none" }
43
+ }
44
+ ) });
45
+ }
46
+ );
47
+ M.displayName = "PlayEditor";
48
+ export {
49
+ M as PlayEditor
50
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "plug-and-play-editor",
3
+ "private": false,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./react": {
16
+ "types": "./dist/react.d.ts",
17
+ "import": "./dist/react.mjs",
18
+ "require": "./dist/react.cjs"
19
+ },
20
+ "./style.css": "./dist/style.css"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "dev": "vite",
27
+ "build": "tsc && vite build",
28
+ "preview": "vite preview"
29
+ },
30
+ "peerDependencies": {
31
+ "react": ">=17.0.0",
32
+ "react-dom": ">=17.0.0"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "react": {
36
+ "optional": true
37
+ },
38
+ "react-dom": {
39
+ "optional": true
40
+ }
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^25.3.2",
44
+ "@types/react": "^19.1.6",
45
+ "@types/react-dom": "^19.1.6",
46
+ "typescript": "~5.9.3",
47
+ "vite": "^7.3.1",
48
+ "vite-plugin-dts": "^4.5.4"
49
+ },
50
+ "dependencies": {
51
+ "lucide": "^0.575.0"
52
+ }
53
+ }