2016-10-15 6 views
1

私はこの単純なv6還元型を、反応日ピッカーを使用して値を入力するカスタムコンポーネントをレンダリングする入力としています。React Date Pickerに接続しているReact/ReduxフォームフィールドをonFocusおよびonBlurに移動する方法は?

https://github.com/gpbl/react-day-picker

それはmoment.jsに依存し、私の現在のセットアップで動作しないので、私は他のものより反応する日ピッカーを選択しました。

フィールドにフォーカスすると、日付ピッカーがポップアップしますが、日付ピッカーでない場所をクリックすると消えます。

基本的に、私は私の中でjQueryUI 1のように動作するように日付ピッカーに反応します:

https://jqueryui.com/datepicker/

私はで終わる3つのシナリオは以下のとおりです。

  1. 私は、フィールドをクリックし、 datepickerがポップアップしますが、日付を選択するか、フィールドを再度クリックしないと消えません。

  2. 私は、フィールドをクリックし、日付ピッカーがポップアップし、それが選択した日付を持つフィールドを移入する日付ピッカーのためのクリックイベントを処理する前に、入力フィールドのonBlurイベントが呼び出されると、私は、任意の場所をクリックした場合TOOすぐに消えます。

  3. フィールドをクリックすると、日付ピッカーがポップアップ表示され、自動フォーカスが適用され、<ボディ>またはdatepickerでないものをクリックした場合を除き、正しくブラーされます。

私は、最初のページ全体をラップ兄弟の空のdivを使用しようとしましたので、私は空のdivをクリックしたときに、それが適切に日付ピッカーを切り替えます。これはz-indexesとpositionで正常に機能しました:datepickerの月を変更するまで固定されていました。これはdatepickerを再描画してクリックの順序を乱したように見え、状況2)に戻りました。

私の最も最近の試みは、日付ピッカーdivがポップアップするときに自動的にフォーカスすることです。そのため、日付ピッカーではないものをぼかすと、datepickerが切り替わります。これは理論で働いていましたが、日付ピッカーは、日、週、月、無効日を制御するためにdiv0>の中に入れ子になっている多くの要素を含んでいます。だから私は 'day'をクリックすると、 「day」<div>をクリックして、ルート 'datepicker' <div>ではなく、最初にフォーカスしたものをクリックします。上記の解決策は、「日付ピッカー」を微調整することでした

<div>のdocument.activeElementが<体であるとき、それが唯一の日付ピッカーをトグルすることonBlurイベントは、>が、私は別のフォームフィールドをクリックしない場合にのみ動作します。

WizardFormPageOne.js:

