@trail-ui/react
Version:
136 lines (134 loc) • 4.09 kB
JavaScript
// src/chip/chip.tsx
import { CloseIcon } from "@trail-ui/icons";
import { clsx } from "@trail-ui/shared-utils";
import { chip, filterVariantProps } from "@trail-ui/theme";
import { filterDOMProps } from "@react-aria/utils";
import {
cloneElement,
createContext,
forwardRef,
isValidElement,
useMemo
} from "react";
import { mergeProps, useFocusRing, usePress } from "react-aria";
import { useContextProps } from "react-aria-components";
import { jsx, jsxs } from "react/jsx-runtime";
var ChipContext = createContext({});
function Chip(props, ref) {
[props, ref] = useContextProps(props, ref, ChipContext);
const ctx = props;
const {
elementType,
children,
avatar,
startContent: startContentProps,
endContent: endContentProps,
classNames,
className,
onClose,
isBordered,
...chipProps
} = props;
const variantProps = filterVariantProps(props, chip.variantKeys);
const Component = elementType || "div";
let componentProps = chipProps;
if (typeof Component === "string") {
componentProps = filterDOMProps(chipProps);
}
const baseStyles = clsx(classNames == null ? void 0 : classNames.base, className);
const isCloseable = !!onClose;
const isDot = props.variant === "dot";
const { focusProps: closeFocusProps, isFocusVisible: isCloseButtonFocusVisible } = useFocusRing();
const { pressProps: closePressProps } = usePress({
isDisabled: !!(ctx == null ? void 0 : ctx.isDisabled),
onPress: onClose
});
const hasStartContent = useMemo(
() => !!avatar || !!startContentProps,
[avatar, startContentProps]
);
const hasEndContent = useMemo(
() => !!endContentProps || isCloseable,
[endContentProps, isCloseable]
);
const slots = useMemo(
() => chip({
...variantProps,
hasStartContent,
hasEndContent,
isCloseable,
isCloseButtonFocusVisible,
isBordered
}),
[
variantProps,
hasStartContent,
hasEndContent,
isCloseable,
isCloseButtonFocusVisible,
isBordered
]
);
const getAvatarClone = (avatar2) => {
if (!isValidElement(avatar2))
return null;
return cloneElement(avatar2, {
// @ts-ignore
className: slots.avatar({ class: classNames == null ? void 0 : classNames.avatar })
});
};
const getContentClone = (content) => isValidElement(content) ? cloneElement(content, {
// @ts-ignore
className: clsx("max-h-[80%]", content.props.className)
}) : null;
const startContent = getAvatarClone(avatar) || getContentClone(startContentProps);
const endContent = getContentClone(endContentProps);
const start = useMemo(() => {
if (isDot && !startContent) {
return /* @__PURE__ */ jsx("span", { className: slots.dot({ class: classNames == null ? void 0 : classNames.dot }) });
}
return startContent;
}, [isDot, startContent, slots, classNames == null ? void 0 : classNames.dot]);
const end = useMemo(() => {
if (isCloseable) {
return /* @__PURE__ */ jsx(
"span",
{
role: "button",
tabIndex: 0,
"aria-label": `Remove ${children}`,
className: slots.closeButton({ class: classNames == null ? void 0 : classNames.closeButton }),
...mergeProps(closePressProps, closeFocusProps),
children: endContent || /* @__PURE__ */ jsx(CloseIcon, {})
}
);
}
return endContent;
}, [
classNames == null ? void 0 : classNames.closeButton,
closeFocusProps,
closePressProps,
children,
endContent,
isCloseable,
slots
]);
return /* @__PURE__ */ jsxs(
Component,
{
ref,
className: `${slots.base({ class: baseStyles })} ${isBordered && slots.border({ class: classNames == null ? void 0 : classNames.border })}`,
...componentProps,
children: [
start,
/* @__PURE__ */ jsx("span", { className: slots.content({ class: classNames == null ? void 0 : classNames.content }), children }),
end
]
}
);
}
var _Chip = forwardRef(Chip);
export {
ChipContext,
_Chip
};