Skip to content

Commit

Permalink
feat(rating): Add Rating component (jd-solanki#60)
Browse files Browse the repository at this point in the history
Co-authored-by: JD Solanki <jdsolanki0001@gmail.com>
  • Loading branch information
brojor and jd-solanki committed Nov 27, 2022
1 parent 2eb7394 commit 023f169
Show file tree
Hide file tree
Showing 16 changed files with 999 additions and 835 deletions.
1 change: 1 addition & 0 deletions packages/anu-vue/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export { AList } from './list'
export { AListItem } from './list-item'
export { AMenu } from './menu'
export { ARadio } from './radio'
export { ARating } from './rating'
export { ASelect } from './select'
export { ASwitch } from './switch'
export { ATable } from './table'
Expand Down
164 changes: 164 additions & 0 deletions packages/anu-vue/src/components/rating/ARating.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { computed, defineComponent, ref, toRef } from 'vue'
import { useLayer, useProps as useLayerProps } from '@/composables/useLayer'
import { disabled, readonly } from '@/composables/useProps'

export const ARating = defineComponent({
name: 'ARating',
props: {
// color: { default: 'primary' },
...useLayerProps({
color: {
default: 'warning',
},
}),

/**
* Bind v-model value to rating
*/
modelValue: {
type: Number,
default: undefined,
},

/**
* Sets amount of rating items
*/
length: {
type: [Number, String],
default: 5,
},

/**
* Allows the award of half a point
*/
halve: {
type: Boolean,
default: false,
},

/**
* Sets empty icon
*/
emptyIcon: {
type: String,
default: 'i-bx:star',
},

/**
* Sets half-filled icon
*/
halfIcon: {
type: String,
default: 'i-bx:bxs-star-half',
},

/**
* Sets filled icon
*/
fullIcon: {
type: String,
default: 'i-bx:bxs-star',
},

/**
* Allows to see visual changes of value on hover
*/
noHoverHint: {
type: Boolean,
default: false,
},

/**
* Animate icon on hover
*/
animate: {
type: Boolean,
default: false,
},

/**
* Make rating component readonly
*/
readonly,

/**
* Disable rating selection
*/
disabled,
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const { getLayerClasses } = useLayer()

const { styles, classes } = getLayerClasses(
toRef(props, 'color'),
ref(''),
ref(false),
)

const rating = ref(0)
const isHovered = ref(false)

const visibleRating = computed(() =>
!props.noHoverHint
&& !props.readonly
&& !props.disabled
&& isHovered.value
? rating.value
: props.modelValue ?? 0,
)

const items = computed(() =>
Array.from({ length: Number(props.length) }, (_, i) => i + 1).map(item =>
item <= visibleRating.value
? props.fullIcon
: item - visibleRating.value === 0.5
? props.halfIcon
: props.emptyIcon,
),
)

const handleClick = () => {
emit('update:modelValue', rating.value)
}

const onMouseEnter = (e: MouseEvent, index: number) => {
isHovered.value = true

const { offsetX, target } = e
if (target instanceof HTMLElement) {
const widthPercentage = (offsetX * 100) / target.clientWidth
props.halve
? (rating.value = widthPercentage < 50 ? index + 0.5 : index + 1)
: (rating.value = index + 1)
}
}

const onMouseLeave = () => {
isHovered.value = false
}

return () => (
<div
class={[
'a-rating flex',
(props.animate && !props.readonly && !props.disabled) && 'a-rating-animated',
props.readonly && 'a-rating-readonly pointer-events-none',
props.disabled && 'a-rating-disabled pointer-events-none',
...classes.value,
]}
style={[...styles.value]}
>

{items.value.map((icon, i) => {
return <i class={['cursor-pointer', icon]}
onClick={handleClick}
onMouseenter={(event => onMouseEnter(event, i))}
onMouseleave={onMouseLeave}/>
})}
</div>
)
},
})

export type ARating = InstanceType<typeof ARating>
2 changes: 2 additions & 0 deletions packages/anu-vue/src/components/rating/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ARating } from './ARating'

5 changes: 5 additions & 0 deletions packages/anu-vue/src/presets/theme-default/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ const themeShortcuts: Exclude<Preset['shortcuts'], undefined> = [
// 馃憠 Menu
'a-menu': 'z-[51] shadow-xl [--a-transition-slide-up-transform:10px]',

// 馃憠 Rating
'a-rating': 'uno-layer-base-text-xl',
'a-rating-animated': 'i:(transition-transform ease-in-out duration-250) hover:i:scale-125',
'a-rating-disabled': 'opacity-50',

// 馃憠 Radio
'a-radio-circle': 'border-solid h-5 w-5 border-(2 a-border) rounded-full mr-2 p-1 after:(duration-250 ease-in-out)', // 鈩癸笍 :after is inner dot
'a-radio-disabled': 'opacity-50',
Expand Down
1 change: 1 addition & 0 deletions packages/documentation/docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export default defineConfig({
{ text: 'List', link: '/guide/components/list' },
{ text: 'Menu', link: '/guide/components/menu' },
{ text: 'Radio', link: '/guide/components/radio' },
{ text: 'Rating', link: '/guide/components/rating' },
{ text: 'Select', link: '/guide/components/select' },
{ text: 'Switch', link: '/guide/components/switch' },
{ text: 'Table', link: '/guide/components/table' },
Expand Down
15 changes: 15 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingAnimate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<ARating
v-model="rating"
animate
empty-icon="i-bx:heart"
full-icon="i-bx:bxs-heart"
color="danger"
/>
</template>
9 changes: 9 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingBasic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<ARating v-model="rating" />
</template>
12 changes: 12 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingColor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<ARating
v-model="rating"
color="primary"
/>
</template>
14 changes: 14 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingCustomIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<ARating
v-model="rating"
empty-icon="i-bx:heart"
full-icon="i-bx:bxs-heart"
color="danger"
/>
</template>
13 changes: 13 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingHalve.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<ARating
v-model="rating"
halve
class="text-2xl"
/>
</template>
14 changes: 14 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingLength.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<div class="flex flex-col gap-2">
<ARating
v-model="rating"
length="3"
/>
</div>
</template>
13 changes: 13 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingNoHoverHint.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<ARating
v-model="rating"
halve
no-hover-hint
/>
</template>
27 changes: 27 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingSizing.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(0)
</script>

<template>
<div class="flex flex-col gap-2">
<ARating
v-model="rating"
class="text-sm"
/>
<ARating
v-model="rating"
class="text-base"
/>
<ARating
v-model="rating"
class="text-lg"
/>
<ARating v-model="rating" />
<ARating
v-model="rating"
class="text-2xl"
/>
</div>
</template>
16 changes: 16 additions & 0 deletions packages/documentation/docs/demos/rating/DemoRatingStates.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { ref } from 'vue'
const rating = ref(3)
</script>

<template>
<ARating
v-model="rating"
readonly
/>
<ARating
v-model="rating"
disabled
/>
</template>
Loading

0 comments on commit 023f169

Please sign in to comment.