Add form support to `Rating`

pull/6632/head
Mert Sincan 2024-10-18 16:57:59 +01:00
parent 2a63b3fdce
commit 97e27ad77a
3 changed files with 37 additions and 30 deletions

View File

@ -1,19 +1,11 @@
<script> <script>
import BaseComponent from '@primevue/core/basecomponent'; import BaseEditableHolder from '@primevue/core/baseeditableholder';
import RatingStyle from 'primevue/rating/style'; import RatingStyle from 'primevue/rating/style';
export default { export default {
name: 'BaseRating', name: 'BaseRating',
extends: BaseComponent, extends: BaseEditableHolder,
props: { props: {
modelValue: {
type: Number,
default: null
},
disabled: {
type: Boolean,
default: false
},
readonly: { readonly: {
type: Boolean, type: Boolean,
default: false default: false

View File

@ -1,13 +1,13 @@
<template> <template>
<div :class="cx('root')" v-bind="ptmi('root')"> <div :class="cx('root')" v-bind="ptmi('root')">
<template v-for="value in stars" :key="value"> <template v-for="value in stars" :key="value">
<div :class="cx('option', { value })" @click="onOptionClick($event, value)" v-bind="getPTOptions('option', value)" :data-p-active="value <= modelValue" :data-p-focused="value === focusedOptionIndex"> <div :class="cx('option', { value })" @click="onOptionClick($event, value)" v-bind="getPTOptions('option', value)" :data-p-active="value <= d_value" :data-p-focused="value === focusedOptionIndex">
<span class="p-hidden-accessible" v-bind="ptm('hiddenOptionInputContainer')" :data-p-hidden-accessible="true"> <span class="p-hidden-accessible" v-bind="ptm('hiddenOptionInputContainer')" :data-p-hidden-accessible="true">
<input <input
type="radio" type="radio"
:value="value" :value="value"
:name="name" :name="d_name"
:checked="modelValue === value" :checked="d_value === value"
:disabled="disabled" :disabled="disabled"
:readonly="readonly" :readonly="readonly"
:aria-label="starAriaLabel(value)" :aria-label="starAriaLabel(value)"
@ -17,7 +17,7 @@
v-bind="ptm('hiddenOptionInput')" v-bind="ptm('hiddenOptionInput')"
/> />
</span> </span>
<slot v-if="value <= modelValue" name="onicon" :value="value" :class="cx('onIcon')"> <slot v-if="value <= d_value" name="onicon" :value="value" :class="cx('onIcon')">
<component :is="onIcon ? 'span' : 'StarFillIcon'" :class="[cx('onIcon'), onIcon]" v-bind="ptm('onIcon')" /> <component :is="onIcon ? 'span' : 'StarFillIcon'" :class="[cx('onIcon'), onIcon]" v-bind="ptm('onIcon')" />
</slot> </slot>
<slot v-else name="officon" :value="value" :class="cx('offIcon')"> <slot v-else name="officon" :value="value" :class="cx('offIcon')">
@ -29,8 +29,8 @@
</template> </template>
<script> <script>
import { focus, getFirstFocusableElement } from '@primeuix/utils/dom';
import { UniqueComponentId } from '@primevue/core/utils'; import { UniqueComponentId } from '@primevue/core/utils';
import { getFirstFocusableElement, focus } from '@primeuix/utils/dom';
import BanIcon from '@primevue/icons/ban'; import BanIcon from '@primevue/icons/ban';
import StarIcon from '@primevue/icons/star'; import StarIcon from '@primevue/icons/star';
import StarFillIcon from '@primevue/icons/starfill'; import StarFillIcon from '@primevue/icons/starfill';
@ -40,27 +40,27 @@ export default {
name: 'Rating', name: 'Rating',
extends: BaseRating, extends: BaseRating,
inheritAttrs: false, inheritAttrs: false,
emits: ['update:modelValue', 'change', 'focus', 'blur'], emits: ['change', 'focus', 'blur'],
data() { data() {
return { return {
name: this.$attrs.name, d_name: this.name,
focusedOptionIndex: -1, focusedOptionIndex: -1,
isFocusVisibleItem: true isFocusVisibleItem: true
}; };
}, },
watch: { watch: {
'$attrs.name': function (newValue) { name: function (newValue) {
this.name = newValue || UniqueComponentId(); this.d_name = newValue || UniqueComponentId();
} }
}, },
mounted() { mounted() {
this.name = this.name || UniqueComponentId(); this.d_name = this.d_name || UniqueComponentId();
}, },
methods: { methods: {
getPTOptions(key, value) { getPTOptions(key, value) {
return this.ptm(key, { return this.ptm(key, {
context: { context: {
active: value <= this.modelValue, active: value <= this.d_value,
focused: value === this.focusedOptionIndex focused: value === this.focusedOptionIndex
} }
}); });
@ -81,13 +81,14 @@ export default {
onBlur(event) { onBlur(event) {
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
this.$emit('blur', event); this.$emit('blur', event);
this.formField.onBlur?.();
}, },
onChange(event, value) { onChange(event, value) {
this.onOptionSelect(event, value); this.onOptionSelect(event, value);
this.isFocusVisibleItem = true; this.isFocusVisibleItem = true;
}, },
onOptionSelect(event, value) { onOptionSelect(event, value) {
if (this.focusedOptionIndex === value || this.modelValue === value) { if (this.focusedOptionIndex === value || this.d_value === value) {
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
this.updateModel(event, null); this.updateModel(event, null);
} else { } else {
@ -96,7 +97,7 @@ export default {
} }
}, },
updateModel(event, value) { updateModel(event, value) {
this.$emit('update:modelValue', value); this.updateValue(value, event);
this.$emit('change', { originalEvent: event, value }); this.$emit('change', { originalEvent: event, value });
}, },
starAriaLabel(value) { starAriaLabel(value) {
@ -104,9 +105,9 @@ export default {
} }
}, },
components: { components: {
StarFillIcon: StarFillIcon, StarFillIcon,
StarIcon: StarIcon, StarIcon,
BanIcon: BanIcon BanIcon
} }
}; };
</script> </script>

View File

@ -43,6 +43,10 @@ const theme = ({ dt }) => `
.p-rating-option-active .p-rating-icon { .p-rating-option-active .p-rating-icon {
color: ${dt('rating.icon.active.color')}; color: ${dt('rating.icon.active.color')};
} }
.p-rating-icon.p-invalid { /* @todo */
stroke: ${dt('rating.invalid.icon.color')};
}
`; `;
const classes = { const classes = {
@ -53,15 +57,25 @@ const classes = {
'p-disabled': props.disabled 'p-disabled': props.disabled
} }
], ],
option: ({ instance, props, value }) => [ option: ({ instance, value }) => [
'p-rating-option', 'p-rating-option',
{ {
'p-rating-option-active': value <= props.modelValue, 'p-rating-option-active': value <= instance.d_value,
'p-focus-visible': value === instance.focusedOptionIndex && instance.isFocusVisibleItem 'p-focus-visible': value === instance.focusedOptionIndex && instance.isFocusVisibleItem
} }
], ],
onIcon: 'p-rating-icon p-rating-on-icon', onIcon: ({ instance }) => [
offIcon: 'p-rating-icon p-rating-off-icon' 'p-rating-icon p-rating-on-icon',
{
'p-invalid': instance.$invalid
}
],
offIcon: ({ instance }) => [
'p-rating-icon p-rating-off-icon',
{
'p-invalid': instance.$invalid
}
]
}; };
export default BaseStyle.extend({ export default BaseStyle.extend({