diff --git a/docs/examples/multiple.tsx b/docs/examples/multiple.tsx
index 0510eed20..0d667c893 100644
--- a/docs/examples/multiple.tsx
+++ b/docs/examples/multiple.tsx
@@ -29,11 +29,50 @@ export default () => {
return (
-
-
+
+
+ {
+ const locked = value.isBefore(dayjs('2021-01-05'), 'day');
+
+ return (
+
+ {label}
+ {!closable || locked ? (
+ locked
+ ) : (
+
+ )}
+
+ );
+ }}
+ />
);
};
diff --git a/src/PickerInput/Selector/SingleSelector/MultipleDates.tsx b/src/PickerInput/Selector/SingleSelector/MultipleDates.tsx
index 38afe3e8a..84d731ee3 100644
--- a/src/PickerInput/Selector/SingleSelector/MultipleDates.tsx
+++ b/src/PickerInput/Selector/SingleSelector/MultipleDates.tsx
@@ -2,11 +2,11 @@ import { clsx } from 'clsx';
import Overflow from '@rc-component/overflow';
import * as React from 'react';
import type { MouseEventHandler } from 'react';
-import type { PickerProps } from '../../SinglePicker';
+import type { CustomTagProps, PickerProps } from '../../SinglePicker';
export interface MultipleDatesProps extends Pick<
PickerProps,
- 'maxTagCount'
+ 'maxTagCount' | 'tagRender'
> {
prefixCls: string;
value: DateType[];
@@ -28,6 +28,7 @@ export default function MultipleDates(
formatDate,
disabled,
maxTagCount,
+ tagRender,
placeholder,
} = props;
@@ -60,12 +61,25 @@ export default function MultipleDates(
function renderItem(date: DateType) {
const displayLabel: React.ReactNode = formatDate(date);
+ const closable = !disabled;
- const onClose = (event?: React.MouseEvent) => {
+ const onClose: CustomTagProps['onClose'] = (event) => {
if (event) event.stopPropagation();
- onRemove(date);
+ if (!disabled) {
+ onRemove(date);
+ }
};
+ if (tagRender) {
+ return tagRender({
+ label: displayLabel,
+ value: date,
+ disabled: !!disabled,
+ closable,
+ onClose,
+ });
+ }
+
return renderSelector(displayLabel, onClose);
}
diff --git a/src/PickerInput/Selector/SingleSelector/index.tsx b/src/PickerInput/Selector/SingleSelector/index.tsx
index 7a373b9c0..ed97ba1ad 100644
--- a/src/PickerInput/Selector/SingleSelector/index.tsx
+++ b/src/PickerInput/Selector/SingleSelector/index.tsx
@@ -11,8 +11,7 @@ import useRootProps from '../hooks/useRootProps';
import MultipleDates from './MultipleDates';
export interface SingleSelectorProps
- extends SelectorProps,
- Pick {
+ extends SelectorProps, Pick {
id?: string;
value?: DateType[];
@@ -75,6 +74,7 @@ function SingleSelector(
onInputChange,
multiple,
maxTagCount,
+ tagRender,
// Valid
format,
@@ -170,6 +170,7 @@ function SingleSelector(
onRemove={onMultipleRemove}
formatDate={getText}
maxTagCount={maxTagCount}
+ tagRender={tagRender}
disabled={disabled}
removeIcon={removeIcon}
placeholder={placeholder}
diff --git a/src/PickerInput/SinglePicker.tsx b/src/PickerInput/SinglePicker.tsx
index df2a86a81..bdb8b78cb 100644
--- a/src/PickerInput/SinglePicker.tsx
+++ b/src/PickerInput/SinglePicker.tsx
@@ -36,16 +36,27 @@ import useSemantic from '../hooks/useSemantic';
// TODO: isInvalidateDate with showTime.disabledTime should not provide `range` prop
-export interface BasePickerProps
- extends SharedPickerProps {
+export interface CustomTagProps {
+ label: React.ReactNode;
+ value: DateType;
+ disabled: boolean;
+ onClose: (event?: React.MouseEvent) => void;
+ closable: boolean;
+}
+
+export interface BasePickerProps<
+ DateType extends object = any,
+> extends SharedPickerProps {
// Structure
id?: string;
/** Not support `time` or `datetime` picker */
multiple?: boolean;
removeIcon?: React.ReactNode;
- /** Only work when `multiple` is in used */
+ /** Only works when `multiple` is in use */
maxTagCount?: number | 'responsive';
+ /** Only works when `multiple` is in use */
+ tagRender?: (props: CustomTagProps) => React.ReactNode;
// Value
value?: DateType | DateType[] | null;
@@ -100,8 +111,7 @@ export interface BasePickerProps
}
export interface PickerProps
- extends BasePickerProps,
- Omit, 'format' | 'defaultValue'> {}
+ extends BasePickerProps, Omit, 'format' | 'defaultValue'> {}
/** Internal usage. For cross function get same aligned props */
export type ReplacedPickerProps = {
@@ -174,6 +184,7 @@ function Picker(
suffixIcon,
removeIcon,
+ tagRender,
// Focus
onFocus,
@@ -657,6 +668,7 @@ function Picker(
// Icon
suffixIcon={suffixIcon}
removeIcon={removeIcon}
+ tagRender={tagRender}
// Active
activeHelp={!!internalHoverValue}
allHelp={!!internalHoverValue && hoverSource === 'preset'}
diff --git a/src/index.tsx b/src/index.tsx
index 68b81a4a1..773412e66 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -29,7 +29,11 @@
import type { PickerRef, SharedTimeProps } from './interface';
import RangePicker, { type RangePickerProps } from './PickerInput/RangePicker';
-import Picker, { type BasePickerProps, type PickerProps } from './PickerInput/SinglePicker';
+import Picker, {
+ type BasePickerProps,
+ type CustomTagProps,
+ type PickerProps,
+} from './PickerInput/SinglePicker';
import PickerPanel, { type BasePickerPanelProps, type PickerPanelProps } from './PickerPanel';
export { Picker, RangePicker, PickerPanel };
@@ -40,6 +44,7 @@ export type {
PickerRef,
BasePickerProps,
BasePickerPanelProps,
+ CustomTagProps,
SharedTimeProps,
};
export default Picker;
diff --git a/tests/multiple.spec.tsx b/tests/multiple.spec.tsx
index ee1af0495..3a9de1f88 100644
--- a/tests/multiple.spec.tsx
+++ b/tests/multiple.spec.tsx
@@ -140,6 +140,63 @@ describe('Picker.Multiple', () => {
expect(container.querySelector('.custom-remove')).toBeTruthy();
});
+ it('tagRender', () => {
+ const onChange = jest.fn();
+ const { container } = render(
+ (
+
+ {label}
+ {value.date() !== 1 && (
+
+ )}
+
+ )}
+ />,
+ );
+
+ expect(container.querySelectorAll('.custom-tag')).toHaveLength(2);
+ expect(container.querySelectorAll('.custom-tag-close')).toHaveLength(1);
+
+ fireEvent.click(container.querySelector('.custom-tag-close'));
+ expect(onChange).toHaveBeenCalledWith(expect.anything(), ['2000-01-01']);
+ });
+
+ it('tagRender should not remove when disabled', () => {
+ const onChange = jest.fn();
+ const tagRender = jest.fn(({ label, onClose }) => (
+
+ ));
+
+ const { container } = render(
+ ,
+ );
+
+ expect(tagRender).toHaveBeenCalledWith(
+ expect.objectContaining({
+ disabled: true,
+ closable: false,
+ }),
+ );
+
+ fireEvent.click(container.querySelector('.custom-tag-close'));
+ expect(onChange).not.toHaveBeenCalled();
+ expect(container.querySelectorAll('.custom-tag-close')).toHaveLength(1);
+ });
+
describe('placeholder', () => {
it('show placeholder', () => {
const { container } = render();