principles-ui-components
Version:
Supporting UI controller for Tizen TV web application, which developed base on React Framework.
450 lines (391 loc) • 15.6 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { is, Map, fromJS } from 'immutable';
import { MAIN_TEXT_FONT, TextItemFamily, AnimationEffect } from './common/CommonDefine';
import './css/Button.css';
import CommonAPI from './common/CommonAPI';
import ScrollText from './common/ScrollText';
import Image from './common/Image';
import TTS from './common/TTS';
export const DROPDOWN_TYPE = {
ICON: 'icon',
TEXT: 'text',
};
export const ARROW_TYPE = {
UP: 'up',
DOWN: 'down',
};
const STATUS = {
NORMAL: 0,
FOCUSED: 1,
SELECTED: 2,
DISABLED: 3,
};
const BtnResource = {
NORMAL: 'url(./images/btn/c_basic_button_white_bg_normal_9patch.png) 5 4 fill stretch',
FOCUS: 'url(./images/btn/r_highlight_bg_focus_9patch.png) 20 14 fill stretch',
HCNORMAL: 'url(./images/btn/c_basic_button_highcontrast_bg_normal_9patch.png) 6 5 fill stretch',
HCFOCUS: 'url(./images/btn/r_highlight_bg_focus_9patch.png) 20 14 fill stretch',
SELECTED: 'url(./images/btn/c_basic_button_white_bg_select_9patch.png) 5 4 fill stretch',
BAR: 'images/btn/c_buttondropdown_bar.png',
ARROWUP_N: 'images/btn/c_buttondropdown_arrow_up.png',
ARROWDOWN_N: 'images/btn/c_buttondropdown_arrow_down.png',
ARROWUP_F: 'images/btn/c_buttondropdown_arrow_up_focus.png',
ARROWDOWN_F: 'images/btn/c_buttondropdown_arrow_down_focus.png',
};
const TEXT_LEFT_BORDER = 21;
export default class DropdownButton extends Component {
constructor(props) {
super(props);
this.state = {
refresh: false,
};
this.status = STATUS.NORMAL;
this.AnimationName = '';
this.selectIdx = 1;
this.animationDone = this.animationDone.bind(this);
this.bodyGap = TextItemFamily.BODY_TEXT_GAP;
}
componentWillMount() {
const { buttonScale, refname } = this.props;
this.style = document.createElement('style');
this.style.type = 'text/css';
const keyFrames = ` FocusIn${refname}
{
0% {transform: scale(1.00) translateZ(100px);}
100% {transform: scale(${buttonScale}) translateZ(100px);}
}
FocusOut${refname}
{
0% {transform: scale(${buttonScale}) translateZ(100px);}
100% {transform: scale(1.00) translateZ(100px);}
}
SelectedIn${refname}
{
0% {transform: scale(${buttonScale}) translateZ(100px);}
100% {transform: scale(1.00) translateZ(100px);}
}
SelectedOut${refname}
{
0% {transform: scale(1.00) translateZ(100px);}
100% {transform: scale(${buttonScale}) translateZ(100px);}
}
`;
this.style.innerHTML = keyFrames;
document.getElementsByTagName('head')[0].appendChild(this.style);
}
componentDidMount() {
this.motionContent.addEventListener('animationend', this.animationDone, false);
}
componentWillReceiveProps(nextProps) {
if (this.props.buttonScale !== nextProps.buttonScale) {
this.removeStyleFromHeader();
this.style = document.createElement('style');
this.style.type = 'text/css';
const keyFrames = ` FocusIn${nextProps.refname}
{
0% {transform: scale(1.00) translateZ(100px);}
100% {transform: scale(${nextProps.buttonScale}) translateZ(100px);}
}
FocusOut${nextProps.refname}
{
0% {transform: scale(${nextProps.buttonScale}) translateZ(100px);}
100% {transform: scale(1.00) translateZ(100px);}
}
SelectedIn${nextProps.refname}
{
0% {transform: scale(${nextProps.buttonScale}) translateZ(100px);}
100% {transform: scale(1.00) translateZ(100px);}
}
SelectedOut${nextProps.refname}
{
0% {transform: scale(1.00) translateZ(100px);}
100% {transform: scale(${nextProps.buttonScale}) translateZ(100px);}
}
`;
this.style.innerHTML = keyFrames;
document.getElementsByTagName('head')[0].appendChild(this.style);
}
}
shouldComponentUpdate(nextProps, nextState) {
return (JSON.stringify(nextProps) !== JSON.stringify(this.props)) || (JSON.stringify(nextState) !== JSON.stringify(this.state));
}
componentWillUnmount() {
this.removeStyleFromHeader();
this.motionContent.removeEventListener('animationend', this.animationDone, false);
}
getButtonStyle() {
const { layout, highContrast, refname } = this.props;
const ButtonStyle = {};
ButtonStyle.position = 'absolute';
ButtonStyle.left = layout.l;
ButtonStyle.top = layout.t;
ButtonStyle.width = layout.w;
ButtonStyle.height = layout.h;
// ButtonStyle.border = '2px solid rgb(255,255,255)';
if (this.status === STATUS.FOCUSED) {
ButtonStyle.borderImage = highContrast ? BtnResource.HCFOCUS : BtnResource.FOCUS;
} else if (this.status === STATUS.SELECTED) {
ButtonStyle.borderImage = BtnResource.SELECTED;
} else { // NORMAL and DISABLED
ButtonStyle.borderImage = highContrast ? BtnResource.HCNORMAL : BtnResource.NORMAL;
}
ButtonStyle.animationName = this.AnimationName;
ButtonStyle.animationFillMode = 'forwards';
if (this.AnimationName === `FocusIn${refname}`) {
ButtonStyle.animationDuration = '1.10s';
ButtonStyle.animationTimingFunction = AnimationEffect.Elastic;
} else if (this.AnimationName === `FocusOut${refname}`) {
ButtonStyle.animationDuration = '0.85s';
ButtonStyle.animationTimingFunction = AnimationEffect.Out;
} else if (this.AnimationName === `SelectedIn${refname}`) {
ButtonStyle.animationDuration = '0.85s';
ButtonStyle.animationTimingFunction = AnimationEffect.Out;
} else if (this.AnimationName === `SelectedOut${refname}`) {
ButtonStyle.animationDuration = '1.10s';
ButtonStyle.animationTimingFunction = AnimationEffect.Elastic;
} else {
ButtonStyle.animationDuration = '0s';
ButtonStyle.animationTimingFunction = '';
}
return ButtonStyle;
}
getDimStyle() {
const { layout } = this.props;
const dimAnimation = {};
dimAnimation.position = 'absolute';
dimAnimation.width = layout.w; // if have border, need to sub 2* borderwidth
dimAnimation.height = layout.h; // if have border, need to sub 2* borderwidth
dimAnimation.name = this.AnimationName;
if (this.AnimationName === 'DimIn' || this.AnimationName === 'DimOut') {
dimAnimation.animationDuration = '0.333s';
dimAnimation.animationTimingFunction = AnimationEffect.Basic;
} else {
dimAnimation.animationDuration = '0s';
dimAnimation.animationTimingFunction = '';
}
return dimAnimation;
}
getIcon() {
const { iconOSD } = this.props;
const imageOSD = {
l: iconOSD.l,
t: iconOSD.t,
w: iconOSD.w,
h: iconOSD.h,
url: iconOSD.url_n,
};
if (this.status === STATUS.FOCUSED && typeof iconOSD.url_f !== 'undefined') {
imageOSD.url = iconOSD.url_f;
}
return (<Image OSD={imageOSD} opacity={0} />);
}
getTextAndBar() {
const { layout, text, enlarge, textFont, textSize } = this.props;
const barOsd = {
l: layout.w - 76,
t: (layout.h - 64) / 2,
w: 2,
h: 64,
url: BtnResource.BAR,
};
return (<div>
<ScrollText
left={TEXT_LEFT_BORDER}
top={TEXT_LEFT_BORDER}
scroll={true}
fontSize={enlarge ? (textSize * 1.2) : textSize}
fontFamily={textFont}
textGap={this.bodyGap}
width={layout.w - (TEXT_LEFT_BORDER * 2)}
height={layout.h - (TEXT_LEFT_BORDER * 2)}
lineHeight={`${layout.h - (TEXT_LEFT_BORDER * 2)}px`}
textAlign={'center'}
>
{text}
</ScrollText>
<Image OSD={barOsd} />
</div>
);
}
getArrow() {
const { arrowType, layout, buttonType } = this.props;
const arrowOsd = {};
arrowOsd.w = 17;
arrowOsd.h = 17;
arrowOsd.l = layout.w - arrowOsd.w - (buttonType === DROPDOWN_TYPE.ICON ? 11 : 30);
arrowOsd.t = (layout.h - arrowOsd.h) / 2;
if (arrowType === ARROW_TYPE.UP) {
if (this.status === STATUS.FOCUSED) {
arrowOsd.url = BtnResource.ARROWUP_F;
} else if (this.status === STATUS.SELECTED) {
arrowOsd.url = BtnResource.ARROWDOWN_F;
} else {
arrowOsd.url = BtnResource.ARROWUP_N;
}
} else if (arrowType === ARROW_TYPE.DOWN) {
if (this.status === STATUS.FOCUSED) {
arrowOsd.url = BtnResource.ARROWDOWN_F;
} else if (this.status === STATUS.SELECTED) {
arrowOsd.url = BtnResource.ARROWUP_F;
} else {
arrowOsd.url = BtnResource.ARROWDOWN_N;
}
}
return (<Image OSD={arrowOsd} />);
}
SelectButton(flag) {
if (this.status === STATUS.DISABLED) {
console.log('Button disabled!');
return;
}
if (flag && this.status === STATUS.FOCUSED) {
this.AnimationName = `SelectedIn${this.props.refname}`;
this.status = STATUS.SELECTED;
} else if (!flag && this.status === STATUS.SELECTED) {
this.AnimationName = `SelectedOut${this.props.refname}`;
this.status = STATUS.FOCUSED;
} else {
return;
}
this.setState({ refresh: !this.state.refresh }); // render and refresh the button
}
DisableButton(flag) {
if (flag && this.status !== STATUS.DISABLED) {
this.AnimationName = 'DimIn';
this.status = STATUS.DISABLED;
} else if (!flag && this.status === STATUS.DISABLED) {
this.AnimationName = 'DimOut';
this.status = STATUS.NORMAL;
} else {
return;
}
this.setState({ refresh: !this.state.refresh }); // render and refresh the button
}
playTTS() {
if (this.props.ttsEnable) {
console.log('drop down button play TTS');
this.TTSnode.playTTS();
}
}
SetFocus(flag) {
if (this.status === STATUS.DISABLED) {
console.log('Button disabled!');
return;
}
if (flag && this.status === STATUS.NORMAL) {
this.AnimationName = `FocusIn${this.props.refname}`;
this.status = STATUS.FOCUSED;
this.playTTS();
} else if (!flag && this.status === STATUS.FOCUSED) {
this.AnimationName = `FocusOut${this.props.refname}`;
this.status = STATUS.NORMAL;
} else {
return;
}
this.setState({ refresh: !this.state.refresh }); // render and refresh the button
}
animationDone(e) {
e.preventDefault();
const target = e.target;
if (target === e.currentTarget) {
if (e.type === 'animationend') {
if (e.animationName === `SelectedIn${this.props.refname}`) {
if (this.props.selectCB && typeof this.props.selectCB === 'function') {
this.props.selectCB();
}
} else if (e.animationName === `SelectedOut${this.props.refname}`) {
if (this.props.unselectCB && typeof this.props.unselectCB === 'function') {
this.props.unselectCB();
}
}
}
}
}
removeStyleFromHeader() {
if (this.style) {
document.getElementsByTagName('head')[0].removeChild(this.style);
this.style = null;
}
}
render() {
const { buttonType, ttsEnable, ttsText } = this.props;
let contentComponent = null;
if (buttonType === DROPDOWN_TYPE.ICON) {
contentComponent = this.getIcon();
} else if (buttonType === DROPDOWN_TYPE.TEXT) {
contentComponent = this.getTextAndBar();
}
const arrowComponent = this.getArrow();
let TTSComponent = null;
if (ttsEnable) {
TTSComponent = <TTS ref={(TTSnode) => { this.TTSnode = TTSnode; }} ttsEnable={ttsEnable} ttsText={ttsText} />;
}
const ButtonStyle = this.getButtonStyle();
const dimAnimation = this.getDimStyle();
return (<div ref={(motionContent) => { this.motionContent = motionContent; }} style={ButtonStyle}>
<div style={dimAnimation}>
{contentComponent}
{arrowComponent}
</div>
{TTSComponent}
</div>
);
}
}
DropdownButton.defaultProps = {
layout: {
l: 0,
t: 0,
w: 414,
h: 84,
},
highContrast: false,
enlarge: false,
ttsEnable: false,
ttsText: '',
selectCB: null,
unselectCB: null,
buttonScale: 1.08,
refname: '',
text: '',
textFont: MAIN_TEXT_FONT,
textSize: TextItemFamily.TITLE2_TEXT_SIZE,
iconOSD: {
l: 14,
t: 23,
w: 38,
h: 38,
url_n: '',
url_f: '',
},
};
DropdownButton.propTypes = {
buttonType: PropTypes.oneOf(['icon', 'text']).isRequired, // The type of button, "ICON" means only icon, "TEXT means only text
arrowType: PropTypes.oneOf(['up', 'down']).isRequired,
layout: PropTypes.shape({
l: PropTypes.number,
t: PropTypes.number,
w: PropTypes.number,
h: PropTypes.number,
}),
selectCB: PropTypes.func,
unselectCB: PropTypes.func,
highContrast: PropTypes.bool,
enlarge: PropTypes.bool,
ttsEnable: PropTypes.bool,
ttsText: PropTypes.string,
buttonScale: PropTypes.number,
refname: PropTypes.string,
text: PropTypes.string, // message of button show
textFont: PropTypes.string,
textSize: PropTypes.number,
iconOSD: PropTypes.shape({
l: PropTypes.number,
t: PropTypes.number,
w: PropTypes.number,
h: PropTypes.number,
url_n: PropTypes.string,
url_f: PropTypes.string,
}),
};