add vc-slider-demo

pull/9/head
wangxueliang 2018-03-28 11:51:27 +08:00
parent d3a59a5aae
commit 12bd8af6fc
8 changed files with 239 additions and 126 deletions

View File

@ -3,16 +3,22 @@ import Tooltip from '../../vc-tooltip'
import '../assets/index.less' import '../assets/index.less'
import '../../vc-tooltip/assets/bootstrap.less' import '../../vc-tooltip/assets/bootstrap.less'
const { createSliderWithTooltip, Handle } = Slider const { Handle, Range } = Slider
const Range = createSliderWithTooltip(Slider.Range)
export default { export default {
data () { data () {
return { return {
visibles: [],
} }
}, },
methods: {
handleTooltipVisibleChange (index, visible) {
this.visibles[index] = visible
this.visibles = { ...this.visibles }
},
},
render () { render () {
const handle = (props) => { const handle = (h, props) => {
const { value, dragging, index, refStr, ...restProps } = props const { value, dragging, index, refStr, ...restProps } = props
const handleProps = { const handleProps = {
props: { props: {
@ -32,10 +38,66 @@ export default {
placement='top' placement='top'
key={index} key={index}
> >
<Handle {...handleProps} /> <Handle {...handleProps} />
</Tooltip> </Tooltip>
) )
} }
const handleRange = (h, { value, dragging, index, disabled, ...restProps }) => {
const tipFormatter = value => `${value}%`
const handleStyle = [{}]
const tipProps = {}
const {
prefixCls = 'rc-slider-tooltip',
overlay = tipFormatter(value),
placement = 'top',
visible = visible || false,
...restTooltipProps } = tipProps
let handleStyleWithIndex
if (Array.isArray(handleStyle)) {
handleStyleWithIndex = handleStyle[index] || handleStyle[0]
} else {
handleStyleWithIndex = handleStyle
}
const tooltipProps = {
props: {
prefixCls,
overlay,
placement,
visible: (!disabled && (this.visibles[index] || dragging)) || visible,
...restTooltipProps,
},
key: index,
}
const handleProps = {
props: {
value,
...restProps,
},
on: {
mouseenter: () => this.handleTooltipVisibleChange(index, true),
mouseleave: () => this.handleTooltipVisibleChange(index, false),
},
style: {
...handleStyleWithIndex,
},
}
return (
<Tooltip
{...tooltipProps}
>
<Handle
{...handleProps}
/>
</Tooltip>
)
}
const wrapperStyle = 'width: 400px; margin: 50px' const wrapperStyle = 'width: 400px; margin: 50px'
return ( return (
@ -44,10 +106,10 @@ export default {
<p>Slider with custom handle</p> <p>Slider with custom handle</p>
<Slider min={0} max={20} defaultValue={3} handle={handle} /> <Slider min={0} max={20} defaultValue={3} handle={handle} />
</div> </div>
{/* <div style={wrapperStyle}> <div style={wrapperStyle}>
<p>Range with custom handle</p> <p>Range with custom handle</p>
<Range min={0} max={20} defaultValue={[3, 10]} tipFormatter={value => `${value}%`} /> <Range min={0} max={20} defaultValue={[3, 10]} handle={handleRange} />
</div> */} </div>
</div> </div>
) )
}, },

View File

@ -1,8 +1,12 @@
import PropTypes from '../../_util/vue-types' import PropTypes from '../../_util/vue-types'
import addEventListener from '../../_util/Dom/addEventListener' import addEventListener from '../../_util/Dom/addEventListener'
import BaseMixin from '../../_util/BaseMixin' import BaseMixin from '../../_util/BaseMixin'
import { getOptionProps } from '../../_util/props-util'
function noop () {}
export default { export default {
name: 'Handle',
mixins: [BaseMixin], mixins: [BaseMixin],
props: { props: {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
@ -14,6 +18,8 @@ export default {
value: PropTypes.number, value: PropTypes.number,
tabIndex: PropTypes.number, tabIndex: PropTypes.number,
refStr: PropTypes.any, refStr: PropTypes.any,
handleFocus: PropTypes.func.def(noop),
handleBlur: PropTypes.func.def(noop),
}, },
data () { data () {
return { return {
@ -22,6 +28,8 @@ export default {
}, },
mounted () { mounted () {
this.$nextTick(() => { this.$nextTick(() => {
// mouseup won't trigger if mouse moved out of handle,
// so we listen on document here.
this.onMouseUpListener = addEventListener(document, 'mouseup', this.handleMouseUp) this.onMouseUpListener = addEventListener(document, 'mouseup', this.handleMouseUp)
this.refStr = this.$props.refStr this.refStr = this.$props.refStr
}) })
@ -42,8 +50,12 @@ export default {
this.setClickFocus(true) this.setClickFocus(true)
} }
}, },
handleBlur () { onBlur (e) {
this.setClickFocus(false) this.setClickFocus(false)
this.handleBlur(e)
},
onFocus (e) {
this.handleFocus(e)
}, },
handleKeyDown () { handleKeyDown () {
this.setClickFocus(false) this.setClickFocus(false)
@ -61,8 +73,8 @@ export default {
}, },
render () { render () {
const { const {
prefixCls, vertical, offset, disabled, min, max, value, tabIndex, refStr, ...restProps prefixCls, vertical, offset, disabled, min, max, value, tabIndex, refStr,
} = this.$props } = getOptionProps(this)
const className = { const className = {
[`${prefixCls}-handle`]: true, [`${prefixCls}-handle`]: true,
@ -89,13 +101,14 @@ export default {
tabIndex: disabled ? null : (tabIndex || 0), tabIndex: disabled ? null : (tabIndex || 0),
refStr, refStr,
...ariaProps, ...ariaProps,
...restProps,
}, },
style: elStyle, style: elStyle,
class: className, class: className,
on: { on: {
blur: this.handleBlur, blur: this.onBlur,
focus: this.onFocus,
keydown: this.handleKeyDown, keydown: this.handleKeyDown,
...this.$listeners,
}, },
ref: 'handle', ref: 'handle',
} }

View File

@ -19,6 +19,7 @@ const rangeProps = {
tabIndex: PropTypes.arrayOf(PropTypes.number), tabIndex: PropTypes.arrayOf(PropTypes.number),
} }
const Range = { const Range = {
name: 'Range',
displayName: 'Range', displayName: 'Range',
mixins: [BaseMixin], mixins: [BaseMixin],
props: initDefaultProps(rangeProps, { props: initDefaultProps(rangeProps, {
@ -39,7 +40,7 @@ const Range = {
const bounds = value.map((v, i) => this.trimAlignValue(v, i)) const bounds = value.map((v, i) => this.trimAlignValue(v, i))
const recent = bounds[0] === max ? 0 : bounds.length - 1 const recent = bounds[0] === max ? 0 : bounds.length - 1
return { return {
handle: null, sHandle: null,
recent, recent,
bounds, bounds,
} }
@ -86,7 +87,7 @@ const Range = {
if (isNotControlled) { if (isNotControlled) {
this.setState(state) this.setState(state)
} else if (state.handle !== undefined) { } else if (state.handle !== undefined) {
this.setState({ handle: state.handle }) this.setState({ sHandle: state.handle })
} }
const data = { ...this.$data, ...state } const data = { ...this.$data, ...state }
@ -103,7 +104,7 @@ const Range = {
this.prevMovedHandleIndex = this.getBoundNeedMoving(value, closestBound) this.prevMovedHandleIndex = this.getBoundNeedMoving(value, closestBound)
this.setState({ this.setState({
handle: this.prevMovedHandleIndex, sHandle: this.prevMovedHandleIndex,
recent: this.prevMovedHandleIndex, recent: this.prevMovedHandleIndex,
}) })
@ -115,14 +116,15 @@ const Range = {
this.$emit('change', { bounds: nextBounds }) this.$emit('change', { bounds: nextBounds })
}, },
onEnd () { onEnd () {
this.setState({ sHandle: null })
this.removeDocumentEvents() this.removeDocumentEvents()
this.$emit('afterChange', this.bounds) this.$emit('afterChange', this.bounds)
}, },
onMove (e, position) { onMove (e, position) {
utils.pauseEvent(e) utils.pauseEvent(e)
const { bounds, handle } = this const { bounds, sHandle } = this
const value = this.calcValueByPos(position) const value = this.calcValueByPos(position)
const oldValue = bounds[handle] const oldValue = bounds[sHandle]
if (value === oldValue) return if (value === oldValue) return
this.moveTo(value) this.moveTo(value)
@ -132,8 +134,8 @@ const Range = {
if (valueMutator) { if (valueMutator) {
utils.pauseEvent(e) utils.pauseEvent(e)
const { bounds, handle } = this const { bounds, sHandle } = this
const oldValue = bounds[handle] const oldValue = bounds[sHandle]
const mutatedValue = valueMutator(oldValue, this.$props) const mutatedValue = valueMutator(oldValue, this.$props)
const value = this.trimAlignValue(mutatedValue) const value = this.trimAlignValue(mutatedValue)
if (value === oldValue) return if (value === oldValue) return
@ -194,18 +196,18 @@ const Range = {
return this._getPointsCache.points return this._getPointsCache.points
}, },
moveTo (value, isFromKeyboardEvent) { moveTo (value, isFromKeyboardEvent) {
const { bounds, handle } = this const { bounds, sHandle } = this
const nextBounds = [...bounds] const nextBounds = [...bounds]
nextBounds[handle] = value nextBounds[sHandle] = value
let nextHandle = handle let nextHandle = sHandle
if (this.pushable !== false) { if (this.pushable !== false) {
this.pushSurroundingHandles(nextBounds, nextHandle) this.pushSurroundingHandles(nextBounds, nextHandle)
} else if (this.allowCross) { } else if (this.allowCross) {
nextBounds.sort((a, b) => a - b) nextBounds.sort((a, b) => a - b)
nextHandle = nextBounds.indexOf(value) nextHandle = nextBounds.indexOf(value)
} }
this.$emit('change', { this.onChange({
handle: nextHandle, sHandle: nextHandle,
bounds: nextBounds, bounds: nextBounds,
}) })
if (isFromKeyboardEvent) { if (isFromKeyboardEvent) {
@ -276,7 +278,7 @@ const Range = {
return true return true
}, },
trimAlignValue (v, handle, nextProps = {}) { trimAlignValue (v, handle, nextProps = {}) {
const mergedProps = { ...this, ...nextProps } const mergedProps = { ...this.$props, ...nextProps }
const valInRange = utils.ensureValueInRange(v, mergedProps) const valInRange = utils.ensureValueInRange(v, mergedProps)
const valNotConflict = this.ensureValueNotConflict(handle, valInRange, mergedProps) const valNotConflict = this.ensureValueNotConflict(handle, valInRange, mergedProps)
return utils.ensureValuePrecision(valNotConflict, mergedProps) return utils.ensureValuePrecision(valNotConflict, mergedProps)
@ -284,7 +286,7 @@ const Range = {
ensureValueNotConflict (handle, val, { allowCross, pushable: thershold }) { ensureValueNotConflict (handle, val, { allowCross, pushable: thershold }) {
const state = this.$data || {} const state = this.$data || {}
const { bounds } = state const { bounds } = state
handle = handle === undefined ? state.handle : handle handle = handle === undefined ? state.sHandle : handle
thershold = Number(thershold) thershold = Number(thershold)
/* eslint-disable eqeqeq */ /* eslint-disable eqeqeq */
if (!allowCross && handle != null && bounds !== undefined) { if (!allowCross && handle != null && bounds !== undefined) {
@ -298,10 +300,29 @@ const Range = {
/* eslint-enable eqeqeq */ /* eslint-enable eqeqeq */
return val return val
}, },
getTrack ({ bounds, prefixCls, vertical, included, offsets, trackStyle }) {
return bounds.slice(0, -1).map((_, index) => {
const i = index + 1
const trackClassName = classNames({
[`${prefixCls}-track`]: true,
[`${prefixCls}-track-${i}`]: true,
})
return (
<Track
class={trackClassName}
vertical={vertical}
included={included}
offset={offsets[i - 1]}
length={offsets[i] - offsets[i - 1]}
style={trackStyle[index]}
key={i}
/>
)
})
}, },
render () { renderSlider (h) {
const { const {
handle, sHandle,
bounds, bounds,
prefixCls, prefixCls,
vertical, vertical,
@ -318,7 +339,7 @@ const Range = {
const offsets = bounds.map(v => this.calcOffset(v)) const offsets = bounds.map(v => this.calcOffset(v))
const handleClassName = `${prefixCls}-handle` const handleClassName = `${prefixCls}-handle`
const handles = bounds.map((v, i) => handleGenerator({ const handles = bounds.map((v, i) => handleGenerator(h, {
className: classNames({ className: classNames({
[handleClassName]: true, [handleClassName]: true,
[`${handleClassName}-${i + 1}`]: true, [`${handleClassName}-${i + 1}`]: true,
@ -327,7 +348,7 @@ const Range = {
vertical, vertical,
offset: offsets[i], offset: offsets[i],
value: v, value: v,
dragging: handle === i, dragging: sHandle === i,
index: i, index: i,
tabIndex: tabIndex[i] || 0, tabIndex: tabIndex[i] || 0,
min, min,
@ -335,28 +356,15 @@ const Range = {
disabled, disabled,
style: handleStyle[i], style: handleStyle[i],
refStr: 'handleRef' + i, refStr: 'handleRef' + i,
handleFocus: this.onFocus,
handleBlur: this.onBlur,
})) }))
const tracks = bounds.slice(0, -1).map((_, index) => { return {
const i = index + 1 tracks: this.getTrack({ bounds, prefixCls, vertical, included, offsets, trackStyle }),
const trackClassName = classNames({ handles,
[`${prefixCls}-track`]: true, }
[`${prefixCls}-track-${i}`]: true, },
})
return (
<Track
className={trackClassName}
vertical={vertical}
included={included}
offset={offsets[i - 1]}
length={offsets[i] - offsets[i - 1]}
style={trackStyle[index]}
key={i}
/>
)
})
return { tracks, handles }
}, },
} }

View File

@ -7,6 +7,7 @@ import createSlider from './common/createSlider'
import * as utils from './utils' import * as utils from './utils'
const Slider = { const Slider = {
name: 'Slider',
mixins: [BaseMixin], mixins: [BaseMixin],
props: { props: {
defaultValue: PropTypes.number, defaultValue: PropTypes.number,
@ -151,8 +152,7 @@ const Slider = {
/> />
) )
}, },
}, renderSlider (h) {
render () {
const { const {
prefixCls, prefixCls,
vertical, vertical,
@ -168,7 +168,7 @@ const Slider = {
} = this } = this
const { sValue, dragging } = this const { sValue, dragging } = this
const offset = this.calcOffset(sValue) const offset = this.calcOffset(sValue)
const handle = handleGenerator({ const handle = handleGenerator(h, {
prefixCls, prefixCls,
vertical, vertical,
offset, offset,
@ -181,6 +181,8 @@ const Slider = {
tabIndex, tabIndex,
style: handleStyle[0] || handleStyle, style: handleStyle[0] || handleStyle,
refStr: 'handleRef0', refStr: 'handleRef0',
handleFocus: this.onFocus,
handleBlur: this.onBlur,
}) })
const _trackStyle = trackStyle[0] || trackStyle const _trackStyle = trackStyle[0] || trackStyle
@ -189,6 +191,7 @@ const Slider = {
handles: handle, handles: handle,
} }
}, },
},
} }
export default createSlider(Slider) export default createSlider(Slider)

View File

@ -34,6 +34,7 @@ export default function createSlider (Component) {
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
} }
return { return {
name: 'createSlider',
mixins: [Component], mixins: [Component],
props: initDefaultProps(propTypes, { props: initDefaultProps(propTypes, {
...Component.defaultProps, ...Component.defaultProps,
@ -42,7 +43,7 @@ export default function createSlider (Component) {
max: 100, max: 100,
step: 1, step: 1,
marks: {}, marks: {},
handle ({ index, refStr, ...restProps }) { handle (h, { index, refStr, className, ...restProps }) {
delete restProps.dragging delete restProps.dragging
const handleProps = { const handleProps = {
props: { props: {
@ -51,6 +52,7 @@ export default function createSlider (Component) {
attrs: { attrs: {
refStr, refStr,
}, },
class: className,
key: index, key: index,
} }
return <Handle {...handleProps} /> return <Handle {...handleProps} />
@ -167,8 +169,11 @@ export default function createSlider (Component) {
/* eslint-enable no-unused-expressions */ /* eslint-enable no-unused-expressions */
}, },
onMouseUp () { onMouseUp () {
if (this.handlesRefs[this.prevMovedHandleIndex]) { if (this.$children && this.$children[this.prevMovedHandleIndex]) {
this.handlesRefs[this.prevMovedHandleIndex].clickFocus() const handleCom = utils.getComponentProps(this.$children[this.prevMovedHandleIndex], 'clickFocus')
if (handleCom) {
handleCom.clickFocus()
}
} }
}, },
onMouseMove (e) { onMouseMove (e) {
@ -239,7 +244,7 @@ export default function createSlider (Component) {
this.$emit('change', { value }) this.$emit('change', { value })
}, },
}, },
render () { render (h) {
const { const {
prefixCls, prefixCls,
marks, marks,
@ -255,7 +260,7 @@ export default function createSlider (Component) {
dotStyle, dotStyle,
activeDotStyle, activeDotStyle,
} = this } = this
const { tracks, handles } = Component.render.call(this) const { tracks, handles } = this.renderSlider(h)
const sliderClassName = classNames(prefixCls, { const sliderClassName = classNames(prefixCls, {
[`${prefixCls}-with-marks`]: Object.keys(marks).length, [`${prefixCls}-with-marks`]: Object.keys(marks).length,

View File

@ -1,11 +1,12 @@
import PropTypes from '../../_util/vue-types' import PropTypes from '../../_util/vue-types'
import BaseMixin from '../../_util/BaseMixin' import BaseMixin from '../../_util/BaseMixin'
import Tooltip from '../../vc-tooltip' import Tooltip from '../../vc-tooltip'
import { getOptionProps } from '../../_util/props-util'
import Handle from './Handle' import Handle from './Handle'
export default function createSliderWithTooltip (Component) { export default function createSliderWithTooltip (Component) {
return { return {
mixins: [BaseMixin], mixins: [BaseMixin, Component],
props: { props: {
tipFormatter: PropTypes.func.def((value) => { return value }), tipFormatter: PropTypes.func.def((value) => { return value }),
handleStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]).def([{}]), handleStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]).def([{}]),
@ -85,7 +86,13 @@ export default function createSliderWithTooltip (Component) {
}, },
}, },
render () { render () {
return <Component {...this.$props} handle={this.handleWithTooltip} /> const componentProps = {
props: {
...getOptionProps(this),
handle: this.handleWithTooltip,
},
}
return <Component {...componentProps}/>
}, },
} }
} }

View File

@ -89,3 +89,18 @@ export function getKeyboardValueMutator (e) {
default: return undefined default: return undefined
} }
} }
export function getComponentProps (obj, prop) {
if (obj[prop]) {
return obj
} else if (obj.$children.length) {
const len = obj.$children.length
for (let i = 0; i < len; i++) {
if (obj.$children[i][prop]) {
return obj.$children[i]
} else if (obj.$children[i].$children.length) {
return getComponentProps(obj.$children[i], prop)
}
}
}
}

View File

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