parent
82d4a580e9
commit
f8b519ea6c
@ -0,0 +1,96 @@
|
|||||||
|
@rate-prefix-cls: rc-rate;
|
||||||
|
@rate-star-color: #f5a623;
|
||||||
|
@font-size-base: 13px;
|
||||||
|
|
||||||
|
.@{rate-prefix-cls} {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
font-size: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&-disabled &-star {
|
||||||
|
&:before,
|
||||||
|
&-content:before {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-star {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 8px;
|
||||||
|
position: relative;
|
||||||
|
transition: all .3s;
|
||||||
|
color: #e9e9e9;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
&-first,
|
||||||
|
&-second {
|
||||||
|
transition: all .3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-focused, &:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-first {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-half &-first,
|
||||||
|
&-half &-second {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-half &-first,
|
||||||
|
&-full &-second {
|
||||||
|
color: @rate-star-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-half:hover &-first,
|
||||||
|
&-full:hover &-second {
|
||||||
|
color: tint(@rate-star-color,30%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@icon-url: "//at.alicdn.com/t/font_r5u29ls31bgldi";
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'anticon';
|
||||||
|
src: url('@{icon-url}.eot'); /* IE9*/
|
||||||
|
src: url('@{icon-url}.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('@{icon-url}.woff') format('woff'), /* chrome、firefox */ url('@{icon-url}.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ url('@{icon-url}.svg#iconfont') format('svg'); /* iOS 4.1- */
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
font-style: normal;
|
||||||
|
vertical-align: baseline;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: none;
|
||||||
|
line-height: 1;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
&:before {
|
||||||
|
display: block;
|
||||||
|
font-family: "anticon" !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon-star:before { content: "\e660"; };
|
@ -0,0 +1,84 @@
|
|||||||
|
import Rate from '../index'
|
||||||
|
import '../assets/index.less'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange (v) {
|
||||||
|
console.log('selected star', v)
|
||||||
|
},
|
||||||
|
onFocus () {
|
||||||
|
console.dir('focus')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const { onChange, onFocus } = this
|
||||||
|
const scopedSlots = (
|
||||||
|
<i class='anticon anticon-star' />
|
||||||
|
)
|
||||||
|
const rateProps = {
|
||||||
|
props: {
|
||||||
|
defaultValue: 2.5,
|
||||||
|
allowHalf: true,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
change: onChange,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontSize: '50px', marginTop: '24px',
|
||||||
|
},
|
||||||
|
scopedSlots: {
|
||||||
|
character: scopedSlots,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const rateProps1 = {
|
||||||
|
props: {
|
||||||
|
defaultValue: 2,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
change: onChange,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontSize: '50px', marginTop: '24px',
|
||||||
|
},
|
||||||
|
scopedSlots: {
|
||||||
|
character: scopedSlots,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div style='margin: 100px'>
|
||||||
|
<Rate
|
||||||
|
defaultValue={2.5}
|
||||||
|
onChange={onChange}
|
||||||
|
onFocus={onFocus}
|
||||||
|
style='fontSize: 40px'
|
||||||
|
allowHalf
|
||||||
|
allowClear={false}
|
||||||
|
autoFocus
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<Rate
|
||||||
|
defaultValue={2.5}
|
||||||
|
onChange={onChange}
|
||||||
|
style='fontSize: 50px; marginTop: 24px'
|
||||||
|
allowHalf
|
||||||
|
character='$'
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<Rate
|
||||||
|
{...rateProps}
|
||||||
|
>
|
||||||
|
</Rate>
|
||||||
|
<br />
|
||||||
|
<Rate
|
||||||
|
{...rateProps1}
|
||||||
|
>
|
||||||
|
</Rate>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
// export this package's api
|
||||||
|
import Rate from './src/'
|
||||||
|
export default Rate
|
@ -0,0 +1,216 @@
|
|||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
import KeyCode from '../../_util/KeyCode'
|
||||||
|
import { initDefaultProps, hasProp, getOptionProps } from '../../_util/props-util'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import { getOffsetLeft } from './util'
|
||||||
|
import Star from './Star'
|
||||||
|
|
||||||
|
const rateProps = {
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
value: PropTypes.number,
|
||||||
|
defaultValue: PropTypes.number,
|
||||||
|
count: PropTypes.number,
|
||||||
|
allowHalf: PropTypes.bool,
|
||||||
|
allowClear: PropTypes.bool,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
character: PropTypes.any,
|
||||||
|
tabIndex: PropTypes.number,
|
||||||
|
autoFocus: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Rate',
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: initDefaultProps(rateProps, {
|
||||||
|
defaultValue: 0,
|
||||||
|
count: 5,
|
||||||
|
allowHalf: false,
|
||||||
|
allowClear: true,
|
||||||
|
prefixCls: 'rc-rate',
|
||||||
|
tabIndex: 0,
|
||||||
|
character: '★',
|
||||||
|
}),
|
||||||
|
modal: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change',
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
let value = this.value
|
||||||
|
if (!hasProp(this, 'value')) {
|
||||||
|
value = this.defaultValue
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sValue: value,
|
||||||
|
focused: false,
|
||||||
|
cleanedValue: null,
|
||||||
|
hoverValue: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.autoFocus && !this.disabled) {
|
||||||
|
this.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value (val) {
|
||||||
|
this.setState({
|
||||||
|
sValue: val,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onHover (event, index) {
|
||||||
|
const hoverValue = this.getStarValue(index, event.pageX)
|
||||||
|
const { cleanedValue } = this
|
||||||
|
if (hoverValue !== cleanedValue) {
|
||||||
|
this.setState({
|
||||||
|
hoverValue,
|
||||||
|
cleanedValue: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$emit('hover-change', hoverValue)
|
||||||
|
},
|
||||||
|
onMouseLeave () {
|
||||||
|
this.setState({
|
||||||
|
hoverValue: undefined,
|
||||||
|
cleanedValue: null,
|
||||||
|
})
|
||||||
|
this.$emit('hover-change', undefined)
|
||||||
|
},
|
||||||
|
onClick (event, index) {
|
||||||
|
const value = this.getStarValue(index, event.pageX)
|
||||||
|
let isReset = false
|
||||||
|
if (this.allowClear) {
|
||||||
|
isReset = value === this.sValue
|
||||||
|
}
|
||||||
|
this.onMouseLeave(true)
|
||||||
|
this.changeValue(isReset ? 0 : value)
|
||||||
|
this.setState({
|
||||||
|
cleanedValue: isReset ? value : null,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onFocus () {
|
||||||
|
this.setState({
|
||||||
|
focused: true,
|
||||||
|
})
|
||||||
|
this.$emit('focus')
|
||||||
|
},
|
||||||
|
onBlur () {
|
||||||
|
this.setState({
|
||||||
|
focused: false,
|
||||||
|
})
|
||||||
|
this.$emit('blur')
|
||||||
|
},
|
||||||
|
onKeyDown (event) {
|
||||||
|
const { keyCode } = event
|
||||||
|
const { count, allowHalf } = this
|
||||||
|
let { sValue } = this
|
||||||
|
if (keyCode === KeyCode.RIGHT && sValue < count) {
|
||||||
|
if (allowHalf) {
|
||||||
|
sValue += 0.5
|
||||||
|
} else {
|
||||||
|
sValue += 1
|
||||||
|
}
|
||||||
|
this.changeValue(sValue)
|
||||||
|
event.preventDefault()
|
||||||
|
} else if (keyCode === KeyCode.LEFT && sValue > 0) {
|
||||||
|
if (allowHalf) {
|
||||||
|
sValue -= 0.5
|
||||||
|
} else {
|
||||||
|
sValue -= 1
|
||||||
|
}
|
||||||
|
this.changeValue(sValue)
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
this.$emit('keydown', event)
|
||||||
|
},
|
||||||
|
getStarDOM (index) {
|
||||||
|
return this.$refs['stars' + index].$el
|
||||||
|
},
|
||||||
|
getStarValue (index, x) {
|
||||||
|
let value = index + 1
|
||||||
|
if (this.allowHalf) {
|
||||||
|
const starEle = this.getStarDOM(index)
|
||||||
|
const leftDis = getOffsetLeft(starEle)
|
||||||
|
const width = starEle.clientWidth
|
||||||
|
if ((x - leftDis) < width / 2) {
|
||||||
|
value -= 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
focus () {
|
||||||
|
if (!this.disabled) {
|
||||||
|
this.$refs.rateRef.focus()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// blur () {
|
||||||
|
// if (!this.disabled) {
|
||||||
|
// this.$refs.rateRef.blur()
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
changeValue (value) {
|
||||||
|
if (!hasProp(this, 'value')) {
|
||||||
|
this.setState({
|
||||||
|
sValue: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$emit('change', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const {
|
||||||
|
count,
|
||||||
|
allowHalf,
|
||||||
|
prefixCls,
|
||||||
|
disabled,
|
||||||
|
character,
|
||||||
|
tabIndex,
|
||||||
|
} = getOptionProps(this)
|
||||||
|
const { sValue, hoverValue, focused } = this
|
||||||
|
const stars = []
|
||||||
|
const disabledClass = disabled ? `${prefixCls}-disabled` : ''
|
||||||
|
const slotCharacter = this.$scopedSlots.character
|
||||||
|
for (let index = 0; index < count; index++) {
|
||||||
|
const starProps = {
|
||||||
|
props: {
|
||||||
|
index,
|
||||||
|
disabled,
|
||||||
|
prefixCls: `${prefixCls}-star`,
|
||||||
|
allowHalf,
|
||||||
|
value: hoverValue === undefined ? sValue : hoverValue,
|
||||||
|
character: slotCharacter === undefined ? character : undefined,
|
||||||
|
focused,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
click: this.onClick,
|
||||||
|
hover: this.onHover,
|
||||||
|
},
|
||||||
|
key: index,
|
||||||
|
ref: `stars${index}`,
|
||||||
|
scopedSlots: this.$scopedSlots,
|
||||||
|
}
|
||||||
|
stars.push(
|
||||||
|
<Star
|
||||||
|
{...starProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ul
|
||||||
|
class={classNames(prefixCls, disabledClass)}
|
||||||
|
onMouseleave={disabled ? null : this.onMouseLeave}
|
||||||
|
tabIndex={disabled ? -1 : tabIndex}
|
||||||
|
onFocus={disabled ? null : this.onFocus}
|
||||||
|
onBlur={disabled ? null : this.onBlur}
|
||||||
|
onKeydown={disabled ? null : this.onKeyDown}
|
||||||
|
ref='rateRef'
|
||||||
|
>
|
||||||
|
{stars}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Star',
|
||||||
|
props: {
|
||||||
|
value: PropTypes.number,
|
||||||
|
index: PropTypes.number,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
allowHalf: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
character: PropTypes.any,
|
||||||
|
focused: PropTypes.bool,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onHover (e) {
|
||||||
|
const { index } = this
|
||||||
|
this.$emit('hover', e, index)
|
||||||
|
},
|
||||||
|
onClick (e) {
|
||||||
|
const { index } = this
|
||||||
|
this.$emit('click', e, index)
|
||||||
|
},
|
||||||
|
getClassName () {
|
||||||
|
const { prefixCls, index, value, allowHalf, focused } = this
|
||||||
|
const starValue = index + 1
|
||||||
|
let className = prefixCls
|
||||||
|
if (value === 0 && index === 0 && focused) {
|
||||||
|
className += ` ${prefixCls}-focused`
|
||||||
|
} else if (allowHalf && value + 0.5 === starValue) {
|
||||||
|
className += ` ${prefixCls}-half ${prefixCls}-active`
|
||||||
|
if (focused) {
|
||||||
|
className += ` ${prefixCls}-focused`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
className += starValue <= value ? ` ${prefixCls}-full` : ` ${prefixCls}-zero`
|
||||||
|
if (starValue === value && focused) {
|
||||||
|
className += ` ${prefixCls}-focused`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return className
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const { onHover, onClick, disabled, prefixCls } = this
|
||||||
|
let character = this.character
|
||||||
|
if (character === undefined) {
|
||||||
|
character = this.$scopedSlots.character
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
class={this.getClassName()}
|
||||||
|
onClick={disabled ? null : onClick}
|
||||||
|
onMousemove={disabled ? null : onHover}
|
||||||
|
>
|
||||||
|
<div class={`${prefixCls}-first`}>{character}</div>
|
||||||
|
<div class={`${prefixCls}-second`}>{character}</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
import Rate from './Rate'
|
||||||
|
export default Rate
|
@ -0,0 +1,39 @@
|
|||||||
|
function getScroll (w, top) {
|
||||||
|
let ret = top ? w.pageYOffset : w.pageXOffset
|
||||||
|
const method = top ? 'scrollTop' : 'scrollLeft'
|
||||||
|
if (typeof ret !== 'number') {
|
||||||
|
const d = w.document
|
||||||
|
// ie6,7,8 standard mode
|
||||||
|
ret = d.documentElement[method]
|
||||||
|
if (typeof ret !== 'number') {
|
||||||
|
// quirks mode
|
||||||
|
ret = d.body[method]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClientPosition (elem) {
|
||||||
|
let x
|
||||||
|
let y
|
||||||
|
const doc = elem.ownerDocument
|
||||||
|
const body = doc.body
|
||||||
|
const docElem = doc && doc.documentElement
|
||||||
|
const box = elem.getBoundingClientRect()
|
||||||
|
x = box.left
|
||||||
|
y = box.top
|
||||||
|
x -= docElem.clientLeft || body.clientLeft || 0
|
||||||
|
y -= docElem.clientTop || body.clientTop || 0
|
||||||
|
return {
|
||||||
|
left: x,
|
||||||
|
top: y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOffsetLeft (el) {
|
||||||
|
const pos = getClientPosition(el)
|
||||||
|
const doc = el.ownerDocument
|
||||||
|
const w = doc.defaultView || doc.parentWindow
|
||||||
|
pos.left += getScroll(w)
|
||||||
|
return pos.left
|
||||||
|
}
|
Loading…
Reference in new issue