UNPKG

@anywhichway/nerd-editor

Version:

A JavaScript rich text editor based on and with support for custom elements.

61 lines (59 loc) 2.99 kB
import {bind} from "./bind"; import {createElement} from "./create-element"; import {typeAheads} from "./type-aheads"; const cursor = createElement({tagName:"cursor",attributes:{contenteditable: false,display:"none"}}), popper = createElement({tagName:"popper",attributes:{style:{position:"absolute",display:"none","zIndex":100,"backgroundColor":"whitesmoke"}}}); document.body.appendChild(popper); const showPopper = (target,content,{position= {},offset={}}={}) => { const rect = target.getBoundingClientRect(), vertical = rect[position.vertical||"bottom"], horizontal = rect[position.horizontal||"right"] popper.innerHTML = ""; popper.appendChild(content); popper.style.top = `calc(${vertical}px + ${offset.vertical||"0px"})`; popper.style.left = `calc(${horizontal}px + ${offset.horizontal||"0px"})`; popper.style.display = ""; } const hidePopper = () => { popper.style.display = "none"; } const typeAhead = (editor) => { popper.style.display = "none"; const selection = window.getSelection(), {anchorNode,anchorOffset,focusNode,focusOffset} = selection, node = selection.anchorNode; if(node.nodeType===Node.TEXT_NODE) { const text = node.textContent.substring(0,selection.anchorOffset); Object.entries(typeAheads).some(([name,typeAhead]) => { const {match,matchEnd=/.+\s/s,options,template,content} = bind(typeAhead), matched = (text.match(match)||[])[1], end = matched ? matched.match(matchEnd) : false; if(matched) { if(end) { selection.setBaseAndExtent(anchorNode,focusOffset-matched.length,focusNode,anchorOffset); editor.replaceSelection(content(matched)) } else { const use = (items) => { const div = document.createElement("div"); items = items.map((item) => { const el = template(item); el.onclick = () => { hidePopper(); selection.setBaseAndExtent(anchorNode,focusOffset-matched.length,focusNode,anchorOffset); editor.replaceSelection(content(item)); }; return el; }); items.forEach((item) => div.appendChild(item)); showPopper(cursor,div,{position:{horizontal:"left"},offset:{horizontal:(-(matched.length-2))+"ch"}}); selection.setBaseAndExtent(anchorNode,anchorOffset,focusNode,focusOffset); }; selection.getRangeAt(0).surroundContents(cursor); options(matched,use); } return true; } }) } } export {typeAhead}