@dodona/papyros
Version:
Scratchpad for multiple programming languages in the browser.
142 lines • 5.21 kB
JavaScript
import { getElement } from "./Util";
import escapeHTML from "escape-html";
/**
* Helper method to append classes to the class attribute of an HTMLElement
* as consecutive whitespace is not allowed
* @param {Object} options Object containing classNames
* @param {string} classNames The classes to append
*/
export function appendClasses(options, classNames) {
if (options.classNames && !options.classNames.includes(classNames)) {
options.classNames = `${options.classNames} ${classNames}`;
}
else {
options.classNames = classNames;
}
}
/**
* Helper method to add attributes to options with a possibly undefined attribute Map
* @param {Object} options Object containing attributes
* @param {Map<string, string>} attributes The attributes to add
*/
export function addAttributes(options, attributes) {
options.attributes = new Map([
...(options.attributes || []),
...attributes
]);
}
/**
* Renders an element with the given options
* @param {RenderOptions} options Options to be used while rendering
* @param {string | HTMLElement} content What to fill the parent with.
* If the content is a string, it should be properly formatted HTML
* @return {HTMLElement} The parent with the new child
*/
export function renderWithOptions(options, content) {
const parent = getElement(options.parentElementId);
if (options.classNames) {
parent.classList.add(...options.classNames.split(" "));
}
parent.classList.toggle("_tw-dark", Boolean(options.darkMode));
if (options.attributes) {
for (const [attr, value] of options.attributes.entries()) {
parent.setAttribute(attr, value);
}
}
if (typeof content === "string") {
parent.innerHTML = content;
}
else {
parent.replaceChildren(content);
}
return parent;
}
/**
* Construct a HTML button string from the given options
* @param {ButtonOptions} options The options for the button
* @return {string} HTML string for the button
*/
export function renderButton(options) {
appendClasses(options, "papyros-button");
if (options.icon) {
appendClasses(options, "with-icon");
}
return `
<button id="${options.id}" type="button"
class="${options.classNames}">
${options.icon || ""}
${options.buttonText}
</button>`;
}
/**
* Constructs the options for use within an HTML select element
* @param {Array<T>} options All options to display in the list
* @param {function(T):string} optionText Function to convert the elements to a string
* @param {T} selected The initially selected element in the list, if any
* @return {string} The string representation of the select options
*/
export function renderSelectOptions(options, optionText, selected) {
return options.map((option) => {
const selectedValue = option === selected ? "selected" : "";
return `
<option ${selectedValue} value="${escapeHTML(option)}" class="dark:_tw-text-white dark:_tw-bg-dark-mode-bg">
${optionText(option)}
</option>
`;
}).join("\n");
}
/**
* Build a string representation of an HTML label element
* @param {string} labelText Optional text to display in a label
* If not provided, no label is created
* @param {string} forElement The id of the element this label is for
* @return {string} The HTML string of the label
*/
export function renderLabel(labelText, forElement) {
return labelText ? `
<label for="${forElement}"
class="dark:_tw-text-white dark:_tw-bg-dark-mode-bg _tw-px-1">${labelText}: </label>` : "";
}
/**
* Constructs an HTML select element
* @param {string} selectId The HTML id for the element
* @param {Array<T>} options to display in the list
* @param {function(T):string} optionText to convert elements to a string
* @param {T} selected The initially selected element in the list, if any
* @param {string} labelText Optional text to display in a label
* @return {string} The string representation of the select element
*/
export function renderSelect(selectId, options, optionText, selected, labelText) {
const select = `
<select id="${selectId}" class="_tw-m-2 _tw-border-2 _tw-px-1 _tw-rounded-lg
dark:_tw-text-white dark:_tw-bg-dark-mode-bg dark:_tw-border-dark-mode-content">
${renderSelectOptions(options, optionText, selected)}
</select>`;
return `
${renderLabel(labelText, selectId)}
${select}
`;
}
/**
* Helper superclass to handle storing options used during rendering
* to allow re-rendering without needing to explicitly store used options each time
*/
export class Renderable extends EventTarget {
get renderOptions() {
if (!this._renderOptions) {
throw new Error(`${this.constructor.name} not yet rendered!`);
}
return this._renderOptions;
}
/**
* Render this component into the DOM
* @param {Options} options Optional options to render with. If omitted, stored options are used
*/
render(options) {
if (options) {
this._renderOptions = options;
}
this._render(this.renderOptions);
}
}
//# sourceMappingURL=Rendering.js.map