import { makeComponentProps } from '@/composables/component' import { makeTagProps } from '@/composables/tag' import { genericComponent, propsFactory } from '@/utils' import { ExtractPropTypes, ref, shallowRef, watch, computed, onMounted, PropType, } from 'vue' import { USemiCircleXXS } from './UProgressCircleSVG/USemiCircleXXS' import { USemiCircleXS } from './UProgressCircleSVG/USemiCircleXS' import { USemiCircleSM } from './UProgressCircleSVG/USemiCircleSM' import { USemiCircleMD } from './UProgressCircleSVG/USemiCircleMD' import { USemiCircleLG } from './UProgressCircleSVG/USemiCircleLG' export const makeUProgressCircleProps = propsFactory( { progress: { type: Number, default: 0, required: false, }, shape: { type: String, default: 'circle', required: false, }, size: { type: String, required: false, }, label: { type: [String, undefined] as PropType, default: undefined, required: false, }, ...makeComponentProps(), ...makeTagProps(), }, 'UProgressCircle' ) export type UProgressCircleProps = ExtractPropTypes< typeof makeUProgressCircleProps > export const UProgressCircle = genericComponent()({ name: 'UProgressCircle', props: makeUProgressCircleProps(), setup(props) { const progress = ref(0) const size = ref(64) const cx = ref(32) const cy = ref(32) const r = ref(26) const strokeWidth = ref(6) const textSize = ref('text-text-sm') const strokeDasharray = ref(163.3) const strokeDashoffset = ref(98) const rotate = ref('-rotate-90') const progressDashoffset = ref(0) const semiCircleIcon = shallowRef< | typeof USemiCircleXXS | typeof USemiCircleXS | typeof USemiCircleSM | typeof USemiCircleMD | typeof USemiCircleLG | null >(null) const XXS_SCALE = 3.9 const XS_SCALE = 1.59 const SM_SCALE = 1.27 const MD_SCALE = 1.05 const label = computed(() => { return props.label }) const labelSize = computed(() => { return props.size }) onMounted(() => { cx.value = size.value / 2 cy.value = size.value / 2 r.value = size.value / 2 - strokeWidth.value strokeDasharray.value = 2 * Math.PI * r.value strokeDashoffset.value = strokeDasharray.value * ((100 - progress.value) / 100) rotate.value = '-rotate-90' if (props.size === 'xxs') { progressDashoffset.value = (-props.progress / XXS_SCALE) * 3.6 + 360 } else if (props.size === 'xs') { progressDashoffset.value = (-props.progress / XS_SCALE) * 3.6 + 360 } else if (props.size === 'sm') { progressDashoffset.value = (-props.progress / SM_SCALE) * 3.6 + 360 } else if (props.size === 'md') { progressDashoffset.value = (-props.progress / MD_SCALE) * 3.6 + 360 } else if (props.size === 'lg') { progressDashoffset.value = (1 - props.progress / 100) * 395 } }) watch([() => props.size, () => props.shape], ([sizeValue, shapeValue]) => { if (shapeValue === 'semi-circle' && sizeValue === 'xxs') { semiCircleIcon.value = USemiCircleXXS } else if (shapeValue === 'semi-circle' && sizeValue === 'xs') { semiCircleIcon.value = USemiCircleXS } else if (shapeValue === 'semi-circle' && sizeValue === 'sm') { semiCircleIcon.value = USemiCircleSM } else if (shapeValue === 'semi-circle' && sizeValue === 'md') { semiCircleIcon.value = USemiCircleMD } else if (shapeValue === 'semi-circle' && sizeValue === 'lg') { semiCircleIcon.value = USemiCircleLG } }) watch( () => props.progress, (progressValue) => { progress.value = progressValue if (props.size === 'xxs') { progressDashoffset.value = (-progressValue / XXS_SCALE) * 3.6 + 360 } else if (props.size === 'xs') { progressDashoffset.value = (-progressValue / XS_SCALE) * 3.6 + 360 } else if (props.size === 'sm') { progressDashoffset.value = (-progressValue / SM_SCALE) * 3.6 + 360 } else if (props.size === 'md') { progressDashoffset.value = (-progressValue / MD_SCALE) * 3.6 + 360 } else if (props.size === 'lg') { progressDashoffset.value = (1 - progressValue / 100) * 395 } } ) watch( [() => props.size, () => r.value, () => props.progress], ([sizeValue, rValue, progressValue]) => { if (sizeValue === 'xxs') { strokeWidth.value = 6 size.value = 64 textSize.value = 'text-text-sm' } else if (sizeValue === 'xs') { strokeWidth.value = 16 size.value = 160 textSize.value = 'text-display-xs' } else if (sizeValue === 'sm') { strokeWidth.value = 20 size.value = 200 textSize.value = 'text-display-sm' } else if (sizeValue === 'md') { strokeWidth.value = 24 size.value = 240 textSize.value = 'text-display-md' } else { strokeWidth.value = 28 size.value = 280 textSize.value = 'text-display-lg' } cx.value = size.value / 2 cy.value = size.value / 2 r.value = size.value / 2 - strokeWidth.value progress.value = progressValue strokeDasharray.value = 2 * Math.PI * rValue strokeDashoffset.value = strokeDasharray.value * ((100 - progressValue) / 100) rotate.value = '-rotate-90' } ) const textAlignClasses = computed(() => { let alignClasses = '' if ( (props.shape === 'circle' && props.size !== 'xxs') || (props.label === undefined && props.shape === 'circle') ) { alignClasses = `top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2` } else if ( props.shape === 'semi-circle' && props.size !== 'xxs' && props.label === undefined ) { alignClasses = `left-1/2 transform -translate-x-1/2 top-0.33` } else if ( props.shape === 'semi-circle' && props.size === 'xxs' && props.label === undefined ) { alignClasses = `left-1/2 transform -translate-x-1/2 top-0.28` } else if (props.shape === 'semi-circle' && props.label !== undefined) { alignClasses = `left-1/2 transform -translate-x-1/2 top-0.26` } else if ( props.shape === 'circle' && props.size === 'xxs' && props.label !== undefined ) { alignClasses = `gap-5 left-1/2 transform -translate-x-1/2 top-0.37` } return alignClasses }) const TextClasses = computed( () => `absolute ${textAlignClasses.value} text-gray-900 flex flex-col items-center align-center` ) const textClasses = computed(() => ({ [TextClasses.value]: true, })) return () => (
{props.shape === 'circle' ? ( ) : semiCircleIcon.value ? ( ) : null} {label.value !== undefined && labelSize.value !== 'xxs' ? ( {props.label} ) : null} {props.progress}% {label.value !== undefined && labelSize.value === 'xxs' ? ( {props.label} ) : null}
) }, }) export type UProgressCircle = InstanceType