// Menu-like Picker variant with keyboard control // Version 2020-04-27 // License: LGPLv3.0+ // (c) Vitaliy Filippov 2020+ import React from 'react'; import PropTypes from 'prop-types'; import autocomplete_css from './autocomplete.css'; import Picker from './Picker.js'; export default class PickerMenu extends Picker { static propTypes = { ...Picker.propTypes, // menu options items: PropTypes.array, // additional text/items to render before menu items beforeItems: PropTypes.any, // additional text/items to render after menu items afterItems: PropTypes.any, // menuitem callback onSelectItem: PropTypes.func, // menuitem name key - default empty (render the item itself) labelKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // change theme (CSS module) for this input theme: PropTypes.object, } onKeyDown = (ev) => { if ((ev.which == 40 || ev.which == 38) && this.props.items.length) { // up / down this.setState({ active: this.state.active == null ? 0 : ( (this.state.active + (event.which === 40 ? 1 : this.props.items.length-1)) % this.props.items.length ), }); if (!this.state.focused) { this.focus(); } } else if ((ev.which == 10 || ev.which == 13) && this.state.active != null && this.state.active < this.props.items.length) { // enter this.onMouseDown(); } } onMouseDown = () => { const sel = this.props.items[this.state.active]; const f = this.props.onSelectItem; f && f(sel); } onMouseOver = (ev) => { let e = ev.target; while (e && e != ev.currentTarget && !e.id) { e = e.parentNode; } if (e && e.id) { this.setState({ active: e.id }); } } animatePicker = (e) => { if (e) { e.style.visibility = 'hidden'; e.style.overflowY = 'hidden'; const anim = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame; anim(() => { e.style.visibility = ''; e.style.maxHeight = '1px'; anim(() => { e.style.maxHeight = '100%'; const end = () => { e.style.overflowY = 'auto'; e.removeEventListener('transitionend', end); }; e.addEventListener('transitionend', end); }); }); } } renderPicker = () => { const theme = this.props.theme || autocomplete_css; return (
{this.props.beforeItems} {this.props.items.map((e, i) => (
{!this.props.labelKey ? e : e[this.props.labelKey]}
))} {this.props.afterItems}
); } getInputProps() { return { ...super.getInputProps(), onKeyDown: this.onKeyDown, }; } componentDidUpdate() { super.componentDidUpdate(); if (this.input) { if (this.prevHeight && this.input.offsetHeight != this.prevHeight) { this.calculateDirection(); } this.prevHeight = this.input.offsetHeight; } } componentDidMount() { this.componentDidUpdate(); } } delete PickerMenu.propTypes.renderPicker;