<commit_before>import includes from 'lodash/includes'
import join from 'lodash/join'
import later from 'later'
import map from 'lodash/map'
import React from 'react'
import sortedIndex from 'lodash/sortedIndex'
import { FormattedDate, FormattedTime } from 'react-intl'
import {
  Tab,
  Tabs
} from 'react-bootstrap-4/lib'

import _ from './intl'
import Component from './base-component'
import propTypes from './prop-types'
import TimezonePicker from './timezone-picker'
import { Card, CardHeader, CardBlock } from './card'
import { Col, Row } from './grid'
import { Range } from './form'

// ===================================================================

// By default later use UTC but we use this line for futures versions.
later.date.UTC()

// ===================================================================

const NAV_EACH_SELECTED = 1
const NAV_EVERY_N = 2

const MIN_PREVIEWS = 5
const MAX_PREVIEWS = 20

const MONTHS = [
  [ 0, 1, 2 ],
  [ 3, 4, 5 ],
  [ 6, 7, 8 ],
  [ 9, 10, 11 ]
]

const DAYS = (() => {
  const days = []

  for (let i = 0; i < 4; i++) {
    days[i] = []

    for (let j = 1; j < 8; j++) {
      days[i].push(7 * i + j)
    }
  }

  days.push([29, 30, 31])

  return days
})()

const WEEK_DAYS = [
  [ 0, 1, 2 ],
  [ 3, 4, 5 ],
  [ 6 ]
]

const HOURS = (() => {
  const hours = []

  for (let i = 0; i < 3; i++) {
    hours[i] = []

    for (let j = 0; j < 8; j++) {
      hours[i].push(8 * i + j)
    }
  }

  return hours
})()

const MINS = (() => {
  const minutes = []

  for (let i = 0; i < 6; i++) {
    minutes[i] = []

    for (let j = 0; j < 10; j++) {
      minutes[i].push(10 * i + j)
    }
  }

  return minutes
})()

const PICKTIME_TO_ID = {
  minute: 0,
  hour: 1,
  monthDay: 2,
  month: 3,
  weekDay: 4
}

const TIME_FORMAT = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',

  // The timezone is not significant for displaying the date previews
  // as long as it is the same used to generate the next occurrences
  // from the cron patterns.

  // Therefore we can use UTC everywhere and say to the user that the
  // previews are in the configured timezone.
  timeZone: 'UTC'
}

// ===================================================================

// monthNum: [ 0 : 11 ]
const getMonthName = (monthNum) =>
  <FormattedDate value={Date.UTC(1970, monthNum)} month='long' timeZone='UTC' />

// dayNum: [ 0 : 6 ]
const getDayName = (dayNum) =>
  // January, 1970, 5th => Monday
  <FormattedDate value={Date.UTC(1970, 0, 4 + dayNum)} weekday='long' timeZone='UTC' />

// ===================================================================

@propTypes({
  cronPattern: propTypes.string.isRequired
})
export class SchedulePreview extends Component {
  _handleChange = value => {
    this.setState({
      value
    })
  }

  render () {
    const { cronPattern } = this.props
    const cronSched = later.parse.cron(cronPattern)
    const dates = later.schedule(cronSched).next(this.state.value || MIN_PREVIEWS)

    return (

          {_('cronPattern')} {cronPattern}

          <Range min={MIN_PREVIEWS} max={MAX_PREVIEWS} onChange={this._handleChange} />

          {map(dates, (date, id) => (

              <FormattedTime value={date} {...TIME_FORMAT} />

          ))}

...

    )
  }
}

// ===================================================================

@propTypes({
  children: propTypes.any.isRequired,
  onChange: propTypes.func.isRequired,
  tdId: propTypes.number.isRequired,
  value: propTypes.bool.isRequired
})
class ToggleTd extends Component {
  _onClick = () => {
    const { props } = this
    props.onChange(props.tdId, !props.value)
  }

  render () {
    const { props } = this
    return (

        {props.children}

    )
  }
}

// ===================================================================

@propTypes({
  options: propTypes.array.isRequired,
  optionsRenderer: propTypes.func,
  onChange: propTypes.func.isRequired,
  value: propTypes.array.isRequired
})
class TableSelect extends Component {
  static defaultProps = {
    optionsRenderer: value => value
  }

  _reset = () => {
    this.props.onChange([])
  }

  _handleChange = (tdId, tdValue) => {
    const { props } = this

    const newValue = props.value.slice()
    const index = sortedIndex(newValue, tdId)

    if (tdValue) {
      // Add
      if (newValue[index] !== tdId) {
        newValue.splice(index, 0, tdId)
      }
    } else {
      // Remove
      if (newValue[index] === tdId) {
        newValue.splice(index, 1)
      }
    }

    props.onChange(newValue)
  }

  render () {
    const {
      options,
      optionsRenderer,
      value
    } = this.props
    const { length } = options[0]

    return (

            {map(options, (line, i) => (

                {map(line, (tdOption, j) => {
                  const tdId = length * i + j
                  return (
                    <ToggleTd
                      children={optionsRenderer(tdOption)}
                      tdId={tdId}
                      key={tdId}
                      onChange={this._handleChange}
                      value={includes(value, tdId)}
                    />
                  )
                })}

            ))}

        <button className='btn btn-secondary pull-right' onClick={this._reset}>
          {_('selectTableReset')}
        </button>

    )
  }
}

// ===================================================================

@propTypes({
  optionsRenderer: propTypes.func,
  onChange: propTypes.func.isRequired,
  range: propTypes.array,
  labelId: propTypes.string.isRequired,
  value: propTypes.any.isRequired,
  valueRenderer: propTypes.func
})
class TimePicker extends Component {
  static defaultProps = {
    valueRenderer: e => +e
  }

  constructor () {
    super()
    this.state = {
      activeKey: NAV_EACH_SELECTED,
      tableValue: []
    }
  }

  _update (props) {
    const { value, valueRenderer } = props

    if (value.indexOf('/') === 1) {
      this.setState({
        activeKey: NAV_EVERY_N
      }, () => { this.refs.range.value = value.split('/')[1] })
    } else {
      this.setState({
        activeKey: NAV_EACH_SELECTED,
        tableValue: value === '*'
          ? []
          : map(value.split(','), valueRenderer)
      })
    }
  }

  componentWillMount () {
    this._update(this.props)
  }

  componentWillReceiveProps (props) {
    this._update(props)
  }

  _selectTab = activeKey => {
    this.setState({
      activeKey
    }, () => {
      const { activeKey, tableValue } = this.state
      const { onChange } = this.props
      const { refs } = this

      if (activeKey === NAV_EACH_SELECTED) {
        onChange(tableValue)
      } else {
        onChange(refs.range.value)
      }
    })
  }

  _handleTableValue = tableValue => {
    this.setState({
      tableValue
    }, () => this.props.onChange(tableValue))
  }

  render () {
    const {
      onChange,
      options,
      optionsRenderer,
      range,
      labelId
    } = this.props
    const { tableValue } = this.state

    const tableSelect = (
      <TableSelect
        onChange={this._handleTableValue}
        options={options}
        optionsRenderer={optionsRenderer}
        value={tableValue}
      />
    )

    return (
      <Card>
        <CardHeader>
          {_(`scheduling${labelId}`)}
        </CardHeader>
        <CardBlock>
          {range
            ? (
              <Tabs bsStyle='tabs' activeKey={this.state.activeKey} onSelect={this._selectTab}>
                <Tab tabClassName='nav-item' eventKey={NAV_EACH_SELECTED} title={_(`schedulingEachSelected${labelId}`)}>
                  {tableSelect}
                </Tab>
                <Tab tabClassName='nav-item' eventKey={NAV_EVERY_N} title={_(`schedulingEveryN${labelId}`)}>
                  <Range ref='range' min={range[0]} max={range[1]} onChange={onChange} />
                </Tab>
              </Tabs>
            ) : tableSelect
          }
        </CardBlock>
      </Card>
    )
  }
}

// ===================================================================

const HOURS_RANGE = [2, 12]
const MINUTES_RANGE = [2, 30]

const decrement = e => e - 1

@propTypes({
  cronPattern: propTypes.string.isRequired,
  onChange: propTypes.func,
  timezone: propTypes.string
})
export default class Scheduler extends Component {
  _update (type, value) {
    if (Array.isArray(value)) {
      if (!value.length) {
        value = '*'
      } else {
        value = join(
          (type === 'monthDay' || type === 'month')
            ? map(value, n => n + 1)
            : value,
          ','
        )
      }
    } else {
      value = `*/${value}`
    }

    const { props } = this
    const cronPattern = props.cronPattern.split(' ')
    cronPattern[PICKTIME_TO_ID[type]] = value

    this.props.onChange({
      cronPattern: cronPattern.join(' '),
      timezone: props.timezone
    })
  }

  _onHourChange = value => this._update('hour', value)
  _onMinuteChange = value => this._update('minute', value)
  _onMonthChange = value => this._update('month', value)
  _onMonthDayChange = value => this._update('monthDay', value)
  _onWeekDayChange = value => this._update('weekDay', value)

  _onTimezoneChange = timezone => {
    const { props } = this
    props.onChange({
      cronPattern: props.cronPattern,
      timezone
    })
  }

