Refactor language menu and dom utils (#32450)

1. Make `queryElem*` functions could correctly handle TS types
2. Remove some legacy jQuery $ calls (introduce fomanticQuery for Fomantic UI only)
3. Fix some TS typing problems
This commit is contained in:
wxiaoguang 2024-11-08 14:04:24 +08:00 committed by GitHub
parent 35bcd667b2
commit 0f397ae09b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 70 additions and 62 deletions

View file

@ -5,7 +5,7 @@ import type $ from 'jquery';
type ElementArg = Element | string | NodeListOf<Element> | Array<Element> | ReturnType<typeof $>;
type ElementsCallback = (el: Element) => Promisable<any>;
type ElementsCallbackWithArgs = (el: Element, ...args: any[]) => Promisable<any>;
type IterableElements = NodeListOf<Element> | Array<Element>;
type ArrayLikeIterable<T> = ArrayLike<T> & Iterable<T>; // for NodeListOf and Array
function elementsCall(el: ElementArg, func: ElementsCallbackWithArgs, ...args: any[]) {
if (typeof el === 'string' || el instanceof String) {
@ -15,7 +15,7 @@ function elementsCall(el: ElementArg, func: ElementsCallbackWithArgs, ...args: a
func(el, ...args);
} else if (el.length !== undefined) {
// this works for: NodeList, HTMLCollection, Array, jQuery
for (const e of (el as IterableElements)) {
for (const e of (el as ArrayLikeIterable<Element>)) {
func(e, ...args);
}
} else {
@ -58,7 +58,7 @@ export function isElemHidden(el: ElementArg) {
return res[0];
}
function applyElemsCallback(elems: IterableElements, fn?: ElementsCallback) {
function applyElemsCallback<T extends Element>(elems: ArrayLikeIterable<T>, fn?: ElementsCallback): ArrayLikeIterable<T> {
if (fn) {
for (const el of elems) {
fn(el);
@ -67,19 +67,22 @@ function applyElemsCallback(elems: IterableElements, fn?: ElementsCallback) {
return elems;
}
export function queryElemSiblings(el: Element, selector = '*', fn?: ElementsCallback) {
return applyElemsCallback(Array.from(el.parentNode.children).filter((child: Element) => {
export function queryElemSiblings<T extends Element>(el: Element, selector = '*', fn?: ElementsCallback): ArrayLikeIterable<T> {
const elems = Array.from(el.parentNode.children) as T[];
return applyElemsCallback<T>(elems.filter((child: Element) => {
return child !== el && child.matches(selector);
}), fn);
}
// it works like jQuery.children: only the direct children are selected
export function queryElemChildren(parent: Element | ParentNode, selector = '*', fn?: ElementsCallback) {
return applyElemsCallback(parent.querySelectorAll(`:scope > ${selector}`), fn);
export function queryElemChildren<T extends Element>(parent: Element | ParentNode, selector = '*', fn?: ElementsCallback): ArrayLikeIterable<T> {
return applyElemsCallback<T>(parent.querySelectorAll(`:scope > ${selector}`), fn);
}
export function queryElems(selector: string, fn?: ElementsCallback) {
return applyElemsCallback(document.querySelectorAll(selector), fn);
// it works like parent.querySelectorAll: all descendants are selected
// in the future, all "queryElems(document, ...)" should be refactored to use a more specific parent
export function queryElems<T extends Element>(parent: Element | ParentNode, selector: string, fn?: ElementsCallback): ArrayLikeIterable<T> {
return applyElemsCallback<T>(parent.querySelectorAll(selector), fn);
}
export function onDomReady(cb: () => Promisable<void>) {
@ -92,7 +95,7 @@ export function onDomReady(cb: () => Promisable<void>) {
// checks whether an element is owned by the current document, and whether it is a document fragment or element node
// if it is, it means it is a "normal" element managed by us, which can be modified safely.
export function isDocumentFragmentOrElementNode(el: Element | Node) {
export function isDocumentFragmentOrElementNode(el: Node) {
try {
return el.ownerDocument === document && el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
} catch {