UNPKG

principles-ui-components

Version:

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

379 lines (330 loc) 13.4 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { is, Map, fromJS } from 'immutable'; import { MAIN_TEXT_FONT, TextItemFamily, AnimationEffect, KEY } from './common/CommonDefine'; import './css/Button.css'; import ScrollText from './common/ScrollText'; import Image from './common/Image'; import TTS from './common/TTS'; // Define Button Type export const BUTTON_TYPE = { ICON: 'icon', TEXT: 'text', }; const STATUS = { NORMAL: 0, FOCUSED: 1, 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', }; const TEXT_LEFT_BORDER = 21; export default class BasicButton 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 = `@keyframes FocusIn${refname} { 0% {transform: scale(1.00) translateZ(100px);} 100% {transform: scale(${buttonScale}) translateZ(100px);} } @keyframes Selected1${refname} { 0% {transform: scale(${buttonScale}) translateZ(100px); opacity: 1.0;} 40% {transform: scale(1.00) translateZ(100px); opacity: 0.8;} 100% {transform: scale(${buttonScale}) translateZ(100px); opacity: 1.0;} } @keyframes Selected2${refname} { 0% {transform: scale(${buttonScale}) translateZ(100px); opacity: 1.0;} 40% {transform: scale(1.00) translateZ(100px); opacity: 0.8;} 100% {transform: scale(${buttonScale}) translateZ(100px); opacity: 1.0;} } @keyframes FocusOut${refname} { 0% {transform: scale(${buttonScale}) translateZ(100px);} 100% {transform: scale(1.00) 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 = `@keyframes FocusIn${nextProps.refname} { 0% {transform: scale(1.00) translateZ(100px);} 100% {transform: scale(${nextProps.buttonScale}) translateZ(10px);} } @keyframes Selected1${nextProps.refname} { 0% {transform: scale(${nextProps.buttonScale}) translateZ(100px); opacity: 1.0;} 40% {transform: scale(1.00) translateZ(100px); opacity: 0.8;} 100% {transform: scale(${nextProps.buttonScale}) translateZ(100px); opacity: 1.0;} } @keyframes Selected2${nextProps.refname} { 0% {transform: scale(${nextProps.buttonScale}) translateZ(100px); opacity: 1.0;} 40% {transform: scale(1.00) translateZ(100px); opacity: 0.8;} 100% {transform: scale(${nextProps.buttonScale}) translateZ(100px); opacity: 1.0;} } @keyframes FocusOut${nextProps.refname} { 0% {transform: scale(${nextProps.buttonScale}) translateZ(100px);} 100% {transform: scale(1.00) 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)'; //white border if (this.status === STATUS.FOCUSED) { ButtonStyle.borderImage = highContrast ? BtnResource.HCFOCUS : BtnResource.FOCUS; } 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 === `Selected1${refname}` || this.AnimationName === `Selected2${refname}`) { ButtonStyle.animationDuration = '0.417s'; ButtonStyle.animationTimingFunction = AnimationEffect.Basic; } 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} />); } getText() { const { layout, text, enlarge, textFont, textSize } = this.props; return ( <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> ); } 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('basic 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 } EnterButton() { if (this.status === STATUS.FOCUSED) { this.AnimationName = `Selected${this.selectIdx}${this.props.refname}`; 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 === `Selected1${this.props.refname}` || e.animationName === `Selected2${this.props.refname}`) { if (this.props.clickCB && typeof this.props.clickCB === 'function') { this.props.clickCB(); } if (this.selectIdx === 1) { this.selectIdx = 2; } else { this.selectIdx = 1; } } } } } 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 === BUTTON_TYPE.ICON) { contentComponent = this.getIcon(); } else if (buttonType === BUTTON_TYPE.TEXT) { contentComponent = this.getText(); } 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} </div> {TTSComponent} </div> ); } } BasicButton.defaultProps = { layout: { l: 0, t: 0, w: 414, h: 84, }, highContrast: false, enlarge: false, ttsEnable: false, ttsText: '', clickCB: null, buttonScale: 1.08, refname: '', text: '', textFont: MAIN_TEXT_FONT, textSize: TextItemFamily.TITLE2_TEXT_SIZE, iconOSD: { l: 22, t: 23, w: 38, h: 38, url_n: '', url_f: '', }, }; BasicButton.propTypes = { buttonType: PropTypes.oneOf(['icon', 'text']).isRequired, // The type of button, "ICON_BUTTON" means only icon, "TEXT_BUTTON" means only text layout: PropTypes.shape({ l: PropTypes.number, t: PropTypes.number, w: PropTypes.number, h: PropTypes.number, }), highContrast: PropTypes.bool, enlarge: PropTypes.bool, ttsEnable: PropTypes.bool, ttsText: PropTypes.string, clickCB: PropTypes.func, // button call back func 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, }), };