UNPKG

antd

Version:

An enterprise-class UI design language and React components implementation

101 lines (100 loc) • 4.07 kB
"use client"; import React, { useMemo } from 'react'; import { unit } from '@ant-design/cssinjs'; import { isNonNullable } from '@rc-component/util'; import { clsx } from 'clsx'; import { isNumber, isString } from '../_util/is'; import { devUseWarning } from '../_util/warning'; import { useComponentConfig } from '../config-provider/context'; import { genCssVar } from '../theme/util/genStyleUtils'; import BorderBeamEffect from './BorderBeamEffect'; import useBorderSize from './hooks/useBorderSize'; import useChildDom from './hooks/useChildDom'; import useStyle from './style'; import { DEFAULT_BORDER_BEAM_DURATION, getBorderBeamGradient } from './util'; const getInset = width => { return isString(width) ? `calc(-1 * ${width})` : `-${width}px`; }; const BorderBeam = props => { const { prefixCls: customizePrefixCls, className, style, children, color, count, duration, items, lineWidth, outset, size } = props; const { className: contextClassName, style: contextStyle, getPrefixCls } = useComponentConfig('borderBeam'); // ============================ Prefix ============================ const prefixCls = getPrefixCls('border-beam', customizePrefixCls); const [hashId, cssVarCls] = useStyle(prefixCls); const [varName] = genCssVar(getPrefixCls(), 'border-beam'); // ============================= Host ============================= const [childNode, childDomNode] = useChildDom(children); const borderWidth = useBorderSize(childDomNode); const beamGradient = useMemo(() => getBorderBeamGradient(color), [color]); const mergedCount = items?.length ?? (isNumber(count) && Number.isFinite(count) && count >= 1 ? Math.floor(count) : 1); const hasValidDuration = isNumber(duration) && duration > 0; const mergedDuration = hasValidDuration ? duration : DEFAULT_BORDER_BEAM_DURATION; // ============================ Warning ============================ if (process.env.NODE_ENV !== 'production') { const warning = devUseWarning('BorderBeam'); process.env.NODE_ENV !== "production" ? warning(!isNonNullable(items) || !isNonNullable(count), 'usage', '`count` is ignored when `items` is provided.') : void 0; } // ============================ Border ============================ const insetOffset = useMemo(() => { return isNonNullable(outset) ? getInset(outset) : borderWidth.map(getInset).join(' '); }, [borderWidth, outset]); const beamClassName = clsx(contextClassName, className, hashId, cssVarCls); // ============================ Render ============================ return /*#__PURE__*/React.createElement(React.Fragment, null, childNode, Array.from({ length: mergedCount }, (_, index) => { const item = items?.[index] ?? {}; const beamPhase = index / mergedCount; const beamKey = `${mergedCount}-${beamPhase}`; const itemBeamGradient = item.color ? getBorderBeamGradient(item.color) : beamGradient; const itemLineWidth = item.lineWidth ?? lineWidth; const itemSize = item.size ?? size; const itemInsetOffset = isNonNullable(item.outset) ? getInset(item.outset) : insetOffset; return /*#__PURE__*/React.createElement(BorderBeamEffect, { key: beamKey, prefixCls: prefixCls, hostDom: childDomNode, className: beamClassName, style: { ...contextStyle, ...style, ...(itemBeamGradient && { [varName('beam-gradient')]: itemBeamGradient }), ...(hasValidDuration && { [varName('duration')]: `${duration}s` }), ...(isNonNullable(itemLineWidth) && { [varName('line-width')]: unit(itemLineWidth) }), ...(isNonNullable(itemSize) && { [varName('size')]: unit(itemSize) }), ...(beamPhase > 0 && { [varName('delay')]: `${-mergedDuration * beamPhase}s` }), [varName('inset-offset')]: itemInsetOffset } }); })); }; if (process.env.NODE_ENV !== 'production') { BorderBeam.displayName = 'BorderBeam'; } export default BorderBeam;