/** @jsxImportSource react */ import type { RenderingContext } from "../../ui/RenderingContext"; import type { Instance } from "../../ui/Instance"; import { Widget, VDOM, getContent } from "../../ui/Widget"; import { Field, FieldConfig, getFieldTooltip, FieldInstance } from "./Field"; import { tooltipMouseMove, tooltipMouseLeave } from "../overlay/tooltip-ops"; import { stopPropagation } from "../../util/eventCallbacks"; import { KeyCode } from "../../util/KeyCode"; import { isUndefined } from "../../util/isUndefined"; import { BooleanProp, Prop, StringProp } from "../../ui/Prop"; export interface RadioConfig extends FieldConfig { /** Selected value. If the value is equal to `option`, the radio button appears checked. */ value?: Prop; /** Selected value. If the value is equal to `option`, the radio button appears checked. */ selection?: Prop; /** Value to be written into `value` if radio button is clicked. */ option?: Prop; /** Defaults to `false`. Used to make the field read-only. */ readOnly?: BooleanProp; /** Text description. */ text?: StringProp; /** Base CSS class to be applied to the field. Defaults to `radio`. */ baseClass?: string; /** Use native radio HTML element. Default is `false`. */ native?: boolean; /** Set to `true` to make the radio initially selected. */ default?: boolean; /** Custom validation function. */ onValidate?: | string | ((value: number | string | boolean, instance: Instance, validationParams: Record) => unknown); } export class Radio extends Field { declare public selection?: Prop; declare public option?: Prop; declare public native?: boolean; declare public default?: boolean; declare public value?: Prop; constructor(config?: RadioConfig) { super(config); } declareData(...args: Record[]): void { super.declareData( { value: undefined, selection: undefined, option: undefined, disabled: undefined, enabled: undefined, readOnly: undefined, required: undefined, text: undefined, }, ...args, ); } init(): void { if (this.selection) this.value = this.selection; super.init(); } formatValue(context: RenderingContext, { data }: Instance): React.ReactNode { return data.text; } prepareData(context: RenderingContext, instance: FieldInstance): void { super.prepareData(context, instance); let { data } = instance; data.checked = data.value === data.option; if (this.default && isUndefined(data.value)) instance.set("value", data.option); } renderValue(context: RenderingContext, { data }: FieldInstance): React.ReactNode { if (data.value === data.option) return super.renderValue(context, { data } as FieldInstance); return null; } renderWrap( context: RenderingContext, instance: FieldInstance, key: string, content: React.ReactNode, ): React.ReactElement { var { data } = instance; return ( ); } renderNativeCheck(context: RenderingContext, instance: Instance): React.ReactElement { var { CSS, baseClass } = this; var { data } = instance; return ( ) => { this.handleChange(e, instance); }} /> ); } renderCheck(context: RenderingContext, instance: FieldInstance): React.ReactElement { return ; } renderInput(context: RenderingContext, instance: FieldInstance, key: string): React.ReactElement { var { data } = instance; var text = data.text || this.renderChildren(context, instance); var { CSS, baseClass } = this; return this.renderWrap(context, instance, key, [ this.native ? this.renderNativeCheck(context, instance) : this.renderCheck(context, instance), text ? (
{text}
) : (   ), ]); } handleClick(e: React.MouseEvent, instance: Instance): void { if (this.native) e.stopPropagation(); else { var el = document.getElementById(instance.data.id); if (el) el.focus(); e.preventDefault(); this.handleChange(e, instance); } } handleChange(e: React.ChangeEvent | React.MouseEvent, instance: Instance): void { var { data } = instance; if (data.disabled || data.readOnly || data.viewMode) return; instance.set("value", data.option); } } Radio.prototype.baseClass = "radio"; Radio.prototype.native = false; Radio.prototype.default = false; Widget.alias("radio", Radio); interface RadioCmpProps { key?: string; instance: FieldInstance; data: Record; } interface RadioCmpState { value: unknown; } class RadioCmp extends VDOM.Component { constructor(props: RadioCmpProps) { super(props); this.state = { value: props.data.checked, }; } UNSAFE_componentWillReceiveProps(props: RadioCmpProps): void { this.setState({ value: props.data.checked, }); } render(): React.ReactElement { var { instance, data } = this.props; var { widget } = instance; var { baseClass, CSS } = widget; return ( ); } onClick(e: React.MouseEvent): void { var { instance, data } = this.props; var { widget } = instance; if (!data.disabled && !data.readOnly) { e.stopPropagation(); e.preventDefault(); (widget as Radio).handleChange(e, instance); } } onKeyDown(e: React.KeyboardEvent): void { let { instance } = this.props; const widget = instance.widget as Radio; if (widget.handleKeyDown && widget.handleKeyDown(e, instance) === false) return; switch (e.keyCode) { case KeyCode.space: this.onClick(e as unknown as React.MouseEvent); break; } } }