@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
347 lines (346 loc) • 12.3 kB
JavaScript
import React__default, { useRef, useState, useMemo, useEffect } from "react";
import { Notice, Close } from "@nutui/icons-react";
import classNames from "classnames";
import { g as getRect } from "./use-client-rect.js";
import { C as ComponentDefaults } from "./typings.js";
import { useRtl } from "./ConfigProvider.js";
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { align: "left", direction: "horizontal", list: [], duration: 1e3, height: 40, content: "", closeable: false, wrap: false, leftIcon: React__default.createElement(Notice, { width: 16, height: 16 }), rightIcon: null, right: null, delay: 1, scrollable: null, speed: 50 });
const NoticeBar = (props) => {
const rtl = useRtl();
const { children, className, style, align, direction, list, duration, height, content, closeable, wrap, leftIcon, rightIcon, right, delay, scrollable, speed, close, click, onClose, onClick, onItemClick } = Object.assign(Object.assign({}, defaultProps), props);
const classPrefix = "nut-noticebar";
const wrapRef = useRef(null);
const contentRef = useRef(null);
const [showNoticeBar, SetShowNoticeBar] = useState(true);
const scrollList = useRef([]);
const [wrapWidth, SetWrapWidth] = useState(0);
const [firstRound, SetFirstRound] = useState(true);
const [animationDuration, SetAnimationDuration] = useState(0);
const [offsetWidth, SetOffsetW] = useState(0);
const [animationClass, SetAnimationClass] = useState("");
const [animate, SetAnimate] = useState(false);
const [timer, SetTimer] = useState(0);
const [isCanScroll, SetIsCanScroll] = useState(null);
const isVertical = direction === "vertical";
const [rect, setRect] = useState(null);
let active = 0;
const [ready, setReady] = useState(false);
const container = useRef(null);
const innerRef = useRef(null);
const swiperRef = useRef({
moving: false,
autoplayTimer: null,
width: 0,
height: 0,
offset: 0,
size: 0
});
const [childOffset, setChildOffset] = useState([]);
const [offset, setOffset] = useState(0);
const { childs, childCount } = useMemo(() => {
let childCount2 = 0;
const childs2 = React__default.Children.map(children, (child) => {
if (!React__default.isValidElement(child))
return null;
childCount2++;
return child;
});
return {
childs: childs2,
childCount: childCount2
};
}, [children]);
let trackSize = childCount * Number(height);
const minOffset = (() => {
if (rect) {
const base = isVertical ? rect.height : rect.width;
return base - Number(height) * childCount;
}
return 0;
})();
useEffect(() => {
if (isVertical) {
if (children) {
scrollList.current = [].concat(childs);
} else {
scrollList.current = [].concat(list);
startRollEasy();
}
} else {
initScrollWrap();
}
return () => {
clearInterval(timer);
};
}, []);
useEffect(() => {
initScrollWrap();
}, [content]);
useEffect(() => {
if (list && list.length) {
scrollList.current = [].concat(list);
}
}, [list]);
const initScrollWrap = (value) => {
if (showNoticeBar === false) {
return;
}
setTimeout(() => {
if (!wrapRef.current || !contentRef.current) {
return;
}
const wrapW = getRect(wrapRef.current).width;
const offsetW = getRect(contentRef.current).width;
const canScroll = align === "left" && scrollable == null ? offsetW > wrapW : scrollable;
SetIsCanScroll(canScroll);
if (canScroll) {
SetWrapWidth(wrapW);
SetOffsetW(offsetW);
SetAnimationDuration(offsetW / speed);
SetAnimationClass("play");
} else {
SetAnimationClass("");
}
}, 0);
};
const handleClick = (event) => {
click && click(event);
onClick && onClick(event);
};
const onClickIcon = (event) => {
event.stopPropagation();
SetShowNoticeBar(!closeable);
close && close(event);
onClose && onClose(event);
};
const onAnimationEnd = () => {
SetFirstRound(false);
setTimeout(() => {
SetAnimationDuration((offsetWidth + wrapWidth) / speed);
SetAnimationClass("play-infinite");
}, 0);
};
const time = height / speed / 4 < 1 ? Number((height / speed / 4).toFixed(1)) * 1e3 : ~~(height / speed / 4) * 1e3;
const startRollEasy = () => {
showhorseLamp();
const timerCurr = window.setInterval(showhorseLamp, time + Number(duration));
SetTimer(timerCurr);
};
const showhorseLamp = () => {
SetAnimate(true);
setTimeout(() => {
scrollList.current.push(scrollList.current[0]);
scrollList.current.shift();
SetAnimate(false);
}, time);
};
const handleClickIcon = (event) => {
event.stopPropagation();
SetShowNoticeBar(!closeable);
close && close(event);
onClose && onClose(event);
};
const isEllipsis = () => {
if (isCanScroll == null && align === "left") {
return wrap;
}
return !isCanScroll && !wrap;
};
const contentStyle = {
animationDelay: `${firstRound ? delay : 0}s`,
animationDuration: `${animationDuration}s`,
transform: `translateX(${firstRound ? 0 : `${rtl ? -wrapWidth : wrapWidth}px`})`
};
const barStyle = {
height: isVertical ? `${height}px` : ""
};
const duringTime = height / speed / 4 < 1 ? Number((height / speed / 4).toFixed(1)) : ~~(height / speed / 4);
const noDuring = height / speed < 1 ? (height / speed).toFixed(1) : ~~(height / speed);
const horseLampStyle = {
transition: animate ? `all ${duringTime === 0 ? noDuring : duringTime}s` : "",
marginTop: animate ? `-${height}px` : ""
};
const init = (active2 = 0) => {
const rects = getRect(container === null || container === void 0 ? void 0 : container.current);
const _active = Math.max(Math.min(childCount - 1, active2), 0);
const _height = rects.height;
trackSize = childCount * Number(_height);
const targetOffset = getOffset(_active);
swiperRef.current.moving = true;
if (ready) {
swiperRef.current.moving = false;
}
active2 = _active;
setRect(rects);
setOffset(targetOffset);
setReady(true);
};
useEffect(() => {
if (ready) {
stopAutoPlay();
autoplay();
}
return () => {
setReady(false);
};
}, [ready]);
useEffect(() => {
if (isVertical && children) {
init();
stopAutoPlay();
autoplay();
}
}, [children, container === null || container === void 0 ? void 0 : container.current]);
const stopAutoPlay = () => {
clearTimeout(swiperRef.current.autoplayTimer);
swiperRef.current.autoplayTimer = null;
};
const autoplay = () => {
if (childCount <= 1)
return;
stopAutoPlay();
swiperRef.current.autoplayTimer = setTimeout(() => {
next();
autoplay();
}, Number(duration) + 100 * speed);
};
const move = ({ pace = 0, offset: offset2 = 0 }) => {
if (childCount <= 1)
return;
const targetActive = getActive(pace);
const targetOffset = getOffset(targetActive, offset2);
if (Array.isArray(children) && children[0] && targetOffset !== minOffset) {
const rightBound = targetOffset < minOffset;
childOffset[0] = rightBound ? trackSize : 0;
}
if (Array.isArray(children) && children[childCount - 1] && targetOffset !== 0) {
const leftBound = targetOffset > 0;
childOffset[childCount - 1] = leftBound ? -trackSize : 0;
}
setChildOffset(childOffset);
active = targetActive;
setOffset(targetOffset);
getStyle(targetOffset);
};
const next = () => {
resettPosition();
requestFrame(() => {
requestFrame(() => {
swiperRef.current.moving = false;
move({
pace: 1
});
});
});
};
const handleItemClick = (event, value) => {
onItemClick && onItemClick(event, value);
};
const getStyle = (moveOffset = offset) => {
const target = innerRef.current;
if (!target) {
return;
}
let _offset = 0;
const val = rect.height - height;
_offset = moveOffset + Number(active === childCount - 1 && val / 2);
target.style.transitionDuration = `${swiperRef.current.moving ? 0 : duration}ms`;
target.style.height = `${Number(height) * childCount}px`;
target.style.transform = `translate3D(0,${_offset}px,0)`;
};
const itemStyle = (index) => {
const style2 = {};
if (height) {
style2.height = `${height}px`;
style2.lineHeight = `${height}px`;
}
const offset2 = childOffset[index];
if (offset2) {
style2.transform = `translate3D(0,${offset2}px,0)`;
}
return style2;
};
const getActive = (pace) => {
if (pace) {
const _active = active + pace;
return range(_active, -1, childCount);
}
return active;
};
const getOffset = (active2, offset2 = 0) => {
const currentPosition = active2 * Number(height);
const targetOffset = offset2 - currentPosition;
return targetOffset;
};
const requestFrame = (fn) => {
window.requestAnimationFrame.call(window, fn);
};
const range = (num, min, max) => {
return Math.min(Math.max(num, min), max);
};
const resettPosition = () => {
swiperRef.current.moving = true;
if (active <= -1) {
move({ pace: childCount });
}
if (active >= childCount) {
move({ pace: -childCount });
}
};
const noticebarClass = classNames({
[`${classPrefix}-box`]: true,
[`${classPrefix}-box-wrapable`]: wrap,
[`${classPrefix}-box-${align}`]: true
});
const cls = classNames(classPrefix, className);
useEffect(() => {
return () => {
stopAutoPlay();
};
}, []);
return React__default.createElement(
"div",
{ className: cls, style },
showNoticeBar && direction === "horizontal" ? React__default.createElement(
"div",
{ className: noticebarClass, style: barStyle, onClick: handleClick },
leftIcon ? React__default.createElement("div", { className: "nut-noticebar-box-left-icon" }, leftIcon) : null,
React__default.createElement(
"div",
{ ref: wrapRef, className: "nut-noticebar-box-wrap" },
React__default.createElement(
"div",
{ ref: contentRef, className: `nut-noticebar-box-wrap-content ${animationClass} ${isEllipsis() ? "nut-ellipsis" : ""}`, style: contentStyle, onAnimationEnd },
children,
content
)
),
right ? React__default.createElement("div", { className: "nut-noticebar-box-right" }, right) : null,
closeable || rightIcon ? React__default.createElement("div", { className: "nut-noticebar-box-right-icon", onClick: onClickIcon }, rightIcon || React__default.createElement(Close, { width: 12, height: 12 })) : null
) : null,
showNoticeBar && scrollList.current.length > 0 && isVertical ? React__default.createElement(
"div",
{ className: "nut-noticebar-vertical", style: barStyle, ref: container, onClick: handleClick },
leftIcon ? React__default.createElement("div", { className: "nut-noticebar-box-left-icon" }, leftIcon) : null,
children ? React__default.createElement("div", { className: "nut-noticebar-box-wrap", ref: innerRef }, scrollList.current.map((item, index) => {
return React__default.createElement("div", { style: itemStyle(index), key: index, onClick: (e) => {
handleItemClick(e, item);
} }, item);
})) : React__default.createElement("div", { className: "nut-noticebar-box-horseLamp-list", style: horseLampStyle }, scrollList.current.map((item, index) => {
return (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
React__default.createElement("div", { className: "nut-noticebar-box-horseLamp-list-item", style: { height }, key: index, onClick: (e) => {
handleItemClick(e, item);
} }, item)
);
})),
React__default.createElement("div", { className: "nut-noticebar-box-right-icon", onClick: (e) => {
handleClickIcon(e);
} }, rightIcon || (closeable ? React__default.createElement(Close, { width: 12, height: 12 }) : null))
) : null
);
};
NoticeBar.displayName = "NutNoticeBar";
export {
NoticeBar as default
};