index.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  21. import { Input, Checkbox, Tag, Tooltip } from 'antd'
  22. import { ICheckPanelProps } from './types'
  23. import { getSeparatedContent } from './utils'
  24. import debounce from 'lodash/debounce'
  25. import concat from 'lodash/concat'
  26. import styles from './CheckPanel.less'
  27. const CheckPanel: React.FC<ICheckPanelProps> = (
  28. {
  29. dataSource,
  30. defaultKeys,
  31. placeholder,
  32. labelKey,
  33. valueKey,
  34. labelInValue,
  35. closableByTag,
  36. tokenSeparators,
  37. onChange,
  38. }
  39. ) => {
  40. const label = labelKey || 'label'
  41. const value = valueKey || 'value'
  42. const [checkedKeys, setCheckedKeys] = useState<any[]>([])
  43. const [targets, setTargets] = useState<any[]>([])
  44. const [filterValue, setFilterValue] = useState<string>('')
  45. useEffect(() => {
  46. if (defaultKeys && defaultKeys.length) {
  47. setCheckedKeys(defaultKeys)
  48. setTargets(dataSource.filter(v => defaultKeys.some(key => key === v[value])))
  49. }
  50. }, [])
  51. const filterDataSource = useMemo(() => {
  52. if (tokenSeparators && tokenSeparators.length) {
  53. const separatedContent = getSeparatedContent(filterValue, tokenSeparators)
  54. const pathLabels = separatedContent && separatedContent.length ? separatedContent : [filterValue]
  55. return dataSource
  56. .filter(data => pathLabels.some(key => data[label].includes(key)))
  57. }
  58. return dataSource
  59. .filter((data) => data[label].includes(filterValue))
  60. }, [filterValue])
  61. const debouncedSearch = useCallback(
  62. debounce((searchValue: string) => {
  63. setFilterValue(searchValue)
  64. }, 300),
  65. []
  66. )
  67. const panel = useMemo(() => ({
  68. indeterminate: !!checkedKeys.length && checkedKeys.length < dataSource.length,
  69. checkAll: checkedKeys.length === dataSource.length,
  70. }), [checkedKeys])
  71. const checkOrNot = useCallback(
  72. (value) => checkedKeys.some(key => key === value),
  73. [checkedKeys]
  74. )
  75. const handleChange = useCallback((key: string | number, checked: boolean) => {
  76. const holderKeys = concat([], checkedKeys)
  77. const holderTargets = concat([], targets)
  78. if (checked) {
  79. const index = dataSource.findIndex(item => item[value] === key)
  80. holderTargets.push(dataSource[index])
  81. holderKeys.push(key)
  82. } else {
  83. holderKeys.splice(holderKeys.findIndex(k => k === key), 1)
  84. holderTargets.splice(holderTargets.findIndex(tag => tag[value] === key), 1)
  85. }
  86. setCheckedKeys(holderKeys)
  87. setTargets(holderTargets)
  88. if (onChange) {
  89. onChange(labelInValue ? holderTargets : holderKeys)
  90. }
  91. }, [checkedKeys, targets, onChange])
  92. const onCheckAllChange = useCallback((checked: boolean) => {
  93. const keys = dataSource.map(item => item[value])
  94. const items = [...dataSource]
  95. setCheckedKeys(checked ? keys : [])
  96. setTargets(checked ? items : [])
  97. if (onChange) {
  98. onChange(labelInValue ? items : keys)
  99. }
  100. }, [onChange])
  101. return (
  102. <div className={styles.checkPanelWrapper}>
  103. <div className={styles.leftWrapper}>
  104. <div className={styles.headWrapper}>
  105. <Checkbox
  106. indeterminate={panel.indeterminate}
  107. checked={panel.checkAll}
  108. onChange={e => onCheckAllChange(e.target.checked)}
  109. >{filterDataSource.length} 项</Checkbox>
  110. <div>列表</div>
  111. </div>
  112. <div className={styles.contentWrapper}>
  113. <Input.Search
  114. placeholder={placeholder || '请输入关键词'}
  115. onChange={e => debouncedSearch(e.target.value)}
  116. />
  117. {/* <div className={styles.tips}></div> */}
  118. <div className={styles.optionRow}>
  119. {filterDataSource.length ? filterDataSource.map((item, index) => (
  120. <Checkbox
  121. key={`${item[value]}-${index}`}
  122. checked={checkOrNot(item[value])}
  123. value={item[value]}
  124. onChange={e => handleChange(e.target.value, e.target.checked)}
  125. >
  126. <Tooltip title={item[label]}>
  127. {item[label]}
  128. </Tooltip>
  129. </Checkbox>
  130. )) : null}
  131. </div>
  132. </div>
  133. </div>
  134. <div className={styles.rightWrapper}>
  135. <div className={styles.headWrapper}>
  136. <div>{targets.length} 项</div>
  137. <div>已选</div>
  138. </div>
  139. <div className={styles.tagWrapper}>
  140. {
  141. targets.length ? targets.map((tag, index) => (
  142. <Tag
  143. key={`${tag[value]}-${index}`}
  144. color="#108ee9"
  145. closable={closableByTag}
  146. onClose={e => {
  147. e.preventDefault()
  148. handleChange(tag[value], false)
  149. }}
  150. >
  151. {tag[label]}
  152. </Tag>
  153. )) : null
  154. }
  155. </div>
  156. </div>
  157. </div>
  158. )
  159. }
  160. export default CheckPanel