UNPKG

@coreui/vue-pro

Version:

UI Components Library for Vue.js

289 lines (270 loc) 8.61 kB
import { PropType, defineComponent, h, ref, watch, withDirectives } from 'vue' import vCTooltip from '../../directives/v-c-tooltip' const CRating = defineComponent({ name: 'CRating', props: { /** * Enables the clearing upon clicking the selected item again. */ allowClear: Boolean, /** * Toggle the disabled state for the component. */ disabled: Boolean, /** * If enabled, only the currently selected icon will be visibly highlighted. */ highlightOnlySelected: Boolean, /** * Specifies the total number of stars to be displayed in the star rating component. This property determines the scale of the rating, such as out of 5 stars, 10 stars, etc. */ itemCount: { type: Number, default: 5, }, /** * The default name for a value passed using v-model. */ modelValue: Number, /** * The name attribute of the radio input elements. */ name: String, /** * Minimum increment value change allowed. */ precision: { type: Number, default: 1, }, /** * Toggle the readonly state for the component. */ readOnly: Boolean, /** * Size the component small, large, or custom if you define custom icons with custom height. * * @values 'sm', 'lg', 'custom' */ size: { type: String, validator: (value: string) => { return ['sm', 'lg', 'custom'].includes(value) }, }, /** * Enable tooltips with default values or set specific labels for each icon. */ tooltips: { type: [Boolean, Array] as PropType<boolean | string[]>, }, /** * The `value` attribute of component. * */ value: Number, }, emits: [ /** * Execute a function when a user changes the selected element. * * @property {number | null} value */ 'change', /** * Execute a function when a user hover the element. * * @property {number | null} value */ 'hover', /** * Emit the new value whenever there’s a change event. */ 'update:modelValue', ], setup(props, { slots, emit }) { const cleared = ref(false) const currentValue = ref((props.modelValue || props.value) ?? null) const hoverValue = ref<number | null>(null) const tooltipValue = ref<number | null>(null) const name = props.name || `name${Math.floor(Math.random() * 1_000_000)}` const uid = `id${Math.floor(Math.random() * 1_000_000)}` watch( () => props.value, () => { if (props.value !== undefined) { currentValue.value = props.value } }, ) watch( () => props.modelValue, () => { if (props.modelValue !== undefined) { currentValue.value = props.modelValue } }, ) const handleMouseEnter = (value: number) => { if (props.disabled || props.readOnly) { return } emit('hover', value) hoverValue.value = value tooltipValue.value = value } const handleMouseLeave = () => { if (props.disabled || props.readOnly) { return } emit('hover', null) hoverValue.value = null } const handleOnChange = (value: number) => { if (props.disabled || props.readOnly) { return } if (cleared.value) { cleared.value = false return } currentValue.value = value emit('change', value) emit('update:modelValue', value) } const handleOnClick = (value: number) => { if (props.disabled || props.readOnly) { return } if (props.allowClear && value === currentValue.value) { emit('change', value) cleared.value = true currentValue.value = null hoverValue.value = null } } return () => h( 'div', { class: [ 'rating', { [`rating-${props.size}`]: props.size, disabled: props.disabled, readonly: props.readOnly, }, ], role: 'radiogroup', }, [ Array.from({ length: props.itemCount }, (_, index) => { const numberOfRadios = 1 / props.precision return withDirectives( h( 'div', { class: 'rating-item', }, [ Array.from({ length: numberOfRadios }, (_, _index) => { const isNotLastItem = _index + 1 < numberOfRadios const value = numberOfRadios === 1 ? index + 1 : index + (_index + 1) * (1 * props.precision) const id = `${uid}${value}` const isItemChecked = () => value === currentValue.value const isItemActive = () => { if ( props.highlightOnlySelected ? hoverValue.value === value : hoverValue.value && hoverValue.value >= value ) { return true } if ( hoverValue.value === null && (props.highlightOnlySelected ? isItemChecked() : currentValue.value && currentValue.value >= value) ) { return true } return false } return [ h( 'label', { class: [ 'rating-item-label', { active: isItemActive(), }, ], for: id, onClick: () => handleOnClick(value), onMouseenter: () => handleMouseEnter(value), onMouseleave: () => handleMouseLeave(), ...(isNotLastItem && { style: { zIndex: 1 / props.precision - _index, position: 'absolute', width: `${props.precision * (_index + 1) * 100}%`, overflow: 'hidden', opacity: 0, }, }), }, { default: () => [ slots.icon ? h( 'div', { class: 'rating-item-custom-icon' }, slots.icon({ value: index + 1 }), ) : h('div', { class: 'rating-item-icon' }), slots.activeIcon && h( 'div', { class: 'rating-item-custom-icon-active' }, slots.activeIcon({ value: index + 1 }), ), ], }, ), h('input', { checked: isItemChecked(), class: 'rating-item-input', disabled: props.disabled || props.readOnly, id: id, name: name, onBlur: () => handleMouseLeave(), onChange: () => handleOnChange(value), onFocus: () => handleMouseEnter(value), type: 'radio', value: value, }), ] }), ], ), props.tooltips ? [ [ vCTooltip, { content: Array.isArray(props.tooltips) ? props.tooltips[index] : index + 1, placement: 'top', }, ], ] : [], ) }), ], ) }, }) export { CRating }