UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

291 lines (290 loc) 12.9 kB
import TinyHtml from '../TinyHtml.mjs'; import TinyHtmlTemplate from './TinyHtmlTemplate.mjs'; /** * TinyHtmlButton is a helper class for <button> elements with full attribute support and validation. * It extends TinyHtmlTemplate for direct DOM manipulation. * * Supported attributes: * - autofocus * - command, commandfor * - disabled * - form, formaction, formenctype, formmethod, formnovalidate, formtarget * - name * - popovertarget, popovertargetaction * - type (button|submit|reset) * - value * * @extends TinyHtmlTemplate<HTMLButtonElement> */ class TinyHtmlButton extends TinyHtmlTemplate { /** * Creates a new TinyHtmlButton instance. * * @param {Object} config - Configuration object for the button. * @param {string|Element|TinyHtml<any>} config.label - The text/HTML/element to place inside the button. * If a non-string Element/TinyHtml is provided, `allowHtml` must be true. * @param {string|string[]|Set<string>} [config.tags=[]] - Initial CSS classes to apply. * @param {boolean} [config.allowHtml=false] - Whether to allow HTML or DOM nodes as label. * @param {'button'|'submit'|'reset'} [config.type='button'] - Button type. Allowed values: "button", "submit", "reset". * @param {boolean} [config.autofocus=false] - Whether the button should autofocus on load. * @param {'show-modal'|'close'|'request-close'|'show-popover'|'hide-popover'|'toggle-popover'} [config.command] - Command action (built-ins: "show-modal","close","request-close","show-popover","hide-popover","toggle-popover") * or a custom command which MUST start with `--` (two hyphens). * @param {string} [config.commandfor] - ID of the element controlled by the command. Must be a non-empty string. * @param {boolean} [config.disabled=false] - Whether the button is disabled. * @param {string} [config.form] - ID of the associated <form>. Must be a non-empty string if provided. * @param {string} [config.formaction] - URL that overrides the form action when this button is used to submit. * @param {'application/x-www-form-urlencoded'|'multipart/form-data'|'text/plain'} [config.formenctype] * - Encoding type used when the button submits the form. * @param {'get'|'post'|'dialog'} [config.formmethod] - HTTP method used for this button submission. * @param {boolean} [config.formnovalidate=false] - If true, disables validation when this button submits. * @param {'_self'|'_blank'|'_parent'|'_top'|'_unfencedTop'} [config.formtarget] - Target browsing context for this button's submission (keywords or name). * @param {string} [config.name] - Button name (sent with value when submitting). * @param {string} [config.popovertarget] - ID of the popover to control (non-empty string). * @param {'show'|'hide'|'toggle'} [config.popovertargetaction] - Action for popovertarget. * @param {string} [config.value] - Value submitted with the form when this button is used. * @param {string} [config.mainClass=''] - Main CSS class to append to the element. * * @throws {TypeError} If `label` is not a string, Element, or TinyHtml instance. * @throws {Error} If an Element/TinyHtml `label` is passed but `allowHtml` is false. * @throws {TypeError} If `type` is not one of "button", "submit", "reset". * @throws {TypeError} If `allowHtml` is not a boolean. * @throws {TypeError} If `autofocus`, `disabled` or `formnovalidate` are not booleans. * @throws {TypeError} If `command` is not a valid builtin command nor a custom `--*` token. * @throws {TypeError} If `commandfor`, `form`, `formaction`, `name`, `popovertarget`, or `value` are not strings. * @throws {TypeError} If `formenctype` is not one of the allowed encodings. * @throws {TypeError} If `formmethod` is not one of 'get','post','dialog'. * @throws {TypeError} If `formtarget` is not a valid keyword or browsing context name. */ constructor({ label, tags = [], allowHtml = false, type = 'button', autofocus = false, command, commandfor, disabled = false, form, formaction, formenctype, formmethod, formnovalidate = false, formtarget, name, popovertarget, popovertargetaction, value, mainClass = '', }) { super(document.createElement('button'), tags, mainClass); if (label === undefined || label === null) throw new TypeError('TinyHtmlButton: "label" is required.'); this.setLabel(label, allowHtml); this.type = type; this.autofocus = autofocus; this.disabled = disabled; this.formnovalidate = formnovalidate; if (command !== undefined) this.command = command; if (commandfor !== undefined) this.commandfor = commandfor; if (form !== undefined) this.form = form; if (formaction !== undefined) this.formaction = formaction; if (formenctype !== undefined) this.formenctype = formenctype; if (formmethod !== undefined) this.formmethod = formmethod; if (formtarget !== undefined) this.formtarget = formtarget; if (name !== undefined) this.name = name; if (popovertarget !== undefined) this.popovertarget = popovertarget; if (popovertargetaction !== undefined) this.popovertargetaction = popovertargetaction; if (value !== undefined) this.value = value; } /** * Updates the button label with text, HTML, or an element. * * @param {string|Element|TinyHtml<any>} label - The new label. * @param {boolean} [allowHtml=false] - Whether to allow raw HTML or DOM elements. * @returns {this} * @throws {TypeError} If the label is invalid. * @throws {Error} If DOM/HTML is passed but allowHtml=false. */ setLabel(label, allowHtml = false) { if (typeof label === 'string') { if (!allowHtml) this.setText(label); else this.setHtml(label); } else if (label instanceof Element || label instanceof TinyHtml) { if (!allowHtml) throw new Error('setLabel: Passing an Element/TinyHtml requires allowHtml=true.'); this.empty().append(label); } else { throw new TypeError("setLabel: 'label' must be a string, Element, or TinyHtml instance."); } return this; } // --- Getters & Setters --- /** @returns {string|null} */ get type() { return this.attrString('type'); } /** @param {'button'|'submit'|'reset'} val */ set type(val) { const allowed = ['button', 'submit', 'reset']; if (typeof val !== 'string' || !allowed.includes(val)) throw new TypeError(`TinyHtmlButton.type must be one of: ${allowed.join(', ')}`); this.setAttr('type', val); } /** @returns {boolean} */ get disabled() { return this.hasProp('disabled'); } /** @param {boolean} state */ set disabled(state) { if (typeof state !== 'boolean') throw new TypeError('TinyHtmlButton.disabled must be boolean.'); if (state) this.addProp('disabled'); else this.removeProp('disabled'); } /** @returns {boolean} */ get autofocus() { return this.hasProp('autofocus'); } /** @param {boolean} state */ set autofocus(state) { if (typeof state !== 'boolean') throw new TypeError('TinyHtmlButton.autofocus must be boolean.'); if (state) this.addProp('autofocus'); else this.removeProp('autofocus'); } /** @returns {boolean} */ get formnovalidate() { return this.hasProp('formnovalidate'); } /** @param {boolean} state */ set formnovalidate(state) { if (typeof state !== 'boolean') throw new TypeError('TinyHtmlButton.formnovalidate must be boolean.'); if (state) this.addProp('formnovalidate'); else this.removeProp('formnovalidate'); } /** @returns {string|null} */ get command() { return this.attrString('command'); } /** @param {'show-modal'|'close'|'request-close'|'show-popover'|'hide-popover'|'toggle-popover'} cmd */ set command(cmd) { const builtins = [ 'show-modal', 'close', 'request-close', 'show-popover', 'hide-popover', 'toggle-popover', ]; if (typeof cmd !== 'string' || (!builtins.includes(cmd) && !cmd.startsWith('--'))) throw new TypeError('TinyHtmlButton.command must be a built-in or custom string starting with "--".'); this.setAttr('command', cmd); } /** @returns {string|null} */ get commandfor() { return this.attrString('commandfor'); } /** @param {string} val */ set commandfor(val) { if (typeof val !== 'string' || !val.trim()) throw new TypeError('TinyHtmlButton.commandfor must be a non-empty string.'); this.setAttr('commandfor', val); } /** @returns {string|null} */ get form() { return this.attrString('form'); } /** @param {string} val */ set form(val) { if (typeof val !== 'string' || !val.trim()) throw new TypeError('TinyHtmlButton.form must be a non-empty string.'); this.setAttr('form', val); } /** @returns {string|null} */ get formaction() { return this.attrString('formaction'); } /** @param {string} val */ set formaction(val) { if (typeof val !== 'string' || !val.trim()) throw new TypeError('TinyHtmlButton.formaction must be a non-empty string.'); this.setAttr('formaction', val); } /** @returns {string|null} */ get formenctype() { return this.attrString('formenctype'); } /** @param {'application/x-www-form-urlencoded'|'multipart/form-data'|'text/plain'} val */ set formenctype(val) { const valid = ['application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']; if (!valid.includes(val)) throw new TypeError(`TinyHtmlButton.formenctype must be one of: ${valid.join(', ')}`); this.setAttr('formenctype', val); } /** @returns {string|null} */ get formmethod() { return this.attrString('formmethod'); } /** @param {'get'|'post'|'dialog'} val */ set formmethod(val) { const valid = ['get', 'post', 'dialog']; if (typeof val !== 'string' || !valid.includes(val.toLowerCase())) throw new TypeError(`TinyHtmlButton.formmethod must be one of: ${valid.join(', ')}`); this.setAttr('formmethod', val.toLowerCase()); } /** @returns {string|null} */ get formtarget() { return this.attrString('formtarget'); } /** @param {'_self'|'_blank'|'_parent'|'_top'|'_unfencedTop'} val */ set formtarget(val) { const validTargets = ['_self', '_blank', '_parent', '_top', '_unfencedTop']; const validName = /^[a-zA-Z_][\w-]*$/; if (typeof val !== 'string' || (!validTargets.includes(val) && !validName.test(val))) throw new TypeError(`TinyHtmlButton.formtarget must be a keyword (${validTargets.join(', ')}) or valid name.`); this.setAttr('formtarget', val); } /** @returns {string|null} */ get name() { return this.attr('name'); } /** @param {string} val */ set name(val) { if (typeof val !== 'string' || !val.trim()) throw new TypeError('TinyHtmlButton.name must be a non-empty string.'); this.setAttr('name', val); } /** @returns {string|null} */ get popovertarget() { return this.attr('popovertarget'); } /** @param {string} val */ set popovertarget(val) { if (typeof val !== 'string' || !val.trim()) throw new TypeError('TinyHtmlButton.popovertarget must be a non-empty string.'); this.setAttr('popovertarget', val); } /** @returns {string|null} */ get popovertargetaction() { return this.attrString('popovertargetaction'); } /** @param {'show'|'hide'|'toggle'} val */ set popovertargetaction(val) { const allowed = ['show', 'hide', 'toggle']; if (typeof val !== 'string' || !allowed.includes(val)) throw new TypeError(`TinyHtmlButton.popovertargetaction must be one of: ${allowed.join(', ')}`); this.setAttr('popovertargetaction', val); } /** @returns {string|null} */ get value() { return this.attr('value'); } set value(val) { if (typeof val !== 'string') throw new TypeError('TinyHtmlButton.value must be a string.'); this.setAttr('value', val); } } export default TinyHtmlButton;