|
<template>
|
|
<view class="l-radio" :class="classes" :style="[styles]" @click="onClick">
|
|
<slot name="radio" :checked="radioChecked" :disabled="isDisabled">
|
|
<slot name="icon" :checked="radioChecked" :disabled="isDisabled">
|
|
<view class="l-radio__icon" ref="iconRef" :class="iconClasses" :style="[iconStyle]"></view>
|
|
</slot>
|
|
<text class="l-radio__label" :class="labelClass" v-if="label != null || $slots['default'] != null">
|
|
<slot>{{label}}</slot>
|
|
</text>
|
|
</slot>
|
|
</view>
|
|
</template>
|
|
<script lang="uts" setup>
|
|
import { RadioProps } from './type';
|
|
// #ifndef APP
|
|
import type { ComputedRef } from 'vue';
|
|
type ComputedRefImpl<T> = ComputedRef<T>
|
|
// #endif
|
|
const emit = defineEmits(['change', 'update:checked'])
|
|
const props = withDefaults(defineProps<RadioProps>(), {
|
|
allowUncheck: false,
|
|
checked: false,
|
|
disabled: false,
|
|
icon: 'circle',
|
|
size: 'medium'
|
|
})
|
|
|
|
defineSlots<{
|
|
radio(props : { checked : boolean, disabled: boolean}) : any,
|
|
icon(props : { checked : boolean, disabled: boolean}) : any,
|
|
default(props : { checked : boolean, disabled: boolean}) : any,
|
|
}>()
|
|
|
|
const name = 'l-radio'
|
|
const radioGroupProps = inject<LRadioGroupComponentPublicInstance|null>('limeRadioGroupProps', null);
|
|
const radioGroupValue = inject<ComputedRefImpl<any>|null>('limeRadioGroupValue', null);
|
|
const radioGroupChange = inject<((value: any|null) => void)|null>('limeRadioGroupChange', null);
|
|
const modelValue = defineModel({type: Boolean, default: false});
|
|
const innerChecked = computed({
|
|
set(value: boolean){
|
|
modelValue.value = value;
|
|
emit('change', value)
|
|
},
|
|
get(): boolean{
|
|
return props.checked || modelValue.value
|
|
},
|
|
} as WritableComputedOptions<boolean>)
|
|
|
|
const isDisabled = computed(():boolean => (props.disabled || (radioGroupProps?.disabled ?? false)))
|
|
const groupDisabled = computed(():boolean|null => radioGroupProps?.disabled);
|
|
const radioChecked = computed(():boolean => innerChecked.value || (props.name != null && props.name == radioGroupValue?.value) || (props.value != null && props.value == radioGroupValue?.value));
|
|
|
|
const finalAllowUncheck = computed(():boolean => props.allowUncheck || (radioGroupProps?.allowUncheck ?? false));
|
|
|
|
const innerIcon = computed(():string => radioGroupProps?.icon ?? props.icon)
|
|
const innerSize = computed(():string => radioGroupProps?.size ?? props.size)
|
|
const innerIconSize = computed(():string|null => radioGroupProps?.iconSize ?? props.iconSize)
|
|
const innerFontSize = computed(():string|null => radioGroupProps?.fontSize ?? props.fontSize)
|
|
const innerCheckedColor = computed(():string|null => radioGroupProps?.checkedColor ?? props.checkedColor)
|
|
const innerIconBgColor = computed(():string => props.iconBgColor ?? radioGroupProps?.iconBgColor ?? 'white')
|
|
const innerIconBorderColor = computed(():string => props.iconBorderColor ?? radioGroupProps?.iconBorderColor ?? '#c5c5c5')
|
|
const innerIconDisabledColor = computed(():string => props.iconDisabledColor ?? radioGroupProps?.iconDisabledColor ?? '#c5c5c5')
|
|
const innerIconDisabledBgColor = computed(():string => props.iconDisabledBgColor ?? radioGroupProps?.iconDisabledBgColor ?? '#f3f3f3')
|
|
|
|
const classes = computed(():Map<string, boolean>=>{
|
|
const cls = new Map<string, boolean>();
|
|
cls.set(`${name}--disabled`, isDisabled.value)
|
|
return cls
|
|
})
|
|
|
|
const iconClasses = computed(():Map<string, boolean>=>{
|
|
const cls = new Map<string, boolean>();
|
|
// #ifdef APP
|
|
cls.set(`${name}__icon--checked`, radioChecked.value && innerCheckedColor.value == null)
|
|
cls.set(`${name}__icon--${innerIcon.value}`, true)
|
|
cls.set(`${name}__icon--disabled`, isDisabled.value)
|
|
// #endif
|
|
// #ifndef APP
|
|
cls.set(`${name}__icon--checked`, radioChecked.value)
|
|
cls.set(`${name}__icon--${innerIcon.value}`, true)
|
|
cls.set(`${name}__icon--disabled`, isDisabled.value)
|
|
// #endif
|
|
return cls
|
|
})
|
|
|
|
const labelClass = computed(():Map<string, boolean>=>{
|
|
const cls = new Map<string, boolean>();
|
|
cls.set(`${name}__label--disabled`, isDisabled.value)
|
|
return cls
|
|
})
|
|
|
|
const styles = computed(():Map<string, any>=>{
|
|
const style = new Map<string, any>();
|
|
if(radioGroupProps != null && radioGroupProps.gap != null) {
|
|
style.set(radioGroupProps.direction == 'horizontal' ? 'margin-right' : 'margin-bottom', radioGroupProps.gap!)
|
|
}
|
|
|
|
// #ifndef APP
|
|
if(innerCheckedColor.value != null) {
|
|
style.set('--l-radio-icon-checked-color', innerCheckedColor.value!)
|
|
}
|
|
if(innerIconBorderColor.value != null) {
|
|
style.set('--l-radio-icon-border-color', innerIconBorderColor.value!)
|
|
}
|
|
if(innerIconDisabledColor.value != null) {
|
|
style.set('--l-radio-icon-disabled-color', innerIconDisabledColor.value!)
|
|
}
|
|
if(innerIconDisabledBgColor.value != null) {
|
|
style.set('--l-radio-icon-disabled-bg-color', innerIconDisabledBgColor.value!)
|
|
}
|
|
if(innerFontSize.value != null) {
|
|
style.set('--l-radio-font-size', innerFontSize.value!)
|
|
}
|
|
// #endif
|
|
return style
|
|
})
|
|
|
|
const iconStyle = computed(():Map<string, any>=>{
|
|
const style = new Map<string, any>();
|
|
if(innerIconSize.value != null) {
|
|
style.set('width', innerIconSize.value!)
|
|
style.set('height', innerIconSize.value!)
|
|
// #ifndef APP
|
|
style.set('--l-radio-icon-size', innerIconSize.value!)
|
|
// #endif
|
|
}
|
|
|
|
if(innerCheckedColor.value != null) {
|
|
// #ifndef APP
|
|
style.set('--l-radio-icon-checked-color', innerCheckedColor.value!)
|
|
// #endif
|
|
// #ifdef APP
|
|
if(radioChecked.value && ['dot', 'circle'].includes(innerIcon.value)) {
|
|
style.set('background', innerCheckedColor.value!)
|
|
style.set('border-color', innerCheckedColor.value!)
|
|
}
|
|
if(!isDisabled.value && !radioChecked.value && ['dot', 'circle'].includes(innerIcon.value)) {
|
|
style.set('background', innerIconBgColor.value)
|
|
style.set('border-color', innerIconBorderColor.value)
|
|
}
|
|
if(isDisabled.value && ['dot', 'circle'].includes(innerIcon.value)) {
|
|
style.set('background', innerIconDisabledBgColor.value)
|
|
style.set('border-color', innerIconDisabledColor.value)
|
|
}
|
|
// #endif
|
|
}
|
|
|
|
return style
|
|
})
|
|
const labelStyle = computed(():Map<string, any>=>{
|
|
const style = new Map<string, any>();
|
|
const fontSize = props.fontSize ?? radioGroupProps?.fontSize
|
|
if(fontSize != null) {
|
|
style.set('font-size', fontSize)
|
|
}
|
|
return style
|
|
})
|
|
|
|
const onClick = (e: UniPointerEvent) => {
|
|
if(isDisabled.value) return;
|
|
const _name = props.value ?? props.name
|
|
if(radioGroupChange != null && _name != null) {
|
|
const value = finalAllowUncheck.value && radioChecked.value ? null : _name;
|
|
radioGroupChange(value);
|
|
} else {
|
|
const value = finalAllowUncheck.value ? !radioChecked.value : true;
|
|
innerChecked.value = value
|
|
}
|
|
}
|
|
// #ifdef APP
|
|
const iconRef = ref<UniElement|null>(null)
|
|
const update = () => {
|
|
if(iconRef.value == null) return
|
|
const ctx = iconRef.value!.getDrawableContext()!;
|
|
const rect = iconRef.value!.getBoundingClientRect();
|
|
const x = rect.width / 2;
|
|
const y = rect.height / 2;
|
|
|
|
ctx.reset();
|
|
if(innerIcon.value == 'circle') {
|
|
if(radioChecked.value) {
|
|
ctx.strokeStyle = isDisabled.value ? innerIconDisabledColor.value : 'white';
|
|
ctx.lineWidth = rect.width * 0.12;
|
|
ctx.lineCap = 'round';
|
|
ctx.moveTo(rect.width * 0.2967, rect.height * 0.53)
|
|
ctx.lineTo(rect.width * 0.4342, rect.height * 0.6675)
|
|
ctx.lineTo(rect.width * 0.7092, rect.height * 0.3925)
|
|
ctx.stroke()
|
|
}
|
|
} else if(innerIcon.value == 'line') {
|
|
if(radioChecked.value) {
|
|
ctx.strokeStyle = isDisabled.value ? innerIconDisabledColor.value : props.checkedColor ?? '#3283ff';
|
|
ctx.lineWidth = rect.width * 0.16;
|
|
ctx.lineCap = 'round';
|
|
ctx.moveTo(rect.width * 0.10, rect.height * 0.5466)
|
|
ctx.lineTo(rect.width * 0.3666, rect.height * 0.8134)
|
|
ctx.lineTo(rect.width * 0.90, rect.height * 0.28)
|
|
ctx.stroke()
|
|
}
|
|
} else if(innerIcon.value == 'dot'){
|
|
if(radioChecked.value) {
|
|
ctx.fillStyle = isDisabled.value ? innerIconDisabledColor.value : 'white';
|
|
ctx.arc(x, y, rect.width * 0.22, 0, Math.PI * 2)
|
|
ctx.fill()
|
|
}
|
|
}
|
|
ctx.update();
|
|
}
|
|
watchEffect(update)
|
|
|
|
onMounted(()=>{
|
|
nextTick(update)
|
|
})
|
|
// #endif
|
|
|
|
</script>
<style lang="scss">
@import './index-u';
</style>
|