type BxButton = { style?: number | string; url?: string; classes?: string[]; icon?: string; label?: string; title?: string; disabled?: boolean; onClick?: EventListener; } type ButtonStyle = {[index: string]: number} & {[index: number]: string}; // Quickly create a tree of elements without having to use innerHTML function createElement(elmName: string, props: {[index: string]: any}={}, ..._: any): T { let $elm; const hasNs = 'xmlns' in props; if (hasNs) { $elm = document.createElementNS(props.xmlns, elmName); delete props.xmlns; } else { $elm = document.createElement(elmName); } for (const key in props) { if ($elm.hasOwnProperty(key)) { continue; } if (hasNs) { $elm.setAttributeNS(null, key, props[key]); } else { $elm.setAttribute(key, props[key]); } } for (let i = 2, size = arguments.length; i < size; i++) { const arg = arguments[i]; const argType = typeof arg; if (argType === 'string' || argType === 'number') { $elm.appendChild(document.createTextNode(arg)); } else if (arg) { $elm.appendChild(arg); } } return $elm as T; } export const CE = createElement; // Credit: https://phosphoricons.com export enum Icon { STREAM_SETTINGS = '', STREAM_STATS = '', CONTROLLER = '', DISPLAY = '', MOUSE = '', MOUSE_SETTINGS = '', NEW = '', COPY = '', TRASH = '', CURSOR_TEXT = '', QUESTION = '', REMOTE_PLAY = '', HAND_TAP = '', }; export const createSvgIcon = (icon: string, strokeWidth=2) => { const $svg = CE('svg', { 'xmlns': 'http://www.w3.org/2000/svg', 'fill': 'none', 'stroke': '#fff', 'fill-rule': 'evenodd', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': strokeWidth, }); $svg.innerHTML = icon; $svg.setAttribute('viewBox', '0 0 32 32'); return $svg; }; export const ButtonStyle: DualEnum = {}; ButtonStyle[ButtonStyle.PRIMARY = 1] = 'bx-primary'; ButtonStyle[ButtonStyle.DANGER = 2] = 'bx-danger'; ButtonStyle[ButtonStyle.GHOST = 4] = 'bx-ghost'; ButtonStyle[ButtonStyle.FOCUSABLE = 8] = 'bx-focusable'; ButtonStyle[ButtonStyle.FULL_WIDTH = 16] = 'bx-full-width'; ButtonStyle[ButtonStyle.FULL_HEIGHT = 32] = 'bx-full-height'; const ButtonStyleIndices = Object.keys(ButtonStyle).splice(0, Object.keys(ButtonStyle).length / 2).map(i => parseInt(i)); export const createButton = (options: BxButton): T => { let $btn; if (options.url) { $btn = CE('a', {'class': 'bx-button'}) as HTMLAnchorElement; $btn.href = options.url; $btn.target = '_blank'; } else { $btn = CE('button', {'class': 'bx-button'}) as HTMLButtonElement; } const style = (options.style || 0) as number; style && ButtonStyleIndices.forEach(index => { (style & index) && $btn.classList.add(ButtonStyle[index] as string); }); options.classes && $btn.classList.add(...options.classes); options.icon && $btn.appendChild(createSvgIcon(options.icon, 4)); options.label && $btn.appendChild(CE('span', {}, options.label)); options.title && $btn.setAttribute('title', options.title); options.disabled && (($btn as HTMLButtonElement).disabled = true); options.onClick && $btn.addEventListener('click', options.onClick); return $btn as T; } export const CTN = document.createTextNode.bind(document); window.BX_CE = createElement;