import { extend } from '../Helpers/Extend'; import { wrap } from '../DOM/Wrap'; import { isString } from '../Helpers/Type'; const defaultOptions = { "wrap": "", "invalidClass": "invalid", "disabledClass": "disabled", "checkedClass": "checked" }; /** * Skin an HTML input radio element. * You can access the skin API in the __skinAPI property of the $radio HTMLElement or its wrapper. * * @example * ```ts * // Call with default options: * skinRadio( $radio, { * "wrap": "<span class=\"rb-skin\"></span>", * "invalidClass": "invalid", * "disabledClass": "disabled", * "checkedClass": "checked" * } ); * ``` */ export default class SkinRadioButton implements FLib.SkinRadio.SkinRadio { #$radio: FLib.SkinRadio.CustomRadioButton; #options: FLib.SkinRadio.Options; #$parent: FLib.SkinRadio.CustomRadioButtonParent; #$rdGroup: NodeListOf; constructor( $radio: HTMLInputElement, userOptions?: Partial ) { // Already skinned if ( ($radio as FLib.SkinRadio.CustomRadioButton).__skinAPI ) { throw 'SkinSelect: Select already skinned'; } this.#$radio = $radio; this.#options = extend( defaultOptions, userOptions ); const rdName = this.#$radio.getAttribute( 'name' ); // Store all radio with same name if ( !this.#$radio.__$radioGroup ) { this.#$rdGroup = document.querySelectorAll( `input[type="radio"][name="${ rdName }"]` ) as NodeListOf; this.#$rdGroup.forEach( $r => { ($r as FLib.SkinRadio.CustomRadioButton).__$radioGroup = this.#$rdGroup; } ); } else { this.#$rdGroup = this.#$radio.__$radioGroup; } wrap( this.#$radio, this.#options.wrap ); this.#$parent = this.#$radio.parentNode as FLib.SkinRadio.CustomRadioButtonParent; this.#$parent.__skinAPI = this.#$radio.__skinAPI = this; if ( this.#$radio.hasAttribute( 'data-error' ) ) { this.setInvalid(); } this.#$radio.addEventListener( 'click', this.#changeHandler ); this.#update( this.#$radio ); } #update = ( $rd: FLib.SkinRadio.CustomRadioButton ): void => { $rd.__skinAPI[ $rd.checked ? 'check' : 'uncheck' ](); } #changeHandler = (): void => { this.#$rdGroup.forEach( $rd => this.#update( $rd ) ); } #enableDisable = ( fnName: string, disabled: boolean ): void => { this.#$rdGroup.forEach( ( $r: Node ) => { ($r as HTMLInputElement).disabled = disabled; ($r.parentNode as HTMLElement).classList[ fnName ]( this.#options.disabledClass ); } ) } #validInvalid = ( fnName: string ): void => { this.#$rdGroup.forEach( ( $r: Node ) => { ($r.parentNode as HTMLElement).classList[ fnName ]( this.#options.invalidClass ); } ); } /** * Force the radio button to be check */ check(): this { this.#$rdGroup.forEach( ( $r: Node ) => { ($r as HTMLInputElement).checked = $r === this.#$radio; ($r.parentNode as HTMLElement).classList[ $r === this.#$radio ? 'add' : 'remove' ]( this.#options.checkedClass ); } ); return this; } /** * Force the radio button to be uncheck */ uncheck(): this { this.#$radio.checked = false; this.#$parent.classList.remove( this.#options.checkedClass ); return this; } /** * Force the radio button to be enable */ enable(): this { this.#enableDisable( 'remove', false ); return this; } /** * Force the radio button to be disable */ disable(): this { this.#enableDisable( 'add', true ); return this; } /** * Force the state of the radio button to invalid */ setInvalid(): this { this.#validInvalid( 'add' ); return this; } /** * Force the state of the radio button to valid */ setValid(): this { this.#validInvalid( 'remove' ); return this; } } /** * Skin a radio input * * @example * ```ts * // Call with default options: * skinRadio( $radio, { * "wrap": "<span class=\"rb-skin\"></span>", * "invalidClass": "invalid", * "disabledClass": "disabled", * "checkedClass": "checked" * } ); * ``` */ export function skinRadio( $radio: HTMLInputElement, options: Partial ): SkinRadioButton { return new SkinRadioButton( $radio, options ); } /** * Select all radio input in a $wrapper * * @example * ```ts * // Call with default options: * skinRadioAll( $wrapper, { * "wrap": "<span class=\"rb-skin\"></span>", * "selector": "input[type=\"radio\"]", * "invalidClass": "invalid", * "disabledClass": "disabled", * "checkedClass": "checked" * } ); * ``` */ export function skinRadioAll( $wrapper: HTMLElement, options: Partial = {} ): SkinRadioButton[] { const $radioButtons = $wrapper.querySelectorAll( options.selector || 'input[type="radio"]' ); const skinList: SkinRadioButton[] = []; $radioButtons.forEach( $radio => { skinList.push( skinRadio( $radio as HTMLInputElement, options ) ) } ); return skinList; } /** * Select all radio input of the same group (same name) * * @example * ```ts * // Call with default options: * skinRadioGroup( $radio, { * "wrap": "", * "invalidClass": "invalid", * "disabledClass": "disabled", * "checkedClass": "checked" * } ); * ``` */ export function skinRadioGroup( $radioButtonOrInputName: HTMLInputElement | string, options: FLib.SkinRadio.Options ): SkinRadioButton[] { const inputName = isString( $radioButtonOrInputName ) ? $radioButtonOrInputName : ($radioButtonOrInputName as HTMLInputElement).getAttribute( 'name' ); const $radioGroup = document.querySelectorAll( `input[type="radio"][name="${ inputName }"]` ); const skinList: SkinRadioButton[] = []; $radioGroup.forEach( $rd => { skinList.push( skinRadio( $rd as HTMLInputElement, options ) ); } ); return skinList; }