lightning_scheduler-8.x-1.x-dev/js/TransitionSet.es6.js

js/TransitionSet.es6.js
import { Component, createElement } from 'react';
import dateFormat from 'dateformat';

export default class extends Component
{
    /**
     * Constructor.
     *
     * @param {object} props
     * @param {object[]} props.transitions
     * @param {object} props.states
     * @param {string} props.input
     * @param {bool} props.step
     */
    constructor (props)
    {
        super (props);

        const transitions = props.transitions || [];

        const states = Object.entries( props.states );

        this.state = {
            transitions: transitions.map(t => {
                // Special case: when node edit page is viewed after preview.
                // Unix timestamp is stored in input of Drupal.
                // Converting this so that this is readable by JS.
                if (typeof t.when == 'number') {
                    t.when = t.when * 1000;
                }

                // The date and time of a transition is passed in as an ISO
                // 8601 UTC string, which we need to convert to a date object.
                t.when = new Date(t.when);
                return t;
            }),
        };

        // When creating a new transition, default to the first available
        // moderation state.
        this.defaultState = states[0][0];

        // The moderation state options never change, so build them once now.
        this.stateOptions = states.map(state => {
            const [ id, label ] = state;
            return <option key={ id } value={ id }>{ label }</option>
        });
    }

    /**
     * Renders a saved transition.
     *
     * @param {object} transition
     * @param {string} transition.state
     * @param {Date} transition.when
     * @param {number} index
     * @returns {*}
     */
    renderSaved (transition, index)
    {
        // Render the human-friendly label of the moderation state.
        const state = this.props.states[ transition.state ];
        // Render the date and time in a human-friendly format.
        const date = this.localizeDate(document.documentElement.lang, transition.when);

        // Event handler invoked when the transition is removed.
        const onRemove = (event) => {
            event.preventDefault();

            this.setState(prev => {
                // Splice the transition out of the current set.
                prev.transitions.splice(index, 1);
                return prev;
            });
        };

        // Provide a detailed title so we can remove specific transitions in
        // testing.
        const remove_title = Drupal.t('Remove transition to @state on @date', {
            '@state': state,
            '@date': date
        });

        const class_list = ['scheduled-transition'];

        return (
            <div className={ class_list.join(' ') }>
                { Drupal.t('Change to') } <b>{ state }</b> { Drupal.t('on') } { date }
                &nbsp;<a title={ remove_title } href="#" onClick={ onRemove }>{ Drupal.t('remove') }</a>
            </div>
        );
    }

  /**
   * Renders the date.
   *
   * @returns {*}
   */
    localizeDate(langCode, currentDate) {
        const formatter = new Intl.DateTimeFormat(langCode, {
            year: 'numeric',
            month: 'long',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit'
        });
        return formatter.format(currentDate);
    }

    /**
     * Renders the transition edit form.
     *
     * @returns {*}
     */
    renderForm ()
    {
        return createElement('div', { className: 'scheduled-transition' },
            Drupal.t('Change to'),
            this.renderStateControl(),
            Drupal.t('on'),
            this.renderDateControl(),
            Drupal.t('at'),
            this.renderTimeControl(),
            this.renderFormActions()
        );
    }

    /**
     * Renders the moderation state select box for a transition.
     *
     * @returns {*}
     */
    renderStateControl ()
    {
        const onChange = (event) => {
            // The event target will not be available when updating state, so
            // we need to bind it here.
            const element = event.target;

            this.setState(prev => {
                prev.edit.state = element.options[element.selectedIndex].value;
                return prev;
            });
        };

        const value = this.state.edit.state;

        return (
            <label>
                <span hidden>{ Drupal.t('Scheduled moderation state') }</span>
                <select defaultValue={ value } onChange={ onChange } class="form-select form-element form-element--type-select">{ this.stateOptions }</select>
            </label>
        );
    }

    /**
     * Renders the date input field for a transition.
     *
     * @returns {*}
     */
    renderDateControl ()
    {
        const onChange = (event) => {
            // The event target will not be available when updating state, so
            // we need to bind it here.
            const element = event.target;

            // Only change the date in state if it's valid.
            if (element.checkValidity())
            {
                this.setState(prev => {
                    const date = element.value.split('-');
                    // The month needs to be zero-based, not one-based.
                    date[1]--;
                    prev.edit.when.setFullYear(...date);
                    return prev;
                });
            }
        };

        const value = dateFormat(this.state.edit.when, 'isoDate');

        let mindate = "";

        if (!drupalSettings.lightning_scheduler.allow_past_dates) {
            let today;
            today = new Date();
            mindate = today.getFullYear() + '-' + ("0" + (today.getMonth() + 1)).slice(-2) + '-' + ("0" + today.getDate()).slice(-2);
        }

        return (
            <label>
                <span hidden>{ Drupal.t('Scheduled transition date') }</span>
                <input required min={ mindate } defaultValue={ value } type="date" onChange={ onChange } class="form-date form-element form-element--type-date form-element--api-date"/>
            </label>
        );
    }

