From 239515a1792cff18002ab29b5adae3ff4056f1ef Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Tue, 20 Nov 2018 19:00:08 +0100 Subject: [PATCH 1/3] =?UTF-8?q?Affiche=20la=20diff=C3=A9rence=20en=20euro?= =?UTF-8?q?=20lors=20du=20choix=20des=20options=20dans=20le=20simulateur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/components/AnimatedTargetValue.css | 29 +++++-- source/components/AnimatedTargetValue.js | 95 ++++++++++++++++++----- 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/source/components/AnimatedTargetValue.css b/source/components/AnimatedTargetValue.css index 407ebfc7e..e83019313 100644 --- a/source/components/AnimatedTargetValue.css +++ b/source/components/AnimatedTargetValue.css @@ -1,14 +1,27 @@ .Rule-value { - transition: background 0.8s; font-size: 105%; + position: relative; } -/* Animation of summary figures changes : flash ! */ -.flash-enter { - background: rgba(255, 255, 255, 1); -} - -.flash-leave { - /* Completely hide the button while it's being animated and before it's removed from the DOM. */ +.evaporate { display: none; + font-size: 80%; + /* text-shadow: 0 0 2px var(--colour); */ +} +.evaporate-enter { + display: block; + position: absolute; + right: 0; + top: -20px; + opacity: 1; + animation: evaporate 1.3s ease-out; +} +@keyframes evaporate { + 50% { + opacity: 1; + } + to { + transform: translateY(-15px); + opacity: 0; + } } diff --git a/source/components/AnimatedTargetValue.js b/source/components/AnimatedTargetValue.js index 3bc17e04d..aca14226a 100644 --- a/source/components/AnimatedTargetValue.js +++ b/source/components/AnimatedTargetValue.js @@ -1,32 +1,87 @@ +/* @flow */ import withLanguage from 'Components/utils/withLanguage' -import React, { Component } from 'react' +import React, { Component, PureComponent } from 'react' import ReactCSSTransitionGroup from 'react-addons-css-transition-group' import './AnimatedTargetValue.css' +type Props = { + value: ?number, + language: string +} +type State = { + difference: number +} export default withLanguage( - class AnimatedTargetValue extends Component { + class AnimatedTargetValue extends Component { + previousValue: ?number = null + timeoutId: ?TimeoutID = null + state = { difference: 0 } + + componentDidUpdate(prevProps) { + if (prevProps.value === this.props.value) { + return + } + if (this.timeoutId) { + clearTimeout(this.timeoutId) + } + this.previousValue = + this.previousValue === null ? prevProps.value : this.previousValue + + this.timeoutId = setTimeout(() => { + this.setState({ + difference: (this.props.value || 0) - (this.previousValue || 0) + }) + this.previousValue = null + this.timeoutId = null + }, 200) + } + format = value => { + return value == null + ? '' + : Intl.NumberFormat(this.props.language, { + style: 'currency', + currency: 'EUR', + maximumFractionDigits: 0, + minimumFractionDigits: 0 + }).format(value) + } render() { - let { value, language } = this.props - let formattedValue = - value == null - ? '' - : Intl.NumberFormat(language, { - style: 'currency', - currency: 'EUR', - maximumFractionDigits: 0, - minimumFractionDigits: 0 - }).format(value) + const formattedValue = this.format(this.props.value) + const formattedDifference = this.format(this.state.difference) return ( - - - {' '} - {formattedValue} + <> + + {Math.abs(this.state.difference) > 1 && + formattedDifference !== formattedValue && ( + 0 ? 'chartreuse' : 'red' + }}> + {(this.state.difference > 0 ? '+' : '') + formattedDifference} + + )}{' '} + {this.format(this.props.value)} - + ) } } ) + +class Evaporate extends PureComponent<{ children: string, style: Object }> { + render() { + return ( + + + {this.props.children} + + + ) + } +} From c8ccac62b1536ce1f956b7b3a28866a797f81e12 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Tue, 20 Nov 2018 19:34:45 +0100 Subject: [PATCH 2/3] Ajoute un debounce sur les champs de valeur dans les questions --- source/components/AnimatedTargetValue.js | 4 ++-- source/components/conversation/FormDecorator.js | 4 +--- source/components/conversation/Input.js | 14 +++++++++----- source/utils.js | 10 +++++----- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/source/components/AnimatedTargetValue.js b/source/components/AnimatedTargetValue.js index aca14226a..b1bda7080 100644 --- a/source/components/AnimatedTargetValue.js +++ b/source/components/AnimatedTargetValue.js @@ -33,7 +33,7 @@ export default withLanguage( }) this.previousValue = null this.timeoutId = null - }, 200) + }, 250) } format = value => { return value == null @@ -73,7 +73,7 @@ class Evaporate extends PureComponent<{ children: string, style: Object }> { return ( RenderField => } render() { let { - setFormValue, stepAction, subquestion, valueType, defaultValue, fieldName, inversion, + setFormValue, themeColours } = this.props - let validate = buildValidationFunction(valueType) - let submit = cause => stepAction('fold', fieldName, cause), stepProps = { ...this.props, diff --git a/source/components/conversation/Input.js b/source/components/conversation/Input.js index 7eb714355..07a3220f0 100644 --- a/source/components/conversation/Input.js +++ b/source/components/conversation/Input.js @@ -3,6 +3,7 @@ import withColours from 'Components/utils/withColours' import { compose } from 'ramda' import React, { Component } from 'react' import { withI18n } from 'react-i18next' +import { debounce } from '../../utils' import { FormDecorator } from './FormDecorator' import InputSuggestions from './InputSuggestions' import SendButton from './SendButton' @@ -13,6 +14,7 @@ export default compose( withColours )( class Input extends Component { + debouncedOnChange = debounce(750, this.props.input.onChange) render() { let { input, @@ -27,16 +29,18 @@ export default compose( suffixed = answerSuffix != null, inputError = dirty && error, submitDisabled = !dirty || inputError - return (
{ - this.inputElement = el - }} type="text" - {...input} + key={input.value} + autoFocus + defaultValue={input.value} + onChange={e => { + e.persist() + this.debouncedOnChange(e) + }} className={classnames({ suffixed })} id={'step-' + dottedName} inputMode="numeric" diff --git a/source/utils.js b/source/utils.js index f80f5b52e..6a71efe7c 100644 --- a/source/utils.js +++ b/source/utils.js @@ -9,11 +9,11 @@ export let parseDataAttributes = (value: any) => value === 'undefined' ? undefined : value === null - ? null - : !isNaN(value) - ? +value - : /* value is a normal string */ - value + ? null + : !isNaN(value) + ? +value + : /* value is a normal string */ + value export let getIframeOption = (optionName: string) => { let url = getUrl(), From 326909fa8718c0cebc705f04a31aba1a2eb9fcaf Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 21 Nov 2018 16:33:03 +0100 Subject: [PATCH 3/3] :art: meilleur animation et visualisation --- source/components/AnimatedTargetValue.css | 12 +++++++++--- source/components/AnimatedTargetValue.js | 24 +++++++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/source/components/AnimatedTargetValue.css b/source/components/AnimatedTargetValue.css index e83019313..e4f074e8d 100644 --- a/source/components/AnimatedTargetValue.css +++ b/source/components/AnimatedTargetValue.css @@ -13,13 +13,19 @@ position: absolute; right: 0; top: -20px; - opacity: 1; - animation: evaporate 1.3s ease-out; + opacity: 0; + animation: evaporate 1.6s ease-out; + transform: scaleY(0.1); } @keyframes evaporate { - 50% { + 5% { + opacity: 1; + transform: scaleY(1); + } + 70% { opacity: 1; } + to { transform: translateY(-15px); opacity: 0; diff --git a/source/components/AnimatedTargetValue.js b/source/components/AnimatedTargetValue.js index b1bda7080..d103ff619 100644 --- a/source/components/AnimatedTargetValue.js +++ b/source/components/AnimatedTargetValue.js @@ -48,18 +48,22 @@ export default withLanguage( render() { const formattedValue = this.format(this.props.value) const formattedDifference = this.format(this.state.difference) + const shouldDisplayDifference = + Math.abs(this.state.difference) > 1 && + formattedDifference !== formattedValue && + this.props.value != null && + this.state.difference < 0.5 * this.props.value return ( <> - {Math.abs(this.state.difference) > 1 && - formattedDifference !== formattedValue && ( - 0 ? 'chartreuse' : 'red' - }}> - {(this.state.difference > 0 ? '+' : '') + formattedDifference} - - )}{' '} + {shouldDisplayDifference && ( + 0 ? 'chartreuse' : 'red' + }}> + {(this.state.difference > 0 ? '+' : '') + formattedDifference} + + )}{' '} {this.format(this.props.value)} @@ -73,7 +77,7 @@ class Evaporate extends PureComponent<{ children: string, style: Object }> { return (