diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f0e6fb7..e12818ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ on: jobs: release: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v1 diff --git a/.storybook/stories/Components/Select.stories.mdx b/.storybook/stories/Components/Select.stories.mdx index 0fac1fad..fdf26c4a 100644 --- a/.storybook/stories/Components/Select.stories.mdx +++ b/.storybook/stories/Components/Select.stories.mdx @@ -65,6 +65,10 @@ Basic usages for `Select` component.

Normal

+

Disabled

; + return e.preventDefault()} + onCut={(e) => e.preventDefault()} + onContextMenu={(e) => e.preventDefault()} /> { - const { multi, children } = this.props; + const { multi, children, showTip, option } = this.props; return multi ? ( <> {this.renderCheckbox()} - {children} + + {children} + ) : ( <> - {children} + + {children} + {this.renderIcon()} ); diff --git a/src/components/Select/Select.jsx b/src/components/Select/Select.jsx index 9202f5d3..5c961392 100644 --- a/src/components/Select/Select.jsx +++ b/src/components/Select/Select.jsx @@ -1,7 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import classNames from "classnames"; -import { get, isEmpty, isUndefined, isFunction } from "lodash"; +import { get, isEmpty, isUndefined, isFunction, isEqual } from "lodash"; import Tag from "../Tag"; import Icon from "../Icon"; @@ -44,6 +44,9 @@ export default class Select extends React.Component { onChange: PropTypes.func, optionRenderer: PropTypes.func, valueRenderer: PropTypes.func, + dorpdownRender: PropTypes.func, + disableRemoteSearch: PropTypes.bool, + showTip: PropTypes.bool, }; static defaultProps = { @@ -60,6 +63,8 @@ export default class Select extends React.Component { options: [], onChange() {}, onPaging() {}, + disableRemoteSearch: false, + showTip: false, }; state = { @@ -67,6 +72,8 @@ export default class Select extends React.Component { value: this.props.multi ? [] : "", inputValue: "", inputVisible: true, + options: this.props.options ? this.props.options : [], + options_copy: this.props.options ? this.props.options : [], }; inputRef = React.createRef(); @@ -87,12 +94,16 @@ export default class Select extends React.Component { componentDidUpdate(prevProps, prevState) { const { value, options } = this.props; + const equal = isEqual(prevProps.options, options); if (prevProps.options.length !== options.length) { this.reachBottom = false; + this.setState({ options, options_copy: options }); + } else if (!equal) { + this.setState({ options, options_copy: options }); } if (!isUndefined(value) && value !== prevState.value) { - if (isEmpty(value)) { + if (value === "" || value === null) { this.setState({ value, inputValue: value, @@ -285,7 +296,7 @@ export default class Select extends React.Component { const { onFetch, pagination = {} } = this.props; const { page = 1 } = pagination; const { scrollTop, scrollHeight, clientHeight } = e.target; - if (scrollTop + clientHeight - scrollHeight >= 0) { + if (Math.abs(scrollTop + clientHeight - scrollHeight) <= 1) { this.reachBottom = true; onFetch({ page: page + 1, more: true }); } @@ -301,10 +312,11 @@ export default class Select extends React.Component { }; handleInputStatus = (visible) => { - const { value, inputValue } = this.state; - const { multi, searchable, options } = this.props; + const { value, inputValue, options } = this.state; + const { multi, searchable } = this.props; const option = options.find((item) => item.value === value) || {}; const currentInputValue = multi ? "" : option.value || inputValue || ""; + this.setState( { inputVisible: visible, inputValue: currentInputValue }, () => { @@ -320,7 +332,7 @@ export default class Select extends React.Component { handleInputChange = (e) => { const value = e.target.value; - const { multi, searchable, onFetch, onChange } = this.props; + const { multi, searchable, onFetch, disableRemoteSearch } = this.props; const newState = { inputValue: value }; if (!multi) { @@ -333,11 +345,17 @@ export default class Select extends React.Component { : `${get(this.inputValueRef, "current.clientWidth", 0) + 5}px`; this.updateInputDOM({ width }); } - onChange(this.state.value); }); - if (isFunction(onFetch)) { + if (isFunction(onFetch) && !disableRemoteSearch) { onFetch({ name: value }); + } else if (searchable) { + const options = value + ? this.state.options_copy.filter((item) => + item.label.toLowerCase().includes(value.toLowerCase()) + ) + : this.state.options_copy; + this.setState({ options }); } }; @@ -347,7 +365,9 @@ export default class Select extends React.Component { let { value } = this.state; value.splice(i, 1); - this.setState({ value }); + this.setState({ value }, () => { + this.props.onChange(value); + }); if (isEmpty(value)) { if (this.props.searchable) { @@ -360,18 +380,27 @@ export default class Select extends React.Component { handleClearValue = (e) => { e.nativeEvent.stopImmediatePropagation(); e.stopPropagation(); - const { multi, searchable, onChange, onFetch } = this.props; + const { + multi, + searchable, + onChange, + onFetch, + disableRemoteSearch, + } = this.props; this.setState( { value: multi ? [] : "", inputValue: "", inputVisible: true, + visible: false, }, () => { onChange(); - if (searchable && isFunction(onFetch)) { + if (searchable && isFunction(onFetch) && !disableRemoteSearch) { onFetch(); + } else if (searchable) { + this.setState({ options: this.state.options_copy }); } } ); @@ -410,8 +439,8 @@ export default class Select extends React.Component { }; renderOptions = () => { - const { visible } = this.state; - const { options, isLoading, pagination = {}, onFetch } = this.props; + const { visible, options } = this.state; + const { isLoading, pagination = {}, onFetch } = this.props; const { page = 1, total = 0, limit = 10 } = pagination; if (!visible || !get(this.selectRef, "current")) { @@ -437,7 +466,7 @@ export default class Select extends React.Component { > {options.length === 0 && !isLoading ? this.renderEmpty() - : this.renderOption(options)} + : this.renderDorpdownRender(options)} {isLoading && (
@@ -455,16 +484,26 @@ export default class Select extends React.Component { ); }; + renderDorpdownRender = (options) => { + const { dorpdownRender } = this.props; + const optionNode = this.renderOption(options); + + if (dorpdownRender) { + return dorpdownRender(optionNode); + } + return optionNode; + }; + renderOption = (options) => { const { value } = this.state; - const { multi, optionRenderer } = this.props; + const { multi, optionRenderer, showTip } = this.props; const isActive = (v) => multi ? value.includes(v.value) : v.disabled ? false : v.value === value; - return options.map((item, i) => { + return options.map((item) => { if (item.options) { return ( -
+

{item.label}

{this.renderOption(item.options)}
@@ -472,12 +511,13 @@ export default class Select extends React.Component { } else { return ( @@ -488,7 +528,8 @@ export default class Select extends React.Component { renderInput = () => { const { searchable, name, placeholder, multi } = this.props; - const { inputVisible, inputValue, value } = this.state; + const { inputVisible, inputValue, value, options } = this.state; + const multiClassName = multi && searchable ? classNames("select-input", { @@ -507,6 +548,19 @@ export default class Select extends React.Component { : "" : localePlaceholder; + let option = { label: value, value }; + options.forEach((item) => { + if (item.value === value) { + option = item; + } else if (item.options) { + item.options.forEach((op) => { + if (op.value === value) { + option = op; + } + }); + } + }); + return (
{ - const { valueRenderer, options } = this.props; + const { valueRenderer, showTip } = this.props; + const { options } = this.state; const option = options.find((item) => item.value === value) || { label: value, @@ -536,7 +591,10 @@ export default class Select extends React.Component { return ( - + {valueRenderer ? valueRenderer(option) : option.label} { - const { value, inputVisible } = this.state; - const { multi, valueRenderer, options } = this.props; + const { value, inputVisible, options } = this.state; + const { multi, valueRenderer, showTip } = this.props; if (multi) { if (isEmpty(value)) { @@ -581,6 +639,7 @@ export default class Select extends React.Component { "select-value-opacity": inputVisible, "select-multi-value": multi, })} + title={showTip ? option?.label || "" : ""} > {valueRenderer ? valueRenderer(option) : option.label}
diff --git a/src/components/Select/styles.scss b/src/components/Select/styles.scss index 70fe3651..f17c26b8 100644 --- a/src/components/Select/styles.scss +++ b/src/components/Select/styles.scss @@ -48,6 +48,7 @@ display: inline-flex; align-items: center; max-width: 100%; + z-index: 1; @include TypographySymbolText; @include ellipsis; } diff --git a/src/components/Table/Table.jsx b/src/components/Table/Table.jsx index fbfc9d3f..7eca2b11 100644 --- a/src/components/Table/Table.jsx +++ b/src/components/Table/Table.jsx @@ -1,7 +1,7 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import classNames from "classnames"; -import { get, isEmpty } from "lodash"; +import { get, isEmpty, isEqual } from "lodash"; import ColGroup from "./ColGroup"; import Thead from "./Thead"; @@ -43,7 +43,7 @@ export default class Table extends Component { } componentDidUpdate(prevProps, prevState) { - if (this.props.columns.length !== prevProps.columns.length) { + if (!isEqual(this.props.columns, prevProps.columns)) { const [heads, columns] = this.getColumns(this.props); this.setState({ heads, diff --git a/src/components/Table/Tbody.jsx b/src/components/Table/Tbody.jsx index bdfcc1d4..c2dfe6a3 100644 --- a/src/components/Table/Tbody.jsx +++ b/src/components/Table/Tbody.jsx @@ -15,6 +15,7 @@ export default class Tbody extends Component { rowSelection, expandedRowRender, defaultExpandAllRows, + rowExpandable, }) => ( {dataSource.map((item) => ( @@ -27,6 +28,7 @@ export default class Tbody extends Component { rowSelection={rowSelection} expandedRowRender={expandedRowRender} defaultExpandAllRows={defaultExpandAllRows} + rowExpandable={rowExpandable} > {(column) => ( {column.render(value, record)}; + return ( + {column.render(value, record)} + ); } if (!"dataIndex" in column) { return ; } - return {value}; + return {value}; } diff --git a/src/components/Table/Tr.jsx b/src/components/Table/Tr.jsx index d93479ee..cfc4d200 100644 --- a/src/components/Table/Tr.jsx +++ b/src/components/Table/Tr.jsx @@ -87,8 +87,16 @@ export default class Tr extends Component { 0 + selectedRowKeys.length === + records.filter((item) => { + if (getCheckboxProps) { + const result = getCheckboxProps(item); + if (result && result.disabled) { + return false; + } + } + return true; + }).length && selectedRowKeys.length > 0 } indeterminate={ selectedRowKeys.length < records.length && @@ -118,8 +126,9 @@ export default class Tr extends Component { } renderExpandToggle() { - const { expandedRowRender } = this.props; + const { record, expandedRowRender, rowExpandable } = this.props; const { showExpand } = this.state; + const disabled = rowExpandable && !rowExpandable(record); if (!expandedRowRender) { return null; @@ -130,7 +139,8 @@ export default class Tr extends Component { ); @@ -144,14 +154,15 @@ export default class Tr extends Component { return null; } + const colSpan = rowSelection ? columns.length + 2 : columns.length + 1; + return ( - {rowSelection && } - {expandedRowRender(record)} + {expandedRowRender(record)} ); } diff --git a/src/components/Table/styles.scss b/src/components/Table/styles.scss index 78b1f362..6b2326ef 100644 --- a/src/components/Table/styles.scss +++ b/src/components/Table/styles.scss @@ -5,16 +5,19 @@ width: 100%; text-align: left; + a { + color: $dark-color01; + &:hover { + color: $primary; + } + } + & > thead { & > tr > th { padding: 16px 12px; @include TypographySymbolText; color: $dark-color01; - a { - color: $dark-color01; - } - &[colspan] { padding: 8px 12px; text-align: center; @@ -68,10 +71,6 @@ &:hover { & > td { background-color: $bg-color; - - a { - color: $primary; - } } } @@ -97,6 +96,10 @@ border-right: 1px solid $primary; } } + + &.table-row-expand > td { + background-color: $light-color02; + } } .table-row-selected + tr { diff --git a/src/components/Tag/Tag.jsx b/src/components/Tag/Tag.jsx index 941e52a3..31b32236 100644 --- a/src/components/Tag/Tag.jsx +++ b/src/components/Tag/Tag.jsx @@ -11,6 +11,8 @@ export default class Tag extends React.Component { "info", "warning", "primary", + "critical", + "error", ]), className: PropTypes.string, }; diff --git a/src/components/Tag/styles.scss b/src/components/Tag/styles.scss index b6a049dc..073a3998 100644 --- a/src/components/Tag/styles.scss +++ b/src/components/Tag/styles.scss @@ -1,37 +1,49 @@ +/** @format */ + @import "../../styles/variables"; @import "../../styles/mixins"; .tag { - display: inline-block; - height: 20px; - padding: 0 4px; - font-size: $size-small; - line-height: 20px; - font-weight: 600; - border-radius: 2px; - - &-default { - color: $white; - background-color: $dark-color07; - } - - &-secondary { - color: $dark-color06; - background-color: $light-color03; - } - - &-warning { - color: $yellow-color05; - background-color: $yellow-color01; - } - - &-primary { - color: $green-color05; - background-color: $green-color01; - } - - &-info { - color: $blue-color05; - background-color: $blue-color01; - } + display: inline-block; + height: 20px; + padding: 0 4px; + font-size: $size-small; + line-height: 20px; + font-weight: 600; + border-radius: 2px; + + &-default { + color: $white; + background-color: $dark-color07; + } + + &-secondary { + color: $dark-color06; + background-color: $light-color03; + } + + &-warning { + color: $yellow-color05; + background-color: $yellow-color01; + } + + &-primary { + color: $green-color05; + background-color: $green-color01; + } + + &-info { + color: $blue-color05; + background-color: $blue-color01; + } + + &-critical { + color: $white; + background-color: $red-color03; + } + + &-error { + color: $white; + background-color: $yellow-color03; + } } diff --git a/yarn.lock b/yarn.lock index 7a5354d3..3b4e2fab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12177,9 +12177,9 @@ path-key@^3.0.0, path-key@^3.1.0: integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" @@ -15121,9 +15121,9 @@ tmp@^0.0.33: os-tmpdir "~1.0.2" tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-absolute-glob@^2.0.0: version "2.0.2" @@ -16284,16 +16284,16 @@ write@1.0.3: mkdirp "^0.5.1" ws@^5.2.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" - integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + version "5.2.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.4.tgz#c7bea9f1cfb5f410de50e70e82662e562113f9a7" + integrity sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ== dependencies: async-limiter "~1.0.0" ws@^7.2.3: - version "7.3.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" - integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== xdg-basedir@^3.0.0: version "3.0.0"