deepsea-components 2.3.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.tsx CHANGED
@@ -126,6 +126,29 @@ export interface ImportExcelProps extends Omit<InputFileProps, "multiple" | "onC
126
126
  onChange?: (data: Record<string, string>[]) => void
127
127
  }
128
128
 
129
+ export type InputFileButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
130
+ input: InputFileProps
131
+ }
132
+
133
+ /** 专用于读取文件的 button 组件 */
134
+ export const InputFileButton = forwardRef<HTMLButtonElement, InputFileButtonProps>((props, ref) => {
135
+ const { onClick, children, input: inputProps, ...others } = props
136
+ const { style, ...otherInputProps } = inputProps
137
+ const input = useRef<HTMLInputElement>(null)
138
+
139
+ function onBtnClick(e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) {
140
+ input.current?.click()
141
+ onClick?.(e)
142
+ }
143
+
144
+ return (
145
+ <button ref={ref} type="button" onClick={onBtnClick} {...others}>
146
+ <InputFile ref={input} style={{ display: "none", ...style }} {...otherInputProps} />
147
+ {children}
148
+ </button>
149
+ )
150
+ })
151
+
129
152
  /** 专门用于读取 excel 的组件 */
130
153
  export const ImportExcel = forwardRef<HTMLInputElement, ImportExcelProps>((props, ref) => {
131
154
  const { onChange, ...others } = props
@@ -501,111 +524,96 @@ export const Trapezium = forwardRef<HTMLDivElement, TrapeziumProps>((props, ref)
501
524
  return <div ref={ref} style={{ width: bottom, height, clipPath: `path("M ${diff + f} ${0} A ${borderRadius} ${borderRadius} 0 0 0 ${diff - g} ${h} L ${c} ${height - d} A ${borderRadius} ${borderRadius} 0 0 0 ${b} ${height} L ${bottom - b} ${height} A ${borderRadius} ${borderRadius} 0 0 0 ${bottom - c} ${height - d} L ${top + diff + g} ${h} A ${borderRadius} ${borderRadius} 0 0 0 ${top + diff - f} ${0} Z")`, ...style }} {...other} />
502
525
  })
503
526
 
504
- export interface LoopSwiperProps<T> {
505
- /** 源数据 */
506
- data: T[]
507
- /** 渲染函数 */
508
- render: (item: T, index: number, array: T[]) => ReactNode
509
- /** 每个元素的key */
510
- keyExactor?: (item: T, index: number, array: T[]) => string
511
- /** 水平方向是宽度,垂直方向是高度 */
512
- size: number
513
- /** 元素之间间隔 */
514
- gap?: number
515
- /** 起始位置 */
516
- start?: number
517
- /** 速度,水平方向正数为向左,水平方向负数为向右,垂直方向正数为向上,水平方向负数为向下 */
518
- speed: number
519
- /** 是否暂停播放 */
520
- paused?: boolean
521
- /** 容器的类名 */
522
- className?: string
523
- /** x 水平方向,y 垂直方向 */
524
- direction?: "x" | "y"
525
- }
526
-
527
- export interface LoopSwiperItem {
528
- key: string
529
- dom: HTMLDivElement | null
530
- offset: number
527
+ export interface LoopSwiperProps extends HTMLAttributes<HTMLDivElement> {
528
+ direction?: "horizontal" | "vertical"
529
+ reverse?: boolean
530
+ period: number
531
+ gap?: CSSProperties["gap"]
531
532
  }
532
533
 
533
- /** 循环播放组件 */
534
- export function LoopSwiper<T>(props: LoopSwiperProps<T>) {
535
- const { data, render, keyExactor, size, gap = 0, start = 0, speed, paused = false, className = "", direction = "x" } = props
536
- if (!(size > 0 && (speed > 0 || speed < 0))) {
537
- throw new RangeError("size 必须是正数,speed 必须非0")
534
+ css`
535
+ @keyframes deepsea-horizontal-loop-swipe {
536
+ from {
537
+ transform: translateX(0);
538
+ }
539
+ to {
540
+ transform: translateX(-100%);
541
+ }
538
542
  }
539
- const keys = data.map((it, idx, arr) => (keyExactor ? keyExactor(it, idx, arr) : String(idx)))
540
- const keysRef = useRef(keys)
541
- const cache = useRef({ size, gap, speed, keys, paused, className, direction })
542
- cache.current = { size, gap, speed, keys, paused, className, direction }
543
- const eles = useRef<LoopSwiperItem[]>(keys.map((key, idx) => ({ key, dom: null, offset: idx * (size + gap) + start })))
544
-
545
- if (keysRef.current.length !== keys.length || keysRef.current.some((it, idx) => it !== keys[idx])) {
546
- keysRef.current = keys
547
- eles.current = keys.map((key, idx) => ({ key, dom: null, offset: idx * (size + gap) + start }))
543
+ @keyframes deepsea-reverse-horizontal-loop-swipe {
544
+ from {
545
+ transform: translateX(0);
546
+ }
547
+ to {
548
+ transform: translateX(100%);
549
+ }
548
550
  }
549
-
550
- function setStyles() {
551
- const { size, speed, className, direction } = cache.current
552
- eles.current.forEach(it => {
553
- it.dom!.className = className
554
- it.dom!.style.setProperty("position", `absolute`)
555
- it.dom!.style.setProperty("width", `${size}px`)
556
- direction === "x" && speed < 0 ? it.dom!.style.setProperty("right", `0`) : it.dom!.style.setProperty("left", `0`)
557
- direction === "y" && speed < 0 ? it.dom!.style.setProperty("bottom", `0`) : it.dom!.style.setProperty("top", `0`)
558
- it.dom!.style.setProperty("transform", `translate${direction.toUpperCase()}(${it.offset * (speed > 0 ? 1 : -1)}px)`)
559
- })
551
+ @keyframes deepsea-vertical-loop-swipe {
552
+ from {
553
+ transform: translateY(0);
554
+ }
555
+ to {
556
+ transform: translateY(-100%);
557
+ }
558
+ }
559
+ @keyframes deepsea-reverse-vertical-loop-swipe {
560
+ from {
561
+ transform: translateY(0);
562
+ }
563
+ to {
564
+ transform: translateY(100%);
565
+ }
560
566
  }
567
+ `
561
568
 
562
- useEffect(() => {
563
- setStyles()
564
- }, [])
569
+ /** 循环播放组件 */
570
+ export const LoopSwiper = forwardRef<HTMLDivElement, LoopSwiperProps>((props, ref) => {
571
+ const { style, children, direction, period, reverse, gap, ...others } = props
572
+ const wrapper = useRef<HTMLDivElement>(null)
573
+ const container = useRef<HTMLDivElement>(null)
574
+ const [swiper, setSwiper] = useState(false)
575
+ const directionRef = useRef(direction)
576
+ directionRef.current = direction
577
+ const flexDirection: CSSProperties["flexDirection"] = direction === "vertical" ? (reverse ? "column-reverse" : "column") : reverse ? "row-reverse" : "row"
578
+ const animationName = swiper ? (direction === "vertical" ? (reverse ? "deepsea-reverse-vertical-loop-swipe" : "deepsea-vertical-loop-swipe") : reverse ? "deepsea-reverse-horizontal-loop-swipe" : "deepsea-horizontal-loop-swipe") : "none"
579
+ const animationDuration = `${period}ms`
580
+ const animationTimingFunction = "linear"
581
+ const animationIterationCount = "infinite"
582
+
583
+ useImperativeHandle(ref, () => wrapper.current!)
565
584
 
566
585
  useEffect(() => {
567
- const stop = setFrameInterval(() => {
568
- const { size, gap, speed, keys, paused } = cache.current
569
- if (paused) return
570
- eles.current.length = keys.length
571
- let minIndex = 0
572
- eles.current.forEach((it, idx) => {
573
- if (it.offset < eles.current[minIndex].offset) {
574
- minIndex = idx
575
- }
576
- })
577
- const minOffset = eles.current[minIndex].offset
578
- eles.current.forEach((it, idx) => {
579
- let index = idx
580
- if (idx < minIndex) index = eles.current.length + idx
581
- it.offset = minOffset + (index - minIndex) * (size + gap)
582
- let newOffset = it.offset - Math.abs(speed)
583
- if (newOffset + size + gap <= 0) {
584
- newOffset += eles.current.length * (size + gap)
586
+ const wrapperEle = wrapper.current!
587
+ const containerEle = container.current!
588
+ let wrapperWidth = 0
589
+ let wrapperHeight = 0
590
+ let containerWidth = 0
591
+ let containerHeight = 0
592
+ const observer = new ResizeObserver(entries => {
593
+ entries.forEach(entry => {
594
+ if (entry.target === wrapperEle) {
595
+ wrapperWidth = entry.contentRect.width
596
+ wrapperHeight = entry.contentRect.height
597
+ } else if (entry.target === containerEle) {
598
+ containerWidth = entry.contentRect.width
599
+ containerHeight = entry.contentRect.height
585
600
  }
586
- it.offset = newOffset
587
601
  })
588
- setStyles()
589
- }, 1)
590
-
591
- return stop
602
+ setSwiper(directionRef.current === "vertical" ? containerHeight > wrapperHeight : containerWidth > wrapperWidth)
603
+ })
604
+ observer.observe(wrapperEle)
605
+ observer.observe(containerEle)
592
606
  }, [])
593
607
 
594
- function ref(dom: HTMLDivElement | null, index: number) {
595
- if (!eles.current[index]) return
596
- eles.current[index].dom = dom
597
- }
598
-
599
608
  return (
600
- <Fragment>
601
- {data.map((it, idx, arr) => (
602
- <div key={keys[idx]} ref={dom => ref(dom, idx)}>
603
- {render(it, idx, arr)}
604
- </div>
605
- ))}
606
- </Fragment>
609
+ <div ref={wrapper} style={{ display: "flex", flexDirection, gap, ...style }} {...others}>
610
+ <div ref={container} style={{ display: "flex", flexDirection, gap, animationName, animationTimingFunction, animationDuration, animationIterationCount }}>
611
+ {children}
612
+ </div>
613
+ <div style={{ display: swiper ? "flex" : "none", flexDirection, gap, animationName, animationTimingFunction, animationDuration, animationIterationCount }}>{children}</div>
614
+ </div>
607
615
  )
608
- }
616
+ })
609
617
 
610
618
  export interface SectionRingProps extends HTMLAttributes<HTMLDivElement> {
611
619
  outerRadius: number