antd
Version:
An enterprise-class UI design language and React components implementation
101 lines (100 loc) • 4.07 kB
JavaScript
"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;