UNPKG

principles-ui-components

Version:

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

376 lines (341 loc) 13.1 kB
/** * @author Haipeng Zhang(hp.zhang@samsung.com) * @fileoverview This module manages text item. * @date 2017/06/09 (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 Immutable from 'immutable'; import './css/TextItem.css'; import CommonAPI from './common/CommonAPI'; import ScrollText from './common/ScrollText'; import { MAIN_TEXT_FONT, SUB_TEXT_FONT, TextItemFamily, AnimationEffect } from './common/CommonDefine'; export default class TextItem extends Component { constructor(props) { super(props); // title & body & caption this.mainTextFont = MAIN_TEXT_FONT; this.subTextFont = SUB_TEXT_FONT; this.titleGap = TextItemFamily.TITLE_TEXT_GAP; this.bodyGap = TextItemFamily.BODY_TEXT_GAP; this.captionGap = TextItemFamily.CAPTION_TEXT_GAP; this.titleLength = 0; this.bodyLength = 0; this.captionLength = 0; // bind function this.setTextGapProps = this.setTextGapProps.bind(this); this.getShadowComponent = this.getShadowComponent.bind(this); this.getContentLabelComponent = this.getContentLabelComponent.bind(this); this.setTextGapProps(props); } componentDidMount() { console.log('TextItem componentDidMount'); } componentWillReceiveProps(nextProps) { if (this.props.enlarge !== nextProps.enlarge || this.props.data.title !== nextProps.data.title || this.props.data.body !== nextProps.data.body || this.props.data.caption !== nextProps.data.caption) { this.setTextGapProps(nextProps); } } shouldComponentUpdate(nextProps, nextState) { const needUpdateProps = !Immutable.is(nextProps, this.props); const needUpdateState = !Immutable.is(nextState, this.state); const needUpdate = needUpdateProps || needUpdateState; console.log(`TextItem shouldComponentUpdate ${needUpdate}`); return needUpdate; } componentDidUpdate(prevProps, prevState) { } componentWillUnmount() { } /** * set text length and gap of item * @name setTextGapProps * @method * @memberof item * @param {props} react props */ setTextGapProps(props) { // Set Size this.titleTextSize = TextItemFamily.TITLE2_TEXT_SIZE; this.bodyTextSize = TextItemFamily.BODY2_TEXT_SIZE; this.captionTextSize = TextItemFamily.CAPTION_TEXT_SIZE; if (props.enlarge) { this.titleTextSize = Math.ceil(this.titleTextSize * 1.2); this.bodyTextSize = Math.ceil(this.bodyTextSize * 1.2); this.captionTextSize = Math.ceil(this.captionTextSize * 1.2); } // Set Length this.titleLength = CommonAPI.getTextWidth(props.data.title, this.titleTextSize, this.mainTextFont); let maxLength = this.titleLength; if (props.data.body) { if (props.data.caption) { this.bodyLength = CommonAPI.getTextWidth(props.data.body, this.bodyTextSize, this.mainTextFont); if (maxLength < this.bodyLength) { maxLength = this.bodyLength; } this.captionLength = CommonAPI.getTextWidth(props.data.caption, this.captionTextSize, this.subTextFont); if (maxLength < this.captionLength) { maxLength = this.captionLength; } } else { this.bodyLength = CommonAPI.getTextWidth(props.data.body, this.bodyTextSize, this.subTextFont); if (maxLength < this.bodyLength) { maxLength = this.bodyLength; } } } // Set Gap this.titleGap = (maxLength + this.titleGap) - this.titleLength; if (props.data.body) { if (props.data.caption) { this.bodyGap = (maxLength + this.bodyGap) - this.bodyLength; this.captionGap = (maxLength + this.captionGap) - this.captionLength; } else { this.bodyGap = (maxLength + this.bodyGap) - this.bodyLength; } } } /** * get Shadow Component * @name getShadowComponent * @method * @memberof */ getShadowComponent() { const shadowClass = this.props.highContrast ? 'focus-shadow-hc' : 'focus-shadow'; return (<div className={shadowClass} />); } /** * get Content Label Component * @name getContentLabelComponent * @method * @memberof */ getContentLabelComponent() { const { data, OSD, focus, highContrast, enlarge } = this.props; // 1. contentLabelStyle const contentLabelStyle = { position: 'absolute', top: 0, left: 0, height: this.props.OSD.textSize.h, width: this.props.OSD.textSize.w, backgroundColor: focus ? 'hsla(0,0%,100%,1)' : 'hsla(0,0%,100%,0.5)', }; // 2. icon let iconComponent = null; const iconBGStyle = { marginTop: (OSD.textSize.h - OSD.iconSize.h - 2) / 2, // BG image pos +2 width: (OSD.iconSize.w + 2), height: (OSD.iconSize.h + 2), }; const iconStyle = { width: OSD.iconSize.w, height: OSD.iconSize.h, }; if (data.iconUrl) { iconComponent = (<div className='icon-bg' style={iconBGStyle}> <img className='icon-image' style={iconStyle} src={data.iconUrl} /> </div>); } // 3. title & sub title // In a High Contrast || enlarge mode, do not support sliding text feature. Even if the item is focused, keep the ellipsis() mark continuously. const marqueeFlag = focus && !highContrast && !enlarge; let left = 20 + iconBGStyle.width + 30; let titleStyle = {}; let marqueeW = OSD.textSize.w - left - 20; let top = (OSD.textSize.h - 48 - 1 - 40) / 2; if (!iconComponent) { left = 20; marqueeW = OSD.textSize.w - 40; } if (!data.body) { top = (OSD.textSize.h - 48) / 2; } if (data.caption) { top = (OSD.textSize.h - 48 - 1 - 34 - 28) / 2; } titleStyle = { left, width: marqueeW, top, }; // 3.1 title let titleComponent = null; if (data.title) { let titleClassName = 'title2'; if (focus) { titleClassName = 'title2 focus-title2'; if (enlarge) { titleClassName = 'title2 focus-title2-enlarge'; } } titleComponent = (<div className={titleClassName} style={titleStyle}> <ScrollText scroll={marqueeFlag} fontSize={this.titleTextSize} fontFamily={this.mainTextFont} textGap={this.titleGap} width={marqueeW} height={TextItemFamily.TITLE_TEXT_HEIGHT} > {data.title} </ScrollText> </div>); } // 3.2 body let bodyComponent = null; if (data.body) { let bodyStyle = {}; top = top + 48 + 1; if (data.caption) { top -= 1; } bodyStyle = { left, top, width: marqueeW, }; let bodyClassName = data.caption ? 'body3' : 'body2'; if (focus) { bodyClassName = data.caption ? 'body3 focus-body3' : 'body2 focus-body2'; if (enlarge) { bodyClassName = data.caption ? 'body3 focus-body3-enlarge' : 'body2 focus-body2-enlarge'; } } const textFont = data.caption ? this.mainTextFont : this.subTextFont; const textHeight = data.caption ? TextItemFamily.BODY3_TEXT_HEIGHT : TextItemFamily.BODY2_TEXT_HEIGHT; const textLineHeight = data.caption ? TextItemFamily.BODY3_TEXT_LINEHEIGHT : TextItemFamily.BODY2_TEXT_LINEHEIGHT; bodyComponent = (<div className={bodyClassName} style={bodyStyle}> <ScrollText scroll={marqueeFlag} fontSize={this.bodyTextSize} fontFamily={textFont} textGap={this.bodyGap} width={marqueeW} height={textHeight} lineHeight={textLineHeight} > {data.body} </ScrollText> </div>); } // 3.3 caption let captionComponent = null; if (data.caption) { let captionStyle = {}; captionStyle = { left, width: marqueeW, top: top + 34 + 1, }; let captionClassName = 'caption'; if (focus) { captionClassName = 'caption focus-caption'; if (enlarge) { captionClassName = 'caption focus-caption-enlarge'; } } captionComponent = (<div className={captionClassName} style={captionStyle}> <ScrollText scroll={marqueeFlag} fontSize={this.captionTextSize} fontFamily={this.subTextFont} textGap={this.captionGap} width={marqueeW} height={TextItemFamily.CAPTION_TEXT_HEIGHT} lineHeight={TextItemFamily.CAPTION_TEXT_LINEHEIGHT} > {data.caption} </ScrollText> </div>); } return ( <div style={contentLabelStyle}> {iconComponent} {titleComponent} {bodyComponent} {captionComponent} </div> ); } render() { const { enter, focus, OSD } = this.props; let shadowComponent = null; if (focus) { shadowComponent = this.getShadowComponent(); } const contentLabelComponent = this.getContentLabelComponent(); const tileContainerStyle = { zIndex: focus ? 5 : 1, transform: `scale(${focus ? (1.08) : 1.0}) translateZ(10px)`, transition: focus ? `all 0.6s ${AnimationEffect.Elastic}` : `all 0.85s ${AnimationEffect.Out}`, width: OSD.textSize.w, height: OSD.textSize.h, }; const borderStyle = { width: OSD.textSize.w, height: OSD.textSize.h, borderRadius: '3px', overflow: 'hidden', }; const domRef = (node) => { this.node = node; }; return ( <div ref={domRef} className='tileContainer' style={tileContainerStyle} tabIndex='-1' > <div style={borderStyle}> {shadowComponent} {contentLabelComponent} </div> </div> ); } } TextItem.defaultProps = { focus: false, enlarge: false, highContrast: false, data: null, OSD: { textSize: { w: 285, h: 126, }, iconSize: { w: 66, h: 66, }, }, }; TextItem.propTypes = { focus: PropTypes.bool.isRequired, // The item is focus or not enlarge: PropTypes.bool, // enlarge title and SubTitle font size flag highContrast: PropTypes.bool, // Background image, false use r_highlight_bg_focus_9patch.png ,true use c_imageitem_highlight_bg_focus_9patch.png data: PropTypes.shape({ // UI content iconUrl: PropTypes.string, // Text icon title: PropTypes.string, // Text title body: PropTypes.string, // Text body caption: PropTypes.string, // Text caption }), OSD: PropTypes.shape({ // UI content textSize: PropTypes.shape({ w: PropTypes.number.isRequired, h: PropTypes.number.isRequired, }), iconSize: PropTypes.shape({ w: PropTypes.number.isRequired, h: PropTypes.number.isRequired, }), }), };