feat: Reinitialize frontend with SvelteKit and TypeScript

- Delete old Vite+Svelte frontend
- Initialize new SvelteKit project with TypeScript
- Configure Tailwind CSS v4 + DaisyUI
- Implement JWT authentication with auto-refresh
- Create login page with form validation (Zod)
- Add protected route guards
- Update Docker configuration for single-stage build
- Add E2E tests with Playwright (6/11 passing)
- Fix Svelte 5 reactivity with $state() runes

Known issues:
- 5 E2E tests failing (timing/async issues)
- Token refresh implementation needs debugging
- Validation error display timing
This commit is contained in:
2026-02-17 16:19:59 -05:00
parent 54df6018f5
commit de2d83092e
28274 changed files with 3816354 additions and 90 deletions

View File

@@ -0,0 +1,379 @@
import Element from '../element/Element.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import CSSStyleDeclaration from '../../css/declaration/CSSStyleDeclaration.js';
import Event from '../../event/Event.js';
import DOMStringMap from '../../dom/DOMStringMap.js';
import Attr from '../attr/Attr.js';
/**
* HTML Element.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.
*/
export default class HTMLElement extends Element {
#private;
cloneNode: (deep?: boolean) => HTMLElement;
static observedAttributes?: string[];
[PropertySymbol.accessKey]: string;
[PropertySymbol.offsetHeight]: number;
[PropertySymbol.offsetWidth]: number;
[PropertySymbol.offsetLeft]: number;
[PropertySymbol.offsetTop]: number;
[PropertySymbol.clientHeight]: number;
[PropertySymbol.clientWidth]: number;
[PropertySymbol.clientLeft]: number;
[PropertySymbol.clientTop]: number;
[PropertySymbol.style]: CSSStyleDeclaration;
[PropertySymbol.dataset]: DOMStringMap | null;
get oncancel(): ((event: Event) => void) | null;
set oncancel(value: ((event: Event) => void) | null);
get onerror(): ((event: Event) => void) | null;
set onerror(value: ((event: Event) => void) | null);
get onscroll(): ((event: Event) => void) | null;
set onscroll(value: ((event: Event) => void) | null);
get onselect(): ((event: Event) => void) | null;
set onselect(value: ((event: Event) => void) | null);
get onwheel(): ((event: Event) => void) | null;
set onwheel(value: ((event: Event) => void) | null);
get oncopy(): ((event: Event) => void) | null;
set oncopy(value: ((event: Event) => void) | null);
get oncut(): ((event: Event) => void) | null;
set oncut(value: ((event: Event) => void) | null);
get onpaste(): ((event: Event) => void) | null;
set onpaste(value: ((event: Event) => void) | null);
get oncompositionend(): ((event: Event) => void) | null;
set oncompositionend(value: ((event: Event) => void) | null);
get oncompositionstart(): ((event: Event) => void) | null;
set oncompositionstart(value: ((event: Event) => void) | null);
get oncompositionupdate(): ((event: Event) => void) | null;
set oncompositionupdate(value: ((event: Event) => void) | null);
get onblur(): ((event: Event) => void) | null;
set onblur(value: ((event: Event) => void) | null);
get onfocus(): ((event: Event) => void) | null;
set onfocus(value: ((event: Event) => void) | null);
get onfocusin(): ((event: Event) => void) | null;
set onfocusin(value: ((event: Event) => void) | null);
get onfocusout(): ((event: Event) => void) | null;
set onfocusout(value: ((event: Event) => void) | null);
get onkeydown(): ((event: Event) => void) | null;
set onkeydown(value: ((event: Event) => void) | null);
get onkeyup(): ((event: Event) => void) | null;
set onkeyup(value: ((event: Event) => void) | null);
get onauxclick(): ((event: Event) => void) | null;
set onauxclick(value: ((event: Event) => void) | null);
get onclick(): ((event: Event) => void) | null;
set onclick(value: ((event: Event) => void) | null);
get oncontextmenu(): ((event: Event) => void) | null;
set oncontextmenu(value: ((event: Event) => void) | null);
get ondblclick(): ((event: Event) => void) | null;
set ondblclick(value: ((event: Event) => void) | null);
get onmousedown(): ((event: Event) => void) | null;
set onmousedown(value: ((event: Event) => void) | null);
get onmouseenter(): ((event: Event) => void) | null;
set onmouseenter(value: ((event: Event) => void) | null);
get onmouseleave(): ((event: Event) => void) | null;
set onmouseleave(value: ((event: Event) => void) | null);
get onmousemove(): ((event: Event) => void) | null;
set onmousemove(value: ((event: Event) => void) | null);
get onmouseout(): ((event: Event) => void) | null;
set onmouseout(value: ((event: Event) => void) | null);
get onmouseover(): ((event: Event) => void) | null;
set onmouseover(value: ((event: Event) => void) | null);
get onmouseup(): ((event: Event) => void) | null;
set onmouseup(value: ((event: Event) => void) | null);
get ontouchcancel(): ((event: Event) => void) | null;
set ontouchcancel(value: ((event: Event) => void) | null);
get ontouchend(): ((event: Event) => void) | null;
set ontouchend(value: ((event: Event) => void) | null);
get ontouchmove(): ((event: Event) => void) | null;
set ontouchmove(value: ((event: Event) => void) | null);
get ontouchstart(): ((event: Event) => void) | null;
set ontouchstart(value: ((event: Event) => void) | null);
get oninvalid(): ((event: Event) => void) | null;
set oninvalid(value: ((event: Event) => void) | null);
get onanimationcancel(): ((event: Event) => void) | null;
set onanimationcancel(value: ((event: Event) => void) | null);
get onanimationend(): ((event: Event) => void) | null;
set onanimationend(value: ((event: Event) => void) | null);
get onanimationiteration(): ((event: Event) => void) | null;
set onanimationiteration(value: ((event: Event) => void) | null);
get onanimationstart(): ((event: Event) => void) | null;
set onanimationstart(value: ((event: Event) => void) | null);
get onbeforeinput(): ((event: Event) => void) | null;
set onbeforeinput(value: ((event: Event) => void) | null);
get oninput(): ((event: Event) => void) | null;
set oninput(value: ((event: Event) => void) | null);
get onchange(): ((event: Event) => void) | null;
set onchange(value: ((event: Event) => void) | null);
get ongotpointercapture(): ((event: Event) => void) | null;
set ongotpointercapture(value: ((event: Event) => void) | null);
get onlostpointercapture(): ((event: Event) => void) | null;
set onlostpointercapture(value: ((event: Event) => void) | null);
get onpointercancel(): ((event: Event) => void) | null;
set onpointercancel(value: ((event: Event) => void) | null);
get onpointerdown(): ((event: Event) => void) | null;
set onpointerdown(value: ((event: Event) => void) | null);
get onpointerenter(): ((event: Event) => void) | null;
set onpointerenter(value: ((event: Event) => void) | null);
get onpointerleave(): ((event: Event) => void) | null;
set onpointerleave(value: ((event: Event) => void) | null);
get onpointermove(): ((event: Event) => void) | null;
set onpointermove(value: ((event: Event) => void) | null);
get onpointerout(): ((event: Event) => void) | null;
set onpointerout(value: ((event: Event) => void) | null);
get onpointerover(): ((event: Event) => void) | null;
set onpointerover(value: ((event: Event) => void) | null);
get onpointerup(): ((event: Event) => void) | null;
set onpointerup(value: ((event: Event) => void) | null);
get ontransitioncancel(): ((event: Event) => void) | null;
set ontransitioncancel(value: ((event: Event) => void) | null);
get ontransitionend(): ((event: Event) => void) | null;
set ontransitionend(value: ((event: Event) => void) | null);
get ontransitionrun(): ((event: Event) => void) | null;
set ontransitionrun(value: ((event: Event) => void) | null);
get ontransitionstart(): ((event: Event) => void) | null;
set ontransitionstart(value: ((event: Event) => void) | null);
/**
* Returns access key.
*
* @returns Access key.
*/
get accessKey(): string;
/**
* Sets access key.
*
* @param accessKey Access key.
*/
set accessKey(accessKey: string);
/**
* Returns content editable.
*
* @returns Content editable.
*/
get contentEditable(): string;
/**
* Sets content editable.
*
* @param contentEditable Content editable.
*/
set contentEditable(contentEditable: string);
/**
* Returns is content editable.
*
* @returns Is content editable.
*/
get isContentEditable(): boolean;
/**
* Returns offset height.
*
* @returns Offset height.
*/
get offsetHeight(): number;
/**
* Returns offset width.
*
* @returns Offset width.
*/
get offsetWidth(): number;
/**
* Returns offset left.
*
* @returns Offset left.
*/
get offsetLeft(): number;
/**
* Returns offset top.
*
* @returns Offset top.
*/
get offsetTop(): number;
/**
* Returns client height.
*
* @returns Client height.
*/
get clientHeight(): number;
/**
* Returns client width.
*
* @returns Client width.
*/
get clientWidth(): number;
/**
* Returns client left.
*
* @returns Client left.
*/
get clientLeft(): number;
/**
* Returns client top.
*
* @returns Client top.
*/
get clientTop(): number;
/**
* Returns tab index.
*
* @returns Tab index.
*/
get tabIndex(): number;
/**
* Returns tab index.
*
* @param tabIndex Tab index.
*/
set tabIndex(tabIndex: number);
/**
* Returns inner text, which is the rendered appearance of text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @returns Inner text.
*/
get innerText(): string;
/**
* Sets the inner text, which is the rendered appearance of text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @param innerText Inner text.
*/
set innerText(text: string);
/**
* Returns outer text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @returns HTML.
*/
get outerText(): string;
/**
* Sets outer text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @param text Text.
*/
set outerText(text: string);
/**
* Returns style.
*
* @returns Style.
*/
get style(): CSSStyleDeclaration;
/**
* Sets style.
*
* @param cssText Style as text.
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style#setting_styles
*/
set style(cssText: string | CSSStyleDeclaration | null);
/**
* Returns data set.
*
* @returns Data set.
*/
get dataset(): DOMStringMap;
/**
* Returns direction.
*
* @returns Direction.
*/
get dir(): string;
/**
* Returns direction.
*
* @param direction Direction.
*/
set dir(direction: string);
/**
* Returns hidden.
*
* @returns Hidden.
*/
get hidden(): boolean;
/**
* Returns hidden.
*
* @param hidden Hidden.
*/
set hidden(hidden: boolean);
/**
* Returns inert.
*
* @returns Inert.
*/
get inert(): boolean;
/**
* Returns inert.
*
* @param inert Inert.
*/
set inert(inert: boolean);
/**
* Returns language.
*
* @returns Language.
*/
get lang(): string;
/**
* Returns language.
*
* @param language Language.
*/
set lang(lang: string);
/**
* Returns title.
*
* @returns Title.
*/
get title(): string;
/**
* Returns title.
*
* @param title Title.
*/
set title(title: string);
/**
* Returns popover.
*
* @returns Popover.
*/
get popover(): string | null;
/**
* Sets popover.
*
* @param value Value.
*/
set popover(value: string | null);
/**
* Triggers a click event.
*/
click(): void;
/**
* Triggers a blur event.
*/
blur(): void;
/**
* Triggers a focus event.
*/
focus(): void;
/**
* @override
*/
[PropertySymbol.cloneNode](deep?: boolean): HTMLElement;
/**
* @override
* @see https://html.spec.whatwg.org/multipage/dom.html#htmlelement
*/
[PropertySymbol.connectedToNode](): void;
/**
* @override
*/
[PropertySymbol.disconnectedFromNode](): void;
/**
* @override
*/
[PropertySymbol.onSetAttribute](attribute: Attr, replacedAttribute: Attr | null): void;
/**
* @override
*/
[PropertySymbol.onRemoveAttribute](removedAttribute: Attr): void;
}
//# sourceMappingURL=HTMLElement.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,930 @@
import Element from '../element/Element.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import CSSStyleDeclaration from '../../css/declaration/CSSStyleDeclaration.js';
import PointerEvent from '../../event/events/PointerEvent.js';
import NodeTypeEnum from '../node/NodeTypeEnum.js';
import HTMLElementUtility from './HTMLElementUtility.js';
import DOMStringMap from '../../dom/DOMStringMap.js';
import ElementEventAttributeUtility from '../element/ElementEventAttributeUtility.js';
/**
* HTML Element.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.
*/
export default class HTMLElement extends Element {
static observedAttributes;
// Internal properties
[PropertySymbol.accessKey] = '';
[PropertySymbol.offsetHeight] = 0;
[PropertySymbol.offsetWidth] = 0;
[PropertySymbol.offsetLeft] = 0;
[PropertySymbol.offsetTop] = 0;
[PropertySymbol.clientHeight] = 0;
[PropertySymbol.clientWidth] = 0;
[PropertySymbol.clientLeft] = 0;
[PropertySymbol.clientTop] = 0;
[PropertySymbol.style] = null;
[PropertySymbol.dataset] = null;
// Private properties
#customElementDefineCallback = null;
// Events
/* eslint-disable jsdoc/require-jsdoc */
get oncancel() {
return ElementEventAttributeUtility.getEventListener(this, 'oncancel');
}
set oncancel(value) {
this[PropertySymbol.propertyEventListeners].set('oncancel', value);
}
get onerror() {
return ElementEventAttributeUtility.getEventListener(this, 'onerror');
}
set onerror(value) {
this[PropertySymbol.propertyEventListeners].set('onerror', value);
}
get onscroll() {
return ElementEventAttributeUtility.getEventListener(this, 'onscroll');
}
set onscroll(value) {
this[PropertySymbol.propertyEventListeners].set('onscroll', value);
}
get onselect() {
return ElementEventAttributeUtility.getEventListener(this, 'onselect');
}
set onselect(value) {
this[PropertySymbol.propertyEventListeners].set('onselect', value);
}
get onwheel() {
return ElementEventAttributeUtility.getEventListener(this, 'onwheel');
}
set onwheel(value) {
this[PropertySymbol.propertyEventListeners].set('onwheel', value);
}
get oncopy() {
return ElementEventAttributeUtility.getEventListener(this, 'oncopy');
}
set oncopy(value) {
this[PropertySymbol.propertyEventListeners].set('oncopy', value);
}
get oncut() {
return ElementEventAttributeUtility.getEventListener(this, 'oncut');
}
set oncut(value) {
this[PropertySymbol.propertyEventListeners].set('oncut', value);
}
get onpaste() {
return ElementEventAttributeUtility.getEventListener(this, 'onpaste');
}
set onpaste(value) {
this[PropertySymbol.propertyEventListeners].set('onpaste', value);
}
get oncompositionend() {
return ElementEventAttributeUtility.getEventListener(this, 'oncompositionend');
}
set oncompositionend(value) {
this[PropertySymbol.propertyEventListeners].set('oncompositionend', value);
}
get oncompositionstart() {
return ElementEventAttributeUtility.getEventListener(this, 'oncompositionstart');
}
set oncompositionstart(value) {
this[PropertySymbol.propertyEventListeners].set('oncompositionstart', value);
}
get oncompositionupdate() {
return ElementEventAttributeUtility.getEventListener(this, 'oncompositionupdate');
}
set oncompositionupdate(value) {
this[PropertySymbol.propertyEventListeners].set('oncompositionupdate', value);
}
get onblur() {
return ElementEventAttributeUtility.getEventListener(this, 'onblur');
}
set onblur(value) {
this[PropertySymbol.propertyEventListeners].set('onblur', value);
}
get onfocus() {
return ElementEventAttributeUtility.getEventListener(this, 'onfocus');
}
set onfocus(value) {
this[PropertySymbol.propertyEventListeners].set('onfocus', value);
}
get onfocusin() {
return ElementEventAttributeUtility.getEventListener(this, 'onfocusin');
}
set onfocusin(value) {
this[PropertySymbol.propertyEventListeners].set('onfocusin', value);
}
get onfocusout() {
return ElementEventAttributeUtility.getEventListener(this, 'onfocusout');
}
set onfocusout(value) {
this[PropertySymbol.propertyEventListeners].set('onfocusout', value);
}
get onkeydown() {
return ElementEventAttributeUtility.getEventListener(this, 'onkeydown');
}
set onkeydown(value) {
this[PropertySymbol.propertyEventListeners].set('onkeydown', value);
}
get onkeyup() {
return ElementEventAttributeUtility.getEventListener(this, 'onkeyup');
}
set onkeyup(value) {
this[PropertySymbol.propertyEventListeners].set('onkeyup', value);
}
get onauxclick() {
return ElementEventAttributeUtility.getEventListener(this, 'onauxclick');
}
set onauxclick(value) {
this[PropertySymbol.propertyEventListeners].set('onauxclick', value);
}
get onclick() {
return ElementEventAttributeUtility.getEventListener(this, 'onclick');
}
set onclick(value) {
this[PropertySymbol.propertyEventListeners].set('onclick', value);
}
get oncontextmenu() {
return ElementEventAttributeUtility.getEventListener(this, 'oncontextmenu');
}
set oncontextmenu(value) {
this[PropertySymbol.propertyEventListeners].set('oncontextmenu', value);
}
get ondblclick() {
return ElementEventAttributeUtility.getEventListener(this, 'ondblclick');
}
set ondblclick(value) {
this[PropertySymbol.propertyEventListeners].set('ondblclick', value);
}
get onmousedown() {
return ElementEventAttributeUtility.getEventListener(this, 'onmousedown');
}
set onmousedown(value) {
this[PropertySymbol.propertyEventListeners].set('onmousedown', value);
}
get onmouseenter() {
return ElementEventAttributeUtility.getEventListener(this, 'onmouseenter');
}
set onmouseenter(value) {
this[PropertySymbol.propertyEventListeners].set('onmouseenter', value);
}
get onmouseleave() {
return ElementEventAttributeUtility.getEventListener(this, 'onmouseleave');
}
set onmouseleave(value) {
this[PropertySymbol.propertyEventListeners].set('onmouseleave', value);
}
get onmousemove() {
return ElementEventAttributeUtility.getEventListener(this, 'onmousemove');
}
set onmousemove(value) {
this[PropertySymbol.propertyEventListeners].set('onmousemove', value);
}
get onmouseout() {
return ElementEventAttributeUtility.getEventListener(this, 'onmouseout');
}
set onmouseout(value) {
this[PropertySymbol.propertyEventListeners].set('onmouseout', value);
}
get onmouseover() {
return ElementEventAttributeUtility.getEventListener(this, 'onmouseover');
}
set onmouseover(value) {
this[PropertySymbol.propertyEventListeners].set('onmouseover', value);
}
get onmouseup() {
return ElementEventAttributeUtility.getEventListener(this, 'onmouseup');
}
set onmouseup(value) {
this[PropertySymbol.propertyEventListeners].set('onmouseup', value);
}
get ontouchcancel() {
return ElementEventAttributeUtility.getEventListener(this, 'ontouchcancel');
}
set ontouchcancel(value) {
this[PropertySymbol.propertyEventListeners].set('ontouchcancel', value);
}
get ontouchend() {
return ElementEventAttributeUtility.getEventListener(this, 'ontouchend');
}
set ontouchend(value) {
this[PropertySymbol.propertyEventListeners].set('ontouchend', value);
}
get ontouchmove() {
return ElementEventAttributeUtility.getEventListener(this, 'ontouchmove');
}
set ontouchmove(value) {
this[PropertySymbol.propertyEventListeners].set('ontouchmove', value);
}
get ontouchstart() {
return ElementEventAttributeUtility.getEventListener(this, 'ontouchstart');
}
set ontouchstart(value) {
this[PropertySymbol.propertyEventListeners].set('ontouchstart', value);
}
get oninvalid() {
return ElementEventAttributeUtility.getEventListener(this, 'oninvalid');
}
set oninvalid(value) {
this[PropertySymbol.propertyEventListeners].set('oninvalid', value);
}
get onanimationcancel() {
return ElementEventAttributeUtility.getEventListener(this, 'onanimationcancel');
}
set onanimationcancel(value) {
this[PropertySymbol.propertyEventListeners].set('onanimationcancel', value);
}
get onanimationend() {
return ElementEventAttributeUtility.getEventListener(this, 'onanimationend');
}
set onanimationend(value) {
this[PropertySymbol.propertyEventListeners].set('onanimationend', value);
}
get onanimationiteration() {
return ElementEventAttributeUtility.getEventListener(this, 'onanimationiteration');
}
set onanimationiteration(value) {
this[PropertySymbol.propertyEventListeners].set('onanimationiteration', value);
}
get onanimationstart() {
return ElementEventAttributeUtility.getEventListener(this, 'onanimationstart');
}
set onanimationstart(value) {
this[PropertySymbol.propertyEventListeners].set('onanimationstart', value);
}
get onbeforeinput() {
return ElementEventAttributeUtility.getEventListener(this, 'onbeforeinput');
}
set onbeforeinput(value) {
this[PropertySymbol.propertyEventListeners].set('onbeforeinput', value);
}
get oninput() {
return ElementEventAttributeUtility.getEventListener(this, 'oninput');
}
set oninput(value) {
this[PropertySymbol.propertyEventListeners].set('oninput', value);
}
get onchange() {
return ElementEventAttributeUtility.getEventListener(this, 'onchange');
}
set onchange(value) {
this[PropertySymbol.propertyEventListeners].set('onchange', value);
}
get ongotpointercapture() {
return ElementEventAttributeUtility.getEventListener(this, 'ongotpointercapture');
}
set ongotpointercapture(value) {
this[PropertySymbol.propertyEventListeners].set('ongotpointercapture', value);
}
get onlostpointercapture() {
return ElementEventAttributeUtility.getEventListener(this, 'onlostpointercapture');
}
set onlostpointercapture(value) {
this[PropertySymbol.propertyEventListeners].set('onlostpointercapture', value);
}
get onpointercancel() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointercancel');
}
set onpointercancel(value) {
this[PropertySymbol.propertyEventListeners].set('onpointercancel', value);
}
get onpointerdown() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointerdown');
}
set onpointerdown(value) {
this[PropertySymbol.propertyEventListeners].set('onpointerdown', value);
}
get onpointerenter() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointerenter');
}
set onpointerenter(value) {
this[PropertySymbol.propertyEventListeners].set('onpointerenter', value);
}
get onpointerleave() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointerleave');
}
set onpointerleave(value) {
this[PropertySymbol.propertyEventListeners].set('onpointerleave', value);
}
get onpointermove() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointermove');
}
set onpointermove(value) {
this[PropertySymbol.propertyEventListeners].set('onpointermove', value);
}
get onpointerout() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointerout');
}
set onpointerout(value) {
this[PropertySymbol.propertyEventListeners].set('onpointerout', value);
}
get onpointerover() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointerover');
}
set onpointerover(value) {
this[PropertySymbol.propertyEventListeners].set('onpointerover', value);
}
get onpointerup() {
return ElementEventAttributeUtility.getEventListener(this, 'onpointerup');
}
set onpointerup(value) {
this[PropertySymbol.propertyEventListeners].set('onpointerup', value);
}
get ontransitioncancel() {
return ElementEventAttributeUtility.getEventListener(this, 'ontransitioncancel');
}
set ontransitioncancel(value) {
this[PropertySymbol.propertyEventListeners].set('ontransitioncancel', value);
}
get ontransitionend() {
return ElementEventAttributeUtility.getEventListener(this, 'ontransitionend');
}
set ontransitionend(value) {
this[PropertySymbol.propertyEventListeners].set('ontransitionend', value);
}
get ontransitionrun() {
return ElementEventAttributeUtility.getEventListener(this, 'ontransitionrun');
}
set ontransitionrun(value) {
this[PropertySymbol.propertyEventListeners].set('ontransitionrun', value);
}
get ontransitionstart() {
return ElementEventAttributeUtility.getEventListener(this, 'ontransitionstart');
}
set ontransitionstart(value) {
this[PropertySymbol.propertyEventListeners].set('ontransitionstart', value);
}
/* eslint-enable jsdoc/require-jsdoc */
/**
* Returns access key.
*
* @returns Access key.
*/
get accessKey() {
return this[PropertySymbol.accessKey];
}
/**
* Sets access key.
*
* @param accessKey Access key.
*/
set accessKey(accessKey) {
this[PropertySymbol.accessKey] = accessKey;
}
/**
* Returns content editable.
*
* @returns Content editable.
*/
get contentEditable() {
const contentEditable = String(this.getAttribute('contentEditable')).toLowerCase();
switch (contentEditable) {
case 'false':
case 'true':
case 'plaintext-only':
return contentEditable;
default:
return 'inherit';
}
}
/**
* Sets content editable.
*
* @param contentEditable Content editable.
*/
set contentEditable(contentEditable) {
contentEditable = String(contentEditable).toLowerCase();
switch (contentEditable) {
case 'false':
case 'true':
case 'plaintext-only':
case 'inherit':
this.setAttribute('contentEditable', contentEditable);
break;
default:
throw new this[PropertySymbol.window].SyntaxError(`Failed to set the 'contentEditable' property on 'HTMLElement': The value provided ('${contentEditable}') is not one of 'true', 'false', 'plaintext-only', or 'inherit'.`);
}
}
/**
* Returns is content editable.
*
* @returns Is content editable.
*/
get isContentEditable() {
const contentEditable = this.contentEditable;
if (contentEditable === 'true' || contentEditable === 'plaintext-only') {
return true;
}
if (contentEditable === 'inherit') {
return this[PropertySymbol.parentNode]?.isContentEditable ?? false;
}
return false;
}
/**
* Returns offset height.
*
* @returns Offset height.
*/
get offsetHeight() {
return this[PropertySymbol.offsetHeight];
}
/**
* Returns offset width.
*
* @returns Offset width.
*/
get offsetWidth() {
return this[PropertySymbol.offsetWidth];
}
/**
* Returns offset left.
*
* @returns Offset left.
*/
get offsetLeft() {
return this[PropertySymbol.offsetLeft];
}
/**
* Returns offset top.
*
* @returns Offset top.
*/
get offsetTop() {
return this[PropertySymbol.offsetTop];
}
/**
* Returns client height.
*
* @returns Client height.
*/
get clientHeight() {
return this[PropertySymbol.clientHeight];
}
/**
* Returns client width.
*
* @returns Client width.
*/
get clientWidth() {
return this[PropertySymbol.clientWidth];
}
/**
* Returns client left.
*
* @returns Client left.
*/
get clientLeft() {
return this[PropertySymbol.clientLeft];
}
/**
* Returns client top.
*
* @returns Client top.
*/
get clientTop() {
return this[PropertySymbol.clientTop];
}
/**
* Returns tab index.
*
* @returns Tab index.
*/
get tabIndex() {
const tabIndex = this.getAttribute('tabindex');
if (tabIndex !== null) {
const parsed = Number(tabIndex);
return isNaN(parsed) ? -1 : parsed;
}
return -1;
}
/**
* Returns tab index.
*
* @param tabIndex Tab index.
*/
set tabIndex(tabIndex) {
const parsed = Number(tabIndex);
if (isNaN(parsed)) {
this.setAttribute('tabindex', '0');
}
else {
this.setAttribute('tabindex', String(parsed));
}
}
/**
* Returns inner text, which is the rendered appearance of text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @returns Inner text.
*/
get innerText() {
if (!this[PropertySymbol.isConnected]) {
return this.textContent;
}
let result = '';
for (const childNode of this[PropertySymbol.nodeArray]) {
if (childNode[PropertySymbol.nodeType] === NodeTypeEnum.elementNode) {
const childElement = childNode;
const computedStyle = this[PropertySymbol.window].getComputedStyle(childElement);
if (childElement[PropertySymbol.tagName] !== 'SCRIPT' &&
childElement[PropertySymbol.tagName] !== 'STYLE' &&
childElement[PropertySymbol.tagName] !== 'svg') {
const display = computedStyle.display;
if (display !== 'none') {
const textTransform = computedStyle.textTransform;
const innerText = childElement.innerText;
// Only add newline if it's a block/flex element and there's more content coming after
if ((display === 'block' || display === 'flex') && result && innerText) {
result += '\n';
}
let text = innerText;
switch (textTransform) {
case 'uppercase':
text = text.toUpperCase();
break;
case 'lowercase':
text = text.toLowerCase();
break;
case 'capitalize':
text = text.replace(/(^|\s)\S/g, (l) => l.toUpperCase());
break;
}
result += text;
}
}
}
else if (childNode[PropertySymbol.nodeType] === NodeTypeEnum.textNode) {
result += childNode.textContent.replace(/[\n\r]/, '');
}
}
return result;
}
/**
* Sets the inner text, which is the rendered appearance of text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @param innerText Inner text.
*/
set innerText(text) {
const childNodes = this[PropertySymbol.nodeArray];
while (childNodes.length) {
this.removeChild(childNodes[0]);
}
const texts = text.split(/[\n\r]/);
const ownerDocument = this[PropertySymbol.ownerDocument];
for (let i = 0, max = texts.length; i < max; i++) {
if (i !== 0) {
this.appendChild(ownerDocument.createElement('br'));
}
this.appendChild(ownerDocument.createTextNode(texts[i]));
}
}
/**
* Returns outer text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @returns HTML.
*/
get outerText() {
return this.innerText;
}
/**
* Sets outer text.
*
* @see https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute
* @param text Text.
*/
set outerText(text) {
if (!this[PropertySymbol.parentNode]) {
throw new this[PropertySymbol.window].DOMException("Failed to set the 'outerHTML' property on 'Element': This element has no parent node.");
}
const texts = text.split(/[\n\r]/);
for (let i = 0, max = texts.length; i < max; i++) {
if (i !== 0) {
this[PropertySymbol.parentNode].insertBefore(this[PropertySymbol.ownerDocument].createElement('br'), this);
}
this[PropertySymbol.parentNode].insertBefore(this[PropertySymbol.ownerDocument].createTextNode(texts[i]), this);
}
this[PropertySymbol.parentNode].removeChild(this);
}
/**
* Returns style.
*
* @returns Style.
*/
get style() {
if (!this[PropertySymbol.style]) {
this[PropertySymbol.style] = new CSSStyleDeclaration(PropertySymbol.illegalConstructor, this[PropertySymbol.window], { element: this });
}
return this[PropertySymbol.style];
}
/**
* Sets style.
*
* @param cssText Style as text.
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style#setting_styles
*/
set style(cssText) {
this.style.cssText = typeof cssText === 'string' ? cssText : '';
}
/**
* Returns data set.
*
* @returns Data set.
*/
get dataset() {
return (this[PropertySymbol.dataset] ??= new DOMStringMap(PropertySymbol.illegalConstructor, this));
}
/**
* Returns direction.
*
* @returns Direction.
*/
get dir() {
return this.getAttribute('dir') || '';
}
/**
* Returns direction.
*
* @param direction Direction.
*/
set dir(direction) {
this.setAttribute('dir', direction);
}
/**
* Returns hidden.
*
* @returns Hidden.
*/
get hidden() {
return this.getAttribute('hidden') !== null;
}
/**
* Returns hidden.
*
* @param hidden Hidden.
*/
set hidden(hidden) {
if (!hidden) {
this.removeAttribute('hidden');
}
else {
this.setAttribute('hidden', '');
}
}
/**
* Returns inert.
*
* @returns Inert.
*/
get inert() {
return this.getAttribute('inert') !== null;
}
/**
* Returns inert.
*
* @param inert Inert.
*/
set inert(inert) {
if (!inert) {
this.removeAttribute('inert');
}
else {
this.setAttribute('inert', '');
}
}
/**
* Returns language.
*
* @returns Language.
*/
get lang() {
return this.getAttribute('lang') || '';
}
/**
* Returns language.
*
* @param language Language.
*/
set lang(lang) {
this.setAttribute('lang', lang);
}
/**
* Returns title.
*
* @returns Title.
*/
get title() {
return this.getAttribute('title') || '';
}
/**
* Returns title.
*
* @param title Title.
*/
set title(title) {
this.setAttribute('title', title);
}
/**
* Returns popover.
*
* @returns Popover.
*/
get popover() {
const value = this.getAttribute('popover');
switch (value) {
case null:
return null;
case '':
case 'auto':
return 'auto';
case 'manual':
return 'manual';
default:
return 'manual';
}
}
/**
* Sets popover.
*
* @param value Value.
*/
set popover(value) {
if (value === null) {
this.removeAttribute('popover');
return;
}
this.setAttribute('popover', value);
}
/**
* Triggers a click event.
*/
click() {
this.dispatchEvent(new PointerEvent('click', {
bubbles: true,
composed: true,
cancelable: true
}));
}
/**
* Triggers a blur event.
*/
blur() {
HTMLElementUtility.blur(this);
}
/**
* Triggers a focus event.
*/
focus() {
HTMLElementUtility.focus(this);
}
/**
* @override
*/
[PropertySymbol.cloneNode](deep = false) {
const clone = super[PropertySymbol.cloneNode](deep);
clone[PropertySymbol.accessKey] = this[PropertySymbol.accessKey];
return clone;
}
/**
* @override
* @see https://html.spec.whatwg.org/multipage/dom.html#htmlelement
*/
[PropertySymbol.connectedToNode]() {
const window = this[PropertySymbol.window];
const localName = this[PropertySymbol.localName];
const allCallbacks = window.customElements[PropertySymbol.callbacks];
// This element can potentially be a custom element that has not been defined yet
// Therefore we need to register a callback for when it is defined in CustomElementRegistry and replace it with the registered element (see #404)
if (this.constructor === window.HTMLElement && localName.includes('-') && allCallbacks) {
if (!this.#customElementDefineCallback) {
const callback = this.#onCustomElementConnected.bind(this);
const callbacks = allCallbacks.get(localName);
if (callbacks) {
callbacks.unshift(callback);
}
else {
allCallbacks.set(localName, [callback]);
}
this.#customElementDefineCallback = callback;
}
}
super[PropertySymbol.connectedToNode]();
}
/**
* @override
*/
[PropertySymbol.disconnectedFromNode]() {
const window = this[PropertySymbol.window];
const localName = this[PropertySymbol.localName];
const allCallbacks = window.customElements[PropertySymbol.callbacks];
// This element can potentially be a custom element that has not been defined yet
// Therefore we need to register a callback for when it is defined in CustomElementRegistry and replace it with the registered element (see #404)
if (this.constructor === window.HTMLElement && localName.includes('-') && allCallbacks) {
const callbacks = allCallbacks.get(localName);
if (callbacks && this.#customElementDefineCallback) {
const index = callbacks.indexOf(this.#customElementDefineCallback);
if (index !== -1) {
callbacks.splice(index, 1);
}
if (!callbacks.length) {
allCallbacks.delete(localName);
}
this.#customElementDefineCallback = null;
}
}
super[PropertySymbol.disconnectedFromNode]();
}
/**
* @override
*/
[PropertySymbol.onSetAttribute](attribute, replacedAttribute) {
super[PropertySymbol.onSetAttribute](attribute, replacedAttribute);
this[PropertySymbol.window][PropertySymbol.customElementReactionStack].enqueueReaction(this, 'attributeChangedCallback', [attribute.name, replacedAttribute?.value ?? null, attribute.value]);
}
/**
* @override
*/
[PropertySymbol.onRemoveAttribute](removedAttribute) {
super[PropertySymbol.onRemoveAttribute](removedAttribute);
this[PropertySymbol.window][PropertySymbol.customElementReactionStack].enqueueReaction(this, 'attributeChangedCallback', [removedAttribute.name, removedAttribute.value, null]);
}
/**
* Triggered when a custom element is connected to the DOM.
*/
#onCustomElementConnected() {
if (!this[PropertySymbol.parentNode]) {
return;
}
const window = this[PropertySymbol.window];
const localName = this[PropertySymbol.localName];
const newElement = this[PropertySymbol.ownerDocument].createElement(localName);
const newCache = newElement[PropertySymbol.cache];
newElement[PropertySymbol.nodeArray] = this[PropertySymbol.nodeArray];
newElement[PropertySymbol.elementArray] = this[PropertySymbol.elementArray];
newElement[PropertySymbol.childNodes] = null;
newElement[PropertySymbol.children] = null;
newElement[PropertySymbol.isConnected] = this[PropertySymbol.isConnected];
newElement[PropertySymbol.rootNode] = this[PropertySymbol.rootNode];
newElement[PropertySymbol.formNode] = this[PropertySymbol.formNode];
newElement[PropertySymbol.parentNode] = this[PropertySymbol.parentNode];
newElement[PropertySymbol.selectNode] = this[PropertySymbol.selectNode];
newElement[PropertySymbol.textAreaNode] = this[PropertySymbol.textAreaNode];
newElement[PropertySymbol.mutationListeners] = this[PropertySymbol.mutationListeners];
newElement[PropertySymbol.isValue] = this[PropertySymbol.isValue];
newElement[PropertySymbol.cache] = this[PropertySymbol.cache];
newElement[PropertySymbol.affectsCache] = this[PropertySymbol.affectsCache];
newElement[PropertySymbol.attributes][PropertySymbol.itemsByNamespaceURI] =
this[PropertySymbol.attributes][PropertySymbol.itemsByNamespaceURI];
newElement[PropertySymbol.attributes][PropertySymbol.itemsByName] =
this[PropertySymbol.attributes][PropertySymbol.itemsByName];
newElement[PropertySymbol.attributes][PropertySymbol.items] =
this[PropertySymbol.attributes][PropertySymbol.items];
for (const attr of newElement[PropertySymbol.attributes][PropertySymbol.items].values()) {
attr[PropertySymbol.ownerElement] = newElement;
}
this[PropertySymbol.clearCache]();
this[PropertySymbol.nodeArray] = [];
this[PropertySymbol.elementArray] = [];
this[PropertySymbol.childNodes] = null;
this[PropertySymbol.children] = null;
this[PropertySymbol.parentNode] = null;
this[PropertySymbol.rootNode] = null;
this[PropertySymbol.formNode] = null;
this[PropertySymbol.selectNode] = null;
this[PropertySymbol.textAreaNode] = null;
this[PropertySymbol.mutationListeners] = [];
this[PropertySymbol.isValue] = null;
this[PropertySymbol.cache] = newCache;
this[PropertySymbol.affectsCache] = [];
this[PropertySymbol.attributes][PropertySymbol.itemsByNamespaceURI] = new Map();
this[PropertySymbol.attributes][PropertySymbol.itemsByName] = new Map();
this[PropertySymbol.attributes][PropertySymbol.items] = new Map();
for (const node of newElement[PropertySymbol.nodeArray]) {
node[PropertySymbol.parentNode] = newElement;
}
const parentChildNodes = newElement[PropertySymbol.parentNode][PropertySymbol.nodeArray];
const parentChildElements = newElement[PropertySymbol.parentNode][PropertySymbol.elementArray];
parentChildNodes[parentChildNodes.indexOf(this)] = newElement;
parentChildElements[parentChildElements.indexOf(this)] = newElement;
const allCallbacks = window.customElements[PropertySymbol.callbacks];
const callbacks = allCallbacks.get(localName);
if (callbacks && this.#customElementDefineCallback) {
const index = callbacks.indexOf(this.#customElementDefineCallback);
if (index !== -1) {
callbacks.splice(index, 1);
}
if (!callbacks.length) {
allCallbacks.delete(localName);
}
this.#customElementDefineCallback = null;
}
if (newElement[PropertySymbol.isConnected]) {
if (newElement[PropertySymbol.shadowRoot]) {
newElement[PropertySymbol.shadowRoot][PropertySymbol.isConnected] = true;
}
newElement[PropertySymbol.connectedToDocument]();
}
}
}
//# sourceMappingURL=HTMLElement.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
import HTMLElement from '../html-element/HTMLElement.js';
import SVGElement from '../svg-element/SVGElement.js';
/**
* HTMLElement utility.
*/
export default class HTMLElementUtility {
/**
* Triggers a blur event.
*
* @param element Element.
*/
static blur(element: HTMLElement | SVGElement): void;
/**
* Triggers a focus event.
*
* @param element Element.
*/
static focus(element: HTMLElement | SVGElement): void;
}
//# sourceMappingURL=HTMLElementUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLElementUtility.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-element/HTMLElementUtility.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,MAAM,gCAAgC,CAAC;AACzD,OAAO,UAAU,MAAM,8BAA8B,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACtC;;;;OAIG;WACW,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,GAAG,IAAI;IAoC3D;;;;OAIG;WACW,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,GAAG,IAAI;CA2C5D"}