  render () {
    const {
      cronPattern,
      timezone
    } = this.props
    const cronPatternArr = cronPattern.split(' ')

    return (

        <Row>
          <Col mediumSize={6}>
            <TimePicker
              labelId='Month'
              optionsRenderer={getMonthName}
              options={MONTHS}
              onChange={this._onMonthChange}
              value={cronPatternArr[PICKTIME_TO_ID['month']]}
              valueRenderer={decrement}
            />
            <TimePicker
              labelId='MonthDay'
              options={DAYS}
              onChange={this._onMonthDayChange}
              value={cronPatternArr[PICKTIME_TO_ID['monthDay']]}
              valueRenderer={decrement}
            />
            <TimePicker
              labelId='WeekDay'
              optionsRenderer={getDayName}
              options={WEEK_DAYS}
              onChange={this._onWeekDayChange}
              value={cronPatternArr[PICKTIME_TO_ID['weekDay']]}
            />
          </Col>
          <Col mediumSize={6}>
            <TimePicker
              labelId='Hour'
              options={HOURS}
              range={HOURS_RANGE}
              onChange={this._onHourChange}
              value={cronPatternArr[PICKTIME_TO_ID['hour']]}
            />
            <TimePicker
              labelId='Minute'
              options={MINS}
              range={MINUTES_RANGE}
              onChange={this._onMinuteChange}
              value={cronPatternArr[PICKTIME_TO_ID['minute']]}
            />
          </Col>
        </Row>
        <Row>
          <Col>

            <TimezonePicker value={timezone} onChange={this._onTimezoneChange} />
          </Col>
        </Row>

    )
  }
}
<commit_msg>fix(scheduling): month range is 0-11 not 1-12 (#1894)<commit_after>import includes from 'lodash/includes'
import later from 'later'
import map from 'lodash/map'
import React from 'react'
import sortedIndex from 'lodash/sortedIndex'
import { FormattedDate, FormattedTime } from 'react-intl'
import {
  Tab,
  Tabs
} from 'react-bootstrap-4/lib'

import _ from './intl'
import Component from './base-component'
import propTypes from './prop-types'
import TimezonePicker from './timezone-picker'
import { Card, CardHeader, CardBlock } from './card'
import { Col, Row } from './grid'
import { Range } from './form'

// ===================================================================

// By default later use UTC but we use this line for futures versions.
later.date.UTC()

// ===================================================================

const NAV_EACH_SELECTED = 1
const NAV_EVERY_N = 2

const MIN_PREVIEWS = 5
const MAX_PREVIEWS = 20

const MONTHS = [
  [ 0, 1, 2 ],
  [ 3, 4, 5 ],
  [ 6, 7, 8 ],
  [ 9, 10, 11 ]
]

const DAYS = (() => {
  const days = []

  for (let i = 0; i < 4; i++) {
    days[i] = []

    for (let j = 1; j < 8; j++) {
      days[i].push(7 * i + j)
    }
  }

  days.push([29, 30, 31])

  return days
})()

const WEEK_DAYS = [
  [ 0, 1, 2 ],
  [ 3, 4, 5 ],
  [ 6 ]
]

const HOURS = (() => {
  const hours = []

  for (let i = 0; i < 3; i++) {
    hours[i] = []

    for (let j = 0; j < 8; j++) {
      hours[i].push(8 * i + j)
    }
  }

  return hours
})()

const MINS = (() => {
  const minutes = []

  for (let i = 0; i < 6; i++) {
    minutes[i] = []

    for (let j = 0; j < 10; j++) {
      minutes[i].push(10 * i + j)
    }
  }

  return minutes
})()

const PICKTIME_TO_ID = {
  minute: 0,
  hour: 1,
  monthDay: 2,
  month: 3,
  weekDay: 4
}

const TIME_FORMAT = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',

  // The timezone is not significant for displaying the date previews
  // as long as it is the same used to generate the next occurrences
  // from the cron patterns.

  // Therefore we can use UTC everywhere and say to the user that the
  // previews are in the configured timezone.
  timeZone: 'UTC'
}

// ===================================================================

// monthNum: [ 0 : 11 ]
const getMonthName = (monthNum) =>
  <FormattedDate value={Date.UTC(1970, monthNum)} month='long' timeZone='UTC' />

// dayNum: [ 0 : 6 ]
const getDayName = (dayNum) =>
  // January, 1970, 5th => Monday
  <FormattedDate value={Date.UTC(1970, 0, 4 + dayNum)} weekday='long' timeZone='UTC' />

// ===================================================================

@propTypes({
  cronPattern: propTypes.string.isRequired
})
export class SchedulePreview extends Component {
  _handleChange = value => {
    this.setState({
      value
    })
  }

  render () {
    const { cronPattern } = this.props
    const cronSched = later.parse.cron(cronPattern)
    const dates = later.schedule(cronSched).next(this.state.value || MIN_PREVIEWS)

    return (

          {_('cronPattern')} {cronPattern}

          <Range min={MIN_PREVIEWS} max={MAX_PREVIEWS} onChange={this._handleChange} />

          {map(dates, (date, id) => (

              <FormattedTime value={date} {...TIME_FORMAT} />

          ))}

...

    )
  }
}

// ===================================================================

@propTypes({
  children: propTypes.any.isRequired,
  onChange: propTypes.func.isRequired,
  tdId: propTypes.number.isRequired,
  value: propTypes.bool.isRequired
})
class ToggleTd extends Component {
  _onClick = () => {
    const { props } = this
    props.onChange(props.tdId, !props.value)
  }

  render () {
    const { props } = this
    return (

        {props.children}

    )
  }
}

// ===================================================================

@propTypes({
  options: propTypes.array.isRequired,
  optionsRenderer: propTypes.func,
  onChange: propTypes.func.isRequired,
  value: propTypes.array.isRequired
})
class TableSelect extends Component {
  static defaultProps = {
    optionsRenderer: value => value
  }

  _reset = () => {
    this.props.onChange([])
  }

  _handleChange = (tdId, tdValue) => {
    const { props } = this

    const newValue = props.value.slice()
    const index = sortedIndex(newValue, tdId)

    if (tdValue) {
      // Add
      if (newValue[index] !== tdId) {
        newValue.splice(index, 0, tdId)
      }
    } else {
      // Remove
      if (newValue[index] === tdId) {
        newValue.splice(index, 1)
      }
    }

    props.onChange(newValue)
  }

  render () {
    const {
      options,
      optionsRenderer,
      value
    } = this.props

    return (

            {map(options, (line, i) => (

                {map(line, tdOption => {
                  return (
                    <ToggleTd
                      children={optionsRenderer(tdOption)}
                      tdId={tdOption}
                      key={tdOption}
                      onChange={this._handleChange}
                      value={includes(value, tdOption)}
                    />
                  )
                })}

            ))}

        <button className='btn btn-secondary pull-right' onClick={this._reset}>
          {_('selectTableReset')}
        </button>

    )
  }
}

// ===================================================================

@propTypes({
  optionsRenderer: propTypes.func,
  onChange: propTypes.func.isRequired,
  range: propTypes.array,
  labelId: propTypes.string.isRequired,
  value: propTypes.any.isRequired
})
class TimePicker extends Component {
  constructor () {
    super()
    this.state = {
      activeKey: NAV_EACH_SELECTED,
      tableValue: []
    }
  }

  _update (props) {
    const { value } = props

    if (value.indexOf('/') === 1) {
      this.setState({
        activeKey: NAV_EVERY_N
      }, () => { this.refs.range.value = value.split('/')[1] })
    } else {
      this.setState({
        activeKey: NAV_EACH_SELECTED,
        tableValue: value === '*'
          ? []
          : map(value.split(','), Number)
      })
    }
  }

  componentWillMount () {
    this._update(this.props)
  }

  componentWillReceiveProps (props) {
    this._update(props)
  }

  _selectTab = activeKey => {
    this.setState({
      activeKey
    }, () => {
      const { activeKey, tableValue } = this.state
      const { onChange } = this.props
      const { refs } = this

      if (activeKey === NAV_EACH_SELECTED) {
        onChange(tableValue)
      } else {
        onChange(refs.range.value)
      }
    })
  }

  _handleTableValue = tableValue => {
    this.setState({
      tableValue
    }, () => this.props.onChange(tableValue))
  }

  render () {
    const {
      onChange,
      options,
      optionsRenderer,
      range,
      labelId
    } = this.props
    const { tableValue } = this.state

    const tableSelect = (
      <TableSelect
        onChange={this._handleTableValue}
        options={options}
        optionsRenderer={optionsRenderer}
        value={tableValue}
      />
    )

    return (
      <Card>
        <CardHeader>
          {_(`scheduling${labelId}`)}
        </CardHeader>
        <CardBlock>
          {range
            ? (
              <Tabs bsStyle='tabs' activeKey={this.state.activeKey} onSelect={this._selectTab}>
                <Tab tabClassName='nav-item' eventKey={NAV_EACH_SELECTED} title={_(`schedulingEachSelected${labelId}`)}>
                  {tableSelect}
                </Tab>
                <Tab tabClassName='nav-item' eventKey={NAV_EVERY_N} title={_(`schedulingEveryN${labelId}`)}>
                  <Range ref='range' min={range[0]} max={range[1]} onChange={onChange} />
                </Tab>
              </Tabs>
            ) : tableSelect
          }
        </CardBlock>
      </Card>
    )
  }
}

// ===================================================================

const HOURS_RANGE = [2, 12]
const MINUTES_RANGE = [2, 30]

