import { html, css, LitElement } from "lit"; import { customElement, property, query, state } from "lit/decorators.js"; import { checkFonts } from "../utils/check-fonts"; import { live } from "../utils/live"; import "./rich-action"; import { editorCommand } from "./rich-action"; @customElement("rich-toolbar") export class RichToolbar extends LitElement { static styles = css` header { width: 100%; color: var(--rich-color); display: flex; flex-direction: row; } input[type="color"] { -webkit-appearance: none; border: none; width: var(--icon-size); height: var(--icon-size); } input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; } input[type="color"]::-webkit-color-swatch { border: none; } `; @live selection: Selection | null = null; @query("#fg-color") fgColorInput!: HTMLInputElement; @query("#bd-color") bdColorInput!: HTMLInputElement; @state() fileHandle?: any; @property({ type: Object, hasChanged: () => true }) node!: Element; render() { const tags = this.getTags(); return html`
{ const newLink = prompt("Write the URL here", "https://"); // Check if valid url if (newLink && newLink.match(/^(http|https):\/\/[^ "]+$/)) { editorCommand("createlink", newLink); } }} > this.fgColorInput.click()} > { const input = e.target as HTMLInputElement; editorCommand("forecolor", input.value); }} /> this.bdColorInput.click()} > { const input = e.target as HTMLInputElement; editorCommand("backcolor", input.value); }} /> ({ name: font, value: font, })), ]} > { if ("showOpenFilePicker" in window) { // File system api // @ts-ignore const [fileHandle] = await window.showOpenFilePicker(); this.fileHandle = fileHandle; if (fileHandle) { const file = await fileHandle.getFile(); const contents = await file.text(); this.dispatchEvent( new CustomEvent("set-content", { detail: contents, bubbles: true, composed: true, }) ); } } else { // Fallback to input const input = document.createElement("input"); input.type = "file"; input.click(); input.onchange = async () => { const file = input.files![0]; if (file) { const reader = new FileReader(); reader.onload = () => { const data = reader.result as string; this.dispatchEvent( new CustomEvent("set-content", { detail: data, bubbles: true, composed: true, }) ); }; reader.readAsText(file); } }; } }} > { const contents = this.node.innerHTML; if (this.fileHandle) { const writable = await this.fileHandle.createWritable(); await writable.write( [ ``, ``, ` `, ` ${contents}`, ``, ].join("\n") ); await writable.close(); } else { // Download file const url = window.URL.createObjectURL( new Blob([contents], { type: "text/html" }) ); const link = document.createElement("a"); link.href = url; link.download = "index.html"; link.click(); } }} >
`; } getTags() { const { selection } = this; let tags: string[] = []; if (selection) { if (selection.type === "Range") { // @ts-ignore let parentNode = selection?.baseNode; if (parentNode) { const checkNode = () => { const tag = parentNode?.tagName?.toLowerCase()?.trim(); if (tag) tags.push(tag); }; while (parentNode != null) { checkNode(); parentNode = parentNode?.parentNode; } } // Remove root tag tags.pop(); } else { const content = this.selection?.toString() || ""; tags = (content.match(/<[^>]+>/g) || []) .filter((tag) => !tag.startsWith(" tag.replace(/<|>/g, "")); } } return tags; } } declare global { interface HTMLElementTagNameMap { "rich-toolbar": RichToolbar; } }