2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
import { withInstall, withNoopInstall } from '../../utils'
import Radio from './src/radio.vue'
import RadioGroup from './src/radio-group.vue'
export const TnRadio = withInstall(Radio, {
RadioGroup,
})
export default TnRadio
export const TnRadioGroup = withNoopInstall(RadioGroup)
export * from './src/radio'
export * from './src/radio-group'
export type { RadioInstance, RadioGroupInstance } from './src/instance'
@@ -0,0 +1,4 @@
export * from './use-radio-common-props'
export * from './radio-custom'
export * from './use-radio'
export * from './use-radio-group'
@@ -0,0 +1,110 @@
import { computed } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { isEmpty } from '../../../../utils'
import { useRadioCommonProps } from './use-radio-common-props'
import type { CSSProperties } from 'vue'
import type { RadioProps } from '../radio'
export const useRadioCustomStyle = (props: RadioProps) => {
const ns = useNamespace('radio')
const { size, disabled, border, activeColor } = useRadioCommonProps(props)
// 解析颜色
const [bgColorClass, bgColorStyle] = useComponentColor(activeColor, 'bg')
const [textColorClass, textColorStyle] = useComponentColor(
activeColor,
'text'
)
const [borderColorClass, borderColorStyle] = useComponentColor(
activeColor,
'border'
)
// radio所属类
const radioClass = computed<(selected: boolean) => string>(() => {
return (selected: boolean) => {
const cls: string[] = [ns.b()]
// 禁止选择
if (disabled.value) cls.push(ns.m('disabled'))
// 设置尺寸
if (size.value) cls.push(ns.m(size.value))
// 激活样式
if (selected) {
cls.push(ns.m('selected'))
if (textColorClass.value) cls.push(textColorClass.value)
}
// 设置激活时的边框颜色
if (border.value) {
cls.push('tn-border')
if (selected && borderColorClass.value) cls.push(borderColorClass.value)
else cls.push('tn-gray-disabled_border')
} else {
cls.push(ns.m('no-border'))
}
if (props.customClass) cls.push(props.customClass)
return cls.join(' ')
}
})
// radio所属样式
const radioStyle = computed<(selected: boolean) => CSSProperties>(() => {
return (selected: boolean) => {
const style: CSSProperties = {}
// 设置激活时的颜色
if (selected) {
if (border.value && !borderColorClass.value)
style.borderColor =
borderColorStyle.value || 'var(--tn-color-primary)'
if (!textColorClass.value) {
style.color = textColorStyle.value || 'var(--tn-color-primary)'
}
}
if (!isEmpty(props.customStyle)) Object.assign(style, props.customStyle)
return style
}
})
// radio选中点所属类
const radioDotClass = computed<(selected: boolean) => string>(() => {
return (selected: boolean) => {
const cls: string[] = [ns.e('dot')]
if (selected) {
cls.push(ns.em('dot', 'selected'))
if (bgColorClass.value) cls.push(bgColorClass.value)
} else {
cls.push('tn-border tn-gray-disabled_border')
}
return cls.join(' ')
}
})
// radio选中点所属样式
const radioDotStyle = computed<(selected: boolean) => CSSProperties>(() => {
return (selected: boolean) => {
const style: CSSProperties = {}
if (selected && !bgColorClass.value) {
style.backgroundColor = bgColorStyle.value || 'var(--tn-color-primary)'
}
return style
}
})
return {
ns,
radioClass,
radioStyle,
radioDotClass,
radioDotStyle,
}
}
@@ -0,0 +1,46 @@
import { computed, inject } from 'vue'
import { radioGroupKey } from '../../../../tokens'
import { isEmptyDoubleVariableInDefault } from '../../../../utils'
import { useFormDisabled, useFormSize } from '../../../form'
import type { RadioProps } from '../radio'
export const useRadioCommonProps = (props: RadioProps) => {
const radioGroupContext = inject(radioGroupKey, undefined)
// radio当选框尺寸
const size = useFormSize(
computed(() =>
isEmptyDoubleVariableInDefault(props?.size, radioGroupContext?.size)
)
)
// radio单选框是否禁用
const disabled = useFormDisabled(
computed(() => props?.disabled || radioGroupContext?.disabled || false)
)
// radio禁止点击标签进行选择
const labelDisabled = computed(
() => props?.labelDisabled || radioGroupContext?.labelDisabled || false
)
// 是否显示边框
const border = computed(
() => props?.border || radioGroupContext?.border || false
)
// radio激活时的颜色
const activeColor = computed(
() => props?.activeColor || radioGroupContext?.activeColor
)
return {
radioGroupContext,
size,
disabled,
labelDisabled,
border,
activeColor,
}
}
@@ -0,0 +1,38 @@
import { nextTick, watch } from 'vue'
import { useFormItem } from '../../../form'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { debugWarn } from '../../../../utils'
import type { SetupContext } from 'vue'
import type { RadioGroupEmits, RadioGroupProps } from '../radio-group'
export const useRadioGroup = (
props: RadioGroupProps,
emits: SetupContext<RadioGroupEmits>['emit']
) => {
const { formItem } = useFormItem()
// 值更新事件
const changeEvent = (val: RadioGroupProps['modelValue']) => {
emits(UPDATE_MODEL_EVENT, val)
nextTick(() => {
emits(CHANGE_EVENT, val)
})
}
// 当值发生改变时触发校验
watch(
() => props.modelValue,
() => {
if (props.validateEvent) {
formItem?.validate?.('change').catch((err) => {
debugWarn(err)
})
}
}
)
return {
changeEvent,
}
}
@@ -0,0 +1,49 @@
import { computed, nextTick } from 'vue'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants'
import { useRadioCommonProps } from './use-radio-common-props'
import type { SetupContext } from 'vue'
import type { RadioEmits, RadioProps } from '../radio'
export const useRadio = (
props: RadioProps,
emits: SetupContext<RadioEmits>['emit']
) => {
const {
radioGroupContext: radioGroup,
disabled,
labelDisabled,
} = useRadioCommonProps(props)
// 判断是否为单选组
const isGroup = computed(() => !!radioGroup)
const modelValue = computed<RadioProps['modelValue']>({
get() {
return isGroup.value ? radioGroup!.modelValue : props.modelValue!
},
set(val) {
if (isGroup.value) {
radioGroup!.changeEvent(val)
} else {
emits(UPDATE_MODEL_EVENT, val)
}
},
})
// radio标签点击事件
const radioClickEvent = (type: 'radio' | 'label') => {
if (disabled.value) return
if (type === 'label' && labelDisabled.value) return
modelValue.value = props.label
nextTick(() => {
emits(CHANGE_EVENT, props.label)
})
}
return {
isGroup,
modelValue,
radioClickEvent,
}
}
@@ -0,0 +1,5 @@
import type Radio from './radio.vue'
import type RadioGroup from './radio-group.vue'
export type RadioInstance = InstanceType<typeof Radio>
export type RadioGroupInstance = InstanceType<typeof RadioGroup>
@@ -0,0 +1,39 @@
import { buildProps, isBoolean, isNumber, isString } from '../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import { radioBaseProps } from '../../base/common-props/radio'
import type { ExtractPropTypes } from 'vue'
export const radioGroupProps = buildProps({
...radioBaseProps,
/**
* @description radio单选组绑定的值
*/
modelValue: {
type: [String, Number, Boolean],
default: '',
},
/**
* @description 每个选项独占一行
*/
wrap: {
type: Boolean,
default: false,
},
/**
* @description 值发生修改时是否触发表单验证
*/
validateEvent: {
type: Boolean,
default: true,
},
})
export const radioGroupEmits = {
[UPDATE_MODEL_EVENT]: (value: string | number | boolean) =>
isString(value) || isNumber(value) || isBoolean(value),
[CHANGE_EVENT]: (value: string | number | boolean) =>
isString(value) || isNumber(value) || isBoolean(value),
}
export type RadioGroupProps = ExtractPropTypes<typeof radioGroupProps>
export type RadioGroupEmits = typeof radioGroupEmits
@@ -0,0 +1,32 @@
<script lang="ts" setup>
import { provide, reactive, toRefs } from 'vue'
import { useNamespace } from '../../../hooks'
import { radioGroupKey } from '../../../tokens'
import { radioGroupEmits, radioGroupProps } from './radio-group'
import { useRadioGroup } from './composables'
const props = defineProps(radioGroupProps)
const emits = defineEmits(radioGroupEmits)
const ns = useNamespace('radio-group')
const { changeEvent } = useRadioGroup(props, emits)
provide(
radioGroupKey,
reactive({
...toRefs(props),
changeEvent,
})
)
</script>
<template>
<view :class="[ns.b(), ns.is('wrap', props.wrap)]">
<slot />
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/radio-group.scss';
</style>
+41
View File
@@ -0,0 +1,41 @@
import { buildProps, isBoolean, isNumber, isString } from '../../../utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../constants'
import { radioBaseProps } from '../../base/common-props/radio'
import { useComponentCustomStyleProp } from '../../base/composables/use-component-common-props'
import type { ExtractPropTypes } from 'vue'
export const radioProps = buildProps({
...radioBaseProps,
/**
* @description radio单选框绑定的值
*/
modelValue: {
type: [String, Number, Boolean],
default: '',
},
/**
* @description radio单选框的值,在使用单选组时,radio的值就是label
*/
label: {
type: [String, Number, Boolean],
default: '',
},
/**
* @description 自定义样式
*/
customStyle: useComponentCustomStyleProp,
/**
* @description 自定义类名
*/
customClass: String,
})
export const radioEmits = {
[UPDATE_MODEL_EVENT]: (value: string | number | boolean) =>
isString(value) || isNumber(value) || isBoolean(value),
[CHANGE_EVENT]: (value: string | number | boolean) =>
isString(value) || isNumber(value) || isBoolean(value),
}
export type RadioProps = ExtractPropTypes<typeof radioProps>
export type RadioEmits = typeof radioEmits
+41
View File
@@ -0,0 +1,41 @@
<script lang="ts" setup>
import { radioEmits, radioProps } from './radio'
import { useRadio, useRadioCustomStyle } from './composables'
const props = defineProps(radioProps)
const emits = defineEmits(radioEmits)
const { isGroup, modelValue, radioClickEvent } = useRadio(props, emits)
const { ns, radioClass, radioDotClass, radioStyle, radioDotStyle } =
useRadioCustomStyle(props)
</script>
<template>
<view
:class="[radioClass(modelValue === label), { [ns.m('group')]: isGroup }]"
:style="radioStyle(modelValue === label)"
@tap.stop="radioClickEvent('label')"
>
<!-- 左边内容 -->
<view
v-if="$slots.left && !$slots.default"
:class="[ns.em('content', 'left')]"
>
<slot name="left" />
</view>
<!-- 选中小点 -->
<view
:class="[radioDotClass(modelValue === label)]"
:style="radioDotStyle(modelValue === label)"
@tap.stop="radioClickEvent('radio')"
/>
<!-- 右边内容 -->
<view v-if="$slots.default" :class="[ns.em('content', 'right')]">
<slot />
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/radio.scss';
</style>