UNPKG

principles-ui-components

Version:

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

525 lines (442 loc) 18 kB
/** * @author Ma Cong(cong001.ma@samsung.com) * @fileoverview This module manages Button item. * @date 2017/08/07 (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 { is, Map, fromJS } from 'immutable'; import { MAIN_TEXT_FONT, TextItemFamily, AnimationEffect, KEY, TTSTYPE } from './common/CommonDefine'; import './css/Button.css'; import CommonAPI from './common/CommonAPI'; import PressFeedback from './common/PressFeedback'; import ScrollText from './common/ScrollText'; import Image from './common/Image'; import TTS from './common/TTS'; // Define Button Type export const BUTTON_TYPE = { ICON_BUTTON: 'icon', TEXT_BUTTON: 'text', DROP_DOWN_TEXT_BUTTON: 'dropdown_text', }; const btnResource = { normal: 'url(./images/btn/c_basic_button_white_bg_normal_9patch.png) 5 4 fill stretch', highContrast: 'url(./images/btn/c_basic_button_highcontrast_bg_normal_9patch.png) 6 5 fill stretch', focus: 'url(./images/btn/r_highlight_bg_focus_9patch.png) 20 14 fill stretch', select: 'url(./images/btn/c_basic_button_white_bg_select_9patch.png) 5 4 fill stretch', }; export default class Button extends Component { constructor(props) { super(props); this.state = { hasFocus: false, }; this.animationflag = false; this.animationDone = this.animationDone.bind(this); this.mainTextFont = MAIN_TEXT_FONT; this.mainTextSize = TextItemFamily.TITLE2_TEXT_SIZE; this.bodyGap = TextItemFamily.BODY_TEXT_GAP; } componentDidMount() { console.log('Button componentDidMount'); const { ttsEnable } = this.props; if (ttsEnable) { this.playTTS(); } } componentWillReceiveProps(nextProps) { } /* shouldComponentUpdate(nextProps, nextState) { // return true -->render() return !(is(fromJS(nextProps), fromJS(this.props))) || !(is(fromJS(nextState), fromJS(this.state))); }*/ componentDidUpdate() { console.log('Button componentDidUpdate'); const { ttsEnable } = this.props; if (ttsEnable) { this.playTTS(); } } componentWillUnmount() { } getElementPos(elem) { console.log('Button.getElementPos IN'); const { buttonW } = this.props; let x = 0; let y = 0; let mElem = elem; while (mElem != null) { x += mElem.offsetLeft; y += mElem.offsetTop; mElem = mElem.offsetParent; } x += buttonW / 2; console.log(`getElementPos x:${x}`); console.log(`getElementPos y:${y}`); return { x, y }; } getButtonStyle() { const { focus, buttonScale, buttonType } = this.props; const ButtonStyle = {}; if ((focus === true || this.state.hasFocus === true) && buttonScale !== 1) { ButtonStyle.animationName = 'doScale'; ButtonStyle.animationDuration = '1.1s'; ButtonStyle.transform = `scale(${buttonScale},${buttonScale}) translateZ(100px)`; ButtonStyle.willChange = 'transform'; ButtonStyle.animationTimingFunction = AnimationEffect.Elastic; } else if ((focus === false || this.state.hasFocus === false) && buttonScale !== 1) { ButtonStyle.animationName = 'doScaleBack'; if (buttonType !== BUTTON_TYPE.ICON_BUTTON) { ButtonStyle.animationDuration = '0.85s'; ButtonStyle.animationTimingFunction = AnimationEffect.out; } } return ButtonStyle; } getEnlargeButtonStyle() { const { highContrast, enlarge, focus } = this.props; const ButtonStyle = {}; if (focus === true || this.state.hasFocus === true) { ButtonStyle.animationName = 'doScaleEnlargeFocus'; ButtonStyle.animationDuration = '0.85s'; ButtonStyle.transform = 'scale(1.296,1.296) translateZ(100px)'; ButtonStyle.willChange = 'transform'; ButtonStyle.animationTimingFunction = AnimationEffect.Elastic; } else if (focus === false || this.state.hasFocus === false) { ButtonStyle.animationName = 'doScaleEnlarge'; ButtonStyle.animationDuration = '0.85s'; ButtonStyle.transform = 'scale(1.2,1.2) translateZ(100px)'; ButtonStyle.willChange = 'transform'; ButtonStyle.animationTimingFunction = AnimationEffect.Elastic; } return ButtonStyle; } animationDone(e) { this.animationflag = true; e.preventDefault(); const target = e.target; if (target === e.currentTarget) { if (e.type === 'animationend') { if (this.props.clickCB && typeof this.props.clickCB === 'function') { this.props.clickCB(); } } } } calculateTextW() { const { message } = this.props; let textWidth = 0; if (message) { textWidth = CommonAPI.getTextWidth(message, this.mainTextSize, this.mainTextFont); } console.log(`textWidth=${textWidth}`); return textWidth; } keyFocusOut() { console.log('[Button.js::keyFocusOut()]'); this.setState({ hasFocus: false }); return this.getElementPos(this.motionContent); } keyFocusIn(position) { console.log('[Button.js::keyFocusIn()] '); this.setState({ hasFocus: true }); } /** * voice guide play * @name playTTS * @method * @param * @memberof */ playTTS() { console.log('[Button.js::playTTS()] '); if (this.props.focus || this.state.hasFocus) { this.TTSnode.playTTS(); } } procIconBtn() { const { iconOSD, clickCB, highContrast, enlarge, disabled, focus, selected, bgcolor, bgimg, ttsEnable, ttsText } = this.props; const { buttonW, buttonH } = this.props; const ImgStyle = {}; const ButtonStyle = enlarge ? this.getEnlargeButtonStyle() : this.getButtonStyle(); let imageOSD = { l: iconOSD.l, t: iconOSD.t, w: iconOSD.w, h: iconOSD.h, url: iconOSD.url_n, }; console.log(`selected:${selected}`); if (buttonW && buttonH) { ButtonStyle.width = buttonW; ButtonStyle.height = buttonH; } ButtonStyle.backgroundColor = bgcolor; // for vodpage set icon bg ButtonStyle.borderImage = highContrast ? btnResource.highContrast : bgimg; // disabled if (disabled) { ImgStyle.opacity = '0.2'; } // focus if ((focus === true || this.state.hasFocus === true) && iconOSD.url_f) { imageOSD = { l: iconOSD.l, t: iconOSD.t, w: iconOSD.w, h: iconOSD.h, url: iconOSD.url_f, }; } // TTS let TTSComponent = null; if (ttsEnable) { TTSComponent = <TTS ref={(TTSnode) => { this.TTSnode = TTSnode; }} ttsEnable={ttsEnable} ttsText={ttsText} />; } return ( <div className='iconArea'> <PressFeedback start={selected} animationDoneCB={this.props.clickCB}> <div ref={(motionContent) => { this.motionContent = motionContent; }} className='image-bg' style={ButtonStyle}> <div style={ImgStyle}> <Image OSD={imageOSD} opacity={0} /> </div> </div> </PressFeedback> {TTSComponent} </div> ); } procTextBtn() { const { message, highContrast, enlarge, clickCB, disabled, focus, selected, ttsEnable, ttsText } = this.props; const { buttonW, buttonH } = this.props; let textStyle = {}; let marqueeFlag = false; this.textWidth = this.calculateTextW(); // highContrast & enlarge const ButtonStyle = enlarge ? this.getEnlargeButtonStyle() : this.getButtonStyle(); const BtnText = highContrast ? 'btn-text-highContrast' : 'btn-text'; ButtonStyle.border = '2px solid rgb(255,255,255)'; ButtonStyle.borderImage = highContrast ? btnResource.highContrast : btnResource.normal; if (focus === true || this.state.hasFocus === true) { textStyle.color = '#000000'; ButtonStyle.borderImage = btnResource.focus; } if (buttonW && buttonH) { ButtonStyle.width = buttonW; ButtonStyle.height = buttonH; } console.log(`ButtonStyle.width=${ButtonStyle.width}`); textStyle = { left: 21, width: ButtonStyle.width - 42, height: buttonH, lineHeight: buttonH, }; // disabled if (disabled) { textStyle.opacity = '0.2'; } if (this.textWidth > textStyle.width && (focus || this.state.hasFocus)) { marqueeFlag = true; } const aligntext = marqueeFlag ? 'left' : 'center'; // TTS let TTSComponent = null; if (ttsEnable) { TTSComponent = <TTS ref={(TTSnode) => { this.TTSnode = TTSnode; }} ttsEnable={ttsEnable} ttsText={ttsText} />; } return ( <div style={{ position: 'relative' }}> <PressFeedback start={selected} animationDoneCB={this.props.clickCB}> <div ref={(motionContent) => { this.motionContent = motionContent; }} className='button-ani' style={ButtonStyle}> <div className={BtnText} style={textStyle}> <ScrollText scroll={marqueeFlag} fontSize={this.mainTextSize} fontFamily={this.mainTextFont} textGap={this.bodyGap} width={textStyle.width} height={textStyle.height} lineHeight={`${textStyle.height}px`} textAlign={aligntext} > {message} </ScrollText> </div> </div> </PressFeedback> {TTSComponent} </div> ); } procDropdowntextBtn() { const { message, highContrast, enlarge, clickCB, disabled, focus, selected, ttsEnable, ttsText } = this.props; const { buttonW, buttonH } = this.props; const textStyle = {}; const iconStyle = {}; let marqueeFlag = false; this.textWidth = this.calculateTextW(); // highContrast & enlarge const ButtonStyle = enlarge ? this.getEnlargeButtonStyle() : this.getButtonStyle(); const BtnText = highContrast ? 'btn-text-highContrast' : 'text-icon-title'; if (buttonW && buttonH) { ButtonStyle.width = buttonW; ButtonStyle.height = buttonH; } ButtonStyle.border = '2px solid rgb(255,255,255)'; ButtonStyle.borderImage = highContrast ? btnResource.highContrast : btnResource.normal; if ((focus || this.state.hasFocus)) { iconStyle.backgroundImage = 'url(./images/btn/c_buttondropdown_arrow_down_focus.png)'; } else { iconStyle.backgroundImage = 'url(./images/btn/c_buttondropdown_arrow_down.png)'; } if (focus === true || this.state.hasFocus === true) { textStyle.color = '#000000'; ButtonStyle.borderImage = btnResource.focus; } const elem = this.motionContent; if (this.animationflag) { this.animationflag = false; elem.removeEventListener('animationend', this.animationDone, false); } if (selected && !enlarge) { ButtonStyle.animationName = 'doScaleBack'; ButtonStyle.animationDuration = '0.85s'; ButtonStyle.transform = 'scale(1,1) translateZ(100px)'; ButtonStyle.animationTimingFunction = AnimationEffect.Out; ButtonStyle.borderImage = btnResource.select; elem.addEventListener('animationend', this.animationDone, false); iconStyle.backgroundImage = 'url(./images/btn/c_buttondropdown_arrow_up_focus.png)'; } else if (selected && enlarge) { ButtonStyle.animationName = 'doScaleEnlarge'; ButtonStyle.animationDuration = '0.85s'; ButtonStyle.transform = 'scale(1.2,1.2) translateZ(100px)'; ButtonStyle.animationTimingFunction = AnimationEffect.Out; ButtonStyle.borderImage = btnResource.select; elem.addEventListener('animationend', this.animationDone, false); iconStyle.backgroundImage = 'url(./images/btn/c_buttondropdown_arrow_up_focus.png)'; } else if (!selected && (focus || this.state.hasFocus) && !enlarge) { ButtonStyle.animationName = 'doScale'; ButtonStyle.animationDuration = '0.85s'; ButtonStyle.transform = 'scale(1.08,1.08) translateZ(100px)'; ButtonStyle.willChange = 'transform'; ButtonStyle.animationTimingFunction = AnimationEffect.Out; } textStyle.width = ButtonStyle.width - 126; textStyle.height = buttonH; iconStyle.left = textStyle.width + 82; const barStyle = { position: 'absolute', zIndex: 1000, left: textStyle.width + 50, top: (ButtonStyle.height - 64) / 2, width: '2px', height: '64px', backgroundImage: 'url(images/btn/c_buttondropdown_bar.png)', }; // for scrollText if (this.textWidth > textStyle.width && (focus || this.state.hasFocus) && !selected) { marqueeFlag = true; } // disabled if (disabled) { textStyle.opacity = '0.4'; } const aligntext = marqueeFlag ? 'left' : 'center'; // TTS let TTSComponent = null; if (ttsEnable) { TTSComponent = <TTS ref={(TTSnode) => { this.TTSnode = TTSnode; }} ttsEnable={ttsEnable} ttsText={ttsText} />; } return ( <div> <div ref={(motionContent) => { this.motionContent = motionContent; }} className='text-image-bg' style={ButtonStyle} > <div className={BtnText} style={textStyle} > <ScrollText scroll={marqueeFlag} fontSize={this.mainTextSize} fontFamily={this.mainTextFont} textGap={this.bodyGap} width={textStyle.width} height={textStyle.height} lineHeight={`${textStyle.height}px`} textAlign={aligntext} > {message} </ScrollText> </div> <div style={barStyle} /> <div className='text-icon-img' style={iconStyle} /> </div> {TTSComponent} </div> ); } render() { console.log('[Button.js::render()]'); const { buttonType } = this.props; switch (buttonType) { case BUTTON_TYPE.ICON_BUTTON: return this.procIconBtn(); case BUTTON_TYPE.TEXT_BUTTON: return this.procTextBtn(); case BUTTON_TYPE.DROP_DOWN_TEXT_BUTTON: return this.procDropdowntextBtn(); default: return (<div />); } } } Button.defaultProps = { focus: false, buttonW: 414, buttonH: 84, highContrast: false, enlarge: false, disabled: false, message: '', selected: false, clickCB: null, buttonScale: 1.08, bgcolor: null, bgimg: null, ttsEnable: false, ttsText: '', iconOSD: { l: 22, t: 23, w: 38, h: 38, url_n: '', url_f: '', }, }; Button.propTypes = { focus: PropTypes.bool, buttonType: PropTypes.string.isRequired, // The type of button, "ICON_BUTTON" means only icon, "TEXT_BUTTON" means only text, "DROP_DOWN_BUTTON" means dropdown button(for text) clickCB: PropTypes.func, // button call back func message: PropTypes.string.isRequired, // message of button show highContrast: PropTypes.bool, enlarge: PropTypes.bool, disabled: PropTypes.bool, selected: PropTypes.bool, buttonW: PropTypes.number, // button Width, if null, will calculate width according to rule buttonH: PropTypes.number, // button Height bgcolor: PropTypes.string, bgimg: PropTypes.string, ttsEnable: PropTypes.bool, ttsText: PropTypes.string, iconOSD: PropTypes.shape({ l: PropTypes.number, t: PropTypes.number, w: PropTypes.number, h: PropTypes.number, url_n: PropTypes.string.isRequired, url_f: PropTypes.string, }), };