Browse Source

add vc-rate

pull/9/head
wangxueliang 7 years ago
parent
commit
f8b519ea6c
  1. 96
      components/vc-rate/assets/index.less
  2. 84
      components/vc-rate/demo/simple.jsx
  3. 3
      components/vc-rate/index.js
  4. 216
      components/vc-rate/src/Rate.jsx
  5. 60
      components/vc-rate/src/Star.jsx
  6. 2
      components/vc-rate/src/index.js
  7. 39
      components/vc-rate/src/util.js
  8. 2
      examples/routes.js

96
components/vc-rate/assets/index.less

@ -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"; };

84
components/vc-rate/demo/simple.jsx

@ -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>
)
},
}

3
components/vc-rate/index.js

@ -0,0 +1,3 @@
// export this package's api
import Rate from './src/'
export default Rate

216
components/vc-rate/src/Rate.jsx

@ -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>
)
},
}

60
components/vc-rate/src/Star.jsx

@ -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>
)
},
}

2
components/vc-rate/src/index.js

@ -0,0 +1,2 @@
import Rate from './Rate'
export default Rate

39
components/vc-rate/src/util.js

@ -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
}

2
examples/routes.js

@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
component: import(`../components/locale-provider/demo/${d}`),
component: import(`../components/vc-rate/demo/${d}`),
}
}
export default [

Loading…
Cancel
Save