refactor(slider): use v-binder, remove placeable mixin

This commit is contained in:
07akioni 2020-11-27 01:40:47 +08:00
parent 271753707b
commit 50ec676e2d
4 changed files with 245 additions and 671 deletions

View File

@ -1,7 +1,6 @@
export { default as useAsFormItem } from './use-as-form-item'
export { default as asFormItem } from './as-form-item'
export { default as locale } from './locale'
export { default as placeable } from './placeable/index'
export { default as themeable } from './themeable'
export { default as configurable } from './configurable'
export { default as withCssr } from './with-cssr'

View File

@ -1,137 +0,0 @@
const oppositeDirection = {
top: 'bottom',
bottom: 'top',
left: 'right',
right: 'left'
}
const lengthToCompare = {
top: 'height',
bottom: 'height',
left: 'width',
right: 'width'
}
const placementToTransformOrigin = {
'bottom-start': 'top left',
bottom: 'top center',
'bottom-end': 'top right',
'top-start': 'bottom left',
top: 'bottom',
'top-end': 'bottom right',
'right-start': 'top left',
right: 'center left',
'right-end': 'bottom left',
'left-start': 'top right',
left: 'center right',
'left-end': 'bottom right'
}
const positionDirections = {
'bottom-start': 'right',
'bottom-end': 'left',
'top-start': 'right',
'top-end': 'left',
'right-start': 'bottom',
'right-end': 'top',
'left-start': 'bottom',
'left-end': 'top'
}
const oppositePosition = {
start: 'end',
end: 'start'
}
export function getAdjustedPlacementOfTrackingElement (placement, trackedRect, trackingRect, flip) {
if (!flip) {
return placement
}
const [direction, position] = placement.split('-')
let adjustedPosition = position
if (position) {
const adjacentPositionDirection = positionDirections[placement]
if (trackingRect[lengthToCompare[adjacentPositionDirection]] > trackedRect[lengthToCompare[adjacentPositionDirection]]) {
if (trackedRect[adjacentPositionDirection] + trackedRect[lengthToCompare[adjacentPositionDirection]] <= trackingRect[lengthToCompare[adjacentPositionDirection]]) {
adjustedPosition = oppositePosition[position]
}
} else if (trackingRect[lengthToCompare[adjacentPositionDirection]] < trackedRect[lengthToCompare[adjacentPositionDirection]]) {
if (trackedRect[oppositeDirection[adjacentPositionDirection]] < 0 && trackedRect[adjacentPositionDirection] > 0) {
adjustedPosition = oppositePosition[position]
}
}
}
if (trackedRect[direction] >= trackingRect[lengthToCompare[direction]]) {
return adjustedPosition ? (direction + '-' + adjustedPosition) : direction
} else if (trackedRect[oppositeDirection[direction]] >= trackingRect[lengthToCompare[direction]]) {
return adjustedPosition ? `${oppositeDirection[direction]}-${adjustedPosition}` : oppositeDirection[direction]
} else {
return adjustedPosition ? (direction + '-' + adjustedPosition) : direction
}
}
export function getTransformOriginByPlacement (placement) {
return placementToTransformOrigin[placement] || null
}
export function getPosition (placement, offsetContainerRect, trackedRect) {
const offset = {
top: null,
bottom: null,
left: null,
right: null,
transform: null
}
if (placement === 'bottom-start') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left) + 'px'
} else if (placement === 'bottom-end') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width) + 'px'
offset.transform = 'translateX(-100%)'
} else if (placement === 'top-start') {
offset.top = (trackedRect.top - offsetContainerRect.top) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateY(-100%)'
} else if (placement === 'top-end') {
offset.top = (trackedRect.top - offsetContainerRect.top) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width) + 'px'
offset.transform = 'translateY(-100%) translateX(-100%)'
} else if (placement === 'right-start') {
offset.top = (trackedRect.top - offsetContainerRect.top) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width) + 'px'
} else if (placement === 'right-end') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width) + 'px'
offset.transform = 'translateY(-100%)'
} else if (placement === 'left-start') {
offset.top = (trackedRect.top - offsetContainerRect.top) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateX(-100%)'
} else if (placement === 'left-end') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateX(-100%) translateY(-100%)'
} else if (placement === 'top') {
offset.top = (trackedRect.top - offsetContainerRect.top) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width / 2) + 'px'
offset.transform = 'translateX(-50%) translateY(-100%)'
} else if (placement === 'right') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height / 2) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width) + 'px'
offset.transform = 'translateY(-50%)'
} else if (placement === 'bottom') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left + trackedRect.width / 2) + 'px'
offset.transform = 'translateX(-50%)'
} else if (placement === 'left') {
offset.top = (trackedRect.top - offsetContainerRect.top + trackedRect.height / 2) + 'px'
offset.left = (trackedRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateX(-100%) translateY(-50%)'
} else {
console.error(
'[naive-ui/mixins/placeable]: Placement %s is not supported.',
placement
)
}
return offset
}

