Skip to content

Commit f416666

Browse files
committed
Improvement - VueUiRating - Add layer-under and layer-above scoped slots to customize rating units
1 parent 585835a commit f416666

File tree

1 file changed

+114
-96
lines changed

1 file changed

+114
-96
lines changed

src/components/vue-ui-rating.vue

Lines changed: 114 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const props = defineProps({
3333
const uid = ref(createUid());
3434
const isTooltip = ref(false);
3535
const hoveredValue = ref(undefined);
36+
const focusedValue = ref(undefined);
3637
const units = ref([]);
3738
const slots = useSlots();
3839
@@ -214,104 +215,117 @@ defineExpose({
214215
class="vue-ui-rating-unit-container"
215216
:style="`position:relative;height:${FINAL_CONFIG.style.itemSize}px;width:${FINAL_CONFIG.style.itemSize}px`"
216217
>
217-
<!-- IMAGE FIRST LAYER -->
218-
<img
219-
:data-cy="`rating-image-${i}`"
220-
v-if="isImage"
221-
:src="FINAL_CONFIG.style.image.src"
222-
:height="FINAL_CONFIG.style.itemSize"
223-
:width="FINAL_CONFIG.style.itemSize"
224-
class="vue-ui-rating-unit"
225-
:style="`position:absolute;top:0;left:0;opacity:${!isNaN(hoveredValue) ? getInactiveFill(value, true) : FINAL_CONFIG.style.image.inactiveOpacity}`"
226-
/>
227-
228-
<!-- STAR FIRST LAYER -->
229-
<svg
230-
:xmlns="XMLNS"
231-
v-else
232-
viewBox="0 0 100 100"
233-
:height="FINAL_CONFIG.style.itemSize"
234-
:width="FINAL_CONFIG.style.itemSize"
235-
class="vue-ui-rating-unit"
236-
>
237-
<defs>
238-
<radialGradient
239-
cx="50%" cy="50%" r="50%" fx="50%" fy="50%"
240-
:id="`star_gradient_under_${uid}`"
241-
>
242-
<stop offset="0%" :stop-color="`${shiftHue(FINAL_CONFIG.style.star.activeColor, 0.05)}`"/>
243-
<stop offset="100%" :stop-color="FINAL_CONFIG.style.star.activeColor" />
244-
</radialGradient>
245-
</defs>
246-
<polygon
247-
:data-cy="`rating-shape-${i}`"
248-
:points="createStar({
249-
plot: { x: 50, y: 50 },
250-
radius: 30,
251-
apexes: FINAL_CONFIG.style.star.apexes
252-
})"
253-
:fill="
254-
!isNaN(hoveredValue)
255-
? getInactiveFill(value)
256-
: FINAL_CONFIG.style.star.inactiveColor
257-
"
258-
:stroke="
259-
FINAL_CONFIG.style.star.borderColor
260-
? FINAL_CONFIG.style.star.borderColor
261-
: hoveredValue
262-
? getInactiveFill(value)
263-
: FINAL_CONFIG.style.star.inactiveColor
264-
"
265-
:stroke-width="FINAL_CONFIG.style.star.borderWidth"
266-
stroke-linecap="round"
267-
stroke-linejoin="round"
218+
<!-- LAYER SLOTS -->
219+
<div v-if="$slots['layer-under'] || $slots['layer-above']" style="position:relative">
220+
<div v-if="$slots['layer-under']" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%">
221+
<slot name="layer-under" v-bind="{ value, size: FINAL_CONFIG.style.itemSize, hoveredValue, focusedValue }"/>
222+
</div>
223+
<div v-if="$slots['layer-above']" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%">
224+
<slot name="layer-above" v-bind="{ value, size: FINAL_CONFIG.style.itemSize, hoveredValue, focusedValue }"/>
225+
</div>
226+
</div>
227+
228+
229+
<template v-else>
230+
<!-- IMAGE FIRST LAYER -->
231+
<img
232+
:data-cy="`rating-image-${i}`"
233+
v-if="isImage"
234+
:src="FINAL_CONFIG.style.image.src"
235+
:height="FINAL_CONFIG.style.itemSize"
236+
:width="FINAL_CONFIG.style.itemSize"
237+
class="vue-ui-rating-unit"
238+
:style="`position:absolute;top:0;left:0;opacity:${!isNaN(hoveredValue) ? getInactiveFill(value, true) : FINAL_CONFIG.style.image.inactiveOpacity}`"
268239
/>
269-
</svg>
270-
271-
<!-- IMAGE SECOND LAYER -->
272-
<img
273-
:data-cy="`rating-image-overlay-${i}`"
274-
v-if="isImage"
275-
:src="FINAL_CONFIG.style.image.src"
276-
:alt="`${FINAL_CONFIG.style.image.alt} ${value}`"
277-
:height="FINAL_CONFIG.style.itemSize"
278-
:width="FINAL_CONFIG.style.itemSize"
279-
:id="`active_${uid}_${value}`"
280-
class="vue-ui-rating-unit"
281-
:style="`position:absolute;top:0;left:0;clip:rect(0px,${calcShapeFill(i, true) * FINAL_CONFIG.style.itemSize}px,${FINAL_CONFIG.style.itemSize}px,0px`"
282-
/>
283-
284-
<!-- STAR SECOND LAYER -->
285-
<svg
286-
:xmlns="XMLNS"
287-
:data-cy="`rating-shape-overlay-${i}`"
288-
v-else
289-
:viewBox="`0 0 ${calcShapeFill(i)} 100`"
290-
:height="FINAL_CONFIG.style.itemSize"
291-
class="vue-ui-rating-unit"
292-
:id="`active_${uid}_${value}`"
293-
style="position:absolute;top:0;left:0"
294-
>
295-
<defs>
296-
<radialGradient
297-
cx="50%" cy="50%" r="50%" fx="50%" fy="50%"
298-
:id="`star_gradient_over_${uid}`"
299-
>
300-
<stop offset="0%" :stop-color="`${shiftHue(FINAL_CONFIG.style.star.activeColor, 0.05)}`"/>
301-
<stop offset="100%" :stop-color="FINAL_CONFIG.style.star.activeColor" />
302-
</radialGradient>
303-
</defs>
304-
305-
<polygon
306-
:points="createStar({
307-
plot: { x: 50, y: 50 },
308-
radius: 30,
309-
apexes: FINAL_CONFIG.style.star.apexes
310-
})"
311-
:fill="FINAL_CONFIG.style.star.useGradient ? `url(#star_gradient_over_${uid})` : FINAL_CONFIG.style.star.activeColor"
312-
:stroke="FINAL_CONFIG.style.star.activeColor"
240+
241+
<!-- STAR FIRST LAYER -->
242+
<svg
243+
:xmlns="XMLNS"
244+
v-else
245+
viewBox="0 0 100 100"
246+
:height="FINAL_CONFIG.style.itemSize"
247+
:width="FINAL_CONFIG.style.itemSize"
248+
class="vue-ui-rating-unit"
249+
>
250+
<defs>
251+
<radialGradient
252+
cx="50%" cy="50%" r="50%" fx="50%" fy="50%"
253+
:id="`star_gradient_under_${uid}`"
254+
>
255+
<stop offset="0%" :stop-color="`${shiftHue(FINAL_CONFIG.style.star.activeColor, 0.05)}`"/>
256+
<stop offset="100%" :stop-color="FINAL_CONFIG.style.star.activeColor" />
257+
</radialGradient>
258+
</defs>
259+
<polygon
260+
:data-cy="`rating-shape-${i}`"
261+
:points="createStar({
262+
plot: { x: 50, y: 50 },
263+
radius: 30,
264+
apexes: FINAL_CONFIG.style.star.apexes
265+
})"
266+
:fill="
267+
!isNaN(hoveredValue)
268+
? getInactiveFill(value)
269+
: FINAL_CONFIG.style.star.inactiveColor
270+
"
271+
:stroke="
272+
FINAL_CONFIG.style.star.borderColor
273+
? FINAL_CONFIG.style.star.borderColor
274+
: hoveredValue
275+
? getInactiveFill(value)
276+
: FINAL_CONFIG.style.star.inactiveColor
277+
"
278+
:stroke-width="FINAL_CONFIG.style.star.borderWidth"
279+
stroke-linecap="round"
280+
stroke-linejoin="round"
281+
/>
282+
</svg>
283+
284+
<!-- IMAGE SECOND LAYER -->
285+
<img
286+
:data-cy="`rating-image-overlay-${i}`"
287+
v-if="isImage"
288+
:src="FINAL_CONFIG.style.image.src"
289+
:alt="`${FINAL_CONFIG.style.image.alt} ${value}`"
290+
:height="FINAL_CONFIG.style.itemSize"
291+
:width="FINAL_CONFIG.style.itemSize"
292+
:id="`active_${uid}_${value}`"
293+
class="vue-ui-rating-unit"
294+
:style="`position:absolute;top:0;left:0;clip:rect(0px,${calcShapeFill(i, true) * FINAL_CONFIG.style.itemSize}px,${FINAL_CONFIG.style.itemSize}px,0px`"
313295
/>
314-
</svg>
296+
297+
<!-- STAR SECOND LAYER -->
298+
<svg
299+
:xmlns="XMLNS"
300+
:data-cy="`rating-shape-overlay-${i}`"
301+
v-else
302+
:viewBox="`0 0 ${calcShapeFill(i)} 100`"
303+
:height="FINAL_CONFIG.style.itemSize"
304+
class="vue-ui-rating-unit"
305+
:id="`active_${uid}_${value}`"
306+
style="position:absolute;top:0;left:0"
307+
>
308+
<defs>
309+
<radialGradient
310+
cx="50%" cy="50%" r="50%" fx="50%" fy="50%"
311+
:id="`star_gradient_over_${uid}`"
312+
>
313+
<stop offset="0%" :stop-color="`${shiftHue(FINAL_CONFIG.style.star.activeColor, 0.05)}`"/>
314+
<stop offset="100%" :stop-color="FINAL_CONFIG.style.star.activeColor" />
315+
</radialGradient>
316+
</defs>
317+
318+
<polygon
319+
:points="createStar({
320+
plot: { x: 50, y: 50 },
321+
radius: 30,
322+
apexes: FINAL_CONFIG.style.star.apexes
323+
})"
324+
:fill="FINAL_CONFIG.style.star.useGradient ? `url(#star_gradient_over_${uid})` : FINAL_CONFIG.style.star.activeColor"
325+
:stroke="FINAL_CONFIG.style.star.activeColor"
326+
/>
327+
</svg>
328+
</template>
315329

316330
<!-- MOUSE TRAPS -->
317331
<svg
@@ -333,6 +347,8 @@ defineExpose({
333347
@click="rate(value)"
334348
@mouseenter="hoveredValue = value"
335349
@mouseleave="hoveredValue = undefined"
350+
@focus="focusedValue = value"
351+
@blur="focusedValue = undefined"
336352
tabindex="0"
337353
@keyup.enter="rate(value)"
338354
/>
@@ -347,6 +363,8 @@ defineExpose({
347363
fill="transparent"
348364
@mouseenter="hoveredValue = value"
349365
@mouseleave="hoveredValue = undefined"
366+
@focus="focusedValue = value"
367+
@blur="focusedValue = undefined"
350368
/>
351369
</svg>
352370
<template v-if="FINAL_CONFIG.style.tooltip.show && hasBreakdown && isReadonly">

0 commit comments

Comments
 (0)