UNPKG

principles-ui-components

Version:

Supporting UI controller for Tizen TV web application, which developed base on React Framework.

1,009 lines (895 loc) 35.8 kB
/** * @author Song Zhang (song8.zhang@samsung.com) and Haipeng Zhang(hp.zhang@samsung.com) * @fileoverview This module manages single list. * @date 2017/07/18 (last modified date) * * Copyright 2017 by Samsung Electronics, Inc., * * This software is the confidential and proprietary information * of Samsung Electronics, Inc. ("Confidential Information"). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with Samsung. */ import React, { Component, PropTypes } from 'react'; import ReactDOM from 'react-dom'; import { is, Map } from 'immutable'; import ScrollBar from './ScrollBar'; import ImageItem from './ImageItem'; import TTS from './common/TTS'; import { AnimationEffect, KEY, TTSTYPE } from './common/CommonDefine'; import './css/CommonList.css'; import ScrollText from './common/ScrollText'; const ListType = { FOCUSIN: 0, FOCUSOUT: 1, }; export default class CommonList extends Component { constructor(props) { super(props); this.node = []; // define state. this.state = { itemsStyle: [], itemsFocus: [], hScrollPx: 0, scrollHPercent: 0, bounce: '', titleAni: '', }; this.itemsFocus = []; // init each item's focus and style. const { focusScale, maxScale, gap, details, title } = this.props; this.detailslen = details.length; for (let i = 0; i < this.detailslen; i += 1) { this.state.itemsFocus[i] = false; this.state.itemsStyle[i] = {}; this.itemsFocus[i] = false; } this.itemsInfo = []; // item information, w,h,marginRight this.itemsHMove = []; this.itemsStyle = []; this.lastFocusOutPos = null; this.contentTop = 0; this.listheight = 0; this.scrollBarTop = 0; this.hasFocus = props.initFocus; this.bounceL = ''; this.bounceR = ''; this.bounce = ''; this.titleAni = ''; this.ttsType = TTSTYPE.LIST; this.lastKey = 'keyup'; // record which index has max height. this.hScrollPx = 0; for (let i = 0; i < this.detailslen; i += 1) { const info = { focusScale, maxScale, gap, w: details[i].OSD.layout.w, h: details[i].OSD.layout.h, }; this.itemsInfo[i] = info; } this.focusIndex = props.initIndex; this.scaleW = this.itemsInfo[0].w * (this.itemsInfo[0].focusScale - 1); this.halfScaleW = this.scaleW / 2; if (this.hasFocus) { // title if (title !== undefined && title !== null) { this.titleAni = 'commonListFocusInAni'; this.state.titleAni = this.titleAni; } this.state.itemsFocus[this.focusIndex] = true; this.itemsFocus[this.focusIndex] = true; this.setItemsHMove(ListType.FOCUSIN); } else { this.setItemsHMove(ListType.FOCUSOUT); } this.refreshItemStyle(); this.state.itemsStyle = this.itemsStyle.slice(0, this.itemsStyle.length); } componentWillMount() { const { title } = this.props; const halfScaleH = (this.itemsInfo[0].h * (this.itemsInfo[0].focusScale - 1)) / 2; if (title !== undefined && title !== null) { this.style = document.createElement('style'); this.style.type = 'text/css'; const keyFrames = `@keyframes commonListFocusInAni { 0% {transform: translateY(0px) translateZ(100px);} 50% {transform: translateY(-${title.t}px) translateZ(100px);} 100% {transform: translateY(-${halfScaleH}px) translateZ(100px);} } @keyframes commonListFocusOutAni { 0% {transform: translateY(-${halfScaleH}px) translateZ(100px);} 100% {transform: translateY(0px) translateZ(100px);} } @keyframes commonListInitAni { 0% {transform: translateY(0px) translateZ(100px);} 100% {transform: translateY(0px) translateZ(100px);} } `; this.style.innerHTML = keyFrames; document.getElementsByTagName('head')[0].appendChild(this.style); } } componentDidMount() { const { morefetchEnable, ttsEnable } = this.props; if (!morefetchEnable && ttsEnable && this.hasFocus) { this.playTTS(this.ttsType); } } componentWillReceiveProps(nextProps) { this.detailslen = nextProps.details.length; } shouldComponentUpdate(nextProps, nextState) { const needUpdate = (JSON.stringify(nextProps) !== JSON.stringify(this.props) || JSON.stringify(nextState) !== JSON.stringify(this.state)); return needUpdate; } componentDidUpdate() { if (this.scroll) { this.scroll.showBar(); } const { morefetchEnable, ttsEnable } = this.props; if (!morefetchEnable && ttsEnable && this.hasFocus) { this.playTTS(this.ttsType); } } componentWillUnmount() { this.removeStyleFromHeader(); } /** * set each item horizontal move value * @name setItemsHMove * @method * @memberof * @param */ setItemsHMove(type) { let i = 0; // init for (i = 0; i < this.detailslen; i += 1) { if (i > 0) { this.itemsHMove[i] = this.itemsHMove[i - 1] + this.itemsInfo[i - 1].w + this.itemsInfo[i - 1].gap; } else { this.itemsHMove[i] = 0; } } // move if (type === ListType.FOCUSIN) { if (this.focusIndex === 0 && !this.props.firstItemMoveEnable) { this.itemsHMove[this.focusIndex] = 0; for (i = this.focusIndex + 1; i < this.detailslen; i += 1) { this.itemsHMove[i] += this.halfScaleW; } } else { this.itemsHMove[this.focusIndex] += this.halfScaleW; for (i = this.focusIndex + 1; i < this.detailslen; i += 1) { this.itemsHMove[i] += this.scaleW; } } } } getElementPos(elem) { let x = 0; let y = 0; let tmpElem = elem; while (tmpElem != null) { x += tmpElem.offsetLeft; y += tmpElem.offsetTop; tmpElem = tmpElem.offsetParent; } return { x, y }; } getFocusElmSize() { let outPosition = null; // const elem = ReactDOM.findDOMNode(this.refs[`item${this.focusIndex}`]); const { details } = this.props; const elem = this.node[this.focusIndex]; if (elem !== null) { outPosition = this.getElementPos(elem); outPosition.x += this.itemsHMove[this.focusIndex]; outPosition.x += this.props.transLeft; outPosition.x += this.hScrollPx; outPosition.x -= Math.floor((details[this.focusIndex].OSD.image[0].w * 0.2) / 2); outPosition.y -= Math.floor((details[this.focusIndex].OSD.image[0].h * 0.2) / 2) + 8; outPosition.w = Math.floor(details[this.focusIndex].OSD.image[0].w * 1.2); outPosition.h = Math.floor(details[this.focusIndex].OSD.image[0].h * 1.2); } return outPosition; } getFocusIndex() { return this.focusIndex; } getFocusIndexByPos(position) { const { transLeft, width } = this.props; const totalLength = position.x - this.hScrollPx - transLeft; // for near rule if (totalLength <= 0) { this.focusIndex = 0; } else { this.focusIndex = this.detailslen - 1; for (let i = 0; i < this.detailslen; i += 1) { if (this.itemsHMove[i] + this.itemsInfo[i].w > totalLength && this.itemsHMove[i] + this.hScrollPx + transLeft >= 0) { this.focusIndex = i; break; } } } if (this.focusIndex >= this.detailslen) { this.focusIndex = this.detailslen - 1; } return this.focusIndex; } getListArea() { const { transLeft } = this.props; const value = { lx: 0, rx: 0, }; if (this.detailslen > 0) { const lx = this.itemsHMove[0] + transLeft + this.hScrollPx; let rx = this.itemsHMove[this.detailslen - 1] + this.itemsInfo[this.detailslen - 1].w + transLeft + this.hScrollPx; if (this.focusIndex === this.detailslen - 1) { rx += this.halfScaleW; } value.lx = lx; value.rx = rx; } return value; } /** * get scroll flag for the list * @name getScrollFlag * @method * @memberof * @param */ getScrollFlag() { const { transLeft, width, gap } = this.props; let screenWidth = 0; let screenItemsNum = 0; screenWidth = width - transLeft - this.scaleW; screenItemsNum = Math.floor(screenWidth / (this.itemsInfo[0].w + gap)); if (this.detailslen > screenItemsNum) { return true; } return false; } /** * get lastItem scroll width for the last item is not show all in screen * @name getLastItemScrollPx * @method * @memberof * @param */ getLastItemScrollPx() { // for example: 1920 - 80 - 57 = 1783 1783/287 = 6.212 1- 0.212 = 0.788 0.788*287 = 226 const { transLeft, width, gap, transRight } = this.props; let screenWidth = 0; let screenItemsNumT = 0; let screenItemsNum = 0; let lastScrollPx = 0; screenWidth = width - transLeft - this.scaleW; screenItemsNumT = screenWidth / (this.itemsInfo[0].w + gap); screenItemsNum = Math.floor(screenItemsNumT); lastScrollPx = (1 - (screenItemsNumT - screenItemsNum)) * (this.itemsInfo[0].w + gap); return lastScrollPx + transRight; } /** * get last total scroll width * @name getLastHScrollPx * @method * @memberof * @param */ getLastHScrollPx() { // for example: 10 - 6 = 4 4 *287 = -1148 const { transLeft, width, gap } = this.props; let screenWidth = 0; let screenItemsNum = 0; let value = 0; screenWidth = width - transLeft - this.scaleW; screenItemsNum = Math.floor(screenWidth / (this.itemsInfo[0].w + gap)); value = (this.detailslen - screenItemsNum) * (this.itemsInfo[0].w + gap) * -1; return value; } /** * get correct last scroll width * @name getLastScrollPx * @method * @memberof * @param */ getLastScrollPx() { const { gap } = this.props; // for example: -1184 + 287 - 226 = -1123 return this.getLastHScrollPx() + ((this.itemsInfo[0].w + gap) - this.getLastItemScrollPx()); } keyFocusIn(position) { if (this.hasFocus) { console.error('[CommonList.js:keyFocusIn] List already have focus, doing nothing.'); return; } const { details, transLeft, width, morefetchEnable, ttsEnable, title } = this.props; if (!details || this.detailslen <= 0) { console.error('[CommonList.js:keyFocusIn] data is empty, doing nothing.'); return; } if (position === null || position === undefined || position.x === 0) { /* * if not giving focus in position, set focus to default position. */ if (!this.lastFocusOutPos) { this.focusIndex = 0; } else { this.focusIndex = this.getFocusIndexByPos(this.lastFocusOutPos); } } else { this.focusIndex = this.getFocusIndexByPos(position); } this.hasFocus = true; if (!morefetchEnable && ttsEnable) { this.ttsType = TTSTYPE.LIST; } this.setItemsHMove(ListType.FOCUSIN); this.itemsFocus[this.focusIndex] = true; this.refreshItemStyle(); if (!morefetchEnable) { this.adjustListPosition(); } // last page if (this.getScrollFlag()) { if (this.hScrollPx !== 0 && this.hScrollPx === this.getLastScrollPx() + this.scaleW) { this.hScrollPx -= this.scaleW; } } // title if (title !== undefined && title !== null) { this.titleAni = 'commonListFocusInAni'; } this.setState({ itemsFocus: this.itemsFocus.slice(0, this.itemsFocus.length), itemsStyle: this.itemsStyle.slice(0, this.itemsStyle.length), hScrollPx: this.hScrollPx, titleAni: this.titleAni, }); } keyFocusInByIndex(index) { if (this.hasFocus) { console.error('[CommonList.js:keyFocusIn] List already have focus, doing nothing.'); return; } const { details, transLeft, width, morefetchEnable, ttsEnable, title } = this.props; if (!details || this.detailslen <= 0) { console.error('[CommonList.js:keyFocusIn] data is empty, doing nothing.'); return; } this.focusIndex = index; this.hasFocus = true; if (!morefetchEnable && ttsEnable) { this.ttsType = TTSTYPE.LIST; } this.setItemsHMove(ListType.FOCUSIN); this.itemsFocus[this.focusIndex] = true; this.refreshItemStyle(); if (!morefetchEnable) { this.adjustListPosition(); } // last page if (this.getScrollFlag()) { if (this.hScrollPx !== 0 && this.hScrollPx === this.getLastScrollPx() + this.scaleW) { this.hScrollPx -= this.scaleW; } } // title if (title !== undefined && title !== null) { this.titleAni = 'commonListFocusInAni'; } this.setState({ itemsFocus: this.itemsFocus.slice(0, this.itemsFocus.length), itemsStyle: this.itemsStyle.slice(0, this.itemsStyle.length), hScrollPx: this.hScrollPx, titleAni: this.titleAni, }); } playTTS(type) { if (type === TTSTYPE.LIST) { this.TTSnode.playTTS(); } else if (type === TTSTYPE.ITEM) { this[`item${this.focusIndex}`].playTTS(); } } refreshItemStyle() { let index = 0; for (index = 0; index < this.detailslen; index++) { this.itemsStyle[index] = { position: 'absolute', width: this.itemsInfo[index].w, heigth: this.itemsInfo[index].h, transform: `translateX(${this.itemsHMove[index]}px) scale(${this.itemsFocus[index] ? (this.itemsInfo[index].focusScale) : 1.0}) translateZ(10px)`, transition: this.itemsFocus[index] ? `all 1.1s ${AnimationEffect.Elastic}` : `all 0.3s ${AnimationEffect.Basic}`, zIndex: this.itemsFocus[index] ? 2 : 1, }; } } keyFocusOut() { if (!this.hasFocus) { console.error('[CommonList.js:keyFocusOut] List do not have focus, doing nothing.'); return false; } let outPosition = null; const elem = this.node[this.focusIndex]; if (elem !== null) { outPosition = this.getElementPos(elem); outPosition.x = 0; outPosition.x += this.itemsHMove[this.focusIndex]; outPosition.x += this.props.transLeft; outPosition.x += this.hScrollPx; outPosition.x += Math.floor(this.itemsInfo[this.focusIndex].w / 2); // for near rule,get the center position of the focus item } this.setItemsHMove(ListType.FOCUSOUT); this.itemsFocus[this.focusIndex] = false; this.refreshItemStyle(); this.hasFocus = false; this.ttsType = TTSTYPE.LIST; this.lastKey = 'keyup'; // last page if (this.getScrollFlag()) { if (this.hScrollPx !== 0 && this.hScrollPx === this.getLastScrollPx()) { this.hScrollPx += this.scaleW; } } this.focusIndex = -1; // title if (this.props.title !== undefined && this.props.title !== null) { this.titleAni = 'commonListFocusOutAni'; } this.setState({ itemsStyle: this.itemsStyle.slice(0, this.itemsStyle.length), itemsFocus: this.itemsFocus.slice(0, this.itemsFocus.length), hScrollPx: this.hScrollPx, titleAni: this.titleAni, }); if (this.props.title !== undefined && this.props.title !== null) { this.titleAni = 'commonListInitAni'; this.setState({ titleAni: this.titleAni, }); } this.lastFocusOutPos = outPosition; return outPosition; } adjustListPosition() { const { width, transLeft } = this.props; const focusItemInfo = this.itemsInfo[this.focusIndex]; const oldScroll = this.hScrollPx; if (this.focusIndex === 0) { this.hScrollPx = 0; } else if (this.itemsHMove[this.focusIndex] + this.hScrollPx + transLeft < this.halfScaleW) { // left side covered. if (this.hScrollPx === this.getLastScrollPx() + this.scaleW && this.getScrollFlag()) { console.log('focus in, No need to move'); } else { while (this.itemsHMove[this.focusIndex] + this.hScrollPx + transLeft < this.halfScaleW) { this.hScrollPx += (focusItemInfo.w + focusItemInfo.gap); } if (this.getScrollFlag()) { if (this.hScrollPx === this.getLastScrollPx() + (focusItemInfo.w + focusItemInfo.gap)) { this.hScrollPx -= (focusItemInfo.w + focusItemInfo.gap); this.hScrollPx += this.getLastItemScrollPx(); } } } } else if ((this.hScrollPx + this.itemsHMove[this.focusIndex]) + ((focusItemInfo.w * focusItemInfo.focusScale) - this.halfScaleW) + transLeft > width) { // right side covered if (this.hScrollPx === this.getLastScrollPx() + this.scaleW && this.getScrollFlag()) { console.log('focus in, No need to move'); } else { while (this.hScrollPx + this.itemsHMove[this.focusIndex] + ((focusItemInfo.w * focusItemInfo.focusScale) - this.halfScaleW) + transLeft > width) { this.hScrollPx -= (focusItemInfo.w + focusItemInfo.gap); } if (this.getScrollFlag()) { if (this.hScrollPx !== 0 && this.hScrollPx === this.getLastHScrollPx()) { this.hScrollPx = this.getLastScrollPx(); } } } } if (oldScroll !== this.hScrollPx && this.scrollCB) { // scroll to next page let scrollPx = this.hScrollPx; if (scrollPx < 0) { scrollPx *= -1; } const listArea = this.getListArea(); const hiddenPx = (listArea.rx - listArea.lx) - width; if (hiddenPx > 0) { if (scrollPx > hiddenPx) { scrollPx = hiddenPx; } this.scrollCB(scrollPx / hiddenPx); } } } scrollCB(percent) { this.setState({ scrollHPercent: percent }); } removeStyleFromHeader() { if (this.style) { document.getElementsByTagName('head')[0].removeChild(this.style); this.style = null; } } handleBounce(direct) { if (this.props.bounceEnable) { if (direct === -1) { if (this.bounceL === 'bounceL1') { this.bounceL = 'bounceL2'; } else { this.bounceL = 'bounceL1'; } // do bounceL this.bounce = this.bounceL; } else if (direct === 1) { if (this.bounceR === 'bounceR1') { this.bounceR = 'bounceR2'; } else { this.bounceR = 'bounceR1'; } // do bounceR this.bounce = this.bounceR; } } this.setState({ bounce: this.bounce, }); } handleLeftKey(event) { if (!this.hasFocus) { console.error('[CommonList.js:handleLeftKey] List do not have focus, doing nothing.'); return false; } const { details, morefetchEnable, bounceEnable, ttsEnable } = this.props; if (details) { if (this.focusIndex < 0 || this.focusIndex >= this.detailslen) { console.error(`[CommonList.js:handleLeftKey] no focusIndex available, doing nothing. index is ${this.focusIndex}`); return false; } if (this.focusIndex === 0) { if ((event.type === 'keydown' && this.lastKey === 'keydown') || (event.type === 'keyup' && this.lastKey === 'keydown') || (event.type === 'keyup' && this.lastKey === 'keyup')) { this.lastKey = event.type; return false; } this.lastKey = event.type; this.handleBounce(-1); return true; } if (event.type === 'keyup') { return false; } if (this.focusIndex - 1 >= 0 && this.focusIndex < this.detailslen) { this.itemsFocus[this.focusIndex] = false; this.itemsFocus[this.focusIndex - 1] = true; this.focusIndex -= 1; this.setItemsHMove(ListType.FOCUSIN); this.refreshItemStyle(); if (!morefetchEnable) { if (ttsEnable) { this.ttsType = TTSTYPE.ITEM; } this.adjustListPosition(); } this.setState({ itemsFocus: this.itemsFocus.slice(0, this.itemsFocus.length), itemsStyle: this.itemsStyle.slice(0, this.itemsStyle.length), hScrollPx: this.hScrollPx, }); } else { return false; } } else { console.error(`[CommonList.js:handleLeftKey] no details available ${details}, doing nothing.`); return false; } return true; } handleRightKey(event) { if (!this.hasFocus) { console.error('[CommonList.js:handleRightKey] List do not have focus, doing nothing.'); return false; } const { details, width, morefetchEnable, bounceEnable, ttsEnable } = this.props; if (details) { if (this.focusIndex < 0 || this.focusIndex >= this.detailslen) { console.error('[CommonList.js:handleRightKey] no focusIndex available, doing nothing.'); return false; } if (this.focusIndex === this.detailslen - 1) { if ((event.type === 'keydown' && this.lastKey === 'keydown') || (event.type === 'keyup' && this.lastKey === 'keydown') || (event.type === 'keyup' && this.lastKey === 'keyup')) { this.lastKey = event.type; return false; } this.lastKey = event.type; this.handleBounce(1); return true; } if (event.type === 'keyup') { return false; } if (this.focusIndex + 1 < this.detailslen) { this.itemsFocus[this.focusIndex] = false; this.itemsFocus[this.focusIndex + 1] = true; this.focusIndex += 1; this.setItemsHMove(ListType.FOCUSIN); this.refreshItemStyle(); if (!morefetchEnable) { if (ttsEnable) { this.ttsType = TTSTYPE.ITEM; } this.adjustListPosition(); } this.setState({ itemsFocus: this.itemsFocus.slice(0, this.itemsFocus.length), itemsStyle: this.itemsStyle.slice(0, this.itemsStyle.length), hScrollPx: this.hScrollPx, }); } else { console.error('[CommonList.js:handleRightKey] Not able to handle left key.'); return false; } } else { console.error(`[CommonList.js:handleRightKey] no details available ${details}, doing nothing.`); return false; } return true; } /* * Handle user's key press, called by parent component. * Only support left/right/enter key. * if list don't have focus, doing nothing. */ handleKey(event) { if (!this.hasFocus) { console.error('[CommonList.js:handleKey] List do not have focus, doing nothing.'); return false; } switch (event.keyCode) { case KEY.ENTER: break; case KEY.LEFT: // LEFT return this.handleLeftKey(event); case KEY.RIGHT: // RIGHT return this.handleRightKey(event); default: break; } return true; } render() { const { details, gap, width, transLeft, top, transition, bounceDelay, scrollBarEnable, scrollbar, maxScale } = this.props; const { contentTop, height, scrollBarTop, morefetchEnable, bounceEnable, ttsEnable, ttsText, firstItemMoveEnable, title } = this.props; const { itemsStyle, itemsFocus, bounce, titleAni } = this.state; // list content const content = []; for (let i = 0; i < this.detailslen; i += 1) { let delay = 0; const idx = i; if (bounce === 'bounceL1' || bounce === 'bounceL2') { delay = (idx * 35) + bounceDelay; } else if (bounce === 'bounceR1' || bounce === 'bounceR2') { delay = ((this.detailslen - idx) * 35) + bounceDelay; } const bounceStyle = {}; if (bounceEnable) { if (this.bounce !== '') { bounceStyle.animationName = bounce; bounceStyle.animationDelay = `${delay}ms`; bounceStyle.animationIterationCount = '1'; bounceStyle.animationDuration = '0.7s'; bounceStyle.animationTimingFunction = 'cubic-bezier(0.3,0,0.15,1)'; } } if (this.itemsFocus[idx]) { bounceStyle.zIndex = 99; } else { bounceStyle.zIndex = 0; } content.push(<div key={i} style={itemsStyle[i]} ref={(node) => { this.node[i] = node; }}> <div style={bounceStyle}> <ImageItem OSD={details[i].OSD} ref={(items) => { this[`item${i}`] = items; }} focus={itemsFocus[i]} enter={this.props.enter && i === this.focusIndex} enterCB={this.props.enterCB} scaleFactor={1.0} ttsEnable={ttsEnable} key={`item${i}`} /> </div> </div>); } if (bounceEnable) { this.bounce = ''; } if (scrollBarTop === 0) { this.scrollBarTop = details[0].OSD.layout.h * maxScale; } else { this.scrollBarTop = scrollBarTop; } // scrollbar let scrollbarComponent = null; const totalLength = (details[0].OSD.layout.w + gap) * this.detailslen; const scrollLeft = firstItemMoveEnable ? transLeft : (transLeft - this.halfScaleW); const barLength = width - (scrollLeft * 2); if (scrollBarEnable && this.hasFocus && totalLength > width) { scrollbarComponent = (<ScrollBar ref={(scroll) => { this.scroll = scroll; }} left={scrollLeft} top={this.scrollBarTop} barType='h' bgWidth={barLength} bgHeight={3} barPercent={barLength / totalLength} scrollPercent={this.state.scrollHPercent} />); } // TTS let TTSComponent = null; if (ttsEnable) { TTSComponent = <TTS ref={(TTSnode) => { this.TTSnode = TTSnode; }} ttsEnable={ttsEnable} ttsText={ttsText} />; } if (height === 0) { this.listheight = details[0].OSD.layout.h * maxScale; } else { this.listheight = height; } // list style const listStyle = { position: 'relative', width, height: this.listheight, // list height overflow: morefetchEnable ? 'visible' : 'hidden', top, }; if (contentTop === 0) { this.contentTop = details[0].OSD.layout.h * ((maxScale - 1.0) / 2); } else { this.contentTop = contentTop; } const contentStyle = { position: 'absolute', top: this.contentTop, // make item at center of the list container transform: `translateX(${this.hScrollPx + transLeft}px)`, transition, }; // title const titleStyle = { position: 'absolute', }; let titleComponent = null; if (title !== undefined && title !== null) { if (this.titleAni !== '') { titleStyle.animationName = titleAni; titleStyle.animationDelay = '0.083s'; titleStyle.animationIterationCount = '1'; titleStyle.animationDuration = '0.7s'; titleStyle.animationTimingFunction = 'cubic-bezier(0.3,0,0.15,1)'; titleStyle.animationFillMode = 'forwards'; } titleComponent = (<ScrollText top={title.t} left={title.l} width={title.w} height={title.h} lineHeight={`${title.h}px`} scroll={false} fontSize={title.fontSize} fontFamily={title.fontFamily} color={title.color} > {title.text} </ScrollText>); } return (<div> <div style={listStyle}> <div style={titleStyle}> {titleComponent} </div> <div style={contentStyle}> {content} </div> </div> {scrollbarComponent} {TTSComponent} </div>); } } CommonList.defaultProps = { morefetchEnable: false, // flag for whether to use morefetch function width: 1920, // width of display window of the list. height: 0, transLeft: 0, // when you want to move the list, but don't want to move display window, use this value. transRight: 0, // lastItem margin right top: 0, // same meaning as html top. it's used for display window. contentTop: 0, scrollBarTop: 0, enter: false, // if enter pressed. bounceEnable: true, // bounce back animation name. aviable value: bounce1/bounce2. transition: `all 0.2s ${AnimationEffect.Basic}`, // when list move out from display window edege, this define the transition. bounceDelay: 0, // delay time for bounce animation. enterCB: null, // enter call back. initFocus: false, initIndex: 0, focusScale: 1.2, maxScale: 1.3, gap: 2, scrollBarEnable: true, ttsEnable: false, ttsText: '', firstItemMoveEnable: true, title: undefined, }; CommonList.propTypes = { morefetchEnable: PropTypes.bool, // flag for whether to use morefetch function width: PropTypes.number, height: PropTypes.number, transLeft: PropTypes.number, transRight: PropTypes.number, top: PropTypes.number, contentTop: PropTypes.number, scrollBarTop: PropTypes.number, enter: PropTypes.bool, bounceEnable: PropTypes.bool, transition: PropTypes.string, bounceDelay: PropTypes.number, enterCB: PropTypes.func, initFocus: PropTypes.bool, // List has focus or not initIndex: PropTypes.number, focusScale: PropTypes.number, maxScale: PropTypes.number, gap: PropTypes.number, scrollBarEnable: PropTypes.bool, ttsEnable: PropTypes.bool, ttsText: PropTypes.string, firstItemMoveEnable: PropTypes.bool, title: PropTypes.shape({ l: PropTypes.number, t: PropTypes.number, w: PropTypes.number, h: PropTypes.number, fontSize: PropTypes.number, text: PropTypes.string, fontFamily: PropTypes.string, color: PropTypes.string, }), details: PropTypes.arrayOf(PropTypes.shape({ OSD: PropTypes.shape({ // UI content for ImageItem layout: PropTypes.shape({ l: PropTypes.number.isRequired, t: PropTypes.number.isRequired, w: PropTypes.number.isRequired, h: PropTypes.number.isRequired, }), image: PropTypes.array, icon: PropTypes.array, text: PropTypes.array, ttsText: PropTypes.string, progress: PropTypes.shape({ l: PropTypes.number, t: PropTypes.number, w: PropTypes.number, h: PropTypes.number, rate: PropTypes.number, }), }), })).isRequired, };