feat: select auto-complete support virtual list

feat-dayjs
tangjinzhou 2020-10-17 22:19:52 +08:00
parent 4d63b7cab6
commit 46149b660b
17 changed files with 1005 additions and 756 deletions

View File

@ -1,18 +1,21 @@
import { App, defineComponent, inject, provide } from 'vue'; import { App, defineComponent, inject, provide } from 'vue';
import { Option, OptGroup } from '../vc-select';
import Select, { SelectProps } from '../select'; import Select, { SelectProps } from '../select';
import Input from '../input'; import Input from '../input';
import InputElement from './InputElement'; import InputElement from './InputElement';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import { getComponent, getOptionProps, isValidElement, getSlot } from '../_util/props-util'; 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 { function isSelectOptionOrSelectOptGroup(child: any): Boolean {
return child && child.type && (child.type.isSelectOption || child.type.isSelectOptGroup); return child && child.type && (child.type.isSelectOption || child.type.isSelectOptGroup);
} }
const AutoCompleteProps = { const AutoCompleteProps = {
...SelectProps, ...SelectProps(),
dataSource: PropTypes.array, dataSource: PropTypes.array,
dropdownMenuStyle: PropTypes.style, dropdownMenuStyle: PropTypes.style,
optionLabelProp: PropTypes.string, optionLabelProp: PropTypes.string,
@ -39,7 +42,12 @@ const AutoComplete = defineComponent({
}, },
Option: { ...Option, name: 'AAutoCompleteOption' }, Option: { ...Option, name: 'AAutoCompleteOption' },
OptGroup: { ...OptGroup, name: 'AAutoCompleteOptGroup' }, OptGroup: { ...OptGroup, name: 'AAutoCompleteOptGroup' },
setup() { setup(props, { slots }) {
warning(
!('dataSource' in props || 'dataSource' in slots),
'AutoComplete',
'`dataSource` is deprecated, please use `options` instead.',
);
return { return {
configProvider: inject('configProvider', defaultConfigProvider), configProvider: inject('configProvider', defaultConfigProvider),
popupRef: null, popupRef: null,
@ -59,11 +67,7 @@ const AutoComplete = defineComponent({
getInputElement() { getInputElement() {
const children = getSlot(this); const children = getSlot(this);
const element = children.length ? children[0] : <Input lazy={false} />; const element = children.length ? children[0] : <Input lazy={false} />;
return ( return <InputElement {...element.props}>{element}</InputElement>;
<InputElement placeholder={this.placeholder} {...element.props}>
{element}
</InputElement>
);
}, },
focus() { focus() {
@ -80,8 +84,8 @@ const AutoComplete = defineComponent({
}, },
render() { render() {
const { size, prefixCls: customizePrefixCls, optionLabelProp, dataSource } = this; const { size, prefixCls: customizePrefixCls, dataSource } = this;
let optionChildren: any;
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('select', customizePrefixCls); const prefixCls = getPrefixCls('select', customizePrefixCls);
const { class: className } = this.$attrs as any; const { class: className } = this.$attrs as any;
@ -92,22 +96,28 @@ const AutoComplete = defineComponent({
[`${prefixCls}-show-search`]: true, [`${prefixCls}-show-search`]: true,
[`${prefixCls}-auto-complete`]: true, [`${prefixCls}-auto-complete`]: true,
}; };
let options;
const childArray = getSlot(this, 'dataSource'); const childArray = getSlot(this, 'dataSource');
if (childArray.length && isSelectOptionOrSelectOptGroup(childArray[0])) { if (childArray.length && isSelectOptionOrSelectOptGroup(childArray[0])) {
options = childArray; optionChildren = childArray;
} else { } else {
options = dataSource optionChildren = dataSource
? dataSource.map((item: any) => { ? dataSource.map((item: any) => {
if (isValidElement(item)) { if (isValidElement(item)) {
return item; return item;
} }
switch (typeof item) { switch (typeof item) {
case 'string': case 'string':
return <Option key={item}>{item}</Option>; return (
<Option key={item} value={item}>
{item}
</Option>
);
case 'object': case 'object':
return <Option key={item.value}>{item.text}</Option>; return (
<Option key={item.value} value={item.value}>
{item.text}
</Option>
);
default: default:
throw new Error( throw new Error(
'AutoComplete[dataSource] only supports type `string[] | Object[]`.', 'AutoComplete[dataSource] only supports type `string[] | Object[]`.',
@ -117,17 +127,17 @@ const AutoComplete = defineComponent({
: []; : [];
} }
const selectProps = { const selectProps = {
...getOptionProps(this), ...Omit(getOptionProps(this), ['dataSource', 'optionLabelProp'] as any),
...this.$attrs, ...this.$attrs,
mode: Select.SECRET_COMBOBOX_MODE_DO_NOT_USE, mode: Select.SECRET_COMBOBOX_MODE_DO_NOT_USE,
optionLabelProp, // optionLabelProp,
getInputElement: this.getInputElement, getInputElement: this.getInputElement,
notFoundContent: getComponent(this, 'notFoundContent'), notFoundContent: getComponent(this, 'notFoundContent'),
placeholder: '', // placeholder: '',
class: cls, class: cls,
ref: this.saveSelect, ref: this.saveSelect,
}; };
return <Select {...selectProps}>{options}</Select>; return <Select {...selectProps}>{optionChildren}</Select>;
}, },
}); });

View File

@ -9,84 +9,8 @@
.@{autocomplete-prefix-cls} { .@{autocomplete-prefix-cls} {
.reset-component; .reset-component;
&.@{select-prefix-cls} { // https://github.com/ant-design/ant-design/issues/22302
.@{select-prefix-cls} { .@{select-prefix-cls}-clear {
&-selection { right: 13px;
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;
} }
} }

View File

@ -4,7 +4,7 @@ import { getOptionProps, getSlot } from '../_util/props-util';
export default { export default {
inheritAttrs: false, inheritAttrs: false,
props: { props: {
...SelectProps, ...SelectProps(),
}, },
Option: VcSelect.Option, Option: VcSelect.Option,
render() { render() {

View File

@ -1,7 +1,7 @@
import omit from 'omit.js'; import omit from 'omit.js';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, props } from '../vc-select2'; import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, BaseProps } from '../vc-select2';
import { OptionProps } from '../vc-select2/Option'; import { OptionProps as OptionPropsType } from '../vc-select2/Option';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import getIcons from './utils/iconUtil'; import getIcons from './utils/iconUtil';
import { computed, defineComponent, inject, ref, VNodeChild, App, PropType } from 'vue'; import { computed, defineComponent, inject, ref, VNodeChild, App, PropType } from 'vue';
@ -10,7 +10,7 @@ import { tuple } from '../_util/type';
type RawValue = string | number; type RawValue = string | number;
export { OptionProps }; export type OptionProps = OptionPropsType;
export type OptionType = typeof Option; export type OptionType = typeof Option;
@ -31,36 +31,40 @@ export interface InternalSelectProps<VT> extends Omit<RcSelectProps<VT>, 'mode'>
} }
export interface SelectPropsTypes<VT> export interface SelectPropsTypes<VT>
extends Omit<InternalSelectProps<VT>, 'inputIcon' | 'mode' | 'getInputElement' | 'backfill' | 'class' | 'style'> { extends Omit<
InternalSelectProps<VT>,
'inputIcon' | 'mode' | 'getInputElement' | 'backfill' | 'class' | 'style'
> {
mode?: 'multiple' | 'tags'; mode?: 'multiple' | 'tags';
} }
export type SelectTypes = SelectPropsTypes<SelectValue> export type SelectTypes = SelectPropsTypes<SelectValue>;
export const SelectProps = { export const SelectProps = () => ({
...omit(props, ['inputIcon' ,'mode' ,'getInputElement' ,'backfill' ,'class' ,'style']), ...omit(BaseProps(), ['inputIcon', 'mode', 'getInputElement', 'backfill', 'class', 'style']),
value: { value: {
type: [Array, Object, String, Number] as PropType<SelectValue> type: [Array, Object, String, Number] as PropType<SelectValue>,
}, },
defaultValue: { defaultValue: {
type: [Array, Object, String, Number] as PropType<SelectValue> type: [Array, Object, String, Number] as PropType<SelectValue>,
}, },
notFoundContent: PropTypes.VNodeChild,
suffixIcon: PropTypes.VNodeChild, suffixIcon: PropTypes.VNodeChild,
itemIcon: PropTypes.VNodeChild, itemIcon: PropTypes.VNodeChild,
size: PropTypes.oneOf(tuple('small', 'middle', 'large', undefined, 'default')), size: PropTypes.oneOf(tuple('small', 'middle', 'large', 'default')),
mode: PropTypes.oneOf(tuple('multiple', 'tags')), mode: PropTypes.oneOf(tuple('multiple', 'tags', 'SECRET_COMBOBOX_MODE_DO_NOT_USE')),
bordered: PropTypes.looseBool.def(true), bordered: PropTypes.looseBool.def(true),
transitionName: PropTypes.string.def('slide-up'), transitionName: PropTypes.string.def('slide-up'),
choiceTransitionName: PropTypes.string.def(''), choiceTransitionName: PropTypes.string.def(''),
} });
const Select = defineComponent({ const Select = defineComponent({
name: 'ASelect', name: 'ASelect',
Option, Option,
OptGroup, OptGroup,
inheritAttrs: false, inheritAttrs: false,
props: SelectProps, props: SelectProps(),
SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE', SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE',
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup(props: any, {attrs, emit}) { setup(props: any, { attrs, emit }) {
const selectRef = ref(null); const selectRef = ref(null);
const configProvider = inject('configProvider', defaultConfigProvider); const configProvider = inject('configProvider', defaultConfigProvider);
@ -77,8 +81,8 @@ const Select = defineComponent({
} }
}; };
const mode = computed(()=>{ const mode = computed(() => {
const { mode } = props const { mode } = props;
if ((mode as any) === 'combobox') { if ((mode as any) === 'combobox') {
return undefined; return undefined;
@ -90,35 +94,47 @@ const Select = defineComponent({
return mode; return mode;
}); });
const prefixCls = computed(() => {
const mergedClassName = computed(()=> classNames( return configProvider.getPrefixCls('select', props.prefixCls);
{ });
[`${props.prefixCls}-lg`]: props.size === 'large', const mergedClassName = computed(() =>
[`${props.prefixCls}-sm`]: props.size === 'small', classNames(
[`${props.prefixCls}-rtl`]: props.direction === 'rtl', {
[`${props.prefixCls}-borderless`]: !props.bordered, [`${prefixCls.value}-lg`]: props.size === 'large',
}, [`${prefixCls.value}-sm`]: props.size === 'small',
attrs.class, [`${prefixCls.value}-rtl`]: props.direction === 'rtl',
)); [`${prefixCls.value}-borderless`]: !props.bordered,
const triggerChange=(...args: any[])=>{ },
console.log(args) attrs.class,
emit('update:value', ...args) ),
emit('change', ...args) );
} const triggerChange = (...args: any[]) => {
console.log(args);
emit('update:value', ...args);
emit('change', ...args);
};
return { return {
mergedClassName, mergedClassName,
mode, mode,
focus, focus,
blur, blur,
configProvider, configProvider,
triggerChange triggerChange,
} prefixCls,
};
}, },
render() { render() {
const {configProvider, mode, mergedClassName,triggerChange, $slots: slots, $props} = this as any;
const props: SelectTypes = $props
const { const {
prefixCls: customizePrefixCls, configProvider,
mode,
mergedClassName,
triggerChange,
prefixCls,
$slots: slots,
$props,
} = this as any;
const props: SelectTypes = $props;
const {
notFoundContent, notFoundContent,
listHeight = 256, listHeight = 256,
listItemHeight = 24, listItemHeight = 24,
@ -126,11 +142,10 @@ const Select = defineComponent({
dropdownClassName, dropdownClassName,
direction, direction,
virtual, virtual,
dropdownMatchSelectWidth dropdownMatchSelectWidth,
} = props; } = props;
const { getPrefixCls, renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider;
const prefixCls = getPrefixCls('select', customizePrefixCls);
const isMultiple = mode === 'multiple' || mode === 'tags'; const isMultiple = mode === 'multiple' || mode === 'tags';
@ -138,20 +153,24 @@ const Select = defineComponent({
let mergedNotFound: VNodeChild; let mergedNotFound: VNodeChild;
if (notFoundContent !== undefined) { if (notFoundContent !== undefined) {
mergedNotFound = notFoundContent; mergedNotFound = notFoundContent;
} else if(slots.notFoundContent){ } else if (slots.notFoundContent) {
mergedNotFound = slots.notFoundContent() mergedNotFound = slots.notFoundContent();
} else if (mode === 'combobox') { } else if (mode === 'combobox') {
mergedNotFound = null; mergedNotFound = null;
} else { } else {
console.log(111);
mergedNotFound = renderEmpty('Select') as any; mergedNotFound = renderEmpty('Select') as any;
} }
// ===================== Icons ===================== // ===================== Icons =====================
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({ const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons(
...this.$props, {
multiple: isMultiple, ...this.$props,
prefixCls, multiple: isMultiple,
}, slots); prefixCls,
},
slots,
);
const selectProps = omit(props, [ const selectProps = omit(props, [
'prefixCls', 'prefixCls',
@ -166,30 +185,33 @@ const Select = defineComponent({
const rcSelectRtlDropDownClassName = classNames(dropdownClassName, { const rcSelectRtlDropDownClassName = classNames(dropdownClassName, {
[`${prefixCls}-dropdown-${direction}`]: direction === 'rtl', [`${prefixCls}-dropdown-${direction}`]: direction === 'rtl',
}); });
<RcSelect return (
ref="selectRef" <RcSelect
virtual={virtual} ref="selectRef"
dropdownMatchSelectWidth={dropdownMatchSelectWidth} virtual={virtual}
{...selectProps} dropdownMatchSelectWidth={dropdownMatchSelectWidth}
listHeight={listHeight} {...selectProps}
listItemHeight={listItemHeight} {...this.$attrs}
mode={mode} listHeight={listHeight}
prefixCls={prefixCls} listItemHeight={listItemHeight}
direction={direction} mode={mode}
inputIcon={suffixIcon} prefixCls={prefixCls}
menuItemSelectedIcon={itemIcon} direction={direction}
removeIcon={removeIcon} inputIcon={suffixIcon}
clearIcon={clearIcon} menuItemSelectedIcon={itemIcon}
notFoundContent={mergedNotFound} removeIcon={removeIcon}
class={mergedClassName} clearIcon={clearIcon}
getPopupContainer={getPopupContainer || getContextPopupContainer} notFoundContent={mergedNotFound}
dropdownClassName={rcSelectRtlDropDownClassName} class={mergedClassName}
onChange={triggerChange} getPopupContainer={getPopupContainer || getContextPopupContainer}
> dropdownClassName={rcSelectRtlDropDownClassName}
{slots?.default()} onChange={triggerChange}
</RcSelect> >
} {slots?.default()}
}) </RcSelect>
);
},
});
/* istanbul ignore next */ /* istanbul ignore next */
Select.install = function(app: App) { Select.install = function(app: App) {
app.component(Select.name, Select); app.component(Select.name, Select);

View File

@ -99,7 +99,7 @@ const Select = defineComponent({
...SelectProps, ...SelectProps,
showSearch: PropTypes.looseBool.def(false), showSearch: PropTypes.looseBool.def(false),
transitionName: PropTypes.string.def('slide-up'), transitionName: PropTypes.string.def('slide-up'),
choiceTransitionName: PropTypes.string.def('zoom'), choiceTransitionName: PropTypes.string.def(''),
}, },
propTypes: SelectPropTypes, propTypes: SelectPropTypes,
setup() { setup() {

View File

@ -2,603 +2,303 @@
@import '../../style/mixins/index'; @import '../../style/mixins/index';
@import '../../input/style/mixin'; @import '../../input/style/mixin';
@select-prefix-cls: ~'@{ant-prefix}-select'; @import './single';
@import './multiple';
.selection__clear() { @select-prefix-cls: ~'@{ant-prefix}-select';
position: absolute; @select-height-without-border: @input-height-base - 2 * @border-width-base;
top: 50%; @select-dropdown-edge-child-vertical-padding: @dropdown-edge-child-vertical-padding;
right: @control-padding-horizontal - 1px;
z-index: 1; .select-selector() {
display: inline-block; position: relative;
width: 12px; background-color: @select-background;
height: 12px; border: @border-width-base @border-style-base @select-border-color;
margin-top: -6px; border-radius: @border-radius-base;
color: @disabled-color; transition: all 0.3s @ease-in-out;
font-size: @font-size-sm;
font-style: normal; input {
line-height: 12px; cursor: pointer;
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;
} }
&: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} { .@{select-prefix-cls} {
.reset-component; .reset-component;
position: relative; position: relative;
display: inline-block; display: inline-block;
outline: 0; cursor: pointer;
ul, &:not(.@{select-prefix-cls}-disabled):hover &-selector {
ol { .hover();
margin: 0;
padding: 0;
list-style: none;
} }
> ul > li > a { // ======================== Selection ========================
padding: 0; &-selection-item {
background-color: @component-background; 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 { &-arrow {
.iconfont-mixin(); .iconfont-mixin();
position: absolute; position: absolute;
top: 50%; top: 53%;
right: @control-padding-horizontal - 1px; right: @control-padding-horizontal - 1px;
width: @font-size-sm;
height: @font-size-sm;
margin-top: -@font-size-sm / 2; margin-top: -@font-size-sm / 2;
color: @disabled-color; color: @disabled-color;
font-size: @font-size-sm; font-size: @font-size-sm;
line-height: 1; 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; transition: transform 0.3s;
> svg {
vertical-align: top;
}
&:not(.@{select-prefix-cls}-suffix) {
pointer-events: auto;
}
}
.@{select-prefix-cls}-disabled & {
cursor: not-allowed;
} }
} }
&-selection { // ========================== Clear ==========================
display: block; &-clear {
box-sizing: border-box; position: absolute;
background-color: @select-background; top: 50%;
border: @border-width-base @border-style-base @select-border-color; right: @control-padding-horizontal - 1px;
// strange align fix for chrome but works z-index: 1;
// https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif display: inline-block;
border-top-width: @border-width-base + 0.02px; width: @font-size-sm;
border-radius: @border-radius-base; height: @font-size-sm;
outline: none; margin-top: -@font-size-sm / 2;
transition: all 0.3s @ease-in-out; color: @disabled-color;
user-select: none; 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 {
.hover; color: @text-color-secondary;
} }
.@{select-prefix-cls}-focused &, .@{select-prefix-cls}:hover & {
&:focus,
&:active {
.active;
}
&__clear {
.selection__clear();
}
&:hover &__clear {
opacity: 1; opacity: 1;
} }
&-selected-value {
float: left;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
} }
&-no-arrow &-selection-selected-value { // ========================== Popup ==========================
padding-right: 0; &-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 { &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
color: @disabled-color; &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft {
} animation-name: antSlideUpIn;
&-disabled &-selection {
background: @input-disabled-bg;
cursor: not-allowed;
&:hover,
&:focus,
&:active {
border-color: @select-border-color;
box-shadow: none;
} }
&__clear { &.slide-up-enter.slide-up-enter-active&-placement-topLeft,
display: none; &.slide-up-appear.slide-up-appear-active&-placement-topLeft {
visibility: hidden; animation-name: antSlideDownIn;
pointer-events: none;
} }
}
&-disabled &-selection--multiple &-selection__choice { &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft {
padding-right: 10px; animation-name: antSlideUpOut;
color: fade(@black, 33%); }
background: @background-color-base;
&__remove { &.slide-up-leave.slide-up-leave-active&-placement-topLeft {
animation-name: antSlideDownOut;
}
&-hidden {
display: none; display: none;
} }
}
&-selection--single { &-empty {
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 {
color: @disabled-color; color: @disabled-color;
} }
} }
&-search__field__wrap { // ========================= Options =========================
.item() {
position: relative; 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, &-item-empty {
&-search__field__placeholder { .item();
// for TreeSelect compatibility color: @disabled-color;
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;
} }
&-search__field__placeholder { &-item {
left: @control-padding-horizontal; .item();
}
&-search__field__mirror { cursor: pointer;
position: absolute; transition: background 0.3s ease;
top: 0;
left: 0;
white-space: pre;
opacity: 0;
pointer-events: none;
}
&-search--inline { // =========== Group ============
position: absolute; &-group {
width: 100%; color: @text-color-secondary;
height: 100%; font-size: @font-size-sm;
.@{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;
cursor: default; cursor: default;
transition: padding 0.3s @ease-in-out;
&__disabled {
padding: 0 10px;
}
} }
.@{select-prefix-cls}-selection__choice__content { // =========== Option ===========
display: inline-block; &-option {
max-width: 100%; display: flex;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
transition: margin 0.3s @ease-in-out;
}
.@{select-prefix-cls}-selection__choice__remove { &-content {
.iconfont-mixin(); flex: auto;
overflow: hidden;
position: absolute; white-space: nowrap;
right: 4px; text-overflow: ellipsis;
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;
} }
&:first-child { &-state {
& when (@select-dropdown-edge-child-vertical-padding = 0) { flex: none;
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;
}
} }
&-active:not(&-disabled) { &-active:not(&-disabled) {
background-color: @select-item-active-bg; background-color: @select-item-active-bg;
} }
&-divider { &-selected:not(&-disabled) {
height: 1px; color: @select-item-selected-color;
margin: 1px 0; font-weight: @select-item-selected-font-weight;
overflow: hidden; background-color: @select-item-selected-bg;
line-height: 0;
background-color: @border-color-split; .@{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 { // == Size ==
padding-right: @control-padding-horizontal + 20; // ============================================================
& .@{select-prefix-cls}-selected-icon { &-lg {
position: absolute; font-size: @font-size-lg;
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;
}
}
} }
// Patch for popup adjust // no border style
// https://github.com/ant-design/ant-design/issues/14422 &-borderless &-selector {
&--empty&--multiple &-menu-item { background-color: transparent !important;
padding-right: @control-padding-horizontal; border-color: transparent !important;
} box-shadow: none !important;
&-container-open,
&-open {
.@{select-prefix-cls}-dropdown {
display: block;
}
} }
} }
@import './rtl';

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -339,13 +339,23 @@
@select-item-selected-color: @text-color; @select-item-selected-color: @text-color;
@select-item-selected-font-weight: 600; @select-item-selected-font-weight: 600;
@select-dropdown-bg: @component-background; @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-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-font-size: @dropdown-font-size;
@select-dropdown-line-height: @dropdown-line-height; @select-dropdown-line-height: @dropdown-line-height;
@select-item-selected-bg: @background-color-light; @select-dropdown-height: 32px;
@select-item-active-bg: @item-active-bg;
@select-background: @component-background; @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 // Cascader
// ---- // ----

View File

@ -23,7 +23,7 @@ const TreeSelect = {
name: 'ATreeSelect', name: 'ATreeSelect',
props: initDefaultProps(TreeSelectProps(), { props: initDefaultProps(TreeSelectProps(), {
transitionName: 'slide-up', transitionName: 'slide-up',
choiceTransitionName: 'zoom', choiceTransitionName: '',
showSearch: false, showSearch: false,
}), }),
setup() { setup() {

View File

@ -10,7 +10,7 @@ export const TreeData = PropTypes.shape({
}).loose; }).loose;
export const TreeSelectProps = () => ({ export const TreeSelectProps = () => ({
...SelectProps, ...SelectProps(),
autofocus: PropTypes.looseBool, autofocus: PropTypes.looseBool,
dropdownStyle: PropTypes.object, dropdownStyle: PropTypes.object,
filterTreeNode: withUndefined(PropTypes.oneOfType([Function, Boolean])), filterTreeNode: withUndefined(PropTypes.oneOfType([Function, Boolean])),

View File

@ -55,7 +55,7 @@ const props = {
accessibilityIndex: PropTypes.number, accessibilityIndex: PropTypes.number,
tabindex: PropTypes.number, tabindex: PropTypes.number,
removeIcon: PropTypes.looseBool, removeIcon: PropTypes.VNodeChild,
choiceTransitionName: PropTypes.string, choiceTransitionName: PropTypes.string,
maxTagCount: PropTypes.number, maxTagCount: PropTypes.number,
@ -158,9 +158,11 @@ const SelectSelector = defineComponent<SelectorProps>({
: maxTagPlaceholder, : maxTagPlaceholder,
}); });
} }
const transitionProps = getTransitionGroupProps(choiceTransitionName, { const transitionProps = choiceTransitionName
appear: motionAppear, ? getTransitionGroupProps(choiceTransitionName, {
}); appear: motionAppear,
})
: { css: false };
selectionNode.value = ( selectionNode.value = (
<TransitionGroup {...transitionProps}> <TransitionGroup {...transitionProps}>
{...displayValues.map( {...displayValues.map(
@ -237,7 +239,6 @@ const SelectSelector = defineComponent<SelectorProps>({
onInputMouseDown, onInputMouseDown,
onInputCompositionStart, onInputCompositionStart,
onInputCompositionEnd, onInputCompositionEnd,
choiceTransitionName,
} = props; } = props;
return ( return (
<> <>

View File

@ -49,9 +49,11 @@ import {
VNode, VNode,
VNodeChild, VNodeChild,
watch, watch,
watchEffect,
} from 'vue'; } from 'vue';
import createRef from '../_util/createRef'; 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 = [ const DEFAULT_OMIT_PROPS = [
'children', 'children',
@ -65,7 +67,7 @@ const DEFAULT_OMIT_PROPS = [
'onInputKeyDown', 'onInputKeyDown',
]; ];
export const props = { export const BaseProps = () => ({
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
id: PropTypes.string, id: PropTypes.string,
class: PropTypes.string, class: PropTypes.string,
@ -84,7 +86,7 @@ export const props = {
// Search // Search
inputValue: PropTypes.string, inputValue: PropTypes.string,
searchValue: PropTypes.string, searchValue: PropTypes.string,
optionFilterProp: PropTypes.string.def('value'), optionFilterProp: PropTypes.string,
/** /**
* In Select, `false` means do nothing. * In Select, `false` means do nothing.
* In TreeSelect, `false` will highlight match item. * In TreeSelect, `false` will highlight match item.
@ -98,20 +100,20 @@ export const props = {
// Icons // Icons
allowClear: PropTypes.looseBool, allowClear: PropTypes.looseBool,
clearIcon: PropTypes.any, clearIcon: PropTypes.VNodeChild,
showArrow: PropTypes.looseBool, showArrow: PropTypes.looseBool,
inputIcon: PropTypes.any, inputIcon: PropTypes.VNodeChild,
removeIcon: PropTypes.any, removeIcon: PropTypes.VNodeChild,
menuItemSelectedIcon: PropTypes.func, menuItemSelectedIcon: PropTypes.VNodeChild,
// Dropdown // Dropdown
open: PropTypes.looseBool, open: PropTypes.looseBool,
defaultOpen: PropTypes.looseBool, defaultOpen: PropTypes.looseBool,
listHeight: PropTypes.number.def(200), listHeight: PropTypes.number,
listItemHeight: PropTypes.number.def(20), listItemHeight: PropTypes.number,
dropdownStyle: PropTypes.object, dropdownStyle: PropTypes.object,
dropdownClassName: PropTypes.string, dropdownClassName: PropTypes.string,
dropdownMatchSelectWidth: PropTypes.oneOfType([Boolean, Number]).def(true), dropdownMatchSelectWidth: withUndefined(PropTypes.oneOfType([Boolean, Number])),
virtual: PropTypes.looseBool, virtual: PropTypes.looseBool,
dropdownRender: PropTypes.func, dropdownRender: PropTypes.func,
dropdownAlign: PropTypes.any, dropdownAlign: PropTypes.any,
@ -125,8 +127,8 @@ export const props = {
loading: PropTypes.looseBool, loading: PropTypes.looseBool,
autofocus: PropTypes.looseBool, autofocus: PropTypes.looseBool,
defaultActiveFirstOption: PropTypes.looseBool, defaultActiveFirstOption: PropTypes.looseBool,
notFoundContent: PropTypes.any.def('Not Found'), notFoundContent: PropTypes.VNodeChild,
placeholder: PropTypes.any, placeholder: PropTypes.VNodeChild,
backfill: PropTypes.looseBool, backfill: PropTypes.looseBool,
getInputElement: PropTypes.func, getInputElement: PropTypes.func,
optionLabelProp: PropTypes.string, optionLabelProp: PropTypes.string,
@ -135,7 +137,7 @@ export const props = {
maxTagPlaceholder: PropTypes.any, maxTagPlaceholder: PropTypes.any,
tokenSeparators: PropTypes.array, tokenSeparators: PropTypes.array,
tagRender: PropTypes.func, tagRender: PropTypes.func,
showAction: PropTypes.array.def([]), showAction: PropTypes.array,
tabindex: PropTypes.number, tabindex: PropTypes.number,
// Events // Events
@ -162,8 +164,8 @@ export const props = {
* Only used in current version for internal event process. * Only used in current version for internal event process.
* Do not use in production environment. * Do not use in production environment.
*/ */
internalProps: PropTypes.object.def({}), internalProps: PropTypes.object,
} });
export interface SelectProps<OptionsType extends object[], ValueType> { export interface SelectProps<OptionsType extends object[], ValueType> {
prefixCls?: string; prefixCls?: string;
@ -336,7 +338,9 @@ export default function generateSelector<
const Select = defineComponent<SelectProps<OptionsType, ValueType>>({ const Select = defineComponent<SelectProps<OptionsType, ValueType>>({
name: 'Select', name: 'Select',
setup(props: SelectProps<OptionsType, ValueType>) { setup(props: SelectProps<OptionsType, ValueType>) {
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 containerRef = ref(null);
const triggerRef = ref(null); const triggerRef = ref(null);
@ -526,8 +530,8 @@ export default function generateSelector<
const triggerSelect = (newValue: RawValueType, isSelect: boolean, source: SelectSource) => { const triggerSelect = (newValue: RawValueType, isSelect: boolean, source: SelectSource) => {
const newValueOption = getValueOption([newValue]); const newValueOption = getValueOption([newValue]);
const outOption = findValueOption([newValue], newValueOption)[0]; const outOption = findValueOption([newValue], newValueOption)[0];
const { internalProps = {} } = props;
if (!props.internalProps.skipTriggerSelect) { if (!internalProps.skipTriggerSelect) {
// Skip trigger `onSelect` or `onDeselect` if configured // Skip trigger `onSelect` or `onDeselect` if configured
const selectValue = (mergedLabelInValue.value const selectValue = (mergedLabelInValue.value
? getLabeledValue(newValue, { ? getLabeledValue(newValue, {
@ -547,10 +551,10 @@ export default function generateSelector<
// Trigger internal event // Trigger internal event
if (useInternalProps.value) { if (useInternalProps.value) {
if (isSelect && props.internalProps.onRawSelect) { if (isSelect && internalProps.onRawSelect) {
props.internalProps.onRawSelect(newValue, outOption, source); internalProps.onRawSelect(newValue, outOption, source);
} else if (!isSelect && props.internalProps.onRawDeselect) { } else if (!isSelect && internalProps.onRawDeselect) {
props.internalProps.onRawDeselect(newValue, outOption, source); internalProps.onRawDeselect(newValue, outOption, source);
} }
} }
}; };
@ -561,7 +565,11 @@ export default function generateSelector<
prevValueOptions.value = val; prevValueOptions.value = val;
}; };
const triggerChange = (newRawValues: RawValueType[]) => { const triggerChange = (newRawValues: RawValueType[]) => {
if (useInternalProps.value && props.internalProps.skipTriggerChange) { if (
useInternalProps.value &&
props.internalProps &&
props.internalProps.skipTriggerChange
) {
return; return;
} }
const newRawValuesOptions = getValueOption(newRawValues); const newRawValuesOptions = getValueOption(newRawValues);
@ -656,8 +664,10 @@ export default function generateSelector<
const innerOpen = ref(undefined); const innerOpen = ref(undefined);
const mergedOpen = ref(undefined); const mergedOpen = ref(undefined);
const setInnerOpen = (val: boolean) => { const setInnerOpen = (val: boolean) => {
innerOpen.value = val; setTimeout(() => {
mergedOpen.value = innerOpen.value; innerOpen.value = val;
mergedOpen.value = innerOpen.value;
});
}; };
watch( watch(
computed(() => [props.defaultOpen, props.open]), computed(() => [props.defaultOpen, props.open]),
@ -671,21 +681,16 @@ export default function generateSelector<
const emptyListContent = computed( const emptyListContent = computed(
() => !props.notFoundContent && !displayOptions.value.length, () => !props.notFoundContent && !displayOptions.value.length,
); );
watch(
computed( watchEffect(() => {
() => mergedOpen.value = innerOpen.value;
props.disabled || if (
(emptyListContent.value && innerOpen.value && props.mode === 'combobox'), props.disabled ||
), (emptyListContent.value && mergedOpen.value && props.mode === 'combobox')
val => { ) {
if (val) { mergedOpen.value = false;
mergedOpen.value = false; }
} else { });
mergedOpen.value = innerOpen.value;
}
},
{ immediate: true },
);
const triggerOpen = computed(() => (emptyListContent.value ? false : mergedOpen.value)); const triggerOpen = computed(() => (emptyListContent.value ? false : mergedOpen.value));
@ -706,6 +711,7 @@ export default function generateSelector<
const triggerSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => { const triggerSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => {
let ret = true; let ret = true;
let newSearchText = searchText; let newSearchText = searchText;
const preSearchValue = mergedSearchValue.value;
setActiveValue(null); setActiveValue(null);
// Check if match the `tokenSeparators` // Check if match the `tokenSeparators`
@ -750,7 +756,7 @@ export default function generateSelector<
setInnerSearchValue(newSearchText); setInnerSearchValue(newSearchText);
if (props.onSearch && mergedSearchValue.value !== newSearchText) { if (props.onSearch && preSearchValue !== newSearchText) {
props.onSearch(newSearchText); props.onSearch(newSearchText);
} }
@ -806,7 +812,6 @@ export default function generateSelector<
const onInternalKeyDown = (event: KeyboardEvent) => { const onInternalKeyDown = (event: KeyboardEvent) => {
const clearLock = getClearLock(); const clearLock = getClearLock();
const { which } = event; const { which } = event;
// We only manage open state here, close logic should handle by list component // We only manage open state here, close logic should handle by list component
if (!mergedOpen.value && which === KeyCode.ENTER) { if (!mergedOpen.value && which === KeyCode.ENTER) {
onToggleOpen(true); onToggleOpen(true);
@ -863,7 +868,7 @@ export default function generateSelector<
} }
// `showAction` should handle `focus` if set // `showAction` should handle `focus` if set
if (props.showAction.includes('focus')) { if (props.showAction && props.showAction.includes('focus')) {
onToggleOpen(true); onToggleOpen(true);
} }
} }
@ -1321,6 +1326,6 @@ export default function generateSelector<
}, },
}); });
Select.inheritAttrs = false; Select.inheritAttrs = false;
Select.props = props; Select.props = initDefaultProps(BaseProps(), {});
return Select; return Select;
} }

View File

@ -1,7 +1,11 @@
import Select, { ExportedSelectProps as SelectProps } from './Select'; import Select from './Select';
import Option from './Option'; import Option from './Option';
import OptGroup from './OptGroup'; import OptGroup from './OptGroup';
import { props } from './generate'; import { BaseProps } from './generate';
export { Option, OptGroup, SelectProps, props };
// eslint-disable-next-line prettier/prettier
export type { ExportedSelectProps as SelectProps } from './Select';
export { Option, OptGroup, BaseProps };
export default Select; export default Select;

View File

@ -13,7 +13,6 @@
"noUnusedParameters": true, "noUnusedParameters": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noImplicitAny": false, "noImplicitAny": false,
"module": "esnext",
"target": "es6", "target": "es6",
"lib": ["dom", "es2017"], "lib": ["dom", "es2017"],
"skipLibCheck": true, "skipLibCheck": true,