monogon
Version:
Modern syntax highlighting for developer tooling
99 lines • 3.56 kB
JavaScript
import { transformModule } from './utils.js';
import { getModule } from './modules.js';
import { css } from './theme.js';
class MonogonCode extends HTMLElement {
constructor() {
super();
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: ''
});
Object.defineProperty(this, "codeEl", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "styleEl", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "applyHighlights", {
enumerable: true,
configurable: true,
writable: true,
value: () => { }
});
}
async connectedCallback() {
await this.prepare();
setTimeout(() => {
this.refresh();
}, 1); // TODO: handle with element creation order better
}
async attributeChangedCallback(oldValue, newValue) {
if (oldValue !== newValue) {
await this.refresh();
}
}
async prepare() {
const shadow = this.shadowRoot || this.attachShadow({ mode: 'open' });
shadow.innerHTML = '';
/** Style */
const themeStyleEl = document.createElement('style');
themeStyleEl.textContent = css;
shadow.appendChild(themeStyleEl);
const preEl = document.createElement('pre');
const codeEl = document.createElement('code');
const editable = this.hasAttribute('readonly') ? 'false' : 'plaintext-only';
codeEl.setAttribute('contenteditable', editable);
codeEl.setAttribute('spellcheck', 'false');
shadow.appendChild(preEl);
preEl.appendChild(codeEl);
/** Listeners */
codeEl.addEventListener('input', () => {
this.codeEl.normalize();
this.value = this.codeEl.textContent ?? '';
this.applyHighlights();
});
this.codeEl = codeEl;
this.styleEl = themeStyleEl;
}
async refresh() {
if (!this.codeEl)
return;
/** Module */
const moduleName = this.getAttribute('lang') ?? 'plaintext';
const module = await getModule(moduleName);
const content = this.getAttribute('content') ?? '';
this.value = content;
this.codeEl.textContent = module.format ? module.format(content) : content;
// When having multiple blocks on the same page, generated highlights will conflict with each other
if (!this.codeEl.childNodes?.[0])
return;
const littleHash = window.crypto.randomUUID().substring(0, 6);
const definitions = transformModule(module.definitions, this.codeEl, littleHash);
const moduleCss = definitions.map((m) => m.css).join(' ');
/** Highlights */
this.styleEl.textContent += `${moduleCss}`;
this.applyHighlights = () => {
definitions.forEach((highlight) => {
highlight.apply();
});
};
this.applyHighlights();
}
}
Object.defineProperty(MonogonCode, "observedAttributes", {
enumerable: true,
configurable: true,
writable: true,
value: ['content', 'lang', 'readonly']
});
if (!customElements.get('monogon-code'))
customElements.define('monogon-code', MonogonCode);
//# sourceMappingURL=monogon-code.js.map