Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Select support maxCount #1015

Merged
merged 19 commits into from
Jan 2, 2024
1 change: 1 addition & 0 deletions docs/examples/auto-tokenization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const Demo: React.FC = () => (
<h2>自动分词</h2>
<Select
mode="tags"
maxCount={3}
style={{ width: '100%' }}
tokenSeparators={[',']}
options={Array.from({ length: 20 }, (_, i) => ({ label: i.toString(), value: i.toString() }))}
Expand Down
12 changes: 11 additions & 1 deletion src/BaseSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type { RefTriggerProps } from './SelectTrigger';
import SelectTrigger from './SelectTrigger';
import TransBtn from './TransBtn';
import { getSeparatedContent } from './utils/valueUtil';
import useMaxCount from './hooks/useMaxCount';

export type {
DisplayInfoType,
Expand Down Expand Up @@ -394,12 +395,21 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
[tokenSeparators],
);

const { truncateLength, shouldTruncate } = useMaxCount(multiple);
Fixed Show fixed Hide fixed

const onInternalSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => {
if (shouldTruncate()) {
return;
}
let ret = true;
let newSearchText = searchText;
onActiveValueChange?.(null);

const separatedList = getSeparatedContent(searchText, tokenSeparators);
const separatedList = getSeparatedContent(
searchText,
tokenSeparators,
shouldTruncate(false) ? truncateLength : undefined,
li-jia-nan marked this conversation as resolved.
Show resolved Hide resolved
);

// Check if match the `tokenSeparators`
const patchLabels: string[] = isCompositing ? null : separatedList;
Expand Down
9 changes: 4 additions & 5 deletions src/OptionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import TransBtn from './TransBtn';
import useBaseProps from './hooks/useBaseProps';
import type { FlattenOptionData } from './interface';
import { isPlatformMac } from './utils/platformUtil';
import useMaxCount from './hooks/useMaxCount';

// export interface OptionListProps<OptionsType extends object[]> {
export type OptionListProps = Record<string, never>;
Expand Down Expand Up @@ -45,7 +46,6 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
onPopupScroll,
} = useBaseProps();
const {
maxCount,
flattenOptions,
onActiveValue,
defaultActiveFirstOption,
Expand All @@ -71,10 +71,9 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
// =========================== List ===========================
const listRef = React.useRef<ListRef>(null);

const overMaxCount = React.useMemo<boolean>(
() => multiple && typeof maxCount !== 'undefined' && rawValues?.size >= maxCount,
[multiple, maxCount, rawValues?.size],
);
const { shouldTruncate } = useMaxCount(multiple);

const overMaxCount = shouldTruncate();

const onListMouseDown: React.MouseEventHandler<HTMLDivElement> = (event) => {
event.preventDefault();
Expand Down
23 changes: 23 additions & 0 deletions src/hooks/useMaxCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import SelectContext from '../SelectContext';
import type { SelectContextProps } from '../SelectContext';

const useMaxCount = (multiple: boolean) => {
const { maxCount, rawValues } = React.useContext<SelectContextProps>(SelectContext) || {};
const truncateLength = React.useMemo<number>(
() => maxCount - rawValues?.size,
[maxCount, rawValues?.size],
);
const shouldTruncate = React.useCallback(
(overCount = true) => {
if (!multiple || typeof maxCount === 'undefined') {
return false;
}
return overCount ? truncateLength <= 0 : truncateLength > 0;
},
[multiple, maxCount, truncateLength],
);
return { truncateLength, shouldTruncate };
li-jia-nan marked this conversation as resolved.
Show resolved Hide resolved
};

export default useMaxCount;
8 changes: 6 additions & 2 deletions src/utils/valueUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export function injectPropsWithOption<T extends object>(option: T): T {
return newOption;
}

export const getSeparatedContent = (text: string, tokens: string[]): string[] => {
export const getSeparatedContent = (text: string, tokens: string[], end?: number): string[] => {
if (!tokens || !tokens.length) {
return null;
}
Expand All @@ -127,5 +127,9 @@ export const getSeparatedContent = (text: string, tokens: string[]): string[] =>
.filter(Boolean);
};
const list = separate(text, tokens);
return match ? list : null;
if (match) {
return typeof end !== 'undefined' ? list.slice(0, end) : list;
} else {
return null;
}
};
23 changes: 22 additions & 1 deletion tests/Tags.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import { fireEvent, render } from '@testing-library/react';
import KeyCode from 'rc-util/lib/KeyCode';
import classNames from 'classnames';
import * as React from 'react';
Expand Down Expand Up @@ -505,4 +505,25 @@ describe('Select.Tags', () => {
expect(wrapper.find('.rc-select-item-option').length).toBe(1);
expect(errSpy).not.toHaveBeenCalled();
});

it(`paste content to split when count >= maxCount`, () => {
const onChange = jest.fn();
const { container } = render(
<Select
mode="tags"
maxCount={3}
onChange={onChange}
tokenSeparators={[',']}
value={['1', '2', '3']}
/>,
);
const input = container.querySelector<HTMLInputElement>('input');
fireEvent.paste(input, {
clipboardData: { getData: () => 'test' },
});
fireEvent.change(input, {
target: { value: 'test' },
});
expect(onChange).not.toBeCalled();
});
});
Loading