mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-22 01:29:04 +01:00
fix type issues in h() (#155600)
- improve regex with named capture groups - drop $ in favor of inline id - add tests Co-authored-by: Henning Dieterichs <notify.henning.dieterichs@live.de> Co-authored-by: Henning Dieterichs <notify.henning.dieterichs@live.de>
This commit is contained in:
@@ -1738,56 +1738,81 @@ type HTMLElementAttributeKeys<T> = Partial<{ [K in keyof T]: T[K] extends Functi
|
||||
type ElementAttributes<T> = HTMLElementAttributeKeys<T> & Record<string, any>;
|
||||
type RemoveHTMLElement<T> = T extends HTMLElement ? never : T;
|
||||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
||||
type ArrayToObj<T extends any[]> = UnionToIntersection<RemoveHTMLElement<T[number]>>;
|
||||
type ArrayToObj<T extends readonly any[]> = UnionToIntersection<RemoveHTMLElement<T[number]>>;
|
||||
type HHTMLElementTagNameMap = HTMLElementTagNameMap & { '': HTMLDivElement };
|
||||
|
||||
type TagToElement<T> = T extends `.${string}`
|
||||
? HTMLDivElement
|
||||
: T extends `#${string}`
|
||||
? HTMLDivElement
|
||||
: T extends `${infer TStart}#${string}`
|
||||
? TStart extends keyof HTMLElementTagNameMap
|
||||
? HTMLElementTagNameMap[TStart]
|
||||
type TagToElement<T> = T extends `${infer TStart}#${string}`
|
||||
? TStart extends keyof HHTMLElementTagNameMap
|
||||
? HHTMLElementTagNameMap[TStart]
|
||||
: HTMLElement
|
||||
: T extends `${infer TStart}.${string}`
|
||||
? TStart extends keyof HTMLElementTagNameMap
|
||||
? HTMLElementTagNameMap[TStart]
|
||||
? TStart extends keyof HHTMLElementTagNameMap
|
||||
? HHTMLElementTagNameMap[TStart]
|
||||
: HTMLElement
|
||||
: T extends keyof HTMLElementTagNameMap
|
||||
? HTMLElementTagNameMap[T]
|
||||
: HTMLElement;
|
||||
|
||||
type TagToElementAndId<TTag> = TTag extends `${infer TTag}@${infer TId}`
|
||||
? { element: TagToElement<TTag>; id: TId }
|
||||
: { element: TagToElement<TTag>; id: 'root' };
|
||||
|
||||
type TagToRecord<TTag> = TagToElementAndId<TTag> extends { element: infer TElement; id: infer TId }
|
||||
? Record<(TId extends string ? TId : never) | 'root', TElement>
|
||||
: never;
|
||||
|
||||
type Child = HTMLElement | string | Record<string, HTMLElement>;
|
||||
type Children = []
|
||||
| [Child]
|
||||
| [Child, Child]
|
||||
| [Child, Child, Child]
|
||||
| [Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]
|
||||
| [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child];
|
||||
|
||||
const H_REGEX = /(?<tag>[\w\-]+)?(?:#(?<id>[\w\-]+))?(?<class>(?:\.(?:[\w\-]+))*)(?:@(?<name>(?:[\w\_])+))?/;
|
||||
|
||||
/**
|
||||
* A helper function to create nested dom nodes.
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* private readonly htmlElements = h('div.code-view', [
|
||||
* h('div.title', { $: 'title' }),
|
||||
* const elements = h('div.code-view', [
|
||||
* h('div.title@title'),
|
||||
* h('div.container', [
|
||||
* h('div.gutter', { $: 'gutterDiv' }),
|
||||
* h('div', { $: 'editor' }),
|
||||
* h('div.gutter@gutterDiv'),
|
||||
* h('div@editor'),
|
||||
* ]),
|
||||
* ]);
|
||||
* private readonly editor = createEditor(this.htmlElements.editor);
|
||||
* const editor = createEditor(elements.editor);
|
||||
* ```
|
||||
*/
|
||||
export function h<TTag extends string>(
|
||||
tag: TTag
|
||||
): (Record<'root', TagToElement<TTag>>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
export function h<TTag extends string, TId extends string>(
|
||||
tag: TTag,
|
||||
attributes: { $: TId } & Partial<ElementAttributes<TagToElement<TTag>>>
|
||||
): Record<TId | 'root', TagToElement<TTag>>;
|
||||
export function h<TTag extends string, T extends (HTMLElement | string | Record<string, HTMLElement>)[]>(
|
||||
tag: TTag,
|
||||
children: T
|
||||
): (ArrayToObj<T> & Record<'root', TagToElement<TTag>>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
export function h<TTag extends string>(tag: TTag, attributes: Partial<ElementAttributes<TagToElement<TTag>>>): Record<'root', TagToElement<TTag>>;
|
||||
export function h<TTag extends string, TId extends string, T extends (HTMLElement | string | Record<string, HTMLElement>)[]>(
|
||||
tag: TTag,
|
||||
attributes: { $: TId } & Partial<ElementAttributes<TagToElement<TTag>>>,
|
||||
children: T
|
||||
): (ArrayToObj<T> & Record<TId, TagToElement<TTag>>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
export function h<TTag extends string>
|
||||
(tag: TTag):
|
||||
TagToRecord<TTag> extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
|
||||
export function h<TTag extends string, T extends Children>
|
||||
(tag: TTag, children: T):
|
||||
(ArrayToObj<T> & TagToRecord<TTag>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
|
||||
export function h<TTag extends string>
|
||||
(tag: TTag, attributes: Partial<ElementAttributes<TagToElement<TTag>>>):
|
||||
TagToRecord<TTag> extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
|
||||
export function h<TTag extends string, T extends Children>
|
||||
(tag: TTag, attributes: Partial<ElementAttributes<TagToElement<TTag>>>, children: T):
|
||||
(ArrayToObj<T> & TagToRecord<TTag>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
|
||||
|
||||
export function h(tag: string, ...args: [] | [attributes: { $: string } & Partial<ElementAttributes<HTMLElement>> | Record<string, any>, children?: any[]] | [children: any[]]): Record<string, HTMLElement> {
|
||||
let attributes: { $?: string } & Partial<ElementAttributes<HTMLElement>>;
|
||||
let children: (Record<string, HTMLElement> | HTMLElement)[] | undefined;
|
||||
@@ -1800,25 +1825,29 @@ export function h(tag: string, ...args: [] | [attributes: { $: string } & Partia
|
||||
children = args[1];
|
||||
}
|
||||
|
||||
const match = SELECTOR_REGEX.exec(tag);
|
||||
const match = H_REGEX.exec(tag);
|
||||
|
||||
if (!match) {
|
||||
if (!match || !match.groups) {
|
||||
throw new Error('Bad use of h');
|
||||
}
|
||||
|
||||
const tagName = match[1] || 'div';
|
||||
const tagName = match.groups['tag'] || 'div';
|
||||
const el = document.createElement(tagName);
|
||||
|
||||
if (match[3]) {
|
||||
el.id = match[3];
|
||||
if (match.groups['id']) {
|
||||
el.id = match.groups['id'];
|
||||
}
|
||||
|
||||
if (match[4]) {
|
||||
el.className = match[4].replace(/\./g, ' ').trim();
|
||||
if (match.groups['class']) {
|
||||
el.className = match.groups['class'].replace(/\./g, ' ').trim();
|
||||
}
|
||||
|
||||
const result: Record<string, HTMLElement> = {};
|
||||
|
||||
if (match.groups['name']) {
|
||||
result[match.groups['name']] = el;
|
||||
}
|
||||
|
||||
if (children) {
|
||||
for (const c of children) {
|
||||
if (c instanceof HTMLElement) {
|
||||
@@ -1833,10 +1862,6 @@ export function h(tag: string, ...args: [] | [attributes: { $: string } & Partia
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
if (key === '$') {
|
||||
result[value] = el;
|
||||
continue;
|
||||
}
|
||||
if (key === 'style') {
|
||||
for (const [cssKey, cssValue] of Object.entries(value)) {
|
||||
el.style.setProperty(
|
||||
|
||||
Reference in New Issue
Block a user