    /**
     * Renders the time input field for a transition.
     *
     * @returns {*}
     */
    renderTimeControl ()
    {
        const onChange = (event) => {
            // The event target will not be available when updating state, so
            // we need to bind it here.
            const element = event.target;

            this.setState(prev => {
                prev.edit.when.setHours(...element.value.split(':'));
                return prev;
            });
        };

        let format;

        if (this.props.step >= 3600) {
            format = 'HH:00';
        }
        else if (this.props.step >= 60) {
            format = 'HH:MM';
        }
        else {
            format = 'isoTime';
        }

        const value = dateFormat(this.state.edit.when, format);

        return (
            <label>
                <span hidden>{ Drupal.t('Scheduled transition time') }</span>
                <input required defaultValue={ value } type="time" onChange={ onChange } step={ this.props.step } class="form-time form-element form-element--type-time form-element--api-date"/>
            </label>
        );
    }

    /**
     * Renders the form actions for a transition being edited.
     *
     * @returns {*[]}
     */
    renderFormActions ()
    {
        // Event handler invoked when the form is cancelled.
        const onCancel = (event) => {
            // Don't actually click the link.
            event.preventDefault();

            this.setState(prev => {
                // The form will not appear if edit is null. See render().
                prev.edit = null;
                return prev;
            });
        };

        // Event handler invoked when the form is saved.
        const onSave = (event) => {
            // Don't actually click the link.
            event.preventDefault();

            this.setState(prev => {
                prev.transitions.push(prev.edit);
                // The form will not appear if edit is null. See render().
                prev.edit = null;
                return prev;
            });
        };

        return (
          <span>
              <button className="button" title={ Drupal.t('Save transition') } onClick={ onSave }>{ Drupal.t('Save') }</button>
              &nbsp;{ Drupal.t('or') }&nbsp;
              <a title={ Drupal.t('Cancel transition') } href="#" onClick={ onCancel }>{ Drupal.t('cancel') }</a>
          </span>
        );
    }

    /**
     * Renders the component.
     *
     * @returns {*[]}
     */
    render ()
    {
        const elements = [
            createElement('input', {
                type: 'hidden',
                name: this.props.input,
                value: JSON.stringify(this),
            }),
            this.state.transitions.map((t, i) => this.renderSaved(t, i))
        ];

        // If we're currently editing a transition, render the form. Otherwise,
        // render an 'add another' link.
        elements.push( this.state.edit ? this.renderForm() : this.renderAddLink() );

        return elements;
    }

    toJSON ()
    {
        let propsStep = parseInt(this.props.step);
        return this.state.transitions.map(t => {
            // Trim additional seconds based on configured granularity.
            let trimWhen = t.when.getTime() / 1000;
            if (!isNaN(propsStep) && propsStep >= 60) {
              trimWhen -= trimWhen % 60;
            }

            return {
              // JavaScript returns time stamps in milliseconds, but PHP handles them
              // in seconds. Divide by 1000 to convert to seconds, then round down to
              // ensure we do not send a floating-point value back to the server. We
              // always round down (instead of using Math.round()) to in order to
              // prevent off-by-one-second timing failures in tests.
              when: Math.floor(trimWhen),
              state: t.state,
            };
        });
    }

    renderAddLink ()
    {
        // Event handler invoked to add another transition.
        const onAdd = (event) => {
            // Don't actually click the link.
            event.preventDefault();

            this.add();
        };

        const link_text = this.state.transitions.length
            ? Drupal.t('add another')
            : Drupal.t('Schedule a status change');

        return <a href="#" onClick={ onAdd }>{ link_text }</a>
    }

    /**
     * Adds a new transition, for editing.
     */
    add ()
    {
        let propsStep = parseInt(this.props.step);
        this.setState(prev => {
            let newWhen = new Date();

            // JavaScript returns time stamps in milliseconds, but PHP handles them
            // in seconds. Divide by 1000 to convert to seconds, then round down to
            // ensure we do not send a floating-point value back to the server. We
            // always round down (instead of using Math.round()) to in order to
            // prevent off-by-one-second timing failures in tests.
            let trimWhen = newWhen.getTime() / 1000;

            // Trim additional seconds based on configured granularity.
            if (!isNaN(propsStep) && propsStep >= 60) {
              trimWhen -= trimWhen % 60;
            }

            prev.edit = {
              state: this.defaultState,
              when: new Date(Math.floor(trimWhen) * 1000),
            };

            // If there is an existing transition, use its date and time as
            // the default for one we are creating.
            const last = prev.transitions.slice(-1).shift();

            if (last)
            {
                prev.edit.when.setTime( last.when.getTime() );
            }

            return prev;
        });
    }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc