|
| 1 | +import { h as createElement, text, patch } from './lib/patched-superfine' |
| 2 | +import htm from 'htm' |
| 3 | +import { createDefiner, createRenderer, Component } from 'js-element' |
| 4 | + |
| 5 | +export { |
| 6 | + attr, |
| 7 | + event, |
| 8 | + hook, |
| 9 | + ref, |
| 10 | + Attr, |
| 11 | + EventHandler, |
| 12 | + MethodsOf, |
| 13 | + Ref, |
| 14 | + UIEvent |
| 15 | +} from 'js-element' |
| 16 | + |
| 17 | +export const define = createDefiner<VNode>('define', renderContent) |
| 18 | + |
| 19 | +export const render = createRenderer<VNode>( |
| 20 | + 'render', |
| 21 | + (it: any) => !!it && it.isVElement === true, |
| 22 | + renderContent |
| 23 | +) |
| 24 | + |
| 25 | +export { h, VNode, VElement } |
| 26 | +export const html = htm.bind(h) |
| 27 | + |
| 28 | +// === types ========================================================= |
| 29 | + |
| 30 | +type Props = Record<string, any> // TODO |
| 31 | +type VElement<T extends Props = Props> = Record<any, any> // TODO!!!!! |
| 32 | +type VNode = null | boolean | number | string | VElement | Iterable<VNode> |
| 33 | + |
| 34 | +// === helpers ======================================================= |
| 35 | + |
| 36 | +function renderContent(content: VNode, target: Element) { |
| 37 | + if (target.hasChildNodes()) { |
| 38 | + patch(target.firstChild, content) |
| 39 | + } else { |
| 40 | + const newTarget = document.createElement('span') |
| 41 | + |
| 42 | + target.append(newTarget) |
| 43 | + patch(newTarget, content) |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +function asVNode(x: any): any { |
| 48 | + return typeof x === 'number' || typeof x === 'string' ? text(x, null) : x |
| 49 | +} |
| 50 | + |
| 51 | +// === h ============================================================== |
| 52 | + |
| 53 | +function h( |
| 54 | + type: string, |
| 55 | + props?: Props | null, // TODO!!! |
| 56 | + ...children: VNode[] |
| 57 | +): VElement |
| 58 | + |
| 59 | +function h<P extends Props>( |
| 60 | + type: Component<P>, |
| 61 | + props?: Partial<P> | null, |
| 62 | + ...children: VNode[] |
| 63 | +): VElement |
| 64 | + |
| 65 | +function h(type: string | Component<any>, props?: Props | null): VElement { |
| 66 | + const argc = arguments.length |
| 67 | + const tagName = typeof type === 'function' ? (type as any).tagName : type |
| 68 | + |
| 69 | + if (process.env.NODE_ENV === ('development' as string)) { |
| 70 | + if (typeof tagName !== 'string') { |
| 71 | + throw new Error('[h] First argument must be a string or a component') |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + const children = argc > 2 ? [] : EMPTY_ARR |
| 76 | + |
| 77 | + if (argc > 2) { |
| 78 | + for (let i = 2; i < argc; ++i) { |
| 79 | + const child = arguments[i] |
| 80 | + |
| 81 | + if (!Array.isArray(child)) { |
| 82 | + children.push(asVNode(child)) |
| 83 | + } else { |
| 84 | + for (let j = 0; j < child.length; ++j) { |
| 85 | + children.push(asVNode(child[j])) |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + const ret: any = createElement(tagName, props || EMPTY_OBJ, children) |
| 92 | + ret.isVElement = true |
| 93 | + return ret |
| 94 | +} |
| 95 | + |
| 96 | +// === constants ===================================================== |
| 97 | + |
| 98 | +const EMPTY_ARR: any[] = [] |
| 99 | +const EMPTY_OBJ = {} |
0 commit comments