wangxueliang
7 years ago
8 changed files with 501 additions and 1 deletions
@ -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