UNPKG

@furystack/shades

Version:

Google Authentication Provider for FuryStack

116 lines (102 loc) 3.35 kB
import type { ChildrenList, ShadeComponent } from './models/index.js' import { isShadeComponent } from './models/shade-component.js' /** * Appends a list of items to a HTML element * @param el the root element * @param children array of items to append */ export const appendChild = (el: HTMLElement | DocumentFragment, children: ChildrenList) => { for (const child of children) { if (typeof child === 'string' || typeof child === 'number') { el.appendChild(document.createTextNode(child)) } else { if (child instanceof HTMLElement || child instanceof DocumentFragment) { el.appendChild(child) } else if (child instanceof Array) { appendChild(el, child) } } } } export const hasStyle = (props: unknown): props is { style: Partial<CSSStyleDeclaration> } => { return ( !!props && typeof props === 'object' && typeof (props as { style: Partial<CSSStyleDeclaration> }).style === 'object' ) } /** * @param el The Target HTML Element * @param props The Properties to fetch The Styles Object */ export const attachStyles = (el: HTMLElement, props: unknown) => { if (hasStyle(props)) for (const key in props.style) { if (Object.prototype.hasOwnProperty.call(props.style, key)) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error el.style[key] = props.style[key] } } } export const attachDataAttributes = <TProps extends object>(el: HTMLElement, props: TProps) => { if (props) { Object.entries(props) .filter(([key]) => key.startsWith('data-')) .forEach(([key, value]) => el.setAttribute(key, (value as string) || '')) } } /** * * @param el The Target HTML Element * @param props The Props to attach */ export const attachProps = <TProps extends object>(el: HTMLElement, props: TProps) => { if (!props) { return } attachStyles(el, props) if (hasStyle(props)) { const { style, ...rest } = props Object.assign(el, rest) } else { Object.assign(el, props) } attachDataAttributes(el, props) } type CreateComponentArgs<TProps> = [ elementType: string | ShadeComponent<TProps>, props: TProps, ...children: ChildrenList, ] /** * Factory method that creates a component. This should be configured as a default JSX Factory in tsconfig. * @returns the created JSX element */ export const createComponentInner = <TProps extends object>( ...[elementType, props, ...children]: CreateComponentArgs<TProps> ) => { if (typeof elementType === 'string') { const el = document.createElement(elementType) attachProps(el, props) if (children) { appendChild(el, children) } return el } else if (isShadeComponent(elementType)) { const el = elementType(props, children) attachStyles(el, props) return el } return undefined } type CreateFragmentArgs = [props: null, ...children: ChildrenList] export const createFragmentInner = (...[_props, ...children]: CreateFragmentArgs) => { const fragment = document.createDocumentFragment() appendChild(fragment, children) return fragment } export const createComponent = <TProps extends object>(...args: CreateComponentArgs<TProps> | CreateFragmentArgs) => { if (args[0] === null) { return createFragmentInner(...args) } return createComponentInner(...args) }