mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-28 08:06:56 +00:00
refactor(metadata): remove ui-component (#7492)
1. ModalPortal 2. Icon 3. IconBtn 4. Loading 5. CenteredLoading 6. ClickOutside 7. SearchInput 8. Switch 9. CustomizeAddTool 10. SfCalendar 11. SfFilterCalendar 12. CustomizeSelect 13. CustomizePopover 14. FieldDisplaySettings 15. Formatters 16. remove duplicate codes
This commit is contained in:
100
frontend/src/components/customize-select/index.css
Normal file
100
frontend/src/components/customize-select/index.css
Normal file
@@ -0,0 +1,100 @@
|
||||
.seafile-customize-select {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 0 10px;
|
||||
border-radius: 3px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 900px;
|
||||
user-select: none;
|
||||
text-align: left;
|
||||
line-height: 1.5;
|
||||
background-image: none;
|
||||
font-size: 14px;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.seafile-customize-select:focus,
|
||||
.seafile-customize-select.focus {
|
||||
border-color: #1991eb !important;
|
||||
box-shadow: 0 0 0 2px rgba(70, 127, 207, 0.25);
|
||||
}
|
||||
|
||||
.seafile-customize-select.disabled:focus,
|
||||
.seafile-customize-select.focus.disabled,
|
||||
.seafile-customize-select.disabled:hover {
|
||||
border-color: rgba(0, 40, 100, 0.12) !important;
|
||||
box-shadow: unset;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.seafile-customize-select:hover {
|
||||
cursor: pointer;
|
||||
border-color: rgb(179, 179, 179);
|
||||
}
|
||||
|
||||
.seafile-customize-select .sf3-font-down {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.seafile-customize-select .selected-option {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.seafile-customize-select .selected-option .custom-select-dropdown-icon {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
color: #999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.seafile-customize-select.selector-collaborator .seafile-option-group .seafile-option-group-content,
|
||||
.seafile-customize-select.selector-group .seafile-option-group .seafile-option-group-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.seafile-customize-select.selector-collaborator .seafile-option-group .seafile-option-group-content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.seafile-customize-select.selector-collaborator .option {
|
||||
padding: 5px 0 5px 10px !important;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.seafile-customize-select.selector-group .option {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.seafile-customize-select.selector-group .select-group-option {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.seafile-customize-select.selector-group .selected-option .selected-group {
|
||||
padding: 0 2px;
|
||||
background: #eceff4;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.seafile-customize-select .selected-option-show {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.seafile-customize-select .select-placeholder {
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
173
frontend/src/components/customize-select/index.js
Normal file
173
frontend/src/components/customize-select/index.js
Normal file
@@ -0,0 +1,173 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import ModalPortal from '../modal-portal';
|
||||
import SelectOptionGroup from './select-option-group';
|
||||
import { getEventClassName } from '../../utils/dom';
|
||||
|
||||
import './index.css';
|
||||
|
||||
class CustomizeSelect extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isShowSelectOptions: false
|
||||
};
|
||||
}
|
||||
|
||||
onSelectToggle = (event) => {
|
||||
event.preventDefault();
|
||||
/*
|
||||
if select is showing, click events do not need to be monitored by other click events,
|
||||
so it can be closed when other select is clicked.
|
||||
*/
|
||||
if (this.state.isShowSelectOptions) event.stopPropagation();
|
||||
const eventClassName = getEventClassName(event);
|
||||
if (this.props.readOnly || eventClassName.indexOf('option-search-control') > -1 || eventClassName === 'seafile-option-group-search') return;
|
||||
// Prevent closing by pressing the space bar in the search input
|
||||
if (event.target.value === '') return;
|
||||
this.setState({
|
||||
isShowSelectOptions: !this.state.isShowSelectOptions
|
||||
});
|
||||
};
|
||||
|
||||
onClick = (event) => {
|
||||
if (this.props.isShowSelected && event.target.className.includes('icon-fork-number')) {
|
||||
return;
|
||||
}
|
||||
if (!this.selector.contains(event.target)) {
|
||||
this.closeSelect();
|
||||
}
|
||||
};
|
||||
|
||||
closeSelect = () => {
|
||||
this.setState({ isShowSelectOptions: false });
|
||||
};
|
||||
|
||||
getSelectedOptionTop = () => {
|
||||
if (!this.selector) return 38;
|
||||
const { height } = this.selector.getBoundingClientRect();
|
||||
return height;
|
||||
};
|
||||
|
||||
getFilterOptions = (searchValue) => {
|
||||
const { options, searchable } = this.props;
|
||||
if (!searchable) return options || [];
|
||||
const validSearchVal = searchValue.trim().toLowerCase();
|
||||
if (!validSearchVal) return options || [];
|
||||
return options.filter(option => {
|
||||
const { value, name } = option;
|
||||
if (typeof name === 'string') {
|
||||
return name.toLowerCase().indexOf(validSearchVal) > -1;
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
if (value.column) {
|
||||
return value.column.name.toLowerCase().indexOf(validSearchVal) > -1;
|
||||
}
|
||||
if (value.name) {
|
||||
return value.name.toLowerCase().indexOf(validSearchVal) > -1;
|
||||
}
|
||||
return value.columnOption && value.columnOption.name.toLowerCase().indexOf(validSearchVal) > -1;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
renderDropDownIcon = () => {
|
||||
const { readOnly, component } = this.props;
|
||||
if (readOnly) return;
|
||||
const { DropDownIcon } = component || {};
|
||||
if (DropDownIcon) {
|
||||
return (
|
||||
<div className="custom-select-dropdown-icon">{DropDownIcon}</div>
|
||||
);
|
||||
}
|
||||
return (<i className="sf3-font sf3-font-down" aria-hidden="true"></i>);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, value, options, placeholder, searchable, searchPlaceholder, noOptionsPlaceholder,
|
||||
readOnly, isInModal, addOptionAble, component } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={(node) => this.selector = node}
|
||||
className={classnames('seafile-customize-select custom-select',
|
||||
{ 'focus': this.state.isShowSelectOptions },
|
||||
{ 'disabled': readOnly },
|
||||
className
|
||||
)}
|
||||
onClick={this.onSelectToggle}>
|
||||
<div className="selected-option">
|
||||
{value && value.label ?
|
||||
<span className="selected-option-show">{value.label}</span>
|
||||
:
|
||||
<span className="select-placeholder">{placeholder}</span>
|
||||
}
|
||||
{this.renderDropDownIcon()}
|
||||
</div>
|
||||
{this.state.isShowSelectOptions && !isInModal && (
|
||||
<SelectOptionGroup
|
||||
value={value}
|
||||
addOptionAble={addOptionAble}
|
||||
component={component}
|
||||
isShowSelected={this.props.isShowSelected}
|
||||
top={this.getSelectedOptionTop()}
|
||||
options={options}
|
||||
onSelectOption={this.props.onSelectOption}
|
||||
searchable={searchable}
|
||||
searchPlaceholder={searchPlaceholder}
|
||||
noOptionsPlaceholder={noOptionsPlaceholder}
|
||||
onClickOutside={this.onClick}
|
||||
closeSelect={this.closeSelect}
|
||||
getFilterOptions={this.getFilterOptions}
|
||||
supportMultipleSelect={this.props.supportMultipleSelect}
|
||||
/>
|
||||
)}
|
||||
{this.state.isShowSelectOptions && isInModal && (
|
||||
<ModalPortal>
|
||||
<SelectOptionGroup
|
||||
className={className}
|
||||
value={value}
|
||||
addOptionAble={addOptionAble}
|
||||
component={component}
|
||||
isShowSelected={this.props.isShowSelected}
|
||||
position={this.selector.getBoundingClientRect()}
|
||||
isInModal={isInModal}
|
||||
top={this.getSelectedOptionTop()}
|
||||
options={options}
|
||||
onSelectOption={this.props.onSelectOption}
|
||||
searchable={searchable}
|
||||
searchPlaceholder={searchPlaceholder}
|
||||
noOptionsPlaceholder={noOptionsPlaceholder}
|
||||
onClickOutside={this.onClick}
|
||||
closeSelect={this.closeSelect}
|
||||
getFilterOptions={this.getFilterOptions}
|
||||
supportMultipleSelect={this.props.supportMultipleSelect}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CustomizeSelect.propTypes = {
|
||||
className: PropTypes.string,
|
||||
value: PropTypes.object,
|
||||
options: PropTypes.array,
|
||||
placeholder: PropTypes.string,
|
||||
onSelectOption: PropTypes.func,
|
||||
readOnly: PropTypes.bool,
|
||||
searchable: PropTypes.bool,
|
||||
addOptionAble: PropTypes.bool,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
noOptionsPlaceholder: PropTypes.string,
|
||||
component: PropTypes.object,
|
||||
supportMultipleSelect: PropTypes.bool,
|
||||
isShowSelected: PropTypes.bool,
|
||||
isInModal: PropTypes.bool, // if select component in a modal (option group need ModalPortal to show)
|
||||
};
|
||||
|
||||
export default CustomizeSelect;
|
@@ -0,0 +1,103 @@
|
||||
.seafile-option-group {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
min-height: 60px;
|
||||
max-height: 300px;
|
||||
min-width: 100%;
|
||||
max-width: 15rem;
|
||||
padding: 0.5rem 0;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 40, 100, 0.12);
|
||||
border-radius: 3px;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.seafile-option-group .seafile-option-group-search {
|
||||
width: 100%;
|
||||
padding: 0 10px 6px 10px;
|
||||
min-width: 170px;
|
||||
}
|
||||
|
||||
.seafile-option-group-search .form-control {
|
||||
height: 31px;
|
||||
}
|
||||
|
||||
.seafile-option-group .none-search-result {
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.seafile-option-group .seafile-option-group-content {
|
||||
max-height: 252px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.seafile-select-option {
|
||||
display: block;
|
||||
width: 100%;
|
||||
line-height: 24px;
|
||||
padding: 0.25rem 10px;
|
||||
clear: both;
|
||||
font-weight: 400;
|
||||
text-align: inherit;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.seafile-select-option.seafile-select-option-active {
|
||||
background-color: #20a0ff;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.seafile-select-option.seafile-select-option-active .select-option-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.seafile-select-option:hover .header-icon .seafile-multicolor-icon,
|
||||
.seafile-select-option.seafile-select-option-active .header-icon .seafile-multicolor-icon {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.seafile-select-option:not(.seafile-select-option-active):hover .header-icon .seafile-multicolor-icon {
|
||||
fill: #aaa;
|
||||
}
|
||||
|
||||
.seafile-select-option .select-option-name .single-select-option {
|
||||
margin: 0 0 0 12px;
|
||||
}
|
||||
|
||||
.seafile-select-option .select-option-name .multiple-select-option {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.seafile-option-group-selector-single-select .select-option-name,
|
||||
.seafile-option-group-selector-multiple-select .multiple-option-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.seafile-option-group-selector-multiple-select .multiple-check-icon {
|
||||
display: inline-flex;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.seafile-option-group-selector-multiple-select .multiple-check-icon .seafile-multicolor-icon-check-mark {
|
||||
font-size: 12px;
|
||||
color: #798d99;
|
||||
}
|
||||
|
||||
.seafile-option-group-selector-single-select .seafile-select-option:hover,
|
||||
.seafile-option-group-selector-single-select .seafile-select-option.seafile-select-option-active,
|
||||
.seafile-option-group-selector-multiple-select .seafile-select-option:hover,
|
||||
.seafile-option-group-selector-multiple-select .seafile-select-option.seafile-select-option-active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
@@ -0,0 +1,233 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import ClickOutside from '../../click-outside';
|
||||
import SearchInput from '../../search-input';
|
||||
import Option from './option';
|
||||
import { KeyCodes } from '../../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const OPTION_HEIGHT = 32;
|
||||
|
||||
class SelectOptionGroup extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
searchVal: '',
|
||||
activeIndex: -1,
|
||||
disableHover: false,
|
||||
};
|
||||
this.filterOptions = null;
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('keydown', this.onHotKey);
|
||||
setTimeout(() => {
|
||||
this.resetMenuStyle();
|
||||
}, 1);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.filterOptions = null;
|
||||
this.timer && clearTimeout(this.timer);
|
||||
window.removeEventListener('keydown', this.onHotKey);
|
||||
}
|
||||
|
||||
resetMenuStyle = () => {
|
||||
const { isInModal, position } = this.props;
|
||||
const { top, height } = this.optionGroupRef.getBoundingClientRect();
|
||||
if (isInModal) {
|
||||
if (position.y + position.height + height > window.innerHeight) {
|
||||
this.optionGroupRef.style.top = (position.y - height) + 'px';
|
||||
}
|
||||
this.optionGroupRef.style.opacity = 1;
|
||||
}
|
||||
else {
|
||||
if (height + top > window.innerHeight) {
|
||||
const borderWidth = 2;
|
||||
this.optionGroupRef.style.top = -1 * (height + borderWidth) + 'px';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onHotKey = (event) => {
|
||||
const keyCode = event.keyCode;
|
||||
if (keyCode === KeyCodes.UpArrow) {
|
||||
this.onPressUp();
|
||||
} else if (keyCode === KeyCodes.DownArrow) {
|
||||
this.onPressDown();
|
||||
} else if (keyCode === KeyCodes.Enter) {
|
||||
let option = this.filterOptions && this.filterOptions[this.state.activeIndex];
|
||||
if (option) {
|
||||
this.props.onSelectOption(option.value);
|
||||
if (!this.props.supportMultipleSelect) {
|
||||
this.props.closeSelect();
|
||||
}
|
||||
}
|
||||
} else if (keyCode === KeyCodes.Tab || keyCode === KeyCodes.Escape) {
|
||||
this.props.closeSelect();
|
||||
}
|
||||
};
|
||||
|
||||
onPressUp = () => {
|
||||
if (this.state.activeIndex > 0) {
|
||||
this.setState({ activeIndex: this.state.activeIndex - 1 }, () => {
|
||||
this.scrollContent();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onPressDown = () => {
|
||||
if (this.filterOptions && this.state.activeIndex < this.filterOptions.length - 1) {
|
||||
this.setState({ activeIndex: this.state.activeIndex + 1 }, () => {
|
||||
this.scrollContent();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMouseDown = (e) => {
|
||||
const { isInModal } = this.props;
|
||||
// prevent event propagation when click option or search input
|
||||
if (isInModal) {
|
||||
e.stopPropagation();
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
scrollContent = () => {
|
||||
const { offsetHeight, scrollTop } = this.optionGroupContentRef;
|
||||
this.setState({ disableHover: true });
|
||||
this.timer = setTimeout(() => {
|
||||
this.setState({ disableHover: false });
|
||||
}, 500);
|
||||
if (this.state.activeIndex * OPTION_HEIGHT === 0) {
|
||||
this.optionGroupContentRef.scrollTop = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.activeIndex * OPTION_HEIGHT < scrollTop) {
|
||||
this.optionGroupContentRef.scrollTop = scrollTop - OPTION_HEIGHT;
|
||||
}
|
||||
else if (this.state.activeIndex * OPTION_HEIGHT > offsetHeight + scrollTop) {
|
||||
this.optionGroupContentRef.scrollTop = scrollTop + OPTION_HEIGHT;
|
||||
}
|
||||
};
|
||||
|
||||
changeIndex = (index) => {
|
||||
this.setState({ activeIndex: index });
|
||||
};
|
||||
|
||||
onChangeSearch = (searchVal) => {
|
||||
let value = searchVal || '';
|
||||
if (value !== this.state.searchVal) {
|
||||
this.setState({ searchVal: value, activeIndex: -1, });
|
||||
}
|
||||
};
|
||||
|
||||
renderOptGroup = (searchVal) => {
|
||||
let { noOptionsPlaceholder, onSelectOption } = this.props;
|
||||
this.filterOptions = this.props.getFilterOptions(searchVal);
|
||||
if (this.filterOptions.length === 0) {
|
||||
return (
|
||||
<div className="none-search-result">{noOptionsPlaceholder}</div>
|
||||
);
|
||||
}
|
||||
return this.filterOptions.map((opt, i) => {
|
||||
let key = opt.value.column ? opt.value.column.key : i;
|
||||
let isActive = this.state.activeIndex === i;
|
||||
return (
|
||||
<Option
|
||||
key={key}
|
||||
index={i}
|
||||
isActive={isActive}
|
||||
value={opt.value}
|
||||
onSelectOption={onSelectOption}
|
||||
changeIndex={this.changeIndex}
|
||||
supportMultipleSelect={this.props.supportMultipleSelect}
|
||||
disableHover={this.state.disableHover}
|
||||
>
|
||||
{opt.label}
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { searchable, searchPlaceholder, top, left, minWidth, value, isShowSelected, isInModal, position,
|
||||
className, addOptionAble, component } = this.props;
|
||||
const { AddOption } = component || {};
|
||||
let { searchVal } = this.state;
|
||||
let style = { top: top || 0, left: left || 0 };
|
||||
if (minWidth) {
|
||||
style = { top: top || 0, left: left || 0, minWidth };
|
||||
}
|
||||
if (isInModal) {
|
||||
style = {
|
||||
position: 'fixed',
|
||||
left: position.x,
|
||||
top: position.y + position.height,
|
||||
minWidth: position.width,
|
||||
opacity: 0,
|
||||
};
|
||||
}
|
||||
return (
|
||||
<ClickOutside onClickOutside={this.props.onClickOutside}>
|
||||
<div
|
||||
className={classnames('seafile-option-group', className ? 'seafile-option-group-' + className : '', {
|
||||
'pt-0': isShowSelected,
|
||||
'create-new-seafile-option-group': addOptionAble,
|
||||
})}
|
||||
ref={(ref) => this.optionGroupRef = ref}
|
||||
style={style}
|
||||
onMouseDown={this.onMouseDown}
|
||||
>
|
||||
{isShowSelected &&
|
||||
<div className="editor-list-delete mb-2" onClick={(e) => e.stopPropagation()}>{value.label || ''}</div>
|
||||
}
|
||||
{searchable && (
|
||||
<div className="seafile-option-group-search">
|
||||
<SearchInput
|
||||
className="option-search-control"
|
||||
placeholder={searchPlaceholder}
|
||||
onChange={this.onChangeSearch}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="seafile-option-group-content" ref={(ref) => this.optionGroupContentRef = ref}>
|
||||
{this.renderOptGroup(searchVal)}
|
||||
</div>
|
||||
{addOptionAble && AddOption}
|
||||
</div>
|
||||
</ClickOutside>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SelectOptionGroup.propTypes = {
|
||||
top: PropTypes.number,
|
||||
left: PropTypes.number,
|
||||
minWidth: PropTypes.number,
|
||||
options: PropTypes.array,
|
||||
onSelectOption: PropTypes.func,
|
||||
searchable: PropTypes.bool,
|
||||
addOptionAble: PropTypes.bool,
|
||||
component: PropTypes.object,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
noOptionsPlaceholder: PropTypes.string,
|
||||
onClickOutside: PropTypes.func.isRequired,
|
||||
closeSelect: PropTypes.func.isRequired,
|
||||
getFilterOptions: PropTypes.func.isRequired,
|
||||
supportMultipleSelect: PropTypes.bool,
|
||||
value: PropTypes.object,
|
||||
isShowSelected: PropTypes.bool,
|
||||
stopClickEvent: PropTypes.bool,
|
||||
isInModal: PropTypes.bool,
|
||||
position: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default SelectOptionGroup;
|
@@ -0,0 +1,50 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
class Option extends Component {
|
||||
|
||||
onSelectOption = (value, event) => {
|
||||
if (this.props.supportMultipleSelect) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.props.onSelectOption(value, event);
|
||||
};
|
||||
|
||||
onMouseEnter = () => {
|
||||
if (!this.props.disableHover) {
|
||||
this.props.changeIndex(this.props.index);
|
||||
}
|
||||
};
|
||||
|
||||
onMouseLeave = () => {
|
||||
if (!this.props.disableHover) {
|
||||
this.props.changeIndex(-1);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className={classnames('seafile-select-option', { 'seafile-select-option-active': this.props.isActive })}
|
||||
onClick={this.onSelectOption.bind(this, this.props.value)}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Option.propTypes = {
|
||||
index: PropTypes.number,
|
||||
isActive: PropTypes.bool,
|
||||
changeIndex: PropTypes.func,
|
||||
value: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
|
||||
onSelectOption: PropTypes.func,
|
||||
supportMultipleSelect: PropTypes.bool,
|
||||
disableHover: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Option;
|
Reference in New Issue
Block a user