View File

@@ -0,0 +1,71 @@
import FocusEvent from '../../event/events/FocusEvent.js';
import * as PropertySymbol from '../../PropertySymbol.js';
/**
* HTMLElement utility.
*/
export default class HTMLElementUtility {
/**
* Triggers a blur event.
*
* @param element Element.
*/
static blur(element) {
const target = element[PropertySymbol.proxy] || element;
const document = target[PropertySymbol.ownerDocument];
if (document[PropertySymbol.activeElement] !== target ||
!target[PropertySymbol.isConnected] ||
target.disabled) {
return;
}
const relatedTarget = document[PropertySymbol.nextActiveElement] ?? null;
document[PropertySymbol.activeElement] = null;
document[PropertySymbol.clearCache]();
target.dispatchEvent(new FocusEvent('blur', {
relatedTarget,
bubbles: false,
composed: true,
cancelable: true
}));
target.dispatchEvent(new FocusEvent('focusout', {
relatedTarget,
bubbles: true,
composed: true,
cancelable: true
}));
}
/**
* Triggers a focus event.
*
* @param element Element.
*/
static focus(element) {
const target = element[PropertySymbol.proxy] || element;
const document = target[PropertySymbol.ownerDocument];
if (document[PropertySymbol.activeElement] === target ||
!target[PropertySymbol.isConnected] ||
target.disabled) {
return;
}
// Set the next active element so `blur` can use it for `relatedTarget`.
document[PropertySymbol.nextActiveElement] = target;
const relatedTarget = document[PropertySymbol.activeElement];
if (document[PropertySymbol.activeElement] !== null) {
document[PropertySymbol.activeElement].blur();
}
// Clean up after blur, so it does not affect next blur call.
document[PropertySymbol.nextActiveElement] = null;
document[PropertySymbol.activeElement] = target;
document[PropertySymbol.clearCache]();
target.dispatchEvent(new FocusEvent('focus', {
relatedTarget,
bubbles: false,
composed: true
}));
target.dispatchEvent(new FocusEvent('focusin', {
relatedTarget,
bubbles: true,
composed: true
}));
}
}
//# sourceMappingURL=HTMLElementUtility.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLElementUtility.js","sourceRoot":"","sources":["../../../src/nodes/html-element/HTMLElementUtility.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,kCAAkC,CAAC;AAC1D,OAAO,KAAK,cAAc,MAAM,yBAAyB,CAAC;AAI1D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACtC;;;;OAIG;IACI,MAAM,CAAC,IAAI,CAAC,OAAiC;QACnD,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC;QACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAEtD,IACC,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,MAAM;YACjD,CAAC,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC;YACnC,MAAM,CAAC,QAAQ,EACd,CAAC;YACF,OAAO;QACR,CAAC;QAED,MAAM,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC;QAEzE,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;QAE9C,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAEtC,MAAM,CAAC,aAAa,CACnB,IAAI,UAAU,CAAC,MAAM,EAAE;YACtB,aAAa;YACb,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;SAChB,CAAC,CACF,CAAC;QACF,MAAM,CAAC,aAAa,CACnB,IAAI,UAAU,CAAC,UAAU,EAAE;YAC1B,aAAa;YACb,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,UAAU,EAAE,IAAI;SAChB,CAAC,CACF,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,KAAK,CAAC,OAAiC;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC;QACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAEtD,IACC,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,MAAM;YACjD,CAAC,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC;YACnC,MAAM,CAAC,QAAQ,EACd,CAAC;YACF,OAAO;QACR,CAAC;QAED,wEAAwE;QACxE,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC;QAEpD,MAAM,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAE7D,IAAI,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAAC;YACrD,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,CAAC;QAED,6DAA6D;QAC7D,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;QAElD,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC;QAEhD,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;QAEtC,MAAM,CAAC,aAAa,CACnB,IAAI,UAAU,CAAC,OAAO,EAAE;YACvB,aAAa;YACb,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,IAAI;SACd,CAAC,CACF,CAAC;QACF,MAAM,CAAC,aAAa,CACnB,IAAI,UAAU,CAAC,SAAS,EAAE;YACzB,aAAa;YACb,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;SACd,CAAC,CACF,CAAC;IACH,CAAC;CACD"}