blob: 69e59e853a3f55753f6a1b9c6539193388135de1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use strict'
// TODO: refactor this scss code since this is strongly
// dependent on lib.flexible other than the value of
// scale.
import './tabheader.css'
function initFoldBtn (tabheader) {
const node = tabheader.node
const btn = document.createElement('span')
btn.className = 'fold-toggle iconfont'
btn.innerHTML = ''
node.appendChild(btn)
btn.addEventListener('click', function () {
if (tabheader.unfolding) {
folding(tabheader)
}
else {
unfolding(tabheader)
}
})
}
function initMask (tabheader) {
const mask = document.createElement('div')
mask.className = 'tabheader-mask'
tabheader.mask = mask
// stop default behavior: page moving.
mask.addEventListener('touchmove', function (evt) {
evt.preventDefault()
})
// click to unfold.
mask.addEventListener('click', function () {
folding(tabheader)
})
document.body.appendChild(mask)
}
function unfolding (tabheader) {
// mark the initial posiiton of tabheader
if (!tabheader.flag) {
const flag = document.createComment('tabheader')
tabheader.flag = flag
tabheader.node.parentNode.insertBefore(flag, tabheader.node)
}
if (!tabheader.mask) {
initMask(tabheader)
}
// record the scroll position.
tabheader._scrollVal = tabheader._body.scrollLeft
// record the position in document.
tabheader._topVal = tabheader.node.getBoundingClientRect().top
tabheader._styleTop = tabheader.node.style.top
document.body.appendChild(tabheader.node)
tabheader.node.classList.add('unfold-header')
tabheader.node.style.height = 'auto'
// recalc the position when it is unfolded.
const thHeight = tabheader.node.getBoundingClientRect().height
if (thHeight + tabheader._topVal > window.innerHeight) {
tabheader._topVal = tabheader._topVal
+ (window.innerHeight - thHeight - tabheader._topVal)
}
tabheader.node.style.top = tabheader._topVal + 'px'
// process mask style
tabheader.mask.classList.add('unfold-header')
tabheader.mask.style.height = window.innerHeight + 'px'
tabheader.unfolding = true
}
function folding (tabheader) {
if (tabheader.unfolding !== true) {
return
}
tabheader.mask.classList.remove('unfold-header')
tabheader.node.classList.remove('unfold-header')
tabheader.node.style.height = ''
tabheader.node.style.top = tabheader._styleTop
// recover the position of tabheader.
tabheader.flag.parentNode.insertBefore(tabheader.node, tabheader.flag)
// recover the position of scoller.
tabheader._body.scrollLeft = tabheader._scrollVal
scrollToView(tabheader)
tabheader.unfolding = false
}
function initEvent (tabheader) {
initClickEvent(tabheader)
initSelectEvent(tabheader)
}
// init events.
function initClickEvent (tabheader) {
const box = tabheader.box
box.addEventListener('click', function (evt) {
let target = evt.target
if (target.nodeName === 'UL') {
return
}
if (target.parentNode.nodeName === 'LI') {
target = target.parentNode
}
const floor = target.getAttribute('data-floor')
/* eslint-disable eqeqeq */
if (tabheader.data.attr.selectedIndex == floor) {
// Duplicated clicking, not to trigger select event.
return
}
/* eslint-enable eqeqeq */
fireEvent(target, 'select', { index: floor })
})
}
function initSelectEvent (tabheader) {
const node = tabheader.node
node.addEventListener('select', function (evt) {
let index
if (evt.index !== undefined) {
index = evt.index
}
else if (evt.data && evt.data.index !== undefined) {
index = evt.data.index
}
if (index === undefined) {
return
}
tabheader.attr.selectedIndex.call(tabheader, index)
})
}
function scrollToView (tabheader, node) {
if (!node) {
const attr = tabheader.data.attr
node = tabheader.node.querySelector('[data-floor="' + attr.selectedIndex + '"]')
}
if (!node) {
return
}
// const defaultVal = tabheader._body.scrollLeft
// const leftVal = defaultVal - node.offsetLeft + 300
const scrollVal = getScrollVal(tabheader._body.getBoundingClientRect(), node)
doScroll(tabheader._body, scrollVal)
}
// scroll the tabheader.
// positive val means to scroll right.
// negative val means to scroll left.
function doScroll (node, val, finish) {
if (!val) {
return
}
if (finish === undefined) {
finish = Math.abs(val)
}
if (finish <= 0) {
return
}
setTimeout(function () {
if (val > 0) {
node.scrollLeft += 2
}
else {
node.scrollLeft -= 2
}
finish -= 2
doScroll(node, val, finish)
})
}
// get scroll distance.
function getScrollVal (rect, node) {
const left = node.previousSibling
const right = node.nextSibling
let scrollVal
// process left-side element first.
if (left) {
const leftRect = left.getBoundingClientRect()
// only need to compare the value of left.
if (leftRect.left < rect.left) {
scrollVal = leftRect.left
return scrollVal
}
}
if (right) {
const rightRect = right.getBoundingClientRect()
// compare the value of right.
if (rightRect.right > rect.right) {
scrollVal = rightRect.right - rect.right
return scrollVal
}
}
// process current node, from left to right.
const nodeRect = node.getBoundingClientRect()
if (nodeRect.left < rect.left) {
scrollVal = nodeRect.left
}
else if (nodeRect.right > rect.right) {
scrollVal = nodeRect.right - rect.right
}
return scrollVal
}
// trigger and broadcast events.
function fireEvent (element, type, data) {
const evt = document.createEvent('Event')
evt.data = data
for (const k in data) {
if (data.hasOwnProperty(k)) {
evt[k] = data[k]
}
}
// need bubble.
evt.initEvent(type, true, true)
element.dispatchEvent(evt)
}
function createHighlightIcon (code) {
const html = '<i class="hl-icon iconfont">' + '&#xe650' + '</i>'
return html
}
function isValidColor (color) {
if (!color) {
return false
}
if (color.charAt(0) !== '#') {
return false
}
if (color.length !== 7) {
return false
}
return true
}
const proto = {
create () {
// outside container.
const node = document.createElement('div')
node.className = 'tab-header'
// tip on the top.
const bar = document.createElement('div')
bar.className = 'header-bar'
bar.textContent = 'CHANGE FLOOR'
// middle layer.
const body = document.createElement('div')
body.className = 'header-body'
const box = document.createElement('ul')
box.className = 'tabheader'
body.appendChild(box)
node.appendChild(bar)
node.appendChild(body)
this._bar = bar
this._body = body
this.box = box
this.node = node
// init events.
initFoldBtn(this)
initEvent(this)
return node
}
}
const attr = {
highlightIcon () {
return createHighlightIcon()
},
data () {
const attr = this.data.attr
// Ensure there is a default selected value.
if (attr.selectedIndex === undefined) {
attr.selectedIndex = 0
}
const list = attr.data || []
const curItem = attr.selectedIndex
const ret = []
const itemTmpl = '<li class="th-item" data-floor="{{floor}}">'
+ '{{hlIcon}}{{floorName}}</li>'
list.forEach(function (item, idx) {
let html = itemTmpl.replace('{{floor}}', idx)
/* eslint-disable eqeqeq */
if (curItem == idx) {
html = html.replace('{{hlIcon}}', createHighlightIcon())
}
else {
html = html.replace('{{hlIcon}}', '')
}
/* eslint-enable eqeqeq */
html = html.replace('{{floorName}}', item)
ret.push(html)
}, this)
this.box.innerHTML = ret.join('')
},
selectedIndex (val) {
const attr = this.data.attr
if (val === undefined) {
val = 0
}
// if (val == attr.selectedIndex) {
// return
// }
attr.selectedIndex = val
this.attr.data.call(this)
folding(this)
this.style.textHighlightColor.call(this, this.textHighlightColor)
}
}
const style = {
opacity (val) {
if (val === undefined || val < 0 || val > 1) {
val = 1
}
this.node.style.opacity = val
},
textColor (val) {
if (!isValidColor(val)) {
return
}
this.node.style.color = val
},
textHighlightColor (val) {
if (!isValidColor(val)) {
return
}
this.textHighlightColor = val
const attr = this.data.attr
const node = this.node.querySelector('[data-floor="'
+ attr.selectedIndex + '"]')
if (node) {
node.style.color = val
scrollToView(this, node)
}
}
}
function init (Weex) {
const Atomic = Weex.Atomic
const extend = Weex.utils.extend
function TabHeader (data) {
Atomic.call(this, data)
}
TabHeader.prototype = Object.create(Atomic.prototype)
extend(TabHeader.prototype, proto)
extend(TabHeader.prototype, { attr })
extend(TabHeader.prototype, {
style: extend(Object.create(Atomic.prototype.style), style)
})
Weex.registerComponent('tabheader', TabHeader)
}
export default { init }