domShowInputDatatime.mjs

import ot from 'dayjs'
import get from 'lodash-es/get.js'
import genPm from './genPm.mjs'
import genID from './genID.mjs'
import isestr from './isestr.mjs'
import isday from './isday.mjs'
import istime from './istime.mjs'
import isEle from './isEle.mjs'
import domRemove from './domRemove.mjs'
import domFind from './domFind.mjs'


/**
 * 前端開啟選擇時間視窗並回傳新時間
 *
 * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/domShowInputDatatime.test.mjs Github}
 * @memberOf wsemi
 * @param {String} [time=''] 輸入時間字串,可不提供,時間視窗預設為今日,預設為''
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Element} [opt.eleRef=null] 輸入觸發點擊Element元素,若不給則使用document.activeElement取得,預設為null
 * @param {String} [opt.type='datetime-local'] 輸入時間類型字串,可選為'datetime-local'與'date',預設為'datetime-local'
 * @returns {Promise} 回傳Promise,resolve為選擇新時間,無reject
 * @example
 * need test in browser
 *
 * let timePrev
 *
 * timePrev = '2000-01-01T00:00:00'
 * domShowInputDatatime(timePrev)
 *     .then((timeNew)=>{
 *         console.log('timeNew', timeNew)
 *     })
 *
 * timePrev = '2000-01-01'
 * domShowInputDatatime(timePrev, { type: 'date' })
 *     .then((timeNew)=>{
 *         console.log('timeNew', timeNew)
 *     })
 *
 */
function domShowInputDatatime(time, opt = {}) {

    //type
    let type = get(opt, 'type', '')
    if (type !== 'date' && type !== 'datetime-local') {
        type = 'datetime-local'
    }

    //eleRef
    let eleRef = get(opt, 'eleRef', null)
    if (!isEle(eleRef)) {
        eleRef = document.activeElement
    }
    // console.log('eleRef', eleRef)

    //pm
    let pm = genPm()

    //id
    let id = 'drf' + genID() //若全英文數字可能會導致出現特例如hex問題, 添加字首以避免

    //gname
    let gname = 'GrpDomShowDatatime'

    //remove
    domRemove(`[name=${gname}]`)

    //v, inp顯示用時間
    let b = false
    let v = ''
    if (type === 'date') {
        if (isday(time)) {
            b = true
            v = time
        }
    }
    else {
        if (istime(time)) {
            b = true
            let vt = ot(time)
            v = vt.format('YYYY-MM-DDTHH:mm')
        }
    }
    if (isestr(v)) {
        // console.log(type, 'v', v)
        v = `value="${v}"`
    }
    // console.log(type, 'v', v)

    //div, inp會放在div內, div會依照有無提供
    let div = document.createElement('div')
    div.setAttribute('name', gname) //div非表單元素沒有name, 不能使用div.name = gname
    div.style.width = 0
    div.style.height = 0
    div.style.overflow = 'hidden'
    div.innerHTML = `
        <div style="transform:translateY(-100%);">
            <input id="${id}" type="${type}" style="opacity:0;" ${b ? v : ''} />
        </div>
    `

    //inp, 函數內取用
    let inp = null

    //check
    if (!isEle(eleRef)) {

        //appendChild
        document.querySelector('body').appendChild(div)

    }
    else {

        //insertBefore
        eleRef.parentNode.insertBefore(div, eleRef.nextSibling)

    }

    //bTrigger
    let bTrigger = false

    //Trigger
    function Trigger() {

        //check, 因可能change與focusout皆會觸發, 故通過bTrigger只觸發1次
        if (bTrigger) {
            return
        }
        bTrigger = true

        //removeEventListener
        try {
            inp.removeEventListener('change', evChange, true)
            eleRef.removeEventListener('focusout', evFocusout, true)
            // console.log('removeEventListener')
        }
        catch (err) {}

        //remove element
        domRemove(`[name=${gname}]`)
        // console.log('domRemove')

        //check
        if (isestr(timeChange)) {
            // console.log('取得變更時間')

            //timeNew
            let timeNew = ''
            if (type === 'date') {
                timeNew = timeChange
            }
            else {
                timeNew = `${timeChange}:00`
            }
            // console.log(type, 'timeNew', timeNew)

            //resolve
            pm.resolve(timeNew)

        }
        else {
            // console.log('未變更時間')
        }

    }

    //evChange
    let timeChange = ''
    function evChange(msg) {
        // console.log('evChange', msg)

        //save
        try {
            timeChange = msg.target.value
            // console.log('timeChange', timeChange)
        }
        catch (err) {
            console.log(err)
        }

        //Trigger
        Trigger()

    }

    //evFocusout
    function evFocusout(msg) {
        // console.log('evFocusout', msg)

        //setTimeout, picker取消時靠觸發元素的focusout事件來得知, focusout會比change觸發還快, 故得要延遲觸發
        setTimeout(() => {
            Trigger()
        }, 50) //不能給1, 觸發時間最小可能為20ms, 手速過快, 可能會導致focusout仍比change還快觸發, 導致無法接收與處理change事件

    }

    //setTimeout, 因若是順發太快, 瀏覽器inp位置尚未出現, 挑選日曆時間視窗無法定位會出現至瀏覽器左上角, 故須延遲觸發
    setTimeout(() => {

        //inp, 須更新函數內取用, 才能於結束階段inp取消監聽
        inp = domFind('#' + id)

        //change
        inp.addEventListener('change', evChange, true)

        //showPicker
        inp.showPicker() //彈出日曆視窗
        // inp.focus() //自動聚焦至年選項

    }, 1)

    //監聽觸發者取消焦點
    eleRef.addEventListener('focusout', evFocusout, true)

    return pm
}


export default domShowInputDatatime