refactor(core): [CORE-20] type entry and converge initialization

This commit is contained in:
Harvey Zhao committed 2026-09-11 16:07:06 +08:00
1 parent ccf77c4ed2
commit ee895a558f
100 files changed
+33854 -839

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
interface EventPathSource {
target: EventTarget | null
composedPath?: () => EventTarget[]
}
export function getComposedPath(event: EventPathSource): EventTarget[] {
if (event.composedPath)
return event.composedPath()
const path: EventTarget[] = []
let node = event.target
while (node) {
path.push(node)
node = (node as EventTarget & { parentNode?: EventTarget | null }).parentNode ?? null
}
if (typeof window !== 'undefined' && !path.includes(window))
path.push(window)
return path
}
export function includeFromEvent(event: EventPathSource, target: EventTarget | null | undefined): boolean {
return getComposedPath(event).includes(target as EventTarget)
}
+37
View File
@@ -0,0 +1,37 @@
export function supportsFlex(): boolean {
const div = document.createElement('div')
div.style.display = 'flex'
return div.style.display === 'flex'
}
export function getRect(el: Pick<Element, 'getBoundingClientRect'>): DOMRect {
return el.getBoundingClientRect()
}
export function isInViewport(el: Pick<Element, 'getBoundingClientRect'>, offset = 0): boolean {
const rect = el.getBoundingClientRect()
const windowHeight = window.innerHeight || document.documentElement.clientHeight
const windowWidth = window.innerWidth || document.documentElement.clientWidth
const vertInView = rect.top - offset <= windowHeight && rect.top + rect.height + offset >= 0
const horInView = rect.left - offset <= windowWidth + offset && rect.left + rect.width + offset >= 0
return vertInView && horInView
}
export function getSafeAreaInsets(): { top: number, right: number, bottom: number, left: number } {
const div = document.createElement('div')
div.style.cssText
= 'position:fixed;top:env(safe-area-inset-top,0px);right:env(safe-area-inset-right,0px);bottom:env(safe-area-inset-bottom,0px);left:env(safe-area-inset-left,0px);pointer-events:none;visibility:hidden;'
try {
document.body.appendChild(div)
const style = getComputedStyle(div)
return {
top: Number.parseFloat(style.top) || 0,
right: Number.parseFloat(style.right) || 0,
bottom: Number.parseFloat(style.bottom) || 0,
left: Number.parseFloat(style.left) || 0,
}
}
finally {
div.remove()
}
}
@@ -0,0 +1,18 @@
import { isMobile } from '../utils/compatibility'
import { addClass, append, createElement } from './tree'
export function tooltip(target: Element, msg: string | number, pos = 'top'): void {
if (isMobile)
return
target.setAttribute('aria-label', msg as string)
addClass(target, 'hint--rounded')
addClass(target, `hint--${pos}`)
}
export function getIcon(key = '', html: string | HTMLElement = ''): HTMLElement {
const icon = createElement('i')
addClass(icon, 'art-icon')
addClass(icon, `art-icon-${key}`)
append(icon, html)
return icon
}
+22
View File
@@ -0,0 +1,22 @@
interface StyledElement { style: CSSStyleDeclaration }
export function setStyle<T extends StyledElement>(element: T, key: PropertyKey, value: unknown): T {
// Preserve bracket assignment, including native conversion and thrown setters.
const style = element.style as unknown as Record<PropertyKey, unknown>
style[key] = value
return element
}
export function setStyles<T extends StyledElement>(element: T, styles: object): T {
for (const key in styles)
setStyle(element, key, (styles as Record<string, unknown>)[key])
return element
}
export function getStyle(element: Element, key: string, numberType?: true): number
export function getStyle(element: Element, key: string, numberType: false): string
export function getStyle(element: Element, key: string, numberType: boolean): number | string
export function getStyle(element: Element, key: string, numberType = true): number | string {
const value = window.getComputedStyle(element, null).getPropertyValue(key)
return numberType ? Number.parseFloat(value) : value
}
+54
View File
@@ -0,0 +1,54 @@
export function query<T extends Element = Element>(selector: string, parent: ParentNode = document): T | null {
return parent.querySelector<T>(selector)
}
export function queryAll<T extends Element = Element>(selector: string, parent: ParentNode = document): T[] {
return Array.from(parent.querySelectorAll<T>(selector))
}
export function addClass(target: Pick<Element, 'classList'>, className: string): void {
return target.classList.add(className)
}
export function removeClass(target: Pick<Element, 'classList'>, className: string): void {
return target.classList.remove(className)
}
export function hasClass(target: Pick<Element, 'classList'>, className: string): boolean {
return target.classList.contains(className)
}
export function append(parent: Element, child: unknown): Element | ChildNode | null {
if (child instanceof Element) {
parent.appendChild(child)
}
else {
parent.insertAdjacentHTML('beforeend', String(child))
}
return parent.lastElementChild || parent.lastChild
}
export function remove<T extends Node>(child: T): T {
// Detached nodes retain their historical failure rather than becoming no-ops.
return child.parentNode!.removeChild(child)
}
export function siblings(target: Element): Element[] {
return Array.from(target.parentElement!.children).filter(item => item !== target)
}
export function inverseClass(target: Element, className: string): void {
siblings(target).forEach(item => removeClass(item, className))
addClass(target, className)
}
export function replaceElement<T extends Node>(newChild: T, oldChild: Node): T {
oldChild.parentNode!.replaceChild(newChild, oldChild)
return newChild
}
export function createElement<K extends keyof HTMLElementTagNameMap>(tag: K): HTMLElementTagNameMap[K]
export function createElement(tag: string): HTMLElement
export function createElement(tag: string): HTMLElement {
return document.createElement(tag)
}