Skip to content

Instantly share code, notes, and snippets.

@RShergold
Last active July 11, 2020 14:33
Show Gist options
  • Save RShergold/5c4b8d10c3e7eac69bb325223688ac43 to your computer and use it in GitHub Desktop.
Save RShergold/5c4b8d10c3e7eac69bb325223688ac43 to your computer and use it in GitHub Desktop.
function el<K extends keyof HTMLElementTagNameMap>(
tagName: K,
props?: Partial<HTMLElementTagNameMap[K]> & {
ref?: (r: HTMLElementTagNameMap[K]) => void;
},
...children: Array<HTMLElement | string | null | undefined>
): HTMLElementTagNameMap[K] {
const element = document.createElement(tagName);
Object.assign(element, props);
children.filter(Boolean).forEach((child) => {
if (child instanceof HTMLElement) {
element.appendChild(child);
} else {
const textNode = document.createTextNode(child);
element.appendChild(textNode);
}
});
if (props?.ref) {
props.ref(element);
}
return element;
}
// Returns a render function. Each time render is run the old element is replaced
// with the new one in the DOM.
function DOMUpdater<T extends HTMLElement>(Component: () => T): () => T {
let element: T | undefined;
return () => {
const newElement = Component();
element?.replaceWith(newElement);
element = newElement;
return newElement;
};
}
// App Component
function App() {
let toDos: string[] = [];
const onSubmit = (todo: string) => {
// Add to do
toDos.push(todo);
render();
};
const onRemove = (index: number) => {
// Remove to do
toDos.splice(index, 1);
render();
};
const render = DOMUpdater(() =>
el(
'div',
{ className: 'App' },
Form({ onSubmit }),
List({ items: toDos, onRemove }),
toDos.length > 0 &&
el('div', null, 'you have ', toDos.length.toString(), ' to dos')
)
);
return render();
}
// Form Component
interface FormProps {
onSubmit: (item: string) => void;
}
function Form(props: FormProps) {
let elInput: HTMLInputElement;
const onsubmit = (event: Event) => {
event.preventDefault();
props.onSubmit(elInput.value);
elInput.value = '';
};
return el(
'form',
{ onsubmit },
el('input', { ref: (element) => (elInput = element) }),
el('input', { type: 'submit' })
);
}
// List Component
interface ListProps {
items: string[];
onRemove?: (index: number) => void;
}
function List({ items, onRemove }: ListProps) {
const elItems = items.map((item, index) =>
el('li', null, item, el('button', { onclick: () => onRemove(index) }, 'x'))
);
return el('ol', null, ...elItems);
}
document.body.appendChild(App());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment