import type { VNode } from 'vue'; import type { DropdownButtonItemArgs } from '../dropdown-button/dropdown-button-item'; import type { FormItemWrapperArgs, HintType, MarginType } from '../form/form-item-wrapper'; import { render } from 'vue'; import { Prop, toNative } from 'vue-facing-decorator'; import TsxComponent, { Component } from '../../app/vuetsx'; import { capitalize } from '../../common/extensions/string-extensions'; import { PortalUtils } from '../../common/utils/utils'; import FormItemWrapper from '../form/form-item-wrapper'; import HtmlLiteral from '../html-literal/html-literal'; import './css/radio-button-group.css'; type RowToString = (row) => string; interface RadioButtonGroupArgs extends Omit { changed: (newValue: string | any) => void; customRenderOption?: (h, state: RadioDisplayArgs) => any; customRenderSelectionResult?: (h, state: RadioDisplayArgs) => any; options: Array | Array; displayMember?: string | RowToString; valueMember?: string | RowToString; selected: string | any; radioButtonSize?: RadioButtonSize; } export const enum RadioButtonSize { Small = 'sm', Medium = 'md', } interface RadioDisplayArgs { id: string; text: string; dataRow: any; } @Component class RadioButtonGroupComponent extends TsxComponent implements RadioButtonGroupArgs { @Prop() label!: string | VNode; @Prop() labelButtons!: DropdownButtonItemArgs[]; @Prop() subtitle!: string; @Prop() cssClass!: string; @Prop() mandatory!: boolean; @Prop() wrap!: boolean; @Prop() hint: string; @Prop() hintType!: HintType; @Prop() appendIcon: string; @Prop() prependIcon: string; @Prop() appendClicked: () => void; @Prop() prependClicked: () => void; @Prop() radioButtonSize?: RadioButtonSize; @Prop() marginType?: MarginType; @Prop() maxWidth?: number; @Prop() options!: Array | Array; @Prop() displayMember!: (row) => string | string; @Prop() valueMember!: (row) => string | string; @Prop() selected!: string | any; @Prop() changed: (newValue: string | any) => void; @Prop() customRenderOption?: (h, state: RadioDisplayArgs) => any; @Prop() customRenderSelectionResult?: (h, state: RadioDisplayArgs) => any; private get errorId(): string { return `radiogroup-error-${this.$.uid}`; } getCustomFormatOptions(h, item: RadioDisplayArgs): string { if (this.customRenderOption != null) { return this.customRenderOption(h, item); } } raiseChangedEvent(newValue: RadioDisplayArgs) { if (newValue == this.selected) { return; } this.populateValidationDeclaration(); if (this.changed != null) { this.changed(newValue); } } getValueFromArr(paramArr: string[], row): string { for (let i = 0, len = paramArr.length; i < len; i++) { let val = row[paramArr[i]]; if (val == null) { val = row[paramArr[i][capitalize]()]; } if (val != null) { if (PortalUtils.isString(val) && val.length > 0) { return val; } else if (PortalUtils.isNumber(val)) { return val.toString(); } else if (PortalUtils.isFunction(val)) { return val.call(row, row); } } } return null; } getReflectedRowValue(row: any, isValueMember: boolean): string { const member = (isValueMember ? this.valueMember : this.displayMember); if (member == null) { if (isValueMember) { return this.getValueFromArr(['id'], row); } else { return this.getValueFromArr([ 'name', 'text', 'identifier', ], row); } } else if (PortalUtils.isString(member)) { return row[member as any]; } else if (PortalUtils.isNumber(member)) { return row[member as any].toString(); } else if (PortalUtils.isFunction(member)) { return member.call(row, row); } } getOptions(h): RadioDisplayArgs[] { const retVal: RadioDisplayArgs[] = []; const opts = this.options as any; if (opts != null && opts.length > 0) { const firstItem = opts[0]; if (PortalUtils.isString(firstItem)) { opts.forEach(item => retVal.push({ id: item, text: item, dataRow: item })); } else if (PortalUtils.isNumber(firstItem)) { opts.forEach(item => retVal.push({ id: item.toString(), text: item.toString(), dataRow: item.toString() })); } else { opts.forEach((item) => { retVal.push({ id: this.getReflectedRowValue(item, true), text: this.getReflectedRowValue(item, false), dataRow: item, }); }); } } return retVal; } private handleCustomRenderResult(renderResult: any): string { if (renderResult == null) { return ''; } if (renderResult?.__v_isVNode) { const container = document.createElement('div'); render(renderResult as any, container); return container.innerHTML; } if (renderResult instanceof HTMLElement) { return renderResult.outerHTML; } return renderResult; } getSelectedItem(h, opts?: RadioDisplayArgs[]): RadioDisplayArgs { opts = opts || this.getOptions(h); if (this.selected == null || opts.length == 0) { return opts[0]; } let selVal: string; const selectedItem = this.selected; if (PortalUtils.isString(selectedItem) || PortalUtils.isNumber(selectedItem)) { selVal = selectedItem.toString(); } else { selVal = this.getReflectedRowValue(selectedItem, true); } for (let i = 0, len = opts.length; i < len; i++) { const ci = opts[i]; if (ci.id == selVal) { return ci; } } return null; } render(h) { return ( {this.renderInput(h)} ); } handleRadioValueChanged(h) { const selectedValue = this.$el.querySelector('input[type="radio"]:checked').value; if (selectedValue != null) { const opts = this.getOptions(h); for (let i = 0, len = opts.length; i < len; i++) { const optItem = opts[i]; if (optItem.id == selectedValue) { this.raiseChangedEvent(optItem.dataRow); break; } } } } renderInput(h) { const nameId = `iei-group-${PortalUtils.randomString(6)}`; const selectedItem = this.getSelectedItem(h); const isSmall = this.radioButtonSize === RadioButtonSize.Small; const hasCustomRender = this.customRenderOption != null; return (
{this.getOptions(h).map((optionItem) => { const uuid = `iei-input-${PortalUtils.randomString(10)}`; const selected = optionItem.id == selectedItem?.id; const disabled = optionItem.dataRow?.disabled ?? false; const wrapperClass = [ 'inv-rbchb', 'form-check', 'inv-mdfw-radio', selected ? 'inv-selected' : '', isSmall ? 'inv-md-radio-sm' : '', ].join(' '); return (
this.handleRadioValueChanged(h)} />
); })}
); } } const RadioButtonGroup = toNative(RadioButtonGroupComponent); export default RadioButtonGroup;