View File

@ -1,300 +0,0 @@
import { nextTick } from 'vue'
import { on, off } from 'evtd'
import getScrollParent from '../../_utils/dom/get-scroll-parent'
import { warn } from '../../_utils/naive/warn'
import {
getAdjustedPlacementOfTrackingElement,
getTransformOriginByPlacement,
getPosition
} from './calc-placement-transform'
let viewMeasurerInitialized = false
let viewMeasurer = null
if (!viewMeasurerInitialized) {
viewMeasurer = document.getElementById('n-view-measurer')
if (!viewMeasurer) {
viewMeasurer = document.createElement('div')
viewMeasurer.id = 'n-view-measurer'
viewMeasurer.style = `
position: fixed !important;
left: 0 !important;
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
pointer-events: none !important;
visibility: hidden !important;
`
document.body.appendChild(viewMeasurer)
}
viewMeasurerInitialized = true
}
function getViewBoundingRect () {
const view = viewMeasurer.getBoundingClientRect()
return {
left: view.left,
top: view.top,
right: view.right,
bottom: view.bottom,
width: view.width,
height: view.height
}
}
function getActivatorRect (manuallyPositioned, x, y, trackedElement, viewRect) {
if (manuallyPositioned) {
return {
top: y,
left: x,
height: 0,
width: 0,
right: viewRect.width - x,
bottom: viewRect.height - y
}
} else {
const triggerRect = trackedElement.getBoundingClientRect()
return {
left: triggerRect.left - viewRect.left,
top: triggerRect.top - viewRect.top,
bottom: viewRect.height + viewRect.top - triggerRect.bottom,
right: viewRect.width + viewRect.left - triggerRect.right,
width: triggerRect.width,
height: triggerRect.height
}
}
}
// dependencies:
// methods.__placeableTracking
// methods.__placeableTracked
// methods.__placeableBody
// methods.__placeableOffsetContainer
// data.__placeableEnabled
export default {
inject: {
NModal: {
default: null
},
NDrawer: {
default: null
}
},
props: {
placement: {
validator (value) {
return [
'top',
'bottom',
'left',
'right',
'top-start',
'top-end',
'left-start',
'left-end',
'right-start',
'right-end',
'bottom-start',
'bottom-end'
].includes(value)
},
default: 'bottom'
},
flip: {
type: Boolean,
default: true
},
widthMode: {
validator (value) {
return ['self', 'trigger'].includes(value)
},
default: 'self'
},
manuallyPositioned: {
type: Boolean,
default: false
},
x: {
type: Number,
default: undefined
},
y: {
type: Number,
default: undefined
}
},
watch: {
__placeableEnabled (value) {
if (value) {
if (!this.__placeableListenerAdded && !this.manuallyPositioned) {
this.__addScrollListeners()
this.__addResizeListener()
this.__placeableListenerAdded = true
}
nextTick(this.__placeableSyncPosition)
}
},
x () {
nextTick(this.__placeableSyncPosition)
},
y () {
nextTick(this.__placeableSyncPosition)
}
},
data () {
return {
__placeableScrollListeners: [],
__placeableAdjustedPlacement: this.placement,
__placeableListenerAdded: false
}
},
mounted () {
if (this.__placeableEnabled) {
if (!this.manuallyPositioned) {
this.__addScrollListeners()
this.__addResizeListener()
this.__placeableListenerAdded = true
}
this.__placeableSyncPosition()
}
},
beforeUnmount () {
if (this.__placeableListenerAdded) {
this.__removeScrollListeners()
this.__removeResizeListener()
}
},
created: __DEV__ ? function () {
[
'__placeableOffsetContainer',
'__placeableTracking',
'__placeableTracked',
'__placeableBody'
].forEach(key => {
if (!this[key]) {
warn('mixins/placeable', `\`${key}\` is not defined.`)
}
})
} : undefined,
methods: {
__placeableSyncPosition () {
if (!this.__placeableEnabled) {
return
}
const trackingElement = this.__getTrackingElement()
let trackedElement = null
trackingElement.style.position = 'absolute'
if (this.manuallyPositioned) {
if (__DEV__ && !trackingElement) {
warn('mixins/placeable', 'Tracking element not found.')
return
}
} else {
trackedElement = this.__getTrackedElement()
if (__DEV__ && (!trackedElement || !trackingElement)) {
warn('mixins/placeable', 'Traked element or tracking element not found.')
return
}
}
const viewRect = getViewBoundingRect()
const triggerRect = getActivatorRect(this.manuallyPositioned, this.x, this.y, trackedElement, viewRect)
const {
widthMode
} = this
if (
(widthMode === 'trigger' || widthMode === 'activator')
) {
const body = this.__getBodyElement()
body.style.width = triggerRect.width + 'px'
}
let adjustedPlacement = this.placement
let contentBoundingClientRect = null
if (this.flip) {
contentBoundingClientRect = {
width: trackingElement.offsetWidth,
height: trackingElement.offsetHeight
}
adjustedPlacement = getAdjustedPlacementOfTrackingElement(this.placement, triggerRect, contentBoundingClientRect, true)
}
const suggestedTransformOrigin = getTransformOriginByPlacement(adjustedPlacement)
let offset = null
this.__placeableAdjustedPlacement = adjustedPlacement
const offsetContainer = this.__getOffsetContainer()
offset = getPosition(
adjustedPlacement,
offsetContainer.getBoundingClientRect(),
triggerRect
)
this.__setOffset(offset, suggestedTransformOrigin)
},
__getTrackingElement () {
const { __placeableTracking } = this
if (__placeableTracking) {
const el = __placeableTracking()
return el.$el || el
} else if (__DEV__) {
warn('mixins/placeable', 'No tracking element getter.')
}
},
__getTrackedElement () {
const { __placeableTracked } = this
if (__placeableTracked) {
const el = __placeableTracked()
return el.$el || el
} else if (__DEV__) {
warn('mixins/placeable', 'No tracked element getter.')
}
},
__getBodyElement () {
const { __placeableBody } = this
if (__placeableBody) {
const el = __placeableBody()
return el.$el || el
} else if (__DEV__) {
warn('mixins/placeable', 'No body element getter.')
}
},
__getOffsetContainer () {
const __placeableOffsetContainer = this.__placeableOffsetContainer
if (__placeableOffsetContainer) {
const el = __placeableOffsetContainer()
return el.$el || el
} else if (__DEV__) {
warn('mixins/placeable', 'No offset container getter.')
}
},
__setOffset (position, transformOrigin) {
const el = this.__getTrackingElement()
el.style.position = 'absolute'
el.style.top = position.top
el.style.left = position.left
el.style.right = position.right
el.style.bottom = position.bottom
el.style.transform = position.transform
el.style.transformOrigin = transformOrigin
el.setAttribute('n-suggested-transform-origin', transformOrigin)
},
__addResizeListener () {
on('resize', window, this.__placeableSyncPosition)
},
__addScrollListeners () {
let currentElement = this.__getTrackedElement()
while (true) {
currentElement = getScrollParent(currentElement)
if (currentElement === null) break
this.__placeableScrollListeners.push([currentElement, this.__placeableSyncPosition])
}
for (const [el, handler] of this.__placeableScrollListeners) {
on('scroll', el, handler, true)
}
},
__removeResizeListener () {
off('resize', window, this.__placeableSyncPosition)
},
__removeScrollListeners () {
for (const [el, handler] of this.__placeableScrollListeners) {
off('scroll', el, handler, true)
}
this.__placeableScrollListeners = []
}
}
}

