Update.
This commit is contained in:
		
							parent
							
								
									75f12c1971
								
							
						
					
					
						commit
						52538d0181
					
				
							
								
								
									
										226
									
								
								src/snek/static/editor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								src/snek/static/editor.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,226 @@
 | 
				
			|||||||
 | 
					import { NjetComponent} from "/njext.ks"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class NjetEditor extends NjetComponent {
 | 
				
			||||||
 | 
					      constructor() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.attachShadow({ mode: 'open' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const style = document.createElement('style');
 | 
				
			||||||
 | 
					        style.textContent = `
 | 
				
			||||||
 | 
					          #editor {
 | 
				
			||||||
 | 
					            padding: 1rem;
 | 
				
			||||||
 | 
					            outline: none;
 | 
				
			||||||
 | 
					            white-space: pre-wrap;
 | 
				
			||||||
 | 
					            line-height: 1.5;
 | 
				
			||||||
 | 
					            height: 100%;
 | 
				
			||||||
 | 
					            overflow-y: auto;
 | 
				
			||||||
 | 
					            background: #1e1e1e;
 | 
				
			||||||
 | 
					            color: #d4d4d4;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          #command-line {
 | 
				
			||||||
 | 
					            position: absolute;
 | 
				
			||||||
 | 
					            bottom: 0;
 | 
				
			||||||
 | 
					            left: 0;
 | 
				
			||||||
 | 
					            width: 100%;
 | 
				
			||||||
 | 
					            padding: 0.2rem 1rem;
 | 
				
			||||||
 | 
					            background: #333;
 | 
				
			||||||
 | 
					            color: #0f0;
 | 
				
			||||||
 | 
					            display: none;
 | 
				
			||||||
 | 
					            font-family: monospace;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.editor = document.createElement('div');
 | 
				
			||||||
 | 
					        this.editor.id = 'editor';
 | 
				
			||||||
 | 
					        this.editor.contentEditable = true;
 | 
				
			||||||
 | 
					        this.editor.innerText = `Welcome to VimEditor Component
 | 
				
			||||||
 | 
					Line 2 here
 | 
				
			||||||
 | 
					Another line
 | 
				
			||||||
 | 
					Try i, Esc, v, :, yy, dd, 0, $, gg, G, and p`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.cmdLine = document.createElement('div');
 | 
				
			||||||
 | 
					        this.cmdLine.id = 'command-line';
 | 
				
			||||||
 | 
					        this.shadowRoot.append(style, this.editor, this.cmdLine);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.mode = 'normal'; // normal | insert | visual | command
 | 
				
			||||||
 | 
					        this.keyBuffer = '';
 | 
				
			||||||
 | 
					        this.lastDeletedLine = '';
 | 
				
			||||||
 | 
					        this.yankedLine = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.editor.addEventListener('keydown', this.handleKeydown.bind(this));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      connectedCallback() {
 | 
				
			||||||
 | 
					        this.editor.focus();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      getCaretOffset() {
 | 
				
			||||||
 | 
					        let caretOffset = 0;
 | 
				
			||||||
 | 
					        const sel = this.shadowRoot.getSelection();
 | 
				
			||||||
 | 
					        if (!sel || sel.rangeCount === 0) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const range = sel.getRangeAt(0);
 | 
				
			||||||
 | 
					        const preCaretRange = range.cloneRange();
 | 
				
			||||||
 | 
					        preCaretRange.selectNodeContents(this.editor);
 | 
				
			||||||
 | 
					        preCaretRange.setEnd(range.endContainer, range.endOffset);
 | 
				
			||||||
 | 
					        caretOffset = preCaretRange.toString().length;
 | 
				
			||||||
 | 
					        return caretOffset;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setCaretOffset(offset) {
 | 
				
			||||||
 | 
					        const range = document.createRange();
 | 
				
			||||||
 | 
					        const sel = this.shadowRoot.getSelection();
 | 
				
			||||||
 | 
					        const walker = document.createTreeWalker(this.editor, NodeFilter.SHOW_TEXT, null, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let currentOffset = 0;
 | 
				
			||||||
 | 
					        let node;
 | 
				
			||||||
 | 
					        while ((node = walker.nextNode())) {
 | 
				
			||||||
 | 
					          if (currentOffset + node.length >= offset) {
 | 
				
			||||||
 | 
					            range.setStart(node, offset - currentOffset);
 | 
				
			||||||
 | 
					            range.collapse(true);
 | 
				
			||||||
 | 
					            sel.removeAllRanges();
 | 
				
			||||||
 | 
					            sel.addRange(range);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          currentOffset += node.length;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      handleKeydown(e) {
 | 
				
			||||||
 | 
					        const key = e.key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.mode === 'insert') {
 | 
				
			||||||
 | 
					          if (key === 'Escape') {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.mode = 'normal';
 | 
				
			||||||
 | 
					            this.editor.blur();
 | 
				
			||||||
 | 
					            this.editor.focus();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.mode === 'command') {
 | 
				
			||||||
 | 
					          if (key === 'Enter' || key === 'Escape') {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.cmdLine.style.display = 'none';
 | 
				
			||||||
 | 
					            this.mode = 'normal';
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.mode === 'visual') {
 | 
				
			||||||
 | 
					          if (key === 'Escape') {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.mode = 'normal';
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Handle normal mode
 | 
				
			||||||
 | 
					        this.keyBuffer += key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const text = this.editor.innerText;
 | 
				
			||||||
 | 
					        const caretPos = this.getCaretOffset();
 | 
				
			||||||
 | 
					        const lines = text.split('\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let charCount = 0, lineIdx = 0;
 | 
				
			||||||
 | 
					        for (let i = 0; i < lines.length; i++) {
 | 
				
			||||||
 | 
					          if (caretPos <= charCount + lines[i].length) {
 | 
				
			||||||
 | 
					            lineIdx = i;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          charCount += lines[i].length + 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const offsetToLine = idx =>
 | 
				
			||||||
 | 
					          text.split('\n').slice(0, idx).reduce((acc, l) => acc + l.length + 1, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (this.keyBuffer) {
 | 
				
			||||||
 | 
					          case 'i':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.mode = 'insert';
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'v':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.mode = 'visual';
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case ':':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.mode = 'command';
 | 
				
			||||||
 | 
					            this.cmdLine.style.display = 'block';
 | 
				
			||||||
 | 
					            this.cmdLine.textContent = ':';
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'yy':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.yankedLine = lines[lineIdx];
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'dd':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.lastDeletedLine = lines[lineIdx];
 | 
				
			||||||
 | 
					            lines.splice(lineIdx, 1);
 | 
				
			||||||
 | 
					            this.editor.innerText = lines.join('\n');
 | 
				
			||||||
 | 
					            this.setCaretOffset(offsetToLine(lineIdx));
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'p':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            const lineToPaste = this.yankedLine || this.lastDeletedLine;
 | 
				
			||||||
 | 
					            if (lineToPaste) {
 | 
				
			||||||
 | 
					              lines.splice(lineIdx + 1, 0, lineToPaste);
 | 
				
			||||||
 | 
					              this.editor.innerText = lines.join('\n');
 | 
				
			||||||
 | 
					              this.setCaretOffset(offsetToLine(lineIdx + 1));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case '0':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.setCaretOffset(offsetToLine(lineIdx));
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case '$':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.setCaretOffset(offsetToLine(lineIdx) + lines[lineIdx].length);
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'gg':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.setCaretOffset(0);
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'G':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.setCaretOffset(text.length);
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          case 'Escape':
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            this.mode = 'normal';
 | 
				
			||||||
 | 
					            this.keyBuffer = '';
 | 
				
			||||||
 | 
					            this.cmdLine.style.display = 'none';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          default:
 | 
				
			||||||
 | 
					            // allow up to 2 chars for combos
 | 
				
			||||||
 | 
					            if (this.keyBuffer.length > 2) this.keyBuffer = '';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    customElements.define('njet-editor', NjetEditor);
 | 
				
			||||||
 | 
					export {NjetEditor}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user