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
JavaScript
/**
* @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 = ` commonListFocusInAni
{
0% {transform: translateY(0px) translateZ(100px);}
50% {transform: translateY(-${title.t}px) translateZ(100px);}
100% {transform: translateY(-${halfScaleH}px) translateZ(100px);}
}
commonListFocusOutAni
{
0% {transform: translateY(-${halfScaleH}px) translateZ(100px);}
100% {transform: translateY(0px) translateZ(100px);}
}
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,
};