|
|
- <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>
|