UNPKG

rabbit-simple-ui

Version:

A simple UI component library based on JavaScript

234 lines (193 loc) 7.38 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { type, validComps } from '../../utils'; import { $el, bind, getBooleanTypeAttr, getStrTypeAttr, removeAttrs, setHtml, siblings } from '../../dom-utils'; import PREFIX from '../prefix'; interface Config { config( el: string ): { checked: boolean; value: string; events({ onChange }: RadioEvent): void; }; } interface ItemData { index: number; label: string; current: Element; } interface RadioEvent { onChange: (param: boolean & ItemData) => void; } class Radio implements Config { readonly VERSION: string; private COMPONENTS: NodeListOf<Element>; constructor() { this.VERSION = 'v1.0'; this.COMPONENTS = $el('r-radio', { all: true }); this._create(this.COMPONENTS); } public config( el: string ): { checked: boolean; value: string; events({ onChange }: RadioEvent): void; } { const target = $el(el) as HTMLElement; const isGroup = target.tagName.toLowerCase() == 'r-radio-group'; // 排除 group 项 if (!isGroup) { validComps(target, 'radio'); } else { validComps(target, 'radio-group'); } const { _isChecked, _isDisabled, _setSingleChecked, _setCurrentlyChecked } = Radio.prototype; return { get checked() { return !isGroup && _isChecked(target); }, set checked(newVal: boolean) { if (isGroup || !type.isBol(newVal)) return; _setSingleChecked(target, newVal); }, get value() { // @ts-ignore return isGroup && target.getAttribute('value'); }, set value(newVal: string) { if (!isGroup && !type.isStr(newVal)) return; Array.from(target.children).forEach((child) => { const label = child.getAttribute('label')!; _setCurrentlyChecked(child, newVal, label); }); }, events({ onChange }) { if (!isGroup) { bind(target, 'click', () => { if (_isDisabled(target)) return false; onChange && type.isFn(onChange, true); }); } else { const data = Object.create(null); bind(target, 'click', () => { Array.from(target.children).forEach((child, index) => { if (_isDisabled(child)) return false; data['index'] = index; data['label'] = child.getAttribute('label'); data['current'] = child; if (_isChecked(child)) onChange && type.isFn(onChange, data); }); }); } } }; } private _create(COMPONENTS: NodeListOf<Element>) { COMPONENTS.forEach((node) => { const { checked, label, icon, name } = this._attrs(node); const content = setHtml(node); const RadioGroupWrapper = this._getGroupElm(node); if (RadioGroupWrapper) { const { value } = this._attrs(RadioGroupWrapper); this._setCurrentlyChecked(node, value, label); } this._setMainTemplate(node, content, name); this._setSingleChecked(node, checked, RadioGroupWrapper); this._setIcon(node, icon); this._handleClick(node, RadioGroupWrapper); removeAttrs(node, ['checked', 'icon']); }); } private _setMainTemplate(node: Element, content: string, name: string): void { const template = ` <span class="${PREFIX.radio}"> <span class="${PREFIX.radio}-inner"></span> <input type="radio" name="${name}" class="${PREFIX.radio}-input" /> </span> ${content} `; setHtml(node, template); } // 设置单个radio选中 private _setSingleChecked( node: Element, checked: boolean, groupWrapper?: Element | null ): void { if (groupWrapper) return; checked ? node.classList.add(`${PREFIX.radio}-checked`) : node.classList.remove(`${PREFIX.radio}-checked`); } // 设置radio组的当前选中项 private _setCurrentlyChecked(node: Element, value: string, label: string): void { if (value !== label) return; node.classList.add(`${PREFIX.radio}-wrapper-checked`); node.classList.add(`${PREFIX.radio}-checked`); siblings(node).forEach((o: Element) => { o.classList.remove(`${PREFIX.radio}-wrapper-checked`); o.classList.remove(`${PREFIX.radio}-checked`); }); } private _setIcon(node: Element, icon: string): void { if (!icon) return; const template = `<i class="${PREFIX.icon} ${PREFIX.icon}-${icon}"></i>`; node.querySelector(`.${PREFIX.radio}`)?.insertAdjacentHTML('afterend', template); } private _handleClick(node: Element, groupWrapper: Element | null): void { if (this._isDisabled(node)) return; const changeStatus = () => { if (groupWrapper) { node.classList.add(`${PREFIX.radio}-wrapper-checked`); siblings(node).forEach((o: Element) => { if (this._isChecked(o)) { o.classList.remove(`${PREFIX.radio}-checked`); o.classList.remove(`${PREFIX.radio}-wrapper-checked`); o.classList.remove(`${PREFIX.radio}-focus`); } }); } node.classList.add(`${PREFIX.radio}-checked`); }; bind(node, 'click', () => changeStatus()); bind(node, 'mousedown', () => { node.classList.add(`${PREFIX.radio}-focus`); }); bind(node, 'mouseup', () => { setTimeout(() => node.classList.remove(`${PREFIX.radio}-focus`), 1000); }); } private _getGroupElm(node: Element): Element | null { const tag = node.parentElement; return tag?.tagName.toLowerCase() === 'r-radio-group' ? tag : null; } private _isDisabled(node: Element) { return node.getAttribute('disabled') == '' || node.getAttribute('disabled') == 'true'; } private _isChecked(node: Element) { return node.classList.contains(`${PREFIX.radio}-checked`); } private _attrs(node: Element) { return { value: getStrTypeAttr(node, 'value', ''), name: getStrTypeAttr(node, 'name', ''), label: getStrTypeAttr(node, 'label', ''), icon: getStrTypeAttr(node, 'icon', ''), checked: getBooleanTypeAttr(node, 'checked') }; } } export default Radio;