function WizardFormPageOne({ handleSubmit }) { 
    return (
    <form onSubmit={handleSubmit} className="col-xs-6"> 
     <h1>WizardFormPageOne</h1> 

     <div className="card"> 
     <div className="card-block"> 
      <div className="form-group"> 
      <label htmlFor="first">Label 1</label> 
      <Field type="text" name="first" component={DateInput} className="form-control" /> 
      </div> 
      ... 

export default reduxForm({ 
    form: 'wizardForm', 
    destroyOnUnmount: false, 
})(WizardFormPageOne); 

DateInput.js:

import React from 'react'; 

import styles from './styles.css'; 
import DatePicker from '../DatePicker'; 

class DateInput extends React.Component { // eslint-disable-line react/prefer-stateless-function 
    constructor(props) { 
    super(props); 

    this.state = { 
     dateValue: new Date(), 
     activeDateWidget: false, 
    }; 
    } 

    changeDate = (date) => { 
    this.setState({ 
     dateValue: date, 
    }); 
    } 

    changeActiveDateWidget = (e) => { 
    e.stopPropagation(); 
    this.setState({ 
     activeDateWidget: !this.state.activeDateWidget, 
    }); 
    } 

    render() { 
    const { input, meta } = this.props; 
    const { dateValue, activeDateWidget } = this.state; 
    return (
     <div className={styles.dateInput}> 
     <input 
      {...input} 
      className="form-control" 
      type="text" 
      value={dateValue} 
      onClick={this.changeActiveDateWidget} 
      // onBlur={this.changeActiveDateWidget} 
     /> 

     {activeDateWidget ? (
      <div> 
      <DatePicker 
       changeActiveDateWidget={this.changeActiveDateWidget} 
       changeDate={this.changeDate} 
       dateValue={dateValue} 
      /> 
      </div> 
     ) : (
      <div></div> 
     )} 
     </div> 
    ); 
    } 
} 

export default DateInput; 

日付ピッカー。JS:

import React from 'react'; 
import 'react-day-picker/lib/style.css'; 
import DayPicker, { DateUtils } from 'react-day-picker'; 

import styles from './styles.css'; 
import disabledDays from './disabledDays'; 


class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function 
    constructor(props) { 
    super(props); 
    this.state = { 
     selectedDay: new Date(), 
    }; 
    } 

    componentDidMount() { 
    if (this._input) { 
     this._input.focus(); 
    } 
    } 

    handleDayClick = (e, day, { disabled }) => { 
    e.stopPropagation(); 
    if (disabled) { 
     return; 
    } 
    this.setState({ selectedDay: day },() => { 
     this.props.changeDate(day); 
     this.props.changeActiveDateWidget(); 
    }); 
    } 

    focusThisComponent = (e) => { 
    if (e) { 
     this._input = e; 
    } 
    } 

    focusIt =() => { 
    console.log('focusing'); 
    } 

    blurIt =() => { 
    console.log('blurring'); 
    setTimeout(() => { 
     if (document.activeElement == document.body) { 
     this.props.changeActiveDateWidget(); 
     } 
    }, 1); 
    } 

    render() { 
    const { changeActiveDateWidget } = this.props; 
    const { selectedDay } = this.state; 
    return (
     <div 
     className={styles.datePicker} 
     ref={this.focusThisComponent} 
     tabIndex="1" 
     onFocus={this.focusIt} 
     onBlur={this.blurIt} 
     > 
     <DayPicker 
      id="THISTHING" 
      initialMonth={selectedDay} 
      disabledDays={disabledDays} 
      selectedDays={(day) => DateUtils.isSameDay(selectedDay, day)} 
      onDayClick={this.handleDayClick} 
     /> 
     </div> 
    ); 
    } 
} 

export default DatePicker; 

は、ここで私が今抱えている問題のスクリーンキャストです:

http://screencast.com/t/kZuIwUzl

日付ピッカーは、それがブレ停止した時点で別のフィールド、/をクリックしたときを除いて、適切に切り替わります適切に切り替える。すべての私のしつけは、上記の3つのシナリオのいずれかに私を導いた。

答えて

1

日付選択ツールを開いて日付ピッカーの非アクティブな部分をクリックし、日付の外にあるをクリックすると、http://react-day-picker.js.org/examples/?overlayの例が適切にぼやけませんピッカー。私はこの時点でニックピッキングしているかもしれませんが、彼の解決策はおそらく実装がはるかに簡単ですが、ここで私が解決したのはこれです:

DatePicker.jsでは、有効な< divのコレクションとして機能する空の配列を設定します> 's。 onBlurがトリガーされると、ルートDatePicker < div>をとり、すべての子を解析して空の配列に追加する再帰関数を呼び出します。その後、document.activeElementを調べて、配列内にあるかどうかを確認します。そうでない場合は、DatePickerウィジェットを切り替え、それ以外の場合は何もしないでください。

ぼかしの後にdocument.activeElementのチェックを1ティック実行する必要があります。そうしないと、activeElementは< body>になります。

関連リンク:

/** 
* 
* DatePicker 
* 
*/ 

import React from 'react'; 
import 'react-day-picker/lib/style.css'; 
import DayPicker, { DateUtils } from 'react-day-picker'; 

import styles from './styles.css'; 
import disabledDays from './disabledDays'; 

class DatePicker extends React.Component { // eslint-disable-line react/prefer-stateless-function 
    constructor(props) { 
    super(props); 
    this.state = { 
     selectedDay: new Date(), 
    }; 

    this.validElements = []; 
    } 

    componentDidMount() { 
    // Once DatePicker successfully sets a ref, component will mount 
    // and autofocus onto DatePicker's wrapper div. 
    if (this.refComponent) { 
     this.refComponent.focus(); 
    } 
    } 

    setRefComponent = (e) => { 
    if (e) { 
     this.refComponent = e; 
    } 
    } 

    findDatePickerDOMNodes = (element) => { 
    if (element.hasChildNodes()) { 
     this.validElements.push(element); 
     const children = element.childNodes; 
     for (let i = 0; i < children.length; i++) { 
     this.validElements.push(children[i]); 
     this.findDatePickerDOMNodes(children[i]); 
     } 
     return; 
    } 
    } 

    handleDayClick = (e, day, { disabled }) => { 
    if (disabled) { 
     return; 
    } 
    this.setState({ selectedDay: day },() => { 
     this.props.changeDate(day); 
     this.props.changeActiveDateWidget(); 
    }); 
    } 

    handleBlur =() => { 
    // Since DatePicker's wrapper div has been autofocused on mount, all 
    // that needs to be done is to blur on anything that's not the DatePicker. 
    // DatePicker has many child divs that handle things like day, week, month... 
    // invoke a recursive function to gather all children of root DatePicker div, then run a test against valid DatePicker elements. If test fails, then changeActiveDateWidget. 
    setTimeout(() => { 
     const rootDatePickerElement = document.getElementById('datePickerWidget'); 
     this.findDatePickerDOMNodes(rootDatePickerElement); 
     if (!this.validElements.includes(document.activeElement)) { 
     this.props.changeActiveDateWidget(); 
     } 
    }, 1); 
    } 

    render() { 
    const { selectedDay } = this.state; 
    return (
     <div 
     className={styles.datePicker} 
     onBlur={this.handleBlur} 
     // tabIndex necessary for element to be auto-focused. 
     tabIndex="1" 
     ref={this.setRefComponent} 
     > 
     <DayPicker 
      initialMonth={selectedDay} 
      disabledDays={disabledDays} 
      selectedDays={(day) => DateUtils.isSameDay(selectedDay, day)} 
      onDayClick={this.handleDayClick} 
      id="datePickerWidget" 
     /> 
     </div> 
    ); 
    } 
} 

DatePicker.propTypes = { 
    changeDate: React.PropTypes.func, 
    changeActiveDateWidget: React.PropTypes.func, 
}; 

export default DatePicker; 

とDateInput.jsで

Get the newly focussed element (if any) from the onBlur event.

Which HTML elements can receive focus?は、トグルを引き起こす可能性があり、入力をクリックすると、二回トリガするので、私はちょうどそれが常にtrueを切り替えるように設定入力をクリックした場合:

render() { 
    const { input, meta } = this.props; 
    const { dateValue, activeDateWidget } = this.state; 
    return (
     <div className={styles.dateInput}> 
     <input 
      {...input} 
      className="form-control" 
      type="text" 
      value={dateValue} 
      onClick={() => { this.setState({ activeDateWidget: true }); }} 
     /> 
+1

o他の日付選択コンポーネントをチェックします。私の意見では、フォーカスの開閉はコンポーネントによって適切に処理されるべきです。回避策として、次のようなコンポーネント内にコンポーネントをラップすることができます:https://github.com/nkbt/react-page-click。他の実装を利用できる場合は、次のコンポーネントをチェックしてください:http://airbnb.io/react-dates/?selectedKind=SingleDatePicker&selectedStory=single%20month&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions% 2Factions-panel – Steffen

+0

反応ページのクリックはすばらしくて、私たちの頭痛の救済になります。 Airbnb datepickerが私の最初の選択でしたが、mxstbr/react-boilerplateの依存関係と矛盾するかどうかを調べるだけでなく、create-react-appを使って簡単なサンプルを作成して実行することはできませんでした。 –

1

http://react-day-picker.js.org/examples/?overlayというこの例を使用して、いくつかの小さな修正を加えて、redux-form v6互換にすることができます。ローカル状態を使用する代わりに、レンダー機能内にFieldコンポーネントをredux形式で提供してthis.props.input.valueを使用する必要があります。また、handleInputChangeイベントハンドラでは、this.setState({ value: e.target.value })の代わりにthis.props.input.onChange(e.target.value)handleInputBlurイベントコールthis.props.input.onBlur(e.target.value)の代わりに呼び出す必要があります。これは、それがredux形式のFieldコンポーネントとして機能するために必要なすべてです。

関連する問題