@propTypes({
  cronPattern: propTypes.string.isRequired,
  onChange: propTypes.func,
  timezone: propTypes.string
})
export default class Scheduler extends Component {
  _update (type, value) {
    if (Array.isArray(value)) {
      if (!value.length) {
        value = '*'
      } else {
        value = value.join(',')
      }
    } else {
      value = `*/${value}`
    }

    const { props } = this
    const cronPattern = props.cronPattern.split(' ')
    cronPattern[PICKTIME_TO_ID[type]] = value

    this.props.onChange({
      cronPattern: cronPattern.join(' '),
      timezone: props.timezone
    })
  }

  _onHourChange = value => this._update('hour', value)
  _onMinuteChange = value => this._update('minute', value)
  _onMonthChange = value => this._update('month', value)
  _onMonthDayChange = value => this._update('monthDay', value)
  _onWeekDayChange = value => this._update('weekDay', value)

  _onTimezoneChange = timezone => {
    const { props } = this
    props.onChange({
      cronPattern: props.cronPattern,
      timezone
    })
  }

  render () {
    const {
      cronPattern,
      timezone
    } = this.props
    const cronPatternArr = cronPattern.split(' ')

    return (

        <Row>
          <Col mediumSize={6}>
            <TimePicker
              labelId='Month'
              optionsRenderer={getMonthName}
              options={MONTHS}
              onChange={this._onMonthChange}
              value={cronPatternArr[PICKTIME_TO_ID['month']]}
            />
            <TimePicker
              labelId='MonthDay'
              options={DAYS}
              onChange={this._onMonthDayChange}
              value={cronPatternArr[PICKTIME_TO_ID['monthDay']]}
            />
            <TimePicker
              labelId='WeekDay'
              optionsRenderer={getDayName}
              options={WEEK_DAYS}
              onChange={this._onWeekDayChange}
              value={cronPatternArr[PICKTIME_TO_ID['weekDay']]}
            />
          </Col>
          <Col mediumSize={6}>
            <TimePicker
              labelId='Hour'
              options={HOURS}
              range={HOURS_RANGE}
              onChange={this._onHourChange}
              value={cronPatternArr[PICKTIME_TO_ID['hour']]}
            />
            <TimePicker
              labelId='Minute'
              options={MINS}
              range={MINUTES_RANGE}
              onChange={this._onMinuteChange}
              value={cronPatternArr[PICKTIME_TO_ID['minute']]}
            />
          </Col>
        </Row>
        <Row>
          <Col>

            <TimezonePicker value={timezone} onChange={this._onTimezoneChange} />
          </Col>
        </Row>

    )
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
import MdMenu from 'react-icons/lib/md/menu'
import debounce from '../util/debounce'
import {
  PRIMARY_COLOR,
  BOX_SHADOW_1,
  BOX_SHADOW_2,
  FONT_FAMILY_SECONDARY,
  FONT_COLOR_LIGHT
} from '../util/constants'

const Outer = styled.div`
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;

  padding: 12px;

  color: #fff;
  background: ${PRIMARY_COLOR};
  box-shadow: ${BOX_SHADOW_1};

  font-family: ${FONT_FAMILY_SECONDARY};

  a {
    text-decoration: none;
    color: inherit;
  }
`

const MenuIcon = styled(MdMenu)`
  margin-right: 12px;
  font-size: 1.2rem;
`

const Search = styled.div`
  position: relative;
  padding: 4px 12px;
  border-bottom: 2px solid rgba(255, 255, 255, .3);

  input {
    border: none;
    outline: none;
    box-sizing: border-box;

    background: none;
    color: inherit;

    font-family: inherit;
    font-size: inherit;
  }
`

const Suggestions = styled.div`
  position: absolute;
  left: 0;
  right: 0;
  z-index: 10;

  background: #fff;
  color: ${FONT_COLOR_LIGHT};
  box-shadow: ${BOX_SHADOW_2};

  font-size: 13px;
  padding: 12px;

  a {
    display: block;
    text-decoration: none;
    color: inherit;
  }
`

class AppBar extends Component {
  constructor (props) {
    super(props)

    this.state = {
      suggestions: []
    }

    this.onChange = debounce(this.onChange, 250).bind(this)
  }

  onChange (value) {
    if (value.length > 2) {
      this.props.api.searchSubreddits(value)
        .then(results => results.subreddits.map(x => x.name))
        .then(suggestions => this.setState({ suggestions }))
    } else if (value.length === 0) {
      this.setState({ suggestions: [] })
    }
  }

  render () {
    const { r } = this.props
    const title = r
      ? `/r/${r}`
      : 'Frontpage'
    const href = r
      ? title
      : '/'

    return (
      <Outer>

          <MenuIcon onClick={() => this.props.actions.toggleDrawer()} />
          <Link to={href}>{title}</Link>

        <Search>
          /r/
          <input
            placeholder='browse...'
            onChange={e => this.onChange(e.target.value)}
          />

          {this.state.suggestions.length > 0 && (
            <Suggestions>
              {this.state.suggestions.map(x => (
                <Link
                  key={x}
                  to={`/r/${x}`}
                  onClick={() => this.setState({ suggestions: [] })}
                >
                  {x}
                </Link>
              ))}
            </Suggestions>
          )}
        </Search>
      </Outer>
    )
  }
}

export default AppBar
<commit_msg>Add support for custom styled components<commit_after>import React, { Component } from 'react'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
import MdMenu from 'react-icons/lib/md/menu'
import debounce from '../util/debounce'
import {
  PRIMARY_COLOR,
  BOX_SHADOW_1,
  BOX_SHADOW_2,
  FONT_FAMILY_SECONDARY,
  FONT_COLOR_LIGHT
} from '../util/constants'

const Outer = styled.div`
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;

  padding: 12px;

  color: #fff;
  background: ${PRIMARY_COLOR};
  box-shadow: ${BOX_SHADOW_1};

  font-family: ${FONT_FAMILY_SECONDARY};

  a {
    text-decoration: none;
    color: inherit;
  }
`

const MenuIcon = styled(MdMenu)`
  margin-right: 12px;
  font-size: 1.2rem;
`

const Search = styled.div`
  position: relative;
  padding: 4px 12px;
  border-bottom: 2px solid rgba(255, 255, 255, .3);

  input {
    border: none;
    outline: none;
    box-sizing: border-box;

    background: none;
    color: inherit;

    font-family: inherit;
    font-size: inherit;
  }
`

const Suggestions = styled.div`
  position: absolute;
  left: 0;
  right: 0;
  z-index: 10;

  background: #fff;
  color: ${FONT_COLOR_LIGHT};
  box-shadow: ${BOX_SHADOW_2};

  font-size: 13px;
  padding: 12px;

  a {
    display: block;
    text-decoration: none;
    color: inherit;
  }
`

class AppBar extends Component {
  constructor (props) {
    super(props)

    this.state = {
      suggestions: []
    }

    this.onChange = debounce(this.onChange, 250).bind(this)
  }

  onChange (value) {
    if (value.length > 2) {
      this.props.api.searchSubreddits(value)
        .then(results => results.subreddits.map(x => x.name))
        .then(suggestions => this.setState({ suggestions }))
    } else if (value.length === 0) {
      this.setState({ suggestions: [] })
    }
  }

  render () {
    const { r } = this.props
    const title = r
      ? `/r/${r}`
      : 'Frontpage'
    const href = r
      ? title
      : '/'

    return (
      <Outer className={this.props.className}>

          <MenuIcon onClick={() => this.props.actions.toggleDrawer()} />
          <Link to={href}>{title}</Link>

        <Search>
          /r/
          <input
            placeholder='browse...'
            onChange={e => this.onChange(e.target.value)}
          />

          {this.state.suggestions.length > 0 && (
            <Suggestions>
              {this.state.suggestions.map(x => (
                <Link
                  key={x}
                  to={`/r/${x}`}
                  onClick={() => this.setState({ suggestions: [] })}
                >
                  {x}
                </Link>
              ))}
            </Suggestions>
          )}
        </Search>
      </Outer>
    )
  }
}

export default AppBar
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Relay from 'react-relay';
import { withRouter, Link } from 'react-router';
import _ from 'lodash';

import Hamburger from './Hamburger';

import styles from './css/App.module.css';

class Header extends Component {
    static propTypes = {
        showHamburger: React.PropTypes.bool,
        menuItems: React.PropTypes.arrayOf(React.PropTypes.element)
    }

    static defaultProps = {
        showHamburger: true,
        menuItems: []
    }

    constructor (props) {
        super(props);

        this.logout = this.logout.bind(this);

        var fullscreen =
            document.fullScreen ||
            document.mozFullScreen ||
            document.webkitIsFullScreen;

        this.state = {
            profile: props.auth.getProfile(),
            hamburgerExpanded: false,
            fullscreen
        };

        this.onHamburgerExpand = _.partial(this.onHamburgerToggle.bind(this), 'expanded');
        this.onHamburgerCollapse = _.partial(this.onHamburgerToggle.bind(this), 'collapsed');
        this.handleProfileUpdated = this.handleProfileUpdated.bind(this);
    }

    handleProfileUpdated (profile) {
        this.setState({
            profile
        });
    }

    componentDidMount () {
        this.props.auth.on('profile_updated', this.handleProfileUpdated);
    }

    componentWillUnmount () {
        this.props.auth.removeListener('profile_updated', this.handleProfileUpdated);
    }

    logout (e) {
        e.preventDefault();

        // Destroys the session data
        this.props.auth.logout();

        // Redirects to login page
        this.props.router.push('/login');
    }

    onHamburgerToggle (state) {
        this.setState({
            hamburgerExpanded: state === 'expanded'
        });
    }

    render () {
        return (

                {this.state.hamburgerExpanded && (

                )}

                        <Link to={'/app'}>Daybook</Link>

                    {!this.state.hamburgerExpanded && this.props.children}

                    {this.props.showHamburger && (
                        <Hamburger
                            text={this.state.profile && this.state.profile.name}
                            toggleClassName={styles.contextMenuToggle}
                            contentClassName={styles.contextMenuContent}
                            onExpand={this.onHamburgerExpand}
                            onCollapse={this.onHamburgerCollapse}
                        >

                                {(this.state.profile) && (

                                        <Link to='/app/home'>Home</Link>

                                )}

                                {(this.state.profile && this.props.auth.loggedIn()) && (

                                        <Link to={'/app/settings/' + this.props.user.id}>Settings</Link>

                                )}

                                {(this.state.profile) && (

                                        {this.props.auth.loggedIn() && (
                                            Log out
                                        )}

                                )}

                                {(this.props.menuItems.length > 0) && this.props.menuItems.map((item, i) => (

{item}

                                ))}

                            {this.state.profile && (

{this.props.menuContent}

                            )}
                        </Hamburger>
                    )}

        );
    }
}

class Content extends Component {
    render () {
        return (

{this.props.children}

        );
    }
}

export default class Layout extends Component {
    static Header = withRouter(Relay.createContainer(Header, {
        fragments: {
            user: () => Relay.QL`
                fragment on User {
                    id
                }
            `
        }
    }));
    static Content = Content;

    render () {
        return (

                {this.props.children}

        );
    }
}
<commit_msg>Prevented settings page from being accessed by admin users<commit_after>import React, { Component } from 'react';
import Relay from 'react-relay';
import { withRouter, Link } from 'react-router';
import _ from 'lodash';

import Hamburger from './Hamburger';

import styles from './css/App.module.css';

class Header extends Component {
    static propTypes = {
        showHamburger: React.PropTypes.bool,
        menuItems: React.PropTypes.arrayOf(React.PropTypes.element)
    }

    static defaultProps = {
        showHamburger: true,
        menuItems: []
    }

    constructor (props) {
        super(props);

        this.logout = this.logout.bind(this);

        var fullscreen =
            document.fullScreen ||
            document.mozFullScreen ||
            document.webkitIsFullScreen;

        this.state = {
            profile: props.auth.getProfile(),
            hamburgerExpanded: false,
            fullscreen
        };

        this.onHamburgerExpand = _.partial(this.onHamburgerToggle.bind(this), 'expanded');
        this.onHamburgerCollapse = _.partial(this.onHamburgerToggle.bind(this), 'collapsed');
        this.handleProfileUpdated = this.handleProfileUpdated.bind(this);
    }

    handleProfileUpdated (profile) {
        this.setState({
            profile
        });
    }

    componentDidMount () {
        this.props.auth.on('profile_updated', this.handleProfileUpdated);
    }

    componentWillUnmount () {
        this.props.auth.removeListener('profile_updated', this.handleProfileUpdated);
    }

    logout (e) {
        e.preventDefault();

        // Destroys the session data
        this.props.auth.logout();

        // Redirects to login page
        this.props.router.push('/login');
    }

    onHamburgerToggle (state) {
        this.setState({
            hamburgerExpanded: state === 'expanded'
        });
    }

    render () {
        return (

                {this.state.hamburgerExpanded && (

                )}

                        <Link to={'/app'}>Daybook</Link>

                    {!this.state.hamburgerExpanded && this.props.children}

                    {this.props.showHamburger && (
                        <Hamburger
                            text={this.state.profile && this.state.profile.name}
                            toggleClassName={styles.contextMenuToggle}
                            contentClassName={styles.contextMenuContent}
                            onExpand={this.onHamburgerExpand}
                            onCollapse={this.onHamburgerCollapse}
                        >

                                {(this.state.profile) && (

                                        <Link to='/app/home'>Home</Link>

                                )}

                                {(this.state.profile && this.props.auth.loggedIn() && this.props.user.role === 'creator') && (

                                        <Link to={'/app/settings/' + this.props.user.id}>Settings</Link>

                                )}

                                {(this.state.profile) && (

                                        {this.props.auth.loggedIn() && (
                                            Log out
                                        )}

                                )}

                                {(this.props.menuItems.length > 0) && this.props.menuItems.map((item, i) => (

{item}

                                ))}

                            {this.state.profile && (

{this.props.menuContent}

                            )}
                        </Hamburger>
                    )}

        );
    }
}

class Content extends Component {
    render () {
        return (

{this.props.children}

        );
    }
}

export default class Layout extends Component {
    static Header = withRouter(Relay.createContainer(Header, {
        fragments: {
            user: () => Relay.QL`
                fragment on User {
                    id
                    role
                }
            `
        }
    }));
    static Content = Content;

    render () {
        return (

                {this.props.children}

        );
    }
}
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { Link, matchPath, withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import map from 'lodash/map'

import { withStyles } from '@material-ui/core/styles'
import AppBar from '@material-ui/core/AppBar'
import Button from '@material-ui/core/Button'
import Grid from '@material-ui/core/Grid'
import IconButton from '@material-ui/core/IconButton'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Toolbar from '@material-ui/core/Toolbar'
import Tooltip from '@material-ui/core/Tooltip'
import Typography from '@material-ui/core/Typography'

import * as actions from '../redux/actions'
import { getThemeId } from '../redux/reducers/theme'
import { getSongsForCurrentSet } from '../redux/reducers/current-set'
import chordboardLogoDark from '../chordboard-logo-light.png'
import chordboardLogoLight from '../chordboard-logo-dark.png'
import {
	Brightness4 as LightModeIcon,
	Brightness5 as DarkModeIcon,
	Close as CloseIcon,
} from 'mdi-material-ui'

const styles = theme => ({
	root: {
		'@media print': {
			display: 'none !important',
		},
	},
	flex: {
		flex: 1,
	},
	menuButton: {
		marginLeft: -12,
		marginRight: 20,
	},
	logoBig: {
		height: theme.spacing(2),
		verticalAlign: 'middle',
	},
	logoWrapper: {
		paddingRight: theme.spacing(),
		paddingTop: theme.spacing(),
	},
	tabs: {
		flexGrow: 1,
		width: '100%',
	},
	tab: {
		root: {
			padding: 0,
		},
	},
	setToolbar: {},
	miniButton: {
		zoom: 0.8,
	},
})

class Navbar extends React.Component {
	static propTypes = {
		classes: PropTypes.object,
		history: PropTypes.object,
		location: PropTypes.object,
		// Redux props
		currentSet: PropTypes.object,
		setCurrentSetId: PropTypes.func.isRequired,
		setCurrentUser: PropTypes.func.isRequired,
		songs: PropTypes.array,
		updateTheme: PropTypes.func.isRequired,
	}

	handleBackButton = () => {
		this.props.setCurrentSetId(null)
		this.props.history.push('/sets')
	}

	logout = () => {
		localStorage.setItem('user', '')

		this.props.setCurrentUser({ name: null })

		if (this.props.history) {
			this.props.history.push({ pathname: '/login' })
		}

		let loginFrom = localStorage.getItem('loginFrom')

		if (loginFrom === 'google') {
			//google logout as per https://developers.google.com/identity/sign-in/web/sign-in
			var auth2 = window.gapi.auth2.getAuthInstance()
			auth2.signOut().then(() => {
				console.log('Google user signed out')
			})
		} else if (loginFrom === 'facebook') {
			//facebook logout as per https://developers.facebook.com/docs/facebook-login/web/
			/*FB.logout( function ( response ) {
				// Person is now logged out
				console.log( 'Facebook user signed out' );
			} );*/
		}
	}

	setUserTextSize = () => this.props.setCurrentUser({ textSize: 82 })

	toggleTheme = () => {
		const { themeId } = this.props
		if (themeId === 'light') {
			this.props.updateTheme('dark')
		} else {
			this.props.updateTheme('light')
		}
	}

	render() {
		const { classes, currentSet, location, songs, themeId } = this.props

		let songId
		const match = matchPath(location.pathname, {
			path: '/sets/:setId/songs/:songId',
			exact: true,
		})
		if (match) {
			songId = match.params.songId
		}

		return (
			<AppBar
				className={classes.root}
				color={'secondary'}
				position={'sticky'}
			>
				{currentSet ? (
					<Toolbar className={classes.noPrint} variant={'dense'}>
						<IconButton
							color={'inherit'}
							onClick={this.handleBackButton}
							className={classes.miniButton}
						>
							<CloseIcon />
						</IconButton>
						<Tabs
							className={classes.tabs}
							indicatorColor={'primary'}
							scrollButtons={'auto'}
							value={songId || 0}
							variant={'scrollable'}
						>
							<Tab
								key={'tabs-setlist'}
								component={Link}
								to={`/sets/${currentSet.id}`}
								label={
									<Typography variant={'button'} noWrap>
										Setlist
									</Typography>
								}
								className={classes.tab}
								color={'inherit'}
								value={0}
							/>

							{map(songs, song => (
								<Tab
									key={`tabs-${song.id}`}
									component={Link}
									to={`/sets/${currentSet.id}/songs/${song.id}`}
									label={
										<Typography variant={'button'} noWrap>
											{song.title}
										</Typography>
									}
									className={classes.tab}
									color={'inherit'}
									value={song.id}
								/>
							))}
						</Tabs>
					</Toolbar>
				) : (
					<Toolbar variant={'dense'}>
						<Grid container alignItems={'center'}>
							<Grid item xs>
								<Link to={'/'} className={classes.logoWrapper}>
									<img
										src={
											themeId === 'dark'
												? chordboardLogoDark
												: chordboardLogoLight
										}
										className={classes.logoBig}
										alt={'chordboard logo'}
									/>
								</Link>
								<Button
									component={Link}
									color={'inherit'}
									to={'/sets'}
								>
									Sets
								</Button>
								<Button
									component={Link}
									color={'inherit'}
									to={'/songs'}
								>
									Songs
								</Button>
							</Grid>

							<Grid item>
								<Tooltip
									title={
										themeId === 'dark'
											? 'light mode'
											: 'dark mode'
									}
								>
									<IconButton onClick={this.toggleTheme}>
										{themeId === 'dark' ? (
											<LightModeIcon />
										) : (
											<DarkModeIcon />
										)}
									</IconButton>
								</Tooltip>
							</Grid>
						</Grid>
					</Toolbar>
				)}
			</AppBar>
		)
	}
}

const mapStateToProps = state => ({
	currentSet: state.currentSet.id
		? state.sets.byId[state.currentSet.id]
		: null,
	currentSong: state.songs.byId[state.currentSong.id],
	themeId: getThemeId(state),
	songs: getSongsForCurrentSet(state),
})

export default withRouter(
	connect(mapStateToProps, actions)(withStyles(styles)(Navbar))
)
<commit_msg>add version number to navbar<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { Link, matchPath, withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import map from 'lodash/map'

import { withStyles } from '@material-ui/core/styles'
import AppBar from '@material-ui/core/AppBar'
import Button from '@material-ui/core/Button'
import Grid from '@material-ui/core/Grid'
import IconButton from '@material-ui/core/IconButton'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Toolbar from '@material-ui/core/Toolbar'
import Tooltip from '@material-ui/core/Tooltip'
import Typography from '@material-ui/core/Typography'

import * as actions from '../redux/actions'
import { getThemeId } from '../redux/reducers/theme'
import { getSongsForCurrentSet } from '../redux/reducers/current-set'
import chordboardLogoDark from '../chordboard-logo-light.png'
import chordboardLogoLight from '../chordboard-logo-dark.png'
import {
	Brightness4 as LightModeIcon,
	Brightness5 as DarkModeIcon,
	Close as CloseIcon,
} from 'mdi-material-ui'

const version = require('../../package.json').version

const styles = theme => ({
	root: {
		'@media print': {
			display: 'none !important',
		},
	},
	flex: {
		flex: 1,
	},
	menuButton: {
		marginLeft: -12,
		marginRight: 20,
	},
	logoBig: {
		height: theme.spacing(2),
		verticalAlign: 'middle',
	},
	logoWrapper: {
		paddingRight: theme.spacing(),
		paddingTop: theme.spacing(),
	},
	tabs: {
		flexGrow: 1,
		width: '100%',
	},
	tab: {
		root: {
			padding: 0,
		},
	},
	setToolbar: {},
	miniButton: {
		zoom: 0.8,
	},
})

class Navbar extends React.Component {
	static propTypes = {
		classes: PropTypes.object,
		history: PropTypes.object,
		location: PropTypes.object,
		// Redux props
		currentSet: PropTypes.object,
		setCurrentSetId: PropTypes.func.isRequired,
		setCurrentUser: PropTypes.func.isRequired,
		songs: PropTypes.array,
		updateTheme: PropTypes.func.isRequired,
	}

	handleBackButton = () => {
		this.props.setCurrentSetId(null)
		this.props.history.push('/sets')
	}

	logout = () => {
		localStorage.setItem('user', '')

		this.props.setCurrentUser({ name: null })

		if (this.props.history) {
			this.props.history.push({ pathname: '/login' })
		}

		let loginFrom = localStorage.getItem('loginFrom')

		if (loginFrom === 'google') {
			//google logout as per https://developers.google.com/identity/sign-in/web/sign-in
			var auth2 = window.gapi.auth2.getAuthInstance()
			auth2.signOut().then(() => {
				console.log('Google user signed out')
			})
		} else if (loginFrom === 'facebook') {
			//facebook logout as per https://developers.facebook.com/docs/facebook-login/web/
			/*FB.logout( function ( response ) {
				// Person is now logged out
				console.log( 'Facebook user signed out' );
			} );*/
		}
	}

	setUserTextSize = () => this.props.setCurrentUser({ textSize: 82 })

	toggleTheme = () => {
		const { themeId } = this.props
		if (themeId === 'light') {
			this.props.updateTheme('dark')
		} else {
			this.props.updateTheme('light')
		}
	}

	render() {
		const { classes, currentSet, location, songs, themeId } = this.props

		let songId
		const match = matchPath(location.pathname, {
			path: '/sets/:setId/songs/:songId',
			exact: true,
		})
		if (match) {
			songId = match.params.songId
		}

		return (
			<AppBar
				className={classes.root}
				color={'secondary'}
				position={'sticky'}
			>
				{currentSet ? (
					<Toolbar className={classes.noPrint} variant={'dense'}>
						<IconButton
							color={'inherit'}
							onClick={this.handleBackButton}
							className={classes.miniButton}
						>
							<CloseIcon />
						</IconButton>
						<Tabs
							className={classes.tabs}
							indicatorColor={'primary'}
							scrollButtons={'auto'}
							value={songId || 0}
							variant={'scrollable'}
						>
							<Tab
								key={'tabs-setlist'}
								component={Link}
								to={`/sets/${currentSet.id}`}
								label={
									<Typography variant={'button'} noWrap>
										Setlist
									</Typography>
								}
								className={classes.tab}
								color={'inherit'}
								value={0}
							/>

							{map(songs, song => (
								<Tab
									key={`tabs-${song.id}`}
									component={Link}
									to={`/sets/${currentSet.id}/songs/${song.id}`}
									label={
										<Typography variant={'button'} noWrap>
											{song.title}
										</Typography>
									}
									className={classes.tab}
									color={'inherit'}
									value={song.id}
								/>
							))}
						</Tabs>
					</Toolbar>
				) : (
					<Toolbar variant={'dense'}>
						<Grid container alignItems={'center'}>
							<Grid item xs>
								<Link to={'/'} className={classes.logoWrapper}>
									<img
										src={
											themeId === 'dark'
												? chordboardLogoDark
												: chordboardLogoLight
										}
										className={classes.logoBig}
										alt={'chordboard logo'}
									/>
								</Link>
								<Button
									component={Link}
									color={'inherit'}
									to={'/sets'}
								>
									Sets
								</Button>
								<Button
									component={Link}
									color={'inherit'}
									to={'/songs'}
								>
									Songs
								</Button>
							</Grid>

							<Grid item>
								<Typography variant={'caption'}>
									v{version}
								</Typography>
								<Tooltip
									title={
										themeId === 'dark'
											? 'light mode'
											: 'dark mode'
									}
								>
									<IconButton onClick={this.toggleTheme}>
										{themeId === 'dark' ? (
											<LightModeIcon />
										) : (
											<DarkModeIcon />
										)}
									</IconButton>
								</Tooltip>
							</Grid>
						</Grid>
					</Toolbar>
				)}
			</AppBar>
		)
	}
}

const mapStateToProps = state => ({
	currentSet: state.currentSet.id
		? state.sets.byId[state.currentSet.id]
		: null,
	currentSong: state.songs.byId[state.currentSong.id],
	themeId: getThemeId(state),
	songs: getSongsForCurrentSet(state),
})

export default withRouter(
	connect(mapStateToProps, actions)(withStyles(styles)(Navbar))
)
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import LazyLoad from 'react-lazyload';
import createURL from '../utils/utils';

const Photos = (props) => {

    const handleClick = (index) => {
        props._onClick(index);
    }

    const mouseHover = (index) => {
        props.onMouseHover(index);
    }

    const mouseUnhover = (index) => {
        props.onMouseUnhover(index);
    }

    const divStyle = {
        height: props.imageWidth
    }

    const orientationMaxSize = () => {
        // let orientation = props.imageOrientation;
        //d
    }
    // const hoverTitle = (hover) => {

    // }

    const photos = props.images.map((image, index) => {
        if (image[0]) {
            const source = image[0];
            const sourceWithSize = createURL('large', source);
            const title = image[1];
            const description = image[2];

            return (
                <LazyLoad height={200} offset={1000} once key={index}>

                        {/*
{title}
*/}

{title}

                </LazyLoad>
            );
        }
    });

    return (

              {photos}

    );
}

Photos.propTypes = {
    images: PropTypes.array.isRequired,
    _onClick: PropTypes.func.isRequired,
}

export default Photos;
<commit_msg>Revert "test"<commit_after>import React, { PropTypes } from 'react';
import LazyLoad from 'react-lazyload';
import createURL from '../utils/utils';

const Photos = (props) => {

    const handleClick = (index) => {
        props._onClick(index);
    }

    const mouseHover = (index) => {
        props.onMouseHover(index);
    }

    const mouseUnhover = (index) => {
        props.onMouseUnhover(index);
    }

    const divStyle = {
        height: props.imageWidth
    }

    const orientationMaxSize = () => {
        // let orientation = props.imageOrientation;

    }
    // const hoverTitle = (hover) => {

    // }

    const photos = props.images.map((image, index) => {
        if (image[0]) {
            const source = image[0];
            const sourceWithSize = createURL('large', source);
            const title = image[1];
            const description = image[2];

            return (
                <LazyLoad height={200} offset={1000} once key={index}>

                        {/*
{title}
*/}

{title}

                </LazyLoad>
            );
        }
    });

    return (

              {photos}

    );
}

Photos.propTypes = {
    images: PropTypes.array.isRequired,
    _onClick: PropTypes.func.isRequired,
}

export default Photos;
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import Paper from 'material-ui/Paper';
import { connect } from 'react-redux';
import { Doc } from '../redux/actions';
import RaisedButton from 'material-ui/RaisedButton';
import { editorStyle, inputStyle } from '../styles';
import { Text, ArrayText } from './text';
import Writer from './writer';
import { createSelector } from 'reselect';

let Editor = React.createClass({
  propTypes: {
    editor: React.PropTypes.object,
    handleNew: React.PropTypes.func,
    handleSave: React.PropTypes.func
  },

  render() {
    let { editor, handleNew, handleSave } = this.props;

    return (
      <Paper style={editorStyle} zDepth={0}>
        <Paper style={inputStyle}>
          <Text id="company" hint="Company" value={editor.company} />
          <Text id="product" hint="Product" value={editor.product} />
          <Text id="type" hint="Type" value={editor.type} />
          <ArrayText id="lang" hint="Langs" value={editor.lang} />
          <Text id="ticket" hint="JIRA" value={editor.ticket} />
          <Text id="date" hint="Created on" value={editor.date} />
        </Paper>
        <Writer id="desc" value={editor.desc} />

          <RaisedButton primary={true} label={'new'} onTouchTap={handleNew}
            style={{marginRight: 10}}
          />
          <RaisedButton secondary={true} label={'save'} onTouchTap={handleSave} />

      </Paper>
    );
  }
});

const mapStateToProps = state => ({
  config: state.config,
  editor: state.editor
});

const mapDispatchToProps = {
  handleNew: () => Doc.new(),
  handleSave: () => Doc.commit()
};

const mergeProps = createSelector(
  state => state.editor,
  state => state.config,
  (state, actions) => actions.handleNew,
  (state, actions) => actions.handleSave,
  (editor, config, handleNew, handleSave) => ({
    editor: {
      company: editor.company || '',
      product: editor.product || '',
      type: editor.type || '',
      lang: editor.lang || [],
      ticket: editor.ticket || '',
      desc: editor.desc || '',
      date: new Date(editor._id).toLocaleDateString(config.locale, {
        year: 'numeric', month: 'numeric', day: 'numeric',
        hour: '2-digit', minute: '2-digit', second: '2-digit'
      })
    },
    handleNew,
    handleSave
  })
);

export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Editor);
<commit_msg>simpifying editor component<commit_after>'use strict';

import React from 'react';
import Paper from 'material-ui/Paper';
import { connect } from 'react-redux';
import { Doc } from '../redux/actions';
import RaisedButton from 'material-ui/RaisedButton';
import { editorStyle, inputStyle } from '../styles';
import { Text, ArrayText } from './text';
import Writer from './writer';
import { createSelector } from 'reselect';

const Input = editor => (
  <Paper style={inputStyle}>
    <Text id="company" hint="Company" value={editor.company} />
    <Text id="product" hint="Product" value={editor.product} />
    <Text id="type" hint="Type" value={editor.type} />
    <ArrayText id="lang" hint="Langs" value={editor.lang} />
    <Text id="ticket" hint="JIRA" value={editor.ticket} />
    <Text id="date" hint="Created on" value={editor.date} />
  </Paper>
);

const Buttons = (handleNew, handleSave) => (

    <RaisedButton primary={true} label={'new'} onTouchTap={handleNew} style={{marginRight: 10}} />
    <RaisedButton secondary={true} label={'save'} onTouchTap={handleSave} />

);

let Editor = React.createClass({
  propTypes: {
    editor: React.PropTypes.object,
    handleNew: React.PropTypes.func,
    handleSave: React.PropTypes.func
  },

  render() {
    let { editor, handleNew, handleSave } = this.props;

    return (
      <Paper style={editorStyle} zDepth={0}>
        {Input(editor)}
        <Writer id="desc" value={editor.desc} />
        {Buttons(handleNew, handleSave)}
      </Paper>
    );
  }
});

const mapStateToProps = state => ({
  config: state.config,
  editor: state.editor
});

const mapDispatchToProps = {
  handleNew: () => Doc.new(),
  handleSave: () => Doc.commit()
};

const mergeProps = createSelector(
  state => state.editor,
  state => state.config,
  (state, actions) => actions.handleNew,
  (state, actions) => actions.handleSave,
  (editor, config, handleNew, handleSave) => ({
    editor: {
      company: editor.company || '',
      product: editor.product || '',
      type: editor.type || '',
      lang: editor.lang || [],
      ticket: editor.ticket || '',
      desc: editor.desc || '',
      date: new Date(editor._id).toLocaleDateString(config.locale, {
        year: 'numeric', month: 'numeric', day: 'numeric',
        hour: '2-digit', minute: '2-digit', second: '2-digit'
      })
    },
    handleNew,
    handleSave
  })
);

export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Editor);
<|endoftext|>


<commit_before>var _clone = require('lodash/clone');
module.exports = function(app) {
  app.config([
    'formioComponentsProvider',
    function(formioComponentsProvider) {
      formioComponentsProvider.register('select', {
        icon: 'fa fa-th-list',
        views: [
          {
            name: 'Display',
            template: 'formio/components/select/display.html'
          },
          {
            name: 'Data',
            template: 'formio/components/select/data.html'
          },
          {
            name: 'Validation',
            template: 'formio/components/select/validate.html'
          },
          {
            name: 'API',
            template: 'formio/components/common/api.html'
          },
          {
            name: 'Layout',
            template: 'formio/components/common/layout.html'
          },
          {
            name: 'Conditional',
            template: 'formio/components/common/conditional.html'
          }
        ],
        onEdit: ['$scope', 'FormioUtils', function($scope, FormioUtils) {
          $scope.dataSources = {
            values: 'Values',
            json: 'Raw JSON',
            url: 'URL',
            resource: 'Resource',
            custom: 'Custom'
          };
          $scope.resources = [];
          $scope.resourceFields = [];

          // Returns only input fields we are interested in.
          var getInputFields = function(components) {
            var fields = [];
            FormioUtils.eachComponent(components, function(component) {
              if (component.key && component.input && (component.type !== 'button') && component.key !== $scope.component.key) {
                var comp = _clone(component);
                if (!comp.label) {
                  comp.label = comp.key;
                }
                fields.push(comp);
              }
            });
            return fields;
          };

          $scope.formFields = [{label: 'Any Change', key: 'data'}].concat(getInputFields($scope.form.components));

          // Loads the selected fields.
          var loadFields = function() {
            if (!$scope.component.data.resource || ($scope.resources.length === 0)) {
              return;
            }
            var selected = null;
            $scope.resourceFields = [
              {
                property: '',
                title: '{Entire Object}'
              },
              {
                property: '_id',
                title: 'Submission Id'
              }
            ];
            if ($scope.formio.projectId) {
              $scope.component.data.project = $scope.formio.projectId;
            }
            for (var index in $scope.resources) {
              if ($scope.resources[index]._id.toString() === $scope.component.data.resource) {
                selected = $scope.resources[index];
                break;
              }
            }
            if (selected) {
              var fields = getInputFields(selected.components);
              for (var i in fields) {
                var field = fields[i];
                var title = field.label || field.key;
                $scope.resourceFields.push({
                  property: 'data.' + field.key,
                  title: title
                });
              }
              if (!$scope.component.valueProperty && $scope.resourceFields.length) {
                $scope.component.valueProperty = $scope.resourceFields[0].property;
              }
            }
          };

          $scope.$watch('component.dataSrc', function(source, prevSource) {
            if (source !== prevSource) {
              $scope.component.template = '{{ item.label }}';
            }

            if (($scope.resources.length === 0) && (source === 'resource')) {
              $scope.formio.loadForms({params: {
                type: 'resource',
                limit: 4294967295,
                select: '_id,title,components'
              }}).then(function(resources) {
                $scope.resources = resources;
                loadFields();
              });
            }
          });

          // Trigger when the resource changes.
          $scope.$watch('component.data.resource', function(resourceId) {
            if (!resourceId) {
              return;
            }
            loadFields();
          });

          // Update other parameters when the value property changes.
          $scope.currentValueProperty = $scope.component.valueProperty;
          $scope.$watch('component.valueProperty', function(property) {
            if ($scope.component.dataSrc === 'resource' && $scope.currentValueProperty !== property) {
              if (!property) {
                $scope.component.searchField = '';
                $scope.component.template = '{{ item.data }}';
              }
              else {
                $scope.component.searchField = property + '__regex';
                $scope.component.template = '{{ item.' + property + ' }}';
              }
            }
          });

          loadFields();
        }],
        documentation: 'http://help.form.io/userguide/#select'
      });
    }
  ]);
  app.run([
    '$templateCache',
    function($templateCache) {
      // Create the settings markup.
      $templateCache.put('formio/components/select/display.html',
        '<ng-form>' +
          '<form-builder-option property="label"></form-builder-option>' +
          '<form-builder-option property="hideLabel"></form-builder-option>' +
          '<form-builder-option-label-position></form-builder-option-label-position>' +
          '<form-builder-option property="placeholder"></form-builder-option>' +
          '<form-builder-option property="description"></form-builder-option>' +
          '<form-builder-option property="tooltip"></form-builder-option>' +
          '<form-builder-option property="errorLabel"></form-builder-option>' +
          '<form-builder-option property="customClass"></form-builder-option>' +
          '<form-builder-option property="tabindex"></form-builder-option>' +
          '<form-builder-option property="multiple"></form-builder-option>' +
          '<form-builder-option property="clearOnHide"></form-builder-option>' +
          '<form-builder-option property="protected"></form-builder-option>' +
          '<form-builder-option property="persistent"></form-builder-option>' +
          '<form-builder-option property="autofocus" type="checkbox" label="Initial Focus" tooltip="Make this field the initially focused element on this form."></form-builder-option>' +
          '<form-builder-option property="encrypted" class="form-builder-premium"></form-builder-option>' +
          '<form-builder-option property="hidden"></form-builder-option>' +
          '<form-builder-option property="disabled"></form-builder-option>' +
          '<form-builder-option property="dataGridLabel"></form-builder-option>' +
          '<form-builder-option property="tableView"></form-builder-option>' +
        '</ng-form>'
      );

      $templateCache.put('formio/components/select/data.html',
        '<ng-form>' +
          '
' +
            '{{\'Data Source Type\' | formioTranslate}}' +
            '<select class="form-control" id="dataSrc" name="dataSrc" ng-model="component.dataSrc" ng-options="value as label | formioTranslate for (value, label) in dataSources"></select>' +
          '
' +
          '<ng-switch on="component.dataSrc">' +
            '
' +
              '{{\'Data Source Raw JSON\' | formioTranslate}}' +
              '<textarea class="form-control" id="data.json" name="data.json" ng-model="component.data.json" placeholder="Raw JSON Array" json-input rows="3">{{ component.data.json }}</textarea>' +
            '
' +
            '
' +
            '  <form-builder-option property="data.url" label="Data Source URL" placeholder="Data Source URL" title="A URL that returns a JSON array to use as the data source."></form-builder-option>' +
            '
' +
            '<value-builder ng-switch-when="url" data=component.data.headers label="Request Headers" tooltip-text="Set any headers that should be sent along with the request to the url. This is useful for authentication." label-label="Key" label-property="key" />' +
            '<value-builder ng-switch-when="values" data="component.data.values" label="Data Source Values" tooltip-text="Values to use as the data source. Labels are shown in the select field. Values are the corresponding values saved with the submission."></value-builder>' +
            '
' +
            '{{\'Resource\' | formioTranslate}}' +
            '<ui-select ui-select-required ui-select-open-on-focus ng-model="component.data.resource" theme="bootstrap">' +
              '<ui-select-match class="ui-select-match" placeholder="">' +
                '{{$select.selected.title}}' +
              '</ui-select-match>' +
              '<ui-select-choices class="ui-select-choices" repeat="value._id as value in resources | filter: $select.search" refresh="refreshSubmissions($select.search)" refresh-delay="250">' +
                '

' +
              '</ui-select-choices>' +
            '</ui-select>' +
          '
' +
          '</ng-switch>' +
          '<form-builder-option ng-hide="component.dataSrc !== \'url\'" property="selectValues" label="Data Path" type="text" placeholder="The object path to the iterable items." title="The property within the source data, where iterable items reside. For example: results.items or results[0].items"></form-builder-option>' +
          '<form-builder-option ng-hide="component.dataSrc == \'values\' || component.dataSrc == \'resource\' || component.dataSrc == \'custom\'" property="valueProperty" label="Value Property" placeholder="The selected item\'s property to save." title="The property of each item in the data source to use as the select value. If not specified, the item itself will be used."></form-builder-option>' +
          '
' +
            '{{\'Value\' | formioTranslate}}' +
            '<select class="form-control" id="valueProperty" name="valueProperty" ng-options="value.property as value.title for value in resourceFields" ng-model="component.valueProperty"></select>' +
          '
' +
          '
' +
          '  {{\'Select Fields\' | formioTranslate}}' +
          '  <input type="text" class="form-control" id="selectFields" name="selectFields" ng-model="component.selectFields" placeholder="Comma separated list of fields to select." value="{{ component.selectFields }}">' +
          '
' +
          '
' +
          '<input type="checkbox" ng-model="component.data.disableLimit" name="disableLimit"></input>' +
          '  {{\'Disable limiting response\' | formioTranslate}}' +
          '
' +
          '<form-builder-option ng-show="component.dataSrc == \'url\' || component.dataSrc == \'resource\'" property="searchField" label="Search Query Name" placeholder="Name of URL query parameter" title="The name of the search querystring parameter used when sending a request to filter results with. The server at the URL must handle this query parameter."></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'url\' || component.dataSrc == \'resource\'" property="filter" label="Filter Query" placeholder="The filter query for results." title="Use this to provide additional filtering using query parameters."></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'url\' || component.dataSrc == \'resource\' || component.dataSrc == \'json\'" property="limit" label="Limit" placeholder="Maximum number of items to view per page of results." title="Use this to limit the number of items to request or view."></form-builder-option>' +
          '
' +
          '  {{\'Search Filter\' | formioTranslate}}' +
          '  <select class="form-control" id="filter" name="filter" ng-model="component.filter" ng-options="value as label | formioTranslate for (value, label) in {none: \'No Search\', contains: \'Contains\', startsWith: \'Starts With\'}"></select>' +
          '
' +
          '
' +
          '  {{\'Custom Values\' | formioTranslate}}' +
          '  <formio-script-editor rows="10" id="custom" name="custom" ng-model="component.data.custom" placeholder="/*** Example Code ***/\nvalues = data[\'mykey\'];"></formio-script-editor>' +
          '
' +
          '
' +
            '{{\'Item Template\' | formioTranslate}}' +
            '<textarea class="form-control" id="template" name="template" ng-model="component.template" rows="3">{{ component.template }}</textarea>' +
          '
' +
          '
' +
          '  {{\'Refresh On\' | formioTranslate}}' +
          '  <select class="form-control" id="refreshOn" name="refreshOn" ng-options="field.key as field.label for field in formFields" ng-model="component.refreshOn"></select>' +
          '
' +
          '<form-builder-option ng-show="component.dataSrc == \'resource\' || component.dataSrc == \'url\' || component.dataSrc == \'custom\'" property="clearOnRefresh"></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'resource\'" property="reference"></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'url\'" property="authenticate"></form-builder-option>' +
          '<form-builder-option property="defaultValue"></form-builder-option>' +
          '<form-builder-option property="dbIndex" class="form-builder-premium form-builder-dbindex"></form-builder-option>' +
        '</ng-form>'
      );

      // Create the API markup.
      $templateCache.put('formio/components/select/validate.html',
        '<ng-form>' +
          '<form-builder-option property="validate.required"></form-builder-option>' +
          '<form-builder-option ng-show="!!component.searchField" property="validate.select"></form-builder-option>' +
          '<form-builder-option property="unique"></form-builder-option>' +
          '<form-builder-option property="validate.customMessage"></form-builder-option>' +
          '<form-builder-option-custom-validation></form-builder-option-custom-validation>' +
        '</ng-form>'
      );
    }
  ]);
};
<commit_msg>Change end tag on the select component element<commit_after>var _clone = require('lodash/clone');
module.exports = function(app) {
  app.config([
    'formioComponentsProvider',
    function(formioComponentsProvider) {
      formioComponentsProvider.register('select', {
        icon: 'fa fa-th-list',
        views: [
          {
            name: 'Display',
            template: 'formio/components/select/display.html'
          },
          {
            name: 'Data',
            template: 'formio/components/select/data.html'
          },
          {
            name: 'Validation',
            template: 'formio/components/select/validate.html'
          },
          {
            name: 'API',
            template: 'formio/components/common/api.html'
          },
          {
            name: 'Layout',
            template: 'formio/components/common/layout.html'
          },
          {
            name: 'Conditional',
            template: 'formio/components/common/conditional.html'
          }
        ],
        onEdit: ['$scope', 'FormioUtils', function($scope, FormioUtils) {
          $scope.dataSources = {
            values: 'Values',
            json: 'Raw JSON',
            url: 'URL',
            resource: 'Resource',
            custom: 'Custom'
          };
          $scope.resources = [];
          $scope.resourceFields = [];

          // Returns only input fields we are interested in.
          var getInputFields = function(components) {
            var fields = [];
            FormioUtils.eachComponent(components, function(component) {
              if (component.key && component.input && (component.type !== 'button') && component.key !== $scope.component.key) {
                var comp = _clone(component);
                if (!comp.label) {
                  comp.label = comp.key;
                }
                fields.push(comp);
              }
            });
            return fields;
          };

          $scope.formFields = [{label: 'Any Change', key: 'data'}].concat(getInputFields($scope.form.components));

          // Loads the selected fields.
          var loadFields = function() {
            if (!$scope.component.data.resource || ($scope.resources.length === 0)) {
              return;
            }
            var selected = null;
            $scope.resourceFields = [
              {
                property: '',
                title: '{Entire Object}'
              },
              {
                property: '_id',
                title: 'Submission Id'
              }
            ];
            if ($scope.formio.projectId) {
              $scope.component.data.project = $scope.formio.projectId;
            }
            for (var index in $scope.resources) {
              if ($scope.resources[index]._id.toString() === $scope.component.data.resource) {
                selected = $scope.resources[index];
                break;
              }
            }
            if (selected) {
              var fields = getInputFields(selected.components);
              for (var i in fields) {
                var field = fields[i];
                var title = field.label || field.key;
                $scope.resourceFields.push({
                  property: 'data.' + field.key,
                  title: title
                });
              }
              if (!$scope.component.valueProperty && $scope.resourceFields.length) {
                $scope.component.valueProperty = $scope.resourceFields[0].property;
              }
            }
          };

          $scope.$watch('component.dataSrc', function(source, prevSource) {
            if (source !== prevSource) {
              $scope.component.template = '{{ item.label }}';
            }

            if (($scope.resources.length === 0) && (source === 'resource')) {
              $scope.formio.loadForms({params: {
                type: 'resource',
                limit: 4294967295,
                select: '_id,title,components'
              }}).then(function(resources) {
                $scope.resources = resources;
                loadFields();
              });
            }
          });

          // Trigger when the resource changes.
          $scope.$watch('component.data.resource', function(resourceId) {
            if (!resourceId) {
              return;
            }
            loadFields();
          });

          // Update other parameters when the value property changes.
          $scope.currentValueProperty = $scope.component.valueProperty;
          $scope.$watch('component.valueProperty', function(property) {
            if ($scope.component.dataSrc === 'resource' && $scope.currentValueProperty !== property) {
              if (!property) {
                $scope.component.searchField = '';
                $scope.component.template = '{{ item.data }}';
              }
              else {
                $scope.component.searchField = property + '__regex';
                $scope.component.template = '{{ item.' + property + ' }}';
              }
            }
          });

          loadFields();
        }],
        documentation: 'http://help.form.io/userguide/#select'
      });
    }
  ]);
  app.run([
    '$templateCache',
    function($templateCache) {
      // Create the settings markup.
      $templateCache.put('formio/components/select/display.html',
        '<ng-form>' +
          '<form-builder-option property="label"></form-builder-option>' +
          '<form-builder-option property="hideLabel"></form-builder-option>' +
          '<form-builder-option-label-position></form-builder-option-label-position>' +
          '<form-builder-option property="placeholder"></form-builder-option>' +
          '<form-builder-option property="description"></form-builder-option>' +
          '<form-builder-option property="tooltip"></form-builder-option>' +
          '<form-builder-option property="errorLabel"></form-builder-option>' +
          '<form-builder-option property="customClass"></form-builder-option>' +
          '<form-builder-option property="tabindex"></form-builder-option>' +
          '<form-builder-option property="multiple"></form-builder-option>' +
          '<form-builder-option property="clearOnHide"></form-builder-option>' +
          '<form-builder-option property="protected"></form-builder-option>' +
          '<form-builder-option property="persistent"></form-builder-option>' +
          '<form-builder-option property="autofocus" type="checkbox" label="Initial Focus" tooltip="Make this field the initially focused element on this form."></form-builder-option>' +
          '<form-builder-option property="encrypted" class="form-builder-premium"></form-builder-option>' +
          '<form-builder-option property="hidden"></form-builder-option>' +
          '<form-builder-option property="disabled"></form-builder-option>' +
          '<form-builder-option property="dataGridLabel"></form-builder-option>' +
          '<form-builder-option property="tableView"></form-builder-option>' +
        '</ng-form>'
      );

      $templateCache.put('formio/components/select/data.html',
        '<ng-form>' +
          '
' +
            '{{\'Data Source Type\' | formioTranslate}}' +
            '<select class="form-control" id="dataSrc" name="dataSrc" ng-model="component.dataSrc" ng-options="value as label | formioTranslate for (value, label) in dataSources"></select>' +
          '
' +
          '<ng-switch on="component.dataSrc">' +
            '
' +
              '{{\'Data Source Raw JSON\' | formioTranslate}}' +
              '<textarea class="form-control" id="data.json" name="data.json" ng-model="component.data.json" placeholder="Raw JSON Array" json-input rows="3">{{ component.data.json }}</textarea>' +
            '
' +
            '
' +
            '  <form-builder-option property="data.url" label="Data Source URL" placeholder="Data Source URL" title="A URL that returns a JSON array to use as the data source."></form-builder-option>' +
            '
' +
            '<value-builder ng-switch-when="url" data=component.data.headers label="Request Headers" tooltip-text="Set any headers that should be sent along with the request to the url. This is useful for authentication." label-label="Key" label-property="key"></value-builder>' +
            '<value-builder ng-switch-when="values" data="component.data.values" label="Data Source Values" tooltip-text="Values to use as the data source. Labels are shown in the select field. Values are the corresponding values saved with the submission."></value-builder>' +
            '
' +
            '{{\'Resource\' | formioTranslate}}' +
            '<ui-select ui-select-required ui-select-open-on-focus ng-model="component.data.resource" theme="bootstrap">' +
              '<ui-select-match class="ui-select-match" placeholder="">' +
                '{{$select.selected.title}}' +
              '</ui-select-match>' +
              '<ui-select-choices class="ui-select-choices" repeat="value._id as value in resources | filter: $select.search" refresh="refreshSubmissions($select.search)" refresh-delay="250">' +
                '

' +
              '</ui-select-choices>' +
            '</ui-select>' +
          '
' +
          '</ng-switch>' +
          '<form-builder-option ng-hide="component.dataSrc !== \'url\'" property="selectValues" label="Data Path" type="text" placeholder="The object path to the iterable items." title="The property within the source data, where iterable items reside. For example: results.items or results[0].items"></form-builder-option>' +
          '<form-builder-option ng-hide="component.dataSrc == \'values\' || component.dataSrc == \'resource\' || component.dataSrc == \'custom\'" property="valueProperty" label="Value Property" placeholder="The selected item\'s property to save." title="The property of each item in the data source to use as the select value. If not specified, the item itself will be used."></form-builder-option>' +
          '
' +
            '{{\'Value\' | formioTranslate}}' +
            '<select class="form-control" id="valueProperty" name="valueProperty" ng-options="value.property as value.title for value in resourceFields" ng-model="component.valueProperty"></select>' +
          '
' +
          '
' +
          '  {{\'Select Fields\' | formioTranslate}}' +
          '  <input type="text" class="form-control" id="selectFields" name="selectFields" ng-model="component.selectFields" placeholder="Comma separated list of fields to select." value="{{ component.selectFields }}">' +
          '
' +
          '
' +
          '<input type="checkbox" ng-model="component.data.disableLimit" name="disableLimit"></input>' +
          '  {{\'Disable limiting response\' | formioTranslate}}' +
          '
' +
          '<form-builder-option ng-show="component.dataSrc == \'url\' || component.dataSrc == \'resource\'" property="searchField" label="Search Query Name" placeholder="Name of URL query parameter" title="The name of the search querystring parameter used when sending a request to filter results with. The server at the URL must handle this query parameter."></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'url\' || component.dataSrc == \'resource\'" property="filter" label="Filter Query" placeholder="The filter query for results." title="Use this to provide additional filtering using query parameters."></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'url\' || component.dataSrc == \'resource\' || component.dataSrc == \'json\'" property="limit" label="Limit" placeholder="Maximum number of items to view per page of results." title="Use this to limit the number of items to request or view."></form-builder-option>' +
          '
' +
          '  {{\'Search Filter\' | formioTranslate}}' +
          '  <select class="form-control" id="filter" name="filter" ng-model="component.filter" ng-options="value as label | formioTranslate for (value, label) in {none: \'No Search\', contains: \'Contains\', startsWith: \'Starts With\'}"></select>' +
          '
' +
          '
' +
          '  {{\'Custom Values\' | formioTranslate}}' +
          '  <formio-script-editor rows="10" id="custom" name="custom" ng-model="component.data.custom" placeholder="/*** Example Code ***/\nvalues = data[\'mykey\'];"></formio-script-editor>' +
          '
' +
          '
' +
            '{{\'Item Template\' | formioTranslate}}' +
            '<textarea class="form-control" id="template" name="template" ng-model="component.template" rows="3">{{ component.template }}</textarea>' +
          '
' +
          '
' +
          '  {{\'Refresh On\' | formioTranslate}}' +
          '  <select class="form-control" id="refreshOn" name="refreshOn" ng-options="field.key as field.label for field in formFields" ng-model="component.refreshOn"></select>' +
          '
' +
          '<form-builder-option ng-show="component.dataSrc == \'resource\' || component.dataSrc == \'url\' || component.dataSrc == \'custom\'" property="clearOnRefresh"></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'resource\'" property="reference"></form-builder-option>' +
          '<form-builder-option ng-show="component.dataSrc == \'url\'" property="authenticate"></form-builder-option>' +
          '<form-builder-option property="defaultValue"></form-builder-option>' +
          '<form-builder-option property="dbIndex" class="form-builder-premium form-builder-dbindex"></form-builder-option>' +
        '</ng-form>'
      );

      // Create the API markup.
      $templateCache.put('formio/components/select/validate.html',
        '<ng-form>' +
          '<form-builder-option property="validate.required"></form-builder-option>' +
          '<form-builder-option ng-show="!!component.searchField" property="validate.select"></form-builder-option>' +
          '<form-builder-option property="unique"></form-builder-option>' +
          '<form-builder-option property="validate.customMessage"></form-builder-option>' +
          '<form-builder-option-custom-validation></form-builder-option-custom-validation>' +
        '</ng-form>'
      );
    }
  ]);
};
<|endoftext|>