From 46149b660bce9565aa5b1f60b3407c8169971971 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sat, 17 Oct 2020 22:19:52 +0800 Subject: [PATCH] feat: select auto-complete support virtual list --- components/auto-complete/index.tsx | 50 +- components/auto-complete/style/index.less | 82 +- components/pagination/MiniSelect.jsx | 2 +- components/select/index.tsx | 162 ++-- components/select/index1.tsx | 2 +- components/select/style/index.less | 754 ++++++------------ .../select/style/{index.js => index.tsx} | 0 components/select/style/multiple.less | 206 +++++ components/select/style/rtl.less | 188 +++++ components/select/style/single.less | 180 +++++ components/style/themes/default.less | 16 +- components/tree-select/index.jsx | 2 +- components/tree-select/interface.jsx | 2 +- .../vc-select2/Selector/MultipleSelector.tsx | 11 +- components/vc-select2/generate.tsx | 93 ++- components/vc-select2/index.ts | 10 +- tsconfig.json | 1 - 17 files changed, 1005 insertions(+), 756 deletions(-) rename components/select/style/{index.js => index.tsx} (100%) create mode 100644 components/select/style/multiple.less create mode 100644 components/select/style/rtl.less create mode 100644 components/select/style/single.less diff --git a/components/auto-complete/index.tsx b/components/auto-complete/index.tsx index 08c2063b2..8b81626c3 100644 --- a/components/auto-complete/index.tsx +++ b/components/auto-complete/index.tsx @@ -1,18 +1,21 @@ import { App, defineComponent, inject, provide } from 'vue'; -import { Option, OptGroup } from '../vc-select'; import Select, { SelectProps } from '../select'; import Input from '../input'; import InputElement from './InputElement'; import PropTypes from '../_util/vue-types'; import { defaultConfigProvider } from '../config-provider'; import { getComponent, getOptionProps, isValidElement, getSlot } from '../_util/props-util'; +import Omit from 'omit.js'; +import warning from '../_util/warning'; + +const { Option, OptGroup } = Select; function isSelectOptionOrSelectOptGroup(child: any): Boolean { return child && child.type && (child.type.isSelectOption || child.type.isSelectOptGroup); } const AutoCompleteProps = { - ...SelectProps, + ...SelectProps(), dataSource: PropTypes.array, dropdownMenuStyle: PropTypes.style, optionLabelProp: PropTypes.string, @@ -39,7 +42,12 @@ const AutoComplete = defineComponent({ }, Option: { ...Option, name: 'AAutoCompleteOption' }, OptGroup: { ...OptGroup, name: 'AAutoCompleteOptGroup' }, - setup() { + setup(props, { slots }) { + warning( + !('dataSource' in props || 'dataSource' in slots), + 'AutoComplete', + '`dataSource` is deprecated, please use `options` instead.', + ); return { configProvider: inject('configProvider', defaultConfigProvider), popupRef: null, @@ -59,11 +67,7 @@ const AutoComplete = defineComponent({ getInputElement() { const children = getSlot(this); const element = children.length ? children[0] : ; - return ( - - {element} - - ); + return {element}; }, focus() { @@ -80,8 +84,8 @@ const AutoComplete = defineComponent({ }, render() { - const { size, prefixCls: customizePrefixCls, optionLabelProp, dataSource } = this; - + const { size, prefixCls: customizePrefixCls, dataSource } = this; + let optionChildren: any; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('select', customizePrefixCls); const { class: className } = this.$attrs as any; @@ -92,22 +96,28 @@ const AutoComplete = defineComponent({ [`${prefixCls}-show-search`]: true, [`${prefixCls}-auto-complete`]: true, }; - - let options; const childArray = getSlot(this, 'dataSource'); if (childArray.length && isSelectOptionOrSelectOptGroup(childArray[0])) { - options = childArray; + optionChildren = childArray; } else { - options = dataSource + optionChildren = dataSource ? dataSource.map((item: any) => { if (isValidElement(item)) { return item; } switch (typeof item) { case 'string': - return ; + return ( + + ); case 'object': - return ; + return ( + + ); default: throw new Error( 'AutoComplete[dataSource] only supports type `string[] | Object[]`.', @@ -117,17 +127,17 @@ const AutoComplete = defineComponent({ : []; } const selectProps = { - ...getOptionProps(this), + ...Omit(getOptionProps(this), ['dataSource', 'optionLabelProp'] as any), ...this.$attrs, mode: Select.SECRET_COMBOBOX_MODE_DO_NOT_USE, - optionLabelProp, + // optionLabelProp, getInputElement: this.getInputElement, notFoundContent: getComponent(this, 'notFoundContent'), - placeholder: '', + // placeholder: '', class: cls, ref: this.saveSelect, }; - return ; + return ; }, }); diff --git a/components/auto-complete/style/index.less b/components/auto-complete/style/index.less index 6187de2c7..043091876 100644 --- a/components/auto-complete/style/index.less +++ b/components/auto-complete/style/index.less @@ -9,84 +9,8 @@ .@{autocomplete-prefix-cls} { .reset-component; - &.@{select-prefix-cls} { - .@{select-prefix-cls} { - &-selection { - border: 0; - box-shadow: none; - &__rendered { - height: 100%; - margin-right: 0; - margin-left: 0; - line-height: @input-height-base; - } - &__placeholder { - margin-right: (@input-padding-horizontal-base + 1px); - margin-left: (@input-padding-horizontal-base + 1px); - } - - &--single { - height: auto; - } - } - } - - // Fix https://github.com/ant-design/ant-design/issues/7800 - .@{select-prefix-cls}-search--inline { - position: static; - float: left; - } - - &-allow-clear { - .@{select-prefix-cls}-selection:hover .@{select-prefix-cls}-selection__rendered { - margin-right: 0 !important; - } - } - - .@{input-prefix-cls} { - height: @input-height-base; - line-height: @line-height-base; - background: transparent; - border-width: @border-width-base; - &:focus, - &:hover { - .hover; - } - &[disabled] { - .disabled; - - background-color: transparent; - } - } - - &-lg { - .@{select-prefix-cls}-selection__rendered { - line-height: @input-height-lg; - } - .@{input-prefix-cls} { - height: @input-height-lg; - padding-top: @input-padding-vertical-lg; - padding-bottom: @input-padding-vertical-lg; - } - } - - &-sm { - .@{select-prefix-cls}-selection__rendered { - line-height: @input-height-sm; - } - .@{input-prefix-cls} { - height: @input-height-sm; - padding-top: @input-padding-vertical-sm; - padding-bottom: @input-padding-vertical-sm; - } - } - } -} - -// https://github.com/ant-design/ant-design/issues/14156 -.@{input-prefix-cls}-group > .@{autocomplete-prefix-cls} { - .@{select-prefix-cls}-search__field.@{input-prefix-cls}-affix-wrapper { - display: inline; - float: none; + // https://github.com/ant-design/ant-design/issues/22302 + .@{select-prefix-cls}-clear { + right: 13px; } } diff --git a/components/pagination/MiniSelect.jsx b/components/pagination/MiniSelect.jsx index 80e63b66e..ec79f8981 100644 --- a/components/pagination/MiniSelect.jsx +++ b/components/pagination/MiniSelect.jsx @@ -4,7 +4,7 @@ import { getOptionProps, getSlot } from '../_util/props-util'; export default { inheritAttrs: false, props: { - ...SelectProps, + ...SelectProps(), }, Option: VcSelect.Option, render() { diff --git a/components/select/index.tsx b/components/select/index.tsx index 84592ea11..12f5cbbf9 100644 --- a/components/select/index.tsx +++ b/components/select/index.tsx @@ -1,7 +1,7 @@ import omit from 'omit.js'; import classNames from '../_util/classNames'; -import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, props } from '../vc-select2'; -import { OptionProps } from '../vc-select2/Option'; +import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, BaseProps } from '../vc-select2'; +import { OptionProps as OptionPropsType } from '../vc-select2/Option'; import { defaultConfigProvider } from '../config-provider'; import getIcons from './utils/iconUtil'; import { computed, defineComponent, inject, ref, VNodeChild, App, PropType } from 'vue'; @@ -10,7 +10,7 @@ import { tuple } from '../_util/type'; type RawValue = string | number; -export { OptionProps }; +export type OptionProps = OptionPropsType; export type OptionType = typeof Option; @@ -31,36 +31,40 @@ export interface InternalSelectProps extends Omit, 'mode'> } export interface SelectPropsTypes - extends Omit, 'inputIcon' | 'mode' | 'getInputElement' | 'backfill' | 'class' | 'style'> { + extends Omit< + InternalSelectProps, + 'inputIcon' | 'mode' | 'getInputElement' | 'backfill' | 'class' | 'style' + > { mode?: 'multiple' | 'tags'; } -export type SelectTypes = SelectPropsTypes -export const SelectProps = { - ...omit(props, ['inputIcon' ,'mode' ,'getInputElement' ,'backfill' ,'class' ,'style']), +export type SelectTypes = SelectPropsTypes; +export const SelectProps = () => ({ + ...omit(BaseProps(), ['inputIcon', 'mode', 'getInputElement', 'backfill', 'class', 'style']), value: { - type: [Array, Object, String, Number] as PropType + type: [Array, Object, String, Number] as PropType, }, defaultValue: { - type: [Array, Object, String, Number] as PropType + type: [Array, Object, String, Number] as PropType, }, + notFoundContent: PropTypes.VNodeChild, suffixIcon: PropTypes.VNodeChild, itemIcon: PropTypes.VNodeChild, - size: PropTypes.oneOf(tuple('small', 'middle', 'large', undefined, 'default')), - mode: PropTypes.oneOf(tuple('multiple', 'tags')), + size: PropTypes.oneOf(tuple('small', 'middle', 'large', 'default')), + mode: PropTypes.oneOf(tuple('multiple', 'tags', 'SECRET_COMBOBOX_MODE_DO_NOT_USE')), bordered: PropTypes.looseBool.def(true), transitionName: PropTypes.string.def('slide-up'), choiceTransitionName: PropTypes.string.def(''), -} +}); const Select = defineComponent({ name: 'ASelect', Option, OptGroup, inheritAttrs: false, - props: SelectProps, + props: SelectProps(), SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE', emits: ['change', 'update:value'], - setup(props: any, {attrs, emit}) { + setup(props: any, { attrs, emit }) { const selectRef = ref(null); const configProvider = inject('configProvider', defaultConfigProvider); @@ -77,8 +81,8 @@ const Select = defineComponent({ } }; - const mode = computed(()=>{ - const { mode } = props + const mode = computed(() => { + const { mode } = props; if ((mode as any) === 'combobox') { return undefined; @@ -90,35 +94,47 @@ const Select = defineComponent({ return mode; }); - - const mergedClassName = computed(()=> classNames( - { - [`${props.prefixCls}-lg`]: props.size === 'large', - [`${props.prefixCls}-sm`]: props.size === 'small', - [`${props.prefixCls}-rtl`]: props.direction === 'rtl', - [`${props.prefixCls}-borderless`]: !props.bordered, - }, - attrs.class, - )); - const triggerChange=(...args: any[])=>{ - console.log(args) - emit('update:value', ...args) - emit('change', ...args) - } + const prefixCls = computed(() => { + return configProvider.getPrefixCls('select', props.prefixCls); + }); + const mergedClassName = computed(() => + classNames( + { + [`${prefixCls.value}-lg`]: props.size === 'large', + [`${prefixCls.value}-sm`]: props.size === 'small', + [`${prefixCls.value}-rtl`]: props.direction === 'rtl', + [`${prefixCls.value}-borderless`]: !props.bordered, + }, + attrs.class, + ), + ); + const triggerChange = (...args: any[]) => { + console.log(args); + emit('update:value', ...args); + emit('change', ...args); + }; return { mergedClassName, mode, focus, blur, configProvider, - triggerChange - } + triggerChange, + prefixCls, + }; }, render() { - const {configProvider, mode, mergedClassName,triggerChange, $slots: slots, $props} = this as any; - const props: SelectTypes = $props const { - prefixCls: customizePrefixCls, + configProvider, + mode, + mergedClassName, + triggerChange, + prefixCls, + $slots: slots, + $props, + } = this as any; + const props: SelectTypes = $props; + const { notFoundContent, listHeight = 256, listItemHeight = 24, @@ -126,11 +142,10 @@ const Select = defineComponent({ dropdownClassName, direction, virtual, - dropdownMatchSelectWidth + dropdownMatchSelectWidth, } = props; - const { getPrefixCls, renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider - const prefixCls = getPrefixCls('select', customizePrefixCls); + const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider; const isMultiple = mode === 'multiple' || mode === 'tags'; @@ -138,20 +153,24 @@ const Select = defineComponent({ let mergedNotFound: VNodeChild; if (notFoundContent !== undefined) { mergedNotFound = notFoundContent; - } else if(slots.notFoundContent){ - mergedNotFound = slots.notFoundContent() + } else if (slots.notFoundContent) { + mergedNotFound = slots.notFoundContent(); } else if (mode === 'combobox') { mergedNotFound = null; } else { + console.log(111); mergedNotFound = renderEmpty('Select') as any; } // ===================== Icons ===================== - const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({ - ...this.$props, - multiple: isMultiple, - prefixCls, - }, slots); + const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons( + { + ...this.$props, + multiple: isMultiple, + prefixCls, + }, + slots, + ); const selectProps = omit(props, [ 'prefixCls', @@ -166,30 +185,33 @@ const Select = defineComponent({ const rcSelectRtlDropDownClassName = classNames(dropdownClassName, { [`${prefixCls}-dropdown-${direction}`]: direction === 'rtl', }); - - {slots?.default()} - - } -}) + return ( + + {slots?.default()} + + ); + }, +}); /* istanbul ignore next */ Select.install = function(app: App) { app.component(Select.name, Select); diff --git a/components/select/index1.tsx b/components/select/index1.tsx index b7388283b..f72d52c21 100644 --- a/components/select/index1.tsx +++ b/components/select/index1.tsx @@ -99,7 +99,7 @@ const Select = defineComponent({ ...SelectProps, showSearch: PropTypes.looseBool.def(false), transitionName: PropTypes.string.def('slide-up'), - choiceTransitionName: PropTypes.string.def('zoom'), + choiceTransitionName: PropTypes.string.def(''), }, propTypes: SelectPropTypes, setup() { diff --git a/components/select/style/index.less b/components/select/style/index.less index 182b2a686..5bfef19f4 100644 --- a/components/select/style/index.less +++ b/components/select/style/index.less @@ -2,603 +2,303 @@ @import '../../style/mixins/index'; @import '../../input/style/mixin'; -@select-prefix-cls: ~'@{ant-prefix}-select'; +@import './single'; +@import './multiple'; -.selection__clear() { - position: absolute; - top: 50%; - right: @control-padding-horizontal - 1px; - z-index: 1; - display: inline-block; - width: 12px; - height: 12px; - margin-top: -6px; - color: @disabled-color; - font-size: @font-size-sm; - font-style: normal; - line-height: 12px; - text-align: center; - text-transform: none; - background: @component-background; - cursor: pointer; - opacity: 0; - transition: color 0.3s ease, opacity 0.15s ease; - text-rendering: auto; - &::before { - display: block; +@select-prefix-cls: ~'@{ant-prefix}-select'; +@select-height-without-border: @input-height-base - 2 * @border-width-base; +@select-dropdown-edge-child-vertical-padding: @dropdown-edge-child-vertical-padding; + +.select-selector() { + position: relative; + background-color: @select-background; + border: @border-width-base @border-style-base @select-border-color; + border-radius: @border-radius-base; + transition: all 0.3s @ease-in-out; + + input { + cursor: pointer; } - &:hover { - color: @text-color-secondary; + + .@{select-prefix-cls}-show-search& { + cursor: text; + + input { + cursor: auto; + } + } + + .@{select-prefix-cls}-focused:not(.@{select-prefix-cls}-disabled)& { + .active(); + } + + .@{select-prefix-cls}-disabled& { + color: @disabled-color; + background: @input-disabled-bg; + cursor: not-allowed; + + input { + cursor: not-allowed; + } + } +} + +/* Reset search input style */ +.select-search-input-without-border() { + .@{select-prefix-cls}-selection-search-input { + margin: 0; + padding: 0; + background: transparent; + border: none; + outline: none; + appearance: none; + + &::-webkit-search-cancel-button { + display: none; + -webkit-appearance: none; + } } } .@{select-prefix-cls} { .reset-component; - position: relative; display: inline-block; - outline: 0; + cursor: pointer; - ul, - ol { - margin: 0; - padding: 0; - list-style: none; + &:not(.@{select-prefix-cls}-disabled):hover &-selector { + .hover(); } - > ul > li > a { - padding: 0; - background-color: @component-background; + // ======================== Selection ======================== + &-selection-item { + flex: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + // IE11 css hack. `*::-ms-backdrop,` is a must have + @media all and (-ms-high-contrast: none) { + *::-ms-backdrop, + & { + flex: auto; + } + } } - // arrow + // ======================= Placeholder ======================= + &-selection-placeholder { + flex: 1; + overflow: hidden; + color: @input-placeholder-color; + white-space: nowrap; + text-overflow: ellipsis; + + // IE11 css hack. `*::-ms-backdrop,` is a must have + @media all and (-ms-high-contrast: none) { + *::-ms-backdrop, + & { + flex: auto; + } + } + } + + // ========================== Arrow ========================== &-arrow { .iconfont-mixin(); - position: absolute; - top: 50%; + top: 53%; right: @control-padding-horizontal - 1px; + width: @font-size-sm; + height: @font-size-sm; margin-top: -@font-size-sm / 2; color: @disabled-color; font-size: @font-size-sm; line-height: 1; - transform-origin: 50% 50%; + text-align: center; + pointer-events: none; - & &-icon svg { + .@{iconfont-css-prefix} { + vertical-align: top; transition: transform 0.3s; + + > svg { + vertical-align: top; + } + + &:not(.@{select-prefix-cls}-suffix) { + pointer-events: auto; + } + } + + .@{select-prefix-cls}-disabled & { + cursor: not-allowed; } } - &-selection { - display: block; - box-sizing: border-box; - background-color: @select-background; - border: @border-width-base @border-style-base @select-border-color; - // strange align fix for chrome but works - // https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif - border-top-width: @border-width-base + 0.02px; - border-radius: @border-radius-base; - outline: none; - transition: all 0.3s @ease-in-out; - user-select: none; - + // ========================== Clear ========================== + &-clear { + position: absolute; + top: 50%; + right: @control-padding-horizontal - 1px; + z-index: 1; + display: inline-block; + width: @font-size-sm; + height: @font-size-sm; + margin-top: -@font-size-sm / 2; + color: @disabled-color; + font-size: @font-size-sm; + font-style: normal; + line-height: 1; + text-align: center; + text-transform: none; + background: @select-clear-background; + cursor: pointer; + opacity: 0; + transition: color 0.3s ease, opacity 0.15s ease; + text-rendering: auto; + &::before { + display: block; + } &:hover { - .hover; + color: @text-color-secondary; } - .@{select-prefix-cls}-focused &, - &:focus, - &:active { - .active; - } - - &__clear { - .selection__clear(); - } - - &:hover &__clear { + .@{select-prefix-cls}:hover & { opacity: 1; } - - &-selected-value { - float: left; - max-width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } } - &-no-arrow &-selection-selected-value { - padding-right: 0; - } + // ========================== Popup ========================== + &-dropdown { + .reset-component; + position: absolute; + top: -9999px; + left: -9999px; + z-index: @zindex-dropdown; + box-sizing: border-box; + padding: @select-dropdown-edge-child-vertical-padding 0; + overflow: hidden; + font-size: @font-size-base; + // Fix select render lag of long text in chrome + // https://github.com/ant-design/ant-design/issues/11456 + // https://github.com/ant-design/ant-design/issues/11843 + font-variant: initial; + background-color: @select-dropdown-bg; + border-radius: @border-radius-base; + outline: none; + box-shadow: @box-shadow-base; - &-disabled { - color: @disabled-color; - } - - &-disabled &-selection { - background: @input-disabled-bg; - cursor: not-allowed; - &:hover, - &:focus, - &:active { - border-color: @select-border-color; - box-shadow: none; + &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft, + &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft { + animation-name: antSlideUpIn; } - &__clear { - display: none; - visibility: hidden; - pointer-events: none; + &.slide-up-enter.slide-up-enter-active&-placement-topLeft, + &.slide-up-appear.slide-up-appear-active&-placement-topLeft { + animation-name: antSlideDownIn; } - } - &-disabled &-selection--multiple &-selection__choice { - padding-right: 10px; - color: fade(@black, 33%); - background: @background-color-base; - &__remove { + &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft { + animation-name: antSlideUpOut; + } + + &.slide-up-leave.slide-up-leave-active&-placement-topLeft { + animation-name: antSlideDownOut; + } + + &-hidden { display: none; } - } - &-selection--single { - position: relative; - height: @input-height-base; - cursor: pointer; - - .@{select-prefix-cls}-selection__rendered { - margin-right: 24px; - } - } - - &-no-arrow { - .@{select-prefix-cls}-selection__rendered { - margin-right: @control-padding-horizontal - 1px; - } - } - - &-selection__rendered { - position: relative; - display: block; - margin-right: @control-padding-horizontal - 1px; - margin-left: @control-padding-horizontal - 1px; - line-height: @input-height-base - 2px; - // https://github.com/ant-design/ant-design/issues/3481#issuecomment-254721026 - &::after { - display: inline-block; - width: 0; - visibility: hidden; - content: '.'; - pointer-events: none; - } - } - - &-lg { - font-size: @font-size-lg; - .@{select-prefix-cls}-selection--single { - height: @input-height-lg; - } - .@{select-prefix-cls}-selection__rendered { - line-height: @input-height-lg - 2px; - } - .@{select-prefix-cls}-selection--multiple { - min-height: @input-height-lg; - .@{select-prefix-cls}-selection__rendered { - li { - height: @input-height-lg - 8px; - line-height: @input-height-lg - 8px; - } - } - .@{select-prefix-cls}-selection__clear, - .@{select-prefix-cls}-arrow { - top: @input-height-lg / 2; - } - } - } - - &-sm { - .@{select-prefix-cls}-selection--single { - height: @input-height-sm; - } - .@{select-prefix-cls}-selection__rendered { - margin-left: @control-padding-horizontal-sm - 1px; - line-height: @input-height-sm - 2px; - } - .@{select-prefix-cls}-selection--multiple { - min-height: @input-height-sm; - .@{select-prefix-cls}-selection__rendered { - li { - height: @input-height-sm - 8px; - line-height: @input-height-sm - 10px; - } - } - .@{select-prefix-cls}-selection__clear, - .@{select-prefix-cls}-arrow { - top: @input-height-sm / 2; - } - } - .@{select-prefix-cls}-selection__clear, - .@{select-prefix-cls}-arrow { - right: @control-padding-horizontal-sm; - } - } - - &-disabled &-selection__choice__remove { - color: @disabled-color; - cursor: default; - &:hover { + &-empty { color: @disabled-color; } } - &-search__field__wrap { + // ========================= Options ========================= + .item() { position: relative; - display: inline-block; + display: block; + min-height: @select-dropdown-height; + padding: @select-dropdown-vertical-padding @control-padding-horizontal; + color: @text-color; + font-weight: normal; + font-size: @select-dropdown-font-size; + line-height: @select-dropdown-line-height; } - &-selection__placeholder, - &-search__field__placeholder { - // for TreeSelect compatibility - position: absolute; - top: 50%; - right: 9px; - left: 0; - max-width: 100%; - height: 20px; - margin-top: -10px; - overflow: hidden; - color: @input-placeholder-color; - line-height: 20px; - white-space: nowrap; - text-align: left; - text-overflow: ellipsis; + &-item-empty { + .item(); + color: @disabled-color; } - &-search__field__placeholder { - left: @control-padding-horizontal; - } + &-item { + .item(); - &-search__field__mirror { - position: absolute; - top: 0; - left: 0; - white-space: pre; - opacity: 0; - pointer-events: none; - } + cursor: pointer; + transition: background 0.3s ease; - &-search--inline { - position: absolute; - width: 100%; - height: 100%; - - .@{select-prefix-cls}-search__field__wrap { - width: 100%; - height: 100%; - } - - .@{select-prefix-cls}-search__field { - width: 100%; - height: 100%; - font-size: 100%; - line-height: 1; - background: transparent; - border-width: 0; - border-radius: @border-radius-base; - outline: 0; - } - - > i { - float: right; - } - } - - &-selection--multiple { - min-height: @input-height-base; - padding-bottom: 3px; - cursor: text; - .clearfix; - - .@{select-prefix-cls}-search--inline { - position: static; - float: left; - width: auto; - max-width: 100%; - padding: 0; - .@{select-prefix-cls}-search__field { - width: 0.75em; - max-width: 100%; - padding: 1px; - } - } - - .@{select-prefix-cls}-selection__rendered { - height: auto; - margin-bottom: -3px; - margin-left: 5px; - } - - .@{select-prefix-cls}-selection__placeholder { - margin-left: 6px; - } - - > ul > li, - .@{select-prefix-cls}-selection__rendered > ul > li { - height: @input-height-base - 8px; - // for tree-select - margin-top: 3px; - line-height: @input-height-base - 8px - 2px; - } - - .@{select-prefix-cls}-selection__choice { - position: relative; - float: left; - max-width: 99%; - margin-right: 4px; - padding: 0 20px 0 10px; - overflow: hidden; - color: @tag-default-color; - background-color: @tag-default-bg; - border: 1px solid @border-color-split; - border-radius: @border-radius-sm; + // =========== Group ============ + &-group { + color: @text-color-secondary; + font-size: @font-size-sm; cursor: default; - transition: padding 0.3s @ease-in-out; - &__disabled { - padding: 0 10px; - } } - .@{select-prefix-cls}-selection__choice__content { - display: inline-block; - max-width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - transition: margin 0.3s @ease-in-out; - } + // =========== Option =========== + &-option { + display: flex; - .@{select-prefix-cls}-selection__choice__remove { - .iconfont-mixin(); - - position: absolute; - right: 4px; - display: inline-block; - color: @text-color-secondary; - font-weight: bold; - font-size: @font-size-sm; - line-height: inherit; - cursor: pointer; - transition: all 0.3s; - .iconfont-size-under-12px(10px); - &:hover { - color: @icon-color-hover; - } - } - - .@{select-prefix-cls}-selection__clear, - .@{select-prefix-cls}-arrow { - top: @input-height-base / 2; - } - } - - &-allow-clear &-selection--multiple &-selection__rendered, - &-show-arrow &-selection--multiple &-selection__rendered { - margin-right: 20px; // In case that clear button will overlap content - } - - &-open { - .@{select-prefix-cls}-arrow { - &-icon svg { - transform: rotate(180deg); - } - } - .@{select-prefix-cls}-selection { - .active(); - } - } - - &-combobox { - .@{select-prefix-cls}-arrow { - display: none; - } - .@{select-prefix-cls}-search--inline { - float: none; - width: 100%; - height: 100%; - } - .@{select-prefix-cls}-search__field__wrap { - width: 100%; - height: 100%; - } - .@{select-prefix-cls}-search__field { - position: relative; - z-index: 1; - width: 100%; - height: 100%; - box-shadow: none; - transition: all 0.3s @ease-in-out, height 0s; - } - } - &-combobox&-allow-clear &-selection:hover &-selection__rendered, - &-combobox&-show-arrow &-selection:hover &-selection__rendered { - margin-right: 20px; // In case that clear button will overlap content - } -} - -.@{select-prefix-cls}-dropdown { - .reset-component; - - position: absolute; - top: -9999px; - left: -9999px; - z-index: @zindex-dropdown; - box-sizing: border-box; - font-size: @font-size-base; - // Fix select render lag of long text in chrome - // https://github.com/ant-design/ant-design/issues/11456 - // https://github.com/ant-design/ant-design/issues/11843 - font-variant: initial; - background-color: @select-dropdown-bg; - border-radius: @border-radius-base; - outline: none; - box-shadow: @box-shadow-base; - - &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft, - &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft { - animation-name: antSlideUpIn; - } - - &.slide-up-enter.slide-up-enter-active&-placement-topLeft, - &.slide-up-appear.slide-up-appear-active&-placement-topLeft { - animation-name: antSlideDownIn; - } - - &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft { - animation-name: antSlideUpOut; - } - - &.slide-up-leave.slide-up-leave-active&-placement-topLeft { - animation-name: antSlideDownOut; - } - - &-hidden { - display: none; - } - - &-menu { - max-height: 250px; - margin-bottom: 0; - padding: @select-dropdown-edge-child-vertical-padding 0; //Change - padding-left: 0; // Override default ul/ol - overflow: auto; - list-style: none; - outline: none; - - &-item-group-list { - margin: 0; - padding: 0; - - > .@{select-prefix-cls}-dropdown-menu-item { - padding-left: 20px; - } - } - - &-item-group-title { - height: 32px; - padding: 0 @control-padding-horizontal; - color: @text-color-secondary; - font-size: @font-size-sm; - line-height: 32px; - } - - &-item-group-list &-item:first-child:not(:last-child), - &-item-group:not(:last-child) &-item-group-list &-item:last-child { - border-radius: 0; - } - - &-item { - position: relative; - display: block; - padding: @select-dropdown-vertical-padding @control-padding-horizontal; - overflow: hidden; - color: @text-color; - font-weight: normal; - font-size: @select-dropdown-font-size; - line-height: @select-dropdown-line-height; - white-space: nowrap; - text-overflow: ellipsis; - cursor: pointer; - transition: background 0.3s ease; - - &:hover:not(&-disabled) { - background-color: @item-hover-bg; + &-content { + flex: auto; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } - &:first-child { - & when (@select-dropdown-edge-child-vertical-padding = 0) { - border-radius: @border-radius-base @border-radius-base 0 0; - } - } - - &:last-child { - & when (@select-dropdown-edge-child-vertical-padding = 0) { - border-radius: 0 0 @border-radius-base @border-radius-base; - } - } - - &-selected { - color: @select-item-selected-color; - font-weight: @select-item-selected-font-weight; - background-color: @select-item-selected-bg; - } - - &-disabled { - color: @disabled-color; - cursor: not-allowed; - - &:hover { - color: @disabled-color; - cursor: not-allowed; - } + &-state { + flex: none; } &-active:not(&-disabled) { background-color: @select-item-active-bg; } - &-divider { - height: 1px; - margin: 1px 0; - overflow: hidden; - line-height: 0; - background-color: @border-color-split; + &-selected:not(&-disabled) { + color: @select-item-selected-color; + font-weight: @select-item-selected-font-weight; + background-color: @select-item-selected-bg; + + .@{select-prefix-cls}-item-option-state { + color: @primary-color; + } + } + + &-disabled { + color: @disabled-color; + cursor: not-allowed; + } + + &-grouped { + padding-left: @control-padding-horizontal * 2; } } } - &&--multiple { - .@{select-prefix-cls}-dropdown-menu-item { - padding-right: @control-padding-horizontal + 20; - & .@{select-prefix-cls}-selected-icon { - position: absolute; - top: 50%; - right: @control-padding-horizontal; - color: transparent; - font-weight: bold; - font-size: 12px; - text-shadow: 0 0.1px 0, 0.1px 0 0, 0 -0.1px 0, -0.1px 0; - transform: translateY(-50%); - transition: all 0.2s; - } - - &:hover .@{select-prefix-cls}-selected-icon { - color: fade(@black, 87%); - } - - &-disabled .@{select-prefix-cls}-selected-icon { - display: none; - } - - &-selected .@{select-prefix-cls}-selected-icon, - &-selected:hover .@{select-prefix-cls}-selected-icon { - display: inline-block; - color: @primary-color; - } - } + // ============================================================ + // == Size == + // ============================================================ + &-lg { + font-size: @font-size-lg; } - // Patch for popup adjust - // https://github.com/ant-design/ant-design/issues/14422 - &--empty&--multiple &-menu-item { - padding-right: @control-padding-horizontal; - } - - &-container-open, - &-open { - .@{select-prefix-cls}-dropdown { - display: block; - } + // no border style + &-borderless &-selector { + background-color: transparent !important; + border-color: transparent !important; + box-shadow: none !important; } } + +@import './rtl'; diff --git a/components/select/style/index.js b/components/select/style/index.tsx similarity index 100% rename from components/select/style/index.js rename to components/select/style/index.tsx diff --git a/components/select/style/multiple.less b/components/select/style/multiple.less new file mode 100644 index 000000000..548abdfe0 --- /dev/null +++ b/components/select/style/multiple.less @@ -0,0 +1,206 @@ +@import './index'; + +@select-multiple-item-border-width: 1px; + +@select-multiple-padding: max( + @input-padding-vertical-base - @select-multiple-item-border-width - + @select-multiple-item-spacing-half, + 0 +); + +/** + * Do not merge `height` & `line-height` under style with `selection` & `search`, + * since chrome may update to redesign with its align logic. + */ + +.@{select-prefix-cls} { + &-multiple { + // ========================= Selector ========================= + .@{select-prefix-cls}-selector { + .select-selector(); + .select-search-input-without-border(); + + display: flex; + flex-wrap: wrap; + align-items: center; + // Multiple is little different that horizontal is follow the vertical + padding: @select-multiple-padding @input-padding-vertical-base; + + .@{select-prefix-cls}-show-search& { + cursor: text; + } + + .@{select-prefix-cls}-disabled& { + background: @select-multiple-disabled-background; + cursor: not-allowed; + } + + &::after { + display: inline-block; + width: 0; + margin: @select-multiple-item-spacing-half 0; + line-height: @select-multiple-item-height; + content: '\a0'; + } + } + + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selector, + &.@{select-prefix-cls}-allow-clear .@{select-prefix-cls}-selector { + padding-right: @font-size-sm + @control-padding-horizontal; + } + + // ======================== Selections ======================== + .@{select-prefix-cls}-selection-item { + position: relative; + display: flex; + flex: none; + box-sizing: border-box; + max-width: 100%; + + height: @select-multiple-item-height; + margin-top: @select-multiple-item-spacing-half; + margin-right: @input-padding-vertical-base; + margin-bottom: @select-multiple-item-spacing-half; + padding: 0 (@padding-xs / 2) 0 @padding-xs; + line-height: @select-multiple-item-height - @select-multiple-item-border-width * 2; + background: @select-selection-item-bg; + border: 1px solid @select-selection-item-border-color; + border-radius: @border-radius-base; + cursor: default; + transition: font-size 0.3s, line-height 0.3s, height 0.3s; + user-select: none; + + .@{select-prefix-cls}-disabled& { + color: @select-multiple-item-disabled-color; + border-color: @select-multiple-item-disabled-border-color; + cursor: not-allowed; + } + + // It's ok not to do this, but 24px makes bottom narrow in view should adjust + &-content { + display: inline-block; + margin-right: @padding-xs / 2; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + &-remove { + .iconfont-mixin(); + display: inline-block; + color: @text-color-secondary; + font-weight: bold; + font-size: @font-size-sm; + line-height: inherit; + cursor: pointer; + .iconfont-size-under-12px(10px); + + > .@{iconfont-css-prefix} { + vertical-align: -0.2em; + } + + &:hover { + color: @icon-color-hover; + } + } + } + + // ========================== Input ========================== + .@{select-prefix-cls}-selection-search { + position: relative; + margin-left: @select-multiple-padding / 2; + + &-input, + &-mirror { + font-family: @font-family; + line-height: @line-height-base; + transition: all 0.3s; + } + + &-input { + width: 100%; + min-width: 4.1px; // fix search cursor missing + } + + &-mirror { + position: absolute; + top: 0; + left: 0; + z-index: 999; + white-space: nowrap; + visibility: hidden; + } + + // https://github.com/ant-design/ant-design/issues/22906 + &:first-child .@{select-prefix-cls}-selection-search-input { + margin-left: 6.5px; + } + } + + // ======================= Placeholder ======================= + .@{select-prefix-cls}-selection-placeholder { + position: absolute; + top: 50%; + right: @input-padding-horizontal; + left: @input-padding-horizontal; + transform: translateY(-50%); + transition: all 0.3s; + } + + // ============================================================ + // == Size == + // ============================================================ + .select-size(@suffix, @input-height) { + @merged-cls: ~'@{select-prefix-cls}-@{suffix}'; + &.@{merged-cls} { + @select-selection-height: @input-height - @input-padding-vertical-base * 2; + @select-height-without-border: @input-height - @border-width-base * 2; + + .@{select-prefix-cls}-selector::after { + line-height: @select-selection-height; + } + + .@{select-prefix-cls}-selection-item { + height: @select-selection-height; + line-height: @select-selection-height - @border-width-base * 2; + } + + .@{select-prefix-cls}-selection-search { + height: @select-selection-height + @select-multiple-padding; + line-height: @select-selection-height + @select-multiple-padding; + + &-input, + &-mirror { + height: @select-selection-height; + line-height: @select-selection-height - @border-width-base * 2; + } + } + } + } + + .select-size('lg', @input-height-lg); + .select-size('sm', @input-height-sm); + + // Size small need additional set padding + &.@{select-prefix-cls}-sm { + .@{select-prefix-cls}-selection-placeholder { + left: @input-padding-horizontal-sm; + } + // https://github.com/ant-design/ant-design/issues/22906 + .@{select-prefix-cls}-selection-search:first-child + .@{select-prefix-cls}-selection-search-input { + margin-left: 3px; + } + } + &.@{select-prefix-cls}-lg { + .@{select-prefix-cls}-selection-item { + height: @select-multiple-item-height-lg; + line-height: @select-multiple-item-height-lg; + } + } + } + + &-disabled .@{select-prefix-cls}-selection-item-remove { + display: none; + } +} diff --git a/components/select/style/rtl.less b/components/select/style/rtl.less new file mode 100644 index 000000000..82e055e66 --- /dev/null +++ b/components/select/style/rtl.less @@ -0,0 +1,188 @@ +@import '../../style/themes/index'; +@import '../../style/mixins/index'; +@import '../../input/style/mixin'; + +@select-prefix-cls: ~'@{ant-prefix}-select'; + +.@{select-prefix-cls} { + &-rtl { + direction: rtl; + } + + // ========================== Arrow ========================== + &-arrow { + .@{select-prefix-cls}-rtl & { + right: initial; + left: @control-padding-horizontal - 1px; + } + } + + // ========================== Clear ========================== + &-clear { + .@{select-prefix-cls}-rtl & { + right: initial; + left: @control-padding-horizontal - 1px; + } + } + + // ========================== Popup ========================== + &-dropdown { + &-rtl { + direction: rtl; + } + } + + // ========================= Options ========================= + &-item { + &-option { + &-grouped { + .@{select-prefix-cls}-dropdown-rtl & { + padding-right: @control-padding-horizontal * 2; + padding-left: @control-padding-horizontal; + } + } + } + } +} + +// multiple +@select-multiple-item-border-width: 1px; +@select-multiple-item-spacing-half: ceil(@input-padding-vertical-base / 2); +@select-multiple-padding: max( + @input-padding-vertical-base - @select-multiple-item-border-width - + @select-multiple-item-spacing-half, + 0 +); + +.@{select-prefix-cls}-multiple { + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selector, + &.@{select-prefix-cls}-allow-clear .@{select-prefix-cls}-selector { + .@{select-prefix-cls}-rtl& { + padding-right: @input-padding-vertical-base; + padding-left: @font-size-sm + @control-padding-horizontal; + } + } + + // ======================== Selections ======================== + .@{select-prefix-cls}-selection-item { + .@{select-prefix-cls}-rtl& { + margin-right: 0; + margin-left: @input-padding-vertical-base; + padding: 0 @padding-xs 0 (@padding-xs / 2); + text-align: right; + } + // It's ok not to do this, but 24px makes bottom narrow in view should adjust + &-content { + .@{select-prefix-cls}-rtl& { + margin-right: 0; + margin-left: @padding-xs / 2; + text-align: right; + } + } + } + + // ========================== Input ========================== + .@{select-prefix-cls}-selection-search { + .@{select-prefix-cls}-rtl& { + margin-right: @select-multiple-padding / 2; + margin-left: @input-padding-vertical-base; + } + + &-mirror { + .@{select-prefix-cls}-rtl& { + right: 0; + left: auto; + } + } + } + + // ======================= Placeholder ======================= + .@{select-prefix-cls}-selection-placeholder { + .@{select-prefix-cls}-rtl& { + right: @input-padding-horizontal; + left: auto; + } + } + + // ============================================================ + // == Size == + // ============================================================ + + // Size small need additional set padding + &.@{select-prefix-cls}-sm { + .@{select-prefix-cls}-selection-placeholder { + .@{select-prefix-cls}-rtl& { + right: @input-padding-horizontal-sm; + } + } + } +} + +// single +@selection-item-padding: ceil(@font-size-base * 1.25); + +.@{select-prefix-cls}-single { + // ========================= Selector ========================= + .@{select-prefix-cls}-selector { + .@{select-prefix-cls}-selection-item, + .@{select-prefix-cls}-selection-placeholder { + .@{select-prefix-cls}-rtl& { + right: 0; + left: 9px; + text-align: right; + } + } + } + + // With arrow should provides `padding-right` to show the arrow + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-search { + .@{select-prefix-cls}-rtl& { + right: @input-padding-horizontal-base; + left: @input-padding-horizontal-base + @font-size-base; + } + } + + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-item, + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-placeholder { + .@{select-prefix-cls}-rtl& { + padding-right: 0; + padding-left: @selection-item-padding; + } + } + + // ========================== Input ========================== + // We only change the style of non-customize input which is only support by `combobox` mode. + + // Not customize + &:not(.@{select-prefix-cls}-customize-input) { + .@{select-prefix-cls}-selector { + .@{select-prefix-cls}-rtl& { + padding: 0 @input-padding-horizontal-base; + } + } + } + + // ============================================================ + // == Size == + // ============================================================ + + // Size small need additional set padding + &.@{select-prefix-cls}-sm { + &:not(.@{select-prefix-cls}-customize-input) { + // With arrow should provides `padding-right` to show the arrow + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-search { + .@{select-prefix-cls}-rtl& { + right: 0; + } + } + + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-item, + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-placeholder { + .@{select-prefix-cls}-rtl& { + padding-right: 0; + padding-left: @font-size-base * 1.5; + } + } + } + } +} diff --git a/components/select/style/single.less b/components/select/style/single.less new file mode 100644 index 000000000..0eed6c96c --- /dev/null +++ b/components/select/style/single.less @@ -0,0 +1,180 @@ +@import './index'; + +@selection-item-padding: ceil(@font-size-base * 1.25); + +.@{select-prefix-cls}-single { + // ========================= Selector ========================= + .@{select-prefix-cls}-selector { + display: flex; + + .@{select-prefix-cls}-selection-search { + position: absolute; + top: 0; + right: @input-padding-horizontal-base; + bottom: 0; + left: @input-padding-horizontal-base; + + &-input { + width: 100%; + } + } + + .@{select-prefix-cls}-selection-item, + .@{select-prefix-cls}-selection-placeholder { + padding: 0; + line-height: @select-height-without-border; + transition: all 0.3s; + + // Firefox inline-block position calculation is not same as Chrome & Safari. Patch this: + @supports (-moz-appearance: meterbar) { + & { + line-height: @select-height-without-border; + } + } + } + + .@{select-prefix-cls}-selection-item { + position: relative; + user-select: none; + } + + .@{select-prefix-cls}-selection-placeholder { + pointer-events: none; + } + + // For common baseline align + &::after, + // For '' value baseline align + .@{select-prefix-cls}-selection-item::after, + // For undefined value baseline align + .@{select-prefix-cls}-selection-placeholder::after { + display: inline-block; + width: 0; + visibility: hidden; + content: '\a0'; + } + } + + // With arrow should provides `padding-right` to show the arrow + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-search { + right: @input-padding-horizontal-base + @font-size-base; + } + + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-item, + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-placeholder { + padding-right: @selection-item-padding; + } + + // Opacity selection if open + &.@{select-prefix-cls}-open .@{select-prefix-cls}-selection-item { + color: @input-placeholder-color; + } + + // ========================== Input ========================== + // We only change the style of non-customize input which is only support by `combobox` mode. + + // Not customize + &:not(.@{select-prefix-cls}-customize-input) { + .@{select-prefix-cls}-selector { + .select-selector(); + .select-search-input-without-border(); + width: 100%; + + height: @input-height-base; + padding: 0 @input-padding-horizontal-base; + + .@{select-prefix-cls}-selection-search-input { + height: @select-height-without-border; + } + + &::after { + line-height: @select-height-without-border; + } + } + } + + &.@{select-prefix-cls}-customize-input { + .@{select-prefix-cls}-selector { + &::after { + display: none; + } + + .@{select-prefix-cls}-selection-search { + position: static; + width: 100%; + } + + .@{select-prefix-cls}-selection-placeholder { + position: absolute; + right: 0; + left: 0; + padding: 0 @input-padding-horizontal-base; + + &::after { + display: none; + } + } + } + } + + // ============================================================ + // == Size == + // ============================================================ + .select-size(@suffix, @input-height) { + @merged-cls: ~'@{select-prefix-cls}-@{suffix}'; + + &.@{merged-cls}:not(.@{select-prefix-cls}-customize-input) { + .@{select-prefix-cls}-selector { + height: @input-height; + + &::after, + .@{select-prefix-cls}-selection-item, + .@{select-prefix-cls}-selection-placeholder { + line-height: @input-height - 2 * @border-width-base; + } + } + + // Not customize + &:not(.@{select-prefix-cls}-customize-input) { + .@{select-prefix-cls}-selection-search-input { + height: @input-height - 2 * @border-width-base; + } + } + } + } + + .select-size('lg', @select-single-item-height-lg); + .select-size('sm', @input-height-sm); + + // Size small need additional set padding + &.@{select-prefix-cls}-sm { + &:not(.@{select-prefix-cls}-customize-input) { + .@{select-prefix-cls}-selection-search { + right: @input-padding-horizontal-sm; + left: @input-padding-horizontal-sm; + } + + .@{select-prefix-cls}-selector { + padding: 0 @input-padding-horizontal-sm; + } + + // With arrow should provides `padding-right` to show the arrow + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-search { + right: @input-padding-horizontal-sm + @font-size-base * 1.5; + } + + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-item, + &.@{select-prefix-cls}-show-arrow .@{select-prefix-cls}-selection-placeholder { + padding-right: @font-size-base * 1.5; + } + } + } + + &.@{select-prefix-cls}-lg { + &:not(.@{select-prefix-cls}-customize-input) { + .@{select-prefix-cls}-selector { + padding: 0 @input-padding-horizontal-lg; + } + } + } +} diff --git a/components/style/themes/default.less b/components/style/themes/default.less index ec1c82365..80bca6926 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -339,13 +339,23 @@ @select-item-selected-color: @text-color; @select-item-selected-font-weight: 600; @select-dropdown-bg: @component-background; +@select-item-selected-bg: @primary-1; +@select-item-active-bg: @item-hover-bg; @select-dropdown-vertical-padding: @dropdown-vertical-padding; -@select-dropdown-edge-child-vertical-padding: @dropdown-edge-child-vertical-padding; @select-dropdown-font-size: @dropdown-font-size; @select-dropdown-line-height: @dropdown-line-height; -@select-item-selected-bg: @background-color-light; -@select-item-active-bg: @item-active-bg; +@select-dropdown-height: 32px; @select-background: @component-background; +@select-clear-background: @select-background; +@select-selection-item-bg: @background-color-base; +@select-selection-item-border-color: @border-color-split; +@select-single-item-height-lg: 40px; +@select-multiple-item-height: @input-height-base - @input-padding-vertical-base * 2; // Normal 24px +@select-multiple-item-height-lg: 32px; +@select-multiple-item-spacing-half: ceil(@input-padding-vertical-base / 2); +@select-multiple-disabled-background: @input-disabled-bg; +@select-multiple-item-disabled-color: #bfbfbf; +@select-multiple-item-disabled-border-color: @select-border-color; // Cascader // ---- diff --git a/components/tree-select/index.jsx b/components/tree-select/index.jsx index db6f2becd..d14c03010 100644 --- a/components/tree-select/index.jsx +++ b/components/tree-select/index.jsx @@ -23,7 +23,7 @@ const TreeSelect = { name: 'ATreeSelect', props: initDefaultProps(TreeSelectProps(), { transitionName: 'slide-up', - choiceTransitionName: 'zoom', + choiceTransitionName: '', showSearch: false, }), setup() { diff --git a/components/tree-select/interface.jsx b/components/tree-select/interface.jsx index 2524ae37b..9c5f16bf0 100644 --- a/components/tree-select/interface.jsx +++ b/components/tree-select/interface.jsx @@ -10,7 +10,7 @@ export const TreeData = PropTypes.shape({ }).loose; export const TreeSelectProps = () => ({ - ...SelectProps, + ...SelectProps(), autofocus: PropTypes.looseBool, dropdownStyle: PropTypes.object, filterTreeNode: withUndefined(PropTypes.oneOfType([Function, Boolean])), diff --git a/components/vc-select2/Selector/MultipleSelector.tsx b/components/vc-select2/Selector/MultipleSelector.tsx index 4856c4d4f..b97efcac5 100644 --- a/components/vc-select2/Selector/MultipleSelector.tsx +++ b/components/vc-select2/Selector/MultipleSelector.tsx @@ -55,7 +55,7 @@ const props = { accessibilityIndex: PropTypes.number, tabindex: PropTypes.number, - removeIcon: PropTypes.looseBool, + removeIcon: PropTypes.VNodeChild, choiceTransitionName: PropTypes.string, maxTagCount: PropTypes.number, @@ -158,9 +158,11 @@ const SelectSelector = defineComponent({ : maxTagPlaceholder, }); } - const transitionProps = getTransitionGroupProps(choiceTransitionName, { - appear: motionAppear, - }); + const transitionProps = choiceTransitionName + ? getTransitionGroupProps(choiceTransitionName, { + appear: motionAppear, + }) + : { css: false }; selectionNode.value = ( {...displayValues.map( @@ -237,7 +239,6 @@ const SelectSelector = defineComponent({ onInputMouseDown, onInputCompositionStart, onInputCompositionEnd, - choiceTransitionName, } = props; return ( <> diff --git a/components/vc-select2/generate.tsx b/components/vc-select2/generate.tsx index 230ec974a..105c8b2d1 100644 --- a/components/vc-select2/generate.tsx +++ b/components/vc-select2/generate.tsx @@ -49,9 +49,11 @@ import { VNode, VNodeChild, watch, + watchEffect, } from 'vue'; import createRef from '../_util/createRef'; -import PropTypes from '../_util/vue-types'; +import PropTypes, { withUndefined } from '../_util/vue-types'; +import initDefaultProps from '../_util/props-util/initDefaultProps'; const DEFAULT_OMIT_PROPS = [ 'children', @@ -65,7 +67,7 @@ const DEFAULT_OMIT_PROPS = [ 'onInputKeyDown', ]; -export const props = { +export const BaseProps = () => ({ prefixCls: PropTypes.string, id: PropTypes.string, class: PropTypes.string, @@ -84,7 +86,7 @@ export const props = { // Search inputValue: PropTypes.string, searchValue: PropTypes.string, - optionFilterProp: PropTypes.string.def('value'), + optionFilterProp: PropTypes.string, /** * In Select, `false` means do nothing. * In TreeSelect, `false` will highlight match item. @@ -98,20 +100,20 @@ export const props = { // Icons allowClear: PropTypes.looseBool, - clearIcon: PropTypes.any, + clearIcon: PropTypes.VNodeChild, showArrow: PropTypes.looseBool, - inputIcon: PropTypes.any, - removeIcon: PropTypes.any, - menuItemSelectedIcon: PropTypes.func, + inputIcon: PropTypes.VNodeChild, + removeIcon: PropTypes.VNodeChild, + menuItemSelectedIcon: PropTypes.VNodeChild, // Dropdown open: PropTypes.looseBool, defaultOpen: PropTypes.looseBool, - listHeight: PropTypes.number.def(200), - listItemHeight: PropTypes.number.def(20), + listHeight: PropTypes.number, + listItemHeight: PropTypes.number, dropdownStyle: PropTypes.object, dropdownClassName: PropTypes.string, - dropdownMatchSelectWidth: PropTypes.oneOfType([Boolean, Number]).def(true), + dropdownMatchSelectWidth: withUndefined(PropTypes.oneOfType([Boolean, Number])), virtual: PropTypes.looseBool, dropdownRender: PropTypes.func, dropdownAlign: PropTypes.any, @@ -125,8 +127,8 @@ export const props = { loading: PropTypes.looseBool, autofocus: PropTypes.looseBool, defaultActiveFirstOption: PropTypes.looseBool, - notFoundContent: PropTypes.any.def('Not Found'), - placeholder: PropTypes.any, + notFoundContent: PropTypes.VNodeChild, + placeholder: PropTypes.VNodeChild, backfill: PropTypes.looseBool, getInputElement: PropTypes.func, optionLabelProp: PropTypes.string, @@ -135,7 +137,7 @@ export const props = { maxTagPlaceholder: PropTypes.any, tokenSeparators: PropTypes.array, tagRender: PropTypes.func, - showAction: PropTypes.array.def([]), + showAction: PropTypes.array, tabindex: PropTypes.number, // Events @@ -162,8 +164,8 @@ export const props = { * Only used in current version for internal event process. * Do not use in production environment. */ - internalProps: PropTypes.object.def({}), -} + internalProps: PropTypes.object, +}); export interface SelectProps { prefixCls?: string; @@ -336,7 +338,9 @@ export default function generateSelector< const Select = defineComponent>({ name: 'Select', setup(props: SelectProps) { - const useInternalProps = computed(() => props.internalProps.mark === INTERNAL_PROPS_MARK); + const useInternalProps = computed( + () => props.internalProps && props.internalProps.mark === INTERNAL_PROPS_MARK, + ); const containerRef = ref(null); const triggerRef = ref(null); @@ -526,8 +530,8 @@ export default function generateSelector< const triggerSelect = (newValue: RawValueType, isSelect: boolean, source: SelectSource) => { const newValueOption = getValueOption([newValue]); const outOption = findValueOption([newValue], newValueOption)[0]; - - if (!props.internalProps.skipTriggerSelect) { + const { internalProps = {} } = props; + if (!internalProps.skipTriggerSelect) { // Skip trigger `onSelect` or `onDeselect` if configured const selectValue = (mergedLabelInValue.value ? getLabeledValue(newValue, { @@ -547,10 +551,10 @@ export default function generateSelector< // Trigger internal event if (useInternalProps.value) { - if (isSelect && props.internalProps.onRawSelect) { - props.internalProps.onRawSelect(newValue, outOption, source); - } else if (!isSelect && props.internalProps.onRawDeselect) { - props.internalProps.onRawDeselect(newValue, outOption, source); + if (isSelect && internalProps.onRawSelect) { + internalProps.onRawSelect(newValue, outOption, source); + } else if (!isSelect && internalProps.onRawDeselect) { + internalProps.onRawDeselect(newValue, outOption, source); } } }; @@ -561,7 +565,11 @@ export default function generateSelector< prevValueOptions.value = val; }; const triggerChange = (newRawValues: RawValueType[]) => { - if (useInternalProps.value && props.internalProps.skipTriggerChange) { + if ( + useInternalProps.value && + props.internalProps && + props.internalProps.skipTriggerChange + ) { return; } const newRawValuesOptions = getValueOption(newRawValues); @@ -656,8 +664,10 @@ export default function generateSelector< const innerOpen = ref(undefined); const mergedOpen = ref(undefined); const setInnerOpen = (val: boolean) => { - innerOpen.value = val; - mergedOpen.value = innerOpen.value; + setTimeout(() => { + innerOpen.value = val; + mergedOpen.value = innerOpen.value; + }); }; watch( computed(() => [props.defaultOpen, props.open]), @@ -671,21 +681,16 @@ export default function generateSelector< const emptyListContent = computed( () => !props.notFoundContent && !displayOptions.value.length, ); - watch( - computed( - () => - props.disabled || - (emptyListContent.value && innerOpen.value && props.mode === 'combobox'), - ), - val => { - if (val) { - mergedOpen.value = false; - } else { - mergedOpen.value = innerOpen.value; - } - }, - { immediate: true }, - ); + + watchEffect(() => { + mergedOpen.value = innerOpen.value; + if ( + props.disabled || + (emptyListContent.value && mergedOpen.value && props.mode === 'combobox') + ) { + mergedOpen.value = false; + } + }); const triggerOpen = computed(() => (emptyListContent.value ? false : mergedOpen.value)); @@ -706,6 +711,7 @@ export default function generateSelector< const triggerSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => { let ret = true; let newSearchText = searchText; + const preSearchValue = mergedSearchValue.value; setActiveValue(null); // Check if match the `tokenSeparators` @@ -750,7 +756,7 @@ export default function generateSelector< setInnerSearchValue(newSearchText); - if (props.onSearch && mergedSearchValue.value !== newSearchText) { + if (props.onSearch && preSearchValue !== newSearchText) { props.onSearch(newSearchText); } @@ -806,7 +812,6 @@ export default function generateSelector< const onInternalKeyDown = (event: KeyboardEvent) => { const clearLock = getClearLock(); const { which } = event; - // We only manage open state here, close logic should handle by list component if (!mergedOpen.value && which === KeyCode.ENTER) { onToggleOpen(true); @@ -863,7 +868,7 @@ export default function generateSelector< } // `showAction` should handle `focus` if set - if (props.showAction.includes('focus')) { + if (props.showAction && props.showAction.includes('focus')) { onToggleOpen(true); } } @@ -1321,6 +1326,6 @@ export default function generateSelector< }, }); Select.inheritAttrs = false; - Select.props = props; + Select.props = initDefaultProps(BaseProps(), {}); return Select; } diff --git a/components/vc-select2/index.ts b/components/vc-select2/index.ts index 9652b88e2..3f2b9d890 100644 --- a/components/vc-select2/index.ts +++ b/components/vc-select2/index.ts @@ -1,7 +1,11 @@ -import Select, { ExportedSelectProps as SelectProps } from './Select'; +import Select from './Select'; import Option from './Option'; import OptGroup from './OptGroup'; -import { props } from './generate'; -export { Option, OptGroup, SelectProps, props }; +import { BaseProps } from './generate'; + +// eslint-disable-next-line prettier/prettier +export type { ExportedSelectProps as SelectProps } from './Select'; + +export { Option, OptGroup, BaseProps }; export default Select; diff --git a/tsconfig.json b/tsconfig.json index 52bce7dcf..201741eba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,6 @@ "noUnusedParameters": true, "noUnusedLocals": true, "noImplicitAny": false, - "module": "esnext", "target": "es6", "lib": ["dom", "es2017"], "skipLibCheck": true,