View File

@ -33,25 +33,80 @@
/>
</div>
</div>
<div
ref="firstHandleRef"
class="n-slider-handle"
tabindex="0"
:style="firstHandleStyle"
@mousedown="handleFirstHandleMouseDown"
@mouseenter="handleFirstHandleMouseEnter"
@mouseleave="handleFirstHandleMouseLeave"
/>
<div
<v-binder>
<v-target>
<div
ref="handleRef1"
class="n-slider-handle"
tabindex="0"
:style="firstHandleStyle"
@mousedown="handleFirstHandleMouseDown"
@mouseenter="handleFirstHandleMouseEnter"
@mouseleave="handleFirstHandleMouseLeave"
/>
</v-target>
<v-follower
ref="followerRef1"
:show="mergedShowTooltip1"
:to="adjustedTo"
:placement="placement"
:container-class="namespace"
>
<transition
name="n-fade-in-scale-up-transition"
:appear="isMounted"
:css="!(active && prevActive)"
>
<div
v-if="mergedShowTooltip1"
class="n-slider-handle-indicator"
:class="{
[`n-${mergedTheme}-theme`]: mergedTheme
}"
>
{{ handleValue1 }}
</div>
</transition>
</v-follower>
</v-binder>
<v-binder
v-if="range"
ref="secondHandleRef"
class="n-slider-handle"
tabindex="0"
:style="secondHandleStyle"
@mousedown="handleSecondHandleMouseDown"
@mouseenter="handleSecondHandleMouseEnter"
@mouseleave="handleSecondHandleMouseLeave"
/>
>
<v-target>
<div
ref="handleRef2"
class="n-slider-handle"
tabindex="0"
:style="secondHandleStyle"
@mousedown="handleSecondHandleMouseDown"
@mouseenter="handleSecondHandleMouseEnter"
@mouseleave="handleSecondHandleMouseLeave"
/>
</v-target>
<v-follower
ref="followerRef2"
:show="mergedShowTooltip2"
:to="adjustedTo"
:placement="placement"
:container-class="namespace"
>
<transition
name="n-fade-in-scale-up-transition"
:appear="isMounted"
:css="!(active && prevActive)"
>
<div
v-if="mergedShowTooltip2"
class="n-slider-handle-indicator"
:class="{
[`n-${mergedTheme}-theme`]: mergedTheme
}"
>
{{ handleValue2 }}
</div>
</transition>
</v-follower>
</v-binder>
<div
v-if="marks"
class="n-slider-marks"
@ -65,68 +120,35 @@
{{ mark.label }}
</div>
</div>
<n-base-lazy-teleport
:show="showTooltip"
adjust-to
>
<div
ref="offsetContainerRef"
v-zindexable="{ enabled: showTooltip }"
class="n-positioning-container"
:class="{
[namespace]: namespace
}"
>
<div
ref="trackingRef"
class="n-positioning-content"
>
<transition
name="n-fade-in-scale-up-transition"
:appear="isMounted"
>
<div
v-if="showTooltip"
class="n-slider-handle-indicator"
:class="{
[`n-${mergedTheme}-theme`]: mergedTheme
}"
>
{{ activeHandleValue === null ? tooltipHoverDisplayValue : activeHandleValue }}
</div>
</transition>
</div>
</div>
</n-base-lazy-teleport>
</div>
</template>
<script>
import { ref } from 'vue'
import { ref, toRef, computed, watch, nextTick } from 'vue'
import {
VBinder,
VTarget,
VFollower
} from 'vueuc'
import { useIsMounted, useMergedState } from 'vooks'
import { on, off } from 'evtd'
import {
configurable,
themeable,
placeable,
asFormItem,
withCssr
} from '../../_mixins'
import {
zindexable
} from 'vdirs'
import styles from './styles'
import { warn } from '../../_utils/naive'
import { call } from '../../_utils/vue'
import { useIsMounted } from 'vooks'
import { NBaseLazyTeleport } from '../../_base'
import { warn, call, useAdjustedTo } from '../../_utils'
function handleFirstHandleMouseMove (e) {
const railRect = this.railRef.getBoundingClientRect()
const offsetRatio = (e.clientX - railRect.left) / railRect.width
const newValue = this.min + (this.max - this.min) * offsetRatio
if (this.range) {
this.emitInputEvent([this.memoziedOtherValue, newValue])
this.dispatchValueUpdate([this.memoziedOtherValue, newValue])
} else {
this.emitInputEvent(newValue)
this.dispatchValueUpdate(newValue)
}
}
@ -135,22 +157,20 @@ function handleSecondHandleMouseMove (e) {
const offsetRatio = (e.clientX - railRect.left) / railRect.width
const newValue = this.min + (this.max - this.min) * offsetRatio
if (this.range) {
this.emitInputEvent([this.memoziedOtherValue, newValue])
this.dispatchValueUpdate([this.memoziedOtherValue, newValue])
}
}
export default {
name: 'Slider',
directives: {
zindexable
},
components: {
NBaseLazyTeleport
VBinder,
VTarget,
VFollower
},
mixins: [
configurable,
themeable,
placeable,
withCssr(styles),
asFormItem()
],
@ -187,6 +207,10 @@ export default {
type: String,
default: 'top'
},
showTooltip: {
type: Boolean,
default: undefined
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': {
type: Function,
@ -201,34 +225,55 @@ export default {
default: undefined
}
},
setup () {
setup (props) {
const handleActive1Ref = ref(false)
const handleActive2Ref = ref(false)
const controlledShowTooltipRef = toRef(props, 'showTooltip')
const mergedShowTooltip1Ref = useMergedState(
controlledShowTooltipRef,
handleActive1Ref
)
const mergedShowTooltip2Ref = useMergedState(
controlledShowTooltipRef,
handleActive2Ref
)
const handleClicked1Ref = ref(false)
const handleClicked2Ref = ref(false)
const activeRef = computed(() => {
return handleActive1Ref.value || handleActive2Ref.value
})
const prevActiveRef = ref(activeRef.value)
watch(activeRef, (value) => {
nextTick(() => {
prevActiveRef.value = value
})
})
const clickedRef = computed(() => {
return handleClicked1Ref.value || handleClicked2Ref.value
})
return {
isMounted: useIsMounted(),
adjustedTo: useAdjustedTo(props),
mergedShowTooltip1: mergedShowTooltip1Ref,
mergedShowTooltip2: mergedShowTooltip2Ref,
handleActive1: handleActive1Ref,
handleActive2: handleActive2Ref,
handleClicked1: handleClicked1Ref,
handleClicked2: handleClicked2Ref,
memoziedOtherValue: ref(null),
valueChangedByRailClick: ref(true),
active: activeRef,
prevActive: prevActiveRef,
clicked: clickedRef,
// https://github.com/vuejs/vue-next/issues/2283
firstHandleRef: ref(null),
secondHandleRef: ref(null),
handleRef1: ref(null),
handleRef2: ref(null),
railRef: ref(null),
offsetContainerRef: ref(null),
trackingRef: ref(null)
}
},
data () {
return {
unstableMemorizedRef: {},
showTooltip: false,
firstHandleActive: false,
secondHandleActive: false,
firstHandleClicked: false,
secondHandleClicked: false,
memoziedOtherValue: null,
valueChangedByRailClick: true,
tooltipHoverDisplayValue: ''
followerRef1: ref(null),
followerRef2: ref(null)
}
},
computed: {
__placeableEnabled () {
return this.showTooltip
},
computedMarks () {
const marks = []
for (const value of Object.keys(this.marks)) {
@ -244,25 +289,17 @@ export default {
fillStyle () {
if (this.range) {
return {
left: ((this.firstHandleValue - this.min) / (this.max - this.min) * 100) + '%',
width: ((this.secondHandleValue - this.firstHandleValue) / (this.max - this.min) * 100) + '%'
left: ((this.handleValue1 - this.min) / (this.max - this.min) * 100) + '%',
width: ((this.handleValue2 - this.handleValue1) / (this.max - this.min) * 100) + '%'
}
} else {
return {
left: 0,
width: ((this.firstHandleValue - this.min) / (this.max - this.min) * 100) + '%'
width: ((this.handleValue1 - this.min) / (this.max - this.min) * 100) + '%'
}
}
},
activeHandleValue () {
if (this.firstHandleActive) {
return this.firstHandleValue
} else if (this.secondHandleActive) {
return this.secondHandleValue
}
return null
},
firstHandleValue () {
handleValue1 () {
if (this.range) {
if (this.value) {
if (this.value[0] > this.value[1]) {
@ -275,7 +312,7 @@ export default {
return this.justifyValue(this.value)
}
},
secondHandleValue () {
handleValue2 () {
if (this.range && this.value) {
if (this.value[0] > this.value[1]) {
return this.justifyValue(this.value[0])
@ -287,21 +324,15 @@ export default {
},
firstHandleStyle () {
return {
left: ((this.firstHandleValue - this.min) / (this.max - this.min) * 100) + '%',
zIndex: this.firstHandleActive ? 1 : 0
left: ((this.handleValue1 - this.min) / (this.max - this.min) * 100) + '%',
zIndex: this.handleClicked1 ? 1 : 0
}
},
secondHandleStyle () {
return {
left: ((this.secondHandleValue - this.min) / (this.max - this.min) * 100) + '%',
zIndex: this.secondHandleActive ? 1 : 0
left: ((this.handleValue2 - this.min) / (this.max - this.min) * 100) + '%',
zIndex: this.handleClicked2 ? 1 : 0
}
},
active () {
return this.firstHandleActive || this.secondHandleActive
},
clicked () {
return this.firstHandleClicked || this.secondHandleClicked
}
},
watch: {
@ -310,8 +341,8 @@ export default {
if (oldValue && oldValue[1] !== newValue[1]) {
this.$nextTick(() => {
if (!this.valueChangedByRailClick) {
this.firstHandleActive = false
this.secondHandleActive = true
this.handleActive1 = false
this.handleActive2 = true
} else {
this.valueChangedByRailClick = false
}
@ -320,8 +351,8 @@ export default {
} else if (oldValue && oldValue[0] !== newValue[0]) {
this.$nextTick(() => {
if (!this.valueChangedByRailClick) {
this.firstHandleActive = true
this.secondHandleActive = false
this.handleActive1 = true
this.handleActive2 = false
} else {
this.valueChangedByRailClick = false
}
@ -330,8 +361,8 @@ export default {
} else if (newValue[0] === newValue[1]) {
this.$nextTick(() => {
if (!this.valueChangedByRailClick) {
this.firstHandleActive = false
this.secondHandleActive = true
this.handleActive1 = false
this.handleActive2 = true
} else {
this.valueChangedByRailClick = false
}
@ -343,47 +374,22 @@ export default {
if (this.range) {
if (newValue && oldValue) {
if (newValue[0] !== oldValue[0] || newValue[1] !== oldValue[1]) {
this.__placeableSyncPosition()
this.syncPosition()
}
}
} else {
this.__placeableSyncPosition()
this.syncPosition()
}
})
}
},
beforeUnmount () {
window.removeEventListener('mousemove', this.handleFirstHandleMouseMove)
window.removeEventListener('mouseup', this.handleFirstHandleMouseUp)
window.removeEventListener('mousemove', this.handleSecondHandleMouseMove)
window.removeEventListener('mouseup', this.handleSecondHandleMouseUp)
off('mousemove', window, this.handleFirstHandleMouseMove)
off('mouseup', window, this.handleFirstHandleMouseUp)
off('mousemove', window, this.handleSecondHandleMouseMove)
off('mouseup', window, this.handleSecondHandleMouseUp)
},
methods: {
__placeableTracked () {
if (this.firstHandleActive) {
return this.firstHandleRef
} else if (this.secondHandleActive) {
return this.secondHandleRef
}
return this.$el // for registering scroll listeners
},
__placeableTracking () {
if (this.trackingRef) {
return (this.unstableMemorizedRef.tracking = this.trackingRef)
} else {
return this.unstableMemorizedRef.tracking
}
},
__placeableOffsetContainer () {
if (this.offsetContainerRef) {
return (this.unstableMemorizedRef.offsetContainer = this.offsetContainerRef)
} else {
return this.unstableMemorizedRef.offsetContainer
}
},
__placeableBody () {
return null
},
doUpdateValue (value) {
const {
onChange,
@ -396,37 +402,50 @@ export default {
nTriggerFormInput()
nTriggerFormChange()
},
doUpdateShow (show1, show2) {
if (show1 !== undefined) {
this.handleActive1 = show1
}
if (show2 !== undefined) {
this.handleActive2 = show2
}
},
syncPosition () {
const { followerRef1, followerRef2 } = this
if (followerRef1) followerRef1.syncPosition()
if (followerRef2) followerRef2.syncPosition()
},
handleRailClick (e) {
this.valueChangedByRailClick = true
const railRect = this.railRef.getBoundingClientRect()
const offsetRatio = (e.clientX - railRect.left) / railRect.width
const newValue = this.min + (this.max - this.min) * offsetRatio
if (!this.range) {
this.emitInputEvent(newValue)
this.firstHandleRef.focus()
this.dispatchValueUpdate(newValue)
this.handleRef1.focus()
} else {
if (this.value) {
if (Math.abs(this.firstHandleValue - newValue) < Math.abs(this.secondHandleValue - newValue)) {
this.emitInputEvent([newValue, this.secondHandleValue])
this.firstHandleRef.focus()
if (Math.abs(this.handleValue1 - newValue) < Math.abs(this.handleValue2 - newValue)) {
this.dispatchValueUpdate([newValue, this.handleValue2])
this.handleRef1.focus()
} else {
this.emitInputEvent([this.firstHandleValue, newValue])
this.secondHandleRef.focus()
this.dispatchValueUpdate([this.handleValue1, newValue])
this.handleRef2.focus()
}
} else {
this.emitInputEvent([newValue, newValue])
this.firstHandleRef.focus()
this.dispatchValueUpdate([newValue, newValue])
this.handleRef1.focus()
}
}
},
handleKeyDownRight () {
let firstHandleFocused = false
let handleValue = null
if (document.activeElement === this.firstHandleRef) {
if (document.activeElement === this.handleRef1) {
firstHandleFocused = true
handleValue = this.firstHandleValue
handleValue = this.handleValue1
} else {
handleValue = this.secondHandleValue
handleValue = this.handleValue2
}
let nextValue = Math.floor(handleValue / this.step) * this.step + this.step
if (this.marks) {
@ -439,22 +458,22 @@ export default {
}
if (this.range) {
if (firstHandleFocused) {
this.emitInputEvent([nextValue, this.secondHandleValue])
this.dispatchValueUpdate([nextValue, this.handleValue2])
} else {
this.emitInputEvent([this.firstHandleValue, nextValue])
this.dispatchValueUpdate([this.handleValue1, nextValue])
}
} else {
this.emitInputEvent(nextValue)
this.dispatchValueUpdate(nextValue)
}
},
handleKeyDownLeft () {
let firstHandleFocused = false
let handleValue = null
if (document.activeElement === this.firstHandleRef) {
if (document.activeElement === this.handleRef1) {
firstHandleFocused = true
handleValue = this.firstHandleValue
handleValue = this.handleValue1
} else {
handleValue = this.secondHandleValue
handleValue = this.handleValue2
}
let nextValue = Math.ceil(handleValue / this.step) * this.step - this.step
if (this.marks) {
@ -467,23 +486,23 @@ export default {
}
if (this.range) {
if (firstHandleFocused) {
this.emitInputEvent([nextValue, this.secondHandleValue])
this.dispatchValueUpdate([nextValue, this.handleValue2])
} else {
this.emitInputEvent([this.firstHandleValue, nextValue])
this.dispatchValueUpdate([this.handleValue1, nextValue])
}
} else {
this.emitInputEvent(nextValue)
this.dispatchValueUpdate(nextValue)
}
},
switchFocus () {
if (this.range) {
const firstHandle = this.firstHandleRef
const secondHandle = this.secondHandleRef
const firstHandle = this.handleRef1
const secondHandle = this.handleRef2
if (firstHandle && secondHandle) {
if (this.firstHandleActive && document.activeElement === secondHandle) {
if (this.handleActive1 && document.activeElement === secondHandle) {
this.disableTransitionOneTick()
firstHandle.focus()
} else if (this.secondHandleActive && document.activeElement === firstHandle) {
} else if (this.handleActive2 && document.activeElement === firstHandle) {
this.disableTransitionOneTick()
secondHandle.focus()
}
@ -525,49 +544,41 @@ export default {
},
handleFirstHandleMouseDown () {
if (this.range) {
this.memoziedOtherValue = this.secondHandleValue
this.memoziedOtherValue = this.handleValue2
}
this.firstHandleActive = true
this.firstHandleClicked = true
window.addEventListener('mouseup', this.handleFirstHandleMouseUp)
window.addEventListener('mousemove', this.handleFirstHandleMouseMove)
this.handleActive1 = true
this.handleClicked1 = true
on('mouseup', window, this.handleFirstHandleMouseUp)
on('mousemove', window, this.handleFirstHandleMouseMove)
},
handleSecondHandleMouseDown () {
if (this.range) {
this.memoziedOtherValue = this.firstHandleValue
this.memoziedOtherValue = this.handleValue1
}
this.secondHandleActive = true
this.secondHandleClicked = true
window.addEventListener('mouseup', this.handleSecondHandleMouseUp)
window.addEventListener('mousemove', this.handleSecondHandleMouseMove)
this.handleActive2 = true
this.handleClicked2 = true
on('mouseup', window, this.handleSecondHandleMouseUp)
on('mousemove', window, this.handleSecondHandleMouseMove)
},
handleFirstHandleMouseUp (e) {
this.secondHandleActive = false
this.firstHandleActive = false
this.secondHandleClicked = false
this.firstHandleClicked = false
if (!this.firstHandleRef.contains(e.target)) {
this.showTooltip = false
} else {
this.tooltipHoverDisplayValue = this.firstHandleValue
this.handleClicked2 = false
this.handleClicked1 = false
if (!this.handleRef1.contains(e.target)) {
this.doUpdateShow(false, undefined)
}
window.removeEventListener('mouseup', this.handleFirstHandleMouseUp)
window.removeEventListener('mousemove', this.handleFirstHandleMouseMove)
off('mouseup', window, this.handleFirstHandleMouseUp)
off('mousemove', window, this.handleFirstHandleMouseMove)
},
handleSecondHandleMouseUp (e) {
this.secondHandleActive = false
this.firstHandleActive = false
this.secondHandleClicked = false
this.firstHandleClicked = false
if (!this.firstHandleRef.contains(e.target)) {
this.showTooltip = false
} else {
this.tooltipHoverDisplayValue = this.secondHandleValue
this.handleClicked2 = false
this.handleClicked1 = false
if (!this.handleRef1.contains(e.target)) {
this.doUpdateShow(undefined, false)
}
window.removeEventListener('mouseup', this.handleSecondHandleMouseUp)
window.removeEventListener('mousemove', this.handleSecondHandleMouseMove)
off('mouseup', window, this.handleSecondHandleMouseUp)
off('mousemove', window, this.handleSecondHandleMouseMove)
},
emitInputEvent (value) {
dispatchValueUpdate (value) {
if (this.range) {
if (Array.isArray(value)) {
if (value[0] > value[1]) {
@ -598,56 +609,57 @@ export default {
handleSecondHandleMouseMove,
handleFirstHandleMouseEnter () {
if (!this.active) {
this.showTooltip = true
this.firstHandleActive = true
this.tooltipHoverDisplayValue = this.firstHandleValue
this.doUpdateShow(true, undefined)
this.handleActive1 = true
this.$nextTick(() => {
this.__placeableSyncPosition()
this.syncPosition()
})
}
},
handleFirstHandleMouseLeave () {
if (!this.active) this.showTooltip = false
if (this.active && !this.clicked) {
this.secondHandleActive = false
this.firstHandleActive = false
this.showTooltip = false
if (!this.active) {
this.doUpdateShow(false, false)
} else if (!this.clicked) {
this.handleActive2 = false
this.handleActive1 = false
this.doUpdateShow(false, false)
}
},
handleSecondHandleMouseEnter () {
if (!this.active) {
this.showTooltip = true
this.secondHandleActive = true
this.tooltipHoverDisplayValue = this.secondHandleValue
this.doUpdateShow(undefined, true)
this.handleActive2 = true
this.$nextTick(() => {
this.__placeableSyncPosition()
this.syncPosition()
})
}
},
handleSecondHandleMouseLeave () {
if (!this.active) this.showTooltip = false
if (this.active && !this.clicked) {
this.secondHandleActive = false
this.firstHandleActive = false
this.showTooltip = false
if (!this.active) {
this.doUpdateShow(false, false)
} else if (!this.clicked) {
this.handleActive2 = false
this.handleActive1 = false
this.doUpdateShow(false, false)
}
},
disableTransitionOneTick () {
const firstHandle = this.firstHandleRef
if (firstHandle) {
firstHandle.style.transition = 'none'
const { handleRef1, handleRef2 } = this
if (handleRef1) {
handleRef1.style.transition = 'none'
this.$nextTick(() => {
if (this.firstHandleRef) {
this.firstHandleRef.style.transition = null
const { handleRef1 } = this
if (handleRef1) {
handleRef1.style.transition = ''
}
})
}
const secondHandle = this.secondHandleRef
if (secondHandle) {
secondHandle.style.transition = 'none'
if (handleRef2) {
handleRef2.style.transition = 'none'
this.$nextTick(() => {
if (this.secondHandleRef) {
this.secondHandleRef.style.transition = null
const { handleRef2 } = this
if (handleRef2) {
handleRef2.style.transition = ''
}
})
}