diff --git a/src/AsyncUserSelect/index.css b/src/AsyncUserSelect/index.css index 6b3bd912..355e2b21 100644 --- a/src/AsyncUserSelect/index.css +++ b/src/AsyncUserSelect/index.css @@ -3,19 +3,35 @@ height: auto !important; min-height: 38px; max-width: calc(100% - 10px); + padding: .375rem 40px .375rem 1rem; + position: relative; +} + +.dtable-ui-selected-users-container.focus { + border: 1px solid var(--bs-bg-border-color) !important; } .dtable-ui-selected-users-container .dtable-ui-user-select-placeholder { - color: #808080; + color: var(--bs-icon-color); +} + +.select-dropdown-indicator { + margin-left: -2px; + position: absolute; + right: 16px; + top: 50%; + transform: translateY(-50%) +} + +.select-dropdown-indicator .multicolor-icon { + font-size: 12px; + width: 12px; + height: 12px; } .dtable-ui-user-select-popover .popover { width: 385px; max-width: 385px; - background: var(--bs-popover-bg); - border: 1px solid var(--bs-border-secondary-color); - border-radius: 4px; - box-shadow: var(--bs-border-secondary-shadow); margin-top: -4px; } @@ -23,59 +39,48 @@ position: relative; } -.dtable-ui-user-select-container .dtable-ui-user-search-container { - padding: 10px 10px 0 10px; -} - -.dtable-ui-user-select-container .dtable-ui-user-search-container input { - height: 28px; -} - .dtable-ui-user-select-container .dtable-ui-user-list-container { min-height: 160px; max-height: 200px; - margin: 10px 0; + padding: 8px; overflow: auto; + position: relative; } .dtable-ui-user-select-container .dtable-ui-user-list-container .dtable-ui-user-item-container { display: flex; align-items: center; justify-content: space-between; - height: 30px; - padding: 0 10px; + height: 32px; + padding: 0 8px; font-size: 14px; cursor: pointer; + border-radius: 4px; } .dtable-ui-user-select-container .dtable-ui-user-list-container .dtable-ui-user-item-container:hover, .dtable-ui-user-select-container .dtable-ui-user-list-container .dtable-ui-user-item-container-highlight { - background: #f5f5f5; + background-color: var(--bs-btn-background-hover); cursor: pointer; } -.no-user-search-result { - color: #666666; - font-size: 14px; - padding-left: 10px; - padding: 10px; - overflow: auto; +.dtable-ui-user-select-container .dtable-ui-user-list-container { + margin: 0 !important; } .dtable-ui-user-select-popover .popover .dtable-ui-user-item { background: transparent; } -.dtable-ui-user-list-container .dtable-ui-collaborator-check-icon .dtable-icon-check-mark { - color: #798d99; - font-size: 12px; +.dtable-ui-user-list-container .dtable-ui-collaborator-check-icon .dtable-icon-check { + color: var(--bs-icon-color); + font-size: 14px; } -/* user-item */ .dtable-ui-user-item { display: inline-flex; align-items: center; - margin: 2px 10px 2px 0; + margin: 2px 4px 2px 0; padding: 0 8px 0 2px; height: 20px; font-size: 13px; @@ -95,6 +100,7 @@ justify-content: center; transform: translateY(0); flex-shrink: 0; + margin-right: 4px; } .dtable-ui-user-item .user-name { @@ -125,3 +131,7 @@ color: #666666; cursor: pointer; } + +.dtable-ui-selected-users-container .dtable-ui-users-input { + padding-right: 37px; +} diff --git a/src/AsyncUserSelect/index.js b/src/AsyncUserSelect/index.js index c624ec44..420aa46d 100644 --- a/src/AsyncUserSelect/index.js +++ b/src/AsyncUserSelect/index.js @@ -3,14 +3,15 @@ import PropTypes from 'prop-types'; import classnames from 'classnames'; import { Popover } from 'reactstrap'; import ClickOutside from '../ClickOutside'; -import DtableSearchInput from '../DTableSearchInput'; +import DTableCustomizeSearchInput from '../DTableCustomizeSearchInput'; import UserItem from './user-item'; import { getLocale } from '../lang'; import keyCodes from '../constants/key-codes'; +import DTableIcon from '../DTableIcon'; import './index.css'; -const AsyncUserSelect = ({ className, emptyPlaceholder = '', searchPlaceholder = '', isMulti = true, enableShowIDInOrgWhenSearchUser = false, selectedUsers = [], loadOptions, modifySelectedUsers }) => { +const AsyncUserSelect = ({ className, emptyPlaceholder = '', searchPlaceholder = '', isMulti = true, enableShowIDInOrgWhenSearchUser = false, selectedUsers = [], loadOptions, modifySelectedUsers, showDeptBtn = false }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [searchValue, setSearchValue] = useState(''); const [searchedUsers, setSearchedUsers] = useState([]); @@ -191,20 +192,23 @@ const AsyncUserSelect = ({ className, emptyPlaceholder = '', searchPlaceholder = <>
- {selectedUsers.map((user, index) => { - return ( - - ); - })} - {selectedUsers.length === 0 && ( -
- {emptyPlaceholder || getLocale('Search_users')} -
- )} +
+ {selectedUsers.map((user, index) => { + return ( + + ); + })} + {selectedUsers.length === 0 && ( +
+ {emptyPlaceholder || getLocale('Search_users')} +
+ )} + {!showDeptBtn && } +
{selectorRef.current && ( -
e.stopPropagation()} > -
- e.stopPropagation()}> +
+ onSearchValueChanged('')} + autoFocus={true} + isClearable={true} + value={searchValue} />
@@ -239,7 +244,7 @@ const AsyncUserSelect = ({ className, emptyPlaceholder = '', searchPlaceholder = {selectedUsers.find(u => u.email === user.email) && (
- +
)}
@@ -269,6 +274,7 @@ AsyncUserSelect.propTypes = { selectedUsers: PropTypes.array, loadOptions: PropTypes.func, modifySelectedUsers: PropTypes.func, + showDeptBtn: PropTypes.bool, }; export default AsyncUserSelect; diff --git a/src/DTableCustomizeCollaboratorSelect/index.css b/src/DTableCustomizeCollaboratorSelect/index.css deleted file mode 100644 index 43576f22..00000000 --- a/src/DTableCustomizeCollaboratorSelect/index.css +++ /dev/null @@ -1,86 +0,0 @@ -.dtable-customize-collaborator-select.dtable-select { - position: unset; -} - -.dtable-customize-collaborator-select .collaborator-avatar { - width: 16px; - height: 16px; - transform: translateY(-1px); -} - -.dtable-customize-collaborator-select .selected-option-show { - width: calc(100% - 20px); - height: 20px; -} - -.dtable-customize-collaborator-select .selected-option { - width: auto; - overflow-x: auto; -} - -.dtable-customize-collaborator-select .dtable-icon-down3 { - margin-left: .5rem; -} - -.dtable-customize-collaborator-select .option-collaborator { - display: flex; -} - -.dtable-customize-collaborator-select .collaborator-container { - flex: 1 1; -} - -.dtable-customize-collaborator-select .editor-list-delete .collaborator-container { - flex: 1 1; - display: inline; -} - -.dtable-customize-collaborator-select .multiple-check-icon { - display: inline-flex; - width: 20px; - text-align: center; -} - -.dtable-customize-collaborator-select .collaborator-check-icon .dtable-icon-check-mark, -.dtable-customize-collaborator-select .multiple-check-icon .dtable-icon-check-mark { - font-size: 12px; - color: #798d99; -} - -.dtable-customize-collaborator-select.dtable-select .option-active, -.dtable-customize-collaborator-select.dtable-select .option:hover { - color: #212529; - background-color: #f5f5f5; -} - -.dtable-customize-collaborator-select.dtable-select .option.option-active .select-option-name { - color: #212529; -} - -.collaborator-select .option-group-content .collaborator, -.dtable-customize-collaborator-select .option-group .option-group-content .collaborator { - background: none; -} - -.dtable-customize-collaborator-select .collaborator-avatar-container { - width: 16px; -} - -.dtable-customize-collaborator-select .option-group-content .remove-container { - display: none; -} - -.dtable-customize-collaborator-select .collaborator-container { - display: flex; - justify-content: space-between; -} - -.collaborator-container .collaborator-name { - margin-left: 5px; - max-width: 200px; - color: #212529; -} - -.collaborator-select .option-group { - max-height: fit-content; -} diff --git a/src/DTableCustomizeCollaboratorSelect/index.js b/src/DTableCustomizeCollaboratorSelect/index.js deleted file mode 100644 index 727a42c4..00000000 --- a/src/DTableCustomizeCollaboratorSelect/index.js +++ /dev/null @@ -1,165 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { Popover } from 'reactstrap'; -import { searchCollaborators } from 'dtable-utils'; -import SelectOptionGroup from '../SelectOptionGroup'; - -import './index.css'; - -class CollaboratorSelect extends Component { - constructor(props) { - super(props); - this.state = { - isShowSelectOptions: false - }; - this.id = 'collaborator-select-' + Math.trunc(Math.random() * 10000); - } - - static defaultProps = { - top: -3, - left: -3, - }; - - componentDidMount() { - document.addEventListener('mousedown', this.onMousedown); - this.btnWidth = this.selector.clientWidth; - } - - componentWillUnmount() { - document.removeEventListener('mousedown', this.onMousedown); - } - - 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.nativeEvent.stopImmediatePropagation(); - let eventClassName = event.target.className; - if (this.props.isLocked || eventClassName.indexOf('option-search-control') > -1 || eventClassName === 'option-group-search') return; - // Prevent closing by pressing the spacebar in the search input - if (event.target.value === '') return; - this.selectedOptionWidth = this.selectedOptionRef.clientWidth; - this.setState({ - isShowSelectOptions: !this.state.isShowSelectOptions - }); - }; - - onMousedown = (event) => { - const name = event.target.className; - if (name === 'select-placeholder' || name.includes('icon-fork-number')) { - return; - } - if (!this.selector.contains(event.target)) { - this.closeSelect(); - } - }; - - closeSelect = () => { - this.setState({ isShowSelectOptions: false }); - }; - - getFilterOptions = (searchValue) => { - const { options, searchable } = this.props; - if (!searchable) return options || []; - return searchCollaborators(options, searchValue); - }; - - renderOptionGroup = () => { - const { value, options, searchable, searchPlaceholder, noOptionsPlaceholder, top, left, - isUsePopover } = this.props; - if (!isUsePopover) { - return ( - - ); - } - return ( - - - - ); - }; - - render() { - let { className, value, placeholder, isLocked } = this.props; - return ( - - ); - } -} - -CollaboratorSelect.propTypes = { - className: PropTypes.string, - value: PropTypes.object, - options: PropTypes.array, - placeholder: PropTypes.string, - onSelectOption: PropTypes.func, - isLocked: PropTypes.bool, - searchable: PropTypes.bool, - searchPlaceholder: PropTypes.string, - noOptionsPlaceholder: PropTypes.string, - supportMultipleSelect: PropTypes.bool, - isUsePopover: PropTypes.bool, - top: PropTypes.number, - left: PropTypes.number, -}; - -export default CollaboratorSelect; diff --git a/src/DTableCustomizeSearchInput/index.css b/src/DTableCustomizeSearchInput/index.css new file mode 100644 index 00000000..ef080b65 --- /dev/null +++ b/src/DTableCustomizeSearchInput/index.css @@ -0,0 +1,75 @@ +.seatable-search-input { + width: 100%; + padding: 6px 10px; + min-width: 170px; + position: relative; + padding: 0; + height: 32px; + border-bottom: 1px solid var(--bs-border-secondary-color); +} + +.seatable-select-search { + border-bottom: 1px solid var(--bs-border-secondary-color); + height: 32px; +} + +.select-search-control { + height: 100%; + border: none; + cursor: pointer; + display: block; + background-color: var(--bs-popover-bg); + font-weight: 400; + font-size: .875rem; + line-height: 1.6; + width: 100%; + padding: .375rem 1rem; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.select-search-control:focus { + outline: none; + box-shadow: none; + border: none; + cursor: pointer; + height: 100%; +} + +.clear-icon-x { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + min-width: 20px; + pointer-events: auto; + color: var(--bs-icon-color); + right: 13px; + top: 6px; + z-index: 1; + height: 20px; + width: 20px; + cursor: pointer; + border-radius: 3px; +} + +.clear-icon-x .multicolor-icon { + font-size: 12px; +} + +.clear-icon-x:hover { + background-color: var(--bs-btn-background-hover); +} + +.seatable-search-input .none-search-result { + height: 100px; + width: 100%; + padding: 10px; + color: var(--bs-icon-color); +} + +.dtable-ui-user-select-popover .select-search-control, +.dtable-popover .select-search-control { + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} diff --git a/src/DTableCustomizeSearchInput/index.js b/src/DTableCustomizeSearchInput/index.js new file mode 100644 index 00000000..4057442d --- /dev/null +++ b/src/DTableCustomizeSearchInput/index.js @@ -0,0 +1,136 @@ +import React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import DTableIcon from '../DTableIcon'; + +import './index.css'; + +const propTypes = { + placeholder: PropTypes.string, + autoFocus: PropTypes.bool, + className: PropTypes.string, + onChange: PropTypes.func.isRequired, + onKeyDown: PropTypes.func, + wait: PropTypes.number, + disabled: PropTypes.bool, + style: PropTypes.object, + isClearable: PropTypes.bool, + clearValue: PropTypes.func, + clearClassName: PropTypes.string, + value: PropTypes.string, +}; + +class DTableCustomizeSearchInput extends Component { + + constructor(props) { + super(props); + this.state = { + searchValue: props.value || '', + }; + this.isInputtingChinese = false; + this.timer = null; + this.inputRef = null; + } + + componentDidMount() { + if (this.props.autoFocus && this.inputRef && this.inputRef !== document.activeElement) { + setTimeout(() => { + this.inputRef.focus(); + }, 0); + } + } + + UNSAFE_componentWillReceiveProps(nextProps) { + if (nextProps.value !== this.props.value) { + this.setState({ searchValue: nextProps.value }); + } + } + + componentWillUnmount() { + this.timer && clearTimeout(this.timer); + this.timer = null; + this.inputRef = null; + } + + onCompositionStart = () => { + this.isInputtingChinese = true; + }; + + onChange = (e) => { + this.timer && clearTimeout(this.timer); + const { onChange, wait = 100 } = this.props; + let text = e.target.value; + this.setState({ searchValue: text || '' }, () => { + if (this.isInputtingChinese) return; + this.timer = setTimeout(() => { + onChange && onChange(this.state.searchValue.trim()); + }, wait); + }); + }; + + onCompositionEnd = (e) => { + this.isInputtingChinese = false; + this.onChange(e); + }; + + clearSearch = (e) => { + e && e.stopPropagation && e.stopPropagation(); + const { clearValue } = this.props; + this.setState({ searchValue: '' }, () => { + clearValue && clearValue(); + }); + }; + + setFocus = (isSelectAllText) => { + if (this.inputRef === document.activeElement) return; + this.inputRef.focus(); + if (isSelectAllText) { + const txtLength = this.state.searchValue.length; + this.inputRef.setSelectionRange(0, txtLength); + } + }; + + renderClear = () => { + const { isClearable, clearClassName } = this.props; + const { searchValue } = this.state; + if (!isClearable || !searchValue) return null; + + return ( + + + + ); + }; + + render() { + const { placeholder, autoFocus, className, onKeyDown, disabled = false, style = {}, isClearable, isMultiple = false } = this.props; + const { searchValue } = this.state; + const inputWidth = (isClearable && searchValue && !isMultiple) ? 'calc(100% - 40px)' : '100%'; + style.width = inputWidth; + + return ( + + this.inputRef = ref} + /> + {this.renderClear()} + + ); + } +} + +DTableCustomizeSearchInput.propTypes = propTypes; + +export default DTableCustomizeSearchInput; diff --git a/src/DTableCustomizeSelect/index.css b/src/DTableCustomizeSelect/index.css index c425303e..8ab81935 100644 --- a/src/DTableCustomizeSelect/index.css +++ b/src/DTableCustomizeSelect/index.css @@ -1,100 +1,178 @@ -.dtable-select { +.seatable-customize-select { position: relative; -} - -.dtable-select.custom-select { display: flex; - padding: 0 10px; - border-radius: 3px; + padding: 0 16px; + border-radius: 4px; align-items: center; justify-content: space-between; max-width: 900px; user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -o-user-select: none; - -ms-user-select: none; text-align: left; + line-height: 1.5; + background-image: none; + font-size: 14px; + color: var(--bs-body-color); + border: 1px solid rgba(0, 40, 100, 0.12); } -.dtable-select.custom-select:focus, -.dtable-select.custom-select.focus { - border-color: #1991eb !important; - box-shadow: 0 0 0 2px rgba(70, 127, 207, 0.25); +.seatable-customize-select:focus, +.seatable-customize-select.focus { + border: 1px solid var(--bs-bg-border-color) !important; } -.dtable-select.custom-select.disabled:focus, -.dtable-select.custom-select.focus.disabled, -.dtable-select.custom-select.disabled:hover { +.seatable-customize-select.disabled:focus, +.seatable-customize-select.focus.disabled, +.seatable-customize-select.disabled:hover { border-color: rgba(0, 40, 100, 0.12) !important; box-shadow: unset; cursor: default; } -.dtable-select.custom-select:hover { +.seatable-customize-select:hover { cursor: pointer; - border-color: rgb(179, 179, 179); -} - -.dtable-select .dtable-icon-down3 { - display: inline-block; - font-size: 12px; - color: #999; - transition: all 0.1s; + border-color: var(--bs-border-color); } -.dtable-select .dtable-icon-down3:hover { - color: #555; +.seatable-customize-select .seatable-multicolor-icon-down { + width: 12px; + height: 12px; + color: var(--bs-icon-color); } -.dtable-select .selected-option { +.seatable-customize-select .selected-option { display: flex; flex: 1; overflow: hidden; flex-wrap: nowrap; align-items: center; justify-content: space-between; - background: #fff; + background: inherit; + height: 20px; +} + +.seatable-check-color { + color: var(--bs-icon-color); } -.dtable-select.dtable-ui-collaborator-selector .option-group .option-group-content, -.dtable-select.selector-group .option-group .option-group-content { +.seatable-customize-select.dtable-ui-collaborator-selector .seatable-option-group .seatable-option-group-content, +.seatable-customize-select.selector-group .seatable-option-group .seatable-option-group-content { padding: 10px; } -.dtable-select.custom-select.dtable-ui-collaborator-selector .option-group .option-group-content { +.seatable-customize-select.dtable-ui-collaborator-selector .seatable-option-group .seatable-option-group-content { padding: 10px 0; } -.dtable-select.custom-select.dtable-ui-collaborator-selector .option { +.seatable-customize-select.dtable-ui-collaborator-selector .option { padding: 5px 0 5px 10px !important; line-height: 20px; } -.dtable-select.selector-group .option { +.seatable-customize-select.selector-group .option { height: 30px; display: flex; align-items: center; } -.dtable-select.selector-group .select-group-option { +.seatable-customize-select.selector-group .select-group-option { justify-content: space-between; } -.dtable-select.selector-group .selected-option .selected-group { +.seatable-customize-select.selector-group .selected-option .selected-group { padding: 0 2px; background: #eceff4; border-radius: 3px; } -.dtable-select .selected-option-show { - overflow: hidden; +.seatable-customize-select .select-placeholder { + line-height: 1; + font-size: 14px; + white-space: nowrap; + color: var(--bs-bg-placeholder-color); + margin-right: 8px; +} + +.seatable-customize-select .disabled .select-placeholder { + opacity: 0.65; +} + +.seatable-customize-select .selected-option-show { + height: 20px; + line-height: 20px; + margin-right: 8px; + width: calc(100% - 20px); + display: flex; text-overflow: ellipsis; + overflow: hidden; white-space: nowrap; } -.dtable-select .select-placeholder { - line-height: 1; +.seatable-customize-select .selected-option .multicolor-icon { + font-size: 12px; +} + +.seatable-customize-select .multiple-check-icon, +.seatable-customize-select .header-icon .dtable-font, +.seatable-option-group .select-label .header-icon .dtable-font { + color: var(--bs-icon-secondary-color) !important; + cursor: default; font-size: 14px; - white-space: nowrap; +} + +.seatable-customize-select .header-icon { + display: inline-block; + padding: 0 .5rem 0 0!important; + margin-left: 0 !important; +} + +.selector-group-select .selected-option-show .selected-option-item { + align-items: center; + border-radius: 4px; + display: flex; + height: 24px; + line-height: 24px; + margin-right: 4px; + padding: 0 6px 0 6px; + background-color: #f7f7f5; +} + +.selector-group-select .selected-option-show .selected-option-item .selected-option-item-name { + color: #212519; + font-size: 14px; + margin-right: 8px +} + +.selector-group-select .dtable-icon-x { + color: var(--bs-icon-color); + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} + +.selector-group-select .selected-option-show { + display: flex; + flex-wrap: wrap; + gap: 4px; + height: auto; +} + +.selector-group-select { + padding: 7px 16px 7px 8px; +} + +.selector-group-select .selected-option { + align-items: center; + display: flex; + flex: 1 1; + flex-wrap: nowrap; + justify-content: space-between; + overflow: hidden; + height: auto; + min-height: 24px +} + +.selector-group-select { + height: auto; } diff --git a/src/DTableCustomizeSelect/index.js b/src/DTableCustomizeSelect/index.js index 8cb78673..d4c725f4 100644 --- a/src/DTableCustomizeSelect/index.js +++ b/src/DTableCustomizeSelect/index.js @@ -1,8 +1,10 @@ import React, { Component } from 'react'; import SelectOptionGroup from '../SelectOptionGroup'; +import DTableIcon from '../DTableIcon'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import ModalPortal from '../ModalPortal'; +import { getEventClassName } from '../utils/utils'; import './index.css'; @@ -22,8 +24,8 @@ class DTableCustomizeSelect extends Component { so it can be closed when other select is clicked. */ if (this.state.isShowSelectOptions) event.stopPropagation(); - let eventClassName = event.target.className; - if (this.props.isLocked || eventClassName.indexOf('option-search-control') > -1 || eventClassName === 'option-group-search') return; + const eventClassName = getEventClassName(event); + if (this.props.isLocked || eventClassName.indexOf('option-search-control') > -1 || eventClassName === 'seatable-select-search') return; // Prevent closing by pressing the spacebar in the search input if (event.target.value === '') return; this.setState({ @@ -32,7 +34,7 @@ class DTableCustomizeSelect extends Component { }; onClick = (event) => { - if (this.props.isShowSelected && event.target.className.includes('icon-fork-number')) { + if (this.props.isShowSelected && getEventClassName(event).includes('icon-fork-number')) { return; } if (!this.selector.contains(event.target)) { @@ -79,19 +81,19 @@ class DTableCustomizeSelect extends Component { return (
this.selector = node} - className={classnames('dtable-select custom-select', + className={classnames('seatable-customize-select custom-select', { 'focus': this.state.isShowSelectOptions }, { 'disabled': isLocked }, className )} - onClick={this.onSelectToggle}> + onClick={this.onSelectToggle} + >
{value && value.label ? {value.label} - : - {placeholder} + : {placeholder} } - {!isLocked && } + {!isLocked && }
{this.state.isShowSelectOptions && !isInModal && (
{option.name}
-
- {selectedOption?.id === option.id && } -
) }; @@ -81,13 +78,10 @@ class FilterItemUtils { static generatorMultipleSelectOption(option, filterTerm) { return { - value: { columnOption: option }, + value: option, label: (
{option.name}
-
- {filterTerm.indexOf(option.id) > -1 && } -
) }; diff --git a/src/DTableFiltersPopover/widgets/collaborator-filter/index.js b/src/DTableFiltersPopover/widgets/collaborator-filter/index.js index d9efc883..4493aa5b 100644 --- a/src/DTableFiltersPopover/widgets/collaborator-filter/index.js +++ b/src/DTableFiltersPopover/widgets/collaborator-filter/index.js @@ -30,7 +30,6 @@ class CollaboratorFilter extends Component { createCollaboratorOptions = (filterIndex, collaborators, filterTerm) => { return collaborators.map((collaborator) => { - let isSelected = filterTerm.findIndex(item => item === collaborator.email) > -1; return { value: { filterIndex, columnOption: collaborator }, label: ( @@ -50,9 +49,6 @@ class CollaboratorFilter extends Component {
-
- {isSelected && } -
) @@ -90,7 +86,8 @@ class CollaboratorFilter extends Component { ); }); - let value = selectedCollaborators ? { label: (<>{selectedCollaborators}) } : {}; + let selectCollaborators = Array.isArray(filterTerm) && filterTerm.length > 0 ? collaborators.filter(col => filterTerm.includes(col.email)) : []; + let value = selectedCollaborators ? { value: selectCollaborators, label: (<>{selectedCollaborators}) } : {}; let options = Array.isArray(filterTerm) ? this.createCollaboratorOptions(filterIndex, collaborators, filterTerm) : []; return ( { const { index, filter } = this.props; - const { columnOption: option } = value; - let newFilter = getUpdatedFilterBySelectMultiple(filter, option); + let newFilter = getUpdatedFilterBySelectMultiple(filter, value); this.resetState(newFilter); this.props.updateFilter(index, newFilter); }; onSelectCollaborator = (value) => { const { index, filter } = this.props; - const { columnOption: collaborator } = value; - let newFilter = getUpdatedFilterByCollaborator(filter, collaborator); + let newFilter = getUpdatedFilterByCollaborator(filter, value); this.resetState(newFilter); this.props.updateFilter(index, newFilter); }; onSelectCreator = (value) => { const { index, filter } = this.props; - const { columnOption: collaborator } = value; + const collaborator = value && (value.columnOption || value.value || value); + if (!collaborator || !collaborator.email) { + return; + } let newFilter = getUpdatedFilterByCreator(filter, collaborator); // the predicate is 'is' or 'is not' if (!newFilter) { @@ -352,10 +353,11 @@ class FilterItem extends React.Component { } const className = 'select-option-name multiple-select-option'; let labelArray = []; + let valueArray = []; if (Array.isArray(options) && Array.isArray(filterTerm)) { filterTerm.forEach((item) => { let inOption = options.find(option => option.id === item); - let optionStyle = { margin: '0 10px 0 0' }; + let optionStyle = { margin: '0 4px 0 0' }; let optionName = null; if (inOption) { optionName = inOption.name; @@ -365,6 +367,7 @@ class FilterItem extends React.Component { optionStyle.background = DELETED_OPTION_BACKGROUND_COLOR; optionName = getLocale(DELETED_OPTION_TIPS); } + valueArray.push( inOption ); labelArray.push( {optionName} @@ -372,7 +375,7 @@ class FilterItem extends React.Component { ); }); } - const selectedOptionNames = labelArray.length > 0 ? { label: ({labelArray}) } : {}; + const selectedOptionNames = labelArray.length > 0 ? { value: valueArray, label: ({labelArray}) } : {}; const dataOptions = options.map(option => { return FilterItemUtils.generatorMultipleSelectOption(option, filterTerm); @@ -466,7 +469,7 @@ class FilterItem extends React.Component { if (selectedOption) { const className = 'select-option-name single-select-option'; const style = { background: selectedOption.color, color: selectedOption.textColor || null }; - selectedOptionName = { label: {selectedOption.name} }; + selectedOptionName = { value: { columnOption: selectedOption }, label: {selectedOption.name} }; } let dataOptions = options.map(option => { @@ -546,6 +549,7 @@ class FilterItem extends React.Component { onSelectCollaborator={this.onSelectCreator} isInModal={this.props.isInModal} readOnly={readOnly} + filter_predicate={filter_predicate} /> ); } diff --git a/src/DTableFiltersPopover/widgets/filter-list/index.css b/src/DTableFiltersPopover/widgets/filter-list/index.css index 0b076a0c..b8b039fd 100644 --- a/src/DTableFiltersPopover/widgets/filter-list/index.css +++ b/src/DTableFiltersPopover/widgets/filter-list/index.css @@ -333,7 +333,7 @@ display: inline-flex; align-items: center; height: 20px; - margin-right: 10px; + margin-right: 4px; padding: 0 8px 0 2px; font-size: 13px; border-radius: 10px; @@ -412,14 +412,14 @@ .filter-header-icon { display: inline-block; - padding: 0 0.3125rem; + padding-right: 0.5rem; margin-left: -0.3125rem; } .filter-header-icon .dtable-font, .filter-header-icon .multicolor-icon { font-size: 14px; - color: #aaa; + color: var(--bs-icon-secondary-color); cursor: default; } diff --git a/src/DTableGroupSelect/index.css b/src/DTableGroupSelect/index.css deleted file mode 100644 index 142f671c..00000000 --- a/src/DTableGroupSelect/index.css +++ /dev/null @@ -1,96 +0,0 @@ -.group-select { - position: relative; -} - -.group-select.custom-select { - display: flex; - padding: 5px 10px; - border-radius: 3px; - align-items: center; - justify-content: space-between; - max-width: 900px; - user-select: none; - text-align: left; - border-color: 1px solid rgba(0, 40, 100, 0.12); - height: auto; - min-height: 38px; - cursor: pointer; -} - -.group-select.custom-select:focus, -.group-select.custom-select.focus { - border-color: #1991eb !important; - box-shadow: 0 0 0 2px rgba(70, 127, 207, 0.25); -} - -.group-select.custom-select.disabled:focus, -.group-select.custom-select.focus.disabled, -.group-select.custom-select.disabled:hover { - border-color: rgba(0, 40, 100, 0.12) !important; - box-shadow: unset; - cursor: default; -} - -.group-select .sf3-font-down { - display: inline-block; - color: #999; - transform: translateY(2px); - transition: all 0.1s; - font-size: 14px !important; -} - -.group-select .sf3-font-down:hover { - color: #666; -} - -.group-select .selected-option { - display: flex; - flex: 1; - overflow: hidden; - flex-wrap: nowrap; - align-items: center; - justify-content: space-between; - background: #fff; -} - -.group-select.dtable-ui-collaborator-selector .option-group .option-group-content { - padding: 10px; -} - -.group-select.custom-select.dtable-ui-collaborator-selector .option-group .option-group-content { - padding: 10px 0; -} - -.group-select.custom-select.dtable-ui-collaborator-selector .option { - padding: 5px 0 5px 10px !important; - line-height: 20px; -} - -.group-select .select-placeholder { - line-height: 1; - font-size: 14px; - white-space: nowrap; -} - -.group-select .selected-option-show { - display: flex; - flex-wrap: wrap; - gap: 4px; -} - -.group-select .selected-option-show .selected-option-item { - background-color: rgb(240, 240, 240); - border-radius: 16px; - display: flex; - align-items: center; -} - -.group-select .selected-option-show .selected-option-item .selected-option-item-name { - font-size: 13px; - color: #212529; -} - -.group-select .selected-option-show .selected-option-item .dtable-icon-x { - cursor: pointer; - color: rgb(103, 103, 103); -} diff --git a/src/DTableGroupSelect/index.js b/src/DTableGroupSelect/index.js deleted file mode 100644 index 03437265..00000000 --- a/src/DTableGroupSelect/index.js +++ /dev/null @@ -1,138 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import ModalPortal from '../ModalPortal'; -import SelectOptionGroup from './select-option-group'; - -import './index.css'; - -class DTableGroupSelect extends Component { - - constructor(props) { - super(props); - this.state = { - isShowSelectOptions: false - }; - } - - onSelectToggle = (event) => { - event.preventDefault(); - if (this.state.isShowSelectOptions) event.stopPropagation(); - let eventClassName = event.target.className; - if (eventClassName.indexOf('dtable-icon-x') > -1 || eventClassName === 'option-group-search') return; - if (event.target.value === '') return; - this.setState({ - isShowSelectOptions: !this.state.isShowSelectOptions - }); - }; - - onClickOutside = (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 }); - }; - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.selectedOptions.length !== this.props.selectedOptions.length) { - // when selectedOptions change and dom rendered, calculate top - setTimeout(() => { - this.forceUpdate(); - }, 1); - } - } - - getSelectedOptionTop = () => { - if (!this.selector) return 38; - const { height } = this.selector.getBoundingClientRect(); - return height; - }; - - getFilterOptions = (searchValue) => { - const { options } = this.props; - const validSearchVal = searchValue.trim().toLowerCase(); - if (!validSearchVal) return options || []; - return options.filter(option => option.name.toLowerCase().includes(validSearchVal)); - }; - - render() { - let { className, selectedOptions, options, placeholder, searchPlaceholder, noOptionsPlaceholder, isInModal } = this.props; - return ( -
this.selector = node} - className={classnames('group-select custom-select', - { 'focus': this.state.isShowSelectOptions }, - className - )} - onClick={this.onSelectToggle}> -
- {selectedOptions.length > 0 ? - - {selectedOptions.map(item => - - {item.name} - { this.props.onDeleteOption(item); }}> - - )} - - : - {placeholder} - } - -
- {this.state.isShowSelectOptions && !isInModal && ( - - )} - {this.state.isShowSelectOptions && isInModal && ( - - - - )} -
- ); - } -} - -DTableGroupSelect.propTypes = { - className: PropTypes.string, - selectedOptions: PropTypes.array, - options: PropTypes.array, - placeholder: PropTypes.string, - onSelectOption: PropTypes.func, - onDeleteOption: PropTypes.func, - searchable: PropTypes.bool, - searchPlaceholder: PropTypes.string, - noOptionsPlaceholder: PropTypes.string, - isInModal: PropTypes.bool, // if select component in a modal (option group need ModalPortal to show) -}; - -export default DTableGroupSelect; diff --git a/src/DTableGroupSelect/option.js b/src/DTableGroupSelect/option.js deleted file mode 100644 index 0c2d46cb..00000000 --- a/src/DTableGroupSelect/option.js +++ /dev/null @@ -1,47 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -class Option extends Component { - - onSelectOption = (e) => { - e.stopPropagation(); - this.props.onSelectOption(this.props.option); - }; - - onMouseEnter = () => { - if (!this.props.disableHover) { - this.props.changeIndex(this.props.index); - } - }; - - onMouseLeave = () => { - if (!this.props.disableHover) { - this.props.changeIndex(-1); - } - }; - - render() { - return ( -
{this.props.children} -
- ); - } -} - -Option.propTypes = { - index: PropTypes.number, - isActive: PropTypes.bool, - changeIndex: PropTypes.func, - option: PropTypes.object, - children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), - onSelectOption: PropTypes.func, - disableHover: PropTypes.bool, -}; - -export default Option; diff --git a/src/DTableGroupSelect/select-option-group.css b/src/DTableGroupSelect/select-option-group.css deleted file mode 100644 index 90f53cc9..00000000 --- a/src/DTableGroupSelect/select-option-group.css +++ /dev/null @@ -1,54 +0,0 @@ -.group-selector .option { - display: block; - width: 100%; - line-height: 24px; - padding: 6px 10px; - clear: both; - font-weight: 400; - text-align: inherit; - background-color: transparent; - border: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: flex; - align-items: center; - justify-content: space-between; -} - -.group-selector .option .dtable-icon-check-mark { - font-size: 12px; - color: #798d99; -} - -.group-selector .option.option-active { - background-color: #20a0ff; - color: #fff; - cursor: pointer; -} - -.group-selector .option.option-active .dtable-icon-check-mark, -.group-selector .option.option-active .select-option-name { - color: #fff !important; -} - -.group-selector .option .select-option-name .single-select-option { - margin: 0 0 0 12px; -} - -.group-selector .option .select-option-name .multiple-select-option { - margin: 0; -} - -.group-selector .option-group-dtable-ui-single-select-selector .select-option-name { - display: flex; - align-items: center; - justify-content: space-between; -} - -.group-selector .option-group-dtable-ui-single-select-selector .option:hover, -.group-selector .option-group-dtable-ui-single-select-selector .option.option-active, -.group-selector .option-group-dtable-ui-multiple-select-selector .option:hover, -.group-selector .option-group-dtable-ui-multiple-select-selector .option.option-active { - background-color: #f5f5f5; -} diff --git a/src/DTableGroupSelect/select-option-group.js b/src/DTableGroupSelect/select-option-group.js deleted file mode 100644 index 2fd83a94..00000000 --- a/src/DTableGroupSelect/select-option-group.js +++ /dev/null @@ -1,218 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import DTableSearchInput from '../DTableSearchInput'; -import Option from './option'; -import KeyCodes from '../SelectOptionGroup/KeyCodes'; - -import './select-option-group.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; - this.searchInputRef = React.createRef(); - } - - componentDidMount() { - window.addEventListener('keydown', this.onHotKey); - document.addEventListener('mousedown', this.handleDocumentClick); - setTimeout(() => { - this.resetMenuStyle(); - }, 1); - } - - componentWillUnmount() { - this.filterOptions = null; - this.timer && clearTimeout(this.timer); - window.removeEventListener('keydown', this.onHotKey); - document.removeEventListener('mousedown', this.handleDocumentClick); - } - - handleDocumentClick = (e) => { - this.props.onClickOutside(e); - }; - - 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; - this.searchInputRef.current && this.searchInputRef.current.inputRef.focus(); - } - 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); - } - } 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) => { - this.setState({ searchVal: searchVal || '', activeIndex: -1, }); - }; - - clearValue = () => { - this.setState({ searchVal: '', activeIndex: -1, }); - }; - - renderOptGroup = (searchVal) => { - let { noOptionsPlaceholder, onSelectOption, selectedOptions } = this.props; - this.filterOptions = this.props.getFilterOptions(searchVal); - if (this.filterOptions.length === 0) { - return ( -
{noOptionsPlaceholder}
- ); - } - return this.filterOptions.map((option, index) => { - const isSelected = selectedOptions.some(item => item.id === option.id); - return ( - - ); - }); - }; - - render() { - const { searchPlaceholder, top, left, minWidth, isInModal, position, className } = this.props; - 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 ( -
this.optionGroupRef = ref} - style={style} - onMouseDown={this.onMouseDown} - > -
- -
-
this.optionGroupContentRef = ref}> - {this.renderOptGroup(searchVal)} -
-
- ); - } -} - -SelectOptionGroup.propTypes = { - top: PropTypes.number, - left: PropTypes.number, - minWidth: PropTypes.number, - options: PropTypes.array, - onSelectOption: PropTypes.func, - searchPlaceholder: PropTypes.string, - noOptionsPlaceholder: PropTypes.string, - onClickOutside: PropTypes.func.isRequired, - closeSelect: PropTypes.func.isRequired, - getFilterOptions: PropTypes.func.isRequired, - selectedOptions: PropTypes.array, - isInModal: PropTypes.bool, - position: PropTypes.object, - className: PropTypes.string, -}; - -export default SelectOptionGroup; diff --git a/src/DTableSelect/dtable-select-label.css b/src/DTableSelect/dtable-select-label.css index 4dbf522a..acc87b1d 100644 --- a/src/DTableSelect/dtable-select-label.css +++ b/src/DTableSelect/dtable-select-label.css @@ -1,10 +1,15 @@ .dtable-select-check-icon { - width: 20px; - height: 20px; + width: 14px; + height: 14px; text-align: center; } .dtable-select-check-icon .dtable-font { + font-size: 14px; + color: var(--bs-icon-color); +} + +.seatable-option .select-option-name .seatable-tip-default { font-size: 12px; - color: #798d99; + margin-top: 4px } diff --git a/src/DTableSelect/dtable-select-label.js b/src/DTableSelect/dtable-select-label.js index 2deca46f..a54a7ccf 100644 --- a/src/DTableSelect/dtable-select-label.js +++ b/src/DTableSelect/dtable-select-label.js @@ -9,7 +9,7 @@ function DTableSelectLabel({ classname = '', name = '', isSelect = false }) { {name} {isSelect &&
- +
} diff --git a/src/DTableSelect/index.js b/src/DTableSelect/index.js index 45a87799..f20e248c 100644 --- a/src/DTableSelect/index.js +++ b/src/DTableSelect/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Select from 'react-select'; -import { MenuSelectStyle, DropdownIndicator, ClearIndicator, MenuList, Option } from './utils'; +import { MenuSelectStyle, DropdownIndicator, ClearIndicator, MenuList, Option, processOptionsWithClear, createHandleChange } from './utils'; export default class DTableSelect extends React.Component { @@ -49,11 +49,12 @@ export default class DTableSelect extends React.Component { const { options, onChange, value, isSearchable, placeholder, isMulti, menuPosition, isClearable, noOptionsMessage, classNamePrefix, style, innerRef, isDisabled, form, customFilterOption, autoFocus, className, closeMenuOnSelect, onMenuClose, components: userComponents } = this.props; const mergedComponents = { Option, DropdownIndicator, MenuList, ClearIndicator, ...userComponents }; + const processedOptions = processOptionsWithClear(options, isClearable); return (