UNPKG

@trail-ui/react

Version:
136 lines (134 loc) 4.09 kB
// 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 };