diff --git a/docs/components/data-view/chip.mdx b/docs/components/data-view/chip.mdx
index f0b4821a..473e3b10 100644
--- a/docs/components/data-view/chip.mdx
+++ b/docs/components/data-view/chip.mdx
@@ -18,6 +18,10 @@
+### 基本用法
+
+
+
## API
* 通用属性-`Input`
diff --git a/examples/chip/group.tsx b/examples/chip/group.tsx
new file mode 100644
index 00000000..06c5e5fe
--- /dev/null
+++ b/examples/chip/group.tsx
@@ -0,0 +1,17 @@
+import { IChipGroup, IFlex } from '@/components';
+
+const App: React.FC = () => (
+
+
+
+);
+
+export default App;
diff --git a/src/components/widgets/chip/index.tsx b/src/components/widgets/chip/index.tsx
index 2d73bc5c..c2dce6f9 100644
--- a/src/components/widgets/chip/index.tsx
+++ b/src/components/widgets/chip/index.tsx
@@ -1,4 +1,4 @@
-import { useRef } from 'react';
+import { Fragment, useRef } from 'react';
import classNames from 'classnames';
import { AnimatePresence, motion } from 'framer-motion';
@@ -8,15 +8,45 @@ import { useMemoFunc } from '@/hooks';
import type { ReactCFC } from '@/models';
import { iEscapeEvent, iPropagation } from '@/utils';
-import { IFlex } from '../flex';
+import { useControlState } from '../control';
import { ISignLine } from '../sign';
import { ISpinner } from '../spinners';
import { ISVGWrap } from '../svg-wrap';
import { IWave } from '../wave';
-import type { IChipGroupProps, IChipProps } from './models';
+import type {
+ IChipGroupProps,
+ IChipPrefixRender,
+ IChipProps,
+ IChipState,
+ IChipSuffixRender,
+} from './models';
import * as styles from './index.scss';
+const iPrefixRender: IChipPrefixRender = (
+ { icon, close, ...others },
+ { isLoading }
+) => (
+
+ {isLoading ? : icon}
+
+);
+
+const iSuffixRender: IChipSuffixRender = ({
+ icon,
+ close,
+ onClose,
+ ...others
+}) => (
+
+ {close && (
+
+ {isTrue(close) ? : close}
+
+ )}
+
+);
+
export const IChip: ReactCFC = (props) => {
const {
icon,
@@ -29,6 +59,7 @@ export const IChip: ReactCFC = (props) => {
size = 'medium',
disabled = false,
variant = 'filled',
+ render,
onKeyDown,
onClose,
...others
@@ -36,12 +67,18 @@ export const IChip: ReactCFC = (props) => {
const target = useRef(null);
- const iClose = useMemoFunc(
- (event: React.MouseEvent) => {
- iPropagation(event);
- onClose?.(event);
- }
- );
+ const states: IChipState = {
+ clickable,
+ disabled,
+ isLoading,
+ variant,
+ size,
+ };
+
+ const iClose = useMemoFunc((event: React.UIEvent) => {
+ iPropagation(event);
+ onClose?.(event);
+ });
return (
= (props) => {
{...others}
>
{clickable && }
-
- {isLoading ? (
-
-
-
- ) : (
- icon && {icon}
- )}
-
+ {(render?.prefix ?? iPrefixRender)(
+ { icon, close, onClose: iClose, className: styles.icon },
+ states
+ )}
{children}
-
- {close && (
-
- {isTrue(close) ? : close}
-
- )}
-
+ {(render?.suffix ?? iSuffixRender)(
+ { icon, close, onClose: iClose, className: styles.close },
+ states
+ )}
);
};
-export const IChipGroup: React.FC = () => {''};
+export const IChipGroup: React.FC = (props) => {
+ const { value, chips, variant, icon, onChange, onChipsChange } = props;
+
+ const [iChips, iChipsChange] = useControlState({
+ value: chips,
+ onChange: onChipsChange,
+ });
+
+ return (
+
+ {iChips?.map(({ value, label, ...others }, index) => (
+
+ {label ?? value?.toString()}
+
+ ))}
+
+ );
+};
diff --git a/src/components/widgets/chip/models/index.tsx b/src/components/widgets/chip/models/index.tsx
index 03b9702f..cd5775b3 100644
--- a/src/components/widgets/chip/models/index.tsx
+++ b/src/components/widgets/chip/models/index.tsx
@@ -5,12 +5,12 @@
import type { HTMLMotionProps } from 'framer-motion';
-import type { ReactValue, ReactValueChangeFunc } from '@/models';
+import type { ReactRender, ReactValue, ReactValueChangeFunc } from '@/models';
import type { ControlUISize } from '../../control';
+import type { ISVGWrapProps } from '../../svg-wrap';
-export interface IChipProps extends HTMLMotionProps<'span'> {
- close?: React.ReactNode;
+export interface IChipState {
/**
* If `true`, the chip will appear clickable, and will raise when pressed,
* even if the onClick prop is not defined.
@@ -22,14 +22,6 @@ export interface IChipProps extends HTMLMotionProps<'span'> {
clickable?: boolean;
/** @default false */
disabled?: boolean;
-
- /** Icon element */
- icon?: React.ReactNode;
- /**
- * Callback fired when the delete icon is clicked.
- * If set, the delete icon will be shown.
- */
- onClose?: (event: React.UIEvent) => void;
/** @default 'medium' */
size?: ControlUISize;
/** @default 'outlined' */
@@ -38,16 +30,61 @@ export interface IChipProps extends HTMLMotionProps<'span'> {
isLoading?: boolean;
}
+export type IChipCloseFunc = (event: React.UIEvent) => void;
+
+export type IChipPrefixRender = ReactRender<
+ ISVGWrapProps & {
+ icon?: React.ReactNode;
+ close?: React.ReactNode;
+ onClose: IChipCloseFunc;
+ },
+ IChipState
+>;
+
+export type IChipSuffixRender = ReactRender<
+ ISVGWrapProps & {
+ icon?: React.ReactNode;
+ close?: React.ReactNode;
+ onClose: IChipCloseFunc;
+ },
+ IChipState
+>;
+
+export interface IChipRenders {
+ prefix?: IChipPrefixRender;
+ suffix?: IChipSuffixRender;
+}
+
+export interface IChipProps extends IChipState, HTMLMotionProps<'span'> {
+ /** Prefix icon element */
+ icon?: React.ReactNode;
+ /** Close icon element */
+ close?: React.ReactNode;
+ render?: IChipRenders;
+ /**
+ * Callback fired when the delete icon is clicked.
+ * If set, the delete icon will be shown.
+ */
+ onClose?: IChipCloseFunc;
+}
+
export type IChipConfig = Pick<
IChipProps,
'isLoading' | 'variant' | 'size' | 'icon' | 'disabled' | 'clickable' | 'close'
> & {
value?: React.Key;
+ label?: React.ReactNode;
};
-export interface IChipGroupProps {
+export interface IChipGroupProps
+ extends Pick<
+ IChipConfig,
+ 'variant' | 'size' | 'icon' | 'disabled' | 'clickable' | 'close'
+ > {
chips?: IChipConfig[];
value?: ReactValue;
+ editable?: boolean;
+ creatable?: boolean;
onChange?: ReactValueChangeFunc;
onChipsChange?: (current: IChipConfig, chips?: IChipConfig[]) => void;
}
diff --git a/src/components/widgets/spinners/index.tsx b/src/components/widgets/spinners/index.tsx
index 8d461370..e782f78f 100644
--- a/src/components/widgets/spinners/index.tsx
+++ b/src/components/widgets/spinners/index.tsx
@@ -20,6 +20,7 @@ export const ISpinner: React.FC = ({
repeat: Infinity,
ease: 'linear',
},
+ ...animate,
}}
className={className}
exit={{ x: 0, y: 0 }}