diff --git a/lib/components/form/checkbox-selector.js b/lib/components/form/checkbox-selector.js deleted file mode 100644 index daabe0a6f..000000000 --- a/lib/components/form/checkbox-selector.js +++ /dev/null @@ -1,50 +0,0 @@ -import React, {Component} from 'react' -import PropTypes from 'prop-types' -import { Form, FormGroup, Row, Col, Checkbox } from 'react-bootstrap' -import { connect } from 'react-redux' - -import { setQueryParam } from '../../actions/form' - -class CheckboxSelector extends Component { - static propTypes = { - name: PropTypes.string, - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.bool - ]), - label: PropTypes.string, - setQueryParam: PropTypes.func - } - - _onQueryParamChange = (evt) => { - this.props.setQueryParam({ [this.props.name]: evt.target.checked }) - } - - render () { - const { label } = this.props - let value = this.props.value - if (typeof value === 'string') value = (value === 'true') - - return ( -
- - -
- - {label} - -
- -
-
- ) - } -} - -const mapStateToProps = (state, ownProps) => { - return { } -} - -const mapDispatchToProps = { setQueryParam } - -export default connect(mapStateToProps, mapDispatchToProps)(CheckboxSelector) diff --git a/lib/components/form/connected-settings-selector-panel.js b/lib/components/form/connected-settings-selector-panel.js new file mode 100644 index 000000000..ffa4122e1 --- /dev/null +++ b/lib/components/form/connected-settings-selector-panel.js @@ -0,0 +1,61 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' + +import { setQueryParam } from '../../actions/form' +import { getShowUserSettings } from '../../util/state' + +import { StyledSettingsSelectorPanel } from './styled' +import UserTripSettings from './user-trip-settings' + +// TODO: Button title should be bold when button is selected. + +class ConnectedSettingsSelectorPanel extends Component { + static propTypes = { + icons: PropTypes.object + } + + render () { + const { + config, + icons, + query, + setQueryParam, + showUserSettings + } = this.props + + return ( +
+
+ {showUserSettings && } + + +
+
+ ) + } +} + +// connect to redux store + +const mapStateToProps = (state, ownProps) => { + const { config, currentQuery } = state.otp + return { + query: currentQuery, + config, + showUserSettings: getShowUserSettings(state.otp) + } +} + +const mapDispatchToProps = { + setQueryParam +} + +export default connect(mapStateToProps, mapDispatchToProps)(ConnectedSettingsSelectorPanel) diff --git a/lib/components/form/date-time-modal.js b/lib/components/form/date-time-modal.js index 70b22e611..6b54176c4 100644 --- a/lib/components/form/date-time-modal.js +++ b/lib/components/form/date-time-modal.js @@ -2,90 +2,11 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import styled from 'styled-components' import { getTimeFormat, getDateFormat } from '@opentripplanner/core-utils/lib/time' -import { DateTimeSelector } from '@opentripplanner/trip-form' -import * as TripFormClasses from '@opentripplanner/trip-form/lib/styled' import { setQueryParam } from '../../actions/form' -// Styles for the DateTimeSelector. -// TODO: Find a way to bring OTP CSS classes in here. -// See for instance: -// https://github.com/theKashey/styled-components-mixins -// https://github.com/kingpowerclick/styled-bootstrap-mixins - -const StyledDateTimeSelector = styled(DateTimeSelector)` - margin: 0 -15px 20px; - ${TripFormClasses.DateTimeSelector.DateTimeRow} { - margin-top: 20px; - } - - input { - -webkit-appearance: textfield; - -moz-appearance: textfield; - appearance: textfield; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: none; - color: #555; - font-family: inherit; - font-size: 16px; - height: 34px; - padding: 6px 12px; - text-align: center; - &.focused { - outline: 0; - border-color: #66afe9; - font-weight: 400; - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - } - } - - button { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - background-color: #fff; - border: 1px solid #ccc; - border-radius: 4px; - color: #333; - cursor: pointer; - font-family: inherit; - font-weight: 400; - font-size: 14px; - line-height: 1.42857143; - outline-offset:-2px; - padding: 6px 12px; - text-align: center; - touch-action: manipulation; - user-select: none; - white-space: nowrap; - - &.active { - background-color: #e6e6e6; - border-color: #adadad; - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - font-weight: 400; - } - &:hover { - background-color: #e6e6e6; - border-color: #adadad; - } - &.active { - background-color: #e6e6e6; - border-color: #adadad; - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - font-weight: 400; - &:hover { - background-color: #d4d4d4; - border-color: #8c8c8c; - } - } - } -` +import { StyledDateTimeSelector } from './styled' class DateTimeModal extends Component { static propTypes = { diff --git a/lib/components/form/dropdown-selector.js b/lib/components/form/dropdown-selector.js deleted file mode 100644 index 3afeeced6..000000000 --- a/lib/components/form/dropdown-selector.js +++ /dev/null @@ -1,60 +0,0 @@ -import React, {Component} from 'react' -import PropTypes from 'prop-types' -import { Form, FormGroup, FormControl, Row, Col } from 'react-bootstrap' -import { connect } from 'react-redux' - -import { setQueryParam } from '../../actions/form' - -class DropdownSelector extends Component { - static propTypes = { - name: PropTypes.string, - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]), - label: PropTypes.string, - options: PropTypes.array, - setQueryParam: PropTypes.func - } - - _onQueryParamChange = (evt) => { - const val = evt.target.value - this.props.setQueryParam({ - [this.props.name]: isNaN(val) ? val : parseFloat(val) - }) - } - - render () { - const { value, label, options } = this.props - - return ( - - {label} - -
- - - {options.map((o, i) => ( - - ))} - - -
- -
- ) - } -} - -const mapStateToProps = (state, ownProps) => { - return { } -} - -const mapDispatchToProps = { setQueryParam } - -export default connect(mapStateToProps, mapDispatchToProps)(DropdownSelector) diff --git a/lib/components/form/general-settings-panel.js b/lib/components/form/general-settings-panel.js deleted file mode 100644 index bb8fe5a27..000000000 --- a/lib/components/form/general-settings-panel.js +++ /dev/null @@ -1,73 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -import CheckboxSelector from './checkbox-selector' -import DropdownSelector from './dropdown-selector' -import queryParams from '../../util/query-params' -import { defaultParams, getQueryParamProperty } from '../../util/query' - -class GeneralSettingsPanel extends Component { - static propTypes = { - query: PropTypes.object, - paramNames: PropTypes.array - } - - static defaultProps = { - // The universe of properties to include in this form: - // TODO: allow override in config - paramNames: defaultParams - } - - render () { - const { paramNames, query, config } = this.props - return ( -
- {paramNames.map(param => { - const paramInfo = queryParams.find(qp => qp.name === param) - // Check that the parameter applies to the specified routingType - if (!paramInfo.routingTypes.includes(query.routingType)) return - - // Check that the applicability test (if provided) is satisfied - if (typeof paramInfo.applicable === 'function' && - !paramInfo.applicable(query, config)) return - - // Create the UI component based on the selector type - switch (paramInfo.selector) { - case 'DROPDOWN': - return - case 'CHECKBOX': - return - } - })} -
- ) - } -} - -// connect to redux store - -const mapStateToProps = (state, ownProps) => { - return { - config: state.otp.config, - query: state.otp.currentQuery - } -} - -const mapDispatchToProps = (dispatch, ownProps) => { - return { - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(GeneralSettingsPanel) diff --git a/lib/components/form/mode-button.js b/lib/components/form/mode-button.js deleted file mode 100644 index 58771ec20..000000000 --- a/lib/components/form/mode-button.js +++ /dev/null @@ -1,153 +0,0 @@ -import React, {Component, PureComponent} from 'react' -import PropTypes from 'prop-types' - -import { getIcon, isTransit } from '../../util/itinerary' - -export default class ModeButton extends Component { - static propTypes = { - active: PropTypes.bool, - label: PropTypes.string, - mode: PropTypes.any, // currently a mode object or string - icons: PropTypes.object, - onClick: PropTypes.func - } - - _getButtonStyle ({ - active, - enabled, - height, - modeStr - }) { - const buttonStyle = { height } - - if (modeStr !== 'TRANSIT' && isTransit(modeStr)) { - buttonStyle.width = height - buttonStyle.border = `2px solid ${enabled ? (active ? '#000' : '#bbb') : '#ddd'}` - if (active && enabled) buttonStyle.backgroundColor = '#fff' - buttonStyle.borderRadius = height / 2 - } else { - buttonStyle.border = active ? '2px solid #000' : '1px solid #bbb' - if (active) buttonStyle.backgroundColor = '#add8e6' - } - - return buttonStyle - } - - render () { - const { - active, - enabled, - icons, - label, - mode, - onClick, - inlineLabel, - showPlusTransit - } = this.props - const height = this.props.height || 48 - const iconSize = height - 20 - const iconColor = enabled ? '#000' : '#ccc' - const modeStr = mode.company || mode.mode || mode - const buttonStyle = this._getButtonStyle({ active, enabled, height, modeStr }) - - return ( -
- - - {/* If not in inline-label mode, label directly below the button */} - {!inlineLabel && ( -
- {label} -
- )} -
- ) - } -} - -class PlusTransit extends PureComponent { - render () { - const {enabled, iconColor, icons, iconSize} = this.props - return ( - -
- {enabled - ? getIcon('TRANSIT', icons) - : ( -
- ) - } -
- - - ) - } -} diff --git a/lib/components/form/mode-selector.js b/lib/components/form/mode-selector.js deleted file mode 100644 index 5f8abe3a9..000000000 --- a/lib/components/form/mode-selector.js +++ /dev/null @@ -1,70 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { FormGroup, ControlLabel, FormControl } from 'react-bootstrap' -import { connect } from 'react-redux' - -import { setQueryParam } from '../../actions/form' - -class ModeSelector extends Component { - static propTypes = { - config: PropTypes.object, - label: PropTypes.string, - mode: PropTypes.string, - setQueryParam: PropTypes.func, - showLabel: PropTypes.bool - } - - static defaultProps = { - label: 'Mode', - showLabel: true - } - - _onChange = (evt) => this.props.setQueryParam({ mode: evt.target.value }) - - _getDisplayText (mode) { - switch (mode) { - case 'TRANSIT,WALK': return 'Walk to Transit' - case 'TRANSIT,BICYCLE': return 'Bike to Transit' - case 'WALK': return 'Walk Only' - case 'BICYCLE': return 'Bike Only' - } - return mode - } - - render () { - const { config, mode, label, showLabel } = this.props - - return ( -
- - {showLabel - ? {label} - : null - } - - {config.modes.map((m, i) => ( - - ))} - - -
- ) - } -} - -const mapStateToProps = (state, ownProps) => { - return { - config: state.otp.config, - mode: state.otp.currentQuery.mode - } -} - -const mapDispatchToProps = { - setQueryParam -} - -export default connect(mapStateToProps, mapDispatchToProps)(ModeSelector) diff --git a/lib/components/form/modes-panel.js b/lib/components/form/modes-panel.js deleted file mode 100644 index bbfb4dd5b..000000000 --- a/lib/components/form/modes-panel.js +++ /dev/null @@ -1,152 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -import { setQueryParam } from '../../actions/form' -import ModeButton from './mode-button' -import { isAccessMode } from '../../util/itinerary' - -class ModesPanel extends Component { - static propTypes = { - icons: PropTypes.object, - modeGroups: PropTypes.array, - queryModes: PropTypes.array, - setQueryParam: PropTypes.func - } - - _getVisibleModes (group) { - // Don't show the CAR_HAIL services in profile modes - // TODO: this could be handled more elegantly? - return group.modes.filter(mode => - mode.mode !== 'CAR_HAIL' || this.props.routingType !== 'PROFILE' - ) - } - - // Returns whether a particular mode or TNC agency is active - _modeIsActive (mode) { - const { companies, queryModes } = this.props - if (mode.mode === 'CAR_HAIL') { - return Boolean(companies && companies.includes(mode.label.toUpperCase())) - } else { - return queryModes.includes(mode.mode || mode) - } - } - - _setGroupSelected (group, isSelected) { - let queryModes = this.props.queryModes.slice(0) // Clone the modes array - - this._getVisibleModes(group).forEach(mode => { - const modeStr = mode.mode || mode - queryModes = queryModes.filter(m => m !== modeStr) - if (isSelected) queryModes.push(modeStr) - }) - - // Update the mode array in the store - this.props.setQueryParam({ mode: queryModes.join(',') }) - } - - _toggleMode (mode) { - const modeStr = mode.mode || mode - - const { routingType, setQueryParam } = this.props - let queryModes = this.props.queryModes.slice(0) // Clone the modes array - - const queryParamUpdate = {} - - // Special case: we are in ITINERARY mode and changing the one access mode - if (routingType === 'ITINERARY' && isAccessMode(modeStr)) { - queryModes = queryModes.filter(m => !isAccessMode(m)) - queryModes.push(modeStr) - - // do extra stuff if mode selected was a TNC - queryParamUpdate.companies = modeStr === 'CAR_HAIL' ? mode.label.toUpperCase() : null - - // Otherwise, if mode is currently selected, deselect it - } else if (queryModes.includes(modeStr)) { - queryModes = queryModes.filter(m => m !== modeStr) - - // Or, if mode is currently not selected, select it - } else if (!queryModes.includes(modeStr)) { - queryModes.push(modeStr) - } - - queryParamUpdate.mode = queryModes.join(',') - - // Update the mode array in the store - setQueryParam(queryParamUpdate) - } - - render () { - const { icons, modeGroups, routingType } = this.props - - return ( -
- {modeGroups.map((group, k) => { - const groupModes = this._getVisibleModes(group) - // Determine whether to show Select/Deselect All actions - const accessCount = groupModes.filter(m => isAccessMode(m.mode || m)).length - const showGroupSelect = - (routingType === 'PROFILE' || - (routingType === 'ITINERARY' && accessCount === 0)) && - groupModes.length > 1 - - return ( -
-
- {showGroupSelect && ( -
- {' '}|{' '} - -
- )} -
{group.name}
-
-
- {groupModes.map(mode => { - return this._toggleMode(mode)} - /> - })} -
-
- ) - })} -
- ) - } -} - -// Make a mode string more readable (e.g. 'BICYCLE_RENT' -> 'Bicycle Rent') -function readableModeString (mode) { - const str = mode.replace('_', ' ') - return str.replace(/\w\S*/g, txt => { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() }) -} - -// connect to redux store - -const mapStateToProps = (state, ownProps) => { - const { companies, mode, routingType } = state.otp.currentQuery - return { - companies, - modeGroups: state.otp.config.modeGroups, - queryModes: !mode || mode.length === 0 ? [] : mode.split(','), - routingType - } -} - -const mapDispatchToProps = { setQueryParam } - -export default connect(mapStateToProps, mapDispatchToProps)(ModesPanel) diff --git a/lib/components/form/settings-selector-panel.js b/lib/components/form/settings-selector-panel.js deleted file mode 100644 index 9718fcd45..000000000 --- a/lib/components/form/settings-selector-panel.js +++ /dev/null @@ -1,588 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { Row, Col, Button } from 'react-bootstrap' -import { connect } from 'react-redux' - -import { - clearDefaultSettings, - resetForm, - setQueryParam, - storeDefaultSettings -} from '../../actions/form' -import GeneralSettingsPanel from './general-settings-panel' -import Icon from '../narrative/icon' -import ModeButton from './mode-button' -import { - getIcon, - isAccessMode, - hasBike, - isTransit, - hasMicromobility, - hasHail, - hasRental, - hasTransit, - getTransitModes -} from '../../util/itinerary' -import { getTripOptionsFromQuery, isNotDefaultQuery } from '../../util/query' -import { getShowUserSettings } from '../../util/state' - -class SettingsSelectorPanel extends Component { - static propTypes = { - icons: PropTypes.object - } - - constructor (props) { - super(props) - this.state = { activePanel: 'MODES' } - } - - // returns whether a micromobility company is selected or not - _companyIsActive (company) { - const {companies} = this.props - return companies && companies.indexOf(company.id) > -1 - } - - // Returns whether a particular mode or TNC agency is active - _modeIsActive (mode) { - const { companies, queryModes } = this.props - if (mode.mode === 'CAR_HAIL' || mode.mode === 'CAR_RENT') { - return Boolean(companies && mode.company && companies.includes(mode.company.toUpperCase())) - } - - for (const m of queryModes) { - if (m === mode.mode) return true - } - // All transit modes are selected - // if (isTransit(mode.mode) && queryModes.indexOf('TRANSIT') !== -1) return true - return false - } - - _setSoloMode (mode) { - // save current access/transit modes - if (hasTransit(this.props.mode)) this._lastTransitMode = this.props.mode - this.props.setQueryParam({ mode }) - } - - _setWalkOnly = () => { this._setSoloMode('WALK') } - - _setBikeOnly = () => { this._setSoloMode('BICYCLE') } - - _setMicromobilityOnly = () => { this._setSoloMode('MICROMOBILITY') } - - /** - * Replace own mode with new mode. The only mode will have already been set, - * so this toggles whether the own mode includes a rental. - */ - _replaceOwnMode = (newMode, referenceOwnMode) => { - const { queryModes, setQueryParam } = this.props - const nonOwnModes = queryModes.filter(m => !m.startsWith(referenceOwnMode)) - setQueryParam({ mode: [...nonOwnModes, newMode].join(',') }) - } - - _setOwnBike = () => this._replaceOwnMode('BICYCLE', 'BICYCLE') - - _setRentedBike = () => this._replaceOwnMode('BICYCLE_RENT', 'BICYCLE') - - _setOwnMicromobility = () => this._replaceOwnMode('MICROMOBILITY', 'MICROMOBILITY') - - _setRentedMicromobility = () => { - this._replaceOwnMode('MICROMOBILITY_RENT', 'MICROMOBILITY') - this.props.setQueryParam({ companies: this._getCompaniesForMode('MICROMOBILITY_RENT') }) - } - - _getCompaniesForMode = (modeStr) => { - const {config} = this.props - return config.companies - .filter(co => co.modes.indexOf(modeStr) > -1) - .map(co => co.id) - .join(',') - } - - _toggleCompany (company) { - const {companies, setQueryParam} = this.props - - // set company if no companies set yet - if (!companies) { - setQueryParam({ companies: company }) - return - } - - // add or remove from existing companies - if (companies.indexOf(company) > -1) { - // company already present in query, remove - setQueryParam({ - companies: companies - .split(',') - .filter(co => co !== company) - .join(',') - }) - } else { - // company not yet present, add to string list - setQueryParam({ companies: `${companies},${company}` }) - } - } - - _toggleTransitMode (mode) { - const {queryModes, setQueryParam} = this.props - const modeStr = mode.mode || mode - let newQueryModes = queryModes.slice(0) // Clone the modes array - - // do not allow the last transit mode to be deselected - const transitModes = newQueryModes.filter(m => isTransit(m)) - if (transitModes.length === 1 && transitModes[0] === modeStr) return - - // If mode is currently selected, deselect it - if (newQueryModes.includes(modeStr)) { - newQueryModes = newQueryModes.filter(m => m !== modeStr) - // Or, if mode is currently not selected, select it - } else if (!newQueryModes.includes(modeStr)) { - newQueryModes.push(modeStr) - } - setQueryParam({ mode: newQueryModes.join(',') }) - } - - _toggleStoredSettings = () => { - const options = getTripOptionsFromQuery(this.props.query) - // If user defaults are set, clear them. Otherwise, store them. - if (this.props.defaults) this.props.clearDefaultSettings() - else this.props.storeDefaultSettings(options) - } - - _resetForm = () => this.props.resetForm() - - _setAccessMode = (mode) => { - const {config, queryModes} = this.props - let newQueryModes = queryModes.slice(0) // Clone the modes array - const modeStr = mode.mode || mode - - // Create object to contain multiple parameter updates if needed (i.e. 'mode', 'compainies') - const queryParamUpdate = {} - - if (this._lastTransitMode) { - // Restore previous transit selection, if present - newQueryModes = this._lastTransitMode.split(',').filter(m => !isAccessMode(m)) - this._lastTransitMode = null - } else { - // Otherwise, retain any currently selected transit modes - newQueryModes = newQueryModes.filter(m => !isAccessMode(m)) - } - - // If no transit modes selected, select all - if (!newQueryModes || newQueryModes.length === 0) { - newQueryModes = getTransitModes(config) - } - - // Add the access mode - newQueryModes.push(modeStr) - - // apply needed companies to query - queryParamUpdate.companies = mode.company - // mode is associated with a specific company - ? mode.company.toUpperCase() - // mode is either a rental or hailing mode, but not associated with - // a specific company - : (hasRental(modeStr) || hasHail(modeStr)) - // when switching, add all companies at first - ? this._getCompaniesForMode(modeStr) - // mode is not renting or hailing and not associated with any company - : null - - queryParamUpdate.mode = newQueryModes.join(',') - - this.props.setQueryParam(queryParamUpdate) - } - - _renderCompanies = () => { - const {companies: queryCompanies, config, icons, mode} = this.props - const {companies: configCompanies, modes} = config - const {accessModes} = modes - - // check if a single company has an exclusive button - if (queryCompanies && accessModes.some( - accessMode => accessMode.company === queryCompanies.toUpperCase()) - ) { - // a match has been found for an access mode that exclusively belongs to - // a particular company - return null - } - - // hack for TriMet-MOD project, don't show companies if Biketown enabled - // when using just bike rentals - if (mode && mode.indexOf('BICYCLE_RENT') > -1) { - return null - } - - // check if renting or hailing - if (hasRental(mode) || hasHail(mode)) { - const queryModes = mode.split(',') - const activeCompanies = configCompanies - .filter(company => - company.modes - .split(',') - .some(companyMode => queryModes.indexOf(companyMode) > -1) - ) - - return ( -
-
Use Companies
-
- {activeCompanies.length === 0 && -

No comapnies available for this mode!

- } - {activeCompanies.map((company) => { - let classNames = ['select-button'] - if (this._companyIsActive(company)) classNames.push('active') - return - })} -
-
-
- ) - } - } - - _renderExclusiveAccessSelectors = () => { - const {config, mode, icons} = this.props - const {exclusiveModes} = config.modes - const modeHasTransit = hasTransit(mode) - // Use int for array element keys - let key = 0 - if (!exclusiveModes) return null - - // create an array of children to display within a mode-group-row - // at most 2 exclusive modes will be displayed side-by-side - const children = [] - const spacer = () => ( -   - ) - - exclusiveModes.forEach((exclusiveMode, idx) => { - // add left padding for every evenly indexed exclusiveMode - if (idx % 2 === 0) { - children.push(spacer()) - } - - switch (exclusiveMode) { - case 'WALK': - children.push( - - - - ) - break - case 'BICYCLE': - children.push( - - - - ) - break - case 'MICROMOBILITY': - children.push( - - - - ) - break - default: - throw new Error(`Unsupported exclusive mode: ${exclusiveMode}`) - } - - // add right padding for every odd indexed exclusiveMode - if (idx % 2 !== 0) { - children.push(spacer()) - } - }) - - return ( - - {children} - - ) - } - - render () { - const { - config, - defaults, - mode, - icons, - query, - queryModes, - showUserSettings - } = this.props - const modeHasTransit = hasTransit(mode) - const { transitModes, accessModes, bicycleModes, micromobilityModes } = config.modes - - // Do not permit remembering trip options if they do not differ from the - // defaults and nothing has been stored - const queryIsDefault = !isNotDefaultQuery(query, config) - const rememberIsDisabled = queryIsDefault && !defaults - - return ( -
-
- {showUserSettings && -
- - -
- } - {/* Take Transit button */} - - - this._setAccessMode('WALK')} - /> - - - - {/* transit access mode selector */} - - {accessModes.map((mode, k) => { - return - this._setAccessMode(mode)} - /> - - })} - - - {this._renderExclusiveAccessSelectors()} - - {/* Transit mode selector */} - {/* - -
-
Filter Transit Modes
-
- - - {transitModes.map((mode, k) => { - return (
- this._toggleTransitMode(mode)} - /> -
) - })} - -
*/} - -
- - {/* Travel Preferences */} - - -
Travel Preferences
- - {/* The bike trip type selector */} - {hasBike(mode) && !hasTransit(mode) && ( -
-
Use
-
- {bicycleModes.map((option, k) => { - let action = this._setOwnBike - if (option.mode === 'BICYCLE_RENT') action = this._setRentedBike - let classNames = ['select-button'] - if (queryModes.includes(option.mode)) classNames.push('active') - // TODO: Handle different bikeshare networks - return ( - - ) - })} -
-
- )} - - {/* The micromobility trip type selector */} - {hasMicromobility(mode) && !hasTransit(mode) && ( -
-
Use
-
- {micromobilityModes.map((option, k) => { - let action = this._setOwnMicromobility - if (option.mode === 'MICROMOBILITY_RENT') action = this._setRentedMicromobility - let classNames = ['select-button'] - if (queryModes.includes(option.mode)) classNames.push('active') - // TODO: Handle different bikeshare networks - return ( - - ) - })} -
-
- )} - - {this._renderCompanies()} - - {/* The transit mode selected */} - {hasTransit(mode) && (
-
Use
-
- {transitModes.map((mode, k) => { - let classNames = ['select-button'] - if (this._modeIsActive(mode)) classNames.push('active') - return - })} -
-
-
)} - - {/* Other general settings */} - - - -
- ) - } -} - -// connect to redux store - -const mapStateToProps = (state, ownProps) => { - const { config, currentQuery, user } = state.otp - const { defaults } = user - const showUserSettings = getShowUserSettings(state.otp) - const { companies, mode, routingType } = currentQuery - return { - defaults, - query: currentQuery, - config, - mode, - companies, - modeGroups: config.modeGroups, - queryModes: !mode || mode.length === 0 ? [] : mode.split(','), - routingType, - showUserSettings - } -} - -const mapDispatchToProps = { - clearDefaultSettings, - resetForm, - setQueryParam, - storeDefaultSettings -} - -export default connect(mapStateToProps, mapDispatchToProps)(SettingsSelectorPanel) diff --git a/lib/components/form/styled.js b/lib/components/form/styled.js new file mode 100644 index 000000000..0ff6705a9 --- /dev/null +++ b/lib/components/form/styled.js @@ -0,0 +1,184 @@ +import styled, { css } from 'styled-components' +import { DateTimeSelector, SettingsSelectorPanel } from '@opentripplanner/trip-form' +import * as TripFormClasses from '@opentripplanner/trip-form/lib/styled' + +const commonButtonCss = css` + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + background: none; + font-family: inherit; + user-select: none; + text-align: center; + touch-action: manipulation; +` + +const commonInputCss = css` + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + color: #555; + font-family: inherit; + padding: 6px 12px; + transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; + + &:focus { + border-color: #66afe9; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102,175,233,.6); + outline: 0; + } +` + +const modeButtonButtonCss = css` + ${TripFormClasses.ModeButton.Button} { + ${commonButtonCss} + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; + color: #333; + font-weight: 400; + font-size: 14px; + line-height: 1.42857143; + outline-offset:-2px; + padding: 6px 12px; + &.active { + background-color: #e6e6e6; + border-color: #adadad; + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + font-weight: 400; + } + &:hover { + background-color: #e6e6e6; + border-color: #adadad; + } + &.active { + background-color: #e6e6e6; + border-color: #adadad; + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + font-weight: 400; + &:hover { + background-color: #d4d4d4; + border-color: #8c8c8c; + } + } + } +` + +export const StyledSettingsSelectorPanel = styled(SettingsSelectorPanel)` + ${TripFormClasses.SettingLabel} { + font-weight: 400; + margin-bottom: 0; + } + ${TripFormClasses.SettingsHeader} { + font-size: 18px; + margin: 16px 0px; + } + ${TripFormClasses.SettingsSection} { + margin-bottom: 16px; + } + ${TripFormClasses.DropdownSelector} { + margin-bottom:20px; + select { + ${commonInputCss} + font-size: 14px; + height: 34px; + line-height: 1.42857143; + } + } + + ${TripFormClasses.ModeSelector} { + ${TripFormClasses.ModeButton.Button} { + ${commonButtonCss} + border: 1px solid rgb(187, 187, 187); + border-radius: 3px; + box-shadow: none; + outline: 0; + padding: 3px; + &.active { + background-color: rgb(173, 216, 230); + border: 2px solid rgb(0, 0, 0); + } + } + ${TripFormClasses.ModeButton.Title} { + font-size: 10px; + font-weight: 300; + line-height: 12px; + padding: 4px 0px 0px; + &.active { + font-weight: 600; + } + } + } + ${TripFormClasses.ModeSelector.MainRow} { + margin: 0 -10px 18px; + padding: 0 5px; + + ${TripFormClasses.ModeButton.Button} { + font-size: 200%; + font-weight: 300; + height: 54px; + &.active { + font-weight: 600; + } + } + } + ${TripFormClasses.ModeSelector.SecondaryRow} { + margin: 0 -10px 10px; + ${TripFormClasses.ModeButton.Button} { + font-size: 150%; + font-weight: 600; + height: 46px; + } + } + ${TripFormClasses.ModeSelector.TertiaryRow} { + margin: 0 -10px 10px; + ${TripFormClasses.ModeButton.Button} { + font-size: 90%; + height: 36px; + } + } + + ${TripFormClasses.SubmodeSelector.Row} { + > * { + padding: 3px 5px 3px 0px; + } + > :last-child { + padding-right: 0px; + } + ${TripFormClasses.ModeButton.Button} { + padding: 6px 12px; + } + svg, + img { + margin-left: 0px; + } + } + ${TripFormClasses.SubmodeSelector.InlineRow} { + margin: -3px 0px; + } + + ${TripFormClasses.SubmodeSelector} { + ${modeButtonButtonCss} + } +` + +export const StyledDateTimeSelector = styled(DateTimeSelector)` + margin: 0 -15px 20px; + ${TripFormClasses.DateTimeSelector.DateTimeRow} { + margin-top: 20px; + } + + input { + ${commonInputCss} + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; + box-shadow: none; + font-size: 16px; + height: 34px; + text-align: center; /* For legacy browsers. */ + } + + ${modeButtonButtonCss} +` diff --git a/lib/components/form/tabbed-form-panel.js b/lib/components/form/tabbed-form-panel.js index c3fededce..c41c5650d 100644 --- a/lib/components/form/tabbed-form-panel.js +++ b/lib/components/form/tabbed-form-panel.js @@ -6,7 +6,7 @@ import { connect } from 'react-redux' import DateTimePreview from './date-time-preview' import SettingsPreview from './settings-preview' import DateTimeModal from './date-time-modal' -import SettingsSelectorPanel from './settings-selector-panel' +import ConnectedSettingsSelectorPanel from './connected-settings-selector-panel' import { setMainPanelContent } from '../../actions/ui' @@ -49,7 +49,7 @@ class TabbedFormPanel extends Component { {(mainPanelContent === 'EDIT_DATETIME' || mainPanelContent === 'EDIT_SETTINGS') && (
{mainPanelContent === 'EDIT_DATETIME' && ()} - {mainPanelContent === 'EDIT_SETTINGS' && ()} + {mainPanelContent === 'EDIT_SETTINGS' && ()}
+ +
+ ) + } +} + +// connect to redux store + +const mapStateToProps = (state, ownProps) => { + const { config, currentQuery, user } = state.otp + const { defaults } = user + + return { + config, + defaults, + query: currentQuery + } +} + +const mapDispatchToProps = { + clearDefaultSettings, + resetForm, + storeDefaultSettings +} + +export default connect(mapStateToProps, mapDispatchToProps)(UserTripSettings) diff --git a/lib/components/mobile/options-screen.js b/lib/components/mobile/options-screen.js index 893ba1357..8e20f4331 100644 --- a/lib/components/mobile/options-screen.js +++ b/lib/components/mobile/options-screen.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux' import MobileContainer from './container' import MobileNavigationBar from './navigation-bar' -import SettingsSelectorPanel from '../form/settings-selector-panel' +import ConnectedSettingsSelectorPanel from '../form/connected-settings-selector-panel' import PlanTripButton from '../form/plan-trip-button' import { MobileScreens, setMobileScreen } from '../../actions/ui' @@ -30,7 +30,7 @@ class MobileOptionsScreen extends Component { />
- +
diff --git a/lib/index.js b/lib/index.js index 0a7755351..5512a9108 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,13 +2,9 @@ import DateTimeModal from './components/form/date-time-modal' import DateTimePreview from './components/form/date-time-preview' import DefaultSearchForm from './components/form/default-search-form' import ErrorMessage from './components/form/error-message' -import GeneralSettingsPanel from './components/form/general-settings-panel' import LocationField from './components/form/connected-location-field' -import ModeSelector from './components/form/mode-selector' -import ModesPanel from './components/form/modes-panel' import PlanTripButton from './components/form/plan-trip-button' import SettingsPreview from './components/form/settings-preview' -import SettingsSelectorPanel from './components/form/settings-selector-panel' import SwitchButton from './components/form/switch-button' import LocationIcon from './components/icons/location-icon' @@ -64,13 +60,9 @@ export { DateTimePreview, DefaultSearchForm, ErrorMessage, - GeneralSettingsPanel, LocationField, - ModeSelector, - ModesPanel, PlanTripButton, SettingsPreview, - SettingsSelectorPanel, StylizedMap, SwitchButton, diff --git a/yarn.lock b/yarn.lock index 39df936a4..6d7ac4463 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6675,9 +6675,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^2.3.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.0.tgz#094ec359dc4b55e7d62e0db4acd76e89fe874d37" - integrity sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6"