shinkansen-cogs
Version:
202 lines (184 loc) • 4.19 kB
JSX
/**
* @typedef {CogsTypes.OnEventType} OnEventType
* @typedef {CogsTypes.Components.Field.ValueProps} ValueProps
* @typedef {CogsTypes.Components.Field.Select.SelectProps} SelectProps
*/
/**
* SelectField component
*/
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import {
ValueField,
toInputValue
} from '#cogs/components/field'
/**
* @type {OnEventType}
*/
function DEFAULT_HANDLE_EVENT () {
//
}
/**
* @param {HTMLOptionElement[]} selectedOptions
* @returns {string[]}
*/
function fromSelectedOptions (selectedOptions) {
return (
Array.from(selectedOptions)
.map(({ value, text }) => (value || text) ?? '')
)
}
/**
* @param {{ target: { selectedOptions: HTMLOptionElement[] } }} event
* @returns {string[]}
*/
function getSelectedValues ({ target: { selectedOptions } }) {
return (
fromSelectedOptions(selectedOptions)
)
}
/**
* @param {{ target: { value?: string } }} event
* @returns {string}
*/
function getSelectedValue ({ target: { value } }) {
return value ?? ''
}
/**
* @extends {ValueField<ValueProps & SelectProps>}
*/
export default class SelectField extends ValueField {
getClassName () {
return classnames(super.getClassName(), 'select')
}
/**
* @param {SelectProps} props
* @returns {boolean}
*/
shouldComponentUpdate (props) {
const {
multiple,
children,
...superProps
} = props
return (
super.shouldComponentUpdate(superProps) || // , state ||
(multiple !== this.props.multiple) ||
(children !== this.props.children)
)
}
/**
* @overload
* @param {{ target: { selectedOptions: { value?: string, text?: string }[] } }} event
* @returns {void}
*
* @overload
* @param {{ target: { value?: string } }} event
* @returns {void}
*
* @param {any} event
* @returns {void}
*/
handleChange = (event) => {
const {
multiple = false,
onChange = DEFAULT_HANDLE_EVENT,
name
} = this.props
if (multiple) {
onChange(name, getSelectedValues(event))
} else {
onChange(name, getSelectedValue(event))
}
}
render () {
const {
name,
id,
defaultValue,
required = false,
disabled = false,
readOnly = false,
tabIndex,
accessKey,
multiple = false,
children,
fieldRef
} = this.props
const className = this.getClassName()
if (defaultValue === undefined) {
const {
value
} = this.props
return (
<select
name={name}
id={id}
value={(
multiple
? Array.isArray(value)
? value
: [toInputValue(value)]
: toInputValue(value)
)}
required={required}
disabled={disabled} // @ts-ignore
readOnly={readOnly}
tabIndex={tabIndex}
accessKey={accessKey}
multiple={multiple}
onChange={this.handleChange}
className={className}
ref={fieldRef}>
{children}
</select>
)
}
return (
<select
name={name}
id={id}
defaultValue={(
multiple
? Array.isArray(defaultValue)
? defaultValue
: [String(defaultValue)]
: String(defaultValue)
)}
required={required}
disabled={disabled} // @ts-ignore
readOnly={readOnly}
tabIndex={tabIndex}
accessKey={accessKey}
multiple={multiple}
onChange={this.handleChange}
className={className}
ref={fieldRef}>
{children}
</select>
)
}
}
SelectField.propTypes = {
...ValueField.propTypes,
multiple: PropTypes.bool,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.arrayOf(
PropTypes.string
)
]),
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.arrayOf(
PropTypes.string
)
]),
children: PropTypes.oneOfType([
PropTypes.node,
PropTypes.arrayOf(
PropTypes.node
)
])
}